1 /* 2 Copyright (C) 2007 - 2018 by Mark de Wever <koraq@xs4all.nl> 3 Part of the Battle for Wesnoth Project https://www.wesnoth.org/ 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 2 of the License, or 8 (at your option) any later version. 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY. 11 12 See the COPYING file for more details. 13 */ 14 15 #pragma once 16 17 #include "gui/core/event/dispatcher.hpp" 18 #include "sdl/point.hpp" 19 #include "gui/widgets/event_executor.hpp" 20 #include "color.hpp" 21 22 #include <string> 23 24 class surface; 25 26 typedef std::map<std::string, t_string> string_map; 27 28 namespace gui2 29 { 30 31 struct builder_widget; 32 class window; 33 class grid; 34 35 namespace iteration 36 { 37 class walker_base; 38 } // namespace iteration 39 40 /** 41 * Base class for all widgets. 42 * 43 * From this abstract all other widgets should derive. It contains the minimal 44 * info needed for a real widget and some pure abstract functions which need to 45 * be implemented by classes deriving from this class. 46 */ 47 class widget : public event_executor, public event::dispatcher 48 { 49 friend class debug_layout_graph; 50 friend class window; // needed for modifying the layout_size. 51 52 53 /***** ***** ***** ***** ***** Types. ***** ***** ***** ***** *****/ 54 55 public: 56 /** Visibility settings done by the user. */ 57 enum class visibility 58 { 59 /** 60 * The user sets the widget visible, that means: 61 * * The widget is visible. 62 * * @ref find_at always 'sees' the widget (the active flag is 63 * tested later). 64 * * The widget (if active) handles events (and sends events to 65 * its children). 66 * * The widget is drawn (and sends the call to 67 * @ref populate_dirty_list to children). 68 */ 69 visible, 70 71 /** 72 * The user sets the widget hidden, that means: 73 * * The widget is invisible but keeps its size. 74 * * @ref find_at 'sees' the widget if active is @c false. 75 * * The widget doesn't handle events (and doesn't send events to 76 * its children). 77 * * The widget doesn't add itself @ref window::dirty_list_ when 78 * @ref populate_dirty_list is called (nor does it send the 79 * request to its children). 80 */ 81 hidden, 82 83 /** 84 * The user set the widget invisible, that means: 85 * * The widget is invisible and its grid cell has size 0,0. 86 * * @ref find_at never 'sees' the widget. 87 * * The widget doesn't handle events (and doesn't send events to 88 * its children). 89 * * The widget doesn't add itself @ref window::dirty_list_ when 90 * @ref populate_dirty_list is called (nor does it send the 91 * request to its children). 92 */ 93 invisible 94 }; 95 96 /** 97 * Visibility set by the engine. 98 * 99 * This state only will be used if @ref visible_ is @ref visibility::visible 100 * depending on this state the widget might not be visible after all. 101 */ 102 enum class redraw_action 103 { 104 /** 105 * The widget is fully visible. 106 * 107 * The widget should be drawn if @ref dirty_ is @c true. The entire 108 * widget's rectangle should be redrawn. 109 */ 110 full, 111 112 /** 113 * The widget is partly visible. 114 * 115 * The should be drawn if @ref dirty_ is @c true. The rectangle to 116 * redraw in determined by @ref clipping_rectangle_ 117 */ 118 partly, 119 120 /** 121 * The widget is not visible. 122 * 123 * The widget should not be drawn if @ref dirty_ is @c true. 124 */ 125 none 126 }; 127 128 129 /***** ***** ***** Constructor and destructor. ***** ***** *****/ 130 131 public: 132 widget(const widget&) = delete; 133 widget& operator=(const widget&) = delete; 134 135 /** @deprecated use the second overload. */ 136 widget(); 137 138 /** 139 * Constructor. 140 * 141 * @param builder The builder object with the settings for the 142 * object. 143 */ 144 explicit widget(const builder_widget& builder); 145 146 virtual ~widget() override; 147 148 149 /***** ***** ***** ***** ID functions. ***** ***** ***** *****/ 150 151 public: 152 /*** *** *** *** *** *** Setters and getters. *** *** *** *** *** ***/ 153 154 void set_id(const std::string& id); 155 const std::string& id() const; 156 157 /*** *** *** *** *** *** *** *** Members. *** *** *** *** *** *** *** ***/ 158 159 private: 160 /** 161 * The id is the unique name of the widget in a certain context. 162 * 163 * This is needed for certain widgets so the engine knows which widget is 164 * which. E.g. it knows which button is pressed and thus which engine action 165 * is connected to the button. This doesn't mean that the id is unique in a 166 * window, e.g. a listbox can have the same id for every row. 167 */ 168 std::string id_; 169 170 171 /***** ***** ***** ***** Parent functions ***** ***** ***** *****/ 172 173 public: 174 /** 175 * Get the parent window. 176 * 177 * @returns Pointer to parent window. 178 * @retval nullptr No parent window found. 179 */ 180 window* get_window(); 181 182 /** The constant version of @ref get_window. */ 183 const window* get_window() const; 184 185 /** 186 * Get the parent grid. 187 * 188 * @returns Pointer to parent grid. 189 * @retval nullptr No parent grid found. 190 */ 191 grid* get_parent_grid(); 192 193 /*** *** *** *** *** *** Setters and getters. *** *** *** *** *** ***/ 194 195 void set_parent(widget* parent); 196 widget* parent(); 197 198 /*** *** *** *** *** *** *** *** Members. *** *** *** *** *** *** *** ***/ 199 200 private: 201 /** 202 * The parent widget. 203 * 204 * If the widget has a parent it contains a pointer to the parent, else it 205 * is set to @c nullptr. 206 */ 207 widget* parent_; 208 209 210 /***** ***** ***** ***** Size and layout functions. ***** ***** ***** *****/ 211 212 public: 213 /** 214 * How the layout engine works. 215 * 216 * Every widget has a member @ref layout_size_ which holds the best size in 217 * the current layout phase. When the windows starts the layout phase it 218 * calls @ref layout_initialize which resets this value. 219 * 220 * Every widget has two function to get the best size. @ref get_best_size 221 * tests whether layout_size_ is set and if so returns that value otherwise 222 * it calls @ref calculate_best_size so the size can be updated. 223 * 224 * During the layout phase some functions can modify layout_size_ so the 225 * next call to @ref get_best_size returns the currently best size. This 226 * means that after the layout phase @ref get_best_size still returns this 227 * value. 228 */ 229 230 /** 231 * Initialises the layout phase. 232 * 233 * Clears the initial best size for the widgets. 234 * 235 * See @ref layout_algorithm for more information. 236 * 237 * @param full_initialization For widgets with scrollbars it hides them 238 * unless the mode is 239 * @ref scrollbar_mode::ALWAYS_VISIBLE. For 240 * other widgets this flag is a @em NOP. 241 */ 242 virtual void layout_initialize(const bool full_initialization); 243 244 /** 245 * Tries to reduce the width of a widget. 246 * 247 * This function tries to do it 'friendly' and only use scrollbars or 248 * tries to wrap the widget. 249 * 250 * See @ref layout_algorithm for more information. 251 * 252 * @param maximum_width The wanted maximum width. 253 */ 254 virtual void request_reduce_width(const unsigned maximum_width) = 0; 255 256 /** 257 * Tries to reduce the width of a widget. 258 * 259 * This function does it more aggressively and should only be used when 260 * using scrollbars and wrapping failed. 261 * 262 * @todo Make pure virtual. 263 * 264 * See @ref layout_algorithm for more information. 265 * 266 * @param maximum_width The wanted maximum width. 267 */ 268 virtual void demand_reduce_width(const unsigned maximum_width); 269 270 /** 271 * Tries to reduce the height of a widget. 272 * 273 * This function tries to do it 'friendly' and only use scrollbars. 274 * 275 * @todo Make pure virtual. 276 * 277 * See @ref layout_algorithm for more information. 278 * 279 * @param maximum_height The wanted maximum height. 280 */ 281 virtual void request_reduce_height(const unsigned maximum_height); 282 283 /** 284 * Tries to reduce the height of a widget. 285 * 286 * This function does it more aggressively and should only be used when 287 * using scrollbars failed. 288 * 289 * @todo Make pure virtual. 290 * 291 * See @ref layout_algorithm for more information. 292 * 293 * @param maximum_height The wanted maximum height. 294 */ 295 virtual void demand_reduce_height(const unsigned maximum_height); 296 297 /** 298 * Gets the best size for the widget. 299 * 300 * During the layout phase a best size will be determined, several stages 301 * might change the best size. This function will return the currently best 302 * size as determined during the layout phase. 303 * 304 * @returns The best size for the widget. 305 * @retval 0,0 The best size is 0,0. 306 */ 307 point get_best_size() const; 308 309 private: 310 /** 311 * Calculates the best size. 312 * 313 * This function calculates the best size and ignores the current values in 314 * the layout phase. Note containers can call the @ref get_best_size() of 315 * their children since it is meant to update itself. 316 * 317 * @returns The best size for the widget. 318 * @retval 0,0 The best size is 0,0. 319 */ 320 virtual point calculate_best_size() const = 0; 321 322 public: 323 /** 324 * Whether the mouse move/click event go 'through' this widget. 325 */ can_mouse_focus() const326 virtual bool can_mouse_focus() const { return true; } 327 /** 328 * Can the widget wrap. 329 * 330 * When a widget can wrap it can reduce its width by increasing its 331 * height. When a layout is too wide it should first try to wrap and if 332 * that fails it should check the vertical scrollbar status. After wrapping 333 * the height might (probably will) change so the layout engine needs to 334 * recalculate the height after wrapping. 335 */ 336 virtual bool can_wrap() const; 337 338 /** 339 * Sets the origin of the widget. 340 * 341 * This function can be used to move the widget without dirtying it. The 342 * location is an absolute position, if a relative more is required use 343 * @ref move. 344 * 345 * 346 * @param origin The new origin. 347 */ 348 virtual void set_origin(const point& origin); 349 350 /** 351 * Sets the size of the widget. 352 * 353 * This version is meant to resize a widget, since the origin isn't 354 * modified. This can be used if a widget needs to change its size and the 355 * layout will be fixed later. 356 * 357 * @param size The size of the widget. 358 */ 359 virtual void set_size(const point& size); 360 361 /** 362 * Places the widget. 363 * 364 * This function is normally called by a layout function to do the 365 * placement of a widget. 366 * 367 * @param origin The position of top left of the widget. 368 * @param size The size of the widget. 369 */ 370 virtual void place(const point& origin, const point& size); 371 372 /** 373 * Moves a widget. 374 * 375 * This function can be used to move the widget without dirtying it. 376 * 377 * @todo Implement the function to all derived classes. 378 * 379 * @param x_offset The amount of pixels to move the widget in 380 * the x-direction. 381 * @param y_offset The amount of pixels to move the widget in 382 * the y-direction. 383 */ 384 virtual void move(const int x_offset, const int y_offset); 385 386 /** 387 * Sets the horizontal alignment of the widget within its parent grid. 388 * 389 * @param alignment The new alignment. 390 */ 391 virtual void set_horizontal_alignment(const std::string& alignment); 392 393 /** 394 * Sets the horizontal alignment of the widget within its parent grid. 395 * 396 * @param alignment The new alignment. 397 */ 398 virtual void set_vertical_alignment(const std::string& alignment); 399 400 /** 401 * Allows a widget to update its children. 402 * 403 * Before the window is populating the dirty list the widgets can update 404 * their content, which allows delayed initialization. This delayed 405 * initialization is only allowed if the widget resizes itself, not when 406 * being placed. 407 */ 408 virtual void layout_children(); 409 410 /** 411 * Returns the screen origin of the widget. 412 * 413 * @returns The origin of the widget. 414 */ 415 point get_origin() const; 416 417 /** 418 * Returns the size of the widget. 419 * 420 * @returns The size of the widget. 421 */ 422 point get_size() const; 423 424 /** 425 * Gets the bounding rectangle of the widget on the screen. 426 * 427 * @returns The bounding rectangle of the widget. 428 */ 429 SDL_Rect get_rectangle() const; 430 431 /*** *** *** *** *** *** Setters and getters. *** *** *** *** *** ***/ 432 433 int get_x() const; 434 435 int get_y() const; 436 437 unsigned get_width() const; 438 439 unsigned get_height() const; 440 441 protected: 442 void set_layout_size(const point& size); 443 const point& layout_size() const; 444 445 /** 446 * Throws away @ref layout_size_. 447 * 448 * Use with care: this function does not recurse to child widgets. 449 * 450 * See @ref layout_algorithm for more information. 451 */ clear_layout_size()452 void clear_layout_size() { set_layout_size(point()); } 453 454 public: 455 void set_linked_group(const std::string& linked_group); 456 457 /*** *** *** *** *** *** *** *** Members. *** *** *** *** *** *** *** ***/ 458 459 private: 460 /** The x-coordinate of the widget on the screen. */ 461 int x_; 462 463 /** The y-coordinate of the widget on the screen. */ 464 int y_; 465 466 /** The width of the widget. */ 467 unsigned width_; 468 469 /** The height of the widget. */ 470 unsigned height_; 471 472 /** 473 * The best size for the widget. 474 * 475 * When 0,0 the real best size is returned, but in the layout phase a 476 * wrapping or a scrollbar might change the best size for that widget. 477 * This variable holds that best value. 478 * 479 * If the widget size hasn't been changed from the default that 480 * calculate_best_size() returns, layout_size_ is (0,0). 481 */ 482 point layout_size_; 483 484 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS 485 486 /** 487 * Debug helper to store last value of get_best_size(). 488 * 489 * We're mutable so calls can stay const and this is disabled in 490 * production code. 491 */ 492 mutable point last_best_size_; 493 494 #endif 495 496 /** 497 * The linked group the widget belongs to. 498 * 499 * @todo For now the linked group is initialized when the layout of the 500 * widget is initialized. The best time to set it would be upon adding the 501 * widget in the window. Need to look whether it is possible in a clean way. 502 * Maybe a signal just prior to showing a window where the widget can do 503 * some of it's on things, would also be nice for widgets that need a 504 * finalizer function. 505 */ 506 std::string linked_group_; 507 508 509 /***** ***** ***** ***** Drawing functions. ***** ***** ***** *****/ 510 511 public: 512 /** 513 * Calculates the blitting rectangle of the widget. 514 * 515 * The blitting rectangle is the entire widget rectangle, but offsetted for 516 * drawing position. 517 * 518 * @param x_offset The offset in the x-direction when drawn. 519 * @param y_offset The offset in the y-direction when drawn. 520 * 521 * @returns The drawing rectangle. 522 */ 523 SDL_Rect calculate_blitting_rectangle(const int x_offset, 524 const int y_offset); 525 526 /** 527 * Calculates the clipping rectangle of the widget. 528 * 529 * The clipping rectangle is used then the @ref redraw_action_ is 530 * @ref redraw_action::partly. Since the drawing can be offsetted it also 531 * needs offset parameters. 532 * 533 * @param x_offset The offset in the x-direction when drawn. 534 * @param y_offset The offset in the y-direction when drawn. 535 * 536 * @returns The clipping rectangle. 537 */ 538 SDL_Rect calculate_clipping_rectangle(const int x_offset, 539 const int y_offset); 540 541 /** 542 * Draws the background of a widget. 543 * 544 * Derived should override @ref impl_draw_background instead of changing 545 * this function. 546 * 547 * @param frame_buffer The surface to draw upon. 548 * @param x_offset The offset in the x-direction in the 549 * @p frame_buffer to draw. 550 * @param y_offset The offset in the y-direction in the 551 * @p frame_buffer to draw. 552 */ 553 void draw_background(surface& frame_buffer, int x_offset, int y_offset); 554 555 /** 556 * Draws the children of a widget. 557 * 558 * Containers should draw their children when they get this request. 559 * 560 * Derived should override @ref impl_draw_children instead of changing 561 * this function. 562 * 563 * @param frame_buffer The surface to draw upon. 564 * @param x_offset The offset in the x-direction in the 565 * @p frame_buffer to draw. 566 * @param y_offset The offset in the y-direction in the 567 * @p frame_buffer to draw. 568 */ 569 void draw_children(surface& frame_buffer, int x_offset, int y_offset); 570 571 /** 572 * Draws the foreground of the widget. 573 * 574 * Some widgets e.g. panel and window have a back and foreground layer this 575 * function requests the drawing of the foreground. 576 * 577 * Derived should override @ref impl_draw_foreground instead of changing 578 * this function. 579 * 580 * @param frame_buffer The surface to draw upon. 581 * @param x_offset The offset in the x-direction in the 582 * @p frame_buffer to draw. 583 * @param y_offset The offset in the y-direction in the 584 * @p frame_buffer to draw. 585 */ 586 void draw_foreground(surface& frame_buffer, int x_offset, int y_offset); 587 588 private: 589 /** See @ref draw_background. */ impl_draw_background(surface &)590 virtual void impl_draw_background(surface& /*frame_buffer*/) 591 { 592 } impl_draw_background(surface &,int,int)593 virtual void impl_draw_background(surface& /*frame_buffer*/ 594 , 595 int /*x_offset*/ 596 , 597 int /*y_offset*/) 598 { 599 } 600 601 /** See @ref draw_children. */ impl_draw_children(surface &,int,int)602 virtual void impl_draw_children(surface& /*frame_buffer*/ 603 , 604 int /*x_offset*/ 605 , 606 int /*y_offset*/) 607 { 608 } 609 610 /** See @ref draw_foreground. */ impl_draw_foreground(surface &,int,int)611 virtual void impl_draw_foreground(surface& /*frame_buffer*/ 612 , 613 int /*x_offset*/ 614 , 615 int /*y_offset*/) 616 { 617 } 618 619 public: 620 /** 621 * Adds a widget to the dirty list if it is dirty. 622 * 623 * See @ref window::dirty_list_ for more information regarding the dirty 624 * list. 625 * 626 * If the widget is not dirty and has children it should add itself to the 627 * call_stack and call child_populate_dirty_list with the new call_stack. 628 * 629 * @param caller The parent window, if dirty it should 630 * register itself to this window. 631 * @param call_stack The call-stack of widgets traversed to reach 632 * this function. 633 */ 634 void populate_dirty_list(window& caller, 635 std::vector<widget*>& call_stack); 636 637 private: 638 /** 639 * Tries to add all children of a container to the dirty list. 640 * 641 * @note The function is private since everybody should call 642 * @ref populate_dirty_list instead. 643 * 644 * @param caller The parent window, if dirty it should 645 * register itself to this window. 646 * @param call_stack The call-stack of widgets traversed to reach 647 * this function. 648 */ 649 virtual void 650 child_populate_dirty_list(window& caller, 651 const std::vector<widget*>& call_stack); 652 653 public: 654 /** 655 * Gets the dirty rectangle of the widget. 656 * 657 * Depending on the @ref redraw_action_ it returns the rectangle this 658 * widget dirties while redrawing. 659 * 660 * @returns The dirty rectangle. 661 */ 662 SDL_Rect get_dirty_rectangle() const; 663 664 /** 665 * Sets the visible rectangle for a widget. 666 * 667 * This function sets the @ref redraw_action_ and the 668 * @ref clipping_rectangle_. 669 * 670 * @param rectangle The visible rectangle in screen coordinates. 671 */ 672 virtual void set_visible_rectangle(const SDL_Rect& rectangle); 673 674 /*** *** *** *** *** *** Setters and getters. *** *** *** *** *** ***/ 675 676 void set_is_dirty(const bool is_dirty); 677 bool get_is_dirty() const; 678 679 void set_visible(const visibility visible); 680 visibility get_visible() const; 681 682 redraw_action get_drawing_action() const; 683 684 void set_debug_border_mode(const unsigned debug_border_mode); 685 686 void set_debug_border_color(const color_t debug_border_color); 687 688 /*** *** *** *** *** *** *** *** Members. *** *** *** *** *** *** *** ***/ 689 690 private: 691 /** 692 * Is the widget dirty? 693 * 694 * When a widget is dirty it needs to be redrawn at the next drawing cycle. 695 * 696 * The top-level window will use @ref populate_dirty_list and 697 * @ref child_populate_dirty_list to find al dirty widgets, so the widget 698 * doesn't need to inform its parent regarding it being marked dirty. 699 */ 700 bool is_dirty_; 701 702 /** Field for the status of the visibility. */ 703 visibility visible_; 704 705 /** Field for the action to do on a drawing request. */ 706 redraw_action redraw_action_; 707 708 /** The clipping rectangle if a widget is partly visible. */ 709 SDL_Rect clipping_rectangle_; 710 711 /** 712 * Mode for drawing the debug border. 713 * 714 * The debug border is a helper border to determine where a widget is 715 * placed. It is only intended for debugging purposes. 716 * 717 * Possible values: 718 * - 0 no border 719 * - 1 single pixel border 720 * - 2 flood-filled rectangle 721 */ 722 unsigned debug_border_mode_; 723 724 /** The color for the debug border. */ 725 color_t debug_border_color_; 726 727 void draw_debug_border(); 728 void draw_debug_border(int x_offset, int y_offset); 729 730 /***** ***** ***** ***** Query functions ***** ***** ***** *****/ 731 732 public: 733 /** 734 * Returns the widget at the wanted coordinates. 735 * 736 * @param coordinate The coordinate which should be inside the 737 * widget. 738 * @param must_be_active The widget should be active, not all widgets 739 * have an active flag, those who don't ignore 740 * flag. 741 * 742 * @returns The widget with the id. 743 * @retval nullptr No widget at the wanted coordinate found (or 744 * not active if must_be_active was set). 745 */ 746 virtual widget* find_at(const point& coordinate, 747 const bool must_be_active); 748 749 /** The constant version of @ref find_at. */ 750 virtual const widget* find_at(const point& coordinate, 751 const bool must_be_active) const; 752 753 /** 754 * Returns @em a widget with the wanted id. 755 * 756 * @note Since the id might not be unique inside a container there is no 757 * guarantee which widget is returned. 758 * 759 * @param id The id of the widget to find. 760 * @param must_be_active The widget should be active, not all widgets 761 * have an active flag, those who don't ignore 762 * flag. 763 * 764 * @returns The widget with the id. 765 * @retval nullptr No widget with the id found (or not active if 766 * must_be_active was set). 767 */ 768 virtual widget* find(const std::string& id, const bool must_be_active); 769 770 /** The constant version of @ref find. */ 771 virtual const widget* find(const std::string& id, 772 const bool must_be_active) const; 773 774 /** 775 * Does the widget contain the widget. 776 * 777 * Widgets can be containers which have more widgets inside them, this 778 * function will traverse in those child widgets and tries to find the 779 * wanted widget. 780 * 781 * @param widget Pointer to the widget to find. 782 * 783 * @returns Whether or not the @p widget was found. 784 */ 785 virtual bool has_widget(const widget& widget) const; 786 787 private: 788 /** See @ref event::dispatcher::is_at. */ 789 virtual bool is_at(const point& coordinate) const override; 790 791 /** 792 * Is the coordinate inside our area. 793 * 794 * Helper for find_at so also looks at our visibility. 795 * 796 * @param coordinate The coordinate which should be inside the 797 * widget. 798 * @param must_be_active The widget should be active, not all widgets 799 * have an active flag, those who don't ignore 800 * flag. 801 * 802 * @returns Status. 803 */ 804 bool is_at(const point& coordinate, const bool must_be_active) const; 805 806 /** 807 * Is the widget and every single one of its parents visible? 808 * 809 * @param widget Widget where to start the check. 810 * @param must_be_active The widget should be active, not all widgets 811 * have an active flag, those who don't ignore 812 * flag. 813 * 814 * @returns Status. 815 */ 816 bool recursive_is_visible(const widget* widget, const bool must_be_active) const; 817 818 /***** ***** ***** ***** Miscellaneous ***** ***** ****** *****/ 819 820 public: 821 /** Does the widget disable easy close? */ 822 virtual bool disable_click_dismiss() const = 0; 823 824 /** Creates a new walker object on the heap. */ 825 virtual iteration::walker_base* create_walker() = 0; 826 }; 827 828 } // namespace gui2 829