]> Dogcows Code - chaz/openbox/blob - otk/widget.cc
Brand spankin new widgets for otk (Label and Button).
[chaz/openbox] / otk / widget.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "config.h"
4 #include "widget.hh"
5 #include "display.hh"
6 #include "surface.hh"
7 #include "rendertexture.hh"
8 #include "rendercolor.hh"
9 #include "eventdispatcher.hh"
10 #include "screeninfo.hh"
11
12 #include <climits>
13 #include <cassert>
14 #include <algorithm>
15
16 namespace otk {
17
18 Widget::Widget(int screen, EventDispatcher *ed, Direction direction, int bevel,
19 bool overrideredir)
20 : _texture(0),
21 _screen(screen),
22 _parent(0),
23 _window(0),
24 _surface(0),
25 _event_mask(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
26 ExposureMask | StructureNotifyMask),
27 _alignment(RenderStyle::CenterJustify),
28 _direction(direction),
29 _max_size(UINT_MAX, UINT_MAX),
30 _visible(false),
31 _bordercolor(0),
32 _borderwidth(0),
33 _bevel(bevel),
34 _dirty(true),
35 _dispatcher(ed),
36 _ignore_config(0)
37 {
38 createWindow(overrideredir);
39 _dispatcher->registerHandler(_window, this);
40 }
41
42 Widget::Widget(Widget *parent, Direction direction, int bevel)
43 : _texture(0),
44 _screen(parent->_screen),
45 _parent(parent),
46 _window(0),
47 _surface(0),
48 _event_mask(ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
49 ExposureMask | StructureNotifyMask),
50 _alignment(RenderStyle::CenterJustify),
51 _direction(direction),
52 _max_size(UINT_MAX, UINT_MAX),
53 _visible(false),
54 _bordercolor(0),
55 _borderwidth(0),
56 _bevel(bevel),
57 _dirty(true),
58 _dispatcher(parent->_dispatcher),
59 _ignore_config(0)
60 {
61 assert(parent);
62 createWindow(false);
63 parent->addChild(this);
64 parent->layout();
65 _dispatcher->registerHandler(_window, this);
66 }
67
68 Widget::~Widget()
69 {
70 assert(_children.empty()); // this would be bad. theyd have a hanging _parent
71
72 if (_surface) delete _surface;
73 if (_parent) _parent->removeChild(this);
74
75 _dispatcher->clearHandler(_window);
76 XDestroyWindow(**display, _window);
77 }
78
79 void Widget::show(bool children)
80 {
81 if (children) {
82 std::list<Widget*>::iterator it , end = _children.end();
83 for (it = _children.begin(); it != end; ++it)
84 (*it)->show(true);
85 }
86 if (!_visible) {
87 _visible = true;
88 XMapWindow(**display, _window);
89 update();
90 }
91 }
92
93 void Widget::hide()
94 {
95 if (_visible) {
96 _visible = false;
97 XUnmapWindow(**display, _window);
98 if (_parent) _parent->layout();
99 }
100 }
101
102 void Widget::setEventMask(long e)
103 {
104 XSelectInput(**display, _window, e);
105 _event_mask = e;
106 }
107
108 void Widget::update()
109 {
110 _dirty = true;
111 if (parent())
112 parent()->layout(); // relay-out us and our siblings
113 else {
114 render();
115 layout();
116 }
117 }
118
119 void Widget::moveresize(const Rect &r)
120 {
121 unsigned int w, h;
122 w = std::min(std::max(r.width(), minSize().width()), maxSize().width());
123 h = std::min(std::max(r.height(), minSize().height()), maxSize().height());
124
125 if (r.x() == area().x() && r.y() == area().y() &&
126 w == area().width() && h == area().height()) {
127 return; // no change, don't cause a big layout chain to occur!
128 }
129
130 internal_moveresize(r.x(), r.y(), w, h);
131
132 update();
133 }
134
135 void Widget::internal_moveresize(int x, int y, unsigned w, unsigned int h)
136 {
137 assert(w > 0);
138 assert(h > 0);
139 assert(_borderwidth >= 0);
140 _dirty = true;
141 XMoveResizeWindow(**display, _window, x, y,
142 w - _borderwidth * 2,
143 h - _borderwidth * 2);
144 _ignore_config++;
145
146 _area = Rect(x, y, w, h);
147 }
148
149 void Widget::setAlignment(RenderStyle::Justify a)
150 {
151 _alignment = a;
152 layout();
153 }
154
155 void Widget::createWindow(bool overrideredir)
156 {
157 const ScreenInfo *info = display->screenInfo(_screen);
158 XSetWindowAttributes attrib;
159 unsigned long mask = CWEventMask | CWBorderPixel;
160
161 attrib.event_mask = _event_mask;
162 attrib.border_pixel = (_bordercolor ?
163 _bordercolor->pixel():
164 BlackPixel(**display, _screen));
165
166 if (overrideredir) {
167 mask |= CWOverrideRedirect;
168 attrib.override_redirect = true;
169 }
170
171 _window = XCreateWindow(**display, (_parent ?
172 _parent->_window :
173 RootWindow(**display, _screen)),
174 _area.x(), _area.y(),
175 _area.width(), _area.height(),
176 _borderwidth,
177 info->depth(),
178 InputOutput,
179 info->visual(),
180 mask,
181 &attrib);
182 assert(_window != None);
183 ++_ignore_config;
184 }
185
186 void Widget::setBorderWidth(int w)
187 {
188 assert(w >= 0);
189 if (!parent()) return; // top-level windows cannot have borders
190 if (w == borderWidth()) return; // no change
191
192 _borderwidth = w;
193 XSetWindowBorderWidth(**display, _window, _borderwidth);
194
195 calcDefaultSizes();
196 update();
197 }
198
199 void Widget::setMinSize(const Size &s)
200 {
201 _min_size = s;
202 update();
203 }
204
205 void Widget::setMaxSize(const Size &s)
206 {
207 _max_size = s;
208 update();
209 }
210
211 void Widget::setBorderColor(const RenderColor *c)
212 {
213 _bordercolor = c;
214 XSetWindowBorder(**otk::display, _window,
215 c ? c->pixel() : BlackPixel(**otk::display, _screen));
216 }
217
218 void Widget::setBevel(int b)
219 {
220 _bevel = b;
221 calcDefaultSizes();
222 layout();
223 }
224
225 void Widget::layout()
226 {
227 if (_direction == Horizontal)
228 layoutHorz();
229 else
230 layoutVert();
231 }
232
233 void Widget::layoutHorz()
234 {
235 std::list<Widget*>::iterator it, end;
236
237 // work with just the visible children
238 std::list<Widget*> visible = _children;
239 for (it = visible.begin(), end = visible.end(); it != end;) {
240 std::list<Widget*>::iterator next = it; ++next;
241 if (!(*it)->visible())
242 visible.erase(it);
243 it = next;
244 }
245
246 if (visible.empty()) return;
247
248 if ((unsigned)(_borderwidth * 2 + _bevel * 2) > _area.width() ||
249 (unsigned)(_borderwidth * 2 + _bevel * 2) > _area.height())
250 return; // not worth laying anything out!
251
252 int x, y; unsigned int w, h; // working area
253 x = y = _bevel;
254 w = _area.width() - _borderwidth * 2 - _bevel * 2;
255 h = _area.height() - _borderwidth * 2 - _bevel * 2;
256
257 int free = w - (visible.size() - 1) * _bevel;
258 if (free < 0) free = 0;
259 unsigned int each;
260
261 std::list<Widget*> adjustable = visible;
262
263 // find the 'free' space, and how many children will be using it
264 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
265 std::list<Widget*>::iterator next = it; ++next;
266 free -= (*it)->minSize().width();
267 if (free < 0) free = 0;
268 if ((*it)->maxSize().width() - (*it)->minSize().width() <= 0)
269 adjustable.erase(it);
270 it = next;
271 }
272 // some widgets may have max widths that restrict them, find the 'true'
273 // amount of free space after these widgets are not included
274 if (!adjustable.empty()) {
275 do {
276 each = free / adjustable.size();
277 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
278 std::list<Widget*>::iterator next = it; ++next;
279 unsigned int m = (*it)->maxSize().width() - (*it)->minSize().width();
280 if (m > 0 && m < each) {
281 free -= m;
282 if (free < 0) free = 0;
283 adjustable.erase(it);
284 break; // if one is found to be fixed, then the free space needs to
285 // change, and the rest need to be reexamined
286 }
287 it = next;
288 }
289 } while (it != end && !adjustable.empty());
290 }
291
292 // place/size the widgets
293 if (!adjustable.empty())
294 each = free / adjustable.size();
295 else
296 each = 0;
297 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
298 unsigned int w;
299 // is the widget adjustable?
300 std::list<Widget*>::const_iterator
301 found = std::find(adjustable.begin(), adjustable.end(), *it);
302 if (found != adjustable.end()) {
303 // adjustable
304 w = (*it)->minSize().width() + each;
305 } else {
306 // fixed
307 w = (*it)->minSize().width();
308 }
309 // align it vertically
310 int yy = y;
311 unsigned int hh = std::max(std::min(h, (*it)->_max_size.height()),
312 (*it)->_min_size.height());
313 if (hh < h) {
314 switch(_alignment) {
315 case RenderStyle::RightBottomJustify:
316 yy += h - hh;
317 break;
318 case RenderStyle::CenterJustify:
319 yy += (h - hh) / 2;
320 break;
321 case RenderStyle::LeftTopJustify:
322 break;
323 }
324 }
325 (*it)->internal_moveresize(x, yy, w, hh);
326 (*it)->render();
327 (*it)->layout();
328 x += w + _bevel;
329 }
330 }
331
332 void Widget::layoutVert()
333 {
334 std::list<Widget*>::iterator it, end;
335
336 // work with just the visible children
337 std::list<Widget*> visible = _children;
338 for (it = visible.begin(), end = visible.end(); it != end;) {
339 std::list<Widget*>::iterator next = it; ++next;
340 if (!(*it)->visible())
341 visible.erase(it);
342 it = next;
343 }
344
345 if (visible.empty()) return;
346
347 if ((unsigned)(_borderwidth * 2 + _bevel * 2) > _area.width() ||
348 (unsigned)(_borderwidth * 2 + _bevel * 2) > _area.height())
349 return; // not worth laying anything out!
350
351 int x, y; unsigned int w, h; // working area
352 x = y = _bevel;
353 w = _area.width() - _borderwidth * 2 - _bevel * 2;
354 h = _area.height() - _borderwidth * 2 - _bevel * 2;
355
356 int free = h - (visible.size() - 1) * _bevel;
357 if (free < 0) free = 0;
358 unsigned int each;
359
360 std::list<Widget*> adjustable = visible;
361
362 // find the 'free' space, and how many children will be using it
363 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
364 std::list<Widget*>::iterator next = it; ++next;
365 free -= (*it)->minSize().height();
366 if (free < 0) free = 0;
367 if ((*it)->maxSize().height() - (*it)->minSize().height() <= 0)
368 adjustable.erase(it);
369 it = next;
370 }
371 // some widgets may have max heights that restrict them, find the 'true'
372 // amount of free space after these widgets are not included
373 if (!adjustable.empty()) {
374 do {
375 each = free / adjustable.size();
376 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
377 std::list<Widget*>::iterator next = it; ++next;
378 unsigned int m = (*it)->maxSize().height() - (*it)->minSize().height();
379 if (m > 0 && m < each) {
380 free -= m;
381 if (free < 0) free = 0;
382 adjustable.erase(it);
383 break; // if one is found to be fixed, then the free space needs to
384 // change, and the rest need to be reexamined
385 }
386 it = next;
387 }
388 } while (it != end && !adjustable.empty());
389 }
390
391 // place/size the widgets
392 if (!adjustable.empty())
393 each = free / adjustable.size();
394 else
395 each = 0;
396 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
397 unsigned int h;
398 // is the widget adjustable?
399 std::list<Widget*>::const_iterator
400 found = std::find(adjustable.begin(), adjustable.end(), *it);
401 if (found != adjustable.end()) {
402 // adjustable
403 h = (*it)->minSize().height() + each;
404 } else {
405 // fixed
406 h = (*it)->minSize().height();
407 }
408 // align it horizontally
409 int xx = x;
410 unsigned int ww = std::max(std::min(w, (*it)->_max_size.width()),
411 (*it)->_min_size.width());
412 if (ww < w) {
413 switch(_alignment) {
414 case RenderStyle::RightBottomJustify:
415 xx += w - ww;
416 break;
417 case RenderStyle::CenterJustify:
418 xx += (w - ww) / 2;
419 break;
420 case RenderStyle::LeftTopJustify:
421 break;
422 }
423 }
424 (*it)->internal_moveresize(xx, y, ww, h);
425 (*it)->render();
426 (*it)->layout();
427 y += h + _bevel;
428 }
429 }
430
431 void Widget::render()
432 {
433 if (!_texture || !_dirty) return;
434 if ((unsigned)_borderwidth * 2 > _area.width() ||
435 (unsigned)_borderwidth * 2 > _area.height())
436 return; // no surface to draw on
437
438 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
439 _area.height() - _borderwidth * 2));
440 display->renderControl(_screen)->drawBackground(*s, *_texture);
441
442 renderForeground(*s); // for inherited types to render onto the _surface
443
444 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
445 XClearWindow(**display, _window);
446
447 // delete the old surface *after* its pixmap isn't in use anymore
448 if (_surface) delete _surface;
449
450 _surface = s;
451
452 _dirty = false;
453 }
454
455 void Widget::renderChildren()
456 {
457 std::list<Widget*>::iterator it, end = _children.end();
458 for (it = _children.begin(); it != end; ++it)
459 (*it)->render();
460 }
461
462 void Widget::exposeHandler(const XExposeEvent &e)
463 {
464 EventHandler::exposeHandler(e);
465 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
466 }
467
468 void Widget::configureHandler(const XConfigureEvent &e)
469 {
470 if (_ignore_config) {
471 _ignore_config--;
472 } else {
473 XEvent ev;
474 ev.xconfigure.width = e.width;
475 ev.xconfigure.height = e.height;
476 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
477
478 if (!((unsigned)ev.xconfigure.width == area().width() &&
479 (unsigned)ev.xconfigure.height == area().height())) {
480 _area = Rect(_area.position(), Size(e.width, e.height));
481 update();
482 }
483 }
484 }
485
486 }
This page took 0.062134 seconds and 5 git commands to generate.