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