The Battle for Wesnoth  1.17.23+dev
tree_view.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2023
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.
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(VALIDATE_WML_CHILD(cfg, "state_enabled", _("Missing required state for tree view")));
269  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", _("Missing required state for tree view")));
270 
271  auto child = VALIDATE_WML_CHILD(cfg, "grid", _("No grid defined for tree view"));
272 
273  grid = std::make_shared<builder_grid>(child);
274 }
275 
276 // }---------- BUILDER -----------{
277 
278 namespace implementation
279 {
280 builder_tree_view::builder_tree_view(const config& cfg)
281  : builder_styled_widget(cfg)
282  , vertical_scrollbar_mode(get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
283  , horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
284  , indentation_step_size(cfg["indentation_step_size"])
285  , nodes()
286 {
287  for(const auto& node : cfg.child_range("node")) {
288  nodes.emplace_back(node);
289  }
290 
291  VALIDATE(!nodes.empty(), _("No nodes defined for a tree view."));
292 }
293 
294 std::unique_ptr<widget> builder_tree_view::build() const
295 {
296  /*
297  * TODO see how much we can move in the constructor instead of
298  * building in several steps.
299  */
300  auto widget = std::make_unique<tree_view>(*this);
301 
302  widget->set_vertical_scrollbar_mode(vertical_scrollbar_mode);
303  widget->set_horizontal_scrollbar_mode(horizontal_scrollbar_mode);
304 
305  widget->set_indentation_step_size(indentation_step_size);
306 
307  DBG_GUI_G << "Window builder: placed tree_view '" << id << "' with definition '" << definition << "'.";
308 
309  const auto conf = widget->cast_config_to<tree_view_definition>();
310  assert(conf);
311 
312  widget->init_grid(*conf->grid);
313  widget->finalize_setup();
314 
315  return widget;
316 }
317 
319  : id(cfg["id"])
320  , unfolded(cfg["unfolded"].to_bool(false))
321  , builder(nullptr)
322 {
323  VALIDATE(!id.empty(), missing_mandatory_wml_key("node", "id"));
324 
325  // TODO: interpolate this value into the error message
326  VALIDATE(id != tree_view::root_node_id, _("[node]id 'root' is reserved for the implementation."));
327 
328  auto node_definition = cfg.optional_child("node_definition");
329 
330  VALIDATE(node_definition, _("No node defined."));
331 
332  builder = std::make_shared<builder_grid>(*node_definition);
333 }
334 
335 } // namespace implementation
336 
337 // }------------ END --------------
338 
339 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
child_itors child_range(config_key_type key)
Definition: config.cpp:277
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Euivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:389
Base container class.
Definition: grid.hpp:32
static const unsigned HORIZONTAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:56
static const unsigned VERTICAL_GROW_SEND_TO_CLIENT
Definition: grid.hpp:49
void set_rows_cols(const unsigned rows, const unsigned cols)
Wrapper to set_rows and set_cols.
Definition: grid.cpp:712
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
Base class for creating containers with one or two scrollbar(s).
SDL_Rect content_visible_area_
Cache for the visible area for the content.
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool &handled)
Down arrow key pressed.
void finalize_setup()
The builder needs to call us so we do our setup.
bool content_resize_request(const bool force_sizing=false)
Notification if the content of a child needs a resize.
const SDL_Rect & content_visible_area() const
void show_content_rect(const SDL_Rect &rect)
Shows a certain part of the content.
virtual void handle_key_up_arrow(SDL_Keymod modifier, bool &handled)
Up arrow key pressed.
virtual void handle_key_left_arrow(SDL_Keymod modifier, bool &handled)
Left arrow key pressed.
virtual void handle_key_right_arrow(SDL_Keymod modifier, bool &handled)
Right arrow key pressed.
void clear()
Removes all child items from the widget.
virtual void set_visible_rectangle(const SDL_Rect &rectangle) override
See widget::set_visible_rectangle.
bool empty() const
Does the node have children?
tree_view_node * parent_node_
Our parent node.
node_children_vector children_
Our children.
std::vector< std::shared_ptr< tree_view_node > > node_children_vector
tree_view_node & add_child(const std::string &id, const widget_data &data, const int index=-1)
Constructs a new child node.
virtual void place(const point &origin, const point &size) override
See widget::place.
A tree view is a control that holds several items of the same or different types.
Definition: tree_view.hpp:61
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
unsigned indentation_step_size_
Definition: tree_view.hpp:151
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:241
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: tree_view.cpp:95
static const std::string root_node_id
Definition: tree_view.hpp:141
tree_view_node * get_next_node()
Definition: tree_view.cpp:180
tree_view_node & get_root_node()
Definition: tree_view.hpp:75
tree_view_node * root_node_
Definition: tree_view.hpp:155
virtual void finalize_setup()
Inherited from container_base.
Definition: tree_view.cpp:158
bool empty() const
Definition: tree_view.cpp:100
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:211
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:229
tree_view_node & add_node(const std::string &id, const widget_data &data, const int index=-1)
Definition: tree_view.cpp:57
void signal_handler_left_button_down(const event::ui_event event)
Definition: tree_view.cpp:172
tree_view_node * selected_item()
Definition: tree_view.hpp:110
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:220
virtual void layout_children() override
See widget::layout_children.
Definition: tree_view.cpp:105
bool handle_up_down_arrow()
Definition: tree_view.cpp:196
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
Base class for all widgets.
Definition: widget.hpp:54
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:456
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:303
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:308
window * get_window()
Get the parent window.
Definition: widget.cpp:118
virtual void set_size(const point &size)
Sets the size of the widget.
Definition: widget.cpp:228
void keyboard_capture(widget *widget)
Definition: window.cpp:1224
const std::vector< node > & nodes
static std::string _(const char *str)
Definition: gettext.hpp:93
Define the common log macros for the gui toolkit.
#define DBG_GUI_L
Definition: log.hpp:55
#define DBG_GUI_G
Definition: log.hpp:41
#define DBG_GUI_P
Definition: log.hpp:66
#define DBG_GUI_E
Definition: log.hpp:35
std::string id
Text to match against addon_info.tags()
Definition: manager.cpp:215
This file contains the window object, this object is a top level container which has the event manage...
void point(int x, int y)
Draw a single point.
Definition: draw.cpp:203
std::string selected
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
scrollbar_mode get_scrollbar_mode(const std::string &scrollbar_mode)
Returns the scrollbar mode flags.
Definition: helper.cpp:121
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
Contains the implementation details for lexical_cast and shouldn't be used directly.
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
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
std::string_view data
Definition: picture.cpp:199
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
This file contains the settings handling of the widget library.
std::string definition
Parameters for the styled_widget.
std::vector< tree_node > nodes
The types of nodes in the tree view.
Definition: tree_view.hpp:253
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: tree_view.hpp:242
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: tree_view.hpp:243
virtual std::unique_ptr< widget > build() const override
Definition: tree_view.cpp:294
tree_node(const config &cfg)
Definition: tree_view.cpp:318
Base class of a resolution, contains the common keys for a resolution.
std::vector< state_definition > state
tree_view_definition(const config &cfg)
Definition: tree_view.cpp:255
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:47
mock_char c
#define LOG_HEADER
Definition: tree_view.cpp:30
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.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
#define VALIDATE_WML_CHILD(cfg, key, message)
#define VALIDATE(cond, message)
The macro to use for the validation of WML.