1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
class UserInterface{
public:
virtual ~UserInterface(){}
//Handle events from an internal object. Return a flag indicating one or
//more actions that need to be performed afterwards (e.g. redraw the screen).
virtual unsigned receive(TotalTimeUpdate &) = 0;
virtual unsigned receive(MetaDataUpdate &) = 0;
};
//A class for the UI as a whole. All GUIElements are children
//or grandchildren of an instance of this class.
class GUI;
class GUIElement : public UserInterface{
protected:
GUI *sui;
GUIElement *parent;
public:
GUIElement(GUI *sui, GUIElement *parent): sui(sui), parent(parent){}
virtual ~GUIElement(){}
//Redraw the object.
virtual void update(){}
//Handle SDL events. Return a flag. The GUIElement implementations are
//trivial: they do nothing and return 0.
virtual unsigned handle_event(const SDL_Event &);
virtual unsigned receive(TotalTimeUpdate &);
virtual unsigned receive(MetaDataUpdate &);
//Handle a signal from a GUI element (e.g. a button that was pressed).
virtual void gui_signal(unsigned){}
};
class GUI : public GUIElement{
public:
//Return values for handle_event() and receive().
enum InputStatus{
NOTHING = 0,
QUIT = 1,
REDRAW = 2,
};
private:
std::list<boost::shared_ptr<GUIElement> > gui_elements;
unsigned handle_in_events();
public:
void main_loop();
unsigned receive(TotalTimeUpdate &);
unsigned receive(MetaDataUpdate &);
};
void GUI::main_loop(){
unsigned status;
while (!check_flag(status = this->handle_in_events(), QUIT)){
//Do other stuff, such as drawing.
}
}
unsigned GUI::handle_in_events(){
SDL_Event e;
unsigned ret = NOTHING;
while (SDL_PollEvent(&e)){
//Handle input events that are considered "global" to the application.
switch (e.type){
case SDL_QUIT:
return QUIT;
case SDL_WINDOWEVENT:
ret |= REDRAW;
break;
//etc.
}
//Notify children.
for (auto &p : this->gui_elements)
ret |= p->handle_event(e);
}
return ret;
}
class Button : public GUIElement{
protected:
//(x, y): The position of the top-left corner.
//(w, h): The size of the button.
SDL_Rect bounding_box;
public:
Button(GUI *sui, GUIElement *parent): GUIElement(sui, parent){}
virtual ~Button(){}
unsigned handle_event(const SDL_Event &);
void set_bounding_box(const SDL_Rect &bb){
this->bounding_box = bb;
}
virtual void on_click() = 0;
};
unsigned Button::handle_event(const SDL_Event &event){
if (event.type != SDL_MOUSEBUTTONUP)
return GUI::NOTHING;
auto x = event.button.x;
auto y = event.button.y;
const auto &bb = this->bounding_box;
if (x >= bb.x && x < bb.x + bb.w && y >= bb.y && y < bb.y + bb.h)
this->on_click();
return GUI::NOTHING;
}
class IntegerSignalButton : public Button{
protected:
unsigned signal_value;
//If true, the button calls gui_signal(signal_value) on the global GUI
//object; otherwise, it calls the function on the parent.
bool global_button;
public:
IntegerSignalButton(GUI *sui, GUIElement *parent): Button(sui, parent), signal_value(0), global_button(0){}
virtual ~IntegerSignalButton(){}
void set_signal_value(unsigned signal_value){
this->signal_value = signal_value;
}
void set_global_button(bool global_button){
this->global_button = global_button;
}
void on_click(){
(this->global_button ? this->sui : this->parent)->gui_signal(this->signal_value);
}
};
class GraphicButton : public IntegerSignalButton{
protected:
Texture graphic;
public:
GraphicButton(GUI *sui, GUIElement *parent): IntegerSignalButton(sui, parent), graphic(){}
void set_graphic(Texture graphic){
this->graphic = graphic;
this->bounding_box.w = graphic.get_rect().w;
this->bounding_box.h = graphic.get_rect().h;
}
void update();
};
class TextButton : public IntegerSignalButton{
protected:
std::wstring text;
int wrapping_limit;
double scale;
void calculate_bounding_box();
public:
TextButton(GUI *sui, GUIElement *parent): IntegerSignalButton(sui, parent), scale(1){
this->bounding_box.w = max_possible_value(this->bounding_box.w);
this->bounding_box.h = max_possible_value(this->bounding_box.h);
}
void set_text(const std::wstring &text, int max_width = INT_MAX){
this->text = text;
this->wrapping_limit = max_width;
}
void update();
};
|