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