blob: c66e30668158ee574e518220bed4bff17cb2ca7c [file] [log] [blame]
[email protected]3cb676a12012-06-30 15:46:031// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]44cbd9e2011-01-14 15:49:405#ifndef UI_BASE_MODELS_TREE_NODE_MODEL_H_
6#define UI_BASE_MODELS_TREE_NODE_MODEL_H_
initial.commit09911bf2008-07-26 23:55:297
avi20f6a6d532015-12-23 08:05:248#include <stddef.h>
9
[email protected]775cfd762008-12-09 17:12:1210#include <algorithm>
danakj25c52c32016-04-12 21:51:0811#include <memory>
Mikel Astize83917a2022-01-27 20:21:0312#include <set>
Jan Wilken Dörriead587c32021-03-11 14:09:2713#include <string>
initial.commit09911bf2008-07-26 23:55:2914#include <vector>
15
Hans Wennborg4b46b3b2020-06-23 08:29:0816#include "base/check_op.h"
Keishi Hattori0e45c022021-11-27 09:25:5217#include "base/memory/raw_ptr.h"
[email protected]0457c6b2010-02-10 18:44:4818#include "base/observer_list.h"
[email protected]44cbd9e2011-01-14 15:49:4019#include "ui/base/models/tree_model.h"
20
21namespace ui {
initial.commit09911bf2008-07-26 23:55:2922
23// TreeNodeModel and TreeNodes provide an implementation of TreeModel around
[email protected]23db9f72011-03-11 19:43:2424// TreeNodes.
initial.commit09911bf2008-07-26 23:55:2925//
26// TreeNodes own their children, so that deleting a node deletes all
27// descendants.
28//
29// TreeNodes do NOT maintain a pointer back to the model. As such, if you
30// are using TreeNodes with a TreeNodeModel you will need to notify the observer
31// yourself any time you make any change directly to the TreeNodes. For example,
[email protected]23db9f72011-03-11 19:43:2432// if you directly invoke set_title on a node it does not notify the observer,
33// you will need to do it yourself. This includes the following methods: Add,
34// Remove and set_title. TreeNodeModel provides cover methods that mutate the
35// TreeNodes and notify the observer. If you are using TreeNodes with a
36// TreeNodeModel use the cover methods to save yourself the headache.
initial.commit09911bf2008-07-26 23:55:2937//
38// The following example creates a TreeNode with two children and then
39// creates a TreeNodeModel from it:
40//
avicee78e4e2016-09-30 17:08:1441// std::unique_ptr<TreeNodeWithValue<int>> root =
Jeremy Romanb4600572017-10-17 01:31:5042// std::make_unique<TreeNodeWithValue<int>>();
avicee78e4e2016-09-30 17:08:1443// root->Add(
Jan Wilken Dörrie8aeb5742021-03-23 19:27:0244// std::make_unique<TreeNodeWithValue<int>>(u"child 1", 0));
avicee78e4e2016-09-30 17:08:1445// root->Add(
Jan Wilken Dörrie8aeb5742021-03-23 19:27:0246// std::make_unique<TreeNodeWithValue<int>>(u"child 2", 1));
avicee78e4e2016-09-30 17:08:1447// TreeNodeModel<TreeNodeWithValue<int>> model(std::move(root));
initial.commit09911bf2008-07-26 23:55:2948//
49// Two variants of TreeNode are provided here:
50//
51// . TreeNode itself is intended for subclassing. It has one type parameter
52// that corresponds to the type of the node. When subclassing use your class
avicee78e4e2016-09-30 17:08:1453// name as the type parameter, e.g.:
initial.commit09911bf2008-07-26 23:55:2954// class MyTreeNode : public TreeNode<MyTreeNode> .
55// . TreeNodeWithValue is a trivial subclass of TreeNode that has one type
56// type parameter: a value type that is associated with the node.
57//
58// Which you use depends upon the situation. If you want to subclass and add
59// methods, then use TreeNode. If you don't need any extra methods and just
60// want to associate a value with each node, then use TreeNodeWithValue.
61//
62// Regardless of which TreeNode you use, if you are using the nodes with a
63// TreeView take care to notify the observer when mutating the nodes.
64
initial.commit09911bf2008-07-26 23:55:2965// TreeNode -------------------------------------------------------------------
66
Brett Wilson05c93882017-12-12 23:06:1067// See above for documentation. Example:
68//
69// class MyNode : public ui::TreeNode<MyNode> {
70// ...<custom class logic>...
71// };
72// using MyModel = ui::TreeNodeModel<MyNode>;
initial.commit09911bf2008-07-26 23:55:2973template <class NodeType>
74class TreeNode : public TreeModelNode {
75 public:
Peter Kastingfc86fec62019-05-21 20:43:4776 using TreeNodes = std::vector<std::unique_ptr<NodeType>>;
77
avicee78e4e2016-09-30 17:08:1478 TreeNode() : parent_(nullptr) {}
initial.commit09911bf2008-07-26 23:55:2979
Jan Wilken Dörrie52639572021-03-11 16:49:5480 explicit TreeNode(const std::u16string& title)
avicee78e4e2016-09-30 17:08:1481 : title_(title), parent_(nullptr) {}
initial.commit09911bf2008-07-26 23:55:2982
Peter Boströmc8c12352021-09-21 23:37:1583 TreeNode(const TreeNode&) = delete;
84 TreeNode& operator=(const TreeNode&) = delete;
85
avicee78e4e2016-09-30 17:08:1486 ~TreeNode() override {}
initial.commit09911bf2008-07-26 23:55:2987
avicee78e4e2016-09-30 17:08:1488 // Adds |node| as a child of this node, at |index|. Returns a raw pointer to
89 // the node.
Peter Kasting8cf4c23e2019-06-06 00:38:3090 NodeType* Add(std::unique_ptr<NodeType> node, size_t index) {
[email protected]a0dd6a32011-03-18 17:31:3791 DCHECK(node);
Peter Kasting8cf4c23e2019-06-06 00:38:3092 DCHECK_LE(index, children_.size());
avicee78e4e2016-09-30 17:08:1493 DCHECK(!node->parent_);
[email protected]a0dd6a32011-03-18 17:31:3794 node->parent_ = static_cast<NodeType*>(this);
avicee78e4e2016-09-30 17:08:1495 NodeType* node_ptr = node.get();
Peter Kastingd2a681f2022-06-24 23:08:0596 children_.insert(children_.begin() + static_cast<ptrdiff_t>(index),
97 std::move(node));
avicee78e4e2016-09-30 17:08:1498 return node_ptr;
initial.commit09911bf2008-07-26 23:55:2999 }
100
Peter Kasting4ea13a52019-06-04 01:17:06101 // Shorthand for "add at end".
102 NodeType* Add(std::unique_ptr<NodeType> node) {
Peter Kasting8cf4c23e2019-06-06 00:38:30103 return Add(std::move(node), children_.size());
Peter Kasting4ea13a52019-06-04 01:17:06104 }
105
Brett Wilson05c93882017-12-12 23:06:10106 // Removes the node at the given index. Returns the removed node.
Peter Kasting8cf4c23e2019-06-06 00:38:30107 std::unique_ptr<NodeType> Remove(size_t index) {
108 DCHECK_LT(index, children_.size());
Brett Wilson05c93882017-12-12 23:06:10109 children_[index]->parent_ = nullptr;
110 std::unique_ptr<NodeType> ptr = std::move(children_[index]);
Peter Kastingd2a681f2022-06-24 23:08:05111 children_.erase(children_.begin() + static_cast<ptrdiff_t>(index));
Brett Wilson05c93882017-12-12 23:06:10112 return ptr;
113 }
114
avicee78e4e2016-09-30 17:08:14115 // Removes all the children from this node.
116 void DeleteAll() { children_.clear(); }
[email protected]58b359d2009-02-27 22:05:08117
avicee78e4e2016-09-30 17:08:14118 // Returns the parent node, or nullptr if this is the root node.
[email protected]26b2cb722011-06-07 16:35:09119 const NodeType* parent() const { return parent_; }
120 NodeType* parent() { return parent_; }
121
122 // Returns true if this is the root node.
avicee78e4e2016-09-30 17:08:14123 bool is_root() const { return parent_ == nullptr; }
[email protected]26b2cb722011-06-07 16:35:09124
Peter Kastingfc86fec62019-05-21 20:43:47125 const TreeNodes& children() const { return children_; }
initial.commit09911bf2008-07-26 23:55:29126
[email protected]18c917e2011-06-01 16:05:56127 // Returns the number of all nodes in the subtree rooted at this node,
[email protected]9ff22ee42009-10-25 06:03:03128 // including this node.
Peter Kasting070111cf2022-07-27 00:19:02129 size_t GetTotalNodeCount() const {
130 size_t count = 1; // Start with one to include the node itself.
avicee78e4e2016-09-30 17:08:14131 for (const auto& child : children_)
132 count += child->GetTotalNodeCount();
[email protected]9ff22ee42009-10-25 06:03:03133 return count;
134 }
135
Peter Kasting070111cf2022-07-27 00:19:02136 // Returns the index of |node|, or nullopt if |node| is not a child of this.
137 absl::optional<size_t> GetIndexOf(const NodeType* node) const {
initial.commit09911bf2008-07-26 23:55:29138 DCHECK(node);
avicee78e4e2016-09-30 17:08:14139 auto i = std::find_if(children_.begin(), children_.end(),
140 [node](const std::unique_ptr<NodeType>& ptr) {
141 return ptr.get() == node;
142 });
Peter Kasting070111cf2022-07-27 00:19:02143 return i != children_.end()
144 ? absl::make_optional(static_cast<size_t>(i - children_.begin()))
145 : absl::nullopt;
initial.commit09911bf2008-07-26 23:55:29146 }
147
148 // Sets the title of the node.
Jan Wilken Dörrie52639572021-03-11 16:49:54149 virtual void SetTitle(const std::u16string& title) { title_ = title; }
initial.commit09911bf2008-07-26 23:55:29150
[email protected]23db9f72011-03-11 19:43:24151 // TreeModelNode:
Jan Wilken Dörrie52639572021-03-11 16:49:54152 const std::u16string& GetTitle() const override { return title_; }
initial.commit09911bf2008-07-26 23:55:29153
Elaine Chienec39da12022-07-20 16:59:06154 const std::u16string& GetAccessibleTitle() const override {
155 return title_.empty() ? placeholder_accessible_title_ : title_;
156 }
157
158 void SetPlaceholderAccessibleTitle(
159 std::u16string placeholder_accessible_title) {
160 placeholder_accessible_title_ = placeholder_accessible_title;
161 }
162
initial.commit09911bf2008-07-26 23:55:29163 // Returns true if this == ancestor, or one of this nodes parents is
164 // ancestor.
[email protected]b3c33d462009-06-26 22:29:20165 bool HasAncestor(const NodeType* ancestor) const {
initial.commit09911bf2008-07-26 23:55:29166 if (ancestor == this)
167 return true;
168 if (!ancestor)
169 return false;
170 return parent_ ? parent_->HasAncestor(ancestor) : false;
171 }
172
Mikel Astize83917a2022-01-27 20:21:03173 // Reorders children according to a new arbitrary order. |new_order| must
174 // contain one entry per child node, and the value of the entry at position
175 // |i| represents the new position, which must be unique and in the range
176 // between 0 (inclusive) and the number of children (exclusive).
177 void ReorderChildren(const std::vector<size_t>& new_order) {
178 const size_t children_count = children_.size();
179 DCHECK_EQ(children_count, new_order.size());
180 DCHECK_EQ(children_count,
181 std::set(new_order.begin(), new_order.end()).size());
182 TreeNodes new_children(children_count);
183 for (size_t old_index = 0; old_index < children_count; ++old_index) {
184 const size_t new_index = new_order[old_index];
185 DCHECK_LT(new_index, children_count);
186 DCHECK(children_[old_index]);
187 DCHECK(!new_children[new_index]);
188 new_children[new_index] = std::move(children_[old_index]);
189 }
190 children_ = std::move(new_children);
191 }
Peter Kastingfc86fec62019-05-21 20:43:47192
Mikel Astize83917a2022-01-27 20:21:03193 // Sorts children according to a comparator.
194 template <typename Compare>
195 void SortChildren(Compare comp) {
196 std::stable_sort(children_.begin(), children_.end(), comp);
197 }
198
199 private:
initial.commit09911bf2008-07-26 23:55:29200 // Title displayed in the tree.
Jan Wilken Dörrie52639572021-03-11 16:49:54201 std::u16string title_;
initial.commit09911bf2008-07-26 23:55:29202
Elaine Chienec39da12022-07-20 16:59:06203 // If set, a placeholder accessible title to fall back to if there is no
204 // title.
205 std::u16string placeholder_accessible_title_;
206
[email protected]9c1a75a2011-03-10 02:38:12207 // This node's parent.
Keishi Hattori0e45c022021-11-27 09:25:52208 raw_ptr<NodeType> parent_;
initial.commit09911bf2008-07-26 23:55:29209
[email protected]9c1a75a2011-03-10 02:38:12210 // This node's children.
Peter Kastingfc86fec62019-05-21 20:43:47211 TreeNodes children_;
initial.commit09911bf2008-07-26 23:55:29212};
213
214// TreeNodeWithValue ----------------------------------------------------------
215
Brett Wilson05c93882017-12-12 23:06:10216// See top of file for documentation. Example:
217//
218// using MyNode = ui::TreeNodeWithValue<MyData>;
219// using MyModel = ui::TreeNodeModel<MyNode>;
initial.commit09911bf2008-07-26 23:55:29220template <class ValueType>
avicee78e4e2016-09-30 17:08:14221class TreeNodeWithValue : public TreeNode<TreeNodeWithValue<ValueType>> {
initial.commit09911bf2008-07-26 23:55:29222 public:
[email protected]23db9f72011-03-11 19:43:24223 TreeNodeWithValue() {}
initial.commit09911bf2008-07-26 23:55:29224
[email protected]a5b58f52009-11-17 22:15:44225 explicit TreeNodeWithValue(const ValueType& value)
Jan Wilken Dörrie52639572021-03-11 16:49:54226 : ParentType(std::u16string()), value(value) {}
initial.commit09911bf2008-07-26 23:55:29227
Jan Wilken Dörrie52639572021-03-11 16:49:54228 TreeNodeWithValue(const std::u16string& title, const ValueType& value)
[email protected]23db9f72011-03-11 19:43:24229 : ParentType(title), value(value) {}
initial.commit09911bf2008-07-26 23:55:29230
Peter Boström03d27022021-09-27 19:45:39231 TreeNodeWithValue(const TreeNodeWithValue&) = delete;
232 TreeNodeWithValue& operator=(const TreeNodeWithValue&) = delete;
233
initial.commit09911bf2008-07-26 23:55:29234 ValueType value;
235
236 private:
avicee78e4e2016-09-30 17:08:14237 using ParentType = TreeNode<TreeNodeWithValue<ValueType>>;
initial.commit09911bf2008-07-26 23:55:29238};
239
240// TreeNodeModel --------------------------------------------------------------
241
242// TreeModel implementation intended to be used with TreeNodes.
243template <class NodeType>
244class TreeNodeModel : public TreeModel {
245 public:
avicee78e4e2016-09-30 17:08:14246 // Creates a TreeNodeModel with the specified root node.
247 explicit TreeNodeModel(std::unique_ptr<NodeType> root)
248 : root_(std::move(root)) {}
Peter Boströmc8c12352021-09-21 23:37:15249
250 TreeNodeModel(const TreeNodeModel&) = delete;
251 TreeNodeModel& operator=(const TreeNodeModel&) = delete;
252
nick9530326e82015-04-23 17:38:09253 virtual ~TreeNodeModel() override {}
initial.commit09911bf2008-07-26 23:55:29254
Peter Kastingf094ef92019-05-25 01:55:57255 static NodeType* AsNode(TreeModelNode* model_node) {
[email protected]dce51622009-11-06 04:58:48256 return static_cast<NodeType*>(model_node);
initial.commit09911bf2008-07-26 23:55:29257 }
Peter Kastingf094ef92019-05-25 01:55:57258 static const NodeType* AsNode(const TreeModelNode* model_node) {
259 return static_cast<const NodeType*>(model_node);
260 }
initial.commit09911bf2008-07-26 23:55:29261
Peter Kasting8cf4c23e2019-06-06 00:38:30262 NodeType* Add(NodeType* parent,
263 std::unique_ptr<NodeType> node,
264 size_t index) {
Peter Kasting4d4ded12019-06-05 22:53:00265 DCHECK(parent);
266 DCHECK(node);
avicee78e4e2016-09-30 17:08:14267 NodeType* node_ptr = parent->Add(std::move(node), index);
initial.commit09911bf2008-07-26 23:55:29268 NotifyObserverTreeNodesAdded(parent, index, 1);
avicee78e4e2016-09-30 17:08:14269 return node_ptr;
initial.commit09911bf2008-07-26 23:55:29270 }
271
Peter Kasting4ea13a52019-06-04 01:17:06272 // Shorthand for "add at end".
273 NodeType* Add(NodeType* parent, std::unique_ptr<NodeType> node) {
Peter Kasting8cf4c23e2019-06-06 00:38:30274 return Add(parent, std::move(node), parent->children().size());
Peter Kasting4ea13a52019-06-04 01:17:06275 }
276
Peter Kasting8cf4c23e2019-06-06 00:38:30277 std::unique_ptr<NodeType> Remove(NodeType* parent, size_t index) {
initial.commit09911bf2008-07-26 23:55:29278 DCHECK(parent);
Brett Wilson05c93882017-12-12 23:06:10279 std::unique_ptr<NodeType> owned_node = parent->Remove(index);
initial.commit09911bf2008-07-26 23:55:29280 NotifyObserverTreeNodesRemoved(parent, index, 1);
avicee78e4e2016-09-30 17:08:14281 return owned_node;
initial.commit09911bf2008-07-26 23:55:29282 }
283
Brett Wilson05c93882017-12-12 23:06:10284 std::unique_ptr<NodeType> Remove(NodeType* parent, NodeType* node) {
285 DCHECK(parent);
Peter Kasting070111cf2022-07-27 00:19:02286 return Remove(parent, parent->GetIndexOf(node).value());
Brett Wilson05c93882017-12-12 23:06:10287 }
288
Peter Kasting8cf4c23e2019-06-06 00:38:30289 void NotifyObserverTreeNodesAdded(NodeType* parent,
290 size_t start,
291 size_t count) {
tfarinab5319f192016-10-14 00:35:09292 for (TreeModelObserver& observer : observer_list_)
293 observer.TreeNodesAdded(this, parent, start, count);
initial.commit09911bf2008-07-26 23:55:29294 }
295
Peter Kasting8cf4c23e2019-06-06 00:38:30296 void NotifyObserverTreeNodesRemoved(NodeType* parent,
297 size_t start,
298 size_t count) {
tfarinab5319f192016-10-14 00:35:09299 for (TreeModelObserver& observer : observer_list_)
300 observer.TreeNodesRemoved(this, parent, start, count);
initial.commit09911bf2008-07-26 23:55:29301 }
302
[email protected]23db9f72011-03-11 19:43:24303 void NotifyObserverTreeNodeChanged(TreeModelNode* node) {
tfarinab5319f192016-10-14 00:35:09304 for (TreeModelObserver& observer : observer_list_)
305 observer.TreeNodeChanged(this, node);
initial.commit09911bf2008-07-26 23:55:29306 }
307
[email protected]23db9f72011-03-11 19:43:24308 // TreeModel:
Brett Wilson05c93882017-12-12 23:06:10309
310 // C++ allows one to override a base class' virtual function with one that
311 // returns a different type, as long as that type implements the base class'
312 // return type. This is convenient because it allows callers with references
313 // to the specific TreeNodeModel to get the proper return type without
314 // casting.
315 //
316 // However, this does require that the NodeType be defined when this is
317 // parsed (normally one could forward define this).
nick9530326e82015-04-23 17:38:09318 NodeType* GetRoot() override {
[email protected]23db9f72011-03-11 19:43:24319 return root_.get();
320 }
321
Peter Kastingf094ef92019-05-25 01:55:57322 Nodes GetChildren(const TreeModelNode* parent) const override {
[email protected]23db9f72011-03-11 19:43:24323 DCHECK(parent);
Peter Kastingf094ef92019-05-25 01:55:57324 const auto& children = AsNode(parent)->children();
325 Nodes nodes;
326 nodes.reserve(children.size());
327 std::transform(children.cbegin(), children.cend(),
328 std::back_inserter(nodes),
329 [](const auto& child) { return child.get(); });
330 return nodes;
[email protected]23db9f72011-03-11 19:43:24331 }
332
Peter Kasting070111cf2022-07-27 00:19:02333 absl::optional<size_t> GetIndexOf(TreeModelNode* parent,
334 TreeModelNode* child) const override {
[email protected]23db9f72011-03-11 19:43:24335 DCHECK(parent);
336 return AsNode(parent)->GetIndexOf(AsNode(child));
337 }
338
Peter Kastingf094ef92019-05-25 01:55:57339 TreeModelNode* GetParent(TreeModelNode* node) const override {
[email protected]23db9f72011-03-11 19:43:24340 DCHECK(node);
341 return AsNode(node)->parent();
342 }
343
nick9530326e82015-04-23 17:38:09344 void AddObserver(TreeModelObserver* observer) override {
[email protected]23db9f72011-03-11 19:43:24345 observer_list_.AddObserver(observer);
346 }
347
nick9530326e82015-04-23 17:38:09348 void RemoveObserver(TreeModelObserver* observer) override {
[email protected]23db9f72011-03-11 19:43:24349 observer_list_.RemoveObserver(observer);
350 }
351
Jan Wilken Dörrie52639572021-03-11 16:49:54352 void SetTitle(TreeModelNode* node, const std::u16string& title) override {
[email protected]23db9f72011-03-11 19:43:24353 DCHECK(node);
[email protected]0491ff72011-12-30 00:45:59354 AsNode(node)->SetTitle(title);
[email protected]23db9f72011-03-11 19:43:24355 NotifyObserverTreeNodeChanged(node);
356 }
[email protected]0457c6b2010-02-10 18:44:48357
initial.commit09911bf2008-07-26 23:55:29358 private:
[email protected]0457c6b2010-02-10 18:44:48359 // The observers.
Trent Apteda250ec3ab2018-08-19 08:52:19360 base::ObserverList<TreeModelObserver>::Unchecked observer_list_;
[email protected]23db9f72011-03-11 19:43:24361
initial.commit09911bf2008-07-26 23:55:29362 // The root.
danakj25c52c32016-04-12 21:51:08363 std::unique_ptr<NodeType> root_;
initial.commit09911bf2008-07-26 23:55:29364};
365
[email protected]44cbd9e2011-01-14 15:49:40366} // namespace ui
367
368#endif // UI_BASE_MODELS_TREE_NODE_MODEL_H_