1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
7 #include "rendertexture.hh"
8 #include "rendercolor.hh"
9 #include "eventdispatcher.hh"
10 #include "screeninfo.hh"
18 Widget::Widget(int screen
, EventDispatcher
*ed
, Direction direction
, int bevel
,
25 _event_mask(ButtonPressMask
| ButtonReleaseMask
| ButtonMotionMask
|
26 ExposureMask
| StructureNotifyMask
),
27 _alignment(RenderStyle::CenterJustify
),
28 _direction(direction
),
29 _max_size(INT_MAX
, INT_MAX
),
38 createWindow(overrideredir
);
39 _dispatcher
->registerHandler(_window
, this);
42 Widget::Widget(Widget
*parent
, Direction direction
, int bevel
)
44 _screen(parent
->_screen
),
48 _event_mask(ButtonPressMask
| ButtonReleaseMask
| ButtonMotionMask
|
49 ExposureMask
| StructureNotifyMask
),
50 _alignment(RenderStyle::CenterJustify
),
51 _direction(direction
),
52 _max_size(INT_MAX
, INT_MAX
),
58 _dispatcher(parent
->_dispatcher
),
63 parent
->addChild(this);
64 if (parent
->visible()) parent
->layout();
65 _dispatcher
->registerHandler(_window
, this);
66 styleChanged(*RenderStyle::style(_screen
));
71 assert(_children
.empty()); // this would be bad. theyd have a hanging _parent
73 if (_surface
) delete _surface
;
74 if (_parent
) _parent
->removeChild(this);
76 _dispatcher
->clearHandler(_window
);
77 XDestroyWindow(**display
, _window
);
80 void Widget::show(bool children
)
83 std::list
<Widget
*>::iterator it
, end
= _children
.end();
84 for (it
= _children
.begin(); it
!= end
; ++it
) {
90 if (_parent
) _parent
->calcDefaultSizes();
94 XMapWindow(**display
, _window
);
103 XUnmapWindow(**display
, _window
);
105 _parent
->calcDefaultSizes();
111 void Widget::setEventMask(long e
)
113 XSelectInput(**display
, _window
, e
);
117 void Widget::update()
119 if (!_visible
) return;
122 _parent
->calcDefaultSizes();
123 _parent
->layout(); // relay-out us and our siblings
130 void Widget::moveresize(const Rect
&r
)
133 w
= std::max(std::min(r
.width(), maxSize().width()), minSize().width());
134 h
= std::max(std::min(r
.height(), maxSize().height()), minSize().height());
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!
141 internal_moveresize(r
.x(), r
.y(), w
, h
);
146 void Widget::internal_moveresize(int x
, int y
, int w
, int h
)
150 assert(_borderwidth
>= 0);
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);
158 XMoveWindow(**display
, _window
, x
, y
);
160 XResizeWindow(**display
, _window
, w
- _borderwidth
*2, h
- _borderwidth
*2);
163 _area
= Rect(x
, y
, w
, h
);
166 void Widget::setAlignment(RenderStyle::Justify a
)
172 void Widget::createWindow(bool overrideredir
)
174 const ScreenInfo
*info
= display
->screenInfo(_screen
);
175 XSetWindowAttributes attrib
;
176 unsigned long mask
= CWEventMask
| CWBorderPixel
;
178 attrib
.event_mask
= _event_mask
;
179 attrib
.border_pixel
= (_bordercolor
?
180 _bordercolor
->pixel():
181 BlackPixel(**display
, _screen
));
184 mask
|= CWOverrideRedirect
;
185 attrib
.override_redirect
= true;
188 _window
= XCreateWindow(**display
, (_parent
?
190 RootWindow(**display
, _screen
)),
191 _area
.x(), _area
.y(),
192 _area
.width(), _area
.height(),
199 assert(_window
!= None
);
203 void Widget::calcDefaultSizes()
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;
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
)
221 max_sum
+= _bevel
+ max
.width();
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
)
229 max_sum
+= _bevel
+ max
.height();
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),
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));
247 void Widget::setBorderWidth(int w
)
250 if (!parent()) return; // top-level windows cannot have borders
251 if (w
== borderWidth()) return; // no change
254 XSetWindowBorderWidth(**display
, _window
, _borderwidth
);
260 void Widget::setMinSize(const Size
&s
)
266 void Widget::setMaxSize(const Size
&s
)
272 void Widget::setBorderColor(const RenderColor
*c
)
275 XSetWindowBorder(**otk::display
, _window
,
276 c
? c
->pixel() : BlackPixel(**otk::display
, _screen
));
279 void Widget::setBevel(int b
)
286 void Widget::layout()
288 if (_children
.empty() || !_visible
) return;
289 if (_direction
== Horizontal
)
295 void Widget::layoutHorz()
297 std::list
<Widget
*>::iterator it
, end
;
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())
308 if (visible
.empty()) return;
310 int x
, y
, w
, h
; // working area
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!
316 int free
= w
- (visible
.size() - 1) * _bevel
;
317 if (free
< 0) free
= 0;
320 std::list
<Widget
*> adjustable
= visible
;
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
);
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()) {
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
) {
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
348 } while (it
!= end
&& !adjustable
.empty());
351 // place/size the widgets
352 if (!adjustable
.empty())
353 each
= free
/ adjustable
.size();
356 for (it
= visible
.begin(), end
= visible
.end(); it
!= end
; ++it
) {
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()) {
363 w
= (*it
)->minSize().width() + each
;
366 w
= (*it
)->minSize().width();
368 // align it vertically
370 int hh
= std::max(std::min(h
, (*it
)->_max_size
.height()),
371 (*it
)->_min_size
.height());
374 case RenderStyle::RightBottomJustify
:
377 case RenderStyle::CenterJustify
:
380 case RenderStyle::LeftTopJustify
:
384 (*it
)->internal_moveresize(x
, yy
, w
, hh
);
391 void Widget::layoutVert()
393 std::list
<Widget
*>::iterator it
, end
;
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())
404 if (visible
.empty()) return;
406 int x
, y
, w
, h
; // working area
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!
412 int free
= h
- (visible
.size() - 1) * _bevel
;
413 if (free
< 0) free
= 0;
416 std::list
<Widget
*> adjustable
= visible
;
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
);
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()) {
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
) {
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
444 } while (it
!= end
&& !adjustable
.empty());
447 // place/size the widgets
448 if (!adjustable
.empty())
449 each
= free
/ adjustable
.size();
452 for (it
= visible
.begin(), end
= visible
.end(); it
!= end
; ++it
) {
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()) {
459 h
= (*it
)->minSize().height() + each
;
462 h
= (*it
)->minSize().height();
464 // align it horizontally
466 int ww
= std::max(std::min(w
, (*it
)->_max_size
.width()),
467 (*it
)->_min_size
.width());
470 case RenderStyle::RightBottomJustify
:
473 case RenderStyle::CenterJustify
:
476 case RenderStyle::LeftTopJustify
:
480 (*it
)->internal_moveresize(xx
, y
, ww
, h
);
487 void Widget::render()
491 // set a solid color as the default background
492 XSetWindowBackground(**display
, _window
,
493 RenderStyle::style(_screen
)->
494 titlebarUnfocusBackground()->color().pixel());
497 if (_borderwidth
* 2 > _area
.width() ||
498 _borderwidth
* 2 > _area
.height())
499 return; // no surface to draw on
501 Surface
*s
= new Surface(_screen
, Size(_area
.width() - _borderwidth
* 2,
502 _area
.height() - _borderwidth
* 2));
503 display
->renderControl(_screen
)->drawBackground(*s
, *_texture
);
505 renderForeground(*s
); // for inherited types to render onto the _surface
507 XSetWindowBackgroundPixmap(**display
, _window
, s
->pixmap());
508 XClearWindow(**display
, _window
);
510 // delete the old surface *after* its pixmap isn't in use anymore
511 if (_surface
) delete _surface
;
513 s
->freePixelData(); // done rendering with this surface
519 void Widget::renderChildren()
521 std::list
<Widget
*>::iterator it
, end
= _children
.end();
522 for (it
= _children
.begin(); it
!= end
; ++it
)
526 void Widget::styleChanged(const RenderStyle
&)
531 void Widget::exposeHandler(const XExposeEvent
&e
)
533 EventHandler::exposeHandler(e
);
534 XClearArea(**display
, _window
, e
.x
, e
.y
, e
.width
, e
.height
, false);
537 void Widget::configureHandler(const XConfigureEvent
&e
)
539 if (_ignore_config
) {
542 // only interested in these for top level windows
546 ev
.xconfigure
.width
= e
.width
;
547 ev
.xconfigure
.height
= e
.height
;
548 while (XCheckTypedWindowEvent(**display
, window(), ConfigureNotify
, &ev
));
550 if (!(ev
.xconfigure
.width
== area().width() &&
551 ev
.xconfigure
.height
== area().height())) {
552 _area
= Rect(_area
.position(), Size(e
.width
, e
.height
));