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(UINT_MAX
, UINT_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(UINT_MAX
, UINT_MAX
),
58 _dispatcher(parent
->_dispatcher
),
63 parent
->addChild(this);
64 if (parent
->visible()) parent
->layout();
65 _dispatcher
->registerHandler(_window
, this);
70 assert(_children
.empty()); // this would be bad. theyd have a hanging _parent
72 if (_surface
) delete _surface
;
73 if (_parent
) _parent
->removeChild(this);
75 _dispatcher
->clearHandler(_window
);
76 XDestroyWindow(**display
, _window
);
79 void Widget::show(bool children
)
82 std::list
<Widget
*>::iterator it
, end
= _children
.end();
83 for (it
= _children
.begin(); it
!= end
; ++it
) {
89 XMapWindow(**display
, _window
);
98 XUnmapWindow(**display
, _window
);
99 if (_parent
) _parent
->layout();
103 void Widget::setEventMask(long e
)
105 XSelectInput(**display
, _window
, e
);
109 void Widget::update()
111 if (!_visible
) return;
114 parent()->layout(); // relay-out us and our siblings
121 void Widget::moveresize(const Rect
&r
)
124 w
= std::min(std::max(r
.width(), minSize().width()), maxSize().width());
125 h
= std::min(std::max(r
.height(), minSize().height()), maxSize().height());
127 if (r
.x() == area().x() && r
.y() == area().y() &&
128 w
== area().width() && h
== area().height()) {
129 return; // no change, don't cause a big layout chain to occur!
132 internal_moveresize(r
.x(), r
.y(), w
, h
);
137 void Widget::internal_moveresize(int x
, int y
, unsigned w
, unsigned int h
)
141 assert(_borderwidth
>= 0);
143 XMoveResizeWindow(**display
, _window
, x
, y
,
144 w
- _borderwidth
* 2,
145 h
- _borderwidth
* 2);
148 _area
= Rect(x
, y
, w
, h
);
151 void Widget::setAlignment(RenderStyle::Justify a
)
157 void Widget::createWindow(bool overrideredir
)
159 const ScreenInfo
*info
= display
->screenInfo(_screen
);
160 XSetWindowAttributes attrib
;
161 unsigned long mask
= CWEventMask
| CWBorderPixel
;
163 attrib
.event_mask
= _event_mask
;
164 attrib
.border_pixel
= (_bordercolor
?
165 _bordercolor
->pixel():
166 BlackPixel(**display
, _screen
));
169 mask
|= CWOverrideRedirect
;
170 attrib
.override_redirect
= true;
173 _window
= XCreateWindow(**display
, (_parent
?
175 RootWindow(**display
, _screen
)),
176 _area
.x(), _area
.y(),
177 _area
.width(), _area
.height(),
184 assert(_window
!= None
);
188 void Widget::setBorderWidth(int w
)
191 if (!parent()) return; // top-level windows cannot have borders
192 if (w
== borderWidth()) return; // no change
195 XSetWindowBorderWidth(**display
, _window
, _borderwidth
);
201 void Widget::setMinSize(const Size
&s
)
207 void Widget::setMaxSize(const Size
&s
)
213 void Widget::setBorderColor(const RenderColor
*c
)
216 XSetWindowBorder(**otk::display
, _window
,
217 c
? c
->pixel() : BlackPixel(**otk::display
, _screen
));
220 void Widget::setBevel(int b
)
227 void Widget::layout()
229 if (_children
.empty() || !_visible
) return;
230 if (_direction
== Horizontal
)
236 void Widget::layoutHorz()
238 std::list
<Widget
*>::iterator it
, end
;
240 // work with just the visible children
241 std::list
<Widget
*> visible
= _children
;
242 for (it
= visible
.begin(), end
= visible
.end(); it
!= end
;) {
243 std::list
<Widget
*>::iterator next
= it
; ++next
;
244 if (!(*it
)->visible())
249 if (visible
.empty()) return;
251 if ((unsigned)(_borderwidth
* 2 + _bevel
* 2) > _area
.width() ||
252 (unsigned)(_borderwidth
* 2 + _bevel
* 2) > _area
.height())
253 return; // not worth laying anything out!
255 int x
, y
; unsigned int w
, h
; // working area
257 w
= _area
.width() - _borderwidth
* 2 - _bevel
* 2;
258 h
= _area
.height() - _borderwidth
* 2 - _bevel
* 2;
260 int free
= w
- (visible
.size() - 1) * _bevel
;
261 if (free
< 0) free
= 0;
264 std::list
<Widget
*> adjustable
= visible
;
266 // find the 'free' space, and how many children will be using it
267 for (it
= adjustable
.begin(), end
= adjustable
.end(); it
!= end
;) {
268 std::list
<Widget
*>::iterator next
= it
; ++next
;
269 free
-= (*it
)->minSize().width();
270 if (free
< 0) free
= 0;
271 if ((*it
)->maxSize().width() - (*it
)->minSize().width() <= 0)
272 adjustable
.erase(it
);
275 // some widgets may have max widths that restrict them, find the 'true'
276 // amount of free space after these widgets are not included
277 if (!adjustable
.empty()) {
279 each
= free
/ adjustable
.size();
280 for (it
= adjustable
.begin(), end
= adjustable
.end(); it
!= end
;) {
281 std::list
<Widget
*>::iterator next
= it
; ++next
;
282 unsigned int m
= (*it
)->maxSize().width() - (*it
)->minSize().width();
283 if (m
> 0 && m
< each
) {
285 if (free
< 0) free
= 0;
286 adjustable
.erase(it
);
287 break; // if one is found to be fixed, then the free space needs to
288 // change, and the rest need to be reexamined
292 } while (it
!= end
&& !adjustable
.empty());
295 // place/size the widgets
296 if (!adjustable
.empty())
297 each
= free
/ adjustable
.size();
300 for (it
= visible
.begin(), end
= visible
.end(); it
!= end
; ++it
) {
302 // is the widget adjustable?
303 std::list
<Widget
*>::const_iterator
304 found
= std::find(adjustable
.begin(), adjustable
.end(), *it
);
305 if (found
!= adjustable
.end()) {
307 w
= (*it
)->minSize().width() + each
;
310 w
= (*it
)->minSize().width();
312 // align it vertically
314 unsigned int hh
= std::max(std::min(h
, (*it
)->_max_size
.height()),
315 (*it
)->_min_size
.height());
318 case RenderStyle::RightBottomJustify
:
321 case RenderStyle::CenterJustify
:
324 case RenderStyle::LeftTopJustify
:
328 (*it
)->internal_moveresize(x
, yy
, w
, hh
);
335 void Widget::layoutVert()
337 std::list
<Widget
*>::iterator it
, end
;
339 // work with just the visible children
340 std::list
<Widget
*> visible
= _children
;
341 for (it
= visible
.begin(), end
= visible
.end(); it
!= end
;) {
342 std::list
<Widget
*>::iterator next
= it
; ++next
;
343 if (!(*it
)->visible())
348 if (visible
.empty()) return;
350 if ((unsigned)(_borderwidth
* 2 + _bevel
* 2) > _area
.width() ||
351 (unsigned)(_borderwidth
* 2 + _bevel
* 2) > _area
.height())
352 return; // not worth laying anything out!
354 int x
, y
; unsigned int w
, h
; // working area
356 w
= _area
.width() - _borderwidth
* 2 - _bevel
* 2;
357 h
= _area
.height() - _borderwidth
* 2 - _bevel
* 2;
359 int free
= h
- (visible
.size() - 1) * _bevel
;
360 if (free
< 0) free
= 0;
363 std::list
<Widget
*> adjustable
= visible
;
365 // find the 'free' space, and how many children will be using it
366 for (it
= adjustable
.begin(), end
= adjustable
.end(); it
!= end
;) {
367 std::list
<Widget
*>::iterator next
= it
; ++next
;
368 free
-= (*it
)->minSize().height();
369 if (free
< 0) free
= 0;
370 if ((*it
)->maxSize().height() - (*it
)->minSize().height() <= 0)
371 adjustable
.erase(it
);
374 // some widgets may have max heights that restrict them, find the 'true'
375 // amount of free space after these widgets are not included
376 if (!adjustable
.empty()) {
378 each
= free
/ adjustable
.size();
379 for (it
= adjustable
.begin(), end
= adjustable
.end(); it
!= end
;) {
380 std::list
<Widget
*>::iterator next
= it
; ++next
;
381 unsigned int m
= (*it
)->maxSize().height() - (*it
)->minSize().height();
382 if (m
> 0 && m
< each
) {
384 if (free
< 0) free
= 0;
385 adjustable
.erase(it
);
386 break; // if one is found to be fixed, then the free space needs to
387 // change, and the rest need to be reexamined
391 } while (it
!= end
&& !adjustable
.empty());
394 // place/size the widgets
395 if (!adjustable
.empty())
396 each
= free
/ adjustable
.size();
399 for (it
= visible
.begin(), end
= visible
.end(); it
!= end
; ++it
) {
401 // is the widget adjustable?
402 std::list
<Widget
*>::const_iterator
403 found
= std::find(adjustable
.begin(), adjustable
.end(), *it
);
404 if (found
!= adjustable
.end()) {
406 h
= (*it
)->minSize().height() + each
;
409 h
= (*it
)->minSize().height();
411 // align it horizontally
413 unsigned int ww
= std::max(std::min(w
, (*it
)->_max_size
.width()),
414 (*it
)->_min_size
.width());
417 case RenderStyle::RightBottomJustify
:
420 case RenderStyle::CenterJustify
:
423 case RenderStyle::LeftTopJustify
:
427 (*it
)->internal_moveresize(xx
, y
, ww
, h
);
434 void Widget::render()
436 if (!_texture
|| !_dirty
) return;
437 if ((unsigned)_borderwidth
* 2 > _area
.width() ||
438 (unsigned)_borderwidth
* 2 > _area
.height())
439 return; // no surface to draw on
441 Surface
*s
= new Surface(_screen
, Size(_area
.width() - _borderwidth
* 2,
442 _area
.height() - _borderwidth
* 2));
443 display
->renderControl(_screen
)->drawBackground(*s
, *_texture
);
445 renderForeground(*s
); // for inherited types to render onto the _surface
447 XSetWindowBackgroundPixmap(**display
, _window
, s
->pixmap());
448 XClearWindow(**display
, _window
);
450 // delete the old surface *after* its pixmap isn't in use anymore
451 if (_surface
) delete _surface
;
458 void Widget::renderChildren()
460 std::list
<Widget
*>::iterator it
, end
= _children
.end();
461 for (it
= _children
.begin(); it
!= end
; ++it
)
465 void Widget::exposeHandler(const XExposeEvent
&e
)
467 EventHandler::exposeHandler(e
);
468 XClearArea(**display
, _window
, e
.x
, e
.y
, e
.width
, e
.height
, false);
471 void Widget::configureHandler(const XConfigureEvent
&e
)
473 if (_ignore_config
) {
476 // only interested in these for top level windows
480 ev
.xconfigure
.width
= e
.width
;
481 ev
.xconfigure
.height
= e
.height
;
482 while (XCheckTypedWindowEvent(**display
, window(), ConfigureNotify
, &ev
));
484 if (!((unsigned)ev
.xconfigure
.width
== area().width() &&
485 (unsigned)ev
.xconfigure
.height
== area().height())) {
486 _area
= Rect(_area
.position(), Size(e
.width
, e
.height
));