]> git.tdb.fi Git - libs/gltk.git/blob - source/widget.cpp
Avoid autosizing widgets with no style
[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         const Style::PartSeq &parts = style->get_parts();
160         list<CachedPart>::iterator j = cached_parts.begin();
161         for(Style::PartSeq::const_iterator i=parts.begin(); i!=parts.end(); ++i, ++j)
162         {
163                 if(j==cached_parts.end())
164                         j = cached_parts.insert(j, CachedPart());
165                 if(i->get_name().empty())
166                         i->build(geom, state, *j);
167                 else
168                         rebuild_special(*i, *j);
169         }
170 }
171
172 void Widget::render(GL::Renderer &renderer) const
173 {
174         if(!style)
175                 throw logic_error(format("Attempt to render a widget with null style (class=\"%s\", style_name=\"%s\")", get_class(), style_name));
176
177         GL::MatrixStack::Push _pushm(renderer.matrix_stack());
178         renderer.matrix_stack() *= GL::Matrix::translation(geom.x, geom.y, 0);
179         const Style::PartSeq &parts = style->get_parts();
180         list<CachedPart>::const_iterator j = cached_parts.begin();
181         for(Style::PartSeq::const_iterator i=parts.begin(); (i!=parts.end() && j!=cached_parts.end()); ++i, ++j)
182         {
183                 if(j->mesh && j->texture)
184                 {
185                         renderer.set_texture(j->texture);
186                         j->mesh->draw(renderer);
187                 }
188                 else if(!i->get_name().empty())
189                         render_special(*i, renderer);
190         }
191 }
192
193 void Widget::pointer_enter()
194 {
195         set_state(HOVER);
196 }
197
198 void Widget::pointer_leave()
199 {
200         clear_state(HOVER);
201 }
202
203 void Widget::focus_in()
204 {
205         set_state(FOCUS);
206 }
207
208 void Widget::focus_out()
209 {
210         clear_state(FOCUS);
211 }
212
213
214 Widget::Loader::Loader(Widget &w):
215         DataFile::ObjectLoader<Widget>(w)
216 {
217         add("position", &Loader::position);
218         add("size",     &Loader::size);
219         add("style",    &Loader::style);
220         add("visible",  &Widget::visible);
221 }
222
223 void Widget::Loader::position(int x, int y)
224 {
225         obj.set_position(x, y);
226 }
227
228 void Widget::Loader::size(unsigned w, unsigned h)
229 {
230         obj.set_size(w, h);
231 }
232
233 void Widget::Loader::style(const string &s)
234 {
235         obj.set_style(s);
236 }
237
238 } // namespace GLtk
239 } // namespace Msp