diff options
-rw-r--r-- | demos/main/main.cpp | 8 | ||||
-rw-r--r-- | demos/scroll_view/main.cpp | 2 | ||||
-rw-r--r-- | include/cru/ui/controls/Control.h | 11 | ||||
-rw-r--r-- | include/cru/ui/controls/LayoutControl.h | 32 | ||||
-rw-r--r-- | include/cru/ui/controls/NoChildControl.h | 2 | ||||
-rw-r--r-- | include/cru/ui/controls/SingleChildControl.h | 8 | ||||
-rw-r--r-- | include/cru/ui/render/LayoutRenderObject.h | 12 | ||||
-rw-r--r-- | include/cru/ui/render/RenderObject.h | 59 | ||||
-rw-r--r-- | src/theme_builder/components/MainWindow.cpp | 6 | ||||
-rw-r--r-- | src/ui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/ui/components/Menu.cpp | 6 | ||||
-rw-r--r-- | src/ui/controls/Control.cpp | 10 | ||||
-rw-r--r-- | src/ui/controls/NoChildControl.cpp | 2 | ||||
-rw-r--r-- | src/ui/render/TextRenderObject.cpp | 5 |
14 files changed, 111 insertions, 53 deletions
diff --git a/demos/main/main.cpp b/demos/main/main.cpp index 41320858..a45b7185 100644 --- a/demos/main/main.cpp +++ b/demos/main/main.cpp @@ -40,19 +40,19 @@ int main() { flex_layout->SetContentMainAlign(FlexCrossAlignment::Center); flex_layout->SetItemCrossAlign(FlexCrossAlignment::Center); - window->AddChild(flex_layout, 0); + window->AddChildAt(flex_layout, 0); const auto text_block = TextBlock::Create(u"Hello World from CruUI!", true); - flex_layout->AddChild(text_block, 0); + flex_layout->AddChildAt(text_block, 0); const auto button_text_block = TextBlock::Create(u"OK"); const auto button = Button::Create(); button->SetChild(button_text_block); - flex_layout->AddChild(button, 1); + flex_layout->AddChildAt(button, 1); const auto text_box = TextBox::Create(); text_box->SetMultiLine(true); - flex_layout->AddChild(text_box, 2); + flex_layout->AddChildAt(text_box, 2); auto popup_menu = std::make_unique<cru::ui::components::PopupMenu>(window); popup_menu->GetMenu()->AddTextItem(u"Item 1", [] {}); diff --git a/demos/scroll_view/main.cpp b/demos/scroll_view/main.cpp index fa693cf3..d859ce3a 100644 --- a/demos/scroll_view/main.cpp +++ b/demos/scroll_view/main.cpp @@ -17,7 +17,7 @@ int main() { auto scroll_view = ScrollView::Create(); - window->AddChild(scroll_view, 0); + window->AddChild(scroll_view); auto text_block = TextBlock::Create( uR"([Verse 1] diff --git a/include/cru/ui/controls/Control.h b/include/cru/ui/controls/Control.h index 9a237f8b..242db3f4 100644 --- a/include/cru/ui/controls/Control.h +++ b/include/cru/ui/controls/Control.h @@ -10,6 +10,7 @@ namespace cru::ui::controls { * methods: * - GetControlType() * - ForEachChild(const std::function<void(Control*)>& predicate) + * - RemoveChild(Control* child) * - GetRenderObject() */ class CRU_UI_API Control : public Object { @@ -35,6 +36,14 @@ class CRU_UI_API Control : public Object { virtual void ForEachChild(const std::function<void(Control*)>& predicate) = 0; + /** + * \remarks This method should be permissive, which means if the specified + * child control is not a real child of this then nothing will be done. + */ + virtual void RemoveChild(Control* child) = 0; + + void RemoveFromParent(); + public: virtual render::RenderObject* GetRenderObject() const = 0; @@ -129,7 +138,7 @@ class CRU_UI_API Control : public Object { //*************** region: tree *************** protected: - virtual void OnParentChanged(Control* old_parent, Control* new_parent); + virtual void OnParentChanged(Control* old_parent, Control* new_parent) {} protected: virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) } diff --git a/include/cru/ui/controls/LayoutControl.h b/include/cru/ui/controls/LayoutControl.h index b42cfc9a..59ffaee2 100644 --- a/include/cru/ui/controls/LayoutControl.h +++ b/include/cru/ui/controls/LayoutControl.h @@ -17,12 +17,30 @@ class CRU_UI_API LayoutControl : public Control { ~LayoutControl() override = default; public: + const std::vector<Control*>& GetChildren() const { return children_; } + Index GetChildCount() const { return children_.size(); } + Control* GetChild(Index index) const { return children_[index]; } + Index IndexOf(Control* control) const { + auto it = std::find(children_.begin(), children_.end(), control); + if (it == children_.end()) { + return -1; + } + return it - children_.begin(); + } + void ForEachChild(const std::function<void(Control*)>& callback) override { for (auto child : children_) { callback(child); } } + void RemoveChild(Control* child) override { + auto index = IndexOf(child); + if (index != -1) { + RemoveChildAt(index); + } + } + render::RenderObject* GetRenderObject() const override { return container_render_object_.get(); } @@ -31,7 +49,7 @@ class CRU_UI_API LayoutControl : public Control { return container_render_object_.get(); } - void AddChild(Control* child, Index position) { + void AddChildAt(Control* child, Index position) { Expects(child); Expects(child->GetParent() == nullptr); if (position < 0) position = 0; @@ -43,7 +61,9 @@ class CRU_UI_API LayoutControl : public Control { container_render_object_->AddChild(child->GetRenderObject(), position); } - void RemoveChild(Index position) { + void AddChild(Control* child) { AddChildAt(child, GetChildCount()); } + + void RemoveChildAt(Index position) { if (position < 0 || position >= children_.size()) return; auto child = children_[position]; children_.erase(children_.begin() + position); @@ -51,6 +71,14 @@ class CRU_UI_API LayoutControl : public Control { container_render_object_->RemoveChild(position); } + void ClearChildren() { + for (auto child : children_) { + child->SetParent(nullptr); + } + children_.clear(); + container_render_object_->ClearChildren(); + } + const typename TRenderObject::ChildLayoutData& GetChildLayoutData( Index position) { return container_render_object_->GetChildLayoutDataAt(position); diff --git a/include/cru/ui/controls/NoChildControl.h b/include/cru/ui/controls/NoChildControl.h index 1b864b20..f22fd85e 100644 --- a/include/cru/ui/controls/NoChildControl.h +++ b/include/cru/ui/controls/NoChildControl.h @@ -15,5 +15,7 @@ class CRU_UI_API NoChildControl : public Control { public: void ForEachChild(const std::function<void(Control*)>& callback) override; + + void RemoveChild(Control* child) override; }; } // namespace cru::ui::controls diff --git a/include/cru/ui/controls/SingleChildControl.h b/include/cru/ui/controls/SingleChildControl.h index 037d6dd3..d40d7a27 100644 --- a/include/cru/ui/controls/SingleChildControl.h +++ b/include/cru/ui/controls/SingleChildControl.h @@ -48,8 +48,14 @@ class CRU_UI_API SingleChildControl : public Control { } } + void RemoveChild(Control* child) override { + if (child_ == child) { + SetChild(nullptr); + } + } + private: - Control* child_; + Control* child_ = nullptr; std::unique_ptr<TRenderObject> container_render_object_; }; } // namespace cru::ui::controls diff --git a/include/cru/ui/render/LayoutRenderObject.h b/include/cru/ui/render/LayoutRenderObject.h index d6882f9a..c8678deb 100644 --- a/include/cru/ui/render/LayoutRenderObject.h +++ b/include/cru/ui/render/LayoutRenderObject.h @@ -26,7 +26,7 @@ class CRU_UI_API LayoutRenderObject : public RenderObject { Index GetChildCount() const { return static_cast<Index>(children_.size()); } RenderObject* GetChildAt(Index position) { - Expects(position > 0 && position < GetChildCount()); + Expects(position >= 0 && position < GetChildCount()); return children_[position].render_object; } void AddChild(RenderObject* render_object, Index position) { @@ -35,12 +35,22 @@ class CRU_UI_API LayoutRenderObject : public RenderObject { children_.insert(children_.begin() + position, ChildData{render_object, ChildLayoutData()}); render_object->SetParent(this); + InvalidateLayout(); } void RemoveChild(Index position) { Expects(position > 0 && position < GetChildCount()); children_[position].render_object->SetParent(nullptr); children_.erase(children_.begin() + position); + InvalidateLayout(); + } + + void ClearChildren() { + for (auto child : children_) { + child.render_object->SetParent(nullptr); + } + children_.clear(); + InvalidateLayout(); } const ChildLayoutData& GetChildLayoutDataAt(Index position) const { diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h index cfa72b7d..bef2859e 100644 --- a/include/cru/ui/render/RenderObject.h +++ b/include/cru/ui/render/RenderObject.h @@ -5,34 +5,36 @@ #include "cru/common/String.h" namespace cru::ui::render { -// Render object will not destroy its children when destroyed. Control must -// manage lifecycle of its render objects. Since control will destroy its -// children when destroyed, render objects will be destroyed along with it. -// -// About layout: -// Render object should be able to deal with arbitrary size as the result of -// measure and layout. -// -// Parent may pass calculated preferred size down. But the preferred size set on -// child itself takes precedence. -// -// Each render object should obey the measure requirement to set size and report -// a warning when that requirement can't be satisfied with probably bigger size -// of children than that of itself and optional visual effect to indicate that. -// -// If size of chilren are less than min size requirement, then render object -// should try to fill the rest area. If size of children are more than max size -// requirement, then render object should display a warning of the layout -// problem and use the max size of itself with children exceeding its bound. -// (Or better you could use some visual effect to indicate that.) -// -// To write a custom RenderObject, override following methods: -// public: -// void Draw(platform::graphics::IPainter* painter) override; -// RenderObject* HitTest(const Point& point) override; -// protected: -// Size OnMeasureContent(const MeasureRequirement& requirement) override; -// void OnLayoutContent(const Rect& content_rect) override; +/** + * ### Layout + * + * Render object should be able to deal with _arbitrary_ size as the result of + * measure and layout. + * + * Parent may pass calculated preferred size down. But the preferred size set on + * child itself takes precedence. + * + * Each render object should obey the measure requirement to set size and report + * a warning when that requirement can't be satisfied with probably bigger size + * of children than that of itself and optional visual effect to indicate that. + * + * If size of chilren are less than min size requirement, then render object + * should try to fill the rest area. If size of children are more than max size + * requirement, then render object should display a warning of the layout + * problem and use the max size of itself with children exceeding its bound. + * (Or better you could use some visual effect to indicate that.) + * + * ### Create New Render Object + * + * To write a custom RenderObject, override following methods: + * + * public: + * void Draw(platform::graphics::IPainter* painter) override; + * RenderObject* HitTest(const Point& point) override; + * protected: + * Size OnMeasureContent(const MeasureRequirement& requirement) override; + * void OnLayoutContent(const Rect& content_rect) override; + */ class CRU_UI_API RenderObject : public Object { CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::RenderObject") @@ -137,6 +139,7 @@ class CRU_UI_API RenderObject : public Object { virtual void OnAttachedControlChanged(controls::Control* old_control, controls::Control* new_control) {} + virtual void OnResize(const Size& new_size) {} private: diff --git a/src/theme_builder/components/MainWindow.cpp b/src/theme_builder/components/MainWindow.cpp index 31530491..4c605756 100644 --- a/src/theme_builder/components/MainWindow.cpp +++ b/src/theme_builder/components/MainWindow.cpp @@ -14,14 +14,14 @@ MainWindow::MainWindow() { main_layout_ = FlexLayout::Create(); main_layout_->SetFlexDirection(FlexDirection::Horizontal); - window_->AddChild(main_layout_, 0); + window_->AddChild(main_layout_); preview_layout_ = StackLayout::Create(); - main_layout_->AddChild(preview_layout_, 0); + main_layout_->AddChild(preview_layout_); preview_button_ = Button::Create(); preview_button_->SetChild(TextBlock::Create(u"Preview")); - preview_layout_->AddChild(preview_button_, 0); + preview_layout_->AddChild(preview_button_); preview_layout_->SetChildLayoutData( 0, StackChildLayoutData{Alignment::Center, Alignment::Center}); } diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 98d21c9e..6ed06bc2 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -50,6 +50,7 @@ add_library(cru_ui SHARED render/RenderObject.cpp render/ScrollBar.cpp render/ScrollRenderObject.cpp + render/SingleChildRenderObject.cpp render/StackLayoutRenderObject.cpp render/TextRenderObject.cpp render/TreeRenderObject.cpp diff --git a/src/ui/components/Menu.cpp b/src/ui/components/Menu.cpp index 3134bbb6..af2043a9 100644 --- a/src/ui/components/Menu.cpp +++ b/src/ui/components/Menu.cpp @@ -49,7 +49,7 @@ void Menu::AddItem(Component* item, gsl::index index) { Expects(index >= 0 && index <= GetItemCount()); items_.insert(items_.cbegin() + index, item); - container_->AddChild(item->GetRootControl(), index); + container_->AddChildAt(item->GetRootControl(), index); } Component* Menu::RemoveItem(gsl::index index) { @@ -58,7 +58,7 @@ Component* Menu::RemoveItem(gsl::index index) { Component* item = items_[index]; items_.erase(items_.cbegin() + index); - container_->RemoveChild(index); + container_->RemoveChildAt(index); return item; } @@ -91,7 +91,7 @@ PopupMenu::PopupMenu(controls::Control* attached_control) menu_->SetOnItemClick([this](Index) { this->Close(); }); - popup_->AddChild(menu_->GetRootControl(), 0); + popup_->AddChildAt(menu_->GetRootControl(), 0); } PopupMenu::~PopupMenu() { diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp index df71f660..ba43b2b8 100644 --- a/src/ui/controls/Control.cpp +++ b/src/ui/controls/Control.cpp @@ -1,15 +1,11 @@ #include "cru/ui/controls/Control.h" -#include "cru/common/Base.h" #include "cru/platform/gui/Cursor.h" #include "cru/platform/gui/UiApplication.h" -#include "cru/ui/Base.h" #include "cru/ui/host/WindowHost.h" #include "cru/ui/render/RenderObject.h" #include "cru/ui/style/StyleRuleSet.h" -#include <memory> - namespace cru::ui::controls { using platform::gui::ICursor; using platform::gui::IUiApplication; @@ -48,6 +44,12 @@ void Control::SetParent(Control* parent) { OnParentChanged(old_parent, parent); } +void Control::RemoveFromParent() { + if (parent_) { + parent_->RemoveChild(this); + } +} + bool Control::HasFocus() { auto host = GetWindowHost(); if (host == nullptr) return false; diff --git a/src/ui/controls/NoChildControl.cpp b/src/ui/controls/NoChildControl.cpp index 4a9002ed..382a5d18 100644 --- a/src/ui/controls/NoChildControl.cpp +++ b/src/ui/controls/NoChildControl.cpp @@ -5,4 +5,6 @@ void NoChildControl::ForEachChild( const std::function<void(Control*)>& callback) { CRU_UNUSED(callback); } + +void NoChildControl::RemoveChild(Control* child) { CRU_UNUSED(child); } } // namespace cru::ui::controls diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp index 6f4a8320..a64c96d2 100644 --- a/src/ui/render/TextRenderObject.cpp +++ b/src/ui/render/TextRenderObject.cpp @@ -184,9 +184,6 @@ void TextRenderObject::Draw(platform::graphics::IPainter* painter) { this->brush_->GetDebugString()); } - painter->PushState(); - painter->ConcatTransform(Matrix::Translation(GetOffset())); - if (this->selection_range_.has_value()) { const auto&& rects = text_layout_->TextRangeRect(this->selection_range_.value()); @@ -199,8 +196,6 @@ void TextRenderObject::Draw(platform::graphics::IPainter* painter) { if (this->draw_caret_ && this->caret_width_ != 0.0f) { painter->FillRectangle(GetCaretRectInContent(), this->caret_brush_.get()); } - - painter->PopState(); } Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement, |