The Battle for Wesnoth  1.17.12+dev
tree_view.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2022
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
20 #include "gettext.hpp"
21 #include "gui/core/log.hpp"
24 #include "gui/widgets/settings.hpp"
25 #include "gui/widgets/window.hpp"
26 #include <functional>
27 #include "wml_exception.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 // ------------ WIDGET -----------{
35 
36 REGISTER_WIDGET(tree_view)
37 
38 tree_view::tree_view(const implementation::builder_tree_view& builder)
39  : scrollbar_container(builder, type())
40  , node_definitions_(builder.nodes)
41  , indentation_step_size_(0)
42  , need_layout_(false)
43  , root_node_(nullptr)
44  , selected_item_(nullptr)
45 {
46  connect_signal<event::LEFT_BUTTON_DOWN>(
47  std::bind(&tree_view::signal_handler_left_button_down, this, std::placeholders::_2), event::dispatcher::back_pre_child);
48 }
49 
51 {
52  if(root_node_) {
54  }
55 }
56 
58  const std::string& id, const widget_data& data, const int index)
59 {
60  return get_root_node().add_child(id, data, index);
61 }
62 
63 std::pair<std::shared_ptr<tree_view_node>, int> tree_view::remove_node(tree_view_node* node)
64 {
65  assert(node && node != root_node_ && node->parent_node_);
66  const point node_size = node->get_size();
67 
69 
70  auto node_itor = std::find_if(siblings.begin(), siblings.end(), [node](const auto& c) { return c.get() == node; });
71 
72  assert(node_itor != siblings.end());
73 
74  auto old_node = std::move(*node_itor);
75  old_node->parent_node_ = nullptr;
76 
77  const int position = std::distance(siblings.begin(), node_itor);
78 
79  siblings.erase(node_itor);
80 
81  if(get_size() != point()) {
82  // Don't shrink the width, need to think about a good algorithm to do so.
83  resize_content(0, -node_size.y);
84  }
85 
86  return std::pair(std::move(old_node), position);
87 }
88 
90 {
91  get_root_node().clear();
93 }
94 
95 void tree_view::set_self_active(const bool /*active*/)
96 {
97  /* DO NOTHING */
98 }
99 
100 bool tree_view::empty() const
101 {
102  return root_node_->empty();
103 }
104 
106 {
107  layout_children(false);
108 }
109 
110 void tree_view::resize_content(const int width_modification,
111  const int height_modification,
112  const int width_modification_pos,
113  const int height_modification_pos)
114 {
115  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " width_modification "
116  << width_modification << " height_modification " << height_modification << ".";
117 
119  width_modification,
120  height_modification,
121  width_modification_pos,
122  height_modification_pos
123  )) {
124  // Calculate new size.
126  size.x += width_modification;
127  size.y += height_modification;
128 
129  // Set new size.
130  content_grid()->set_size(size);
131 
132  // Set status.
133  need_layout_ = true;
134  // If the content grows assume it "overwrites" the old content.
135  if(width_modification < 0 || height_modification < 0) {
136  queue_redraw();
137  }
139  DBG_GUI_L << LOG_HEADER << " succeeded.";
140  } else {
141  DBG_GUI_L << LOG_HEADER << " failed.";
142  }
143 }
144 
145 void tree_view::layout_children(const bool force)
146 {
147  assert(root_node_ && content_grid());
148 
149  if(need_layout_ || force) {
152 
153  need_layout_ = false;
155  }
156 }
157 
159 {
160  // Inherited.
162 
163  auto root = std::make_unique<tree_view_node>(root_node_id, nullptr, *this, widget_data{});
164  root_node_ = root.get();
165 
166  assert(content_grid());
167  content_grid()->set_rows_cols(1, 1);
170 }
171 
173 {
174  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
175 
176  get_window()->keyboard_capture(this);
177 }
178 
179 template<tree_view_node* (tree_view_node::*func)()>
181 {
183  if(!selected) {
184  return nullptr;
185  }
186 
187  tree_view_node* visible = selected->get_last_visible_parent_node();
188  if(visible != selected) {
189  return visible;
190  }
191 
192  return (selected->*func)();
193 }
194 
195 template<tree_view_node* (tree_view_node::*func)()>
197 {
198  if(tree_view_node* next = get_next_node<func>()) {
199  next->select_node();
200  SDL_Rect visible = content_visible_area();
201  SDL_Rect rect = next->get_grid().get_rectangle();
202  visible.y = rect.y; // - content_grid()->get_y();
203  visible.h = rect.h;
204  show_content_rect(visible);
205  return true;
206  }
207 
208  return false;
209 }
210 
211 void tree_view::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
212 {
213  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_above>()) {
214  handled = true;
215  } else {
216  scrollbar_container::handle_key_up_arrow(modifier, handled);
217  }
218 }
219 
220 void tree_view::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
221 {
222  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_below>()) {
223  handled = true;
224  } else {
226  }
227 }
228 
229 void tree_view::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
230 {
232  if(!selected || selected->is_folded()) {
234  return;
235  }
236 
237  selected->fold();
238  handled = true;
239 }
240 
241 void tree_view::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
242 {
244  if(!selected || !selected->is_folded()) {
246  return;
247  }
248 
249  selected->unfold();
250  handled = true;
251 }
252 
253 // }---------- DEFINITION ---------{
254 
257 {
258  DBG_GUI_P << "Parsing tree view " << id;
259 
260  load_resolutions<resolution>(cfg);
261 }
262 
264  : resolution_definition(cfg)
265  , grid(nullptr)
266 {
267  // Note the order should be the same as the enum state_t is listbox.hpp.
268  state.emplace_back(cfg.child("state_enabled"));
269  state.emplace_back(cfg.child("state_disabled"));
270 
271  const config& child = cfg.child("grid");
272  VALIDATE(child, _("No grid defined."));
273 
274  grid = std::make_shared<builder_grid>(child);
275 }
276 
277 // }---------- BUILDER -----------{
278 
279 namespace implementation
280 {
281 builder_tree_view::builder_tree_view(const config& cfg)
282  : builder_styled_widget(cfg)
283  , vertical_scrollbar_mode(get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
284  , horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
285  , indentation_step_size(cfg["indentation_step_size"])
286  , nodes()
287 {
288  for(const auto& node : cfg.child_range("node")) {
289  nodes.emplace_back(node);
290  }
291 
292  VALIDATE(!nodes.empty(), _("No nodes defined for a tree view."));
293 }
294 
295 std::unique_ptr<widget> builder_tree_view::build() const
296 {
297  /*
298  * TODO see how much we can move in the constructor instead of
299  * building in several steps.
300  */
301  auto widget = std::make_unique<tree_view>(*this);
302 
303  widget->set_vertical_scrollbar_mode(vertical_scrollbar_mode);
304  widget->set_horizontal_scrollbar_mode(horizontal_scrollbar_mode);
305 
306  widget->set_indentation_step_size(indentation_step_size);
307 
308  DBG_GUI_G << "Window builder: placed tree_view '" << id << "' with definition '" << definition << "'.";
309 
310  const auto conf = widget->cast_config_to<tree_view_definition>();
311  assert(conf);
312 
313  widget->init_grid(*conf->grid);
314  widget->finalize_setup();
315 
316  return widget;
317 }
318 
320  : id(cfg["id"])
321  , unfolded(cfg["unfolded"].to_bool(false))
322  , builder(nullptr)
323 {
324  VALIDATE(!id.empty(), missing_mandatory_wml_key("node", "id"));
325 
326  // TODO: interpolate this value into the error message
327  VALIDATE(id != tree_view::root_node_id, _("[node]id 'root' is reserved for the implementation."));
328 
329  const config& node_definition = cfg.child("node_definition");
330 
331  VALIDATE(node_definition, _("No node defined."));
332 
333  builder = std::make_shared<builder_grid>(node_definition);
334 }
335 
336 } // namespace implementation
337 
338 // }------------ END --------------
339 
340 } // 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:1131
#define DBG_GUI_P
Definition: log.hpp:66
virtual void finalize_setup()
Inherited from container_base.
Definition: tree_view.cpp:158
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:402
std::vector< state_definition > state
const std::vector< node > & nodes
std::vector< std::shared_ptr< tree_view_node > > node_children_vector
#define DBG_GUI_L
Definition: log.hpp:55
#define LOG_HEADER
Definition: tree_view.cpp:30
void finalize_setup()
The builder needs to call us so we do our setup.
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
unsigned indentation_step_size_
Definition: tree_view.hpp:151
Add a special kind of assert to validate whether the input from WML doesn&#39;t contain any problems that...
tree_view_node & add_node(const std::string &id, const widget_data &data, const int index=-1)
Definition: tree_view.cpp:57
tree_view_node & get_root_node()
Definition: tree_view.hpp:75
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: tree_view.cpp:95
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:344
Base class for all widgets.
Definition: widget.hpp:53
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:220
std::string_view data
Definition: picture.cpp:206
tree_view_node * selected_item()
Definition: tree_view.hpp:110
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: tree_view.hpp:242
static std::string _(const char *str)
Definition: gettext.hpp:93
tree_view_node & add_child(const std::string &id, const widget_data &data, const int index=-1)
Constructs a new child node.
void signal_handler_left_button_down(const event::ui_event event)
Definition: tree_view.cpp:172
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.
Generic file dialog.
bool empty() const
Does the node have children?
bool is_folded() const
Is the node folded?
Base container class.
Definition: grid.hpp:31
node_children_vector children_
Our children.
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
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:87
#define VALIDATE(cond, message)
The macro to use for the validation of WML.
This file contains the settings handling of the widget library.
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:211
virtual std::unique_ptr< widget > build() const override
Definition: tree_view.cpp:295
std::string selected
bool handle_up_down_arrow()
Definition: tree_view.cpp:196
tree_view_node * root_node_
Definition: tree_view.hpp:155
std::vector< tree_node > nodes
The types of nodes in the tree view.
Definition: tree_view.hpp:253
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:229
A tree view is a control that holds several items of the same or different types. ...
Definition: tree_view.hpp:60
void fold(const bool recursive=false)
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:110
std::pair< std::shared_ptr< tree_view_node >, int > remove_node(tree_view_node *node)
Removes the given node as a child of its parent node.
Definition: tree_view.cpp:63
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)
Right arrow key pressed.
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).
#define DBG_GUI_E
Definition: log.hpp:35
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:442
window * get_window()
Get the parent window.
Definition: widget.cpp:118
tree_view_node * get_last_visible_parent_node()
const SDL_Rect & content_visible_area() const
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:46
Holds a 2D point.
Definition: point.hpp:24
void unfold(const bool recursive=false)
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:56
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:72
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:49
scrollbar_mode get_scrollbar_mode(const std::string &scrollbar_mode)
Returns the scrollbar mode flags.
Definition: helper.cpp:121
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:712
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:306
SDL_Rect content_visible_area_
Cache for the visible area for the content.
bool empty() const
Definition: tree_view.cpp:100
tree_view_node * get_next_node()
Definition: tree_view.cpp:180
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: tree_view.hpp:243
virtual void set_size(const point &size)
Sets the size of the widget.
Definition: widget.cpp:228
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:193
tree_view_definition(const config &cfg)
Definition: tree_view.cpp:255
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
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:105
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)
Up arrow key pressed.
static const std::string root_node_id
Definition: tree_view.hpp:141
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
mock_char c
#define DBG_GUI_G
Definition: log.hpp:41
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:241
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:301
Contains the implementation details for lexical_cast and shouldn&#39;t be used directly.
void set_child(std::unique_ptr< widget > widget, const unsigned row, const unsigned col, const unsigned flags, const unsigned border_size)
Sets a child in the grid.
Definition: grid.cpp:71
tree_node(const config &cfg)
Definition: tree_view.cpp:319