aboutsummaryrefslogtreecommitdiff
path: root/src/ui/host
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/host')
-rw-r--r--src/ui/host/LayoutPaintCycler.cpp35
-rw-r--r--src/ui/host/RoutedEventDispatch.h128
-rw-r--r--src/ui/host/WindowHost.cpp458
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