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