The Battle for Wesnoth  1.19.14+dev
tree_view.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2025
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/window.hpp"
25 #include <functional>
26 #include "wml_exception.hpp"
27 
28 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
29 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
30 
31 namespace gui2
32 {
33 // ------------ WIDGET -----------{
34 
35 REGISTER_WIDGET(tree_view)
36 
37 tree_view::tree_view(const implementation::builder_tree_view& builder)
38  : scrollbar_container(builder, type())
39  , node_definitions_(builder.nodes)
40  , indentation_step_size_(builder.indentation_step_size)
41  , need_layout_(false)
42  , root_node_(nullptr)
43  , selected_item_(nullptr)
44 {
45  connect_signal<event::LEFT_BUTTON_DOWN>(
46  std::bind(&tree_view::signal_handler_left_button_down, this, std::placeholders::_2), event::dispatcher::back_pre_child);
47 }
48 
50 {
51  if(root_node_) {
53  }
54 }
55 
57  const std::string& id, const widget_data& data, const int index)
58 {
59  return get_root_node().add_child(id, data, index);
60 }
61 
62 std::pair<std::shared_ptr<tree_view_node>, int> tree_view::remove_node(tree_view_node* node)
63 {
64  assert(node && node != root_node_ && node->parent_node_);
65  const point node_size = node->get_size();
66 
68 
69  auto node_itor = utils::ranges::find(siblings, node, [](const auto& c) { return c.get(); });
70  assert(node_itor != siblings.end());
71 
72  auto old_node = std::move(*node_itor);
73  old_node->parent_node_ = nullptr;
74 
75  const int position = std::distance(siblings.begin(), node_itor);
76 
77  siblings.erase(node_itor);
78 
79  if(get_size() != point()) {
80  // Don't shrink the width, need to think about a good algorithm to do so.
81  resize_content(0, -node_size.y);
82  }
83 
84  return std::pair(std::move(old_node), position);
85 }
86 
88 {
89  get_root_node().clear();
91 }
92 
93 void tree_view::set_self_active(const bool /*active*/)
94 {
95  /* DO NOTHING */
96 }
97 
98 bool tree_view::empty() const
99 {
100  return root_node_->empty();
101 }
102 
104 {
105  layout_children(false);
106 }
107 
108 void tree_view::resize_content(const int width_modification,
109  const int height_modification,
110  const int width_modification_pos,
111  const int height_modification_pos)
112 {
113  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " width_modification "
114  << width_modification << " height_modification " << height_modification << ".";
115 
117  width_modification,
118  height_modification,
119  width_modification_pos,
120  height_modification_pos
121  )) {
122  // Calculate new size.
124  size.x += width_modification;
125  size.y += height_modification;
126 
127  // Set new size.
129 
130  // Set status.
131  need_layout_ = true;
132  // If the content grows assume it "overwrites" the old content.
133  if(width_modification < 0 || height_modification < 0) {
134  queue_redraw();
135  }
137  DBG_GUI_L << LOG_HEADER << " succeeded.";
138  } else {
139  DBG_GUI_L << LOG_HEADER << " failed.";
140  }
141 }
142 
143 void tree_view::layout_children(const bool force)
144 {
145  assert(root_node_ && content_grid());
146 
147  if(need_layout_ || force) {
150 
151  need_layout_ = false;
153  }
154 }
155 
157 {
158  // Inherited.
160 
161  auto root = std::make_unique<tree_view_node>(root_node_id, nullptr, *this, widget_data{});
162  root_node_ = root.get();
163 
164  assert(content_grid());
165  content_grid()->set_rows_cols(1, 1);
168 }
169 
171 {
172  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
173 
174  get_window()->keyboard_capture(this);
175 }
176 
177 template<tree_view_node* (tree_view_node::*func)()>
179 {
180  tree_view_node* selected = selected_item();
181  if(!selected) {
182  return nullptr;
183  }
184 
185  tree_view_node* visible = selected->get_last_visible_parent_node();
186  if(visible != selected) {
187  return visible;
188  }
189 
190  return (selected->*func)();
191 }
192 
193 template<tree_view_node* (tree_view_node::*func)()>
195 {
196  if(tree_view_node* next = get_next_node<func>()) {
197  next->select_node();
198  rect visible = content_visible_area();
199  rect rect = next->get_grid().get_rectangle();
200  visible.y = rect.y; // - content_grid()->get_y();
201  visible.h = rect.h;
202  show_content_rect(visible);
203  return true;
204  }
205 
206  return false;
207 }
208 
209 void tree_view::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
210 {
211  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_above>()) {
212  handled = true;
213  } else {
214  scrollbar_container::handle_key_up_arrow(modifier, handled);
215  }
216 }
217 
218 void tree_view::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
219 {
220  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_below>()) {
221  handled = true;
222  } else {
224  }
225 }
226 
227 void tree_view::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
228 {
229  tree_view_node* selected = selected_item();
230  if(!selected || selected->is_folded()) {
232  return;
233  }
234 
235  selected->fold();
236  handled = true;
237 }
238 
239 void tree_view::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
240 {
241  tree_view_node* selected = selected_item();
242  if(!selected || !selected->is_folded()) {
244  return;
245  }
246 
247  selected->unfold();
248  handled = true;
249 }
250 
251 // }---------- DEFINITION ---------{
252 
255 {
256  DBG_GUI_P << "Parsing tree view " << id;
257 
258  load_resolutions<resolution>(cfg);
259 }
260 
263  , grid(nullptr)
264 {
265  // Note the order should be the same as the enum state_t is listbox.hpp.
266  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("tree_view_definition][resolution", "state_enabled")));
267  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("tree_view_definition][resolution", "state_disabled")));
268 
269  auto child = VALIDATE_WML_CHILD(cfg, "grid", missing_mandatory_wml_tag("tree_view_definition][resolution", "grid"));
270 
271  grid = std::make_shared<builder_grid>(child);
272 }
273 
274 // }---------- BUILDER -----------{
275 
276 namespace implementation
277 {
278 builder_tree_view::builder_tree_view(const config& cfg)
280  , indentation_step_size(cfg["indentation_step_size"].to_unsigned())
281  , nodes()
282 {
283  for(const auto& node : cfg.child_range("node")) {
284  nodes.emplace_back(node);
285  }
286 
287  VALIDATE(!nodes.empty(), _("No nodes defined for a tree view."));
288 }
289 
290 std::unique_ptr<widget> builder_tree_view::build() const
291 {
292  /*
293  * TODO see how much we can move in the constructor instead of
294  * building in several steps.
295  */
296  auto widget = std::make_unique<tree_view>(*this);
297 
298  DBG_GUI_G << "Window builder: placed tree_view '" << id << "' with definition '" << definition << "'.";
299 
300  const auto conf = widget->cast_config_to<tree_view_definition>();
301  assert(conf);
302 
303  widget->init_grid(*conf->grid);
304  widget->finalize_setup();
305 
306  return widget;
307 }
308 
310  : id(cfg["id"])
311  , unfolded(cfg["unfolded"].to_bool(false))
312  , builder(nullptr)
313 {
314  VALIDATE(!id.empty(), missing_mandatory_wml_key("node", "id"));
315 
316  // TODO: interpolate this value into the error message
317  VALIDATE(id != tree_view::root_node_id, _("[node]id ‘root’ is reserved for the implementation."));
318 
319  auto node_definition = VALIDATE_WML_CHILD(cfg, "node_definition", missing_mandatory_wml_tag("node", "node_definition"));
320  builder = std::make_shared<builder_grid>(node_definition);
321 }
322 
323 } // namespace implementation
324 
325 // }------------ END --------------
326 
327 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
child_itors child_range(config_key_type key)
Definition: config.cpp:268
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:711
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).
virtual void handle_key_down_arrow(SDL_Keymod modifier, bool &handled)
Down arrow key pressed.
void show_content_rect(const rect &rect)
Shows a certain part of the content.
const rect & content_visible_area() const
rect content_visible_area_
Cache for the visible area for the content.
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.
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.
bool empty() const
Does the node have children?
tree_view_node * parent_node_
Our parent node.
virtual void set_visible_rectangle(const rect &rectangle) override
See widget::set_visible_rectangle.
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.
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:108
const tree_view_node & get_root_node() const
Definition: tree_view.hpp:53
unsigned indentation_step_size_
Definition: tree_view.hpp:139
void handle_key_right_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:239
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: tree_view.cpp:93
static const std::string root_node_id
Definition: tree_view.hpp:129
tree_view_node * get_next_node()
Definition: tree_view.cpp:178
tree_view_node * root_node_
Definition: tree_view.hpp:143
virtual void finalize_setup()
Inherited from container_base.
Definition: tree_view.cpp:156
bool empty() const
Definition: tree_view.cpp:98
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:209
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:227
tree_view_node & add_node(const std::string &id, const widget_data &data, const int index=-1)
Definition: tree_view.cpp:56
void signal_handler_left_button_down(const event::ui_event event)
Definition: tree_view.cpp:170
tree_view_node * selected_item()
Definition: tree_view.hpp:98
void handle_key_down_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:218
virtual void layout_children() override
See widget::layout_children.
Definition: tree_view.cpp:103
bool handle_up_down_arrow()
Definition: tree_view.cpp:194
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:62
Base class for all widgets.
Definition: widget.hpp:55
void queue_redraw()
Indicates that this widget should be redrawn.
Definition: widget.cpp:464
point get_origin() const
Returns the screen origin of the widget.
Definition: widget.cpp:311
point get_size() const
Returns the size of the widget.
Definition: widget.cpp:316
window * get_window()
Get the parent window.
Definition: widget.cpp:117
virtual void set_size(const point &size)
Sets the size of the widget.
Definition: widget.cpp:236
void keyboard_capture(widget *widget)
Definition: window.cpp:1198
const config * cfg
static std::string _(const char *str)
Definition: gettext.hpp:97
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:199
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:214
ui_event
The event sent to the dispatcher.
Definition: handler.hpp:115
Generic file dialog.
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
Contains the implementation details for lexical_cast and shouldn't be used directly.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
auto find(Container &container, const Value &value, const Projection &projection={})
Definition: general.hpp:179
const std::vector< findroute_node > & nodes
Definition: pathfind.cpp:220
std::string_view data
Definition: picture.cpp:188
#define REGISTER_WIDGET(id)
Wrapper for REGISTER_WIDGET3.
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:237
virtual std::unique_ptr< widget > build() const override
Definition: tree_view.cpp:290
tree_node(const config &cfg)
Definition: tree_view.cpp:309
std::vector< state_definition > state
tree_view_definition(const config &cfg)
Definition: tree_view.cpp:253
Holds a 2D point.
Definition: point.hpp:25
An abstract description of a rectangle with integer coordinates.
Definition: rect.hpp:49
mock_char c
#define LOG_HEADER
Definition: tree_view.cpp:29
std::string missing_mandatory_wml_tag(const std::string &section, const std::string &tag)
Returns a standard message for a missing wml child (tag).
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 (attribute).
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.