diff options
Diffstat (limited to 'CruUI-Generate')
| -rw-r--r-- | CruUI-Generate/cru_ui.cpp | 1244 | ||||
| -rw-r--r-- | CruUI-Generate/cru_ui.hpp | 312 | 
2 files changed, 635 insertions, 921 deletions
| diff --git a/CruUI-Generate/cru_ui.cpp b/CruUI-Generate/cru_ui.cpp index afd588d6..2a1d93e1 100644 --- a/CruUI-Generate/cru_ui.cpp +++ b/CruUI-Generate/cru_ui.cpp @@ -352,7 +352,7 @@ int APIENTRY wWinMain(      //test 2      const auto layout = CreateWithLayout<LinearLayout>(LayoutSideParams::Exactly(500), LayoutSideParams::Content()); -    layout->mouse_click_event.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args) +    layout->mouse_click_event.bubble.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args)      {          if (args.GetSender() == args.GetOriginalSender())              layout->AddChild(TextBlock::Create(L"Layout is clicked!")); @@ -381,7 +381,7 @@ int APIENTRY wWinMain(          const auto button = Button::Create();          button->GetLayoutParams()->padding = Thickness(20, 5);          button->AddChild(TextBlock::Create(L"Show popup window parenting this.")); -        button->mouse_click_event.AddHandler([window, button](auto) +        button->mouse_click_event.bubble.AddHandler([window, button](auto)          {              std::vector<cru::ui::controls::MenuItemInfo> items;              items.emplace_back(L"Hello world!", []{}); @@ -398,7 +398,7 @@ int APIENTRY wWinMain(          button->GetLayoutParams()->padding = Thickness(20, 5);          button->AddChild(TextBlock::Create(L"Show popup window parenting null."));          button->SetBackgroundBrush(cru::graph::CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gold))); -        button->mouse_click_event.AddHandler([](auto) +        button->mouse_click_event.bubble.AddHandler([](auto)          {              auto popup = Window::CreatePopup(nullptr);              popup->SetWindowRect(Rect(100, 100, 300, 300)); @@ -411,7 +411,7 @@ int APIENTRY wWinMain(          const auto button = Button::Create();          button->GetLayoutParams()->padding = Thickness(20, 5);          button->AddChild(TextBlock::Create(L"Show popup window with caption.")); -        button->mouse_click_event.AddHandler([](auto) +        button->mouse_click_event.bubble.AddHandler([](auto)          {              auto popup = Window::CreatePopup(nullptr, true);              popup->SetWindowRect(Rect(100, 100, 300, 300)); @@ -423,7 +423,7 @@ int APIENTRY wWinMain(      {          const auto text_block = CreateWithLayout<TextBlock>(LayoutSideParams::Exactly(200), LayoutSideParams::Exactly(80), L"Hello World!!!"); -        text_block->mouse_click_event.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args) +        text_block->mouse_click_event.bubble.AddHandler([layout](cru::ui::events::MouseButtonEventArgs& args)          {              layout->AddChild(TextBlock::Create(L"Hello world is clicked!"));          }); @@ -435,6 +435,11 @@ int APIENTRY wWinMain(          const auto text_box = TextBox::Create();          text_box->GetLayoutParams()->width.min = 50.0f;          text_box->GetLayoutParams()->width.max = 100.0f; +        text_box->char_event.tunnel.AddHandler([](cru::ui::events::CharEventArgs& args) +        { +            if (args.GetChar() == L'1') +                args.SetHandled(); +        });          layout->AddChild(text_box);      } @@ -821,12 +826,52 @@ namespace cru::ui  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) @@ -968,12 +1013,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();      } @@ -1281,8 +1355,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);              }); @@ -1291,8 +1364,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);              }); @@ -1303,85 +1375,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()) @@ -1443,223 +1441,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) @@ -1667,11 +1454,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(); @@ -1877,8 +1659,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_;          }      } @@ -2736,11 +2518,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;      } @@ -2859,13 +2641,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) @@ -2890,18 +2672,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;      } @@ -2916,7 +2698,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) @@ -2930,7 +2712,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) @@ -2944,22 +2726,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() @@ -2980,10 +2762,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();              }          } @@ -3398,49 +3180,50 @@ 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; -    } -    StringView ListItem::GetControlType() const -    { -        return control_type; -    } +        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); +        }); -    void ListItem::SetState(const State state) -    { -        state_ = state; -        InvalidateDraw(); -    } +        mouse_enter_event.direct.AddHandler([this](events::MouseEventArgs& args) +        { +            if (GetState() == State::Select) +                return; -    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); -    } +            if (IsAnyMouseButtonDown()) +                return; -    void ListItem::OnMouseEnterCore(events::MouseEventArgs& args) -    { -        if (GetState() == State::Select) -            return; +            SetState(State::Hover); +        }); -        if (IsAnyMouseButtonDown()) -            return; +        mouse_leave_event.direct.AddHandler([this](events::MouseEventArgs& args) +        { +            if (GetState() == State::Select) +                return; -        SetState(State::Hover); +            SetState(State::Normal); +        }); + +        mouse_click_event.direct.AddHandler([this](events::MouseButtonEventArgs& args) +        { +            if (args.GetMouseButton() == MouseButton::Left) +                SetState(State::Select); +        });      } -    void ListItem::OnMouseLeaveCore(events::MouseEventArgs& args) +    StringView ListItem::GetControlType() const      { -        if (GetState() == State::Select) -            return; - -        SetState(State::Normal); +        return control_type;      } -    void ListItem::OnMouseClickCore(events::MouseButtonEventArgs& args) +    void ListItem::SetState(const State state)      { -        if (args.GetMouseButton() == MouseButton::Left) -            SetState(State::Select); +        state_ = state; +        InvalidateDraw();      }  }  //-------------------------------------------------------- @@ -3457,7 +3240,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(); @@ -3474,7 +3257,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)                  { @@ -3518,6 +3301,131 @@ 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.tunnel.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; +                    args.SetHandled(); +                    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; +                    args.SetHandled(); +                    return; +                } +            } +        }); + +        mouse_move_event.tunnel.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); +                args.SetHandled(); +                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); +                args.SetHandled(); +                return; +            } +        }); + +        mouse_up_event.tunnel.AddHandler([this](events::MouseButtonEventArgs& args) +        { +            if (args.GetMouseButton() == MouseButton::Left && is_pressing_scroll_bar_.has_value()) +            { +                GetWindow()->ReleaseCurrentMouseCapture(); +                is_pressing_scroll_bar_ = std::nullopt; +                args.SetHandled(); +            } +        }); + +        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); +                args.SetHandled(); +                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); +                args.SetHandled(); +                return; +            } +        });      }      ScrollControl::~ScrollControl() @@ -3705,133 +3613,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_; @@ -3946,149 +3727,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.direct.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.direct.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 (GetCaretSelectionSide()) -                    ShiftLeftSelectionRange(1); -                else -                    ShiftRightSelectionRange(1); -            } -            else +            if (args.GetVirtualCode() == VK_RIGHT && caret_position_ < GetText().size())              { -                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()) +            if (args.GetChar() == L'\b')              { -                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 (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) @@ -4138,6 +3915,40 @@ namespace cru::ui::controls  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)      { @@ -4150,6 +3961,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.direct.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(); +            } +        });      } @@ -4204,112 +4083,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)); @@ -4407,13 +4180,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; @@ -4424,9 +4218,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;      } @@ -4458,7 +4252,8 @@ namespace cru::ui::controls                  })                  .Start(); -            RaiseToggleEvent(state); +            events::ToggleEventArgs args(this, this, state); +            toggle_event.Raise(args);              InvalidateDraw();          }      } @@ -4468,36 +4263,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( @@ -4507,13 +4272,6 @@ 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); -    }  }  //--------------------------------------------------------  //-------end of file: src\ui\controls\toggle_button.cpp diff --git a/CruUI-Generate/cru_ui.hpp b/CruUI-Generate/cru_ui.hpp index 6dff57cd..1912e243 100644 --- a/CruUI-Generate/cru_ui.hpp +++ b/CruUI-Generate/cru_ui.hpp @@ -586,6 +586,7 @@ namespace cru  // ReSharper disable once CppUnusedIncludeDirective +#include <list>  #include <map>  #include <memory> @@ -996,7 +997,7 @@ namespace cru::ui  #include <type_traits>  #include <functional> -#include <unordered_map> +#include <map>  namespace cru { @@ -1066,13 +1067,15 @@ namespace cru {  				(handler.second)(args);  		} + +        //TODO: Remove this!          bool IsNoHandler() const  		{  		    return handlers_.empty();  		}      private: -        std::unordered_map<EventHandlerToken, EventHandler> handlers_; +        std::map<EventHandlerToken, EventHandler> handlers_;          EventHandlerToken current_token_ = 0;      }; @@ -1092,7 +1095,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)          {          } @@ -1108,10 +1111,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      { @@ -1397,19 +1430,6 @@ 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>;  }  //--------------------------------------------------------  //-------end of file: src\ui\events\ui_event.hpp @@ -1782,34 +1802,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); @@ -1821,32 +1842,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 @@ -1854,63 +1858,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); @@ -1949,8 +1909,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 }, @@ -1983,6 +1941,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); @@ -2001,6 +2024,8 @@ namespace cru::ui      } +    //*************** region: create helper *************** +      template <typename TControl, typename... Args>      TControl* CreateWithLayout(const Thickness& padding, const Thickness& margin, Args&&... args)      { @@ -2260,10 +2285,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 *************** @@ -2300,30 +2325,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: @@ -2522,18 +2523,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); @@ -2639,23 +2629,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_; @@ -2747,14 +2726,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: @@ -2831,13 +2802,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_{}; @@ -3034,14 +2998,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(); | 
