aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2022-02-10 19:26:19 +0800
committercrupest <crupest@outlook.com>2022-02-10 19:26:19 +0800
commitb2622f654598f82a220a98daaa84fed9ce3b92b2 (patch)
tree544d5a9a52d7530bf54e888d3fabd79ff85bfdb7
parentb8863c403a44c1c7ac35f1a1da92bbf3c8858552 (diff)
downloadcru-b2622f654598f82a220a98daaa84fed9ce3b92b2.tar.gz
cru-b2622f654598f82a220a98daaa84fed9ce3b92b2.tar.bz2
cru-b2622f654598f82a220a98daaa84fed9ce3b92b2.zip
...
-rw-r--r--demos/main/main.cpp8
-rw-r--r--demos/scroll_view/main.cpp2
-rw-r--r--include/cru/ui/controls/Control.h11
-rw-r--r--include/cru/ui/controls/LayoutControl.h32
-rw-r--r--include/cru/ui/controls/NoChildControl.h2
-rw-r--r--include/cru/ui/controls/SingleChildControl.h8
-rw-r--r--include/cru/ui/render/LayoutRenderObject.h12
-rw-r--r--include/cru/ui/render/RenderObject.h59
-rw-r--r--src/theme_builder/components/MainWindow.cpp6
-rw-r--r--src/ui/CMakeLists.txt1
-rw-r--r--src/ui/components/Menu.cpp6
-rw-r--r--src/ui/controls/Control.cpp10
-rw-r--r--src/ui/controls/NoChildControl.cpp2
-rw-r--r--src/ui/render/TextRenderObject.cpp5
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,