diff options
Diffstat (limited to 'src/ui/controls')
| -rw-r--r-- | src/ui/controls/Control.cpp | 120 | ||||
| -rw-r--r-- | src/ui/controls/Popup.cpp | 18 | ||||
| -rw-r--r-- | src/ui/controls/RootControl.cpp | 49 | ||||
| -rw-r--r-- | src/ui/controls/TextHostControlService.cpp | 31 | ||||
| -rw-r--r-- | src/ui/controls/Window.cpp | 426 |
5 files changed, 500 insertions, 144 deletions
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 |
