diff options
Diffstat (limited to 'src/ui/host')
| -rw-r--r-- | src/ui/host/LayoutPaintCycler.cpp | 35 | ||||
| -rw-r--r-- | src/ui/host/RoutedEventDispatch.h | 128 | ||||
| -rw-r--r-- | src/ui/host/WindowHost.cpp | 458 |
3 files changed, 0 insertions, 621 deletions
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 0729d176..00000000 --- a/src/ui/host/RoutedEventDispatch.h +++ /dev/null @@ -1,128 +0,0 @@ -#pragma once -#include "cru/base/SelfResolvable.h" -#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<ObjectResolver<controls::Control>> receive_list; - - auto parent = original_sender; - while (parent != last_receiver) { - receive_list.push_back(parent->CreateResolver()); - 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->Resolve()->GetControlType(); - log += " -> "; - } - log += i->Resolve()->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->Resolve(); - if (!control) continue; - 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 (const auto& resolver : receive_list) { - count--; - auto control = resolver.Resolve(); - if (!control) continue; - 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 resolver : receive_list) { - auto control = resolver.Resolve(); - if (!control) continue; - 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 7417740d..00000000 --- a/src/ui/host/WindowHost.cpp +++ /dev/null @@ -1,458 +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/InputMethod.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 |
