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 ¤tPlace); 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