diff options
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/ui/control.cpp | 73 | ||||
-rw-r--r-- | src/ui/controls/button.cpp | 14 | ||||
-rw-r--r-- | src/ui/render/border_render_object.cpp | 27 | ||||
-rw-r--r-- | src/ui/routed_event_dispatch.hpp | 60 | ||||
-rw-r--r-- | src/ui/window.cpp | 56 |
6 files changed, 161 insertions, 71 deletions
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 6d6fc02d..62ae1775 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -1,6 +1,8 @@ file(GLOB CRU_UI_INCLUDE_DIR ${CRU_INCLUDE_DIR}/cru/ui) add_library(cru_ui STATIC + routed_event_dispatch.hpp + content_control.cpp control.cpp layout_control.cpp diff --git a/src/ui/control.cpp b/src/ui/control.cpp index 7563dc91..51e1937a 100644 --- a/src/ui/control.cpp +++ b/src/ui/control.cpp @@ -1,6 +1,10 @@ #include "cru/ui/control.hpp" +#include "cru/platform/native/basic_types.hpp" +#include "cru/ui/base.hpp" +#include "cru/ui/event/ui_event.hpp" #include "cru/ui/window.hpp" +#include "routed_event_dispatch.hpp" #include <cassert> @@ -10,7 +14,69 @@ void Control::_SetParent(Control* parent) { parent_ = parent; const auto new_parent = GetParent(); if (old_parent != new_parent) OnParentChanged(old_parent, new_parent); -} + + MouseDownEvent()->direct->AddHandler( + [this](event::MouseButtonEventArgs& args) { + switch (args.GetMouseButton()) { + case MouseButton::Left: + click_map_.left = true; + OnMouseClickBegin(MouseButton::Left); + break; + case MouseButton::Middle: + click_map_.middle = true; + OnMouseClickBegin(MouseButton::Middle); + break; + case MouseButton::Right: + click_map_.right = true; + OnMouseClickBegin(MouseButton::Right); + break; + } + }); + + MouseEnterEvent()->direct->AddHandler([this](event::MouseEventArgs&) { + this->is_mouse_over_ = true; + }); + + MouseLeaveEvent()->direct->AddHandler([this](event::MouseEventArgs&) { + this->is_mouse_over_ = false; + if (click_map_.left) { + OnMouseClickCancel(MouseButton::Left); + } + if (click_map_.middle) { + OnMouseClickCancel(MouseButton::Middle); + } + if (click_map_.right) { + OnMouseClickCancel(MouseButton::Right); + } + click_map_.left = click_map_.middle = click_map_.right = false; + }); + + MouseUpEvent()->direct->AddHandler([this](event::MouseButtonEventArgs& args) { + switch (args.GetMouseButton()) { + case MouseButton::Left: + if (click_map_.left) { + click_map_.left = false; + OnMouseClickEnd(MouseButton::Left); + DispatchEvent(this, &Control::MouseClickEvent, nullptr, args.GetPoint(), args.GetMouseButton()); + } + break; + case MouseButton::Middle: + if (click_map_.middle) { + click_map_.middle = false; + OnMouseClickEnd(MouseButton::Middle); + DispatchEvent(this, &Control::MouseClickEvent, nullptr, args.GetPoint(), args.GetMouseButton()); + } + break; + case MouseButton::Right: + if (click_map_.right) { + click_map_.right = false; + OnMouseClickEnd(MouseButton::Right); + DispatchEvent(this, &Control::MouseClickEvent, nullptr, args.GetPoint(), args.GetMouseButton()); + } + break; + } + }); +} // namespace cru::ui void Control::_SetDescendantWindow(Window* window) { if (window == nullptr && window_ == nullptr) return; @@ -40,8 +106,7 @@ void Control::TraverseDescendants( void Control::_TraverseDescendants( Control* control, const std::function<void(Control*)>& predicate) { predicate(control); - for (auto c : control->GetChildren()) - _TraverseDescendants(c, predicate); + for (auto c : control->GetChildren()) _TraverseDescendants(c, predicate); } bool Control::RequestFocus() { auto window = GetWindow(); @@ -66,4 +131,6 @@ void Control::OnDetachToWindow(Window* window) {} void Control::OnMouseClickBegin(platform::native::MouseButton button) {} void Control::OnMouseClickEnd(platform::native::MouseButton button) {} + +void Control::OnMouseClickCancel(platform::native::MouseButton button) {} } // namespace cru::ui diff --git a/src/ui/controls/button.cpp b/src/ui/controls/button.cpp index 0ac65d26..2228343c 100644 --- a/src/ui/controls/button.cpp +++ b/src/ui/controls/button.cpp @@ -23,4 +23,18 @@ void Button::OnChildChanged(Control* old_child, Control* new_child) { if (new_child != nullptr) render_object_->AddChild(new_child->GetRenderObject(), 0); } + +void Button::OnMouseClickBegin(platform::native::MouseButton button) { + if (button & trigger_button_) { + SetState(ButtonState::Press); + //TODO! + } +} + +void Button::OnMouseClickEnd(platform::native::MouseButton button) { + if (button & trigger_button_) { + SetState(ButtonState::Normal); + //TODO! + } +} } // namespace cru::ui::controls diff --git a/src/ui/render/border_render_object.cpp b/src/ui/render/border_render_object.cpp index 16d2f1ea..1bec3e0b 100644 --- a/src/ui/render/border_render_object.cpp +++ b/src/ui/render/border_render_object.cpp @@ -12,12 +12,12 @@ namespace cru::ui::render { BorderRenderObject::BorderRenderObject( std::shared_ptr<platform::graph::Brush> brush) { assert(brush); - this->border_brush_ = std::move(brush); + this->style_.brush = std::move(brush); RecreateGeometry(); } void BorderRenderObject::Draw(platform::graph::Painter* painter) { - painter->FillGeometry(geometry_.get(), border_brush_.get()); + painter->FillGeometry(geometry_.get(), style_.brush.get()); if (const auto child = GetChild()) { auto offset = child->GetOffset(); platform::graph::util::WithTransform( @@ -69,8 +69,8 @@ void BorderRenderObject::OnMeasureCore(const Size& available_size) { margin.GetVerticalTotal() + padding.GetVerticalTotal()}; if (is_enabled_) { - margin_border_padding_size.width += border_thickness_.GetHorizontalTotal(); - margin_border_padding_size.height += border_thickness_.GetVerticalTotal(); + margin_border_padding_size.width += style_.thickness.GetHorizontalTotal(); + margin_border_padding_size.height += style_.thickness.GetVerticalTotal(); } auto coerced_margin_border_padding_size = margin_border_padding_size; @@ -104,8 +104,8 @@ void BorderRenderObject::OnLayoutCore(const Rect& rect) { margin.GetVerticalTotal() + padding.GetVerticalTotal()}; if (is_enabled_) { - margin_border_padding_size.width += border_thickness_.GetHorizontalTotal(); - margin_border_padding_size.height += border_thickness_.GetVerticalTotal(); + margin_border_padding_size.width += style_.thickness.GetHorizontalTotal(); + margin_border_padding_size.height += style_.thickness.GetVerticalTotal(); } const auto content_available_size = @@ -121,14 +121,13 @@ void BorderRenderObject::OnLayoutCore(const Rect& rect) { if (coerced_content_available_size.height < 0) { platform::DebugMessage( L"Layout: vertical length of padding, border and margin is bigger " - L"than " - L"available length."); + L"than available length."); coerced_content_available_size.height = 0; } OnLayoutContent(Rect{ - margin.left + (is_enabled_ ? border_thickness_.left : 0) + padding.left, - margin.top + (is_enabled_ ? border_thickness_.top : 0) + padding.top, + margin.left + (is_enabled_ ? style_.thickness.left : 0) + padding.left, + margin.top + (is_enabled_ ? style_.thickness.top : 0) + padding.top, coerced_content_available_size.width, coerced_content_available_size.height}); } @@ -184,14 +183,14 @@ void BorderRenderObject::RecreateGeometry() { const auto graph_factory = platform::graph::GraphFactory::GetInstance(); std::unique_ptr<platform::graph::GeometryBuilder> builder{ graph_factory->CreateGeometryBuilder()}; - f(builder.get(), outer_rect, corner_radius_); + f(builder.get(), outer_rect, style_.corner_radius); border_outer_geometry_.reset(builder->Build()); builder.reset(); - const Rect inner_rect = outer_rect.Shrink(border_thickness_); + const Rect inner_rect = outer_rect.Shrink(style_.thickness); builder.reset(graph_factory->CreateGeometryBuilder()); - f(builder.get(), outer_rect, corner_radius_); - f(builder.get(), inner_rect, corner_radius_); + f(builder.get(), outer_rect, style_.corner_radius); + f(builder.get(), inner_rect, style_.corner_radius); geometry_.reset(builder->Build()); builder.reset(); } diff --git a/src/ui/routed_event_dispatch.hpp b/src/ui/routed_event_dispatch.hpp new file mode 100644 index 00000000..3427441b --- /dev/null +++ b/src/ui/routed_event_dispatch.hpp @@ -0,0 +1,60 @@ +#pragma once +#include "cru/ui/control.hpp" + +namespace cru::ui { +// 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(Control* const original_sender, + event::RoutedEvent<EventArgs>* (Control::*event_ptr)(), + Control* const last_receiver, Args&&... args) { + std::list<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) { + EventArgs event_args(*i, original_sender, std::forward<Args>(args)...); + static_cast<Event<EventArgs>*>(((*i)->*event_ptr)()->tunnel.get()) + ->Raise(event_args); + if (event_args.IsHandled()) { + handled = true; + break; + } + } + + // bubble + if (!handled) { + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward<Args>(args)...); + static_cast<Event<EventArgs>*>((i->*event_ptr)()->bubble.get()) + ->Raise(event_args); + if (event_args.IsHandled()) break; + } + } + + // direct + for (auto i : receive_list) { + EventArgs event_args(i, original_sender, std::forward<Args>(args)...); + static_cast<Event<EventArgs>*>((i->*event_ptr)()->direct.get()) + ->Raise(event_args); + } +} +} // namespace cru::ui diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 9eabc4a6..e17d603c 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -4,64 +4,12 @@ #include "cru/platform/native/native_window.hpp" #include "cru/platform/native/ui_applicaition.hpp" #include "cru/ui/render/window_render_object.hpp" +#include "routed_event_dispatch.hpp" #include <cassert> namespace cru::ui { namespace { -// 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(Control* const original_sender, - event::RoutedEvent<EventArgs>* (Control::*event_ptr)(), - Control* const last_receiver, Args&&... args) { - std::list<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) { - EventArgs event_args(*i, original_sender, std::forward<Args>(args)...); - (*i->*event_ptr)()->tunnel.Raise(event_args); - if (event_args.IsHandled()) { - handled = true; - break; - } - } - - // bubble - if (!handled) { - for (auto i : receive_list) { - EventArgs event_args(i, original_sender, std::forward<Args>(args)...); - (i->*event_ptr)()->bubble.Raise(event_args); - if (event_args.IsHandled()) break; - } - } - - // direct - for (auto i : receive_list) { - EventArgs event_args(i, original_sender, std::forward<Args>(args)...); - (i->*event_ptr)()->direct.Raise(event_args); - } -} - std::list<Control*> GetAncestorList(Control* control) { std::list<Control*> l; while (control != nullptr) { @@ -157,7 +105,7 @@ void Window::InvalidateLayout() { window->Relayout(); window->need_layout_ = false; } - }); + }); need_layout_ = true; } } |