The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
listbox.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 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 #ifndef GUI2_EXPERIMENTAL_LISTBOX
16 
17 #define GETTEXT_DOMAIN "wesnoth-lib"
18 
19 #include "gui/widgets/listbox.hpp"
20 
23 #include "gui/core/log.hpp"
28 #include "gui/widgets/pane.hpp"
29 #include "gui/widgets/settings.hpp"
31 #include "gui/widgets/viewport.hpp"
32 #include "gui/widgets/window.hpp"
33 
34 #include "gettext.hpp"
35 
36 #include "utils/functional.hpp"
37 
38 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
39 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
40 
41 namespace gui2
42 {
43 
44 // ------------ WIDGET -----------{
45 
46 REGISTER_WIDGET(listbox)
47 
48 namespace
49 {
50 // in separate namespace to avoid name classes
51 REGISTER_WIDGET3(tlistbox_definition, horizontal_listbox, _4)
52 
53 void callback_list_item_clicked(twidget& caller)
54 {
55  get_parent<tlistbox>(caller).list_item_clicked(caller);
56 }
57 
58 } // namespace
59 
60 tlistbox::tlistbox(const bool has_minimum,
61  const bool has_maximum,
62  const tgenerator_::tplacement placement,
63  const bool select)
64  : tscrollbar_container(2) // FIXME magic number
65  , generator_(tgenerator_::build(has_minimum, has_maximum, placement, select))
66  , is_horizonal_(placement == tgenerator_::horizontal_list)
67  , list_builder_(nullptr)
68  , callback_value_changed_()
69  , need_layout_(false)
70  , orders_()
71 {
72 }
73 
74 void tlistbox::add_row(const string_map& item, const int index)
75 {
76  assert(generator_);
77  const tgrid& row = generator_->create_item(
78  index, list_builder_, item, callback_list_item_clicked);
79 
80  resize_content(row);
81 }
82 
83 void
84 tlistbox::add_row(const std::map<std::string /* widget id */, string_map>& data,
85  const int index)
86 {
87  assert(generator_);
88  const tgrid& row = generator_->create_item(
89  index, list_builder_, data, callback_list_item_clicked);
90 
91  resize_content(row);
92 }
93 
94 void tlistbox::remove_row(const unsigned row, unsigned count)
95 {
96  assert(generator_);
97 
98  if(row >= get_item_count()) {
99  return;
100  }
101 
102  if(!count || count + row > get_item_count()) {
103  count = get_item_count() - row;
104  }
105 
106  int height_reduced = 0;
107  int width_reduced = 0;
108  //TODO: Fix this for horizinal listboxes
109  //Note the we have to use content_grid_ and cannot use "_list_grid" which is what generator_ uses.
110  int row_pos_y = is_horizonal_ ? -1 : generator_->item(row).get_y() - content_grid_->get_y();
111  int row_pos_x = is_horizonal_ ? -1 : 0;
112  for(; count; --count) {
114  if(is_horizonal_) {
115  width_reduced += generator_->item(row).get_width();
116  }
117  else {
118  height_reduced += generator_->item(row).get_height();
119  }
120  }
121  generator_->delete_item(row);
122  }
123 
124  if((height_reduced != 0 || width_reduced != 0) && get_item_count() != 0) {
125  resize_content(-width_reduced, -height_reduced, row_pos_x, row_pos_y);
126  } else {
128  }
129 }
130 
132 {
133  // Due to the removing from the linked group, don't use
134  // generator_->clear() directly.
135  remove_row(0, 0);
136 }
137 
138 unsigned tlistbox::get_item_count() const
139 {
140  assert(generator_);
141  return generator_->get_item_count();
142 }
143 
144 void tlistbox::set_row_active(const unsigned row, const bool active)
145 {
146  assert(generator_);
147  generator_->item(row).set_active(active);
148 }
149 
150 void tlistbox::set_row_shown(const unsigned row, const bool shown)
151 {
152  assert(generator_);
153 
154  twindow* window = get_window();
155  assert(window);
156 
157  const int selected_row = get_selected_row();
158 
159  bool resize_needed;
160  {
161  twindow::tinvalidate_layout_blocker invalidate_layout_blocker(*window);
162 
163  generator_->set_item_shown(row, shown);
166  resize_needed = !content_resize_request();
167  }
168 
169  if(resize_needed) {
170  window->invalidate_layout();
171  } else {
173  set_is_dirty(true);
174  }
175 
176  if(selected_row != get_selected_row() && callback_value_changed_) {
178  }
179 }
180 
181 void tlistbox::set_row_shown(const std::vector<bool>& shown)
182 {
183  assert(generator_);
184  assert(shown.size() == get_item_count());
185 
186  twindow* window = get_window();
187  assert(window);
188 
189  const int selected_row = get_selected_row();
190 
191  bool resize_needed;
192  {
193  twindow::tinvalidate_layout_blocker invalidate_layout_blocker(*window);
194 
195  for(size_t i = 0; i < shown.size(); ++i) {
196  generator_->set_item_shown(i, shown[i]);
197  }
200  resize_needed = !content_resize_request();
201  }
202 
203  if(resize_needed) {
204  window->invalidate_layout();
205  } else {
207  set_is_dirty(true);
208  }
209 
210  if(selected_row != get_selected_row() && callback_value_changed_) {
212  }
213 }
214 
215 const tgrid* tlistbox::get_row_grid(const unsigned row) const
216 {
217  assert(generator_);
218  // rename this function and can we return a reference??
219  return &generator_->item(row);
220 }
221 
223 {
224  assert(generator_);
225  return &generator_->item(row);
226 }
227 
228 bool tlistbox::select_row(const unsigned row, const bool select)
229 {
230  assert(generator_);
231 
232  generator_->select_item(row, select);
233 
234  return true; // FIXME test what result should have been!!!
235 }
236 
238 {
239  assert(generator_);
240 
241  return generator_->get_selected_item();
242 }
243 
245 {
246  assert(generator_);
247 
248  /** @todo Hack to capture the keyboard focus. */
249  get_window()->keyboard_capture(this);
250 
251  for(size_t i = 0; i < generator_->get_item_count(); ++i) {
252 
253  if(generator_->item(i).has_widget(caller)) {
257  }
260  }
261  return;
262  }
263  }
264  assert(false);
265 }
266 
267 void tlistbox::set_self_active(const bool /*active*/)
268 {
269  /* DO NOTHING */
270 }
271 
273 {
275  return true;
276  }
277 
278  if(get_size() == tpoint(0, 0)) {
279  return false;
280  }
281 
282  if(content_resize_request(true)) {
284  set_is_dirty(true);
285  return true;
286  }
287 
288  return false;
289 }
290 
291 void tlistbox::place(const tpoint& origin, const tpoint& size)
292 {
293  // Inherited.
294  tscrollbar_container::place(origin, size);
295 
296  /**
297  * @todo Work-around to set the selected item visible again.
298  *
299  * At the moment the listboxes and dialogs in general are resized a lot as
300  * work-around for sizing. So this function makes the selected item in view
301  * again. It doesn't work great in all cases but the proper fix is to avoid
302  * resizing dialogs a lot. Need more work later on.
303  */
304  const int selected_item = generator_->get_selected_item();
305  if(selected_item != -1) {
306  const SDL_Rect& visible = content_visible_area();
307  SDL_Rect rect = generator_->item(selected_item).get_rectangle();
308 
309  rect.x = visible.x;
310  rect.w = visible.w;
311 
312  show_content_rect(rect);
313  }
314 }
315 
316 void tlistbox::resize_content(const int width_modification,
317  const int height_modification,
318  const int width_modification_pos,
319  const int height_modification_pos)
320 {
321  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size()
322  << " width_modification " << width_modification
323  << " height_modification " << height_modification << ".\n";
324 
325  if(content_resize_request(width_modification, height_modification, width_modification_pos, height_modification_pos)) {
326 
327  // Calculate new size.
329  size.x += width_modification;
330  size.y += height_modification;
331 
332  // Set new size.
333  content_grid()->set_size(size);
334 
335  // Set status.
336  need_layout_ = true;
337  // If the content grows assume it "overwrites" the old content.
338  if(width_modification < 0 || height_modification < 0) {
339  set_is_dirty(true);
340  }
341  DBG_GUI_L << LOG_HEADER << " succeeded.\n";
342  } else {
343  DBG_GUI_L << LOG_HEADER << " failed.\n";
344  }
345 }
346 
348 {
349  if(row.get_visible() == tvisible::invisible) {
350  return;
351  }
352 
353  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size()
354  << " row size " << row.get_best_size() << ".\n";
355 
356  const tpoint content = content_grid()->get_size();
357  tpoint size = row.get_best_size();
358  if(size.x < content.x) {
359  size.x = 0;
360  } else {
361  size.x -= content.x;
362  }
363 
364  resize_content(size.x, size.y);
365 }
366 
368 {
369  layout_children(false);
370 }
371 
372 void
374  const std::vector<twidget*>& call_stack)
375 {
376  // Inherited.
378 
379  assert(generator_);
380  std::vector<twidget*> child_call_stack = call_stack;
381  generator_->populate_dirty_list(caller, child_call_stack);
382 }
383 
384 void tlistbox::handle_key_up_arrow(SDLMod modifier, bool& handled)
385 {
386  assert(generator_);
387 
388  generator_->handle_key_up_arrow(modifier, handled);
389 
390  if(handled) {
391  // When scrolling make sure the new items is visible but leave the
392  // horizontal scrollbar position.
393  const SDL_Rect& visible = content_visible_area();
394  SDL_Rect rect = generator_->item(generator_->get_selected_item())
395  .get_rectangle();
396 
397  rect.x = visible.x;
398  rect.w = visible.w;
399 
400  show_content_rect(rect);
401 
404  }
405  } else {
406  // Inherited.
408  }
409 }
410 
411 void tlistbox::handle_key_down_arrow(SDLMod modifier, bool& handled)
412 {
413  assert(generator_);
414 
415  generator_->handle_key_down_arrow(modifier, handled);
416 
417  if(handled) {
418  // When scrolling make sure the new items is visible but leave the
419  // horizontal scrollbar position.
420  const SDL_Rect& visible = content_visible_area();
421  SDL_Rect rect = generator_->item(generator_->get_selected_item())
422  .get_rectangle();
423 
424  rect.x = visible.x;
425  rect.w = visible.w;
426 
427  show_content_rect(rect);
428 
431  }
432  } else {
433  // Inherited.
435  }
436 }
437 
438 void tlistbox::handle_key_left_arrow(SDLMod modifier, bool& handled)
439 {
440  assert(generator_);
441 
442  generator_->handle_key_left_arrow(modifier, handled);
443 
444  // Inherited.
445  if(handled) {
446  // When scrolling make sure the new items is visible but leave the
447  // vertical scrollbar position.
448  const SDL_Rect& visible = content_visible_area();
449  SDL_Rect rect = generator_->item(generator_->get_selected_item())
450  .get_rectangle();
451 
452  rect.y = visible.y;
453  rect.h = visible.h;
454 
455  show_content_rect(rect);
456 
459  }
460  } else {
462  }
463 }
464 
465 void tlistbox::handle_key_right_arrow(SDLMod modifier, bool& handled)
466 {
467  assert(generator_);
468 
469  generator_->handle_key_right_arrow(modifier, handled);
470 
471  // Inherited.
472  if(handled) {
473  // When scrolling make sure the new items is visible but leave the
474  // vertical scrollbar position.
475  const SDL_Rect& visible = content_visible_area();
476  SDL_Rect rect = generator_->item(generator_->get_selected_item())
477  .get_rectangle();
478 
479  rect.y = visible.y;
480  rect.h = visible.h;
481 
482  show_content_rect(rect);
483 
486  }
487  } else {
489  }
490 }
491 
492 namespace
493 {
494 
495 /**
496  * Swaps an item in a grid for another one.*/
497 void swap_grid(tgrid* grid,
498  tgrid* content_grid,
499  twidget* widget,
500  const std::string& id)
501 {
502  assert(content_grid);
503  assert(widget);
504 
505  // Make sure the new child has same id.
506  widget->set_id(id);
507 
508  // Get the container containing the wanted widget.
509  tgrid* parent_grid = nullptr;
510  if(grid) {
511  parent_grid = find_widget<tgrid>(grid, id, false, false);
512  }
513  if(!parent_grid) {
514  parent_grid = find_widget<tgrid>(content_grid, id, true, false);
515  }
516  parent_grid = dynamic_cast<tgrid*>(parent_grid->parent());
517  assert(parent_grid);
518 
519  // Replace the child.
520  widget = parent_grid->swap_child(id, widget, false);
521  assert(widget);
522 
523  delete widget;
524 }
525 
526 } // namespace
527 
530  const std::vector<string_map>& list_data)
531 {
532  // "Inherited."
534 
535  assert(generator_);
536 
537  if(header) {
538  swap_grid(&grid(), content_grid(), header->build(), "_header_grid");
539  }
540  tgrid& p = find_widget<tgrid>(this, "_header_grid", false);
541  for(unsigned i = 0, max = std::max(p.get_cols(), p.get_rows()); i < max; ++i) {
542  if(tselectable_* selectable = find_widget<tselectable_>(&p, "sort_" + std::to_string(i), false, false)) {
543  selectable->set_callback_state_change(std::bind(&tlistbox::order_by_column, this, i, _1));
544  if(orders_.size() < max ) {
545  orders_.resize(max);
546  }
547  orders_[i].first = selectable;
548  }
549  }
550  if(footer) {
551  swap_grid(&grid(), content_grid(), footer->build(), "_footer_grid");
552  }
553 
555  -1, list_builder_, list_data, callback_list_item_clicked);
556  swap_grid(nullptr, content_grid(), generator_, "_list_grid");
557 }
558 namespace {
559  bool default_sort(unsigned i1, unsigned i2)
560  {
561  return i1 < i2;
562  }
563 }
564 
565 void tlistbox::order_by_column(unsigned column, twidget& widget)
566 {
567  tselectable_& selectable = dynamic_cast<tselectable_&>(widget);
568  if(column >= orders_.size()) {
569  return;
570  }
571  for(auto& pair : orders_)
572  {
573  if(pair.first != nullptr && pair.first != &selectable) {
574  pair.first->set_value(0);
575  }
576  }
577  if(selectable.get_value() > orders_[column].second.size()) {
578  return;
579  }
580  if(selectable.get_value() == 0) {
581  order_by(tgenerator_::torder_func(&default_sort));
582  }
583  else {
584  order_by(orders_[column].second[selectable.get_value() - 1]);
585  }
586 }
587 
589 {
590  generator_->set_order(func);
591 
592  set_is_dirty(true);
593  need_layout_ = true;
594 }
595 
596 void tlistbox::set_column_order(unsigned col, const std::vector<tgenerator_::torder_func>& func)
597 {
598  if(col >= orders_.size()) {
599  orders_.resize(col + 1);
600  }
601  orders_[col].second = func;
602 }
603 
604 void tlistbox::set_content_size(const tpoint& origin, const tpoint& size)
605 {
606  /** @todo This function needs more testing. */
607  assert(content_grid());
608 
609  const int best_height = content_grid()->get_best_size().y;
610  const tpoint s(size.x, size.y < best_height ? size.y : best_height);
611 
612  content_grid()->place(origin, s);
613 }
614 
615 void tlistbox::layout_children(const bool force)
616 {
617  assert(content_grid());
618 
619  if(need_layout_ || force) {
621  content_grid()->get_size());
622 
624 
625  need_layout_ = false;
626  set_is_dirty(true);
627  }
628 }
629 
631 {
632  static const std::string type = "listbox";
633  return type;
634 }
635 
636 // }---------- DEFINITION ---------{
637 
639  : tcontrol_definition(cfg)
640 {
641  DBG_GUI_P << "Parsing listbox " << id << '\n';
642 
643  load_resolutions<tresolution>(cfg);
644 }
645 
646 /*WIKI
647  * @page = GUIWidgetDefinitionWML
648  * @order = 1_listbox
649  * @begin{parent}{name="gui/"}
650  * @begin{tag}{name="listbox_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
651  * == Listbox ==
652  *
653  * @macro = listbox_description
654  *
655  * The definition of a listbox contains the definition of its scrollbar.
656  *
657  * The resolution for a listbox also contains the following keys:
658  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super=generic/widget_definition/resolution}
659  * @begin{table}{config}
660  * scrollbar & section & & A grid containing the widgets for the
661  * scrollbar. The scrollbar has some special
662  * widgets so it can make default behavior
663  * for certain widgets. $
664  * @end{table}
665  * @begin{table}{dialog_widgets}
666  * _begin & & clickable & o & Moves the position to the beginning
667  * of the list. $
668  * _line_up & & clickable & o & Move the position one item up. (NOTE
669  * if too many items to move per item it
670  * might be more items.) $
671  * _half_page_up & & clickable & o &
672  * Move the position half the number of the
673  * visible items up. (See note at
674  * _line_up.) $
675  * _page_up & & clickable & o & Move the position the number of
676  * visible items up. (See note at
677  * _line_up.) $
678  *
679  * _end & & clickable & o & Moves the position to the end of the
680  * list. $
681  * _line_down & & clickable & o & Move the position one item down.(See
682  * note at _line_up.) $
683  * _half_page_down & & clickable & o &
684  * Move the position half the number of the
685  * visible items down. (See note at
686  * _line_up.) $
687  * _page_down & & clickable & o & Move the position the number of
688  * visible items down. (See note at
689  * _line_up.) $
690  *
691  * _scrollbar & & vertical_scrollbar & m &
692  * This is the scrollbar so the user can
693  * scroll through the list. $
694  * @end{table}
695  * A clickable is one of:
696  * * button
697  * * repeating_button
698  * @{allow}{link}{name="gui/window/resolution/grid/row/column/button"}
699  * @{allow}{link}{name="gui/window/resolution/grid/row/column/repeating_button"}
700  * The following states exist:
701  * * state_enabled, the listbox is enabled.
702  * * state_disabled, the listbox is disabled.
703  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
704  * @end{tag}{name="state_enabled"}
705  * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
706  * @end{tag}{name="state_disabled"}
707  * @allow{link}{name="gui/window/resolution/grid"}
708  * @end{tag}{name="resolution"}
709  * @end{tag}{name="listbox_definition"}
710  * @end{parent}{name="gui/"}
711  */
712 
713 /*WIKI
714  * @page = GUIWidgetDefinitionWML
715  * @order = 1_horizonal_listbox
716  *
717  * == Horizontal listbox ==
718  * @begin{parent}{name="gui/"}
719  * @begin{tag}{name="horizontal_listbox_definition"}{min=0}{max=-1}{super="gui/listbox_definition"}
720  * @end{tag}{name="horizontal_listbox_definition"}
721  * @end{parent}{name="gui/"}
722  * @macro = horizontal_listbox_description
723  * The definition of a horizontal listbox is the same as for a normal listbox.
724  */
726  : tresolution_definition_(cfg), grid(nullptr)
727 {
728  // Note the order should be the same as the enum tstate in listbox.hpp.
729  state.push_back(tstate_definition(cfg.child("state_enabled")));
730  state.push_back(tstate_definition(cfg.child("state_disabled")));
731 
732  const config& child = cfg.child("grid");
733  VALIDATE(child, _("No grid defined."));
734 
735  grid = new tbuilder_grid(child);
736 }
737 
738 // }---------- BUILDER -----------{
739 
740 /*WIKI_MACRO
741  * @begin{macro}{listbox_description}
742  *
743  * A listbox is a control that holds several items of the same type.
744  * Normally the items in a listbox are ordered in rows, this version
745  * might allow more options for ordering the items in the future.
746  * @end{macro}
747  */
748 
749 /*WIKI
750  * @page = GUIWidgetInstanceWML
751  * @order = 2_listbox
752  *
753  * == Listbox ==
754  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
755  * @begin{tag}{name="listbox"}{min=0}{max=-1}{super="generic/widget_instance"}
756  * @macro = listbox_description
757  *
758  * List with the listbox specific variables:
759  * @begin{table}{config}
760  * vertical_scrollbar_mode & scrollbar_mode & initial_auto &
761  * Determines whether or not to show the
762  * scrollbar. $
763  * horizontal_scrollbar_mode & scrollbar_mode & initial_auto &
764  * Determines whether or not to show the
765  * scrollbar. $
766  *
767  * header & grid & [] & Defines the grid for the optional
768  * header. (This grid will automatically
769  * get the id _header_grid.) $
770  * footer & grid & [] & Defines the grid for the optional
771  * footer. (This grid will automatically
772  * get the id _footer_grid.) $
773  *
774  * list_definition & section & & This defines how a listbox item
775  * looks. It must contain the grid
776  * definition for 1 row of the list. $
777  *
778  * list_data & section & [] & A grid alike section which stores the
779  * initial data for the listbox. Every row
780  * must have the same number of columns as
781  * the 'list_definition'. $
782  *
783  * has_minimum & bool & true & If false, less than one row can be selected. $
784  *
785  * has_maximum & bool & true & If false, more than one row can be selected. $
786  *
787  * @end{table}
788  * @begin{tag}{name="header"}{min=0}{max=1}{super="gui/window/resolution/grid"}
789  * @end{tag}{name="header"}
790  * @begin{tag}{name="footer"}{min=0}{max=1}{super="gui/window/resolution/grid"}
791  * @end{tag}{name="footer"}
792  * @begin{tag}{name="list_definition"}{min=0}{max=1}
793  * @begin{tag}{name="row"}{min=1}{max=1}{super="generic/listbox_grid/row"}
794  * @end{tag}{name="row"}
795  * @end{tag}{name="list_definition"}x
796  * @begin{tag}{name="list_data"}{min=0}{max=1}{super="generic/listbox_grid"}
797  * @end{tag}{name="list_data"}
798  *
799  * In order to force widgets to be the same size inside a listbox, the widgets
800  * need to be inside a linked_group.
801  *
802  * Inside the list section there are only the following widgets allowed
803  * * grid (to nest)
804  * * selectable widgets which are
805  * ** toggle_button
806  * ** toggle_panel
807  * @end{tag}{name="listbox"}
808  *
809  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
810  */
811 
812 /*WIKI
813  * @begin{parent}{name="generic/"}
814  * @begin{tag}{name="listbox_grid"}{min="0"}{max="-1"}
815  * @begin{tag}{name="row"}{min="0"}{max="-1"}
816  * @begin{table}{config}
817  * grow_factor & unsigned & 0 & The grow factor for a row. $
818  * @end{table}
819  * @begin{tag}{name="column"}{min="0"}{max="-1"}{super="gui/window/resolution/grid/row/column"}
820  * @begin{table}{config}
821  * label & t_string & "" & $
822  * tooltip & t_string & "" & $
823  * icon & t_string & "" & $
824  * @end{table}
825  * @allow{link}{name="gui/window/resolution/grid/row/column/toggle_button"}
826  * @allow{link}{name="gui/window/resolution/grid/row/column/toggle_panel"}
827  * @end{tag}{name="column"}
828  * @end{tag}{name="row"}
829  * @end{tag}{name="listbox_grid"}
830  * @end{parent}{name="generic/"}
831  */
832 
833 namespace implementation
834 {
835 
836 tbuilder_listbox::tbuilder_listbox(const config& cfg)
837  : tbuilder_control(cfg)
838  , vertical_scrollbar_mode(
839  get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
840  , horizontal_scrollbar_mode(
841  get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
842  , header(nullptr)
843  , footer(nullptr)
844  , list_builder(nullptr)
845  , list_data()
846  , has_minimum_(cfg["has_minimum"].to_bool(true))
847  , has_maximum_(cfg["has_maximum"].to_bool(true))
848 {
849  if(const config& h = cfg.child("header")) {
850  header = new tbuilder_grid(h);
851  }
852 
853  if(const config& f = cfg.child("footer")) {
854  footer = new tbuilder_grid(f);
855  }
856 
857  const config& l = cfg.child("list_definition");
858 
859  VALIDATE(l, _("No list defined."));
860  list_builder = new tbuilder_grid(l);
861  assert(list_builder);
862  VALIDATE(list_builder->rows == 1,
863  _("A 'list_definition' should contain one row."));
864 
865  const config& data = cfg.child("list_data");
866  if(!data) {
867  return;
868  }
869 
870  for(const auto & row : data.child_range("row"))
871  {
872  unsigned col = 0;
873 
874  for(const auto & c : row.child_range("column"))
875  {
876  list_data.push_back(string_map());
877  for(const auto & i : c.attribute_range())
878  {
879  list_data.back()[i.first] = i.second;
880  }
881  ++col;
882  }
883 
884  VALIDATE(col == list_builder->cols,
885  _("'list_data' must have the same number of "
886  "columns as the 'list_definition'."));
887  }
888 }
889 
891 {
892 #ifdef GUI2_EXPERIMENTAL_LISTBOX
893  tlist* widget = new tlist(
894  true, true, tgenerator_::vertical_list, true, list_builder);
895 
896  init_control(widget);
897  if(!list_data.empty()) {
898  widget->append_rows(list_data);
899  }
900  return widget;
901 #else
902  if(new_widgets) {
903 
904  tpane* pane = new tpane(list_builder);
905  pane->set_id(id);
906 
907 
908  tgrid* grid = new tgrid();
909  grid->set_rows_cols(1, 1);
910 #if 0
911  grid->set_child(
912  pane
913  , 0
914  , 0
918 #else
919  tviewport* viewport = new tviewport(*pane);
920  grid->set_child(viewport,
921  0,
922  0,
926 #endif
927  return grid;
928  }
929 
930  tlistbox* widget
932 
933  init_control(widget);
934 
935  widget->set_list_builder(list_builder); // FIXME in finalize???
936 
939 
940  DBG_GUI_G << "Window builder: placed listbox '" << id
941  << "' with definition '" << definition << "'.\n";
942 
944  conf = boost::dynamic_pointer_cast<const tlistbox_definition::tresolution>(
945  widget->config());
946  assert(conf);
947 
948  widget->init_grid(conf->grid);
949 
950  widget->finalize(header, footer, list_data);
951 
952  return widget;
953 #endif
954 }
955 
956 /*WIKI_MACRO
957  * @begin{macro}{horizontal_listbox_description}
958  *
959  * A horizontal listbox is a control that holds several items of the
960  * same type. Normally the items in a listbox are ordered in rows,
961  * this version orders them in columns instead.
962  * @end{macro}
963  */
964 
965 /*WIKI
966  * @page = GUIWidgetInstanceWML
967  * @order = 2_horizontal_listbox
968  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
969  * @begin{tag}{name="horizontal_listbox"}{min="0"}{max="-1"}{super="generic/widget_instance"}
970  * == Horizontal listbox ==
971  *
972  * @macro = horizontal_listbox_description
973  *
974  * List with the horizontal listbox specific variables:
975  * @begin{table}{config}
976  * vertical_scrollbar_mode & scrollbar_mode & initial_auto &
977  * Determines whether or not to show the
978  * scrollbar. $
979  * horizontal_scrollbar_mode & scrollbar_mode & initial_auto &
980  * Determines whether or not to show the
981  * scrollbar. $
982  *
983  * list_definition & section & & This defines how a listbox item
984  * looks. It must contain the grid
985  * definition for 1 column of the list. $
986  *
987  * list_data & section & [] & A grid alike section which stores the
988  * initial data for the listbox. Every row
989  * must have the same number of columns as
990  * the 'list_definition'. $
991  *
992  * @end{table}
993  * @begin{tag}{name="header"}{min=0}{max=1}{super="gui/window/resolution/grid"}
994  * @end{tag}{name="header"}
995  * @begin{tag}{name="footer"}{min=0}{max=1}{super="gui/window/resolution/grid"}
996  * @end{tag}{name="footer"}
997  * @begin{tag}{name="list_definition"}{min=0}{max=1}
998  * @begin{tag}{name="row"}{min=1}{max=1}{super="generic/listbox_grid/row"}
999  * @end{tag}{name="row"}
1000  * @end{tag}{name="list_definition"}
1001  * @begin{tag}{name="list_data"}{min=0}{max=1}{super="generic/listbox_grid"}
1002  * @end{tag}{name="list_data"}
1003  * In order to force widgets to be the same size inside a horizontal listbox,
1004  * the widgets need to be inside a linked_group.
1005  *
1006  * Inside the list section there are only the following widgets allowed
1007  * * grid (to nest)
1008  * * selectable widgets which are
1009  * ** toggle_button
1010  * ** toggle_panel
1011  * @end{tag}{name="horizontal_listbox"}
1012  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
1013  */
1014 
1016  : tbuilder_control(cfg)
1017  , vertical_scrollbar_mode(
1018  get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
1019  , horizontal_scrollbar_mode(
1020  get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
1021  , list_builder(nullptr)
1022  , list_data()
1023 {
1024  const config& l = cfg.child("list_definition");
1025 
1026  VALIDATE(l, _("No list defined."));
1027  list_builder = new tbuilder_grid(l);
1028  assert(list_builder);
1029  VALIDATE(list_builder->rows == 1,
1030  _("A 'list_definition' should contain one row."));
1031 
1032  const config& data = cfg.child("list_data");
1033  if(!data)
1034  return;
1035 
1036  for(const auto & row : data.child_range("row"))
1037  {
1038  unsigned col = 0;
1039 
1040  for(const auto & c : row.child_range("column"))
1041  {
1042  list_data.push_back(string_map());
1043  for(const auto & i : c.attribute_range())
1044  {
1045  list_data.back()[i.first] = i.second;
1046  }
1047  ++col;
1048  }
1049 
1050  VALIDATE(col == list_builder->cols,
1051  _("'list_data' must have "
1052  "the same number of columns as the 'list_definition'."));
1053  }
1054 }
1055 
1057 {
1058 #ifdef GUI2_EXPERIMENTAL_LISTBOX
1059  tlist* widget = new tlist(
1060  true, true, tgenerator_::horizontal_list, true, list_builder);
1061 
1062  init_control(widget);
1063  if(!list_data.empty()) {
1064  widget->append_rows(list_data);
1065  }
1066  return widget;
1067 #else
1068  tlistbox* widget
1069  = new tlistbox(true, true, tgenerator_::horizontal_list, true);
1070 
1071  init_control(widget);
1072 
1073  widget->set_list_builder(list_builder); // FIXME in finalize???
1074 
1077 
1078  DBG_GUI_G << "Window builder: placed listbox '" << id
1079  << "' with definition '" << definition << "'.\n";
1080 
1082  conf = boost::dynamic_pointer_cast<const tlistbox_definition::tresolution>(
1083  widget->config());
1084  assert(conf);
1085 
1086  widget->init_grid(conf->grid);
1087 
1088  widget->finalize(nullptr, nullptr, list_data);
1089 
1090  return widget;
1091 #endif
1092 }
1093 
1094 } // namespace implementation
1095 
1096 // }------------ END --------------
1097 
1098 } // namespace gui2
1099 
1100 #endif
Define the common log macros for the gui toolkit.
unsigned get_width() const
Definition: widget.cpp:294
virtual void set_content_size(const tpoint &origin, const tpoint &size)
Inherited from tscrollbar_container.
Definition: listbox.cpp:604
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
child_itors child_range(const std::string &key)
Definition: config.cpp:613
Abstract base class for the generator.
Definition: generator.hpp:41
Defines the exception classes for the layout algorithm.
void set_list_builder(tbuilder_grid_ptr list_builder)
Definition: listbox.hpp:230
void finalize(tbuilder_grid_const_ptr header, tbuilder_grid_const_ptr footer, const std::vector< string_map > &list_data)
Finishes the building initialization of the widget.
Definition: listbox.cpp:528
virtual void select_item(const unsigned index, const bool select)=0
(De)selects an item.
static const unsigned BORDER_ALL
Definition: grid.hpp:59
#define SDLMod
Definition: compat.hpp:30
const tgrid & grid() const
Definition: container.hpp:229
#define DBG_GUI_L
Definition: log.hpp:58
virtual void place(const tpoint &origin, const tpoint &size) override=0
See twidget::place.
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:150
tbuilder_grid_const_ptr list_builder_
Contains the builder for the new items.
Definition: listbox.hpp:291
virtual unsigned get_value() const =0
Is the control selected?
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:94
twindow * build(CVideo &video, const twindow_builder::tresolution *definition)
Builds a window.
const GLfloat * c
Definition: glew.h:12741
bool update_content_size()
Request to update the size of the content after changing the content.
Definition: listbox.cpp:272
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
virtual void child_populate_dirty_list(twindow &caller, const std::vector< twidget * > &call_stack) override
See twidget::child_populate_dirty_list.
virtual const std::string & get_control_type() const override
See tcontrol::get_control_type.
Definition: listbox.cpp:630
#define LOG_HEADER
Definition: listbox.cpp:39
Base container class.
Definition: grid.hpp:29
tresolution_definition_ptr config()
Definition: control.hpp:299
virtual int get_selected_item() const =0
Returns the selected item.
This file contains the window object, this object is a top level container which has the event manage...
tvisible::scoped_enum get_visible() const
Definition: widget.cpp:471
bool select_row(const unsigned row, const bool select=true)
Selectes a row.
Definition: listbox.cpp:228
tpoint get_size() const
Returns the size of the widget.
Definition: widget.cpp:274
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:41
virtual void handle_key_right_arrow(SDLMod modifier, bool &handled)=0
Right arrow key pressed.
virtual tgrid & item(const unsigned index)=0
Gets the grid of an item.
unsigned int get_rows() const
Definition: grid.hpp:288
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: grid.cpp:435
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:48
tplacement
Determines how the items are placed.
Definition: generator.hpp:51
int get_y() const
Definition: widget.cpp:289
void set_row_active(const unsigned row, const bool active)
Makes a row active or inactive.
Definition: listbox.cpp:144
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
tscrollbar_container::tscrollbar_mode get_scrollbar_mode(const std::string &scrollbar_mode)
Returns the scrollbar mode flags.
Definition: helper.cpp:114
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:435
tpoint get_best_size() const
Gets the best size for the widget.
Definition: widget.cpp:188
std::function< bool(unsigned, unsigned)> torder_func
Definition: generator.hpp:239
Base class of a resolution, contains the common keys for a resolution.
virtual void layout_children() override
See twidget::layout_children.
Definition: listbox.cpp:367
virtual void set_value(const unsigned)=0
Select the control.
void order_by_column(unsigned column, twidget &widget)
Definition: listbox.cpp:565
GLdouble l
Definition: glew.h:6966
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:62
A class inherited from ttext_box that displays its input as stars.
Definition: field-fwd.hpp:23
void init_control(tcontrol *control) const
Definition: control.cpp:675
std::string definition
Parameters for the control.
Definition: control.hpp:530
twidget * parent()
Definition: widget.cpp:155
The user set the widget invisible, that means:
Definition: widget.hpp:103
bool content_resize_request(const bool force_sizing=false)
Notification if the content of a child needs a resize.
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
void set_vertical_scrollbar_mode(const tscrollbar_mode scrollbar_mode)
virtual void handle_key_down_arrow(SDLMod modifier, bool &handled)=0
Down arrow key pressed.
SDL_Rect content_visible_area_
Cache for the visible area for the content.
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
This file contains the settings handling of the widget library.
const SDL_Rect & content_visible_area() const
void order_by(const tgenerator_::torder_func &func)
Definition: listbox.cpp:588
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:138
void add_row(const string_map &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:74
int y
y coordinate.
Definition: point.hpp:34
virtual unsigned get_item_count() const =0
Returns the number of items.
virtual void set_self_active(const bool active) override
See tcontainer_::set_self_active.
Definition: listbox.cpp:267
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
bool need_layout_
Definition: listbox.hpp:309
virtual tgrid & create_item(const int index, tbuilder_grid_const_ptr list_builder, const string_map &item_data, const std::function< void(twidget &)> &callback)=0
Creates a new item.
void handle_key_up_arrow(SDLMod modifier, bool &handled)
Inherited from tscrollbar_container.
Definition: listbox.cpp:384
tscrollbar_container::tscrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:409
tgenerator_ * generator_
Contains a pointer to the generator.
Definition: listbox.hpp:286
virtual void handle_key_up_arrow(SDLMod modifier, bool &handled)=0
Up arrow key pressed.
void set_column_order(unsigned col, const std::vector< tgenerator_::torder_func > &func)
Definition: listbox.cpp:596
void show_content_rect(const SDL_Rect &rect)
Shows a certain part of the content.
void init_grid(const boost::intrusive_ptr< tbuilder_grid > &grid_builder)
Initializes and builds the grid.
Definition: container.cpp:209
tlistbox(const bool has_minimum, const bool has_maximum, const tgenerator_::tplacement placement, const bool select)
Constructor.
Definition: listbox.cpp:60
std::vector< string_map > list_data
Listbox data.
Definition: listbox.hpp:420
void set_active(const bool active)
Activates all children.
Definition: grid.cpp:168
void set_id(const std::string &id)
Definition: widget.cpp:97
void resize_content(const int width_modification, const int height_modification, const int width__modification_pos=-1, const int height_modification_pos=-1)
Resizes the content.
Definition: listbox.cpp:316
static size_t id
Ids for the timers.
Definition: timer.cpp:39
virtual void set_item_shown(const unsigned index, const bool show)=0
Shows or hides an item.
GLfloat GLfloat p
Definition: glew.h:12766
const bool is_horizonal_
Definition: listbox.hpp:288
GLuint GLuint GLsizei count
Definition: glew.h:1221
Contains the state info for a resolution.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
void finalize_setup()
The builder needs to call us so we do our setup.
std::map< std::string, t_string > string_map
Definition: generator.hpp:23
virtual bool has_widget(const twidget &widget) const override
See twidget::has_widget.
Definition: grid.cpp:623
Small abstract helper class.
Definition: selectable.hpp:32
int x
x coordinate.
Definition: point.hpp:31
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See twidget::set_visible_rectangle.
Definition: grid.cpp:560
virtual void set_order(const torder_func &order)=0
void handle_key_down_arrow(SDLMod modifier, bool &handled)
Inherited from tscrollbar_container.
Definition: listbox.cpp:411
GLenum GLenum GLvoid GLvoid * column
Definition: glew.h:3805
tscrollbar_container::tscrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:383
tlistbox_definition(const config &cfg)
Definition: listbox.cpp:638
tgrid * content_grid_
The grid that holds the content.
tpoint get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:269
GLuint index
Definition: glew.h:1782
torder_list orders_
Definition: listbox.hpp:312
void handle_key_right_arrow(SDLMod modifier, bool &handled)
Inherited from tscrollbar_container.
Definition: listbox.cpp:465
The listbox class.
Definition: listbox.hpp:39
unsigned get_height() const
Definition: widget.cpp:299
virtual tpoint calculate_best_size() const override=0
See twidget::calculate_best_size.
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
twidget * swap_child(const std::string &id, twidget *widget, const bool recurse, twidget *new_parent=nullptr)
Exchanges a child in the grid.
Definition: grid.cpp:99
Holds a 2D point.
Definition: point.hpp:24
void set_horizontal_scrollbar_mode(const tscrollbar_mode scrollbar_mode)
void toggle_item(const unsigned index)
Toggles the selection state of an item.
Definition: generator.hpp:99
virtual void handle_key_up_arrow(SDLMod modifier, bool &handled)
Up arrow key pressed.
virtual void create_items(const int index, tbuilder_grid_const_ptr list_builder, const std::vector< string_map > &data, const std::function< void(twidget &)> &callback)=0
Creates one or more new item(s).
Helper class to block invalidate_layout.
Definition: window.hpp:250
GLsizeiptr size
Definition: glew.h:1649
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:679
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
GLenum GLenum GLvoid * row
Definition: glew.h:3805
std::vector< tstate_definition > state
std::function< void(twidget &)> callback_value_changed_
This callback is called when the value in the listbox changes.
Definition: listbox.hpp:307
virtual void handle_key_left_arrow(SDLMod modifier, bool &handled)=0
Left arrow key pressed.
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
std::vector< string_map > list_data
Listbox data.
Definition: listbox.hpp:396
#define REGISTER_WIDGET3(type, id, key)
Registers a widget.
tscrollbar_container::tscrollbar_mode horizontal_scrollbar_mode
Definition: listbox.hpp:410
void set_child(twidget *widget, const unsigned row, const unsigned col, const unsigned flags, const unsigned border_size)
Sets a child in the grid.
Definition: grid.cpp:69
void keyboard_capture(twidget *widget)
Definition: window.cpp:1394
twindow * get_window()
Get the parent window.
Definition: widget.cpp:116
tscrollbar_container::tscrollbar_mode vertical_scrollbar_mode
Definition: listbox.hpp:382
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
SDL_Rect get_rectangle() const
Gets the bounding rectangle of the widget on the screen.
Definition: widget.cpp:279
void handle_key_left_arrow(SDLMod modifier, bool &handled)
Inherited from tscrollbar_container.
Definition: listbox.cpp:438
virtual void set_size(const tpoint &size)
Sets the size of the widget.
Definition: widget.cpp:222
void list_item_clicked(twidget &caller)
Function to call after the user clicked on a row.
Definition: listbox.cpp:244
GLdouble s
Definition: glew.h:1358
virtual void child_populate_dirty_list(twindow &caller, const std::vector< twidget * > &call_stack) override
See twidget::child_populate_dirty_list.
Definition: listbox.cpp:373
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:237
unsigned int get_cols() const
Definition: grid.hpp:294
#define DBG_GUI_G
Definition: log.hpp:41
virtual void handle_key_left_arrow(SDLMod modifier, bool &handled)
Left arrow key pressed.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
virtual void place(const tpoint &origin, const tpoint &size) override
See twidget::place.
Definition: listbox.cpp:291
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:131
const tgrid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:215
Contains the implementation details for lexical_cast and shouldn't be used directly.
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:941
Base class for creating containers with one or two scrollbar(s).
GLclampf f
Definition: glew.h:3024
std::function< void(size_t)> callback_item_changed_
This callback is called when a list item is clicked (toggled).
Definition: listbox.hpp:298
virtual void delete_item(const unsigned index)=0
Deletes an item.