The Battle for Wesnoth  1.13.4+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 - 2016 by Mark de Wever <[email protected]>
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 "display.hpp"
27 #include "events.hpp"
28 #include "floating_label.hpp"
29 #include "formula/callable.hpp"
30 #include "font.hpp"
31 #include "gettext.hpp"
32 #include "log.hpp"
38 #include "gui/core/log.hpp"
40 #include "gui/core/point.hpp"
43 #include "gui/dialogs/tip.hpp"
44 #include "gui/widgets/button.hpp"
47 #include "gui/widgets/grid.hpp"
48 #include "gui/widgets/helper.hpp"
49 #include "gui/widgets/panel.hpp"
50 #include "gui/widgets/settings.hpp"
51 #include "gui/widgets/widget.hpp"
52 #include "gui/widgets/window.hpp"
53 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
54 #include "gui/widgets/debug.hpp"
55 #endif
56 #include "preferences.hpp"
57 #include "preferences_display.hpp"
59 #include "sdl/rect.hpp"
60 #include "sdl/utils.hpp"
61 #include "tstring.hpp"
62 #include "formula/variant.hpp"
63 #include "video.hpp"
64 #include "wml_exception.hpp"
65 
66 #include "utils/functional.hpp"
67 
68 namespace game_logic { class function_symbol_table; }
69 namespace gui2 { class tbutton; }
70 
71 
72 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
73 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
74 
75 #define LOG_IMPL_SCOPE_HEADER \
76  window.get_control_type() + " [" + window.id() + "] " + __func__
77 #define LOG_IMPL_HEADER LOG_IMPL_SCOPE_HEADER + ':'
78 
79 namespace gui2
80 {
81 
82 // ------------ WIDGET -----------{
83 
84 namespace implementation
85 {
86 /** @todo See whether this hack can be removed. */
87 // Needed to fix a compiler error in REGISTER_WIDGET.
89 {
90 public:
92  {
93  }
94 
96 
97  twidget* build() const
98  {
99  return nullptr;
100  }
101 };
102 
103 } // namespace implementation
104 REGISTER_WIDGET(window)
105 
106 unsigned twindow::sunset_ = 0;
107 
108 namespace
109 {
110 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
111 const unsigned SHOW = tdebug_layout_graph::SHOW;
112 const unsigned LAYOUT = tdebug_layout_graph::LAYOUT;
113 #else
114 // values are irrelavant when DEBUG_WINDOW_LAYOUT_GRAPHS is not defined.
115 const unsigned SHOW = 0;
116 const unsigned LAYOUT = 0;
117 #endif
118 
119 /**
120  * The interval between draw events.
121  *
122  * When the window is shown this value is set, the callback function always
123  * uses this value instead of the parameter send, that way the window can stop
124  * drawing when it wants.
125  */
126 static int draw_interval = 0;
127 
128 /**
129  * SDL_AddTimer() callback for the draw event.
130  *
131  * When this callback is called it pushes a new draw event in the event queue.
132  *
133  * @returns The new timer interval, 0 to stop.
134  */
135 static Uint32 draw_timer(Uint32, void*)
136 {
137  // DBG_GUI_E << "Pushing draw event in queue.\n";
138 
139  SDL_Event event;
140  SDL_UserEvent data;
141 
142  data.type = DRAW_EVENT;
143  data.code = 0;
144  data.data1 = nullptr;
145  data.data2 = nullptr;
146 
147  event.type = DRAW_EVENT;
148  event.user = data;
149 
150  SDL_PushEvent(&event);
151  return draw_interval;
152 }
153 
154 /**
155  * SDL_AddTimer() callback for delay_event.
156  *
157  * @param event The event to push in the event queue.
158  *
159  * @return The new timer interval (always 0).
160  */
161 static Uint32 delay_event_callback(const Uint32, void* event)
162 {
163  SDL_PushEvent(static_cast<SDL_Event*>(event));
164  return 0;
165 }
166 
167 /**
168  * Allows an event to be delayed a certain amount of time.
169  *
170  * @note the delay is the minimum time, after the time has passed the event
171  * will be pushed in the SDL event queue, so it might delay more.
172  *
173  * @param event The event to delay.
174  * @param delay The number of ms to delay the event.
175  */
176 static void delay_event(const SDL_Event& event, const Uint32 delay)
177 {
178  SDL_AddTimer(delay, delay_event_callback, new SDL_Event(event));
179 }
180 
181 /**
182  * Adds a SHOW_HELPTIP event to the SDL event queue.
183  *
184  * The event is used to show the helptip for the currently focused widget.
185  */
186 static bool helptip()
187 {
188  DBG_GUI_E << "Pushing SHOW_HELPTIP_EVENT event in queue.\n";
189 
190  SDL_Event event;
191  SDL_UserEvent data;
192 
193  data.type = SHOW_HELPTIP_EVENT;
194  data.code = 0;
195  data.data1 = nullptr;
196  data.data2 = nullptr;
197 
198  event.type = SHOW_HELPTIP_EVENT;
199  event.user = data;
200 
201  SDL_PushEvent(&event);
202 
203  return true;
204 }
205 
206 /**
207  * Small helper class to get an unique id for every window instance.
208  *
209  * This is used to send event to the proper window, this allows windows to post
210  * messages to themselves and let them delay for a certain amount of time.
211  */
212 class tmanager
213 {
214  tmanager();
215 
216 public:
217  static tmanager& instance();
218 
219  void add(twindow& window);
220 
221  void remove(twindow& window);
222 
223  unsigned get_id(twindow& window);
224 
225  twindow* window(const unsigned id);
226 
227 private:
228  // The number of active window should be rather small
229  // so keep it simple and don't add a reverse lookup map.
230  std::map<unsigned, twindow*> windows_;
231 };
232 
233 tmanager::tmanager() : windows_()
234 {
235 }
236 
237 tmanager& tmanager::instance()
238 {
239  static tmanager window_manager;
240  return window_manager;
241 }
242 
243 void tmanager::add(twindow& window)
244 {
245  static unsigned id;
246  ++id;
247  windows_[id] = &window;
248 }
249 
250 void tmanager::remove(twindow& window)
251 {
253  itor != windows_.end();
254  ++itor) {
255 
256  if(itor->second == &window) {
257  windows_.erase(itor);
258  return;
259  }
260  }
261  assert(false);
262 }
263 
264 unsigned tmanager::get_id(twindow& window)
265 {
267  itor != windows_.end();
268  ++itor) {
269 
270  if(itor->second == &window) {
271  return itor->first;
272  }
273  }
274  assert(false);
275 
276  return 0;
277 }
278 
279 twindow* tmanager::window(const unsigned id)
280 {
282 
283  if(itor == windows_.end()) {
284  return nullptr;
285  } else {
286  return itor->second;
287  }
288 }
289 
290 } // namespace
291 
292 twindow::twindow(CVideo& video,
297  tformula<bool> reevaluate_best_size,
298  const game_logic::function_symbol_table& functions,
299  const bool automatic_placement,
300  const unsigned horizontal_placement,
301  const unsigned vertical_placement,
302  const unsigned maximum_width,
303  const unsigned maximum_height,
304  const std::string& definition,
305  const twindow_builder::tresolution::ttip& tooltip,
306  const twindow_builder::tresolution::ttip& helptip)
307  : tpanel()
308  , cursor::setter(cursor::NORMAL)
309  , video_(video)
310  , status_(NEW)
311  , show_mode_(none)
312  , retval_(NONE)
313  , owner_(0)
314  , need_layout_(true)
315  , variables_()
316  , invalidate_layout_blocked_(false)
317  , suspend_drawing_(true)
318  , restore_(true)
319  , restorer_()
320  , automatic_placement_(automatic_placement)
321  , horizontal_placement_(horizontal_placement)
322  , vertical_placement_(vertical_placement)
323  , maximum_width_(maximum_width)
324  , maximum_height_(maximum_height)
325  , x_(x)
326  , y_(y)
327  , w_(w)
328  , h_(h)
329  , reevaluate_best_size_(reevaluate_best_size)
330  , functions_(functions)
331  , tooltip_(tooltip)
332  , helptip_(helptip)
333  , click_dismiss_(false)
334  , enter_disabled_(false)
335  , escape_disabled_(false)
336  , linked_size_()
337  , mouse_button_state_(0) /**< Needs to be initialised in @ref show. */
338  , dirty_list_()
339 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
340  , debug_layout_(new tdebug_layout_graph(this))
341 #endif
342  , event_distributor_(
343  new event::tdistributor(*this, event::tdispatcher::front_child))
344 {
345  // We load the config in here as exception.
346  // Our caller did update the screen size so no need for us to do that again.
347  set_definition(definition);
348  load_config();
349 
350  tmanager::instance().add(*this);
351 
352  connect();
353 
354  connect_signal<event::DRAW>(std::bind(&twindow::draw, this));
355 
356  connect_signal<event::SDL_VIDEO_RESIZE>(std::bind(
357  &twindow::signal_handler_sdl_video_resize, this, _2, _3, _5));
358 
359  connect_signal<event::SDL_ACTIVATE>(std::bind(
361 
362  connect_signal<event::SDL_LEFT_BUTTON_UP>(
364  this,
365  _2,
366  _3,
367  _4,
368  SDL_BUTTON_LMASK),
370  connect_signal<event::SDL_MIDDLE_BUTTON_UP>(
372  this,
373  _2,
374  _3,
375  _4,
376  SDL_BUTTON_MMASK),
378  connect_signal<event::SDL_RIGHT_BUTTON_UP>(
380  this,
381  _2,
382  _3,
383  _4,
384  SDL_BUTTON_RMASK),
386 
387  connect_signal<event::SDL_KEY_DOWN>(
388  std::bind(
389  &twindow::signal_handler_sdl_key_down, this, _2, _3, _5),
391  connect_signal<event::SDL_KEY_DOWN>(std::bind(
392  &twindow::signal_handler_sdl_key_down, this, _2, _3, _5));
393 
394  connect_signal<event::MESSAGE_SHOW_TOOLTIP>(
396  this,
397  _2,
398  _3,
399  _5),
401 
402  connect_signal<event::MESSAGE_SHOW_HELPTIP>(
404  this,
405  _2,
406  _3,
407  _5),
409 
410  connect_signal<event::REQUEST_PLACEMENT>(
411  std::bind(
414 
415  register_hotkey(hotkey::GLOBAL__HELPTIP, std::bind(gui2::helptip));
416 }
417 
419 {
420  /*
421  * We need to delete our children here instead of waiting for the grid to
422  * automatically do it. The reason is when the grid deletes its children
423  * they will try to unregister them self from the linked widget list. At
424  * this point the member of twindow are destroyed and we enter UB. (For
425  * some reason the bug didn't trigger on g++ but it does on MSVC.
426  */
427  for(unsigned row = 0; row < grid().get_rows(); ++row) {
428  for(unsigned col = 0; col < grid().get_cols(); ++col) {
429  grid().remove_child(row, col);
430  }
431  }
432 
433  /*
434  * The tip needs to be closed if the window closes and the window is
435  * not a tip. If we don't do that the tip will unrender in the next
436  * window and cause drawing glitches.
437  * Another issue is that on smallgui and an MP game the tooltip not
438  * unrendered properly can capture the mouse and make playing impossible.
439  */
440  if(show_mode_ == modal) {
441  tip::remove();
442  }
443 
444  tmanager::instance().remove(*this);
445 
446 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
447 
448  delete debug_layout_;
449 
450 #endif
451  delete event_distributor_;
452 }
453 
455 {
456  return tmanager::instance().window(handle);
457 }
458 
460 {
461  // Only if we're the toplevel window we need to update the size, otherwise
462  // it's done in the resize event.
463  if(draw_interval == 0) {
464  const SDL_Rect rect = screen_area();
465  settings::screen_width = rect.w;
466  settings::screen_height = rect.h;
467 
470 
472  if(display) {
473  const SDL_Rect rect_gm = display->map_outside_area();
474 
475  if(rect_gm.w && rect_gm.h) {
476  settings::gamemap_width = rect_gm.w;
477  settings::gamemap_height = rect_gm.h;
478  settings::gamemap_x_offset = rect_gm.x;
479  }
480  }
481  }
482 }
483 
484 /*WIKI
485  * @page = GUIToolkitWML
486  * @order = 3_widget_window_2
487  *
488  * List if the id's that have generate a return value:
489  * * ok confirms the dialog.
490  * * cancel cancels the dialog.
491  *
492  */
494 {
495  // Note it might change to a map later depending on the number
496  // of items.
497  if(id == "ok") {
498  return OK;
499  } else if(id == "cancel") {
500  return CANCEL;
501 
502  /**
503  * The ones for the title screen.
504  *
505  * This is a kind of hack, but the values are hardcoded in the
506  * titlescreen and don't want to change them at the moment. It would be
507  * a good idea to
508  * add some namespaces to avoid names clashing.
509  */
510  } else if(id == "tutorial") {
511  return static_cast<tretval>(ttitle_screen::TUTORIAL);
512  } else if(id == "editor") {
513  return static_cast<tretval>(ttitle_screen::START_MAP_EDITOR);
514  } else if(id == "credits") {
515  return static_cast<tretval>(ttitle_screen::SHOW_ABOUT);
516  } else if(id == "quit") {
517  return static_cast<tretval>(ttitle_screen::QUIT_GAME);
518 
519  /**
520  * The hacks which are here so the old engine can handle the event. The
521  * new engine can't handle all dialogs yet, so it needs to fall back to
522  * the old engine to make certain things happen.
523  */
524  } else if(id == "help") {
525  return static_cast<tretval>(ttitle_screen::SHOW_HELP);
526  } else if(id == "campaign") {
527  return static_cast<tretval>(ttitle_screen::NEW_CAMPAIGN);
528  } else if(id == "multiplayer") {
529  return static_cast<tretval>(ttitle_screen::MULTIPLAYER);
530  } else if(id == "load") {
531  return static_cast<tretval>(ttitle_screen::LOAD_GAME);
532  } else if(id == "addons") {
533  return static_cast<tretval>(ttitle_screen::GET_ADDONS);
534  } else if(id == "cores") {
535  return static_cast<tretval>(ttitle_screen::CORES);
536  } else if(id == "language") {
537  return static_cast<tretval>(ttitle_screen::CHANGE_LANGUAGE);
538  } else if(id == "preferences") {
539  return static_cast<tretval>(ttitle_screen::EDIT_PREFERENCES);
540 
541  // default if nothing matched
542  } else {
543  return NONE;
544  }
545 }
546 
547 void twindow::show_tooltip(/*const unsigned auto_close_timeout*/)
548 {
549  log_scope2(log_gui_draw, "Window: show as tooltip.");
550 
551  generate_dot_file("show", SHOW);
552 
553  assert(status_ == NEW);
554 
557 
559 
560  /*
561  * Before show has been called, some functions might have done some testing
562  * on the window and called layout, which can give glitches. So
563  * reinvalidate the window to avoid those glitches.
564  */
566  suspend_drawing_ = false;
567 }
568 
569 void twindow::show_non_modal(/*const unsigned auto_close_timeout*/)
570 {
571  log_scope2(log_gui_draw, "Window: show non modal.");
572 
573  generate_dot_file("show", SHOW);
574 
575  assert(status_ == NEW);
576 
578 
579  show_mode_ = modal;
580 
581  /*
582  * Before show has been called, some functions might have done some testing
583  * on the window and called layout, which can give glitches. So
584  * reinvalidate the window to avoid those glitches.
585  */
587  suspend_drawing_ = false;
588 
589  events::pump();
590 }
591 
592 int twindow::show(const bool restore, const unsigned auto_close_timeout)
593 {
594  /*
595  * Removes the old tip if one shown. The show_tip doesn't remove
596  * the tip, since it's the tip.
597  */
598  tip::remove();
599 
600  show_mode_ = modal;
601  restore_ = restore;
602 
603  /**
604  * Helper class to set and restore the drawing interval.
605  *
606  * We need to make sure we restore the value when the function ends, be it
607  * normally or due to an exception.
608  */
609  class tdraw_interval_setter
610  {
611  public:
612  tdraw_interval_setter() : interval_(draw_interval)
613  {
614  if(interval_ == 0) {
615  draw_interval = 30;
616  SDL_AddTimer(draw_interval, draw_timer, nullptr);
617 
618  // There might be some time between creation and showing so
619  // reupdate the sizes.
621  }
622  }
623 
624  ~tdraw_interval_setter()
625  {
626  draw_interval = interval_;
627  }
628 
629  private:
630  int interval_;
631  };
632 
634 
635  generate_dot_file("show", SHOW);
636 
637  assert(status_ == NEW);
638 
639  tdraw_interval_setter draw_interval_setter;
640 
641  /*
642  * Before show has been called, some functions might have done some testing
643  * on the window and called layout, which can give glitches. So
644  * reinvalidate the window to avoid those glitches.
645  */
647  suspend_drawing_ = false;
648 
649  if(auto_close_timeout) {
650  // Make sure we're drawn before we try to close ourselves, which can
651  // happen if the timeout is small.
652  draw();
653 
654  SDL_Event event;
655  SDL_UserEvent data;
656 
657  data.type = CLOSE_WINDOW_EVENT;
658  data.code = tmanager::instance().get_id(*this);
659  data.data1 = nullptr;
660  data.data2 = nullptr;
661 
662  event.type = CLOSE_WINDOW_EVENT;
663  event.user = data;
664 
665  delay_event(event, auto_close_timeout);
666  }
667 
668 
669  try
670  {
671  // Start our loop drawing will happen here as well.
672  bool mouse_button_state_initialised = false;
673  for(status_ = SHOWING; status_ != REQUEST_CLOSE;) {
674  // process installed callback if valid, to allow e.g. network
675  // polling
676  events::pump();
677 
678  if(!mouse_button_state_initialised) {
679  /*
680  * The state must be initialize when showing the dialogue.
681  * However when initialized before this point there were random
682  * errors. This only happened when the 'click' was done fast; a
683  * slower click worked properly.
684  *
685  * So it seems the events need to be processed before SDL can
686  * return the proper button state. When initializing here all
687  * works fine.
688  */
689  mouse_button_state_ = SDL_GetMouseState(nullptr, nullptr);
690  mouse_button_state_initialised = true;
691  }
692 
693  // Add a delay so we don't keep spinning if there's no event.
694  SDL_Delay(10);
695  }
696  }
697  catch(...)
698  {
699  /**
700  * @todo Clean up the code duplication.
701  *
702  * In the future the restoring shouldn't be needed so the duplication
703  * doesn't hurt too much but keep this todo as a reminder.
704  */
705  suspend_drawing_ = true;
706 
707  // restore area
708  if(restore_) {
709  SDL_Rect rect = get_rectangle();
710  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
712 #ifdef SDL_GPU
714 #else
716 #endif
717  }
718  throw;
719  }
720 
721  suspend_drawing_ = true;
722 
723  // restore area
724  if(restore_) {
725  SDL_Rect rect = get_rectangle();
726  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
728 #ifdef SDL_GPU
730 #else
732 #endif
733  }
734 
735  return retval_;
736 }
737 
739 {
740  /***** ***** ***** ***** Init ***** ***** ***** *****/
741  // Prohibited from drawing?
742  if(suspend_drawing_) {
743  return;
744  }
745 
746  surface& frame_buffer = video_.getSurface();
747 
748  /***** ***** Layout and get dirty list ***** *****/
749  if(need_layout_) {
750  // Restore old surface. In the future this phase will not be needed
751  // since all will be redrawn when needed with dirty rects. Since that
752  // doesn't work yet we need to undraw the window.
753  if(restore_ && restorer_) {
754  SDL_Rect rect = get_rectangle();
755  sdl_blit(restorer_, 0, frame_buffer, &rect);
756  // Since the old area might be bigger as the new one, invalidate
757  // it.
758  update_rect(rect);
759  }
760 
761  layout();
762 
763  // Get new surface for restoring
764  SDL_Rect rect = get_rectangle();
765  // We want the labels underneath the window so draw them and use them
766  // as restore point.
767 #ifdef SDL_GPU
769 #else
770  font::draw_floating_labels(frame_buffer);
771 #endif
772  if(restore_) {
773  restorer_ = get_surface_portion(frame_buffer, rect);
774  }
775 
776  // Need full redraw so only set ourselves dirty.
777  dirty_list_.push_back(std::vector<twidget*>(1, this));
778  } else {
779 
780  // Let widgets update themselves, which might dirty some things.
781  layout_children();
782 
783  // Now find the widgets that are dirty.
784  std::vector<twidget*> call_stack;
785  if(!new_widgets) {
786  populate_dirty_list(*this, call_stack);
787  } else {
788  /* Force to update and redraw the entire screen */
789  dirty_list_.clear();
790  dirty_list_.push_back(std::vector<twidget*>(1, this));
792  }
793  }
794 
795  if(dirty_list_.empty()) {
797  surface& frame_buffer = get_video_surface();
798 
799  if(sunset_) {
800  /** @todo should probably be moved to event::thandler::draw. */
801  static unsigned i = 0;
802  if(++i % sunset_ == 0) {
803  SDL_Rect r = sdl::create_rect(
804  0, 0, frame_buffer->w, frame_buffer->h);
805  const Uint32 color
806  = SDL_MapRGBA(frame_buffer->format, 0, 0, 0, SDL_ALPHA_OPAQUE);
807 
808  sdl::fill_rect_alpha(r, color, 1, frame_buffer);
809  update_rect(r);
810  }
811  }
812  }
813  return;
814  }
815 
816  for(auto & item : dirty_list_)
817  {
818 
819  assert(!item.empty());
820 
821  const SDL_Rect dirty_rect
823  : item.back()->get_dirty_rectangle();
824 
825 // For testing we disable the clipping rect and force the entire screen to
826 // update. This way an item rendered at the wrong place is directly visible.
827 #if 0
828  dirty_list_.clear();
829  dirty_list_.push_back(std::vector<twidget*>(1, this));
831 #else
832  clip_rect_setter clip(frame_buffer, &dirty_rect);
833 #endif
834 
835  /*
836  * The actual update routine does the following:
837  * - Restore the background.
838  *
839  * - draw [begin, end) the back ground of all widgets.
840  *
841  * - draw the children of the last item in the list, if this item is
842  * a container it's children get a full redraw. If it's not a
843  * container nothing happens.
844  *
845  * - draw [rbegin, rend) the fore ground of all widgets. For items
846  * which have two layers eg window or panel it draws the foreground
847  * layer. For other widgets it's a nop.
848  *
849  * Before drawing there needs to be determined whether a dirty widget
850  * really needs to be redrawn. If the widget doesn't need to be
851  * redrawing either being not tvisible::visible or has status
852  * twidget::tredraw_action::none. If it's not drawn it's still set not
853  * dirty to avoid it keep getting on the dirty list.
854  */
855 
856  for(std::vector<twidget*>::iterator itor = item.begin();
857  itor != item.end();
858  ++itor) {
859 
860  if((**itor).get_visible() != twidget::tvisible::visible
861  || (**itor).get_drawing_action()
863 
864  for(std::vector<twidget*>::iterator citor = itor;
865  citor != item.end();
866  ++citor) {
867 
868  (**citor).set_is_dirty(false);
869  }
870 
871  item.erase(itor, item.end());
872  break;
873  }
874  }
875 
876  // Restore.
877  if(restore_) {
878  SDL_Rect rect = get_rectangle();
879  sdl_blit(restorer_, 0, frame_buffer, &rect);
880  }
881 
882  // Background.
883  for(std::vector<twidget*>::iterator itor = item.begin();
884  itor != item.end();
885  ++itor) {
886 
887  (**itor).draw_background(frame_buffer, 0, 0);
888  }
889 
890  // Children.
891  if(!item.empty()) {
892  item.back()->draw_children(frame_buffer, 0, 0);
893  }
894 
895  // Foreground.
896  for(std::vector<twidget*>::reverse_iterator ritor = item.rbegin();
897  ritor != item.rend();
898  ++ritor) {
899 
900  (**ritor).draw_foreground(frame_buffer, 0, 0);
901  (**ritor).set_is_dirty(false);
902  }
903 
904  update_rect(dirty_rect);
905  }
906 
907  dirty_list_.clear();
908 
909  std::vector<twidget*> call_stack;
910  populate_dirty_list(*this, call_stack);
911  assert(dirty_list_.empty());
912 
913  SDL_Rect rect = get_rectangle();
914  update_rect(rect);
915 }
916 
918 {
919  if(restore_ && restorer_) {
920  SDL_Rect rect = get_rectangle();
921  sdl_blit(restorer_, 0, video_.getSurface(), &rect);
922  // Since the old area might be bigger as the new one, invalidate
923  // it.
924  update_rect(rect);
925  }
926 }
927 
929  : window_(window)
930 {
933 }
934 
936 {
937  assert(window_.invalidate_layout_blocked_);
938  window_.invalidate_layout_blocked_ = false;
939 }
940 
942 {
944  need_layout_ = true;
945  }
946 }
947 twidget* twindow::find_at(const tpoint& coordinate, const bool must_be_active)
948 {
949  return tpanel::find_at(coordinate, must_be_active);
950 }
951 
952 const twidget* twindow::find_at(const tpoint& coordinate,
953  const bool must_be_active) const
954 {
955  return tpanel::find_at(coordinate, must_be_active);
956 }
957 
958 twidget* twindow::find(const std::string& id, const bool must_be_active)
959 {
960  return tcontainer_::find(id, must_be_active);
961 }
962 
963 const twidget* twindow::find(const std::string& id, const bool must_be_active)
964  const
965 {
966  return tcontainer_::find(id, must_be_active);
967 }
968 
970  const bool fixed_width,
971  const bool fixed_height)
972 {
973  assert(fixed_width || fixed_height);
974  assert(!has_linked_size_group(id));
975 
976  linked_size_[id] = tlinked_size(fixed_width, fixed_height);
977 }
978 
980 {
981  return linked_size_.find(id) != linked_size_.end();
982 }
983 
985 {
986  assert(widget);
987  assert(has_linked_size_group(id));
988 
989  std::vector<twidget*>& widgets = linked_size_[id].widgets;
990  if(std::find(widgets.begin(), widgets.end(), widget) == widgets.end()) {
991  widgets.push_back(widget);
992  }
993 }
994 
995 void twindow::remove_linked_widget(const std::string& id, const twidget* widget)
996 {
997  assert(widget);
998  assert(has_linked_size_group(id));
999 
1000  std::vector<twidget*>& widgets = linked_size_[id].widgets;
1001 
1003  = std::find(widgets.begin(), widgets.end(), widget);
1004 
1005  if(itor != widgets.end()) {
1006  widgets.erase(itor);
1007 
1008  assert(std::find(widgets.begin(), widgets.end(), widget)
1009  == widgets.end());
1010  }
1011 }
1012 
1014 {
1015  /***** Initialize. *****/
1016 
1018  conf = boost::dynamic_pointer_cast<const twindow_definition::tresolution>(
1019  config());
1020  assert(conf);
1021 
1023 
1024  const tpoint mouse = get_mouse_position();
1025  variables_.add("mouse_x", variant(mouse.x));
1026  variables_.add("mouse_y", variant(mouse.y));
1027  variables_.add("window_width", variant(0));
1028  variables_.add("window_height", variant(0));
1029  variables_.add("size_request_mode", variant("maximum"));
1031 
1032  const int maximum_width = automatic_placement_ ? maximum_width_
1035  : w_(variables_, &functions_);
1036 
1037  const int maximum_height = automatic_placement_ ? maximum_height_
1040  : h_(variables_, &functions_);
1041 
1042  /***** Handle click dismiss status. *****/
1043  tbutton* click_dismiss_button = nullptr;
1044  if((click_dismiss_button
1045  = find_widget<tbutton>(this, "click_dismiss", false, false))) {
1046 
1047  click_dismiss_button->set_visible(twidget::tvisible::invisible);
1048  }
1049  if(click_dismiss_) {
1050  tbutton* button = find_widget<tbutton>(this, "ok", false, false);
1051  if(button) {
1053  click_dismiss_button = button;
1054  }
1055  VALIDATE(click_dismiss_button,
1056  _("Click dismiss needs a 'click_dismiss' or 'ok' button."));
1057  }
1058 
1059  /***** Layout. *****/
1060  layout_initialise(true);
1061  generate_dot_file("layout_initialise", LAYOUT);
1062 
1064 
1065  try
1066  {
1067  twindow_implementation::layout(*this, maximum_width, maximum_height);
1068  }
1070  {
1071 
1072  /** @todo implement the scrollbars on the window. */
1073 
1074  std::stringstream sstr;
1075  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
1076  << "' found the following problem: Failed to size window;"
1077  << " wanted size " << get_best_size() << " available size "
1078  << maximum_width << ',' << maximum_height << " screen size "
1079  << settings::screen_width << ',' << settings::screen_height << '.';
1080 
1081  throw twml_exception(_("Failed to show a dialog, "
1082  "which doesn't fit on the screen."),
1083  sstr.str());
1084  }
1085 
1086  /****** Validate click dismiss status. *****/
1088  assert(click_dismiss_button);
1089  click_dismiss_button->set_visible(twidget::tvisible::visible);
1090 
1092  *click_dismiss_button,
1093  std::bind(&twindow::set_retval, this, OK, true));
1094 
1095  layout_initialise(true);
1096  generate_dot_file("layout_initialise", LAYOUT);
1097 
1099 
1100  try
1101  {
1103  *this, maximum_width, maximum_height);
1104  }
1106  {
1107 
1108  /** @todo implement the scrollbars on the window. */
1109 
1110  std::stringstream sstr;
1111  sstr << __FILE__ << ":" << __LINE__ << " in function '" << __func__
1112  << "' found the following problem: Failed to size window;"
1113  << " wanted size " << get_best_size() << " available size "
1114  << maximum_width << ',' << maximum_height << " screen size "
1116  << '.';
1117 
1118  throw twml_exception(_("Failed to show a dialog, "
1119  "which doesn't fit on the screen."),
1120  sstr.str());
1121  }
1122  }
1123 
1124  /***** Get the best location for the window *****/
1126 
1127  assert(size.x <= maximum_width && size.y <= maximum_height);
1128 
1129  tpoint origin(0, 0);
1130 
1131  if(automatic_placement_) {
1132 
1133  switch(horizontal_placement_) {
1135  // Do nothing
1136  break;
1138  origin.x = (settings::screen_width - size.x) / 2;
1139  break;
1141  origin.x = settings::screen_width - size.x;
1142  break;
1143  default:
1144  assert(false);
1145  }
1146  switch(vertical_placement_) {
1148  // Do nothing
1149  break;
1151  origin.y = (settings::screen_height - size.y) / 2;
1152  break;
1154  origin.y = settings::screen_height - size.y;
1155  break;
1156  default:
1157  assert(false);
1158  }
1159  } else {
1160 
1161  variables_.add("window_width", variant(size.x));
1162  variables_.add("window_height", variant(size.y));
1163 
1165  layout_initialise(true);
1166 
1169  h_(variables_, &functions_));
1170 
1171  size = get_best_size();
1172  variables_.add("window_width", variant(size.x));
1173  variables_.add("window_height", variant(size.y));
1174  }
1175 
1176  variables_.add("size_request_mode", variant("size"));
1177 
1178  origin.x = x_(variables_, &functions_);
1179  origin.y = y_(variables_, &functions_);
1180 
1181  size.x = w_(variables_, &functions_);
1182  size.y = h_(variables_, &functions_);
1183  }
1184 
1185  /***** Set the window size *****/
1186  place(origin, size);
1187 
1188  generate_dot_file("layout_finished", LAYOUT);
1189  need_layout_ = false;
1190 
1192 }
1193 
1195 {
1196  // evaluate the group sizes
1197  for(auto & linked_size : linked_size_)
1198  {
1199 
1200  tpoint max_size(0, 0);
1201 
1202  // Determine the maximum size.
1203  for(auto widget : linked_size.second.widgets)
1204  {
1205 
1206  const tpoint size = widget->get_best_size();
1207 
1208  if(size.x > max_size.x) {
1209  max_size.x = size.x;
1210  }
1211  if(size.y > max_size.y) {
1212  max_size.y = size.y;
1213  }
1214  }
1215  if(linked_size.second.width != -1) {
1216  linked_size.second.width = max_size.x;
1217  }
1218  if(linked_size.second.height != -1) {
1219  linked_size.second.height = max_size.y;
1220  }
1221 
1222  // Set the maximum size.
1223  for(auto widget : linked_size.second.widgets)
1224  {
1225 
1226  tpoint size = widget->get_best_size();
1227 
1228  if(linked_size.second.width != -1) {
1229  size.x = max_size.x;
1230  }
1231  if(linked_size.second.height != -1) {
1232  size.y = max_size.y;
1233  }
1234 
1235  widget->set_layout_size(size);
1236  }
1237  }
1238 }
1239 
1240 bool twindow::click_dismiss(const Uint8 mouse_button_mask)
1241 {
1242  if(does_click_dismiss()) {
1243  if((mouse_button_state_ & mouse_button_mask) == 0) {
1244  set_retval(OK);
1245  } else {
1246  mouse_button_state_ &= ~mouse_button_mask;
1247  }
1248  return true;
1249  }
1250  return false;
1251 }
1252 
1254 {
1255  static const std::string type = "window";
1256  return type;
1257 }
1258 
1259 namespace
1260 {
1261 
1262 /**
1263  * Swaps an item in a grid for another one.*/
1264 void swap_grid(tgrid* grid,
1265  tgrid* content_grid,
1266  twidget* widget,
1267  const std::string& id)
1268 {
1269  assert(content_grid);
1270  assert(widget);
1271 
1272  // Make sure the new child has same id.
1273  widget->set_id(id);
1274 
1275  // Get the container containing the wanted widget.
1276  tgrid* parent_grid = nullptr;
1277  if(grid) {
1278  parent_grid = find_widget<tgrid>(grid, id, false, false);
1279  }
1280  if(!parent_grid) {
1281  parent_grid = find_widget<tgrid>(content_grid, id, true, false);
1282  assert(parent_grid);
1283  }
1284  if(tgrid* g = dynamic_cast<tgrid*>(parent_grid->parent())) {
1285  widget = g->swap_child(id, widget, false);
1286  } else if(tcontainer_* c
1287  = dynamic_cast<tcontainer_*>(parent_grid->parent())) {
1288 
1289  widget = c->grid().swap_child(id, widget, true);
1290  } else {
1291  assert(false);
1292  }
1293 
1294  assert(widget);
1295 
1296  delete widget;
1297 }
1298 
1299 } // namespace
1300 
1302 {
1303  swap_grid(nullptr, &grid(), content_grid->build(), "_window_content_grid");
1304 }
1305 
1306 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1307 
1309  const unsigned domain)
1310 {
1311  debug_layout_->generate_dot_file(generator, domain);
1312 }
1313 #endif
1314 
1316  const unsigned maximum_width,
1317  const unsigned maximum_height)
1318 {
1320 
1321  /*
1322  * For now we return the status, need to test later whether this can
1323  * entirely be converted to an exception based system as in 'promised' on
1324  * the algorithm page.
1325  */
1326 
1327  try
1328  {
1329  tpoint size = window.get_best_size();
1330 
1331  DBG_GUI_L << LOG_IMPL_HEADER << " best size : " << size
1332  << " maximum size : " << maximum_width << ','
1333  << maximum_height << ".\n";
1334  if(size.x <= static_cast<int>(maximum_width)
1335  && size.y <= static_cast<int>(maximum_height)) {
1336 
1337  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Fits, nothing to do.\n";
1338  return;
1339  }
1340 
1341  if(size.x > static_cast<int>(maximum_width)) {
1342  window.reduce_width(maximum_width);
1343 
1344  size = window.get_best_size();
1345  if(size.x > static_cast<int>(maximum_width)) {
1346  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize width failed."
1347  << " Wanted width " << maximum_width
1348  << " resulting width " << size.x << ".\n";
1350  }
1352  << " Status: Resize width succeeded.\n";
1353  }
1354 
1355  if(size.y > static_cast<int>(maximum_height)) {
1356  window.reduce_height(maximum_height);
1357 
1358  size = window.get_best_size();
1359  if(size.y > static_cast<int>(maximum_height)) {
1360  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resize height failed."
1361  << " Wanted height " << maximum_height
1362  << " resulting height " << size.y << ".\n";
1364  }
1366  << " Status: Resize height succeeded.\n";
1367  }
1368 
1369  assert(size.x <= static_cast<int>(maximum_width)
1370  && size.y <= static_cast<int>(maximum_height));
1371 
1372 
1373  DBG_GUI_L << LOG_IMPL_HEADER << " Result: Resizing succeeded.\n";
1374  return;
1375  }
1377  {
1379  << " Status: Width has been modified, rerun.\n";
1380 
1381  window.layout_initialise(false);
1382  window.layout_linked_widgets();
1383  layout(window, maximum_width, maximum_height);
1384  return;
1385  }
1386 }
1387 
1388 void twindow::mouse_capture(const bool capture)
1389 {
1390  assert(event_distributor_);
1391  event_distributor_->capture_mouse(capture);
1392 }
1393 
1395 {
1396  assert(event_distributor_);
1397  event_distributor_->keyboard_capture(widget);
1398 }
1399 
1401 {
1402  assert(event_distributor_);
1403  event_distributor_->keyboard_add_to_chain(widget);
1404 }
1405 
1407 {
1408  assert(event_distributor_);
1409  event_distributor_->keyboard_remove_from_chain(widget);
1410 }
1411 
1413  bool& handled,
1414  const tpoint& new_size)
1415 {
1416  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1417 
1420  settings::screen_width = new_size.x;
1421  settings::screen_height = new_size.y;
1423 
1424  handled = true;
1425 }
1426 
1428  bool& handled,
1429  bool& halt,
1430  const Uint8 mouse_button_mask)
1431 {
1432  DBG_GUI_E << LOG_HEADER << ' ' << event << " mouse_button_mask "
1433  << static_cast<unsigned>(mouse_button_mask) << ".\n";
1434 
1435  handled = halt = click_dismiss(mouse_button_mask);
1436 }
1437 
1439  bool& handled,
1440  SDLKey key)
1441 {
1442  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1443 
1444  if(!enter_disabled_ && (key == SDLK_KP_ENTER || key == SDLK_RETURN)) {
1445  set_retval(OK);
1446  handled = true;
1447  } else if(key == SDLK_ESCAPE && !escape_disabled_) {
1448  set_retval(CANCEL);
1449  handled = true;
1450  } else if(key == SDLK_SPACE) {
1451  handled = click_dismiss(0);
1452  }
1453 #ifdef DEBUG_WINDOW_LAYOUT_GRAPHS
1454  if(key == SDLK_F12) {
1455  debug_layout_->generate_dot_file("manual", tdebug_layout_graph::MANUAL);
1456  handled = true;
1457  }
1458 #endif
1459 }
1460 
1462  bool& handled,
1463  event::tmessage& message)
1464 {
1465  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1466 
1467  event::tmessage_show_tooltip& request
1468  = dynamic_cast<event::tmessage_show_tooltip&>(message);
1469 
1470  tip::show(video_, tooltip_.id, request.message, request.location);
1471 
1472  handled = true;
1473 }
1474 
1476  bool& handled,
1477  event::tmessage& message)
1478 {
1479  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1480 
1481  event::tmessage_show_helptip& request
1482  = dynamic_cast<event::tmessage_show_helptip&>(message);
1483 
1484  tip::show(video_, helptip_.id, request.message, request.location);
1485 
1486  handled = true;
1487 }
1488 
1490  bool& handled)
1491 {
1492  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
1493 
1495 
1496  handled = true;
1497 }
1498 
1499 // }---------- DEFINITION ---------{
1500 
1501 /*WIKI
1502  * @page = GUIWidgetDefinitionWML
1503  * @order = 1_window
1504  *
1505  * == Window ==
1506  *
1507  * The definition of a window. A window is a kind of panel see the panel for
1508  * which fields exist
1509  *
1510  * @begin{parent}{name="gui/"}
1511  * @begin{tag}{name="window_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
1512  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="gui/panel_definition/resolution"}
1513  * @allow{link}{name="gui/window/resolution/grid"}
1514  * @allow{link}{name="gui/panel_definition/resolution/background"}
1515  * @allow{link}{name="gui/panel_definition/resolution/foreground"}
1516  * @end{tag}{name="resolution"}
1517  * @end{tag}{name="window_definition"}
1518  * @end{parent}{name="gui/"}
1519  */
1521  : tcontrol_definition(cfg)
1522 {
1523  DBG_GUI_P << "Parsing window " << id << '\n';
1524 
1525  load_resolutions<tresolution>(cfg);
1526 }
1527 
1529  : tpanel_definition::tresolution(cfg), grid(nullptr)
1530 {
1531  const config& child = cfg.child("grid");
1532  // VALIDATE(child, _("No grid defined."));
1533 
1534  /** @todo Evaluate whether the grid should become mandatory. */
1535  if(child) {
1536  grid = new tbuilder_grid(child);
1537  }
1538 }
1539 
1540 // }------------ END --------------
1541 
1542 } // namespace gui2
1543 
1544 
1545 /**
1546  * @page layout_algorithm Layout algorithm
1547  *
1548  * @section introduction Introduction
1549  *
1550  * This page describes how the layout engine for the dialogs works. First
1551  * a global overview of some terms used in this document.
1552  *
1553  * - @ref gui2::twidget "Widget"; Any item which can be used in the widget
1554  * toolkit. Not all widgets are visible. In general widgets cannot be
1555  * sized directly, but this is controlled by a window. A widget has an
1556  * internal size cache and if the value in the cache is not equal to 0,0
1557  * that value is its best size. This value gets set when the widget can
1558  * honor a resize request. It will be set with the value which honors
1559  * the request.
1560  *
1561  * - @ref gui2::tgrid "Grid"; A grid is an invisible container which holds
1562  * one or more widgets. Several widgets have a grid in them to hold
1563  * multiple widgets eg panels and windows.
1564  *
1565  * - @ref gui2::tgrid::tchild "Grid cell"; Every widget which is in a grid is
1566  * put in a grid cell. These cells also hold the information about the gaps
1567  * between widgets the behavior on growing etc. All grid cells must have a
1568  * widget inside them.
1569  *
1570  * - @ref gui2::twindow "Window"; A window is a top level item which has a
1571  * grid with its children. The window handles the sizing of the window and
1572  * makes sure everything fits.
1573  *
1574  * - @ref gui2::twindow::tlinked_size "Shared size group"; A shared size
1575  * group is a number of widgets which share width and or height. These
1576  * widgets are handled separately in the layout algorithm. All grid cells
1577  * width such a widget will get the same height and or width and these
1578  * widgets won't be resized when there's not enough size. To be sure that
1579  * these widgets don't cause trouble for the layout algorithm, they must be
1580  * in a container with scrollbars so there will always be a way to properly
1581  * layout them. The engine must enforce this restriction so the shared
1582  * layout property must be set by the engine after validation.
1583  *
1584  * - All visible grid cells; A grid cell is visible when the widget inside
1585  * of it doesn't have the state tvisible::invisible. Widgets which have the
1586  * state @ref tvisible::hidden are sized properly since when they become
1587  * @ref tvisible::visible the layout shouldn't be invalidated. A grid cell
1588  * that's invisible has size 0,0.
1589  *
1590  * - All resizable grid cells; A grid cell is resizable under the following
1591  * conditions:
1592  * - The widget is tvisible::visible.
1593  * - The widget is not in a shared size group.
1594  *
1595  * There are two layout algorithms with a different purpose.
1596  *
1597  * - The Window algorithm; this algorithm's goal is it to make sure all grid
1598  * cells fit in the window. Sizing the grid cells depends on the widget
1599  * size as well, but this algorithm only sizes the grid cells and doesn't
1600  * handle the widgets inside them.
1601  *
1602  * - The Grid algorithm; after the Window algorithm made sure that all grid
1603  * cells fit this algorithm makes sure the widgets are put in the optimal
1604  * state in their grid cell.
1605  *
1606  * @section layout_algorithm_window Window
1607  *
1608  * Here is the algorithm used to layout the window:
1609  *
1610  * - Perform a full initialization
1611  * (@ref gui2::twidget::layout_initialise (full_initialisation = true)):
1612  * - Clear the internal best size cache for all widgets.
1613  * - For widgets with scrollbars hide them unless the
1614  * @ref gui2::tscrollbar_container::tscrollbar_mode "scrollbar_mode" is
1615  * always_visible or auto_visible.
1616  * - Handle shared sizes:
1617  * - Height and width:
1618  * - Get the best size for all widgets that share height and width.
1619  * - Set the maximum of width and height as best size for all these
1620  * widgets.
1621  * - Width only:
1622  * - Get the best width for all widgets which share their width.
1623  * - Set the maximum width for all widgets, but keep their own height.
1624  * - Height only:
1625  * - Get the best height for all widgets which share their height.
1626  * - Set the maximum height for all widgets, but keep their own width.
1627  * - Start layout loop:
1628  * - Get best size.
1629  * - If width <= maximum_width && height <= maximum_height we're done.
1630  * - If width > maximum_width, optimize the width:
1631  * - For every grid cell in a grid row there will be a resize request
1632  * (@ref gui2::tgrid::reduce_width):
1633  * - Sort the widgets in the row on the resize priority.
1634  * - Loop through this priority queue until the row fits
1635  * - If priority != 0 try to share the extra width else all
1636  * widgets are tried to reduce the full size.
1637  * - Try to shrink the widgets by either wrapping or using a
1638  * scrollbar (@ref gui2::twidget::request_reduce_width).
1639  * - If the row fits in the wanted width this row is done.
1640  * - Else try the next priority.
1641  * - All priorities done and the width still doesn't fit.
1642  * - Loop through this priority queue until the row fits.
1643  * - If priority != 0:
1644  * - try to share the extra width
1645  * -Else:
1646  * - All widgets are tried to reduce the full size.
1647  * - Try to shrink the widgets by sizing them smaller as really
1648  * wanted (@ref gui2::twidget::demand_reduce_width).
1649  * For labels, buttons etc. they get ellipsized.
1650  * - If the row fits in the wanted width this row is done.
1651  * - Else try the next priority.
1652  * - All priorities done and the width still doesn't fit.
1653  * - Throw a layout width doesn't fit exception.
1654  * - If height > maximum_height, optimize the height
1655  * (@ref gui2::tgrid::reduce_height):
1656  * - For every grid cell in a grid column there will be a resize request:
1657  * - Sort the widgets in the column on the resize priority.
1658  * - Loop through this priority queue until the column fits:
1659  * - If priority != 0 try to share the extra height else all
1660  * widgets are tried to reduce the full size.
1661  * - Try to shrink the widgets by using a scrollbar
1662  * (@ref gui2::twidget::request_reduce_height).
1663  * - If succeeded for a widget the width is influenced and the
1664  * width might be invalid.
1665  * - Throw a width modified exception.
1666  * - If the column fits in the wanted height this column is done.
1667  * - Else try the next priority.
1668  * - All priorities done and the height still doesn't fit.
1669  * - Loop through this priority queue until the column fits.
1670  * - If priority != 0 try to share the extra height else all
1671  * widgets are tried to reduce the full size.
1672  * - Try to shrink the widgets by sizing them smaller as really
1673  * wanted (@ref gui2::twidget::demand_reduce_width).
1674  * For labels, buttons etc. they get ellipsized .
1675  * - If the column fits in the wanted height this column is done.
1676  * - Else try the next priority.
1677  * - All priorities done and the height still doesn't fit.
1678  * - Throw a layout height doesn't fit exception.
1679  * - End layout loop.
1680  *
1681  * - Catch @ref gui2::tlayout_exception_width_modified "width modified":
1682  * - Goto relayout.
1683  *
1684  * - Catch
1685  * @ref gui2::tlayout_exception_width_resize_failed "width resize failed":
1686  * - If the window has a horizontal scrollbar which isn't shown but can be
1687  * shown.
1688  * - Show the scrollbar.
1689  * - goto relayout.
1690  * - Else show a layout failure message.
1691  *
1692  * - Catch
1693  * @ref gui2::tlayout_exception_height_resize_failed "height resize failed":
1694  * - If the window has a vertical scrollbar which isn't shown but can be
1695  * shown:
1696  * - Show the scrollbar.
1697  * - goto relayout.
1698  * - Else:
1699  * - show a layout failure message.
1700  *
1701  * - Relayout:
1702  * - Initialize all widgets
1703  * (@ref gui2::twidget::layout_initialise (full_initialisation = false))
1704  * - Handle shared sizes, since the reinitialization resets that state.
1705  * - Goto start layout loop.
1706  *
1707  * @section grid Grid
1708  *
1709  * This section will be documented later.
1710  */
Define the common log macros for the gui toolkit.
bool escape_disabled_
Disable the escape key see our setter for more info.
Definition: window.hpp:585
bool new_widgets
Do we wish to use the new library or not.
Definition: settings.cpp:40
#define DBG_GUI_P
Definition: log.hpp:69
virtual void layout_initialise(const bool full_initialisation) override
See twidget::layout_initialise.
Definition: container.cpp:33
int w_
Definition: font.cpp:607
Defines the exception classes for the layout algorithm.
tformula< unsigned > x_
The x coordinate of the rectangle.
Definition: canvas.cpp:682
void remove_child(const unsigned row, const unsigned col)
Removes and frees a widget in a cell.
Definition: grid.cpp:140
unsigned maximum_height_
The maximum height if automatic_placement_ is true.
Definition: window.hpp:536
Helper for header for the window.
twidget * find(const std::string &id, const bool must_be_active) override
See twidget::find.
Definition: window.cpp:958
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
const tgrid & grid() const
Definition: container.hpp:229
tformula< unsigned > x_
The formula to calulate the x value of the dialog.
Definition: window.hpp:539
tformula< int > maximum_height_
The maximum height for the text.
Definition: canvas.cpp:1286
#define DBG_GUI_L
Definition: log.hpp:58
bool enter_disabled_
Disable the enter key see our setter for more info.
Definition: window.hpp:582
bool does_click_dismiss() const
Does the window close easily?
Definition: window.hpp:332
void remove_from_keyboard_chain(twidget *widget)
Remove the widget from the keyboard chain.
Definition: window.cpp:1406
Play single scenario against humans or AI.
const GLfloat * c
Definition: glew.h:12741
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
rng * generator
This generator is automatically synced during synced context.
Definition: random_new.cpp:52
tretval
Default return values.
Definition: window.hpp:119
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
Definition: video.hpp:58
void set_want_keyboard_input(const bool want_keyboard_input)
Definition: dispatcher.hpp:506
Base container class.
Definition: grid.hpp:29
#define LOG_SCOPE_HEADER
Definition: window.cpp:72
game_logic::function_symbol_table functions_
The formula definitions available for the calulation formulas.
Definition: window.hpp:554
tresolution_definition_ptr config()
Definition: control.hpp:299
static const unsigned HORIZONTAL_ALIGN_RIGHT
Definition: grid.hpp:52
This file contains the window object, this object is a top level container which has the event manage...
twindow_builder::tresolution::ttip tooltip_
The settings for the tooltip.
Definition: window.hpp:557
void signal_handler_message_show_helptip(const event::tevent event, bool &handled, event::tmessage &message)
Definition: window.cpp:1475
std::vector< std::vector< twidget * > > dirty_list_
The list with dirty items in the window.
Definition: window.hpp:679
void signal_handler_sdl_key_down(const event::tevent event, bool &handled, const SDLKey key)
Definition: window.cpp:1438
const unsigned horizontal_placement_
Sets the horizontal placement.
Definition: window.hpp:522
The widget is not visible.
Definition: widget.hpp:142
twidget * find(const std::string &id, const bool must_be_active) override
See twidget::find.
Definition: container.cpp:175
void add_to_keyboard_chain(twidget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1400
#define DRAW_EVENT
Definition: events.hpp:26
static void update_screen_size()
Update the size of the screen variables in settings.
Definition: window.cpp:459
The window has been requested to be closed but still needs to evaluate the request.
Definition: window.hpp:222
surface restorer_
When the window closes this surface is used to undraw the window.
Definition: window.hpp:511
unsigned int get_rows() const
Definition: grid.hpp:288
lg::log_domain log_gui_layout("gui/layout")
Definition: log.hpp:57
void connect_signal_mouse_left_click(tdispatcher &dispatcher, const tsignal_function &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.hpp:710
void register_hotkey(const hotkey::HOTKEY_COMMAND id, const thotkey_function &function)
Registers a hotkey.
Definition: dispatcher.cpp:303
#define LOG_IMPL_HEADER
Definition: window.cpp:77
static tretval get_retval_by_id(const std::string &id)
Gets the retval for the default buttons.
Definition: window.cpp:493
GLboolean GLboolean g
Definition: glew.h:7319
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
Contains the event distributor.
void undraw()
Undraws the window.
Definition: window.cpp:917
unsigned gamemap_width
The size of the map area, if not available equal to the screen size.
Definition: settings.cpp:49
virtual const std::string & get_control_type() const override
See tcontrol::get_control_type.
Definition: window.cpp:1253
std::map< std::string, tlinked_size > linked_size_
List of the widgets, whose size are linked together.
Definition: window.hpp:621
surface get_surface_portion(const surface &src, SDL_Rect &area)
Get a portion of the screen.
Definition: utils.cpp:2335
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
static twindow * window_instance(const unsigned handle)
Returns the instance of a window.
Definition: window.cpp:454
-file util.hpp
Definitions for the interface to Wesnoth Markup Language (WML).
tpoint get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:188
#define SDLKey
Definition: compat.hpp:29
The window is new and not yet shown.
Definition: window.hpp:220
surface & getSurface()
Definition: dummy_video.cpp:22
The window is being shown.
Definition: window.hpp:221
tcontainer_(const unsigned canvas_count)
Definition: container.hpp:37
Start special campaign 'tutorial'.
bool suspend_drawing_
Avoid drawing the window.
Definition: window.hpp:505
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
void init_mouse_location()
Initializes the location of the mouse.
Definition: handler.cpp:788
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
SDL_Rect screen_area()
Definition: video.cpp:135
Simple push button.
Definition: button.hpp:32
#define CLOSE_WINDOW_EVENT
Definition: events.hpp:27
static const unsigned HORIZONTAL_ALIGN_LEFT
Definition: grid.hpp:50
twidget * parent()
Definition: widget.cpp:155
The user set the widget invisible, that means:
Definition: widget.hpp:103
void initialize_state()
Initializes the state of the keyboard and mouse.
void signal_handler_sdl_video_resize(const event::tevent event, bool &handled, const tpoint &new_size)
Definition: window.cpp:1412
int h_
Definition: font.cpp:607
void finalize(const boost::intrusive_ptr< tbuilder_grid > &content_grid)
Finishes the initialization of the grid.
Definition: window.cpp:1301
virtual twidget * build() const =0
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
#define LOG_HEADER
Definition: window.cpp:73
bool disable_click_dismiss() const override
See twidget::disable_click_dismiss.
Definition: container.cpp:203
Dialog is closed with ok button.
Definition: window.hpp:125
GLuint id
Definition: glew.h:1647
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
void signal_handler_click_dismiss(const event::tevent event, bool &handled, bool &halt, const Uint8 mouse_button_mask)
The handler for the click dismiss mouse 'event'.
Definition: window.cpp:1427
This file contains the settings handling of the widget library.
Dialog is closed with no return value, should be rare but eg a message popup can do it...
Definition: window.hpp:120
void draw()
Draws the window.
Definition: window.cpp:738
void remove()
Removes a tip.
Definition: tip.cpp:167
void show_tooltip()
Shows the window as a tooltip.
Definition: window.cpp:547
int y
y coordinate.
Definition: point.hpp:34
int show(const bool restore=true, const unsigned auto_close_timeout=0)
Shows the window.
Definition: window.cpp:592
static void layout(twindow &window, const unsigned maximum_width, const unsigned maximum_height)
Layouts the window.
Definition: window.cpp:1315
Dialog is closed with the cancel button.
Definition: window.hpp:126
void populate_dirty_list(twindow &caller, std::vector< twidget * > &call_stack)
Adds a widget to the dirty list if it is dirty.
Definition: widget.cpp:386
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
Uint8 mouse_button_state_
The state of the mouse button.
Definition: window.hpp:668
unsigned gamemap_height
Definition: settings.cpp:50
const t_string & tooltip() const
Definition: control.hpp:260
void connect()
Connects the dispatcher to the event handler.
Definition: dispatcher.cpp:49
twindow_builder::tresolution::ttip helptip_
The settings for the helptip.
Definition: window.hpp:560
This file contains the defintions for the gui2::event::tmessage class.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:422
tpoint get_mouse_position()
Returns the current mouse position.
Definition: helper.cpp:149
void generate_dot_file(const std::string &, const unsigned)
Definition: window.hpp:697
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: container.cpp:76
#define log_scope2(domain, description)
Definition: log.hpp:186
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:969
void set_id(const std::string &id)
Definition: widget.cpp:97
map_display and display: classes which take care of displaying the map and game-data on the screen...
tformula< unsigned > h_
The formula to calulate the height of the dialog.
Definition: window.hpp:548
Exception thrown when the width has been modified during resizing.
void load_config()
Loads the configuration of the widget.
Definition: control.cpp:274
static const unsigned VERTICAL_ALIGN_TOP
Definition: grid.hpp:42
tevent
The event send to the dispatcher.
Definition: handler.hpp:54
void signal_handler_request_placement(const event::tevent event, bool &handled)
Definition: window.cpp:1489
Exception thrown when the width resizing has failed.
Visible container to hold multiple widgets.
Definition: panel.hpp:34
tformula< bool > reevaluate_best_size_
The formula to determine whether the size is good.
Definition: window.hpp:551
void layout()
Layouts the window.
Definition: window.cpp:1013
GLuint color
Definition: glew.h:5801
void pump()
Definition: events.cpp:336
void set_mouse_behavior(const tmouse_behavior mouse_behavior)
Definition: dispatcher.hpp:496
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
void show(CVideo &video, const std::string &window_id, const t_string &message, const tpoint &mouse)
Shows a tip.
Definition: tip.cpp:133
void reduce_width(const unsigned maximum_width)
Tries to reduce the width of a container.
Definition: container.cpp:41
int x
x coordinate.
Definition: point.hpp:31
bool invalidate_layout_blocked_
Is invalidate layout blocked see tinvalidate_layout_blocker.
Definition: window.hpp:502
virtual void layout_children() override
See twidget::layout_children.
Definition: container.cpp:150
The user sets the widget visible, that means:
Definition: widget.hpp:79
bool restore_
Whether the window should undraw the window using restorer_.
Definition: window.hpp:508
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
virtual twidget * find_at(const tpoint &coordinate, const bool must_be_active) override
See twidget::find_at.
Definition: window.cpp:947
#define DBG_GUI_E
Definition: log.hpp:35
#define SHOW_HELPTIP_EVENT
Definition: events.hpp:28
unsigned gamemap_x_offset
The offset between the left edge of the screen and the gamemap.
Definition: settings.cpp:47
bool use_color_cursors()
const std::string & id() const
Definition: widget.cpp:109
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
void undraw_floating_labels(surface screen)
size_t i
Definition: function.cpp:1057
Holds a 2D point.
Definition: point.hpp:24
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
std::map< unsigned, twindow * > windows_
Definition: window.cpp:230
unsigned screen_width
The screen resolution should be available for all widgets since their drawing method will depend on i...
Definition: settings.cpp:44
Helper struct to store information about the tips.
virtual twidget * find_at(const tpoint &coordinate, const bool must_be_active) override
See twidget::find_at.
Definition: container.cpp:163
tformula< unsigned > y_
The y coordinate of the rectangle.
Definition: canvas.cpp:682
map_formula_callable & add(const std::string &key, const variant &value)
Definition: formula.cpp:41
tshow_mode show_mode_
The mode in which the window is shown.
Definition: window.hpp:482
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
lg::log_domain log_gui_draw("gui/draw")
Definition: log.hpp:28
static const unsigned VERTICAL_ALIGN_CENTER
Definition: grid.hpp:43
void show_non_modal()
Shows the window non modal.
Definition: window.cpp:569
surface & get_video_surface()
Definition: dummy_video.cpp:36
tformula< unsigned > y_
The formula to calulate the y value of the dialog.
Definition: window.hpp:542
SDL_Rect create_rect(const int x, const int y, const int w, const int h)
Creates an empty SDL_Rect.
Definition: rect.cpp:28
tformula< int > maximum_width_
The maximum width for the text.
Definition: canvas.cpp:1280
Let user select a campaign to play.
GLsizeiptr size
Definition: glew.h:1649
Basic exception when the layout doesn't fit.
bool has_linked_size_group(const std::string &id)
Is the linked size group defined for this window?
Definition: window.cpp:979
GLenum GLenum GLvoid * row
Definition: glew.h:3805
Contains the SDL_Rect helper code.
void add_linked_widget(const std::string &id, twidget *widget)
Adds a widget to a linked size group.
Definition: window.cpp:984
twindow_definition(const config &cfg)
Definition: window.cpp:1520
tbuilder_window(const config &cfg)
Definition: window.cpp:91
unsigned screen_height
Definition: settings.cpp:45
void fill_rect_alpha(SDL_Rect &rect, Uint32 color, Uint8 alpha, surface target)
Fills a specified area of a surface with a given color and opacity.
Definition: rect.cpp:83
tstatus status_
The status of the window.
Definition: window.hpp:469
void reduce_height(const unsigned maximum_height)
Tries to reduce the height of a container.
Definition: container.cpp:56
bool find(E event, F functor)
Tests whether an event handler is available.
cl_event event
Definition: glew.h:3070
const unsigned vertical_placement_
Sets the vertical placement.
Definition: window.hpp:530
Helper struct to force widgets the have the same size.
Definition: window.hpp:603
config & child(const std::string &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:658
Base class for all widgets.
Definition: widget.hpp:49
static const unsigned HORIZONTAL_ALIGN_CENTER
Definition: grid.hpp:51
void mouse_capture(const bool capture=true)
Definition: window.cpp:1388
const SDL_Rect & map_outside_area() const
Returns the available area for a map, this may differ from the above.
Definition: display.hpp:248
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
const std::string remove
remove directive
unsigned maximum_width_
The maximum width if automatic_placement_ is true.
Definition: window.hpp:533
void layout_linked_widgets()
Layouts the linked widgets.
Definition: window.cpp:1194
void get_screen_size_variables(game_logic::map_formula_callable &variable)
Gets a formula object with the screen size.
Definition: helper.cpp:132
void set_definition(const std::string &definition)
Sets the definition.
Definition: control.cpp:318
const bool automatic_placement_
Do we wish to place the widget automatically?
Definition: window.hpp:514
void sdl_blit(const surface &src, SDL_Rect *src_rect, surface &dst, SDL_Rect *dst_rect)
Definition: utils.hpp:112
void keyboard_capture(twidget *widget)
Definition: window.cpp:1394
tformula< unsigned > w_
The formula to calulate the width of the dialog.
Definition: window.hpp:545
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
Helper class, don't construct this directly.
SDL_Rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:279
void remove_linked_widget(const std::string &id, const twidget *widget)
Removes a widget from a linked size group.
Definition: window.cpp:995
Exception thrown when the height resizing has failed.
static const unsigned VERTICAL_ALIGN_BOTTOM
Definition: grid.hpp:44
CVideo & video_
Needed so we can change what's drawn on the screen.
Definition: window.hpp:466
void update_rect(const SDL_Rect &)
Definition: dummy_video.cpp:27
game_logic::map_formula_callable variables_
The variables of the canvas.
Definition: window.hpp:499
unsigned int get_cols() const
Definition: grid.hpp:294
void set_visible(const tvisible::scoped_enum visible)
Definition: widget.cpp:445
bool click_dismiss_
Do we want to have easy close behavior?
Definition: window.hpp:579
#define LOG_IMPL_SCOPE_HEADER
Definition: window.cpp:75
GLsizei const GLcharARB ** string
Definition: glew.h:4503
bool click_dismiss(const Uint8 mouse_button_mask)
Handles a mouse click event for dismissing the dialogue.
Definition: window.cpp:1240
void signal_handler_message_show_tooltip(const event::tevent event, bool &handled, event::tmessage &message)
Definition: window.cpp:1461
HOTKEY_COMMAND get_id(const std::string &command)
returns get_hotkey_command(command).id
event::tdistributor * event_distributor_
Definition: window.hpp:702
Contains the implementation details for lexical_cast and shouldn't be used directly.
void draw_floating_labels(surface screen)
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:941
static unsigned sunset_
Controls the sunset feature.
Definition: window.hpp:594
boost::shared_ptr< halo_record > handle
Definition: halo.hpp:34
bool need_layout_
When set the form needs a full layout redraw cycle.
Definition: window.hpp:496