diff options
-rw-r--r-- | include/cru/ui/controls/Control.h | 4 | ||||
-rw-r--r-- | include/cru/ui/controls/TreeView.h | 41 | ||||
-rw-r--r-- | src/ui/controls/TreeView.cpp | 87 | ||||
-rw-r--r-- | src/ui/render/TreeRenderObject.cpp | 12 |
4 files changed, 141 insertions, 3 deletions
diff --git a/include/cru/ui/controls/Control.h b/include/cru/ui/controls/Control.h index 242db3f4..1d976970 100644 --- a/include/cru/ui/controls/Control.h +++ b/include/cru/ui/controls/Control.h @@ -9,9 +9,11 @@ namespace cru::ui::controls { * \remarks If you want to write a new control. You should override following * methods: * - GetControlType() + * - GetRenderObject() * - ForEachChild(const std::function<void(Control*)>& predicate) * - RemoveChild(Control* child) - * - GetRenderObject() + * The last two methods are totally for convenient control tree management. The + * former one is even not used. */ class CRU_UI_API Control : public Object { protected: diff --git a/include/cru/ui/controls/TreeView.h b/include/cru/ui/controls/TreeView.h index b36ca553..6c3c360b 100644 --- a/include/cru/ui/controls/TreeView.h +++ b/include/cru/ui/controls/TreeView.h @@ -1,5 +1,6 @@ #pragma once #include "Control.h" +#include "cru/ui/render/TreeRenderObject.h" namespace cru::ui::controls { class TreeView; @@ -7,31 +8,67 @@ class TreeView; class CRU_UI_API TreeViewItem : public Object { friend TreeView; + private: + TreeViewItem(TreeView* tree_view, TreeViewItem* parent, + render::TreeRenderObjectItem* render_object_item); + ~TreeViewItem() override; + public: - TreeViewItem(TreeView* tree_view, TreeViewItem* parent); CRU_DELETE_COPY(TreeViewItem) CRU_DELETE_MOVE(TreeViewItem) - ~TreeViewItem() override; TreeView* GetTreeView() { return tree_view_; } TreeViewItem* GetParent() { return parent_; } + render::TreeRenderObjectItem* GetRenderObjectItem() const { + return render_object_item_; + } + const std::vector<TreeViewItem*>& GetChildren() const { return children_; } Index GetChildCount() const { return children_.size(); } TreeViewItem* GetChildAt(Index index) { Expects(index >= 0 && index < children_.size()); return children_[index]; } + Index IndexOf(TreeViewItem* item) const; TreeViewItem* AddItem(Index position); void RemoveItem(Index position); + void RemoveFromParent(); + + Control* GetControl() const { return control_; } + void SetControl(Control* control); + + void TraverseDescendants(std::function<void(TreeViewItem*)> callback); private: TreeView* tree_view_; + render::TreeRenderObjectItem* render_object_item_; TreeViewItem* parent_; std::vector<TreeViewItem*> children_; Control* control_; }; +class CRU_UI_API TreeView : public Control { + public: + constexpr static StringView kControlType = u"TreeView"; + + TreeView(); + CRU_DELETE_COPY(TreeView) + CRU_DELETE_MOVE(TreeView) + ~TreeView() override; + + String GetControlType() const override { return kControlType.ToString(); } + render::TreeRenderObject* GetRenderObject() { return &render_object_; } + + void ForEachChild(const std::function<void(Control*)>& predicate) override; + void RemoveChild(Control* control) override; + + TreeViewItem* GetRootItem() { return &root_item_; } + + private: + render::TreeRenderObject render_object_; + TreeViewItem root_item_; +}; } // namespace cru::ui::controls diff --git a/src/ui/controls/TreeView.cpp b/src/ui/controls/TreeView.cpp index e69de29b..659aef00 100644 --- a/src/ui/controls/TreeView.cpp +++ b/src/ui/controls/TreeView.cpp @@ -0,0 +1,87 @@ +#include "cru/ui/controls/TreeView.h" + +namespace cru::ui::controls { +TreeViewItem::TreeViewItem(TreeView* tree_view, TreeViewItem* parent, + render::TreeRenderObjectItem* render_object_item) + : tree_view_(tree_view), + parent_(parent), + render_object_item_(render_object_item) {} + +TreeViewItem::~TreeViewItem() { + if (control_) { + control_->SetParent(nullptr); + } + + for (auto item : children_) { + delete item; + } +} + +Index TreeViewItem::IndexOf(TreeViewItem* item) const { + auto result = std::find(children_.begin(), children_.end(), item); + if (result == children_.end()) { + return -1; + } + return result - children_.begin(); +} + +void TreeViewItem::RemoveFromParent() { + if (parent_ == nullptr) return; + parent_->RemoveItem(parent_->IndexOf(this)); +} + +TreeViewItem* TreeViewItem::AddItem(Index position) { + auto render_object_item = render_object_item_->AddItem(position); + auto item = new TreeViewItem(tree_view_, this, render_object_item); + children_.insert(children_.begin() + position, item); + return item; +} + +void TreeViewItem::RemoveItem(Index position) { + Expects(position >= 0 && position < children_.size()); + delete children_[position]; + children_.erase(children_.begin() + position); + render_object_item_->RemoveItem(position); +} + +void TreeViewItem::SetControl(Control* control) { + if (control_) { + control_->SetParent(nullptr); + render_object_item_->SetRenderObject(nullptr); + } + control_ = control; + if (control) { + control->SetParent(tree_view_); + render_object_item_->SetRenderObject(control->GetRenderObject()); + } +} + +void TreeViewItem::TraverseDescendants( + std::function<void(TreeViewItem*)> callback) { + callback(this); + for (auto item : children_) { + TraverseDescendants(callback); + } +} + +TreeView::TreeView() + : root_item_(this, nullptr, render_object_.GetRootItem()) {} + +TreeView::~TreeView() {} + +void TreeView::ForEachChild(const std::function<void(Control*)>& predicate) { + root_item_.TraverseDescendants([&predicate](TreeViewItem* item) { + if (auto control = item->GetControl()) { + predicate(control); + } + }); +} + +void TreeView::RemoveChild(Control* control) { + root_item_.TraverseDescendants([&control](TreeViewItem* item) { + if (item->GetControl() == control) { + item->SetControl(nullptr); + } + }); +} +} // namespace cru::ui::controls diff --git a/src/ui/render/TreeRenderObject.cpp b/src/ui/render/TreeRenderObject.cpp index 9bf74535..8bf662e4 100644 --- a/src/ui/render/TreeRenderObject.cpp +++ b/src/ui/render/TreeRenderObject.cpp @@ -9,6 +9,11 @@ TreeRenderObjectItem::TreeRenderObjectItem(TreeRenderObject* tree_render_object, : tree_render_object_(tree_render_object), parent_(parent) {} TreeRenderObjectItem::~TreeRenderObjectItem() { + if (render_object_) { + render_object_->SetParent(nullptr); + render_object_ = nullptr; + } + for (auto child : children_) { delete child; } @@ -16,7 +21,14 @@ TreeRenderObjectItem::~TreeRenderObjectItem() { void TreeRenderObjectItem::SetRenderObject(RenderObject* render_object) { if (render_object == render_object_) return; + if (render_object_) { + render_object_->SetParent(nullptr); + } render_object_ = render_object; + if (render_object) { + assert(render_object->GetParent() == nullptr); + render_object->SetParent(tree_render_object_); + } tree_render_object_->InvalidateLayout(); } |