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