The Battle for Wesnoth  1.19.5+dev
tree_view.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2010 - 2024
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_(0)
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 = std::find_if(siblings.begin(), siblings.end(), [node](const auto& c) { return c.get() == node; });
70 
71  assert(node_itor != siblings.end());
72 
73  auto old_node = std::move(*node_itor);
74  old_node->parent_node_ = nullptr;
75 
76  const int position = std::distance(siblings.begin(), node_itor);
77 
78  siblings.erase(node_itor);
79 
80  if(get_size() != point()) {
81  // Don't shrink the width, need to think about a good algorithm to do so.
82  resize_content(0, -node_size.y);
83  }
84 
85  return std::pair(std::move(old_node), position);
86 }
87 
89 {
90  get_root_node().clear();
92 }
93 
94 void tree_view::set_self_active(const bool /*active*/)
95 {
96  /* DO NOTHING */
97 }
98 
99 bool tree_view::empty() const
100 {
101  return root_node_->empty();
102 }
103 
105 {
106  layout_children(false);
107 }
108 
109 void tree_view::resize_content(const int width_modification,
110  const int height_modification,
111  const int width_modification_pos,
112  const int height_modification_pos)
113 {
114  DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size() << " width_modification "
115  << width_modification << " height_modification " << height_modification << ".";
116 
118  width_modification,
119  height_modification,
120  width_modification_pos,
121  height_modification_pos
122  )) {
123  // Calculate new size.
125  size.x += width_modification;
126  size.y += height_modification;
127 
128  // Set new size.
130 
131  // Set status.
132  need_layout_ = true;
133  // If the content grows assume it "overwrites" the old content.
134  if(width_modification < 0 || height_modification < 0) {
135  queue_redraw();
136  }
138  DBG_GUI_L << LOG_HEADER << " succeeded.";
139  } else {
140  DBG_GUI_L << LOG_HEADER << " failed.";
141  }
142 }
143 
144 void tree_view::layout_children(const bool force)
145 {
146  assert(root_node_ && content_grid());
147 
148  if(need_layout_ || force) {
151 
152  need_layout_ = false;
154  }
155 }
156 
158 {
159  // Inherited.
161 
162  auto root = std::make_unique<tree_view_node>(root_node_id, nullptr, *this, widget_data{});
163  root_node_ = root.get();
164 
165  assert(content_grid());
166  content_grid()->set_rows_cols(1, 1);
169 }
170 
172 {
173  DBG_GUI_E << LOG_HEADER << ' ' << event << ".";
174 
175  get_window()->keyboard_capture(this);
176 }
177 
178 template<tree_view_node* (tree_view_node::*func)()>
180 {
182  if(!selected) {
183  return nullptr;
184  }
185 
186  tree_view_node* visible = selected->get_last_visible_parent_node();
187  if(visible != selected) {
188  return visible;
189  }
190 
191  return (selected->*func)();
192 }
193 
194 template<tree_view_node* (tree_view_node::*func)()>
196 {
197  if(tree_view_node* next = get_next_node<func>()) {
198  next->select_node();
199  SDL_Rect visible = content_visible_area();
200  SDL_Rect rect = next->get_grid().get_rectangle();
201  visible.y = rect.y; // - content_grid()->get_y();
202  visible.h = rect.h;
203  show_content_rect(visible);
204  return true;
205  }
206 
207  return false;
208 }
209 
210 void tree_view::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
211 {
212  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_above>()) {
213  handled = true;
214  } else {
215  scrollbar_container::handle_key_up_arrow(modifier, handled);
216  }
217 }
218 
219 void tree_view::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
220 {
221  if(handle_up_down_arrow<&tree_view_node::get_selectable_node_below>()) {
222  handled = true;
223  } else {
225  }
226 }
227 
228 void tree_view::handle_key_left_arrow(SDL_Keymod modifier, bool& handled)
229 {
231  if(!selected || selected->is_folded()) {
233  return;
234  }
235 
236  selected->fold();
237  handled = true;
238 }
239 
240 void tree_view::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
241 {
243  if(!selected || !selected->is_folded()) {
245  return;
246  }
247 
248  selected->unfold();
249  handled = true;
250 }
251 
252 // }---------- DEFINITION ---------{
253 
256 {
257  DBG_GUI_P << "Parsing tree view " << id;
258 
259  load_resolutions<resolution>(cfg);
260 }
261 
263  : resolution_definition(cfg)
264  , grid(nullptr)
265 {
266  // Note the order should be the same as the enum state_t is listbox.hpp.
267  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_enabled", missing_mandatory_wml_tag("tree_view_definition][resolution", "state_enabled")));
268  state.emplace_back(VALIDATE_WML_CHILD(cfg, "state_disabled", missing_mandatory_wml_tag("tree_view_definition][resolution", "state_disabled")));
269 
270  auto child = VALIDATE_WML_CHILD(cfg, "grid", missing_mandatory_wml_tag("tree_view_definition][resolution", "grid"));
271 
272  grid = std::make_shared<builder_grid>(child);
273 }
274 
275 // }---------- BUILDER -----------{
276 
277 namespace implementation
278 {
279 builder_tree_view::builder_tree_view(const config& cfg)
280  : builder_styled_widget(cfg)
281  , vertical_scrollbar_mode(get_scrollbar_mode(cfg["vertical_scrollbar_mode"]))
282  , horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
283  , indentation_step_size(cfg["indentation_step_size"].to_unsigned())
284  , nodes()
285 {
286  for(const auto& node : cfg.child_range("node")) {
287  nodes.emplace_back(node);
288  }
289 
290  VALIDATE(!nodes.empty(), _("No nodes defined for a tree view."));
291 }
292 
293 std::unique_ptr<widget> builder_tree_view::build() const
294 {
295  /*
296  * TODO see how much we can move in the constructor instead of
297  * building in several steps.
298  */
299  auto widget = std::make_unique<tree_view>(*this);
300 
301  widget->set_vertical_scrollbar_mode(vertical_scrollbar_mode);
302  widget->set_horizontal_scrollbar_mode(horizontal_scrollbar_mode);
303 
304  widget->set_indentation_step_size(indentation_step_size);
305 
306  DBG_GUI_G << "Window builder: placed tree_view '" << id << "' with definition '" << definition << "'.";
307 
308  const auto conf = widget->cast_config_to<tree_view_definition>();
309  assert(conf);
310 
311  widget->init_grid(*conf->grid);
312  widget->finalize_setup();
313 
314  return widget;
315 }
316 
318  : id(cfg["id"])
319  , unfolded(cfg["unfolded"].to_bool(false))
320  , builder(nullptr)
321 {
322  VALIDATE(!id.empty(), missing_mandatory_wml_key("node", "id"));
323 
324  // TODO: interpolate this value into the error message
325  VALIDATE(id != tree_view::root_node_id, _("[node]id ‘root’ is reserved for the implementation."));
326 
327  auto node_definition = VALIDATE_WML_CHILD(cfg, "node_definition", missing_mandatory_wml_tag("node", "node_definition"));
328  builder = std::make_shared<builder_grid>(node_definition);
329 }
330 
331 } // namespace implementation
332 
333 // }------------ END --------------
334 
335 } // namespace gui2
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
child_itors child_range(config_key_type key)
Definition: config.cpp:272
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.
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:109
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:240
virtual void set_self_active(const bool active) override
See container_base::set_self_active.
Definition: tree_view.cpp:94
static const std::string root_node_id
Definition: tree_view.hpp:129
tree_view_node * get_next_node()
Definition: tree_view.cpp:179
tree_view_node * root_node_
Definition: tree_view.hpp:143
virtual void finalize_setup()
Inherited from container_base.
Definition: tree_view.cpp:157
bool empty() const
Definition: tree_view.cpp:99
void handle_key_up_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:210
void handle_key_left_arrow(SDL_Keymod modifier, bool &handled) override
Inherited from scrollbar_container.
Definition: tree_view.cpp:228
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:171
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:219
virtual void layout_children() override
See widget::layout_children.
Definition: tree_view.cpp:104
bool handle_up_down_arrow()
Definition: tree_view.cpp:195
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:1207
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:198
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:202
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:36
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:70
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string_view data
Definition: picture.cpp:178
#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:241
scrollbar_container::scrollbar_mode vertical_scrollbar_mode
Definition: tree_view.hpp:230
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode
Definition: tree_view.hpp:231
virtual std::unique_ptr< widget > build() const override
Definition: tree_view.cpp:293
tree_node(const config &cfg)
Definition: tree_view.cpp:317
std::vector< state_definition > state
tree_view_definition(const config &cfg)
Definition: tree_view.cpp:254
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: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.