diff options
-rw-r--r-- | include/cru/common/self_resolvable.hpp | 45 | ||||
-rw-r--r-- | include/cru/ui/controls/flex_layout.hpp | 22 | ||||
-rw-r--r-- | include/cru/ui/render/flex_layout_render_object.hpp | 39 | ||||
-rw-r--r-- | include/cru/ui/ui_manager.hpp | 1 | ||||
-rw-r--r-- | include/cru/ui/window.hpp | 10 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/ui/render/flex_layout_render_object.cpp | 30 | ||||
-rw-r--r-- | src/ui/window.cpp | 15 |
8 files changed, 140 insertions, 23 deletions
diff --git a/include/cru/common/self_resolvable.hpp b/include/cru/common/self_resolvable.hpp new file mode 100644 index 00000000..edeb1434 --- /dev/null +++ b/include/cru/common/self_resolvable.hpp @@ -0,0 +1,45 @@ +#pragma once +#include "pre_config.hpp" + +#include <memory> + +namespace cru { +template <typename T> +class SelfResovable; + +template <typename T> +class ObjectResovler { + friend SelfResovable<T>; + + private: + ObjectResovler(const std::shared_ptr<T*>& resolver) : resolver_(resolver) {} + + public: + ObjectResovler(const ObjectResovler&) = default; + ObjectResovler& operator=(const ObjectResovler&) = default; + ObjectResovler(ObjectResovler&&) = default; + ObjectResovler& operator=(ObjectResovler&&) = default; + ~ObjectResovler() = default; + + T* Resolve() const { return *resolver_; } + + private: + std::shared_ptr<T*> resolver_; +}; + +template <typename T> +class SelfResovable { + public: + SelfResovable() : resolver_(new T*(static_cast<T*>(this))) {} + SelfResovable(const SelfResovable&) = delete; + SelfResovable& operator=(const SelfResovable&) = delete; + SelfResovable(SelfResovable&&) = delete; + SelfResovable& operator=(SelfResovable&&) = delete; + virtual ~SelfResovable() { (*resolver_) = nullptr; } + + ObjectResovler<T> CreateResolver() { return ObjectResovler<T>(resolver_); } + + private: + std::shared_ptr<T*> resolver_; +}; +} // namespace cru diff --git a/include/cru/ui/controls/flex_layout.hpp b/include/cru/ui/controls/flex_layout.hpp index 7422bc05..46c4ea55 100644 --- a/include/cru/ui/controls/flex_layout.hpp +++ b/include/cru/ui/controls/flex_layout.hpp @@ -1,13 +1,17 @@ #pragma once #include "../layout_control.hpp" -#include <memory> +#include "../render/flex_layout_render_object.hpp" +#include "../window.hpp" -namespace cru::ui::render { -class FlexLayoutRenderObject; -} +#include <memory> namespace cru::ui::controls { +// import these basic entities +using render::FlexChildLayoutData; +using render::FlexCrossAlignment; +using render::FlexDirection; +using render::FlexMainAlignment; class FlexLayout : public LayoutControl { public: @@ -31,6 +35,16 @@ class FlexLayout : public LayoutControl { render::RenderObject* GetRenderObject() const override; + FlexMainAlignment GetContentMainAlign() const { + return render_object_->GetContentMainAlign(); + } + + void SetContentMainAlign(FlexMainAlignment value) { + if (value == GetContentMainAlign()) return; + render_object_->SetContentMainAlign(value); + GetWindow()->InvalidateLayout(); + } + protected: void OnAddChild(Control* child, int position) override; void OnRemoveChild(Control* child, int position) override; diff --git a/include/cru/ui/render/flex_layout_render_object.hpp b/include/cru/ui/render/flex_layout_render_object.hpp index 99ec1247..8d881239 100644 --- a/include/cru/ui/render/flex_layout_render_object.hpp +++ b/include/cru/ui/render/flex_layout_render_object.hpp @@ -11,13 +11,32 @@ enum class FlexDirection { VertivalReverse }; -enum class Alignment { Start, End, Center }; +namespace internal { +constexpr int align_start = 0; +constexpr int align_end = align_start + 1; +constexpr int align_center = align_end + 1; +//constexpr int align_stretch = align_center + 1; +} // namespace internal + +enum class FlexMainAlignment { + Start = internal::align_start, + End = internal::align_end, + Center = internal::align_center +}; +enum class FlexCrossAlignment { + Start = internal::align_start, + End = internal::align_end, + Center = internal::align_center, +// Stretch = internal::align_stretch +}; struct FlexChildLayoutData { - std::optional<float> flex_basis; // nullopt stands for content + // nullopt stands for looking at my content + std::optional<float> flex_basis = std::nullopt; float flex_grow = 0; float flex_shrink = 0; - Alignment alignment = Alignment::Center; + // nullopt stands for looking at parent's setting + std::optional<FlexCrossAlignment> cross_alignment = std::nullopt; }; class FlexLayoutRenderObject : public RenderObject { @@ -33,8 +52,15 @@ class FlexLayoutRenderObject : public RenderObject { FlexDirection GetFlexDirection() const { return direction_; } void SetFlexDirection(FlexDirection direction) { direction_ = direction; } - Alignment GetContentMainAlign() const { return content_main_align_; } - void SetContentMainAlign(Alignment align) { content_main_align_ = align; } + FlexMainAlignment GetContentMainAlign() const { return content_main_align_; } + void SetContentMainAlign(FlexMainAlignment align) { + content_main_align_ = align; + } + + FlexCrossAlignment GetItemCrossAlign() const { return item_cross_align_; } + void SetItemCrossAlign(FlexCrossAlignment align) { + item_cross_align_ = align; + } FlexChildLayoutData* GetChildLayoutData(int position); @@ -51,7 +77,8 @@ class FlexLayoutRenderObject : public RenderObject { private: FlexDirection direction_ = FlexDirection::Horizontal; - Alignment content_main_align_ = Alignment::Start; + FlexMainAlignment content_main_align_ = FlexMainAlignment::Start; + FlexCrossAlignment item_cross_align_ = FlexCrossAlignment::Center; std::vector<FlexChildLayoutData> child_layout_data_{}; }; } // namespace cru::ui::render diff --git a/include/cru/ui/ui_manager.hpp b/include/cru/ui/ui_manager.hpp index 2dc9cf6b..f3f78722 100644 --- a/include/cru/ui/ui_manager.hpp +++ b/include/cru/ui/ui_manager.hpp @@ -9,6 +9,7 @@ struct IFontDescriptor; } // namespace cru::platform namespace cru::ui { +//TODO: Make this theme resource. class PredefineResources : public Object { public: PredefineResources(); diff --git a/include/cru/ui/window.hpp b/include/cru/ui/window.hpp index 4bc0e41d..e175b234 100644 --- a/include/cru/ui/window.hpp +++ b/include/cru/ui/window.hpp @@ -1,6 +1,7 @@ #pragma once #include "content_control.hpp" +#include "cru/common/self_resolvable.hpp" #include "cru/platform/native/native_event.hpp" #include "event/ui_event.hpp" @@ -16,7 +17,7 @@ namespace render { class WindowRenderObject; } -class Window final : public ContentControl { +class Window final : public ContentControl, public SelfResovable<Window> { public: static constexpr auto control_type = L"Window"; @@ -46,6 +47,11 @@ class Window final : public ContentControl { Control* GetMouseHoverControl() const { return mouse_hover_control_; } + //*************** region: layout *************** + void Relayout(); + + void InvalidateLayout(); + //*************** region: focus *************** // Request focus for specified control. @@ -93,5 +99,7 @@ class Window final : public ContentControl { Control* mouse_hover_control_; Control* focus_control_; // "focus_control_" can't be nullptr + + bool need_layout_ = false; }; } // namespace cru::ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e7f0edbb..16e0c10f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(cru_base INTERFACE ${CRU_BASE_INCLUDE_DIR}/event.hpp ${CRU_BASE_INCLUDE_DIR}/format.hpp ${CRU_BASE_INCLUDE_DIR}/pre_config.hpp + ${CRU_BASE_INCLUDE_DIR}/self_resolvable.hpp ${CRU_BASE_INCLUDE_DIR}/ui_base.hpp ) target_include_directories(cru_base INTERFACE ${CRU_INCLUDE_DIR}) diff --git a/src/ui/render/flex_layout_render_object.cpp b/src/ui/render/flex_layout_render_object.cpp index 0093f1ad..a9841813 100644 --- a/src/ui/render/flex_layout_render_object.cpp +++ b/src/ui/render/flex_layout_render_object.cpp @@ -161,15 +161,15 @@ Size FlexLayoutRenderObject::OnMeasureContent(const Size& available_size) { } void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { - auto calculate_anchor = [](Alignment alignment, float start_point, + auto calculate_anchor = [](int alignment, float start_point, float total_length, float content_length) -> float { switch (alignment) { - case Alignment::Start: + case internal::align_start: return start_point; - case Alignment::Center: + case internal::align_center: return start_point + (total_length - content_length) / 2.0f; - case Alignment::End: + case internal::align_end: return start_point + total_length - content_length; default: return 0; @@ -184,8 +184,9 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { actual_content_width += child->GetPreferredSize().width; } - const float content_anchor_x = calculate_anchor( - content_main_align_, 0, content_rect.width, actual_content_width); + const float content_anchor_x = + calculate_anchor(static_cast<int>(content_main_align_), 0, + content_rect.width, actual_content_width); float anchor_x = 0; for (int i = 0; i < children.size(); i++) { @@ -199,8 +200,10 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { real_anchor_x = content_rect.GetRight() - real_anchor_x; child->Layout(Rect{ real_anchor_x, - calculate_anchor(child_layout_data_[i].alignment, content_rect.top, - content_rect.height, size.height), + calculate_anchor( + static_cast<int>(child_layout_data_[i].cross_alignment.value_or( + this->item_cross_align_)), + content_rect.top, content_rect.height, size.height), size.width, size.height}); anchor_x += size.width; @@ -211,8 +214,9 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { actual_content_height = child->GetPreferredSize().height; } - const float content_anchor_y = calculate_anchor( - content_main_align_, 0, content_rect.height, actual_content_height); + const float content_anchor_y = + calculate_anchor(static_cast<int>(content_main_align_), 0, + content_rect.height, actual_content_height); float anchor_y = 0; for (int i = 0; i < children.size(); i++) { @@ -227,8 +231,10 @@ void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { } child->Layout(Rect{ real_anchor_y, - calculate_anchor(child_layout_data_[i].alignment, content_rect.left, - content_rect.width, size.width), + calculate_anchor( + static_cast<int>(child_layout_data_[i].cross_alignment.value_or( + this->item_cross_align_)), + content_rect.left, content_rect.width, size.width), size.width, size.height}); anchor_y += size.height; diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 2215826a..4a1bc108 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -147,6 +147,21 @@ render::RenderObject* Window::GetRenderObject() const { return render_object_.get(); } +void Window::Relayout() { this->render_object_->MeasureAndLayout(); } + +void Window::InvalidateLayout() { + if (!need_layout_) { + platform::native::IUiApplication::GetInstance()->InvokeLater( + [resolver = this->CreateResolver()] { + if (const auto window = resolver.Resolve()) { + window->Relayout(); + window->need_layout_ = false; + } + }); + need_layout_ = true; + } +} + bool Window::RequestFocusFor(Control* control) { assert(control != nullptr); // The control to request focus can't be null. // You can set it as the window. |