diff options
author | crupest <crupest@outlook.com> | 2018-11-28 19:18:00 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2018-11-28 19:18:00 +0800 |
commit | f78458173c1baf567cc96880571b380e95a1039a (patch) | |
tree | 5bf790a573cff30071628329e1457e3affa0c2bc /src/ui/controls | |
parent | ee22597122612cd75fe62f5d808cb51478373fad (diff) | |
download | cru-f78458173c1baf567cc96880571b380e95a1039a.tar.gz cru-f78458173c1baf567cc96880571b380e95a1039a.tar.bz2 cru-f78458173c1baf567cc96880571b380e95a1039a.zip |
Refactor event system.
Diffstat (limited to 'src/ui/controls')
-rw-r--r-- | src/ui/controls/list_item.cpp | 65 | ||||
-rw-r--r-- | src/ui/controls/list_item.hpp | 7 | ||||
-rw-r--r-- | src/ui/controls/popup_menu.cpp | 4 | ||||
-rw-r--r-- | src/ui/controls/scroll_control.cpp | 245 | ||||
-rw-r--r-- | src/ui/controls/scroll_control.hpp | 8 | ||||
-rw-r--r-- | src/ui/controls/text_box.cpp | 220 | ||||
-rw-r--r-- | src/ui/controls/text_box.hpp | 8 | ||||
-rw-r--r-- | src/ui/controls/text_control.cpp | 208 | ||||
-rw-r--r-- | src/ui/controls/text_control.hpp | 13 | ||||
-rw-r--r-- | src/ui/controls/toggle_button.cpp | 74 | ||||
-rw-r--r-- | src/ui/controls/toggle_button.hpp | 13 |
11 files changed, 395 insertions, 470 deletions
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_; |