The Battle for Wesnoth  1.17.0-dev
window.cpp
Go to the documentation of this file.
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 /**
16  * @file
17  * Implementation of window.hpp.
18  */
19 
20 #define GETTEXT_DOMAIN "wesnoth-lib"
21 
23 
24 #include "config.hpp"
25 #include "cursor.hpp"
26 #include "events.hpp"
27 #include "floating_label.hpp"
28 #include "formula/callable.hpp"
29 #include "gettext.hpp"
30 #include "log.hpp"
36 #include "gui/core/log.hpp"
38 #include "sdl/point.hpp"
41 #include "gui/dialogs/tooltip.hpp"
42 #include "gui/widgets/button.hpp"
46 #include "gui/widgets/grid.hpp"
47 #include "gui/widgets/helper.hpp"
48 #include "gui/widgets/panel.hpp"
49 #include "gui/widgets/settings.hpp"
50 #include "gui/widgets/widget.hpp"
51 #include "gui/widgets/window.hpp"
52 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
53 #include "gui/widgets/debug.hpp"
54 #endif
55 #include "preferences/general.hpp"
56 #include "preferences/display.hpp"
57 #include "sdl/rect.hpp"
58 #include "sdl/surface.hpp"
59 #include "formula/variant.hpp"
60 #include "video.hpp"
61 #include "wml_exception.hpp"
62 #include "sdl/userevent.hpp"
63 
64 #include <functional>
65 
66 #include <algorithm>
67 #include <iterator>
68 #include <stdexcept>
69 
70 namespace wfl { class function_symbol_table; }
71 namespace gui2 { class button; }
72 
73 static lg::log_domain log_gui("gui/layout");
74 #define ERR_GUI LOG_STREAM(err, log_gui)
75 
76 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
77 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
78 
79 #define LOG_IMPL_SCOPE_HEADER \
80  window.get_control_type() + " [" + window.id() + "] " + __func__
81 #define LOG_IMPL_HEADER LOG_IMPL_SCOPE_HEADER + ':'
82 
83 namespace gui2
84 {
85 
86 // ------------ WIDGET -----------{
87 
88 namespace implementation
89 {
90 /** @todo See whether this hack can be removed. */
91 // Needed to fix a compiler error in REGISTER_WIDGET.
93 {
94 public:
96  {
97  }
98 
100 
101  virtual widget* build() const override
102  {
103  return nullptr;
104  }
105 };
106 
107 } // namespace implementation
109 
110 namespace
111 {
112 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
113 const unsigned SHOW = debug_layout_graph::SHOW;
114 const unsigned LAYOUT = debug_layout_graph::LAYOUT;
115 #else
116 // values are irrelavant when DEBUG_WINDOW_LAYOUT_GRAPHS is not defined.
117 const unsigned SHOW = 0;
118 const unsigned LAYOUT = 0;
119 #endif
120 
121 /**
122  * Pushes a single draw event to the queue. To be used before calling
123  * events::pump when drawing windows.
124  *
125  * @todo: in the future we should simply call draw functions directly
126  * from events::pump and do away with the custom drawing events, but
127  * that's a 1.15 target. For now, this will have to do.
128  */
129 static void push_draw_event()
130 {
131  // DBG_GUI_E << "Pushing draw event in queue.\n";
132 
133  SDL_Event event;
135 
136  event.type = DRAW_EVENT;
137  event.user = data;
138 
139  SDL_PushEvent(&event);
140 }
141 
142 /**
143  * SDL_AddTimer() callback for delay_event.
144  *
145  * @param event The event to push in the event queue.
146  *
147  * @return The new timer interval (always 0).
148  */
149 static uint32_t delay_event_callback(const uint32_t, void* event)
150 {
151  SDL_PushEvent(static_cast<SDL_Event*>(event));
152  delete static_cast<SDL_Event*>(event);
153  return 0;
154 }
155 
156 /**
157  * Allows an event to be delayed a certain amount of time.
158  *
159  * @note the delay is the minimum time, after the time has passed the event
160  * will be pushed in the SDL event queue, so it might delay more.
161  *
162  * @param event The event to delay.
163  * @param delay The number of ms to delay the event.
164  */
165 static void delay_event(const SDL_Event& event, const uint32_t delay)
166 {
167  SDL_AddTimer(delay, delay_event_callback, new SDL_Event(event));
168 }
169 
170 /**
171  * Adds a SHOW_HELPTIP event to the SDL event queue.
172  *
173  * The event is used to show the helptip for the currently focused widget.
174  */
175 static void helptip()
176 {
177  DBG_GUI_E << "Pushing SHOW_HELPTIP_EVENT event in queue.\n";
178 
179  SDL_Event event;
181 
182  event.type = SHOW_HELPTIP_EVENT;
183  event.user = data;
184 
185  SDL_PushEvent(&event);
186 }
187 
188 /**
189  * Small helper class to get an unique id for every window instance.
190  *
191  * This is used to send event to the proper window, this allows windows to post
192  * messages to themselves and let them delay for a certain amount of time.
193  */
194 class manager
195 {
196  manager();
197 
198 public:
199  static manager& instance();
200 
201  void add(window& window);
202 
203  void remove(window& window);
204 
205  unsigned get_id(window& window);
206 
207  window* get_window(const unsigned id);
208 
209 private:
210  // The number of active window should be rather small
211  // so keep it simple and don't add a reverse lookup map.
212  std::map<unsigned, window*> windows_;
213 };
214 
216 {
217 }
218 
219 manager& manager::instance()
220 {
221  static manager window_manager;
222  return window_manager;
223 }
224 
225 void manager::add(window& win)
226 {
227  static unsigned id;
228  ++id;
229  windows_[id] = &win;
230 }
231 
232 void manager::remove(window& win)
233 {
235  itor != windows_.end();
236  ++itor) {
237 
238  if(itor->second == &win) {
239  windows_.erase(itor);
240  return;
241  }
242  }
243  assert(false);
244 }
245 
246 unsigned manager::get_id(window& win)
247 {
249  itor != windows_.end();
250  ++itor) {
251 
252  if(itor->second == &win) {
253  return itor->first;
254  }
255  }
256  assert(false);
257 
258  return 0;
259 }
260 
261 window* manager::get_window(const unsigned id)
262 {
264 
265  if(itor == windows_.end()) {
266  return nullptr;
267  } else {
268  return itor->second;
269  }
270 }
271 
272 } // namespace
273 
274 window::window(const builder_window::window_resolution& definition)
275  : panel(implementation::builder_window(::config {"definition", definition.definition}), type())
280  , owner_(nullptr)
281  , need_layout_(true)
282  , variables_()
284  , suspend_drawing_(true)
285  , restore_(true)
287  , restorer_()
288  , automatic_placement_(definition.automatic_placement)
289  , horizontal_placement_(definition.horizontal_placement)
290  , vertical_placement_(definition.vertical_placement)
291  , maximum_width_(definition.maximum_width)
292  , maximum_height_(definition.maximum_height)
293  , x_(definition.x)
294  , y_(definition.y)
295  , w_(definition.width)
296  , h_(definition.height)
297  , reevaluate_best_size_(definition.reevaluate_best_size)
298  , functions_(definition.functions)
299  , tooltip_(definition.tooltip)
300  , helptip_(definition.helptip)
301  , click_dismiss_(false)
302  , enter_disabled_(false)
303  , escape_disabled_(false)
304  , linked_size_()
305  , mouse_button_state_(0) /**< Needs to be initialized in @ref show. */
306  , dirty_list_()
307 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
308  , debug_layout_(new debug_layout_graph(this))
309 #endif
311  , exit_hook_([](window&)->bool { return true; })
312  , callback_next_draw_(nullptr)
313 {
314  manager::instance().add(*this);
315 
316  connect();
317 
318  if (!video_.faked())
319  {
320  connect_signal<event::DRAW>(std::bind(&window::draw, this));
321  }
322 
323  connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(
324  &window::signal_handler_sdl_video_resize, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5));
325 
326  connect_signal<event::SDL_ACTIVATE>(std::bind(
328 
329  connect_signal<event::SDL_LEFT_BUTTON_UP>(
331  this,
332  std::placeholders::_2,
333  std::placeholders::_3,
334  std::placeholders::_4,
335  SDL_BUTTON_LMASK),
337  connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
339  this,
340  std::placeholders::_2,
341  std::placeholders::_3,
342  std::placeholders::_4,
343  SDL_BUTTON_MMASK),
345  connect_signal<event::SDL_RIGHT_BUTTON_UP>(
347  this,
348  std::placeholders::_2,
349  std::placeholders::_3,
350  std::placeholders::_4,
351  SDL_BUTTON_RMASK),
353 
354  connect_signal<event::SDL_KEY_DOWN>(
355  std::bind(
356  &window::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6, true),
358  connect_signal<event::SDL_KEY_DOWN>(std::bind(
359  &window::signal_handler_sdl_key_down, this, std::placeholders::_2, std::placeholders::_3, std::placeholders::_5, std::placeholders::_6, false));
360 
361  connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
363  this,
364  std::placeholders::_2,
365  std::placeholders::_3,
366  std::placeholders::_5),
368 
369  connect_signal<event::MESSAGE_SHOW_HELPTIP>(
371  this,
372  std::placeholders::_2,
373  std::placeholders::_3,
374  std::placeholders::_5),
376 
377  connect_signal<event::REQUEST_PLACEMENT>(
378  std::bind(
379  &window::signal_handler_request_placement, this, std::placeholders::_2, std::placeholders::_3),
381 
382  connect_signal<event::CLOSE_WINDOW>(std::bind(&window::signal_handler_close_window, this));
383 
384  register_hotkey(hotkey::GLOBAL__HELPTIP, std::bind(gui2::helptip));
385 
386  /** @todo: should eventally become part of global hotkey handling. */
388  std::bind(&CVideo::toggle_fullscreen, std::ref(video_)));
389 }
390 
392 {
393  /*
394  * We need to delete our children here instead of waiting for the grid to
395  * automatically do it. The reason is when the grid deletes its children
396  * they will try to unregister them self from the linked widget list. At
397  * this point the member of window are destroyed and we enter UB. (For
398  * some reason the bug didn't trigger on g++ but it does on MSVC.
399  */
400  for(unsigned row = 0; row < get_grid().get_rows(); ++row) {
401  for(unsigned col = 0; col < get_grid().get_cols(); ++col) {
402  get_grid().remove_child(row, col);
403  }
404  }
405 
406  /*
407  * The tip needs to be closed if the window closes and the window is
408  * not a tip. If we don't do that the tip will unrender in the next
409  * window and cause drawing glitches.
410  * Another issue is that on smallgui and an MP game the tooltip not
411  * unrendered properly can capture the mouse and make playing impossible.
412  */
415  }
416 
417  manager::instance().remove(*this);
418 
419 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
420 
421  delete debug_layout_;
422 
423 #endif
424 }
425 
427 {
428  return manager::instance().get_window(handle);
429 }
430 
431 retval window::get_retval_by_id(const std::string& id)
432 {
433  // Note it might change to a map later depending on the number
434  // of items.
435  if(id == "ok") {
436  return retval::OK;
437  } else if(id == "cancel" || id == "quit") {
438  return retval::CANCEL;
439  } else {
440  return retval::NONE;
441  }
442 }
443 
444 void window::show_tooltip(/*const unsigned auto_close_timeout*/)
445 {
446  log_scope2(log_gui_draw, "Window: show as tooltip.");
447 
448  generate_dot_file("show", SHOW);
449 
450  assert(status_ == status::NEW);
451 
454 
456 
457  /*
458  * Before show has been called, some functions might have done some testing
459  * on the window and called layout, which can give glitches. So
460  * reinvalidate the window to avoid those glitches.
461  */
463  suspend_drawing_ = false;
464 }
465 
466 void window::show_non_modal(/*const unsigned auto_close_timeout*/)
467 {
468  log_scope2(log_gui_draw, "Window: show non modal.");
469 
470  generate_dot_file("show", SHOW);
471 
472  assert(status_ == status::NEW);
473 
475 
477 
478  /*
479  * Before show has been called, some functions might have done some testing
480  * on the window and called layout, which can give glitches. So
481  * reinvalidate the window to avoid those glitches.
482  */
484  suspend_drawing_ = false;
485 
486  push_draw_event();
487 
488  events::pump();
489 }
490 
491 int window::show(const bool restore, const unsigned auto_close_timeout)
492 {
493  /*
494  * Removes the old tip if one shown. The show_tip doesn't remove
495  * the tip, since it's the tip.
496  */
498 
500  restore_ = restore;
501 
503 
504  generate_dot_file("show", SHOW);
505 
506  assert(status_ == status::NEW);
507 
508  /*
509  * Before show has been called, some functions might have done some testing
510  * on the window and called layout, which can give glitches. So
511  * reinvalidate the window to avoid those glitches.
512  */
514  suspend_drawing_ = false;
515 
516  if(auto_close_timeout) {
517  // Make sure we're drawn before we try to close ourselves, which can
518  // happen if the timeout is small.
519  draw();
520 
521  SDL_Event event;
522  sdl::UserEvent data(CLOSE_WINDOW_EVENT, manager::instance().get_id(*this));
523 
524  event.type = CLOSE_WINDOW_EVENT;
525  event.user = data;
526 
527  delay_event(event, auto_close_timeout);
528  }
529 
530 
531  try
532  {
533  // Start our loop drawing will happen here as well.
534  bool mouse_button_state_initialized = false;
536  push_draw_event();
537 
538  // process installed callback if valid, to allow e.g. network
539  // polling
540  events::pump();
541 
542  if(!mouse_button_state_initialized) {
543  /*
544  * The state must be initialize when showing the dialog.
545  * However when initialized before this point there were random
546  * errors. This only happened when the 'click' was done fast; a
547  * slower click worked properly.
548  *
549  * So it seems the events need to be processed before SDL can
550  * return the proper button state. When initializing here all
551  * works fine.
552  */
553  mouse_button_state_ = SDL_GetMouseState(nullptr, nullptr);
554  mouse_button_state_initialized = true;
555  }
556 
559  }
560 
561  // Add a delay so we don't keep spinning if there's no event.
562  if(status_ != status::CLOSED) {
563  SDL_Delay(10);
564  }
565  }
566  }
567  catch(...)
568  {
569  /**
570  * @todo Clean up the code duplication.
571  *
572  * In the future the restoring shouldn't be needed so the duplication
573  * doesn't hurt too much but keep this todo as a reminder.
574  */
575  suspend_drawing_ = true;
576 
577  // restore area
578  if(restore_) {
579  SDL_Rect rect = get_rectangle();
580  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
582  }
583  throw;
584  }
585 
586  suspend_drawing_ = true;
587 
588  // restore area
589  if(restore_) {
590  SDL_Rect rect = get_rectangle();
591  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
593  }
594 
595  if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
596  tb->interrupt_composition();
597  }
598 
599  return retval_;
600 }
601 
603 {
604  /***** ***** ***** ***** Init ***** ***** ***** *****/
605  // Prohibited from drawing?
606  if(suspend_drawing_) {
607  return;
608  }
609 
610  surface& frame_buffer = video_.getSurface();
611 
612  /***** ***** Layout and get dirty list ***** *****/
613  if(need_layout_) {
614  // Restore old surface. In the future this phase will not be needed
615  // since all will be redrawn when needed with dirty rects. Since that
616  // doesn't work yet we need to undraw the window.
617  if(restore_ && restorer_) {
618  SDL_Rect rect = get_rectangle();
619  sdl_blit(restorer_, 0, frame_buffer, &rect);
620  }
621 
622  layout();
623 
624  // Get new surface for restoring
625  SDL_Rect rect = get_rectangle();
626 
627  // We want the labels underneath the window so draw them and use them
628  // as restore point.
629  if(is_toplevel_) {
630  font::draw_floating_labels(frame_buffer);
631  }
632 
633  if(restore_) {
634  restorer_ = get_surface_portion(frame_buffer, rect);
635  }
636 
637  // Need full redraw so only set ourselves dirty.
638  dirty_list_.emplace_back(1, this);
639  } else {
640 
641  // Let widgets update themselves, which might dirty some things.
642  layout_children();
643 
644  // Now find the widgets that are dirty.
645  std::vector<widget*> call_stack;
646  if(!new_widgets) {
647  populate_dirty_list(*this, call_stack);
648  } else {
649  /* Force to update and redraw the entire screen */
650  dirty_list_.clear();
651  dirty_list_.emplace_back(1, this);
652  }
653  }
654 
655  if (dirty_list_.empty()) {
657  return;
658  }
659 
661  if(consecutive_changed_frames_ >= 100u && id_ == "title_screen") {
662  /* The title screen has changed in 100 consecutive frames, i.e. every
663  frame for two seconds. It looks like the screen is constantly changing
664  or at least marking widgets as dirty.
665 
666  That's a severe problem. Every time the title screen changes, all
667  other GUI windows need to be fully redrawn, with huge CPU usage cost.
668  For that reason, this situation is a hard error. */
669  throw std::logic_error("The title screen is constantly changing, "
670  "which has a huge CPU usage cost. See the code comment.");
671  }
672 
673  for(auto & item : dirty_list_)
674  {
675 
676  assert(!item.empty());
677 
678  const SDL_Rect dirty_rect
680  : item.back()->get_dirty_rectangle();
681 
682 // For testing we disable the clipping rect and force the entire screen to
683 // update. This way an item rendered at the wrong place is directly visible.
684 #if 0
685  dirty_list_.clear();
686  dirty_list_.emplace_back(1, this);
687 #else
688  clip_rect_setter clip(frame_buffer, &dirty_rect);
689 #endif
690 
691  /*
692  * The actual update routine does the following:
693  * - Restore the background.
694  *
695  * - draw [begin, end) the back ground of all widgets.
696  *
697  * - draw the children of the last item in the list, if this item is
698  * a container it's children get a full redraw. If it's not a
699  * container nothing happens.
700  *
701  * - draw [rbegin, rend) the fore ground of all widgets. For items
702  * which have two layers eg window or panel it draws the foreground
703  * layer. For other widgets it's a nop.
704  *
705  * Before drawing there needs to be determined whether a dirty widget
706  * really needs to be redrawn. If the widget doesn't need to be
707  * redrawing either being not visibility::visible or has status
708  * widget::redraw_action::none. If it's not drawn it's still set not
709  * dirty to avoid it keep getting on the dirty list.
710  */
711 
712  for(std::vector<widget*>::iterator itor = item.begin();
713  itor != item.end();
714  ++itor) {
715 
716  if((**itor).get_visible() != widget::visibility::visible
717  || (**itor).get_drawing_action()
719 
720  for(std::vector<widget*>::iterator citor = itor;
721  citor != item.end();
722  ++citor) {
723 
724  (**citor).set_is_dirty(false);
725  }
726 
727  item.erase(itor, item.end());
728  break;
729  }
730  }
731 
732  // Restore.
733  if(restore_) {
734  SDL_Rect rect = get_rectangle();
735  sdl_blit(restorer_, 0, frame_buffer, &rect);
736  }
737 
738  // Background.
739  for(std::vector<widget*>::iterator itor = item.begin();
740  itor != item.end();
741  ++itor) {
742 
743  (**itor).draw_background(frame_buffer, 0, 0);
744  }
745 
746  // Children.
747  if(!item.empty()) {
748  item.back()->draw_children(frame_buffer, 0, 0);
749  }
750 
751  // Foreground.
752  for(std::vector<widget*>::reverse_iterator ritor = item.rbegin();
753  ritor != item.rend();
754  ++ritor) {
755 
756  (**ritor).draw_foreground(frame_buffer, 0, 0);
757  (**ritor).set_is_dirty(false);
758  }
759  }
760 
761  dirty_list_.clear();
762 
764 
765  std::vector<widget*> call_stack;
766  populate_dirty_list(*this, call_stack);
767  assert(dirty_list_.empty());
768 
769  if(callback_next_draw_ != nullptr) {
771  callback_next_draw_ = nullptr;
772  }
773 }
774 
776 {
777  if(restore_ && restorer_) {
778  SDL_Rect rect = get_rectangle();
779  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
780  // Since the old area might be bigger as the new one, invalidate
781  // it.
782  }
783 }
784 
786  : window_(window)
787 {
790 }
791 
793 {
796 }
797 
799 {
801  need_layout_ = true;
802  }
803 }
804 widget* window::find_at(const point& coordinate, const bool must_be_active)
805 {
806  return panel::find_at(coordinate, must_be_active);
807 }
808 
810  const bool must_be_active) const
811 {
812  return panel::find_at(coordinate, must_be_active);
813 }
814 
815 widget* window::find(const std::string& id, const bool must_be_active)
816 {
817  return container_base::find(id, must_be_active);
818 }
819 
820 const widget* window::find(const std::string& id, const bool must_be_active)
821  const
822 {
823  return container_base::find(id, must_be_active);
824 }
825 
826 void window::init_linked_size_group(const std::string& id,
827  const bool fixed_width,
828  const bool fixed_height)
829 {
830  assert(fixed_width || fixed_height);
831  assert(!has_linked_size_group(id));
832 
833  linked_size_[id] = linked_size(fixed_width, fixed_height);
834 }
835 
836 bool window::has_linked_size_group(const std::string& id)
837 {
838  return linked_size_.find(id) != linked_size_.end();
839 }
840 
841 void window::add_linked_widget(const std::string& id, widget* wgt)
842 {
843  assert(wgt);
844  if(!has_linked_size_group(id)) {
845  ERR_GUI << "Unknown linked group '" << id << "'; skipping\n";
846  return;
847  }
848 
849  std::vector<widget*>& widgets = linked_size_[id].widgets;
850  if(std::find(widgets.begin(), widgets.end(), wgt) == widgets.end()) {
851  widgets.push_back(wgt);
852  }
853 }
854 
855 void window::remove_linked_widget(const std::string& id, const widget* wgt)
856 {
857  assert(wgt);
858  if(!has_linked_size_group(id)) {
859  return;
860  }
861 
862  std::vector<widget*>& widgets = linked_size_[id].widgets;
863 
865  = std::find(widgets.begin(), widgets.end(), wgt);
866 
867  if(itor != widgets.end()) {
868  widgets.erase(itor);
869 
870  assert(std::find(widgets.begin(), widgets.end(), wgt)
871  == widgets.end());
872  }
873 }
874 
876 {
877  /***** Initialize. *****/
878 
879  const auto conf = cast_config_to<window_definition>();
880  assert(conf);
881 
883 
885  const point mouse = get_mouse_position();
886 
887  variables_.add("mouse_x", wfl::variant(mouse.x));
888  variables_.add("mouse_y", wfl::variant(mouse.y));
889  variables_.add("window_width", wfl::variant(0));
890  variables_.add("window_height", wfl::variant(0));
891  variables_.add("best_window_width", wfl::variant(size.x));
892  variables_.add("best_window_height", wfl::variant(size.y));
893  variables_.add("size_request_mode", wfl::variant("maximum"));
894 
896 
897  unsigned int maximum_width = maximum_width_(variables_, &functions_);
898  unsigned int maximum_height = maximum_height_(variables_, &functions_);
899 
901  if(maximum_width == 0 || maximum_width > settings::screen_width) {
902  maximum_width = settings::screen_width;
903  }
904 
905  if(maximum_height == 0 || maximum_height > settings::screen_height) {
906  maximum_height = settings::screen_height;
907  }
908  } else {
909  maximum_width = w_(variables_, &functions_);
910  maximum_height = h_(variables_, &functions_);
911  }
912 
913  /***** Handle click dismiss status. *****/
914  button* click_dismiss_button = nullptr;
915  if((click_dismiss_button
916  = find_widget<button>(this, "click_dismiss", false, false))) {
917 
918  click_dismiss_button->set_visible(widget::visibility::invisible);
919  }
920  if(click_dismiss_) {
921  button* btn = find_widget<button>(this, "ok", false, false);
922  if(btn) {
924  click_dismiss_button = btn;
925  }
926  VALIDATE(click_dismiss_button,
927  _("Click dismiss needs a 'click_dismiss' or 'ok' button."));
928  }
929 
930  /***** Layout. *****/
931  layout_initialize(true);
932  generate_dot_file("layout_initialize", LAYOUT);
933 
935 
936  try
937  {
938  window_implementation::layout(*this, maximum_width, maximum_height);
939  }
940  catch(const layout_exception_resize_failed&)
941  {
942 
943  /** @todo implement the scrollbars on the window. */
944 
945  std::stringstream sstr;
946  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
947  << "' found the following problem: Failed to size window;"
948  << " wanted size " << get_best_size() << " available size "
949  << maximum_width << ',' << maximum_height << " screen size "
951 
952  throw wml_exception(_("Failed to show a dialog, "
953  "which doesn't fit on the screen."),
954  sstr.str());
955  }
956 
957  /****** Validate click dismiss status. *****/
959  assert(click_dismiss_button);
960  click_dismiss_button->set_visible(widget::visibility::visible);
961 
963  *click_dismiss_button,
964  std::bind(&window::set_retval, this, retval::OK, true));
965 
966  layout_initialize(true);
967  generate_dot_file("layout_initialize", LAYOUT);
968 
970 
971  try
972  {
974  *this, maximum_width, maximum_height);
975  }
976  catch(const layout_exception_resize_failed&)
977  {
978 
979  /** @todo implement the scrollbars on the window. */
980 
981  std::stringstream sstr;
982  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
983  << "' found the following problem: Failed to size window;"
984  << " wanted size " << get_best_size() << " available size "
985  << maximum_width << ',' << maximum_height << " screen size "
987  << '.';
988 
989  throw wml_exception(_("Failed to show a dialog, "
990  "which doesn't fit on the screen."),
991  sstr.str());
992  }
993  }
994 
995  /***** Get the best location for the window *****/
996  size = get_best_size();
997  /* Although 0-size windows might not seem valid/useful, there are
998  a handful of windows that request 0 size just to get a position
999  chosen via the code below, so at least for now allow them:
1000  */
1001  assert(size.x >= 0 && static_cast<unsigned>(size.x) <= maximum_width
1002  && size.y >= 0 && static_cast<unsigned>(size.y) <= maximum_height);
1003 
1004  point origin(0, 0);
1005 
1006  if(automatic_placement_) {
1007 
1008  switch(horizontal_placement_) {
1010  // Do nothing
1011  break;
1013  origin.x = (settings::screen_width - size.x) / 2;
1014  break;
1016  origin.x = settings::screen_width - size.x;
1017  break;
1018  default:
1019  assert(false);
1020  }
1021  switch(vertical_placement_) {
1023  // Do nothing
1024  break;
1026  origin.y = (settings::screen_height - size.y) / 2;
1027  break;
1029  origin.y = settings::screen_height - size.y;
1030  break;
1031  default:
1032  assert(false);
1033  }
1034  } else {
1035 
1036  variables_.add("window_width", wfl::variant(size.x));
1037  variables_.add("window_height", wfl::variant(size.y));
1038 
1040  layout_initialize(true);
1041 
1044  h_(variables_, &functions_));
1045 
1046  size = get_best_size();
1047  variables_.add("window_width", wfl::variant(size.x));
1048  variables_.add("window_height", wfl::variant(size.y));
1049  }
1050 
1051  variables_.add("size_request_mode", wfl::variant("size"));
1052 
1053  size.x = w_(variables_, &functions_);
1054  size.y = h_(variables_, &functions_);
1055 
1056  variables_.add("window_width", wfl::variant(size.x));
1057  variables_.add("window_height", wfl::variant(size.y));
1058 
1059  origin.x = x_(variables_, &functions_);
1060  origin.y = y_(variables_, &functions_);
1061  }
1062 
1063  /***** Set the window size *****/
1064  place(origin, size);
1065 
1066  generate_dot_file("layout_finished", LAYOUT);
1067  need_layout_ = false;
1068 
1070 }
1071 
1073 {
1074  // evaluate the group sizes
1075  for(auto & linked_size : linked_size_)
1076  {
1077 
1078  point max_size(0, 0);
1079 
1080  // Determine the maximum size.
1081  for(auto widget : linked_size.second.widgets)
1082  {
1083 
1084  const point size = widget->get_best_size();
1085 
1086  if(size.x > max_size.x) {
1087  max_size.x = size.x;
1088  }
1089  if(size.y > max_size.y) {
1090  max_size.y = size.y;
1091  }
1092  }
1093  if(linked_size.second.width != -1) {
1094  linked_size.second.width = max_size.x;
1095  }
1096  if(linked_size.second.height != -1) {
1097  linked_size.second.height = max_size.y;
1098  }
1099 
1100  // Set the maximum size.
1101  for(auto widget : linked_size.second.widgets)
1102  {
1103 
1105 
1106  if(linked_size.second.width != -1) {
1107  size.x = max_size.x;
1108  }
1109  if(linked_size.second.height != -1) {
1110  size.y = max_size.y;
1111  }
1112 
1113  widget->set_layout_size(size);
1114  }
1115  }
1116 }
1117 
1118 bool window::click_dismiss(const int mouse_button_mask)
1119 {
1120  if(does_click_dismiss()) {
1121  if((mouse_button_state_ & mouse_button_mask) == 0) {
1123  } else {
1124  mouse_button_state_ &= ~mouse_button_mask;
1125  }
1126  return true;
1127  }
1128  return false;
1129 }
1130 
1131 namespace
1132 {
1133 
1134 /**
1135  * Swaps an item in a grid for another one.
1136  * This differs slightly from the standard swap_grid utility, so it's defined by itself here.
1137  */
1138 void window_swap_grid(grid* g,
1139  grid* content_grid,
1140  widget* widget,
1141  const std::string& id)
1142 {
1143  assert(content_grid);
1144  assert(widget);
1145 
1146  // Make sure the new child has same id.
1147  widget->set_id(id);
1148 
1149  // Get the container containing the wanted widget.
1150  grid* parent_grid = nullptr;
1151  if(g) {
1152  parent_grid = find_widget<grid>(g, id, false, false);
1153  }
1154  if(!parent_grid) {
1155  parent_grid = find_widget<grid>(content_grid, id, true, false);
1156  assert(parent_grid);
1157  }
1158  if(grid* grandparent_grid = dynamic_cast<grid*>(parent_grid->parent())) {
1159  grandparent_grid->swap_child(id, widget, false);
1160  } else if(container_base* c
1161  = dynamic_cast<container_base*>(parent_grid->parent())) {
1162 
1163  c->get_grid().swap_child(id, widget, true);
1164  } else {
1165  assert(false);
1166  }
1167 }
1168 
1169 } // namespace
1170 
1172 {
1173  std::vector<dispatcher*>& dispatchers = event::get_all_dispatchers();
1174  auto me = std::find(dispatchers.begin(), dispatchers.end(), this);
1175 
1176  for(auto it = std::next(me); it != dispatchers.end(); ++it) {
1177  // Note that setting an entire window dirty like this is expensive.
1178  dynamic_cast<widget&>(**it).set_is_dirty(true);
1179  }
1180 }
1181 
1182 void window::finalize(const builder_grid& content_grid)
1183 {
1184  window_swap_grid(nullptr, &get_grid(), content_grid.build(), "_window_content_grid");
1185 }
1186 
1187 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1188 
1189 void window::generate_dot_file(const std::string& generator,
1190  const unsigned domain)
1191 {
1192  debug_layout_->generate_dot_file(generator, domain);
1193 }
1194 #endif
1195 
1197  const unsigned maximum_width,
1198  const unsigned maximum_height)
1199 {
1201 
1202  /*
1203  * For now we return the status, need to test later whether this can
1204  * entirely be converted to an exception based system as in 'promised' on
1205  * the algorithm page.
1206  */
1207 
1208  try
1209  {
1210  point size = window.get_best_size();
1211 
1212  DBG_GUI_L << LOG_IMPL_HEADER << " best size : " << size
1213  << " maximum size : " << maximum_width << ','
1214  << maximum_height << ".\n";
1215  if(size.x <= static_cast<int>(maximum_width)
1216  && size.y <= static_cast<int>(maximum_height)) {
1217 
1218  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Fits, nothing to do.\n";
1219  return;
1220  }
1221 
1222  if(size.x > static_cast<int>(maximum_width)) {
1223  window.reduce_width(maximum_width);
1224 
1225  size = window.get_best_size();
1226  if(size.x > static_cast<int>(maximum_width)) {
1227  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize width failed."
1228  << " Wanted width " << maximum_width
1229  << " resulting width " << size.x << ".\n";
1231  }
1233  << " Status: Resize width succeeded.\n";
1234  }
1235 
1236  if(size.y > static_cast<int>(maximum_height)) {
1237  window.reduce_height(maximum_height);
1238 
1239  size = window.get_best_size();
1240  if(size.y > static_cast<int>(maximum_height)) {
1241  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize height failed."
1242  << " Wanted height " << maximum_height
1243  << " resulting height " << size.y << ".\n";
1245  }
1247  << " Status: Resize height succeeded.\n";
1248  }
1249 
1250  assert(size.x <= static_cast<int>(maximum_width)
1251  && size.y <= static_cast<int>(maximum_height));
1252 
1253 
1254  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resizing succeeded.\n";
1255  return;
1256  }
1257  catch(const layout_exception_width_modified&)
1258  {
1260  << " Status: Width has been modified, rerun.\n";
1261 
1262  window.layout_initialize(false);
1263  window.layout_linked_widgets();
1264  layout(window, maximum_width, maximum_height);
1265  return;
1266  }
1267 }
1268 
1269 void window::mouse_capture(const bool capture)
1270 {
1271  assert(event_distributor_);
1272  event_distributor_->capture_mouse(capture);
1273 }
1274 
1276 {
1277  assert(event_distributor_);
1278  event_distributor_->keyboard_capture(widget);
1279 }
1280 
1282 {
1283  assert(event_distributor_);
1284  event_distributor_->keyboard_add_to_chain(widget);
1285 }
1286 
1288 {
1289  assert(event_distributor_);
1290  event_distributor_->keyboard_remove_from_chain(widget);
1291 }
1292 
1294 {
1295  if(std::find(tab_order.begin(), tab_order.end(), widget) != tab_order.end()) {
1296  return;
1297  }
1298  assert(event_distributor_);
1299  if(tab_order.empty() && !event_distributor_->keyboard_focus()) {
1300  keyboard_capture(widget);
1301  }
1302  if(at < 0 || at >= static_cast<int>(tab_order.size())) {
1303  tab_order.push_back(widget);
1304  } else {
1305  tab_order.insert(tab_order.begin() + at, widget);
1306  }
1307 }
1308 
1310  bool& handled,
1311  const point& new_size)
1312 {
1313  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1314 
1317  settings::screen_width = new_size.x;
1318  settings::screen_height = new_size.y;
1320 
1321  handled = true;
1322 }
1323 
1325  bool& handled,
1326  bool& halt,
1327  const int mouse_button_mask)
1328 {
1329  DBG_GUI_E << LOG_HEADER << ' ' << event << " mouse_button_mask "
1330  << static_cast<unsigned>(mouse_button_mask) << ".\n";
1331 
1332  handled = halt = click_dismiss(mouse_button_mask);
1333 }
1334 
1335 static bool is_active(const widget* wgt)
1336 {
1337  if(const styled_widget* control = dynamic_cast<const styled_widget*>(wgt)) {
1338  return control->get_active() && control->get_visible() == widget::visibility::visible;
1339  }
1340  return false;
1341 }
1342 
1344  bool& handled,
1345  const SDL_Keycode key,
1346  const SDL_Keymod mod,
1347  bool handle_tab)
1348 {
1349  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1350 
1351  if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
1352  if(tb->is_composing()) {
1353  if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1354  tb->interrupt_composition();
1355  } else {
1356  return;
1357  }
1358  }
1359  }
1360  if(!enter_disabled_ && (key == SDLK_KP_ENTER || key == SDLK_RETURN)) {
1362  handled = true;
1363  } else if(key == SDLK_ESCAPE && !escape_disabled_) {
1365  handled = true;
1366  } else if(key == SDLK_SPACE) {
1367  handled = click_dismiss(0);
1368  } else if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1369  assert(event_distributor_);
1370  widget* focus = event_distributor_->keyboard_focus();
1371  auto iter = std::find(tab_order.begin(), tab_order.end(), focus);
1372  do {
1373  if(mod & KMOD_SHIFT) {
1374  if(iter == tab_order.begin()) {
1375  iter = tab_order.end();
1376  }
1377  iter--;
1378  } else {
1379  if(iter == tab_order.end()) {
1380  iter = tab_order.begin();
1381  } else {
1382  iter++;
1383  if(iter == tab_order.end()) {
1384  iter = tab_order.begin();
1385  }
1386  }
1387  }
1388  } while(!is_active(*iter));
1389  keyboard_capture(*iter);
1390  handled = true;
1391  }
1392 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1393  if(key == SDLK_F12) {
1394  debug_layout_->generate_dot_file("manual", debug_layout_graph::MANUAL);
1395  handled = true;
1396  }
1397 #endif
1398 }
1399 
1401  bool& handled,
1402  const event::message& message)
1403 {
1404  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1405 
1406  const event::message_show_tooltip& request
1407  = dynamic_cast<const event::message_show_tooltip&>(message);
1408 
1409  dialogs::tip::show(tooltip_.id, request.message, request.location, request.source_rect);
1410 
1411  handled = true;
1412 }
1413 
1415  bool& handled,
1416  const event::message& message)
1417 {
1418  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1419 
1420  const event::message_show_helptip& request
1421  = dynamic_cast<const event::message_show_helptip&>(message);
1422 
1423  dialogs::tip::show(helptip_.id, request.message, request.location, request.source_rect);
1424 
1425  handled = true;
1426 }
1427 
1429  bool& handled)
1430 {
1431  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1432 
1434 
1435  handled = true;
1436 }
1437 
1439 {
1441 }
1442 
1443 // }---------- DEFINITION ---------{
1444 
1447 {
1448  DBG_GUI_P << "Parsing window " << id << '\n';
1449 
1450  load_resolutions<resolution>(cfg);
1451 }
1452 
1454  : panel_definition::resolution(cfg), grid(nullptr)
1455 {
1456  const config& child = cfg.child("grid");
1457  // VALIDATE(child, _("No grid defined."));
1458 
1459  /** @todo Evaluate whether the grid should become mandatory. */
1460  if(child) {
1461  grid = std::make_shared<builder_grid>(child);
1462  }
1463 }
1464 
1465 // }------------ END --------------
1466 
1467 } // namespace gui2
1468 
1469 
1470 /**
1471  * @page layout_algorithm Layout algorithm
1472  *
1473  * @section introduction-layout_algorithm Introduction
1474  *
1475  * This page describes how the layout engine for the dialogs works. First
1476  * a global overview of some terms used in this document.
1477  *
1478  * - @ref gui2::widget "Widget"; Any item which can be used in the widget
1479  * toolkit. Not all widgets are visible. In general widgets cannot be
1480  * sized directly, but this is controlled by a window. A widget has an
1481  * internal size cache and if the value in the cache is not equal to 0,0
1482  * that value is its best size. This value gets set when the widget can
1483  * honor a resize request. It will be set with the value which honors
1484  * the request.
1485  *
1486  * - @ref gui2::grid "Grid"; A grid is an invisible container which holds
1487  * one or more widgets. Several widgets have a grid in them to hold
1488  * multiple widgets eg panels and windows.
1489  *
1490  * - @ref gui2::grid::child "Grid cell"; Every widget which is in a grid is
1491  * put in a grid cell. These cells also hold the information about the gaps
1492  * between widgets the behavior on growing etc. All grid cells must have a
1493  * widget inside them.
1494  *
1495  * - @ref gui2::window "Window"; A window is a top level item which has a
1496  * grid with its children. The window handles the sizing of the window and
1497  * makes sure everything fits.
1498  *
1499  * - @ref gui2::window::linked_size "Shared size group"; A shared size
1500  * group is a number of widgets which share width and or height. These
1501  * widgets are handled separately in the layout algorithm. All grid cells
1502  * width such a widget will get the same height and or width and these
1503  * widgets won't be resized when there's not enough size. To be sure that
1504  * these widgets don't cause trouble for the layout algorithm, they must be
1505  * in a container with scrollbars so there will always be a way to properly
1506  * layout them. The engine must enforce this restriction so the shared
1507  * layout property must be set by the engine after validation.
1508  *
1509  * - All visible grid cells; A grid cell is visible when the widget inside
1510  * of it doesn't have the state visibility::invisible. Widgets which have the
1511  * state visibility::hidden are sized properly since when they become
1512  * visibility::visible the layout shouldn't be invalidated. A grid cell
1513  * that's invisible has size 0,0.
1514  *
1515  * - All resizable grid cells; A grid cell is resizable under the following
1516  * conditions:
1517  * - The widget is visibility::visible.
1518  * - The widget is not in a shared size group.
1519  *
1520  * There are two layout algorithms with a different purpose.
1521  *
1522  * - The Window algorithm; this algorithm's goal is it to make sure all grid
1523  * cells fit in the window. Sizing the grid cells depends on the widget
1524  * size as well, but this algorithm only sizes the grid cells and doesn't
1525  * handle the widgets inside them.
1526  *
1527  * - The Grid algorithm; after the Window algorithm made sure that all grid
1528  * cells fit this algorithm makes sure the widgets are put in the optimal
1529  * state in their grid cell.
1530  *
1531  * @section layout_algorithm_window Window
1532  *
1533  * Here is the algorithm used to layout the window:
1534  *
1535  * - Perform a full initialization
1536  * (@ref gui2::widget::layout_initialize (full_initialization = true)):
1537  * - Clear the internal best size cache for all widgets.
1538  * - For widgets with scrollbars hide them unless the
1539  * @ref gui2::scrollbar_container::scrollbar_mode "scrollbar_mode" is
1540  * ALWAYS_VISIBLE or AUTO_VISIBLE.
1541  * - Handle shared sizes:
1542  * - Height and width:
1543  * - Get the best size for all widgets that share height and width.
1544  * - Set the maximum of width and height as best size for all these
1545  * widgets.
1546  * - Width only:
1547  * - Get the best width for all widgets which share their width.
1548  * - Set the maximum width for all widgets, but keep their own height.
1549  * - Height only:
1550  * - Get the best height for all widgets which share their height.
1551  * - Set the maximum height for all widgets, but keep their own width.
1552  * - Start layout loop:
1553  * - Get best size.
1554  * - If width <= maximum_width && height <= maximum_height we're done.
1555  * - If width > maximum_width, optimize the width:
1556  * - For every grid cell in a grid row there will be a resize request
1557  * (@ref gui2::grid::reduce_width):
1558  * - Sort the widgets in the row on the resize priority.
1559  * - Loop through this priority queue until the row fits
1560  * - If priority != 0 try to share the extra width else all
1561  * widgets are tried to reduce the full size.
1562  * - Try to shrink the widgets by either wrapping or using a
1563  * scrollbar (@ref gui2::widget::request_reduce_width).
1564  * - If the row fits in the wanted width this row is done.
1565  * - Else try the next priority.
1566  * - All priorities done and the width still doesn't fit.
1567  * - Loop through this priority queue until the row fits.
1568  * - If priority != 0:
1569  * - try to share the extra width
1570  * -Else:
1571  * - All widgets are tried to reduce the full size.
1572  * - Try to shrink the widgets by sizing them smaller as really
1573  * wanted (@ref gui2::widget::demand_reduce_width).
1574  * For labels, buttons etc. they get ellipsized.
1575  * - If the row fits in the wanted width this row is done.
1576  * - Else try the next priority.
1577  * - All priorities done and the width still doesn't fit.
1578  * - Throw a layout width doesn't fit exception.
1579  * - If height > maximum_height, optimize the height
1580  * (@ref gui2::grid::reduce_height):
1581  * - For every grid cell in a grid column there will be a resize request:
1582  * - Sort the widgets in the column on the resize priority.
1583  * - Loop through this priority queue until the column fits:
1584  * - If priority != 0 try to share the extra height else all
1585  * widgets are tried to reduce the full size.
1586  * - Try to shrink the widgets by using a scrollbar
1587  * (@ref gui2::widget::request_reduce_height).
1588  * - If succeeded for a widget the width is influenced and the
1589  * width might be invalid.
1590  * - Throw a width modified exception.
1591  * - If the column fits in the wanted height this column is done.
1592  * - Else try the next priority.
1593  * - All priorities done and the height still doesn't fit.
1594  * - Loop through this priority queue until the column fits.
1595  * - If priority != 0 try to share the extra height else all
1596  * widgets are tried to reduce the full size.
1597  * - Try to shrink the widgets by sizing them smaller as really
1598  * wanted (@ref gui2::widget::demand_reduce_width).
1599  * For labels, buttons etc. they get ellipsized .
1600  * - If the column fits in the wanted height this column is done.
1601  * - Else try the next priority.
1602  * - All priorities done and the height still doesn't fit.
1603  * - Throw a layout height doesn't fit exception.
1604  * - End layout loop.
1605  *
1606  * - Catch @ref gui2::layout_exception_width_modified "width modified":
1607  * - Goto relayout.
1608  *
1609  * - Catch
1610  * @ref gui2::layout_exception_width_resize_failed "width resize failed":
1611  * - If the window has a horizontal scrollbar which isn't shown but can be
1612  * shown.
1613  * - Show the scrollbar.
1614  * - goto relayout.
1615  * - Else show a layout failure message.
1616  *
1617  * - Catch
1618  * @ref gui2::layout_exception_height_resize_failed "height resize failed":
1619  * - If the window has a vertical scrollbar which isn't shown but can be
1620  * shown:
1621  * - Show the scrollbar.
1622  * - goto relayout.
1623  * - Else:
1624  * - show a layout failure message.
1625  *
1626  * - Relayout:
1627  * - Initialize all widgets
1628  * (@ref gui2::widget::layout_initialize (full_initialization = false))
1629  * - Handle shared sizes, since the reinitialization resets that state.
1630  * - Goto start layout loop.
1631  *
1632  * @section grid Grid
1633  *
1634  * This section will be documented later.
1635  */
Define the common log macros for the gui toolkit.
bool click_dismiss_
Do we want to have easy close behavior?
Definition: window.hpp:546
const std::string message
The message to show on the tooltip.
Definition: message.hpp:66
void keyboard_capture(widget *widget)
Definition: window.cpp:1275
Dialog was closed with the CANCEL button.
Definition: retval.hpp:37
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:24
#define DBG_GUI_P
Definition: log.hpp:65
wfl::function_symbol_table functions_
The formula definitions available for the calculation formulas.
Definition: window.hpp:521
void remove()
Removes a tip.
Definition: tooltip.cpp:174
Defines the exception classes for the layout algorithm.
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:1324
#define ERR_GUI
Definition: window.cpp:74
virtual grid * build() const override
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:414
void finalize(const builder_grid &content_grid)
Finishes the initialization of the grid.
Definition: window.cpp:1182
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:503
bool does_click_dismiss() const
Does the window close easily?
Definition: window.hpp:272
std::function< bool(window &)> exit_hook_
Definition: window.hpp:763
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:1343
#define DBG_GUI_L
Definition: log.hpp:54
static lg::log_domain log_gui("gui/layout")
void set_layout_size(const point &size)
Definition: widget.cpp:334
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: window.cpp:804
typed_formula< unsigned > h_
The formula to calculate the height of the dialog.
Definition: window.hpp:515
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.
const unsigned horizontal_placement_
Sets the horizontal placement.
Definition: window.hpp:489
virtual void place(const point &origin, const point &size) override
See widget::place.
const grid & get_grid() const
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:57
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
Main class to show messages to the user.
Definition: message.hpp:34
void register_hotkey(const hotkey::HOTKEY_COMMAND id, const hotkey_function &function)
Registers a hotkey.
Definition: dispatcher.cpp:141
CVideo & video_
Needed so we can change what&#39;s drawn on the screen.
Definition: window.hpp:436
The window is new and not yet shown.
void show_non_modal()
Shows the window non modal.
Definition: window.cpp:466
void generate_dot_file(const std::string &, const unsigned)
Definition: window.hpp:673
Helper struct to force widgets the have the same size.
Definition: window.hpp:561
#define LOG_SCOPE_HEADER
Definition: window.cpp:76
const std::string & id() const
Definition: widget.cpp:109
unsigned int get_rows() const
Definition: grid.hpp:307
unsigned int consecutive_changed_frames_
In how many consecutive frames the window has changed.
Definition: window.hpp:652
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:602
SDL_Rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:309
The message for MESSAGE_SHOW_HELPTIP.
Definition: message.hpp:76
Base class for all widgets.
Definition: widget.hpp:48
bool is_in_dialog()
Is a dialog open?
Definition: handler.cpp:1115
#define DRAW_EVENT
Definition: events.hpp:26
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:53
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:1309
#define LOG_IMPL_HEADER
Definition: window.cpp:81
static CVideo & get_singleton()
Definition: video.hpp:48
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
virtual widget * build() const override
Definition: window.cpp:101
Contains the event distributor.
unsigned gamemap_width
The size of the map area, if not available equal to the screen size.
Definition: settings.cpp:33
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1281
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2157
typed_formula< unsigned > w_
The formula to calculate the width of the dialog.
Definition: window.hpp:512
static std::string _(const char *str)
Definition: gettext.hpp:92
void signal_handler_message_show_tooltip(const event::ui_event event, bool &handled, const event::message &message)
Definition: window.cpp:1400
Definitions for the interface to Wesnoth Markup Language (WML).
const point location
The location where to show the helptip.
Definition: message.hpp:87
std::string id_
The id is the unique name of the widget in a certain context.
Definition: widget.hpp:177
surface & getSurface()
Returns a reference to the framebuffer.
Definition: video.cpp:482
int mouse_button_state_
The state of the mouse button.
Definition: window.hpp:629
void get_screen_size_variables(wfl::map_formula_callable &variable)
Gets a formula object with the screen size.
Definition: helper.cpp:99
int x
x coordinate.
Definition: point.hpp:44
void init_mouse_location()
Initializes the location of the mouse.
Definition: handler.cpp:910
Generic file dialog.
Definition: field-fwd.hpp:22
int width
The current width of all widgets in the group, -1 if the width is not linked.
Definition: window.hpp:572
The message callbacks hold a reference to a message.
Definition: message.hpp:45
#define CLOSE_WINDOW_EVENT
Definition: events.hpp:27
static bool is_active(const widget *wgt)
Definition: window.cpp:1335
The event handler class for the widget library.
Base container class.
Definition: grid.hpp:30
void connect()
Connects the dispatcher to the event handler.
Definition: dispatcher.cpp:50
static std::string at(const std::string &file, int line)
void populate_dirty_list(window &caller, std::vector< widget *> &call_stack)
Adds a widget to the dirty list if it is dirty.
Definition: widget.cpp:416
status status_
The status of the window.
Definition: window.hpp:439
#define LOG_HEADER
Definition: window.cpp:77
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:86
std::vector< widget * > widgets
The widgets linked.
Definition: window.hpp:569
surface restorer_
When the window closes this surface is used to undraw the window.
Definition: window.hpp:478
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:192
The dialog was closed automatically as its timeout had been reached.
Definition: retval.hpp:40
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
bool suspend_drawing_
Avoid drawing the window.
Definition: window.hpp:469
This file contains the settings handling of the widget library.
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:252
bool enter_disabled_
Disable the enter key see our setter for more info.
Definition: window.hpp:549
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:466
void set_visible(const visibility visible)
Definition: widget.cpp:475
bool need_layout_
When set the form needs a full layout redraw cycle.
Definition: window.hpp:460
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:171
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:465
void signal_handler_close_window()
Definition: window.cpp:1438
typed_formula< bool > reevaluate_best_size_
The formula to determine whether the size is good.
Definition: window.hpp:518
builder_window::window_resolution::tooltip_info helptip_
The settings for the helptip.
Definition: window.hpp:527
bool is_toplevel_
Whether the window has other windows behind it.
Definition: window.hpp:475
unsigned gamemap_height
Definition: settings.cpp:34
widget * parent()
Definition: widget.cpp:159
std::vector< dispatcher * > & get_all_dispatchers()
Gets all event dispatchers in the Z order.
Definition: handler.cpp:904
The widget is not visible.
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:58
static thread_local std::deque< std::string > call_stack
For printing error messages when WFL parsing or evaluation fails, this contains the names of the WFL ...
Definition: function.cpp:46
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:313
typed_formula< unsigned > y_
The formula to calculate the y value of the dialog.
Definition: window.hpp:509
Exception thrown when the width resizing has failed.
Basic template class to generate new items.
#define log_scope2(domain, description)
Definition: log.hpp:207
const bool automatic_placement_
Do we wish to place the widget automatically?
Definition: window.hpp:481
typed_formula< unsigned > maximum_width_
The maximum width if automatic_placement_ is true.
Definition: window.hpp:500
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:826
typed_formula< unsigned > x_
The formula to calculate the x value of the dialog.
Definition: window.hpp:506
std::vector< std::vector< widget * > > dirty_list_
The list with dirty items in the window.
Definition: window.hpp:645
void set_mouse_behavior(const mouse_behavior mouse_behavior)
Definition: dispatcher.hpp:828
bool escape_disabled_
Disable the escape key see our setter for more info.
Definition: window.hpp:552
void pump()
Definition: events.cpp:472
The window has been requested to be closed but still needs to evaluate the request.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
static window * window_instance(const unsigned handle)
Returns the instance of a window.
Definition: window.cpp:426
void add_linked_widget(const std::string &id, widget *widget)
Adds a widget to a linked size group.
Definition: window.cpp:841
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: window.cpp:815
Helper class, don&#39;t construct this directly.
static const unsigned VERTICAL_ALIGN_TOP
Definition: grid.hpp:49
void remove_from_keyboard_chain(widget *widget)
Remove the widget from the keyboard chain.
Definition: window.cpp:1287
bool faked() const
Definition: video.hpp:65
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:214
int show(const bool restore=true, const unsigned auto_close_timeout=0)
Shows the window.
Definition: window.cpp:491
std::map< unsigned, window * > windows_
Definition: window.cpp:212
#define DBG_GUI_E
Definition: log.hpp:34
Default, unset return value.
Definition: retval.hpp:31
#define SHOW_HELPTIP_EVENT
Definition: events.hpp:28
virtual void layout_initialize(const bool full_initialization) override
See widget::layout_initialize.
void toggle_fullscreen()
Definition: video.cpp:566
The user set the widget invisible, that means:
window * get_window()
Get the parent window.
Definition: widget.cpp:116
double g
Definition: astarsearch.cpp:64
const unsigned vertical_placement_
Sets the vertical placement.
Definition: window.hpp:497
void undraw_floating_labels(surface screen)
std::vector< widget * > tab_order
List of widgets in the tabbing order.
Definition: window.hpp:582
static const unsigned VERTICAL_ALIGN_CENTER
Definition: grid.hpp:50
bool has_linked_size_group(const std::string &id)
Is the linked size group defined for this window?
Definition: window.cpp:836
bool click_dismiss(const int mouse_button_mask)
Handles a mouse click event for dismissing the dialog.
Definition: window.cpp:1118
static const unsigned HORIZONTAL_ALIGN_CENTER
Definition: grid.hpp:57
Holds a 2D point.
Definition: point.hpp:23
unsigned screen_width
The screen resolution and pixel pitch should be available for all widgets since their drawing method ...
Definition: settings.cpp:28
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:357
lg::log_domain log_gui_draw("gui/draw")
Definition: log.hpp:27
Base class for all visible items.
A generic container base class.
void signal_handler_request_placement(const event::ui_event event, bool &handled)
Definition: window.cpp:1428
builder_window::window_resolution::tooltip_info tooltip_
The settings for the tooltip.
Definition: window.hpp:524
show_mode show_mode_
The mode in which the window is shown.
Definition: window.hpp:446
static void layout(window &window, const unsigned maximum_width, const unsigned maximum_height)
Layouts the window.
Definition: window.cpp:1196
window_definition(const config &cfg)
Definition: window.cpp:1445
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition: grid.hpp:51
static retval get_retval_by_id(const std::string &id)
Gets the retval for the default buttons.
Definition: window.cpp:431
#define next(ls)
Definition: llex.cpp:32
point get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:117
void undraw()
Undraws the window.
Definition: window.cpp:775
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:140
void show_tooltip()
Shows the window as a tooltip.
Definition: window.cpp:444
unsigned screen_height
Definition: settings.cpp:29
Definition: contexts.hpp:43
void set_id(const std::string &id)
Definition: widget.cpp:97
void mouse_capture(const bool capture=true)
Definition: window.cpp:1269
bool disable_click_dismiss() const override
See widget::disable_click_dismiss.
bool restore_
Whether the window should undraw the window using restorer_.
Definition: window.hpp:472
void redraw_windows_on_top() const
Schedules windows on top of us (if any) to redraw.
Definition: window.cpp:1171
void layout()
Layouts the window.
Definition: window.cpp:875
Simple push button.
Definition: button.hpp:35
std::unique_ptr< event::distributor > event_distributor_
Definition: window.hpp:678
void signal_handler_message_show_helptip(const event::ui_event event, bool &handled, const event::message &message)
Definition: window.cpp:1414
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:798
friend class debug_layout_graph
Definition: window.hpp:66
dialogs::modal_dialog * owner_
The dialog that owns the window.
Definition: window.hpp:452
retval
Default window/dialog return values.
Definition: retval.hpp:28
virtual void layout_children() override
See widget::layout_children.
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition: grid.hpp:56
std::unique_ptr< window > build(const builder_window::window_resolution &definition)
Builds a window.
Dialog was closed with the OK button.
Definition: retval.hpp:34
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:855
map_location coordinate
Contains an x and y coordinate used for starting positions in maps.
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:31
int height
The current height of all widgets in the group, -1 if the height is not linked.
Definition: window.hpp:575
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
void add_to_tab_order(widget *widget, int at=-1)
Add the widget to the tabbing order.
Definition: window.cpp:1293
mock_char c
SDL_Rect screen_area(bool as_pixels=true) const
Returns the current window renderer area, either in pixels or screen coordinates. ...
Definition: video.cpp:270
void set_want_keyboard_input(const bool want_keyboard_input)
Definition: dispatcher.hpp:838
std::shared_ptr< halo_record > handle
Definition: halo.hpp:29
int y
y coordinate.
Definition: point.hpp:47
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:64
#define LOG_IMPL_SCOPE_HEADER
Definition: window.cpp:79
std::map< std::string, linked_size > linked_size_
List of the widgets, whose size are linked together.
Definition: window.hpp:579
wfl::map_formula_callable variables_
The variables of the canvas.
Definition: window.hpp:463
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
void initialize_state()
Initializes the state of the keyboard and mouse.
HOTKEY_COMMAND get_id(const std::string &command)
returns get_hotkey_command(command).id
builder_window(const config &cfg)
Definition: window.cpp:95
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
void draw_floating_labels(surface screen)
ui_event
The event send to the dispatcher.
Definition: handler.hpp:47
void layout_linked_widgets()
Layouts the linked widgets.
Definition: window.cpp:1072
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:409
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:139
std::function< void()> callback_next_draw_
Definition: window.hpp:764
Basic exception when the layout doesn&#39;t fit.