aboutsummaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/control.cpp379
-rw-r--r--src/ui/control.hpp179
-rw-r--r--src/ui/controls/list_item.cpp65
-rw-r--r--src/ui/controls/list_item.hpp7
-rw-r--r--src/ui/controls/popup_menu.cpp4
-rw-r--r--src/ui/controls/scroll_control.cpp245
-rw-r--r--src/ui/controls/scroll_control.hpp8
-rw-r--r--src/ui/controls/text_box.cpp220
-rw-r--r--src/ui/controls/text_box.hpp8
-rw-r--r--src/ui/controls/text_control.cpp208
-rw-r--r--src/ui/controls/text_control.hpp13
-rw-r--r--src/ui/controls/toggle_button.cpp74
-rw-r--r--src/ui/controls/toggle_button.hpp13
-rw-r--r--src/ui/events/ui_event.hpp45
-rw-r--r--src/ui/window.cpp30
-rw-r--r--src/ui/window.hpp33
16 files changed, 616 insertions, 915 deletions
diff --git a/src/ui/control.cpp b/src/ui/control.cpp
index 2a81427a..0fec1ef8 100644
--- a/src/ui/control.cpp
+++ b/src/ui/control.cpp
@@ -16,12 +16,52 @@
namespace cru::ui
{
- using namespace events;
-
Control::Control(const bool container) :
is_container_(container)
{
+ mouse_leave_event.bubble.AddHandler([this](events::MouseEventArgs& args)
+ {
+ if (args.GetOriginalSender() != this)
+ return;
+ for (auto& is_mouse_click_valid : is_mouse_click_valid_map_)
+ {
+ if (is_mouse_click_valid.second)
+ {
+ is_mouse_click_valid.second = false;
+ OnMouseClickEnd(is_mouse_click_valid.first);
+ }
+ }
+ });
+
+ mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (args.GetOriginalSender() != this)
+ return;
+ if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender())
+ RequestFocus();
+ const auto button = args.GetMouseButton();
+ is_mouse_click_valid_map_[button] = true;
+ OnMouseClickBegin(button);
+ });
+
+ mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (args.GetOriginalSender() != this)
+ return;
+
+ const auto button = args.GetMouseButton();
+ if (is_mouse_click_valid_map_[button])
+ {
+ is_mouse_click_valid_map_[button] = false;
+ OnMouseClickEnd(button);
+ const auto point = args.GetPoint(GetWindow());
+ InvokeLater([this, button, point]
+ {
+ DispatchEvent(this, &Control::mouse_click_event, nullptr, point, button);
+ });
+ }
+ });
}
Control::Control(WindowConstructorTag, Window* window) : Control(true)
@@ -163,12 +203,41 @@ namespace cru::ui
return size_;
}
+ namespace
+ {
+#ifdef CRU_DEBUG_LAYOUT
+ Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in)
+ {
+ const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory();
+ Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry;
+ ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry));
+ Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry;
+ ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry));
+ Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry;
+ ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry));
+ Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink;
+ ThrowIfFailed(result_geometry->Open(&sink));
+ ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get()));
+ ThrowIfFailed(sink->Close());
+ return result_geometry;
+ }
+#endif
+ }
+
void Control::SetSize(const Size & size)
{
const auto old_size = size_;
size_ = size;
- SizeChangedEventArgs args(this, this, old_size, size);
- RaiseSizeChangedEvent(args);
+ events::SizeChangedEventArgs args(this, this, old_size, size);
+ size_changed_event.Raise(args);
+
+ RegenerateGeometryInfo();
+
+#ifdef CRU_DEBUG_LAYOUT
+ margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder));
+ padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content));
+#endif
+
if (auto window = GetWindow())
window->InvalidateDraw();
}
@@ -476,8 +545,7 @@ namespace cru::ui
graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top),
[this](ID2D1DeviceContext* device_context)
{
- OnDrawBackground(device_context);
- DrawEventArgs args(this, this, device_context);
+ events::DrawEventArgs args(this, this, device_context);
draw_background_event.Raise(args);
});
@@ -486,8 +554,7 @@ namespace cru::ui
graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(rect.left, rect.top),
[this](ID2D1DeviceContext* device_context)
{
- OnDrawContent(device_context);
- DrawEventArgs args(this, this, device_context);
+ events::DrawEventArgs args(this, this, device_context);
draw_content_event.Raise(args);
});
@@ -498,85 +565,11 @@ namespace cru::ui
graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(padding_rect.left, padding_rect.top),
[this](ID2D1DeviceContext* device_context)
{
- OnDrawForeground(device_context);
- DrawEventArgs args(this, this, device_context);
+ events::DrawEventArgs args(this, this, device_context);
draw_foreground_event.Raise(args);
});
}
- void Control::OnDrawContent(ID2D1DeviceContext * device_context)
- {
-
- }
-
- void Control::OnDrawForeground(ID2D1DeviceContext* device_context)
- {
-
- }
-
- void Control::OnDrawBackground(ID2D1DeviceContext* device_context)
- {
-
- }
-
- void Control::OnPositionChanged(PositionChangedEventArgs & args)
- {
-
- }
-
- void Control::OnSizeChanged(SizeChangedEventArgs & args)
- {
- }
-
- void Control::OnPositionChangedCore(PositionChangedEventArgs & args)
- {
-
- }
-
- namespace
- {
-#ifdef CRU_DEBUG_LAYOUT
- Microsoft::WRL::ComPtr<ID2D1Geometry> CalculateSquareRingGeometry(const Rect& out, const Rect& in)
- {
- const auto d2d1_factory = graph::GraphManager::GetInstance()->GetD2D1Factory();
- Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> out_geometry;
- ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(out), &out_geometry));
- Microsoft::WRL::ComPtr<ID2D1RectangleGeometry> in_geometry;
- ThrowIfFailed(d2d1_factory->CreateRectangleGeometry(Convert(in), &in_geometry));
- Microsoft::WRL::ComPtr<ID2D1PathGeometry> result_geometry;
- ThrowIfFailed(d2d1_factory->CreatePathGeometry(&result_geometry));
- Microsoft::WRL::ComPtr<ID2D1GeometrySink> sink;
- ThrowIfFailed(result_geometry->Open(&sink));
- ThrowIfFailed(out_geometry->CombineWithGeometry(in_geometry.Get(), D2D1_COMBINE_MODE_EXCLUDE, D2D1::Matrix3x2F::Identity(), sink.Get()));
- ThrowIfFailed(sink->Close());
- return result_geometry;
- }
-#endif
- }
-
- void Control::OnSizeChangedCore(SizeChangedEventArgs & args)
- {
- RegenerateGeometryInfo();
-#ifdef CRU_DEBUG_LAYOUT
- margin_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Margin), GetRect(RectRange::FullBorder));
- padding_geometry_ = CalculateSquareRingGeometry(GetRect(RectRange::Padding), GetRect(RectRange::Content));
-#endif
- }
-
- void Control::RaisePositionChangedEvent(PositionChangedEventArgs& args)
- {
- OnPositionChangedCore(args);
- OnPositionChanged(args);
- position_changed_event.Raise(args);
- }
-
- void Control::RaiseSizeChangedEvent(SizeChangedEventArgs& args)
- {
- OnSizeChangedCore(args);
- OnSizeChanged(args);
- size_changed_event.Raise(args);
- }
-
void Control::RegenerateGeometryInfo()
{
if (IsBordered())
@@ -638,223 +631,12 @@ namespace cru::ui
}
}
- void Control::OnMouseEnter(MouseEventArgs & args)
- {
- }
-
- void Control::OnMouseLeave(MouseEventArgs & args)
- {
- }
-
- void Control::OnMouseMove(MouseEventArgs & args)
- {
- }
-
- void Control::OnMouseDown(MouseButtonEventArgs & args)
- {
- }
-
- void Control::OnMouseUp(MouseButtonEventArgs & args)
- {
- }
-
- void Control::OnMouseClick(MouseButtonEventArgs& args)
- {
-
- }
-
- void Control::OnMouseEnterCore(MouseEventArgs & args)
- {
- is_mouse_inside_ = true;
- }
-
- void Control::OnMouseLeaveCore(MouseEventArgs & args)
- {
- is_mouse_inside_ = false;
- for (auto& is_mouse_click_valid : is_mouse_click_valid_map_)
- {
- if (is_mouse_click_valid.second)
- {
- is_mouse_click_valid.second = false;
- OnMouseClickEnd(is_mouse_click_valid.first);
- }
- }
- }
-
- void Control::OnMouseMoveCore(MouseEventArgs & args)
- {
-
- }
-
- void Control::OnMouseDownCore(MouseButtonEventArgs & args)
- {
- if (is_focus_on_pressed_ && args.GetSender() == args.GetOriginalSender())
- RequestFocus();
- is_mouse_click_valid_map_[args.GetMouseButton()] = true;
- OnMouseClickBegin(args.GetMouseButton());
- }
-
- void Control::OnMouseUpCore(MouseButtonEventArgs & args)
- {
- if (is_mouse_click_valid_map_[args.GetMouseButton()])
- {
- is_mouse_click_valid_map_[args.GetMouseButton()] = false;
- RaiseMouseClickEvent(args);
- OnMouseClickEnd(args.GetMouseButton());
- }
- }
-
- void Control::OnMouseClickCore(MouseButtonEventArgs& args)
- {
-
- }
-
- void Control::OnMouseWheel(events::MouseWheelEventArgs& args)
- {
-
- }
-
- void Control::OnMouseWheelCore(events::MouseWheelEventArgs& args)
- {
-
- }
-
- void Control::RaiseMouseEnterEvent(MouseEventArgs& args)
- {
- OnMouseEnterCore(args);
- OnMouseEnter(args);
- mouse_enter_event.Raise(args);
- }
-
- void Control::RaiseMouseLeaveEvent(MouseEventArgs& args)
- {
- OnMouseLeaveCore(args);
- OnMouseLeave(args);
- mouse_leave_event.Raise(args);
- }
-
- void Control::RaiseMouseMoveEvent(MouseEventArgs& args)
- {
- OnMouseMoveCore(args);
- OnMouseMove(args);
- mouse_move_event.Raise(args);
- }
-
- void Control::RaiseMouseDownEvent(MouseButtonEventArgs& args)
- {
- OnMouseDownCore(args);
- OnMouseDown(args);
- mouse_down_event.Raise(args);
- }
-
- void Control::RaiseMouseUpEvent(MouseButtonEventArgs& args)
- {
- OnMouseUpCore(args);
- OnMouseUp(args);
- mouse_up_event.Raise(args);
- }
-
- void Control::RaiseMouseClickEvent(MouseButtonEventArgs& args)
- {
- OnMouseClickCore(args);
- OnMouseClick(args);
- mouse_click_event.Raise(args);
- }
-
- void Control::RaiseMouseWheelEvent(MouseWheelEventArgs& args)
- {
- OnMouseWheelCore(args);
- OnMouseWheel(args);
- mouse_wheel_event.Raise(args);
- }
-
void Control::OnMouseClickBegin(MouseButton button)
{
-
}
void Control::OnMouseClickEnd(MouseButton button)
{
-
- }
-
- void Control::OnKeyDown(KeyEventArgs& args)
- {
- }
-
- void Control::OnKeyUp(KeyEventArgs& args)
- {
- }
-
- void Control::OnChar(CharEventArgs& args)
- {
- }
-
- void Control::OnKeyDownCore(KeyEventArgs& args)
- {
- }
-
- void Control::OnKeyUpCore(KeyEventArgs& args)
- {
- }
-
- void Control::OnCharCore(CharEventArgs& args)
- {
- }
-
- void Control::RaiseKeyDownEvent(KeyEventArgs& args)
- {
- OnKeyDownCore(args);
- OnKeyDown(args);
- key_down_event.Raise(args);
- }
-
- void Control::RaiseKeyUpEvent(KeyEventArgs& args)
- {
- OnKeyUpCore(args);
- OnKeyUp(args);
- key_up_event.Raise(args);
- }
-
- void Control::RaiseCharEvent(CharEventArgs& args)
- {
- OnCharCore(args);
- OnChar(args);
- char_event.Raise(args);
- }
-
- void Control::OnGetFocus(FocusChangeEventArgs& args)
- {
-
- }
-
- void Control::OnLoseFocus(FocusChangeEventArgs& args)
- {
-
- }
-
- void Control::OnGetFocusCore(FocusChangeEventArgs& args)
- {
-
- }
-
- void Control::OnLoseFocusCore(FocusChangeEventArgs& args)
- {
-
- }
-
- void Control::RaiseGetFocusEvent(FocusChangeEventArgs& args)
- {
- OnGetFocusCore(args);
- OnGetFocus(args);
- get_focus_event.Raise(args);
- }
-
- void Control::RaiseLoseFocusEvent(FocusChangeEventArgs& args)
- {
- OnLoseFocusCore(args);
- OnLoseFocus(args);
- lose_focus_event.Raise(args);
}
inline Size ThicknessToSize(const Thickness& thickness)
@@ -862,11 +644,6 @@ namespace cru::ui
return Size(thickness.left + thickness.right, thickness.top + thickness.bottom);
}
- inline float AtLeast0(const float value)
- {
- return value < 0 ? 0 : value;
- }
-
Size Control::OnMeasureCore(const Size& available_size)
{
const auto layout_params = GetLayoutParams();
@@ -1072,8 +849,8 @@ namespace cru::ui
{
if (this->old_position_ != this->position_)
{
- PositionChangedEventArgs args(this, this, this->old_position_, this->position_);
- this->RaisePositionChangedEvent(args);
+ events::PositionChangedEventArgs args(this, this, this->old_position_, this->position_);
+ position_changed_event.Raise(args);
this->old_position_ = this->position_;
}
}
diff --git a/src/ui/control.hpp b/src/ui/control.hpp
index d6ad9f02..f3751ddf 100644
--- a/src/ui/control.hpp
+++ b/src/ui/control.hpp
@@ -249,34 +249,35 @@ namespace cru::ui
//*************** region: events ***************
//Raised when mouse enter the control.
- events::MouseEvent mouse_enter_event;
+ events::RoutedEvent<events::MouseEventArgs> mouse_enter_event;
//Raised when mouse is leave the control.
- events::MouseEvent mouse_leave_event;
+ events::RoutedEvent<events::MouseEventArgs> mouse_leave_event;
//Raised when mouse is move in the control.
- events::MouseEvent mouse_move_event;
+ events::RoutedEvent<events::MouseEventArgs> mouse_move_event;
//Raised when a mouse button is pressed in the control.
- events::MouseButtonEvent mouse_down_event;
+ events::RoutedEvent<events::MouseButtonEventArgs> mouse_down_event;
//Raised when a mouse button is released in the control.
- events::MouseButtonEvent mouse_up_event;
+ events::RoutedEvent<events::MouseButtonEventArgs> mouse_up_event;
//Raised when a mouse button is pressed in the control and released in the control with mouse not leaving it between two operations.
- events::MouseButtonEvent mouse_click_event;
+ events::RoutedEvent<events::MouseButtonEventArgs> mouse_click_event;
- events::MouseWheelEvent mouse_wheel_event;
+ events::RoutedEvent<events::MouseWheelEventArgs> mouse_wheel_event;
- events::KeyEvent key_down_event;
- events::KeyEvent key_up_event;
- events::CharEvent char_event;
+ events::RoutedEvent<events::KeyEventArgs> key_down_event;
+ events::RoutedEvent<events::KeyEventArgs> key_up_event;
+ events::RoutedEvent<events::CharEventArgs> char_event;
- events::FocusChangeEvent get_focus_event;
- events::FocusChangeEvent lose_focus_event;
+ events::RoutedEvent<events::FocusChangeEventArgs> get_focus_event;
+ events::RoutedEvent<events::FocusChangeEventArgs> lose_focus_event;
- events::DrawEvent draw_content_event;
- events::DrawEvent draw_background_event;
- events::DrawEvent draw_foreground_event;
+ Event<events::DrawEventArgs> draw_content_event;
+ Event<events::DrawEventArgs> draw_background_event;
+ Event<events::DrawEventArgs> draw_foreground_event;
- events::PositionChangedEvent position_changed_event;
- events::SizeChangedEvent size_changed_event;
+ Event<events::PositionChangedEventArgs> position_changed_event;
+ Event<events::SizeChangedEventArgs> size_changed_event;
+ //*************** region: tree event ***************
protected:
//Invoked when a child is added. Overrides should invoke base.
virtual void OnAddChild(Control* child);
@@ -288,32 +289,15 @@ namespace cru::ui
//Invoked when the control is detached to a window. Overrides should invoke base.
virtual void OnDetachToWindow(Window* window);
- //*************** region: graphic events ***************
+
+ //*************** region: graphic event ***************
private:
void OnDrawDecoration(ID2D1DeviceContext* device_context);
void OnDrawCore(ID2D1DeviceContext* device_context);
- protected:
- virtual void OnDrawContent(ID2D1DeviceContext* device_context);
- virtual void OnDrawForeground(ID2D1DeviceContext* device_context);
- virtual void OnDrawBackground(ID2D1DeviceContext* device_context);
- // For a event, the window event system will first dispatch event to core functions.
- // Therefore for particular controls, you should do essential actions in core functions,
- // and override version should invoke base version. The base core function
- // in "Control" class will call corresponding non-core function and call "Raise" on
- // event objects. So user custom actions should be done by overriding non-core function
- // and calling the base version is optional.
//*************** region: position and size event ***************
- virtual void OnPositionChanged(events::PositionChangedEventArgs& args);
- virtual void OnSizeChanged(events::SizeChangedEventArgs& args);
-
- virtual void OnPositionChangedCore(events::PositionChangedEventArgs& args);
- virtual void OnSizeChangedCore(events::SizeChangedEventArgs& args);
-
- void RaisePositionChangedEvent(events::PositionChangedEventArgs& args);
- void RaiseSizeChangedEvent(events::SizeChangedEventArgs& args);
-
+ protected:
void RegenerateGeometryInfo();
const GeometryInfo& GetGeometryInfo() const
@@ -321,63 +305,19 @@ namespace cru::ui
return geometry_info_;
}
- //*************** region: mouse event ***************
- virtual void OnMouseEnter(events::MouseEventArgs& args);
- virtual void OnMouseLeave(events::MouseEventArgs& args);
- virtual void OnMouseMove(events::MouseEventArgs& args);
- virtual void OnMouseDown(events::MouseButtonEventArgs& args);
- virtual void OnMouseUp(events::MouseButtonEventArgs& args);
- virtual void OnMouseClick(events::MouseButtonEventArgs& args);
-
- virtual void OnMouseEnterCore(events::MouseEventArgs& args);
- virtual void OnMouseLeaveCore(events::MouseEventArgs& args);
- virtual void OnMouseMoveCore(events::MouseEventArgs& args);
- virtual void OnMouseDownCore(events::MouseButtonEventArgs& args);
- virtual void OnMouseUpCore(events::MouseButtonEventArgs& args);
- virtual void OnMouseClickCore(events::MouseButtonEventArgs& args);
-
- virtual void OnMouseWheel(events::MouseWheelEventArgs& args);
- virtual void OnMouseWheelCore(events::MouseWheelEventArgs& args);
-
- void RaiseMouseEnterEvent(events::MouseEventArgs& args);
- void RaiseMouseLeaveEvent(events::MouseEventArgs& args);
- void RaiseMouseMoveEvent(events::MouseEventArgs& args);
- void RaiseMouseDownEvent(events::MouseButtonEventArgs& args);
- void RaiseMouseUpEvent(events::MouseButtonEventArgs& args);
- void RaiseMouseClickEvent(events::MouseButtonEventArgs& args);
-
- void RaiseMouseWheelEvent(events::MouseWheelEventArgs& args);
+ //*************** region: mouse event ***************
+ protected:
virtual void OnMouseClickBegin(MouseButton button);
virtual void OnMouseClickEnd(MouseButton button);
- //*************** region: keyboard event ***************
- virtual void OnKeyDown(events::KeyEventArgs& args);
- virtual void OnKeyUp(events::KeyEventArgs& args);
- virtual void OnChar(events::CharEventArgs& args);
-
- virtual void OnKeyDownCore(events::KeyEventArgs& args);
- virtual void OnKeyUpCore(events::KeyEventArgs& args);
- virtual void OnCharCore(events::CharEventArgs& args);
-
- void RaiseKeyDownEvent(events::KeyEventArgs& args);
- void RaiseKeyUpEvent(events::KeyEventArgs& args);
- void RaiseCharEvent(events::CharEventArgs& args);
-
- //*************** region: focus event ***************
- virtual void OnGetFocus(events::FocusChangeEventArgs& args);
- virtual void OnLoseFocus(events::FocusChangeEventArgs& args);
-
- virtual void OnGetFocusCore(events::FocusChangeEventArgs& args);
- virtual void OnLoseFocusCore(events::FocusChangeEventArgs& args);
-
- void RaiseGetFocusEvent(events::FocusChangeEventArgs& args);
- void RaiseLoseFocusEvent(events::FocusChangeEventArgs& args);
//*************** region: layout ***************
+ private:
Size OnMeasureCore(const Size& available_size);
void OnLayoutCore(const Rect& rect);
+ protected:
virtual Size OnMeasureContent(const Size& available_size);
virtual void OnLayoutContent(const Rect& rect);
@@ -416,8 +356,6 @@ namespace cru::ui
ControlPositionCache position_cache_{};
- bool is_mouse_inside_ = false;
-
std::unordered_map<MouseButton, bool> is_mouse_click_valid_map_
{
{ MouseButton::Left, true },
@@ -450,6 +388,71 @@ namespace cru::ui
Cursor::Ptr cursor_{};
};
+
+ //*************** region: event dispatcher helper ***************
+
+ // 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, events::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);
+ }
+ }
+
+
+ //*************** region: tree helper ***************
+
// Find the lowest common ancestor.
// Return nullptr if "left" and "right" are not in the same tree.
Control* FindLowestCommonAncestor(Control* left, Control* right);
@@ -468,6 +471,8 @@ namespace cru::ui
}
+ //*************** region: create helper ***************
+
template <typename TControl, typename... Args>
TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args)
{
diff --git a/src/ui/controls/list_item.cpp b/src/ui/controls/list_item.cpp
index bf61010d..92f8750f 100644
--- a/src/ui/controls/list_item.cpp
+++ b/src/ui/controls/list_item.cpp
@@ -15,6 +15,39 @@ namespace cru::ui::controls
brushes_[State::Hover] .fill_brush = predefine_resources->list_item_hover_fill_brush;
brushes_[State::Select].border_brush = predefine_resources->list_item_select_border_brush;
brushes_[State::Select].fill_brush = predefine_resources->list_item_select_fill_brush;
+
+ draw_foreground_event.AddHandler([this](events::DrawEventArgs& args)
+ {
+ const auto device_context = args.GetDeviceContext();
+ const auto rect = Rect(Point::Zero(), GetRect(RectRange::Padding).GetSize());
+ device_context->FillRectangle(Convert(rect), brushes_[state_].fill_brush.Get());
+ device_context->DrawRectangle(Convert(rect.Shrink(Thickness(0.5))), brushes_[state_].border_brush.Get(), 1);
+ });
+
+ mouse_enter_event.bubble.AddHandler([this](events::MouseEventArgs& args)
+ {
+ if (GetState() == State::Select)
+ return;
+
+ if (IsAnyMouseButtonDown())
+ return;
+
+ SetState(State::Hover);
+ });
+
+ mouse_leave_event.bubble.AddHandler([this](events::MouseEventArgs& args)
+ {
+ if (GetState() == State::Select)
+ return;
+
+ SetState(State::Normal);
+ });
+
+ mouse_click_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (args.GetMouseButton() == MouseButton::Left)
+ SetState(State::Select);
+ });
}
StringView ListItem::GetControlType() const
@@ -27,36 +60,4 @@ namespace cru::ui::controls
state_ = state;
InvalidateDraw();
}
-
- void ListItem::OnDrawForeground(ID2D1DeviceContext* device_context)
- {
- const auto rect = Rect(Point::Zero(), GetRect(RectRange::Padding).GetSize());
- device_context->FillRectangle(Convert(rect), brushes_[state_].fill_brush.Get());
- device_context->DrawRectangle(Convert(rect.Shrink(Thickness(0.5))), brushes_[state_].border_brush.Get(), 1);
- }
-
- void ListItem::OnMouseEnterCore(events::MouseEventArgs& args)
- {
- if (GetState() == State::Select)
- return;
-
- if (IsAnyMouseButtonDown())
- return;
-
- SetState(State::Hover);
- }
-
- void ListItem::OnMouseLeaveCore(events::MouseEventArgs& args)
- {
- if (GetState() == State::Select)
- return;
-
- SetState(State::Normal);
- }
-
- void ListItem::OnMouseClickCore(events::MouseButtonEventArgs& args)
- {
- if (args.GetMouseButton() == MouseButton::Left)
- SetState(State::Select);
- }
}
diff --git a/src/ui/controls/list_item.hpp b/src/ui/controls/list_item.hpp
index a77d13e6..e150efbb 100644
--- a/src/ui/controls/list_item.hpp
+++ b/src/ui/controls/list_item.hpp
@@ -56,13 +56,6 @@ namespace cru::ui::controls
void SetState(State state);
- protected:
- void OnDrawForeground(ID2D1DeviceContext* device_context) override;
-
- void OnMouseEnterCore(events::MouseEventArgs& args) override final;
- void OnMouseLeaveCore(events::MouseEventArgs& args) override final;
- void OnMouseClickCore(events::MouseButtonEventArgs& args) override final;
-
private:
State state_ = State::Normal;
std::map<State, StateBrush> brushes_{};
diff --git a/src/ui/controls/popup_menu.cpp b/src/ui/controls/popup_menu.cpp
index 88ea8f17..7f4b9d08 100644
--- a/src/ui/controls/popup_menu.cpp
+++ b/src/ui/controls/popup_menu.cpp
@@ -12,7 +12,7 @@ namespace cru::ui::controls
{
const auto popup = Window::CreatePopup(parent);
- popup->lose_focus_event.AddHandler([popup](events::FocusChangeEventArgs& args)
+ popup->lose_focus_event.bubble.AddHandler([popup](events::FocusChangeEventArgs& args)
{
if (args.IsWindow())
popup->Close();
@@ -29,7 +29,7 @@ namespace cru::ui::controls
ControlList{ text_block }
);
- list_item->mouse_click_event.AddHandler([popup, action](events::MouseButtonEventArgs& args)
+ list_item->mouse_click_event.bubble.AddHandler([popup, action](events::MouseButtonEventArgs& args)
{
if (args.GetMouseButton() == MouseButton::Left)
{
diff --git a/src/ui/controls/scroll_control.cpp b/src/ui/controls/scroll_control.cpp
index aa5403d4..07715892 100644
--- a/src/ui/controls/scroll_control.cpp
+++ b/src/ui/controls/scroll_control.cpp
@@ -17,6 +17,124 @@ namespace cru::ui::controls
ScrollControl::ScrollControl(const bool container) : Control(container)
{
SetClipContent(true);
+
+ draw_foreground_event.AddHandler([this](events::DrawEventArgs& args)
+ {
+ const auto device_context = args.GetDeviceContext();
+ const auto predefined = UiManager::GetInstance()->GetPredefineResources();
+
+ if (is_horizontal_scroll_bar_visible_)
+ {
+ device_context->FillRectangle(
+ Convert(horizontal_bar_info_.border),
+ predefined->scroll_bar_background_brush.Get()
+ );
+
+ device_context->FillRectangle(
+ Convert(horizontal_bar_info_.bar),
+ predefined->scroll_bar_brush.Get()
+ );
+
+ device_context->DrawLine(
+ Convert(horizontal_bar_info_.border.GetLeftTop()),
+ Convert(horizontal_bar_info_.border.GetRightTop()),
+ predefined->scroll_bar_border_brush.Get()
+ );
+ }
+
+ if (is_vertical_scroll_bar_visible_)
+ {
+ device_context->FillRectangle(
+ Convert(vertical_bar_info_.border),
+ predefined->scroll_bar_background_brush.Get()
+ );
+
+ device_context->FillRectangle(
+ Convert(vertical_bar_info_.bar),
+ predefined->scroll_bar_brush.Get()
+ );
+
+ device_context->DrawLine(
+ Convert(vertical_bar_info_.border.GetLeftTop()),
+ Convert(vertical_bar_info_.border.GetLeftBottom()),
+ predefined->scroll_bar_border_brush.Get()
+ );
+ }
+ });
+
+ mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (args.GetMouseButton() == MouseButton::Left)
+ {
+ const auto point = args.GetPoint(this);
+ if (is_vertical_scroll_bar_visible_ && vertical_bar_info_.bar.IsPointInside(point))
+ {
+ GetWindow()->CaptureMouseFor(this);
+ is_pressing_scroll_bar_ = Orientation::Vertical;
+ pressing_delta_ = point.y - vertical_bar_info_.bar.top;
+ return;
+ }
+
+ if (is_horizontal_scroll_bar_visible_ && horizontal_bar_info_.bar.IsPointInside(point))
+ {
+ GetWindow()->CaptureMouseFor(this);
+ pressing_delta_ = point.x - horizontal_bar_info_.bar.left;
+ is_pressing_scroll_bar_ = Orientation::Horizontal;
+ return;
+ }
+ }
+ });
+
+ mouse_move_event.bubble.AddHandler([this](events::MouseEventArgs& args)
+ {
+ const auto mouse_point = args.GetPoint(this);
+
+ if (is_pressing_scroll_bar_ == Orientation::Horizontal)
+ {
+ const auto new_head_position = mouse_point.x - pressing_delta_;
+ const auto new_offset = new_head_position / horizontal_bar_info_.border.width * view_width_;
+ SetScrollOffset(new_offset, std::nullopt);
+ return;
+ }
+
+ if (is_pressing_scroll_bar_ == Orientation::Vertical)
+ {
+ const auto new_head_position = mouse_point.y - pressing_delta_;
+ const auto new_offset = new_head_position / vertical_bar_info_.border.height * view_height_;
+ SetScrollOffset(std::nullopt, new_offset);
+ return;
+ }
+ });
+
+ mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (args.GetMouseButton() == MouseButton::Left && is_pressing_scroll_bar_.has_value())
+ {
+ GetWindow()->ReleaseCurrentMouseCapture();
+ is_pressing_scroll_bar_ = std::nullopt;
+ }
+ });
+
+ mouse_wheel_event.bubble.AddHandler([this](events::MouseWheelEventArgs& args)
+ {
+ constexpr const auto view_delta = 30.0f;
+
+ if (args.GetDelta() == 0.0f)
+ return;
+
+ const auto content_rect = GetRect(RectRange::Content);
+ if (IsVerticalScrollEnabled() && GetScrollOffsetY() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewHeight() - content_rect.height)))
+ {
+ SetScrollOffset(std::nullopt, GetScrollOffsetY() - args.GetDelta() / WHEEL_DELTA * view_delta);
+ return;
+ }
+
+ if (IsHorizontalScrollEnabled() && GetScrollOffsetX() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewWidth() - content_rect.width)))
+ {
+ SetScrollOffset(GetScrollOffsetX() - args.GetDelta() / WHEEL_DELTA * view_delta, std::nullopt);
+ return;
+ }
+ });
}
ScrollControl::~ScrollControl()
@@ -204,133 +322,6 @@ namespace cru::ui::controls
UpdateScrollBarVisibility();
}
- void ScrollControl::OnDrawForeground(ID2D1DeviceContext* device_context)
- {
- Control::OnDrawForeground(device_context);
-
- const auto predefined = UiManager::GetInstance()->GetPredefineResources();
-
- if (is_horizontal_scroll_bar_visible_)
- {
- device_context->FillRectangle(
- Convert(horizontal_bar_info_.border),
- predefined->scroll_bar_background_brush.Get()
- );
-
- device_context->FillRectangle(
- Convert(horizontal_bar_info_.bar),
- predefined->scroll_bar_brush.Get()
- );
-
- device_context->DrawLine(
- Convert(horizontal_bar_info_.border.GetLeftTop()),
- Convert(horizontal_bar_info_.border.GetRightTop()),
- predefined->scroll_bar_border_brush.Get()
- );
- }
-
- if (is_vertical_scroll_bar_visible_)
- {
- device_context->FillRectangle(
- Convert(vertical_bar_info_.border),
- predefined->scroll_bar_background_brush.Get()
- );
-
- device_context->FillRectangle(
- Convert(vertical_bar_info_.bar),
- predefined->scroll_bar_brush.Get()
- );
-
- device_context->DrawLine(
- Convert(vertical_bar_info_.border.GetLeftTop()),
- Convert(vertical_bar_info_.border.GetLeftBottom()),
- predefined->scroll_bar_border_brush.Get()
- );
- }
- }
-
- void ScrollControl::OnMouseDownCore(events::MouseButtonEventArgs& args)
- {
- Control::OnMouseDownCore(args);
-
- if (args.GetMouseButton() == MouseButton::Left)
- {
- const auto point = args.GetPoint(this);
- if (is_vertical_scroll_bar_visible_ && vertical_bar_info_.bar.IsPointInside(point))
- {
- GetWindow()->CaptureMouseFor(this);
- is_pressing_scroll_bar_ = Orientation::Vertical;
- pressing_delta_ = point.y - vertical_bar_info_.bar.top;
- return;
- }
-
- if (is_horizontal_scroll_bar_visible_ && horizontal_bar_info_.bar.IsPointInside(point))
- {
- GetWindow()->CaptureMouseFor(this);
- pressing_delta_ = point.x - horizontal_bar_info_.bar.left;
- is_pressing_scroll_bar_ = Orientation::Horizontal;
- return;
- }
- }
- }
-
- void ScrollControl::OnMouseMoveCore(events::MouseEventArgs& args)
- {
- Control::OnMouseMoveCore(args);
-
- const auto mouse_point = args.GetPoint(this);
-
- if (is_pressing_scroll_bar_ == Orientation::Horizontal)
- {
- const auto new_head_position = mouse_point.x - pressing_delta_;
- const auto new_offset = new_head_position / horizontal_bar_info_.border.width * view_width_;
- SetScrollOffset(new_offset, std::nullopt);
- return;
- }
-
- if (is_pressing_scroll_bar_ == Orientation::Vertical)
- {
- const auto new_head_position = mouse_point.y - pressing_delta_;
- const auto new_offset = new_head_position / vertical_bar_info_.border.height * view_height_;
- SetScrollOffset(std::nullopt, new_offset);
- return;
- }
- }
-
- void ScrollControl::OnMouseUpCore(events::MouseButtonEventArgs& args)
- {
- Control::OnMouseUpCore(args);
-
- if (args.GetMouseButton() == MouseButton::Left && is_pressing_scroll_bar_.has_value())
- {
- GetWindow()->ReleaseCurrentMouseCapture();
- is_pressing_scroll_bar_ = std::nullopt;
- }
- }
-
- void ScrollControl::OnMouseWheelCore(events::MouseWheelEventArgs& args)
- {
- Control::OnMouseWheelCore(args);
-
- constexpr const auto view_delta = 30.0f;
-
- if (args.GetDelta() == 0.0f)
- return;
-
- const auto content_rect = GetRect(RectRange::Content);
- if (IsVerticalScrollEnabled() && GetScrollOffsetY() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewHeight() - content_rect.height)))
- {
- SetScrollOffset(std::nullopt, GetScrollOffsetY() - args.GetDelta() / WHEEL_DELTA * view_delta);
- return;
- }
-
- if (IsHorizontalScrollEnabled() && GetScrollOffsetX() != (args.GetDelta() > 0.0f ? 0.0f : AtLeast0(GetViewWidth() - content_rect.width)))
- {
- SetScrollOffset(GetScrollOffsetX() - args.GetDelta() / WHEEL_DELTA * view_delta, std::nullopt);
- return;
- }
- }
-
void ScrollControl::CoerceAndSetOffsets(const float offset_x, const float offset_y, const bool update_children)
{
const auto old_offset_x = offset_x_;
diff --git a/src/ui/controls/scroll_control.hpp b/src/ui/controls/scroll_control.hpp
index 0541a010..4eabc605 100644
--- a/src/ui/controls/scroll_control.hpp
+++ b/src/ui/controls/scroll_control.hpp
@@ -122,14 +122,6 @@ namespace cru::ui::controls
void AfterLayoutSelf() override;
- void OnDrawForeground(ID2D1DeviceContext* device_context) override;
-
- void OnMouseDownCore(events::MouseButtonEventArgs& args) override final;
- void OnMouseMoveCore(events::MouseEventArgs& args) override final;
- void OnMouseUpCore(events::MouseButtonEventArgs& args) override final;
-
- void OnMouseWheelCore(events::MouseWheelEventArgs& args) override;
-
private:
void CoerceAndSetOffsets(float offset_x, float offset_y, bool update_children = true);
void UpdateScrollBarVisibility();
diff --git a/src/ui/controls/text_box.cpp b/src/ui/controls/text_box.cpp
index 83311548..28e1053d 100644
--- a/src/ui/controls/text_box.cpp
+++ b/src/ui/controls/text_box.cpp
@@ -20,149 +20,145 @@ namespace cru::ui::controls
GetBorderProperty() = UiManager::GetInstance()->GetPredefineResources()->text_box_border;
SetBordered(true);
- }
-
- TextBox::~TextBox() = default;
- StringView TextBox::GetControlType() const
- {
- return control_type;
- }
-
- void TextBox::OnDrawContent(ID2D1DeviceContext* device_context)
- {
- TextControl::OnDrawContent(device_context);
- if (is_caret_show_)
+ draw_content_event.AddHandler([this](events::DrawEventArgs& args)
{
- const auto caret_half_width = UiManager::GetInstance()->GetCaretInfo().half_caret_width;
- FLOAT x, y;
- DWRITE_HIT_TEST_METRICS metrics{};
- ThrowIfFailed(text_layout_->HitTestTextPosition(caret_position_, FALSE, &x, &y, &metrics));
- device_context->FillRectangle(D2D1::RectF(metrics.left - caret_half_width, metrics.top, metrics.left + caret_half_width, metrics.top + metrics.height), caret_brush_.Get());
- }
- }
+ const auto device_context = args.GetDeviceContext();
+ if (is_caret_show_)
+ {
+ const auto caret_half_width = UiManager::GetInstance()->GetCaretInfo().half_caret_width;
+ FLOAT x, y;
+ DWRITE_HIT_TEST_METRICS metrics{};
+ ThrowIfFailed(text_layout_->HitTestTextPosition(caret_position_, FALSE, &x, &y, &metrics));
+ device_context->FillRectangle(D2D1::RectF(metrics.left - caret_half_width, metrics.top, metrics.left + caret_half_width, metrics.top + metrics.height), caret_brush_.Get());
+ }
+ });
- void TextBox::OnGetFocusCore(events::FocusChangeEventArgs& args)
- {
- TextControl::OnGetFocusCore(args);
- assert(!caret_timer_.has_value());
- is_caret_show_ = true;
- caret_timer_ = SetInterval(UiManager::GetInstance()->GetCaretInfo().caret_blink_duration, [this]
+ get_focus_event.bubble.AddHandler([this](events::FocusChangeEventArgs& args)
{
- is_caret_show_ = !is_caret_show_;
- InvalidateDraw();
+ assert(!caret_timer_.has_value());
+ is_caret_show_ = true;
+ caret_timer_ = SetInterval(UiManager::GetInstance()->GetCaretInfo().caret_blink_duration, [this]
+ {
+ is_caret_show_ = !is_caret_show_;
+ InvalidateDraw();
+ });
});
- }
- void TextBox::OnLoseFocusCore(events::FocusChangeEventArgs& args)
- {
- Control::OnLoseFocusCore(args);
- assert(caret_timer_.has_value());
- caret_timer_->Cancel();
- caret_timer_ = std::nullopt;
- is_caret_show_ = false;
- }
+ lose_focus_event.bubble.AddHandler([this](events::FocusChangeEventArgs& args)
+ {
+ assert(caret_timer_.has_value());
+ caret_timer_->Cancel();
+ caret_timer_ = std::nullopt;
+ is_caret_show_ = false;
+ });
- void TextBox::OnKeyDownCore(events::KeyEventArgs& args)
- {
- Control::OnKeyDownCore(args);
- if (args.GetVirtualCode() == VK_LEFT && caret_position_ > 0)
+ key_down_event.bubble.AddHandler([this](events::KeyEventArgs& args)
{
- if (IsKeyDown(VK_SHIFT))
+ if (args.GetVirtualCode() == VK_LEFT && caret_position_ > 0)
{
- if (GetCaretSelectionSide())
- ShiftLeftSelectionRange(-1);
- else
- ShiftRightSelectionRange(-1);
- }
- else
- {
- const auto selection = GetSelectedRange();
- if (selection.has_value())
+ if (IsKeyDown(VK_SHIFT))
{
- ClearSelection();
- caret_position_ = selection.value().position;
+ if (GetCaretSelectionSide())
+ ShiftLeftSelectionRange(-1);
+ else
+ ShiftRightSelectionRange(-1);
}
else
- caret_position_--;
+ {
+ const auto selection = GetSelectedRange();
+ if (selection.has_value())
+ {
+ ClearSelection();
+ caret_position_ = selection.value().position;
+ }
+ else
+ caret_position_--;
+ }
+ InvalidateDraw();
}
- InvalidateDraw();
- }
- if (args.GetVirtualCode() == VK_RIGHT && caret_position_ < GetText().size())
- {
- if (IsKeyDown(VK_SHIFT))
+ if (args.GetVirtualCode() == VK_RIGHT && caret_position_ < GetText().size())
{
- if (GetCaretSelectionSide())
- ShiftLeftSelectionRange(1);
- else
- ShiftRightSelectionRange(1);
- }
- else
- {
- const auto selection = GetSelectedRange();
- if (selection.has_value())
+ if (IsKeyDown(VK_SHIFT))
{
- ClearSelection();
- caret_position_ = selection.value().position + selection.value().count;
+ if (GetCaretSelectionSide())
+ ShiftLeftSelectionRange(1);
+ else
+ ShiftRightSelectionRange(1);
}
else
- caret_position_++;
+ {
+ const auto selection = GetSelectedRange();
+ if (selection.has_value())
+ {
+ ClearSelection();
+ caret_position_ = selection.value().position + selection.value().count;
+ }
+ else
+ caret_position_++;
+ }
}
- }
- }
+ });
- void TextBox::OnCharCore(events::CharEventArgs& args)
- {
- Control::OnCharCore(args);
- if (args.GetChar() == L'\b')
+ char_event.bubble.AddHandler([this](events::CharEventArgs& args)
{
- if (GetSelectedRange().has_value())
- {
- const auto selection_range = GetSelectedRange().value();
- auto text = GetText();
- text.erase(text.cbegin() + selection_range.position, text.cbegin() + selection_range.position + selection_range.count);
- SetText(text);
- caret_position_ = selection_range.position;
- ClearSelection();
- }
- else
+ if (args.GetChar() == L'\b')
{
- if (caret_position_ > 0)
+ if (GetSelectedRange().has_value())
{
+ const auto selection_range = GetSelectedRange().value();
auto text = GetText();
- if (!text.empty())
+ text.erase(text.cbegin() + selection_range.position, text.cbegin() + selection_range.position + selection_range.count);
+ SetText(text);
+ caret_position_ = selection_range.position;
+ ClearSelection();
+ }
+ else
+ {
+ if (caret_position_ > 0)
{
- const auto position = --caret_position_;
- text.erase(text.cbegin() + position);
- SetText(text);
+ auto text = GetText();
+ if (!text.empty())
+ {
+ const auto position = --caret_position_;
+ text.erase(text.cbegin() + position);
+ SetText(text);
+ }
}
}
+ return;
}
- return;
- }
- if (std::iswprint(args.GetChar()))
- {
- if (GetSelectedRange().has_value())
- {
- const auto selection_range = GetSelectedRange().value();
- auto text = GetText();
- text.erase(selection_range.position, selection_range.count);
- text.insert(text.cbegin() + selection_range.position, args.GetChar());
- SetText(text);
- caret_position_ = selection_range.position + 1;
- ClearSelection();
- }
- else
+ if (std::iswprint(args.GetChar()))
{
- ClearSelection();
- const auto position = caret_position_++;
- auto text = GetText();
- text.insert(text.cbegin() + position, { args.GetChar() });
- SetText(text);
+ if (GetSelectedRange().has_value())
+ {
+ const auto selection_range = GetSelectedRange().value();
+ auto text = GetText();
+ text.erase(selection_range.position, selection_range.count);
+ text.insert(text.cbegin() + selection_range.position, args.GetChar());
+ SetText(text);
+ caret_position_ = selection_range.position + 1;
+ ClearSelection();
+ }
+ else
+ {
+ ClearSelection();
+ const auto position = caret_position_++;
+ auto text = GetText();
+ text.insert(text.cbegin() + position, { args.GetChar() });
+ SetText(text);
+ }
}
- }
+ });
+ }
+
+ TextBox::~TextBox() = default;
+
+ StringView TextBox::GetControlType() const
+ {
+ return control_type;
}
void TextBox::RequestChangeCaretPosition(const unsigned position)
diff --git a/src/ui/controls/text_box.hpp b/src/ui/controls/text_box.hpp
index 3a30ecb2..e5cd7545 100644
--- a/src/ui/controls/text_box.hpp
+++ b/src/ui/controls/text_box.hpp
@@ -30,14 +30,6 @@ namespace cru::ui::controls
StringView GetControlType() const override final;
protected:
- void OnDrawContent(ID2D1DeviceContext* device_context) override;
-
- void OnGetFocusCore(events::FocusChangeEventArgs& args) override final;
- void OnLoseFocusCore(events::FocusChangeEventArgs& args) override final;
-
- void OnKeyDownCore(events::KeyEventArgs& args) override final;
- void OnCharCore(events::CharEventArgs& args) override final;
-
void RequestChangeCaretPosition(unsigned position) override final;
private:
diff --git a/src/ui/controls/text_control.cpp b/src/ui/controls/text_control.cpp
index d7d6b810..71a167e2 100644
--- a/src/ui/controls/text_control.cpp
+++ b/src/ui/controls/text_control.cpp
@@ -9,6 +9,40 @@
namespace cru::ui::controls
{
+ namespace
+ {
+ unsigned TextLayoutHitTest(IDWriteTextLayout* text_layout, const Point& point)
+ {
+ BOOL is_trailing, is_inside;
+ DWRITE_HIT_TEST_METRICS metrics{};
+ text_layout->HitTestPoint(point.x, point.y, &is_trailing, &is_inside, &metrics);
+ return is_trailing == 0 ? metrics.textPosition : metrics.textPosition + 1;
+ }
+
+ void DrawSelectionRect(ID2D1DeviceContext* device_context, IDWriteTextLayout* layout, ID2D1Brush* brush, const std::optional<TextRange> range)
+ {
+ if (range.has_value())
+ {
+ DWRITE_TEXT_METRICS text_metrics{};
+ ThrowIfFailed(layout->GetMetrics(&text_metrics));
+ const auto metrics_count = text_metrics.lineCount * text_metrics.maxBidiReorderingDepth;
+
+ std::vector<DWRITE_HIT_TEST_METRICS> hit_test_metrics(metrics_count);
+ UINT32 actual_count;
+ layout->HitTestTextRange(
+ range.value().position, range.value().count,
+ 0, 0,
+ hit_test_metrics.data(), metrics_count, &actual_count
+ );
+
+ hit_test_metrics.erase(hit_test_metrics.cbegin() + actual_count, hit_test_metrics.cend());
+
+ for (const auto& metrics : hit_test_metrics)
+ device_context->FillRoundedRectangle(D2D1::RoundedRect(D2D1::RectF(metrics.left, metrics.top, metrics.left + metrics.width, metrics.top + metrics.height), 3, 3), brush);
+ }
+ }
+ }
+
TextControl::TextControl(const Microsoft::WRL::ComPtr<IDWriteTextFormat>& init_text_format,
const Microsoft::WRL::ComPtr<ID2D1Brush>& init_brush) : Control(false)
{
@@ -21,6 +55,74 @@ namespace cru::ui::controls
selection_brush_ = UiManager::GetInstance()->GetPredefineResources()->text_control_selection_brush;
SetClipContent(true);
+
+ size_changed_event.AddHandler([this](events::SizeChangedEventArgs& args)
+ {
+ const auto content = GetRect(RectRange::Content);
+ ThrowIfFailed(text_layout_->SetMaxWidth(content.width));
+ ThrowIfFailed(text_layout_->SetMaxHeight(content.height));
+ InvalidateDraw();
+ });
+
+ draw_content_event.AddHandler([this](events::DrawEventArgs& args)
+ {
+ const auto device_context = args.GetDeviceContext();
+ DrawSelectionRect(device_context, text_layout_.Get(), selection_brush_.Get(), selected_range_);
+ device_context->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get());
+ });
+
+ mouse_down_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (is_selectable_ && args.GetMouseButton() == MouseButton::Left && GetRect(RectRange::Padding).IsPointInside(args.GetPoint(this, RectRange::Margin)))
+ {
+ selected_range_ = std::nullopt;
+ const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this));
+ RequestChangeCaretPosition(hit_test_result);
+ mouse_down_position_ = hit_test_result;
+ is_selecting_ = true;
+ GetWindow()->CaptureMouseFor(this);
+ InvalidateDraw();
+ }
+ });
+
+ mouse_move_event.bubble.AddHandler([this](events::MouseEventArgs& args)
+ {
+ if (is_selecting_)
+ {
+ const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this));
+ RequestChangeCaretPosition(hit_test_result);
+ selected_range_ = TextRange::FromTwoSides(hit_test_result, mouse_down_position_);
+ InvalidateDraw();
+ }
+ UpdateCursor(args.GetPoint(this, RectRange::Margin));
+ });
+
+
+ mouse_up_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (args.GetMouseButton() == MouseButton::Left)
+ {
+ if (is_selecting_)
+ {
+ is_selecting_ = false;
+ GetWindow()->ReleaseCurrentMouseCapture();
+ }
+ }
+ });
+
+ lose_focus_event.bubble.AddHandler([this](events::FocusChangeEventArgs& args)
+ {
+ if (is_selecting_)
+ {
+ is_selecting_ = false;
+ GetWindow()->ReleaseCurrentMouseCapture();
+ }
+ if (!args.IsWindow()) // If the focus lose is triggered window-wide, then save the selection state. Otherwise, clear selection.
+ {
+ selected_range_ = std::nullopt;
+ InvalidateDraw();
+ }
+ });
}
@@ -75,112 +177,6 @@ namespace cru::ui::controls
}
}
- void TextControl::OnSizeChangedCore(events::SizeChangedEventArgs& args)
- {
- Control::OnSizeChangedCore(args);
- const auto content = GetRect(RectRange::Content);
- ThrowIfFailed(text_layout_->SetMaxWidth(content.width));
- ThrowIfFailed(text_layout_->SetMaxHeight(content.height));
- InvalidateDraw();
- }
-
- namespace
- {
- unsigned TextLayoutHitTest(IDWriteTextLayout* text_layout, const Point& point)
- {
- BOOL is_trailing, is_inside;
- DWRITE_HIT_TEST_METRICS metrics{};
- text_layout->HitTestPoint(point.x, point.y, &is_trailing, &is_inside, &metrics);
- return is_trailing == 0 ? metrics.textPosition : metrics.textPosition + 1;
- }
-
- void DrawSelectionRect(ID2D1DeviceContext* device_context, IDWriteTextLayout* layout, ID2D1Brush* brush, const std::optional<TextRange> range)
- {
- if (range.has_value())
- {
- DWRITE_TEXT_METRICS text_metrics{};
- ThrowIfFailed(layout->GetMetrics(&text_metrics));
- const auto metrics_count = text_metrics.lineCount * text_metrics.maxBidiReorderingDepth;
-
- std::vector<DWRITE_HIT_TEST_METRICS> hit_test_metrics(metrics_count);
- UINT32 actual_count;
- layout->HitTestTextRange(
- range.value().position, range.value().count,
- 0, 0,
- hit_test_metrics.data(), metrics_count, &actual_count
- );
-
- hit_test_metrics.erase(hit_test_metrics.cbegin() + actual_count, hit_test_metrics.cend());
-
- for (const auto& metrics : hit_test_metrics)
- device_context->FillRoundedRectangle(D2D1::RoundedRect(D2D1::RectF(metrics.left, metrics.top, metrics.left + metrics.width, metrics.top + metrics.height), 3, 3), brush);
- }
- }
- }
- void TextControl::OnDrawContent(ID2D1DeviceContext* device_context)
- {
- Control::OnDrawContent(device_context);
- DrawSelectionRect(device_context, text_layout_.Get(), selection_brush_.Get(), selected_range_);
- device_context->DrawTextLayout(D2D1::Point2F(), text_layout_.Get(), brush_.Get());
- }
-
- void TextControl::OnMouseDownCore(events::MouseButtonEventArgs& args)
- {
- Control::OnMouseDownCore(args);
- if (is_selectable_ && args.GetMouseButton() == MouseButton::Left && GetRect(RectRange::Padding).IsPointInside(args.GetPoint(this, RectRange::Margin)))
- {
- selected_range_ = std::nullopt;
- const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this));
- RequestChangeCaretPosition(hit_test_result);
- mouse_down_position_ = hit_test_result;
- is_selecting_ = true;
- GetWindow()->CaptureMouseFor(this);
- InvalidateDraw();
- }
- }
-
- void TextControl::OnMouseMoveCore(events::MouseEventArgs& args)
- {
- Control::OnMouseMoveCore(args);
- if (is_selecting_)
- {
- const auto hit_test_result = TextLayoutHitTest(text_layout_.Get(), args.GetPoint(this));
- RequestChangeCaretPosition(hit_test_result);
- selected_range_ = TextRange::FromTwoSides(hit_test_result, mouse_down_position_);
- InvalidateDraw();
- }
- UpdateCursor(args.GetPoint(this, RectRange::Margin));
- }
-
- void TextControl::OnMouseUpCore(events::MouseButtonEventArgs& args)
- {
- Control::OnMouseUpCore(args);
- if (args.GetMouseButton() == MouseButton::Left)
- {
- if (is_selecting_)
- {
- is_selecting_ = false;
- GetWindow()->ReleaseCurrentMouseCapture();
- }
- }
- }
-
- void TextControl::OnLoseFocusCore(events::FocusChangeEventArgs& args)
- {
- Control::OnLoseFocusCore(args);
- if (is_selecting_)
- {
- is_selecting_ = false;
- GetWindow()->ReleaseCurrentMouseCapture();
- }
- if (!args.IsWindow()) // If the focus lose is triggered window-wide, then save the selection state. Otherwise, clear selection.
- {
- selected_range_ = std::nullopt;
- InvalidateDraw();
- }
- }
-
-
Size TextControl::OnMeasureContent(const Size& available_size)
{
ThrowIfFailed(text_layout_->SetMaxWidth(available_size.width));
diff --git a/src/ui/controls/text_control.hpp b/src/ui/controls/text_control.hpp
index 762d85f3..1e4c985d 100644
--- a/src/ui/controls/text_control.hpp
+++ b/src/ui/controls/text_control.hpp
@@ -62,18 +62,7 @@ namespace cru::ui::controls
protected:
void SetSelectable(bool is_selectable);
- protected:
- void OnSizeChangedCore(events::SizeChangedEventArgs& args) override final;
- void OnDrawContent(ID2D1DeviceContext* device_context) override;
-
- void OnMouseDownCore(events::MouseButtonEventArgs& args) override final;
- void OnMouseMoveCore(events::MouseEventArgs& args) override final;
- void OnMouseUpCore(events::MouseButtonEventArgs& args) override final;
-
- void OnLoseFocusCore(events::FocusChangeEventArgs& args) override;
-
- Size OnMeasureContent(const Size& available_size) override;
-
+ Size OnMeasureContent(const Size& available_size) override final;
virtual void RequestChangeCaretPosition(unsigned position);
diff --git a/src/ui/controls/toggle_button.cpp b/src/ui/controls/toggle_button.cpp
index e3d8662a..874a245f 100644
--- a/src/ui/controls/toggle_button.cpp
+++ b/src/ui/controls/toggle_button.cpp
@@ -3,6 +3,7 @@
#include "graph/graph.hpp"
#include "ui/animations/animation.hpp"
#include "ui/ui_manager.hpp"
+#include "ui/convert_util.hpp"
namespace cru::ui::controls
{
@@ -21,13 +22,34 @@ namespace cru::ui::controls
on_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_on_brush;
off_brush_ = UiManager::GetInstance()->GetPredefineResources()->toggle_button_off_brush;
- }
- inline D2D1_POINT_2F ConvertPoint(const Point& point)
- {
- return D2D1::Point2F(point.x, point.y);
+ draw_content_event.AddHandler([this](events::DrawEventArgs& args)
+ {
+ const auto device_context = args.GetDeviceContext();
+ const auto size = GetSize();
+ graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2), [this](ID2D1DeviceContext* device_context)
+ {
+ if (state_)
+ {
+ device_context->DrawGeometry(frame_path_.Get(), on_brush_.Get(), stroke_width);
+ device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get());
+ }
+ else
+ {
+ device_context->DrawGeometry(frame_path_.Get(), off_brush_.Get(), stroke_width);
+ device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get());
+ }
+ });
+ });
+
+ mouse_click_event.bubble.AddHandler([this](events::MouseButtonEventArgs& args)
+ {
+ if (args.GetMouseButton() == MouseButton::Left)
+ Toggle();
+ });
}
+
StringView ToggleButton::GetControlType() const
{
return control_type;
@@ -38,9 +60,9 @@ namespace cru::ui::controls
const auto size = GetSize();
const auto transform = D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2);
BOOL contains;
- frame_path_->FillContainsPoint(ConvertPoint(point), transform, &contains);
+ frame_path_->FillContainsPoint(Convert(point), transform, &contains);
if (!contains)
- frame_path_->StrokeContainsPoint(ConvertPoint(point), stroke_width, nullptr, transform, &contains);
+ frame_path_->StrokeContainsPoint(Convert(point), stroke_width, nullptr, transform, &contains);
return contains != 0;
}
@@ -72,7 +94,8 @@ namespace cru::ui::controls
})
.Start();
- RaiseToggleEvent(state);
+ events::ToggleEventArgs args(this, this, state);
+ toggle_event.Raise(args);
InvalidateDraw();
}
}
@@ -82,36 +105,6 @@ namespace cru::ui::controls
SetState(!GetState());
}
- void ToggleButton::OnToggle(events::ToggleEventArgs& args)
- {
-
- }
-
- void ToggleButton::OnDrawContent(ID2D1DeviceContext* device_context)
- {
- Control::OnDrawContent(device_context);
- const auto size = GetSize();
- graph::WithTransform(device_context, D2D1::Matrix3x2F::Translation(size.width / 2, size.height / 2), [this](ID2D1DeviceContext* device_context)
- {
- if (state_)
- {
- device_context->DrawGeometry(frame_path_.Get(), on_brush_.Get(), stroke_width);
- device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), on_brush_.Get());
- }
- else
- {
- device_context->DrawGeometry(frame_path_.Get(), off_brush_.Get(), stroke_width);
- device_context->FillEllipse(D2D1::Ellipse(D2D1::Point2F(current_circle_position_, 0), inner_circle_radius, inner_circle_radius), off_brush_.Get());
- }
- });
- }
-
- void ToggleButton::OnMouseClickCore(events::MouseButtonEventArgs& args)
- {
- Control::OnMouseClickCore(args);
- Toggle();
- }
-
Size ToggleButton::OnMeasureContent(const Size& available_size)
{
const Size result_size(
@@ -121,11 +114,4 @@ namespace cru::ui::controls
return result_size;
}
-
- void ToggleButton::RaiseToggleEvent(bool new_state)
- {
- events::ToggleEventArgs args(this, this, new_state);
- OnToggle(args);
- toggle_event.Raise(args);
- }
}
diff --git a/src/ui/controls/toggle_button.hpp b/src/ui/controls/toggle_button.hpp
index 4cbb4f37..8b7402c8 100644
--- a/src/ui/controls/toggle_button.hpp
+++ b/src/ui/controls/toggle_button.hpp
@@ -40,23 +40,12 @@ namespace cru::ui::controls
void Toggle();
- public:
- events::ToggleEvent toggle_event;
-
- protected:
- virtual void OnToggle(events::ToggleEventArgs& args);
+ Event<events::ToggleEventArgs> toggle_event;
protected:
- void OnDrawContent(ID2D1DeviceContext* device_context) override;
-
- void OnMouseClickCore(events::MouseButtonEventArgs& args) override;
-
Size OnMeasureContent(const Size& available_size) override;
private:
- void RaiseToggleEvent(bool new_state);
-
- private:
bool state_ = false;
float current_circle_position_;
diff --git a/src/ui/events/ui_event.hpp b/src/ui/events/ui_event.hpp
index 321e7135..8380827a 100644
--- a/src/ui/events/ui_event.hpp
+++ b/src/ui/events/ui_event.hpp
@@ -22,7 +22,7 @@ namespace cru::ui::events
{
public:
UiEventArgs(Object* sender, Object* original_sender)
- : BasicEventArgs(sender), original_sender_(original_sender)
+ : BasicEventArgs(sender), original_sender_(original_sender), handled_(false)
{
}
@@ -38,10 +38,40 @@ namespace cru::ui::events
return original_sender_;
}
+ bool IsHandled() const
+ {
+ return handled_;
+ }
+
+ void SetHandled(const bool handled = true)
+ {
+ handled_ = handled;
+ }
+
private:
Object* original_sender_;
+ bool handled_;
};
+ template <typename TEventArgs>
+ class RoutedEvent
+ {
+ public:
+ static_assert(std::is_base_of_v<UiEventArgs, TEventArgs>, "TEventArgs must be subclass of UiEventArgs.");
+
+ using EventArgs = TEventArgs;
+
+ RoutedEvent() = default;
+ RoutedEvent(const RoutedEvent& other) = delete;
+ RoutedEvent(RoutedEvent&& other) = delete;
+ RoutedEvent& operator=(const RoutedEvent& other) = delete;
+ RoutedEvent& operator=(RoutedEvent&& other) = delete;
+ ~RoutedEvent() = default;
+
+ Event<TEventArgs> direct;
+ Event<TEventArgs> bubble;
+ Event<TEventArgs> tunnel;
+ };
class MouseEventArgs : public UiEventArgs
{
@@ -327,17 +357,4 @@ namespace cru::ui::events
private:
wchar_t c_;
};
-
- using UiEvent = Event<UiEventArgs>;
- using MouseEvent = Event<MouseEventArgs>;
- using MouseButtonEvent = Event<MouseButtonEventArgs>;
- using MouseWheelEvent = Event<MouseWheelEventArgs>;
- using DrawEvent = Event<DrawEventArgs>;
- using PositionChangedEvent = Event<PositionChangedEventArgs>;
- using SizeChangedEvent = Event<SizeChangedEventArgs>;
- using FocusChangeEvent = Event<FocusChangeEventArgs>;
- using ToggleEvent = Event<ToggleEventArgs>;
- using WindowNativeMessageEvent = Event<WindowNativeMessageEventArgs>;
- using KeyEvent = Event<KeyEventArgs>;
- using CharEvent = Event<CharEventArgs>;
}
diff --git a/src/ui/window.cpp b/src/ui/window.cpp
index 9352b747..c757c4e1 100644
--- a/src/ui/window.cpp
+++ b/src/ui/window.cpp
@@ -533,11 +533,11 @@ namespace cru::ui
if (focus_control_ == control)
return true;
- DispatchEvent(focus_control_, &Control::RaiseLoseFocusEvent, nullptr, false);
+ DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, false);
focus_control_ = control;
- DispatchEvent(control, &Control::RaiseGetFocusEvent, nullptr, false);
+ DispatchEvent(control, &Control::get_focus_event, nullptr, false);
return true;
}
@@ -656,13 +656,13 @@ namespace cru::ui
void Window::OnSetFocusInternal()
{
window_focus_ = true;
- DispatchEvent(focus_control_, &Control::RaiseGetFocusEvent, nullptr, true);
+ DispatchEvent(focus_control_, &Control::get_focus_event, nullptr, true);
}
void Window::OnKillFocusInternal()
{
window_focus_ = false;
- DispatchEvent(focus_control_, &Control::RaiseLoseFocusEvent, nullptr, true);
+ DispatchEvent(focus_control_, &Control::lose_focus_event, nullptr, true);
}
void Window::OnMouseMoveInternal(const POINT point)
@@ -687,18 +687,18 @@ namespace cru::ui
if (mouse_capture_control_) // if mouse is captured
{
- DispatchEvent(mouse_capture_control_, &Control::RaiseMouseMoveEvent, nullptr, dip_point);
+ DispatchEvent(mouse_capture_control_, &Control::mouse_move_event, nullptr, dip_point);
}
else
{
DispatchMouseHoverControlChangeEvent(old_control_mouse_hover, new_control_mouse_hover, dip_point);
- DispatchEvent(new_control_mouse_hover, &Control::RaiseMouseMoveEvent, nullptr, dip_point);
+ DispatchEvent(new_control_mouse_hover, &Control::mouse_move_event, nullptr, dip_point);
}
}
void Window::OnMouseLeaveInternal()
{
- DispatchEvent(mouse_hover_control_, &Control::RaiseMouseLeaveEvent, nullptr);
+ DispatchEvent(mouse_hover_control_, &Control::mouse_leave_event, nullptr);
mouse_hover_control_ = nullptr;
}
@@ -713,7 +713,7 @@ namespace cru::ui
else
control = HitTest(dip_point);
- DispatchEvent(control, &Control::RaiseMouseDownEvent, nullptr, dip_point, button);
+ DispatchEvent(control, &Control::mouse_down_event, nullptr, dip_point, button);
}
void Window::OnMouseUpInternal(MouseButton button, POINT point)
@@ -727,7 +727,7 @@ namespace cru::ui
else
control = HitTest(dip_point);
- DispatchEvent(control, &Control::RaiseMouseUpEvent, nullptr, dip_point, button);
+ DispatchEvent(control, &Control::mouse_up_event, nullptr, dip_point, button);
}
void Window::OnMouseWheelInternal(short delta, POINT point)
@@ -741,22 +741,22 @@ namespace cru::ui
else
control = HitTest(dip_point);
- DispatchEvent(control, &Control::RaiseMouseWheelEvent, nullptr, dip_point, static_cast<float>(delta));
+ DispatchEvent(control, &Control::mouse_wheel_event, nullptr, dip_point, static_cast<float>(delta));
}
void Window::OnKeyDownInternal(int virtual_code)
{
- DispatchEvent(focus_control_, &Control::RaiseKeyDownEvent, nullptr, virtual_code);
+ DispatchEvent(focus_control_, &Control::key_down_event, nullptr, virtual_code);
}
void Window::OnKeyUpInternal(int virtual_code)
{
- DispatchEvent(focus_control_, &Control::RaiseKeyUpEvent, nullptr, virtual_code);
+ DispatchEvent(focus_control_, &Control::key_up_event, nullptr, virtual_code);
}
void Window::OnCharInternal(wchar_t c)
{
- DispatchEvent(focus_control_, &Control::RaiseCharEvent, nullptr, c);
+ DispatchEvent(focus_control_, &Control::char_event, nullptr, c);
}
void Window::OnActivatedInternal()
@@ -777,10 +777,10 @@ namespace cru::ui
{
const auto lowest_common_ancestor = FindLowestCommonAncestor(old_control, new_control);
if (old_control != nullptr) // if last mouse-hover-on control exists
- DispatchEvent(old_control, &Control::RaiseMouseLeaveEvent, lowest_common_ancestor); // dispatch mouse leave event.
+ DispatchEvent(old_control, &Control::mouse_leave_event, lowest_common_ancestor); // dispatch mouse leave event.
if (new_control != nullptr)
{
- DispatchEvent(new_control, &Control::RaiseMouseEnterEvent, lowest_common_ancestor, point); // dispatch mouse enter event.
+ DispatchEvent(new_control, &Control::mouse_enter_event, lowest_common_ancestor, point); // dispatch mouse enter event.
UpdateCursor();
}
}
diff --git a/src/ui/window.hpp b/src/ui/window.hpp
index e82aa585..d7301eb5 100644
--- a/src/ui/window.hpp
+++ b/src/ui/window.hpp
@@ -4,6 +4,7 @@
#include "pre.hpp"
#include "system_headers.hpp"
+#include <list>
#include <map>
#include <memory>
@@ -241,10 +242,10 @@ namespace cru::ui
public:
//*************** region: events ***************
- events::UiEvent activated_event;
- events::UiEvent deactivated_event;
-
- events::WindowNativeMessageEvent native_message_event;
+ Event<events::UiEventArgs> activated_event;
+ Event<events::UiEventArgs> deactivated_event;
+
+ Event<events::WindowNativeMessageEventArgs> native_message_event;
private:
//*************** region: native operations ***************
@@ -281,30 +282,6 @@ namespace cru::ui
//*************** region: event dispatcher helper ***************
- template<typename EventArgs>
- using EventMethod = void (Control::*)(EventArgs&);
-
- // Dispatch the event.
- //
- // This will invoke the "event_method" of the control and its parent and parent's
- // parent ... (until "last_receiver" if it's not nullptr) with appropriate args.
- //
- // 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* original_sender, EventMethod<EventArgs> event_method, Control* last_receiver, Args&&... args)
- {
- auto control = original_sender;
- while (control != nullptr && control != last_receiver)
- {
- EventArgs event_args(control, original_sender, std::forward<Args>(args)...);
- (control->*event_method)(event_args);
- control = control->GetParent();
- }
- }
-
void DispatchMouseHoverControlChangeEvent(Control* old_control, Control * new_control, const Point& point);
private: