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