aboutsummaryrefslogtreecommitdiff
path: root/include/cru/ui/controls
diff options
context:
space:
mode:
Diffstat (limited to 'include/cru/ui/controls')
-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
5 files changed, 198 insertions, 131 deletions
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