1 /* 2 Copyright (C) 2008 - 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/notifiee.hpp" 18 #include "gui/widgets/container_base.hpp" 19 #include "gui/widgets/scrollbar.hpp" 20 21 namespace gui2 22 { 23 class spacer; 24 25 namespace implementation 26 { 27 struct builder_scroll_label; 28 struct builder_scrollbar_panel; 29 struct builder_styled_widget; 30 } 31 32 /** 33 * Base class for creating containers with one or two scrollbar(s). 34 * 35 * For now users can't instantiate this class directly and needs to use small 36 * wrapper classes. Maybe in the future users can use the class directly. 37 * 38 * @todo events are not yet send to the content grid. 39 */ 40 class scrollbar_container : public container_base 41 { 42 friend class debug_layout_graph; 43 44 friend struct implementation::builder_scroll_label; 45 friend struct implementation::builder_scrollbar_panel; 46 friend class listbox; 47 friend class tree_view; 48 friend struct scrollbar_container_implementation; 49 50 public: 51 explicit scrollbar_container(const implementation::builder_styled_widget& builder, const std::string& control_type); 52 ~scrollbar_container()53 virtual ~scrollbar_container() 54 { 55 } 56 57 /** The way to handle the showing or hiding of the scrollbar. */ 58 enum scrollbar_mode { 59 /** 60 * The scrollbar is always shown, whether needed or not. 61 */ 62 ALWAYS_VISIBLE, 63 64 /** 65 * The scrollbar is never shown even notwhen needed. There's also no space 66 * reserved for the scrollbar. 67 */ 68 ALWAYS_INVISIBLE, 69 70 /** 71 * The scrollbar is shown when the number of items is larger as the visible items. 72 * The space for the scrollbar is always reserved, just in case it's needed after 73 * the initial sizing (due to adding items). 74 */ 75 AUTO_VISIBLE, 76 77 /** 78 * Like AUTO_VISIBLE, but when not needed upon the initial layout phase, the bars 79 * are not shown and no space is reserved for them. (The algorithm hides them by 80 * default. 81 */ 82 AUTO_VISIBLE_FIRST_RUN, 83 }; 84 85 /***** ***** ***** ***** layout functions ***** ***** ***** *****/ 86 87 /** See @ref widget::layout_initialize. */ 88 virtual void layout_initialize(const bool full_initialization) override; 89 90 /** See @ref widget::request_reduce_height. */ 91 virtual void request_reduce_height(const unsigned maximum_height) override; 92 93 /** See @ref widget::request_reduce_width. */ 94 virtual void request_reduce_width(const unsigned maximum_width) override; 95 96 /** 97 * See @ref widget::can_wrap. 98 * 99 * @note This function is called before the object is finalized. 100 */ 101 virtual bool can_wrap() const override; 102 103 private: 104 /** See @ref widget::calculate_best_size. */ 105 virtual point calculate_best_size() const override; 106 107 public: 108 /** See @ref widget::place. */ 109 virtual void place(const point& origin, const point& size) override; 110 111 /** See @ref widget::set_origin. */ 112 virtual void set_origin(const point& origin) override; 113 114 /** See @ref widget::set_visible_rectangle. */ 115 virtual void set_visible_rectangle(const SDL_Rect& rectangle) override; 116 117 /***** ***** ***** inherited ****** *****/ 118 119 /** See @ref styled_widget::get_active. */ 120 virtual bool get_active() const override; 121 122 /** See @ref styled_widget::get_state. */ 123 virtual unsigned get_state() const override; 124 125 /** See @ref widget::find_at. */ 126 virtual widget* find_at(const point& coordinate, const bool must_be_active) override; 127 128 /** See @ref widget::find_at. */ 129 virtual const widget* find_at(const point& coordinate, const bool must_be_active) const override; 130 131 /** See @ref widget::find. */ 132 widget* find(const std::string& id, const bool must_be_active) override; 133 134 /** See @ref widget::find. */ 135 const widget* find(const std::string& id, const bool must_be_active) const override; 136 137 /** See @ref widget::disable_click_dismiss. */ 138 bool disable_click_dismiss() const override; 139 140 /***** ***** ***** setters / getters for members ***** ****** *****/ 141 142 /** @note shouldn't be called after being shown in a dialog. */ 143 void set_vertical_scrollbar_mode(const scrollbar_mode scrollbar_mode); 144 get_vertical_scrollbar_mode() const145 scrollbar_mode get_vertical_scrollbar_mode() const 146 { 147 return vertical_scrollbar_mode_; 148 } 149 150 /** @note shouldn't be called after being shown in a dialog. */ 151 void set_horizontal_scrollbar_mode(const scrollbar_mode scrollbar_mode); 152 get_horizontal_scrollbar_mode() const153 scrollbar_mode get_horizontal_scrollbar_mode() const 154 { 155 return horizontal_scrollbar_mode_; 156 } 157 content_grid()158 grid* content_grid() 159 { 160 return content_grid_.get(); 161 } 162 content_grid() const163 const grid* content_grid() const 164 { 165 return content_grid_.get(); 166 } 167 content_visible_area() const168 const SDL_Rect& content_visible_area() const 169 { 170 return content_visible_area_; 171 } 172 173 /***** ***** ***** scrollbar helpers ***** ****** *****/ 174 175 /* Returns at_end status of the vertical scrollbar. 176 * 177 */ 178 bool vertical_scrollbar_at_end(); 179 180 /** 181 * Returns current position of the vertical scrollbar. 182 * 183 */ 184 unsigned get_vertical_scrollbar_item_position() const; 185 186 /** 187 * Move the vertical scrollbar to a position. 188 * 189 * @param position The position to scroll to. 190 */ 191 void set_vertical_scrollbar_item_position(const unsigned position); 192 193 /** 194 * Returns current position of the horizontal scrollbar. 195 * 196 */ 197 unsigned get_horizontal_scrollbar_item_position() const; 198 199 /** 200 * Move the horizontal scrollbar to a position. 201 * 202 * @param position The position to scroll to. 203 */ 204 void set_horizontal_scrollbar_item_position(const unsigned position); 205 206 /** 207 * Scrolls the vertical scrollbar. 208 * 209 * @param scroll The position to scroll to. 210 */ 211 void scroll_vertical_scrollbar(const scrollbar_base::scroll_mode scroll); 212 213 /** 214 * Scrolls the horizontal scrollbar. 215 * 216 * @param scroll The position to scroll to. 217 */ 218 void scroll_horizontal_scrollbar(const scrollbar_base::scroll_mode scroll); 219 220 /** 221 * Callback when the scrollbar moves (NOTE maybe only one callback needed). 222 * Maybe also make protected or private and add a friend. 223 */ vertical_scrollbar_moved()224 void vertical_scrollbar_moved() 225 { 226 scrollbar_moved(); 227 } 228 horizontal_scrollbar_moved()229 void horizontal_scrollbar_moved() 230 { 231 scrollbar_moved(); 232 } 233 234 protected: 235 /** 236 * Shows a certain part of the content. 237 * 238 * When the part to be shown is bigger as the visible viewport the top 239 * left of the wanted rect will be the top left of the viewport. 240 * 241 * @param rect The rect which should be visible. 242 */ 243 void show_content_rect(const SDL_Rect& rect); 244 245 /* 246 * The widget contains the following three grids. 247 * 248 * * _vertical_scrollbar_grid containing at least a widget named 249 * _vertical_scrollbar 250 * 251 * * _horizontal_scrollbar_grid containing at least a widget named 252 * _horizontal_scrollbar 253 * 254 * * _content_grid a grid which holds the contents of the area. 255 * 256 * NOTE maybe just hardcode these in the finalize phase... 257 * 258 */ 259 260 /** 261 * Sets the status of the scrollbar buttons. 262 * 263 * This is needed after the scrollbar moves so the status of the buttons 264 * will be active or inactive as needed. 265 */ 266 void set_scrollbar_button_status(); 267 268 /** 269 * Notification if the content of a child needs a resize. 270 * 271 * When a resize is required the container first can try to handle it 272 * itself. If it can't honor the request the function will call @ref 273 * window::invalidate_layout(). 274 * 275 * @note Calling this function on a widget with size == (0, 0) results 276 * false but doesn't call invalidate_layout, the engine expects to be in 277 * build up phase with the layout already invalidated. 278 * 279 * @param force_sizing If the contents fit do we want to force a 280 * resize? This is needed in the MP lobby since 281 * items might not be properly placed yet. 282 * (The listboxes with the player info need it.) 283 * 284 * @returns True if the resize is handled, false 285 * otherwise. 286 */ 287 bool content_resize_request(const bool force_sizing = false); 288 289 /** 290 * Request from the content to modify the size of the container. 291 * 292 * When the wanted resize fails the function will call @ref 293 * window::invalidate_layout(). 294 * 295 * 296 * @note Calling this function on a widget with size == (0, 0) results 297 * false but doesn't call invalidate_layout, the engine expects to be in 298 * build up phase with the layout already invalidated. 299 * 300 * @note If @ref window::get_need_layout() is true the function returns 301 * false and doesn't try to fit the contents since a layout phase will be 302 * triggered anyway. 303 * 304 * @note This function might replace the @ref content_resize_request above. 305 * 306 * @param width_modification The wanted modification to the width: 307 * * negative values reduce width. 308 * * zero leave width as is. 309 * * positive values increase width. 310 * @param height_modification The wanted modification to the height: 311 * * negative values reduce height. 312 * * zero leave height as is. 313 * * positive values increase height. 314 * @param width_modification_pos 315 * The position where the additional content was 316 * inserted/removed, defaults to -1 which means 317 * 'at end' 318 * @param height_modification_pos 319 * The position where the additional content was 320 * inserted/removed, defaults to -1 which means 321 * 'at end' 322 * 323 * @returns True is wanted modification is accepted false 324 * otherwise. 325 */ 326 bool content_resize_request(const int width_modification, 327 const int height_modification, 328 const int width_modification_pos = -1, 329 const int height_modification_pos = -1); 330 331 private: 332 /** 333 * Helper for @ref content_resize_request. 334 * 335 * Handle the width modification. 336 */ 337 bool content_resize_width(const int width_modification, const int width_modification_pos); 338 339 /** 340 * Helper for @ref content_resize_request. 341 * 342 * Handle the height modification. 343 */ 344 bool content_resize_height(const int height_modification, const int width_modification_pos); 345 346 protected: 347 /***** ***** ***** ***** keyboard functions ***** ***** ***** *****/ 348 349 /** 350 * Home key pressed. 351 * 352 * @param modifier The SDL keyboard modifier when the key was 353 * pressed. 354 * @param handled If the function handles the key it should 355 * set handled to true else do not modify it. 356 * This is used in the keyboard event 357 * changing. 358 */ 359 virtual void handle_key_home(SDL_Keymod modifier, bool& handled); 360 361 /** 362 * End key pressed. 363 * 364 * @param modifier The SDL keyboard modifier when the key was 365 * pressed. 366 * @param handled If the function handles the key it should 367 * set handled to true else do not modify it. 368 * This is used in the keyboard event 369 * changing. 370 */ 371 virtual void handle_key_end(SDL_Keymod modifier, bool& handled); 372 373 /** 374 * Page up key pressed. 375 * 376 * @param modifier The SDL keyboard modifier when the key was 377 * pressed. 378 * @param handled If the function handles the key it should 379 * set handled to true else do not modify it. 380 * This is used in the keyboard event 381 * changing. 382 */ 383 virtual void handle_key_page_up(SDL_Keymod modifier, bool& handled); 384 385 /** 386 * Page down key pressed. 387 * 388 * @param modifier The SDL keyboard modifier when the key was 389 * pressed. 390 * @param handled If the function handles the key it should 391 * set handled to true else do not modify it. 392 * This is used in the keyboard event 393 * changing. 394 */ 395 virtual void handle_key_page_down(SDL_Keymod modifier, bool& handled); 396 397 /** 398 * Up arrow key pressed. 399 * 400 * @param modifier The SDL keyboard modifier when the key was 401 * pressed. 402 * @param handled If the function handles the key it should 403 * set handled to true else do not modify it. 404 * This is used in the keyboard event 405 * changing. 406 */ 407 virtual void handle_key_up_arrow(SDL_Keymod modifier, bool& handled); 408 409 /** 410 * Down arrow key pressed. 411 * 412 * @param modifier The SDL keyboard modifier when the key was 413 * pressed. 414 * @param handled If the function handles the key it should 415 * set handled to true else do not modify it. 416 * This is used in the keyboard event 417 * changing. 418 */ 419 virtual void handle_key_down_arrow(SDL_Keymod modifier, bool& handled); 420 421 /** 422 * Left arrow key pressed. 423 * 424 * @param modifier The SDL keyboard modifier when the key was 425 * pressed. 426 * @param handled If the function handles the key it should 427 * set handled to true else do not modify it. 428 * This is used in the keyboard event 429 * changing. 430 */ 431 virtual void handle_key_left_arrow(SDL_Keymod modifier, bool& handled); 432 433 /** 434 * Right arrow key pressed. 435 * 436 * @param modifier The SDL keyboard modifier when the key was 437 * pressed. 438 * @param handled If the function handles the key it should 439 * set handled to true else do not modify it. 440 * This is used in the keyboard event 441 * changing. 442 */ 443 virtual void handle_key_right_arrow(SDL_Keymod modifier, bool& handled); 444 445 private: 446 /** 447 * Possible states of the widget. 448 * 449 * Note the order of the states must be the same as defined in settings.hpp. 450 */ 451 enum state_t { 452 ENABLED, 453 DISABLED, 454 }; 455 456 /** 457 * Current state of the widget. 458 * 459 * The state of the widget determines what to render and how the widget 460 * reacts to certain 'events'. 461 */ 462 state_t state_; 463 464 /** 465 * The mode of how to show the scrollbar. 466 * 467 * This value should only be modified before showing, doing it while 468 * showing results in UB. 469 */ 470 scrollbar_mode vertical_scrollbar_mode_, horizontal_scrollbar_mode_; 471 472 /** These are valid after finalize_setup(). */ 473 grid *vertical_scrollbar_grid_, *horizontal_scrollbar_grid_; 474 475 /** These are valid after finalize_setup(). */ 476 scrollbar_base *vertical_scrollbar_, *horizontal_scrollbar_; 477 478 /** The grid that holds the content. */ 479 std::unique_ptr<grid> content_grid_; 480 481 /** Dummy spacer to hold the contents location. */ 482 spacer* content_; 483 484 /** 485 * Cache for the visible area for the content. 486 * 487 * The visible area for the content needs to be updated when scrolling. 488 */ 489 SDL_Rect content_visible_area_; 490 491 /** The builder needs to call us so we do our setup. */ 492 void finalize_setup(); // FIXME make protected 493 494 /** 495 * Function for the subclasses to do their setup. 496 * 497 * This function is called at the end of finalize_setup(). 498 */ finalize_subclass()499 virtual void finalize_subclass() 500 { 501 } 502 503 /** See @ref widget::layout_children. */ 504 virtual void layout_children() override; 505 506 /** See @ref widget::impl_draw_children. */ 507 virtual void impl_draw_children(surface& frame_buffer, int x_offset, int y_offset) override; 508 509 /** See @ref widget::child_populate_dirty_list. */ 510 virtual void child_populate_dirty_list(window& caller, const std::vector<widget*>& call_stack) override; 511 512 /** 513 * Sets the size of the content grid. 514 * 515 * This function normally just updates the content grid but can be 516 * overridden by a subclass. 517 * 518 * @param origin The origin for the content. 519 * @param size The size of the content. 520 */ 521 virtual void set_content_size(const point& origin, const point& size); 522 523 /** Helper function which needs to be called after the scollbar moved. */ 524 void scrollbar_moved(); 525 526 public: 527 /** Static type getter that does not rely on the widget being constructed. */ 528 static const std::string& type(); 529 530 private: 531 /** See @ref styled_widget::get_control_type. */ 532 virtual const std::string& get_control_type() const override; 533 534 /***** ***** ***** signal handlers ***** ****** *****/ 535 536 void signal_handler_sdl_key_down( 537 const event::ui_event event, bool& handled, const SDL_Keycode key, SDL_Keymod modifier); 538 539 void signal_handler_sdl_wheel_up(const event::ui_event event, bool& handled); 540 void signal_handler_sdl_wheel_down(const event::ui_event event, bool& handled); 541 void signal_handler_sdl_wheel_left(const event::ui_event event, bool& handled); 542 void signal_handler_sdl_wheel_right(const event::ui_event event, bool& handled); 543 void signal_handler_sdl_touch_motion(const event::ui_event event, bool& handled, 544 const point& position, const point& distance); 545 546 public: horizontal_scrollbar()547 scrollbar_base* horizontal_scrollbar() 548 { 549 return horizontal_scrollbar_; 550 } 551 vertical_scrollbar()552 scrollbar_base* vertical_scrollbar() 553 { 554 return vertical_scrollbar_; 555 } 556 get_horizontal_scrollbar_grid()557 grid* get_horizontal_scrollbar_grid() 558 { 559 return horizontal_scrollbar_grid_; 560 } 561 get_vertical_scrollbar_grid()562 grid* get_vertical_scrollbar_grid() 563 { 564 return vertical_scrollbar_grid_; 565 } 566 }; 567 568 } // namespace gui2 569