]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/mag/include/server/menu
Some minor fixes.
[l4.git] / l4 / pkg / mag / include / server / menu
1 // vi:ft=cpp
2 /*
3  * (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 #pragma once
11
12 #include <l4/mag-gfx/canvas>
13 #include <l4/mag-gfx/clip_guard>
14 #include <l4/mag-gfx/font>
15
16 #include <l4/cxx/observer>
17
18 #include <l4/re/event_enums.h>
19
20 namespace Mag_server {
21
22 template< typename C >
23 class Menu : private cxx::Observer
24 {
25 private:
26   typedef C Content;
27   typedef typename C::Value_type Item;
28   typedef typename C::Iterator Iterator;
29   typedef typename C::Const_iterator Const_iterator;
30
31   View *_view;
32   Core_api const *_core;
33
34   Content const *_content;
35   Const_iterator _current;
36
37   Area _size;
38   Area _cell;
39   int _height;
40   int _offset;
41   bool _scroll;
42   int _speed;
43
44   Font const *_font() const { return _core->label_font(); }
45   View_stack *_vstack() const { return _core->user_state()->vstack(); }
46
47 public:
48   int scroll_button_height() const { return 15; }
49   int item_sep() const { return 2; }
50   int menu_border() const { return 4; }
51   int min_width() const { return 180; }
52
53   Menu(Core_api const *core, View *view, Content const *content)
54   : _view(view), _core(core), _content(content), _current(content->end()),
55     _cell(min_width(), _font()->str_h("X") + 2 * item_sep()), _height(0),
56     _offset(0), _scroll(false), _speed(0)
57   {}
58
59   Area calc_geometry(Area const &size);
60   Const_iterator find(Point const &pos, Rect *cell, int *y);
61   bool find(Const_iterator const &x, Rect *cell, int *y);
62
63   void draw(Canvas *canvas, Point const &pos) const;
64   Const_iterator handle_event(Hid_report *e, Point const &mouse);
65
66   void scroll(int dist)
67   {
68     int h = _size.h() - 2 * scroll_button_height();
69     _offset = std::max(0, std::min(_height - h, _offset + dist));
70   }
71
72   void optimize_offset(int current_pos)
73   {
74     if (!_scroll)
75       {
76         _offset = 0;
77         return;
78       }
79
80     /* usable height */
81     int h = _size.h() - 2 * scroll_button_height();
82     int o_min = std::max(current_pos + _cell.h() - h, 0);
83     int o_max = std::min(current_pos, _height - h);
84
85     if (_offset <= o_max && _offset >= o_min)
86       return;
87
88     if (_offset > o_max)
89       _offset = o_max;
90
91     if (_offset < o_min)
92       _offset = o_min;
93   }
94
95 private:
96   template< typename F >
97   Const_iterator _find(Rect const &_bb, F const &f) const;
98
99   struct Find_for_pos
100   {
101     Point pos;
102     Rect *cell;
103     int *offs;
104
105     Find_for_pos(Point const &p, Rect *cell, int *offs)
106     : pos(p), cell(cell), offs(offs) {}
107
108     bool valid(Rect const &bb) const { return bb.contains(pos); }
109     bool operator () (Const_iterator const &, Rect const &bb, int y) const
110     {
111       if (!bb.contains(pos))
112         return false;
113
114       *cell = bb;
115       *offs = y;
116       return true;
117     }
118   };
119
120   struct Find_item
121   {
122     Const_iterator item;
123     Rect *cell;
124     int *offs;
125
126     Find_item(Const_iterator const &item, Rect *cell, int *offs)
127     : item(item), cell(cell), offs(offs) {}
128
129     bool valid(Rect const &) const { return true; }
130     bool operator () (Const_iterator const &e, Rect const &bb, int y) const
131     {
132       if (e != item)
133         return false;
134
135       *offs = y;
136       *cell = bb;
137       return true;
138     }
139   };
140
141
142   struct Draw_item
143   {
144   private:
145     Draw_item(Draw_item const &);
146
147   public:
148     Canvas *canvas;
149     Menu const *menu;
150     mutable Clip_guard g;
151
152     Draw_item(Menu const *m, Canvas *c) : canvas(c), menu(m) {}
153     bool valid(Rect const &bb) const
154     {
155       g.init(canvas, bb);
156       return canvas->clip_valid();
157     }
158
159     bool operator () (Const_iterator const &e, Rect br, int) const
160     {
161       Rgb32::Color lcol = e->ignore() ? Rgb32::Color(255, 200, 0) : Rgb32::White;
162       Rgb32::Color bcol = e == menu->_current ? Rgb32::Color(99, 120, 180) : Rgb32::Color(99, 99, 88);
163       canvas->draw_box(br, bcol);
164       br = br.offset(menu->menu_border(), menu->item_sep(), -menu->menu_border(), -menu->item_sep());
165       Clip_guard g(canvas, br);
166       canvas->draw_string(br.p1(), menu->_font(), lcol, e->label());
167       return false;
168     }
169   };
170
171   // handle timer ticks
172   void notify();
173 };
174
175 template<typename C>
176 Area
177 Menu<C>::calc_geometry(Area const &max_size)
178 {
179   Area fs(min_width(), 0); //2 * menu_border());
180   bool cf = _current == _content->end();
181   int cp = 0;
182   for (Const_iterator e = _content->begin(); e != _content->end(); ++e)
183     {
184       int w = _font()->str_w(e->label()) + 2 * menu_border();
185       if (w > fs.w())
186         fs.w(w);
187
188       if (_current == e)
189         {
190           cp = fs.h();
191           cf = true;
192         }
193
194       fs.h(fs.h() + _cell.h());
195     }
196
197   /* the current item is gone so select none */
198   if (!cf)
199     _current = _content->end();
200
201   _height = fs.h();
202   if (fs.h() > max_size.h())
203     {
204       fs.h(max_size.h());
205       _scroll = true;
206     }
207   else
208     _scroll = false;
209
210   if (_current != _content->end())
211     optimize_offset(cp);
212
213   if (fs.w() > max_size.w())
214     fs.w(max_size.w());
215
216   _cell.w(fs.w());
217
218   _size = fs;
219   return fs;
220 }
221
222 template< typename C >
223 template< typename F >
224 typename Menu<C>::Const_iterator
225 Menu<C>::_find(Rect const &_bb, F const &f) const
226 {
227   Rect bb = _bb;
228   if (_scroll)
229     bb = _bb.offset(0, scroll_button_height(), 0, -scroll_button_height());
230
231   if (!f.valid(bb))
232     return _content->end();
233
234   int y = 0;
235
236   for (Const_iterator e = _content->begin(); e != _content->end(); ++e)
237     {
238       int ny = y + _cell.h();
239
240       if (y - _offset > bb.h())
241         break;
242
243       if (ny >= _offset)
244         {
245           Rect br(Point(bb.x1(), bb.y1() + y - _offset), _cell);
246           if (f(e, br, y))
247             return e;
248         }
249       y = ny;
250     }
251   return _content->end();
252 }
253
254
255 template< typename C >
256 typename Menu<C>::Const_iterator
257 Menu<C>::find(Point const &pos, Rect *cell, int *y)
258 { return _find(Rect(Point(), _size), Find_for_pos(pos, cell, y)); }
259
260 template< typename C >
261 bool
262 Menu<C>::find(Const_iterator const &x, Rect *cell, int *y)
263 { return _find(Rect(Point(), _size), Find_item(x, cell, y)) != _content->end(); }
264
265 template< typename C >
266 void
267 Menu<C>::draw(Canvas *canvas, Point const &pos) const
268 {
269   using Mag_gfx::Clip_guard;
270
271   Rect bb(pos, _size);
272   Clip_guard g(canvas, bb);
273
274   if (!canvas->clip_valid())
275     return;
276
277   if (_scroll)
278     {
279       Rect s(pos, Area(_size.w(), scroll_button_height()));
280       canvas->draw_box(s, Rgb32::Color(99, 99, 88)); //, 220));
281       s = s + Point(0, _size.h() - scroll_button_height());
282       canvas->draw_box(s, Rgb32::Color(99, 99, 88)); //, 220));
283     }
284
285   // Hm, make g++ 4.2 happy, that tries to call the copy ctor
286   // in the case of directly using a temporary
287   Draw_item di(this, canvas);
288   _find(bb, di);
289 }
290
291 template< typename C >
292 typename Menu<C>::Const_iterator
293 Menu<C>::handle_event(Hid_report *e, Point const &mouse)
294 {
295   Valuator<int> const *abs = e->get_vals(3);
296   if (abs && abs->get(0).valid())
297     {
298       if (_scroll)
299         {
300           int const sbh = scroll_button_height();
301           int speed;
302           if ((speed = sbh - mouse.y()) > 0)
303             {
304               _speed = -speed;
305               if (!cxx::H_list<Observer>::in_list(this))
306                 _core->get_ticks(this);
307             }
308           else if ((speed = sbh - (_size.h() - mouse.y())) > 0)
309             {
310               _speed = speed;
311               if (!cxx::H_list<Observer>::in_list(this))
312                 _core->get_ticks(this);
313             }
314           else
315             {
316               cxx::H_list<Observer>::remove(this);
317               _speed = 0;
318             }
319
320           if (speed > 0)
321             return _content->end();
322         }
323       Rect nr;
324       int ny;
325       Const_iterator s = find(mouse, &nr, &ny);
326       if (_current != s)
327         {
328           Rect cr;
329           int cy;
330           bool refresh_cr = false;
331           if (_current != _content->end() && find(_current, &cr, &cy))
332             refresh_cr = true;
333
334           _current = s;
335
336           int old_offs = _offset;
337
338           if (_current != _content->end())
339             optimize_offset(ny);
340
341           if (old_offs != _offset)
342             _vstack()->refresh_view(_view, 0, *_view);
343           else
344             {
345               if (refresh_cr)
346                 _vstack()->refresh_view(_view, 0, cr + _view->p1());
347
348               if (s != _content->end())
349                 _vstack()->refresh_view(_view, 0, nr + _view->p1());
350             }
351         }
352     }
353
354   Hid_report::Key_event const *btn_left = e->find_key_event(L4RE_BTN_LEFT);
355   if (btn_left && btn_left->value == 0)
356     {
357       Rect nr;
358       int ny;
359       return find(mouse, &nr, &ny);
360     }
361
362   return _content->end();
363 }
364
365 template< typename C >
366 void
367 Menu<C>::notify()
368 {
369   if (!_speed)
370     cxx::H_list<Observer>::remove(this);
371
372   int old = _offset;
373   scroll(_speed);
374   if (old != _offset)
375     _vstack()->refresh_view(_view, 0, *_view);
376   else
377     cxx::H_list<Observer>::remove(this);
378 }
379
380
381 }