From d86a71f79afe0e4dac768f61d6bff690567aca5b Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 24 May 2020 01:40:02 +0800 Subject: ... --- include/cru/ui/ClickDetector.hpp | 86 ++++++++ include/cru/ui/ContentControl.hpp | 29 +++ include/cru/ui/LayoutControl.hpp | 31 +++ include/cru/ui/NoChildControl.hpp | 24 +++ include/cru/ui/UiEvent.hpp | 229 +++++++++++++++++++++ include/cru/ui/UiHost.hpp | 170 +++++++++++++++ include/cru/ui/UiManager.hpp | 35 ++++ include/cru/ui/base.hpp | 6 +- include/cru/ui/click_detector.hpp | 86 -------- include/cru/ui/content_control.hpp | 29 --- include/cru/ui/control.hpp | 8 +- include/cru/ui/controls/FlexLayout.hpp | 41 ++++ include/cru/ui/controls/StackLayout.hpp | 31 +++ include/cru/ui/controls/TextBlock.hpp | 38 ++++ include/cru/ui/controls/TextBox.hpp | 49 +++++ include/cru/ui/controls/base.hpp | 2 +- include/cru/ui/controls/button.hpp | 6 +- include/cru/ui/controls/container.hpp | 2 +- include/cru/ui/controls/flex_layout.hpp | 41 ---- include/cru/ui/controls/stack_layout.hpp | 31 --- include/cru/ui/controls/text_block.hpp | 38 ---- include/cru/ui/controls/text_box.hpp | 49 ----- include/cru/ui/layout_control.hpp | 31 --- include/cru/ui/no_child_control.hpp | 24 --- include/cru/ui/render/BorderRenderObject.hpp | 102 +++++++++ include/cru/ui/render/CanvasRenderObject.hpp | 39 ++++ include/cru/ui/render/FlexLayoutRenderObject.hpp | 42 ++++ include/cru/ui/render/LayoutRenderObject.hpp | 88 ++++++++ include/cru/ui/render/LayoutUtility.hpp | 7 + include/cru/ui/render/RenderObject.hpp | 118 +++++++++++ include/cru/ui/render/ScrollRenderObject.hpp | 30 +++ include/cru/ui/render/StackLayoutRenderObject.hpp | 17 ++ include/cru/ui/render/TextRenderObject.hpp | 85 ++++++++ include/cru/ui/render/WindowRenderObject.hpp | 30 +++ include/cru/ui/render/base.hpp | 2 +- include/cru/ui/render/border_render_object.hpp | 102 --------- include/cru/ui/render/canvas_render_object.hpp | 39 ---- .../cru/ui/render/flex_layout_render_object.hpp | 42 ---- include/cru/ui/render/layout_render_object.hpp | 88 -------- include/cru/ui/render/layout_utility.hpp | 7 - include/cru/ui/render/render_object.hpp | 118 ----------- include/cru/ui/render/scroll_render_object.hpp | 30 --- .../cru/ui/render/stack_layout_render_object.hpp | 17 -- include/cru/ui/render/text_render_object.hpp | 85 -------- include/cru/ui/render/window_render_object.hpp | 30 --- include/cru/ui/ui_event.hpp | 229 --------------------- include/cru/ui/ui_host.hpp | 170 --------------- include/cru/ui/ui_manager.hpp | 35 ---- include/cru/ui/window.hpp | 2 +- 49 files changed, 1335 insertions(+), 1335 deletions(-) create mode 100644 include/cru/ui/ClickDetector.hpp create mode 100644 include/cru/ui/ContentControl.hpp create mode 100644 include/cru/ui/LayoutControl.hpp create mode 100644 include/cru/ui/NoChildControl.hpp create mode 100644 include/cru/ui/UiEvent.hpp create mode 100644 include/cru/ui/UiHost.hpp create mode 100644 include/cru/ui/UiManager.hpp delete mode 100644 include/cru/ui/click_detector.hpp delete mode 100644 include/cru/ui/content_control.hpp create mode 100644 include/cru/ui/controls/FlexLayout.hpp create mode 100644 include/cru/ui/controls/StackLayout.hpp create mode 100644 include/cru/ui/controls/TextBlock.hpp create mode 100644 include/cru/ui/controls/TextBox.hpp delete mode 100644 include/cru/ui/controls/flex_layout.hpp delete mode 100644 include/cru/ui/controls/stack_layout.hpp delete mode 100644 include/cru/ui/controls/text_block.hpp delete mode 100644 include/cru/ui/controls/text_box.hpp delete mode 100644 include/cru/ui/layout_control.hpp delete mode 100644 include/cru/ui/no_child_control.hpp create mode 100644 include/cru/ui/render/BorderRenderObject.hpp create mode 100644 include/cru/ui/render/CanvasRenderObject.hpp create mode 100644 include/cru/ui/render/FlexLayoutRenderObject.hpp create mode 100644 include/cru/ui/render/LayoutRenderObject.hpp create mode 100644 include/cru/ui/render/LayoutUtility.hpp create mode 100644 include/cru/ui/render/RenderObject.hpp create mode 100644 include/cru/ui/render/ScrollRenderObject.hpp create mode 100644 include/cru/ui/render/StackLayoutRenderObject.hpp create mode 100644 include/cru/ui/render/TextRenderObject.hpp create mode 100644 include/cru/ui/render/WindowRenderObject.hpp delete mode 100644 include/cru/ui/render/border_render_object.hpp delete mode 100644 include/cru/ui/render/canvas_render_object.hpp delete mode 100644 include/cru/ui/render/flex_layout_render_object.hpp delete mode 100644 include/cru/ui/render/layout_render_object.hpp delete mode 100644 include/cru/ui/render/layout_utility.hpp delete mode 100644 include/cru/ui/render/render_object.hpp delete mode 100644 include/cru/ui/render/scroll_render_object.hpp delete mode 100644 include/cru/ui/render/stack_layout_render_object.hpp delete mode 100644 include/cru/ui/render/text_render_object.hpp delete mode 100644 include/cru/ui/render/window_render_object.hpp delete mode 100644 include/cru/ui/ui_event.hpp delete mode 100644 include/cru/ui/ui_host.hpp delete mode 100644 include/cru/ui/ui_manager.hpp (limited to 'include/cru/ui') diff --git a/include/cru/ui/ClickDetector.hpp b/include/cru/ui/ClickDetector.hpp new file mode 100644 index 00000000..f5fd3d8f --- /dev/null +++ b/include/cru/ui/ClickDetector.hpp @@ -0,0 +1,86 @@ +#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 { + 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 new file mode 100644 index 00000000..19f13a1d --- /dev/null +++ b/include/cru/ui/ContentControl.hpp @@ -0,0 +1,29 @@ +#pragma once +#include "Control.hpp" + +namespace cru::ui { +class ContentControl : public Control { + protected: + ContentControl(); + + public: + ContentControl(const ContentControl& other) = delete; + ContentControl(ContentControl&& other) = delete; + ContentControl& operator=(const ContentControl& other) = delete; + ContentControl& operator=(ContentControl&& other) = delete; + ~ContentControl() override; + + const std::vector& GetChildren() const override final { + return child_vector_; + } + Control* GetChild() const { return child_; } + void SetChild(Control* child); + + protected: + virtual void OnChildChanged(Control* old_child, Control* new_child); + + private: + std::vector child_vector_; + Control*& child_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/LayoutControl.hpp b/include/cru/ui/LayoutControl.hpp new file mode 100644 index 00000000..7997b37e --- /dev/null +++ b/include/cru/ui/LayoutControl.hpp @@ -0,0 +1,31 @@ +#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; + + 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_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/NoChildControl.hpp b/include/cru/ui/NoChildControl.hpp new file mode 100644 index 00000000..1a31ae7e --- /dev/null +++ b/include/cru/ui/NoChildControl.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "Control.hpp" + +namespace cru::ui { +class NoChildControl : public Control { + private: + static const std::vector empty_control_vector; + + 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; + + protected: + const std::vector& GetChildren() const override final { + return empty_control_vector; + } +}; +} // namespace cru::ui diff --git a/include/cru/ui/UiEvent.hpp b/include/cru/ui/UiEvent.hpp new file mode 100644 index 00000000..29292d75 --- /dev/null +++ b/include/cru/ui/UiEvent.hpp @@ -0,0 +1,229 @@ +#pragma once +#include "Base.hpp" + +#include "cru/common/Event.hpp" +#include "cru/platform/native/Keyboard.hpp" + +#include +#include +#include +#include + +namespace cru::platform::graph { +struct IPainter; +} + +namespace cru::ui { +class Control; +} + +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 EventArgs = TEventArgs; + + 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{}); } + + private: + std::optional point_; +}; + +class MouseButtonEventArgs : public MouseEventArgs { + public: + MouseButtonEventArgs(Object* sender, Object* original_sender, + const Point& point, const MouseButton button, + platform::native::KeyModifier key_modifier) + : MouseEventArgs(sender, original_sender, point), + button_(button), + key_modifier_(key_modifier) {} + MouseButtonEventArgs(Object* sender, Object* original_sender, + const MouseButton button, + platform::native::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::native::KeyModifier GetKeyModifier() const { return key_modifier_; } + + private: + MouseButton button_; + platform::native::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::graph::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::graph::IPainter* GetPainter() const { return painter_; } + + private: + platform::graph::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::native::KeyCode key_code, + platform::native::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::native::KeyCode GetKeyCode() const { return key_code_; } + platform::native::KeyModifier GetKeyModifier() const { return key_modifier_; } + + private: + platform::native::KeyCode key_code_; + platform::native::KeyModifier key_modifier_; +}; + +class CharEventArgs : public UiEventArgs { + public: + CharEventArgs(Object* sender, Object* original_sender, std::string 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::string GetChar() const { return c_; } + + private: + std::string c_; +}; +} // namespace cru::ui::event diff --git a/include/cru/ui/UiHost.hpp b/include/cru/ui/UiHost.hpp new file mode 100644 index 00000000..651dab81 --- /dev/null +++ b/include/cru/ui/UiHost.hpp @@ -0,0 +1,170 @@ +#pragma once +#include "Base.hpp" + +#include "cru/common/Event.hpp" +#include "cru/common/SelfResolvable.hpp" +#include "render/Base.hpp" + +namespace cru::ui { +struct AfterLayoutEventArgs {}; + +// The host of all controls and render objects. +// +// 3 situations on destroy: +// 1. Native window destroyed, IsRetainAfterDestroy: false: +// OnNativeDestroy(set native_window_destroyed_ to true, call ~Window due to +// deleting_ is false and IsRetainAfterDestroy is false) -> ~Window -> +// ~UiHost(not destroy native window repeatedly due to native_window_destroyed_ +// is true) +// 2. Native window destroyed, IsRetainAfterDestroy: true: +// OnNativeDestroy(set native_window_destroyed_ to true, not call ~Window +// because deleting_ is false and IsRetainAfterDestroy is true) +// then, ~Window -> ~UiHost(not destroy native window repeatedly due to +// native_window_destroyed_ is true) +// 3. Native window not destroyed, ~Window is called: +// ~Window -> ~UiHost(set deleting_ to true, destroy native window +// due to native_window_destroyed is false) -> OnNativeDestroy(not call ~Window +// due to deleting_ is true and IsRetainAfterDestroy is whatever) +// In conclusion: +// 1. Set native_window_destroyed_ to true at the beginning of OnNativeDestroy. +// 2. Set deleting_ to true at the beginning of ~UiHost. +// 3. Destroy native window when native_window_destroy_ is false in ~Window. +// 4. Delete Window when deleting_ is false and IsRetainAfterDestroy is false in +// OnNativeDestroy. +class UiHost : public Object, public SelfResolvable { + public: + // This will create root window render object and attach it to window. + // It will also create and manage a native window. + UiHost(Window* window); + + CRU_DELETE_COPY(UiHost) + CRU_DELETE_MOVE(UiHost) + + ~UiHost() override; + + public: + // 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. + void InvalidateLayout(); + + // Mark the paint as invalid, and arrange a re-paint later. + // This method could be called more than one times in a message cycle. But + // paint only takes place once. + void InvalidatePaint(); + + IEvent* AfterLayoutEvent() { + return &after_layout_event_; + } + + 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 + // get more info. + Control* GetMouseHoverControl() const { return mouse_hover_control_; } + + //*************** region: focus *************** + + // Request focus for specified control. + bool RequestFocusFor(Control* control); + + // Get the control that has focus. + Control* GetFocusControl(); + + //*************** region: focus *************** + + // Pass nullptr to release capture. If mouse is already capture by a control, + // this capture will fail and return false. If control is identical to the + // capturing control, capture is not changed and this function will return + // true. + // + // When capturing control changes, + // appropriate event will be sent. If mouse is not on the capturing control + // 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); + + // Return null if not captured. + Control* GetMouseCaptureControl(); + + Control* HitTest(const Point& point); + + void UpdateCursor(); + + std::shared_ptr + GetNativeWindowResolver() { + return native_window_resolver_; + } + + bool IsRetainAfterDestroy() { return retain_after_destroy_; } + + void SetRetainAfterDestroy(bool destroy) { retain_after_destroy_ = destroy; } + + private: + //*************** region: native messages *************** + void OnNativeDestroy(platform::native::INativeWindow* window, std::nullptr_t); + void OnNativePaint(platform::native::INativeWindow* window, std::nullptr_t); + void OnNativeResize(platform::native::INativeWindow* window, + const Size& size); + + void OnNativeFocus(platform::native::INativeWindow* window, + cru::platform::native::FocusChangeType focus); + + void OnNativeMouseEnterLeave( + platform::native::INativeWindow* window, + cru::platform::native::MouseEnterLeaveType enter); + void OnNativeMouseMove(platform::native::INativeWindow* window, + const Point& point); + void OnNativeMouseDown( + platform::native::INativeWindow* window, + const platform::native::NativeMouseButtonEventArgs& args); + void OnNativeMouseUp( + platform::native::INativeWindow* window, + const platform::native::NativeMouseButtonEventArgs& args); + + void OnNativeKeyDown(platform::native::INativeWindow* window, + const platform::native::NativeKeyEventArgs& args); + void OnNativeKeyUp(platform::native::INativeWindow* window, + const platform::native::NativeKeyEventArgs& args); + + //*************** region: event dispatcher helper *************** + + void DispatchMouseHoverControlChangeEvent(Control* old_control, + Control* new_control, + const Point& point, bool no_leave, + bool no_enter); + + private: + bool need_layout_ = false; + + Event after_layout_event_; + + std::shared_ptr + native_window_resolver_; + + // See remarks of UiHost. + bool retain_after_destroy_ = false; + // See remarks of UiHost. + 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 UiHost. + bool native_window_destroyed_ = false; + + std::vector event_revoker_guards_; + + Window* window_control_; + std::unique_ptr root_render_object_; + + Control* mouse_hover_control_; + + Control* focus_control_; // "focus_control_" can't be nullptr + + Control* mouse_captured_control_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/UiManager.hpp b/include/cru/ui/UiManager.hpp new file mode 100644 index 00000000..e6facdbd --- /dev/null +++ b/include/cru/ui/UiManager.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "Base.hpp" + +#include "controls/Base.hpp" + +namespace cru::ui { +struct ThemeResources { + std::shared_ptr default_font; + std::shared_ptr text_brush; + std::shared_ptr text_selection_brush; + std::shared_ptr caret_brush; + controls::ButtonStyle button_style; + controls::TextBoxBorderStyle text_box_border_style; +}; + +class UiManager : public Object { + public: + static UiManager* GetInstance(); + + private: + UiManager(); + + public: + UiManager(const UiManager& other) = delete; + UiManager(UiManager&& other) = delete; + UiManager& operator=(const UiManager& other) = delete; + UiManager& operator=(UiManager&& other) = delete; + ~UiManager() override; + + ThemeResources* GetThemeResources() { return &theme_resource_; } + + private: + ThemeResources theme_resource_; +}; +} // namespace cru::ui diff --git a/include/cru/ui/base.hpp b/include/cru/ui/base.hpp index a1335f90..97b0dbff 100644 --- a/include/cru/ui/base.hpp +++ b/include/cru/ui/base.hpp @@ -1,7 +1,7 @@ #pragma once -#include "cru/common/base.hpp" -#include "cru/platform/graph/base.hpp" -#include "cru/platform/native/base.hpp" +#include "cru/common/Base.hpp" +#include "cru/platform/graph/Base.hpp" +#include "cru/platform/native/Base.hpp" #include #include diff --git a/include/cru/ui/click_detector.hpp b/include/cru/ui/click_detector.hpp deleted file mode 100644 index 6c4761e7..00000000 --- a/include/cru/ui/click_detector.hpp +++ /dev/null @@ -1,86 +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 { - 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/content_control.hpp b/include/cru/ui/content_control.hpp deleted file mode 100644 index f88ec157..00000000 --- a/include/cru/ui/content_control.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "control.hpp" - -namespace cru::ui { -class ContentControl : public Control { - protected: - ContentControl(); - - public: - ContentControl(const ContentControl& other) = delete; - ContentControl(ContentControl&& other) = delete; - ContentControl& operator=(const ContentControl& other) = delete; - ContentControl& operator=(ContentControl&& other) = delete; - ~ContentControl() override; - - const std::vector& GetChildren() const override final { - return child_vector_; - } - Control* GetChild() const { return child_; } - void SetChild(Control* child); - - protected: - virtual void OnChildChanged(Control* old_child, Control* new_child); - - private: - std::vector child_vector_; - Control*& child_; -}; -} // namespace cru::ui diff --git a/include/cru/ui/control.hpp b/include/cru/ui/control.hpp index d66405e6..347163be 100644 --- a/include/cru/ui/control.hpp +++ b/include/cru/ui/control.hpp @@ -1,9 +1,9 @@ #pragma once -#include "base.hpp" +#include "Base.hpp" -#include "cru/common/event.hpp" -#include "render/base.hpp" -#include "ui_event.hpp" +#include "cru/common/Event.hpp" +#include "render/Base.hpp" +#include "UiEvent.hpp" #include diff --git a/include/cru/ui/controls/FlexLayout.hpp b/include/cru/ui/controls/FlexLayout.hpp new file mode 100644 index 00000000..beacd1f9 --- /dev/null +++ b/include/cru/ui/controls/FlexLayout.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "../LayoutControl.hpp" + +namespace cru::ui::controls { +class FlexLayout : public LayoutControl { + public: + static constexpr std::string_view control_type = "FlexLayout"; + + static FlexLayout* Create() { return new FlexLayout(); } + + protected: + FlexLayout(); + + public: + FlexLayout(const FlexLayout& other) = delete; + FlexLayout(FlexLayout&& other) = delete; + FlexLayout& operator=(const FlexLayout& other) = delete; + FlexLayout& operator=(FlexLayout&& other) = delete; + ~FlexLayout() override; + + std::string_view GetControlType() const final { return control_type; } + + render::RenderObject* GetRenderObject() const override; + + FlexMainAlignment GetContentMainAlign() const; + void SetContentMainAlign(FlexMainAlignment value); + + FlexDirection GetFlexDirection() const; + void SetFlexDirection(FlexDirection direction); + + FlexChildLayoutData GetChildLayoutData(Control* control); + void SetChildLayoutData(Control* control, const FlexChildLayoutData& data); + + protected: + void OnAddChild(Control* child, Index position) override; + void OnRemoveChild(Control* child, Index position) override; + + private: + std::shared_ptr render_object_; +}; +} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/StackLayout.hpp b/include/cru/ui/controls/StackLayout.hpp new file mode 100644 index 00000000..d5998cc4 --- /dev/null +++ b/include/cru/ui/controls/StackLayout.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "../LayoutControl.hpp" + +namespace cru::ui::controls { +class StackLayout : public LayoutControl { + public: + static constexpr std::string_view control_type = "StackLayout"; + + static StackLayout* Create() { return new StackLayout(); } + + protected: + StackLayout(); + + public: + CRU_DELETE_COPY(StackLayout) + CRU_DELETE_MOVE(StackLayout) + + ~StackLayout() override; + + std::string_view GetControlType() const final { return control_type; } + + render::RenderObject* GetRenderObject() const override; + + protected: + void OnAddChild(Control* child, Index position) override; + void OnRemoveChild(Control* child, Index position) override; + + private: + std::shared_ptr render_object_; +}; +} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/TextBlock.hpp b/include/cru/ui/controls/TextBlock.hpp new file mode 100644 index 00000000..dd8b40b4 --- /dev/null +++ b/include/cru/ui/controls/TextBlock.hpp @@ -0,0 +1,38 @@ +#pragma once +#include "../NoChildControl.hpp" + +namespace cru::ui::controls { +template +class TextControlService; + +class TextBlock : public NoChildControl { + public: + static constexpr std::string_view control_type = "TextBlock"; + + static TextBlock* Create() { return new TextBlock(); } + + protected: + TextBlock(); + + public: + TextBlock(const TextBlock& other) = delete; + TextBlock(TextBlock&& other) = delete; + TextBlock& operator=(const TextBlock& other) = delete; + TextBlock& operator=(TextBlock&& other) = delete; + ~TextBlock() override; + + std::string_view GetControlType() const final { return control_type; } + + render::RenderObject* GetRenderObject() const override; + + std::string GetText() const; + void SetText(std::string text); + + render::TextRenderObject* GetTextRenderObject(); + + private: + std::unique_ptr text_render_object_; + + std::unique_ptr> service_; +}; +} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/TextBox.hpp b/include/cru/ui/controls/TextBox.hpp new file mode 100644 index 00000000..2f7a12b6 --- /dev/null +++ b/include/cru/ui/controls/TextBox.hpp @@ -0,0 +1,49 @@ +#pragma once +#include "../NoChildControl.hpp" +#include "Base.hpp" + +#include + +namespace cru::ui::controls { +template +class TextControlService; + +class TextBox : public NoChildControl { + public: + static constexpr std::string_view control_type = "TextBox"; + + static TextBox* Create() { return new TextBox(); } + + protected: + TextBox(); + + public: + CRU_DELETE_COPY(TextBox) + CRU_DELETE_MOVE(TextBox) + + ~TextBox() override; + + std::string_view GetControlType() const final { return control_type; } + + render::RenderObject* GetRenderObject() const override; + + render::TextRenderObject* GetTextRenderObject(); + + const TextBoxBorderStyle& GetBorderStyle(); + void SetBorderStyle(TextBoxBorderStyle border_style); + + protected: + void OnMouseHoverChange(bool newHover) override; + + private: + void UpdateBorderStyle(); + + private: + std::unique_ptr border_render_object_; + std::unique_ptr text_render_object_; + + TextBoxBorderStyle border_style_; + + std::unique_ptr> service_; +}; +} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/base.hpp b/include/cru/ui/controls/base.hpp index ebe9cdda..b550601b 100644 --- a/include/cru/ui/controls/base.hpp +++ b/include/cru/ui/controls/base.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../base.hpp" +#include "../Base.hpp" namespace cru::ui::controls { using ButtonStateStyle = BorderStyle; diff --git a/include/cru/ui/controls/button.hpp b/include/cru/ui/controls/button.hpp index fb636a33..8a11409c 100644 --- a/include/cru/ui/controls/button.hpp +++ b/include/cru/ui/controls/button.hpp @@ -1,8 +1,8 @@ #pragma once -#include "../content_control.hpp" -#include "base.hpp" +#include "../ContentControl.hpp" +#include "Base.hpp" -#include "../click_detector.hpp" +#include "../ClickDetector.hpp" namespace cru::ui::controls { class Button : public ContentControl { diff --git a/include/cru/ui/controls/container.hpp b/include/cru/ui/controls/container.hpp index 7d4c0d23..e3d78365 100644 --- a/include/cru/ui/controls/container.hpp +++ b/include/cru/ui/controls/container.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../content_control.hpp" +#include "../ContentControl.hpp" namespace cru::ui::controls { class Container : public ContentControl { diff --git a/include/cru/ui/controls/flex_layout.hpp b/include/cru/ui/controls/flex_layout.hpp deleted file mode 100644 index ab08a80b..00000000 --- a/include/cru/ui/controls/flex_layout.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include "../layout_control.hpp" - -namespace cru::ui::controls { -class FlexLayout : public LayoutControl { - public: - static constexpr std::string_view control_type = "FlexLayout"; - - static FlexLayout* Create() { return new FlexLayout(); } - - protected: - FlexLayout(); - - public: - FlexLayout(const FlexLayout& other) = delete; - FlexLayout(FlexLayout&& other) = delete; - FlexLayout& operator=(const FlexLayout& other) = delete; - FlexLayout& operator=(FlexLayout&& other) = delete; - ~FlexLayout() override; - - std::string_view GetControlType() const final { return control_type; } - - render::RenderObject* GetRenderObject() const override; - - FlexMainAlignment GetContentMainAlign() const; - void SetContentMainAlign(FlexMainAlignment value); - - FlexDirection GetFlexDirection() const; - void SetFlexDirection(FlexDirection direction); - - FlexChildLayoutData GetChildLayoutData(Control* control); - void SetChildLayoutData(Control* control, const FlexChildLayoutData& data); - - protected: - void OnAddChild(Control* child, Index position) override; - void OnRemoveChild(Control* child, Index position) override; - - private: - std::shared_ptr render_object_; -}; -} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/stack_layout.hpp b/include/cru/ui/controls/stack_layout.hpp deleted file mode 100644 index 20da0e82..00000000 --- a/include/cru/ui/controls/stack_layout.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include "../layout_control.hpp" - -namespace cru::ui::controls { -class StackLayout : public LayoutControl { - public: - static constexpr std::string_view control_type = "StackLayout"; - - static StackLayout* Create() { return new StackLayout(); } - - protected: - StackLayout(); - - public: - CRU_DELETE_COPY(StackLayout) - CRU_DELETE_MOVE(StackLayout) - - ~StackLayout() override; - - std::string_view GetControlType() const final { return control_type; } - - render::RenderObject* GetRenderObject() const override; - - protected: - void OnAddChild(Control* child, Index position) override; - void OnRemoveChild(Control* child, Index position) override; - - private: - std::shared_ptr render_object_; -}; -} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/text_block.hpp b/include/cru/ui/controls/text_block.hpp deleted file mode 100644 index 61f568c4..00000000 --- a/include/cru/ui/controls/text_block.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include "../no_child_control.hpp" - -namespace cru::ui::controls { -template -class TextControlService; - -class TextBlock : public NoChildControl { - public: - static constexpr std::string_view control_type = "TextBlock"; - - static TextBlock* Create() { return new TextBlock(); } - - protected: - TextBlock(); - - public: - TextBlock(const TextBlock& other) = delete; - TextBlock(TextBlock&& other) = delete; - TextBlock& operator=(const TextBlock& other) = delete; - TextBlock& operator=(TextBlock&& other) = delete; - ~TextBlock() override; - - std::string_view GetControlType() const final { return control_type; } - - render::RenderObject* GetRenderObject() const override; - - std::string GetText() const; - void SetText(std::string text); - - render::TextRenderObject* GetTextRenderObject(); - - private: - std::unique_ptr text_render_object_; - - std::unique_ptr> service_; -}; -} // namespace cru::ui::controls diff --git a/include/cru/ui/controls/text_box.hpp b/include/cru/ui/controls/text_box.hpp deleted file mode 100644 index 15fcb734..00000000 --- a/include/cru/ui/controls/text_box.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once -#include "../no_child_control.hpp" -#include "base.hpp" - -#include - -namespace cru::ui::controls { -template -class TextControlService; - -class TextBox : public NoChildControl { - public: - static constexpr std::string_view control_type = "TextBox"; - - static TextBox* Create() { return new TextBox(); } - - protected: - TextBox(); - - public: - CRU_DELETE_COPY(TextBox) - CRU_DELETE_MOVE(TextBox) - - ~TextBox() override; - - std::string_view GetControlType() const final { return control_type; } - - render::RenderObject* GetRenderObject() const override; - - render::TextRenderObject* GetTextRenderObject(); - - const TextBoxBorderStyle& GetBorderStyle(); - void SetBorderStyle(TextBoxBorderStyle border_style); - - protected: - void OnMouseHoverChange(bool newHover) override; - - private: - void UpdateBorderStyle(); - - private: - std::unique_ptr border_render_object_; - std::unique_ptr text_render_object_; - - TextBoxBorderStyle border_style_; - - std::unique_ptr> service_; -}; -} // namespace cru::ui::controls diff --git a/include/cru/ui/layout_control.hpp b/include/cru/ui/layout_control.hpp deleted file mode 100644 index e1856403..00000000 --- a/include/cru/ui/layout_control.hpp +++ /dev/null @@ -1,31 +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; - - 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_; -}; -} // namespace cru::ui diff --git a/include/cru/ui/no_child_control.hpp b/include/cru/ui/no_child_control.hpp deleted file mode 100644 index 62a9fa8d..00000000 --- a/include/cru/ui/no_child_control.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "control.hpp" - -namespace cru::ui { -class NoChildControl : public Control { - private: - static const std::vector empty_control_vector; - - 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; - - protected: - const std::vector& GetChildren() const override final { - return empty_control_vector; - } -}; -} // namespace cru::ui diff --git a/include/cru/ui/render/BorderRenderObject.hpp b/include/cru/ui/render/BorderRenderObject.hpp new file mode 100644 index 00000000..c3031f59 --- /dev/null +++ b/include/cru/ui/render/BorderRenderObject.hpp @@ -0,0 +1,102 @@ +#pragma once +#include "RenderObject.hpp" + +namespace cru::ui::render { +class BorderRenderObject : public RenderObject { + public: + BorderRenderObject(); + BorderRenderObject(const BorderRenderObject& other) = delete; + BorderRenderObject(BorderRenderObject&& other) = delete; + BorderRenderObject& operator=(const BorderRenderObject& other) = delete; + BorderRenderObject& operator=(BorderRenderObject&& other) = delete; + ~BorderRenderObject() override; + + bool IsBorderEnabled() const { return is_border_enabled_; } + void SetBorderEnabled(bool enabled) { is_border_enabled_ = enabled; } + + std::shared_ptr GetBorderBrush() { + return border_brush_; + } + + void SetBorderBrush(std::shared_ptr brush) { + if (brush == border_brush_) return; + border_brush_ = std::move(brush); + InvalidatePaint(); + } + + Thickness GetBorderThickness() { return border_thickness_; } + + void SetBorderThickness(const Thickness thickness) { + if (thickness == border_thickness_) return; + border_thickness_ = thickness; + InvalidateLayout(); + } + + CornerRadius GetBorderRadius() { return border_radius_; } + + void SetBorderRadius(const CornerRadius radius) { + if (radius == border_radius_) return; + border_radius_ = radius; + RecreateGeometry(); + } + + std::shared_ptr GetForegroundBrush() { + return foreground_brush_; + } + + void SetForegroundBrush(std::shared_ptr brush) { + if (brush == foreground_brush_) return; + foreground_brush_ = std::move(brush); + InvalidatePaint(); + } + + std::shared_ptr GetBackgroundBrush() { + return background_brush_; + } + + void SetBackgroundBrush(std::shared_ptr brush) { + if (brush == background_brush_) return; + background_brush_ = std::move(brush); + InvalidatePaint(); + } + + void SetBorderStyle(const BorderStyle& style); + + void Draw(platform::graph::IPainter* painter) override; + + RenderObject* HitTest(const Point& point) override; + + protected: + void OnMeasureCore(const Size& available_size) override; + void OnLayoutCore(const Rect& rect) override; + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + void OnAfterLayout() override; + + private: + RenderObject* GetChild() const { + return GetChildren().empty() ? nullptr : GetChildren()[0]; + } + + void RecreateGeometry(); + + private: + bool is_border_enabled_ = false; + + std::shared_ptr border_brush_; + platform::Thickness border_thickness_; + CornerRadius border_radius_; + + std::shared_ptr foreground_brush_; + std::shared_ptr background_brush_; + + // The ring. Used for painting. + std::unique_ptr geometry_; + // Area including inner area of the border. Used for painting foreground and + // background. + std::unique_ptr border_inner_geometry_; + // Area including border ring and inner area. Used for hit test. + std::unique_ptr border_outer_geometry_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/CanvasRenderObject.hpp b/include/cru/ui/render/CanvasRenderObject.hpp new file mode 100644 index 00000000..ba50a985 --- /dev/null +++ b/include/cru/ui/render/CanvasRenderObject.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "RenderObject.hpp" + +namespace cru::ui::render { +// The measure logic for `CanvasRenderObject` is that you set a desired size by +// `SetDesiredSize` (not `SetPreferredSize`) and it will compare desired size +// and available size and use the smaller one (by `Min`). +class CanvasRenderObject : public RenderObject { + public: + CanvasRenderObject(); + + CRU_DELETE_COPY(CanvasRenderObject) + CRU_DELETE_MOVE(CanvasRenderObject) + + ~CanvasRenderObject(); + + public: + void Draw(platform::graph::IPainter* painter) override; + + RenderObject* HitTest(const Point& point) override; + + Size GetDesiredSize() const { return desired_size_; } + + // Set the desired size. This is the content size excluding padding and + // margin. + void SetDesiredSize(const Size& new_size) { desired_size_ = new_size; } + + IEvent* PaintEvent() { return &paint_event_; } + + protected: + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + Size desired_size_{}; + + Event paint_event_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/FlexLayoutRenderObject.hpp b/include/cru/ui/render/FlexLayoutRenderObject.hpp new file mode 100644 index 00000000..bc43141d --- /dev/null +++ b/include/cru/ui/render/FlexLayoutRenderObject.hpp @@ -0,0 +1,42 @@ +#pragma once +#include "LayoutRenderObject.hpp" + +namespace cru::ui::render { +class FlexLayoutRenderObject : public LayoutRenderObject { + public: + FlexLayoutRenderObject() = default; + FlexLayoutRenderObject(const FlexLayoutRenderObject& other) = delete; + FlexLayoutRenderObject& operator=(const FlexLayoutRenderObject& other) = + delete; + FlexLayoutRenderObject(FlexLayoutRenderObject&& other) = delete; + FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete; + ~FlexLayoutRenderObject() override = default; + + FlexDirection GetFlexDirection() const { return direction_; } + void SetFlexDirection(FlexDirection direction) { + direction_ = direction; + InvalidateLayout(); + } + + FlexMainAlignment GetContentMainAlign() const { return content_main_align_; } + void SetContentMainAlign(FlexMainAlignment align) { + content_main_align_ = align; + InvalidateLayout(); + } + + FlexCrossAlignment GetItemCrossAlign() const { return item_cross_align_; } + void SetItemCrossAlign(FlexCrossAlignment align) { + item_cross_align_ = align; + InvalidateLayout(); + } + + protected: + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + FlexDirection direction_ = FlexDirection::Horizontal; + FlexMainAlignment content_main_align_ = FlexMainAlignment::Start; + FlexCrossAlignment item_cross_align_ = FlexCrossAlignment::Center; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/LayoutRenderObject.hpp b/include/cru/ui/render/LayoutRenderObject.hpp new file mode 100644 index 00000000..e6ca9dc0 --- /dev/null +++ b/include/cru/ui/render/LayoutRenderObject.hpp @@ -0,0 +1,88 @@ +#pragma once +#include "RenderObject.hpp" + +#include "cru/platform/graph/util/Painter.hpp" + +namespace cru::ui::render { +template +class LayoutRenderObject : public RenderObject { + public: + using ChildLayoutData = TChildLayoutData; + + protected: + LayoutRenderObject() : RenderObject(ChildMode::Multiple) {} + + public: + CRU_DELETE_COPY(LayoutRenderObject) + CRU_DELETE_MOVE(LayoutRenderObject) + + ~LayoutRenderObject() override = default; + + ChildLayoutData* GetChildLayoutData(Index position) { + Expects(position >= 0 && + position < static_cast(child_layout_data_.size())); + return &child_layout_data_[position]; + } + + void Draw(platform::graph::IPainter* painter) override; + + RenderObject* HitTest(const Point& point) override; + + protected: + void OnAddChild(RenderObject* new_child, Index position) override; + void OnRemoveChild(RenderObject* removed_child, Index position) override; + + private: + std::vector child_layout_data_{}; +}; + +template +void LayoutRenderObject::Draw( + platform::graph::IPainter* painter) { + for (const auto child : GetChildren()) { + auto offset = child->GetOffset(); + platform::graph::util::WithTransform( + painter, platform::Matrix::Translation(offset.x, offset.y), + [child](auto p) { child->Draw(p); }); + } +} + +template +RenderObject* LayoutRenderObject::HitTest( + const Point& point) { + const auto& children = GetChildren(); + for (auto i = children.crbegin(); i != children.crend(); ++i) { + auto offset = (*i)->GetOffset(); + Point p{point.x - offset.x, point.y - offset.y}; + const auto result = (*i)->HitTest(p); + if (result != nullptr) { + return result; + } + } + + const auto margin = GetMargin(); + const auto size = GetSize(); + return Rect{margin.left, margin.top, + std::max(size.width - margin.GetHorizontalTotal(), 0.0f), + std::max(size.height - margin.GetVerticalTotal(), 0.0f)} + .IsPointInside(point) + ? this + : nullptr; +} // namespace cru::ui::render + +template +void LayoutRenderObject::OnAddChild(RenderObject* new_child, + const Index position) { + CRU_UNUSED(new_child) + + child_layout_data_.emplace(child_layout_data_.cbegin() + position); +} + +template +void LayoutRenderObject::OnRemoveChild( + RenderObject* removed_child, const Index position) { + CRU_UNUSED(removed_child) + + child_layout_data_.erase(child_layout_data_.cbegin() + position); +} +} // namespace cru::ui::render diff --git a/include/cru/ui/render/LayoutUtility.hpp b/include/cru/ui/render/LayoutUtility.hpp new file mode 100644 index 00000000..63d13fd3 --- /dev/null +++ b/include/cru/ui/render/LayoutUtility.hpp @@ -0,0 +1,7 @@ +#pragma once +#include "Base.hpp" + +namespace cru::ui::render { +Size Min(const Size& left, const Size& right); +Size Max(const Size& left, const Size& right); +} // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp new file mode 100644 index 00000000..7cfa3883 --- /dev/null +++ b/include/cru/ui/render/RenderObject.hpp @@ -0,0 +1,118 @@ +#pragma once +#include "Base.hpp" + +#include "cru/common/Event.hpp" + +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. +class RenderObject : public Object { + friend WindowRenderObject; + + protected: + enum class ChildMode { + None, + Single, + Multiple, + }; + + RenderObject() = default; + RenderObject(ChildMode child_mode) : RenderObject() { + SetChildMode(child_mode); + } + + public: + RenderObject(const RenderObject& other) = delete; + RenderObject(RenderObject&& other) = delete; + RenderObject& operator=(const RenderObject& other) = delete; + RenderObject& operator=(RenderObject&& other) = delete; + ~RenderObject() override = default; + + Control* GetAttachedControl() const { return control_; } + void SetAttachedControl(Control* new_control) { control_ = new_control; } + + UiHost* GetUiHost() const { return ui_host_; } + + RenderObject* GetParent() const { return parent_; } + + const std::vector& GetChildren() const { return children_; } + Index GetChildCount() const { return static_cast(children_.size()); } + void AddChild(RenderObject* render_object, Index position); + void RemoveChild(Index position); + + Point GetOffset() const { return offset_; } + void SetOffset(const Point& offset) { offset_ = offset; } + Point GetTotalOffset() const; + Point FromRootToContent(const Point& point) const; + Size GetSize() const { return size_; } + void SetSize(const Size& size) { size_ = size; } + + Thickness GetMargin() const { return margin_; } + void SetMargin(const Thickness& margin) { margin_ = margin; } + + Thickness GetPadding() const { return padding_; } + void SetPadding(const Thickness& padding) { padding_ = padding; } + + Size GetPreferredSize() const { return preferred_size_; } + void SetPreferredSize(const Size& preferred_size) { + preferred_size_ = preferred_size; + } + + void Measure(const Size& available_size); + void Layout(const Rect& rect); + + virtual void Draw(platform::graph::IPainter* painter) = 0; + + virtual RenderObject* HitTest(const Point& point) = 0; + + public: + void InvalidateLayout(); + void InvalidatePaint(); + + protected: + void SetChildMode(ChildMode mode) { child_mode_ = mode; } + + protected: + virtual void OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent); + + // default is to invalidate both layout and paint + virtual void OnAddChild(RenderObject* new_child, Index position); + // default is to invalidate both layout and paint + virtual void OnRemoveChild(RenderObject* removed_child, Index position); + + virtual void OnMeasureCore(const Size& available_size); + virtual void OnLayoutCore(const Rect& rect); + virtual Size OnMeasureContent(const Size& available_size) = 0; + virtual void OnLayoutContent(const Rect& content_rect) = 0; + + virtual void OnAfterLayout(); + static void NotifyAfterLayoutRecursive(RenderObject* render_object); + + Rect GetPaddingRect() const; + Rect GetContentRect() const; + + private: + void SetParent(RenderObject* new_parent); + + void SetRenderHostRecursive(UiHost* host); + + private: + Control* control_ = nullptr; + UiHost* ui_host_ = nullptr; + + RenderObject* parent_ = nullptr; + std::vector children_{}; + + ChildMode child_mode_ = ChildMode::None; + + Point offset_{}; + Size size_{}; + + Thickness margin_{}; + Thickness padding_{}; + + Size preferred_size_{}; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp new file mode 100644 index 00000000..dcf6dae6 --- /dev/null +++ b/include/cru/ui/render/ScrollRenderObject.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "RenderObject.hpp" + +#include "cru/platform/graph/util/Painter.hpp" + +namespace cru::ui::render { +class ScrollRenderObject : public RenderObject { + public: + ScrollRenderObject() : RenderObject(ChildMode::Single) {} + + CRU_DELETE_COPY(ScrollRenderObject) + CRU_DELETE_MOVE(ScrollRenderObject) + + ~ScrollRenderObject() override = default; + + void Draw(platform::graph::IPainter* painter) override; + + RenderObject* HitTest(const Point& point) override; + + Point GetScrollOffset() { return scroll_offset_; } + void SetScrollOffset(const Point& offset); + + protected: + void OnAddChild(RenderObject* new_child, Index position) override; + void OnRemoveChild(RenderObject* removed_child, Index position) override; + + private: + Point scroll_offset_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/StackLayoutRenderObject.hpp b/include/cru/ui/render/StackLayoutRenderObject.hpp new file mode 100644 index 00000000..a5bf9335 --- /dev/null +++ b/include/cru/ui/render/StackLayoutRenderObject.hpp @@ -0,0 +1,17 @@ +#pragma once +#include "LayoutRenderObject.hpp" + +namespace cru::ui::render { +class StackLayoutRenderObject + : public LayoutRenderObject { + public: + StackLayoutRenderObject() = default; + CRU_DELETE_COPY(StackLayoutRenderObject) + CRU_DELETE_MOVE(StackLayoutRenderObject) + ~StackLayoutRenderObject() = default; + + protected: + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/TextRenderObject.hpp b/include/cru/ui/render/TextRenderObject.hpp new file mode 100644 index 00000000..7a81ba51 --- /dev/null +++ b/include/cru/ui/render/TextRenderObject.hpp @@ -0,0 +1,85 @@ +#pragma once +#include "RenderObject.hpp" + +#include + +namespace cru::ui::render { +class TextRenderObject : public RenderObject { + public: + constexpr static float default_caret_width = 2; + + public: + TextRenderObject(std::shared_ptr brush, + std::shared_ptr font, + std::shared_ptr selection_brush, + std::shared_ptr caret_brush); + TextRenderObject(const TextRenderObject& other) = delete; + TextRenderObject(TextRenderObject&& other) = delete; + TextRenderObject& operator=(const TextRenderObject& other) = delete; + TextRenderObject& operator=(TextRenderObject&& other) = delete; + ~TextRenderObject() override; + + std::string GetText() const; + void SetText(std::string new_text); + + std::shared_ptr GetBrush() const { return brush_; } + void SetBrush(std::shared_ptr new_brush); + + std::shared_ptr GetFont() const; + void SetFont(std::shared_ptr font); + + std::vector TextRangeRect(const TextRange& text_range); + Point TextSinglePoint(gsl::index position, bool trailing); + platform::graph::TextHitTestResult TextHitTest(const Point& point); + + std::optional GetSelectionRange() const { + return selection_range_; + } + void SetSelectionRange(std::optional new_range); + + std::shared_ptr GetSelectionBrush() const { + return selection_brush_; + } + void SetSelectionBrush(std::shared_ptr new_brush); + + bool IsDrawCaret() const { return draw_caret_; } + void SetDrawCaret(bool draw_caret); + void ToggleDrawCaret() { SetDrawCaret(!IsDrawCaret()); } + + // Caret position can be any value. When it is negative, 0 is used. When it + // exceeds the size of the string, the size of the string is used. + gsl::index GetCaretPosition() const { return caret_position_; } + void SetCaretPosition(gsl::index position); + + std::shared_ptr GetCaretBrush() const { + return caret_brush_; + } + void GetCaretBrush(std::shared_ptr brush); + + float GetCaretWidth() const { return caret_width_; } + void SetCaretWidth(float width); + + void Draw(platform::graph::IPainter* painter) override; + + RenderObject* HitTest(const Point& point) override; + + protected: + Size OnMeasureContent(const Size& available_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + void OnAfterLayout() override; + + private: + std::shared_ptr brush_; + std::shared_ptr font_; + std::unique_ptr text_layout_; + + std::optional selection_range_ = std::nullopt; + std::shared_ptr selection_brush_; + + bool draw_caret_ = false; + gsl::index caret_position_ = 0; + std::shared_ptr caret_brush_; + float caret_width_ = default_caret_width; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/WindowRenderObject.hpp b/include/cru/ui/render/WindowRenderObject.hpp new file mode 100644 index 00000000..00bce29b --- /dev/null +++ b/include/cru/ui/render/WindowRenderObject.hpp @@ -0,0 +1,30 @@ +#pragma once +#include "RenderObject.hpp" + +namespace cru::ui::render { +class WindowRenderObject : public RenderObject { + public: + WindowRenderObject(UiHost* host); + WindowRenderObject(const WindowRenderObject& other) = delete; + WindowRenderObject(WindowRenderObject&& other) = delete; + WindowRenderObject& operator=(const WindowRenderObject& other) = delete; + WindowRenderObject& operator=(WindowRenderObject&& other) = delete; + ~WindowRenderObject() override = default; + + void Draw(platform::graph::IPainter* painter) override; + + RenderObject* HitTest(const Point& point) override; + + protected: + Size OnMeasureContent(const Size& available_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/include/cru/ui/render/base.hpp b/include/cru/ui/render/base.hpp index f9d936e0..c2af5e99 100644 --- a/include/cru/ui/render/base.hpp +++ b/include/cru/ui/render/base.hpp @@ -1,5 +1,5 @@ #pragma once -#include "../base.hpp" +#include "../Base.hpp" namespace cru::ui::render { class RenderObject; diff --git a/include/cru/ui/render/border_render_object.hpp b/include/cru/ui/render/border_render_object.hpp deleted file mode 100644 index 02672309..00000000 --- a/include/cru/ui/render/border_render_object.hpp +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once -#include "render_object.hpp" - -namespace cru::ui::render { -class BorderRenderObject : public RenderObject { - public: - BorderRenderObject(); - BorderRenderObject(const BorderRenderObject& other) = delete; - BorderRenderObject(BorderRenderObject&& other) = delete; - BorderRenderObject& operator=(const BorderRenderObject& other) = delete; - BorderRenderObject& operator=(BorderRenderObject&& other) = delete; - ~BorderRenderObject() override; - - bool IsBorderEnabled() const { return is_border_enabled_; } - void SetBorderEnabled(bool enabled) { is_border_enabled_ = enabled; } - - std::shared_ptr GetBorderBrush() { - return border_brush_; - } - - void SetBorderBrush(std::shared_ptr brush) { - if (brush == border_brush_) return; - border_brush_ = std::move(brush); - InvalidatePaint(); - } - - Thickness GetBorderThickness() { return border_thickness_; } - - void SetBorderThickness(const Thickness thickness) { - if (thickness == border_thickness_) return; - border_thickness_ = thickness; - InvalidateLayout(); - } - - CornerRadius GetBorderRadius() { return border_radius_; } - - void SetBorderRadius(const CornerRadius radius) { - if (radius == border_radius_) return; - border_radius_ = radius; - RecreateGeometry(); - } - - std::shared_ptr GetForegroundBrush() { - return foreground_brush_; - } - - void SetForegroundBrush(std::shared_ptr brush) { - if (brush == foreground_brush_) return; - foreground_brush_ = std::move(brush); - InvalidatePaint(); - } - - std::shared_ptr GetBackgroundBrush() { - return background_brush_; - } - - void SetBackgroundBrush(std::shared_ptr brush) { - if (brush == background_brush_) return; - background_brush_ = std::move(brush); - InvalidatePaint(); - } - - void SetBorderStyle(const BorderStyle& style); - - void Draw(platform::graph::IPainter* painter) override; - - RenderObject* HitTest(const Point& point) override; - - protected: - void OnMeasureCore(const Size& available_size) override; - void OnLayoutCore(const Rect& rect) override; - Size OnMeasureContent(const Size& available_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - void OnAfterLayout() override; - - private: - RenderObject* GetChild() const { - return GetChildren().empty() ? nullptr : GetChildren()[0]; - } - - void RecreateGeometry(); - - private: - bool is_border_enabled_ = false; - - std::shared_ptr border_brush_; - platform::Thickness border_thickness_; - CornerRadius border_radius_; - - std::shared_ptr foreground_brush_; - std::shared_ptr background_brush_; - - // The ring. Used for painting. - std::unique_ptr geometry_; - // Area including inner area of the border. Used for painting foreground and - // background. - std::unique_ptr border_inner_geometry_; - // Area including border ring and inner area. Used for hit test. - std::unique_ptr border_outer_geometry_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/canvas_render_object.hpp b/include/cru/ui/render/canvas_render_object.hpp deleted file mode 100644 index cb3828b6..00000000 --- a/include/cru/ui/render/canvas_render_object.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "render_object.hpp" - -namespace cru::ui::render { -// The measure logic for `CanvasRenderObject` is that you set a desired size by -// `SetDesiredSize` (not `SetPreferredSize`) and it will compare desired size -// and available size and use the smaller one (by `Min`). -class CanvasRenderObject : public RenderObject { - public: - CanvasRenderObject(); - - CRU_DELETE_COPY(CanvasRenderObject) - CRU_DELETE_MOVE(CanvasRenderObject) - - ~CanvasRenderObject(); - - public: - void Draw(platform::graph::IPainter* painter) override; - - RenderObject* HitTest(const Point& point) override; - - Size GetDesiredSize() const { return desired_size_; } - - // Set the desired size. This is the content size excluding padding and - // margin. - void SetDesiredSize(const Size& new_size) { desired_size_ = new_size; } - - IEvent* PaintEvent() { return &paint_event_; } - - protected: - Size OnMeasureContent(const Size& available_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - private: - Size desired_size_{}; - - Event paint_event_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/flex_layout_render_object.hpp b/include/cru/ui/render/flex_layout_render_object.hpp deleted file mode 100644 index 849c1a0d..00000000 --- a/include/cru/ui/render/flex_layout_render_object.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "layout_render_object.hpp" - -namespace cru::ui::render { -class FlexLayoutRenderObject : public LayoutRenderObject { - public: - FlexLayoutRenderObject() = default; - FlexLayoutRenderObject(const FlexLayoutRenderObject& other) = delete; - FlexLayoutRenderObject& operator=(const FlexLayoutRenderObject& other) = - delete; - FlexLayoutRenderObject(FlexLayoutRenderObject&& other) = delete; - FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete; - ~FlexLayoutRenderObject() override = default; - - FlexDirection GetFlexDirection() const { return direction_; } - void SetFlexDirection(FlexDirection direction) { - direction_ = direction; - InvalidateLayout(); - } - - FlexMainAlignment GetContentMainAlign() const { return content_main_align_; } - void SetContentMainAlign(FlexMainAlignment align) { - content_main_align_ = align; - InvalidateLayout(); - } - - FlexCrossAlignment GetItemCrossAlign() const { return item_cross_align_; } - void SetItemCrossAlign(FlexCrossAlignment align) { - item_cross_align_ = align; - InvalidateLayout(); - } - - protected: - Size OnMeasureContent(const Size& available_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - private: - FlexDirection direction_ = FlexDirection::Horizontal; - FlexMainAlignment content_main_align_ = FlexMainAlignment::Start; - FlexCrossAlignment item_cross_align_ = FlexCrossAlignment::Center; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/layout_render_object.hpp b/include/cru/ui/render/layout_render_object.hpp deleted file mode 100644 index 5c4c9c5c..00000000 --- a/include/cru/ui/render/layout_render_object.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once -#include "render_object.hpp" - -#include "cru/platform/graph/util/painter.hpp" - -namespace cru::ui::render { -template -class LayoutRenderObject : public RenderObject { - public: - using ChildLayoutData = TChildLayoutData; - - protected: - LayoutRenderObject() : RenderObject(ChildMode::Multiple) {} - - public: - CRU_DELETE_COPY(LayoutRenderObject) - CRU_DELETE_MOVE(LayoutRenderObject) - - ~LayoutRenderObject() override = default; - - ChildLayoutData* GetChildLayoutData(Index position) { - Expects(position >= 0 && - position < static_cast(child_layout_data_.size())); - return &child_layout_data_[position]; - } - - void Draw(platform::graph::IPainter* painter) override; - - RenderObject* HitTest(const Point& point) override; - - protected: - void OnAddChild(RenderObject* new_child, Index position) override; - void OnRemoveChild(RenderObject* removed_child, Index position) override; - - private: - std::vector child_layout_data_{}; -}; - -template -void LayoutRenderObject::Draw( - platform::graph::IPainter* painter) { - for (const auto child : GetChildren()) { - auto offset = child->GetOffset(); - platform::graph::util::WithTransform( - painter, platform::Matrix::Translation(offset.x, offset.y), - [child](auto p) { child->Draw(p); }); - } -} - -template -RenderObject* LayoutRenderObject::HitTest( - const Point& point) { - const auto& children = GetChildren(); - for (auto i = children.crbegin(); i != children.crend(); ++i) { - auto offset = (*i)->GetOffset(); - Point p{point.x - offset.x, point.y - offset.y}; - const auto result = (*i)->HitTest(p); - if (result != nullptr) { - return result; - } - } - - const auto margin = GetMargin(); - const auto size = GetSize(); - return Rect{margin.left, margin.top, - std::max(size.width - margin.GetHorizontalTotal(), 0.0f), - std::max(size.height - margin.GetVerticalTotal(), 0.0f)} - .IsPointInside(point) - ? this - : nullptr; -} // namespace cru::ui::render - -template -void LayoutRenderObject::OnAddChild(RenderObject* new_child, - const Index position) { - CRU_UNUSED(new_child) - - child_layout_data_.emplace(child_layout_data_.cbegin() + position); -} - -template -void LayoutRenderObject::OnRemoveChild( - RenderObject* removed_child, const Index position) { - CRU_UNUSED(removed_child) - - child_layout_data_.erase(child_layout_data_.cbegin() + position); -} -} // namespace cru::ui::render diff --git a/include/cru/ui/render/layout_utility.hpp b/include/cru/ui/render/layout_utility.hpp deleted file mode 100644 index 16a15d87..00000000 --- a/include/cru/ui/render/layout_utility.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include "base.hpp" - -namespace cru::ui::render { -Size Min(const Size& left, const Size& right); -Size Max(const Size& left, const Size& right); -} // namespace cru::ui::render diff --git a/include/cru/ui/render/render_object.hpp b/include/cru/ui/render/render_object.hpp deleted file mode 100644 index 6a8db52f..00000000 --- a/include/cru/ui/render/render_object.hpp +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once -#include "base.hpp" - -#include "cru/common/event.hpp" - -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. -class RenderObject : public Object { - friend WindowRenderObject; - - protected: - enum class ChildMode { - None, - Single, - Multiple, - }; - - RenderObject() = default; - RenderObject(ChildMode child_mode) : RenderObject() { - SetChildMode(child_mode); - } - - public: - RenderObject(const RenderObject& other) = delete; - RenderObject(RenderObject&& other) = delete; - RenderObject& operator=(const RenderObject& other) = delete; - RenderObject& operator=(RenderObject&& other) = delete; - ~RenderObject() override = default; - - Control* GetAttachedControl() const { return control_; } - void SetAttachedControl(Control* new_control) { control_ = new_control; } - - UiHost* GetUiHost() const { return ui_host_; } - - RenderObject* GetParent() const { return parent_; } - - const std::vector& GetChildren() const { return children_; } - Index GetChildCount() const { return static_cast(children_.size()); } - void AddChild(RenderObject* render_object, Index position); - void RemoveChild(Index position); - - Point GetOffset() const { return offset_; } - void SetOffset(const Point& offset) { offset_ = offset; } - Point GetTotalOffset() const; - Point FromRootToContent(const Point& point) const; - Size GetSize() const { return size_; } - void SetSize(const Size& size) { size_ = size; } - - Thickness GetMargin() const { return margin_; } - void SetMargin(const Thickness& margin) { margin_ = margin; } - - Thickness GetPadding() const { return padding_; } - void SetPadding(const Thickness& padding) { padding_ = padding; } - - Size GetPreferredSize() const { return preferred_size_; } - void SetPreferredSize(const Size& preferred_size) { - preferred_size_ = preferred_size; - } - - void Measure(const Size& available_size); - void Layout(const Rect& rect); - - virtual void Draw(platform::graph::IPainter* painter) = 0; - - virtual RenderObject* HitTest(const Point& point) = 0; - - public: - void InvalidateLayout(); - void InvalidatePaint(); - - protected: - void SetChildMode(ChildMode mode) { child_mode_ = mode; } - - protected: - virtual void OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent); - - // default is to invalidate both layout and paint - virtual void OnAddChild(RenderObject* new_child, Index position); - // default is to invalidate both layout and paint - virtual void OnRemoveChild(RenderObject* removed_child, Index position); - - virtual void OnMeasureCore(const Size& available_size); - virtual void OnLayoutCore(const Rect& rect); - virtual Size OnMeasureContent(const Size& available_size) = 0; - virtual void OnLayoutContent(const Rect& content_rect) = 0; - - virtual void OnAfterLayout(); - static void NotifyAfterLayoutRecursive(RenderObject* render_object); - - Rect GetPaddingRect() const; - Rect GetContentRect() const; - - private: - void SetParent(RenderObject* new_parent); - - void SetRenderHostRecursive(UiHost* host); - - private: - Control* control_ = nullptr; - UiHost* ui_host_ = nullptr; - - RenderObject* parent_ = nullptr; - std::vector children_{}; - - ChildMode child_mode_ = ChildMode::None; - - Point offset_{}; - Size size_{}; - - Thickness margin_{}; - Thickness padding_{}; - - Size preferred_size_{}; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/scroll_render_object.hpp b/include/cru/ui/render/scroll_render_object.hpp deleted file mode 100644 index 1527db6c..00000000 --- a/include/cru/ui/render/scroll_render_object.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "render_object.hpp" - -#include "cru/platform/graph/util/painter.hpp" - -namespace cru::ui::render { -class ScrollRenderObject : public RenderObject { - public: - ScrollRenderObject() : RenderObject(ChildMode::Single) {} - - CRU_DELETE_COPY(ScrollRenderObject) - CRU_DELETE_MOVE(ScrollRenderObject) - - ~ScrollRenderObject() override = default; - - void Draw(platform::graph::IPainter* painter) override; - - RenderObject* HitTest(const Point& point) override; - - Point GetScrollOffset() { return scroll_offset_; } - void SetScrollOffset(const Point& offset); - - protected: - void OnAddChild(RenderObject* new_child, Index position) override; - void OnRemoveChild(RenderObject* removed_child, Index position) override; - - private: - Point scroll_offset_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/stack_layout_render_object.hpp b/include/cru/ui/render/stack_layout_render_object.hpp deleted file mode 100644 index c259b98d..00000000 --- a/include/cru/ui/render/stack_layout_render_object.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "layout_render_object.hpp" - -namespace cru::ui::render { -class StackLayoutRenderObject - : public LayoutRenderObject { - public: - StackLayoutRenderObject() = default; - CRU_DELETE_COPY(StackLayoutRenderObject) - CRU_DELETE_MOVE(StackLayoutRenderObject) - ~StackLayoutRenderObject() = default; - - protected: - Size OnMeasureContent(const Size& available_size) override; - void OnLayoutContent(const Rect& content_rect) override; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/text_render_object.hpp b/include/cru/ui/render/text_render_object.hpp deleted file mode 100644 index 4b1e91e0..00000000 --- a/include/cru/ui/render/text_render_object.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once -#include "render_object.hpp" - -#include - -namespace cru::ui::render { -class TextRenderObject : public RenderObject { - public: - constexpr static float default_caret_width = 2; - - public: - TextRenderObject(std::shared_ptr brush, - std::shared_ptr font, - std::shared_ptr selection_brush, - std::shared_ptr caret_brush); - TextRenderObject(const TextRenderObject& other) = delete; - TextRenderObject(TextRenderObject&& other) = delete; - TextRenderObject& operator=(const TextRenderObject& other) = delete; - TextRenderObject& operator=(TextRenderObject&& other) = delete; - ~TextRenderObject() override; - - std::string GetText() const; - void SetText(std::string new_text); - - std::shared_ptr GetBrush() const { return brush_; } - void SetBrush(std::shared_ptr new_brush); - - std::shared_ptr GetFont() const; - void SetFont(std::shared_ptr font); - - std::vector TextRangeRect(const TextRange& text_range); - Point TextSinglePoint(gsl::index position, bool trailing); - platform::graph::TextHitTestResult TextHitTest(const Point& point); - - std::optional GetSelectionRange() const { - return selection_range_; - } - void SetSelectionRange(std::optional new_range); - - std::shared_ptr GetSelectionBrush() const { - return selection_brush_; - } - void SetSelectionBrush(std::shared_ptr new_brush); - - bool IsDrawCaret() const { return draw_caret_; } - void SetDrawCaret(bool draw_caret); - void ToggleDrawCaret() { SetDrawCaret(!IsDrawCaret()); } - - // Caret position can be any value. When it is negative, 0 is used. When it - // exceeds the size of the string, the size of the string is used. - gsl::index GetCaretPosition() const { return caret_position_; } - void SetCaretPosition(gsl::index position); - - std::shared_ptr GetCaretBrush() const { - return caret_brush_; - } - void GetCaretBrush(std::shared_ptr brush); - - float GetCaretWidth() const { return caret_width_; } - void SetCaretWidth(float width); - - void Draw(platform::graph::IPainter* painter) override; - - RenderObject* HitTest(const Point& point) override; - - protected: - Size OnMeasureContent(const Size& available_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - void OnAfterLayout() override; - - private: - std::shared_ptr brush_; - std::shared_ptr font_; - std::unique_ptr text_layout_; - - std::optional selection_range_ = std::nullopt; - std::shared_ptr selection_brush_; - - bool draw_caret_ = false; - gsl::index caret_position_ = 0; - std::shared_ptr caret_brush_; - float caret_width_ = default_caret_width; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/window_render_object.hpp b/include/cru/ui/render/window_render_object.hpp deleted file mode 100644 index 76b17b82..00000000 --- a/include/cru/ui/render/window_render_object.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "render_object.hpp" - -namespace cru::ui::render { -class WindowRenderObject : public RenderObject { - public: - WindowRenderObject(UiHost* host); - WindowRenderObject(const WindowRenderObject& other) = delete; - WindowRenderObject(WindowRenderObject&& other) = delete; - WindowRenderObject& operator=(const WindowRenderObject& other) = delete; - WindowRenderObject& operator=(WindowRenderObject&& other) = delete; - ~WindowRenderObject() override = default; - - void Draw(platform::graph::IPainter* painter) override; - - RenderObject* HitTest(const Point& point) override; - - protected: - Size OnMeasureContent(const Size& available_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/include/cru/ui/ui_event.hpp b/include/cru/ui/ui_event.hpp deleted file mode 100644 index a9d6028a..00000000 --- a/include/cru/ui/ui_event.hpp +++ /dev/null @@ -1,229 +0,0 @@ -#pragma once -#include "base.hpp" - -#include "cru/common/event.hpp" -#include "cru/platform/native/keyboard.hpp" - -#include -#include -#include -#include - -namespace cru::platform::graph { -struct IPainter; -} - -namespace cru::ui { -class Control; -} - -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 EventArgs = TEventArgs; - - 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{}); } - - private: - std::optional point_; -}; - -class MouseButtonEventArgs : public MouseEventArgs { - public: - MouseButtonEventArgs(Object* sender, Object* original_sender, - const Point& point, const MouseButton button, - platform::native::KeyModifier key_modifier) - : MouseEventArgs(sender, original_sender, point), - button_(button), - key_modifier_(key_modifier) {} - MouseButtonEventArgs(Object* sender, Object* original_sender, - const MouseButton button, - platform::native::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::native::KeyModifier GetKeyModifier() const { return key_modifier_; } - - private: - MouseButton button_; - platform::native::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::graph::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::graph::IPainter* GetPainter() const { return painter_; } - - private: - platform::graph::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::native::KeyCode key_code, - platform::native::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::native::KeyCode GetKeyCode() const { return key_code_; } - platform::native::KeyModifier GetKeyModifier() const { return key_modifier_; } - - private: - platform::native::KeyCode key_code_; - platform::native::KeyModifier key_modifier_; -}; - -class CharEventArgs : public UiEventArgs { - public: - CharEventArgs(Object* sender, Object* original_sender, std::string 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::string GetChar() const { return c_; } - - private: - std::string c_; -}; -} // namespace cru::ui::event diff --git a/include/cru/ui/ui_host.hpp b/include/cru/ui/ui_host.hpp deleted file mode 100644 index ca2b70a4..00000000 --- a/include/cru/ui/ui_host.hpp +++ /dev/null @@ -1,170 +0,0 @@ -#pragma once -#include "base.hpp" - -#include "cru/common/event.hpp" -#include "cru/common/self_resolvable.hpp" -#include "render/base.hpp" - -namespace cru::ui { -struct AfterLayoutEventArgs {}; - -// The host of all controls and render objects. -// -// 3 situations on destroy: -// 1. Native window destroyed, IsRetainAfterDestroy: false: -// OnNativeDestroy(set native_window_destroyed_ to true, call ~Window due to -// deleting_ is false and IsRetainAfterDestroy is false) -> ~Window -> -// ~UiHost(not destroy native window repeatedly due to native_window_destroyed_ -// is true) -// 2. Native window destroyed, IsRetainAfterDestroy: true: -// OnNativeDestroy(set native_window_destroyed_ to true, not call ~Window -// because deleting_ is false and IsRetainAfterDestroy is true) -// then, ~Window -> ~UiHost(not destroy native window repeatedly due to -// native_window_destroyed_ is true) -// 3. Native window not destroyed, ~Window is called: -// ~Window -> ~UiHost(set deleting_ to true, destroy native window -// due to native_window_destroyed is false) -> OnNativeDestroy(not call ~Window -// due to deleting_ is true and IsRetainAfterDestroy is whatever) -// In conclusion: -// 1. Set native_window_destroyed_ to true at the beginning of OnNativeDestroy. -// 2. Set deleting_ to true at the beginning of ~UiHost. -// 3. Destroy native window when native_window_destroy_ is false in ~Window. -// 4. Delete Window when deleting_ is false and IsRetainAfterDestroy is false in -// OnNativeDestroy. -class UiHost : public Object, public SelfResolvable { - public: - // This will create root window render object and attach it to window. - // It will also create and manage a native window. - UiHost(Window* window); - - CRU_DELETE_COPY(UiHost) - CRU_DELETE_MOVE(UiHost) - - ~UiHost() override; - - public: - // 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. - void InvalidateLayout(); - - // Mark the paint as invalid, and arrange a re-paint later. - // This method could be called more than one times in a message cycle. But - // paint only takes place once. - void InvalidatePaint(); - - IEvent* AfterLayoutEvent() { - return &after_layout_event_; - } - - 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 - // get more info. - Control* GetMouseHoverControl() const { return mouse_hover_control_; } - - //*************** region: focus *************** - - // Request focus for specified control. - bool RequestFocusFor(Control* control); - - // Get the control that has focus. - Control* GetFocusControl(); - - //*************** region: focus *************** - - // Pass nullptr to release capture. If mouse is already capture by a control, - // this capture will fail and return false. If control is identical to the - // capturing control, capture is not changed and this function will return - // true. - // - // When capturing control changes, - // appropriate event will be sent. If mouse is not on the capturing control - // 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); - - // Return null if not captured. - Control* GetMouseCaptureControl(); - - Control* HitTest(const Point& point); - - void UpdateCursor(); - - std::shared_ptr - GetNativeWindowResolver() { - return native_window_resolver_; - } - - bool IsRetainAfterDestroy() { return retain_after_destroy_; } - - void SetRetainAfterDestroy(bool destroy) { retain_after_destroy_ = destroy; } - - private: - //*************** region: native messages *************** - void OnNativeDestroy(platform::native::INativeWindow* window, std::nullptr_t); - void OnNativePaint(platform::native::INativeWindow* window, std::nullptr_t); - void OnNativeResize(platform::native::INativeWindow* window, - const Size& size); - - void OnNativeFocus(platform::native::INativeWindow* window, - cru::platform::native::FocusChangeType focus); - - void OnNativeMouseEnterLeave( - platform::native::INativeWindow* window, - cru::platform::native::MouseEnterLeaveType enter); - void OnNativeMouseMove(platform::native::INativeWindow* window, - const Point& point); - void OnNativeMouseDown( - platform::native::INativeWindow* window, - const platform::native::NativeMouseButtonEventArgs& args); - void OnNativeMouseUp( - platform::native::INativeWindow* window, - const platform::native::NativeMouseButtonEventArgs& args); - - void OnNativeKeyDown(platform::native::INativeWindow* window, - const platform::native::NativeKeyEventArgs& args); - void OnNativeKeyUp(platform::native::INativeWindow* window, - const platform::native::NativeKeyEventArgs& args); - - //*************** region: event dispatcher helper *************** - - void DispatchMouseHoverControlChangeEvent(Control* old_control, - Control* new_control, - const Point& point, bool no_leave, - bool no_enter); - - private: - bool need_layout_ = false; - - Event after_layout_event_; - - std::shared_ptr - native_window_resolver_; - - // See remarks of UiHost. - bool retain_after_destroy_ = false; - // See remarks of UiHost. - 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 UiHost. - bool native_window_destroyed_ = false; - - std::vector event_revoker_guards_; - - Window* window_control_; - std::unique_ptr root_render_object_; - - Control* mouse_hover_control_; - - Control* focus_control_; // "focus_control_" can't be nullptr - - Control* mouse_captured_control_; -}; -} // namespace cru::ui diff --git a/include/cru/ui/ui_manager.hpp b/include/cru/ui/ui_manager.hpp deleted file mode 100644 index 9ad184c4..00000000 --- a/include/cru/ui/ui_manager.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "base.hpp" - -#include "controls/base.hpp" - -namespace cru::ui { -struct ThemeResources { - std::shared_ptr default_font; - std::shared_ptr text_brush; - std::shared_ptr text_selection_brush; - std::shared_ptr caret_brush; - controls::ButtonStyle button_style; - controls::TextBoxBorderStyle text_box_border_style; -}; - -class UiManager : public Object { - public: - static UiManager* GetInstance(); - - private: - UiManager(); - - public: - UiManager(const UiManager& other) = delete; - UiManager(UiManager&& other) = delete; - UiManager& operator=(const UiManager& other) = delete; - UiManager& operator=(UiManager&& other) = delete; - ~UiManager() override; - - ThemeResources* GetThemeResources() { return &theme_resource_; } - - private: - ThemeResources theme_resource_; -}; -} // namespace cru::ui diff --git a/include/cru/ui/window.hpp b/include/cru/ui/window.hpp index 2f5df4da..eb2ecfbb 100644 --- a/include/cru/ui/window.hpp +++ b/include/cru/ui/window.hpp @@ -1,5 +1,5 @@ #pragma once -#include "content_control.hpp" +#include "ContentControl.hpp" namespace cru::ui { class Window final : public ContentControl { -- cgit v1.2.3