]> git.tdb.fi Git - libs/gltk.git/blob - source/dropdown.cpp
Rework how widget ownership works in Container
[libs/gltk.git] / source / dropdown.cpp
1 #include <msp/gl/font.h>
2 #include "dropdown.h"
3 #include "list.h"
4 #include "panel.h"
5 #include "part.h"
6 #include "root.h"
7 #include "style.h"
8 #include "text.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GLtk {
14
15 Dropdown::Dropdown()
16 {
17         init();
18 }
19
20 Dropdown::Dropdown(ListData &d):
21         list(d)
22 {
23         init();
24 }
25
26 void Dropdown::init()
27 {
28         input_type = INPUT_NAVIGATION;
29
30         dropped = false;
31
32         add(list);
33         list.set_visible(false);
34         list.set_view_all();
35         list.signal_item_selected.connect(sigc::mem_fun(this, &Dropdown::list_item_selected));
36         list.signal_selection_cleared.connect(sigc::mem_fun(this, &Dropdown::list_selection_cleared));
37         list.signal_autosize_changed.connect(sigc::mem_fun(this, &Dropdown::list_autosize_changed));
38 }
39
40 void Dropdown::autosize_special(const Part &part, Geometry &ageom) const
41 {
42         if(part.get_name()=="list")
43         {
44                 Geometry lgeom;
45                 list.autosize(lgeom);
46                 ageom.w = max(ageom.w, list.get_geometry().w);
47         }
48         else if(part.get_name()=="text")
49         {
50                 const Sides &margin = part.get_margin();
51                 const GL::Font &font = style->get_font();
52                 float font_size = style->get_font_size();
53
54                 unsigned max_w = 0;
55                 const ListData &data = list.get_data();
56                 for(unsigned i=0; i<data.size(); ++i)
57                 {
58                         unsigned w = static_cast<unsigned>(font.get_string_width(data.get_string(i))*font_size);
59                         max_w = max(max_w, w);
60                 }
61                 ageom.w = max(ageom.w, max_w+margin.left+margin.right);
62
63                 unsigned line_height = static_cast<unsigned>((font.get_ascent()-font.get_descent())*font_size);
64                 ageom.h = max(ageom.h, line_height+margin.top+margin.bottom);
65         }
66 }
67
68 void Dropdown::rebuild_special(const Part &part)
69 {
70         if(part.get_name()=="text")
71                 text.build(part, state, geom, part_cache);
72         else
73                 Widget::rebuild_special(part);
74 }
75
76 void Dropdown::render_special(const Part &part, GL::Renderer &renderer) const
77 {
78         if(part.get_name()=="list" && dropped)
79                 list.render(renderer);
80 }
81
82 void Dropdown::button_press(int x, int y, unsigned btn)
83 {
84         if(dropped)
85         {
86                 Container::button_press(x, y, btn);
87                 if(!click_focus)
88                         close_list();
89         }
90         else if(btn==1)
91                 open_list();
92 }
93
94 bool Dropdown::navigate(Navigation nav)
95 {
96         if(dropped)
97         {
98                 if(nav==NAV_CANCEL)
99                         close_list();
100                 else
101                         list.navigate(nav);
102         }
103         else if(nav==NAV_ACTIVATE)
104                 open_list();
105         else
106                 return false;
107
108         return true;
109 }
110
111 void Dropdown::on_geometry_change()
112 {
113         if(dropped)
114                 resize_list();
115 }
116
117 void Dropdown::on_style_change()
118 {
119         text.set_style(style);
120         if(dropped)
121                 resize_list();
122 }
123
124 void Dropdown::open_list()
125 {
126         dropped = true;
127         list.set_visible(true);
128         resize_list();
129         set_state(ACTIVE);
130         set_input_focus(&list);
131         signal_grab_pointer.emit();
132 }
133
134 void Dropdown::close_list()
135 {
136         dropped = false;
137         list.set_visible(false);
138         clear_state(ACTIVE);
139         signal_ungrab_pointer.emit();
140 }
141
142 void Dropdown::list_autosize_changed()
143 {
144         if(dropped)
145                 resize_list();
146         signal_autosize_changed.emit();
147 }
148
149 void Dropdown::resize_list()
150 {
151         Geometry lgeom;
152         list.autosize(lgeom);
153         lgeom.x = 0;
154         lgeom.y = -lgeom.h;
155         lgeom.w = max(geom.w, lgeom.w);
156         int root_x = geom.x;
157         int root_y = geom.y;
158         for(Widget *p=parent; p; p=p->get_parent())
159         {
160                 root_x += p->get_geometry().x;
161                 root_y += p->get_geometry().y;
162                 if(Root *root = dynamic_cast<Root *>(p))
163                 {
164                         const Geometry &rgeom = root->get_geometry();
165                         if(lgeom.h*2>rgeom.h)
166                         {
167                                 lgeom.h = rgeom.h/2;
168                                 lgeom.y = -lgeom.h;
169                         }
170                         if(root_y+lgeom.y<0)
171                                 lgeom.y = -root_y;
172                         if(root_y+lgeom.y+lgeom.h>rgeom.h)
173                                 lgeom.y = rgeom.h-lgeom.h-root_y;
174                         if(root_x+lgeom.x+lgeom.w>rgeom.w)
175                                 lgeom.x = rgeom.w-lgeom.w-root_x;
176                         break;
177                 }
178         }
179         list.set_geometry(lgeom);
180 }
181
182 void Dropdown::list_item_selected(unsigned index)
183 {
184         if(dropped)
185         {
186                 dropped = false;
187                 clear_state(ACTIVE);
188                 signal_ungrab_pointer.emit();
189         }
190
191         text.set(list.get_data().get_string(index));
192
193         signal_item_selected.emit(index);
194         rebuild();
195 }
196
197 void Dropdown::list_selection_cleared()
198 {
199         text.set(string());
200 }
201
202
203 Dropdown::Loader::Loader(Dropdown &d):
204         DataFile::DerivedObjectLoader<Dropdown, Widget::Loader>(d)
205 {
206         add("item", &Loader::item);
207 }
208
209 void Dropdown::Loader::item(const string &v)
210 {
211         dynamic_cast<BasicListData<string> &>(obj.list.get_data()).append(v);
212 }
213
214 } // namespace GLtk
215 } // namespace Msp