aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-11-17 12:54:55 +0800
committerYuqian Yang <crupest@crupest.life>2025-11-17 12:54:55 +0800
commit0f8f98b9005803ab154b43dcad0db1f292072a4d (patch)
tree0a43d5a9c4e3b747ad955fc30a143aa07ab5888d
parentb68f9f52a3ecdd8e379dd60ac1c1366e76695464 (diff)
downloadcru-0f8f98b9005803ab154b43dcad0db1f292072a4d.tar.gz
cru-0f8f98b9005803ab154b43dcad0db1f292072a4d.tar.bz2
cru-0f8f98b9005803ab154b43dcad0db1f292072a4d.zip
Refactor window host.
-rw-r--r--cru-words.txt1
-rw-r--r--demos/ScrollView/main.cpp2
-rw-r--r--demos/main/main.cpp6
-rw-r--r--include/cru/base/Event.h7
-rw-r--r--include/cru/ui/Base.h18
-rw-r--r--include/cru/ui/DeleteLater.h3
-rw-r--r--include/cru/ui/components/Component.h6
-rw-r--r--include/cru/ui/components/Menu.h31
-rw-r--r--include/cru/ui/components/PopupButton.h1
-rw-r--r--include/cru/ui/controls/Control.h83
-rw-r--r--include/cru/ui/controls/Popup.h22
-rw-r--r--include/cru/ui/controls/RootControl.h34
-rw-r--r--include/cru/ui/controls/TextHostControlService.h2
-rw-r--r--include/cru/ui/controls/Window.h188
-rw-r--r--include/cru/ui/events/RoutedEvent.h20
-rw-r--r--include/cru/ui/host/LayoutPaintCycler.h39
-rw-r--r--include/cru/ui/host/WindowHost.h172
-rw-r--r--include/cru/ui/render/RenderObject.h2
-rw-r--r--include/cru/ui/render/ScrollBar.h8
-rw-r--r--src/ui/CMakeLists.txt4
-rw-r--r--src/ui/DeleteLater.cpp2
-rw-r--r--src/ui/components/Component.cpp4
-rw-r--r--src/ui/components/Menu.cpp27
-rw-r--r--src/ui/components/PopupButton.cpp1
-rw-r--r--src/ui/controls/Control.cpp120
-rw-r--r--src/ui/controls/Popup.cpp18
-rw-r--r--src/ui/controls/RootControl.cpp49
-rw-r--r--src/ui/controls/TextHostControlService.cpp31
-rw-r--r--src/ui/controls/Window.cpp426
-rw-r--r--src/ui/events/MouseEventArgs.cpp10
-rw-r--r--src/ui/helper/ClickDetector.cpp12
-rw-r--r--src/ui/host/LayoutPaintCycler.cpp35
-rw-r--r--src/ui/host/RoutedEventDispatch.h122
-rw-r--r--src/ui/host/WindowHost.cpp457
-rw-r--r--src/ui/render/RenderObject.cpp14
-rw-r--r--src/ui/render/ScrollBar.cpp16
36 files changed, 780 insertions, 1213 deletions
diff --git a/cru-words.txt b/cru-words.txt
index e9967942..8205dad2 100644
--- a/cru-words.txt
+++ b/cru-words.txt
@@ -5,3 +5,4 @@ IWYU
# cmake
endfunction
+relayout
diff --git a/demos/ScrollView/main.cpp b/demos/ScrollView/main.cpp
index f8e2adda..ba240bae 100644
--- a/demos/ScrollView/main.cpp
+++ b/demos/ScrollView/main.cpp
@@ -80,7 +80,7 @@ The cold never bothered me anyway)",
scroll_view.SetChild(text_block.get());
- window.GetWindowHost()->GetNativeWindow()->SetVisibility(
+ window.GetNativeWindow()->SetVisibility(
cru::platform::gui::WindowVisibilityType::Show);
return application->Run();
diff --git a/demos/main/main.cpp b/demos/main/main.cpp
index 91c9e85f..70861539 100644
--- a/demos/main/main.cpp
+++ b/demos/main/main.cpp
@@ -1,9 +1,7 @@
#include "cru/platform/bootstrap/Bootstrap.h"
-#include "cru/platform/gui/Base.h"
#include "cru/platform/gui/UiApplication.h"
#include "cru/platform/gui/Window.h"
#include "cru/ui/Base.h"
-#include "cru/ui/components/Menu.h"
#include "cru/ui/components/PopupButton.h"
#include "cru/ui/components/Select.h"
#include "cru/ui/controls/Button.h"
@@ -11,8 +9,6 @@
#include "cru/ui/controls/TextBlock.h"
#include "cru/ui/controls/TextBox.h"
#include "cru/ui/controls/Window.h"
-#include "cru/ui/events/UiEvents.h"
-#include "cru/ui/host/WindowHost.h"
using cru::platform::gui::IUiApplication;
using namespace cru::ui::controls;
@@ -66,7 +62,7 @@ int main() {
select.SetItems({"Item 1", "Item 2", "Item 3"});
flex_layout.AddChild(select.GetRootControl());
- window.GetWindowHost()->GetNativeWindow()->SetVisibility(
+ window.GetNativeWindow()->SetVisibility(
cru::platform::gui::WindowVisibilityType::Show);
return application->Run();
diff --git a/include/cru/base/Event.h b/include/cru/base/Event.h
index 276c313f..c26bea83 100644
--- a/include/cru/base/Event.h
+++ b/include/cru/base/Event.h
@@ -128,6 +128,13 @@ class Event : public EventBase, public IEvent<TEventArgs> {
EventHandlerToken current_token_ = 0;
};
+#define CRU_DEFINE_EVENT(name, arg_type) \
+ private: \
+ ::cru::Event<arg_type> name##Event_; \
+ \
+ public: \
+ ::cru::IEvent<arg_type>* name##Event() { return &name##Event_; }
+
namespace details {
struct EventHandlerRevokerDestroyer {
void operator()(EventHandlerRevoker* p) {
diff --git a/include/cru/ui/Base.h b/include/cru/ui/Base.h
index 3f270b39..9e24cfa2 100644
--- a/include/cru/ui/Base.h
+++ b/include/cru/ui/Base.h
@@ -35,12 +35,9 @@ namespace colors = cru::platform::colors;
namespace controls {
class Control;
+class Window;
} // namespace controls
-namespace host {
-class WindowHost;
-}
-
//-------------------- region: basic types --------------------
enum class Direction { Horizontal, Vertical };
enum class Alignment { Start, End, Center, Stretch };
@@ -67,21 +64,12 @@ struct CornerRadius {
return *this;
}
+ bool operator==(const CornerRadius& other) const = default;
+
Point left_top;
Point right_top;
Point left_bottom;
Point right_bottom;
};
-inline bool operator==(const CornerRadius& left, const CornerRadius& right) {
- return left.left_top == right.left_top &&
- left.left_bottom == right.left_bottom &&
- left.right_top == right.right_top &&
- left.right_bottom == right.right_bottom;
-}
-
-inline bool operator!=(const CornerRadius& left, const CornerRadius& right) {
- return !(left == right);
-}
-
} // namespace cru::ui
diff --git a/include/cru/ui/DeleteLater.h b/include/cru/ui/DeleteLater.h
index ddbf2ce1..95301bc0 100644
--- a/include/cru/ui/DeleteLater.h
+++ b/include/cru/ui/DeleteLater.h
@@ -13,9 +13,6 @@ class CRU_UI_API DeleteLaterImpl {
virtual ~DeleteLaterImpl();
void DeleteLater();
- protected:
- virtual void OnPrepareDelete();
-
private:
bool delete_later_scheduled_;
};
diff --git a/include/cru/ui/components/Component.h b/include/cru/ui/components/Component.h
index 6d31ae79..d8966a89 100644
--- a/include/cru/ui/components/Component.h
+++ b/include/cru/ui/components/Component.h
@@ -10,9 +10,6 @@ namespace cru::ui::components {
*/
class CRU_UI_API Component : public Object, public DeleteLaterImpl {
public:
- Component() = default;
- ~Component() = default;
-
virtual controls::Control* GetRootControl() = 0;
bool IsDeleteByParent() const { return delete_by_parent_; }
@@ -21,9 +18,6 @@ class CRU_UI_API Component : public Object, public DeleteLaterImpl {
}
void DeleteIfDeleteByParent(bool delete_later = true);
- protected:
- void OnPrepareDelete() override;
-
private:
bool delete_by_parent_ = false;
};
diff --git a/include/cru/ui/components/Menu.h b/include/cru/ui/components/Menu.h
index 554a8898..92731f2e 100644
--- a/include/cru/ui/components/Menu.h
+++ b/include/cru/ui/components/Menu.h
@@ -1,11 +1,10 @@
#pragma once
+#include "../controls/Button.h"
+#include "../controls/Control.h"
+#include "../controls/FlexLayout.h"
+#include "../controls/TextBlock.h"
+#include "../controls/Window.h"
#include "Component.h"
-#include "cru/base/Base.h"
-#include "cru/ui/controls/Button.h"
-#include "cru/ui/controls/Control.h"
-#include "cru/ui/controls/FlexLayout.h"
-#include "cru/ui/controls/Popup.h"
-#include "cru/ui/controls/TextBlock.h"
#include <functional>
#include <vector>
@@ -16,11 +15,6 @@ class CRU_UI_API MenuItem : public Component {
MenuItem();
explicit MenuItem(std::string text);
- CRU_DELETE_COPY(MenuItem)
- CRU_DELETE_MOVE(MenuItem)
-
- ~MenuItem();
-
public:
controls::Control* GetRootControl() override { return &container_; }
@@ -39,10 +33,6 @@ class CRU_UI_API MenuItem : public Component {
class CRU_UI_API Menu : public Component {
public:
Menu();
-
- CRU_DELETE_COPY(Menu)
- CRU_DELETE_MOVE(Menu)
-
~Menu();
public:
@@ -58,7 +48,8 @@ class CRU_UI_API Menu : public Component {
void AddTextItem(std::string text, std::function<void()> on_click) {
AddTextItemAt(std::move(text), GetItemCount(), std::move(on_click));
}
- void AddTextItemAt(std::string text, Index index, std::function<void()> on_click);
+ void AddTextItemAt(std::string text, Index index,
+ std::function<void()> on_click);
void SetOnItemClick(std::function<void(Index)> on_item_click) {
on_item_click_ = std::move(on_item_click);
@@ -74,16 +65,12 @@ class CRU_UI_API Menu : public Component {
class CRU_UI_API PopupMenu : public Component {
public:
explicit PopupMenu(controls::Control* attached_control = nullptr);
-
- CRU_DELETE_COPY(PopupMenu)
- CRU_DELETE_MOVE(PopupMenu)
-
~PopupMenu();
public:
controls::Control* GetRootControl() override;
- controls::Popup* GetPopup() { return &popup_; }
+ controls::Window* GetPopup() { return popup_; }
Menu* GetMenu() { return &menu_; }
// position relative to screen left top.
@@ -99,7 +86,7 @@ class CRU_UI_API PopupMenu : public Component {
private:
controls::Control* attached_control_;
- controls::Popup popup_;
+ controls::Window* popup_;
Menu menu_;
};
} // namespace cru::ui::components
diff --git a/include/cru/ui/components/PopupButton.h b/include/cru/ui/components/PopupButton.h
index 5fa69044..0e3d5314 100644
--- a/include/cru/ui/components/PopupButton.h
+++ b/include/cru/ui/components/PopupButton.h
@@ -4,7 +4,6 @@
#include "cru/ui/components/Menu.h"
#include "cru/ui/controls/Button.h"
#include "cru/ui/controls/IconButton.h"
-#include "cru/ui/controls/Popup.h"
#include "cru/ui/controls/TextBlock.h"
namespace cru::ui::components {
diff --git a/include/cru/ui/controls/Control.h b/include/cru/ui/controls/Control.h
index 77f5f392..684bc960 100644
--- a/include/cru/ui/controls/Control.h
+++ b/include/cru/ui/controls/Control.h
@@ -4,6 +4,8 @@
#include "../events/UiEvents.h"
#include "../render/RenderObject.h"
#include "../style/StyleRuleSet.h"
+#include "cru/ui/events/KeyEventArgs.h"
+#include "cru/ui/events/MouseWheelEventArgs.h"
#include "cru/ui/render/MeasureRequirement.h"
namespace cru::ui::controls {
@@ -31,9 +33,11 @@ class CRU_UI_API Control : public Object, public DeleteLaterImpl {
public:
virtual std::string GetControlType() const = 0;
+ std::string GetDebugId() const;
+
//*************** region: tree ***************
public:
- host::WindowHost* GetWindowHost() const { return window_host_; }
+ Window* GetWindow();
Control* GetParent() const { return parent_; }
void SetParent(Control* parent);
@@ -48,6 +52,8 @@ class CRU_UI_API Control : public Object, public DeleteLaterImpl {
void RemoveFromParent();
+ controls::Control* HitTest(const Point& point);
+
public:
virtual render::RenderObject* GetRenderObject() const = 0;
@@ -107,83 +113,30 @@ class CRU_UI_API Control : public Object, public DeleteLaterImpl {
// the mouse, this event is raised as regular. But if mouse is captured by
// another control, the control will not receive any mouse enter event. You
// can use `IsMouseCaptured` to get more info.
- events::RoutedEvent<events::MouseEventArgs>* MouseEnterEvent() {
- return &mouse_enter_event_;
- }
+ CRU_DEFINE_ROUTED_EVENT(MouseEnter, events::MouseEventArgs)
+
// Raised when mouse is leave the control. Even when the control itself
// captures the mouse, this event is raised as regular. But if mouse is
// captured by another control, the control will not receive any mouse leave
// event. You can use `IsMouseCaptured` to get more info.
- events::RoutedEvent<events::MouseEventArgs>* MouseLeaveEvent() {
- return &mouse_leave_event_;
- }
- // Raised when mouse is move in the control.
- events::RoutedEvent<events::MouseEventArgs>* MouseMoveEvent() {
- return &mouse_move_event_;
- }
- // Raised when a mouse button is pressed in the control.
- events::RoutedEvent<events::MouseButtonEventArgs>* MouseDownEvent() {
- return &mouse_down_event_;
- }
- // Raised when a mouse button is released in the control.
- events::RoutedEvent<events::MouseButtonEventArgs>* MouseUpEvent() {
- return &mouse_up_event_;
- }
- events::RoutedEvent<events::MouseWheelEventArgs>* MouseWheelEvent() {
- return &mouse_wheel_event_;
- }
- events::RoutedEvent<events::KeyEventArgs>* KeyDownEvent() {
- return &key_down_event_;
- }
- events::RoutedEvent<events::KeyEventArgs>* KeyUpEvent() {
- return &key_up_event_;
- }
- events::RoutedEvent<events::FocusChangeEventArgs>* GainFocusEvent() {
- return &gain_focus_event_;
- }
- events::RoutedEvent<events::FocusChangeEventArgs>* LoseFocusEvent() {
- return &lose_focus_event_;
- }
+ CRU_DEFINE_ROUTED_EVENT(MouseLeave, events::MouseEventArgs)
- private:
- events::RoutedEvent<events::MouseEventArgs> mouse_enter_event_;
- events::RoutedEvent<events::MouseEventArgs> mouse_leave_event_;
- events::RoutedEvent<events::MouseEventArgs> mouse_move_event_;
- events::RoutedEvent<events::MouseButtonEventArgs> mouse_down_event_;
- events::RoutedEvent<events::MouseButtonEventArgs> mouse_up_event_;
- events::RoutedEvent<events::MouseWheelEventArgs> mouse_wheel_event_;
-
- events::RoutedEvent<events::KeyEventArgs> key_down_event_;
- events::RoutedEvent<events::KeyEventArgs> key_up_event_;
-
- events::RoutedEvent<events::FocusChangeEventArgs> gain_focus_event_;
- events::RoutedEvent<events::FocusChangeEventArgs> lose_focus_event_;
+ CRU_DEFINE_ROUTED_EVENT(MouseMove, events::MouseEventArgs)
+ CRU_DEFINE_ROUTED_EVENT(MouseDown, events::MouseButtonEventArgs)
+ CRU_DEFINE_ROUTED_EVENT(MouseUp, events::MouseButtonEventArgs)
+ CRU_DEFINE_ROUTED_EVENT(MouseWheel, events::MouseWheelEventArgs)
+ CRU_DEFINE_ROUTED_EVENT(KeyDown, events::KeyEventArgs)
+ CRU_DEFINE_ROUTED_EVENT(KeyUp, events::KeyEventArgs)
+ CRU_DEFINE_ROUTED_EVENT(GainFocus, events::FocusChangeEventArgs)
+ CRU_DEFINE_ROUTED_EVENT(LoseFocus, events::FocusChangeEventArgs)
//*************** region: tree ***************
protected:
virtual void OnParentChanged(Control* old_parent, Control* new_parent) {}
- virtual void OnWindowHostChanged(host::WindowHost* old_host,
- host::WindowHost* new_host) {}
-
- protected:
- virtual void OnMouseHoverChange(bool newHover) { CRU_UNUSED(newHover) }
-
- void OnPrepareDelete() override;
-
- private:
- void OnParentChangedCore(Control* old_parent, Control* new_parent);
- void OnWindowHostChangedCore(host::WindowHost* old_host,
- host::WindowHost* new_host);
-
private:
- bool in_destruction_ = false;
-
Control* parent_ = nullptr;
- host::WindowHost* window_host_ = nullptr;
-
- private:
bool is_mouse_over_ = false;
std::shared_ptr<platform::gui::ICursor> cursor_ = nullptr;
diff --git a/include/cru/ui/controls/Popup.h b/include/cru/ui/controls/Popup.h
deleted file mode 100644
index 7c57d257..00000000
--- a/include/cru/ui/controls/Popup.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-#include "RootControl.h"
-
-#include "cru/platform/gui/Base.h"
-
-#include <memory>
-
-namespace cru::ui::controls {
-class CRU_UI_API Popup : public RootControl {
- public:
- static constexpr std::string_view kControlType = "Popup";
-
- explicit Popup(Control* attached_control = nullptr);
-
- CRU_DELETE_COPY(Popup)
- CRU_DELETE_MOVE(Popup)
-
- ~Popup() override;
-
- std::string GetControlType() const override { return std::string(kControlType); }
-};
-} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/RootControl.h b/include/cru/ui/controls/RootControl.h
deleted file mode 100644
index e662b655..00000000
--- a/include/cru/ui/controls/RootControl.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma once
-#include "LayoutControl.h"
-
-#include "cru/base/Event.h"
-#include "cru/platform/gui/Window.h"
-#include "cru/ui/host/WindowHost.h"
-#include "cru/ui/render/StackLayoutRenderObject.h"
-
-namespace cru::ui::controls {
-class CRU_UI_API RootControl
- : public LayoutControl<render::StackLayoutRenderObject> {
- protected:
- explicit RootControl(Control* attached_control);
-
- public:
- CRU_DELETE_COPY(RootControl)
- CRU_DELETE_MOVE(RootControl)
- ~RootControl() override;
-
- public:
- platform::gui::INativeWindow* GetNativeWindow();
-
- protected:
- void SetGainFocusOnCreateAndDestroyWhenLoseFocus(bool value);
-
- private:
- std::unique_ptr<host::WindowHost> window_host_;
-
- Control* attached_control_;
-
- EventHandlerRevokerListGuard
- gain_focus_on_create_and_destroy_when_lose_focus_event_guard_;
-};
-} // namespace cru::ui::controls
diff --git a/include/cru/ui/controls/TextHostControlService.h b/include/cru/ui/controls/TextHostControlService.h
index 454be09b..40ba017a 100644
--- a/include/cru/ui/controls/TextHostControlService.h
+++ b/include/cru/ui/controls/TextHostControlService.h
@@ -201,6 +201,8 @@ class CRU_UI_API TextHostControlService : public Object {
platform::gui::TimerAutoCanceler caret_timer_canceler_;
int caret_blink_duration_ = k_default_caret_blink_duration;
+ platform::gui::TimerAutoCanceler scroll_to_caret_timer_canceler_;
+
helper::ShortcutHub shortcut_hub_;
// true if left mouse is down and selecting
diff --git a/include/cru/ui/controls/Window.h b/include/cru/ui/controls/Window.h
index c4ba94d9..e25694ba 100644
--- a/include/cru/ui/controls/Window.h
+++ b/include/cru/ui/controls/Window.h
@@ -1,20 +1,188 @@
#pragma once
-#include "cru/ui/controls/RootControl.h"
+#include "../render/StackLayoutRenderObject.h"
+#include "LayoutControl.h"
-#include "cru/base/Base.h"
+#include <cru/base/Base.h>
+#include <cru/base/Event.h>
+#include <cru/base/Guard.h>
+#include <cru/base/log/Logger.h>
+#include <cru/platform/gui/UiApplication.h>
+#include <cru/platform/gui/Window.h>
namespace cru::ui::controls {
-class CRU_UI_API Window final : public RootControl {
+class CRU_UI_API Window
+ : public LayoutControl<render::StackLayoutRenderObject> {
+ CRU_DEFINE_CLASS_LOG_TAG("cru::ui::controls::Window")
public:
- static constexpr std::string_view control_type = "Window";
+ static constexpr std::string_view kControlType = "Window";
- public:
- explicit Window(Control* attached_control = nullptr);
- CRU_DELETE_COPY(Window)
- CRU_DELETE_MOVE(Window)
+ Window();
~Window() override;
- public:
- std::string GetControlType() const final { return std::string(control_type); }
+ static Window* CreatePopup();
+
+ std::string GetControlType() const override;
+
+ void SetAttachedControl(Control* control);
+
+ platform::gui::INativeWindow* GetNativeWindow();
+
+ void InvalidateLayout();
+ void InvalidatePaint();
+
+ void Repaint();
+ void Relayout();
+ void RelayoutWithSize(const Size& available_size = Size::Infinite(),
+ bool set_window_size_to_fit_content = false);
+
+ void SetGainFocusOnCreateAndDestroyWhenLoseFocus(bool value);
+
+ // If true, preferred size of root render object is set to window size when
+ // measure. Default is true.
+ bool IsLayoutPreferToFillWindow() const;
+ void SetLayoutPreferToFillWindow(bool value);
+
+ // 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_; }
+
+ Control* GetFocusControl();
+ void SetFocusControl(Control* control);
+
+ Control* GetMouseCaptureControl();
+ bool SetMouseCaptureControl(Control* control);
+
+ std::shared_ptr<platform::gui::ICursor> GetOverrideCursor();
+ void SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor);
+
+ bool IsInEventHandling();
+ void UpdateCursor();
+
+ CRU_DEFINE_EVENT(AfterLayout, std::nullptr_t)
+
+ private:
+ std::unique_ptr<platform::gui::INativeWindow> CreateNativeWindow();
+
+ void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t);
+ void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t);
+ void OnNativeResize(platform::gui::INativeWindow* window, const Size& size);
+ void OnNativeFocus(platform::gui::INativeWindow* window,
+ cru::platform::gui::FocusChangeType focus);
+ void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
+ cru::platform::gui::MouseEnterLeaveType enter);
+ void OnNativeMouseMove(platform::gui::INativeWindow* window,
+ const Point& point);
+ void OnNativeMouseDown(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args);
+ void OnNativeMouseUp(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args);
+ void OnNativeMouseWheel(platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseWheelEventArgs& args);
+ void OnNativeKeyDown(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args);
+ void OnNativeKeyUp(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args);
+
+ void DispatchFocusControlChangeEvent(Control* old_control,
+ Control* new_control, bool is_window);
+ void DispatchMouseHoverControlChangeEvent(Control* old_control,
+ Control* new_control,
+ const Point& point, bool no_leave,
+ bool no_enter);
+
+ template <typename EventArgs, typename... Args>
+ void DispatchEvent(Control* const original_sender,
+ events::RoutedEvent<EventArgs>* (Control::*event_ptr)(),
+ Control* const last_receiver, Args&&... args) {
+ constexpr auto kLogTag = "cru::ui::controls::DispatchEvent";
+
+ event_handling_count_++;
+ Guard event_handling_count_guard([this] { event_handling_count_--; });
+
+ if (original_sender == nullptr || original_sender == last_receiver) return;
+
+ std::string log = "Begin dispatching routed event " +
+ (original_sender->*event_ptr)()->GetName() + ":\n\tTunnel:";
+
+ Guard logging_guard([&] {
+ log += "\nEnd dispatching routed event " +
+ (original_sender->*event_ptr)()->GetName() + ".";
+ CRU_LOG_TAG_DEBUG("{}", log);
+ });
+
+ std::vector<Control*> receive_list;
+
+ auto parent = original_sender;
+ while (parent != last_receiver) {
+ receive_list.push_back(parent);
+ parent = parent->GetParent();
+ }
+
+ auto handled = false;
+
+ // tunnel
+ for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
+ auto control = *i;
+ log += " ";
+ log += control->GetDebugId();
+ EventArgs event_args(control, original_sender,
+ std::forward<Args>(args)...);
+ (control->*event_ptr)()->tunnel_.Raise(event_args);
+ if (event_args.IsHandled()) {
+ log += " marked as handled.";
+ handled = true;
+ break;
+ }
+ }
+
+ // bubble
+ if (!handled) {
+ log += "\n\tBubble:";
+ for (auto control : receive_list) {
+ log += " ";
+ log += control->GetDebugId();
+ EventArgs event_args(control, original_sender,
+ std::forward<Args>(args)...);
+ (control->*event_ptr)()->bubble_.Raise(event_args);
+ if (event_args.IsHandled()) {
+ log += " marked as handled.";
+ break;
+ }
+ }
+ }
+
+ log += "\n\tDirect:";
+ // direct
+ for (auto control : receive_list) {
+ log += " ";
+ log += control->GetDebugId();
+ EventArgs event_args(control, original_sender,
+ std::forward<Args>(args)...);
+ (control->*event_ptr)()->direct_.Raise(event_args);
+ }
+ }
+
+ private:
+ int event_handling_count_;
+
+ std::unique_ptr<platform::gui::INativeWindow> native_window_;
+
+ Control* focus_control_;
+ Control* mouse_hover_control_;
+ Control* mouse_captured_control_;
+
+ std::shared_ptr<platform::gui::ICursor> override_cursor_;
+
+ bool layout_prefer_to_fill_window_;
+
+ platform::gui::TimerAutoCanceler repaint_schedule_canceler_;
+ platform::gui::TimerAutoCanceler relayout_schedule_canceler_;
+
+ Control* attached_control_;
+
+ EventHandlerRevokerListGuard
+ gain_focus_on_create_and_destroy_when_lose_focus_event_guard_;
};
} // namespace cru::ui::controls
diff --git a/include/cru/ui/events/RoutedEvent.h b/include/cru/ui/events/RoutedEvent.h
index aa3331a6..58e50d63 100644
--- a/include/cru/ui/events/RoutedEvent.h
+++ b/include/cru/ui/events/RoutedEvent.h
@@ -8,6 +8,8 @@ namespace cru::ui::events {
// EventArgs must be reference because the IsHandled property must be settable.
template <typename TEventArgs>
class CRU_UI_API RoutedEvent {
+ friend controls::Window;
+
public:
static_assert(std::is_base_of_v<UiEventArgs, TEventArgs>,
"TEventArgs must be subclass of UiEventArgs.");
@@ -16,13 +18,29 @@ class CRU_UI_API RoutedEvent {
using EventArgs = TEventArgs&;
+ explicit RoutedEvent(std::string name) : name_(std::move(name)) {}
+
+ std::string GetName() const { return name_; }
+
IEvent<TEventArgs&>* Direct() { return &direct_; }
IEvent<TEventArgs&>* Bubble() { return &bubble_; }
IEvent<TEventArgs&>* Tunnel() { return &tunnel_; }
private:
+ std::string name_;
+
Event<TEventArgs&> direct_;
Event<TEventArgs&> bubble_;
Event<TEventArgs&> tunnel_;
};
-} // namespace cru::ui::event
+
+#define CRU_DEFINE_ROUTED_EVENT(name, arg_type) \
+ private: \
+ ::cru::ui::events::RoutedEvent<arg_type> name##Event_{#name}; \
+ \
+ public: \
+ ::cru::ui::events::RoutedEvent<arg_type>* name##Event() { \
+ return &name##Event_; \
+ }
+
+} // namespace cru::ui::events
diff --git a/include/cru/ui/host/LayoutPaintCycler.h b/include/cru/ui/host/LayoutPaintCycler.h
deleted file mode 100644
index e4ff7aa8..00000000
--- a/include/cru/ui/host/LayoutPaintCycler.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#pragma once
-#include "../Base.h"
-
-#include "cru/platform/gui/UiApplication.h"
-
-#include <chrono>
-
-namespace cru::ui::host {
-class CRU_UI_API LayoutPaintCycler {
- public:
- explicit LayoutPaintCycler(WindowHost* host);
-
- CRU_DELETE_COPY(LayoutPaintCycler)
- CRU_DELETE_MOVE(LayoutPaintCycler)
-
- ~LayoutPaintCycler();
-
- public:
- void InvalidateLayout();
- void InvalidatePaint();
-
- bool IsLayoutDirty() { return layout_dirty_; }
-
- private:
- void OnCycle();
-
- private:
- WindowHost* host_;
-
- platform::gui::TimerAutoCanceler timer_canceler_;
-
- bool layout_dirty_ = true;
- bool paint_dirty_ = true;
-
- std::chrono::steady_clock::time_point last_cycle_time_;
- std::chrono::steady_clock::duration cycle_threshold_ =
- std::chrono::milliseconds(1000) / 144;
-};
-} // namespace cru::ui::host
diff --git a/include/cru/ui/host/WindowHost.h b/include/cru/ui/host/WindowHost.h
deleted file mode 100644
index 13b06b07..00000000
--- a/include/cru/ui/host/WindowHost.h
+++ /dev/null
@@ -1,172 +0,0 @@
-#pragma once
-#include "../Base.h"
-
-#include "../render/RenderObject.h"
-#include "cru/base/Event.h"
-#include "cru/platform/gui/Cursor.h"
-#include "cru/platform/gui/UiApplication.h"
-#include "cru/platform/gui/Window.h"
-
-#include <functional>
-#include <memory>
-
-namespace cru::ui::host {
-class LayoutPaintCycler;
-
-struct AfterLayoutEventArgs {};
-
-// The bridge between control tree and native window.
-class CRU_UI_API WindowHost : public Object {
- friend controls::Control;
- CRU_DEFINE_CLASS_LOG_TAG("WindowHost")
-
- private:
- static int event_handling_depth_;
-
- public:
- static bool IsInEventHandling() { return event_handling_depth_ > 0; }
- static void EnterEventHandling();
- static void LeaveEventHandling();
-
- public:
- explicit WindowHost(controls::Control* root_control);
- ~WindowHost() override;
-
- public:
- platform::gui::INativeWindow* GetNativeWindow() {
- return native_window_.get();
- }
-
- // 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<AfterLayoutEventArgs>* AfterLayoutEvent() {
- return &after_layout_event_;
- }
-
- void Relayout();
- void RelayoutWithSize(const Size& available_size = Size::Infinite(),
- bool set_window_size_to_fit_content = false);
-
- void Repaint();
-
- // Is layout is invalid, wait for relayout and then run the action. Otherwist
- // run it right now.
- void RunAfterLayoutStable(std::function<void()> action);
-
- // If true, preferred size of root render object is set to window size when
- // measure. Default is true.
- bool IsLayoutPreferToFillWindow() const;
- void SetLayoutPreferToFillWindow(bool value);
-
- // 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.
- controls::Control* GetMouseHoverControl() const {
- return mouse_hover_control_;
- }
-
- //*************** region: focus ***************
-
- controls::Control* GetFocusControl();
-
- void SetFocusControl(controls::Control* control);
-
- //*************** 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(controls::Control* control);
-
- // Return null if not captured.
- controls::Control* GetMouseCaptureControl();
-
- controls::Control* HitTest(const Point& point);
-
- void UpdateCursor();
-
- IEvent<platform::gui::INativeWindow*>* NativeWindowChangeEvent() {
- return &native_window_change_event_;
- }
-
- std::shared_ptr<platform::gui::ICursor> GetOverrideCursor();
- void SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor);
-
- private:
- std::unique_ptr<platform::gui::INativeWindow> CreateNativeWindow();
-
- //*************** region: native messages ***************
- void OnNativeDestroy(platform::gui::INativeWindow* window, std::nullptr_t);
- void OnNativePaint(platform::gui::INativeWindow* window, std::nullptr_t);
- void OnNativeResize(platform::gui::INativeWindow* window, const Size& size);
-
- void OnNativeFocus(platform::gui::INativeWindow* window,
- cru::platform::gui::FocusChangeType focus);
-
- void OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
- cru::platform::gui::MouseEnterLeaveType enter);
- void OnNativeMouseMove(platform::gui::INativeWindow* window,
- const Point& point);
- void OnNativeMouseDown(platform::gui::INativeWindow* window,
- const platform::gui::NativeMouseButtonEventArgs& args);
- void OnNativeMouseUp(platform::gui::INativeWindow* window,
- const platform::gui::NativeMouseButtonEventArgs& args);
- void OnNativeMouseWheel(platform::gui::INativeWindow* window,
- const platform::gui::NativeMouseWheelEventArgs& args);
-
- void OnNativeKeyDown(platform::gui::INativeWindow* window,
- const platform::gui::NativeKeyEventArgs& args);
- void OnNativeKeyUp(platform::gui::INativeWindow* window,
- const platform::gui::NativeKeyEventArgs& args);
-
- //*************** region: event dispatcher helper ***************
-
- void DispatchMouseHoverControlChangeEvent(controls::Control* old_control,
- controls::Control* new_control,
- const Point& point, bool no_leave,
- bool no_enter);
-
- void OnControlDetach(controls::Control* control);
-
- private:
- controls::Control* root_control_ = nullptr;
- render::RenderObject* root_render_object_ = nullptr;
-
- std::unique_ptr<platform::gui::INativeWindow> native_window_;
-
- std::unique_ptr<LayoutPaintCycler> layout_paint_cycler_;
-
- Event<AfterLayoutEventArgs> after_layout_event_;
- std::vector<std::function<void()> > after_layout_stable_action_;
-
- std::vector<EventHandlerRevokerGuard> event_revoker_guards_;
-
- controls::Control* mouse_hover_control_ = nullptr;
-
- controls::Control* focus_control_;
-
- controls::Control* mouse_captured_control_ = nullptr;
-
- bool layout_prefer_to_fill_window_ = false;
-
- Event<platform::gui::INativeWindow*> native_window_change_event_;
-
- std::shared_ptr<platform::gui::ICursor> override_cursor_;
-};
-} // namespace cru::ui::host
diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h
index 34761b51..c299ea24 100644
--- a/include/cru/ui/render/RenderObject.h
+++ b/include/cru/ui/render/RenderObject.h
@@ -137,7 +137,7 @@ class CRU_UI_API RenderObject : public Object {
virtual RenderObject* HitTest(const Point& point) = 0;
public:
- host::WindowHost* GetWindowHost();
+ controls::Window* GetWindow();
void InvalidateLayout();
void InvalidatePaint();
diff --git a/include/cru/ui/render/ScrollBar.h b/include/cru/ui/render/ScrollBar.h
index 13c7d8b0..2325acd1 100644
--- a/include/cru/ui/render/ScrollBar.h
+++ b/include/cru/ui/render/ScrollBar.h
@@ -5,11 +5,9 @@
#include "cru/platform/graphics/Brush.h"
#include "cru/platform/graphics/Geometry.h"
#include "cru/platform/graphics/Painter.h"
-#include "cru/platform/gui/Cursor.h"
#include "cru/platform/gui/UiApplication.h"
#include "cru/ui/Base.h"
#include "cru/ui/controls/Control.h"
-#include "cru/ui/helper/ClickDetector.h"
#include <memory>
#include <optional>
@@ -38,8 +36,8 @@ enum class ScrollBarAreaKind {
enum class ScrollBarBrushUsageKind { Arrow, ArrowBackground, Slot, Thumb };
enum class ScrollBarBrushStateKind { Normal, Hover, Press, Disable };
-std::string CRU_UI_API GenerateScrollBarThemeColorKey(ScrollBarBrushUsageKind usage,
- ScrollBarBrushStateKind state);
+std::string CRU_UI_API GenerateScrollBarThemeColorKey(
+ ScrollBarBrushUsageKind usage, ScrollBarBrushStateKind state);
class CRU_UI_API ScrollBar : public Object {
public:
@@ -139,7 +137,7 @@ class CRU_UI_API ScrollBar : public Object {
Event<Scroll> scroll_attempt_event_;
- bool cursor_overrided_ = false;
+ bool cursor_overridden_ = false;
platform::gui::TimerAutoCanceler auto_collapse_timer_canceler_;
};
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 4dfee0cb..84849d44 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -15,8 +15,6 @@ add_library(CruUi
controls/FlexLayout.cpp
controls/IconButton.cpp
controls/NoChildControl.cpp
- controls/Popup.cpp
- controls/RootControl.cpp
controls/ScrollView.cpp
controls/StackLayout.cpp
controls/TextBlock.cpp
@@ -30,8 +28,6 @@ add_library(CruUi
events/MouseEventArgs.cpp
helper/ClickDetector.cpp
helper/ShortcutHub.cpp
- host/LayoutPaintCycler.cpp
- host/WindowHost.cpp
mapper/BorderStyleMapper.cpp
mapper/BrushMapper.cpp
mapper/ColorMapper.cpp
diff --git a/src/ui/DeleteLater.cpp b/src/ui/DeleteLater.cpp
index 27ec2155..90b07b71 100644
--- a/src/ui/DeleteLater.cpp
+++ b/src/ui/DeleteLater.cpp
@@ -20,6 +20,4 @@ void DeleteLaterImpl::DeleteLater() {
delete_later_scheduled_ = true;
}
}
-
-void DeleteLaterImpl::OnPrepareDelete() {}
} // namespace cru::ui
diff --git a/src/ui/components/Component.cpp b/src/ui/components/Component.cpp
index d0525a1c..77a659dc 100644
--- a/src/ui/components/Component.cpp
+++ b/src/ui/components/Component.cpp
@@ -1,10 +1,6 @@
#include "cru/ui/components/Component.h"
-#include "cru/ui/controls/Control.h"
-
namespace cru::ui::components {
-void Component::OnPrepareDelete() { GetRootControl()->RemoveFromParent(); }
-
void Component::DeleteIfDeleteByParent(bool delete_later) {
if (delete_by_parent_) {
if (delete_later) {
diff --git a/src/ui/components/Menu.cpp b/src/ui/components/Menu.cpp
index 04227d66..c89c7fc9 100644
--- a/src/ui/components/Menu.cpp
+++ b/src/ui/components/Menu.cpp
@@ -4,10 +4,9 @@
#include "cru/ui/controls/Button.h"
#include "cru/ui/controls/Control.h"
#include "cru/ui/controls/FlexLayout.h"
-#include "cru/ui/controls/Popup.h"
#include "cru/ui/controls/TextBlock.h"
+#include "cru/ui/controls/Window.h"
#include "cru/ui/helper/ClickDetector.h"
-#include "cru/ui/host/WindowHost.h"
#include "cru/ui/style/StyleRuleSet.h"
namespace cru::ui::components {
@@ -22,8 +21,6 @@ MenuItem::MenuItem() {
MenuItem::MenuItem(std::string text) : MenuItem() { SetText(std::move(text)); }
-MenuItem::~MenuItem() {}
-
void MenuItem::SetText(std::string text) { text_.SetText(std::move(text)); }
Menu::Menu() {
@@ -33,7 +30,7 @@ Menu::Menu() {
Menu::~Menu() {
for (auto item : items_) {
- item->DeleteIfDeleteByParent(false);
+ item->DeleteIfDeleteByParent();
}
}
@@ -78,27 +75,29 @@ void Menu::AddTextItemAt(std::string text, Index index,
}
PopupMenu::PopupMenu(controls::Control* attached_control)
- : attached_control_(attached_control), popup_(attached_control) {
- menu_.SetOnItemClick([this](Index) { popup_.GetNativeWindow()->Close(); });
- popup_.AddChildAt(menu_.GetRootControl(), 0);
+ : attached_control_(attached_control) {
+ menu_.SetOnItemClick([this](Index) { popup_->GetNativeWindow()->Close(); });
+ popup_ = controls::Window::CreatePopup();
+ popup_->SetAttachedControl(attached_control);
+ popup_->AddChildAt(menu_.GetRootControl(), 0);
}
-PopupMenu::~PopupMenu() {}
+PopupMenu::~PopupMenu() { delete popup_; }
-controls::Control* PopupMenu::GetRootControl() { return &popup_; }
+controls::Control* PopupMenu::GetRootControl() { return popup_; }
void PopupMenu::SetPosition(const Point& position) {
- auto window = popup_.GetWindowHost()->GetNativeWindow();
+ auto window = popup_->GetNativeWindow();
window->SetClientRect(Rect{position, window->GetClientSize()});
}
void PopupMenu::Show() {
- auto native_window = popup_.GetWindowHost()->GetNativeWindow();
+ auto native_window = popup_->GetNativeWindow();
native_window->SetVisibility(platform::gui::WindowVisibilityType::Show);
- popup_.GetWindowHost()->RelayoutWithSize(Size::Infinite(), true);
+ popup_->RelayoutWithSize(Size::Infinite(), true);
native_window->RequestFocus();
native_window->SetToForeground();
}
-void PopupMenu::Close() { popup_.GetWindowHost()->GetNativeWindow()->Close(); }
+void PopupMenu::Close() { popup_->GetNativeWindow()->Close(); }
} // namespace cru::ui::components
diff --git a/src/ui/components/PopupButton.cpp b/src/ui/components/PopupButton.cpp
index 5ea41d78..1869ba31 100644
--- a/src/ui/components/PopupButton.cpp
+++ b/src/ui/components/PopupButton.cpp
@@ -1,6 +1,5 @@
#include "cru/ui/components/PopupButton.h"
#include "cru/ui/components/Menu.h"
-#include "cru/ui/controls/Popup.h"
#include "cru/ui/helper/ClickDetector.h"
namespace cru::ui::components {
diff --git a/src/ui/controls/Control.cpp b/src/ui/controls/Control.cpp
index 93213ecf..e903b5b3 100644
--- a/src/ui/controls/Control.cpp
+++ b/src/ui/controls/Control.cpp
@@ -1,10 +1,12 @@
#include "cru/ui/controls/Control.h"
+#include "cru/ui/controls/Window.h"
#include "cru/platform/gui/Cursor.h"
#include "cru/platform/gui/UiApplication.h"
-#include "cru/ui/host/WindowHost.h"
#include "cru/ui/style/StyleRuleSet.h"
+#include <format>
+
namespace cru::ui::controls {
using platform::gui::ICursor;
using platform::gui::IUiApplication;
@@ -15,32 +17,45 @@ Control::Control() {
style_rule_set_bind_ =
std::make_unique<style::StyleRuleSetBind>(this, style_rule_set_);
- MouseEnterEvent()->Direct()->AddHandler([this](events::MouseEventArgs&) {
- this->is_mouse_over_ = true;
- this->OnMouseHoverChange(true);
- });
+ MouseEnterEvent()->Direct()->AddHandler(
+ [this](events::MouseEventArgs&) { this->is_mouse_over_ = true; });
- MouseLeaveEvent()->Direct()->AddHandler([this](events::MouseEventArgs&) {
- this->is_mouse_over_ = false;
- this->OnMouseHoverChange(true);
- });
+ MouseLeaveEvent()->Direct()->AddHandler(
+ [this](events::MouseEventArgs&) { this->is_mouse_over_ = false; });
}
Control::~Control() {
- if (host::WindowHost::IsInEventHandling()) {
- // Don't delete control during event handling. Use DeleteLater.
- std::terminate();
+ if (auto window = GetWindow()) {
+ if (window->IsInEventHandling()) {
+ // Don't delete control during event handling. Use DeleteLater.
+ std::terminate();
+ }
}
- in_destruction_ = true;
RemoveFromParent();
}
+std::string Control::GetDebugId() const {
+ return std::format("{}({})", GetControlType(),
+ static_cast<const void*>(this));
+}
+
+Window* Control::GetWindow() {
+ auto parent = this;
+ while (parent) {
+ if (auto window = dynamic_cast<Window*>(parent)) {
+ return window;
+ }
+ parent = parent->GetParent();
+ }
+ return nullptr;
+}
+
void Control::SetParent(Control* parent) {
if (parent_ == parent) return;
auto old_parent = parent_;
parent_ = parent;
- OnParentChangedCore(old_parent, parent);
+ OnParentChanged(old_parent, parent);
}
void Control::RemoveFromParent() {
@@ -49,39 +64,49 @@ void Control::RemoveFromParent() {
}
}
+controls::Control* Control::HitTest(const Point& point) {
+ const auto render_object = GetRenderObject()->HitTest(point);
+ if (render_object) {
+ const auto control = render_object->GetAttachedControl();
+ assert(control);
+ return control;
+ }
+ return nullptr;
+}
+
bool Control::HasFocus() {
- auto host = GetWindowHost();
- if (host == nullptr) return false;
+ auto window = GetWindow();
+ if (window == nullptr) return false;
- return host->GetFocusControl() == this;
+ return window->GetFocusControl() == this;
}
bool Control::CaptureMouse() {
- auto host = GetWindowHost();
- if (host == nullptr) return false;
+ auto window = GetWindow();
+ if (window == nullptr) return false;
- return host->CaptureMouseFor(this);
+ return window->SetMouseCaptureControl(this);
}
void Control::SetFocus() {
- auto host = GetWindowHost();
- if (host == nullptr) return;
+ auto window = GetWindow();
+ if (window == nullptr) return;
- host->SetFocusControl(this);
+ window->SetFocusControl(this);
}
bool Control::ReleaseMouse() {
- auto host = GetWindowHost();
- if (host == nullptr) return false;
-
- return host->CaptureMouseFor(nullptr);
+ auto window = GetWindow();
+ if (window == nullptr) return false;
+ if (window->GetMouseCaptureControl() != this) return false;
+ return window->SetMouseCaptureControl(nullptr);
}
bool Control::IsMouseCaptured() {
- auto host = GetWindowHost();
- if (host == nullptr) return false;
+ auto window = GetWindow();
+ if (window == nullptr) return false;
- return host->GetMouseCaptureControl() == this;
+ return window->GetMouseCaptureControl() == this;
}
std::shared_ptr<ICursor> Control::GetCursor() { return cursor_; }
@@ -99,42 +124,13 @@ std::shared_ptr<ICursor> Control::GetInheritedCursor() {
void Control::SetCursor(std::shared_ptr<ICursor> cursor) {
cursor_ = std::move(cursor);
- const auto host = GetWindowHost();
- if (host != nullptr) {
- host->UpdateCursor();
+ const auto window = GetWindow();
+ if (window != nullptr) {
+ window->UpdateCursor();
}
}
std::shared_ptr<style::StyleRuleSet> Control::GetStyleRuleSet() {
return style_rule_set_;
}
-
-void Control::OnParentChangedCore(Control* old_parent, Control* new_parent) {
- auto new_window_host =
- new_parent == nullptr ? nullptr : new_parent->GetWindowHost();
- if (window_host_ != new_window_host) {
- auto old_host = window_host_;
- window_host_ = new_window_host;
- OnWindowHostChangedCore(old_host, new_window_host);
- }
-
- if (!in_destruction_) OnParentChanged(old_parent, new_parent);
-}
-
-void Control::OnWindowHostChangedCore(host::WindowHost* old_host,
- host::WindowHost* new_host) {
- if (old_host != nullptr) {
- old_host->OnControlDetach(this);
- }
-
- if (!in_destruction_) {
- ForEachChild([old_host, new_host](Control* child) {
- child->window_host_ = new_host;
- child->OnWindowHostChangedCore(old_host, new_host);
- });
- OnWindowHostChanged(old_host, new_host);
- }
-}
-
-void Control::OnPrepareDelete() { RemoveFromParent(); }
} // namespace cru::ui::controls
diff --git a/src/ui/controls/Popup.cpp b/src/ui/controls/Popup.cpp
deleted file mode 100644
index 238ddbd4..00000000
--- a/src/ui/controls/Popup.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "cru/ui/controls/Popup.h"
-
-#include "cru/platform/gui/UiApplication.h"
-#include "cru/ui/controls/RootControl.h"
-#include "cru/ui/host/WindowHost.h"
-#include "cru/ui/render/StackLayoutRenderObject.h"
-
-#include <memory>
-
-namespace cru::ui::controls {
-Popup::Popup(Control* attached_control) : RootControl(attached_control) {
- GetWindowHost()->GetNativeWindow()->SetStyleFlag(
- cru::platform::gui::WindowStyleFlags::NoCaptionAndBorder);
- SetGainFocusOnCreateAndDestroyWhenLoseFocus(true);
-}
-
-Popup::~Popup() = default;
-} // namespace cru::ui::controls
diff --git a/src/ui/controls/RootControl.cpp b/src/ui/controls/RootControl.cpp
deleted file mode 100644
index 7be1c630..00000000
--- a/src/ui/controls/RootControl.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#include "cru/ui/controls/RootControl.h"
-
-#include "cru/platform/gui/Window.h"
-#include "cru/ui/Base.h"
-#include "cru/ui/host/WindowHost.h"
-
-#include <memory>
-
-namespace cru::ui::controls {
-RootControl::RootControl(Control* attached_control)
- : attached_control_(attached_control) {
- GetContainerRenderObject()->SetDefaultHorizontalAlignment(Alignment::Stretch);
- GetContainerRenderObject()->SetDefaultVertialAlignment(Alignment::Stretch);
- window_host_ = std::make_unique<host::WindowHost>(this);
-
- Control::window_host_ = this->window_host_.get();
-
- window_host_->SetLayoutPreferToFillWindow(true);
-}
-
-RootControl::~RootControl() {}
-
-platform::gui::INativeWindow* RootControl::GetNativeWindow() {
- return window_host_->GetNativeWindow();
-}
-
-void RootControl::SetGainFocusOnCreateAndDestroyWhenLoseFocus(bool value) {
- gain_focus_on_create_and_destroy_when_lose_focus_event_guard_.Clear();
- if (value) {
- auto native_window = window_host_->GetNativeWindow();
-
- gain_focus_on_create_and_destroy_when_lose_focus_event_guard_ +=
- native_window->VisibilityChangeEvent()->AddHandler(
- [native_window](platform::gui::WindowVisibilityType type) {
- if (type == platform::gui::WindowVisibilityType::Show) {
- native_window->RequestFocus();
- }
- });
-
- gain_focus_on_create_and_destroy_when_lose_focus_event_guard_ +=
- native_window->FocusEvent()->AddHandler(
- [native_window](platform::gui::FocusChangeType type) {
- if (type == platform::gui::FocusChangeType::Lose) {
- native_window->Close();
- }
- });
- }
-}
-} // namespace cru::ui::controls
diff --git a/src/ui/controls/TextHostControlService.cpp b/src/ui/controls/TextHostControlService.cpp
index 99090951..f51199c9 100644
--- a/src/ui/controls/TextHostControlService.cpp
+++ b/src/ui/controls/TextHostControlService.cpp
@@ -13,8 +13,8 @@
#include "cru/ui/Base.h"
#include "cru/ui/DebugFlags.h"
#include "cru/ui/components/Menu.h"
+#include "cru/ui/controls/Window.h"
#include "cru/ui/helper/ShortcutHub.h"
-#include "cru/ui/host/WindowHost.h"
#include "cru/ui/render/ScrollRenderObject.h"
#include "cru/ui/render/TextRenderObject.h"
@@ -293,9 +293,9 @@ void TextHostControlService::DeleteText(TextRange range,
platform::gui::IInputMethodContext*
TextHostControlService ::GetInputMethodContext() {
- host::WindowHost* host = this->control_->GetWindowHost();
- if (!host) return nullptr;
- platform::gui::INativeWindow* native_window = host->GetNativeWindow();
+ Window* window = this->control_->GetWindow();
+ if (!window) return nullptr;
+ platform::gui::INativeWindow* native_window = window->GetNativeWindow();
if (!native_window) return nullptr;
return native_window->GetInputMethodContext();
}
@@ -340,12 +340,13 @@ void TextHostControlService::SetCaretBlinkDuration(int milliseconds) {
void TextHostControlService::ScrollToCaret() {
if (const auto scroll_render_object = this->GetScrollRenderObject()) {
- auto window_host = this->control_->GetWindowHost();
- if (window_host)
- window_host->RunAfterLayoutStable([this, scroll_render_object]() {
- const auto caret_rect = this->GetTextRenderObject()->GetCaretRect();
- scroll_render_object->ScrollToContain(caret_rect, Thickness{5.f});
- });
+ scroll_to_caret_timer_canceler_.Reset(
+ platform::gui::IUiApplication::GetInstance()->SetImmediate(
+ [this, scroll_render_object] {
+ const auto caret_rect =
+ this->GetTextRenderObject()->GetCaretRect();
+ scroll_render_object->ScrollToContain(caret_rect, Thickness{5.f});
+ }));
}
}
@@ -587,10 +588,10 @@ void TextHostControlService::GainFocusHandler(
this->ReplaceSelectedText(text);
});
- host::WindowHost* window_host = control_->GetWindowHost();
- if (window_host)
+ auto window = control_->GetWindow();
+ if (window)
input_method_context_event_guard_ +=
- window_host->AfterLayoutEvent()->AddHandler(
+ window->AfterLayoutEvent()->AddHandler(
[this](auto) { this->UpdateInputMethodPosition(); });
SetCaretVisible(true);
}
@@ -700,7 +701,9 @@ void TextHostControlService::SetUpShortcuts() {
void TextHostControlService::OpenContextMenu(const Point& position,
ContextMenuItem items) {
CRU_LOG_TAG_DEBUG("Open context menu.");
- context_menu_.reset(new components::PopupMenu());
+ if (!context_menu_) {
+ context_menu_.reset(new components::PopupMenu());
+ }
auto menu = context_menu_->GetMenu();
menu->ClearItems();
if (items & ContextMenuItem::kSelectAll) {
diff --git a/src/ui/controls/Window.cpp b/src/ui/controls/Window.cpp
index b1881136..e6abc48d 100644
--- a/src/ui/controls/Window.cpp
+++ b/src/ui/controls/Window.cpp
@@ -1,7 +1,431 @@
#include "cru/ui/controls/Window.h"
+#include "cru/platform/gui/UiApplication.h"
+#include "cru/platform/gui/Window.h"
+#include "cru/ui/Base.h"
+
+#include <cassert>
+
namespace cru::ui::controls {
-Window::Window(Control* attached_control) : RootControl(attached_control) {}
+Window::Window()
+ : event_handling_count_(0),
+ native_window_(CreateNativeWindow()),
+ focus_control_(this),
+ mouse_hover_control_(nullptr),
+ mouse_captured_control_(nullptr),
+ layout_prefer_to_fill_window_(true),
+ attached_control_(nullptr) {
+ GetContainerRenderObject()->SetDefaultHorizontalAlignment(Alignment::Stretch);
+ GetContainerRenderObject()->SetDefaultVertialAlignment(Alignment::Stretch);
+}
Window::~Window() {}
+
+Window* Window::CreatePopup() {
+ auto window = new Window();
+ window->GetNativeWindow()->SetStyleFlag(
+ platform::gui::WindowStyleFlags::NoCaptionAndBorder);
+ window->SetGainFocusOnCreateAndDestroyWhenLoseFocus(true);
+ return window;
+}
+
+std::string Window::GetControlType() const { return std::string(kControlType); }
+
+void Window::SetAttachedControl(Control* control) {
+ attached_control_ = control;
+}
+
+platform::gui::INativeWindow* Window::GetNativeWindow() {
+ return native_window_.get();
+}
+
+void Window::SetGainFocusOnCreateAndDestroyWhenLoseFocus(bool value) {
+ gain_focus_on_create_and_destroy_when_lose_focus_event_guard_.Clear();
+ if (value) {
+ gain_focus_on_create_and_destroy_when_lose_focus_event_guard_ +=
+ native_window_->VisibilityChangeEvent()->AddHandler(
+ [this](platform::gui::WindowVisibilityType type) {
+ if (type == platform::gui::WindowVisibilityType::Show) {
+ native_window_->RequestFocus();
+ }
+ });
+
+ gain_focus_on_create_and_destroy_when_lose_focus_event_guard_ +=
+ native_window_->FocusEvent()->AddHandler(
+ [this](platform::gui::FocusChangeType type) {
+ if (type == platform::gui::FocusChangeType::Lose) {
+ native_window_->Close();
+ }
+ });
+ }
+}
+
+namespace {
+template <typename T>
+inline void BindNativeEvent(
+ Window* window, platform::gui::INativeWindow* native_window,
+ IEvent<T>* event,
+ void (Window::*handler)(platform::gui::INativeWindow*,
+ typename IEvent<T>::Args)) {
+ event->AddHandler(
+ std::bind(handler, window, native_window, std::placeholders::_1));
+}
+} // namespace
+
+namespace {
+bool IsAncestor(Control* control, Control* ancestor) {
+ while (control != nullptr) {
+ if (control == ancestor) return true;
+ control = control->GetParent();
+ }
+ return false;
+}
+
+// Ancestor at last.
+std::vector<Control*> GetAncestorList(Control* control) {
+ if (control == nullptr) return {};
+
+ std::vector<Control*> l;
+ while (control != nullptr) {
+ l.push_back(control);
+ control = control->GetParent();
+ }
+ return l;
+}
+
+Control* FindLowestCommonAncestor(Control* left, Control* right) {
+ if (left == nullptr || right == nullptr) return nullptr;
+
+ auto&& left_list = GetAncestorList(left);
+ auto&& right_list = GetAncestorList(right);
+
+ // the root is different
+ if (left_list.back() != right_list.back()) return nullptr;
+
+ // find the last same control or the last control (one is ancestor of the
+ // other)
+ auto left_iter = left_list.crbegin();
+ auto right_iter = right_list.crbegin();
+
+ while (true) {
+ if (left_iter == left_list.crend()) {
+ return left_list.front();
+ }
+ if (right_iter == right_list.crend()) {
+ return right_list.front();
+ }
+ if (*left_iter != *right_iter) {
+ return *(--left_iter);
+ }
+ ++left_iter;
+ ++right_iter;
+ }
+}
+} // namespace
+
+std::unique_ptr<platform::gui::INativeWindow> Window::CreateNativeWindow() {
+ const auto ui_application = platform::gui::IUiApplication::GetInstance();
+
+ auto native_window = ui_application->CreateWindow();
+ assert(native_window);
+
+ BindNativeEvent(this, native_window, native_window->DestroyEvent(),
+ &Window::OnNativeDestroy);
+ BindNativeEvent(this, native_window, native_window->PaintEvent(),
+ &Window::OnNativePaint);
+ BindNativeEvent(this, native_window, native_window->ResizeEvent(),
+ &Window::OnNativeResize);
+ BindNativeEvent(this, native_window, native_window->FocusEvent(),
+ &Window::OnNativeFocus);
+ BindNativeEvent(this, native_window, native_window->MouseEnterLeaveEvent(),
+ &Window::OnNativeMouseEnterLeave);
+ BindNativeEvent(this, native_window, native_window->MouseMoveEvent(),
+ &Window::OnNativeMouseMove);
+ BindNativeEvent(this, native_window, native_window->MouseDownEvent(),
+ &Window::OnNativeMouseDown);
+ BindNativeEvent(this, native_window, native_window->MouseUpEvent(),
+ &Window::OnNativeMouseUp);
+ BindNativeEvent(this, native_window, native_window->MouseWheelEvent(),
+ &Window::OnNativeMouseWheel);
+ BindNativeEvent(this, native_window, native_window->KeyDownEvent(),
+ &Window::OnNativeKeyDown);
+ BindNativeEvent(this, native_window, native_window->KeyUpEvent(),
+ &Window::OnNativeKeyUp);
+
+ return std::unique_ptr<platform::gui::INativeWindow>(native_window);
+}
+
+void Window::InvalidatePaint() {
+ repaint_schedule_canceler_.Reset(
+ platform::gui::IUiApplication::GetInstance()->SetImmediate(
+ [this] { Repaint(); }));
+}
+
+void Window::InvalidateLayout() {
+ relayout_schedule_canceler_.Reset(
+ platform::gui::IUiApplication::GetInstance()->SetImmediate(
+ [this] { Relayout(); }));
+}
+
+bool Window::IsLayoutPreferToFillWindow() const {
+ return layout_prefer_to_fill_window_;
+}
+
+void Window::SetLayoutPreferToFillWindow(bool value) {
+ if (value == layout_prefer_to_fill_window_) return;
+ layout_prefer_to_fill_window_ = value;
+ InvalidateLayout();
+}
+
+void Window::Repaint() {
+ auto painter = native_window_->BeginPaint();
+ painter->Clear(colors::white);
+ GetRenderObject()->Draw(painter.get());
+ painter->EndDraw();
+}
+
+void Window::Relayout() { RelayoutWithSize(native_window_->GetClientSize()); }
+
+void Window::RelayoutWithSize(const Size& available_size,
+ bool set_window_size_to_fit_content) {
+ auto render_object = GetRenderObject();
+ render_object->Measure(
+ render::MeasureRequirement{
+ available_size,
+ !set_window_size_to_fit_content && IsLayoutPreferToFillWindow()
+ ? render::MeasureSize(available_size)
+ : render::MeasureSize::NotSpecified()},
+ render::MeasureSize::NotSpecified());
+
+ if (set_window_size_to_fit_content) {
+ native_window_->SetClientSize(render_object->GetDesiredSize());
+ }
+
+ render_object->Layout(Point{});
+ CRU_LOG_TAG_DEBUG("A relayout is finished.");
+
+ AfterLayoutEvent_.Raise(nullptr);
+
+ InvalidatePaint();
+}
+
+Control* Window::GetFocusControl() { return focus_control_; }
+
+void Window::SetFocusControl(Control* control) {
+ if (control == nullptr) control = this;
+ if (focus_control_ == control) return;
+
+ const auto old_focus_control = focus_control_;
+
+ focus_control_ = control;
+
+ DispatchFocusControlChangeEvent(old_focus_control, focus_control_, false);
+}
+
+Control* Window::GetMouseCaptureControl() { return mouse_captured_control_; }
+
+bool Window::SetMouseCaptureControl(Control* control) {
+ if (!native_window_->CaptureMouse()) return false;
+
+ if (control == mouse_captured_control_) return true;
+
+ if (control == nullptr) {
+ native_window_->ReleaseMouse();
+ const auto old_capture_control = mouse_captured_control_;
+ mouse_captured_control_ =
+ nullptr; // update this in case this is used in event handlers
+ if (old_capture_control != mouse_hover_control_) {
+ DispatchMouseHoverControlChangeEvent(
+ old_capture_control, mouse_hover_control_,
+ native_window_->GetMousePosition(), true, false);
+ }
+ UpdateCursor();
+ return true;
+ }
+
+ if (mouse_captured_control_) return false;
+
+ mouse_captured_control_ = control;
+ DispatchMouseHoverControlChangeEvent(
+ mouse_hover_control_, mouse_captured_control_,
+ native_window_->GetMousePosition(), false, true);
+ UpdateCursor();
+ return true;
+}
+
+std::shared_ptr<platform::gui::ICursor> Window::GetOverrideCursor() {
+ return override_cursor_;
+}
+
+void Window::SetOverrideCursor(std::shared_ptr<platform::gui::ICursor> cursor) {
+ if (cursor == override_cursor_) return;
+ override_cursor_ = cursor;
+ UpdateCursor();
+}
+
+bool Window::IsInEventHandling() { return event_handling_count_; }
+
+void Window::UpdateCursor() {
+ if (override_cursor_) {
+ native_window_->SetCursor(override_cursor_);
+ } else {
+ const auto capture = GetMouseCaptureControl();
+ native_window_->SetCursor(
+ (capture ? capture : GetMouseHoverControl())->GetInheritedCursor());
+ }
+}
+
+void Window::OnNativeDestroy(platform::gui::INativeWindow* window,
+ std::nullptr_t) {
+ CRU_UNUSED(window)
+}
+
+void Window::OnNativePaint(platform::gui::INativeWindow* window,
+ std::nullptr_t) {
+ CRU_UNUSED(window)
+ InvalidatePaint();
+}
+
+void Window::OnNativeResize(platform::gui::INativeWindow* window,
+ const Size& size) {
+ CRU_UNUSED(window)
+ CRU_UNUSED(size)
+
+ InvalidateLayout();
+}
+
+void Window::OnNativeFocus(platform::gui::INativeWindow* window,
+ platform::gui::FocusChangeType focus) {
+ CRU_UNUSED(window)
+
+ focus == platform::gui::FocusChangeType::Gain
+ ? DispatchEvent(focus_control_, &Control::GainFocusEvent, nullptr, true)
+ : DispatchEvent(focus_control_, &Control::LoseFocusEvent, nullptr, true);
+}
+
+void Window::OnNativeMouseEnterLeave(platform::gui::INativeWindow* window,
+ platform::gui::MouseEnterLeaveType type) {
+ CRU_UNUSED(window)
+
+ if (type == platform::gui::MouseEnterLeaveType::Leave) {
+ DispatchEvent(mouse_hover_control_, &Control::MouseLeaveEvent, nullptr);
+ mouse_hover_control_ = nullptr;
+ }
+}
+
+void Window::OnNativeMouseMove(platform::gui::INativeWindow* window,
+ const Point& point) {
+ CRU_UNUSED(window)
+
+ // Find the first control that hit test succeed.
+ const auto new_mouse_hover_control = HitTest(point);
+ const auto old_mouse_hover_control = mouse_hover_control_;
+ mouse_hover_control_ = new_mouse_hover_control;
+
+ if (mouse_captured_control_) {
+ const auto n = FindLowestCommonAncestor(new_mouse_hover_control,
+ mouse_captured_control_);
+ const auto o = FindLowestCommonAncestor(old_mouse_hover_control,
+ mouse_captured_control_);
+ bool a = IsAncestor(o, n);
+ if (a) {
+ DispatchEvent(o, &Control::MouseLeaveEvent, n);
+ } else {
+ DispatchEvent(n, &Control::MouseEnterEvent, o, point);
+ }
+ DispatchEvent(mouse_captured_control_, &Control::MouseMoveEvent, nullptr,
+ point);
+ UpdateCursor();
+ return;
+ }
+
+ DispatchMouseHoverControlChangeEvent(
+ old_mouse_hover_control, new_mouse_hover_control, point, false, false);
+ DispatchEvent(new_mouse_hover_control, &Control::MouseMoveEvent, nullptr,
+ point);
+ UpdateCursor();
+}
+
+void Window::OnNativeMouseDown(
+ platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args) {
+ CRU_UNUSED(window)
+
+ Control* control =
+ mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
+ DispatchEvent(control, &Control::MouseDownEvent, nullptr, args.point,
+ args.button, args.modifier);
+}
+
+void Window::OnNativeMouseUp(
+ platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseButtonEventArgs& args) {
+ CRU_UNUSED(window)
+
+ Control* control =
+ mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
+ DispatchEvent(control, &Control::MouseUpEvent, nullptr, args.point,
+ args.button, args.modifier);
+}
+
+void Window::OnNativeMouseWheel(
+ platform::gui::INativeWindow* window,
+ const platform::gui::NativeMouseWheelEventArgs& args) {
+ CRU_UNUSED(window)
+
+ Control* control =
+ mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
+ DispatchEvent(control, &Control::MouseWheelEvent, nullptr, args.point,
+ args.delta, args.modifier);
+}
+
+void Window::OnNativeKeyDown(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args) {
+ CRU_UNUSED(window)
+
+ DispatchEvent(focus_control_, &Control::KeyDownEvent, nullptr, args.key,
+ args.modifier);
+}
+
+void Window::OnNativeKeyUp(platform::gui::INativeWindow* window,
+ const platform::gui::NativeKeyEventArgs& args) {
+ CRU_UNUSED(window)
+
+ DispatchEvent(focus_control_, &Control::KeyUpEvent, nullptr, args.key,
+ args.modifier);
+}
+
+void Window::DispatchFocusControlChangeEvent(Control* old_control,
+ Control* new_control,
+ bool is_window) {
+ if (new_control != old_control) {
+ const auto lowest_common_ancestor =
+ FindLowestCommonAncestor(old_control, new_control);
+ DispatchEvent(old_control, &Control::LoseFocusEvent, lowest_common_ancestor,
+ is_window);
+ DispatchEvent(new_control, &Control::GainFocusEvent, lowest_common_ancestor,
+ is_window);
+ }
+}
+
+void Window::DispatchMouseHoverControlChangeEvent(Control* old_control,
+ Control* new_control,
+ const Point& point,
+ bool no_leave,
+ bool no_enter) {
+ if (new_control != old_control) // if the mouse-hover-on control changed
+ {
+ const auto lowest_common_ancestor =
+ FindLowestCommonAncestor(old_control, new_control);
+ if (!no_leave && old_control != nullptr)
+ DispatchEvent(old_control, &Control::MouseLeaveEvent,
+ lowest_common_ancestor); // dispatch mouse leave event.
+ if (!no_enter && new_control != nullptr) {
+ DispatchEvent(new_control, &Control::MouseEnterEvent,
+ lowest_common_ancestor,
+ point); // dispatch mouse enter event.
+ }
+ }
+}
+
} // namespace cru::ui::controls
diff --git a/src/ui/events/MouseEventArgs.cpp b/src/ui/events/MouseEventArgs.cpp
index cad860d7..bbbf5cd4 100644
--- a/src/ui/events/MouseEventArgs.cpp
+++ b/src/ui/events/MouseEventArgs.cpp
@@ -1,7 +1,7 @@
#include "cru/ui/events/MouseEventArgs.h"
#include "cru/ui/controls/Control.h"
-#include "cru/ui/host/WindowHost.h"
+#include "cru/ui/controls/Window.h"
#include "cru/ui/render/RenderObject.h"
namespace cru::ui::events {
@@ -17,11 +17,9 @@ Point MouseEventArgs::GetPointToContent(
Point MouseEventArgs::GetPointOfScreen() const {
auto sender = GetSender();
if (auto control = dynamic_cast<controls::Control*>(sender)) {
- if (auto host = control->GetWindowHost())
- return GetPoint() + control->GetWindowHost()
- ->GetNativeWindow()
- ->GetClientRect()
- .GetLeftTop();
+ if (auto window = control->GetWindow())
+ return GetPoint() +
+ window->GetNativeWindow()->GetClientRect().GetLeftTop();
}
return GetPoint();
}
diff --git a/src/ui/helper/ClickDetector.cpp b/src/ui/helper/ClickDetector.cpp
index 796c3ad4..be1cbca2 100644
--- a/src/ui/helper/ClickDetector.cpp
+++ b/src/ui/helper/ClickDetector.cpp
@@ -3,16 +3,14 @@
#include "cru/base/log/Logger.h"
#include "cru/ui/DebugFlags.h"
#include "cru/ui/controls/Control.h"
-#include "cru/ui/host/WindowHost.h"
-
-#include <string>
+#include "cru/ui/controls/Window.h"
namespace cru::ui::helper {
Point ClickEventArgs::GetDownPointOfScreen() const {
- auto window_host = sender_->GetWindowHost();
- if (window_host != nullptr) {
- auto window = window_host->GetNativeWindow();
- return down_point_ + window->GetClientRect().GetLeftTop();
+ auto window = sender_->GetWindow();
+ if (window != nullptr) {
+ return down_point_ +
+ window->GetNativeWindow()->GetClientRect().GetLeftTop();
} else {
return down_point_;
}
diff --git a/src/ui/host/LayoutPaintCycler.cpp b/src/ui/host/LayoutPaintCycler.cpp
deleted file mode 100644
index 7f8523d4..00000000
--- a/src/ui/host/LayoutPaintCycler.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "cru/ui/host/LayoutPaintCycler.h"
-#include <chrono>
-
-#include "../Helper.h"
-#include "cru/ui/Base.h"
-#include "cru/ui/host/WindowHost.h"
-
-namespace cru::ui::host {
-LayoutPaintCycler::LayoutPaintCycler(WindowHost* host) : host_(host) {
- timer_canceler_ = GetUiApplication()->SetInterval(
- std::chrono::duration_cast<std::chrono::milliseconds>(
- this->cycle_threshold_),
- [this] { OnCycle(); });
-}
-
-LayoutPaintCycler::~LayoutPaintCycler() = default;
-
-void LayoutPaintCycler::InvalidateLayout() { layout_dirty_ = true; }
-
-void LayoutPaintCycler::InvalidatePaint() { paint_dirty_ = true; }
-
-void LayoutPaintCycler::OnCycle() {
- last_cycle_time_ = std::chrono::steady_clock::now();
- if (layout_dirty_) {
- host_->Relayout();
- host_->Repaint();
- } else {
- if (paint_dirty_) {
- host_->Repaint();
- }
- }
- layout_dirty_ = false;
- paint_dirty_ = false;
-}
-} // namespace cru::ui::host
diff --git a/src/ui/host/RoutedEventDispatch.h b/src/ui/host/RoutedEventDispatch.h
deleted file mode 100644
index 6e8b4af1..00000000
--- a/src/ui/host/RoutedEventDispatch.h
+++ /dev/null
@@ -1,122 +0,0 @@
-#pragma once
-#include "cru/base/log/Logger.h"
-#include "cru/ui/DebugFlags.h"
-#include "cru/ui/controls/Control.h"
-#include "cru/ui/host/WindowHost.h"
-
-#include <vector>
-
-namespace cru::ui::host {
-// Dispatch the event.
-//
-// This will raise routed event of the control and its parent and parent's
-// parent ... (until "last_receiver" if it's not nullptr) with appropriate args.
-//
-// First tunnel from top to bottom possibly stopped by "handled" flag in
-// EventArgs. Second bubble from bottom to top possibly stopped by "handled"
-// flag in EventArgs. Last direct to each control.
-//
-// Args is of type "EventArgs". The first init argument is "sender", which is
-// automatically bound to each receiving control. The second init argument is
-// "original_sender", which is unchanged. And "args" will be perfectly forwarded
-// as the rest arguments.
-template <typename EventArgs, typename... Args>
-void DispatchEvent(
- const std::string& event_name, controls::Control* const original_sender,
- events::RoutedEvent<EventArgs>* (controls::Control::*event_ptr)(),
- controls::Control* const last_receiver, Args&&... args) {
- constexpr auto kLogTag = "DispatchEvent";
-
- if (original_sender == nullptr) return;
-
- CRU_UNUSED(event_name)
-
- if (original_sender == last_receiver) {
- if constexpr (debug_flags::routed_event)
- CRU_LOG_TAG_DEBUG(
- "Routed event {} no need to dispatch (original_sender == "
- "last_receiver). Original sender is {}.",
- event_name, original_sender->GetControlType());
- return;
- }
-
- WindowHost::EnterEventHandling();
-
- std::vector<controls::Control*> receive_list;
-
- auto parent = original_sender;
- while (parent != last_receiver) {
- receive_list.push_back(parent);
- auto p = parent->GetParent();
- assert(!(p == nullptr && last_receiver != nullptr));
- parent = p;
- }
-
- if constexpr (debug_flags::routed_event) {
- std::string log = "Dispatch routed event ";
- log += event_name;
- log += ". Path (parent first): ";
- auto i = receive_list.crbegin();
- const auto end = --receive_list.crend();
- for (; i != end; ++i) {
- log += (*i)->GetControlType();
- log += " -> ";
- }
- log += (*i)->GetControlType();
- CRU_LOG_TAG_DEBUG("{}", log);
- }
-
- auto handled = false;
-
- int count = 0;
-
- // tunnel
- for (auto i = receive_list.crbegin(); i != receive_list.crend(); ++i) {
- count++;
- auto control = *i;
- EventArgs event_args(control, original_sender, std::forward<Args>(args)...);
- static_cast<Event<EventArgs&>*>((control->*event_ptr)()->Tunnel())
- ->Raise(event_args);
- if (event_args.IsHandled()) {
- handled = true;
- if constexpr (debug_flags::routed_event)
- CRU_LOG_TAG_DEBUG(
- "Routed event is short-circuit in TUNNEL at {}-st control (count "
- "from parent).",
- count);
- break;
- }
- }
-
- // bubble
- if (!handled) {
- for (auto control : receive_list) {
- count--;
- EventArgs event_args(control, original_sender,
- std::forward<Args>(args)...);
- static_cast<Event<EventArgs&>*>((control->*event_ptr)()->Bubble())
- ->Raise(event_args);
- if (event_args.IsHandled()) {
- if constexpr (debug_flags::routed_event)
- CRU_LOG_TAG_DEBUG(
- "Routed event is short-circuit in BUBBLE at {}-st control "
- "(count from parent).",
- count);
- break;
- }
- }
- }
-
- // direct
- for (auto control : receive_list) {
- EventArgs event_args(control, original_sender, std::forward<Args>(args)...);
- static_cast<Event<EventArgs&>*>((control->*event_ptr)()->Direct())
- ->Raise(event_args);
- }
-
- if constexpr (debug_flags::routed_event)
- CRU_LOG_TAG_DEBUG("Routed event dispatch finished.");
-
- WindowHost::LeaveEventHandling();
-}
-} // namespace cru::ui::host
diff --git a/src/ui/host/WindowHost.cpp b/src/ui/host/WindowHost.cpp
deleted file mode 100644
index ec956bb4..00000000
--- a/src/ui/host/WindowHost.cpp
+++ /dev/null
@@ -1,457 +0,0 @@
-#include "cru/ui/host/WindowHost.h"
-
-#include "RoutedEventDispatch.h"
-#include "cru/base/Base.h"
-#include "cru/base/log/Logger.h"
-#include "cru/platform/graphics/Painter.h"
-#include "cru/platform/gui/UiApplication.h"
-#include "cru/platform/gui/Window.h"
-#include "cru/ui/Base.h"
-#include "cru/ui/DebugFlags.h"
-#include "cru/ui/host/LayoutPaintCycler.h"
-#include "cru/ui/render/MeasureRequirement.h"
-#include "cru/ui/render/RenderObject.h"
-
-#include <cstddef>
-#include <memory>
-
-namespace cru::ui::host {
-using platform::gui::INativeWindow;
-using platform::gui::IUiApplication;
-
-namespace event_names {
-#define CRU_DEFINE_EVENT_NAME(name) constexpr const char* name = #name;
-
-CRU_DEFINE_EVENT_NAME(LoseFocus)
-CRU_DEFINE_EVENT_NAME(GainFocus)
-CRU_DEFINE_EVENT_NAME(MouseEnter)
-CRU_DEFINE_EVENT_NAME(MouseLeave)
-CRU_DEFINE_EVENT_NAME(MouseMove)
-CRU_DEFINE_EVENT_NAME(MouseDown)
-CRU_DEFINE_EVENT_NAME(MouseUp)
-CRU_DEFINE_EVENT_NAME(MouseWheel)
-CRU_DEFINE_EVENT_NAME(KeyDown)
-CRU_DEFINE_EVENT_NAME(KeyUp)
-
-#undef CRU_DEFINE_EVENT_NAME
-} // namespace event_names
-
-namespace {
-bool IsAncestor(controls::Control* control, controls::Control* ancestor) {
- while (control != nullptr) {
- if (control == ancestor) return true;
- control = control->GetParent();
- }
- return false;
-}
-
-// Ancestor at last.
-std::vector<controls::Control*> GetAncestorList(controls::Control* control) {
- if (control == nullptr) return {};
-
- std::vector<controls::Control*> l;
- while (control != nullptr) {
- l.push_back(control);
- control = control->GetParent();
- }
- return l;
-}
-
-controls::Control* FindLowestCommonAncestor(controls::Control* left,
- controls::Control* right) {
- if (left == nullptr || right == nullptr) return nullptr;
-
- auto&& left_list = GetAncestorList(left);
- auto&& right_list = GetAncestorList(right);
-
- // the root is different
- if (left_list.back() != right_list.back()) return nullptr;
-
- // find the last same control or the last control (one is ancestor of the
- // other)
- auto left_iter = left_list.crbegin();
- auto right_iter = right_list.crbegin();
-
- while (true) {
- if (left_iter == left_list.crend()) {
- return left_list.front();
- }
- if (right_iter == right_list.crend()) {
- return right_list.front();
- }
- if (*left_iter != *right_iter) {
- return *(--left_iter);
- }
- ++left_iter;
- ++right_iter;
- }
-}
-} // namespace
-
-namespace {
-template <typename T>
-inline void BindNativeEvent(
- WindowHost* host, INativeWindow* native_window, IEvent<T>* event,
- void (WindowHost::*handler)(INativeWindow*, typename IEvent<T>::Args),
- std::vector<EventHandlerRevokerGuard>& guard_pool) {
- guard_pool.push_back(EventHandlerRevokerGuard(event->AddHandler(
- std::bind(handler, host, native_window, std::placeholders::_1))));
-}
-} // namespace
-
-int WindowHost::event_handling_depth_ = 0;
-
-void WindowHost::EnterEventHandling() { event_handling_depth_++; }
-
-void WindowHost::LeaveEventHandling() {
- Expects(event_handling_depth_ > 0);
- event_handling_depth_--;
-}
-
-WindowHost::WindowHost(controls::Control* root_control)
- : root_control_(root_control), focus_control_(root_control) {
- root_render_object_ = root_control->GetRenderObject();
-
- this->layout_paint_cycler_ = std::make_unique<LayoutPaintCycler>(this);
-
- native_window_ = CreateNativeWindow();
-}
-
-WindowHost::~WindowHost() {}
-
-std::unique_ptr<platform::gui::INativeWindow> WindowHost::CreateNativeWindow() {
- const auto ui_application = IUiApplication::GetInstance();
-
- auto native_window = ui_application->CreateWindow();
- Ensures(native_window);
-
- BindNativeEvent(this, native_window, native_window->DestroyEvent(),
- &WindowHost::OnNativeDestroy, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->PaintEvent(),
- &WindowHost::OnNativePaint, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->ResizeEvent(),
- &WindowHost::OnNativeResize, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->FocusEvent(),
- &WindowHost::OnNativeFocus, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseEnterLeaveEvent(),
- &WindowHost::OnNativeMouseEnterLeave, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseMoveEvent(),
- &WindowHost::OnNativeMouseMove, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseDownEvent(),
- &WindowHost::OnNativeMouseDown, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseUpEvent(),
- &WindowHost::OnNativeMouseUp, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->MouseWheelEvent(),
- &WindowHost::OnNativeMouseWheel, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->KeyDownEvent(),
- &WindowHost::OnNativeKeyDown, event_revoker_guards_);
- BindNativeEvent(this, native_window, native_window->KeyUpEvent(),
- &WindowHost::OnNativeKeyUp, event_revoker_guards_);
-
- native_window_change_event_.Raise(native_window);
-
- return std::unique_ptr<INativeWindow>(native_window);
-}
-
-void WindowHost::InvalidatePaint() {
- if (layout_paint_cycler_) layout_paint_cycler_->InvalidatePaint();
-}
-
-void WindowHost::InvalidateLayout() {
- if (layout_paint_cycler_) layout_paint_cycler_->InvalidateLayout();
-}
-
-bool WindowHost::IsLayoutPreferToFillWindow() const {
- return layout_prefer_to_fill_window_;
-}
-
-void WindowHost::SetLayoutPreferToFillWindow(bool value) {
- if (value == layout_prefer_to_fill_window_) return;
- layout_prefer_to_fill_window_ = value;
- InvalidateLayout();
-}
-
-void WindowHost::Relayout() {
- const auto available_size =
- native_window_ ? native_window_->GetClientSize() : Size::Infinite();
- RelayoutWithSize(available_size);
-}
-
-void WindowHost::RelayoutWithSize(const Size& available_size,
- bool set_window_size_to_fit_content) {
- root_render_object_->Measure(
- render::MeasureRequirement{
- available_size,
- !set_window_size_to_fit_content && IsLayoutPreferToFillWindow()
- ? render::MeasureSize(available_size)
- : render::MeasureSize::NotSpecified()},
- render::MeasureSize::NotSpecified());
-
- if (set_window_size_to_fit_content) {
- native_window_->SetClientSize(root_render_object_->GetDesiredSize());
- }
-
- root_render_object_->Layout(Point{});
- for (auto& action : after_layout_stable_action_) action();
- after_layout_event_.Raise(AfterLayoutEventArgs{});
- after_layout_stable_action_.clear();
- if constexpr (debug_flags::layout)
- CRU_LOG_TAG_DEBUG("A relayout is finished.");
-}
-
-void WindowHost::Repaint() {
- if (native_window_ == nullptr) return;
- auto painter = native_window_->BeginPaint();
- painter->Clear(colors::white);
- root_render_object_->Draw(painter.get());
- painter->EndDraw();
-}
-
-controls::Control* WindowHost::GetFocusControl() { return focus_control_; }
-
-void WindowHost::SetFocusControl(controls::Control* control) {
- if (focus_control_ == control) return;
- if (control == nullptr) control = root_control_;
-
- const auto old_focus_control = focus_control_;
-
- focus_control_ = control;
-
- DispatchEvent(event_names::LoseFocus, old_focus_control,
- &controls::Control::LoseFocusEvent, nullptr, false);
-
- DispatchEvent(event_names::GainFocus, control,
- &controls::Control::GainFocusEvent, nullptr, false);
-}
-
-bool WindowHost::CaptureMouseFor(controls::Control* control) {
- if (!native_window_) return false;
- if (!native_window_->CaptureMouse()) return false;
-
- if (control == mouse_captured_control_) return true;
-
- if (control == nullptr) {
- native_window_->ReleaseMouse();
- const auto old_capture_control = mouse_captured_control_;
- mouse_captured_control_ =
- nullptr; // update this in case this is used in event handlers
- if (old_capture_control != mouse_hover_control_) {
- DispatchMouseHoverControlChangeEvent(
- old_capture_control, mouse_hover_control_,
- native_window_->GetMousePosition(), true, false);
- }
- UpdateCursor();
- return true;
- }
-
- if (mouse_captured_control_) return false;
-
- mouse_captured_control_ = control;
- DispatchMouseHoverControlChangeEvent(
- mouse_hover_control_, mouse_captured_control_,
- native_window_->GetMousePosition(), false, true);
- UpdateCursor();
- return true;
-}
-
-controls::Control* WindowHost::GetMouseCaptureControl() {
- return mouse_captured_control_;
-}
-
-void WindowHost::RunAfterLayoutStable(std::function<void()> action) {
- if (layout_paint_cycler_->IsLayoutDirty()) {
- after_layout_stable_action_.push_back(std::move(action));
- } else {
- action();
- }
-}
-
-void WindowHost::OnNativeDestroy(INativeWindow* window, std::nullptr_t) {
- CRU_UNUSED(window)
-}
-
-void WindowHost::OnNativePaint(INativeWindow* window, std::nullptr_t) {
- CRU_UNUSED(window)
- layout_paint_cycler_->InvalidatePaint();
-}
-
-void WindowHost::OnNativeResize(INativeWindow* window, const Size& size) {
- CRU_UNUSED(window)
- CRU_UNUSED(size)
-
- InvalidateLayout();
-}
-
-void WindowHost::OnNativeFocus(INativeWindow* window,
- platform::gui::FocusChangeType focus) {
- CRU_UNUSED(window)
-
- focus == platform::gui::FocusChangeType::Gain
- ? DispatchEvent(event_names::GainFocus, focus_control_,
- &controls::Control::GainFocusEvent, nullptr, true)
- : DispatchEvent(event_names::LoseFocus, focus_control_,
- &controls::Control::LoseFocusEvent, nullptr, true);
-}
-
-void WindowHost::OnNativeMouseEnterLeave(
- INativeWindow* window, platform::gui::MouseEnterLeaveType type) {
- CRU_UNUSED(window)
-
- if (type == platform::gui::MouseEnterLeaveType::Leave) {
- DispatchEvent(event_names::MouseLeave, mouse_hover_control_,
- &controls::Control::MouseLeaveEvent, nullptr);
- mouse_hover_control_ = nullptr;
- }
-}
-
-void WindowHost::OnNativeMouseMove(INativeWindow* window, const Point& point) {
- CRU_UNUSED(window)
-
- // Find the first control that hit test succeed.
- const auto new_mouse_hover_control = HitTest(point);
- const auto old_mouse_hover_control = mouse_hover_control_;
- mouse_hover_control_ = new_mouse_hover_control;
-
- if (mouse_captured_control_) {
- const auto n = FindLowestCommonAncestor(new_mouse_hover_control,
- mouse_captured_control_);
- const auto o = FindLowestCommonAncestor(old_mouse_hover_control,
- mouse_captured_control_);
- bool a = IsAncestor(o, n);
- if (a) {
- DispatchEvent(event_names::MouseLeave, o,
- &controls::Control::MouseLeaveEvent, n);
- } else {
- DispatchEvent(event_names::MouseEnter, n,
- &controls::Control::MouseEnterEvent, o, point);
- }
- DispatchEvent(event_names::MouseMove, mouse_captured_control_,
- &controls::Control::MouseMoveEvent, nullptr, point);
- UpdateCursor();
- return;
- }
-
- DispatchMouseHoverControlChangeEvent(
- old_mouse_hover_control, new_mouse_hover_control, point, false, false);
- DispatchEvent(event_names::MouseMove, new_mouse_hover_control,
- &controls::Control::MouseMoveEvent, nullptr, point);
- UpdateCursor();
-}
-
-void WindowHost::OnNativeMouseDown(
- INativeWindow* window,
- const platform::gui::NativeMouseButtonEventArgs& args) {
- CRU_UNUSED(window)
-
- controls::Control* control =
- mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
- DispatchEvent(event_names::MouseDown, control,
- &controls::Control::MouseDownEvent, nullptr, args.point,
- args.button, args.modifier);
-}
-
-void WindowHost::OnNativeMouseUp(
- INativeWindow* window,
- const platform::gui::NativeMouseButtonEventArgs& args) {
- CRU_UNUSED(window)
-
- controls::Control* control =
- mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
- DispatchEvent(event_names::MouseUp, control, &controls::Control::MouseUpEvent,
- nullptr, args.point, args.button, args.modifier);
-}
-
-void WindowHost::OnNativeMouseWheel(
- platform::gui::INativeWindow* window,
- const platform::gui::NativeMouseWheelEventArgs& args) {
- CRU_UNUSED(window)
-
- controls::Control* control =
- mouse_captured_control_ ? mouse_captured_control_ : HitTest(args.point);
- DispatchEvent(event_names::MouseWheel, control,
- &controls::Control::MouseWheelEvent, nullptr, args.point,
- args.delta, args.modifier);
-}
-
-void WindowHost::OnNativeKeyDown(
- INativeWindow* window, const platform::gui::NativeKeyEventArgs& args) {
- CRU_UNUSED(window)
-
- DispatchEvent(event_names::KeyDown, focus_control_,
- &controls::Control::KeyDownEvent, nullptr, args.key,
- args.modifier);
-}
-
-void WindowHost::OnNativeKeyUp(INativeWindow* window,
- const platform::gui::NativeKeyEventArgs& args) {
- CRU_UNUSED(window)
-
- DispatchEvent(event_names::KeyUp, focus_control_,
- &controls::Control::KeyUpEvent, nullptr, args.key,
- args.modifier);
-}
-
-void WindowHost::DispatchMouseHoverControlChangeEvent(
- controls::Control* old_control, controls::Control* new_control,
- const Point& point, bool no_leave, bool no_enter) {
- if (new_control != old_control) // if the mouse-hover-on control changed
- {
- const auto lowest_common_ancestor =
- FindLowestCommonAncestor(old_control, new_control);
- if (!no_leave && old_control != nullptr)
- DispatchEvent(event_names::MouseLeave, old_control,
- &controls::Control::MouseLeaveEvent,
- lowest_common_ancestor); // dispatch mouse leave event.
- if (!no_enter && new_control != nullptr) {
- DispatchEvent(event_names::MouseEnter, new_control,
- &controls::Control::MouseEnterEvent, lowest_common_ancestor,
- point); // dispatch mouse enter event.
- }
- }
-}
-
-void WindowHost::UpdateCursor() {
- if (native_window_) {
- if (override_cursor_) {
- native_window_->SetCursor(override_cursor_);
- } else {
- const auto capture = GetMouseCaptureControl();
- native_window_->SetCursor(
- (capture ? capture : GetMouseHoverControl())->GetInheritedCursor());
- }
- }
-}
-
-controls::Control* WindowHost::HitTest(const Point& point) {
- const auto render_object = root_render_object_->HitTest(point);
- if (render_object) {
- const auto control = render_object->GetAttachedControl();
- Ensures(control);
- return control;
- }
- return root_control_;
-}
-
-std::shared_ptr<platform::gui::ICursor> WindowHost::GetOverrideCursor() {
- return override_cursor_;
-}
-
-void WindowHost::SetOverrideCursor(
- std::shared_ptr<platform::gui::ICursor> cursor) {
- if (cursor == override_cursor_) return;
- override_cursor_ = cursor;
- UpdateCursor();
-}
-
-void WindowHost::OnControlDetach(controls::Control* control) {
- if (GetFocusControl() == control) {
- SetFocusControl(nullptr);
- }
- if (GetMouseCaptureControl() == control) {
- CaptureMouseFor(nullptr);
- }
- if (GetMouseHoverControl() == control) {
- mouse_hover_control_ = HitTest(GetNativeWindow()->GetMousePosition());
- }
-}
-} // namespace cru::ui::host
diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp
index fcd44143..a4da2414 100644
--- a/src/ui/render/RenderObject.cpp
+++ b/src/ui/render/RenderObject.cpp
@@ -4,7 +4,7 @@
#include "cru/platform/GraphicsBase.h"
#include "cru/ui/DebugFlags.h"
#include "cru/ui/controls/Control.h"
-#include "cru/ui/host/WindowHost.h"
+#include "cru/ui/controls/Window.h"
namespace cru::ui::render {
const BoxConstraint BoxConstraint::kNotLimit{Size::kMax, Size::kZero};
@@ -265,22 +265,22 @@ Rect RenderObject::GetContentRect() const {
return rect;
}
-host::WindowHost* RenderObject::GetWindowHost() {
+controls::Window* RenderObject::GetWindow() {
if (control_) {
- return control_->GetWindowHost();
+ return control_->GetWindow();
}
return nullptr;
}
void RenderObject::InvalidateLayout() {
- if (auto window_host = GetWindowHost()) {
- window_host->InvalidateLayout();
+ if (auto window = GetWindow()) {
+ window->InvalidateLayout();
}
}
void RenderObject::InvalidatePaint() {
- if (auto window_host = GetWindowHost()) {
- window_host->InvalidatePaint();
+ if (auto window = GetWindow()) {
+ window->InvalidatePaint();
}
}
diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp
index 804395e4..3834fffb 100644
--- a/src/ui/render/ScrollBar.cpp
+++ b/src/ui/render/ScrollBar.cpp
@@ -8,7 +8,7 @@
#include "cru/platform/gui/Cursor.h"
#include "cru/ui/Base.h"
#include "cru/ui/ThemeManager.h"
-#include "cru/ui/host/WindowHost.h"
+#include "cru/ui/controls/Window.h"
#include "cru/ui/render/ScrollRenderObject.h"
#include <algorithm>
@@ -317,23 +317,23 @@ void ScrollBar::OnDraw(platform::graphics::IPainter* painter,
void ScrollBar::SetCursor() {
if (const auto control = render_object_->GetAttachedControl()) {
- if (const auto window_host = control->GetWindowHost()) {
- window_host->SetOverrideCursor(
+ if (const auto window = control->GetWindow()) {
+ window->SetOverrideCursor(
GetUiApplication()->GetCursorManager()->GetSystemCursor(
platform::gui::SystemCursorType::Arrow));
- cursor_overrided_ = true;
+ cursor_overridden_ = true;
}
}
}
void ScrollBar::RestoreCursor() {
- if (cursor_overrided_) {
+ if (cursor_overridden_) {
if (const auto control = render_object_->GetAttachedControl()) {
- if (const auto window_host = control->GetWindowHost()) {
- window_host->SetOverrideCursor(nullptr);
+ if (const auto window = control->GetWindow()) {
+ window->SetOverrideCursor(nullptr);
}
}
- cursor_overrided_ = false;
+ cursor_overridden_ = false;
}
}