The Battle for Wesnoth  1.15.1+dev
tree_view.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2018 by Mark de Wever <koraq@xs4all.nl>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "gui/core/log.hpp"
22 #include "gui/widgets/settings.hpp"
23 #include "gui/widgets/window.hpp"
24 #include "gettext.hpp"
25 #include "wml_exception.hpp"
26 
27 #include "utils/functional.hpp"
28 
29 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
30 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
31 
32 namespace gui2
33 {
34 
35 // ------------ WIDGET -----------{
36 
37 REGISTER_WIDGET(tree_view)
38 
39 tree_view::tree_view(const implementation::builder_tree_view& builder)
40  : scrollbar_container(builder, type())
41  , node_definitions_(builder.nodes)
42  , indentation_step_size_(0)
43  , need_layout_(false)
44  , root_node_(new tree_view_node("root",
45  nullptr,
46  *this,
47  std::map<std::string, string_map>()))
48  , selected_item_(nullptr)
49  , selection_change_callback_()
50 {
51  connect_signal<event::LEFT_BUTTON_DOWN>(
52  std::bind(&tree_view::signal_handler_left_button_down, this, _2),
54 }
56 {
57  if (root_node_) {
59  }
60 }
62  const std::string& id,
63  const std::map<std::string /* widget id */, string_map>& data,
64  const int index)
65 {
66  return get_root_node().add_child(id, data, index);
67 }
68 
69 std::pair<tree_view_node::ptr_t, int> tree_view::remove_node(tree_view_node* node)
70 {
71  assert(node && node != root_node_ && node->parent_node_);
72  const point node_size = node->get_size();
73 
75 
76  auto node_itor = std::find_if(siblings.begin(), siblings.end(),
77  [node](const tree_view_node::ptr_t& c) { return c.get() == node; }
78  );
79 
80  assert(node_itor != siblings.end());
81 
82  auto old_node = std::move(*node_itor);
83  old_node->parent_node_ = nullptr;
84 
85  const int position = std::distance(siblings.begin(), node_itor);
86 
87  siblings.erase(node_itor);
88 
89  if(get_size() != point()) {
90  // Don't shrink the width, need to think about a good algorithm to do so.
91  resize_content(0, -node_size.y);
92  }
93 
94  return std::make_pair(std::move(old_node), position);
95 }
96 
98 {
99  get_root_node().clear();
101 }
102 
103 void
105  const std::vector<widget*>& call_stack)
106 {
107  // Inherited.
109 
110  assert(root_node_);
111  root_node_->impl_populate_dirty_list(caller, call_stack);
112 }
113 
114 void tree_view::set_self_active(const bool /*active*/)
115 {
116  /* DO NOTHING */
117 }
118 
119 bool tree_view::empty() const
120 {
121  return root_node_->empty();
122 }
123 
125 {
126  layout_children(false);
127 }
128 
129 void tree_view::resize_content(const int width_modification,
130  const int height_modification,
131  const int width__modification_pos,
132  const int height_modification_pos)
133 {
134  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size()
135  << " width_modification " << width_modification
136  << " height_modification " << height_modification << ".\n";
137 
138  if(content_resize_request(width_modification, height_modification, width__modification_pos, height_modification_pos)) {
139 
140  // Calculate new size.
142  size.x += width_modification;
143  size.y += height_modification;
144 
145  // Set new size.
146  content_grid()->set_size(size);
147 
148  // Set status.
149  need_layout_ = true;
150  // If the content grows assume it "overwrites" the old content.
151  if(width_modification < 0 || height_modification < 0) {
152  set_is_dirty(true);
153  }
155  DBG_GUI_L << LOG_HEADER << " succeeded.\n";
156  } else {
157  DBG_GUI_L << LOG_HEADER << " failed.\n";
158  }
159 }
160 
161 void tree_view::layout_children(const bool force)
162 {
163  assert(root_node_ && content_grid());
164 
165  if(need_layout_ || force) {
167  get_origin(),
168  content_grid()->get_size().x);
170 
171  need_layout_ = false;
173  }
174 }
175 
177 {
178  // Inherited.
180 
181  assert(content_grid());
182  content_grid()->set_rows_cols(1, 1);
184  0,
185  0,
188  0);
189 }
190 
192 {
193  DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
194 
195  get_window()->keyboard_capture(this);
196 }
197 template<tree_view_node* (tree_view_node::*func) ()>
199 {
201  if(!selected) {
202  return nullptr;
203  }
204  tree_view_node* visible = selected->get_last_visible_parent_node();
205  if(visible != selected) {
206  return visible;
207  }
208  return (selected->*func)();
209 }
210 
211 template<tree_view_node* (tree_view_node::*func) ()>
213 {
214  if(tree_view_node* next = get_next_node<func>())
215  {
216  next->select_node();
217  SDL_Rect visible = content_visible_area();
218  SDL_Rect rect = next->get_grid().get_rectangle();
219  visible.y = rect.y;// - content_grid()->get_y();
220  visible.h = rect.h;
221  show_content_rect(visible);
222  return true;
223  }
224  return false;
225 }
226 
227 void tree_view::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
228 {
229  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_above>()) {
230  handled = true;
231  }
232  else {
233  scrollbar_container::handle_key_up_arrow(modifier, handled);
234  }
235 }
236 
237 void tree_view::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
238 {
239  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_below>()) {
240  handled = true;
241  }
242  else {
244  }
245 }
246 
247 
248 void tree_view::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
249 {
251  if(!selected || selected->is_folded()) {
253  return;
254  }
255  selected->fold();
256  handled = true;
257 }
258 
259 void tree_view::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
260 {
262  if(!selected || !selected->is_folded()) {
264  return;
265  }
266  selected->unfold();
267  handled = true;
268 }
269 
270 // }---------- DEFINITION ---------{
271 
274 {
275  DBG_GUI_P << "Parsing tree view " << id << '\n';
276 
277  load_resolutions<resolution>(cfg);
278 }
279 
280 /*WIKI
281  * @page = GUIWidgetDefinitionWML
282  * @order = 1_tree_view
283  *
284  * == Tree view ==
285  *
286  * @macro = tree_view_description
287  *
288  * The documentation is not written yet.
289  *
290  * The following states exist:
291  * * state_enabled, the listbox is enabled.
292  * * state_disabled, the listbox is disabled.
293  * @begin{parent}{name="gui/"}
294  * @begin{tag}{name="ree_view_definition"}{min=0}{max=-1}{super="generic/widget_definition"}
295  * @begin{tag}{name="resolution"}{min=0}{max=-1}{super="generic/widget_definition/resolution"}
296  * @allow{link}{name="gui/window/resolution/grid"}
297  * @begin{tag}{name="state_enabled"}{min=0}{max=1}{super="generic/state"}
298  * @end{tag}{name="state_enabled"}
299  * @begin{tag}{name="state_disabled"}{min=0}{max=1}{super="generic/state"}
300  * @end{tag}{name="state_disabled"}
301  * @end{tag}{name="resolution"}
302  * @end{tag}{name="ree_view_definition"}
303  * @end{parent}{name="gui/"}
304  */
306  : resolution_definition(cfg), grid(nullptr)
307 {
308  // Note the order should be the same as the enum state_t is listbox.hpp.
309  state.emplace_back(cfg.child("state_enabled"));
310  state.emplace_back(cfg.child("state_disabled"));
311 
312  const config& child = cfg.child("grid");
313  VALIDATE(child, _("No grid defined."));
314 
315  grid = std::make_shared<builder_grid>(child);
316 }
317 
318 // }---------- BUILDER -----------{
319 
320 /*WIKI_MACRO
321  * @begin{macro}{tree_view_description}
322  *
323  * A tree view is a styled_widget that holds several items of the same or
324  * different types. The items shown are called tree view nodes and when
325  * a node has children, these can be shown or hidden. Nodes that contain
326  * children need to provide a clickable button in order to fold or
327  * unfold the children.
328  * @end{macro}
329  */
330 
331 /*WIKI
332  * @page = GUIWidgetInstanceWML
333  * @order = 2_tree_view
334  *
335  * == Tree view ==
336  * @begin{parent}{name="gui/window/resolution/grid/row/column/"}
337  * @begin{tag}{name="tree_view"}{min=0}{max=-1}{super="generic/widget_instance"}
338  * @macro = tree_view_description
339  *
340  * List with the tree view specific variables:
341  * @begin{table}{config}
342  * vertical_scrollbar_mode & scrollbar_mode & initial_auto &
343  * Determines whether or not to show the
344  * scrollbar. $
345  * horizontal_scrollbar_mode & scrollbar_mode & initial_auto &
346  * Determines whether or not to show the
347  * scrollbar. $
348  *
349  * indentation_step_size & unsigned & 0 &
350  * The number of pixels every level of
351  * nodes is indented from the previous
352  * level. $
353  *
354  * node & section & & The tree view can contain multiple node
355  * sections. This part needs more
356  * documentation. $
357  * @end{table}
358  * @begin{tag}{name="node"}{min=0}{max=-1}
359  * @begin{table}{config}
360  * id & string & "" & $
361  * @end{table}
362  * @begin{tag}{name="node_definition"}{min=0}{max=-1}{super="gui/window/resolution/grid"}
363  * @begin{table}{config}
364  * return_value_id & string & "" & $
365  * @end{table}
366  * @end{tag}{name="node_definition"}
367  * @end{tag}{name="node"}
368  * @end{tag}{name="tree_view"}
369  * @end{parent}{name="gui/window/resolution/grid/row/column/"}
370  * NOTE more documentation and examples are needed.
371  */ // TODO annotate node
372 
373 namespace implementation
374 {
375 
376 builder_tree_view::builder_tree_view(const config& cfg)
377  : builder_styled_widget(cfg)
378  , vertical_scrollbar_mode(
379  get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
380  , horizontal_scrollbar_mode(
381  get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
382  , indentation_step_size(cfg["indentation_step_size"])
383  , nodes()
384 {
385 
386  for(const auto & node : cfg.child_range("node"))
387  {
388  nodes.emplace_back(node);
389  }
390 
391  VALIDATE(!nodes.empty(), _("No nodes defined for a tree view."));
392 }
393 
395 {
396  /*
397  * TODO see how much we can move in the constructor instead of
398  * building in several steps.
399  */
400  tree_view* widget = new tree_view(*this);
401 
404 
406 
407  DBG_GUI_G << "Window builder: placed tree_view '" << id
408  << "' with definition '" << definition << "'.\n";
409 
410  const auto conf = widget->cast_config_to<tree_view_definition>();
411  assert(conf);
412 
413  widget->init_grid(conf->grid);
414  widget->finalize_setup();
415 
416  return widget;
417 }
418 
420  : id(cfg["id"])
421  , unfolded(cfg["unfolded"].to_bool(false))
422  , builder(nullptr)
423 {
424  VALIDATE(!id.empty(), missing_mandatory_wml_key("node", "id"));
425 
426  VALIDATE(id != "root",
427  _("[node]id 'root' is reserved for the implementation."));
428 
429  const config& node_definition = cfg.child("node_definition");
430 
431  VALIDATE(node_definition, _("No node defined."));
432 
433  builder = std::make_shared<builder_grid>(node_definition);
434 }
435 
436 } // namespace implementation
437 
438 // }------------ END --------------
439 
440 } // namespace gui2
Define the common log macros for the gui toolkit.
void show_content_rect(const SDL_Rect &rect)
Shows a certain part of the content.
Base class of a resolution, contains the common keys for a resolution.
void keyboard_capture(widget *widget)
Definition: window.cpp:1282
#define DBG_GUI_P
Definition: log.hpp:68
virtual void finalize_setup()
Inherited from container_base.
Definition: tree_view.cpp:176
tree_view_node * parent_node_
Our parent node.
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:420
std::vector< state_definition > state
const std::vector< node > & nodes
void set_horizontal_scrollbar_mode(const scrollbar_mode scrollbar_mode)
#define DBG_GUI_L
Definition: log.hpp:57
#define LOG_HEADER
Definition: tree_view.cpp:30
void finalize_setup()
The builder needs to call us so we do our setup.
unsigned indentation_step_size_
Definition: tree_view.hpp:138
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
tree_view_node & get_root_node()
Definition: tree_view.hpp:53
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: tree_view.cpp:114
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool &handled)
Down arrow key pressed.
This file contains the window object, this object is a top level container which has the event manage...
child_itors child_range(config_key_type key)
Definition: config.cpp:362
void set_vertical_scrollbar_mode(const scrollbar_mode scrollbar_mode)
Base class for all widgets.
Definition: widget.hpp:47
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:237
tree_view_node * selected_item()
Definition: tree_view.hpp:95
STL namespace.
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: tree_view.hpp:221
std::pair< tree_view_node::ptr_t, int > remove_node(tree_view_node *node)
Removes the given node as a child of its parent node.
Definition: tree_view.cpp:69
void signal_handler_left_button_down(const event::ui_event event)
Definition: tree_view.cpp:191
std::string missing_mandatory_wml_key(const std::string &section, const std::string &key, const std::string &primary_key, const std::string &primary_value)
Returns a standard message for a missing wml key.
int x
x coordinate.
Definition: point.hpp:44
Generic file dialog.
Definition: field-fwd.hpp:22
bool empty() const
Does the node have children?
bool is_folded() const
Is the node folded?
Base container class.
Definition: grid.hpp:30
node_children_vector children_
Our children.
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:91
std::string definition
Parameters for the styled_widget.
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
std::shared_ptr< tree_view_node > ptr_t
This file contains the settings handling of the widget library.
void set_is_dirty(const bool is_dirty)
Definition: widget.cpp:463
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:227
std::string selected
bool handle_up_down_arrow()
Definition: tree_view.cpp:212
tree_view_node * root_node_
Definition: tree_view.hpp:142
std::vector< tree_node > nodes
The types of nodes in the tree view.
Definition: tree_view.hpp:232
bool content_resize_request(const bool force_sizing=false)
Notification if the content of a child needs a resize.
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:248
void set_indentation_step_size(const unsigned indentation_step_size)
Definition: tree_view.hpp:90
void fold(const bool recursive=false)
void init_grid(const std::shared_ptr< builder_grid > &grid_builder)
Initializes and builds the grid.
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
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: tree_view.cpp:129
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)
Right arrow key pressed.
void set_child(widget *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 clear()
Removes all child items from the widget.
std::string id
Parameters for the widget.
Base class for creating containers with one or two scrollbar(s).
void impl_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack)
"Inherited" from widget.
#define DBG_GUI_E
Definition: log.hpp:34
std::shared_ptr< const typename T::resolution > cast_config_to() const
Casts the current resolution definition config to the respective type of a derived widget...
window * get_window()
Get the parent window.
Definition: widget.cpp:114
tree_view_node * get_last_visible_parent_node()
const SDL_Rect & content_visible_area() const
std::map< std::string, t_string > string_map
Definition: widget.hpp:24
Holds a 2D point.
Definition: point.hpp:23
void unfold(const bool recursive=false)
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:55
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:48
scrollbar_mode get_scrollbar_mode(const std::string &scrollbar_mode)
Returns the scrollbar mode flags.
Definition: helper.cpp:120
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:722
virtual void child_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack) override
See widget::child_populate_dirty_list.
Definition: tree_view.cpp:104
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:302
#define next(ls)
Definition: llex.cpp:32
virtual void child_populate_dirty_list(window &caller, const std::vector< widget *> &call_stack) override
See widget::child_populate_dirty_list.
SDL_Rect content_visible_area_
Cache for the visible area for the content.
bool empty() const
Definition: tree_view.cpp:119
tree_view_node * get_next_node()
Definition: tree_view.cpp:198
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: tree_view.hpp:222
virtual void set_size(const point &size)
Sets the size of the widget.
Definition: widget.cpp:224
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
tree_view_definition(const config &cfg)
Definition: tree_view.cpp:272
virtual void place(const point &origin, const point &size) override
See widget::place.
virtual void layout_children() override
See widget::layout_children.
Definition: tree_view.cpp:124
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)
Up arrow key pressed.
std::vector< ptr_t > node_children_vector
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:92
mock_char c
tree_view_node & add_child(const std::string &id, const std::map< std::string, string_map > &data, const int index=-1)
Constructs a new child node.
int y
y coordinate.
Definition: point.hpp:47
base class of top level items, the only item which needs to store the final canvases to draw on ...
Definition: window.hpp:63
#define DBG_GUI_G
Definition: log.hpp:40
static std::deque< std::string > call_stack
Definition: function.cpp:39
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:259
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:297
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
ui_event
The event send to the dispatcher.
Definition: handler.hpp:55
tree_view_node & add_node(const std::string &id, const std::map< std::string, string_map > &data, const int index=-1)
Definition: tree_view.cpp:61
tree_node(const config &cfg)
Definition: tree_view.cpp:419