1 // Aseprite UI Library
2 // Copyright (C) 2001-2018 David Capello
3 //
4 // This file is released under the terms of the MIT license.
5 // Read LICENSE.txt for more information.
6
7 // #define REPORT_SIGNALS
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "ui/widget.h"
14
15 #include "base/memory.h"
16 #include "base/string.h"
17 #include "she/display.h"
18 #include "she/font.h"
19 #include "she/surface.h"
20 #include "she/system.h"
21 #include "ui/init_theme_event.h"
22 #include "ui/intern.h"
23 #include "ui/layout_io.h"
24 #include "ui/load_layout_event.h"
25 #include "ui/manager.h"
26 #include "ui/message.h"
27 #include "ui/move_region.h"
28 #include "ui/paint_event.h"
29 #include "ui/size_hint_event.h"
30 #include "ui/resize_event.h"
31 #include "ui/save_layout_event.h"
32 #include "ui/system.h"
33 #include "ui/theme.h"
34 #include "ui/view.h"
35 #include "ui/window.h"
36
37 #include <cctype>
38 #include <cstdarg>
39 #include <cstdio>
40 #include <cstring>
41 #include <limits>
42 #include <queue>
43 #include <sstream>
44
45 namespace ui {
46
47 using namespace gfx;
48
mark_dirty_flag(Widget * widget)49 static inline void mark_dirty_flag(Widget* widget)
50 {
51 while (widget) {
52 widget->enableFlags(DIRTY);
53 widget = widget->parent();
54 }
55 }
56
register_widget_type()57 WidgetType register_widget_type()
58 {
59 static int type = (int)kFirstUserWidget;
60 return (WidgetType)type++;
61 }
62
Widget(WidgetType type)63 Widget::Widget(WidgetType type)
64 : m_type(type)
65 , m_flags(0)
66 , m_theme(get_theme())
67 , m_style(nullptr)
68 , m_font(nullptr)
69 , m_bgColor(gfx::ColorNone)
70 , m_bounds(0, 0, 0, 0)
71 , m_parent(nullptr)
72 , m_sizeHint(nullptr)
73 , m_mnemonic(0)
74 , m_minSize(0, 0)
75 , m_maxSize(std::numeric_limits<int>::max(),
76 std::numeric_limits<int>::max())
77 , m_childSpacing(0)
78 {
79 details::addWidget(this);
80 }
81
~Widget()82 Widget::~Widget()
83 {
84 // First, we remove children (so children's ~Widget() can access to
85 // the manager()).
86 while (!m_children.empty())
87 delete m_children.front();
88
89 // Break relationship with the manager. This cannot be before
90 // deleting children, if we delete children after releasing the
91 // parent, a children deletion could generate a kMouseLeaveMessage
92 // for the parent that will be deleted too.
93 Manager* manager = this->manager();
94 ASSERT(manager);
95 if (manager) {
96 manager->freeWidget(this);
97 manager->removeMessagesFor(this);
98 manager->removeMessageFilterFor(this);
99 }
100
101 // Remove this widget from parent.
102 if (m_parent)
103 m_parent->removeChild(this);
104
105 // Delete fixed size hint if it isn't nullptr
106 delete m_sizeHint;
107
108 // Low level free
109 details::removeWidget(this);
110 }
111
deferDelete()112 void Widget::deferDelete()
113 {
114 manager()->addToGarbage(this);
115 }
116
initTheme()117 void Widget::initTheme()
118 {
119 InitThemeEvent ev(this, m_theme);
120 onInitTheme(ev);
121 }
122
textInt() const123 int Widget::textInt() const
124 {
125 return onGetTextInt();
126 }
127
textDouble() const128 double Widget::textDouble() const
129 {
130 return onGetTextDouble();
131 }
132
setText(const std::string & text)133 void Widget::setText(const std::string& text)
134 {
135 setTextQuiet(text);
136 onSetText();
137 }
138
setTextf(const char * format,...)139 void Widget::setTextf(const char *format, ...)
140 {
141 // formatted string
142 if (format) {
143 va_list ap;
144 va_start(ap, format);
145 char buf[4096];
146 vsprintf(buf, format, ap);
147 va_end(ap);
148
149 setText(buf);
150 }
151 // empty string
152 else {
153 setText("");
154 }
155 }
156
setTextQuiet(const std::string & text)157 void Widget::setTextQuiet(const std::string& text)
158 {
159 assert_ui_thread();
160
161 m_text = text;
162 enableFlags(HAS_TEXT);
163 }
164
font() const165 she::Font* Widget::font() const
166 {
167 if (!m_font && m_theme)
168 m_font = m_theme->getWidgetFont(this);
169 return m_font;
170 }
171
setBgColor(gfx::Color color)172 void Widget::setBgColor(gfx::Color color)
173 {
174 assert_ui_thread();
175
176 m_bgColor = color;
177 onSetBgColor();
178
179 if (m_style) {
180 LOG(WARNING) << "Warning setting bgColor to a widget with style\n";
181 }
182 }
183
setTheme(Theme * theme)184 void Widget::setTheme(Theme* theme)
185 {
186 assert_ui_thread();
187
188 m_theme = theme;
189 m_font = nullptr;
190
191 for (auto child : children())
192 child->setTheme(theme);
193 }
194
setStyle(Style * style)195 void Widget::setStyle(Style* style)
196 {
197 assert_ui_thread();
198
199 m_style = style;
200 m_border = m_theme->calcBorder(this, style);
201 m_bgColor = m_theme->calcBgColor(this, style);
202 if (style->font())
203 m_font = style->font();
204 }
205
206 // ===============================================================
207 // COMMON PROPERTIES
208 // ===============================================================
209
setVisible(bool state)210 void Widget::setVisible(bool state)
211 {
212 assert_ui_thread();
213
214 if (state) {
215 if (hasFlags(HIDDEN)) {
216 disableFlags(HIDDEN);
217 invalidate();
218
219 onVisible(true);
220 }
221 }
222 else {
223 if (!hasFlags(HIDDEN)) {
224 manager()->freeWidget(this); // Free from manager
225 enableFlags(HIDDEN);
226
227 onVisible(false);
228 }
229 }
230 }
231
setEnabled(bool state)232 void Widget::setEnabled(bool state)
233 {
234 assert_ui_thread();
235
236 if (state) {
237 if (hasFlags(DISABLED)) {
238 disableFlags(DISABLED);
239 invalidate();
240
241 onEnable(true);
242 }
243 }
244 else {
245 if (!hasFlags(DISABLED)) {
246 manager()->freeWidget(this); // Free from the manager
247
248 enableFlags(DISABLED);
249 invalidate();
250
251 onEnable(false);
252 }
253 }
254 }
255
setSelected(bool state)256 void Widget::setSelected(bool state)
257 {
258 assert_ui_thread();
259
260 if (state) {
261 if (!hasFlags(SELECTED)) {
262 enableFlags(SELECTED);
263 invalidate();
264
265 onSelect(true);
266 }
267 }
268 else {
269 if (hasFlags(SELECTED)) {
270 disableFlags(SELECTED);
271 invalidate();
272
273 onSelect(false);
274 }
275 }
276 }
277
setExpansive(bool state)278 void Widget::setExpansive(bool state)
279 {
280 assert_ui_thread();
281
282 if (state)
283 enableFlags(EXPANSIVE);
284 else
285 disableFlags(EXPANSIVE);
286 }
287
setDecorative(bool state)288 void Widget::setDecorative(bool state)
289 {
290 assert_ui_thread();
291
292 if (state)
293 enableFlags(DECORATIVE);
294 else
295 disableFlags(DECORATIVE);
296 }
297
setFocusStop(bool state)298 void Widget::setFocusStop(bool state)
299 {
300 assert_ui_thread();
301
302 if (state)
303 enableFlags(FOCUS_STOP);
304 else
305 disableFlags(FOCUS_STOP);
306 }
307
setFocusMagnet(bool state)308 void Widget::setFocusMagnet(bool state)
309 {
310 assert_ui_thread();
311
312 if (state)
313 enableFlags(FOCUS_MAGNET);
314 else
315 disableFlags(FOCUS_MAGNET);
316 }
317
isVisible() const318 bool Widget::isVisible() const
319 {
320 assert_ui_thread();
321
322 const Widget* widget = this;
323 const Widget* lastWidget = nullptr;
324
325 do {
326 if (widget->hasFlags(HIDDEN))
327 return false;
328
329 lastWidget = widget;
330 widget = widget->m_parent;
331 } while (widget);
332
333 // The widget is visible if it's inside a visible manager
334 return (lastWidget ? lastWidget->type() == kManagerWidget: false);
335 }
336
isEnabled() const337 bool Widget::isEnabled() const
338 {
339 assert_ui_thread();
340
341 const Widget* widget = this;
342
343 do {
344 if (widget->hasFlags(DISABLED))
345 return false;
346
347 widget = widget->m_parent;
348 } while (widget);
349
350 return true;
351 }
352
isSelected() const353 bool Widget::isSelected() const
354 {
355 assert_ui_thread();
356 return hasFlags(SELECTED);
357 }
358
isExpansive() const359 bool Widget::isExpansive() const
360 {
361 assert_ui_thread();
362 return hasFlags(EXPANSIVE);
363 }
364
isDecorative() const365 bool Widget::isDecorative() const
366 {
367 assert_ui_thread();
368 return hasFlags(DECORATIVE);
369 }
370
isFocusStop() const371 bool Widget::isFocusStop() const
372 {
373 assert_ui_thread();
374 return hasFlags(FOCUS_STOP);
375 }
376
isFocusMagnet() const377 bool Widget::isFocusMagnet() const
378 {
379 assert_ui_thread();
380 return hasFlags(FOCUS_MAGNET);
381 }
382
383 // ===============================================================
384 // PARENTS & CHILDREN
385 // ===============================================================
386
window() const387 Window* Widget::window() const
388 {
389 assert_ui_thread();
390
391 const Widget* widget = this;
392
393 while (widget) {
394 if (widget->type() == kWindowWidget)
395 return static_cast<Window*>(const_cast<Widget*>(widget));
396
397 widget = widget->m_parent;
398 }
399
400 return nullptr;
401 }
402
manager() const403 Manager* Widget::manager() const
404 {
405 assert_ui_thread();
406
407 const Widget* widget = this;
408
409 while (widget) {
410 if (widget->type() == kManagerWidget)
411 return static_cast<Manager*>(const_cast<Widget*>(widget));
412
413 widget = widget->m_parent;
414 }
415
416 return Manager::getDefault();
417 }
418
getParents(bool ascendant,WidgetsList & parents)419 void Widget::getParents(bool ascendant, WidgetsList& parents)
420 {
421 assert_ui_thread();
422
423 for (Widget* widget=this; widget; widget=widget->m_parent) {
424 // append parents in tail
425 if (ascendant)
426 parents.push_back(widget);
427 // append parents in head
428 else
429 parents.insert(parents.begin(), widget);
430 }
431 }
432
nextSibling()433 Widget* Widget::nextSibling()
434 {
435 assert_ui_thread();
436
437 if (!m_parent)
438 return NULL;
439
440 WidgetsList::iterator begin = m_parent->m_children.begin();
441 WidgetsList::iterator end = m_parent->m_children.end();
442 WidgetsList::iterator it = std::find(begin, end, this);
443
444 if (it == end)
445 return NULL;
446
447 if (++it == end)
448 return NULL;
449
450 return *it;
451 }
452
previousSibling()453 Widget* Widget::previousSibling()
454 {
455 assert_ui_thread();
456
457 if (!m_parent)
458 return NULL;
459
460 WidgetsList::iterator begin = m_parent->m_children.begin();
461 WidgetsList::iterator end = m_parent->m_children.end();
462 WidgetsList::iterator it = std::find(begin, end, this);
463
464 if (it == begin || it == end)
465 return NULL;
466
467 return *(++it);
468 }
469
pick(const gfx::Point & pt,const bool checkParentsVisibility) const470 Widget* Widget::pick(const gfx::Point& pt,
471 const bool checkParentsVisibility) const
472 {
473 assert_ui_thread();
474
475 const Widget* inside, *picked = nullptr;
476
477 // isVisible() checks visibility of widget's parent.
478 if (((checkParentsVisibility && isVisible()) ||
479 (!checkParentsVisibility && !hasFlags(HIDDEN))) &&
480 (bounds().contains(pt))) {
481 picked = this;
482
483 for (Widget* child : m_children) {
484 inside = child->pick(pt, false);
485 if (inside) {
486 picked = inside;
487 break;
488 }
489 }
490 }
491
492 return const_cast<Widget*>(picked);
493 }
494
hasChild(Widget * child)495 bool Widget::hasChild(Widget* child)
496 {
497 ASSERT_VALID_WIDGET(child);
498 assert_ui_thread();
499
500 return std::find(m_children.begin(), m_children.end(), child) != m_children.end();
501 }
502
hasAncestor(Widget * ancestor)503 bool Widget::hasAncestor(Widget* ancestor)
504 {
505 for (Widget* widget=m_parent; widget; widget=widget->m_parent) {
506 if (widget == ancestor)
507 return true;
508 }
509 return false;
510 }
511
findChild(const char * id)512 Widget* Widget::findChild(const char* id)
513 {
514 for (auto child : m_children) {
515 if (child->id() == id)
516 return child;
517 }
518
519 for (auto child : m_children) {
520 auto subchild = child->findChild(id);
521 if (subchild)
522 return subchild;
523 }
524
525 return nullptr;
526 }
527
findSibling(const char * id)528 Widget* Widget::findSibling(const char* id)
529 {
530 return window()->findChild(id);
531 }
532
addChild(Widget * child)533 void Widget::addChild(Widget* child)
534 {
535 ASSERT_VALID_WIDGET(this);
536 ASSERT_VALID_WIDGET(child);
537
538 m_children.push_back(child);
539 child->m_parent = this;
540 }
541
removeChild(WidgetsList::iterator & it)542 void Widget::removeChild(WidgetsList::iterator& it)
543 {
544 Widget* child = *it;
545
546 ASSERT(it != m_children.end());
547 if (it != m_children.end())
548 m_children.erase(it);
549
550 // Free from manager
551 Manager* manager = this->manager();
552 if (manager)
553 manager->freeWidget(child);
554
555 child->m_parent = NULL;
556 }
557
removeChild(Widget * child)558 void Widget::removeChild(Widget* child)
559 {
560 ASSERT_VALID_WIDGET(this);
561 ASSERT_VALID_WIDGET(child);
562
563 WidgetsList::iterator it = std::find(m_children.begin(), m_children.end(), child);
564 removeChild(it);
565 }
566
removeAllChildren()567 void Widget::removeAllChildren()
568 {
569 while (!m_children.empty())
570 removeChild(--m_children.end());
571 }
572
replaceChild(Widget * oldChild,Widget * newChild)573 void Widget::replaceChild(Widget* oldChild, Widget* newChild)
574 {
575 ASSERT_VALID_WIDGET(oldChild);
576 ASSERT_VALID_WIDGET(newChild);
577
578 WidgetsList::iterator before =
579 std::find(m_children.begin(), m_children.end(), oldChild);
580 if (before == m_children.end()) {
581 ASSERT(false);
582 return;
583 }
584 int index = before - m_children.begin();
585
586 removeChild(oldChild);
587
588 m_children.insert(m_children.begin()+index, newChild);
589 newChild->m_parent = this;
590 }
591
insertChild(int index,Widget * child)592 void Widget::insertChild(int index, Widget* child)
593 {
594 ASSERT_VALID_WIDGET(this);
595 ASSERT_VALID_WIDGET(child);
596
597 m_children.insert(m_children.begin()+index, child);
598 child->m_parent = this;
599 }
600
601 // ===============================================================
602 // LAYOUT & CONSTRAINT
603 // ===============================================================
604
layout()605 void Widget::layout()
606 {
607 setBounds(bounds());
608 invalidate();
609 }
610
loadLayout()611 void Widget::loadLayout()
612 {
613 if (!m_id.empty()) {
614 LayoutIO* io = manager()->getLayoutIO();
615 if (io) {
616 std::string layout = io->loadLayout(this);
617 if (!layout.empty()) {
618 std::stringstream s(layout);
619 LoadLayoutEvent ev(this, s);
620 onLoadLayout(ev);
621 }
622 }
623 }
624
625 // Do for all children
626 for (auto child : m_children)
627 child->loadLayout();
628 }
629
saveLayout()630 void Widget::saveLayout()
631 {
632 if (!m_id.empty()) {
633 LayoutIO* io = manager()->getLayoutIO();
634 if (io) {
635 std::stringstream s;
636 SaveLayoutEvent ev(this, s);
637 onSaveLayout(ev);
638
639 std::string layout = s.str();
640 if (!layout.empty())
641 io->saveLayout(this, layout);
642 }
643 }
644
645 // Do for all children
646 for (auto child : m_children)
647 child->saveLayout();
648 }
649
setDecorativeWidgetBounds()650 void Widget::setDecorativeWidgetBounds()
651 {
652 onSetDecorativeWidgetBounds();
653 }
654
655 // ===============================================================
656 // POSITION & GEOMETRY
657 // ===============================================================
658
childrenBounds() const659 Rect Widget::childrenBounds() const
660 {
661 return Rect(m_bounds.x + border().left(),
662 m_bounds.y + border().top(),
663 m_bounds.w - border().width(),
664 m_bounds.h - border().height());
665 }
666
clientChildrenBounds() const667 Rect Widget::clientChildrenBounds() const
668 {
669 return Rect(border().left(),
670 border().top(),
671 m_bounds.w - border().width(),
672 m_bounds.h - border().height());
673 }
674
setBounds(const Rect & rc)675 void Widget::setBounds(const Rect& rc)
676 {
677 ResizeEvent ev(this, rc);
678 onResize(ev);
679 }
680
setBoundsQuietly(const gfx::Rect & rc)681 void Widget::setBoundsQuietly(const gfx::Rect& rc)
682 {
683 if (m_bounds != rc) {
684 m_bounds = rc;
685
686 // Remove all paint messages for this widget.
687 if (Manager* manager = this->manager())
688 manager->removeMessagesFor(this, kPaintMessage);
689 }
690
691 // TODO Test moving this inside the if (m_bounds != rc) { ... }
692 // block, so the widget is invalidted only when the bounds are
693 // really changed.
694 invalidate();
695 }
696
setBorder(const Border & br)697 void Widget::setBorder(const Border& br)
698 {
699 m_border = br;
700
701 if (m_style) {
702 LOG(WARNING) << "Warning setting border to a widget with style\n";
703 }
704 }
705
setChildSpacing(int childSpacing)706 void Widget::setChildSpacing(int childSpacing)
707 {
708 m_childSpacing = childSpacing;
709 }
710
noBorderNoChildSpacing()711 void Widget::noBorderNoChildSpacing()
712 {
713 m_border = gfx::Border(0, 0, 0, 0);
714 m_childSpacing = 0;
715
716 if (m_style) {
717 LOG(WARNING) << "Warning setting border to a widget with style\n";
718 }
719 }
720
getRegion(gfx::Region & region)721 void Widget::getRegion(gfx::Region& region)
722 {
723 if (type() == kWindowWidget)
724 theme()->getWindowMask(this, region);
725 else
726 region = bounds();
727 }
728
getDrawableRegion(gfx::Region & region,DrawableRegionFlags flags)729 void Widget::getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags)
730 {
731 Widget* window, *manager, *view;
732
733 getRegion(region);
734
735 // Cut the top windows areas
736 if (flags & kCutTopWindows) {
737 window = this->window();
738 manager = (window ? window->manager(): nullptr);
739
740 while (manager) {
741 const WidgetsList& windows_list = manager->children();
742 WidgetsList::const_reverse_iterator it =
743 std::find(windows_list.rbegin(), windows_list.rend(), window);
744
745 if (!windows_list.empty() &&
746 window != windows_list.front() &&
747 it != windows_list.rend()) {
748 // Subtract the rectangles
749 for (++it; it != windows_list.rend(); ++it) {
750 if (!(*it)->isVisible())
751 continue;
752
753 Region reg1;
754 (*it)->getRegion(reg1);
755 region.createSubtraction(region, reg1);
756 }
757 }
758
759 window = manager->window();
760 manager = (window ? window->manager(): nullptr);
761 }
762 }
763
764 // Clip the areas where are children
765 if (!(flags & kUseChildArea) && !children().empty()) {
766 Region reg1;
767 Region reg2(childrenBounds());
768
769 for (auto child : children()) {
770 if (child->isVisible()) {
771 Region reg3;
772 child->getRegion(reg3);
773
774 if (child->hasFlags(DECORATIVE)) {
775 reg1 = bounds();
776 reg1.createIntersection(reg1, reg3);
777 }
778 else {
779 reg1.createIntersection(reg2, reg3);
780 }
781 region.createSubtraction(region, reg1);
782 }
783 }
784 }
785
786 // Intersect with the parent area
787 if (!hasFlags(DECORATIVE)) {
788 Widget* p = this->parent();
789 while (p) {
790 region.createIntersection(
791 region, Region(p->childrenBounds()));
792 p = p->parent();
793 }
794 }
795 else {
796 Widget* p = parent();
797 if (p) {
798 region.createIntersection(
799 region, Region(p->bounds()));
800 }
801 }
802
803 // Limit to the manager area
804 window = this->window();
805 manager = (window ? window->manager(): nullptr);
806
807 while (manager) {
808 view = View::getView(manager);
809
810 Rect cpos;
811 if (view) {
812 cpos = static_cast<View*>(view)->viewportBounds();
813 }
814 else
815 cpos = manager->childrenBounds();
816
817 region.createIntersection(region, Region(cpos));
818
819 window = manager->window();
820 manager = (window ? window->manager(): nullptr);
821 }
822 }
823
textWidth() const824 int Widget::textWidth() const
825 {
826 return Graphics::measureUITextLength(text().c_str(), font());
827 }
828
textHeight() const829 int Widget::textHeight() const
830 {
831 return font()->height();
832 }
833
getTextIconInfo(gfx::Rect * box,gfx::Rect * text,gfx::Rect * icon,int icon_align,int icon_w,int icon_h)834 void Widget::getTextIconInfo(
835 gfx::Rect* box,
836 gfx::Rect* text,
837 gfx::Rect* icon,
838 int icon_align, int icon_w, int icon_h)
839 {
840 #define SETRECT(r) \
841 if (r) { \
842 r->x = r##_x; \
843 r->y = r##_y; \
844 r->w = r##_w; \
845 r->h = r##_h; \
846 }
847
848 gfx::Rect bounds = clientBounds();
849 int box_x, box_y, box_w, box_h, icon_x, icon_y;
850 int text_x, text_y, text_w, text_h;
851
852 text_x = text_y = 0;
853
854 // Size of the text
855 if (hasText()) {
856 text_w = textWidth();
857 text_h = textHeight();
858 }
859 else {
860 text_w = text_h = 0;
861 }
862
863 // Box size
864 if (icon_align & CENTER) { // With the icon in the center
865 if (icon_align & MIDDLE) { // With the icon inside the text
866 box_w = MAX(icon_w, text_w);
867 box_h = MAX(icon_h, text_h);
868 }
869 // With the icon in the top or bottom
870 else {
871 box_w = MAX(icon_w, text_w);
872 box_h = icon_h + (hasText() ? childSpacing(): 0) + text_h;
873 }
874 }
875 // With the icon in left or right that doesn't care by now
876 else {
877 box_w = icon_w + (hasText() ? childSpacing(): 0) + text_w;
878 box_h = MAX(icon_h, text_h);
879 }
880
881 // Box position
882 if (align() & RIGHT)
883 box_x = bounds.x2() - box_w - border().right();
884 else if (align() & CENTER)
885 box_x = (bounds.x+bounds.x2())/2 - box_w/2;
886 else
887 box_x = bounds.x + border().left();
888
889 if (align() & BOTTOM)
890 box_y = bounds.y2() - box_h - border().bottom();
891 else if (align() & MIDDLE)
892 box_y = (bounds.y+bounds.y2())/2 - box_h/2;
893 else
894 box_y = bounds.y + border().top();
895
896 // With text
897 if (hasText()) {
898 // Text/icon X position
899 if (icon_align & RIGHT) {
900 text_x = box_x;
901 icon_x = box_x + box_w - icon_w;
902 }
903 else if (icon_align & CENTER) {
904 text_x = box_x + box_w/2 - text_w/2;
905 icon_x = box_x + box_w/2 - icon_w/2;
906 }
907 else {
908 text_x = box_x + box_w - text_w;
909 icon_x = box_x;
910 }
911
912 // Text Y position
913 if (icon_align & BOTTOM) {
914 text_y = box_y;
915 icon_y = box_y + box_h - icon_h;
916 }
917 else if (icon_align & MIDDLE) {
918 text_y = box_y + box_h/2 - text_h/2;
919 icon_y = box_y + box_h/2 - icon_h/2;
920 }
921 else {
922 text_y = box_y + box_h - text_h;
923 icon_y = box_y;
924 }
925 }
926 // Without text
927 else {
928 // Icon X/Y position
929 icon_x = box_x;
930 icon_y = box_y;
931 }
932
933 SETRECT(box);
934 SETRECT(text);
935 SETRECT(icon);
936 }
937
setMinSize(const gfx::Size & sz)938 void Widget::setMinSize(const gfx::Size& sz)
939 {
940 m_minSize = sz;
941 }
942
setMaxSize(const gfx::Size & sz)943 void Widget::setMaxSize(const gfx::Size& sz)
944 {
945 m_maxSize = sz;
946 }
947
flushRedraw()948 void Widget::flushRedraw()
949 {
950 std::queue<Widget*> processing;
951 Message* msg;
952
953 if (hasFlags(DIRTY)) {
954 disableFlags(DIRTY);
955 processing.push(this);
956 }
957
958 Manager* manager = this->manager();
959 ASSERT(manager);
960
961 while (!processing.empty()) {
962 Widget* widget = processing.front();
963 processing.pop();
964
965 ASSERT_VALID_WIDGET(widget);
966
967 // If the widget is hidden
968 if (!widget->isVisible())
969 continue;
970
971 for (auto child : widget->children()) {
972 if (child->hasFlags(DIRTY)) {
973 child->disableFlags(DIRTY);
974 processing.push(child);
975 }
976 }
977
978 if (!widget->m_updateRegion.isEmpty()) {
979 // Intersect m_updateRegion with drawable area.
980 {
981 Region drawable;
982 widget->getDrawableRegion(drawable, kCutTopWindows);
983 widget->m_updateRegion &= drawable;
984 }
985
986 std::size_t c, nrects = widget->m_updateRegion.size();
987 Region::const_iterator it = widget->m_updateRegion.begin();
988
989 // Draw the widget
990 int count = nrects-1;
991 for (c=0; c<nrects; ++c, ++it, --count) {
992 // Create the draw message
993 msg = new PaintMessage(count, *it);
994 msg->addRecipient(widget);
995
996 // Enqueue the draw message
997 manager->enqueueMessage(msg);
998 }
999
1000 manager->addInvalidRegion(widget->m_updateRegion);
1001 widget->m_updateRegion.clear();
1002 }
1003 }
1004 }
1005
paint(Graphics * graphics,const gfx::Region & drawRegion,const bool isBg)1006 void Widget::paint(Graphics* graphics,
1007 const gfx::Region& drawRegion,
1008 const bool isBg)
1009 {
1010 if (drawRegion.isEmpty())
1011 return;
1012
1013 std::queue<Widget*> processing;
1014 processing.push(this);
1015
1016 while (!processing.empty()) {
1017 Widget* widget = processing.front();
1018 processing.pop();
1019
1020 ASSERT_VALID_WIDGET(widget);
1021
1022 // If the widget is hidden
1023 if (!widget->isVisible())
1024 continue;
1025
1026 for (auto child : widget->children())
1027 processing.push(child);
1028
1029 // Intersect drawRegion with widget's drawable region.
1030 Region region;
1031 widget->getDrawableRegion(region, kCutTopWindows);
1032 region.createIntersection(region, drawRegion);
1033
1034 Graphics graphics2(
1035 graphics->getInternalSurface(),
1036 widget->bounds().x,
1037 widget->bounds().y);
1038 graphics2.setFont(widget->font());
1039
1040 for (Region::const_iterator
1041 it = region.begin(),
1042 end = region.end(); it != end; ++it) {
1043 IntersectClip clip(&graphics2, Rect(*it).offset(
1044 -widget->bounds().x,
1045 -widget->bounds().y));
1046 widget->paintEvent(&graphics2, isBg);
1047 }
1048 }
1049 }
1050
paintEvent(Graphics * graphics,const bool isBg)1051 bool Widget::paintEvent(Graphics* graphics,
1052 const bool isBg)
1053 {
1054 // For transparent widgets we have to draw the parent first.
1055 if (isTransparent()) {
1056 #if _DEBUG
1057 // In debug mode we can fill the area with Red so we know if the
1058 // we are drawing the parent correctly.
1059 graphics->fillRect(gfx::rgba(255, 0, 0), clientBounds());
1060 #endif
1061
1062 enableFlags(HIDDEN);
1063
1064 if (parent()) {
1065 gfx::Region rgn(parent()->bounds());
1066 rgn.createIntersection(
1067 rgn,
1068 gfx::Region(
1069 graphics->getClipBounds().offset(
1070 graphics->getInternalDeltaX(),
1071 graphics->getInternalDeltaY())));
1072 parent()->paint(graphics, rgn, true);
1073 }
1074
1075 disableFlags(HIDDEN);
1076 }
1077
1078 PaintEvent ev(this, graphics);
1079 ev.setTransparentBg(isBg);
1080 onPaint(ev); // Fire onPaint event
1081 return ev.isPainted();
1082 }
1083
isDoubleBuffered() const1084 bool Widget::isDoubleBuffered() const
1085 {
1086 return hasFlags(DOUBLE_BUFFERED);
1087 }
1088
setDoubleBuffered(bool doubleBuffered)1089 void Widget::setDoubleBuffered(bool doubleBuffered)
1090 {
1091 enableFlags(DOUBLE_BUFFERED);
1092 }
1093
isTransparent() const1094 bool Widget::isTransparent() const
1095 {
1096 return hasFlags(TRANSPARENT);
1097 }
1098
setTransparent(bool transparent)1099 void Widget::setTransparent(bool transparent)
1100 {
1101 enableFlags(TRANSPARENT);
1102 }
1103
invalidate()1104 void Widget::invalidate()
1105 {
1106 // TODO we should use invalidateRect(bounds()) here.
1107
1108 if (isVisible()) {
1109 m_updateRegion.clear();
1110 getDrawableRegion(m_updateRegion, kCutTopWindows);
1111
1112 mark_dirty_flag(this);
1113
1114 for (auto child : m_children)
1115 child->invalidate();
1116 }
1117 }
1118
invalidateRect(const gfx::Rect & rect)1119 void Widget::invalidateRect(const gfx::Rect& rect)
1120 {
1121 if (isVisible())
1122 invalidateRegion(Region(rect));
1123 }
1124
invalidateRegion(const Region & region)1125 void Widget::invalidateRegion(const Region& region)
1126 {
1127 onInvalidateRegion(region);
1128 }
1129
1130 class DeleteGraphicsAndSurface {
1131 public:
DeleteGraphicsAndSurface(const gfx::Rect & clip,she::Surface * surface)1132 DeleteGraphicsAndSurface(const gfx::Rect& clip, she::Surface* surface)
1133 : m_pt(clip.origin()), m_surface(surface) {
1134 }
1135
operator ()(Graphics * graphics)1136 void operator()(Graphics* graphics) {
1137 {
1138 she::Surface* dst = she::instance()->defaultDisplay()->getSurface();
1139 she::SurfaceLock lockSrc(m_surface);
1140 she::SurfaceLock lockDst(dst);
1141 m_surface->blitTo(
1142 dst, 0, 0, m_pt.x, m_pt.y,
1143 m_surface->width(), m_surface->height());
1144 }
1145 m_surface->dispose();
1146 delete graphics;
1147 }
1148
1149 private:
1150 gfx::Point m_pt;
1151 she::Surface* m_surface;
1152 };
1153
getGraphics(const gfx::Rect & clip)1154 GraphicsPtr Widget::getGraphics(const gfx::Rect& clip)
1155 {
1156 GraphicsPtr graphics;
1157 she::Surface* surface;
1158 she::Surface* defaultSurface = she::instance()->defaultDisplay()->getSurface();
1159
1160 // In case of double-buffering, we need to create the temporary
1161 // buffer only if the default surface is the screen.
1162 if (isDoubleBuffered() && defaultSurface->isDirectToScreen()) {
1163 surface = she::instance()->createSurface(clip.w, clip.h);
1164 graphics.reset(new Graphics(surface, -clip.x, -clip.y),
1165 DeleteGraphicsAndSurface(clip, surface));
1166 }
1167 // In other case, we can draw directly onto the screen.
1168 else {
1169 surface = defaultSurface;
1170 graphics.reset(new Graphics(surface, bounds().x, bounds().y));
1171 }
1172
1173 graphics->setFont(font());
1174 return graphics;
1175 }
1176
1177 // ===============================================================
1178 // GUI MANAGER
1179 // ===============================================================
1180
sendMessage(Message * msg)1181 bool Widget::sendMessage(Message* msg)
1182 {
1183 ASSERT(msg);
1184 return onProcessMessage(msg);
1185 }
1186
closeWindow()1187 void Widget::closeWindow()
1188 {
1189 if (Window* w = window())
1190 w->closeWindow(this);
1191 }
1192
broadcastMouseMessage(WidgetsList & targets)1193 void Widget::broadcastMouseMessage(WidgetsList& targets)
1194 {
1195 onBroadcastMouseMessage(targets);
1196 }
1197
1198 // ===============================================================
1199 // SIZE & POSITION
1200 // ===============================================================
1201
1202 /**
1203 Returns the preferred size of the Widget.
1204
1205 It checks if the preferred size is static (it means when it was
1206 set through #setSizeHint before) or if it is dynamic (this is
1207 the default and is when the #onSizeHint is used to determined
1208 the preferred size).
1209
1210 In another words, if you do not use #setSizeHint to set a
1211 <em>static preferred size</em> for the widget then #onSizeHint
1212 will be used to calculate it.
1213
1214 @see setSizeHint, onSizeHint, #sizeHint(const Size &)
1215 */
sizeHint()1216 Size Widget::sizeHint()
1217 {
1218 if (m_sizeHint)
1219 return *m_sizeHint;
1220 else {
1221 SizeHintEvent ev(this, Size(0, 0));
1222 onSizeHint(ev);
1223
1224 Size sz(ev.sizeHint());
1225 sz.w = MID(m_minSize.w, sz.w, m_maxSize.w);
1226 sz.h = MID(m_minSize.h, sz.h, m_maxSize.h);
1227 return sz;
1228 }
1229 }
1230
1231 /**
1232 Returns the preferred size trying to fit in the specified size.
1233 Remember that if you use #setSizeHint this routine will
1234 return the static size which you specified manually.
1235
1236 @param fitIn
1237 This can have both attributes (width and height) in
1238 zero, which means that it'll behave same as #sizeHint().
1239 If the width is great than zero the #onSizeHint will try to
1240 fit in that width (this is useful to fit Label or Edit controls
1241 in a specified width and calculate the height it could occupy).
1242
1243 @see sizeHint
1244 */
sizeHint(const Size & fitIn)1245 Size Widget::sizeHint(const Size& fitIn)
1246 {
1247 if (m_sizeHint)
1248 return *m_sizeHint;
1249 else {
1250 SizeHintEvent ev(this, fitIn);
1251 onSizeHint(ev);
1252
1253 Size sz(ev.sizeHint());
1254 sz.w = MID(m_minSize.w, sz.w, m_maxSize.w);
1255 sz.h = MID(m_minSize.h, sz.h, m_maxSize.h);
1256 return sz;
1257 }
1258 }
1259
1260 /**
1261 Sets a fixed preferred size specified by the user.
1262 Widget::sizeHint() will return this value if it's setted.
1263 */
setSizeHint(const Size & fixedSize)1264 void Widget::setSizeHint(const Size& fixedSize)
1265 {
1266 delete m_sizeHint;
1267 m_sizeHint = new Size(fixedSize);
1268 }
1269
setSizeHint(int fixedWidth,int fixedHeight)1270 void Widget::setSizeHint(int fixedWidth, int fixedHeight)
1271 {
1272 setSizeHint(Size(fixedWidth, fixedHeight));
1273 }
1274
resetSizeHint()1275 void Widget::resetSizeHint()
1276 {
1277 if (m_sizeHint) {
1278 delete m_sizeHint;
1279 m_sizeHint = nullptr;
1280 }
1281 }
1282
1283 // ===============================================================
1284 // FOCUS & MOUSE
1285 // ===============================================================
1286
requestFocus()1287 void Widget::requestFocus()
1288 {
1289 manager()->setFocus(this);
1290 }
1291
releaseFocus()1292 void Widget::releaseFocus()
1293 {
1294 if (hasFocus())
1295 manager()->freeFocus();
1296 }
1297
1298 /**
1299 * Captures the mouse to send all the future mouse messsages to the
1300 * specified widget (included the kMouseMoveMessage and kSetCursorMessage).
1301 */
captureMouse()1302 void Widget::captureMouse()
1303 {
1304 if (!manager()->getCapture()) {
1305 manager()->setCapture(this);
1306 }
1307 }
1308
1309 /**
1310 * Releases the capture of the mouse events.
1311 */
releaseMouse()1312 void Widget::releaseMouse()
1313 {
1314 if (manager()->getCapture() == this) {
1315 manager()->freeCapture();
1316 }
1317 }
1318
offerCapture(ui::MouseMessage * mouseMsg,int widget_type)1319 bool Widget::offerCapture(ui::MouseMessage* mouseMsg, int widget_type)
1320 {
1321 if (hasCapture()) {
1322 Widget* pick = manager()->pick(mouseMsg->position());
1323 if (pick && pick != this && pick->type() == widget_type) {
1324 releaseMouse();
1325
1326 MouseMessage* mouseMsg2 = new MouseMessage(
1327 kMouseDownMessage,
1328 mouseMsg->pointerType(),
1329 mouseMsg->buttons(),
1330 mouseMsg->modifiers(),
1331 mouseMsg->position());
1332 mouseMsg2->addRecipient(pick);
1333 manager()->enqueueMessage(mouseMsg2);
1334 return true;
1335 }
1336 }
1337 return false;
1338 }
1339
hasFocus() const1340 bool Widget::hasFocus() const
1341 {
1342 return hasFlags(HAS_FOCUS);
1343 }
1344
hasMouse() const1345 bool Widget::hasMouse() const
1346 {
1347 return hasFlags(HAS_MOUSE);
1348 }
1349
hasMouseOver() const1350 bool Widget::hasMouseOver() const
1351 {
1352 return (this == pick(get_mouse_position()));
1353 }
1354
hasCapture() const1355 bool Widget::hasCapture() const
1356 {
1357 return hasFlags(HAS_CAPTURE);
1358 }
1359
setMnemonic(int mnemonic)1360 void Widget::setMnemonic(int mnemonic)
1361 {
1362 m_mnemonic = mnemonic;
1363 }
1364
processMnemonicFromText(int escapeChar)1365 void Widget::processMnemonicFromText(int escapeChar)
1366 {
1367 // Avoid calling setText() when the widget doesn't have the HAS_TEXT flag
1368 if (!hasText())
1369 return;
1370
1371 std::wstring newText; // wstring is used to properly push_back() multibyte chars
1372 if (!m_text.empty())
1373 newText.reserve(m_text.size());
1374
1375 for (base::utf8_const_iterator
1376 it(m_text.begin()),
1377 end(m_text.end()); it != end; ++it) {
1378 if (*it == escapeChar) {
1379 ++it;
1380 if (it == end) {
1381 break; // Ill-formed string (it ends with escape character)
1382 }
1383 else if (*it != escapeChar) {
1384 setMnemonic(*it);
1385 }
1386 }
1387 newText.push_back(*it);
1388 }
1389
1390 setText(base::to_utf8(newText));
1391 }
1392
isMnemonicPressed(const KeyMessage * keyMsg) const1393 bool Widget::isMnemonicPressed(const KeyMessage* keyMsg) const
1394 {
1395 int chr = std::tolower(mnemonic());
1396 return
1397 ((chr) &&
1398 ((chr == std::tolower(keyMsg->unicodeChar())) ||
1399 (chr >= 'a' && chr <= 'z' && keyMsg->scancode() == (kKeyA + chr - 'a')) ||
1400 (chr >= '0' && chr <= '9' && keyMsg->scancode() == (kKey0 + chr - '0'))));
1401 }
1402
onProcessMessage(Message * msg)1403 bool Widget::onProcessMessage(Message* msg)
1404 {
1405 ASSERT(msg != NULL);
1406
1407 switch (msg->type()) {
1408
1409 case kFunctionMessage:
1410 static_cast<FunctionMessage*>(msg)->call();
1411 break;
1412
1413 case kOpenMessage:
1414 case kCloseMessage:
1415 case kWinMoveMessage:
1416 // Broadcast the message to the children.
1417 for (auto child : m_children)
1418 child->sendMessage(msg);
1419 break;
1420
1421 case kPaintMessage: {
1422 const PaintMessage* ptmsg = static_cast<const PaintMessage*>(msg);
1423 ASSERT(ptmsg->rect().w > 0);
1424 ASSERT(ptmsg->rect().h > 0);
1425
1426 GraphicsPtr graphics = getGraphics(toClient(ptmsg->rect()));
1427 return paintEvent(graphics.get(), false);
1428 }
1429
1430 case kDoubleClickMessage: {
1431 // Convert double clicks into mouse down
1432 MouseMessage* mouseMsg = static_cast<MouseMessage*>(msg);
1433 MouseMessage mouseMsg2(kMouseDownMessage,
1434 mouseMsg->pointerType(),
1435 mouseMsg->buttons(),
1436 mouseMsg->modifiers(),
1437 mouseMsg->position(),
1438 mouseMsg->wheelDelta());
1439
1440 sendMessage(&mouseMsg2);
1441 break;
1442 }
1443
1444 case kMouseDownMessage:
1445 case kMouseUpMessage:
1446 case kMouseMoveMessage:
1447 case kMouseWheelMessage:
1448 // Propagate the message to the parent.
1449 if (parent() != NULL)
1450 return parent()->sendMessage(msg);
1451 else
1452 break;
1453
1454 case kSetCursorMessage:
1455 // Propagate the message to the parent.
1456 if (parent())
1457 return parent()->sendMessage(msg);
1458 else {
1459 set_mouse_cursor(kArrowCursor);
1460 return true;
1461 }
1462
1463 }
1464
1465 // Broadcast the message to the children.
1466 if (msg->propagateToChildren()) {
1467 for (auto child : m_children)
1468 if (child->sendMessage(msg))
1469 return true;
1470 }
1471
1472 // Propagate the message to the parent.
1473 if (msg->propagateToParent() && parent())
1474 return parent()->sendMessage(msg);
1475
1476 return false;
1477 }
1478
1479 // ===============================================================
1480 // EVENTS
1481 // ===============================================================
1482
onInvalidateRegion(const Region & region)1483 void Widget::onInvalidateRegion(const Region& region)
1484 {
1485 if (isVisible() && region.contains(bounds()) != Region::Out) {
1486 Region reg1;
1487 reg1.createUnion(m_updateRegion, region);
1488 {
1489 Region reg2;
1490 getDrawableRegion(reg2, kCutTopWindows);
1491 m_updateRegion.createIntersection(reg1, reg2);
1492 }
1493 reg1.createSubtraction(region, m_updateRegion);
1494
1495 mark_dirty_flag(this);
1496
1497 for (auto child : m_children)
1498 child->invalidateRegion(reg1);
1499 }
1500 }
1501
onSizeHint(SizeHintEvent & ev)1502 void Widget::onSizeHint(SizeHintEvent& ev)
1503 {
1504 if (m_style) {
1505 ev.setSizeHint(m_theme->calcSizeHint(this, style()));
1506 }
1507 else {
1508 ev.setSizeHint(m_minSize);
1509 }
1510 }
1511
onLoadLayout(LoadLayoutEvent & ev)1512 void Widget::onLoadLayout(LoadLayoutEvent& ev)
1513 {
1514 // Do nothing
1515 }
1516
onSaveLayout(SaveLayoutEvent & ev)1517 void Widget::onSaveLayout(SaveLayoutEvent& ev)
1518 {
1519 // Do nothing
1520 }
1521
onResize(ResizeEvent & ev)1522 void Widget::onResize(ResizeEvent& ev)
1523 {
1524 setBoundsQuietly(ev.bounds());
1525
1526 // Set all the children to the same "cpos".
1527 gfx::Rect cpos = childrenBounds();
1528 for (auto child : m_children)
1529 child->setBounds(cpos);
1530 }
1531
onPaint(PaintEvent & ev)1532 void Widget::onPaint(PaintEvent& ev)
1533 {
1534 if (m_style)
1535 m_theme->paintWidget(ev.graphics(), this, style(),
1536 clientBounds());
1537 }
1538
onBroadcastMouseMessage(WidgetsList & targets)1539 void Widget::onBroadcastMouseMessage(WidgetsList& targets)
1540 {
1541 // Do nothing
1542 }
1543
onInitTheme(InitThemeEvent & ev)1544 void Widget::onInitTheme(InitThemeEvent& ev)
1545 {
1546 for (auto child : children())
1547 child->initTheme();
1548
1549 if (m_theme) {
1550 m_theme->initWidget(this);
1551
1552 if (!hasFlags(INITIALIZED))
1553 enableFlags(INITIALIZED);
1554
1555 InitTheme();
1556 }
1557 }
1558
onSetDecorativeWidgetBounds()1559 void Widget::onSetDecorativeWidgetBounds()
1560 {
1561 if (m_theme)
1562 m_theme->setDecorativeWidgetBounds(this);
1563 }
1564
onVisible(bool visible)1565 void Widget::onVisible(bool visible)
1566 {
1567 // Do nothing
1568 }
1569
onEnable(bool enabled)1570 void Widget::onEnable(bool enabled)
1571 {
1572 // Do nothing
1573 }
1574
onSelect(bool selected)1575 void Widget::onSelect(bool selected)
1576 {
1577 // Do nothing
1578 }
1579
onSetText()1580 void Widget::onSetText()
1581 {
1582 invalidate();
1583 }
1584
onSetBgColor()1585 void Widget::onSetBgColor()
1586 {
1587 invalidate();
1588 }
1589
onGetTextInt() const1590 int Widget::onGetTextInt() const
1591 {
1592 return std::strtol(m_text.c_str(), nullptr, 10);
1593 }
1594
onGetTextDouble() const1595 double Widget::onGetTextDouble() const
1596 {
1597 return std::strtod(m_text.c_str(), nullptr);
1598 }
1599
offsetWidgets(int dx,int dy)1600 void Widget::offsetWidgets(int dx, int dy)
1601 {
1602 m_updateRegion.offset(dx, dy);
1603 m_bounds.offset(dx, dy);
1604
1605 // Remove all paint messages for this widget.
1606 if (Manager* manager = this->manager())
1607 manager->removeMessagesFor(this, kPaintMessage);
1608
1609 for (auto child : m_children)
1610 child->offsetWidgets(dx, dy);
1611 }
1612
1613 } // namespace ui
1614