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