1// Aseprite UI Library
2// Copyright (C) 2018-2022 Igara Studio S.A.
3// Copyright (C) 2001-2018 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifndef UI_WIDGET_H_INCLUDED
9#define UI_WIDGET_H_INCLUDED
10#pragma once
11
12#include "gfx/border.h"
13#include "gfx/color.h"
14#include "gfx/point.h"
15#include "gfx/rect.h"
16#include "gfx/region.h"
17#include "gfx/size.h"
18#include "obs/signal.h"
19#include "os/font.h"
20#include "ui/base.h"
21#include "ui/component.h"
22#include "ui/graphics.h"
23#include "ui/widget_type.h"
24#include "ui/widgets_list.h"
25
26#include <string>
27
28#define ASSERT_VALID_WIDGET(widget) ASSERT((widget) != nullptr)
29
30namespace ui {
31
32 class Display;
33 class InitThemeEvent;
34 class KeyMessage;
35 class LoadLayoutEvent;
36 class Manager;
37 class Message;
38 class MouseMessage;
39 class PaintEvent;
40 class ResizeEvent;
41 class SaveLayoutEvent;
42 class SizeHintEvent;
43 class Style;
44 class Theme;
45 class Window;
46
47 class Widget : public Component {
48 public:
49
50 // ===============================================================
51 // CTOR & DTOR
52 // ===============================================================
53
54 Widget(WidgetType type = kGenericWidget);
55 virtual ~Widget();
56
57 // Safe way to delete a widget when it is not in the manager message
58 // queue anymore.
59 void deferDelete();
60
61 // Main properties.
62
63 WidgetType type() const { return m_type; }
64 void setType(WidgetType type) { m_type = type; } // TODO remove this function
65
66 const std::string& id() const { return m_id; }
67 void setId(const char* id) { m_id = id; }
68
69 int flags() const { return m_flags; }
70 bool hasFlags(int flags) const { return ((m_flags & flags) == flags); }
71 void enableFlags(int flags) { m_flags |= flags; }
72 void disableFlags(int flags) { m_flags &= ~flags; }
73
74 int align() const { return (m_flags & ALIGN_MASK); }
75 void setAlign(int align) {
76 m_flags = ((m_flags & PROPERTIES_MASK) |
77 (align & ALIGN_MASK));
78 }
79
80 // Text property.
81
82 bool hasText() const { return hasFlags(HAS_TEXT); }
83
84 const std::string& text() const { return m_text; }
85 int textInt() const;
86 double textDouble() const;
87 void setText(const std::string& text);
88 void setTextf(const char* text, ...);
89 void setTextQuiet(const std::string& text);
90
91 int textWidth() const;
92 int textHeight() const;
93
94 gfx::Size textSize() const {
95 return gfx::Size(textWidth(), textHeight());
96 }
97
98 // ===============================================================
99 // COMMON PROPERTIES
100 // ===============================================================
101
102 // True if this widget and all its ancestors are visible.
103 bool isVisible() const;
104 void setVisible(bool state);
105
106 // True if this widget can receive user input (is not disabled).
107 bool isEnabled() const;
108 void setEnabled(bool state);
109
110 // True if this widget is selected (pushed in case of a button, or
111 // checked in the case of a check-box).
112 bool isSelected() const;
113 void setSelected(bool state);
114
115 // True if this widget wants more space when it's inside a Box
116 // parent.
117 bool isExpansive() const;
118 void setExpansive(bool state);
119
120 // True if this is a decorative widget created by the current
121 // theme. Decorative widgets are arranged by the theme instead that
122 // the parent's widget.
123 bool isDecorative() const;
124 void setDecorative(bool state);
125
126 // True if this widget can receive the keyboard focus.
127 bool isFocusStop() const;
128 void setFocusStop(bool state);
129
130 // True if this widget wants the focus by default when it's shown by
131 // first time (e.g. when its parent window is opened).
132 void setFocusMagnet(bool state);
133 bool isFocusMagnet() const;
134
135 // ===============================================================
136 // LOOK & FEEL
137 // ===============================================================
138
139 os::Font* font() const;
140
141 // Gets the background color of the widget.
142 gfx::Color bgColor() const {
143 if (gfx::geta(m_bgColor) == 0 && m_parent)
144 return m_parent->bgColor();
145 else
146 return m_bgColor;
147 }
148
149 // Sets the background color of the widget
150 void setBgColor(gfx::Color color);
151
152 Theme* theme() const { return m_theme; }
153 Style* style() const { return m_style; }
154 void setTheme(Theme* theme);
155 void setStyle(Style* style);
156 void initTheme();
157
158 // ===============================================================
159 // PARENTS & CHILDREN
160 // ===============================================================
161
162 Window* window() const;
163 Widget* parent() const { return m_parent; }
164 int parentIndex() const { return m_parentIndex; }
165 Manager* manager() const;
166 Display* display() const;
167
168 // Returns a list of children.
169 const WidgetsList& children() const { return m_children; }
170 bool hasChildren() const { return !m_children.empty(); }
171
172 Widget* at(int index) { return m_children[index]; }
173 int getChildIndex(Widget* child);
174
175 // Returns the first/last child or nullptr if it doesn't exist.
176 Widget* firstChild() {
177 return (hasChildren() ? m_children.front(): nullptr);
178 }
179 Widget* lastChild() {
180 return (hasChildren() ? m_children.back(): nullptr);
181 }
182
183 // Returns the next or previous siblings.
184 Widget* nextSibling();
185 Widget* previousSibling();
186
187 Widget* pick(const gfx::Point& pt,
188 const bool checkParentsVisibility = true) const;
189 virtual Widget* pickFromScreenPos(const gfx::Point& screenPos) const;
190
191 bool hasChild(Widget* child);
192 bool hasAncestor(Widget* ancestor);
193 Widget* findChild(const char* id) const;
194
195 // Returns a widget in the same window that is located "sibling".
196 Widget* findSibling(const char* id) const;
197
198 // Finds a child with the specified ID and dynamic-casts it to type
199 // T.
200 template<class T>
201 T* findChildT(const char* id) const {
202 return dynamic_cast<T*>(findChild(id));
203 }
204
205 template<class T>
206 T* findFirstChildByType() const {
207 for (auto child : m_children) {
208 if (T* specificChild = dynamic_cast<T*>(child))
209 return specificChild;
210 }
211 return nullptr;
212 }
213
214 void addChild(Widget* child);
215 void removeChild(Widget* child);
216 void removeAllChildren();
217 void replaceChild(Widget* oldChild, Widget* newChild);
218 void insertChild(int index, Widget* child);
219 void moveChildTo(Widget* thisChild, Widget* toThisPosition);
220
221 // ===============================================================
222 // LAYOUT & CONSTRAINT
223 // ===============================================================
224
225 void layout();
226 void loadLayout();
227 void saveLayout();
228
229 void setDecorativeWidgetBounds();
230
231 // ===============================================================
232 // POSITION & GEOMETRY
233 // ===============================================================
234
235 gfx::Rect bounds() const { return m_bounds; }
236 gfx::Point origin() const { return m_bounds.origin(); }
237 gfx::Size size() const { return m_bounds.size(); }
238
239 gfx::Rect clientBounds() const {
240 return gfx::Rect(0, 0, m_bounds.w, m_bounds.h);
241 }
242
243 gfx::Rect childrenBounds() const;
244 gfx::Rect clientChildrenBounds() const;
245
246 // Bounds of this widget or window on native screen/desktop coordinates.
247 gfx::Rect boundsOnScreen() const;
248
249 // Sets the bounds of the widget generating a onResize() event.
250 void setBounds(const gfx::Rect& rc);
251
252 // Sets the bounds of the widget without generating any kind of
253 // event. This member function must be used if you override
254 // onResize() and want to change the size of the widget without
255 // generating recursive onResize() events.
256 void setBoundsQuietly(const gfx::Rect& rc);
257 void offsetWidgets(int dx, int dy);
258
259 const gfx::Size& minSize() const { return m_minSize; }
260 const gfx::Size& maxSize() const { return m_maxSize; }
261 void setMinSize(const gfx::Size& sz);
262 void setMaxSize(const gfx::Size& sz);
263 void resetMinSize();
264 void resetMaxSize();
265
266 const gfx::Border& border() const { return m_border; }
267 void setBorder(const gfx::Border& border);
268
269 int childSpacing() const { return m_childSpacing; }
270 void setChildSpacing(int childSpacing);
271
272 void noBorderNoChildSpacing();
273
274 // Flags for getDrawableRegion()
275 enum DrawableRegionFlags {
276 kCutTopWindows = 1, // Cut areas where are windows on top.
277 kUseChildArea = 2, // Use areas where are children.
278 kCutTopWindowsAndUseChildArea = kCutTopWindows | kUseChildArea,
279 };
280
281 void getRegion(gfx::Region& region);
282 void getDrawableRegion(gfx::Region& region, DrawableRegionFlags flags);
283
284 gfx::Point toClient(const gfx::Point& pt) const {
285 return pt - m_bounds.origin();
286 }
287 gfx::Rect toClient(const gfx::Rect& rc) const {
288 return gfx::Rect(rc).offset(-m_bounds.x, -m_bounds.y);
289 }
290
291 void getTextIconInfo(
292 gfx::Rect* box,
293 gfx::Rect* text = nullptr,
294 gfx::Rect* icon = nullptr,
295 int icon_align = 0, int icon_w = 0, int icon_h = 0);
296
297 // ===============================================================
298 // REFRESH ISSUES
299 // ===============================================================
300
301 bool isDoubleBuffered() const;
302 void setDoubleBuffered(bool doubleBuffered);
303
304 bool isTransparent() const;
305 void setTransparent(bool transparent);
306
307 void invalidate();
308 void invalidateRect(const gfx::Rect& rect);
309 void invalidateRegion(const gfx::Region& region);
310
311 // Returns the region to generate PaintMessages. It's cleared
312 // after flushRedraw() is called.
313 const gfx::Region& getUpdateRegion() const {
314 return m_updateRegion;
315 }
316
317 // Generates paint messages for the current update region.
318 void flushRedraw();
319
320 GraphicsPtr getGraphics(const gfx::Rect& clip);
321
322 // ===============================================================
323 // GUI MANAGER
324 // ===============================================================
325
326 bool sendMessage(Message* msg);
327 void closeWindow();
328
329 void broadcastMouseMessage(const gfx::Point& screenPos,
330 WidgetsList& targets);
331
332 // ===============================================================
333 // SIZE & POSITION
334 // ===============================================================
335
336 gfx::Size sizeHint();
337 gfx::Size sizeHint(const gfx::Size& fitIn);
338 void setSizeHint(const gfx::Size& fixedSize);
339 void setSizeHint(int fixedWidth, int fixedHeight);
340 void resetSizeHint();
341
342 // ===============================================================
343 // MOUSE, FOCUS & KEYBOARD
344 // ===============================================================
345
346 void requestFocus();
347 void releaseFocus();
348 void captureMouse();
349 void releaseMouse();
350
351 bool hasFocus() const { return hasFlags(HAS_FOCUS); }
352 bool hasMouse() const { return hasFlags(HAS_MOUSE); }
353 bool hasCapture() const { return hasFlags(HAS_CAPTURE); }
354
355 // Checking if the mouse is currently above the widget.
356 bool hasMouseOver() const;
357
358 // Returns the mouse position relative to the top-left corner of
359 // the ui::Display's client area/content rect.
360 gfx::Point mousePosInDisplay() const;
361
362 // Returns the mouse position relative to the top-left cornder of
363 // the widget bounds.
364 gfx::Point mousePosInClientBounds() const {
365 return toClient(mousePosInDisplay());
366 }
367
368 // Offer the capture to widgets of the given type. Returns true if
369 // the capture was passed to other widget.
370 bool offerCapture(ui::MouseMessage* mouseMsg, int widget_type);
371
372 // Returns lower-case letter that represet the mnemonic of the widget
373 // (the underscored character, i.e. the letter after & symbol).
374 int mnemonic() const { return m_mnemonic; }
375 void setMnemonic(int mnemonic);
376
377 // Assigns mnemonic from the character preceded by the given
378 // escapeChar ('&' by default).
379 void processMnemonicFromText(int escapeChar = '&');
380
381 // Returns true if the mnemonic character is pressed.
382 bool isMnemonicPressed(const ui::KeyMessage* keyMsg) const;
383
384 // Signals
385 obs::signal<void()> InitTheme;
386
387 protected:
388 // ===============================================================
389 // MESSAGE PROCESSING
390 // ===============================================================
391
392 virtual bool onProcessMessage(Message* msg);
393
394 // ===============================================================
395 // EVENTS
396 // ===============================================================
397
398 virtual void onInvalidateRegion(const gfx::Region& region);
399 virtual void onSizeHint(SizeHintEvent& ev);
400 virtual void onLoadLayout(LoadLayoutEvent& ev);
401 virtual void onSaveLayout(SaveLayoutEvent& ev);
402 virtual void onResize(ResizeEvent& ev);
403 virtual void onPaint(PaintEvent& ev);
404 virtual void onBroadcastMouseMessage(const gfx::Point& screenPos,
405 WidgetsList& targets);
406 virtual void onInitTheme(InitThemeEvent& ev);
407 virtual void onSetDecorativeWidgetBounds();
408 virtual void onVisible(bool visible);
409 virtual void onEnable(bool enabled);
410 virtual void onSelect(bool selected);
411 virtual void onSetText();
412 virtual void onSetBgColor();
413 virtual int onGetTextInt() const;
414 virtual double onGetTextDouble() const;
415
416 private:
417 void removeChild(const WidgetsList::iterator& it);
418 void paint(Graphics* graphics,
419 const gfx::Region& drawRegion,
420 const bool isBg);
421 bool paintEvent(Graphics* graphics,
422 const bool isBg);
423 void setDirtyFlag();
424
425 WidgetType m_type; // Widget's type
426 std::string m_id; // Widget's id
427 int m_flags; // Special boolean properties (see flags in ui/base.h)
428 Theme* m_theme; // Widget's theme
429 Style* m_style;
430 std::string m_text; // Widget text
431 mutable os::FontRef m_font; // Cached font returned by the theme
432 gfx::Color m_bgColor; // Background color
433 gfx::Rect m_bounds;
434 gfx::Region m_updateRegion; // Region to be redrawed.
435 WidgetsList m_children; // Sub-widgets
436 Widget* m_parent; // Who is the parent?
437 int m_parentIndex; // Location/index of this widget in the parent's Widget::m_children vector
438 gfx::Size* m_sizeHint;
439 int m_mnemonic; // Keyboard shortcut to access this widget like Alt+mnemonic
440
441 // Widget size limits
442 gfx::Size m_minSize, m_maxSize;
443
444 gfx::Border m_border; // Border separation with the parent
445 int m_childSpacing; // Separation between children
446 };
447
448 WidgetType register_widget_type();
449
450} // namespace ui
451
452#endif
453