]> Dogcows Code - chaz/openbox/blob - otk/widget.cc
dont forget borders/bevels
[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(INT_MAX, INT_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(INT_MAX, INT_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 if (parent->visible()) parent->layout();
65 _dispatcher->registerHandler(_window, this);
66 styleChanged(*RenderStyle::style(_screen));
67 }
68
69 Widget::~Widget()
70 {
71 assert(_children.empty()); // this would be bad. theyd have a hanging _parent
72
73 if (_surface) delete _surface;
74 if (_parent) _parent->removeChild(this);
75
76 _dispatcher->clearHandler(_window);
77 XDestroyWindow(**display, _window);
78 }
79
80 void Widget::show(bool children)
81 {
82 if (children) {
83 std::list<Widget*>::iterator it , end = _children.end();
84 for (it = _children.begin(); it != end; ++it) {
85 (*it)->show(true);
86 }
87 }
88 if (!_visible) {
89 _visible = true;
90 if (_parent) _parent->calcDefaultSizes();
91 else {
92 resize(_min_size);
93 }
94 XMapWindow(**display, _window);
95 update();
96 }
97 }
98
99 void Widget::hide()
100 {
101 if (_visible) {
102 _visible = false;
103 XUnmapWindow(**display, _window);
104 if (_parent) {
105 _parent->calcDefaultSizes();
106 _parent->layout();
107 }
108 }
109 }
110
111 void Widget::setEventMask(long e)
112 {
113 XSelectInput(**display, _window, e);
114 _event_mask = e;
115 }
116
117 void Widget::update()
118 {
119 if (!_visible) return;
120 _dirty = true;
121 if (_parent) {
122 _parent->calcDefaultSizes();
123 _parent->layout(); // relay-out us and our siblings
124 } else {
125 render();
126 layout();
127 }
128 }
129
130 void Widget::moveresize(const Rect &r)
131 {
132 int w, h;
133 w = std::max(std::min(r.width(), maxSize().width()), minSize().width());
134 h = std::max(std::min(r.height(), maxSize().height()), minSize().height());
135
136 if (r.x() == area().x() && r.y() == area().y() &&
137 w == area().width() && h == area().height()) {
138 return; // no change, don't cause a big layout chain to occur!
139 }
140
141 internal_moveresize(r.x(), r.y(), w, h);
142
143 update();
144 }
145
146 void Widget::internal_moveresize(int x, int y, int w, int h)
147 {
148 assert(w > 0);
149 assert(h > 0);
150 assert(_borderwidth >= 0);
151 _dirty = true;
152 if (!(x == _area.x() && y == _area.y())) {
153 if (!(w == _area.width() && h == _area.height()))
154 XMoveResizeWindow(**display, _window, x, y,
155 w - _borderwidth * 2,
156 h - _borderwidth * 2);
157 else
158 XMoveWindow(**display, _window, x, y);
159 } else
160 XResizeWindow(**display, _window, w - _borderwidth*2, h - _borderwidth*2);
161 _ignore_config++;
162
163 _area = Rect(x, y, w, h);
164 }
165
166 void Widget::setAlignment(RenderStyle::Justify a)
167 {
168 _alignment = a;
169 layout();
170 }
171
172 void Widget::createWindow(bool overrideredir)
173 {
174 const ScreenInfo *info = display->screenInfo(_screen);
175 XSetWindowAttributes attrib;
176 unsigned long mask = CWEventMask | CWBorderPixel;
177
178 attrib.event_mask = _event_mask;
179 attrib.border_pixel = (_bordercolor ?
180 _bordercolor->pixel():
181 BlackPixel(**display, _screen));
182
183 if (overrideredir) {
184 mask |= CWOverrideRedirect;
185 attrib.override_redirect = true;
186 }
187
188 _window = XCreateWindow(**display, (_parent ?
189 _parent->_window :
190 RootWindow(**display, _screen)),
191 _area.x(), _area.y(),
192 _area.width(), _area.height(),
193 _borderwidth,
194 info->depth(),
195 InputOutput,
196 info->visual(),
197 mask,
198 &attrib);
199 assert(_window != None);
200 ++_ignore_config;
201 }
202
203 void Widget::calcDefaultSizes()
204 {
205 std::list<Widget*>::const_iterator it, end = _children.end();
206 int min_biggest = 0, max_biggest = 0;
207 int min_sum = _bevel + _borderwidth * 2;
208 int max_sum = _bevel + _borderwidth * 2;
209 bool fullmax = false;
210
211 for (it = _children.begin(); it != end; ++it) {
212 const otk::Size &min = (*it)->minSize();
213 const otk::Size &max = (*it)->maxSize();
214 if (_direction == Horizontal) {
215 if (min.height() > min_biggest) min_biggest = min.height();
216 if (max.height() > max_biggest) max_biggest = max.height();
217 min_sum += _bevel + min.width();
218 if (max.width() == INT_MAX)
219 fullmax = true;
220 else if (!fullmax)
221 max_sum += _bevel + max.width();
222 } else {
223 if (min.width() > min_biggest) min_biggest = min.width();
224 if (max.width() > max_biggest) max_biggest = max.width();
225 min_sum += _bevel + min.height();
226 if (max.height() == INT_MAX)
227 fullmax = true;
228 else if (!fullmax)
229 max_sum += _bevel + max.height();
230 }
231 }
232 if (_direction == Horizontal) {
233 _min_size = otk::Size(min_sum + (_bevel + _borderwidth) * 2,
234 min_biggest + (_bevel + _borderwidth) * 2);
235 _max_size = otk::Size((fullmax ? INT_MAX :
236 max_sum + (_bevel + _borderwidth) * 2),
237 max_biggest);
238 } else {
239 _min_size = otk::Size(min_biggest + (_bevel + _borderwidth) * 2,
240 min_sum + (_bevel + _borderwidth) * 2);
241 _max_size = otk::Size(max_biggest, (fullmax ? INT_MAX : max_sum +
242 (_bevel + _borderwidth) * 2));
243 }
244 update();
245 }
246
247 void Widget::setBorderWidth(int w)
248 {
249 assert(w >= 0);
250 if (!parent()) return; // top-level windows cannot have borders
251 if (w == borderWidth()) return; // no change
252
253 _borderwidth = w;
254 XSetWindowBorderWidth(**display, _window, _borderwidth);
255
256 calcDefaultSizes();
257 update();
258 }
259
260 void Widget::setMinSize(const Size &s)
261 {
262 _min_size = s;
263 update();
264 }
265
266 void Widget::setMaxSize(const Size &s)
267 {
268 _max_size = s;
269 update();
270 }
271
272 void Widget::setBorderColor(const RenderColor *c)
273 {
274 _bordercolor = c;
275 XSetWindowBorder(**otk::display, _window,
276 c ? c->pixel() : BlackPixel(**otk::display, _screen));
277 }
278
279 void Widget::setBevel(int b)
280 {
281 _bevel = b;
282 calcDefaultSizes();
283 layout();
284 }
285
286 void Widget::layout()
287 {
288 if (_children.empty() || !_visible) return;
289 if (_direction == Horizontal)
290 layoutHorz();
291 else
292 layoutVert();
293 }
294
295 void Widget::layoutHorz()
296 {
297 std::list<Widget*>::iterator it, end;
298
299 // work with just the visible children
300 std::list<Widget*> visible = _children;
301 for (it = visible.begin(), end = visible.end(); it != end;) {
302 std::list<Widget*>::iterator next = it; ++next;
303 if (!(*it)->visible())
304 visible.erase(it);
305 it = next;
306 }
307
308 if (visible.empty()) return;
309
310 int x, y, w, h; // working area
311 x = y = _bevel;
312 w = _area.width() - _borderwidth * 2 - _bevel * 2;
313 h = _area.height() - _borderwidth * 2 - _bevel * 2;
314 if (w < 0 || h < 0) return; // not worth laying anything out!
315
316 int free = w - (visible.size() - 1) * _bevel;
317 if (free < 0) free = 0;
318 int each;
319
320 std::list<Widget*> adjustable = visible;
321
322 // find the 'free' space, and how many children will be using it
323 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
324 std::list<Widget*>::iterator next = it; ++next;
325 free -= (*it)->minSize().width();
326 if (free < 0) free = 0;
327 if ((*it)->maxSize().width() - (*it)->minSize().width() <= 0)
328 adjustable.erase(it);
329 it = next;
330 }
331 // some widgets may have max widths that restrict them, find the 'true'
332 // amount of free space after these widgets are not included
333 if (!adjustable.empty()) {
334 do {
335 each = free / adjustable.size();
336 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
337 std::list<Widget*>::iterator next = it; ++next;
338 int m = (*it)->maxSize().width() - (*it)->minSize().width();
339 if (m > 0 && m < each) {
340 free -= m;
341 if (free < 0) free = 0;
342 adjustable.erase(it);
343 break; // if one is found to be fixed, then the free space needs to
344 // change, and the rest need to be reexamined
345 }
346 it = next;
347 }
348 } while (it != end && !adjustable.empty());
349 }
350
351 // place/size the widgets
352 if (!adjustable.empty())
353 each = free / adjustable.size();
354 else
355 each = 0;
356 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
357 int w;
358 // is the widget adjustable?
359 std::list<Widget*>::const_iterator
360 found = std::find(adjustable.begin(), adjustable.end(), *it);
361 if (found != adjustable.end()) {
362 // adjustable
363 w = (*it)->minSize().width() + each;
364 } else {
365 // fixed
366 w = (*it)->minSize().width();
367 }
368 // align it vertically
369 int yy = y;
370 int hh = std::max(std::min(h, (*it)->_max_size.height()),
371 (*it)->_min_size.height());
372 if (hh < h) {
373 switch(_alignment) {
374 case RenderStyle::RightBottomJustify:
375 yy += h - hh;
376 break;
377 case RenderStyle::CenterJustify:
378 yy += (h - hh) / 2;
379 break;
380 case RenderStyle::LeftTopJustify:
381 break;
382 }
383 }
384 (*it)->internal_moveresize(x, yy, w, hh);
385 (*it)->render();
386 (*it)->layout();
387 x += w + _bevel;
388 }
389 }
390
391 void Widget::layoutVert()
392 {
393 std::list<Widget*>::iterator it, end;
394
395 // work with just the visible children
396 std::list<Widget*> visible = _children;
397 for (it = visible.begin(), end = visible.end(); it != end;) {
398 std::list<Widget*>::iterator next = it; ++next;
399 if (!(*it)->visible())
400 visible.erase(it);
401 it = next;
402 }
403
404 if (visible.empty()) return;
405
406 int x, y, w, h; // working area
407 x = y = _bevel;
408 w = _area.width() - _borderwidth * 2 - _bevel * 2;
409 h = _area.height() - _borderwidth * 2 - _bevel * 2;
410 if (w < 0 || h < 0) return; // not worth laying anything out!
411
412 int free = h - (visible.size() - 1) * _bevel;
413 if (free < 0) free = 0;
414 int each;
415
416 std::list<Widget*> adjustable = visible;
417
418 // find the 'free' space, and how many children will be using it
419 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
420 std::list<Widget*>::iterator next = it; ++next;
421 free -= (*it)->minSize().height();
422 if (free < 0) free = 0;
423 if ((*it)->maxSize().height() - (*it)->minSize().height() <= 0)
424 adjustable.erase(it);
425 it = next;
426 }
427 // some widgets may have max heights that restrict them, find the 'true'
428 // amount of free space after these widgets are not included
429 if (!adjustable.empty()) {
430 do {
431 each = free / adjustable.size();
432 for (it = adjustable.begin(), end = adjustable.end(); it != end;) {
433 std::list<Widget*>::iterator next = it; ++next;
434 int m = (*it)->maxSize().height() - (*it)->minSize().height();
435 if (m > 0 && m < each) {
436 free -= m;
437 if (free < 0) free = 0;
438 adjustable.erase(it);
439 break; // if one is found to be fixed, then the free space needs to
440 // change, and the rest need to be reexamined
441 }
442 it = next;
443 }
444 } while (it != end && !adjustable.empty());
445 }
446
447 // place/size the widgets
448 if (!adjustable.empty())
449 each = free / adjustable.size();
450 else
451 each = 0;
452 for (it = visible.begin(), end = visible.end(); it != end; ++it) {
453 int h;
454 // is the widget adjustable?
455 std::list<Widget*>::const_iterator
456 found = std::find(adjustable.begin(), adjustable.end(), *it);
457 if (found != adjustable.end()) {
458 // adjustable
459 h = (*it)->minSize().height() + each;
460 } else {
461 // fixed
462 h = (*it)->minSize().height();
463 }
464 // align it horizontally
465 int xx = x;
466 int ww = std::max(std::min(w, (*it)->_max_size.width()),
467 (*it)->_min_size.width());
468 if (ww < w) {
469 switch(_alignment) {
470 case RenderStyle::RightBottomJustify:
471 xx += w - ww;
472 break;
473 case RenderStyle::CenterJustify:
474 xx += (w - ww) / 2;
475 break;
476 case RenderStyle::LeftTopJustify:
477 break;
478 }
479 }
480 (*it)->internal_moveresize(xx, y, ww, h);
481 (*it)->render();
482 (*it)->layout();
483 y += h + _bevel;
484 }
485 }
486
487 void Widget::render()
488 {
489 if (!_dirty) return;
490 if (!_texture) {
491 // set a solid color as the default background
492 XSetWindowBackground(**display, _window,
493 RenderStyle::style(_screen)->
494 titlebarUnfocusBackground()->color().pixel());
495 return;
496 }
497 if (_borderwidth * 2 > _area.width() ||
498 _borderwidth * 2 > _area.height())
499 return; // no surface to draw on
500
501 Surface *s = new Surface(_screen, Size(_area.width() - _borderwidth * 2,
502 _area.height() - _borderwidth * 2));
503 display->renderControl(_screen)->drawBackground(*s, *_texture);
504
505 renderForeground(*s); // for inherited types to render onto the _surface
506
507 XSetWindowBackgroundPixmap(**display, _window, s->pixmap());
508 XClearWindow(**display, _window);
509
510 // delete the old surface *after* its pixmap isn't in use anymore
511 if (_surface) delete _surface;
512
513 s->freePixelData(); // done rendering with this surface
514 _surface = s;
515
516 _dirty = false;
517 }
518
519 void Widget::renderChildren()
520 {
521 std::list<Widget*>::iterator it, end = _children.end();
522 for (it = _children.begin(); it != end; ++it)
523 (*it)->render();
524 }
525
526 void Widget::styleChanged(const RenderStyle &)
527 {
528 refresh();
529 }
530
531 void Widget::exposeHandler(const XExposeEvent &e)
532 {
533 EventHandler::exposeHandler(e);
534 XClearArea(**display, _window, e.x, e.y, e.width, e.height, false);
535 }
536
537 void Widget::configureHandler(const XConfigureEvent &e)
538 {
539 if (_ignore_config) {
540 _ignore_config--;
541 } else {
542 // only interested in these for top level windows
543 if (_parent) return;
544
545 XEvent ev;
546 ev.xconfigure.width = e.width;
547 ev.xconfigure.height = e.height;
548 while (XCheckTypedWindowEvent(**display, window(), ConfigureNotify, &ev));
549
550 if (!(ev.xconfigure.width == area().width() &&
551 ev.xconfigure.height == area().height())) {
552 _area = Rect(_area.position(), Size(e.width, e.height));
553 update();
554 }
555 }
556 }
557
558 }
This page took 0.068652 seconds and 5 git commands to generate.