The Battle for Wesnoth  1.15.3+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 "utils/functional.hpp"
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  widget* build() const
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())
277  , status_(NEW)
278  , show_mode_(none)
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, _2, _3, _5));
325 
326  connect_signal<event::SDL_ACTIVATE>(std::bind(
328 
329  connect_signal<event::SDL_LEFT_BUTTON_UP>(
331  this,
332  _2,
333  _3,
334  _4,
335  SDL_BUTTON_LMASK),
337  connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
339  this,
340  _2,
341  _3,
342  _4,
343  SDL_BUTTON_MMASK),
345  connect_signal<event::SDL_RIGHT_BUTTON_UP>(
347  this,
348  _2,
349  _3,
350  _4,
351  SDL_BUTTON_RMASK),
353 
354  connect_signal<event::SDL_KEY_DOWN>(
355  std::bind(
356  &window::signal_handler_sdl_key_down, this, _2, _3, _5, _6, true),
358  connect_signal<event::SDL_KEY_DOWN>(std::bind(
359  &window::signal_handler_sdl_key_down, this, _2, _3, _5, _6, false));
360 
361  connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
363  this,
364  _2,
365  _3,
366  _5),
368 
369  connect_signal<event::MESSAGE_SHOW_HELPTIP>(
371  this,
372  _2,
373  _3,
374  _5),
376 
377  connect_signal<event::REQUEST_PLACEMENT>(
378  std::bind(
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  */
413  if(show_mode_ == modal) {
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 /*WIKI
432  * @page = GUIToolkitWML
433  * @order = 3_widget_window_2
434  *
435  * List if the id's that have generate a return value:
436  * * ok confirms the dialog.
437  * * cancel cancels the dialog.
438  *
439  */
440 retval window::get_retval_by_id(const std::string& id)
441 {
442  // Note it might change to a map later depending on the number
443  // of items.
444  if(id == "ok") {
445  return retval::OK;
446  } else if(id == "cancel" || id == "quit") {
447  return retval::CANCEL;
448  } else {
449  return retval::NONE;
450  }
451 }
452 
453 void window::show_tooltip(/*const unsigned auto_close_timeout*/)
454 {
455  log_scope2(log_gui_draw, "Window: show as tooltip.");
456 
457  generate_dot_file("show", SHOW);
458 
459  assert(status_ == NEW);
460 
463 
465 
466  /*
467  * Before show has been called, some functions might have done some testing
468  * on the window and called layout, which can give glitches. So
469  * reinvalidate the window to avoid those glitches.
470  */
472  suspend_drawing_ = false;
473 }
474 
475 void window::show_non_modal(/*const unsigned auto_close_timeout*/)
476 {
477  log_scope2(log_gui_draw, "Window: show non modal.");
478 
479  generate_dot_file("show", SHOW);
480 
481  assert(status_ == NEW);
482 
484 
486 
487  /*
488  * Before show has been called, some functions might have done some testing
489  * on the window and called layout, which can give glitches. So
490  * reinvalidate the window to avoid those glitches.
491  */
493  suspend_drawing_ = false;
494 
495  push_draw_event();
496 
497  events::pump();
498 }
499 
500 int window::show(const bool restore, const unsigned auto_close_timeout)
501 {
502  /*
503  * Removes the old tip if one shown. The show_tip doesn't remove
504  * the tip, since it's the tip.
505  */
507 
508  show_mode_ = modal;
509  restore_ = restore;
510 
512 
513  generate_dot_file("show", SHOW);
514 
515  assert(status_ == NEW);
516 
517  /*
518  * Before show has been called, some functions might have done some testing
519  * on the window and called layout, which can give glitches. So
520  * reinvalidate the window to avoid those glitches.
521  */
523  suspend_drawing_ = false;
524 
525  if(auto_close_timeout) {
526  // Make sure we're drawn before we try to close ourselves, which can
527  // happen if the timeout is small.
528  draw();
529 
530  SDL_Event event;
531  sdl::UserEvent data(CLOSE_WINDOW_EVENT, manager::instance().get_id(*this));
532 
533  event.type = CLOSE_WINDOW_EVENT;
534  event.user = data;
535 
536  delay_event(event, auto_close_timeout);
537  }
538 
539 
540  try
541  {
542  // Start our loop drawing will happen here as well.
543  bool mouse_button_state_initialized = false;
544  for(status_ = SHOWING; status_ != CLOSED;) {
545  push_draw_event();
546 
547  // process installed callback if valid, to allow e.g. network
548  // polling
549  events::pump();
550 
551  if(!mouse_button_state_initialized) {
552  /*
553  * The state must be initialize when showing the dialog.
554  * However when initialized before this point there were random
555  * errors. This only happened when the 'click' was done fast; a
556  * slower click worked properly.
557  *
558  * So it seems the events need to be processed before SDL can
559  * return the proper button state. When initializing here all
560  * works fine.
561  */
562  mouse_button_state_ = SDL_GetMouseState(nullptr, nullptr);
563  mouse_button_state_initialized = true;
564  }
565 
566  if(status_ == REQUEST_CLOSE) {
567  status_ = exit_hook_(*this) ? CLOSED : SHOWING;
568  }
569 
570  // Add a delay so we don't keep spinning if there's no event.
571  SDL_Delay(10);
572  }
573  }
574  catch(...)
575  {
576  /**
577  * @todo Clean up the code duplication.
578  *
579  * In the future the restoring shouldn't be needed so the duplication
580  * doesn't hurt too much but keep this todo as a reminder.
581  */
582  suspend_drawing_ = true;
583 
584  // restore area
585  if(restore_) {
586  SDL_Rect rect = get_rectangle();
587  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
589  }
590  throw;
591  }
592 
593  suspend_drawing_ = true;
594 
595  // restore area
596  if(restore_) {
597  SDL_Rect rect = get_rectangle();
598  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
600  }
601 
602  if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
603  tb->interrupt_composition();
604  }
605 
606  return retval_;
607 }
608 
610 {
611  /***** ***** ***** ***** Init ***** ***** ***** *****/
612  // Prohibited from drawing?
613  if(suspend_drawing_) {
614  return;
615  }
616 
617  surface& frame_buffer = video_.getSurface();
618 
619  /***** ***** Layout and get dirty list ***** *****/
620  if(need_layout_) {
621  // Restore old surface. In the future this phase will not be needed
622  // since all will be redrawn when needed with dirty rects. Since that
623  // doesn't work yet we need to undraw the window.
624  if(restore_ && restorer_) {
625  SDL_Rect rect = get_rectangle();
626  sdl_blit(restorer_, 0, frame_buffer, &rect);
627  }
628 
629  layout();
630 
631  // Get new surface for restoring
632  SDL_Rect rect = get_rectangle();
633 
634  // We want the labels underneath the window so draw them and use them
635  // as restore point.
636  if(is_toplevel_) {
637  font::draw_floating_labels(frame_buffer);
638  }
639 
640  if(restore_) {
641  restorer_ = get_surface_portion(frame_buffer, rect);
642  }
643 
644  // Need full redraw so only set ourselves dirty.
645  dirty_list_.emplace_back(1, this);
646  } else {
647 
648  // Let widgets update themselves, which might dirty some things.
649  layout_children();
650 
651  // Now find the widgets that are dirty.
652  std::vector<widget*> call_stack;
653  if(!new_widgets) {
654  populate_dirty_list(*this, call_stack);
655  } else {
656  /* Force to update and redraw the entire screen */
657  dirty_list_.clear();
658  dirty_list_.emplace_back(1, this);
659  }
660  }
661 
662  if (dirty_list_.empty()) {
664  return;
665  }
666 
668  if(consecutive_changed_frames_ >= 100u && id_ == "title_screen") {
669  /* The title screen has changed in 100 consecutive frames, i.e. every
670  frame for two seconds. It looks like the screen is constantly changing
671  or at least marking widgets as dirty.
672 
673  That's a severe problem. Every time the title screen changes, all
674  other GUI windows need to be fully redrawn, with huge CPU usage cost.
675  For that reason, this situation is a hard error. */
676  throw std::logic_error("The title screen is constantly changing, "
677  "which has a huge CPU usage cost. See the code comment.");
678  }
679 
680  for(auto & item : dirty_list_)
681  {
682 
683  assert(!item.empty());
684 
685  const SDL_Rect dirty_rect
687  : item.back()->get_dirty_rectangle();
688 
689 // For testing we disable the clipping rect and force the entire screen to
690 // update. This way an item rendered at the wrong place is directly visible.
691 #if 0
692  dirty_list_.clear();
693  dirty_list_.emplace_back(1, this);
694 #else
695  clip_rect_setter clip(frame_buffer, &dirty_rect);
696 #endif
697 
698  /*
699  * The actual update routine does the following:
700  * - Restore the background.
701  *
702  * - draw [begin, end) the back ground of all widgets.
703  *
704  * - draw the children of the last item in the list, if this item is
705  * a container it's children get a full redraw. If it's not a
706  * container nothing happens.
707  *
708  * - draw [rbegin, rend) the fore ground of all widgets. For items
709  * which have two layers eg window or panel it draws the foreground
710  * layer. For other widgets it's a nop.
711  *
712  * Before drawing there needs to be determined whether a dirty widget
713  * really needs to be redrawn. If the widget doesn't need to be
714  * redrawing either being not visibility::visible or has status
715  * widget::redraw_action::none. If it's not drawn it's still set not
716  * dirty to avoid it keep getting on the dirty list.
717  */
718 
719  for(std::vector<widget*>::iterator itor = item.begin();
720  itor != item.end();
721  ++itor) {
722 
723  if((**itor).get_visible() != widget::visibility::visible
724  || (**itor).get_drawing_action()
726 
727  for(std::vector<widget*>::iterator citor = itor;
728  citor != item.end();
729  ++citor) {
730 
731  (**citor).set_is_dirty(false);
732  }
733 
734  item.erase(itor, item.end());
735  break;
736  }
737  }
738 
739  // Restore.
740  if(restore_) {
741  SDL_Rect rect = get_rectangle();
742  sdl_blit(restorer_, 0, frame_buffer, &rect);
743  }
744 
745  // Background.
746  for(std::vector<widget*>::iterator itor = item.begin();
747  itor != item.end();
748  ++itor) {
749 
750  (**itor).draw_background(frame_buffer, 0, 0);
751  }
752 
753  // Children.
754  if(!item.empty()) {
755  item.back()->draw_children(frame_buffer, 0, 0);
756  }
757 
758  // Foreground.
759  for(std::vector<widget*>::reverse_iterator ritor = item.rbegin();
760  ritor != item.rend();
761  ++ritor) {
762 
763  (**ritor).draw_foreground(frame_buffer, 0, 0);
764  (**ritor).set_is_dirty(false);
765  }
766  }
767 
768  dirty_list_.clear();
769 
771 
772  std::vector<widget*> call_stack;
773  populate_dirty_list(*this, call_stack);
774  assert(dirty_list_.empty());
775 
776  if(callback_next_draw_ != nullptr) {
778  callback_next_draw_ = nullptr;
779  }
780 }
781 
783 {
784  if(restore_ && restorer_) {
785  SDL_Rect rect = get_rectangle();
786  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
787  // Since the old area might be bigger as the new one, invalidate
788  // it.
789  }
790 }
791 
793  : window_(window)
794 {
797 }
798 
800 {
803 }
804 
806 {
808  need_layout_ = true;
809  }
810 }
811 widget* window::find_at(const point& coordinate, const bool must_be_active)
812 {
813  return panel::find_at(coordinate, must_be_active);
814 }
815 
817  const bool must_be_active) const
818 {
819  return panel::find_at(coordinate, must_be_active);
820 }
821 
822 widget* window::find(const std::string& id, const bool must_be_active)
823 {
824  return container_base::find(id, must_be_active);
825 }
826 
827 const widget* window::find(const std::string& id, const bool must_be_active)
828  const
829 {
830  return container_base::find(id, must_be_active);
831 }
832 
833 void window::init_linked_size_group(const std::string& id,
834  const bool fixed_width,
835  const bool fixed_height)
836 {
837  assert(fixed_width || fixed_height);
838  assert(!has_linked_size_group(id));
839 
840  linked_size_[id] = linked_size(fixed_width, fixed_height);
841 }
842 
843 bool window::has_linked_size_group(const std::string& id)
844 {
845  return linked_size_.find(id) != linked_size_.end();
846 }
847 
848 void window::add_linked_widget(const std::string& id, widget* wgt)
849 {
850  assert(wgt);
851  if(!has_linked_size_group(id)) {
852  ERR_GUI << "Unknown linked group '" << id << "'; skipping\n";
853  return;
854  }
855 
856  std::vector<widget*>& widgets = linked_size_[id].widgets;
857  if(std::find(widgets.begin(), widgets.end(), wgt) == widgets.end()) {
858  widgets.push_back(wgt);
859  }
860 }
861 
862 void window::remove_linked_widget(const std::string& id, const widget* wgt)
863 {
864  assert(wgt);
865  if(!has_linked_size_group(id)) {
866  return;
867  }
868 
869  std::vector<widget*>& widgets = linked_size_[id].widgets;
870 
872  = std::find(widgets.begin(), widgets.end(), wgt);
873 
874  if(itor != widgets.end()) {
875  widgets.erase(itor);
876 
877  assert(std::find(widgets.begin(), widgets.end(), wgt)
878  == widgets.end());
879  }
880 }
881 
883 {
884  /***** Initialize. *****/
885 
886  const auto conf = cast_config_to<window_definition>();
887  assert(conf);
888 
890 
892  const point mouse = get_mouse_position();
893 
894  variables_.add("mouse_x", wfl::variant(mouse.x));
895  variables_.add("mouse_y", wfl::variant(mouse.y));
896  variables_.add("window_width", wfl::variant(0));
897  variables_.add("window_height", wfl::variant(0));
898  variables_.add("best_window_width", wfl::variant(size.x));
899  variables_.add("best_window_height", wfl::variant(size.y));
900  variables_.add("size_request_mode", wfl::variant("maximum"));
901 
903 
904  unsigned int maximum_width = maximum_width_(variables_, &functions_);
905  unsigned int maximum_height = maximum_height_(variables_, &functions_);
906 
908  if(maximum_width == 0 || maximum_width > settings::screen_width) {
909  maximum_width = settings::screen_width;
910  }
911 
912  if(maximum_height == 0 || maximum_height > settings::screen_height) {
913  maximum_height = settings::screen_height;
914  }
915  } else {
916  maximum_width = w_(variables_, &functions_);
917  maximum_height = h_(variables_, &functions_);
918  }
919 
920  /***** Handle click dismiss status. *****/
921  button* click_dismiss_button = nullptr;
922  if((click_dismiss_button
923  = find_widget<button>(this, "click_dismiss", false, false))) {
924 
925  click_dismiss_button->set_visible(widget::visibility::invisible);
926  }
927  if(click_dismiss_) {
928  button* btn = find_widget<button>(this, "ok", false, false);
929  if(btn) {
931  click_dismiss_button = btn;
932  }
933  VALIDATE(click_dismiss_button,
934  _("Click dismiss needs a 'click_dismiss' or 'ok' button."));
935  }
936 
937  /***** Layout. *****/
938  layout_initialize(true);
939  generate_dot_file("layout_initialize", LAYOUT);
940 
942 
943  try
944  {
945  window_implementation::layout(*this, maximum_width, maximum_height);
946  }
947  catch(const layout_exception_resize_failed&)
948  {
949 
950  /** @todo implement the scrollbars on the window. */
951 
952  std::stringstream sstr;
953  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
954  << "' found the following problem: Failed to size window;"
955  << " wanted size " << get_best_size() << " available size "
956  << maximum_width << ',' << maximum_height << " screen size "
958 
959  throw wml_exception(_("Failed to show a dialog, "
960  "which doesn't fit on the screen."),
961  sstr.str());
962  }
963 
964  /****** Validate click dismiss status. *****/
966  assert(click_dismiss_button);
967  click_dismiss_button->set_visible(widget::visibility::visible);
968 
970  *click_dismiss_button,
971  std::bind(&window::set_retval, this, retval::OK, true));
972 
973  layout_initialize(true);
974  generate_dot_file("layout_initialize", LAYOUT);
975 
977 
978  try
979  {
981  *this, maximum_width, maximum_height);
982  }
983  catch(const layout_exception_resize_failed&)
984  {
985 
986  /** @todo implement the scrollbars on the window. */
987 
988  std::stringstream sstr;
989  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
990  << "' found the following problem: Failed to size window;"
991  << " wanted size " << get_best_size() << " available size "
992  << maximum_width << ',' << maximum_height << " screen size "
994  << '.';
995 
996  throw wml_exception(_("Failed to show a dialog, "
997  "which doesn't fit on the screen."),
998  sstr.str());
999  }
1000  }
1001 
1002  /***** Get the best location for the window *****/
1003  size = get_best_size();
1004  assert(size.x > 0 && static_cast<unsigned>(size.x) <= maximum_width
1005  && size.y > 0 && static_cast<unsigned>(size.y) <= maximum_height);
1006 
1007  point origin(0, 0);
1008 
1009  if(automatic_placement_) {
1010 
1011  switch(horizontal_placement_) {
1013  // Do nothing
1014  break;
1016  origin.x = (settings::screen_width - size.x) / 2;
1017  break;
1019  origin.x = settings::screen_width - size.x;
1020  break;
1021  default:
1022  assert(false);
1023  }
1024  switch(vertical_placement_) {
1026  // Do nothing
1027  break;
1029  origin.y = (settings::screen_height - size.y) / 2;
1030  break;
1032  origin.y = settings::screen_height - size.y;
1033  break;
1034  default:
1035  assert(false);
1036  }
1037  } else {
1038 
1039  variables_.add("window_width", wfl::variant(size.x));
1040  variables_.add("window_height", wfl::variant(size.y));
1041 
1043  layout_initialize(true);
1044 
1047  h_(variables_, &functions_));
1048 
1049  size = get_best_size();
1050  variables_.add("window_width", wfl::variant(size.x));
1051  variables_.add("window_height", wfl::variant(size.y));
1052  }
1053 
1054  variables_.add("size_request_mode", wfl::variant("size"));
1055 
1056  size.x = w_(variables_, &functions_);
1057  size.y = h_(variables_, &functions_);
1058 
1059  variables_.add("window_width", wfl::variant(size.x));
1060  variables_.add("window_height", wfl::variant(size.y));
1061 
1062  origin.x = x_(variables_, &functions_);
1063  origin.y = y_(variables_, &functions_);
1064  }
1065 
1066  /***** Set the window size *****/
1067  place(origin, size);
1068 
1069  generate_dot_file("layout_finished", LAYOUT);
1070  need_layout_ = false;
1071 
1073 }
1074 
1076 {
1077  // evaluate the group sizes
1078  for(auto & linked_size : linked_size_)
1079  {
1080 
1081  point max_size(0, 0);
1082 
1083  // Determine the maximum size.
1084  for(auto widget : linked_size.second.widgets)
1085  {
1086 
1087  const point size = widget->get_best_size();
1088 
1089  if(size.x > max_size.x) {
1090  max_size.x = size.x;
1091  }
1092  if(size.y > max_size.y) {
1093  max_size.y = size.y;
1094  }
1095  }
1096  if(linked_size.second.width != -1) {
1097  linked_size.second.width = max_size.x;
1098  }
1099  if(linked_size.second.height != -1) {
1100  linked_size.second.height = max_size.y;
1101  }
1102 
1103  // Set the maximum size.
1104  for(auto widget : linked_size.second.widgets)
1105  {
1106 
1108 
1109  if(linked_size.second.width != -1) {
1110  size.x = max_size.x;
1111  }
1112  if(linked_size.second.height != -1) {
1113  size.y = max_size.y;
1114  }
1115 
1116  widget->set_layout_size(size);
1117  }
1118  }
1119 }
1120 
1121 bool window::click_dismiss(const int mouse_button_mask)
1122 {
1123  if(does_click_dismiss()) {
1124  if((mouse_button_state_ & mouse_button_mask) == 0) {
1126  } else {
1127  mouse_button_state_ &= ~mouse_button_mask;
1128  }
1129  return true;
1130  }
1131  return false;
1132 }
1133 
1134 namespace
1135 {
1136 
1137 /**
1138  * Swaps an item in a grid for another one.
1139  * This differs slightly from the standard swap_grid utility, so it's defined by itself here.
1140  */
1141 void window_swap_grid(grid* g,
1142  grid* content_grid,
1143  widget* widget,
1144  const std::string& id)
1145 {
1146  assert(content_grid);
1147  assert(widget);
1148 
1149  // Make sure the new child has same id.
1150  widget->set_id(id);
1151 
1152  // Get the container containing the wanted widget.
1153  grid* parent_grid = nullptr;
1154  if(g) {
1155  parent_grid = find_widget<grid>(g, id, false, false);
1156  }
1157  if(!parent_grid) {
1158  parent_grid = find_widget<grid>(content_grid, id, true, false);
1159  assert(parent_grid);
1160  }
1161  if(grid* grandparent_grid = dynamic_cast<grid*>(parent_grid->parent())) {
1162  grandparent_grid->swap_child(id, widget, false);
1163  } else if(container_base* c
1164  = dynamic_cast<container_base*>(parent_grid->parent())) {
1165 
1166  c->get_grid().swap_child(id, widget, true);
1167  } else {
1168  assert(false);
1169  }
1170 }
1171 
1172 } // namespace
1173 
1175 {
1176  std::vector<dispatcher*>& dispatchers = event::get_all_dispatchers();
1177  auto me = std::find(dispatchers.begin(), dispatchers.end(), this);
1178 
1179  for(auto it = std::next(me); it != dispatchers.end(); ++it) {
1180  // Note that setting an entire window dirty like this is expensive.
1181  dynamic_cast<widget&>(**it).set_is_dirty(true);
1182  }
1183 }
1184 
1185 void window::finalize(const std::shared_ptr<builder_grid>& content_grid)
1186 {
1187  window_swap_grid(nullptr, &get_grid(), content_grid->build(), "_window_content_grid");
1188 }
1189 
1190 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1191 
1192 void window::generate_dot_file(const std::string& generator,
1193  const unsigned domain)
1194 {
1195  debug_layout_->generate_dot_file(generator, domain);
1196 }
1197 #endif
1198 
1200  const unsigned maximum_width,
1201  const unsigned maximum_height)
1202 {
1204 
1205  /*
1206  * For now we return the status, need to test later whether this can
1207  * entirely be converted to an exception based system as in 'promised' on
1208  * the algorithm page.
1209  */
1210 
1211  try
1212  {
1213  point size = window.get_best_size();
1214 
1215  DBG_GUI_L << LOG_IMPL_HEADER << " best size : " << size
1216  << " maximum size : " << maximum_width << ','
1217  << maximum_height << ".\n";
1218  if(size.x <= static_cast<int>(maximum_width)
1219  && size.y <= static_cast<int>(maximum_height)) {
1220 
1221  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Fits, nothing to do.\n";
1222  return;
1223  }
1224 
1225  if(size.x > static_cast<int>(maximum_width)) {
1226  window.reduce_width(maximum_width);
1227 
1228  size = window.get_best_size();
1229  if(size.x > static_cast<int>(maximum_width)) {
1230  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize width failed."
1231  << " Wanted width " << maximum_width
1232  << " resulting width " << size.x << ".\n";
1234  }
1236  << " Status: Resize width succeeded.\n";
1237  }
1238 
1239  if(size.y > static_cast<int>(maximum_height)) {
1240  window.reduce_height(maximum_height);
1241 
1242  size = window.get_best_size();
1243  if(size.y > static_cast<int>(maximum_height)) {
1244  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize height failed."
1245  << " Wanted height " << maximum_height
1246  << " resulting height " << size.y << ".\n";
1248  }
1250  << " Status: Resize height succeeded.\n";
1251  }
1252 
1253  assert(size.x <= static_cast<int>(maximum_width)
1254  && size.y <= static_cast<int>(maximum_height));
1255 
1256 
1257  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resizing succeeded.\n";
1258  return;
1259  }
1260  catch(const layout_exception_width_modified&)
1261  {
1263  << " Status: Width has been modified, rerun.\n";
1264 
1265  window.layout_initialize(false);
1266  window.layout_linked_widgets();
1267  layout(window, maximum_width, maximum_height);
1268  return;
1269  }
1270 }
1271 
1272 void window::mouse_capture(const bool capture)
1273 {
1274  assert(event_distributor_);
1275  event_distributor_->capture_mouse(capture);
1276 }
1277 
1279 {
1280  assert(event_distributor_);
1281  event_distributor_->keyboard_capture(widget);
1282 }
1283 
1285 {
1286  assert(event_distributor_);
1287  event_distributor_->keyboard_add_to_chain(widget);
1288 }
1289 
1291 {
1292  assert(event_distributor_);
1293  event_distributor_->keyboard_remove_from_chain(widget);
1294 }
1295 
1297 {
1298  if(std::find(tab_order.begin(), tab_order.end(), widget) != tab_order.end()) {
1299  return;
1300  }
1301  assert(event_distributor_);
1302  if(tab_order.empty() && !event_distributor_->keyboard_focus()) {
1303  keyboard_capture(widget);
1304  }
1305  if(at < 0 || at >= static_cast<int>(tab_order.size())) {
1306  tab_order.push_back(widget);
1307  } else {
1308  tab_order.insert(tab_order.begin() + at, widget);
1309  }
1310 }
1311 
1313  bool& handled,
1314  const point& new_size)
1315 {
1316  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1317 
1320  settings::screen_width = new_size.x;
1321  settings::screen_height = new_size.y;
1323 
1324  handled = true;
1325 }
1326 
1328  bool& handled,
1329  bool& halt,
1330  const int mouse_button_mask)
1331 {
1332  DBG_GUI_E << LOG_HEADER << ' ' << event << " mouse_button_mask "
1333  << static_cast<unsigned>(mouse_button_mask) << ".\n";
1334 
1335  handled = halt = click_dismiss(mouse_button_mask);
1336 }
1337 
1338 static bool is_active(const widget* wgt)
1339 {
1340  if(const styled_widget* control = dynamic_cast<const styled_widget*>(wgt)) {
1341  return control->get_active() && control->get_visible() == widget::visibility::visible;
1342  }
1343  return false;
1344 }
1345 
1347  bool& handled,
1348  const SDL_Keycode key,
1349  const SDL_Keymod mod,
1350  bool handle_tab)
1351 {
1352  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1353 
1354  if(text_box_base* tb = dynamic_cast<text_box_base*>(event_distributor_->keyboard_focus())) {
1355  if(tb->is_composing()) {
1356  if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1357  tb->interrupt_composition();
1358  } else {
1359  return;
1360  }
1361  }
1362  }
1363  if(!enter_disabled_ && (key == SDLK_KP_ENTER || key == SDLK_RETURN)) {
1365  handled = true;
1366  } else if(key == SDLK_ESCAPE && !escape_disabled_) {
1368  handled = true;
1369  } else if(key == SDLK_SPACE) {
1370  handled = click_dismiss(0);
1371  } else if(handle_tab && !tab_order.empty() && key == SDLK_TAB) {
1372  assert(event_distributor_);
1373  widget* focus = event_distributor_->keyboard_focus();
1374  auto iter = std::find(tab_order.begin(), tab_order.end(), focus);
1375  do {
1376  if(mod & KMOD_SHIFT) {
1377  if(iter == tab_order.begin()) {
1378  iter = tab_order.end();
1379  }
1380  iter--;
1381  } else {
1382  if(iter == tab_order.end()) {
1383  iter = tab_order.begin();
1384  } else {
1385  iter++;
1386  if(iter == tab_order.end()) {
1387  iter = tab_order.begin();
1388  }
1389  }
1390  }
1391  } while(!is_active(*iter));
1392  keyboard_capture(*iter);
1393  handled = true;
1394  }
1395 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1396  if(key == SDLK_F12) {
1397  debug_layout_->generate_dot_file("manual", debug_layout_graph::MANUAL);
1398  handled = true;
1399  }
1400 #endif
1401 }
1402 
1404  bool& handled,
1405  const event::message& message)
1406 {
1407  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1408 
1409  const event::message_show_tooltip& request
1410  = dynamic_cast<const event::message_show_tooltip&>(message);
1411 
1412  dialogs::tip::show(tooltip_.id, request.message, request.location, request.source_rect);
1413 
1414  handled = true;
1415 }
1416 
1418  bool& handled,
1419  const event::message& message)
1420 {
1421  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1422 
1423  const event::message_show_helptip& request
1424  = dynamic_cast<const event::message_show_helptip&>(message);
1425 
1426  dialogs::tip::show(helptip_.id, request.message, request.location, request.source_rect);
1427 
1428  handled = true;
1429 }
1430 
1432  bool& handled)
1433 {
1434  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1435 
1437 
1438  handled = true;
1439 }
1440 
1442 {
1444 }
1445 
1446 // }---------- DEFINITION ---------{
1447 
1448 /*WIKI
1449  * @page = GUIWidgetDefinitionWML
1450  * @order = 1_window
1451  *
1452  * == Window ==
1453  *
1454  * The definition of a window. A window is a kind of panel see the panel for
1455  * which fields exist
1456  *
1457  * @begin{parent}{name="gui/"}
1458  * @begin{tag}{name="window_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
1459  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="gui/panel_definition/resolution"}
1460  * @allow{link}{name="gui/window/resolution/grid"}
1461  * @allow{link}{name="gui/panel_definition/resolution/background"}
1462  * @allow{link}{name="gui/panel_definition/resolution/foreground"}
1463  * @end{tag}{name="resolution"}
1464  * @end{tag}{name="window_definition"}
1465  * @end{parent}{name="gui/"}
1466  */
1469 {
1470  DBG_GUI_P << "Parsing window " << id << '\n';
1471 
1472  load_resolutions<resolution>(cfg);
1473 }
1474 
1476  : panel_definition::resolution(cfg), grid(nullptr)
1477 {
1478  const config& child = cfg.child("grid");
1479  // VALIDATE(child, _("No grid defined."));
1480 
1481  /** @todo Evaluate whether the grid should become mandatory. */
1482  if(child) {
1483  grid = std::make_shared<builder_grid>(child);
1484  }
1485 }
1486 
1487 // }------------ END --------------
1488 
1489 } // namespace gui2
1490 
1491 
1492 /**
1493  * @page layout_algorithm Layout algorithm
1494  *
1495  * @section introduction Introduction
1496  *
1497  * This page describes how the layout engine for the dialogs works. First
1498  * a global overview of some terms used in this document.
1499  *
1500  * - @ref gui2::widget "Widget"; Any item which can be used in the widget
1501  * toolkit. Not all widgets are visible. In general widgets cannot be
1502  * sized directly, but this is controlled by a window. A widget has an
1503  * internal size cache and if the value in the cache is not equal to 0,0
1504  * that value is its best size. This value gets set when the widget can
1505  * honor a resize request. It will be set with the value which honors
1506  * the request.
1507  *
1508  * - @ref gui2::grid "Grid"; A grid is an invisible container which holds
1509  * one or more widgets. Several widgets have a grid in them to hold
1510  * multiple widgets eg panels and windows.
1511  *
1512  * - @ref gui2::grid::child "Grid cell"; Every widget which is in a grid is
1513  * put in a grid cell. These cells also hold the information about the gaps
1514  * between widgets the behavior on growing etc. All grid cells must have a
1515  * widget inside them.
1516  *
1517  * - @ref gui2::window "Window"; A window is a top level item which has a
1518  * grid with its children. The window handles the sizing of the window and
1519  * makes sure everything fits.
1520  *
1521  * - @ref gui2::window::linked_size "Shared size group"; A shared size
1522  * group is a number of widgets which share width and or height. These
1523  * widgets are handled separately in the layout algorithm. All grid cells
1524  * width such a widget will get the same height and or width and these
1525  * widgets won't be resized when there's not enough size. To be sure that
1526  * these widgets don't cause trouble for the layout algorithm, they must be
1527  * in a container with scrollbars so there will always be a way to properly
1528  * layout them. The engine must enforce this restriction so the shared
1529  * layout property must be set by the engine after validation.
1530  *
1531  * - All visible grid cells; A grid cell is visible when the widget inside
1532  * of it doesn't have the state visibility::invisible. Widgets which have the
1533  * state @ref visibility::hidden are sized properly since when they become
1534  * @ref visibility::visible the layout shouldn't be invalidated. A grid cell
1535  * that's invisible has size 0,0.
1536  *
1537  * - All resizable grid cells; A grid cell is resizable under the following
1538  * conditions:
1539  * - The widget is visibility::visible.
1540  * - The widget is not in a shared size group.
1541  *
1542  * There are two layout algorithms with a different purpose.
1543  *
1544  * - The Window algorithm; this algorithm's goal is it to make sure all grid
1545  * cells fit in the window. Sizing the grid cells depends on the widget
1546  * size as well, but this algorithm only sizes the grid cells and doesn't
1547  * handle the widgets inside them.
1548  *
1549  * - The Grid algorithm; after the Window algorithm made sure that all grid
1550  * cells fit this algorithm makes sure the widgets are put in the optimal
1551  * state in their grid cell.
1552  *
1553  * @section layout_algorithm_window Window
1554  *
1555  * Here is the algorithm used to layout the window:
1556  *
1557  * - Perform a full initialization
1558  * (@ref gui2::widget::layout_initialize (full_initialization = true)):
1559  * - Clear the internal best size cache for all widgets.
1560  * - For widgets with scrollbars hide them unless the
1561  * @ref gui2::scrollbar_container::scrollbar_mode "scrollbar_mode" is
1562  * ALWAYS_VISIBLE or AUTO_VISIBLE.
1563  * - Handle shared sizes:
1564  * - Height and width:
1565  * - Get the best size for all widgets that share height and width.
1566  * - Set the maximum of width and height as best size for all these
1567  * widgets.
1568  * - Width only:
1569  * - Get the best width for all widgets which share their width.
1570  * - Set the maximum width for all widgets, but keep their own height.
1571  * - Height only:
1572  * - Get the best height for all widgets which share their height.
1573  * - Set the maximum height for all widgets, but keep their own width.
1574  * - Start layout loop:
1575  * - Get best size.
1576  * - If width <= maximum_width && height <= maximum_height we're done.
1577  * - If width > maximum_width, optimize the width:
1578  * - For every grid cell in a grid row there will be a resize request
1579  * (@ref gui2::grid::reduce_width):
1580  * - Sort the widgets in the row on the resize priority.
1581  * - Loop through this priority queue until the row fits
1582  * - If priority != 0 try to share the extra width else all
1583  * widgets are tried to reduce the full size.
1584  * - Try to shrink the widgets by either wrapping or using a
1585  * scrollbar (@ref gui2::widget::request_reduce_width).
1586  * - If the row fits in the wanted width this row is done.
1587  * - Else try the next priority.
1588  * - All priorities done and the width still doesn't fit.
1589  * - Loop through this priority queue until the row fits.
1590  * - If priority != 0:
1591  * - try to share the extra width
1592  * -Else:
1593  * - All widgets are tried to reduce the full size.
1594  * - Try to shrink the widgets by sizing them smaller as really
1595  * wanted (@ref gui2::widget::demand_reduce_width).
1596  * For labels, buttons etc. they get ellipsized.
1597  * - If the row fits in the wanted width this row is done.
1598  * - Else try the next priority.
1599  * - All priorities done and the width still doesn't fit.
1600  * - Throw a layout width doesn't fit exception.
1601  * - If height > maximum_height, optimize the height
1602  * (@ref gui2::grid::reduce_height):
1603  * - For every grid cell in a grid column there will be a resize request:
1604  * - Sort the widgets in the column on the resize priority.
1605  * - Loop through this priority queue until the column fits:
1606  * - If priority != 0 try to share the extra height else all
1607  * widgets are tried to reduce the full size.
1608  * - Try to shrink the widgets by using a scrollbar
1609  * (@ref gui2::widget::request_reduce_height).
1610  * - If succeeded for a widget the width is influenced and the
1611  * width might be invalid.
1612  * - Throw a width modified exception.
1613  * - If the column fits in the wanted height this column is done.
1614  * - Else try the next priority.
1615  * - All priorities done and the height still doesn't fit.
1616  * - Loop through this priority queue until the column fits.
1617  * - If priority != 0 try to share the extra height else all
1618  * widgets are tried to reduce the full size.
1619  * - Try to shrink the widgets by sizing them smaller as really
1620  * wanted (@ref gui2::widget::demand_reduce_width).
1621  * For labels, buttons etc. they get ellipsized .
1622  * - If the column fits in the wanted height this column is done.
1623  * - Else try the next priority.
1624  * - All priorities done and the height still doesn't fit.
1625  * - Throw a layout height doesn't fit exception.
1626  * - End layout loop.
1627  *
1628  * - Catch @ref gui2::layout_exception_width_modified "width modified":
1629  * - Goto relayout.
1630  *
1631  * - Catch
1632  * @ref gui2::layout_exception_width_resize_failed "width resize failed":
1633  * - If the window has a horizontal scrollbar which isn't shown but can be
1634  * shown.
1635  * - Show the scrollbar.
1636  * - goto relayout.
1637  * - Else show a layout failure message.
1638  *
1639  * - Catch
1640  * @ref gui2::layout_exception_height_resize_failed "height resize failed":
1641  * - If the window has a vertical scrollbar which isn't shown but can be
1642  * shown:
1643  * - Show the scrollbar.
1644  * - goto relayout.
1645  * - Else:
1646  * - show a layout failure message.
1647  *
1648  * - Relayout:
1649  * - Initialize all widgets
1650  * (@ref gui2::widget::layout_initialize (full_initialization = false))
1651  * - Handle shared sizes, since the reinitialization resets that state.
1652  * - Goto start layout loop.
1653  *
1654  * @section grid Grid
1655  *
1656  * This section will be documented later.
1657  */
Define the common log macros for the gui toolkit.
bool click_dismiss_
Do we want to have easy close behavior?
Definition: window.hpp:552
const std::string message
The message to show on the tooltip.
Definition: message.hpp:66
void keyboard_capture(widget *widget)
Definition: window.cpp:1278
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:68
wfl::function_symbol_table functions_
The formula definitions available for the calculation formulas.
Definition: window.hpp:527
void remove()
Removes a tip.
Definition: tooltip.cpp:189
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:1327
#define ERR_GUI
Definition: window.cpp:74
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:420
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:509
bool does_click_dismiss() const
Does the window close easily?
Definition: window.hpp:273
std::function< bool(window &)> exit_hook_
Definition: window.hpp:769
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:1346
#define DBG_GUI_L
Definition: log.hpp:57
static lg::log_domain log_gui("gui/layout")
void set_layout_size(const point &size)
Definition: widget.cpp:332
virtual widget * find_at(const point &coordinate, const bool must_be_active) override
See widget::find_at.
Definition: window.cpp:811
typed_formula< unsigned > h_
The formula to calculate the height of the dialog.
Definition: window.hpp:521
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
const unsigned horizontal_placement_
Sets the horizontal placement.
Definition: window.hpp:495
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
Visible container to hold multiple widgets.
Definition: panel.hpp:37
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:203
CVideo & video_
Needed so we can change what&#39;s drawn on the screen.
Definition: window.hpp:442
void show_non_modal()
Shows the window non modal.
Definition: window.cpp:475
void generate_dot_file(const std::string &, const unsigned)
Definition: window.hpp:679
Helper struct to force widgets the have the same size.
Definition: window.hpp:567
#define LOG_SCOPE_HEADER
Definition: window.cpp:76
const std::string & id() const
Definition: widget.cpp:107
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:658
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:609
SDL_Rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:307
The message for MESSAGE_SHOW_HELPTIP.
Definition: message.hpp:76
Base class for all widgets.
Definition: widget.hpp:47
bool is_in_dialog()
Is a dialog open?
Definition: handler.cpp:1118
#define DRAW_EVENT
Definition: events.hpp:26
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:56
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:1312
#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.
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:1284
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:518
void signal_handler_message_show_tooltip(const event::ui_event event, bool &handled, const event::message &message)
Definition: window.cpp:1403
-file util.hpp
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:168
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:635
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:913
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:578
The window has been closed.
Definition: window.hpp:173
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:1338
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)
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
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:414
status status_
The status of the window.
Definition: window.hpp:445
#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:575
surface restorer_
When the window closes this surface is used to undraw the window.
Definition: window.hpp:484
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:190
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:475
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:555
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:472
void set_visible(const visibility visible)
Definition: widget.cpp:473
bool need_layout_
When set the form needs a full layout redraw cycle.
Definition: window.hpp:466
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:233
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:463
void signal_handler_close_window()
Definition: window.cpp:1441
typed_formula< bool > reevaluate_best_size_
The formula to determine whether the size is good.
Definition: window.hpp:524
builder_window::window_resolution::tooltip_info helptip_
The settings for the helptip.
Definition: window.hpp:533
bool is_toplevel_
Whether the window has other windows behind it.
Definition: window.hpp:481
unsigned gamemap_height
Definition: settings.cpp:34
widget * parent()
Definition: widget.cpp:157
std::vector< dispatcher * > & get_all_dispatchers()
Gets all event dispatchers in the Z order.
Definition: handler.cpp:907
The widget is not visible.
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:58
void finalize(const std::shared_ptr< builder_grid > &content_grid)
Finishes the initialization of the grid.
Definition: window.cpp:1185
The window is new and not yet shown.
Definition: window.hpp:167
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:515
Exception thrown when the width resizing has failed.
Basic template class to generate new items.
#define log_scope2(domain, description)
Definition: log.hpp:187
const bool automatic_placement_
Do we wish to place the widget automatically?
Definition: window.hpp:487
typed_formula< unsigned > maximum_width_
The maximum width if automatic_placement_ is true.
Definition: window.hpp:506
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:833
typed_formula< unsigned > x_
The formula to calculate the x value of the dialog.
Definition: window.hpp:512
std::vector< std::vector< widget * > > dirty_list_
The list with dirty items in the window.
Definition: window.hpp:651
void set_mouse_behavior(const mouse_behavior mouse_behavior)
Definition: dispatcher.hpp:734
bool escape_disabled_
Disable the escape key see our setter for more info.
Definition: window.hpp:558
void pump()
Definition: events.cpp:475
CVideo & video()
Definition: window.hpp:352
#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:848
widget * find(const std::string &id, const bool must_be_active) override
See widget::find.
Definition: window.cpp:822
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:1290
bool faked() const
Definition: video.hpp:65
int show(const bool restore=true, const unsigned auto_close_timeout=0)
Shows the window.
Definition: window.cpp:500
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:
The window has been requested to be closed but still needs to evaluate the request.
Definition: window.hpp:169
window * get_window()
Get the parent window.
Definition: widget.cpp:114
window * build(const builder_window::window_resolution *definition)
Builds a window.
double g
Definition: astarsearch.cpp:64
const unsigned vertical_placement_
Sets the vertical placement.
Definition: window.hpp:503
void undraw_floating_labels(surface screen)
std::vector< widget * > tab_order
List of widgets in the tabbing order.
Definition: window.hpp:588
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:843
bool click_dismiss(const int mouse_button_mask)
Handles a mouse click event for dismissing the dialog.
Definition: window.cpp:1121
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
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:363
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:1431
builder_window::window_resolution::tooltip_info tooltip_
The settings for the tooltip.
Definition: window.hpp:530
show_mode show_mode_
The mode in which the window is shown.
Definition: window.hpp:452
static void layout(window &window, const unsigned maximum_width, const unsigned maximum_height)
Layouts the window.
Definition: window.cpp:1199
window_definition(const config &cfg)
Definition: window.cpp:1467
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:440
#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:782
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
const t_string & tooltip() const
void show_tooltip()
Shows the window as a tooltip.
Definition: window.cpp:453
unsigned screen_height
Definition: settings.cpp:29
Definition: contexts.hpp:42
bool find(E event, F functor)
Tests whether an event handler is available.
void set_id(const std::string &id)
Definition: widget.cpp:95
void mouse_capture(const bool capture=true)
Definition: window.cpp:1272
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:478
void redraw_windows_on_top() const
Schedules windows on top of us (if any) to redraw.
Definition: window.cpp:1174
void layout()
Layouts the window.
Definition: window.cpp:882
Simple push button.
Definition: button.hpp:35
std::unique_ptr< event::distributor > event_distributor_
Definition: window.hpp:684
void signal_handler_message_show_helptip(const event::ui_event event, bool &handled, const event::message &message)
Definition: window.cpp:1417
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:805
friend class debug_layout_graph
Definition: window.hpp:64
dialogs::modal_dialog * owner_
The dialog that owns the window.
Definition: window.hpp:458
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
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:862
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:33
int height
The current height of all widgets in the group, -1 if the height is not linked.
Definition: window.hpp:581
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
void add_to_tab_order(widget *widget, int at=-1)
Add the widget to the tabbing order.
Definition: window.cpp:1296
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:286
void set_want_keyboard_input(const bool want_keyboard_input)
Definition: dispatcher.hpp:744
std::shared_ptr< halo_record > handle
Definition: halo.hpp:31
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:62
#define LOG_IMPL_SCOPE_HEADER
Definition: window.cpp:79
The window is being shown.
Definition: window.hpp:168
std::map< std::string, linked_size > linked_size_
List of the widgets, whose size are linked together.
Definition: window.hpp:585
wfl::map_formula_callable variables_
The variables of the canvas.
Definition: window.hpp:469
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
static std::deque< std::string > call_stack
Definition: function.cpp:39
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:55
void layout_linked_widgets()
Layouts the linked widgets.
Definition: window.cpp:1075
std::pair< std::string, unsigned > item
Definition: help_impl.hpp:372
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:154
std::function< void()> callback_next_draw_
Definition: window.hpp:770
Basic exception when the layout doesn&#39;t fit.