1 /** @file guiwidget.h  Base class for graphical widgets.
2  *
3  * @authors Copyright (c) 2013-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  *
5  * @par License
6  * LGPL: http://www.gnu.org/licenses/lgpl.html
7  *
8  * <small>This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or (at your
11  * option) any later version. This program is distributed in the hope that it
12  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
14  * General Public License for more details. You should have received a copy of
15  * the GNU Lesser General Public License along with this program; if not, see:
16  * http://www.gnu.org/licenses</small>
17  */
18 
19 #ifndef LIBAPPFW_GUIWIDGET_H
20 #define LIBAPPFW_GUIWIDGET_H
21 
22 #include <de/Widget>
23 #include <de/RuleRectangle>
24 #include <de/MouseEvent>
25 #include <de/GLBuffer>
26 #include <de/Painter>
27 #include <QObject>
28 
29 #include "../Style"
30 #include "../ui/defs.h"
31 #include "../ui/Margins"
32 #include "../framework/guiwidgetprivate.h"
33 
34 namespace de {
35 
36 class GuiRootWidget;
37 class BlurWidget;
38 class PopupWidget;
39 
40 /**
41  * Base class for graphical widgets.
42  *
43  * Each GuiWidget has one RuleRectangle that defines the widget's position in
44  * the view. However, all widgets are allowed to draw outside this rectangle
45  * and react to events occurring outside it. In essence, all widgets thus cover
46  * the entire view area and can be thought as a (hierarchical) stack.
47  *
48  * GuiWidget is the base class for all widgets of a GUI application. However, a
49  * GuiWidget does not necessarily need to have a visible portion on the screen:
50  * its entire purpose may be to handle events, for example.
51  *
52  * The common features GuiWidget offers to all widgets are:
53  *
54  * - Automatically saving and restoring persistent state. Classes that implement
55  *   IPersistent will automatically be saved and restored when the widget is
56  *   (de)initialized.
57  *
58  * - Background geometry builder. All widgets may use this to build geometry for
59  *   the background of the widget. However, widgets are also allowed to fully
60  *   generate all of their geometry from scratch.
61  *
62  * - Access to the UI Style.
63  *
64  * - GuiWidget can be told which font and text color to use using a style
65  *   definition identifier (e.g., "editor.hint"). These style elements are then
66  *   conveniently accessible using methods of GuiWidget.
67  *
68  * - Opacity property. Opacities respect the hierarchical organization of
69  *   widgets: GuiWidget::visibleOpacity() returns the opacity of a particular
70  *   widget where all the parent widgets' opacities are factored in.
71  *
72  * - Hit testing: checking if a view space point should be considered to be
73  *   inside the widget. The default implementation simply checks if the point is
74  *   inside the widget's rectangle. Derived classes may override this to adapt
75  *   hit testing to their particular visual shape.
76  *
77  * - Logic for handling more complicated interactions such as a mouse pointer
78  *   click (press then release inside or outside), and passing received events
79  *   to separately registered event handler objects.
80  *
81  * QObject is a base class for the signals and slots capabilities.
82  *
83  * @note Always use GuiWidget::destroy() to delete any GUI widget. It will
84  * ensure that the widget is properly deinitialized before destruction.
85  *
86  * @ingroup appfw
87  */
88 class LIBAPPFW_PUBLIC GuiWidget : public QObject, public Widget
89 {
90     Q_OBJECT
91 
92 public:
93     /**
94      * Properties of the widget's background's apperance.
95      * GuiWidget::glMakeGeometry() uses this to construct the background
96      * geometry of the widget.
97      *
98      * @todo Refactor: it should be possible to apply any combination of these
99      * in a single widget; use a dynamic array of effects. Base it on ProceduralImage.
100      */
101     struct Background {
102         enum Type {
103             None,               ///< No background, no solid fill.
104             GradientFrame,      ///< Bold round corners, square background.
105             GradientFrameWithRoundedFill, ///< Bold round corners with solid rounded background.
106             GradientFrameWithThinBorder,  ///< Bold round corners, black thin secondary border.
107             BorderGlow,         ///< Border glow with specified color/thickness.
108             Blurred,            ///< Blurs whatever is showing behind the widget.
109             BlurredWithBorderGlow,
110             BlurredWithSolidFill,
111             SharedBlur,         ///< Use the blur background from a BlurWidget.
112             SharedBlurWithBorderGlow,
113             Rounded
114         };
115         Vector4f solidFill;     ///< Always applied if opacity > 0.
116         Type type;
117         Vector4f color;         ///< Secondary color.
118         float thickness;        ///< Frame border thickenss.
119         GuiWidget *blur;
120 
BackgroundBackground121         Background()
122             : type(None), thickness(0), blur(0) {}
123 
BackgroundBackground124         Background(GuiWidget &blurred, Vector4f const &blurColor)
125             : solidFill(blurColor), type(SharedBlur), thickness(0), blur(&blurred) {}
126 
127         Background(Vector4f const &solid, Type t = None)
solidFillBackground128             : solidFill(solid), type(t), thickness(0), blur(0) {}
129 
130         Background(Type t, Vector4f const &borderColor, float borderThickness = 0)
typeBackground131             : type(t), color(borderColor), thickness(borderThickness), blur(0) {}
132 
133         Background(Vector4f const &solid, Type t,
134                    Vector4f const &borderColor,
135                    float borderThickness = 0)
solidFillBackground136             : solidFill(solid), type(t), color(borderColor), thickness(borderThickness),
137               blur(0) {}
138 
withSolidFillBackground139         inline Background withSolidFill(Vector4f const &newSolidFill) const {
140             Background bg = *this;
141             bg.solidFill = newSolidFill;
142             return bg;
143         }
144 
withSolidFillOpacityBackground145         inline Background withSolidFillOpacity(float opacity) const {
146             Background bg = *this;
147             bg.solidFill.w = opacity;
148             return bg;
149         }
150     };
151 
152     typedef Vertex2TexRgba DefaultVertex;
153     typedef GLBufferT<DefaultVertex> DefaultVertexBuf;
154     typedef QList<GuiWidget *> Children;
155 
156     /**
157      * Handles events.
158      */
159     class IEventHandler
160     {
161     public:
~IEventHandler()162         virtual ~IEventHandler() {}
163 
164         /**
165          * Handle an event.
166          *
167          * @param widget  Widget that received the event.
168          * @param event   Event.
169          *
170          * @return @c true, if the event was eaten. @c false otherwise.
171          */
172         virtual bool handleEvent(GuiWidget &widget, Event const &event) = 0;
173     };
174 
175     enum Attribute
176     {
177         /**
178          * Enables or disables automatic state serialization for widgets
179          * derived from IPersistent. State serialization occurs when the widget
180          * is gl(De)Init'd.
181          */
182         RetainStatePersistently = 0x1,
183 
184         AnimateOpacityWhenEnabledOrDisabled = 0x2,
185 
186         /**
187          * Widget will not automatically change opacity depending on state
188          * (e.g., when disabled).
189          */
190         ManualOpacity = 0x10,
191 
192         /**
193          * Widget will automatically change opacity depending on state. This overrides
194          * ManualOpacity has family behavior.
195          */
196         AutomaticOpacity = 0x200,
197 
198         /**
199          * Prevents the drawing of the widget contents even if it visible. The
200          * texture containing the blurred background is updated regardless.
201          */
202         DontDrawContent = 0x4,
203 
204         /**
205          * Visible opacity determined solely by the widget itself, not affected
206          * by ancestors.
207          */
208         IndependentOpacity = 0x8,
209 
210         /**
211          * When focused, don't show the normal focus indicator. The assumption
212          * is that the widget will indicate focused state on its own.
213          */
214         FocusHidden = 0x20,
215 
216         /**
217          * All received mouse events are eaten. Derived classes may handle the
218          * events beforehand, though.
219          */
220         EatAllMouseEvents = 0x40,
221 
222         /**
223          * When the widget is in focus, this will prevent cycling focus away with Tab.
224          */
225         FocusCyclingDisabled = 0x80,
226 
227         /**
228          * When the widget is in focus, this will prevent moving the focus with
229          * arrow keys.
230          */
231         FocusMoveWithArrowKeysDisabled = 0x100,
232 
233         /// Set of attributes that apply to all descendants.
234         FamilyAttributes = ManualOpacity | AnimateOpacityWhenEnabledOrDisabled,
235 
236         /// Default set of attributes.
237         DefaultAttributes = RetainStatePersistently | AnimateOpacityWhenEnabledOrDisabled
238     };
239     Q_DECLARE_FLAGS(Attributes, Attribute)
240 
241     enum ColorTheme { Normal, Inverted };
242 
243 public:
244     GuiWidget(String const &name = String());
245 
246     /**
247      * Deletes a widget. The widget is first deinitialized.
248      *
249      * @param widget  Widget to destroy.
250      */
251     static void destroy(GuiWidget *widget);
252 
253     /**
254      * Deletes a widget at a later point in time. However, the widget is
255      * immediately deinitialized.
256      *
257      * @param widget  Widget to deinitialize now and destroy layer.
258      */
259     static void destroyLater(GuiWidget *widget);
260 
261     GuiRootWidget &root() const;
262     Children childWidgets() const;
263     GuiWidget *parentGuiWidget() const;
264     Style const &style() const;
265 
266     /**
267      * Shortcut for accessing individual rules in the active UI style.
268      * @param path  Identifier of the rule.
269      * @return Rule from the Style.
270      */
271     Rule const &rule(DotPath const &path) const;
272 
273     /**
274      * Returns the rule rectangle that defines the placement of the widget on
275      * the target canvas.
276      */
277     RuleRectangle &rule();
278 
279     Rectanglei contentRect() const;
280 
281     /**
282      * Returns the rule rectangle that defines the placement of the widget on
283      * the target canvas.
284      */
285     RuleRectangle const &rule() const;
286 
287     /**
288      * Calculates an estimate of the height of the widget. Widgets used in virtualized
289      * lists should implement this and return an accurate value.
290      *
291      * By default returns the current height of the widget rectangle.
292      */
293     virtual float estimatedHeight() const;
294 
295     ui::Margins &margins();
296     ui::Margins const &margins() const;
297 
298     Rectanglef normalizedRect() const;
299     Rectanglef normalizedRect(Rectanglei const &viewSpaceRect) const;
300 
301     /**
302      * Normalized content rectangle. Same as normalizedRect() except margins
303      * are applied to all sides.
304      */
305     Rectanglef normalizedContentRect() const;
306 
307     void setFont(DotPath const &id);
308     virtual void setTextColor(DotPath const &id);
309     void set(Background const &bg);
310     void setSaturation(float saturation);
311 
312     Font const &font() const;
313     DotPath const &fontId() const;
314     DotPath const &textColorId() const;
315     ColorBank::Color textColor() const;
316     ColorBank::Colorf textColorf() const;
317 
318     /**
319      * Determines whether the contents of the widget are supposed to be clipped
320      * to its boundaries. The Widget::ContentClipping behavior flag is used for
321      * storing this information.
322      */
323     bool isClipped() const;
324 
325     Background const &background() const;
326 
327     /**
328      * Sets the opacity of the widget. Child widgets' opacity is also affected.
329      *
330      * @param opacity     Opacity.
331      * @param span        Animation transition span.
332      * @param startDelay  Starting delay.
333      */
334     void setOpacity(float opacity, TimeSpan span = 0, TimeSpan startDelay = 0);
335 
336     /**
337      * Determines the widget's opacity animation.
338      */
339     Animation opacity() const;
340 
341     /**
342      * Determines the widget's opacity, factoring in all ancestor opacities.
343      */
344     float visibleOpacity() const;
345 
346     /**
347      * Sets an object that will be offered events received by this widget. The
348      * handler may eat the event. Any number of event handlers can be added;
349      * they are called in the order of addition.
350      *
351      * @param handler  Event handler. Ownership given to GuiWidget.
352      */
353     void addEventHandler(IEventHandler *handler);
354 
355     void removeEventHandler(IEventHandler *handler);
356 
357     /**
358      * Sets, unsets, or replaces one or more widget attributes.
359      *
360      * @param attr  Attribute(s) to modify.
361      * @param op    Flag operation.
362      */
363     void setAttribute(Attributes const &attr, FlagOpArg op = SetFlags);
364 
365     /**
366      * Returns this widget's attributes.
367      */
368     Attributes attributes() const;
369 
370     /**
371      * Returns the attributes that apply to this widget a
372      * @return
373      */
374     Attributes familyAttributes() const;
375 
376     /**
377      * Save the state of the widget and all its children (those who support state
378      * serialization).
379      */
380     void saveState();
381 
382     /**
383      * Restore the state of the widget and all its children (those who support state
384      * serialization).
385      */
386     void restoreState();
387 
388     // Events.
389     void initialize() override;
390     void deinitialize() override;
391     void viewResized() override;
392     void update() override;
393     void draw() override final;
394     void preDrawChildren() override;
395     void postDrawChildren() override;
396     bool handleEvent(Event const &event) override;
397 
398     /**
399      * Determines if the widget occupies on-screen position @a pos.
400      *
401      * @param pos  Coordinates.
402      *
403      * @return @c true, if hit.
404      */
405     virtual bool hitTest(Vector2i const &pos) const;
406 
407     bool hitTest(Event const &event) const;
408 
409     /**
410      * Checks if the position is on any of the children of this widget.
411      *
412      * @param pos  Coordinates.
413      *
414      * @return  The child that occupied the position in the view.
415      */
416     GuiWidget const *treeHitTest(Vector2i const &pos) const;
417 
418     /**
419      * Returns the rule rectangle used for hit testing. Defaults to a rectangle
420      * equivalent to GuiWidget::rule(). Modify the hit test rule to allow
421      * widgets to be hittable outside their default boundaries.
422      *
423      * @return Hit test rule.
424      */
425     RuleRectangle &hitRule();
426 
427     RuleRectangle const &hitRule() const;
428 
429     enum MouseClickStatus {
430         MouseClickUnrelated, ///< Event was not related to mouse clicks.
431         MouseClickStarted,
432         MouseClickFinished,
433         MouseClickAborted
434     };
435 
436     MouseClickStatus handleMouseClick(Event const &event,
437                                       MouseEvent::Button button = MouseEvent::Left);
438 
439     /**
440      * Requests the widget to refresh its geometry, if it has any static
441      * geometry. Normally this does not need to be called. It is provided
442      * mostly as a way for subclasses to ensure that geometry is up to date
443      * when they need it.
444      *
445      * @param yes  @c true to request, @c false to cancel the request.
446      */
447     void requestGeometry(bool yes = true);
448 
449     bool geometryRequested() const;
450 
451     bool isInitialized() const;
452 
453     bool canBeFocused() const override;
454 
455     GuiWidget *guiFind(String const &name);
456     GuiWidget const *guiFind(String const &name) const;
457 
458     /**
459      * Finds the popup widget that this widget resides in.
460      * @return Popup, or @c nullptr if the widget is not inside a popup.
461      */
462     PopupWidget *findParentPopup() const;
463 
464     void collectNotReadyAssets(AssetGroup &collected,
465                                CollectMode = CollectMode::OnlyVisible) override;
466 
467     /**
468      * Blocks until all assets in the widget tree are Ready.
469      */
470     void waitForAssetsReady();
471 
472 public slots:
473     /**
474      * Puts the widget in garbage to be deleted at the next recycling.
475      */
476     void guiDeleteLater();
477 
478 public:
479     /**
480      * Normalize a rectangle within a container rectangle.
481      *
482      * @param rect            Rectangle to normalize.
483      * @param containerRect   Container rectangle to normalize in.
484      *
485      * @return Normalized rectangle.
486      */
487     static Rectanglef normalizedRect(Rectanglei const &rect,
488                                      Rectanglei const &containerRect);
489 
490     static float pointsToPixels(float points);
491     static float pixelsToPoints(float pixels);
492 
pointsToPixels(int points)493     inline static int pointsToPixels(int points) {
494         return int(pointsToPixels(float(points)));
495     }
496 
pointsToPixels(duint points)497     inline static duint pointsToPixels(duint points) {
498         return duint(pointsToPixels(float(points)));
499     }
500 
501     template <typename Vector2>
pointsToPixels(Vector2 const & type)502     static Vector2 pointsToPixels(Vector2 const &type) {
503         return Vector2(typename Vector2::ValueType(pointsToPixels(type.x)),
504                        typename Vector2::ValueType(pointsToPixels(type.y)));
505     }
506 
507     template <typename Vector2>
pixelsToPoints(Vector2 const & type)508     static Vector2 pixelsToPoints(Vector2 const &type) {
509         return Vector2(typename Vector2::ValueType(pixelsToPoints(type.x)),
510                        typename Vector2::ValueType(pixelsToPoints(type.y)));
511     }
512 
513     static ColorTheme invertColorTheme(ColorTheme theme);
514 
515     /**
516      * Immediately deletes all the widgets in the garbage. This is useful to
517      * avoid double deletion in case a trashed widget's parent is deleted
518      * before recycling occurs.
519      */
520     static void recycleTrashedWidgets();
521 
522 protected:
523     /**
524      * Called by GuiWidget::update() the first time an update is being carried
525      * out. Native GL is guaranteed to be available at this time, so the widget
526      * must allocate all its GL resources during this method. Note that widgets
527      * cannot always allocate GL resources during their constructors because GL
528      * may not be initialized yet at that time.
529      */
530     virtual void glInit();
531 
532     /**
533      * Called from deinitialize(). Deinitialization must occur before the
534      * widget is destroyed. This is the appropriate place for the widget to
535      * release its GL resources. If one waits until the widget's destructor to
536      * do so, it may already have lost access to some required information
537      * (such as the root widget, or derived classes' private instances).
538      */
539     virtual void glDeinit();
540 
541     /**
542      * Called by GuiWidget when it is time to draw the widget's content. A
543      * clipping scissor is automatically set before this is called. Derived
544      * classes should override this instead of the draw() method.
545      *
546      * This is not called if the widget's visible opacity is zero or the widget
547      * is hidden.
548      */
549     virtual void drawContent();
550 
551     void drawBlurredRect(Rectanglei const &rect, Vector4f const &color, float opacity = 1.0f);
552 
553     /**
554      * Extensible mechanism for derived widgets to build their geometry. The
555      * assumptions with this are 1) the vertex format is Vertex2TexRgba, 2)
556      * the shared UI atlas is used, and 3) the background is automatically
557      * built by GuiWidget's implementation of the function.
558      *
559      * @param verts  Vertex builder.
560      */
561     virtual void glMakeGeometry(GuiVertexBuilder &verts);
562 
563     /**
564      * Checks if the widget's rectangle has changed.
565      *
566      * @param currentPlace  The widget's current placement is returned here.
567      *
568      * @return @c true, if the place of the widget has changed since the
569      * last call to hasChangedPlace(); otherwise, @c false.
570      */
571     bool hasChangedPlace(Rectanglei &currentPlace);
572 
573     /**
574      * Determines whether update() has been called at least once on the widget.
575      * Before this the widget has not been seen on screen, so it can be manipulated
576      * without visible artifacts.
577      *
578      * @return @c true, if the wdiget has been updated.
579      */
580     bool hasBeenUpdated() const;
581 
582     /**
583      * Called during GuiWidget::update() whenever the style of the widget has
584      * been marked as changed.
585      */
586     virtual void updateStyle();
587 
588     /**
589      * Returns the opacity animation of the widget.
590      */
591     Animation &opacityAnimation();
592 
593 private:
594     DENG2_PRIVATE(d)
595 };
596 
597 Q_DECLARE_OPERATORS_FOR_FLAGS(GuiWidget::Attributes)
598 
599 template <typename WidgetType>
600 struct GuiWidgetDeleter {
operatorGuiWidgetDeleter601     void operator () (WidgetType *w) {
602         GuiWidget::destroy(w);
603     }
604 };
605 
606 typedef GuiWidget::Children GuiWidgetList;
607 
608 template <typename WidgetType>
609 class UniqueWidgetPtr : public std::unique_ptr<WidgetType, GuiWidgetDeleter<WidgetType>> {
610 public:
611     UniqueWidgetPtr(WidgetType *w = nullptr)
612         : std::unique_ptr<WidgetType, GuiWidgetDeleter<WidgetType>>(w) {}
613 };
614 
615 } // namespace de
616 
617 #endif // LIBAPPFW_GUIWIDGET_H
618