From 6ce70ed4b08c7db20b6b384be26c7fc4e11c98de Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 28 Oct 2020 20:51:55 +0800 Subject: ... --- src/ui/LayoutControl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/ui/LayoutControl.cpp') diff --git a/src/ui/LayoutControl.cpp b/src/ui/LayoutControl.cpp index 4813566b..05fcdc4a 100644 --- a/src/ui/LayoutControl.cpp +++ b/src/ui/LayoutControl.cpp @@ -19,7 +19,7 @@ void LayoutControl::AddChild(Control* control, const Index position) { children_.insert(this->children_.cbegin() + position, control); control->_SetParent(this); - control->_SetDescendantUiHost(GetUiHost()); + control->_SetDescendantWindowHost(GetWindowHost()); OnAddChild(control, position); } @@ -36,7 +36,7 @@ void LayoutControl::RemoveChild(const Index position) { children_.erase(i); child->_SetParent(nullptr); - child->_SetDescendantUiHost(nullptr); + child->_SetDescendantWindowHost(nullptr); OnRemoveChild(child, position); } -- cgit v1.2.3 From 52594324b302f6e9da10ac01fe803196371bb2d9 Mon Sep 17 00:00:00 2001 From: crupest Date: Thu, 29 Oct 2020 00:01:26 +0800 Subject: ... --- demos/input_method/main.cpp | 7 +- demos/main/main.cpp | 6 +- include/cru/ui/ContentControl.hpp | 13 ++-- include/cru/ui/Control.hpp | 29 ++++---- include/cru/ui/LayoutControl.hpp | 18 +---- include/cru/ui/NoChildControl.hpp | 10 +-- include/cru/ui/Window.hpp | 18 ++--- include/cru/ui/WindowHost.hpp | 72 +++++++----------- include/cru/ui/render/Base.hpp | 1 - include/cru/ui/render/RenderObject.hpp | 20 +++-- include/cru/ui/render/WindowRenderObject.hpp | 34 --------- src/ui/CMakeLists.txt | 2 - src/ui/ContentControl.cpp | 24 +++--- src/ui/Control.cpp | 106 +++++++++++++++++---------- src/ui/LayoutControl.cpp | 47 ------------ src/ui/NoChildControl.cpp | 4 +- src/ui/Window.cpp | 31 ++++---- src/ui/WindowHost.cpp | 98 ++++++++++--------------- src/ui/controls/TextControlService.hpp | 77 +++++++++++-------- src/ui/render/RenderObject.cpp | 25 +++---- src/ui/render/WindowRenderObject.cpp | 44 ----------- src/win/native/UiApplication.cpp | 5 -- src/win/native/Window.cpp | 1 + 23 files changed, 271 insertions(+), 421 deletions(-) delete mode 100644 include/cru/ui/render/WindowRenderObject.hpp delete mode 100644 src/ui/render/WindowRenderObject.cpp (limited to 'src/ui/LayoutControl.cpp') diff --git a/demos/input_method/main.cpp b/demos/input_method/main.cpp index bff0e1d3..06098253 100644 --- a/demos/input_method/main.cpp +++ b/demos/input_method/main.cpp @@ -14,12 +14,9 @@ int main() { auto graph_factory = application->GetGraphFactory(); - auto window_resolver = application->CreateWindow(nullptr); + auto window = application->CreateWindow(nullptr); - auto window = window_resolver->Resolve(); - - auto input_method_context = - application->GetInputMethodManager()->GetContext(window); + auto input_method_context = window->GetInputMethodContext(); auto brush = graph_factory->CreateSolidColorBrush(); brush->SetColor(colors::black); diff --git a/demos/main/main.cpp b/demos/main/main.cpp index f7635231..e973682e 100644 --- a/demos/main/main.cpp +++ b/demos/main/main.cpp @@ -2,8 +2,8 @@ #include "cru/platform/native/UiApplication.hpp" #include "cru/platform/native/Window.hpp" #include "cru/ui/Base.hpp" -#include "cru/ui/WindowHost.hpp" #include "cru/ui/Window.hpp" +#include "cru/ui/WindowHost.hpp" #include "cru/ui/controls/Button.hpp" #include "cru/ui/controls/FlexLayout.hpp" #include "cru/ui/controls/TextBlock.hpp" @@ -30,7 +30,7 @@ int main() { flex_layout->SetContentMainAlign(cru::ui::FlexCrossAlignment::Center); flex_layout->SetItemCrossAlign(cru::ui::FlexCrossAlignment::Center); - window->SetChild(flex_layout); + window->AddChild(flex_layout, 0); const auto text_block = TextBlock::Create(); text_block->SetText(u"Hello World from CruUI!"); @@ -45,7 +45,7 @@ int main() { const auto text_box = TextBox::Create(); flex_layout->AddChild(text_box, 2); - window->GetWindowHost()->GetNativeWindowResolver()->Resolve()->SetVisible(true); + window->GetWindowHost()->GetNativeWindow()->SetVisible(true); return application->Run(); } diff --git a/include/cru/ui/ContentControl.hpp b/include/cru/ui/ContentControl.hpp index 19f13a1d..ba5b6b2f 100644 --- a/include/cru/ui/ContentControl.hpp +++ b/include/cru/ui/ContentControl.hpp @@ -4,26 +4,23 @@ namespace cru::ui { class ContentControl : public Control { protected: - ContentControl(); + ContentControl() = default; public: ContentControl(const ContentControl& other) = delete; ContentControl(ContentControl&& other) = delete; ContentControl& operator=(const ContentControl& other) = delete; ContentControl& operator=(ContentControl&& other) = delete; - ~ContentControl() override; + ~ContentControl() override = default; - const std::vector& GetChildren() const override final { - return child_vector_; - } - Control* GetChild() const { return child_; } + Control* GetChild() const; void SetChild(Control* child); protected: virtual void OnChildChanged(Control* old_child, Control* new_child); private: - std::vector child_vector_; - Control*& child_; + using Control::AddChild; + using Control::RemoveChild; }; } // namespace cru::ui diff --git a/include/cru/ui/Control.hpp b/include/cru/ui/Control.hpp index 692dcc9b..0021ad62 100644 --- a/include/cru/ui/Control.hpp +++ b/include/cru/ui/Control.hpp @@ -1,9 +1,9 @@ #pragma once #include "Base.hpp" +#include "UiEvent.hpp" #include "cru/common/Event.hpp" #include "render/Base.hpp" -#include "UiEvent.hpp" #include @@ -19,39 +19,31 @@ class Control : public Object { Control(Control&& other) = delete; Control& operator=(const Control& other) = delete; Control& operator=(Control&& other) = delete; - ~Control() override = default; + ~Control() override; public: virtual std::u16string_view GetControlType() const = 0; //*************** region: tree *************** public: - // Get the ui host if attached, otherwise, return nullptr. - WindowHost* GetWindowHost() const { return ui_host_; } + WindowHost* GetWindowHost() const; Control* GetParent() const { return parent_; } - virtual const std::vector& GetChildren() const = 0; + const std::vector& GetChildren() const { return children_; } // Traverse the tree rooted the control including itself. void TraverseDescendants(const std::function& predicate); - void _SetParent(Control* parent); - void _SetDescendantWindowHost(WindowHost* host); - - private: - static void _TraverseDescendants( - Control* control, const std::function& predicate); - public: virtual render::RenderObject* GetRenderObject() const = 0; //*************** region: focus *************** public: - bool RequestFocus(); - bool HasFocus(); + void SetFocus(); + //*************** region: mouse *************** public: bool IsMouseOver() const { return is_mouse_over_; } @@ -134,15 +126,22 @@ class Control : public Object { //*************** region: tree *************** protected: + void AddChild(Control* control, Index position); + void RemoveChild(Index position); + virtual void OnAddChild(Control* child, Index position); + virtual void OnRemoveChild(Control* child, Index position); virtual void OnParentChanged(Control* old_parent, Control* new_parent); virtual void OnAttachToHost(WindowHost* host); virtual void OnDetachFromHost(WindowHost* host); + protected: virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) } private: - WindowHost* ui_host_ = nullptr; Control* parent_ = nullptr; + std::vector children_; + + WindowHost* window_host_ = nullptr; private: bool is_mouse_over_ = false; diff --git a/include/cru/ui/LayoutControl.hpp b/include/cru/ui/LayoutControl.hpp index 7997b37e..69d5cd0b 100644 --- a/include/cru/ui/LayoutControl.hpp +++ b/include/cru/ui/LayoutControl.hpp @@ -11,21 +11,9 @@ class LayoutControl : public Control { LayoutControl(LayoutControl&& other) = delete; LayoutControl& operator=(const LayoutControl& other) = delete; LayoutControl& operator=(LayoutControl&& other) = delete; - ~LayoutControl() override; + ~LayoutControl() override = default; - const std::vector& GetChildren() const override final { - return children_; - } - - void AddChild(Control* control, Index position); - - void RemoveChild(Index position); - - protected: - virtual void OnAddChild(Control* child, Index position); - virtual void OnRemoveChild(Control* child, Index position); - - private: - std::vector children_; + using Control::AddChild; + using Control::RemoveChild; }; } // namespace cru::ui diff --git a/include/cru/ui/NoChildControl.hpp b/include/cru/ui/NoChildControl.hpp index 1a31ae7e..0d8a8e34 100644 --- a/include/cru/ui/NoChildControl.hpp +++ b/include/cru/ui/NoChildControl.hpp @@ -3,9 +3,6 @@ namespace cru::ui { class NoChildControl : public Control { - private: - static const std::vector empty_control_vector; - protected: NoChildControl() = default; @@ -16,9 +13,8 @@ class NoChildControl : public Control { NoChildControl& operator=(NoChildControl&& other) = delete; ~NoChildControl() override = default; - protected: - const std::vector& GetChildren() const override final { - return empty_control_vector; - } + private: + using Control::AddChild; + using Control::RemoveChild; }; } // namespace cru::ui diff --git a/include/cru/ui/Window.hpp b/include/cru/ui/Window.hpp index 5ea24855..0739e3dc 100644 --- a/include/cru/ui/Window.hpp +++ b/include/cru/ui/Window.hpp @@ -1,10 +1,8 @@ #pragma once -#include "ContentControl.hpp" +#include "LayoutControl.hpp" namespace cru::ui { -class Window final : public ContentControl { - friend WindowHost; - +class Window final : public LayoutControl { public: static constexpr std::u16string_view control_type = u"Window"; @@ -12,9 +10,7 @@ class Window final : public ContentControl { static Window* CreateOverlapped(); private: - struct tag_overlapped_constructor {}; - - explicit Window(tag_overlapped_constructor); + Window(); public: Window(const Window& other) = delete; @@ -29,12 +25,12 @@ class Window final : public ContentControl { render::RenderObject* GetRenderObject() const override; protected: - void OnChildChanged(Control* old_child, Control* new_child) override; + void OnAddChild(Control* child, Index position) override; + void OnRemoveChild(Control* child, Index position) override; private: - std::unique_ptr managed_ui_host_; + std::unique_ptr window_host_; - // WindowHost is responsible to take care of lifetime of this. - render::WindowRenderObject* render_object_; + std::unique_ptr render_object_; }; } // namespace cru::ui diff --git a/include/cru/ui/WindowHost.hpp b/include/cru/ui/WindowHost.hpp index 83ef2f64..8efb505d 100644 --- a/include/cru/ui/WindowHost.hpp +++ b/include/cru/ui/WindowHost.hpp @@ -1,20 +1,22 @@ #pragma once -#include #include "Base.hpp" #include "cru/common/Event.hpp" -#include "cru/common/SelfResolvable.hpp" +#include "cru/platform/native/UiApplication.hpp" +#include "cru/platform/native/Window.hpp" #include "render/Base.hpp" +#include + namespace cru::ui { struct AfterLayoutEventArgs {}; -class WindowHost : public Object, public SelfResolvable { + +// The bridge between control tree and native window. +class WindowHost : public Object { CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::WindowHost") public: - // This will create root window render object and attach it to window. - // It will also create and manage a native window. - WindowHost(Window* window); + WindowHost(Control* root_control); CRU_DELETE_COPY(WindowHost) CRU_DELETE_MOVE(WindowHost) @@ -22,6 +24,8 @@ class WindowHost : public Object, public SelfResolvable { ~WindowHost() override; public: + platform::native::INativeWindow* GetNativeWindow() { return native_window_; } + // Mark the layout as invalid, and arrange a re-layout later. // This method could be called more than one times in a message cycle. But // layout only takes place once. @@ -36,13 +40,18 @@ class WindowHost : public Object, public SelfResolvable { return &after_layout_event_; } + void Relayout(); + void Relayout(const Size& available_size); + + // Is layout is invalid, wait for relayout and then run the action. Otherwist + // run it right now. + void RunAfterLayoutStable(std::function action); + // If true, preferred size of root render object is set to window size when // measure. Default is true. bool IsLayoutPreferToFillWindow() const; void SetLayoutPreferToFillWindow(bool value); - void Relayout(); - // Get current control that mouse hovers on. This ignores the mouse-capture // control. Even when mouse is captured by another control, this function // return the control under cursor. You can use `GetMouseCaptureControl` to @@ -51,12 +60,10 @@ class WindowHost : public Object, public SelfResolvable { //*************** region: focus *************** - // Request focus for specified control. - bool RequestFocusFor(Control* control); - - // Get the control that has focus. Control* GetFocusControl(); + void SetFocusControl(Control* control); + //*************** region: focus *************** // Pass nullptr to release capture. If mouse is already capture by a control, @@ -78,19 +85,6 @@ class WindowHost : public Object, public SelfResolvable { void UpdateCursor(); - std::shared_ptr - GetNativeWindowResolver() { - return native_window_resolver_; - } - - bool IsRetainAfterDestroy() { return retain_after_destroy_; } - - void SetRetainAfterDestroy(bool destroy) { retain_after_destroy_ = destroy; } - - // Is layout is invalid, wait for relayout and then run the action. Otherwist - // run it right now. - void RunAfterLayoutStable(std::function action); - private: //*************** region: native messages *************** void OnNativeDestroy(platform::native::INativeWindow* window, std::nullptr_t); @@ -126,33 +120,21 @@ class WindowHost : public Object, public SelfResolvable { bool no_enter); private: - bool need_layout_ = false; + Control* root_control_; + render::RenderObject* root_render_object_; + + platform::native::INativeWindow* native_window_; + bool need_layout_ = false; + platform::native::TimerAutoCanceler relayout_timer_canceler_; Event after_layout_event_; std::vector > after_layout_stable_action_; - std::shared_ptr - native_window_resolver_; - - // See remarks of WindowHost. - bool retain_after_destroy_ = false; - // See remarks of WindowHost. - bool deleting_ = false; - - // We need this because calling Resolve on resolver in handler of destroy - // event is bad and will always get the dying window. But we need to label the - // window as destroyed so the destructor will not destroy native window - // repeatedly. See remarks of WindowHost. - bool native_window_destroyed_ = false; - std::vector event_revoker_guards_; - Window* window_control_; - std::unique_ptr root_render_object_; - - Control* mouse_hover_control_; + Control* mouse_hover_control_ = nullptr; - Control* focus_control_; // "focus_control_" can't be nullptr + Control* focus_control_; Control* mouse_captured_control_; diff --git a/include/cru/ui/render/Base.hpp b/include/cru/ui/render/Base.hpp index 801d58bd..ac67349e 100644 --- a/include/cru/ui/render/Base.hpp +++ b/include/cru/ui/render/Base.hpp @@ -9,5 +9,4 @@ class FlexLayoutRenderObject; class ScrollRenderObject; class StackLayoutRenderObject; class TextRenderObject; -class WindowRenderObject; } // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp index f052221e..4e5c9060 100644 --- a/include/cru/ui/render/RenderObject.hpp +++ b/include/cru/ui/render/RenderObject.hpp @@ -3,12 +3,13 @@ #include "MeasureRequirement.hpp" #include "cru/common/Event.hpp" +#include "cru/ui/Base.hpp" +#include #include #include 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. @@ -38,7 +39,7 @@ namespace cru::ui::render { // Size OnMeasureContent(const MeasureRequirement& requirement) override; // void OnLayoutContent(const Rect& content_rect) override; class RenderObject : public Object { - friend WindowRenderObject; + friend WindowHost; CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::RenderObject") @@ -64,7 +65,7 @@ class RenderObject : public Object { Control* GetAttachedControl() const { return control_; } void SetAttachedControl(Control* new_control) { control_ = new_control; } - WindowHost* GetWindowHost() const { return ui_host_; } + WindowHost* GetWindowHost() const { return window_host_; } RenderObject* GetParent() const { return parent_; } @@ -132,6 +133,11 @@ class RenderObject : public Object { // Add offset before pass point to children. virtual RenderObject* HitTest(const Point& point) = 0; + IEvent* AttachToHostEvent() { return &attach_to_host_event_; } + IEvent* DetachFromHostEvent() { + return &detach_from_host_event_; + } + public: void InvalidateLayout(); void InvalidatePaint(); @@ -190,7 +196,6 @@ class RenderObject : public Object { virtual void OnLayoutContent(const Rect& content_rect) = 0; virtual void OnAfterLayout(); - static void NotifyAfterLayoutRecursive(RenderObject* render_object); virtual Rect GetPaddingRect() const; virtual Rect GetContentRect() const; @@ -198,11 +203,11 @@ class RenderObject : public Object { private: void SetParent(RenderObject* new_parent); - void SetRenderHostRecursive(WindowHost* host); + void SetWindowHostRecursive(WindowHost* host); private: Control* control_ = nullptr; - WindowHost* ui_host_ = nullptr; + WindowHost* window_host_ = nullptr; RenderObject* parent_ = nullptr; std::vector children_{}; @@ -217,5 +222,8 @@ class RenderObject : public Object { Thickness margin_{}; Thickness padding_{}; + + Event attach_to_host_event_; + Event detach_from_host_event_; }; } // namespace cru::ui::render diff --git a/include/cru/ui/render/WindowRenderObject.hpp b/include/cru/ui/render/WindowRenderObject.hpp deleted file mode 100644 index 23fd8748..00000000 --- a/include/cru/ui/render/WindowRenderObject.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "RenderObject.hpp" - -#include - -namespace cru::ui::render { -class WindowRenderObject : public RenderObject { - public: - WindowRenderObject(WindowHost* host); - WindowRenderObject(const WindowRenderObject& other) = delete; - WindowRenderObject(WindowRenderObject&& other) = delete; - WindowRenderObject& operator=(const WindowRenderObject& other) = delete; - WindowRenderObject& operator=(WindowRenderObject&& other) = delete; - ~WindowRenderObject() override = default; - - RenderObject* HitTest(const Point& point) override; - - public: - std::u16string_view GetName() const override; - - protected: - Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - private: - RenderObject* GetChild() const { - return GetChildren().empty() ? nullptr : GetChildren()[0]; - } - - private: - EventRevokerGuard after_layout_event_guard_; -}; -} // namespace cru::ui::render diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index d59fd7da..045fea24 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -30,7 +30,6 @@ add_library(cru_ui STATIC render/ScrollRenderObject.cpp render/StackLayoutRenderObject.cpp render/TextRenderObject.cpp - render/WindowRenderObject.cpp ) target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/Base.hpp @@ -63,6 +62,5 @@ target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/render/ScrollRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/StackLayoutRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/TextRenderObject.hpp - ${CRU_UI_INCLUDE_DIR}/render/WindowRenderObject.hpp ) target_link_libraries(cru_ui PUBLIC cru_platform_native) diff --git a/src/ui/ContentControl.cpp b/src/ui/ContentControl.cpp index 60d944d6..19b1b06f 100644 --- a/src/ui/ContentControl.cpp +++ b/src/ui/ContentControl.cpp @@ -3,25 +3,19 @@ #include "cru/ui/Window.hpp" namespace cru::ui { -ContentControl::ContentControl() - : child_vector_{nullptr}, child_(child_vector_[0]) {} - -ContentControl::~ContentControl() { delete child_; } +Control* ContentControl::GetChild() const { + if (GetChildren().empty()) return nullptr; + return GetChildren()[0]; +} void ContentControl::SetChild(Control* child) { - Expects(!dynamic_cast(child)); // Can't add a window as child. - if (child == child_) return; - - const auto host = GetWindowHost(); - const auto old_child = child_; - child_ = child; - if (old_child) { - old_child->_SetParent(nullptr); - old_child->_SetDescendantWindowHost(nullptr); + Control* old_child = nullptr; + if (!GetChildren().empty()) { + old_child = GetChildren()[0]; + this->RemoveChild(0); } if (child) { - child->_SetParent(this); - child->_SetDescendantWindowHost(host); + this->AddChild(child, 0); } OnChildChanged(old_child, child); } diff --git a/src/ui/Control.cpp b/src/ui/Control.cpp index cd7ed0dc..13b1c780 100644 --- a/src/ui/Control.cpp +++ b/src/ui/Control.cpp @@ -1,10 +1,12 @@ #include "cru/ui/Control.hpp" +#include "RoutedEventDispatch.hpp" +#include "cru/common/Base.hpp" #include "cru/platform/native/Cursor.hpp" #include "cru/platform/native/UiApplication.hpp" #include "cru/ui/Base.hpp" #include "cru/ui/WindowHost.hpp" -#include "RoutedEventDispatch.hpp" +#include "cru/ui/render/RenderObject.hpp" #include @@ -25,49 +27,16 @@ Control::Control() { }); } -void Control::_SetParent(Control* parent) { - const auto old_parent = GetParent(); - parent_ = parent; - const auto new_parent = GetParent(); - if (old_parent != new_parent) OnParentChanged(old_parent, new_parent); +Control::~Control() { + for (const auto child : children_) delete child; } -void Control::_SetDescendantWindowHost(WindowHost* host) { - if (host == nullptr && ui_host_ == nullptr) return; - - // You can only attach or detach window. - Expects((host != nullptr && ui_host_ == nullptr) || - (host == nullptr && ui_host_ != nullptr)); - - if (host == nullptr) { - const auto old = ui_host_; - TraverseDescendants([old](Control* control) { - control->ui_host_ = nullptr; - control->OnDetachFromHost(old); - }); - } else - TraverseDescendants([host](Control* control) { - control->ui_host_ = host; - control->OnAttachToHost(host); - }); -} +WindowHost* Control::GetWindowHost() const { return window_host_; } void Control::TraverseDescendants( const std::function& predicate) { - _TraverseDescendants(this, predicate); -} - -void Control::_TraverseDescendants( - Control* control, const std::function& predicate) { - predicate(control); - for (auto c : control->GetChildren()) _TraverseDescendants(c, predicate); -} - -bool Control::RequestFocus() { - auto host = GetWindowHost(); - if (host == nullptr) return false; - - return host->RequestFocusFor(this); + predicate(this); + for (auto c : GetChildren()) c->TraverseDescendants(predicate); } bool Control::HasFocus() { @@ -84,6 +53,13 @@ bool Control::CaptureMouse() { return host->CaptureMouseFor(this); } +void Control::SetFocus() { + auto host = GetWindowHost(); + if (host == nullptr) return; + + host->SetFocusControl(this); +} + bool Control::ReleaseMouse() { auto host = GetWindowHost(); if (host == nullptr) return false; @@ -119,6 +95,58 @@ void Control::SetCursor(std::shared_ptr cursor) { } } +void Control::AddChild(Control* control, const Index position) { + Expects(control->GetParent() == + nullptr); // The control already has a parent. + Expects(position >= 0); + Expects(position <= static_cast( + children_.size())); // The position is out of range. + + children_.insert(children_.cbegin() + position, control); + + const auto old_parent = control->parent_; + control->parent_ = this; + + OnAddChild(control, position); + control->OnParentChanged(old_parent, this); + + if (window_host_) + control->TraverseDescendants([this](Control* control) { + control->window_host_ = window_host_; + control->OnAttachToHost(window_host_); + }); +} + +void Control::RemoveChild(const Index position) { + Expects(position >= 0); + Expects(position < static_cast( + children_.size())); // The position is out of range. + + const auto i = children_.cbegin() + position; + const auto control = *i; + + children_.erase(i); + control->parent_ = nullptr; + + OnRemoveChild(control, position); + control->OnParentChanged(this, nullptr); + + if (window_host_) + control->TraverseDescendants([this](Control* control) { + control->window_host_ = nullptr; + control->OnDetachFromHost(window_host_); + }); +} + +void Control::OnAddChild(Control* child, Index position) { + CRU_UNUSED(child) + CRU_UNUSED(position) +} +void Control::OnRemoveChild(Control* child, Index position) { + CRU_UNUSED(child) + CRU_UNUSED(position) +} + void Control::OnParentChanged(Control* old_parent, Control* new_parent) { CRU_UNUSED(old_parent) CRU_UNUSED(new_parent) diff --git a/src/ui/LayoutControl.cpp b/src/ui/LayoutControl.cpp index 05fcdc4a..351026f9 100644 --- a/src/ui/LayoutControl.cpp +++ b/src/ui/LayoutControl.cpp @@ -3,51 +3,4 @@ #include "cru/ui/Window.hpp" namespace cru::ui { -LayoutControl::~LayoutControl() { - for (const auto child : children_) delete child; -} - -void LayoutControl::AddChild(Control* control, const Index position) { - Expects(control->GetParent() == - nullptr); // The control already has a parent. - Expects(!dynamic_cast(control)); // Can't add a window as child. - Expects(position >= 0); - Expects(position <= - static_cast( - this->children_.size())); // The position is out of range. - - children_.insert(this->children_.cbegin() + position, control); - - control->_SetParent(this); - control->_SetDescendantWindowHost(GetWindowHost()); - - OnAddChild(control, position); -} - -void LayoutControl::RemoveChild(const Index position) { - Expects(position >= 0); - Expects(position < - static_cast( - this->children_.size())); // The position is out of range. - - const auto i = children_.cbegin() + position; - const auto child = *i; - - children_.erase(i); - - child->_SetParent(nullptr); - child->_SetDescendantWindowHost(nullptr); - - OnRemoveChild(child, position); -} - -void LayoutControl::OnAddChild(Control* child, const Index position) { - CRU_UNUSED(child) - CRU_UNUSED(position) -} - -void LayoutControl::OnRemoveChild(Control* child, const Index position) { - CRU_UNUSED(child) - CRU_UNUSED(position) -} } // namespace cru::ui diff --git a/src/ui/NoChildControl.cpp b/src/ui/NoChildControl.cpp index 86861049..8adbe3bc 100644 --- a/src/ui/NoChildControl.cpp +++ b/src/ui/NoChildControl.cpp @@ -1,5 +1,3 @@ #include "cru/ui/NoChildControl.hpp" -namespace cru::ui { -const std::vector NoChildControl::empty_control_vector{}; -} +namespace cru::ui {} diff --git a/src/ui/Window.cpp b/src/ui/Window.cpp index a8cb315b..6d507858 100644 --- a/src/ui/Window.cpp +++ b/src/ui/Window.cpp @@ -1,28 +1,31 @@ #include "cru/ui/Window.hpp" -#include "cru/ui/render/WindowRenderObject.hpp" +#include "cru/common/Base.hpp" #include "cru/ui/WindowHost.hpp" +#include "cru/ui/render/Base.hpp" +#include "cru/ui/render/StackLayoutRenderObject.hpp" namespace cru::ui { -Window* Window::CreateOverlapped() { - return new Window(tag_overlapped_constructor{}); -} +Window* Window::CreateOverlapped() { return new Window(); } -Window::Window(tag_overlapped_constructor) { - managed_ui_host_ = std::make_unique(this); +Window::Window() : render_object_(new render::StackLayoutRenderObject()) { + window_host_ = std::make_unique(this); } -Window::~Window() { - // explicit destroy ui host first. - managed_ui_host_.reset(); -} +Window::~Window() {} std::u16string_view Window::GetControlType() const { return control_type; } -render::RenderObject* Window::GetRenderObject() const { return render_object_; } +render::RenderObject* Window::GetRenderObject() const { + return render_object_.get(); +} + +void Window::OnAddChild(Control* child, Index position) { + render_object_->AddChild(child->GetRenderObject(), position); +} -void Window::OnChildChanged(Control* old_child, Control* new_child) { - if (old_child) render_object_->RemoveChild(0); - if (new_child) render_object_->AddChild(new_child->GetRenderObject(), 0); +void Window::OnRemoveChild(Control* child, Index position) { + CRU_UNUSED(child); + render_object_->RemoveChild(position); } } // namespace cru::ui diff --git a/src/ui/WindowHost.cpp b/src/ui/WindowHost.cpp index d3dec422..1dba4404 100644 --- a/src/ui/WindowHost.cpp +++ b/src/ui/WindowHost.cpp @@ -9,7 +9,9 @@ #include "cru/ui/DebugFlags.hpp" #include "cru/ui/Window.hpp" #include "cru/ui/render/MeasureRequirement.hpp" -#include "cru/ui/render/WindowRenderObject.hpp" +#include "cru/ui/render/RenderObject.hpp" + +#include namespace cru::ui { using platform::native::INativeWindow; @@ -97,25 +99,18 @@ inline void BindNativeEvent( } } // namespace -WindowHost::WindowHost(Window* window) - : window_control_(window), - mouse_hover_control_(nullptr), - focus_control_(window), - mouse_captured_control_(nullptr) { +WindowHost::WindowHost(Control* root_control) : root_control_(root_control) { const auto ui_application = IUiApplication::GetInstance(); - native_window_resolver_ = ui_application->CreateWindow(nullptr); - - const auto native_window = native_window_resolver_->Resolve(); - - auto input_method_context = - ui_application->GetInputMethodManager()->GetContext(native_window); - input_method_context->DisableIME(); + auto native_window = ui_application->CreateWindow(nullptr); + native_window_ = native_window; - window->ui_host_ = this; + root_control_->TraverseDescendants([this](Control* control) { + control->window_host_ = this; + control->OnAttachToHost(this); + }); - root_render_object_ = std::make_unique(this); - root_render_object_->SetAttachedControl(window); - window->render_object_ = root_render_object_.get(); + root_render_object_ = root_control->GetRenderObject(); + root_render_object_->SetWindowHostRecursive(this); BindNativeEvent(this, native_window, native_window->DestroyEvent(), &WindowHost::OnNativeDestroy, event_revoker_guards_); @@ -140,34 +135,24 @@ WindowHost::WindowHost(Window* window) } WindowHost::~WindowHost() { - deleting_ = true; - window_control_->TraverseDescendants( - [this](Control* control) { control->OnDetachFromHost(this); }); - if (!native_window_destroyed_) { - const auto native_window = native_window_resolver_->Resolve(); - if (native_window) { - native_window->Close(); - } + if (native_window_) { + native_window_->Close(); } } void WindowHost::InvalidatePaint() { - if (const auto native_window = native_window_resolver_->Resolve()) - native_window->RequestRepaint(); + if (native_window_) native_window_->RequestRepaint(); } void WindowHost::InvalidateLayout() { if constexpr (debug_flags::layout) log::TagDebug(log_tag, u"A relayout is requested."); if (!need_layout_) { - platform::native::IUiApplication::GetInstance()->SetImmediate( - [resolver = this->CreateResolver()] { - if (const auto host = resolver.Resolve()) { - host->Relayout(); - host->need_layout_ = false; - host->InvalidatePaint(); - } - }); + platform::native::IUiApplication::GetInstance()->SetImmediate([this] { + Relayout(); + need_layout_ = false; + InvalidatePaint(); + }); need_layout_ = true; } } @@ -183,15 +168,17 @@ void WindowHost::SetLayoutPreferToFillWindow(bool value) { } void WindowHost::Relayout() { - const auto native_window = native_window_resolver_->Resolve(); - const auto client_size = native_window - ? native_window->GetClientSize() - : Size{100, 100}; // a reasonable assumed size + const auto available_size = + native_window_ ? native_window_->GetClientSize() + : Size{100, 100}; // a reasonable assumed size + Relayout(available_size); +} +void WindowHost::Relayout(const Size& available_size) { root_render_object_->Measure( - render::MeasureRequirement{client_size, + render::MeasureRequirement{available_size, IsLayoutPreferToFillWindow() - ? render::MeasureSize(client_size) + ? render::MeasureSize(available_size) : render::MeasureSize::NotSpecified()}, render::MeasureSize::NotSpecified()); root_render_object_->Layout(Point{}); @@ -202,11 +189,11 @@ void WindowHost::Relayout() { log::TagDebug(log_tag, u"A relayout is finished."); } -bool WindowHost::RequestFocusFor(Control* control) { - Expects(control != nullptr); // The control to request focus can't be null. - // You can set it as the window. +Control* WindowHost::GetFocusControl() { return focus_control_; } - if (focus_control_ == control) return true; +void WindowHost::SetFocusControl(Control* control) { + if (focus_control_ == control) return; + if (control == nullptr) control = root_control_; const auto old_focus_control = focus_control_; @@ -217,15 +204,10 @@ bool WindowHost::RequestFocusFor(Control* control) { DispatchEvent(event_names::GainFocus, control, &Control::GainFocusEvent, nullptr, false); - - return true; } -Control* WindowHost::GetFocusControl() { return focus_control_; } - bool WindowHost::CaptureMouseFor(Control* control) { - const auto native_window = native_window_resolver_->Resolve(); - if (!native_window) return false; + if (!native_window_) return false; if (control == mouse_captured_control_) return true; @@ -236,7 +218,7 @@ bool WindowHost::CaptureMouseFor(Control* control) { if (old_capture_control != mouse_hover_control_) { DispatchMouseHoverControlChangeEvent( old_capture_control, mouse_hover_control_, - native_window->GetMousePosition(), true, false); + native_window_->GetMousePosition(), true, false); } UpdateCursor(); return true; @@ -247,7 +229,7 @@ bool WindowHost::CaptureMouseFor(Control* control) { mouse_captured_control_ = control; DispatchMouseHoverControlChangeEvent( mouse_hover_control_, mouse_captured_control_, - native_window->GetMousePosition(), false, true); + native_window_->GetMousePosition(), false, true); UpdateCursor(); return true; } @@ -266,8 +248,8 @@ void WindowHost::RunAfterLayoutStable(std::function action) { void WindowHost::OnNativeDestroy(INativeWindow* window, std::nullptr_t) { CRU_UNUSED(window) - native_window_destroyed_ = true; - if (!deleting_ && !retain_after_destroy_) delete window_control_; + this->relayout_timer_canceler_.Reset(); + this->native_window_ = nullptr; } void WindowHost::OnNativePaint(INativeWindow* window, std::nullptr_t) { @@ -399,9 +381,9 @@ void WindowHost::DispatchMouseHoverControlChangeEvent(Control* old_control, } void WindowHost::UpdateCursor() { - if (const auto native_window = native_window_resolver_->Resolve()) { + if (native_window_) { const auto capture = GetMouseCaptureControl(); - native_window->SetCursor( + native_window_->SetCursor( (capture ? capture : GetMouseHoverControl())->GetInheritedCursor()); } } @@ -413,6 +395,6 @@ Control* WindowHost::HitTest(const Point& point) { Ensures(control); return control; } - return window_control_; + return root_control_; } } // namespace cru::ui diff --git a/src/ui/controls/TextControlService.hpp b/src/ui/controls/TextControlService.hpp index 8c1bd32f..33a6bc36 100644 --- a/src/ui/controls/TextControlService.hpp +++ b/src/ui/controls/TextControlService.hpp @@ -61,7 +61,7 @@ class TextControlService : public Object { void SetEditable(bool editable) { this->editable_ = editable; - this->input_method_context_.reset(); + if (!editable) CancelComposition(); } std::u16string GetText() { return this->text_; } @@ -69,8 +69,8 @@ class TextControlService : public Object { void SetText(std::u16string text, bool stop_composition = false) { this->text_ = std::move(text); CoerceSelection(); - if (stop_composition && this->input_method_context_) { - this->input_method_context_->CancelComposition(); + if (stop_composition) { + CancelComposition(); } SyncTextRenderObject(); } @@ -83,8 +83,8 @@ class TextControlService : public Object { } this->text_.insert(this->text_.cbegin() + position, text.begin(), text.end()); - if (stop_composition && this->input_method_context_) { - this->input_method_context_->CancelComposition(); + if (stop_composition) { + CancelComposition(); } SyncTextRenderObject(); } @@ -129,15 +129,30 @@ class TextControlService : public Object { this->text_.erase(this->text_.cbegin() + range.GetStart(), this->text_.cbegin() + range.GetEnd()); this->CoerceSelection(); - if (stop_composition && this->input_method_context_) { - this->input_method_context_->CancelComposition(); + if (stop_composition) { + CancelComposition(); } this->SyncTextRenderObject(); } + platform::native::IInputMethodContext* GetInputMethodContext() { + WindowHost* host = this->control_->GetWindowHost(); + if (!host) return nullptr; + platform::native::INativeWindow* native_window = host->GetNativeWindow(); + if (!native_window) return nullptr; + return native_window->GetInputMethodContext(); + } + + void CancelComposition() { + auto input_method_context = GetInputMethodContext(); + if (input_method_context == nullptr) return; + input_method_context->CancelComposition(); + } + std::optional GetCompositionInfo() { - if (this->input_method_context_ == nullptr) return std::nullopt; - auto composition_info = this->input_method_context_->GetCompositionText(); + auto input_method_context = GetInputMethodContext(); + if (input_method_context == nullptr) return std::nullopt; + auto composition_info = input_method_context->GetCompositionText(); if (composition_info.text.empty()) return std::nullopt; return composition_info; } @@ -319,12 +334,11 @@ class TextControlService : public Object { } void MouseDownHandler(event::MouseButtonEventArgs& args) { - this->control_->RequestFocus(); if (this->select_down_button_.has_value()) { return; } else { + this->control_->SetFocus(); if (!this->control_->CaptureMouse()) return; - if (!this->control_->RequestFocus()) return; const auto text_render_object = this->GetTextRenderObject(); this->select_down_button_ = args.GetButton(); const auto result = text_render_object->TextHitTest( @@ -408,33 +422,35 @@ class TextControlService : public Object { void GainFocusHandler(event::FocusChangeEventArgs& args) { CRU_UNUSED(args); if (editable_) { - WindowHost* ui_host = this->control_->GetWindowHost(); - auto window = ui_host->GetNativeWindowResolver()->Resolve(); - if (window == nullptr) return; - input_method_context_ = - GetUiApplication()->GetInputMethodManager()->GetContext(window); - input_method_context_->EnableIME(); + auto input_method_context = GetInputMethodContext(); + if (input_method_context == nullptr) return; + input_method_context->EnableIME(); auto sync = [this](std::nullptr_t) { this->SyncTextRenderObject(); ScrollToCaret(); }; - input_method_context_->CompositionStartEvent()->AddHandler( - [this](std::nullptr_t) { this->DeleteSelectedText(); }); - input_method_context_->CompositionEvent()->AddHandler(sync); - input_method_context_->CompositionEndEvent()->AddHandler(sync); - input_method_context_->TextEvent()->AddHandler( - [this](const std::u16string_view& text) { - if (text == u"\b") return; - this->ReplaceSelectedText(text); - }); + input_method_context_event_guard_ += + input_method_context->CompositionStartEvent()->AddHandler( + [this](std::nullptr_t) { this->DeleteSelectedText(); }); + input_method_context_event_guard_ += + input_method_context->CompositionEvent()->AddHandler(sync); + input_method_context_event_guard_ += + input_method_context->CompositionEndEvent()->AddHandler(sync); + input_method_context_event_guard_ += + input_method_context->TextEvent()->AddHandler( + [this](const std::u16string_view& text) { + if (text == u"\b") return; + this->ReplaceSelectedText(text); + }); } } void LoseFocusHandler(event::FocusChangeEventArgs& args) { if (!args.IsWindow()) this->AbortSelection(); - if (input_method_context_) { - input_method_context_->DisableIME(); - input_method_context_.reset(); + input_method_context_event_guard_.Clear(); + auto input_method_context = GetInputMethodContext(); + if (input_method_context) { + input_method_context->DisableIME(); } SyncTextRenderObject(); } @@ -442,6 +458,7 @@ class TextControlService : public Object { private: gsl::not_null control_; EventRevokerListGuard event_guard_; + EventRevokerListGuard input_method_context_event_guard_; std::u16string text_; TextRange selection_; @@ -457,7 +474,5 @@ class TextControlService : public Object { // nullopt means not selecting std::optional select_down_button_; - - std::unique_ptr input_method_context_; }; // namespace cru::ui::controls } // namespace cru::ui::controls diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index 5266daaf..fd0c7712 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -24,7 +24,7 @@ void RenderObject::AddChild(RenderObject* render_object, const Index position) { children_.insert(children_.cbegin() + position, render_object); render_object->SetParent(this); - render_object->SetRenderHostRecursive(GetWindowHost()); + render_object->SetWindowHostRecursive(GetWindowHost()); OnAddChild(render_object, position); } @@ -37,7 +37,7 @@ void RenderObject::RemoveChild(const Index position) { const auto removed_child = *i; children_.erase(i); removed_child->SetParent(nullptr); - removed_child->SetRenderHostRecursive(nullptr); + removed_child->SetWindowHostRecursive(nullptr); OnRemoveChild(removed_child, position); } @@ -269,11 +269,11 @@ void RenderObject::SetParent(RenderObject* new_parent) { } void RenderObject::InvalidateLayout() { - if (ui_host_ != nullptr) ui_host_->InvalidateLayout(); + if (window_host_ != nullptr) window_host_->InvalidateLayout(); } void RenderObject::InvalidatePaint() { - if (ui_host_ != nullptr) ui_host_->InvalidatePaint(); + if (window_host_ != nullptr) window_host_->InvalidatePaint(); } constexpr std::u16string_view kUnamedName(u"UNNAMED"); @@ -297,17 +297,16 @@ std::u16string RenderObject::GetDebugPathInTree() const { return result; } -void RenderObject::NotifyAfterLayoutRecursive(RenderObject* render_object) { - render_object->OnAfterLayout(); - for (const auto o : render_object->GetChildren()) { - NotifyAfterLayoutRecursive(o); +void RenderObject::SetWindowHostRecursive(WindowHost* host) { + if (window_host_ != nullptr) { + detach_from_host_event_.Raise(nullptr); + } + window_host_ = host; + if (host != nullptr) { + attach_to_host_event_.Raise(nullptr); } -} - -void RenderObject::SetRenderHostRecursive(WindowHost* host) { - ui_host_ = host; for (const auto child : GetChildren()) { - child->SetRenderHostRecursive(host); + child->SetWindowHostRecursive(host); } } } // namespace cru::ui::render diff --git a/src/ui/render/WindowRenderObject.cpp b/src/ui/render/WindowRenderObject.cpp deleted file mode 100644 index 23cca12e..00000000 --- a/src/ui/render/WindowRenderObject.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "cru/ui/render/WindowRenderObject.hpp" - -#include "../Helper.hpp" -#include "cru/platform/graph/util/Painter.hpp" -#include "cru/ui/WindowHost.hpp" - -namespace cru::ui::render { -WindowRenderObject::WindowRenderObject(WindowHost* host) { - SetChildMode(ChildMode::Single); - ui_host_ = host; - after_layout_event_guard_.Reset(host->AfterLayoutEvent()->AddHandler( - [this](auto) { NotifyAfterLayoutRecursive(this); })); -} - -RenderObject* WindowRenderObject::HitTest(const Point& point) { - if (const auto child = GetChild()) { - auto offset = child->GetOffset(); - Point p{point.x - offset.x, point.y - offset.y}; - const auto result = child->HitTest(p); - if (result != nullptr) { - return result; - } - } - return Rect{Point{}, GetSize()}.IsPointInside(point) ? this : nullptr; -} - -std::u16string_view WindowRenderObject::GetName() const { - return u"WindowRenderObject"; -} - -Size WindowRenderObject::OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) { - if (const auto child = GetChild()) { - child->Measure(requirement, preferred_size); - return child->GetSize(); - } else { - return Size{}; - } -} - -void WindowRenderObject::OnLayoutContent(const Rect& content_rect) { - if (const auto child = GetChild()) child->Layout(content_rect.GetLeftTop()); -} -} // namespace cru::ui::render diff --git a/src/win/native/UiApplication.cpp b/src/win/native/UiApplication.cpp index 60ff8e8c..87ef0b81 100644 --- a/src/win/native/UiApplication.cpp +++ b/src/win/native/UiApplication.cpp @@ -43,7 +43,6 @@ WinUiApplication::WinUiApplication() { timer_manager_ = std::make_unique(god_window_.get()); window_manager_ = std::make_unique(this); cursor_manager_ = std::make_unique(); - input_method_manager_ = std::make_unique(this); } WinUiApplication::~WinUiApplication() { instance = nullptr; } @@ -116,8 +115,4 @@ cru::platform::graph::IGraphFactory* WinUiApplication::GetGraphFactory() { ICursorManager* WinUiApplication::GetCursorManager() { return cursor_manager_.get(); } - -IInputMethodManager* WinUiApplication::GetInputMethodManager() { - return input_method_manager_.get(); -} } // namespace cru::platform::native::win diff --git a/src/win/native/Window.cpp b/src/win/native/Window.cpp index d9237c4f..1a6fcb07 100644 --- a/src/win/native/Window.cpp +++ b/src/win/native/Window.cpp @@ -55,6 +55,7 @@ WinNativeWindow::WinNativeWindow(WinUiApplication* application, window_render_target_->SetDpi(dpi_, dpi_); input_method_context_ = std::make_unique(this); + input_method_context_->DisableIME(); } WinNativeWindow::~WinNativeWindow() { -- cgit v1.2.3 From 2188845a7acffa653015a1000139ec0a9a3984bc Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 8 Nov 2020 17:45:41 +0800 Subject: ... --- demos/main/main.cpp | 6 +- include/cru/platform/gui/UiApplication.hpp | 19 ++- include/cru/ui/Base.hpp | 4 +- include/cru/ui/ClickDetector.hpp | 87 ----------- include/cru/ui/ContentControl.hpp | 26 ---- include/cru/ui/Control.hpp | 151 ------------------- include/cru/ui/LayoutControl.hpp | 19 --- include/cru/ui/NoChildControl.hpp | 20 --- include/cru/ui/ShortcutHub.hpp | 130 ---------------- include/cru/ui/UiEvent.hpp | 228 ----------------------------- include/cru/ui/Window.hpp | 36 ----- include/cru/ui/controls/Base.hpp | 10 +- include/cru/ui/controls/Button.hpp | 7 +- include/cru/ui/controls/Container.hpp | 2 +- include/cru/ui/controls/ContentControl.hpp | 26 ++++ include/cru/ui/controls/Control.hpp | 151 +++++++++++++++++++ include/cru/ui/controls/FlexLayout.hpp | 2 +- include/cru/ui/controls/LayoutControl.hpp | 19 +++ include/cru/ui/controls/NoChildControl.hpp | 20 +++ include/cru/ui/controls/StackLayout.hpp | 2 +- include/cru/ui/controls/TextBlock.hpp | 2 +- include/cru/ui/controls/TextBox.hpp | 3 +- include/cru/ui/controls/Window.hpp | 36 +++++ include/cru/ui/events/UiEvent.hpp | 228 +++++++++++++++++++++++++++++ include/cru/ui/helper/ClickDetector.hpp | 87 +++++++++++ include/cru/ui/helper/ShortcutHub.hpp | 129 ++++++++++++++++ include/cru/ui/host/WindowHost.hpp | 50 +++---- include/cru/ui/render/RenderObject.hpp | 12 +- include/cru/win/gui/UiApplication.hpp | 2 +- src/ui/CMakeLists.txt | 32 ++-- src/ui/ClickDetector.cpp | 131 ----------------- src/ui/ContentControl.cpp | 27 ---- src/ui/Control.cpp | 157 -------------------- src/ui/LayoutControl.cpp | 6 - src/ui/NoChildControl.cpp | 3 - src/ui/ShortcutHub.cpp | 120 --------------- src/ui/UiEvent.cpp | 10 -- src/ui/Window.cpp | 32 ---- src/ui/controls/Button.cpp | 17 +-- src/ui/controls/ContentControl.cpp | 25 ++++ src/ui/controls/Control.cpp | 157 ++++++++++++++++++++ src/ui/controls/LayoutControl.cpp | 3 + src/ui/controls/NoChildControl.cpp | 3 + src/ui/controls/TextControlService.hpp | 8 +- src/ui/controls/Window.cpp | 32 ++++ src/ui/events/UiEvent.cpp | 10 ++ src/ui/helper/ClickDetector.cpp | 131 +++++++++++++++++ src/ui/helper/ShortcutHub.cpp | 120 +++++++++++++++ src/ui/host/RoutedEventDispatch.hpp | 14 +- src/ui/host/WindowHost.cpp | 83 ++++++----- src/win/gui/UiApplication.cpp | 12 +- 51 files changed, 1333 insertions(+), 1314 deletions(-) delete mode 100644 include/cru/ui/ClickDetector.hpp delete mode 100644 include/cru/ui/ContentControl.hpp delete mode 100644 include/cru/ui/Control.hpp delete mode 100644 include/cru/ui/LayoutControl.hpp delete mode 100644 include/cru/ui/NoChildControl.hpp delete mode 100644 include/cru/ui/ShortcutHub.hpp delete mode 100644 include/cru/ui/UiEvent.hpp delete mode 100644 include/cru/ui/Window.hpp create mode 100644 include/cru/ui/controls/ContentControl.hpp create mode 100644 include/cru/ui/controls/Control.hpp create mode 100644 include/cru/ui/controls/LayoutControl.hpp create mode 100644 include/cru/ui/controls/NoChildControl.hpp create mode 100644 include/cru/ui/controls/Window.hpp create mode 100644 include/cru/ui/events/UiEvent.hpp create mode 100644 include/cru/ui/helper/ClickDetector.hpp create mode 100644 include/cru/ui/helper/ShortcutHub.hpp delete mode 100644 src/ui/ClickDetector.cpp delete mode 100644 src/ui/ContentControl.cpp delete mode 100644 src/ui/Control.cpp delete mode 100644 src/ui/LayoutControl.cpp delete mode 100644 src/ui/NoChildControl.cpp delete mode 100644 src/ui/ShortcutHub.cpp delete mode 100644 src/ui/UiEvent.cpp delete mode 100644 src/ui/Window.cpp create mode 100644 src/ui/controls/ContentControl.cpp create mode 100644 src/ui/controls/Control.cpp create mode 100644 src/ui/controls/LayoutControl.cpp create mode 100644 src/ui/controls/NoChildControl.cpp create mode 100644 src/ui/controls/Window.cpp create mode 100644 src/ui/events/UiEvent.cpp create mode 100644 src/ui/helper/ClickDetector.cpp create mode 100644 src/ui/helper/ShortcutHub.cpp (limited to 'src/ui/LayoutControl.cpp') diff --git a/demos/main/main.cpp b/demos/main/main.cpp index 5546f3dd..dd3e31e9 100644 --- a/demos/main/main.cpp +++ b/demos/main/main.cpp @@ -2,19 +2,19 @@ #include "cru/platform/gui/UiApplication.hpp" #include "cru/platform/gui/Window.hpp" #include "cru/ui/Base.hpp" -#include "cru/ui/Window.hpp" -#include "cru/ui/host/WindowHost.hpp" #include "cru/ui/controls/Button.hpp" #include "cru/ui/controls/FlexLayout.hpp" #include "cru/ui/controls/TextBlock.hpp" #include "cru/ui/controls/TextBox.hpp" +#include "cru/ui/controls/Window.hpp" +#include "cru/ui/host/WindowHost.hpp" using cru::platform::gui::CreateUiApplication; -using cru::ui::Window; using cru::ui::controls::Button; using cru::ui::controls::FlexLayout; using cru::ui::controls::TextBlock; using cru::ui::controls::TextBox; +using cru::ui::controls::Window; int main() { #ifdef CRU_DEBUG diff --git a/include/cru/platform/gui/UiApplication.hpp b/include/cru/platform/gui/UiApplication.hpp index ff947dfc..ba85020a 100644 --- a/include/cru/platform/gui/UiApplication.hpp +++ b/include/cru/platform/gui/UiApplication.hpp @@ -1,12 +1,24 @@ #pragma once #include "Base.hpp" +#include "cru/common/Bitmask.hpp" + #include #include #include #include namespace cru::platform::gui { +namespace details { +struct CreateWindowFlagTag; +} + +using CreateWindowFlag = Bitmask; + +struct CreateWindowFlags { + static constexpr CreateWindowFlag NoCaptionAndBorder{0b1}; +}; + // The entry point of a ui application. struct IUiApplication : public virtual INativeResource { public: @@ -43,7 +55,12 @@ struct IUiApplication : public virtual INativeResource { virtual void CancelTimer(long long id) = 0; virtual std::vector GetAllWindow() = 0; - virtual INativeWindow* CreateWindow(INativeWindow* parent) = 0; + + INativeWindow* CreateWindow(INativeWindow* parent) { + return this->CreateWindow(parent, CreateWindowFlag(0)); + }; + virtual INativeWindow* CreateWindow(INativeWindow* parent, + CreateWindowFlag flags) = 0; virtual cru::platform::graphics::IGraphFactory* GetGraphFactory() = 0; diff --git a/include/cru/ui/Base.hpp b/include/cru/ui/Base.hpp index 39fbb035..8595258d 100644 --- a/include/cru/ui/Base.hpp +++ b/include/cru/ui/Base.hpp @@ -26,9 +26,11 @@ namespace mouse_buttons = cru::platform::gui::mouse_buttons; namespace colors = cru::platform::colors; //-------------------- region: forward declaration -------------------- + +namespace controls { class Window; class Control; -class ClickDetector; +} // namespace controls namespace host { class WindowHost; diff --git a/include/cru/ui/ClickDetector.hpp b/include/cru/ui/ClickDetector.hpp deleted file mode 100644 index 4ffe5d05..00000000 --- a/include/cru/ui/ClickDetector.hpp +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once -#include "Control.hpp" - -namespace cru::ui { -class ClickEventArgs : Object { - public: - ClickEventArgs(Control* sender, const Point& down_point, - const Point& up_point, MouseButton button) - : sender_(sender), - down_point_(down_point), - up_point_(up_point), - button_(button) {} - - CRU_DEFAULT_COPY(ClickEventArgs) - CRU_DEFAULT_MOVE(ClickEventArgs) - - ~ClickEventArgs() override = default; - - Control* GetSender() const { return sender_; } - Point GetDownPoint() const { return down_point_; } - Point GetUpPoint() const { return up_point_; } - MouseButton GetButton() const { return button_; } - - private: - Control* sender_; - Point down_point_; - Point up_point_; - MouseButton button_; -}; - -enum class ClickState { - None, // Mouse is outside the control. - Hover, // Mouse hovers on the control but not pressed - Press, // Mouse is pressed and if released click is done. - PressInactive // Mouse is pressed but if released click is canceled. -}; - -class ClickDetector : public Object { - CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::ClickDetector") - - public: - explicit ClickDetector(Control* control); - - CRU_DELETE_COPY(ClickDetector) - CRU_DELETE_MOVE(ClickDetector) - - ~ClickDetector() override = default; - - Control* GetControl() const { return control_; } - - ClickState GetState() const { return state_; } - - // Default is enable. - bool IsEnabled() const { return enable_; } - // If disable when user is pressing, the pressing is deactivated. - void SetEnabled(bool enable); - - // Default is left and right. - MouseButton GetTriggerButton() const { return trigger_button_; } - // If unset the trigger button when user is pressing, the pressing is - // deactivated. - void SetTriggerButton(MouseButton trigger_button); - - IEvent* ClickEvent() { return &event_; } - - IEvent* StateChangeEvent() { return &state_change_event_; } - - private: - void SetState(ClickState state); - - private: - Control* control_; - - ClickState state_; - - bool enable_ = true; - MouseButton trigger_button_ = mouse_buttons::left | mouse_buttons::right; - - Event event_; - Event state_change_event_; - - std::vector event_rovoker_guards_; - - Point down_point_; - MouseButton button_; -}; -} // namespace cru::ui diff --git a/include/cru/ui/ContentControl.hpp b/include/cru/ui/ContentControl.hpp deleted file mode 100644 index ba5b6b2f..00000000 --- a/include/cru/ui/ContentControl.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once -#include "Control.hpp" - -namespace cru::ui { -class ContentControl : public Control { - protected: - ContentControl() = default; - - public: - ContentControl(const ContentControl& other) = delete; - ContentControl(ContentControl&& other) = delete; - ContentControl& operator=(const ContentControl& other) = delete; - ContentControl& operator=(ContentControl&& other) = delete; - ~ContentControl() override = default; - - Control* GetChild() const; - void SetChild(Control* child); - - protected: - virtual void OnChildChanged(Control* old_child, Control* new_child); - - private: - using Control::AddChild; - using Control::RemoveChild; -}; -} // namespace cru::ui diff --git a/include/cru/ui/Control.hpp b/include/cru/ui/Control.hpp deleted file mode 100644 index fe50624a..00000000 --- a/include/cru/ui/Control.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include "UiEvent.hpp" -#include "cru/common/Event.hpp" -#include "render/Base.hpp" - -#include - -namespace cru::ui { -class Control : public Object { - friend host::WindowHost; - - protected: - Control(); - - public: - Control(const Control& other) = delete; - Control(Control&& other) = delete; - Control& operator=(const Control& other) = delete; - Control& operator=(Control&& other) = delete; - ~Control() override; - - public: - virtual std::u16string_view GetControlType() const = 0; - - //*************** region: tree *************** - public: - host::WindowHost* GetWindowHost() const; - - Control* GetParent() const { return parent_; } - - const std::vector& GetChildren() const { return children_; } - - // Traverse the tree rooted the control including itself. - void TraverseDescendants(const std::function& predicate); - - public: - virtual render::RenderObject* GetRenderObject() const = 0; - - //*************** region: focus *************** - public: - bool HasFocus(); - - void SetFocus(); - - //*************** region: mouse *************** - public: - bool IsMouseOver() const { return is_mouse_over_; } - - bool CaptureMouse(); - - bool ReleaseMouse(); - - bool IsMouseCaptured(); - - //*************** region: cursor *************** - // Cursor is inherited from parent recursively if not set. - public: - // null for not set - std::shared_ptr GetCursor(); - - // will not return nullptr - std::shared_ptr GetInheritedCursor(); - - // null to unset - void SetCursor(std::shared_ptr cursor); - - //*************** region: events *************** - public: - // Raised when mouse enter the control. Even when the control itself captures - // the mouse, this event is raised as regular. But if mouse is captured by - // another control, the control will not receive any mouse enter event. You - // can use `IsMouseCaptured` to get more info. - event::RoutedEvent* MouseEnterEvent() { - return &mouse_enter_event_; - } - // Raised when mouse is leave the control. Even when the control itself - // captures the mouse, this event is raised as regular. But if mouse is - // captured by another control, the control will not receive any mouse leave - // event. You can use `IsMouseCaptured` to get more info. - event::RoutedEvent* MouseLeaveEvent() { - return &mouse_leave_event_; - } - // Raised when mouse is move in the control. - event::RoutedEvent* MouseMoveEvent() { - return &mouse_move_event_; - } - // Raised when a mouse button is pressed in the control. - event::RoutedEvent* MouseDownEvent() { - return &mouse_down_event_; - } - // Raised when a mouse button is released in the control. - event::RoutedEvent* MouseUpEvent() { - return &mouse_up_event_; - } - event::RoutedEvent* MouseWheelEvent() { - return &mouse_wheel_event_; - } - event::RoutedEvent* KeyDownEvent() { - return &key_down_event_; - } - event::RoutedEvent* KeyUpEvent() { - return &key_up_event_; - } - event::RoutedEvent* GainFocusEvent() { - return &gain_focus_event_; - } - event::RoutedEvent* LoseFocusEvent() { - return &lose_focus_event_; - } - - private: - event::RoutedEvent mouse_enter_event_; - event::RoutedEvent mouse_leave_event_; - event::RoutedEvent mouse_move_event_; - event::RoutedEvent mouse_down_event_; - event::RoutedEvent mouse_up_event_; - event::RoutedEvent mouse_wheel_event_; - - event::RoutedEvent key_down_event_; - event::RoutedEvent key_up_event_; - - event::RoutedEvent gain_focus_event_; - event::RoutedEvent lose_focus_event_; - - //*************** region: tree *************** - protected: - void AddChild(Control* control, Index position); - void RemoveChild(Index position); - virtual void OnAddChild(Control* child, Index position); - virtual void OnRemoveChild(Control* child, Index position); - virtual void OnParentChanged(Control* old_parent, Control* new_parent); - virtual void OnAttachToHost(host::WindowHost* host); - virtual void OnDetachFromHost(host::WindowHost* host); - - protected: - virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) } - - private: - Control* parent_ = nullptr; - std::vector children_; - - host::WindowHost* window_host_ = nullptr; - - private: - bool is_mouse_over_ = false; - - std::shared_ptr cursor_ = nullptr; -}; -} // namespace cru::ui diff --git a/include/cru/ui/LayoutControl.hpp b/include/cru/ui/LayoutControl.hpp deleted file mode 100644 index 69d5cd0b..00000000 --- a/include/cru/ui/LayoutControl.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include "Control.hpp" - -namespace cru::ui { -class LayoutControl : public Control { - protected: - LayoutControl() = default; - - public: - LayoutControl(const LayoutControl& other) = delete; - LayoutControl(LayoutControl&& other) = delete; - LayoutControl& operator=(const LayoutControl& other) = delete; - LayoutControl& operator=(LayoutControl&& other) = delete; - ~LayoutControl() override = default; - - using Control::AddChild; - using Control::RemoveChild; -}; -} // namespace cru::ui diff --git a/include/cru/ui/NoChildControl.hpp b/include/cru/ui/NoChildControl.hpp deleted file mode 100644 index 0d8a8e34..00000000 --- a/include/cru/ui/NoChildControl.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "Control.hpp" - -namespace cru::ui { -class NoChildControl : public Control { - protected: - NoChildControl() = default; - - public: - NoChildControl(const NoChildControl& other) = delete; - NoChildControl(NoChildControl&& other) = delete; - NoChildControl& operator=(const NoChildControl& other) = delete; - NoChildControl& operator=(NoChildControl&& other) = delete; - ~NoChildControl() override = default; - - private: - using Control::AddChild; - using Control::RemoveChild; -}; -} // namespace cru::ui diff --git a/include/cru/ui/ShortcutHub.hpp b/include/cru/ui/ShortcutHub.hpp deleted file mode 100644 index 1145c661..00000000 --- a/include/cru/ui/ShortcutHub.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include "cru/common/Base.hpp" -#include "cru/common/Event.hpp" -#include "cru/platform/gui/Keyboard.hpp" -#include "cru/ui/UiEvent.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cru::ui { - -class ShortcutKeyBind { - public: - ShortcutKeyBind(platform::gui::KeyCode key, - platform::gui::KeyModifier modifier) - : key_(key), modifier_(modifier) {} - - CRU_DEFAULT_COPY(ShortcutKeyBind) - CRU_DEFAULT_MOVE(ShortcutKeyBind) - - ~ShortcutKeyBind() = default; - - platform::gui::KeyCode GetKey() const { return key_; } - platform::gui::KeyModifier GetModifier() const { return modifier_; } - - bool Is(platform::gui::KeyCode key, - platform::gui::KeyModifier modifier) const { - return key == key_ && modifier == modifier_; - } - - bool operator==(const ShortcutKeyBind& other) const { - return this->key_ == other.key_ && this->modifier_ == other.modifier_; - } - - bool operator!=(const ShortcutKeyBind& other) const { - return !this->operator==(other); - } - - std::u16string ToString() { - std::u16string result = u"("; - result += platform::gui::ToString(modifier_); - result += u")"; - result += platform::gui::ToString(key_); - return result; - } - - private: - platform::gui::KeyCode key_; - platform::gui::KeyModifier modifier_; -}; -} // namespace cru::ui - -namespace std { -template <> -struct hash { - std::size_t operator()(const cru::ui::ShortcutKeyBind& value) const { - std::size_t result = 0; - cru::hash_combine(result, static_cast(value.GetKey())); - cru::hash_combine(result, static_cast(value.GetModifier())); - return result; - } -}; -} // namespace std - -namespace cru::ui { -struct Shortcut { - // Just for debug. - std::u16string name; - ShortcutKeyBind key_bind; - // Return true if it consumes the shortcut. Or return false if it does not - // handle the shortcut. - std::function handler; -}; - -struct ShortcutInfo { - int id; - std::u16string name; - ShortcutKeyBind key_bind; - std::function handler; -}; - -class ShortcutHub : public Object { - public: - ShortcutHub() = default; - - CRU_DELETE_COPY(ShortcutHub) - CRU_DELETE_MOVE(ShortcutHub) - - ~ShortcutHub() override = default; - - int RegisterShortcut(std::u16string name, ShortcutKeyBind bind, - std::function handler) { - return RegisterShortcut({std::move(name), bind, std::move(handler)}); - } - - // Return an id used for unregistering. - int RegisterShortcut(Shortcut shortcut); - - void UnregisterShortcut(int id); - - std::vector GetAllShortcuts() const; - std::optional GetShortcut(int id) const; - const std::vector& GetShortcutByKeyBind( - const ShortcutKeyBind& key_bind) const; - - void Install(Control* control); - void Uninstall(); - - private: - void OnKeyDown(event::KeyEventArgs& event); - - private: - std::unordered_map> map_; - - const std::vector empty_list_; - - int current_id_ = 1; - - EventRevokerListGuard event_guard_; -}; -} // namespace cru::ui diff --git a/include/cru/ui/UiEvent.hpp b/include/cru/ui/UiEvent.hpp deleted file mode 100644 index c0b2a902..00000000 --- a/include/cru/ui/UiEvent.hpp +++ /dev/null @@ -1,228 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include "cru/common/Event.hpp" -#include "cru/platform/gui/Keyboard.hpp" - -#include -#include -#include -#include - -namespace cru::platform::graphics { -struct IPainter; -} - -namespace cru::ui::event { -class UiEventArgs : public Object { - public: - UiEventArgs(Object* sender, Object* original_sender) - : sender_(sender), original_sender_(original_sender), handled_(false) {} - - UiEventArgs(const UiEventArgs& other) = default; - UiEventArgs(UiEventArgs&& other) = default; - UiEventArgs& operator=(const UiEventArgs& other) = default; - UiEventArgs& operator=(UiEventArgs&& other) = default; - ~UiEventArgs() override = default; - - Object* GetSender() const { return sender_; } - - Object* GetOriginalSender() const { return original_sender_; } - - bool IsHandled() const { return handled_; } - void SetHandled(const bool handled = true) { handled_ = handled; } - - private: - Object* sender_; - Object* original_sender_; - bool handled_; -}; - -// TEventArgs must not be a reference type. This class help add reference. -// EventArgs must be reference because the IsHandled property must be settable. -template -class RoutedEvent { - public: - static_assert(std::is_base_of_v, - "TEventArgs must be subclass of UiEventArgs."); - static_assert(!std::is_reference_v, - "TEventArgs must not be reference."); - - using RawEventArgs = TEventArgs; - using IEventType = IEvent; - using EventArgs = typename IEventType::EventArgs; - - RoutedEvent() = default; - RoutedEvent(const RoutedEvent& other) = delete; - RoutedEvent(RoutedEvent&& other) = delete; - RoutedEvent& operator=(const RoutedEvent& other) = delete; - RoutedEvent& operator=(RoutedEvent&& other) = delete; - ~RoutedEvent() = default; - - IEvent* Direct() { return &direct_; } - - IEvent* Bubble() { return &bubble_; } - - IEvent* Tunnel() { return &tunnel_; } - - private: - Event direct_; - Event bubble_; - Event tunnel_; -}; - -class MouseEventArgs : public UiEventArgs { - public: - MouseEventArgs(Object* sender, Object* original_sender, - const std::optional& point = std::nullopt) - : UiEventArgs(sender, original_sender), point_(point) {} - MouseEventArgs(const MouseEventArgs& other) = default; - MouseEventArgs(MouseEventArgs&& other) = default; - MouseEventArgs& operator=(const MouseEventArgs& other) = default; - MouseEventArgs& operator=(MouseEventArgs&& other) = default; - ~MouseEventArgs() override = default; - - // This point is relative to window client lefttop. - Point GetPoint() const { return point_.value_or(Point{}); } - Point GetPointToContent(render::RenderObject* render_target) const; - - private: - std::optional point_; -}; - -class MouseButtonEventArgs : public MouseEventArgs { - public: - MouseButtonEventArgs(Object* sender, Object* original_sender, - const Point& point, const MouseButton button, - platform::gui::KeyModifier key_modifier) - : MouseEventArgs(sender, original_sender, point), - button_(button), - key_modifier_(key_modifier) {} - MouseButtonEventArgs(Object* sender, Object* original_sender, - const MouseButton button, - platform::gui::KeyModifier key_modifier) - : MouseEventArgs(sender, original_sender), - button_(button), - key_modifier_(key_modifier) {} - MouseButtonEventArgs(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs(MouseButtonEventArgs&& other) = default; - MouseButtonEventArgs& operator=(const MouseButtonEventArgs& other) = default; - MouseButtonEventArgs& operator=(MouseButtonEventArgs&& other) = default; - ~MouseButtonEventArgs() override = default; - - MouseButton GetButton() const { return button_; } - platform::gui::KeyModifier GetKeyModifier() const { return key_modifier_; } - - private: - MouseButton button_; - platform::gui::KeyModifier key_modifier_; -}; - -class MouseWheelEventArgs : public MouseEventArgs { - public: - MouseWheelEventArgs(Object* sender, Object* original_sender, - const Point& point, const float delta) - : MouseEventArgs(sender, original_sender, point), delta_(delta) {} - MouseWheelEventArgs(const MouseWheelEventArgs& other) = default; - MouseWheelEventArgs(MouseWheelEventArgs&& other) = default; - MouseWheelEventArgs& operator=(const MouseWheelEventArgs& other) = default; - MouseWheelEventArgs& operator=(MouseWheelEventArgs&& other) = default; - ~MouseWheelEventArgs() override = default; - - float GetDelta() const { return delta_; } - - private: - float delta_; -}; - -class PaintEventArgs : public UiEventArgs { - public: - PaintEventArgs(Object* sender, Object* original_sender, - platform::graphics::IPainter* painter) - : UiEventArgs(sender, original_sender), painter_(painter) {} - PaintEventArgs(const PaintEventArgs& other) = default; - PaintEventArgs(PaintEventArgs&& other) = default; - PaintEventArgs& operator=(const PaintEventArgs& other) = default; - PaintEventArgs& operator=(PaintEventArgs&& other) = default; - ~PaintEventArgs() = default; - - platform::graphics::IPainter* GetPainter() const { return painter_; } - - private: - platform::graphics::IPainter* painter_; -}; - -class FocusChangeEventArgs : public UiEventArgs { - public: - FocusChangeEventArgs(Object* sender, Object* original_sender, - const bool is_window = false) - : UiEventArgs(sender, original_sender), is_window_(is_window) {} - FocusChangeEventArgs(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs(FocusChangeEventArgs&& other) = default; - FocusChangeEventArgs& operator=(const FocusChangeEventArgs& other) = default; - FocusChangeEventArgs& operator=(FocusChangeEventArgs&& other) = default; - ~FocusChangeEventArgs() override = default; - - // Return whether the focus change is caused by the window-wide focus change. - bool IsWindow() const { return is_window_; } - - private: - bool is_window_; -}; - -/* -class ToggleEventArgs : public UiEventArgs { - public: - ToggleEventArgs(Object* sender, Object* original_sender, bool new_state) - : UiEventArgs(sender, original_sender), new_state_(new_state) {} - ToggleEventArgs(const ToggleEventArgs& other) = default; - ToggleEventArgs(ToggleEventArgs&& other) = default; - ToggleEventArgs& operator=(const ToggleEventArgs& other) = default; - ToggleEventArgs& operator=(ToggleEventArgs&& other) = default; - ~ToggleEventArgs() override = default; - - bool GetNewState() const { return new_state_; } - - private: - bool new_state_; -}; -*/ - -class KeyEventArgs : public UiEventArgs { - public: - KeyEventArgs(Object* sender, Object* original_sender, - platform::gui::KeyCode key_code, - platform::gui::KeyModifier key_modifier) - : UiEventArgs(sender, original_sender), - key_code_(key_code), - key_modifier_(key_modifier) {} - KeyEventArgs(const KeyEventArgs& other) = default; - KeyEventArgs(KeyEventArgs&& other) = default; - KeyEventArgs& operator=(const KeyEventArgs& other) = default; - KeyEventArgs& operator=(KeyEventArgs&& other) = default; - ~KeyEventArgs() override = default; - - platform::gui::KeyCode GetKeyCode() const { return key_code_; } - platform::gui::KeyModifier GetKeyModifier() const { return key_modifier_; } - - private: - platform::gui::KeyCode key_code_; - platform::gui::KeyModifier key_modifier_; -}; - -class CharEventArgs : public UiEventArgs { - public: - CharEventArgs(Object* sender, Object* original_sender, std::u16string c) - : UiEventArgs(sender, original_sender), c_(std::move(c)) {} - CharEventArgs(const CharEventArgs& other) = default; - CharEventArgs(CharEventArgs&& other) = default; - CharEventArgs& operator=(const CharEventArgs& other) = default; - CharEventArgs& operator=(CharEventArgs&& other) = default; - ~CharEventArgs() override = default; - - std::u16string GetChar() const { return c_; } - - private: - std::u16string c_; -}; -} // namespace cru::ui::event diff --git a/include/cru/ui/Window.hpp b/include/cru/ui/Window.hpp deleted file mode 100644 index 70423a14..00000000 --- a/include/cru/ui/Window.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "LayoutControl.hpp" - -namespace cru::ui { -class Window final : public LayoutControl { - public: - static constexpr std::u16string_view control_type = u"Window"; - - public: - static Window* CreateOverlapped(); - - private: - Window(); - - public: - Window(const Window& other) = delete; - Window(Window&& other) = delete; - Window& operator=(const Window& other) = delete; - Window& operator=(Window&& other) = delete; - ~Window() override; - - public: - std::u16string_view GetControlType() const final; - - render::RenderObject* GetRenderObject() const override; - - protected: - void OnAddChild(Control* child, Index position) override; - void OnRemoveChild(Control* child, Index position) override; - - private: - std::unique_ptr window_host_; - - std::unique_ptr render_object_; -}; -} // namespace cru::ui diff --git a/include/cru/ui/controls/Base.hpp b/include/cru/ui/controls/Base.hpp index b550601b..82c31d1e 100644 --- a/include/cru/ui/controls/Base.hpp +++ b/include/cru/ui/controls/Base.hpp @@ -2,7 +2,7 @@ #include "../Base.hpp" namespace cru::ui::controls { -using ButtonStateStyle = BorderStyle; +using ButtonStateStyle = ui::BorderStyle; struct ButtonStyle { // corresponds to ClickState::None @@ -16,9 +16,9 @@ struct ButtonStyle { }; struct TextBoxBorderStyle { - BorderStyle normal; - BorderStyle hover; - BorderStyle focus; - BorderStyle focus_hover; + ui::BorderStyle normal; + ui::BorderStyle hover; + ui::BorderStyle focus; + ui::BorderStyle focus_hover; }; } // namespace cru::ui::controls diff --git a/include/cru/ui/controls/Button.hpp b/include/cru/ui/controls/Button.hpp index a4f727d6..e8285507 100644 --- a/include/cru/ui/controls/Button.hpp +++ b/include/cru/ui/controls/Button.hpp @@ -1,8 +1,7 @@ #pragma once -#include "../ContentControl.hpp" -#include "Base.hpp" +#include "ContentControl.hpp" -#include "../ClickDetector.hpp" +#include "../helper/ClickDetector.hpp" namespace cru::ui::controls { class Button : public ContentControl { @@ -37,6 +36,6 @@ class Button : public ContentControl { ButtonStyle style_; - ClickDetector click_detector_; + helper::ClickDetector click_detector_; }; } // namespace cru::ui::controls diff --git a/include/cru/ui/controls/Container.hpp b/include/cru/ui/controls/Container.hpp index 304d402c..d9cb8aec 100644 --- a/include/cru/ui/controls/Container.hpp +++ b/include/cru/ui/controls/Container.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../ContentControl.hpp" +#include "ContentControl.hpp" namespace cru::ui::controls { class Container : public ContentControl { diff --git a/include/cru/ui/controls/ContentControl.hpp b/include/cru/ui/controls/ContentControl.hpp new file mode 100644 index 00000000..47720a87 --- /dev/null +++ b/include/cru/ui/controls/ContentControl.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "Control.hpp" + +namespace cru::ui::controls { +class ContentControl : public Control { + protected: + ContentControl() = default; + + public: + ContentControl(const ContentControl& other) = delete; + ContentControl(ContentControl&& other) = delete; + ContentControl& operator=(const ContentControl& other) = delete; + ContentControl& operator=(ContentControl&& other) = delete; + ~ContentControl() override = default; + + Control* GetChild() const; + void SetChild(Control* child); + + protected: + virtual void OnChildChanged(Control* old_child, Control* new_child); + + private: + using Control::AddChild; + using Control::RemoveChild; +}; +} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/Control.hpp b/include/cru/ui/controls/Control.hpp new file mode 100644 index 00000000..96aad2bd --- /dev/null +++ b/include/cru/ui/controls/Control.hpp @@ -0,0 +1,151 @@ +#pragma once +#include "Base.hpp" + +#include "../events/UiEvent.hpp" +#include "../render/Base.hpp" +#include "cru/common/Event.hpp" + +#include + +namespace cru::ui::controls { +class Control : public Object { + friend host::WindowHost; + + protected: + Control(); + + public: + Control(const Control& other) = delete; + Control(Control&& other) = delete; + Control& operator=(const Control& other) = delete; + Control& operator=(Control&& other) = delete; + ~Control() override; + + public: + virtual std::u16string_view GetControlType() const = 0; + + //*************** region: tree *************** + public: + host::WindowHost* GetWindowHost() const; + + Control* GetParent() const { return parent_; } + + const std::vector& GetChildren() const { return children_; } + + // Traverse the tree rooted the control including itself. + void TraverseDescendants(const std::function& predicate); + + public: + virtual render::RenderObject* GetRenderObject() const = 0; + + //*************** region: focus *************** + public: + bool HasFocus(); + + void SetFocus(); + + //*************** region: mouse *************** + public: + bool IsMouseOver() const { return is_mouse_over_; } + + bool CaptureMouse(); + + bool ReleaseMouse(); + + bool IsMouseCaptured(); + + //*************** region: cursor *************** + // Cursor is inherited from parent recursively if not set. + public: + // null for not set + std::shared_ptr GetCursor(); + + // will not return nullptr + std::shared_ptr GetInheritedCursor(); + + // null to unset + void SetCursor(std::shared_ptr cursor); + + //*************** region: events *************** + public: + // Raised when mouse enter the control. Even when the control itself captures + // the mouse, this event is raised as regular. But if mouse is captured by + // another control, the control will not receive any mouse enter event. You + // can use `IsMouseCaptured` to get more info. + event::RoutedEvent* MouseEnterEvent() { + return &mouse_enter_event_; + } + // Raised when mouse is leave the control. Even when the control itself + // captures the mouse, this event is raised as regular. But if mouse is + // captured by another control, the control will not receive any mouse leave + // event. You can use `IsMouseCaptured` to get more info. + event::RoutedEvent* MouseLeaveEvent() { + return &mouse_leave_event_; + } + // Raised when mouse is move in the control. + event::RoutedEvent* MouseMoveEvent() { + return &mouse_move_event_; + } + // Raised when a mouse button is pressed in the control. + event::RoutedEvent* MouseDownEvent() { + return &mouse_down_event_; + } + // Raised when a mouse button is released in the control. + event::RoutedEvent* MouseUpEvent() { + return &mouse_up_event_; + } + event::RoutedEvent* MouseWheelEvent() { + return &mouse_wheel_event_; + } + event::RoutedEvent* KeyDownEvent() { + return &key_down_event_; + } + event::RoutedEvent* KeyUpEvent() { + return &key_up_event_; + } + event::RoutedEvent* GainFocusEvent() { + return &gain_focus_event_; + } + event::RoutedEvent* LoseFocusEvent() { + return &lose_focus_event_; + } + + private: + event::RoutedEvent mouse_enter_event_; + event::RoutedEvent mouse_leave_event_; + event::RoutedEvent mouse_move_event_; + event::RoutedEvent mouse_down_event_; + event::RoutedEvent mouse_up_event_; + event::RoutedEvent mouse_wheel_event_; + + event::RoutedEvent key_down_event_; + event::RoutedEvent key_up_event_; + + event::RoutedEvent gain_focus_event_; + event::RoutedEvent lose_focus_event_; + + //*************** region: tree *************** + protected: + void AddChild(Control* control, Index position); + void RemoveChild(Index position); + virtual void OnAddChild(Control* child, Index position); + virtual void OnRemoveChild(Control* child, Index position); + virtual void OnParentChanged(Control* old_parent, Control* new_parent); + virtual void OnAttachToHost(host::WindowHost* host); + virtual void OnDetachFromHost(host::WindowHost* host); + + protected: + virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) } + + private: + Control* parent_ = nullptr; + std::vector children_; + + host::WindowHost* window_host_ = nullptr; + + private: + bool is_mouse_over_ = false; + + std::shared_ptr cursor_ = nullptr; +}; +} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/FlexLayout.hpp b/include/cru/ui/controls/FlexLayout.hpp index 0ffedba5..a6c6a40c 100644 --- a/include/cru/ui/controls/FlexLayout.hpp +++ b/include/cru/ui/controls/FlexLayout.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../LayoutControl.hpp" +#include "LayoutControl.hpp" namespace cru::ui::controls { class FlexLayout : public LayoutControl { diff --git a/include/cru/ui/controls/LayoutControl.hpp b/include/cru/ui/controls/LayoutControl.hpp new file mode 100644 index 00000000..cbdb8aa2 --- /dev/null +++ b/include/cru/ui/controls/LayoutControl.hpp @@ -0,0 +1,19 @@ +#pragma once +#include "Control.hpp" + +namespace cru::ui::controls { +class LayoutControl : public Control { + protected: + LayoutControl() = default; + + public: + LayoutControl(const LayoutControl& other) = delete; + LayoutControl(LayoutControl&& other) = delete; + LayoutControl& operator=(const LayoutControl& other) = delete; + LayoutControl& operator=(LayoutControl&& other) = delete; + ~LayoutControl() override = default; + + using Control::AddChild; + using Control::RemoveChild; +}; +} // namespace cru::ui diff --git a/include/cru/ui/controls/NoChildControl.hpp b/include/cru/ui/controls/NoChildControl.hpp new file mode 100644 index 00000000..562137f1 --- /dev/null +++ b/include/cru/ui/controls/NoChildControl.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "Control.hpp" + +namespace cru::ui::controls { +class NoChildControl : public Control { + protected: + NoChildControl() = default; + + public: + NoChildControl(const NoChildControl& other) = delete; + NoChildControl(NoChildControl&& other) = delete; + NoChildControl& operator=(const NoChildControl& other) = delete; + NoChildControl& operator=(NoChildControl&& other) = delete; + ~NoChildControl() override = default; + + private: + using Control::AddChild; + using Control::RemoveChild; +}; +} // namespace cru::ui diff --git a/include/cru/ui/controls/StackLayout.hpp b/include/cru/ui/controls/StackLayout.hpp index c0b95044..373b4681 100644 --- a/include/cru/ui/controls/StackLayout.hpp +++ b/include/cru/ui/controls/StackLayout.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../LayoutControl.hpp" +#include "LayoutControl.hpp" namespace cru::ui::controls { class StackLayout : public LayoutControl { diff --git a/include/cru/ui/controls/TextBlock.hpp b/include/cru/ui/controls/TextBlock.hpp index 8a9a3bff..fdfdb2fa 100644 --- a/include/cru/ui/controls/TextBlock.hpp +++ b/include/cru/ui/controls/TextBlock.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../NoChildControl.hpp" +#include "NoChildControl.hpp" namespace cru::ui::controls { template diff --git a/include/cru/ui/controls/TextBox.hpp b/include/cru/ui/controls/TextBox.hpp index 5976f6da..91d38c61 100644 --- a/include/cru/ui/controls/TextBox.hpp +++ b/include/cru/ui/controls/TextBox.hpp @@ -1,6 +1,5 @@ #pragma once -#include "../NoChildControl.hpp" -#include "Base.hpp" +#include "NoChildControl.hpp" #include diff --git a/include/cru/ui/controls/Window.hpp b/include/cru/ui/controls/Window.hpp new file mode 100644 index 00000000..616e2ee7 --- /dev/null +++ b/include/cru/ui/controls/Window.hpp @@ -0,0 +1,36 @@ +#pragma once +#include "LayoutControl.hpp" + +namespace cru::ui::controls { +class Window final : public LayoutControl { + public: + static constexpr std::u16string_view control_type = u"Window"; + + public: + static Window* CreateOverlapped(); + + private: + Window(); + + public: + Window(const Window& other) = delete; + Window(Window&& other) = delete; + Window& operator=(const Window& other) = delete; + Window& operator=(Window&& other) = delete; + ~Window() override; + + public: + std::u16string_view GetControlType() const final; + + render::RenderObject* GetRenderObject() const override; + + protected: + void OnAddChild(Control* child, Index position) override; + void OnRemoveChild(Control* child, Index position) override; + + private: + std::unique_ptr window_host_; + + std::unique_ptr render_object_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/events/UiEvent.hpp b/include/cru/ui/events/UiEvent.hpp new file mode 100644 index 00000000..660b33f5 --- /dev/null +++ b/include/cru/ui/events/UiEvent.hpp @@ -0,0 +1,228 @@ +#pragma once +#include "../Base.hpp" + +#include "cru/common/Event.hpp" +#include "cru/platform/gui/Keyboard.hpp" + +#include +#include +#include +#include + +namespace cru::platform::graphics { +struct IPainter; +} + +namespace cru::ui::event { +class UiEventArgs : public Object { + public: + UiEventArgs(Object* sender, Object* original_sender) + : sender_(sender), original_sender_(original_sender), handled_(false) {} + + UiEventArgs(const UiEventArgs& other) = default; + UiEventArgs(UiEventArgs&& other) = default; + UiEventArgs& operator=(const UiEventArgs& other) = default; + UiEventArgs& operator=(UiEventArgs&& other) = default; + ~UiEventArgs() override = default; + + Object* GetSender() const { return sender_; } + + Object* GetOriginalSender() const { return original_sender_; } + + bool IsHandled() const { return handled_; } + void SetHandled(const bool handled = true) { handled_ = handled; } + + private: + Object* sender_; + Object* original_sender_; + bool handled_; +}; + +// TEventArgs must not be a reference type. This class help add reference. +// EventArgs must be reference because the IsHandled property must be settable. +template +class RoutedEvent { + public: + static_assert(std::is_base_of_v, + "TEventArgs must be subclass of UiEventArgs."); + static_assert(!std::is_reference_v, + "TEventArgs must not be reference."); + + using RawEventArgs = TEventArgs; + using IEventType = IEvent; + using EventArgs = typename IEventType::EventArgs; + + RoutedEvent() = default; + RoutedEvent(const RoutedEvent& other) = delete; + RoutedEvent(RoutedEvent&& other) = delete; + RoutedEvent& operator=(const RoutedEvent& other) = delete; + RoutedEvent& operator=(RoutedEvent&& other) = delete; + ~RoutedEvent() = default; + + IEvent* Direct() { return &direct_; } + + IEvent* Bubble() { return &bubble_; } + + IEvent* Tunnel() { return &tunnel_; } + + private: + Event direct_; + Event bubble_; + Event tunnel_; +}; + +class MouseEventArgs : public UiEventArgs { + public: + MouseEventArgs(Object* sender, Object* original_sender, + const std::optional& point = std::nullopt) + : UiEventArgs(sender, original_sender), point_(point) {} + MouseEventArgs(const MouseEventArgs& other) = default; + MouseEventArgs(MouseEventArgs&& other) = default; + MouseEventArgs& operator=(const MouseEventArgs& other) = default; + MouseEventArgs& operator=(MouseEventArgs&& other) = default; + ~MouseEventArgs() override = default; + + // This point is relative to window client lefttop. + Point GetPoint() const { return point_.value_or(Point{}); } + Point GetPointToContent(render::RenderObject* render_target) const; + + private: + std::optional point_; +}; + +class MouseButtonEventArgs : public MouseEventArgs { + public: + MouseButtonEventArgs(Object* sender, Object* original_sender, + const Point& point, const MouseButton button, + platform::gui::KeyModifier key_modifier) + : MouseEventArgs(sender, original_sender, point), + button_(button), + key_modifier_(key_modifier) {} + MouseButtonEventArgs(Object* sender, Object* original_sender, + const MouseButton button, + platform::gui::KeyModifier key_modifier) + : MouseEventArgs(sender, original_sender), + button_(button), + key_modifier_(key_modifier) {} + MouseButtonEventArgs(const MouseButtonEventArgs& other) = default; + MouseButtonEventArgs(MouseButtonEventArgs&& other) = default; + MouseButtonEventArgs& operator=(const MouseButtonEventArgs& other) = default; + MouseButtonEventArgs& operator=(MouseButtonEventArgs&& other) = default; + ~MouseButtonEventArgs() override = default; + + MouseButton GetButton() const { return button_; } + platform::gui::KeyModifier GetKeyModifier() const { return key_modifier_; } + + private: + MouseButton button_; + platform::gui::KeyModifier key_modifier_; +}; + +class MouseWheelEventArgs : public MouseEventArgs { + public: + MouseWheelEventArgs(Object* sender, Object* original_sender, + const Point& point, const float delta) + : MouseEventArgs(sender, original_sender, point), delta_(delta) {} + MouseWheelEventArgs(const MouseWheelEventArgs& other) = default; + MouseWheelEventArgs(MouseWheelEventArgs&& other) = default; + MouseWheelEventArgs& operator=(const MouseWheelEventArgs& other) = default; + MouseWheelEventArgs& operator=(MouseWheelEventArgs&& other) = default; + ~MouseWheelEventArgs() override = default; + + float GetDelta() const { return delta_; } + + private: + float delta_; +}; + +class PaintEventArgs : public UiEventArgs { + public: + PaintEventArgs(Object* sender, Object* original_sender, + platform::graphics::IPainter* painter) + : UiEventArgs(sender, original_sender), painter_(painter) {} + PaintEventArgs(const PaintEventArgs& other) = default; + PaintEventArgs(PaintEventArgs&& other) = default; + PaintEventArgs& operator=(const PaintEventArgs& other) = default; + PaintEventArgs& operator=(PaintEventArgs&& other) = default; + ~PaintEventArgs() = default; + + platform::graphics::IPainter* GetPainter() const { return painter_; } + + private: + platform::graphics::IPainter* painter_; +}; + +class FocusChangeEventArgs : public UiEventArgs { + public: + FocusChangeEventArgs(Object* sender, Object* original_sender, + const bool is_window = false) + : UiEventArgs(sender, original_sender), is_window_(is_window) {} + FocusChangeEventArgs(const FocusChangeEventArgs& other) = default; + FocusChangeEventArgs(FocusChangeEventArgs&& other) = default; + FocusChangeEventArgs& operator=(const FocusChangeEventArgs& other) = default; + FocusChangeEventArgs& operator=(FocusChangeEventArgs&& other) = default; + ~FocusChangeEventArgs() override = default; + + // Return whether the focus change is caused by the window-wide focus change. + bool IsWindow() const { return is_window_; } + + private: + bool is_window_; +}; + +/* +class ToggleEventArgs : public UiEventArgs { + public: + ToggleEventArgs(Object* sender, Object* original_sender, bool new_state) + : UiEventArgs(sender, original_sender), new_state_(new_state) {} + ToggleEventArgs(const ToggleEventArgs& other) = default; + ToggleEventArgs(ToggleEventArgs&& other) = default; + ToggleEventArgs& operator=(const ToggleEventArgs& other) = default; + ToggleEventArgs& operator=(ToggleEventArgs&& other) = default; + ~ToggleEventArgs() override = default; + + bool GetNewState() const { return new_state_; } + + private: + bool new_state_; +}; +*/ + +class KeyEventArgs : public UiEventArgs { + public: + KeyEventArgs(Object* sender, Object* original_sender, + platform::gui::KeyCode key_code, + platform::gui::KeyModifier key_modifier) + : UiEventArgs(sender, original_sender), + key_code_(key_code), + key_modifier_(key_modifier) {} + KeyEventArgs(const KeyEventArgs& other) = default; + KeyEventArgs(KeyEventArgs&& other) = default; + KeyEventArgs& operator=(const KeyEventArgs& other) = default; + KeyEventArgs& operator=(KeyEventArgs&& other) = default; + ~KeyEventArgs() override = default; + + platform::gui::KeyCode GetKeyCode() const { return key_code_; } + platform::gui::KeyModifier GetKeyModifier() const { return key_modifier_; } + + private: + platform::gui::KeyCode key_code_; + platform::gui::KeyModifier key_modifier_; +}; + +class CharEventArgs : public UiEventArgs { + public: + CharEventArgs(Object* sender, Object* original_sender, std::u16string c) + : UiEventArgs(sender, original_sender), c_(std::move(c)) {} + CharEventArgs(const CharEventArgs& other) = default; + CharEventArgs(CharEventArgs&& other) = default; + CharEventArgs& operator=(const CharEventArgs& other) = default; + CharEventArgs& operator=(CharEventArgs&& other) = default; + ~CharEventArgs() override = default; + + std::u16string GetChar() const { return c_; } + + private: + std::u16string c_; +}; +} // namespace cru::ui::event diff --git a/include/cru/ui/helper/ClickDetector.hpp b/include/cru/ui/helper/ClickDetector.hpp new file mode 100644 index 00000000..0df77c60 --- /dev/null +++ b/include/cru/ui/helper/ClickDetector.hpp @@ -0,0 +1,87 @@ +#pragma once +#include "../controls/Control.hpp" + +namespace cru::ui::helper { +class ClickEventArgs : Object { + public: + ClickEventArgs(controls::Control* sender, const Point& down_point, + const Point& up_point, MouseButton button) + : sender_(sender), + down_point_(down_point), + up_point_(up_point), + button_(button) {} + + CRU_DEFAULT_COPY(ClickEventArgs) + CRU_DEFAULT_MOVE(ClickEventArgs) + + ~ClickEventArgs() override = default; + + controls::Control* GetSender() const { return sender_; } + Point GetDownPoint() const { return down_point_; } + Point GetUpPoint() const { return up_point_; } + MouseButton GetButton() const { return button_; } + + private: + controls::Control* sender_; + Point down_point_; + Point up_point_; + MouseButton button_; +}; + +enum class ClickState { + None, // Mouse is outside the control. + Hover, // Mouse hovers on the control but not pressed + Press, // Mouse is pressed and if released click is done. + PressInactive // Mouse is pressed but if released click is canceled. +}; + +class ClickDetector : public Object { + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::ClickDetector") + + public: + explicit ClickDetector(controls::Control* control); + + CRU_DELETE_COPY(ClickDetector) + CRU_DELETE_MOVE(ClickDetector) + + ~ClickDetector() override = default; + + controls::Control* GetControl() const { return control_; } + + ClickState GetState() const { return state_; } + + // Default is enable. + bool IsEnabled() const { return enable_; } + // If disable when user is pressing, the pressing is deactivated. + void SetEnabled(bool enable); + + // Default is left and right. + MouseButton GetTriggerButton() const { return trigger_button_; } + // If unset the trigger button when user is pressing, the pressing is + // deactivated. + void SetTriggerButton(MouseButton trigger_button); + + IEvent* ClickEvent() { return &event_; } + + IEvent* StateChangeEvent() { return &state_change_event_; } + + private: + void SetState(ClickState state); + + private: + controls::Control* control_; + + ClickState state_; + + bool enable_ = true; + MouseButton trigger_button_ = mouse_buttons::left | mouse_buttons::right; + + Event event_; + Event state_change_event_; + + std::vector event_rovoker_guards_; + + Point down_point_; + MouseButton button_; +}; +} // namespace cru::ui::helper diff --git a/include/cru/ui/helper/ShortcutHub.hpp b/include/cru/ui/helper/ShortcutHub.hpp new file mode 100644 index 00000000..a4ff2da2 --- /dev/null +++ b/include/cru/ui/helper/ShortcutHub.hpp @@ -0,0 +1,129 @@ +#pragma once +#include "../Base.hpp" + +#include "../events/UiEvent.hpp" +#include "cru/common/Event.hpp" +#include "cru/platform/gui/Keyboard.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cru::ui::helper { + +class ShortcutKeyBind { + public: + ShortcutKeyBind(platform::gui::KeyCode key, + platform::gui::KeyModifier modifier) + : key_(key), modifier_(modifier) {} + + CRU_DEFAULT_COPY(ShortcutKeyBind) + CRU_DEFAULT_MOVE(ShortcutKeyBind) + + ~ShortcutKeyBind() = default; + + platform::gui::KeyCode GetKey() const { return key_; } + platform::gui::KeyModifier GetModifier() const { return modifier_; } + + bool Is(platform::gui::KeyCode key, + platform::gui::KeyModifier modifier) const { + return key == key_ && modifier == modifier_; + } + + bool operator==(const ShortcutKeyBind& other) const { + return this->key_ == other.key_ && this->modifier_ == other.modifier_; + } + + bool operator!=(const ShortcutKeyBind& other) const { + return !this->operator==(other); + } + + std::u16string ToString() { + std::u16string result = u"("; + result += platform::gui::ToString(modifier_); + result += u")"; + result += platform::gui::ToString(key_); + return result; + } + + private: + platform::gui::KeyCode key_; + platform::gui::KeyModifier modifier_; +}; +} // namespace cru::ui::helper + +namespace std { +template <> +struct hash { + std::size_t operator()(const cru::ui::helper::ShortcutKeyBind& value) const { + std::size_t result = 0; + cru::hash_combine(result, static_cast(value.GetKey())); + cru::hash_combine(result, static_cast(value.GetModifier())); + return result; + } +}; +} // namespace std + +namespace cru::ui::helper { +struct Shortcut { + // Just for debug. + std::u16string name; + ShortcutKeyBind key_bind; + // Return true if it consumes the shortcut. Or return false if it does not + // handle the shortcut. + std::function handler; +}; + +struct ShortcutInfo { + int id; + std::u16string name; + ShortcutKeyBind key_bind; + std::function handler; +}; + +class ShortcutHub : public Object { + public: + ShortcutHub() = default; + + CRU_DELETE_COPY(ShortcutHub) + CRU_DELETE_MOVE(ShortcutHub) + + ~ShortcutHub() override = default; + + int RegisterShortcut(std::u16string name, ShortcutKeyBind bind, + std::function handler) { + return RegisterShortcut({std::move(name), bind, std::move(handler)}); + } + + // Return an id used for unregistering. + int RegisterShortcut(Shortcut shortcut); + + void UnregisterShortcut(int id); + + std::vector GetAllShortcuts() const; + std::optional GetShortcut(int id) const; + const std::vector& GetShortcutByKeyBind( + const ShortcutKeyBind& key_bind) const; + + void Install(controls::Control* control); + void Uninstall(); + + private: + void OnKeyDown(event::KeyEventArgs& event); + + private: + std::unordered_map> map_; + + const std::vector empty_list_; + + int current_id_ = 1; + + EventRevokerListGuard event_guard_; +}; +} // namespace cru::ui::helper diff --git a/include/cru/ui/host/WindowHost.hpp b/include/cru/ui/host/WindowHost.hpp index 81eabb52..56f37382 100644 --- a/include/cru/ui/host/WindowHost.hpp +++ b/include/cru/ui/host/WindowHost.hpp @@ -1,10 +1,10 @@ #pragma once #include "../Base.hpp" +#include "../render/Base.hpp" #include "cru/common/Event.hpp" #include "cru/platform/gui/UiApplication.hpp" #include "cru/platform/gui/Window.hpp" -#include "../render/Base.hpp" #include #include @@ -19,7 +19,7 @@ class WindowHost : public Object { CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::host::WindowHost") public: - WindowHost(Control* root_control); + WindowHost(controls::Control* root_control); CRU_DELETE_COPY(WindowHost) CRU_DELETE_MOVE(WindowHost) @@ -61,13 +61,15 @@ class WindowHost : public Object { // control. Even when mouse is captured by another control, this function // return the control under cursor. You can use `GetMouseCaptureControl` to // get more info. - Control* GetMouseHoverControl() const { return mouse_hover_control_; } + controls::Control* GetMouseHoverControl() const { + return mouse_hover_control_; + } //*************** region: focus *************** - Control* GetFocusControl(); + controls::Control* GetFocusControl(); - void SetFocusControl(Control* control); + void SetFocusControl(controls::Control* control); //*************** region: focus *************** @@ -81,12 +83,12 @@ class WindowHost : public Object { // and capture is released, mouse enter event will be sent to the mouse-hover // control. If mouse is not on the capturing control and capture is set, mouse // leave event will be sent to the mouse-hover control. - bool CaptureMouseFor(Control* control); + bool CaptureMouseFor(controls::Control* control); // Return null if not captured. - Control* GetMouseCaptureControl(); + controls::Control* GetMouseCaptureControl(); - Control* HitTest(const Point& point); + controls::Control* HitTest(const Point& point); void UpdateCursor(); @@ -94,23 +96,19 @@ class WindowHost : public Object { //*************** region: native messages *************** void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t); void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t); - void OnNativeResize(platform::gui::INativeWindow* window, - const Size& size); + void OnNativeResize(platform::gui::INativeWindow* window, const Size& size); void OnNativeFocus(platform::gui::INativeWindow* window, cru::platform::gui::FocusChangeType focus); - void OnNativeMouseEnterLeave( - platform::gui::INativeWindow* window, - cru::platform::gui::MouseEnterLeaveType enter); + void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window, + cru::platform::gui::MouseEnterLeaveType enter); void OnNativeMouseMove(platform::gui::INativeWindow* window, const Point& point); - void OnNativeMouseDown( - platform::gui::INativeWindow* window, - const platform::gui::NativeMouseButtonEventArgs& args); - void OnNativeMouseUp( - platform::gui::INativeWindow* window, - const platform::gui::NativeMouseButtonEventArgs& args); + void OnNativeMouseDown(platform::gui::INativeWindow* window, + const platform::gui::NativeMouseButtonEventArgs& args); + void OnNativeMouseUp(platform::gui::INativeWindow* window, + const platform::gui::NativeMouseButtonEventArgs& args); void OnNativeKeyDown(platform::gui::INativeWindow* window, const platform::gui::NativeKeyEventArgs& args); @@ -119,13 +117,13 @@ class WindowHost : public Object { //*************** region: event dispatcher helper *************** - void DispatchMouseHoverControlChangeEvent(Control* old_control, - Control* new_control, + void DispatchMouseHoverControlChangeEvent(controls::Control* old_control, + controls::Control* new_control, const Point& point, bool no_leave, bool no_enter); private: - Control* root_control_ = nullptr; + controls::Control* root_control_ = nullptr; render::RenderObject* root_render_object_ = nullptr; platform::gui::INativeWindow* native_window_ = nullptr; @@ -137,12 +135,12 @@ class WindowHost : public Object { std::vector event_revoker_guards_; - Control* mouse_hover_control_ = nullptr; + controls::Control* mouse_hover_control_ = nullptr; - Control* focus_control_; + controls::Control* focus_control_; - Control* mouse_captured_control_ = nullptr; + controls::Control* mouse_captured_control_ = nullptr; bool layout_prefer_to_fill_window_ = true; }; -} // namespace cru::ui +} // namespace cru::ui::host diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp index 635a541e..2b166efc 100644 --- a/include/cru/ui/render/RenderObject.hpp +++ b/include/cru/ui/render/RenderObject.hpp @@ -62,8 +62,10 @@ class RenderObject : public Object { RenderObject& operator=(RenderObject&& other) = delete; ~RenderObject() override = default; - Control* GetAttachedControl() const { return control_; } - void SetAttachedControl(Control* new_control) { control_ = new_control; } + controls::Control* GetAttachedControl() const { return control_; } + void SetAttachedControl(controls::Control* new_control) { + control_ = new_control; + } host::WindowHost* GetWindowHost() const { return window_host_; } @@ -135,7 +137,9 @@ class RenderObject : public Object { // Add offset before pass point to children. virtual RenderObject* HitTest(const Point& point) = 0; - IEvent* AttachToHostEvent() { return &attach_to_host_event_; } + IEvent* AttachToHostEvent() { + return &attach_to_host_event_; + } IEvent* DetachFromHostEvent() { return &detach_from_host_event_; } @@ -208,7 +212,7 @@ class RenderObject : public Object { void SetWindowHostRecursive(host::WindowHost* host); private: - Control* control_ = nullptr; + controls::Control* control_ = nullptr; host::WindowHost* window_host_ = nullptr; RenderObject* parent_ = nullptr; diff --git a/include/cru/win/gui/UiApplication.hpp b/include/cru/win/gui/UiApplication.hpp index 0f733cd4..4cf46858 100644 --- a/include/cru/win/gui/UiApplication.hpp +++ b/include/cru/win/gui/UiApplication.hpp @@ -41,7 +41,7 @@ class WinUiApplication : public WinNativeResource, void CancelTimer(long long id) override; std::vector GetAllWindow() override; - INativeWindow* CreateWindow(INativeWindow* parent) override; + INativeWindow* CreateWindow(INativeWindow* parent, CreateWindowFlag flag) override; cru::platform::graphics::IGraphFactory* GetGraphFactory() override; diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 449fb4d9..2f0eb10d 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -4,23 +4,23 @@ add_library(cru_ui STATIC Helper.hpp host/RoutedEventDispatch.hpp - ClickDetector.cpp - ContentControl.cpp - Control.cpp Helper.cpp - LayoutControl.cpp - NoChildControl.cpp - ShortcutHub.cpp - UiEvent.cpp UiManager.cpp - Window.cpp controls/Button.cpp controls/Container.cpp + controls/ContentControl.cpp + controls/Control.cpp controls/FlexLayout.cpp + controls/LayoutControl.cpp + controls/NoChildControl.cpp controls/StackLayout.cpp controls/TextBlock.cpp controls/TextBox.cpp controls/TextControlService.hpp + controls/Window.cpp + events/UiEvent.cpp + helper/ClickDetector.cpp + helper/ShortcutHub.cpp host/LayoutPaintCycler.cpp host/WindowHost.cpp render/BorderRenderObject.cpp @@ -34,23 +34,23 @@ add_library(cru_ui STATIC ) target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/Base.hpp - ${CRU_UI_INCLUDE_DIR}/ClickDetector.hpp - ${CRU_UI_INCLUDE_DIR}/ContentControl.hpp - ${CRU_UI_INCLUDE_DIR}/Control.hpp ${CRU_UI_INCLUDE_DIR}/DebugFlags.hpp - ${CRU_UI_INCLUDE_DIR}/LayoutControl.hpp - ${CRU_UI_INCLUDE_DIR}/NoChildControl.hpp - ${CRU_UI_INCLUDE_DIR}/ShortcutHub.hpp - ${CRU_UI_INCLUDE_DIR}/UiEvent.hpp ${CRU_UI_INCLUDE_DIR}/UiManager.hpp - ${CRU_UI_INCLUDE_DIR}/Window.hpp ${CRU_UI_INCLUDE_DIR}/controls/Base.hpp ${CRU_UI_INCLUDE_DIR}/controls/Button.hpp ${CRU_UI_INCLUDE_DIR}/controls/Container.hpp + ${CRU_UI_INCLUDE_DIR}/controls/ContentControl.hpp + ${CRU_UI_INCLUDE_DIR}/controls/Control.hpp ${CRU_UI_INCLUDE_DIR}/controls/FlexLayout.hpp + ${CRU_UI_INCLUDE_DIR}/controls/LayoutControl.hpp + ${CRU_UI_INCLUDE_DIR}/controls/NoChildControl.hpp ${CRU_UI_INCLUDE_DIR}/controls/StackLayout.hpp ${CRU_UI_INCLUDE_DIR}/controls/TextBox.hpp ${CRU_UI_INCLUDE_DIR}/controls/TextBlock.hpp + ${CRU_UI_INCLUDE_DIR}/controls/Window.hpp + ${CRU_UI_INCLUDE_DIR}/events/UiEvent.hpp + ${CRU_UI_INCLUDE_DIR}/helper/ClickDetector.hpp + ${CRU_UI_INCLUDE_DIR}/helper/ShortcutHub.hpp ${CRU_UI_INCLUDE_DIR}/host/LayoutPaintCycler.hpp ${CRU_UI_INCLUDE_DIR}/host/WindowHost.hpp ${CRU_UI_INCLUDE_DIR}/render/Base.hpp diff --git a/src/ui/ClickDetector.cpp b/src/ui/ClickDetector.cpp deleted file mode 100644 index 09f208cd..00000000 --- a/src/ui/ClickDetector.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "cru/ui/ClickDetector.hpp" - -#include "cru/common/Logger.hpp" - -#include - -namespace cru::ui { -ClickDetector::ClickDetector(Control* control) { - Expects(control); - control_ = control; - - event_rovoker_guards_.push_back( - EventRevokerGuard(control->MouseEnterEvent()->Direct()->AddHandler( - [this](event::MouseEventArgs&) { - if (this->enable_) { - if (this->state_ == ClickState::PressInactive) { - if ((this->button_ & this->trigger_button_)) { - this->SetState(ClickState::Press); - } - } else { - this->SetState(ClickState::Hover); - } - } - }))); - - event_rovoker_guards_.push_back( - EventRevokerGuard(control->MouseLeaveEvent()->Direct()->AddHandler( - [this](event::MouseEventArgs&) { - if (this->enable_) { - if (this->state_ == ClickState::Press) { - if ((this->button_ & this->trigger_button_)) { - this->SetState(ClickState::PressInactive); - } - } else { - this->SetState(ClickState::None); - } - } - }))); - - event_rovoker_guards_.push_back( - EventRevokerGuard(control->MouseDownEvent()->Direct()->AddHandler( - [this](event::MouseButtonEventArgs& args) { - const auto button = args.GetButton(); - if (this->enable_ && (button & this->trigger_button_) && - this->state_ == ClickState::Hover) { - if (!this->control_->CaptureMouse()) { - log::TagDebug(log_tag, - u"Failed to capture mouse when begin click."); - return; - } - this->down_point_ = args.GetPoint(); - this->button_ = button; - this->SetState(ClickState::Press); - } - }))); - - event_rovoker_guards_.push_back( - EventRevokerGuard(control->MouseUpEvent()->Direct()->AddHandler( - [this](event::MouseButtonEventArgs& args) { - const auto button = args.GetButton(); - if (this->enable_ && (button & this->trigger_button_) && - button == button_) { - if (this->state_ == ClickState::Press) { - this->SetState(ClickState::Hover); - this->event_.Raise(ClickEventArgs{this->control_, - this->down_point_, - args.GetPoint(), button}); - this->control_->ReleaseMouse(); - } else if (this->state_ == ClickState::PressInactive) { - this->SetState(ClickState::None); - this->control_->ReleaseMouse(); - } - } - }))); -} // namespace cru::ui - -void ClickDetector::SetEnabled(bool enable) { - if (enable == enable_) { - return; - } - - enable_ = enable; - if (enable) { - SetState(control_->IsMouseOver() ? ClickState::Hover : ClickState::None); - } else { - if (state_ == ClickState::Press || state_ == ClickState::PressInactive) { - SetState(ClickState::None); - control_->ReleaseMouse(); - } else if (state_ == ClickState::Hover) { - SetState(ClickState::None); - } - } -} - -void ClickDetector::SetTriggerButton(MouseButton trigger_button) { - if (trigger_button == trigger_button_) { - return; - } - - trigger_button_ = trigger_button; - if ((state_ == ClickState::Press || state_ == ClickState::PressInactive) && - !(button_ & trigger_button)) { - SetState(control_->IsMouseOver() ? ClickState::Hover : ClickState::None); - control_->ReleaseMouse(); - } -} - -void ClickDetector::SetState(ClickState state) { -#ifdef CRU_DEBUG - auto to_string = [](ClickState state) -> std::u16string_view { - switch (state) { - case ClickState::None: - return u"None"; - case ClickState::Hover: - return u"Hover"; - case ClickState::Press: - return u"Press"; - case ClickState::PressInactive: - return u"PressInvactive"; - default: - UnreachableCode(); - } - }; - log::TagDebug(log_tag, u"Click state changed, new state: {}.", - to_string(state)); -#endif - - state_ = state; - state_change_event_.Raise(state); -} -} // namespace cru::ui diff --git a/src/ui/ContentControl.cpp b/src/ui/ContentControl.cpp deleted file mode 100644 index 19b1b06f..00000000 --- a/src/ui/ContentControl.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "cru/ui/ContentControl.hpp" - -#include "cru/ui/Window.hpp" - -namespace cru::ui { -Control* ContentControl::GetChild() const { - if (GetChildren().empty()) return nullptr; - return GetChildren()[0]; -} - -void ContentControl::SetChild(Control* child) { - Control* old_child = nullptr; - if (!GetChildren().empty()) { - old_child = GetChildren()[0]; - this->RemoveChild(0); - } - if (child) { - this->AddChild(child, 0); - } - OnChildChanged(old_child, child); -} - -void ContentControl::OnChildChanged(Control* old_child, Control* new_child) { - CRU_UNUSED(old_child) - CRU_UNUSED(new_child) -} -} // namespace cru::ui diff --git a/src/ui/Control.cpp b/src/ui/Control.cpp deleted file mode 100644 index 23a3cef2..00000000 --- a/src/ui/Control.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "cru/ui/Control.hpp" - -#include "cru/common/Base.hpp" -#include "cru/platform/gui/Cursor.hpp" -#include "cru/platform/gui/UiApplication.hpp" -#include "cru/ui/Base.hpp" -#include "cru/ui/host/WindowHost.hpp" -#include "cru/ui/render/RenderObject.hpp" - -#include - -namespace cru::ui { -using platform::gui::ICursor; -using platform::gui::IUiApplication; -using platform::gui::SystemCursorType; - -Control::Control() { - MouseEnterEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { - this->is_mouse_over_ = true; - this->OnMouseHoverChange(true); - }); - - MouseLeaveEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { - this->is_mouse_over_ = false; - this->OnMouseHoverChange(true); - }); -} - -Control::~Control() { - for (const auto child : children_) delete child; -} - -host::WindowHost* Control::GetWindowHost() const { return window_host_; } - -void Control::TraverseDescendants( - const std::function& predicate) { - predicate(this); - for (auto c : GetChildren()) c->TraverseDescendants(predicate); -} - -bool Control::HasFocus() { - auto host = GetWindowHost(); - if (host == nullptr) return false; - - return host->GetFocusControl() == this; -} - -bool Control::CaptureMouse() { - auto host = GetWindowHost(); - if (host == nullptr) return false; - - return host->CaptureMouseFor(this); -} - -void Control::SetFocus() { - auto host = GetWindowHost(); - if (host == nullptr) return; - - host->SetFocusControl(this); -} - -bool Control::ReleaseMouse() { - auto host = GetWindowHost(); - if (host == nullptr) return false; - - return host->CaptureMouseFor(nullptr); -} - -bool Control::IsMouseCaptured() { - auto host = GetWindowHost(); - if (host == nullptr) return false; - - return host->GetMouseCaptureControl() == this; -} - -std::shared_ptr Control::GetCursor() { return cursor_; } - -std::shared_ptr Control::GetInheritedCursor() { - Control* control = this; - while (control != nullptr) { - const auto cursor = control->GetCursor(); - if (cursor != nullptr) return cursor; - control = control->GetParent(); - } - return IUiApplication::GetInstance()->GetCursorManager()->GetSystemCursor( - SystemCursorType::Arrow); -} - -void Control::SetCursor(std::shared_ptr cursor) { - cursor_ = std::move(cursor); - const auto host = GetWindowHost(); - if (host != nullptr) { - host->UpdateCursor(); - } -} - -void Control::AddChild(Control* control, const Index position) { - Expects(control->GetParent() == - nullptr); // The control already has a parent. - Expects(position >= 0); - Expects(position <= static_cast( - children_.size())); // The position is out of range. - - children_.insert(children_.cbegin() + position, control); - - const auto old_parent = control->parent_; - control->parent_ = this; - - OnAddChild(control, position); - control->OnParentChanged(old_parent, this); - - if (window_host_) - control->TraverseDescendants([this](Control* control) { - control->window_host_ = window_host_; - control->OnAttachToHost(window_host_); - }); -} - -void Control::RemoveChild(const Index position) { - Expects(position >= 0); - Expects(position < static_cast( - children_.size())); // The position is out of range. - - const auto i = children_.cbegin() + position; - const auto control = *i; - - children_.erase(i); - control->parent_ = nullptr; - - OnRemoveChild(control, position); - control->OnParentChanged(this, nullptr); - - if (window_host_) - control->TraverseDescendants([this](Control* control) { - control->window_host_ = nullptr; - control->OnDetachFromHost(window_host_); - }); -} - -void Control::OnAddChild(Control* child, Index position) { - CRU_UNUSED(child) - CRU_UNUSED(position) -} -void Control::OnRemoveChild(Control* child, Index position) { - CRU_UNUSED(child) - CRU_UNUSED(position) -} - -void Control::OnParentChanged(Control* old_parent, Control* new_parent) { - CRU_UNUSED(old_parent) - CRU_UNUSED(new_parent) -} - -void Control::OnAttachToHost(host::WindowHost* host) { CRU_UNUSED(host) } - -void Control::OnDetachFromHost(host::WindowHost* host) { CRU_UNUSED(host) } -} // namespace cru::ui diff --git a/src/ui/LayoutControl.cpp b/src/ui/LayoutControl.cpp deleted file mode 100644 index 351026f9..00000000 --- a/src/ui/LayoutControl.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "cru/ui/LayoutControl.hpp" - -#include "cru/ui/Window.hpp" - -namespace cru::ui { -} // namespace cru::ui diff --git a/src/ui/NoChildControl.cpp b/src/ui/NoChildControl.cpp deleted file mode 100644 index 8adbe3bc..00000000 --- a/src/ui/NoChildControl.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "cru/ui/NoChildControl.hpp" - -namespace cru::ui {} diff --git a/src/ui/ShortcutHub.cpp b/src/ui/ShortcutHub.cpp deleted file mode 100644 index c9ce6cdd..00000000 --- a/src/ui/ShortcutHub.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "cru/ui/ShortcutHub.hpp" - -#include "cru/common/Logger.hpp" -#include "cru/ui/Control.hpp" -#include "cru/ui/DebugFlags.hpp" - -#include -#include -#include -#include - -namespace cru::ui { -int ShortcutHub::RegisterShortcut(Shortcut shortcut) { - const int id = current_id_++; - map_[shortcut.key_bind].push_back({id, std::move(shortcut.name), - shortcut.key_bind, - std::move(shortcut.handler)}); - return id; -} - -void ShortcutHub::UnregisterShortcut(int id) { - if (id <= 0) return; - for (auto& pair : map_) { - auto& list = pair.second; - auto result = - std::find_if(list.cbegin(), list.cend(), - [id](const ShortcutInfo& info) { return info.id == id; }); - if (result != list.cend()) { - list.erase(result); - } - } -} - -std::vector ShortcutHub::GetAllShortcuts() const { - std::vector result; - - for (const auto& pair : map_) { - std::copy(pair.second.cbegin(), pair.second.cend(), - std::back_inserter(result)); - } - - return result; -} - -std::optional ShortcutHub::GetShortcut(int id) const { - for (auto& pair : map_) { - auto& list = pair.second; - auto result = - std::find_if(list.cbegin(), list.cend(), - [id](const ShortcutInfo& info) { return info.id == id; }); - if (result != list.cend()) { - return *result; - } - } - return std::nullopt; -} - -const std::vector& ShortcutHub::GetShortcutByKeyBind( - const ShortcutKeyBind& key_bind) const { - auto result = map_.find(key_bind); - if (result != map_.cend()) return result->second; - return empty_list_; -} - -void ShortcutHub::Install(Control* control) { - if (!event_guard_.IsEmpty()) { - log::Error(u"Shortcut hub is already installed. Failed to install."); - return; - } - - event_guard_ += control->KeyDownEvent()->Bubble()->AddHandler( - std::bind(&ShortcutHub::OnKeyDown, this, std::placeholders::_1)); -} - -void ShortcutHub::Uninstall() { - if (event_guard_.IsEmpty()) { - log::Warn(u"Shortcut hub is not installed. Failed to uninstall."); - return; - } - - event_guard_.Clear(); -} - -void ShortcutHub::OnKeyDown(event::KeyEventArgs& event) { - ShortcutKeyBind key_bind(event.GetKeyCode(), event.GetKeyModifier()); - const auto& shortcut_list = this->GetShortcutByKeyBind(key_bind); - - if constexpr (debug_flags::shortcut) { - if (shortcut_list.empty()) { - log::Debug(u"No shortcut for key bind {}.", key_bind.ToString()); - } - log::Debug(u"Begin to handle shortcut for key bind {}.", - key_bind.ToString()); - } - - for (const auto& shortcut : shortcut_list) { - auto is_handled = shortcut.handler(); - if (is_handled) { - if constexpr (debug_flags::shortcut) { - log::Debug(u"Handle {} handled it.", shortcut.name); - } - - event.SetHandled(); - - break; - } else { - if constexpr (debug_flags::shortcut) { - log::Debug(u"Handle {} disdn't handle it.", shortcut.name); - } - } - } - - if constexpr (debug_flags::shortcut) { - if (!shortcut_list.empty()) { - log::Debug(u"End handling shortcut for key bind {}.", - key_bind.ToString()); - } - } -} -} // namespace cru::ui diff --git a/src/ui/UiEvent.cpp b/src/ui/UiEvent.cpp deleted file mode 100644 index 74dd54dc..00000000 --- a/src/ui/UiEvent.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "cru/ui/UiEvent.hpp" - -#include "cru/ui/render/RenderObject.hpp" - -namespace cru::ui::event { -Point MouseEventArgs::GetPointToContent( - render::RenderObject* render_object) const { - return render_object->FromRootToContent(GetPoint()); -} -} // namespace cru::ui::event diff --git a/src/ui/Window.cpp b/src/ui/Window.cpp deleted file mode 100644 index c49140a4..00000000 --- a/src/ui/Window.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "cru/ui/Window.hpp" - -#include "cru/common/Base.hpp" -#include "cru/ui/host/WindowHost.hpp" -#include "cru/ui/render/Base.hpp" -#include "cru/ui/render/StackLayoutRenderObject.hpp" - -namespace cru::ui { -Window* Window::CreateOverlapped() { return new Window(); } - -Window::Window() : render_object_(new render::StackLayoutRenderObject()) { - render_object_->SetAttachedControl(this); - window_host_ = std::make_unique(this); -} - -Window::~Window() {} - -std::u16string_view Window::GetControlType() const { return control_type; } - -render::RenderObject* Window::GetRenderObject() const { - return render_object_.get(); -} - -void Window::OnAddChild(Control* child, Index position) { - render_object_->AddChild(child->GetRenderObject(), position); -} - -void Window::OnRemoveChild(Control* child, Index position) { - CRU_UNUSED(child); - render_object_->RemoveChild(position); -} -} // namespace cru::ui diff --git a/src/ui/controls/Button.cpp b/src/ui/controls/Button.cpp index 5f7ed143..b7407ec2 100644 --- a/src/ui/controls/Button.cpp +++ b/src/ui/controls/Button.cpp @@ -5,9 +5,9 @@ #include "cru/platform/graphics/Brush.hpp" #include "cru/platform/gui/Cursor.hpp" #include "cru/platform/gui/UiApplication.hpp" -#include "cru/ui/render/BorderRenderObject.hpp" #include "cru/ui/UiManager.hpp" -#include "cru/ui/Window.hpp" +#include "cru/ui/helper/ClickDetector.hpp" +#include "cru/ui/render/BorderRenderObject.hpp" namespace cru::ui::controls { using cru::platform::gui::SystemCursorType; @@ -21,8 +21,7 @@ void Set(render::BorderRenderObject* o, const ButtonStateStyle& s) { o->SetBackgroundBrush(s.background_brush); } -std::shared_ptr GetSystemCursor( - SystemCursorType type) { +std::shared_ptr GetSystemCursor(SystemCursorType type) { return GetUiApplication()->GetCursorManager()->GetSystemCursor(type); } } // namespace @@ -36,21 +35,21 @@ Button::Button() : click_detector_(this) { render_object_->SetBorderEnabled(true); click_detector_.StateChangeEvent()->AddHandler( - [this](const ClickState& state) { + [this](const helper::ClickState& state) { switch (state) { - case ClickState::None: + case helper::ClickState::None: Set(render_object_.get(), style_.normal); SetCursor(GetSystemCursor(SystemCursorType::Arrow)); break; - case ClickState::Hover: + case helper::ClickState::Hover: Set(render_object_.get(), style_.hover); SetCursor(GetSystemCursor(SystemCursorType::Hand)); break; - case ClickState::Press: + case helper::ClickState::Press: Set(render_object_.get(), style_.press); SetCursor(GetSystemCursor(SystemCursorType::Hand)); break; - case ClickState::PressInactive: + case helper::ClickState::PressInactive: Set(render_object_.get(), style_.press_cancel); SetCursor(GetSystemCursor(SystemCursorType::Arrow)); break; diff --git a/src/ui/controls/ContentControl.cpp b/src/ui/controls/ContentControl.cpp new file mode 100644 index 00000000..653882c0 --- /dev/null +++ b/src/ui/controls/ContentControl.cpp @@ -0,0 +1,25 @@ +#include "cru/ui/controls/ContentControl.hpp" + +namespace cru::ui::controls { +Control* ContentControl::GetChild() const { + if (GetChildren().empty()) return nullptr; + return GetChildren()[0]; +} + +void ContentControl::SetChild(Control* child) { + Control* old_child = nullptr; + if (!GetChildren().empty()) { + old_child = GetChildren()[0]; + this->RemoveChild(0); + } + if (child) { + this->AddChild(child, 0); + } + OnChildChanged(old_child, child); +} + +void ContentControl::OnChildChanged(Control* old_child, Control* new_child) { + CRU_UNUSED(old_child) + CRU_UNUSED(new_child) +} +} // namespace cru::ui::controls diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp new file mode 100644 index 00000000..c1316a62 --- /dev/null +++ b/src/ui/controls/Control.cpp @@ -0,0 +1,157 @@ +#include "cru/ui/controls/Control.hpp" + +#include "cru/common/Base.hpp" +#include "cru/platform/gui/Cursor.hpp" +#include "cru/platform/gui/UiApplication.hpp" +#include "cru/ui/Base.hpp" +#include "cru/ui/host/WindowHost.hpp" +#include "cru/ui/render/RenderObject.hpp" + +#include + +namespace cru::ui::controls { +using platform::gui::ICursor; +using platform::gui::IUiApplication; +using platform::gui::SystemCursorType; + +Control::Control() { + MouseEnterEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { + this->is_mouse_over_ = true; + this->OnMouseHoverChange(true); + }); + + MouseLeaveEvent()->Direct()->AddHandler([this](event::MouseEventArgs&) { + this->is_mouse_over_ = false; + this->OnMouseHoverChange(true); + }); +} + +Control::~Control() { + for (const auto child : children_) delete child; +} + +host::WindowHost* Control::GetWindowHost() const { return window_host_; } + +void Control::TraverseDescendants( + const std::function& predicate) { + predicate(this); + for (auto c : GetChildren()) c->TraverseDescendants(predicate); +} + +bool Control::HasFocus() { + auto host = GetWindowHost(); + if (host == nullptr) return false; + + return host->GetFocusControl() == this; +} + +bool Control::CaptureMouse() { + auto host = GetWindowHost(); + if (host == nullptr) return false; + + return host->CaptureMouseFor(this); +} + +void Control::SetFocus() { + auto host = GetWindowHost(); + if (host == nullptr) return; + + host->SetFocusControl(this); +} + +bool Control::ReleaseMouse() { + auto host = GetWindowHost(); + if (host == nullptr) return false; + + return host->CaptureMouseFor(nullptr); +} + +bool Control::IsMouseCaptured() { + auto host = GetWindowHost(); + if (host == nullptr) return false; + + return host->GetMouseCaptureControl() == this; +} + +std::shared_ptr Control::GetCursor() { return cursor_; } + +std::shared_ptr Control::GetInheritedCursor() { + Control* control = this; + while (control != nullptr) { + const auto cursor = control->GetCursor(); + if (cursor != nullptr) return cursor; + control = control->GetParent(); + } + return IUiApplication::GetInstance()->GetCursorManager()->GetSystemCursor( + SystemCursorType::Arrow); +} + +void Control::SetCursor(std::shared_ptr cursor) { + cursor_ = std::move(cursor); + const auto host = GetWindowHost(); + if (host != nullptr) { + host->UpdateCursor(); + } +} + +void Control::AddChild(Control* control, const Index position) { + Expects(control->GetParent() == + nullptr); // The control already has a parent. + Expects(position >= 0); + Expects(position <= static_cast( + children_.size())); // The position is out of range. + + children_.insert(children_.cbegin() + position, control); + + const auto old_parent = control->parent_; + control->parent_ = this; + + OnAddChild(control, position); + control->OnParentChanged(old_parent, this); + + if (window_host_) + control->TraverseDescendants([this](Control* control) { + control->window_host_ = window_host_; + control->OnAttachToHost(window_host_); + }); +} + +void Control::RemoveChild(const Index position) { + Expects(position >= 0); + Expects(position < static_cast( + children_.size())); // The position is out of range. + + const auto i = children_.cbegin() + position; + const auto control = *i; + + children_.erase(i); + control->parent_ = nullptr; + + OnRemoveChild(control, position); + control->OnParentChanged(this, nullptr); + + if (window_host_) + control->TraverseDescendants([this](Control* control) { + control->window_host_ = nullptr; + control->OnDetachFromHost(window_host_); + }); +} + +void Control::OnAddChild(Control* child, Index position) { + CRU_UNUSED(child) + CRU_UNUSED(position) +} +void Control::OnRemoveChild(Control* child, Index position) { + CRU_UNUSED(child) + CRU_UNUSED(position) +} + +void Control::OnParentChanged(Control* old_parent, Control* new_parent) { + CRU_UNUSED(old_parent) + CRU_UNUSED(new_parent) +} + +void Control::OnAttachToHost(host::WindowHost* host) { CRU_UNUSED(host) } + +void Control::OnDetachFromHost(host::WindowHost* host) { CRU_UNUSED(host) } +} // namespace cru::ui::controls diff --git a/src/ui/controls/LayoutControl.cpp b/src/ui/controls/LayoutControl.cpp new file mode 100644 index 00000000..85417beb --- /dev/null +++ b/src/ui/controls/LayoutControl.cpp @@ -0,0 +1,3 @@ +#include "cru/ui/controls/LayoutControl.hpp" + +namespace cru::ui::controls {} diff --git a/src/ui/controls/NoChildControl.cpp b/src/ui/controls/NoChildControl.cpp new file mode 100644 index 00000000..c62c5819 --- /dev/null +++ b/src/ui/controls/NoChildControl.cpp @@ -0,0 +1,3 @@ +#include "cru/ui/controls/NoChildControl.hpp" + +namespace cru::ui::controls {} diff --git a/src/ui/controls/TextControlService.hpp b/src/ui/controls/TextControlService.hpp index a7e4e440..8ad95dec 100644 --- a/src/ui/controls/TextControlService.hpp +++ b/src/ui/controls/TextControlService.hpp @@ -8,10 +8,10 @@ #include "cru/platform/gui/UiApplication.hpp" #include "cru/platform/gui/Window.hpp" #include "cru/ui/Base.hpp" -#include "cru/ui/Control.hpp" #include "cru/ui/DebugFlags.hpp" -#include "cru/ui/ShortcutHub.hpp" -#include "cru/ui/UiEvent.hpp" +#include "cru/ui/controls/Control.hpp" +#include "cru/ui/events/UiEvent.hpp" +#include "cru/ui/helper/ShortcutHub.hpp" #include "cru/ui/host/WindowHost.hpp" #include "cru/ui/render/CanvasRenderObject.hpp" #include "cru/ui/render/ScrollRenderObject.hpp" @@ -472,7 +472,7 @@ class TextControlService : public Object { platform::gui::TimerAutoCanceler caret_timer_canceler_; int caret_blink_duration_ = k_default_caret_blink_duration; - ShortcutHub shortcut_hub_; + helper::ShortcutHub shortcut_hub_; // nullopt means not selecting std::optional select_down_button_; diff --git a/src/ui/controls/Window.cpp b/src/ui/controls/Window.cpp new file mode 100644 index 00000000..7ce40dfe --- /dev/null +++ b/src/ui/controls/Window.cpp @@ -0,0 +1,32 @@ +#include "cru/ui/controls/Window.hpp" + +#include "cru/common/Base.hpp" +#include "cru/ui/host/WindowHost.hpp" +#include "cru/ui/render/Base.hpp" +#include "cru/ui/render/StackLayoutRenderObject.hpp" + +namespace cru::ui::controls { +Window* Window::CreateOverlapped() { return new Window(); } + +Window::Window() : render_object_(new render::StackLayoutRenderObject()) { + render_object_->SetAttachedControl(this); + window_host_ = std::make_unique(this); +} + +Window::~Window() {} + +std::u16string_view Window::GetControlType() const { return control_type; } + +render::RenderObject* Window::GetRenderObject() const { + return render_object_.get(); +} + +void Window::OnAddChild(Control* child, Index position) { + render_object_->AddChild(child->GetRenderObject(), position); +} + +void Window::OnRemoveChild(Control* child, Index position) { + CRU_UNUSED(child); + render_object_->RemoveChild(position); +} +} // namespace cru::ui::controls diff --git a/src/ui/events/UiEvent.cpp b/src/ui/events/UiEvent.cpp new file mode 100644 index 00000000..b35f15a7 --- /dev/null +++ b/src/ui/events/UiEvent.cpp @@ -0,0 +1,10 @@ +#include "cru/ui/events/UiEvent.hpp" + +#include "cru/ui/render/RenderObject.hpp" + +namespace cru::ui::event { +Point MouseEventArgs::GetPointToContent( + render::RenderObject* render_object) const { + return render_object->FromRootToContent(GetPoint()); +} +} // namespace cru::ui::event diff --git a/src/ui/helper/ClickDetector.cpp b/src/ui/helper/ClickDetector.cpp new file mode 100644 index 00000000..4059f890 --- /dev/null +++ b/src/ui/helper/ClickDetector.cpp @@ -0,0 +1,131 @@ +#include "cru/ui/helper/ClickDetector.hpp" + +#include "cru/common/Logger.hpp" + +#include + +namespace cru::ui::helper { +ClickDetector::ClickDetector(controls::Control* control) { + Expects(control); + control_ = control; + + event_rovoker_guards_.push_back( + EventRevokerGuard(control->MouseEnterEvent()->Direct()->AddHandler( + [this](event::MouseEventArgs&) { + if (this->enable_) { + if (this->state_ == ClickState::PressInactive) { + if ((this->button_ & this->trigger_button_)) { + this->SetState(ClickState::Press); + } + } else { + this->SetState(ClickState::Hover); + } + } + }))); + + event_rovoker_guards_.push_back( + EventRevokerGuard(control->MouseLeaveEvent()->Direct()->AddHandler( + [this](event::MouseEventArgs&) { + if (this->enable_) { + if (this->state_ == ClickState::Press) { + if ((this->button_ & this->trigger_button_)) { + this->SetState(ClickState::PressInactive); + } + } else { + this->SetState(ClickState::None); + } + } + }))); + + event_rovoker_guards_.push_back( + EventRevokerGuard(control->MouseDownEvent()->Direct()->AddHandler( + [this](event::MouseButtonEventArgs& args) { + const auto button = args.GetButton(); + if (this->enable_ && (button & this->trigger_button_) && + this->state_ == ClickState::Hover) { + if (!this->control_->CaptureMouse()) { + log::TagDebug(log_tag, + u"Failed to capture mouse when begin click."); + return; + } + this->down_point_ = args.GetPoint(); + this->button_ = button; + this->SetState(ClickState::Press); + } + }))); + + event_rovoker_guards_.push_back( + EventRevokerGuard(control->MouseUpEvent()->Direct()->AddHandler( + [this](event::MouseButtonEventArgs& args) { + const auto button = args.GetButton(); + if (this->enable_ && (button & this->trigger_button_) && + button == button_) { + if (this->state_ == ClickState::Press) { + this->SetState(ClickState::Hover); + this->event_.Raise(ClickEventArgs{this->control_, + this->down_point_, + args.GetPoint(), button}); + this->control_->ReleaseMouse(); + } else if (this->state_ == ClickState::PressInactive) { + this->SetState(ClickState::None); + this->control_->ReleaseMouse(); + } + } + }))); +} // namespace cru::ui + +void ClickDetector::SetEnabled(bool enable) { + if (enable == enable_) { + return; + } + + enable_ = enable; + if (enable) { + SetState(control_->IsMouseOver() ? ClickState::Hover : ClickState::None); + } else { + if (state_ == ClickState::Press || state_ == ClickState::PressInactive) { + SetState(ClickState::None); + control_->ReleaseMouse(); + } else if (state_ == ClickState::Hover) { + SetState(ClickState::None); + } + } +} + +void ClickDetector::SetTriggerButton(MouseButton trigger_button) { + if (trigger_button == trigger_button_) { + return; + } + + trigger_button_ = trigger_button; + if ((state_ == ClickState::Press || state_ == ClickState::PressInactive) && + !(button_ & trigger_button)) { + SetState(control_->IsMouseOver() ? ClickState::Hover : ClickState::None); + control_->ReleaseMouse(); + } +} + +void ClickDetector::SetState(ClickState state) { +#ifdef CRU_DEBUG + auto to_string = [](ClickState state) -> std::u16string_view { + switch (state) { + case ClickState::None: + return u"None"; + case ClickState::Hover: + return u"Hover"; + case ClickState::Press: + return u"Press"; + case ClickState::PressInactive: + return u"PressInvactive"; + default: + UnreachableCode(); + } + }; + log::TagDebug(log_tag, u"Click state changed, new state: {}.", + to_string(state)); +#endif + + state_ = state; + state_change_event_.Raise(state); +} +} // namespace cru::ui::helper diff --git a/src/ui/helper/ShortcutHub.cpp b/src/ui/helper/ShortcutHub.cpp new file mode 100644 index 00000000..823072f2 --- /dev/null +++ b/src/ui/helper/ShortcutHub.cpp @@ -0,0 +1,120 @@ +#include "cru/ui/helper/ShortcutHub.hpp" + +#include "cru/common/Logger.hpp" +#include "cru/ui/DebugFlags.hpp" +#include "cru/ui/controls/Control.hpp" + +#include +#include +#include +#include + +namespace cru::ui::helper { +int ShortcutHub::RegisterShortcut(Shortcut shortcut) { + const int id = current_id_++; + map_[shortcut.key_bind].push_back({id, std::move(shortcut.name), + shortcut.key_bind, + std::move(shortcut.handler)}); + return id; +} + +void ShortcutHub::UnregisterShortcut(int id) { + if (id <= 0) return; + for (auto& pair : map_) { + auto& list = pair.second; + auto result = + std::find_if(list.cbegin(), list.cend(), + [id](const ShortcutInfo& info) { return info.id == id; }); + if (result != list.cend()) { + list.erase(result); + } + } +} + +std::vector ShortcutHub::GetAllShortcuts() const { + std::vector result; + + for (const auto& pair : map_) { + std::copy(pair.second.cbegin(), pair.second.cend(), + std::back_inserter(result)); + } + + return result; +} + +std::optional ShortcutHub::GetShortcut(int id) const { + for (auto& pair : map_) { + auto& list = pair.second; + auto result = + std::find_if(list.cbegin(), list.cend(), + [id](const ShortcutInfo& info) { return info.id == id; }); + if (result != list.cend()) { + return *result; + } + } + return std::nullopt; +} + +const std::vector& ShortcutHub::GetShortcutByKeyBind( + const ShortcutKeyBind& key_bind) const { + auto result = map_.find(key_bind); + if (result != map_.cend()) return result->second; + return empty_list_; +} + +void ShortcutHub::Install(controls::Control* control) { + if (!event_guard_.IsEmpty()) { + log::Error(u"Shortcut hub is already installed. Failed to install."); + return; + } + + event_guard_ += control->KeyDownEvent()->Bubble()->AddHandler( + std::bind(&ShortcutHub::OnKeyDown, this, std::placeholders::_1)); +} + +void ShortcutHub::Uninstall() { + if (event_guard_.IsEmpty()) { + log::Warn(u"Shortcut hub is not installed. Failed to uninstall."); + return; + } + + event_guard_.Clear(); +} + +void ShortcutHub::OnKeyDown(event::KeyEventArgs& event) { + ShortcutKeyBind key_bind(event.GetKeyCode(), event.GetKeyModifier()); + const auto& shortcut_list = this->GetShortcutByKeyBind(key_bind); + + if constexpr (debug_flags::shortcut) { + if (shortcut_list.empty()) { + log::Debug(u"No shortcut for key bind {}.", key_bind.ToString()); + } + log::Debug(u"Begin to handle shortcut for key bind {}.", + key_bind.ToString()); + } + + for (const auto& shortcut : shortcut_list) { + auto is_handled = shortcut.handler(); + if (is_handled) { + if constexpr (debug_flags::shortcut) { + log::Debug(u"Handle {} handled it.", shortcut.name); + } + + event.SetHandled(); + + break; + } else { + if constexpr (debug_flags::shortcut) { + log::Debug(u"Handle {} disdn't handle it.", shortcut.name); + } + } + } + + if constexpr (debug_flags::shortcut) { + if (!shortcut_list.empty()) { + log::Debug(u"End handling shortcut for key bind {}.", + key_bind.ToString()); + } + } +} +} // namespace cru::ui::helper diff --git a/src/ui/host/RoutedEventDispatch.hpp b/src/ui/host/RoutedEventDispatch.hpp index de94a598..52507fc7 100644 --- a/src/ui/host/RoutedEventDispatch.hpp +++ b/src/ui/host/RoutedEventDispatch.hpp @@ -1,8 +1,7 @@ #pragma once -#include "cru/ui/Control.hpp" - #include "cru/common/Logger.hpp" #include "cru/ui/DebugFlags.hpp" +#include "cru/ui/controls/Control.hpp" #include @@ -21,10 +20,11 @@ namespace cru::ui { // "original_sender", which is unchanged. And "args" will be perfectly forwarded // as the rest arguments. template -void DispatchEvent(const std::u16string_view& event_name, - Control* const original_sender, - event::RoutedEvent* (Control::*event_ptr)(), - Control* const last_receiver, Args&&... args) { +void DispatchEvent( + const std::u16string_view& event_name, + controls::Control* const original_sender, + event::RoutedEvent* (controls::Control::*event_ptr)(), + controls::Control* const last_receiver, Args&&... args) { CRU_UNUSED(event_name) if (original_sender == last_receiver) { @@ -36,7 +36,7 @@ void DispatchEvent(const std::u16string_view& event_name, return; } - std::vector receive_list; + std::vector receive_list; auto parent = original_sender; while (parent != last_receiver) { diff --git a/src/ui/host/WindowHost.cpp b/src/ui/host/WindowHost.cpp index e04fdf31..1702c4ed 100644 --- a/src/ui/host/WindowHost.cpp +++ b/src/ui/host/WindowHost.cpp @@ -8,7 +8,7 @@ #include "cru/platform/gui/UiApplication.hpp" #include "cru/platform/gui/Window.hpp" #include "cru/ui/DebugFlags.hpp" -#include "cru/ui/Window.hpp" +#include "cru/ui/controls/Window.hpp" #include "cru/ui/host/LayoutPaintCycler.hpp" #include "cru/ui/render/MeasureRequirement.hpp" #include "cru/ui/render/RenderObject.hpp" @@ -43,7 +43,7 @@ CRU_DEFINE_EVENT_NAME(KeyUp) } // namespace event_names namespace { -bool IsAncestor(Control* control, Control* ancestor) { +bool IsAncestor(controls::Control* control, controls::Control* ancestor) { while (control != nullptr) { if (control == ancestor) return true; control = control->GetParent(); @@ -52,8 +52,8 @@ bool IsAncestor(Control* control, Control* ancestor) { } // Ancestor at last. -std::vector GetAncestorList(Control* control) { - std::vector l; +std::vector GetAncestorList(controls::Control* control) { + std::vector l; while (control != nullptr) { l.push_back(control); control = control->GetParent(); @@ -61,7 +61,8 @@ std::vector GetAncestorList(Control* control) { return l; } -Control* FindLowestCommonAncestor(Control* left, Control* right) { +controls::Control* FindLowestCommonAncestor(controls::Control* left, + controls::Control* right) { if (left == nullptr || right == nullptr) return nullptr; auto&& left_list = GetAncestorList(left); @@ -102,13 +103,13 @@ inline void BindNativeEvent( } } // namespace -WindowHost::WindowHost(Control* root_control) +WindowHost::WindowHost(controls::Control* root_control) : root_control_(root_control), focus_control_(root_control) { const auto ui_application = IUiApplication::GetInstance(); auto native_window = ui_application->CreateWindow(nullptr); native_window_ = native_window; - root_control_->TraverseDescendants([this](Control* control) { + root_control_->TraverseDescendants([this](controls::Control* control) { control->window_host_ = this; control->OnAttachToHost(this); }); @@ -195,9 +196,9 @@ void WindowHost::Repaint() { painter->EndDraw(); } -Control* WindowHost::GetFocusControl() { return focus_control_; } +controls::Control* WindowHost::GetFocusControl() { return focus_control_; } -void WindowHost::SetFocusControl(Control* control) { +void WindowHost::SetFocusControl(controls::Control* control) { if (focus_control_ == control) return; if (control == nullptr) control = root_control_; @@ -206,13 +207,13 @@ void WindowHost::SetFocusControl(Control* control) { focus_control_ = control; DispatchEvent(event_names::LoseFocus, old_focus_control, - &Control::LoseFocusEvent, nullptr, false); + &controls::Control::LoseFocusEvent, nullptr, false); - DispatchEvent(event_names::GainFocus, control, &Control::GainFocusEvent, - nullptr, false); + DispatchEvent(event_names::GainFocus, control, + &controls::Control::GainFocusEvent, nullptr, false); } -bool WindowHost::CaptureMouseFor(Control* control) { +bool WindowHost::CaptureMouseFor(controls::Control* control) { if (!native_window_) return false; if (control == mouse_captured_control_) return true; @@ -240,7 +241,7 @@ bool WindowHost::CaptureMouseFor(Control* control) { return true; } -Control* WindowHost::GetMouseCaptureControl() { +controls::Control* WindowHost::GetMouseCaptureControl() { return mouse_captured_control_; } @@ -275,9 +276,9 @@ void WindowHost::OnNativeFocus(INativeWindow* window, focus == platform::gui::FocusChangeType::Gain ? DispatchEvent(event_names::GainFocus, focus_control_, - &Control::GainFocusEvent, nullptr, true) + &controls::Control::GainFocusEvent, nullptr, true) : DispatchEvent(event_names::LoseFocus, focus_control_, - &Control::LoseFocusEvent, nullptr, true); + &controls::Control::LoseFocusEvent, nullptr, true); } void WindowHost::OnNativeMouseEnterLeave( @@ -286,7 +287,7 @@ void WindowHost::OnNativeMouseEnterLeave( if (type == platform::gui::MouseEnterLeaveType::Leave) { DispatchEvent(event_names::MouseLeave, mouse_hover_control_, - &Control::MouseLeaveEvent, nullptr); + &controls::Control::MouseLeaveEvent, nullptr); mouse_hover_control_ = nullptr; } } @@ -306,13 +307,14 @@ void WindowHost::OnNativeMouseMove(INativeWindow* window, const Point& point) { mouse_captured_control_); bool a = IsAncestor(o, n); if (a) { - DispatchEvent(event_names::MouseLeave, o, &Control::MouseLeaveEvent, n); + DispatchEvent(event_names::MouseLeave, o, + &controls::Control::MouseLeaveEvent, n); } else { - DispatchEvent(event_names::MouseEnter, n, &Control::MouseEnterEvent, o, - point); + DispatchEvent(event_names::MouseEnter, n, + &controls::Control::MouseEnterEvent, o, point); } DispatchEvent(event_names::MouseMove, mouse_captured_control_, - &Control::MouseMoveEvent, nullptr, point); + &controls::Control::MouseMoveEvent, nullptr, point); UpdateCursor(); return; } @@ -320,7 +322,7 @@ void WindowHost::OnNativeMouseMove(INativeWindow* window, const Point& point) { DispatchMouseHoverControlChangeEvent( old_mouse_hover_control, new_mouse_hover_control, point, false, false); DispatchEvent(event_names::MouseMove, new_mouse_hover_control, - &Control::MouseMoveEvent, nullptr, point); + &controls::Control::MouseMoveEvent, nullptr, point); UpdateCursor(); } @@ -329,10 +331,11 @@ void WindowHost::OnNativeMouseDown( const platform::gui::NativeMouseButtonEventArgs& args) { CRU_UNUSED(window) - Control* control = + controls::Control* control = mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point); - DispatchEvent(event_names::MouseDown, control, &Control::MouseDownEvent, - nullptr, args.point, args.button, args.modifier); + DispatchEvent(event_names::MouseDown, control, + &controls::Control::MouseDownEvent, nullptr, args.point, + args.button, args.modifier); } void WindowHost::OnNativeMouseUp( @@ -340,44 +343,44 @@ void WindowHost::OnNativeMouseUp( const platform::gui::NativeMouseButtonEventArgs& args) { CRU_UNUSED(window) - Control* control = + controls::Control* control = mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point); - DispatchEvent(event_names::MouseUp, control, &Control::MouseUpEvent, nullptr, - args.point, args.button, args.modifier); + DispatchEvent(event_names::MouseUp, control, &controls::Control::MouseUpEvent, + nullptr, args.point, args.button, args.modifier); } void WindowHost::OnNativeKeyDown( INativeWindow* window, const platform::gui::NativeKeyEventArgs& args) { CRU_UNUSED(window) - DispatchEvent(event_names::KeyDown, focus_control_, &Control::KeyDownEvent, - nullptr, args.key, args.modifier); + DispatchEvent(event_names::KeyDown, focus_control_, + &controls::Control::KeyDownEvent, nullptr, args.key, + args.modifier); } void WindowHost::OnNativeKeyUp(INativeWindow* window, const platform::gui::NativeKeyEventArgs& args) { CRU_UNUSED(window) - DispatchEvent(event_names::KeyUp, focus_control_, &Control::KeyUpEvent, - nullptr, args.key, args.modifier); + DispatchEvent(event_names::KeyUp, focus_control_, + &controls::Control::KeyUpEvent, nullptr, args.key, + args.modifier); } -void WindowHost::DispatchMouseHoverControlChangeEvent(Control* old_control, - Control* new_control, - const Point& point, - bool no_leave, - bool no_enter) { +void WindowHost::DispatchMouseHoverControlChangeEvent( + controls::Control* old_control, controls::Control* new_control, + const Point& point, bool no_leave, bool no_enter) { if (new_control != old_control) // if the mouse-hover-on control changed { const auto lowest_common_ancestor = FindLowestCommonAncestor(old_control, new_control); if (!no_leave && old_control != nullptr) DispatchEvent(event_names::MouseLeave, old_control, - &Control::MouseLeaveEvent, + &controls::Control::MouseLeaveEvent, lowest_common_ancestor); // dispatch mouse leave event. if (!no_enter && new_control != nullptr) { DispatchEvent(event_names::MouseEnter, new_control, - &Control::MouseEnterEvent, lowest_common_ancestor, + &controls::Control::MouseEnterEvent, lowest_common_ancestor, point); // dispatch mouse enter event. } } @@ -391,7 +394,7 @@ void WindowHost::UpdateCursor() { } } -Control* WindowHost::HitTest(const Point& point) { +controls::Control* WindowHost::HitTest(const Point& point) { const auto render_object = root_render_object_->HitTest(point); if (render_object) { const auto control = render_object->GetAttachedControl(); diff --git a/src/win/gui/UiApplication.cpp b/src/win/gui/UiApplication.cpp index 5041a6c0..f4541dd0 100644 --- a/src/win/gui/UiApplication.cpp +++ b/src/win/gui/UiApplication.cpp @@ -36,8 +36,8 @@ WinUiApplication::WinUiApplication() { log::Logger::GetInstance()->AddSource( std::make_unique<::cru::platform::win::WinStdOutLoggerSource>()); - graph_factory_ = - std::make_unique(); + graph_factory_ = std::make_unique< + cru::platform::graphics::win::direct::DirectGraphFactory>(); god_window_ = std::make_unique(this); timer_manager_ = std::make_unique(god_window_.get()); @@ -99,13 +99,17 @@ std::vector WinUiApplication::GetAllWindow() { return result; } -INativeWindow* WinUiApplication::CreateWindow(INativeWindow* parent) { +INativeWindow* WinUiApplication::CreateWindow(INativeWindow* parent, + CreateWindowFlag flag) { WinNativeWindow* p = nullptr; if (parent != nullptr) { p = CheckPlatform(parent, GetPlatformId()); } return new WinNativeWindow(this, window_manager_->GetGeneralWindowClass(), - WS_OVERLAPPEDWINDOW, p); + flag & CreateWindowFlags::NoCaptionAndBorder + ? WS_POPUP + : WS_OVERLAPPEDWINDOW, + p); } cru::platform::graphics::IGraphFactory* WinUiApplication::GetGraphFactory() { -- cgit v1.2.3