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