1 /* Widget.cpp
2 * Copyright (C) 2018, 2019 by Sven Jähnichen
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3, or (at your option)
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "Widget.hpp"
20 #include "Window.hpp"
21 #include <algorithm>
22
23 namespace BWidgets
24 {
25
Widget()26 Widget::Widget () : Widget (0.0, 0.0, BWIDGETS_DEFAULT_WIDTH, BWIDGETS_DEFAULT_HEIGHT, "widget") {}
27
Widget(const double x,const double y,const double width,const double height)28 Widget::Widget (const double x, const double y, const double width, const double height) : Widget (x, y, width, height, "widget") {}
29
Widget(const double x,const double y,const double width,const double height,const std::string & name)30 Widget::Widget(const double x, const double y, const double width, const double height, const std::string& name) :
31 area_ (x, y, width, height),
32 visible_ (true), clickable_ (true), draggable_ (false),
33 scrollable_ (true), focusable_ (true), scheduleDraw_ (false), stacking_ (STACKING_NORMAL),
34 main_ (nullptr), parent_ (nullptr), children_ (), border_ (BWIDGETS_DEFAULT_BORDER), background_ (BWIDGETS_DEFAULT_BACKGROUND),
35 name_ (name), widgetSurface_ (), widgetState_ (BWIDGETS_DEFAULT_STATE)
36 {
37 mergeable_.fill (false);
38 mergeable_[BEvents::CONFIGURE_REQUEST_EVENT] = true;
39 mergeable_[BEvents::EXPOSE_REQUEST_EVENT] = true;
40 mergeable_[BEvents::POINTER_MOTION_EVENT] = true;
41 mergeable_[BEvents::POINTER_DRAG_EVENT] = true;
42 mergeable_[BEvents::WHEEL_SCROLL_EVENT] = true;
43 cbfunction_.fill (Widget::defaultCallback);
44 cbfunction_[BEvents::EventType::POINTER_DRAG_EVENT] = Widget::dragAndDropCallback;
45 widgetSurface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
46 }
47
Widget(const Widget & that)48 Widget::Widget (const Widget& that) :
49 area_ (that.area_),
50 visible_ (that.visible_), clickable_ (that.clickable_), draggable_ (that.draggable_), scrollable_ (that.scrollable_),
51 focusable_ (that.focusable_), mergeable_ (that.mergeable_), stacking_ (that.stacking_),
52 main_ (nullptr), parent_ (nullptr), children_ (), border_ (that.border_), background_ (that.background_), name_ (that.name_),
53 cbfunction_ (that.cbfunction_), widgetSurface_ (), widgetState_ (that.widgetState_)
54 {
55 widgetSurface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, that.area_.getWidth (), that.area_.getHeight ());
56 }
57
~Widget()58 Widget::~Widget()
59 {
60 // Release from parent (and main) if still linked
61 if (parent_) parent_->release (this);
62
63 // Release children
64 while (!children_.empty ())
65 {
66 Widget* w = children_.back ();
67 release (w);
68
69 // Hard kick out if release failed
70 if (!children_.empty () && (w == children_.back ())) children_.pop_back ();
71 }
72
73 cairo_surface_destroy (widgetSurface_);
74 }
75
operator =(const Widget & that)76 Widget& Widget::operator= (const Widget& that)
77 {
78 area_ = that.area_;
79 visible_ = that.visible_;
80 clickable_ = that.clickable_;
81 draggable_ = that.draggable_;
82 scrollable_ = that.scrollable_;
83 focusable_ = that.focusable_;
84 mergeable_ = that.mergeable_;
85 stacking_ = that.stacking_;
86 border_ = that.border_;
87 background_ = that.background_;
88 name_ = that.name_;
89 cbfunction_ = that.cbfunction_;
90 widgetState_ = that.widgetState_;
91
92 if (widgetSurface_) cairo_surface_destroy (widgetSurface_);
93 widgetSurface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, that.area_.getWidth (), that.area_.getHeight ());
94 update ();
95 return *this;
96 }
97
clone() const98 Widget* Widget::clone () const {return new Widget (*this);}
99
show()100 void Widget::show ()
101 {
102 visible_ = true;
103
104 if (isVisible ())
105 {
106 // (Re-)draw children as they may become visible too
107 forEachChild ([] (Widget* w)
108 {
109 if (w->isVisible ()) w->draw (BUtilities::RectArea (0, 0, w->area_.getWidth (), w->area_.getHeight ()));
110 return w->isVisible ();
111 });
112
113 // (Re-)draw this widget and post redisplay
114 update ();
115 }
116 }
117
hide()118 void Widget::hide ()
119 {
120 bool wasVisible = isVisible ();
121
122 // Get area occupied by this widget and its children
123 BUtilities::RectArea hideArea = getAbsoluteTotalArea (BWidgets::isVisible);
124 visible_ = false;
125
126 if (wasVisible && main_)
127 {
128 // Limit area to main boundaries
129 hideArea.intersect (main_->getAbsoluteArea());
130
131 // Find closest parent that includes the area
132 Widget* p = getParent();
133 for ( ; p && (!p->getAbsoluteArea().includes (hideArea)); p = p->getParent()) {}
134
135 // Redisplay hidden area
136 if (p) p->postRedisplay ();
137 else main_->postRedisplay (hideArea);
138 }
139 }
140
add(Widget & child)141 void Widget::add (Widget& child)
142 {
143 // Check if already added? -> Release first
144 if (child.parent_) child.parent_->release (&child);
145
146 child.main_ = main_;
147 child.parent_ = this;
148
149 children_.push_back (&child);
150
151 // Link all children of child to main_ and update children of child as
152 // they may become visible too
153 if (main_)
154 {
155 forEachChild ([this] (Widget* w)
156 {
157 w->main_ = this->main_;
158 w->update ();
159 return true;
160 });
161 }
162
163 //TODO Stacking
164
165 // (Re-)draw child widget and post redisplay
166 if (child.isVisible ()) child.update ();
167 }
168
release(Widget * child)169 void Widget::release (Widget* child)
170 {
171 if (child)
172 {
173 std::vector<Widget*>::iterator it = std::find (children_.begin(), children_.end(), child);
174
175 if (it != children_.end())
176 {
177 // Store redisplay area information
178 bool wasVisible = child->isVisible ();
179
180 // Hide child
181 child->hide();
182
183 // Release child and all children of child
184 // from main window and from main windows input connections
185 forEachChild (it, it + 1, [] (Widget* w)
186 {
187 if (w->main_)
188 {
189 w->main_->purgeEventQueue (w);
190 w->main_->getButtonGrabStack()->remove (w);
191 w->main_->getKeyGrabStack()->remove (w);
192 w->main_ = nullptr;
193 }
194 return true;
195 });
196
197 // Delete child's connection to this widget
198 child->parent_ = nullptr;
199
200 // Delete from children_ vector
201 children_.erase (it);
202
203 // Restore visibility information
204 if (wasVisible) child->show();
205 }
206
207 else
208 {
209 std::cerr << "Msg from BWidgets::Widget::release(): Child " << child->name_ << ":" << &(*child)
210 << " is not a child of " << name_ << ":" << &(*this) << std::endl;
211 }
212 }
213 }
214
moveTo(const double x,const double y)215 void Widget::moveTo (const double x, const double y) {moveTo (BUtilities::Point (x, y));}
216
moveTo(const BUtilities::Point & position)217 void Widget::moveTo (const BUtilities::Point& position)
218 {
219 if ((area_.getX () != position.x) || (area_.getY () != position.y))
220 {
221 area_.moveTo (position);
222 if (stacking_ == STACKING_CATCH) stackingCatch();
223 if (isVisible () && parent_) parent_->postRedisplay ();
224 }
225 }
226
getPosition() const227 BUtilities::Point Widget::getPosition () const {return area_.getPosition();}
228
getAbsolutePosition() const229 BUtilities::Point Widget::getAbsolutePosition () const
230 {
231 BUtilities::Point p = BUtilities::Point (0, 0);
232 for (const Widget* w = this; w->parent_; w = w->parent_) p += w->area_.getPosition ();
233 return p;
234 }
235
raiseFrontwards()236 void Widget::raiseFrontwards ()
237 {
238 if (parent_)
239 {
240 int size = parent_->children_.size ();
241 for (int i = 0; (i + 1) < size; ++i)
242 {
243 if (parent_->children_[i] == this)
244 {
245 // Swap
246 Widget* w = parent_->children_[i + 1];
247 parent_->children_[i + 1] = parent_->children_[i];
248 parent_->children_[i] = w;
249
250 if (parent_->isVisible ()) parent_->postRedisplay ();
251 return;
252 }
253 }
254 }
255 }
256
pushBackwards()257 void Widget::pushBackwards ()
258 {
259 if (parent_)
260 {
261 int size = parent_->children_.size ();
262 for (int i = 1; i < size; ++i)
263 {
264 if (parent_->children_[i] == this)
265 {
266 // Swap
267 Widget* w = parent_->children_[i];
268 parent_->children_[i] = parent_->children_[i - 1];
269 parent_->children_[i - 1] = w;
270
271 if (parent_->isVisible ()) parent_->postRedisplay ();
272 return;
273 }
274 }
275 }
276 }
277
raiseToTop()278 void Widget::raiseToTop ()
279 {
280 if (parent_)
281 {
282 // Delete old connection from parent to this widget
283 for (std::vector<Widget*>::iterator it = parent_->children_.begin (); it !=parent_->children_.end (); ++it)
284 {
285 if ((Widget*) *it == this)
286 {
287 parent_->children_.erase (it);
288 break;
289 }
290 }
291 parent_->children_.push_back (this);
292
293 if (parent_->isVisible ()) parent_->postRedisplay ();
294 }
295 }
296
setWidth(const double width)297 void Widget::setWidth (const double width)
298 {
299 if (getWidth () != width)
300 {
301 area_.resize (width, getHeight ());
302 cairo_surface_destroy (widgetSurface_); // destroy old surface first
303 widgetSurface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, getWidth (), getHeight ());
304 update ();
305 if (stacking_ == STACKING_CATCH) stackingCatch();
306 for (Widget* c : children_)
307 {
308 if (c && (c->getStacking() == STACKING_CATCH)) c->stackingCatch();
309 }
310 if (isVisible () && parent_) parent_->postRedisplay ();
311 }
312 }
313
getWidth() const314 double Widget::getWidth () const {return area_.getWidth ();}
315
setHeight(const double height)316 void Widget::setHeight (const double height)
317 {
318 if (getHeight () != height)
319 {
320 area_.resize (getWidth (), height);
321 cairo_surface_destroy (widgetSurface_); // destroy old surface first
322 widgetSurface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, getWidth (), getHeight ());
323 update ();
324 if (stacking_ == STACKING_CATCH) stackingCatch();
325 for (Widget* c : children_)
326 {
327 if (c && (c->getStacking() == STACKING_CATCH)) c->stackingCatch();
328 }
329 if (isVisible () && parent_) parent_->postRedisplay ();
330 }
331 }
332
getHeight() const333 double Widget::getHeight () const {return area_.getHeight ();}
334
resize()335 void Widget::resize ()
336 {
337 double height = 2 * getYOffset ();
338 double width = 2 * getXOffset ();
339
340 for (Widget* w : children_)
341 {
342 if (w->getPosition ().x + w->getWidth() > width) width = w->getPosition ().x + w->getWidth();
343 if (w->getPosition ().y + w->getHeight() > height) height = w->getPosition ().y + w->getHeight();
344 }
345 Widget::resize (BUtilities::Point (width, height));
346 }
347
resize(const double width,const double height)348 void Widget::resize (const double width, const double height) {Widget::resize (BUtilities::Point (width, height));}
349
resize(const BUtilities::Point extends)350 void Widget::resize (const BUtilities::Point extends)
351 {
352 if (area_.getExtends () != extends)
353 {
354 area_.resize (extends.x, extends.y);
355 cairo_surface_destroy (widgetSurface_); // destroy old surface first
356 widgetSurface_ = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, getWidth (), getHeight ());
357 update ();
358 if (stacking_ == STACKING_CATCH) stackingCatch();
359 for (Widget* c : children_)
360 {
361 if (c && (c->getStacking() == STACKING_CATCH)) c->stackingCatch();
362 }
363 if (isVisible () && parent_) parent_->postRedisplay ();
364 }
365 }
366
getExtends() const367 BUtilities::Point Widget::getExtends () const {return BUtilities::Point (area_.getWidth (), area_.getHeight ());}
368
setState(const BColors::State state)369 void Widget::setState (const BColors::State state)
370 {
371 if (state != widgetState_)
372 {
373 widgetState_ = state;
374 update ();
375 }
376 }
377
getState() const378 BColors::State Widget::getState () const {return widgetState_;}
379
setBorder(const BStyles::Border & border)380 void Widget::setBorder (const BStyles::Border& border)
381 {
382 border_ = border;
383 if (stacking_ == STACKING_CATCH) stackingCatch();
384 for (Widget* c : children_)
385 {
386 if (c && (c->getStacking() == STACKING_CATCH)) c->stackingCatch();
387 }
388 update ();
389 }
390
getBorder()391 BStyles::Border* Widget::getBorder () {return &border_;}
392
setBackground(const BStyles::Fill & background)393 void Widget::setBackground (const BStyles::Fill& background)
394 {
395 background_ = background;
396 update ();
397 }
398
getBackground()399 BStyles::Fill* Widget::getBackground () {return &background_;}
400
getMainWindow() const401 Window* Widget::getMainWindow () const {return main_;}
402
getParent() const403 Widget* Widget::getParent () const {return parent_;}
404
hasChildren() const405 bool Widget::hasChildren () const {return (children_.size () > 0 ? true : false);}
406
isChild(Widget * child)407 bool Widget::isChild (Widget* child)
408 {
409 for (Widget* w : children_)
410 {
411 if (w == child) return true;
412 if ((!w->children_.empty()) && w->isChild (child)) return true;
413 }
414
415 return false;
416 }
417
getChildren() const418 std::vector<Widget*> Widget::getChildren () const {return children_;}
419
rename(const std::string & name)420 void Widget::rename (const std::string& name) {name_ = name;}
421
getName() const422 std::string Widget::getName () const {return name_;}
423
setCallbackFunction(const BEvents::EventType eventType,const std::function<void (BEvents::Event *)> & callbackFunction)424 void Widget::setCallbackFunction (const BEvents::EventType eventType, const std::function<void (BEvents::Event*)>& callbackFunction)
425 {
426 if (eventType <= BEvents::EventType::NO_EVENT) cbfunction_[eventType] = callbackFunction;
427 }
428
isVisible()429 bool Widget::isVisible()
430 {
431 Widget* w;
432 for (w = this; w; w = w->parent_) // Go backwards in widget tree until nullptr
433 {
434 if (!w->visible_ || !main_) return false; // widget invisible? -> break as invisible
435 if (w == main_) return true; // main reached ? -> visible
436 }
437 return false; // nullptr reached ? -> not connected to main -> invisible
438 }
439
setClickable(const bool status)440 void Widget::setClickable (const bool status) {clickable_ = status;}
441
isClickable() const442 bool Widget::isClickable () const {return clickable_;}
443
setDraggable(const bool status)444 void Widget::setDraggable (const bool status) {draggable_ = status;}
445
isDraggable() const446 bool Widget::isDraggable () const {return draggable_;}
447
setScrollable(const bool status)448 void Widget::setScrollable (const bool status) {scrollable_ = status;}
449
isScrollable() const450 bool Widget::isScrollable () const {return scrollable_;}
451
setFocusable(const bool status)452 void Widget::setFocusable (const bool status) {focusable_ = status;}
453
isFocusable() const454 bool Widget::isFocusable () const {return focusable_;}
455
setMergeable(const BEvents::EventType eventType,const bool status)456 void Widget::setMergeable (const BEvents::EventType eventType, const bool status) {mergeable_[eventType] = status;}
457
isMergeable(const BEvents::EventType eventType) const458 bool Widget::isMergeable (const BEvents::EventType eventType) const {return mergeable_[eventType];}
459
setStacking(const WidgetStacking stacking)460 void Widget::setStacking (const WidgetStacking stacking) {stacking_ = stacking;}
461
getStacking() const462 WidgetStacking Widget::getStacking () const {return stacking_;};
463
update()464 void Widget::update ()
465 {
466 //draw (0, 0, width_, height_);
467 scheduleDraw_ = true;
468 if (isVisible ()) postRedisplay ();
469 }
470
getArea() const471 BUtilities::RectArea Widget::getArea () const {return area_;}
472
getAbsoluteArea() const473 BUtilities::RectArea Widget::getAbsoluteArea () const
474 {
475 BUtilities::RectArea a = area_;
476 a.moveTo (getAbsolutePosition ());
477 return a;
478 }
479
forEachChild(std::function<bool (Widget * widget)> func)480 void Widget::forEachChild (std::function<bool (Widget* widget)> func)
481 {
482 forEachChild (children_.begin(), children_.end(), func);
483 }
484
forEachChild(std::vector<Widget * >::iterator first,std::vector<Widget * >::iterator last,std::function<bool (Widget * widget)> func)485 void Widget::forEachChild (std::vector<Widget*>::iterator first, std::vector<Widget*>::iterator last,
486 std::function<bool (Widget* widget)> func)
487 {
488 for (std::vector<Widget*>::iterator it = first; it != last; ++it)
489 {
490 Widget* w = *it;
491 if (w && func(w)) w->forEachChild (func);
492 }
493 }
494
getTotalArea(std::function<bool (Widget * widget)> func)495 BUtilities::RectArea Widget::getTotalArea (std::function<bool (Widget* widget)> func)
496 {
497 BUtilities::RectArea a = getAbsoluteTotalArea (func);
498 a.moveTo (a.getPosition() - getAbsolutePosition());
499 return a;
500 }
501
getAbsoluteTotalArea(std::function<bool (Widget * widget)> func)502 BUtilities::RectArea Widget::getAbsoluteTotalArea (std::function<bool (Widget* widget)> func)
503 {
504 BUtilities::RectArea a = getAbsoluteArea();
505 forEachChild ([&a, func] (Widget* w)
506 {
507 bool check = func (w);
508 if (check && (w->getStacking() == STACKING_OVERSIZE)) a.extend (w->getAbsoluteArea());
509 return check;
510 });
511
512 return a;
513 }
514
getWidgetAt(const BUtilities::Point & position,std::function<bool (Widget * widget)> func)515 Widget* Widget::getWidgetAt (const BUtilities::Point& position, std::function<bool (Widget* widget)> func)
516 {
517 BUtilities::RectArea absarea = getAbsoluteArea ();
518 return getWidgetAt (getAbsolutePosition () + position, absarea, absarea, func);
519 }
520
getWidgetAt(const BUtilities::Point & abspos,const BUtilities::RectArea & outerArea,const BUtilities::RectArea & area,std::function<bool (Widget * widget)> func)521 Widget* Widget::getWidgetAt (const BUtilities::Point& abspos, const BUtilities::RectArea& outerArea,
522 const BUtilities::RectArea& area, std::function<bool (Widget* widget)> func)
523 {
524 BUtilities::RectArea a = (getStacking() == STACKING_OVERSIZE? outerArea : area);
525 BUtilities::RectArea thisArea = area_;
526 thisArea.moveTo (getAbsolutePosition());
527 thisArea.intersect (a);
528 if (main_)
529 {
530 Widget* finalw = (((thisArea != BUtilities::RectArea ()) && thisArea.contains (abspos) && func (this)) ? this : nullptr);
531
532 for (Widget* w : children_)
533 {
534 if (w)
535 {
536 Widget* nextw = nullptr;
537 if (filter (w)) nextw = w->getWidgetAt (abspos, outerArea, thisArea, func);
538 if (nextw) finalw = nextw;
539 }
540 }
541 return finalw;
542 }
543
544 else return nullptr;
545 }
546
applyTheme(BStyles::Theme & theme)547 void Widget::applyTheme (BStyles::Theme& theme) {applyTheme (theme, name_);}
548
applyTheme(BStyles::Theme & theme,const std::string & name)549 void Widget::applyTheme (BStyles::Theme& theme, const std::string& name)
550 {
551 // Border
552 void* borderPtr = theme.getStyle(name, BWIDGETS_KEYWORD_BORDER);
553 if (borderPtr) setBorder (*((BStyles::Border*) borderPtr));
554
555 // Background
556 void* backgroundPtr = theme.getStyle(name, BWIDGETS_KEYWORD_BACKGROUND);
557 if (backgroundPtr) setBackground (*((BStyles::Fill*) backgroundPtr));
558
559 if (borderPtr || backgroundPtr)
560 {
561 update ();
562 }
563 }
564
onConfigureRequest(BEvents::ExposeEvent * event)565 void Widget::onConfigureRequest (BEvents::ExposeEvent* event) {cbfunction_[BEvents::EventType::CONFIGURE_REQUEST_EVENT] (event);}
onExposeRequest(BEvents::ExposeEvent * event)566 void Widget::onExposeRequest (BEvents::ExposeEvent* event) {cbfunction_[BEvents::EventType::EXPOSE_REQUEST_EVENT] (event);}
567
onCloseRequest(BEvents::WidgetEvent * event)568 void Widget::onCloseRequest (BEvents::WidgetEvent* event)
569 {
570 cbfunction_[BEvents::EventType::CLOSE_REQUEST_EVENT] (event);
571
572 if ((event) && (event->getWidget () == this))
573 {
574 Widget* requestWidget = event->getRequestWidget ();
575 Widget* parent = (requestWidget ? requestWidget->getParent () : nullptr);
576 if (parent && parent->isChild (requestWidget)) release (requestWidget);
577 }
578 }
579
onKeyPressed(BEvents::KeyEvent * event)580 void Widget::onKeyPressed (BEvents::KeyEvent* event) {cbfunction_[BEvents::EventType::KEY_PRESS_EVENT] (event);}
onKeyReleased(BEvents::KeyEvent * event)581 void Widget::onKeyReleased (BEvents::KeyEvent* event) {cbfunction_[BEvents::EventType::KEY_RELEASE_EVENT] (event);}
onButtonPressed(BEvents::PointerEvent * event)582 void Widget::onButtonPressed (BEvents::PointerEvent* event) {cbfunction_[BEvents::EventType::BUTTON_PRESS_EVENT] (event);}
onButtonReleased(BEvents::PointerEvent * event)583 void Widget::onButtonReleased (BEvents::PointerEvent* event) {cbfunction_[BEvents::EventType::BUTTON_RELEASE_EVENT] (event);}
onButtonClicked(BEvents::PointerEvent * event)584 void Widget::onButtonClicked (BEvents::PointerEvent* event) {cbfunction_[BEvents::EventType::BUTTON_CLICK_EVENT] (event);}
onPointerMotion(BEvents::PointerEvent * event)585 void Widget::onPointerMotion (BEvents::PointerEvent* event) {cbfunction_[BEvents::EventType::POINTER_MOTION_EVENT] (event);}
onPointerDragged(BEvents::PointerEvent * event)586 void Widget::onPointerDragged (BEvents::PointerEvent* event) {cbfunction_[BEvents::EventType::POINTER_DRAG_EVENT] (event);}
onWheelScrolled(BEvents::WheelEvent * event)587 void Widget::onWheelScrolled (BEvents::WheelEvent* event){cbfunction_[BEvents::EventType::WHEEL_SCROLL_EVENT] (event);}
onValueChanged(BEvents::ValueChangedEvent * event)588 void Widget::onValueChanged (BEvents::ValueChangedEvent* event) {cbfunction_[BEvents::EventType::VALUE_CHANGED_EVENT] (event);}
onFocusIn(BEvents::FocusEvent * event)589 void Widget::onFocusIn (BEvents::FocusEvent* event) {cbfunction_[BEvents::EventType::FOCUS_IN_EVENT] (event);}
onFocusOut(BEvents::FocusEvent * event)590 void Widget::onFocusOut (BEvents::FocusEvent* event) {cbfunction_[BEvents::EventType::FOCUS_OUT_EVENT] (event);}
onMessage(BEvents::MessageEvent * event)591 void Widget::onMessage (BEvents::MessageEvent* event) {cbfunction_[BEvents::EventType::MESSAGE_EVENT] (event);}
592
defaultCallback(BEvents::Event * event)593 void Widget::defaultCallback (BEvents::Event* event) {}
594
dragAndDropCallback(BEvents::Event * event)595 void Widget::dragAndDropCallback (BEvents::Event* event)
596 {
597 if (event && event->getWidget())
598 {
599 Widget* w = event->getWidget();
600 BEvents::PointerEvent* pev = (BEvents::PointerEvent*) event;
601
602 w->moveTo (w->getPosition () + pev->getDelta ());
603 }
604 }
605
focusInCallback(BEvents::Event * event)606 void Widget::focusInCallback (BEvents::Event* event) {}
focusOutCallback(BEvents::Event * event)607 void Widget::focusOutCallback (BEvents::Event* event) {}
608
getXOffset()609 double Widget::getXOffset () {return border_.getMargin () + border_.getLine()->getWidth() + border_.getPadding ();}
610
getYOffset()611 double Widget::getYOffset () {return border_.getMargin () + border_.getLine()->getWidth() + border_.getPadding ();}
612
getEffectiveWidth()613 double Widget::getEffectiveWidth ()
614 {
615 double totalBorderWidth = getXOffset ();
616 return (getWidth () > 2 * totalBorderWidth ? getWidth () - 2 * totalBorderWidth : 0);
617 }
618
getEffectiveHeight()619 double Widget::getEffectiveHeight ()
620 {
621 double totalBorderHeight = getYOffset ();
622 return (getHeight () > 2 * totalBorderHeight ? getHeight () - 2 * totalBorderHeight : 0);
623 }
624
getEffectiveArea()625 BUtilities::RectArea Widget::getEffectiveArea ()
626 {
627 return BUtilities::RectArea (getPosition().x + getXOffset(), getPosition().y + getYOffset(), getEffectiveWidth(), getEffectiveHeight());
628 }
629
postMessage(const std::string & name,const BUtilities::Any content)630 void Widget::postMessage (const std::string& name, const BUtilities::Any content)
631 {
632 if (main_)
633 {
634 BEvents::MessageEvent* event = new BEvents::MessageEvent (this, name, content);
635 main_->addEventToQueue (event);
636 }
637 }
638
postRedisplay()639 void Widget::postRedisplay ()
640 {
641 BUtilities::RectArea area = getTotalArea (BWidgets::isVisible);
642 area.moveTo (getAbsolutePosition ());
643 postRedisplay (area);
644 }
645
postRedisplay(const BUtilities::RectArea & area)646 void Widget::postRedisplay (const BUtilities::RectArea& area)
647 {
648 if (main_)
649 {
650 BEvents::ExposeEvent* event = new BEvents::ExposeEvent (main_, this, BEvents::EXPOSE_REQUEST_EVENT, area);
651 main_->addEventToQueue (event);
652 }
653 }
654
postCloseRequest()655 void Widget::postCloseRequest () {postCloseRequest (main_);}
656
postCloseRequest(Widget * handle)657 void Widget::postCloseRequest (Widget* handle)
658 {
659 if (handle)
660 {
661 BEvents::WidgetEvent* event = new BEvents::WidgetEvent (handle, this, BEvents::CLOSE_REQUEST_EVENT);
662 if (event) main_->addEventToQueue (event);
663 }
664 }
665
redisplay(cairo_surface_t * surface,const BUtilities::RectArea & area)666 void Widget::redisplay (cairo_surface_t* surface, const BUtilities::RectArea& area)
667 {
668 if (isVisible())
669 {
670 // Calculate absolute area position and start private core method
671 BUtilities::RectArea absArea = area;
672 absArea.moveTo (absArea.getPosition() + getAbsolutePosition());
673 redisplay (surface, absArea, absArea);
674 }
675 }
676
redisplay(cairo_surface_t * surface,const BUtilities::RectArea & outerArea,const BUtilities::RectArea & area)677 void Widget::redisplay (cairo_surface_t* surface, const BUtilities::RectArea& outerArea, const BUtilities::RectArea& area)
678 {
679 BUtilities::RectArea a = (getStacking() == STACKING_OVERSIZE ? outerArea : area);
680 BUtilities::RectArea thisArea = area_; thisArea.moveTo (getAbsolutePosition());
681 a.intersect (thisArea);
682 if (main_ && visible_)
683 {
684 if (a != BUtilities::RectArea ())
685 {
686 // Update draw
687 if (scheduleDraw_) draw (BUtilities::RectArea (0, 0, getWidth (), getHeight ()));
688
689 // Copy widgets surface onto main surface
690 cairo_t* cr = cairo_create (surface);
691 cairo_set_source_surface (cr, widgetSurface_, thisArea.getX(), thisArea.getY());
692 cairo_rectangle (cr, a.getX (), a.getY (), a.getWidth (), a.getHeight ());
693 cairo_fill (cr);
694 cairo_destroy (cr);
695 }
696
697 for (Widget* w : children_)
698 {
699 if (w && filter (w)) w->redisplay (surface, outerArea, a);
700 }
701 }
702 }
703
stackingCatch()704 void Widget::stackingCatch ()
705 {
706 if (parent_)
707 {
708 BUtilities::RectArea a = area_;
709 BUtilities::RectArea pea = BUtilities::RectArea (parent_->getXOffset(), parent_->getYOffset(), parent_->getEffectiveWidth(), parent_->getEffectiveHeight());
710
711 if (a.getX() + a.getWidth() - getXOffset() <= parent_->getXOffset()) a.setX (parent_->getXOffset() - a.getWidth() + getXOffset());
712 if (a.getY() + a.getHeight() - getYOffset() <= parent_->getYOffset()) a.setY (parent_->getYOffset() - a.getHeight() + getYOffset());
713 if (a.getX() >= parent_->getXOffset() + parent_->getEffectiveWidth() - getXOffset()) a.setX (parent_->getXOffset() + parent_->getEffectiveWidth() - getXOffset());
714 if (a.getY() >= parent_->getYOffset() + pea.getHeight() - getYOffset()) a.setY (parent_->getYOffset() + pea.getHeight() - getYOffset());
715
716 if (a != area_)
717 {
718 area_ = a;
719 if (isVisible()) parent_->postRedisplay ();
720 }
721 }
722 }
723
filter(Widget * widget)724 bool Widget::filter (Widget* widget) {return true;}
725
draw(const BUtilities::RectArea & area)726 void Widget::draw (const BUtilities::RectArea& area)
727 {
728 if ((!widgetSurface_) || (cairo_surface_status (widgetSurface_) != CAIRO_STATUS_SUCCESS)) return;
729 cairo_surface_clear (widgetSurface_);
730 cairo_t* cr = cairo_create (widgetSurface_);
731
732 if (cairo_status (cr) == CAIRO_STATUS_SUCCESS)
733 {
734 scheduleDraw_ = false;
735
736 // Limit cairo-drawing area
737 cairo_rectangle (cr, area.getX (), area.getY (), area.getWidth (), area.getHeight ());
738 cairo_clip (cr);
739
740 double radius = border_.getRadius ();
741
742 // Draw background
743 double innerBorders = getXOffset ();
744 double innerRadius = (radius > border_.getPadding () ? radius - border_.getPadding () : 0);
745 cairo_surface_t* fillSurface = background_.getCairoSurface ();
746 BColors::Color bc = *background_.getColor();
747
748 if ((getEffectiveWidth () > 0) && (getEffectiveHeight () > 0))
749 {
750 if ((fillSurface && cairo_surface_status (fillSurface) == CAIRO_STATUS_SUCCESS) || (bc.getAlpha() != 0.0))
751 {
752 // Background_image ?
753 if (fillSurface && cairo_surface_status (fillSurface) == CAIRO_STATUS_SUCCESS) cairo_set_source_surface (cr, fillSurface, 0, 0);
754
755 // Plain Background color ?
756 else cairo_set_source_rgba (cr, CAIRO_RGBA(bc));
757
758 // If drawing area < background are, draw only a rectangle for the drawing area (faster)
759 if
760 (
761 (area.getX () >= innerBorders) && (area.getX () + area.getWidth () <= getWidth () - innerBorders) &&
762 (area.getY () >= innerBorders) && (area.getY () + area.getHeight () <= getHeight () - innerBorders)
763 )
764 {
765 cairo_rectangle (cr, area.getX (), area.getY (), area.getWidth (), area.getHeight ());
766 }
767 else
768 {
769 cairo_rectangle_rounded (cr, innerBorders, innerBorders, getEffectiveWidth (), getEffectiveHeight (), innerRadius, 0b1111);
770 }
771 cairo_fill (cr);
772 }
773
774 }
775
776 // Draw border frame
777 double outerBorders = border_.getMargin ();
778 BColors::Color lc = *border_.getLine()->getColor();
779
780 if
781 (
782 (lc.getAlpha() != 0.0) &&
783 (border_.getLine()->getWidth() != 0.0) &&
784 (getWidth () >= 2 * outerBorders) &&
785 (getHeight () >= 2 * outerBorders)
786 )
787 {
788 double lw = border_.getLine()->getWidth();
789 cairo_rectangle_rounded
790 (
791 cr,
792 outerBorders + lw / 2,
793 outerBorders + lw / 2,
794 getWidth () - 2 * outerBorders - lw,
795 getHeight () - 2 * outerBorders - lw,
796 radius, 0b1111);
797
798 cairo_set_source_rgba (cr, CAIRO_RGBA (lc));
799 cairo_set_line_width (cr, lw);
800 cairo_stroke (cr);
801 }
802 }
803
804 cairo_destroy (cr);
805 }
806
isVisible(Widget * widget)807 bool isVisible (Widget* widget) {return widget->isVisible();}
isClicklable(Widget * widget)808 bool isClicklable (Widget* widget) {return widget->isClickable();}
isDraggable(Widget * widget)809 bool isDraggable (Widget* widget) {return widget->isDraggable();}
isScrollable(Widget * widget)810 bool isScrollable (Widget* widget) {return widget->isScrollable();}
isFocusable(Widget * widget)811 bool isFocusable (Widget* widget) {return widget->isFocusable();}
812
813 }
814