From 4a0c86d94a06e72be0988062d49a19c05142434a Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 27 Feb 2021 22:16:28 +0800 Subject: ... --- src/ui/render/ScrollBar.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/ui/render/ScrollBar.cpp (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp new file mode 100644 index 00000000..487f0b91 --- /dev/null +++ b/src/ui/render/ScrollBar.cpp @@ -0,0 +1,143 @@ +#include "cru/ui/render/ScrollBar.hpp" + +#include "../Helper.hpp" +#include "cru/common/Base.hpp" +#include "cru/platform/GraphBase.hpp" +#include "cru/platform/graphics/Factory.hpp" +#include "cru/platform/graphics/Painter.hpp" +#include "cru/ui/render/ScrollRenderObject.hpp" + +#include +#include + +namespace cru::ui::render { +constexpr float kScrollBarCollapseThumbWidth = 2; + +ScrollBar::ScrollBar(gsl::not_null render_object) + : render_object_(render_object) { + // TODO: Use theme resource and delete this. + auto collapse_thumb_brush = GetUiApplication() + ->GetInstance() + ->GetGraphFactory() + ->CreateSolidColorBrush(); + collapse_thumb_brush->SetColor(colors::gray.WithAlpha(128)); + collapse_thumb_brush_ = std::move(collapse_thumb_brush); +} + +void ScrollBar::SetEnabled(bool value) { + CRU_UNUSED(value) + // TODO: Implement this. +} + +void ScrollBar::Draw(platform::graphics::IPainter* painter) { + if (is_enabled_) { + OnDraw(painter, is_expanded_); + } +} + +void ScrollBar::InstallHandlers(controls::Control* control) { + CRU_UNUSED(control); + // TODO: Implement this. +} + +gsl::not_null> +ScrollBar::GetCollapseThumbBrush() const { + // TODO: Read theme resource. + return collapse_thumb_brush_; +} + +HorizontalScrollBar::HorizontalScrollBar( + gsl::not_null render_object) + : ScrollBar(render_object) {} + +std::optional HorizontalScrollBar::HitTest( + const Point& point) { + // TODO: Implement this. + CRU_UNUSED(point); + return std::nullopt; +} + +void HorizontalScrollBar::OnDraw(platform::graphics::IPainter* painter, + bool expand) { + const auto child = render_object_->GetFirstChild(); + if (child == nullptr) return; + + const auto view_rect = render_object_->GetViewRect(); + const auto padding_rect = render_object_->GetPaddingRect(); + const auto child_size = child->GetSize(); + + if (view_rect.width >= child_size.width) return; + + const float start_percentage = view_rect.left / child_size.width; + const float length_percentage = view_rect.width / child_size.width; + // const float end_percentage = start_percentage + length_percentage; + + if (expand) { + // TODO: Implement this. + } else { + Rect thumb_rect{padding_rect.left + padding_rect.width * start_percentage, + padding_rect.GetBottom() - kScrollBarCollapseThumbWidth, + padding_rect.width * length_percentage, + kScrollBarCollapseThumbWidth}; + painter->FillRectangle(thumb_rect, GetCollapseThumbBrush().get().get()); + } +} + +VerticalScrollBar::VerticalScrollBar( + gsl::not_null render_object) + : ScrollBar(render_object) {} + +std::optional VerticalScrollBar::HitTest( + const Point& point) { + // TODO: Implement this. + CRU_UNUSED(point); + return std::nullopt; +} + +void VerticalScrollBar::OnDraw(platform::graphics::IPainter* painter, + bool expand) { + const auto child = render_object_->GetFirstChild(); + if (child == nullptr) return; + + const auto view_rect = render_object_->GetViewRect(); + const auto padding_rect = render_object_->GetPaddingRect(); + const auto child_size = child->GetSize(); + + if (view_rect.height >= child_size.height) return; + + const float start_percentage = view_rect.top / child_size.height; + const float length_percentage = view_rect.height / child_size.height; + // const float end_percentage = start_percentage + length_percentage; + + if (expand) { + // TODO: Implement this. + } else { + Rect thumb_rect{padding_rect.GetRight() - kScrollBarCollapseThumbWidth, + padding_rect.top + padding_rect.height * start_percentage, + kScrollBarCollapseThumbWidth, + padding_rect.height * length_percentage}; + painter->FillRectangle(thumb_rect, GetCollapseThumbBrush().get().get()); + } +} + +ScrollBarDelegate::ScrollBarDelegate( + gsl::not_null render_object) + : render_object_(render_object), + horizontal_bar_(render_object), + vertical_bar_(render_object) { + horizontal_bar_.ScrollAttemptEvent()->AddHandler( + [this](auto scroll) { this->scroll_attempt_event_.Raise(scroll); }); + vertical_bar_.ScrollAttemptEvent()->AddHandler( + [this](auto scroll) { this->scroll_attempt_event_.Raise(scroll); }); +} + +void ScrollBarDelegate::DrawScrollBar(platform::graphics::IPainter* painter) { + horizontal_bar_.Draw(painter); + vertical_bar_.Draw(painter); +} + +void ScrollBarDelegate::InstallHandlers(controls::Control* control) { + horizontal_bar_.InstallHandlers(control); + vertical_bar_.InstallHandlers(control); +} +} // namespace cru::ui::render -- cgit v1.2.3 From 4b78e0b74f70bca2e24dc89b4fdca4dc9222c8b9 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 28 Feb 2021 00:22:34 +0800 Subject: ... --- include/cru/platform/graphics/Factory.hpp | 8 +- include/cru/ui/controls/TextHostControlService.hpp | 2 +- include/cru/ui/events/UiEvent.hpp | 1 + include/cru/ui/render/ScrollBar.hpp | 93 ++++- include/cru/ui/render/ScrollRenderObject.hpp | 15 + src/ui/events/UiEvent.cpp | 4 + src/ui/render/ScrollBar.cpp | 446 ++++++++++++++++++--- src/ui/render/ScrollRenderObject.cpp | 33 ++ 8 files changed, 532 insertions(+), 70 deletions(-) (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/include/cru/platform/graphics/Factory.hpp b/include/cru/platform/graphics/Factory.hpp index d1b37783..f9018e13 100644 --- a/include/cru/platform/graphics/Factory.hpp +++ b/include/cru/platform/graphics/Factory.hpp @@ -21,5 +21,11 @@ struct IGraphFactory : virtual INativeResource { virtual std::unique_ptr CreateTextLayout( std::shared_ptr font, std::u16string text) = 0; + + std::unique_ptr CreateSolidColorBrush(const Color& color) { + std::unique_ptr brush = CreateSolidColorBrush(); + brush->SetColor(color); + return brush; + } }; -} // namespace cru::platform::graph +} // namespace cru::platform::graphics diff --git a/include/cru/ui/controls/TextHostControlService.hpp b/include/cru/ui/controls/TextHostControlService.hpp index 9e6a08bc..340228fe 100644 --- a/include/cru/ui/controls/TextHostControlService.hpp +++ b/include/cru/ui/controls/TextHostControlService.hpp @@ -100,7 +100,7 @@ class TextHostControlService : public Object { void SetupOneHandler(event::RoutedEvent* (Control::*event)(), void (TextHostControlService::*handler)( typename event::RoutedEvent::EventArgs)) { - this->event_guard_ += (this->control_->*event)()->Direct()->AddHandler( + this->event_guard_ += (this->control_->*event)()->Bubble()->AddHandler( std::bind(handler, this, std::placeholders::_1)); } diff --git a/include/cru/ui/events/UiEvent.hpp b/include/cru/ui/events/UiEvent.hpp index 660b33f5..22ad0150 100644 --- a/include/cru/ui/events/UiEvent.hpp +++ b/include/cru/ui/events/UiEvent.hpp @@ -84,6 +84,7 @@ class MouseEventArgs : public UiEventArgs { // This point is relative to window client lefttop. Point GetPoint() const { return point_.value_or(Point{}); } + Point GetPoint(render::RenderObject* render_target) const; Point GetPointToContent(render::RenderObject* render_target) const; private: diff --git a/include/cru/ui/render/ScrollBar.hpp b/include/cru/ui/render/ScrollBar.hpp index a9be49a3..e3dabb57 100644 --- a/include/cru/ui/render/ScrollBar.hpp +++ b/include/cru/ui/render/ScrollBar.hpp @@ -15,25 +15,27 @@ namespace cru::ui::render { class ScrollRenderObject; -enum class ScrollKind { Absolute, Page, Line }; +enum class ScrollKind { Absolute, Relative, Page, Line }; struct Scroll { Direction direction; ScrollKind kind; - float offset; + // For absolute, the new scroll position. Otherwise, offset. + float value; }; enum class ScrollBarAreaKind { UpArrow, // Line up DownArrow, // Line down - UpThumb, // Page up - DownThumb, // Page down + UpSlot, // Page up + DownSlot, // Page down Thumb }; class ScrollBar : public Object { public: - explicit ScrollBar(gsl::not_null render_object); + ScrollBar(gsl::not_null render_object, + Direction direction); CRU_DELETE_COPY(ScrollBar) CRU_DELETE_MOVE(ScrollBar) @@ -41,12 +43,15 @@ class ScrollBar : public Object { ~ScrollBar() override = default; public: + Direction GetDirection() const { return direction_; } + bool IsEnabled() const { return is_enabled_; } void SetEnabled(bool value); - void Draw(platform::graphics::IPainter* painter); + bool IsExpanded() const { return is_expanded_; } + void SetExpanded(bool value); - virtual std::optional HitTest(const Point& point) = 0; + void Draw(platform::graphics::IPainter* painter); IEvent* ScrollAttemptEvent() { return &scroll_attempt_event_; } @@ -54,20 +59,54 @@ class ScrollBar : public Object { void UninstallHandlers() { InstallHandlers(nullptr); } gsl::not_null> - GetCollapseThumbBrush() const; + GetCollapsedThumbBrush() const; + gsl::not_null> + GetExpandedThumbBrush() const; + gsl::not_null> + GetExpandedSlotBrush() const; + gsl::not_null> + GetExpandedArrowBrush() const; + gsl::not_null> + GetExpandedArrowBackgroundBrush() const; protected: - virtual void OnDraw(platform::graphics::IPainter* painter, bool expand) = 0; + void OnDraw(platform::graphics::IPainter* painter, bool expand); + + virtual void DrawUpArrow(platform::graphics::IPainter* painter, + const Rect& area) = 0; + virtual void DrawDownArrow(platform::graphics::IPainter* painter, + const Rect& area) = 0; + + std::optional ExpandedHitTest(const Point& point); + + virtual bool IsShowBar() = 0; + + virtual std::optional GetExpandedAreaRect( + ScrollBarAreaKind area_kind) = 0; + virtual std::optional GetCollapsedTriggerExpandAreaRect() = 0; + virtual std::optional GetCollapsedThumbRect() = 0; + + virtual float CalculateNewScrollPosition(const Rect& thumb_original_rect, + const Point& mouse_offset) = 0; protected: gsl::not_null render_object_; private: + Direction direction_; + bool is_enabled_ = true; bool is_expanded_ = false; - std::shared_ptr collapse_thumb_brush_; + std::shared_ptr collapsed_thumb_brush_; + std::shared_ptr expanded_thumb_brush_; + std::shared_ptr expanded_slot_brush_; + std::shared_ptr expanded_arrow_brush_; + std::shared_ptr expanded_arrow_background_brush_; + + Rect move_thumb_thumb_original_rect_; + std::optional move_thumb_start_; EventRevokerListGuard event_guard_; @@ -84,11 +123,20 @@ class HorizontalScrollBar : public ScrollBar { ~HorizontalScrollBar() override = default; - public: - std::optional HitTest(const Point& point) override; - protected: - void OnDraw(platform::graphics::IPainter* painter, bool expand) override; + void DrawUpArrow(platform::graphics::IPainter* painter, + const Rect& area) override; + void DrawDownArrow(platform::graphics::IPainter* painter, + const Rect& area) override; + + bool IsShowBar() override; + + std::optional GetExpandedAreaRect(ScrollBarAreaKind area_kind) override; + std::optional GetCollapsedTriggerExpandAreaRect() override; + std::optional GetCollapsedThumbRect() override; + + float CalculateNewScrollPosition(const Rect& thumb_original_rect, + const Point& mouse_offset) override; }; class VerticalScrollBar : public ScrollBar { @@ -100,11 +148,20 @@ class VerticalScrollBar : public ScrollBar { ~VerticalScrollBar() override = default; - public: - std::optional HitTest(const Point& point) override; - protected: - void OnDraw(platform::graphics::IPainter* painter, bool expand) override; + void DrawUpArrow(platform::graphics::IPainter* painter, + const Rect& area) override; + void DrawDownArrow(platform::graphics::IPainter* painter, + const Rect& area) override; + + bool IsShowBar() override; + + std::optional GetExpandedAreaRect(ScrollBarAreaKind area_kind) override; + std::optional GetCollapsedTriggerExpandAreaRect() override; + std::optional GetCollapsedThumbRect() override; + + float CalculateNewScrollPosition(const Rect& thumb_original_rect, + const Point& mouse_offset) override; }; // A delegate to draw scrollbar and register related events. diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp index 6a6ef198..aed25f8e 100644 --- a/include/cru/ui/render/ScrollRenderObject.hpp +++ b/include/cru/ui/render/ScrollRenderObject.hpp @@ -2,6 +2,7 @@ #include "RenderObject.hpp" #include "cru/platform/graphics/util/Painter.hpp" +#include "cru/ui/Base.hpp" #include "cru/ui/render/ScrollBar.hpp" #include @@ -29,8 +30,22 @@ class ScrollRenderObject : public RenderObject { // Return the coerced scroll offset. Point GetScrollOffset(); + float GetScrollOffset(Direction direction) { + return direction == Direction::Horizontal ? GetScrollOffset().x + : GetScrollOffset().y; + } void SetScrollOffset(const Point& offset); void SetScrollOffset(std::optional x, std::optional y); + void SetScrollOffset(Direction direction, std::optional value) { + if (direction == Direction::Horizontal) { + SetScrollOffset(value, std::nullopt); + } else { + SetScrollOffset(std::nullopt, value); + } + } + + void Scroll(const Scroll& scroll); + Point GetRawScrollOffset() const { return scroll_offset_; } // Return the viewable area rect. diff --git a/src/ui/events/UiEvent.cpp b/src/ui/events/UiEvent.cpp index b35f15a7..4c75f690 100644 --- a/src/ui/events/UiEvent.cpp +++ b/src/ui/events/UiEvent.cpp @@ -3,6 +3,10 @@ #include "cru/ui/render/RenderObject.hpp" namespace cru::ui::event { +Point MouseEventArgs::GetPoint(render::RenderObject* render_object) const { + return GetPoint() - render_object->GetTotalOffset(); +} + Point MouseEventArgs::GetPointToContent( render::RenderObject* render_object) const { return render_object->FromRootToContent(GetPoint()); diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index 487f0b91..6096ab63 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -5,23 +5,44 @@ #include "cru/platform/GraphBase.hpp" #include "cru/platform/graphics/Factory.hpp" #include "cru/platform/graphics/Painter.hpp" +#include "cru/platform/gui/Base.hpp" +#include "cru/ui/Base.hpp" +#include "cru/ui/events/UiEvent.hpp" #include "cru/ui/render/ScrollRenderObject.hpp" +#include "gsl/gsl_assert" +#include +#include #include #include +#include namespace cru::ui::render { constexpr float kScrollBarCollapseThumbWidth = 2; +constexpr float kScrollBarCollapsedTriggerExpandAreaWidth = 5; +constexpr float kScrollBarExpandWidth = 10; -ScrollBar::ScrollBar(gsl::not_null render_object) - : render_object_(render_object) { +constexpr std::array kScrollBarAreaKindList{ + ScrollBarAreaKind::UpArrow, ScrollBarAreaKind::DownArrow, + ScrollBarAreaKind::UpSlot, ScrollBarAreaKind::DownSlot, + ScrollBarAreaKind::Thumb}; + +ScrollBar::ScrollBar(gsl::not_null render_object, + Direction direction) + : render_object_(render_object), direction_(direction) { // TODO: Use theme resource and delete this. - auto collapse_thumb_brush = GetUiApplication() - ->GetInstance() - ->GetGraphFactory() - ->CreateSolidColorBrush(); - collapse_thumb_brush->SetColor(colors::gray.WithAlpha(128)); - collapse_thumb_brush_ = std::move(collapse_thumb_brush); + + auto graphics_factory = GetUiApplication()->GetInstance()->GetGraphFactory(); + + collapsed_thumb_brush_ = + graphics_factory->CreateSolidColorBrush(colors::gray.WithAlpha(128)); + expanded_thumb_brush_ = graphics_factory->CreateSolidColorBrush(colors::gray); + expanded_slot_brush_ = + graphics_factory->CreateSolidColorBrush(colors::seashell); + expanded_arrow_brush_ = + graphics_factory->CreateSolidColorBrush(colors::white); + expanded_arrow_background_brush_ = + graphics_factory->CreateSolidColorBrush(colors::black); } void ScrollBar::SetEnabled(bool value) { @@ -29,6 +50,12 @@ void ScrollBar::SetEnabled(bool value) { // TODO: Implement this. } +void ScrollBar::SetExpanded(bool value) { + if (is_expanded_ == value) return; + is_expanded_ = value; + render_object_->InvalidatePaint(); +} + void ScrollBar::Draw(platform::graphics::IPainter* painter) { if (is_enabled_) { OnDraw(painter, is_expanded_); @@ -36,88 +63,407 @@ void ScrollBar::Draw(platform::graphics::IPainter* painter) { } void ScrollBar::InstallHandlers(controls::Control* control) { - CRU_UNUSED(control); - // TODO: Implement this. + event_guard_.Clear(); + if (control != nullptr) { + event_guard_ += control->MouseDownEvent()->Bubble()->AddHandler( + [control, this](event::MouseButtonEventArgs& event) { + if (event.GetButton() == mouse_buttons::left && IsEnabled() && + IsExpanded()) { + auto hit_test_result = + ExpandedHitTest(event.GetPoint(render_object_)); + if (!hit_test_result) return; + + switch (*hit_test_result) { + case ScrollBarAreaKind::UpArrow: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Line, -1}); + event.SetHandled(); + break; + case ScrollBarAreaKind::DownArrow: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Line, 1}); + event.SetHandled(); + break; + case ScrollBarAreaKind::UpSlot: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Page, -1}); + event.SetHandled(); + break; + case ScrollBarAreaKind::DownSlot: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Page, 1}); + event.SetHandled(); + break; + case ScrollBarAreaKind::Thumb: { + auto thumb_rect = GetExpandedAreaRect(ScrollBarAreaKind::Thumb); + assert(thumb_rect); + + if (!control->CaptureMouse()) break; + move_thumb_thumb_original_rect_ = *thumb_rect; + move_thumb_start_ = event.GetPoint(); + event.SetHandled(); + break; + } + default: + break; + } + } + }); + + event_guard_ += control->MouseUpEvent()->Bubble()->AddHandler( + [control, this](event::MouseButtonEventArgs& event) { + if (event.GetButton() == mouse_buttons::left && move_thumb_start_) { + move_thumb_start_ = std::nullopt; + control->ReleaseMouse(); + event.SetHandled(); + } + }); + + event_guard_ += control->MouseMoveEvent()->Bubble()->AddHandler( + [this](event::MouseEventArgs& event) { + if (move_thumb_start_) { + auto new_scroll_position = CalculateNewScrollPosition( + move_thumb_thumb_original_rect_, + event.GetPoint() - *move_thumb_start_); + + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Absolute, new_scroll_position}); + event.SetHandled(); + return; + } + + if (IsEnabled() && !IsExpanded()) { + auto trigger_expand_area = GetCollapsedTriggerExpandAreaRect(); + if (trigger_expand_area && + trigger_expand_area->IsPointInside( + event.GetPoint(this->render_object_))) + SetExpanded(true); + event.SetHandled(); + } + }); + } } gsl::not_null> -ScrollBar::GetCollapseThumbBrush() const { +ScrollBar::GetCollapsedThumbBrush() const { // TODO: Read theme resource. - return collapse_thumb_brush_; + return collapsed_thumb_brush_; } -HorizontalScrollBar::HorizontalScrollBar( - gsl::not_null render_object) - : ScrollBar(render_object) {} +gsl::not_null> +ScrollBar::GetExpandedThumbBrush() const { + // TODO: Read theme resource. + return expanded_thumb_brush_; +} + +gsl::not_null> +ScrollBar::GetExpandedSlotBrush() const { + // TODO: Read theme resource. + return expanded_slot_brush_; +} + +gsl::not_null> +ScrollBar::GetExpandedArrowBrush() const { + // TODO: Read theme resource. + return expanded_arrow_brush_; +} + +gsl::not_null> +ScrollBar::GetExpandedArrowBackgroundBrush() const { + // TODO: Read theme resource. + return expanded_arrow_brush_; +} + +void ScrollBar::OnDraw(platform::graphics::IPainter* painter, + bool is_expanded) { + if (is_expanded) { + auto thumb_rect = GetExpandedAreaRect(ScrollBarAreaKind::Thumb); + if (thumb_rect) + painter->FillRectangle(*thumb_rect, GetExpandedThumbBrush().get().get()); + + auto slot_brush = GetExpandedSlotBrush().get().get(); + + auto up_slot_rect = GetExpandedAreaRect(ScrollBarAreaKind::UpSlot); + if (up_slot_rect) painter->FillRectangle(*up_slot_rect, slot_brush); + + auto down_slot_rect = GetExpandedAreaRect(ScrollBarAreaKind::DownSlot); + if (down_slot_rect) painter->FillRectangle(*down_slot_rect, slot_brush); + + auto up_arrow = GetExpandedAreaRect(ScrollBarAreaKind::UpArrow); + if (up_arrow) this->DrawUpArrow(painter, *up_arrow); -std::optional HorizontalScrollBar::HitTest( + auto down_arrow = GetExpandedAreaRect(ScrollBarAreaKind::DownArrow); + if (down_arrow) this->DrawUpArrow(painter, *down_arrow); + } else { + auto optional_rect = GetCollapsedThumbRect(); + if (optional_rect) { + painter->FillRectangle(*optional_rect, + GetCollapsedThumbBrush().get().get()); + } + } +} + +std::optional ScrollBar::ExpandedHitTest( const Point& point) { - // TODO: Implement this. - CRU_UNUSED(point); + for (auto kind : kScrollBarAreaKindList) { + auto rect = this->GetExpandedAreaRect(kind); + if (rect) { + if (rect->IsPointInside(point)) return kind; + } + } return std::nullopt; } -void HorizontalScrollBar::OnDraw(platform::graphics::IPainter* painter, - bool expand) { +HorizontalScrollBar::HorizontalScrollBar( + gsl::not_null render_object) + : ScrollBar(render_object, Direction::Horizontal) {} + +void HorizontalScrollBar::DrawUpArrow(platform::graphics::IPainter* painter, + const Rect& area) { + // TODO: Do what you must! + painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); +} + +void HorizontalScrollBar::DrawDownArrow(platform::graphics::IPainter* painter, + const Rect& area) { + // TODO: Do what you must! + painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); +} + +bool HorizontalScrollBar::IsShowBar() { const auto child = render_object_->GetFirstChild(); - if (child == nullptr) return; + if (child == nullptr) return false; const auto view_rect = render_object_->GetViewRect(); + const auto child_size = child->GetSize(); + + if (view_rect.width >= child_size.width) return false; + + return true; +} + +std::optional HorizontalScrollBar::GetExpandedAreaRect( + ScrollBarAreaKind area_kind) { + auto show = IsShowBar(); + if (!show) return std::nullopt; + const auto padding_rect = render_object_->GetPaddingRect(); + + const auto child = render_object_->GetFirstChild(); + + const auto view_rect = render_object_->GetViewRect(); const auto child_size = child->GetSize(); - if (view_rect.width >= child_size.width) return; + const float start_percentage = view_rect.left / child_size.width; + const float length_percentage = view_rect.width / child_size.width; + const float end_percentage = start_percentage + length_percentage; + + const float top = padding_rect.GetBottom() - kScrollBarExpandWidth; + const float height = kScrollBarExpandWidth; + + // Without arrow. + const float bar_area_length = padding_rect.width - 3 * kScrollBarExpandWidth; + const float bar_area_start = padding_rect.left + kScrollBarExpandWidth; + + switch (area_kind) { + case ScrollBarAreaKind::UpArrow: + return Rect{padding_rect.left, top, kScrollBarExpandWidth, height}; + case ScrollBarAreaKind::DownArrow: + return Rect{padding_rect.GetRight() - 2 * kScrollBarExpandWidth, top, + kScrollBarExpandWidth, height}; + case ScrollBarAreaKind::UpSlot: + return Rect{bar_area_start, top, bar_area_length * start_percentage, + height}; + case ScrollBarAreaKind::DownSlot: + return Rect{bar_area_start + bar_area_length * end_percentage, top, + bar_area_length * (1 - end_percentage), height}; + case ScrollBarAreaKind::Thumb: + return Rect{bar_area_start + bar_area_length * start_percentage, top, + bar_area_length * length_percentage, height}; + default: + throw std::invalid_argument("Unsupported scroll area kind."); + } +} + +std::optional HorizontalScrollBar::GetCollapsedTriggerExpandAreaRect() { + auto show = IsShowBar(); + if (!show) return std::nullopt; + + const auto padding_rect = render_object_->GetPaddingRect(); + + return Rect{ + padding_rect.left, + padding_rect.GetBottom() - kScrollBarCollapsedTriggerExpandAreaWidth, + padding_rect.width, kScrollBarCollapseThumbWidth}; +} + +std::optional HorizontalScrollBar::GetCollapsedThumbRect() { + auto show = IsShowBar(); + if (!show) return std::nullopt; + + const auto child = render_object_->GetFirstChild(); + + const auto view_rect = render_object_->GetViewRect(); + const auto child_size = child->GetSize(); const float start_percentage = view_rect.left / child_size.width; const float length_percentage = view_rect.width / child_size.width; // const float end_percentage = start_percentage + length_percentage; - if (expand) { - // TODO: Implement this. - } else { - Rect thumb_rect{padding_rect.left + padding_rect.width * start_percentage, - padding_rect.GetBottom() - kScrollBarCollapseThumbWidth, - padding_rect.width * length_percentage, - kScrollBarCollapseThumbWidth}; - painter->FillRectangle(thumb_rect, GetCollapseThumbBrush().get().get()); - } + const auto padding_rect = render_object_->GetPaddingRect(); + + return Rect{padding_rect.left + padding_rect.width * start_percentage, + padding_rect.GetBottom() - kScrollBarCollapseThumbWidth, + padding_rect.width * length_percentage, + kScrollBarCollapseThumbWidth}; +} + +float HorizontalScrollBar::CalculateNewScrollPosition( + const Rect& thumb_original_rect, const Point& mouse_offset) { + auto new_thumb_start = thumb_original_rect.left + mouse_offset.x; + + const auto padding_rect = render_object_->GetPaddingRect(); + + auto scroll_area_start = padding_rect.left + kScrollBarExpandWidth; + auto scroll_area_end = padding_rect.GetRight() - 2 * kScrollBarExpandWidth; + + auto thumb_head_end = scroll_area_end - thumb_original_rect.width; + + const auto child = render_object_->GetFirstChild(); + const auto child_size = child->GetSize(); + + new_thumb_start = + std::clamp(new_thumb_start, scroll_area_start, thumb_head_end); + + auto offset = new_thumb_start / (scroll_area_end - scroll_area_start) * + child_size.width; + + return offset; } VerticalScrollBar::VerticalScrollBar( gsl::not_null render_object) - : ScrollBar(render_object) {} + : ScrollBar(render_object, Direction::Vertical) {} -std::optional VerticalScrollBar::HitTest( - const Point& point) { - // TODO: Implement this. - CRU_UNUSED(point); - return std::nullopt; +void VerticalScrollBar::DrawUpArrow(platform::graphics::IPainter* painter, + const Rect& area) { + // TODO: Do what you must! + painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); +} + +void VerticalScrollBar::DrawDownArrow(platform::graphics::IPainter* painter, + const Rect& area) { + // TODO: Do what you must! + painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); +} + +bool VerticalScrollBar::IsShowBar() { + const auto child = render_object_->GetFirstChild(); + if (child == nullptr) return false; + + const auto view_rect = render_object_->GetViewRect(); + const auto child_size = child->GetSize(); + + if (view_rect.height >= child_size.height) return false; + + return true; } -void VerticalScrollBar::OnDraw(platform::graphics::IPainter* painter, - bool expand) { +std::optional VerticalScrollBar::GetExpandedAreaRect( + ScrollBarAreaKind area_kind) { + auto show = IsShowBar(); + if (!show) return std::nullopt; + + const auto padding_rect = render_object_->GetPaddingRect(); + const auto child = render_object_->GetFirstChild(); - if (child == nullptr) return; + + const auto view_rect = render_object_->GetViewRect(); + const auto child_size = child->GetSize(); + + const float start_percentage = view_rect.top / child_size.height; + const float length_percentage = view_rect.height / child_size.height; + const float end_percentage = start_percentage + length_percentage; + + const float left = padding_rect.GetRight() - kScrollBarExpandWidth; + const float width = kScrollBarExpandWidth; + + // Without arrow. + const float bar_area_length = padding_rect.height - 3 * kScrollBarExpandWidth; + const float bar_area_start = padding_rect.top + kScrollBarExpandWidth; + + switch (area_kind) { + case ScrollBarAreaKind::UpArrow: + return Rect{left, padding_rect.top, width, kScrollBarExpandWidth}; + case ScrollBarAreaKind::DownArrow: + return Rect{left, padding_rect.GetBottom() - 2 * kScrollBarExpandWidth, + width, kScrollBarExpandWidth}; + case ScrollBarAreaKind::UpSlot: + return Rect{left, bar_area_start, width, + bar_area_length * start_percentage}; + case ScrollBarAreaKind::DownSlot: + return Rect{left, bar_area_start + bar_area_length * end_percentage, + width, bar_area_length * (1 - end_percentage)}; + case ScrollBarAreaKind::Thumb: + return Rect{left, bar_area_start + bar_area_length * start_percentage, + width, bar_area_length * length_percentage}; + default: + throw std::invalid_argument("Unsupported scroll area kind."); + } +} + +std::optional VerticalScrollBar::GetCollapsedTriggerExpandAreaRect() { + auto show = IsShowBar(); + if (!show) return std::nullopt; + + const auto padding_rect = render_object_->GetPaddingRect(); + + return Rect{ + padding_rect.GetRight() - kScrollBarCollapsedTriggerExpandAreaWidth, + padding_rect.top, kScrollBarCollapseThumbWidth, padding_rect.height}; +} + +std::optional VerticalScrollBar::GetCollapsedThumbRect() { + const auto child = render_object_->GetFirstChild(); + if (child == nullptr) return std::nullopt; const auto view_rect = render_object_->GetViewRect(); const auto padding_rect = render_object_->GetPaddingRect(); const auto child_size = child->GetSize(); - if (view_rect.height >= child_size.height) return; + if (view_rect.height >= child_size.height) return std::nullopt; const float start_percentage = view_rect.top / child_size.height; const float length_percentage = view_rect.height / child_size.height; // const float end_percentage = start_percentage + length_percentage; - if (expand) { - // TODO: Implement this. - } else { - Rect thumb_rect{padding_rect.GetRight() - kScrollBarCollapseThumbWidth, - padding_rect.top + padding_rect.height * start_percentage, - kScrollBarCollapseThumbWidth, - padding_rect.height * length_percentage}; - painter->FillRectangle(thumb_rect, GetCollapseThumbBrush().get().get()); - } + return Rect{padding_rect.GetRight() - kScrollBarCollapseThumbWidth, + padding_rect.top + padding_rect.height * start_percentage, + kScrollBarCollapseThumbWidth, + padding_rect.height * length_percentage}; +} + +float VerticalScrollBar::CalculateNewScrollPosition( + const Rect& thumb_original_rect, const Point& mouse_offset) { + auto new_thumb_start = thumb_original_rect.top + mouse_offset.y; + + const auto padding_rect = render_object_->GetPaddingRect(); + + auto scroll_area_start = padding_rect.top + kScrollBarExpandWidth; + auto scroll_area_end = padding_rect.GetBottom() - 2 * kScrollBarExpandWidth; + + auto thumb_head_end = scroll_area_end - thumb_original_rect.height; + + const auto child = render_object_->GetFirstChild(); + const auto child_size = child->GetSize(); + + new_thumb_start = + std::clamp(new_thumb_start, scroll_area_start, thumb_head_end); + + auto offset = new_thumb_start / (scroll_area_end - scroll_area_start) * + child_size.height; + + return offset; } ScrollBarDelegate::ScrollBarDelegate( diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index a9ec729d..0a81eaab 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -2,13 +2,17 @@ #include "cru/platform/graphics/Painter.hpp" #include "cru/platform/graphics/util/Painter.hpp" +#include "cru/ui/Base.hpp" #include "cru/ui/controls/Control.hpp" #include "cru/ui/render/ScrollBar.hpp" #include #include +#include namespace cru::ui::render { +constexpr float kLineHeight = 16; + namespace { // This method assumes margin offset is already considered. // It promises that it won't return negetive value. @@ -36,6 +40,35 @@ Point CoerceScroll(const Point& scroll_offset, const Size& content_size, ScrollRenderObject::ScrollRenderObject() : RenderObject(ChildMode::Single) { scroll_bar_delegate_ = std::make_unique(this); + scroll_bar_delegate_->ScrollAttemptEvent()->AddHandler( + [this](const struct Scroll& scroll) { this->Scroll(scroll); }); +} + +void ScrollRenderObject::Scroll(const struct Scroll& scroll) { + auto direction = scroll.direction; + + switch (scroll.kind) { + case ScrollKind::Absolute: + SetScrollOffset(direction, scroll.value); + break; + case ScrollKind::Relative: + SetScrollOffset(direction, + GetScrollOffset(scroll.direction) + scroll.value); + break; + case ScrollKind::Page: + SetScrollOffset(direction, GetScrollOffset(direction) + + (direction == Direction::Horizontal + ? GetViewRect().width + : GetViewRect().height) * + scroll.value); + break; + case ScrollKind::Line: + SetScrollOffset(direction, + GetScrollOffset(direction) + kLineHeight * scroll.value); + break; + default: + break; + } } RenderObject* ScrollRenderObject::HitTest(const Point& point) { -- cgit v1.2.3 From 568abec9c267d072b03f3fbb27b4bda33307f244 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 28 Feb 2021 00:25:45 +0800 Subject: ... --- src/ui/render/ScrollBar.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index 6096ab63..70255752 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -46,8 +46,16 @@ ScrollBar::ScrollBar(gsl::not_null render_object, } void ScrollBar::SetEnabled(bool value) { - CRU_UNUSED(value) - // TODO: Implement this. + if (value == is_enabled_) return; + if (!value) { + SetExpanded(false); + if (move_thumb_start_) { + if (const auto control = this->render_object_->GetAttachedControl()) { + control->ReleaseMouse(); + } + move_thumb_start_ = std::nullopt; + } + } } void ScrollBar::SetExpanded(bool value) { -- cgit v1.2.3 From 38a5b9eddb75e07695ee38f0bd41d3fd76341a15 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 28 Feb 2021 14:58:20 +0800 Subject: ... --- include/cru/common/Event.hpp | 70 +++++++++++-------- src/ui/render/ScrollBar.cpp | 160 +++++++++++++++++++++++-------------------- 2 files changed, 127 insertions(+), 103 deletions(-) (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/include/cru/common/Event.hpp b/include/cru/common/Event.hpp index aa8fadbb..b6999aa4 100644 --- a/include/cru/common/Event.hpp +++ b/include/cru/common/Event.hpp @@ -110,7 +110,7 @@ struct IEvent : virtual IBaseEvent { public: using EventArgs = DeducedEventArgs; using EventHandler = std::function; - using HandlerVariant = std::variant; + using ShortCircuitHandler = std::function; protected: IEvent() = default; @@ -121,24 +121,27 @@ struct IEvent : virtual IBaseEvent { public: virtual EventRevoker AddHandler(EventHandler handler) = 0; + virtual EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) = 0; + virtual EventRevoker PrependShortCircuitHandler( + ShortCircuitHandler handler) = 0; }; // A non-copyable non-movable Event class. // It stores a list of event handlers. template class Event : public details::EventBase, public IEvent { + using typename IEvent::EventArgs; + using typename IBaseEvent::SpyOnlyHandler; using typename IEvent::EventHandler; - using typename IEvent::HandlerVariant; + using typename IEvent::ShortCircuitHandler; private: struct HandlerData { - HandlerData(EventHandlerToken token, EventHandler handler) - : token(token), handler(handler) {} - HandlerData(EventHandlerToken token, SpyOnlyHandler handler) - : token(token), handler(handler) {} + HandlerData(EventHandlerToken token, ShortCircuitHandler handler) + : token(token), handler(std::move(handler)) {} EventHandlerToken token; - HandlerVariant handler; + ShortCircuitHandler handler; }; public: @@ -148,43 +151,50 @@ class Event : public details::EventBase, public IEvent { ~Event() = default; EventRevoker AddSpyOnlyHandler(SpyOnlyHandler handler) override { + return AddShortCircuitHandler([handler = std::move(handler)](EventArgs) { + handler(); + return false; + }); + } + + EventRevoker AddHandler(EventHandler handler) override { + return AddShortCircuitHandler( + [handler = std::move(handler)](EventArgs args) { + handler(args); + return false; + }); + } + + // Handler return true to short circuit following handlers. + EventRevoker AddShortCircuitHandler(ShortCircuitHandler handler) override { const auto token = current_token_++; this->handler_data_list_.emplace_back(token, std::move(handler)); return CreateRevoker(token); } - EventRevoker AddHandler(EventHandler handler) override { + // Handler return true to short circuit following handlers. + EventRevoker PrependShortCircuitHandler( + ShortCircuitHandler handler) override { const auto token = current_token_++; - this->handler_data_list_.emplace_back(token, std::move(handler)); + this->handler_data_list_.emplace(this->handler_data_list_.cbegin(), token, + std::move(handler)); return CreateRevoker(token); } // This method will make a copy of all handlers. Because user might delete a - // handler in a handler, which may lead to seg fault as the handler is deleted - // while being executed. - // Thanks to this behavior, all handlers will be taken a snapshot when Raise - // is called, so even if you delete a handler during this period, all handlers - // in the snapshot will be executed. - void Raise(typename IEvent::EventArgs args) { - std::vector handlers; + // handler in a handler, which may lead to seg fault as the handler is + // deleted while being executed. Thanks to this behavior, all handlers will + // be taken a snapshot when Raise is called, so even if you delete a handler + // during this period, all handlers in the snapshot will be executed. + void Raise(EventArgs args) { + std::vector handlers; handlers.reserve(this->handler_data_list_.size()); for (const auto& data : this->handler_data_list_) { handlers.push_back(data.handler); } - for (const auto& handler_variant : handlers) { - std::visit( - [&args](auto&& handler) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - handler(); - } else if constexpr (std::is_same_v) { - handler(args); - } else { - static_assert(details::always_false_v, - "non-exhaustive visitor!"); - } - }, - handler_variant); + for (const auto& handler : handlers) { + auto short_circuit = handler(args); + if (short_circuit) return; } } diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index 70255752..00deb479 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -73,82 +73,96 @@ void ScrollBar::Draw(platform::graphics::IPainter* painter) { void ScrollBar::InstallHandlers(controls::Control* control) { event_guard_.Clear(); if (control != nullptr) { - event_guard_ += control->MouseDownEvent()->Bubble()->AddHandler( - [control, this](event::MouseButtonEventArgs& event) { - if (event.GetButton() == mouse_buttons::left && IsEnabled() && - IsExpanded()) { - auto hit_test_result = - ExpandedHitTest(event.GetPoint(render_object_)); - if (!hit_test_result) return; - - switch (*hit_test_result) { - case ScrollBarAreaKind::UpArrow: - this->scroll_attempt_event_.Raise( - {GetDirection(), ScrollKind::Line, -1}); - event.SetHandled(); - break; - case ScrollBarAreaKind::DownArrow: - this->scroll_attempt_event_.Raise( - {GetDirection(), ScrollKind::Line, 1}); - event.SetHandled(); - break; - case ScrollBarAreaKind::UpSlot: - this->scroll_attempt_event_.Raise( - {GetDirection(), ScrollKind::Page, -1}); - event.SetHandled(); - break; - case ScrollBarAreaKind::DownSlot: - this->scroll_attempt_event_.Raise( - {GetDirection(), ScrollKind::Page, 1}); + event_guard_ += + control->MouseDownEvent()->Bubble()->PrependShortCircuitHandler( + [control, this](event::MouseButtonEventArgs& event) { + if (event.GetButton() == mouse_buttons::left && IsEnabled() && + IsExpanded()) { + auto hit_test_result = + ExpandedHitTest(event.GetPoint(render_object_)); + if (!hit_test_result) return false; + + switch (*hit_test_result) { + case ScrollBarAreaKind::UpArrow: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Line, -1}); + event.SetHandled(); + return true; + case ScrollBarAreaKind::DownArrow: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Line, 1}); + event.SetHandled(); + return true; + case ScrollBarAreaKind::UpSlot: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Page, -1}); + event.SetHandled(); + return true; + case ScrollBarAreaKind::DownSlot: + this->scroll_attempt_event_.Raise( + {GetDirection(), ScrollKind::Page, 1}); + event.SetHandled(); + return true; + case ScrollBarAreaKind::Thumb: { + auto thumb_rect = + GetExpandedAreaRect(ScrollBarAreaKind::Thumb); + assert(thumb_rect); + + if (!control->CaptureMouse()) break; + move_thumb_thumb_original_rect_ = *thumb_rect; + move_thumb_start_ = event.GetPoint(); + event.SetHandled(); + return true; + } + default: + break; + } + } + + return false; + }); + + event_guard_ += + control->MouseUpEvent()->Bubble()->PrependShortCircuitHandler( + [control, this](event::MouseButtonEventArgs& event) { + if (event.GetButton() == mouse_buttons::left && + move_thumb_start_) { + move_thumb_start_ = std::nullopt; + control->ReleaseMouse(); event.SetHandled(); - break; - case ScrollBarAreaKind::Thumb: { - auto thumb_rect = GetExpandedAreaRect(ScrollBarAreaKind::Thumb); - assert(thumb_rect); - - if (!control->CaptureMouse()) break; - move_thumb_thumb_original_rect_ = *thumb_rect; - move_thumb_start_ = event.GetPoint(); + return true; + } + return false; + }); + + event_guard_ += + control->MouseMoveEvent()->Bubble()->PrependShortCircuitHandler( + [this](event::MouseEventArgs& event) { + if (move_thumb_start_) { + auto new_scroll_position = CalculateNewScrollPosition( + move_thumb_thumb_original_rect_, + event.GetPoint() - *move_thumb_start_); + + this->scroll_attempt_event_.Raise({GetDirection(), + ScrollKind::Absolute, + new_scroll_position}); event.SetHandled(); - break; + return true; } - default: - break; - } - } - }); - - event_guard_ += control->MouseUpEvent()->Bubble()->AddHandler( - [control, this](event::MouseButtonEventArgs& event) { - if (event.GetButton() == mouse_buttons::left && move_thumb_start_) { - move_thumb_start_ = std::nullopt; - control->ReleaseMouse(); - event.SetHandled(); - } - }); - - event_guard_ += control->MouseMoveEvent()->Bubble()->AddHandler( - [this](event::MouseEventArgs& event) { - if (move_thumb_start_) { - auto new_scroll_position = CalculateNewScrollPosition( - move_thumb_thumb_original_rect_, - event.GetPoint() - *move_thumb_start_); - - this->scroll_attempt_event_.Raise( - {GetDirection(), ScrollKind::Absolute, new_scroll_position}); - event.SetHandled(); - return; - } - - if (IsEnabled() && !IsExpanded()) { - auto trigger_expand_area = GetCollapsedTriggerExpandAreaRect(); - if (trigger_expand_area && - trigger_expand_area->IsPointInside( - event.GetPoint(this->render_object_))) - SetExpanded(true); - event.SetHandled(); - } - }); + + if (IsEnabled() && !IsExpanded()) { + auto trigger_expand_area = GetCollapsedTriggerExpandAreaRect(); + if (trigger_expand_area && + trigger_expand_area->IsPointInside( + event.GetPoint(this->render_object_))) { + SetExpanded(true); + event.SetHandled(); + return true; + } + } + + return false; + }); } } -- cgit v1.2.3 From a64ace1b09cb3c8594f510075ceffee17d8b7cfb Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 28 Feb 2021 15:37:33 +0800 Subject: ... --- src/ui/render/ScrollBar.cpp | 10 +++++----- src/ui/render/ScrollRenderObject.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index 00deb479..fa53292e 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -193,7 +193,7 @@ ScrollBar::GetExpandedArrowBrush() const { gsl::not_null> ScrollBar::GetExpandedArrowBackgroundBrush() const { // TODO: Read theme resource. - return expanded_arrow_brush_; + return expanded_arrow_background_brush_; } void ScrollBar::OnDraw(platform::graphics::IPainter* painter, @@ -357,8 +357,8 @@ float HorizontalScrollBar::CalculateNewScrollPosition( new_thumb_start = std::clamp(new_thumb_start, scroll_area_start, thumb_head_end); - auto offset = new_thumb_start / (scroll_area_end - scroll_area_start) * - child_size.width; + auto offset = (new_thumb_start - scroll_area_start) / + (scroll_area_end - scroll_area_start) * child_size.width; return offset; } @@ -482,8 +482,8 @@ float VerticalScrollBar::CalculateNewScrollPosition( new_thumb_start = std::clamp(new_thumb_start, scroll_area_start, thumb_head_end); - auto offset = new_thumb_start / (scroll_area_end - scroll_area_start) * - child_size.height; + auto offset = (new_thumb_start - scroll_area_start) / + (scroll_area_end - scroll_area_start) * child_size.width; return offset; } diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index 0a81eaab..fd5143ff 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -85,7 +85,7 @@ RenderObject* ScrollRenderObject::HitTest(const Point& point) { void ScrollRenderObject::OnDrawCore(platform::graphics::IPainter* painter) { DefaultDrawContent(painter); if (const auto child = GetSingleChild()) { - painter->PushLayer(this->GetPaddingRect()); + painter->PushLayer(this->GetContentRect()); const auto offset = child->GetOffset(); platform::graphics::util::WithTransform( painter, Matrix::Translation(offset.x, offset.y), -- cgit v1.2.3 From e19d42f2f89ad6670e6b9a226bcf7abc12003bb5 Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 28 Feb 2021 15:57:47 +0800 Subject: ... --- include/cru/ui/render/ScrollBar.hpp | 9 +++++- src/ui/render/ScrollBar.cpp | 58 ++++++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 9 deletions(-) (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/include/cru/ui/render/ScrollBar.hpp b/include/cru/ui/render/ScrollBar.hpp index e3dabb57..fc2910c4 100644 --- a/include/cru/ui/render/ScrollBar.hpp +++ b/include/cru/ui/render/ScrollBar.hpp @@ -4,6 +4,7 @@ #include "cru/common/Event.hpp" #include "cru/platform/graphics/Base.hpp" #include "cru/platform/graphics/Painter.hpp" +#include "cru/platform/gui/Cursor.hpp" #include "cru/platform/gui/UiApplication.hpp" #include "cru/ui/Base.hpp" #include "cru/ui/controls/Control.hpp" @@ -40,7 +41,7 @@ class ScrollBar : public Object { CRU_DELETE_COPY(ScrollBar) CRU_DELETE_MOVE(ScrollBar) - ~ScrollBar() override = default; + ~ScrollBar() override; public: Direction GetDirection() const { return direction_; } @@ -89,6 +90,10 @@ class ScrollBar : public Object { virtual float CalculateNewScrollPosition(const Rect& thumb_original_rect, const Point& mouse_offset) = 0; + private: + void SetCursor(); + void RestoreCursor(); + protected: gsl::not_null render_object_; @@ -111,6 +116,8 @@ class ScrollBar : public Object { EventRevokerListGuard event_guard_; Event scroll_attempt_event_; + + std::optional> old_cursor_; }; class HorizontalScrollBar : public ScrollBar { diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index fa53292e..ee4b9e0d 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -6,6 +6,7 @@ #include "cru/platform/graphics/Factory.hpp" #include "cru/platform/graphics/Painter.hpp" #include "cru/platform/gui/Base.hpp" +#include "cru/platform/gui/Cursor.hpp" #include "cru/ui/Base.hpp" #include "cru/ui/events/UiEvent.hpp" #include "cru/ui/render/ScrollRenderObject.hpp" @@ -45,6 +46,8 @@ ScrollBar::ScrollBar(gsl::not_null render_object, graphics_factory->CreateSolidColorBrush(colors::black); } +ScrollBar::~ScrollBar() { RestoreCursor(); } + void ScrollBar::SetEnabled(bool value) { if (value == is_enabled_) return; if (!value) { @@ -150,19 +153,38 @@ void ScrollBar::InstallHandlers(controls::Control* control) { return true; } - if (IsEnabled() && !IsExpanded()) { - auto trigger_expand_area = GetCollapsedTriggerExpandAreaRect(); - if (trigger_expand_area && - trigger_expand_area->IsPointInside( - event.GetPoint(this->render_object_))) { - SetExpanded(true); - event.SetHandled(); - return true; + if (IsEnabled()) { + if (IsExpanded()) { + auto hit_test_result = + ExpandedHitTest(event.GetPoint(this->render_object_)); + if (hit_test_result) { + SetCursor(); + } else { + RestoreCursor(); + } + } else { + auto trigger_expand_area = + GetCollapsedTriggerExpandAreaRect(); + if (trigger_expand_area && + trigger_expand_area->IsPointInside( + event.GetPoint(this->render_object_))) { + SetExpanded(true); + SetCursor(); + event.SetHandled(); + return true; + } } } return false; }); + + event_guard_ += + control->MouseLeaveEvent()->Bubble()->PrependShortCircuitHandler( + [this](event::MouseEventArgs&) { + if (IsExpanded() && !move_thumb_start_) RestoreCursor(); + return false; + }); } } @@ -225,6 +247,26 @@ void ScrollBar::OnDraw(platform::graphics::IPainter* painter, } } +void ScrollBar::SetCursor() { + if (!old_cursor_) { + if (const auto control = render_object_->GetAttachedControl()) { + old_cursor_ = control->GetCursor(); + control->SetCursor( + GetUiApplication()->GetCursorManager()->GetSystemCursor( + platform::gui::SystemCursorType::Arrow)); + } + } +} + +void ScrollBar::RestoreCursor() { + if (old_cursor_) { + if (const auto control = render_object_->GetAttachedControl()) { + control->SetCursor(*old_cursor_); + } + old_cursor_ = std::nullopt; + } +} + std::optional ScrollBar::ExpandedHitTest( const Point& point) { for (auto kind : kScrollBarAreaKindList) { -- cgit v1.2.3 From 49dfb2bc9f965b398aa12e711148696d28443eaf Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 13 Mar 2021 16:05:21 +0800 Subject: feat: Scrollbar auto collapse. --- include/cru/platform/gui/UiApplication.hpp | 5 +++++ include/cru/ui/render/ScrollBar.hpp | 7 +++++++ src/ui/render/ScrollBar.cpp | 33 ++++++++++++++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/include/cru/platform/gui/UiApplication.hpp b/include/cru/platform/gui/UiApplication.hpp index ba85020a..5a5b0b13 100644 --- a/include/cru/platform/gui/UiApplication.hpp +++ b/include/cru/platform/gui/UiApplication.hpp @@ -79,6 +79,9 @@ class TimerAutoCanceler { } TimerAutoCanceler& operator=(TimerAutoCanceler&& other) { + if (&other == this) { + return *this; + } Reset(other.id_); other.id_ = 0; return *this; @@ -101,6 +104,8 @@ class TimerAutoCanceler { id_ = id; } + explicit operator bool() const { return id_; } + private: long long id_; }; diff --git a/include/cru/ui/render/ScrollBar.hpp b/include/cru/ui/render/ScrollBar.hpp index fc2910c4..7cfd2576 100644 --- a/include/cru/ui/render/ScrollBar.hpp +++ b/include/cru/ui/render/ScrollBar.hpp @@ -94,6 +94,11 @@ class ScrollBar : public Object { void SetCursor(); void RestoreCursor(); + void BeginAutoCollapseTimer(); + void StopAutoCollapseTimer(); + + void OnMouseLeave(); + protected: gsl::not_null render_object_; @@ -118,6 +123,8 @@ class ScrollBar : public Object { Event scroll_attempt_event_; std::optional> old_cursor_; + + platform::gui::TimerAutoCanceler auto_collapse_timer_canceler_; }; class HorizontalScrollBar : public ScrollBar { diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index ee4b9e0d..02e079e9 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -14,14 +14,17 @@ #include #include +#include #include #include #include namespace cru::ui::render { +using namespace std::chrono_literals; constexpr float kScrollBarCollapseThumbWidth = 2; constexpr float kScrollBarCollapsedTriggerExpandAreaWidth = 5; constexpr float kScrollBarExpandWidth = 10; +constexpr auto kScrollBarAutoCollapseDelay = 1500ms; constexpr std::array kScrollBarAreaKindList{ ScrollBarAreaKind::UpArrow, ScrollBarAreaKind::DownArrow, @@ -131,6 +134,13 @@ void ScrollBar::InstallHandlers(controls::Control* control) { if (event.GetButton() == mouse_buttons::left && move_thumb_start_) { move_thumb_start_ = std::nullopt; + + auto hit_test_result = + ExpandedHitTest(event.GetPoint(this->render_object_)); + if (!hit_test_result) { + OnMouseLeave(); + } + control->ReleaseMouse(); event.SetHandled(); return true; @@ -159,8 +169,9 @@ void ScrollBar::InstallHandlers(controls::Control* control) { ExpandedHitTest(event.GetPoint(this->render_object_)); if (hit_test_result) { SetCursor(); + StopAutoCollapseTimer(); } else { - RestoreCursor(); + OnMouseLeave(); } } else { auto trigger_expand_area = @@ -182,7 +193,9 @@ void ScrollBar::InstallHandlers(controls::Control* control) { event_guard_ += control->MouseLeaveEvent()->Bubble()->PrependShortCircuitHandler( [this](event::MouseEventArgs&) { - if (IsExpanded() && !move_thumb_start_) RestoreCursor(); + if (IsExpanded() && !move_thumb_start_) { + OnMouseLeave(); + } return false; }); } @@ -267,6 +280,22 @@ void ScrollBar::RestoreCursor() { } } +void ScrollBar::BeginAutoCollapseTimer() { + if (!auto_collapse_timer_canceler_ && IsExpanded()) { + auto_collapse_timer_canceler_ = GetUiApplication()->SetTimeout( + kScrollBarAutoCollapseDelay, [this] { this->SetExpanded(false); }); + } +} + +void ScrollBar::StopAutoCollapseTimer() { + auto_collapse_timer_canceler_.Reset(); +} + +void ScrollBar::OnMouseLeave() { + RestoreCursor(); + BeginAutoCollapseTimer(); +} + std::optional ScrollBar::ExpandedHitTest( const Point& point) { for (auto kind : kScrollBarAreaKindList) { -- cgit v1.2.3 From 7703063a5816b089483e78ccd74bb9902ccfbea8 Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 13 Mar 2021 17:40:18 +0800 Subject: ... --- include/cru/platform/Matrix.hpp | 5 +++ include/cru/platform/graphics/Painter.hpp | 4 +- include/cru/platform/graphics/util/Painter.hpp | 2 +- include/cru/ui/render/ScrollBar.hpp | 3 ++ include/cru/win/graphics/direct/Painter.hpp | 2 + src/ui/render/ScrollBar.cpp | 56 ++++++++++++++++++++++---- src/win/graphics/direct/Painter.cpp | 8 ++++ test/CMakeLists.txt | 9 ++++- test/platform/CMakeLists.txt | 6 +++ test/platform/MatrixTest.cpp | 37 +++++++++++++++++ test/win/CMakeLists.txt | 1 + test/win/graphics/CMakeLists.txt | 1 + test/win/graphics/direct/CMakeLists.txt | 6 +++ test/win/graphics/direct/ConvertTest.cpp | 29 +++++++++++++ 14 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 test/platform/CMakeLists.txt create mode 100644 test/platform/MatrixTest.cpp create mode 100644 test/win/CMakeLists.txt create mode 100644 test/win/graphics/CMakeLists.txt create mode 100644 test/win/graphics/direct/CMakeLists.txt create mode 100644 test/win/graphics/direct/ConvertTest.cpp (limited to 'src/ui/render/ScrollBar.cpp') diff --git a/include/cru/platform/Matrix.hpp b/include/cru/platform/Matrix.hpp index e702df90..8ec5faaa 100644 --- a/include/cru/platform/Matrix.hpp +++ b/include/cru/platform/Matrix.hpp @@ -50,10 +50,15 @@ struct Matrix { return Matrix{1.0f, 0.0f, 0.0f, 1.0f, x, y}; } + static Matrix Translation(const Point& point) { + return Translation(point.x, point.y); + } + static Matrix Scale(float sx, float sy) { return Matrix{sx, 0.0f, 0.0f, sy, 0.0f, 0.0f}; } + // Clockwise. static Matrix Rotation(float angle) { float r = AngleToRadian(angle); float s = std::sin(r); diff --git a/include/cru/platform/graphics/Painter.hpp b/include/cru/platform/graphics/Painter.hpp index 76140c32..f75ea52b 100644 --- a/include/cru/platform/graphics/Painter.hpp +++ b/include/cru/platform/graphics/Painter.hpp @@ -9,6 +9,8 @@ struct IPainter : virtual INativeResource { virtual void Clear(const Color& color) = 0; + virtual void DrawLine(const Point& start, const Point& end, IBrush* brush, + float width) = 0; virtual void StrokeRectangle(const Rect& rectangle, IBrush* brush, float width) = 0; virtual void FillRectangle(const Rect& rectangle, IBrush* brush) = 0; @@ -26,4 +28,4 @@ struct IPainter : virtual INativeResource { virtual void EndDraw() = 0; }; -} // namespace cru::platform::graph +} // namespace cru::platform::graphics diff --git a/include/cru/platform/graphics/util/Painter.hpp b/include/cru/platform/graphics/util/Painter.hpp index af3a1997..90457cf4 100644 --- a/include/cru/platform/graphics/util/Painter.hpp +++ b/include/cru/platform/graphics/util/Painter.hpp @@ -10,7 +10,7 @@ void WithTransform(IPainter* painter, const Matrix& matrix, const Fn& action) { static_assert(std::is_invocable_v, "Action must can be be invoked with painter."); const auto old = painter->GetTransform(); - painter->SetTransform(old * matrix); + painter->SetTransform(matrix * old); action(painter); painter->SetTransform(old); } diff --git a/include/cru/ui/render/ScrollBar.hpp b/include/cru/ui/render/ScrollBar.hpp index 7cfd2576..3293e9d0 100644 --- a/include/cru/ui/render/ScrollBar.hpp +++ b/include/cru/ui/render/ScrollBar.hpp @@ -3,6 +3,7 @@ #include "cru/common/Base.hpp" #include "cru/common/Event.hpp" #include "cru/platform/graphics/Base.hpp" +#include "cru/platform/graphics/Geometry.hpp" #include "cru/platform/graphics/Painter.hpp" #include "cru/platform/gui/Cursor.hpp" #include "cru/platform/gui/UiApplication.hpp" @@ -102,6 +103,8 @@ class ScrollBar : public Object { protected: gsl::not_null render_object_; + std::unique_ptr arrow_geometry_; + private: Direction direction_; diff --git a/include/cru/win/graphics/direct/Painter.hpp b/include/cru/win/graphics/direct/Painter.hpp index 93c768e7..b34c1563 100644 --- a/include/cru/win/graphics/direct/Painter.hpp +++ b/include/cru/win/graphics/direct/Painter.hpp @@ -27,6 +27,8 @@ class D2DPainter : public DirectResource, void Clear(const Color& color) override; + void DrawLine(const Point& start, const Point& end, IBrush* brush, + float width) override; void StrokeRectangle(const Rect& rectangle, IBrush* brush, float width) override; void FillRectangle(const Rect& rectangle, IBrush* brush) override; diff --git a/src/ui/render/ScrollBar.cpp b/src/ui/render/ScrollBar.cpp index 02e079e9..7f69c1e2 100644 --- a/src/ui/render/ScrollBar.cpp +++ b/src/ui/render/ScrollBar.cpp @@ -4,7 +4,9 @@ #include "cru/common/Base.hpp" #include "cru/platform/GraphBase.hpp" #include "cru/platform/graphics/Factory.hpp" +#include "cru/platform/graphics/Geometry.hpp" #include "cru/platform/graphics/Painter.hpp" +#include "cru/platform/graphics/util/Painter.hpp" #include "cru/platform/gui/Base.hpp" #include "cru/platform/gui/Cursor.hpp" #include "cru/ui/Base.hpp" @@ -16,6 +18,7 @@ #include #include #include +#include #include #include @@ -24,6 +27,7 @@ using namespace std::chrono_literals; constexpr float kScrollBarCollapseThumbWidth = 2; constexpr float kScrollBarCollapsedTriggerExpandAreaWidth = 5; constexpr float kScrollBarExpandWidth = 10; +constexpr float kScrollBarArrowHeight = 3.5; constexpr auto kScrollBarAutoCollapseDelay = 1500ms; constexpr std::array kScrollBarAreaKindList{ @@ -31,6 +35,17 @@ constexpr std::array kScrollBarAreaKindList{ ScrollBarAreaKind::UpSlot, ScrollBarAreaKind::DownSlot, ScrollBarAreaKind::Thumb}; +namespace { +std::unique_ptr CreateScrollBarArrowGeometry() { + auto geometry_builder = GetGraphFactory()->CreateGeometryBuilder(); + geometry_builder->BeginFigure({-kScrollBarArrowHeight / 2, 0}); + geometry_builder->LineTo({kScrollBarArrowHeight / 2, kScrollBarArrowHeight}); + geometry_builder->LineTo({kScrollBarArrowHeight / 2, -kScrollBarArrowHeight}); + geometry_builder->CloseFigure(true); + return geometry_builder->Build(); +} +} // namespace + ScrollBar::ScrollBar(gsl::not_null render_object, Direction direction) : render_object_(render_object), direction_(direction) { @@ -43,10 +58,11 @@ ScrollBar::ScrollBar(gsl::not_null render_object, expanded_thumb_brush_ = graphics_factory->CreateSolidColorBrush(colors::gray); expanded_slot_brush_ = graphics_factory->CreateSolidColorBrush(colors::seashell); - expanded_arrow_brush_ = - graphics_factory->CreateSolidColorBrush(colors::white); + expanded_arrow_brush_ = graphics_factory->CreateSolidColorBrush(colors::gray); expanded_arrow_background_brush_ = - graphics_factory->CreateSolidColorBrush(colors::black); + graphics_factory->CreateSolidColorBrush(colors::seashell); + + arrow_geometry_ = CreateScrollBarArrowGeometry(); } ScrollBar::~ScrollBar() { RestoreCursor(); } @@ -250,7 +266,7 @@ void ScrollBar::OnDraw(platform::graphics::IPainter* painter, if (up_arrow) this->DrawUpArrow(painter, *up_arrow); auto down_arrow = GetExpandedAreaRect(ScrollBarAreaKind::DownArrow); - if (down_arrow) this->DrawUpArrow(painter, *down_arrow); + if (down_arrow) this->DrawDownArrow(painter, *down_arrow); } else { auto optional_rect = GetCollapsedThumbRect(); if (optional_rect) { @@ -313,14 +329,26 @@ HorizontalScrollBar::HorizontalScrollBar( void HorizontalScrollBar::DrawUpArrow(platform::graphics::IPainter* painter, const Rect& area) { - // TODO: Do what you must! painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); + + platform::graphics::util::WithTransform( + painter, Matrix::Translation(area.GetCenter()), + [this](platform::graphics::IPainter* painter) { + painter->FillGeometry(arrow_geometry_.get(), + GetExpandedArrowBrush().get().get()); + }); } void HorizontalScrollBar::DrawDownArrow(platform::graphics::IPainter* painter, const Rect& area) { - // TODO: Do what you must! painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); + + platform::graphics::util::WithTransform( + painter, Matrix::Rotation(180) * Matrix::Translation(area.GetCenter()), + [this](platform::graphics::IPainter* painter) { + painter->FillGeometry(arrow_geometry_.get(), + GetExpandedArrowBrush().get().get()); + }); } bool HorizontalScrollBar::IsShowBar() { @@ -440,14 +468,26 @@ VerticalScrollBar::VerticalScrollBar( void VerticalScrollBar::DrawUpArrow(platform::graphics::IPainter* painter, const Rect& area) { - // TODO: Do what you must! painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); + + platform::graphics::util::WithTransform( + painter, Matrix::Rotation(90) * Matrix::Translation(area.GetCenter()), + [this](platform::graphics::IPainter* painter) { + painter->FillGeometry(arrow_geometry_.get(), + GetExpandedArrowBrush().get().get()); + }); } void VerticalScrollBar::DrawDownArrow(platform::graphics::IPainter* painter, const Rect& area) { - // TODO: Do what you must! painter->FillRectangle(area, GetExpandedArrowBackgroundBrush().get().get()); + + platform::graphics::util::WithTransform( + painter, Matrix::Rotation(270) * Matrix::Translation(area.GetCenter()), + [this](platform::graphics::IPainter* painter) { + painter->FillGeometry(arrow_geometry_.get(), + GetExpandedArrowBrush().get().get()); + }); } bool VerticalScrollBar::IsShowBar() { diff --git a/src/win/graphics/direct/Painter.cpp b/src/win/graphics/direct/Painter.cpp index 91392ba7..d6999cfa 100644 --- a/src/win/graphics/direct/Painter.cpp +++ b/src/win/graphics/direct/Painter.cpp @@ -32,6 +32,14 @@ void D2DPainter::Clear(const Color& color) { render_target_->Clear(Convert(color)); } +void D2DPainter::DrawLine(const Point& start, const Point& end, IBrush* brush, + float width) { + CheckValidation(); + const auto b = CheckPlatform(brush, GetPlatformId()); + render_target_->DrawLine(Convert(start), Convert(end), + b->GetD2DBrushInterface(), width); +} + void D2DPainter::StrokeRectangle(const Rect& rectangle, IBrush* brush, float width) { CheckValidation(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c534b909..3b9567cd 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,7 +2,12 @@ find_package(GTest CONFIG REQUIRED) include(GoogleTest) -add_subdirectory(common) - add_library(cru_test_base INTERFACE) target_link_libraries(cru_test_base INTERFACE GTest::gtest GTest::gtest_main) + +add_subdirectory(common) +add_subdirectory(platform) + +if(WIN32) + add_subdirectory(win) +endif() diff --git a/test/platform/CMakeLists.txt b/test/platform/CMakeLists.txt new file mode 100644 index 00000000..9ad8fb51 --- /dev/null +++ b/test/platform/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(cru_platform_base_test + MatrixTest.cpp +) +target_link_libraries(cru_platform_base_test PRIVATE cru_platform_base cru_test_base) + +gtest_discover_tests(cru_platform_base_test) diff --git a/test/platform/MatrixTest.cpp b/test/platform/MatrixTest.cpp new file mode 100644 index 00000000..3b8aab27 --- /dev/null +++ b/test/platform/MatrixTest.cpp @@ -0,0 +1,37 @@ +#include "cru/platform/GraphBase.hpp" +#include "cru/platform/Matrix.hpp" + +#include + +using cru::platform::Matrix; +using cru::platform::Point; + +TEST(Matrix, Rotation) { + Point p(1, 1); + + Point p90 = Matrix::Rotation(90).TransformPoint(p); + ASSERT_FLOAT_EQ(p90.x, -1); + ASSERT_FLOAT_EQ(p90.y, 1); + + Point p180 = Matrix::Rotation(180).TransformPoint(p); + ASSERT_FLOAT_EQ(p180.x, -1); + ASSERT_FLOAT_EQ(p180.y, -1); + + Point p270 = Matrix::Rotation(270).TransformPoint(p); + ASSERT_FLOAT_EQ(p270.x, 1); + ASSERT_FLOAT_EQ(p270.y, -1); +} + +TEST(Matrix, TranslationAndRotation) { + Point p = + (Matrix::Translation(1, 1) * Matrix::Rotation(90)).TransformPoint({1, 1}); + ASSERT_FLOAT_EQ(p.x, -2); + ASSERT_FLOAT_EQ(p.y, 2); +} + +TEST(Matrix, RotationAndTranslation) { + Point p = + (Matrix::Rotation(90) * Matrix::Translation(1, 1)).TransformPoint({1, 1}); + ASSERT_FLOAT_EQ(p.x, 0); + ASSERT_FLOAT_EQ(p.y, 2); +} diff --git a/test/win/CMakeLists.txt b/test/win/CMakeLists.txt new file mode 100644 index 00000000..0ebdd7fe --- /dev/null +++ b/test/win/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(graphics) diff --git a/test/win/graphics/CMakeLists.txt b/test/win/graphics/CMakeLists.txt new file mode 100644 index 00000000..c90537ac --- /dev/null +++ b/test/win/graphics/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(direct) diff --git a/test/win/graphics/direct/CMakeLists.txt b/test/win/graphics/direct/CMakeLists.txt new file mode 100644 index 00000000..69e22ef7 --- /dev/null +++ b/test/win/graphics/direct/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(cru_win_graphics_direct_test + ConvertTest.cpp +) +target_link_libraries(cru_win_graphics_direct_test PRIVATE cru_win_graphics_direct cru_test_base) + +gtest_discover_tests(cru_win_graphics_direct_test) diff --git a/test/win/graphics/direct/ConvertTest.cpp b/test/win/graphics/direct/ConvertTest.cpp new file mode 100644 index 00000000..f8f95dac --- /dev/null +++ b/test/win/graphics/direct/ConvertTest.cpp @@ -0,0 +1,29 @@ +#include "cru/platform/Matrix.hpp" +#include "cru/win/graphics/direct/ConvertUtil.hpp" + +#include + +using cru::platform::Matrix; +using cru::platform::graphics::win::direct::Convert; + +TEST(MatrixConvert, Rotation) { + auto matrix = Convert(Matrix::Rotation(90)); + + auto m = *D2D1::Matrix3x2F::ReinterpretBaseType(&matrix); + + auto p = m.TransformPoint({1, 1}); + + ASSERT_FLOAT_EQ(p.x, -1); + ASSERT_FLOAT_EQ(p.y, 1); +} + +TEST(MatrixConvert, RotationAndTranslation) { + auto matrix = Convert(Matrix::Rotation(90) * Matrix::Translation(1, 1)); + + auto m = *D2D1::Matrix3x2F::ReinterpretBaseType(&matrix); + + auto p = m.TransformPoint({1, 1}); + + ASSERT_FLOAT_EQ(p.x, 0); + ASSERT_FLOAT_EQ(p.y, 2); +} -- cgit v1.2.3