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