]> git.tdb.fi Git - libs/gltk.git/blob - source/widget.cpp
1113c971974f84779a3ca863df1b57f4ddf973ea
[libs/gltk.git] / source / widget.cpp
1 #include <msp/gl/matrix.h>
2 #include <msp/strings/format.h>
3 #include "container.h"
4 #include "resources.h"
5 #include "root.h"
6 #include "widget.h"
7
8 using namespace std;
9
10 namespace Msp {
11 namespace GLtk {
12
13 Widget::Widget():
14         style(0),
15         state(NORMAL),
16         visible(true),
17         focusable(true),
18         parent(0)
19 { }
20
21 Widget::~Widget()
22 {
23         if(parent)
24         {
25                 Container *p = parent;
26                 parent = 0;
27                 p->remove(*this);
28         }
29 }
30
31 void Widget::set_position(int x, int y)
32 {
33         set_geometry(Geometry(x, y, geom.w, geom.h));
34 }
35
36 void Widget::set_size(unsigned w, unsigned h)
37 {
38         set_geometry(Geometry(geom.x, geom.y, w, h));
39 }
40
41 void Widget::autosize()
42 {
43         geom.w = 0;
44         geom.h = 0;
45         const Style::PartSeq &parts = style->get_parts();
46         for(Style::PartSeq::const_iterator i=parts.begin(); i!=parts.end(); ++i)
47                 if(i->get_name().empty())
48                 {
49                         const Geometry &pgeom = i->get_geometry();
50                         const Sides &pmargin = i->get_margin();
51                         geom.w = max(geom.w, pgeom.w+pmargin.left+pmargin.right);
52                         geom.h = max(geom.h, pgeom.h+pmargin.top+pmargin.bottom);
53                 }
54 }
55
56 void Widget::set_geometry(const Geometry &g)
57 {
58         geom = g;
59         on_geometry_change();
60         rebuild();
61 }
62
63 void Widget::set_parent(Container *p)
64 {
65         if(parent && p)
66                 throw hierarchy_error("widget already parented");
67         else if(p==parent)
68                 return;
69
70         try
71         {
72                 parent = p;
73
74                 on_reparent();
75                 update_style();
76         }
77         catch(...)
78         {
79                 // The container has not yet added the widget as its child
80                 parent = 0;
81                 throw;
82         }
83 }
84
85 void Widget::set_style(const string &s)
86 {
87         style_name = s;
88         update_style();
89 }
90
91 void Widget::update_style()
92 {
93         Widget *top;
94         for(top=this; top->parent; top=top->parent) ;
95         Root *root = dynamic_cast<Root *>(top);
96         if(!root)
97                 style = 0;
98         else
99         {
100                 string sname = get_class();
101                 if(!style_name.empty())
102                 {
103                         sname += '-';
104                         sname += style_name;
105                 }
106
107                 style = &root->get_resources().get<Style>(sname);
108         }
109
110         on_style_change();
111         signal_autosize_changed.emit();
112         rebuild();
113 }
114
115 void Widget::set_tooltip(const string &t)
116 {
117         tooltip = t;
118 }
119
120 void Widget::set_visible(bool v)
121 {
122         if(v==visible)
123                 return;
124
125         visible = v;
126
127         signal_visibility_changed.emit(visible);
128 }
129
130 void Widget::set_focusable(bool f)
131 {
132         focusable = f;
133 }
134
135 void Widget::set_focus()
136 {
137         if(!parent)
138                 throw hierarchy_error("no parent");
139         if(!visible)
140                 throw logic_error("!visible");
141
142         signal_request_focus.emit();
143 }
144
145 void Widget::set_state(State mask, State bits)
146 {
147         state = (state&~mask)|bits;
148         rebuild();
149 }
150
151 void Widget::rebuild()
152 {
153         if(!style)
154                 return;
155
156         const Style::PartSeq &parts = style->get_parts();
157         list<CachedPart>::iterator j = cached_parts.begin();
158         for(Style::PartSeq::const_iterator i=parts.begin(); i!=parts.end(); ++i, ++j)
159         {
160                 if(j==cached_parts.end())
161                         j = cached_parts.insert(j, CachedPart());
162                 if(i->get_name().empty())
163                         i->build(geom, state, *j);
164                 else
165                         rebuild_special(*i, *j);
166         }
167 }
168
169 void Widget::render(GL::Renderer &renderer) const
170 {
171         if(!style)
172                 throw logic_error(format("Attempt to render a widget with null style (class=\"%s\", style_name=\"%s\")", get_class(), style_name));
173
174         GL::MatrixStack::Push _pushm(renderer.matrix_stack());
175         renderer.matrix_stack() *= GL::Matrix::translation(geom.x, geom.y, 0);
176         const Style::PartSeq &parts = style->get_parts();
177         list<CachedPart>::const_iterator j = cached_parts.begin();
178         for(Style::PartSeq::const_iterator i=parts.begin(); (i!=parts.end() && j!=cached_parts.end()); ++i, ++j)
179         {
180                 if(j->mesh && j->texture)
181                 {
182                         renderer.set_texture(j->texture);
183                         j->mesh->draw(renderer);
184                 }
185                 else if(!i->get_name().empty())
186                         render_special(*i, renderer);
187         }
188 }
189
190 void Widget::pointer_enter()
191 {
192         set_state(HOVER);
193 }
194
195 void Widget::pointer_leave()
196 {
197         clear_state(HOVER);
198 }
199
200 void Widget::focus_in()
201 {
202         set_state(FOCUS);
203 }
204
205 void Widget::focus_out()
206 {
207         clear_state(FOCUS);
208 }
209
210
211 Widget::Loader::Loader(Widget &w):
212         DataFile::ObjectLoader<Widget>(w)
213 {
214         add("position", &Loader::position);
215         add("size",     &Loader::size);
216         add("style",    &Loader::style);
217         add("visible",  &Widget::visible);
218 }
219
220 void Widget::Loader::position(int x, int y)
221 {
222         obj.set_position(x, y);
223 }
224
225 void Widget::Loader::size(unsigned w, unsigned h)
226 {
227         obj.set_size(w, h);
228 }
229
230 void Widget::Loader::style(const string &s)
231 {
232         obj.set_style(s);
233 }
234
235 } // namespace GLtk
236 } // namespace Msp