The Battle for Wesnoth  1.17.8+dev
window.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2007 - 2022
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Implementation of window.hpp.
19  */
20 
21 #define GETTEXT_DOMAIN "wesnoth-lib"
22 
24 
25 #include "config.hpp"
26 #include "cursor.hpp"
27 #include "draw.hpp"
28 #include "events.hpp"
29 #include "floating_label.hpp"
30 #include "formula/callable.hpp"
31 #include "formula/string_utils.hpp"
32 #include "gettext.hpp"
33 #include "log.hpp"
39 #include "gui/core/log.hpp"
41 #include "sdl/point.hpp"
44 #include "gui/dialogs/tooltip.hpp"
45 #include "gui/widgets/button.hpp"
49 #include "gui/widgets/grid.hpp"
50 #include "gui/widgets/helper.hpp"
51 #include "gui/widgets/panel.hpp"
52 #include "gui/widgets/settings.hpp"
53 #include "gui/widgets/widget.hpp"
54 #include "gui/widgets/window.hpp"
55 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
56 #include "gui/widgets/debug.hpp"
57 #endif
58 #include "preferences/general.hpp"
59 #include "preferences/display.hpp"
60 #include "sdl/rect.hpp"
61 #include "sdl/surface.hpp"
62 #include "sdl/texture.hpp"
63 #include "formula/variant.hpp"
64 #include "video.hpp" // only for toggle_fullscreen
65 #include "wml_exception.hpp"
66 #include "sdl/userevent.hpp"
67 #include "sdl/input.hpp" // get_mouse_button_mask
68 
69 #include <functional>
70 
71 #include <algorithm>
72 #include <iterator>
73 #include <stdexcept>
74 
75 namespace wfl { class function_symbol_table; }
76 namespace gui2 { class button; }
77 
78 static lg::log_domain log_gui("gui/layout");
79 #define ERR_GUI LOG_STREAM(err, log_gui)
80 
81 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
82 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
83 
84 #define LOG_IMPL_SCOPE_HEADER \
85  window.get_control_type() + " [" + window.id() + "] " + __func__
86 #define LOG_IMPL_HEADER LOG_IMPL_SCOPE_HEADER + ':'
87 
88 static lg::log_domain log_display("display");
89 #define DBG_DP LOG_STREAM(debug, log_display)
90 #define LOG_DP LOG_STREAM(info, log_display)
91 #define WRN_DP LOG_STREAM(warn, log_display)
92 
93 namespace gui2
94 {
95 
96 // ------------ WIDGET -----------{
97 
98 namespace implementation
99 {
100 /** @todo See whether this hack can be removed. */
101 // Needed to fix a compiler error in REGISTER_WIDGET.
103 {
104 public:
106  {
107  }
108 
110 
111  virtual std::unique_ptr<widget> build() const override
112  {
113  return nullptr;
114  }
115 };
116 
117 } // namespace implementation
119 
120 namespace
121 {
122 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
123 const unsigned SHOW = debug_layout_graph::SHOW;
124 const unsigned LAYOUT = debug_layout_graph::LAYOUT;
125 #else
126 // values are irrelavant when DEBUG_WINDOW_LAYOUT_GRAPHS is not defined.
127 const unsigned SHOW = 0;
128 const unsigned LAYOUT = 0;
129 #endif
130 
131 /**
132  * SDL_AddTimer() callback for delay_event.
133  *
134  * @param event The event to push in the event queue.
135  *
136  * @return The new timer interval (always 0).
137  */
138 static uint32_t delay_event_callback(const uint32_t, void* event)
139 {
140  SDL_PushEvent(static_cast<SDL_Event*>(event));
141  delete static_cast<SDL_Event*>(event);
142  return 0;
143 }
144 
145 /**
146  * Allows an event to be delayed a certain amount of time.
147  *
148  * @note the delay is the minimum time, after the time has passed the event
149  * will be pushed in the SDL event queue, so it might delay more.
150  *
151  * @param event The event to delay.
152  * @param delay The number of ms to delay the event.
153  */
154 static void delay_event(const SDL_Event& event, const uint32_t delay)
155 {
156  SDL_AddTimer(delay, delay_event_callback, new SDL_Event(event));
157 }
158 
159 /**
160  * Adds a SHOW_HELPTIP event to the SDL event queue.
161  *
162  * The event is used to show the helptip for the currently focused widget.
163  */
164 static void helptip()
165 {
166  DBG_GUI_E << "Pushing SHOW_HELPTIP_EVENT event in queue.";
167 
168  SDL_Event event;
170 
171  event.type = SHOW_HELPTIP_EVENT;
172  event.user = data;
173 
174  SDL_PushEvent(&event);
175 }
176 
177 /**
178  * Small helper class to get an unique id for every window instance.
179  *
180  * This is used to send event to the proper window, this allows windows to post
181  * messages to themselves and let them delay for a certain amount of time.
182  */
183 class manager
184 {
185  manager();
186 
187 public:
188  static manager& instance();
189 
190  void add(window& window);
191 
192  void remove(window& window);
193 
194  unsigned get_id(window& window);
195 
196  window* get_window(const unsigned id);
197 
198 private:
199  // The number of active window should be rather small
200  // so keep it simple and don't add a reverse lookup map.
201  std::map<unsigned, window*> windows_;
202 };
203 
205 {
206 }
207 
208 manager& manager::instance()
209 {
210  static manager window_manager;
211  return window_manager;
212 }
213 
214 void manager::add(window& win)
215 {
216  static unsigned id;
217  ++id;
218  windows_[id] = &win;
219 }
220 
221 void manager::remove(window& win)
222 {
224  itor != windows_.end();
225  ++itor) {
226 
227  if(itor->second == &win) {
228  windows_.erase(itor);
229  return;
230  }
231  }
232  assert(false);
233 }
234 
235 unsigned manager::get_id(window& win)
236 {
238  itor != windows_.end();
239  ++itor) {
240 
241  if(itor->second == &win) {
242  return itor->first;
243  }
244  }
245  assert(false);
246 
247  return 0;
248 }
249 
250 window* manager::get_window(const unsigned id)
251 {
253 
254  if(itor == windows_.end()) {
255  return nullptr;
256  } else {
257  return itor->second;
258  }
259 }
260 
261 } // namespace
262 
263 window::window(const builder_window::window_resolution& definition)
264  : panel(implementation::builder_window(::config {"definition", definition.definition}), type())
268  , owner_(nullptr)
269  , need_layout_(true)
270  , variables_()
272  , hidden_(true)
273  , automatic_placement_(definition.automatic_placement)
274  , horizontal_placement_(definition.horizontal_placement)
275  , vertical_placement_(definition.vertical_placement)
276  , maximum_width_(definition.maximum_width)
277  , maximum_height_(definition.maximum_height)
278  , x_(definition.x)
279  , y_(definition.y)
280  , w_(definition.width)
281  , h_(definition.height)
282  , reevaluate_best_size_(definition.reevaluate_best_size)
283  , functions_(definition.functions)
284  , tooltip_(definition.tooltip)
285  , helptip_(definition.helptip)
286  , click_dismiss_(false)
287  , enter_disabled_(false)
288  , escape_disabled_(false)
289  , linked_size_()
290  , mouse_button_state_(0) /**< Needs to be initialized in @ref show. */
291 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
292  , debug_layout_(new debug_layout_graph(this))
293 #endif
295  , exit_hook_([](window&)->bool { return true; })
296 {
297  manager::instance().add(*this);
298 
299  connect();
300 
301  connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(
302  &window::signal_handler_sdl_video_resize, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5));
303 
304  connect_signal<event::SDL_ACTIVATE>(std::bind(
306 
307  connect_signal<event::SDL_LEFT_BUTTON_UP>(
309  this,
310  std::placeholders::_2,
311  std::placeholders::_3,
312  std::placeholders::_4,
313  SDL_BUTTON_LMASK),
315  connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
317  this,
318  std::placeholders::_2,
319  std::placeholders::_3,
320  std::placeholders::_4,
321  SDL_BUTTON_MMASK),
323  connect_signal<event::SDL_RIGHT_BUTTON_UP>(
325  this,
326  std::placeholders::_2,
327  std::placeholders::_3,
328  std::placeholders::_4,
329  SDL_BUTTON_RMASK),
331 
332  connect_signal<event::SDL_KEY_DOWN>(
333  std::bind(
334  &window::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6, true),
336  connect_signal<event::SDL_KEY_DOWN>(std::bind(
337  &window::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6, false));
338 
339  connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
341  this,
342  std::placeholders::_2,
343  std::placeholders::_3,
344  std::placeholders::_5),
346 
347  connect_signal<event::MESSAGE_SHOW_HELPTIP>(
349  this,
350  std::placeholders::_2,
351  std::placeholders::_3,
352  std::placeholders::_5),
354 
355  connect_signal<event::REQUEST_PLACEMENT>(
356  std::bind(
357  &window::signal_handler_request_placement, this, std::placeholders::_2, std::placeholders::_3),
359 
360  connect_signal<event::CLOSE_WINDOW>(std::bind(&window::signal_handler_close_window, this));
361 
362  register_hotkey(hotkey::GLOBAL__HELPTIP, std::bind(gui2::helptip));
363 
364  /** @todo: should eventally become part of global hotkey handling. */
366  std::bind(&video::toggle_fullscreen));
367 }
368 
370 {
371  /*
372  * We need to delete our children here instead of waiting for the grid to
373  * automatically do it. The reason is when the grid deletes its children
374  * they will try to unregister them self from the linked widget list. At
375  * this point the member of window are destroyed and we enter UB. (For
376  * some reason the bug didn't trigger on g++ but it does on MSVC.
377  */
378  for(unsigned row = 0; row < get_grid().get_rows(); ++row) {
379  for(unsigned col = 0; col < get_grid().get_cols(); ++col) {
380  get_grid().remove_child(row, col);
381  }
382  }
383 
384  /*
385  * The tip needs to be closed if the window closes and the window is
386  * not a tip. If we don't do that the tip will unrender in the next
387  * window and cause drawing glitches.
388  * Another issue is that on smallgui and an MP game the tooltip not
389  * unrendered properly can capture the mouse and make playing impossible.
390  */
393  }
394 
395  manager::instance().remove(*this);
396 
397  // If we are currently shown, then queue an undraw.
398  if(!hidden_) {
399  queue_redraw();
400  }
401 
402 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
403 
404  delete debug_layout_;
405 
406 #endif
407 }
408 
410 {
411  return manager::instance().get_window(handle);
412 }
413 
414 retval window::get_retval_by_id(const std::string& id)
415 {
416  // Note it might change to a map later depending on the number
417  // of items.
418  if(id == "ok") {
419  return retval::OK;
420  } else if(id == "cancel" || id == "quit") {
421  return retval::CANCEL;
422  } else {
423  return retval::NONE;
424  }
425 }
426 
428 {
429  for(const auto& lg : definition.linked_groups) {
430  if(has_linked_size_group(lg.id)) {
431  t_string msg = VGETTEXT("Linked '$id' group has multiple definitions.", {{"id", lg.id}});
432 
433  FAIL(msg);
434  }
435 
436  init_linked_size_group(lg.id, lg.fixed_width, lg.fixed_height);
437  }
438 
439  set_click_dismiss(definition.click_dismiss);
440 
441  const auto conf = cast_config_to<window_definition>();
442  assert(conf);
443 
444  if(conf->grid) {
445  init_grid(*conf->grid);
446  finalize(*definition.grid);
447  } else {
448  init_grid(*definition.grid);
449  }
450 
451  add_to_keyboard_chain(this);
452 }
453 
454 void window::show_tooltip(/*const unsigned auto_close_timeout*/)
455 {
456  // Unhide in any case.
457  hidden_ = false;
458 
459  // Connect to the event handler, if not yet connected.
460  if(!is_connected()) {
461  LOG_DP << "connecting " << id() << " on show_tooltip";
462  connect();
463  }
464 
465  log_scope2(log_gui_draw, "Window: show as tooltip.");
466 
467  generate_dot_file("show", SHOW);
468 
469  assert(status_ == status::NEW);
470 
473 
475 
476  /*
477  * Before show has been called, some functions might have done some testing
478  * on the window and called layout, which can give glitches. So
479  * reinvalidate the window to avoid those glitches.
480  */
482  queue_redraw();
483  DBG_DP << "show tooltip queued to " << get_rectangle();
484 }
485 
486 void window::show_non_modal(/*const unsigned auto_close_timeout*/)
487 {
488  // Unhide in any case.
489  hidden_ = false;
490 
491  // Connect to the event handler, if not yet connected.
492  if(!is_connected()) {
493  LOG_DP << "connecting " << id() << " on show_non_modal";
494  connect();
495  }
496 
497  log_scope2(log_gui_draw, "Window: show non modal.");
498 
499  generate_dot_file("show", SHOW);
500 
501  assert(status_ == status::NEW);
502 
504 
506 
507  /*
508  * Before show has been called, some functions might have done some testing
509  * on the window and called layout, which can give glitches. So
510  * reinvalidate the window to avoid those glitches.
511  */
513  queue_redraw();
514 
515  DBG_DP << "show non-modal queued to " << get_rectangle();
516 
518 }
519 
520 int window::show(const unsigned auto_close_timeout)
521 {
522  // Unhide in any case.
523  hidden_ = false;
524 
525  // Connect to the event handler, if not yet connected.
526  if(!is_connected()) {
527  LOG_DP << "connecting " << id() << " on show";
528  connect();
529  }
530 
531  /*
532  * Removes the old tip if one shown. The show_tip doesn't remove
533  * the tip, since it's the tip.
534  */
536 
538 
540 
541  generate_dot_file("show", SHOW);
542 
543  //assert(status_ == status::NEW);
544 
545  /*
546  * Before show has been called, some functions might have done some testing
547  * on the window and called layout, which can give glitches. So
548  * reinvalidate the window to avoid those glitches.
549  */
551  queue_redraw();
552 
553  // Make sure we display at least once in all cases.
554  events::draw();
555 
556  if(auto_close_timeout) {
557  SDL_Event event;
558  sdl::UserEvent data(CLOSE_WINDOW_EVENT, manager::instance().get_id(*this));
559 
560  event.type = CLOSE_WINDOW_EVENT;
561  event.user = data;
562 
563  delay_event(event, auto_close_timeout);
564  }
565 
566  try
567  {
568  // Start our loop drawing will happen here as well.
569  bool mouse_button_state_initialized = false;
571  // Process and handle all pending events.
572  events::pump();
573 
574  if(!mouse_button_state_initialized) {
575  /*
576  * The state must be initialize when showing the dialog.
577  * However when initialized before this point there were random
578  * errors. This only happened when the 'click' was done fast; a
579  * slower click worked properly.
580  *
581  * So it seems the events need to be processed before SDL can
582  * return the proper button state. When initializing here all
583  * works fine.
584  */
586  mouse_button_state_initialized = true;
587  }
588 
589  // See if we should close.
592  }
593 
594  // Update the display. This will rate limit to vsync.
595  events::draw();
596  }
597  }
598  catch(...)
599  {
600  // TODO: is this even necessary? What are we catching?
601  DBG_DP << "Caught general exception in show(): " << utils::get_unknown_exception_type();
602  hide();
603  throw;
604  }
605 
606  if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
607  tb->interrupt_composition();
608  }
609 
610  // The window may be kept around to be re-shown later. Hide it for now.
611  hide();
612 
613  return retval_;
614 }
615 
617 {
618  if(hidden_) {
619  return;
620  }
621 
622  // Draw background.
623  this->draw_background();
624 
625  // Draw children.
626  this->draw_children();
627 
628  // Draw foreground.
629  this->draw_foreground();
630 
631  return;
632 }
633 
635 {
636  // Queue a redraw of the region if we were shown.
637  if(!hidden_) {
638  queue_redraw();
639  }
640 
641  // Disconnect from the event handler so we stop receiving events.
642  if(is_connected()) {
643  LOG_DP << "disconnecting " << id() << " on hide";
644  disconnect();
645  }
646 
647  hidden_ = true;
648 }
649 
650 bool window::expose(const rect& region)
651 {
652  DBG_DP << "window::expose " << region;
653  rect i = get_rectangle().intersect(region);
654  i.clip(draw::get_clip());
655  if (i.empty()) {
656  return false;
657  }
658  draw();
659  return true;
660 }
661 
663 {
664  if(hidden_) {
665  return {0,0,0,0};
666  }
667  return get_rectangle();
668 }
669 
671  : window_(window)
672 {
675 }
676 
678 {
681 }
682 
684 {
686  need_layout_ = true;
687  }
688 }
689 widget* window::find_at(const point& coordinate, const bool must_be_active)
690 {
691  return panel::find_at(coordinate, must_be_active);
692 }
693 
695  const bool must_be_active) const
696 {
697  return panel::find_at(coordinate, must_be_active);
698 }
699 
700 widget* window::find(const std::string& id, const bool must_be_active)
701 {
702  return container_base::find(id, must_be_active);
703 }
704 
705 const widget* window::find(const std::string& id, const bool must_be_active)
706  const
707 {
708  return container_base::find(id, must_be_active);
709 }
710 
711 void window::init_linked_size_group(const std::string& id,
712  const bool fixed_width,
713  const bool fixed_height)
714 {
715  assert(fixed_width || fixed_height);
716  assert(!has_linked_size_group(id));
717 
718  linked_size_[id] = linked_size(fixed_width, fixed_height);
719 }
720 
721 bool window::has_linked_size_group(const std::string& id)
722 {
723  return linked_size_.find(id) != linked_size_.end();
724 }
725 
726 void window::add_linked_widget(const std::string& id, widget* wgt)
727 {
728  assert(wgt);
729  if(!has_linked_size_group(id)) {
730  ERR_GUI << "Unknown linked group '" << id << "'; skipping";
731  return;
732  }
733 
734  std::vector<widget*>& widgets = linked_size_[id].widgets;
735  if(std::find(widgets.begin(), widgets.end(), wgt) == widgets.end()) {
736  widgets.push_back(wgt);
737  }
738 }
739 
740 void window::remove_linked_widget(const std::string& id, const widget* wgt)
741 {
742  assert(wgt);
743  if(!has_linked_size_group(id)) {
744  return;
745  }
746 
747  std::vector<widget*>& widgets = linked_size_[id].widgets;
748 
750  = std::find(widgets.begin(), widgets.end(), wgt);
751 
752  if(itor != widgets.end()) {
753  widgets.erase(itor);
754 
755  assert(std::find(widgets.begin(), widgets.end(), wgt)
756  == widgets.end());
757  }
758 }
759 
761 {
762  if(!need_layout_) {
763  return;
764  }
765  DBG_DP << "window::layout";
766 
767  /***** Initialize. *****/
768 
769  const auto conf = cast_config_to<window_definition>();
770  assert(conf);
771 
773 
775  const point mouse = get_mouse_position();
776 
777  variables_.add("mouse_x", wfl::variant(mouse.x));
778  variables_.add("mouse_y", wfl::variant(mouse.y));
779  variables_.add("window_width", wfl::variant(0));
780  variables_.add("window_height", wfl::variant(0));
781  variables_.add("best_window_width", wfl::variant(size.x));
782  variables_.add("best_window_height", wfl::variant(size.y));
783  variables_.add("size_request_mode", wfl::variant("maximum"));
784 
786 
787  unsigned int maximum_width = maximum_width_(variables_, &functions_);
788  unsigned int maximum_height = maximum_height_(variables_, &functions_);
789 
791  if(maximum_width == 0 || maximum_width > settings::screen_width) {
792  maximum_width = settings::screen_width;
793  }
794 
795  if(maximum_height == 0 || maximum_height > settings::screen_height) {
796  maximum_height = settings::screen_height;
797  }
798  } else {
799  maximum_width = w_(variables_, &functions_);
800  maximum_height = h_(variables_, &functions_);
801  }
802 
803  /***** Handle click dismiss status. *****/
804  button* click_dismiss_button = nullptr;
805  if((click_dismiss_button
806  = find_widget<button>(this, "click_dismiss", false, false))) {
807 
808  click_dismiss_button->set_visible(widget::visibility::invisible);
809  }
810  if(click_dismiss_) {
811  button* btn = find_widget<button>(this, "ok", false, false);
812  if(btn) {
814  click_dismiss_button = btn;
815  }
816  VALIDATE(click_dismiss_button,
817  _("Click dismiss needs a 'click_dismiss' or 'ok' button."));
818  }
819 
820  /***** Layout. *****/
821  layout_initialize(true);
822  generate_dot_file("layout_initialize", LAYOUT);
823 
825 
826  try
827  {
828  window_implementation::layout(*this, maximum_width, maximum_height);
829  }
830  catch(const layout_exception_resize_failed&)
831  {
832 
833  /** @todo implement the scrollbars on the window. */
834 
835  std::stringstream sstr;
836  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
837  << "' found the following problem: Failed to size window;"
838  << " wanted size " << get_best_size() << " available size "
839  << maximum_width << ',' << maximum_height << " screen size "
841 
842  throw wml_exception(_("Failed to show a dialog, "
843  "which doesn't fit on the screen."),
844  sstr.str());
845  }
846 
847  /****** Validate click dismiss status. *****/
849  assert(click_dismiss_button);
850  click_dismiss_button->set_visible(widget::visibility::visible);
851 
853  *click_dismiss_button,
854  std::bind(&window::set_retval, this, retval::OK, true));
855 
856  layout_initialize(true);
857  generate_dot_file("layout_initialize", LAYOUT);
858 
860 
861  try
862  {
864  *this, maximum_width, maximum_height);
865  }
866  catch(const layout_exception_resize_failed&)
867  {
868 
869  /** @todo implement the scrollbars on the window. */
870 
871  std::stringstream sstr;
872  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
873  << "' found the following problem: Failed to size window;"
874  << " wanted size " << get_best_size() << " available size "
875  << maximum_width << ',' << maximum_height << " screen size "
877  << '.';
878 
879  throw wml_exception(_("Failed to show a dialog, "
880  "which doesn't fit on the screen."),
881  sstr.str());
882  }
883  }
884 
885  /***** Get the best location for the window *****/
886  size = get_best_size();
887  /* Although 0-size windows might not seem valid/useful, there are
888  a handful of windows that request 0 size just to get a position
889  chosen via the code below, so at least for now allow them:
890  */
891  assert(size.x >= 0 && static_cast<unsigned>(size.x) <= maximum_width
892  && size.y >= 0 && static_cast<unsigned>(size.y) <= maximum_height);
893 
894  point origin(0, 0);
895 
897 
898  switch(horizontal_placement_) {
900  // Do nothing
901  break;
903  origin.x = (settings::screen_width - size.x) / 2;
904  break;
906  origin.x = settings::screen_width - size.x;
907  break;
908  default:
909  assert(false);
910  }
911  switch(vertical_placement_) {
913  // Do nothing
914  break;
916  origin.y = (settings::screen_height - size.y) / 2;
917  break;
919  origin.y = settings::screen_height - size.y;
920  break;
921  default:
922  assert(false);
923  }
924  } else {
925 
926  variables_.add("window_width", wfl::variant(size.x));
927  variables_.add("window_height", wfl::variant(size.y));
928 
930  layout_initialize(true);
931 
935 
936  size = get_best_size();
937  variables_.add("window_width", wfl::variant(size.x));
938  variables_.add("window_height", wfl::variant(size.y));
939  }
940 
941  variables_.add("size_request_mode", wfl::variant("size"));
942 
943  size.x = w_(variables_, &functions_);
944  size.y = h_(variables_, &functions_);
945 
946  variables_.add("window_width", wfl::variant(size.x));
947  variables_.add("window_height", wfl::variant(size.y));
948 
949  origin.x = x_(variables_, &functions_);
950  origin.y = y_(variables_, &functions_);
951  }
952 
953  /***** Set the window size *****/
954  place(origin, size);
955 
956  generate_dot_file("layout_finished", LAYOUT);
957  need_layout_ = false;
958 
960 }
961 
963 {
964  // evaluate the group sizes
965  for(auto & linked_size : linked_size_)
966  {
967 
968  point max_size(0, 0);
969 
970  // Determine the maximum size.
971  for(auto widget : linked_size.second.widgets)
972  {
973 
974  const point size = widget->get_best_size();
975 
976  if(size.x > max_size.x) {
977  max_size.x = size.x;
978  }
979  if(size.y > max_size.y) {
980  max_size.y = size.y;
981  }
982  }
983  if(linked_size.second.width != -1) {
984  linked_size.second.width = max_size.x;
985  }
986  if(linked_size.second.height != -1) {
987  linked_size.second.height = max_size.y;
988  }
989 
990  // Set the maximum size.
991  for(auto widget : linked_size.second.widgets)
992  {
993 
995 
996  if(linked_size.second.width != -1) {
997  size.x = max_size.x;
998  }
999  if(linked_size.second.height != -1) {
1000  size.y = max_size.y;
1001  }
1002 
1003  widget->set_layout_size(size);
1004  }
1005  }
1006 }
1007 
1008 bool window::click_dismiss(const int mouse_button_mask)
1009 {
1010  if(does_click_dismiss()) {
1011  if((mouse_button_state_ & mouse_button_mask) == 0) {
1013  } else {
1014  mouse_button_state_ &= ~mouse_button_mask;
1015  }
1016  return true;
1017  }
1018  return false;
1019 }
1020 
1021 void window::finalize(const builder_grid& content_grid)
1022 {
1023  auto widget = content_grid.build();
1024  assert(widget);
1025 
1026  static const std::string id = "_window_content_grid";
1027 
1028  // Make sure the new child has same id.
1029  widget->set_id(id);
1030 
1031  auto* parent_grid = find_widget<grid>(&get_grid(), id, true, false);
1032  assert(parent_grid);
1033 
1034  if(grid* grandparent_grid = dynamic_cast<grid*>(parent_grid->parent())) {
1035  grandparent_grid->swap_child(id, std::move(widget), false);
1036  } else if(container_base* c = dynamic_cast<container_base*>(parent_grid->parent())) {
1037  c->get_grid().swap_child(id, std::move(widget), true);
1038  } else {
1039  assert(false);
1040  }
1041 }
1042 
1043 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1044 
1045 void window::generate_dot_file(const std::string& generator,
1046  const unsigned domain)
1047 {
1048  debug_layout_->generate_dot_file(generator, domain);
1049 }
1050 #endif
1051 
1053  const unsigned maximum_width,
1054  const unsigned maximum_height)
1055 {
1057 
1058  /*
1059  * For now we return the status, need to test later whether this can
1060  * entirely be converted to an exception based system as in 'promised' on
1061  * the algorithm page.
1062  */
1063 
1064  try
1065  {
1066  point size = window.get_best_size();
1067 
1068  DBG_GUI_L << LOG_IMPL_HEADER << " best size : " << size
1069  << " maximum size : " << maximum_width << ','
1070  << maximum_height << ".";
1071  if(size.x <= static_cast<int>(maximum_width)
1072  && size.y <= static_cast<int>(maximum_height)) {
1073 
1074  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Fits, nothing to do.";
1075  return;
1076  }
1077 
1078  if(size.x > static_cast<int>(maximum_width)) {
1079  window.reduce_width(maximum_width);
1080 
1081  size = window.get_best_size();
1082  if(size.x > static_cast<int>(maximum_width)) {
1083  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize width failed."
1084  << " Wanted width " << maximum_width
1085  << " resulting width " << size.x << ".";
1087  }
1089  << " Status: Resize width succeeded.";
1090  }
1091 
1092  if(size.y > static_cast<int>(maximum_height)) {
1093  window.reduce_height(maximum_height);
1094 
1095  size = window.get_best_size();
1096  if(size.y > static_cast<int>(maximum_height)) {
1097  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize height failed."
1098  << " Wanted height " << maximum_height
1099  << " resulting height " << size.y << ".";
1101  }
1103  << " Status: Resize height succeeded.";
1104  }
1105 
1106  assert(size.x <= static_cast<int>(maximum_width)
1107  && size.y <= static_cast<int>(maximum_height));
1108 
1109 
1110  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resizing succeeded.";
1111  return;
1112  }
1113  catch(const layout_exception_width_modified&)
1114  {
1116  << " Status: Width has been modified, rerun.";
1117 
1118  window.layout_initialize(false);
1119  window.layout_linked_widgets();
1120  layout(window, maximum_width, maximum_height);
1121  return;
1122  }
1123 }
1124 
1125 void window::mouse_capture(const bool capture)
1126 {
1127  assert(event_distributor_);
1128  event_distributor_->capture_mouse(capture);
1129 }
1130 
1132 {
1133  assert(event_distributor_);
1134  event_distributor_->keyboard_capture(widget);
1135 }
1136 
1138 {
1139  assert(event_distributor_);
1140  event_distributor_->keyboard_add_to_chain(widget);
1141 }
1142 
1144 {
1145  assert(event_distributor_);
1146  event_distributor_->keyboard_remove_from_chain(widget);
1147 }
1148 
1150 {
1151  if(std::find(tab_order.begin(), tab_order.end(), widget) != tab_order.end()) {
1152  return;
1153  }
1154  assert(event_distributor_);
1155  if(tab_order.empty() && !event_distributor_->keyboard_focus()) {
1156  keyboard_capture(widget);
1157  }
1158  if(at < 0 || at >= static_cast<int>(tab_order.size())) {
1159  tab_order.push_back(widget);
1160  } else {
1161  tab_order.insert(tab_order.begin() + at, widget);
1162  }
1163 }
1164 
1166  bool& handled,
1167  const point& new_size)
1168 {
1169  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1170 
1173  settings::screen_width = new_size.x;
1174  settings::screen_height = new_size.y;
1176 
1177  handled = true;
1178 }
1179 
1181  bool& handled,
1182  bool& halt,
1183  const int mouse_button_mask)
1184 {
1185  DBG_GUI_E << LOG_HEADER << ' ' << event << " mouse_button_mask "
1186  << static_cast<unsigned>(mouse_button_mask) << ".";
1187 
1188  handled = halt = click_dismiss(mouse_button_mask);
1189 }
1190 
1191 static bool is_active(const widget* wgt)
1192 {
1193  if(const styled_widget* control = dynamic_cast<const styled_widget*>(wgt)) {
1194  return control->get_active() && control->get_visible() == widget::visibility::visible;
1195  }
1196  return false;
1197 }
1198 
1200  bool& handled,
1201  const SDL_Keycode key,
1202  const SDL_Keymod mod,
1203  bool handle_tab)
1204 {
1205  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1206 
1207  if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
1208  if(tb->is_composing()) {
1209  if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1210  tb->interrupt_composition();
1211  } else {
1212  return;
1213  }
1214  }
1215  }
1216  if(!enter_disabled_ && (key == SDLK_KP_ENTER || key == SDLK_RETURN)) {
1218  handled = true;
1219  } else if(key == SDLK_ESCAPE && !escape_disabled_) {
1221  handled = true;
1222  } else if(key == SDLK_SPACE) {
1223  handled = click_dismiss(0);
1224  } else if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1225  assert(event_distributor_);
1226  widget* focus = event_distributor_->keyboard_focus();
1227  auto iter = std::find(tab_order.begin(), tab_order.end(), focus);
1228  do {
1229  if(mod & KMOD_SHIFT) {
1230  if(iter == tab_order.begin()) {
1231  iter = tab_order.end();
1232  }
1233  iter--;
1234  } else {
1235  if(iter == tab_order.end()) {
1236  iter = tab_order.begin();
1237  } else {
1238  iter++;
1239  if(iter == tab_order.end()) {
1240  iter = tab_order.begin();
1241  }
1242  }
1243  }
1244  } while(!is_active(*iter));
1245  keyboard_capture(*iter);
1246  handled = true;
1247  }
1248 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1249  if(key == SDLK_F12) {
1250  debug_layout_->generate_dot_file("manual", debug_layout_graph::MANUAL);
1251  handled = true;
1252  }
1253 #endif
1254 }
1255 
1257  bool& handled,
1258  const event::message& message)
1259 {
1260  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1261 
1262  const event::message_show_tooltip& request
1263  = dynamic_cast<const event::message_show_tooltip&>(message);
1264 
1265  dialogs::tip::show(tooltip_.id, request.message, request.location, request.source_rect);
1266 
1267  handled = true;
1268 }
1269 
1271  bool& handled,
1272  const event::message& message)
1273 {
1274  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1275 
1276  const event::message_show_helptip& request
1277  = dynamic_cast<const event::message_show_helptip&>(message);
1278 
1279  dialogs::tip::show(helptip_.id, request.message, request.location, request.source_rect);
1280 
1281  handled = true;
1282 }
1283 
1285  bool& handled)
1286 {
1287  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
1288 
1290 
1291  handled = true;
1292 }
1293 
1295 {
1297 }
1298 
1299 // }---------- DEFINITION ---------{
1300 
1303 {
1304  DBG_GUI_P << "Parsing window " << id;
1305 
1306  load_resolutions<resolution>(cfg);
1307 }
1308 
1310  : panel_definition::resolution(cfg), grid(nullptr)
1311 {
1312  const config& child = cfg.child("grid");
1313  // VALIDATE(child, _("No grid defined."));
1314 
1315  /** @todo Evaluate whether the grid should become mandatory. */
1316  if(child) {
1317  grid = std::make_shared<builder_grid>(child);
1318  }
1319 }
1320 
1321 // }------------ END --------------
1322 
1323 } // namespace gui2
1324 
1325 
1326 /**
1327  * @page layout_algorithm Layout algorithm
1328  *
1329  * @section introduction-layout_algorithm Introduction
1330  *
1331  * This page describes how the layout engine for the dialogs works. First
1332  * a global overview of some terms used in this document.
1333  *
1334  * - @ref gui2::widget "Widget"; Any item which can be used in the widget
1335  * toolkit. Not all widgets are visible. In general widgets cannot be
1336  * sized directly, but this is controlled by a window. A widget has an
1337  * internal size cache and if the value in the cache is not equal to 0,0
1338  * that value is its best size. This value gets set when the widget can
1339  * honor a resize request. It will be set with the value which honors
1340  * the request.
1341  *
1342  * - @ref gui2::grid "Grid"; A grid is an invisible container which holds
1343  * one or more widgets. Several widgets have a grid in them to hold
1344  * multiple widgets eg panels and windows.
1345  *
1346  * - @ref gui2::grid::child "Grid cell"; Every widget which is in a grid is
1347  * put in a grid cell. These cells also hold the information about the gaps
1348  * between widgets the behavior on growing etc. All grid cells must have a
1349  * widget inside them.
1350  *
1351  * - @ref gui2::window "Window"; A window is a top level item which has a
1352  * grid with its children. The window handles the sizing of the window and
1353  * makes sure everything fits.
1354  *
1355  * - @ref gui2::window::linked_size "Shared size group"; A shared size
1356  * group is a number of widgets which share width and or height. These
1357  * widgets are handled separately in the layout algorithm. All grid cells
1358  * width such a widget will get the same height and or width and these
1359  * widgets won't be resized when there's not enough size. To be sure that
1360  * these widgets don't cause trouble for the layout algorithm, they must be
1361  * in a container with scrollbars so there will always be a way to properly
1362  * layout them. The engine must enforce this restriction so the shared
1363  * layout property must be set by the engine after validation.
1364  *
1365  * - All visible grid cells; A grid cell is visible when the widget inside
1366  * of it doesn't have the state visibility::invisible. Widgets which have the
1367  * state visibility::hidden are sized properly since when they become
1368  * visibility::visible the layout shouldn't be invalidated. A grid cell
1369  * that's invisible has size 0,0.
1370  *
1371  * - All resizable grid cells; A grid cell is resizable under the following
1372  * conditions:
1373  * - The widget is visibility::visible.
1374  * - The widget is not in a shared size group.
1375  *
1376  * There are two layout algorithms with a different purpose.
1377  *
1378  * - The Window algorithm; this algorithm's goal is it to make sure all grid
1379  * cells fit in the window. Sizing the grid cells depends on the widget
1380  * size as well, but this algorithm only sizes the grid cells and doesn't
1381  * handle the widgets inside them.
1382  *
1383  * - The Grid algorithm; after the Window algorithm made sure that all grid
1384  * cells fit this algorithm makes sure the widgets are put in the optimal
1385  * state in their grid cell.
1386  *
1387  * @section layout_algorithm_window Window
1388  *
1389  * Here is the algorithm used to layout the window:
1390  *
1391  * - Perform a full initialization
1392  * (@ref gui2::widget::layout_initialize (full_initialization = true)):
1393  * - Clear the internal best size cache for all widgets.
1394  * - For widgets with scrollbars hide them unless the
1395  * @ref gui2::scrollbar_container::scrollbar_mode "scrollbar_mode" is
1396  * ALWAYS_VISIBLE or AUTO_VISIBLE.
1397  * - Handle shared sizes:
1398  * - Height and width:
1399  * - Get the best size for all widgets that share height and width.
1400  * - Set the maximum of width and height as best size for all these
1401  * widgets.
1402  * - Width only:
1403  * - Get the best width for all widgets which share their width.
1404  * - Set the maximum width for all widgets, but keep their own height.
1405  * - Height only:
1406  * - Get the best height for all widgets which share their height.
1407  * - Set the maximum height for all widgets, but keep their own width.
1408  * - Start layout loop:
1409  * - Get best size.
1410  * - If width <= maximum_width && height <= maximum_height we're done.
1411  * - If width > maximum_width, optimize the width:
1412  * - For every grid cell in a grid row there will be a resize request
1413  * (@ref gui2::grid::reduce_width):
1414  * - Sort the widgets in the row on the resize priority.
1415  * - Loop through this priority queue until the row fits
1416  * - If priority != 0 try to share the extra width else all
1417  * widgets are tried to reduce the full size.
1418  * - Try to shrink the widgets by either wrapping or using a
1419  * scrollbar (@ref gui2::widget::request_reduce_width).
1420  * - If the row fits in the wanted width this row is done.
1421  * - Else try the next priority.
1422  * - All priorities done and the width still doesn't fit.
1423  * - Loop through this priority queue until the row fits.
1424  * - If priority != 0:
1425  * - try to share the extra width
1426  * -Else:
1427  * - All widgets are tried to reduce the full size.
1428  * - Try to shrink the widgets by sizing them smaller as really
1429  * wanted (@ref gui2::widget::demand_reduce_width).
1430  * For labels, buttons etc. they get ellipsized.
1431  * - If the row fits in the wanted width this row is done.
1432  * - Else try the next priority.
1433  * - All priorities done and the width still doesn't fit.
1434  * - Throw a layout width doesn't fit exception.
1435  * - If height > maximum_height, optimize the height
1436  * (@ref gui2::grid::reduce_height):
1437  * - For every grid cell in a grid column there will be a resize request:
1438  * - Sort the widgets in the column on the resize priority.
1439  * - Loop through this priority queue until the column fits:
1440  * - If priority != 0 try to share the extra height else all
1441  * widgets are tried to reduce the full size.
1442  * - Try to shrink the widgets by using a scrollbar
1443  * (@ref gui2::widget::request_reduce_height).
1444  * - If succeeded for a widget the width is influenced and the
1445  * width might be invalid.
1446  * - Throw a width modified exception.
1447  * - If the column fits in the wanted height this column is done.
1448  * - Else try the next priority.
1449  * - All priorities done and the height still doesn't fit.
1450  * - Loop through this priority queue until the column fits.
1451  * - If priority != 0 try to share the extra height else all
1452  * widgets are tried to reduce the full size.
1453  * - Try to shrink the widgets by sizing them smaller as really
1454  * wanted (@ref gui2::widget::demand_reduce_width).
1455  * For labels, buttons etc. they get ellipsized .
1456  * - If the column fits in the wanted height this column is done.
1457  * - Else try the next priority.
1458  * - All priorities done and the height still doesn't fit.
1459  * - Throw a layout height doesn't fit exception.
1460  * - End layout loop.
1461  *
1462  * - Catch @ref gui2::layout_exception_width_modified "width modified":
1463  * - Goto relayout.
1464  *
1465  * - Catch
1466  * @ref gui2::layout_exception_width_resize_failed "width resize failed":
1467  * - If the window has a horizontal scrollbar which isn't shown but can be
1468  * shown.
1469  * - Show the scrollbar.
1470  * - goto relayout.
1471  * - Else show a layout failure message.
1472  *
1473  * - Catch
1474  * @ref gui2::layout_exception_height_resize_failed "height resize failed":
1475  * - If the window has a vertical scrollbar which isn't shown but can be
1476  * shown:
1477  * - Show the scrollbar.
1478  * - goto relayout.
1479  * - Else:
1480  * - show a layout failure message.
1481  *
1482  * - Relayout:
1483  * - Initialize all widgets
1484  * (@ref gui2::widget::layout_initialize (full_initialization = false))
1485  * - Handle shared sizes, since the reinitialization resets that state.
1486  * - Goto start layout loop.
1487  *
1488  * @section grid Grid
1489  *
1490  * This section will be documented later.
1491  */
Define the common log macros for the gui toolkit.
Drawing functions, for drawing things on the screen.
bool click_dismiss_
Do we want to have easy close behavior?
Definition: window.hpp:524
const std::string message
The message to show on the tooltip.
Definition: message.hpp:66
void keyboard_capture(widget *widget)
Definition: window.cpp:1131
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
#define DBG_GUI_P
Definition: log.hpp:66
wfl::function_symbol_table functions_
The formula definitions available for the calculation formulas.
Definition: window.hpp:499
void remove()
Removes a tip.
Definition: tooltip.cpp:111
Defines the exception classes for the layout algorithm.
void finish_build(const builder_window::window_resolution &)
Definition: window.cpp:427
void signal_handler_click_dismiss(const event::ui_event event, bool &handled, bool &halt, const int mouse_button_mask)
The handler for the click dismiss mouse &#39;event&#39;.
Definition: window.cpp:1180
#define ERR_GUI
Definition: window.cpp:79
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:402
void finalize(const builder_grid &content_grid)
Finishes the initialization of the grid.
Definition: window.cpp:1021
void reduce_width(const unsigned maximum_width)
Tries to reduce the width of a container.
Helper for header for the window.
typed_formula< unsigned > maximum_height_
The maximum height if automatic_placement_ is true.
Definition: window.hpp:481
bool does_click_dismiss() const
Does the window close easily?
Definition: window.hpp:273
std::function< bool(window &)> exit_hook_
Definition: window.hpp:723
const std::string message
The message to show on the helptip.
Definition: message.hpp:84
Abstract base class for text items.
void signal_handler_sdl_key_down(const event::ui_event event, bool &handled, const SDL_Keycode key, const SDL_Keymod mod, bool handle_tab)
Definition: window.cpp:1199
#define DBG_GUI_L
Definition: log.hpp:55
static lg::log_domain log_gui("gui/layout")
void set_layout_size(const point &size)
Definition: widget.cpp:336
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: window.cpp:689
typed_formula< unsigned > h_
The formula to calculate the height of the dialog.
Definition: window.hpp:493
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Key Type Default Description window_width unsigned 0 Width of the application window.
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
const unsigned horizontal_placement_
Sets the horizontal placement.
Definition: window.hpp:467
virtual void place(const point &origin, const point &size) override
See widget::place.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:179
const grid & get_grid() const
void pump_and_draw()
pump() then immediately draw()
Definition: events.hpp:151
const point location
The location where to show the tooltip.
Definition: message.hpp:69
A panel is a visible container to hold multiple widgets.
Definition: panel.hpp:58
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:311
Main class to show messages to the user.
Definition: message.hpp:35
void register_hotkey(const hotkey::HOTKEY_COMMAND id, const hotkey_function &function)
Registers a hotkey.
Definition: dispatcher.cpp:149
The window is new and not yet shown.
void show_non_modal()
Shows the window non modal.
Definition: window.cpp:486
void generate_dot_file(const std::string &, const unsigned)
Definition: window.hpp:633
Helper struct to force widgets the have the same size.
Definition: window.hpp:539
#define LOG_SCOPE_HEADER
Definition: window.cpp:81
const std::string & id() const
Definition: widget.cpp:111
unsigned int get_rows() const
Definition: grid.hpp:303
Exception thrown when the height resizing has failed.
This file contains the window object, this object is a top level container which has the event manage...
void draw()
Draws the window.
Definition: window.cpp:616
The message for MESSAGE_SHOW_HELPTIP.
Definition: message.hpp:76
Base class for all widgets.
Definition: widget.hpp:53
std::string_view data
Definition: picture.cpp:208
void set_click_dismiss(const bool click_dismiss)
Definition: window.hpp:375
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:54
const SDL_Rect source_rect
The size of the entity requesting to show a tooltip.
Definition: message.hpp:72
void signal_handler_sdl_video_resize(const event::ui_event event, bool &handled, const point &new_size)
Definition: window.cpp:1165
#define LOG_IMPL_HEADER
Definition: window.cpp:86
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
virtual bool expose(const rect &region) override
Called by draw_manager when it believes a redraw is necessary.
Definition: window.cpp:650
Contains the event distributor.
unsigned gamemap_width
The size of the map area, if not available equal to the screen size.
Definition: settings.cpp:39
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1137
typed_formula< unsigned > w_
The formula to calculate the width of the dialog.
Definition: window.hpp:490
static std::string _(const char *str)
Definition: gettext.hpp:93
void signal_handler_message_show_tooltip(const event::ui_event event, bool &handled, const event::message &message)
Definition: window.cpp:1256
Definitions for the interface to Wesnoth Markup Language (WML).
const point location
The location where to show the helptip.
Definition: message.hpp:87
void draw_foreground()
Draws the foreground of the widget.
Definition: widget.cpp:406
Contains functions for cleanly handling SDL input.
int mouse_button_state_
The state of the mouse button.
Definition: window.hpp:597
void get_screen_size_variables(wfl::map_formula_callable &variable)
Gets a formula object with the screen size.
Definition: helper.cpp:96
void init_mouse_location()
Initializes the location of the mouse.
Definition: handler.cpp:880
Generic file dialog.
int width
The current width of all widgets in the group, -1 if the width is not linked.
Definition: window.hpp:550
#define DBG_DP
Definition: window.cpp:89
std::vector< linked_group_definition > linked_groups
The message callbacks hold a reference to a message.
Definition: message.hpp:45
#define CLOSE_WINDOW_EVENT
Definition: events.hpp:28
static bool is_active(const widget *wgt)
Definition: window.cpp:1191
The event handler class for the widget library.
Base container class.
Definition: grid.hpp:31
void connect()
Connects the dispatcher to the event handler.
Definition: dispatcher.cpp:51
static std::string at(const std::string &file, int line)
status status_
The status of the window.
Definition: window.hpp:426
#define LOG_HEADER
Definition: window.cpp:82
void reduce_height(const unsigned maximum_height)
Tries to reduce the height of a container.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
std::vector< widget * > widgets
The widgets linked.
Definition: window.hpp:547
Exception thrown when the width has been modified during resizing.
point get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:194
The dialog was closed automatically as its timeout had been reached.
Definition: retval.hpp:41
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
This file contains the settings handling of the widget library.
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
bool enter_disabled_
Disable the enter key see our setter for more info.
Definition: window.hpp:527
const SDL_Rect source_rect
The size of the entity requesting to show a helptip.
Definition: message.hpp:90
bool invalidate_layout_blocked_
Is invalidate_layout blocked, see invalidate_layout_blocker.
Definition: window.hpp:453
void set_visible(const visibility visible)
Definition: widget.cpp:456
bool need_layout_
When set the form needs a full layout redraw cycle.
Definition: window.hpp:447
void signal_handler_close_window()
Definition: window.cpp:1294
typed_formula< bool > reevaluate_best_size_
The formula to determine whether the size is good.
Definition: window.hpp:496
builder_window::window_resolution::tooltip_info helptip_
The settings for the helptip.
Definition: window.hpp:505
unsigned gamemap_height
Definition: settings.cpp:40
void clip(const SDL_Rect &r)
Clip this rectangle by the given rectangle.
Definition: rect.cpp:101
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:59
void init_grid(const builder_grid &grid_builder)
Initializes and builds the grid.
::rect get_clip()
Get the current clipping area, in draw coordinates.
Definition: draw.cpp:468
rect intersect(const SDL_Rect &r) const
Calculates the intersection of this rectangle and another; that is, the maximal rectangle that is con...
Definition: rect.cpp:92
The window is being shown.
This file contains the definitions for the gui2::event::message class.
The message for MESSAGE_SHOW_TOOLTIP.
Definition: message.hpp:58
unsigned int get_cols() const
Definition: grid.hpp:309
typed_formula< unsigned > y_
The formula to calculate the y value of the dialog.
Definition: window.hpp:487
Exception thrown when the width resizing has failed.
void hide()
Hides the window.
Definition: window.cpp:634
virtual std::unique_ptr< widget > build() const override
Definition: window.cpp:111
Basic template class to generate new items.
#define log_scope2(domain, description)
Definition: log.hpp:236
const bool automatic_placement_
Do we wish to place the widget automatically?
Definition: window.hpp:459
typed_formula< unsigned > maximum_width_
The maximum width if automatic_placement_ is true.
Definition: window.hpp:478
void init_linked_size_group(const std::string &id, const bool fixed_width, const bool fixed_height)
Initializes a linked size group.
Definition: window.cpp:711
typed_formula< unsigned > x_
The formula to calculate the x value of the dialog.
Definition: window.hpp:484
int show(unsigned auto_close_timeout=0)
Shows the window, running an event loop until it should close.
Definition: window.cpp:520
void set_mouse_behavior(const mouse_behavior mouse_behavior)
Definition: dispatcher.hpp:414
uint32_t get_mouse_button_mask()
Returns the current mouse button mask.
Definition: input.cpp:49
bool escape_disabled_
Disable the escape key see our setter for more info.
Definition: window.hpp:530
void pump()
Process all events currently in the queue.
Definition: events.cpp:478
The window has been requested to be closed but still needs to evaluate the request.
virtual std::unique_ptr< widget > build() const override
Inherited from builder_widget.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
static window * window_instance(const unsigned handle)
Returns the instance of a window.
Definition: window.cpp:409
void add_linked_widget(const std::string &id, widget *widget)
Adds a widget to a linked size group.
Definition: window.cpp:726
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: window.cpp:700
bool empty() const
False if both w and h are > 0, true otherwise.
Definition: rect.cpp:49
Helper class, don&#39;t construct this directly.
bool hidden_
Avoid drawing the window.
Definition: window.hpp:456
static const unsigned VERTICAL_ALIGN_TOP
Definition: grid.hpp:50
Definition: pump.hpp:40
void remove_from_keyboard_chain(widget *widget)
Remove the widget from the keyboard chain.
Definition: window.cpp:1143
bool is_connected() const
Return whether the dispatcher is currently connected.
Definition: dispatcher.hpp:177
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
std::size_t i
Definition: function.cpp:967
std::map< unsigned, window * > windows_
Definition: window.cpp:201
#define DBG_GUI_E
Definition: log.hpp:35
Default, unset return value.
Definition: retval.hpp:32
virtual void layout() override
Lays out the window.
Definition: window.cpp:760
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:442
#define SHOW_HELPTIP_EVENT
Definition: events.hpp:29
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
The user set the widget invisible, that means:
window * get_window()
Get the parent window.
Definition: widget.cpp:118
static lg::log_domain log_display("display")
const unsigned vertical_placement_
Sets the vertical placement.
Definition: window.hpp:475
void draw_children()
Draws the children of a widget.
Definition: widget.cpp:389
std::vector< widget * > tab_order
List of widgets in the tabbing order.
Definition: window.hpp:560
static const unsigned VERTICAL_ALIGN_CENTER
Definition: grid.hpp:51
bool has_linked_size_group(const std::string &id)
Is the linked size group defined for this window?
Definition: window.cpp:721
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:46
bool click_dismiss(const int mouse_button_mask)
Handles a mouse click event for dismissing the dialog.
Definition: window.cpp:1008
static const unsigned HORIZONTAL_ALIGN_CENTER
Definition: grid.hpp:58
Holds a 2D point.
Definition: point.hpp:24
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
Definition: settings.cpp:27
The window has been closed.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:358
lg::log_domain log_gui_draw("gui/draw")
Definition: log.hpp:28
Base class for all visible items.
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
A generic container base class.
void signal_handler_request_placement(const event::ui_event event, bool &handled)
Definition: window.cpp:1284
builder_window::window_resolution::tooltip_info tooltip_
The settings for the tooltip.
Definition: window.hpp:502
show_mode show_mode_
The mode in which the window is shown.
Definition: window.hpp:433
static void layout(window &window, const unsigned maximum_width, const unsigned maximum_height)
Layouts the window.
Definition: window.cpp:1052
window_definition(const config &cfg)
Definition: window.cpp:1301
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition: grid.hpp:52
void toggle_fullscreen()
Toggle fullscreen mode.
Definition: video.cpp:779
static retval get_retval_by_id(const std::string &id)
Gets the retval for the default buttons.
Definition: window.cpp:414
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:114
SDL_Window * get_window()
Definition: video.cpp:638
Contains the SDL_Rect helper code.
The user sets the widget visible, that means:
void remove_child(const unsigned row, const unsigned col)
Removes and frees a widget in a cell.
Definition: grid.cpp:145
void show_tooltip()
Shows the window as a tooltip.
Definition: window.cpp:454
unsigned screen_height
Definition: settings.cpp:28
Definition: contexts.hpp:44
void set_id(const std::string &id)
Definition: widget.cpp:99
void mouse_capture(const bool capture=true)
Definition: window.cpp:1125
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
Simple push button.
Definition: button.hpp:36
void disconnect()
Disconnects the dispatcher from the event handler.
Definition: dispatcher.cpp:58
std::unique_ptr< event::distributor > event_distributor_
Definition: window.hpp:638
void signal_handler_message_show_helptip(const event::ui_event event, bool &handled, const event::message &message)
Definition: window.cpp:1270
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:683
void draw_background()
Draws the background of a widget.
Definition: widget.cpp:371
friend class debug_layout_graph
Definition: window.hpp:68
dialogs::modal_dialog * owner_
The dialog that owns the window.
Definition: window.hpp:439
retval
Default window/dialog return values.
Definition: retval.hpp:29
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition: grid.hpp:57
std::unique_ptr< window > build(const builder_window::window_resolution &definition)
Builds a window.
#define FAIL(message)
Dialog was closed with the OK button.
Definition: retval.hpp:35
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
void remove_linked_widget(const std::string &id, const widget *widget)
Removes a widget from a linked size group.
Definition: window.cpp:740
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
virtual ~window()
Definition: window.cpp:369
int height
The current height of all widgets in the group, -1 if the height is not linked.
Definition: window.hpp:553
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
void add_to_tab_order(widget *widget, int at=-1)
Add the widget to the tabbing order.
Definition: window.cpp:1149
mock_char c
void set_want_keyboard_input(const bool want_keyboard_input)
Definition: dispatcher.hpp:424
#define LOG_DP
Definition: window.cpp:90
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:66
virtual rect screen_location() override
The current draw location of the window, on the screen.
Definition: window.cpp:662
#define LOG_IMPL_SCOPE_HEADER
Definition: window.cpp:84
std::map< std::string, linked_size > linked_size_
List of the widgets, whose size are linked together.
Definition: window.hpp:557
void draw()
Trigger a draw cycle.
Definition: events.cpp:743
wfl::map_formula_callable variables_
The variables of the canvas.
Definition: window.hpp:450
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
void initialize_state()
Initializes the state of the keyboard and mouse.
builder_window(const config &cfg)
Definition: window.cpp:105
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
void layout_linked_widgets()
Layouts the linked widgets.
Definition: window.cpp:962
void show(const std::string &window_id, const t_string &message, const point &mouse, const SDL_Rect &source_rect)
Shows a tip.
Definition: tooltip.cpp:81
Basic exception when the layout doesn&#39;t fit.