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