From 6aa2201797a9ed64ce0178215ae941d0c5f09579 Mon Sep 17 00:00:00 2001 From: crupest Date: Fri, 30 Oct 2020 00:07:57 +0800 Subject: ... --- include/cru/ui/render/ScrollRenderObject.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/cru/ui/render/ScrollRenderObject.hpp') diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp index 9b0cbf9a..3cc0e4c4 100644 --- a/include/cru/ui/render/ScrollRenderObject.hpp +++ b/include/cru/ui/render/ScrollRenderObject.hpp @@ -1,7 +1,7 @@ #pragma once #include "RenderObject.hpp" -#include "cru/platform/graph/util/Painter.hpp" +#include "cru/platform/graphics/util/Painter.hpp" #include @@ -44,7 +44,7 @@ class ScrollRenderObject : public RenderObject { void ScrollToContain(const Rect& rect, const Thickness& margin = Thickness{}); protected: - void OnDrawCore(platform::graph::IPainter* painter) override; + void OnDrawCore(platform::graphics::IPainter* painter) override; // Logic: // If available size is bigger than child's preferred size, then child's -- cgit v1.2.3 From 9c2f860ea80310f87b62a2947b4ddea5e7d85587 Mon Sep 17 00:00:00 2001 From: crupest Date: Wed, 24 Feb 2021 23:01:15 +0800 Subject: feat: Scroll bar. Only collapse state. --- include/cru/platform/GraphBase.hpp | 6 ++ include/cru/ui/render/RenderObject.hpp | 16 +-- include/cru/ui/render/ScrollBarDelegate.hpp | 142 ++++++++++++++++++++++++++ include/cru/ui/render/ScrollRenderObject.hpp | 8 +- src/ui/CMakeLists.txt | 2 + src/ui/render/RenderObject.cpp | 14 +++ src/ui/render/ScrollBarDelegate.cpp | 145 +++++++++++++++++++++++++++ src/ui/render/ScrollRenderObject.cpp | 16 +++ 8 files changed, 342 insertions(+), 7 deletions(-) create mode 100644 include/cru/ui/render/ScrollBarDelegate.hpp create mode 100644 src/ui/render/ScrollBarDelegate.cpp (limited to 'include/cru/ui/render/ScrollRenderObject.hpp') diff --git a/include/cru/platform/GraphBase.hpp b/include/cru/platform/GraphBase.hpp index b580ad31..6bf2736f 100644 --- a/include/cru/platform/GraphBase.hpp +++ b/include/cru/platform/GraphBase.hpp @@ -316,6 +316,12 @@ struct Color { (hex >> 24) & mask); } + constexpr Color WithAlpha(std::uint8_t new_alpha) const { + auto result = *this; + result.alpha = new_alpha; + return result; + } + std::uint8_t red; std::uint8_t green; std::uint8_t blue; diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp index 2b166efc..8bcd4c62 100644 --- a/include/cru/ui/render/RenderObject.hpp +++ b/include/cru/ui/render/RenderObject.hpp @@ -2,6 +2,7 @@ #include "Base.hpp" #include "MeasureRequirement.hpp" +#include "cru/common/Base.hpp" #include "cru/common/Event.hpp" #include "cru/ui/Base.hpp" @@ -63,9 +64,7 @@ class RenderObject : public Object { ~RenderObject() override = default; controls::Control* GetAttachedControl() const { return control_; } - void SetAttachedControl(controls::Control* new_control) { - control_ = new_control; - } + void SetAttachedControl(controls::Control* new_control); host::WindowHost* GetWindowHost() const { return window_host_; } @@ -76,6 +75,7 @@ class RenderObject : public Object { void AddChild(RenderObject* render_object, Index position); void RemoveChild(Index position); + RenderObject* GetFirstChild() const; void TraverseDescendants(const std::function& action); // Offset from parent's lefttop to lefttop of this render object. Margin is @@ -131,6 +131,9 @@ class RenderObject : public Object { // This will set offset of this render object and call OnLayoutCore. void Layout(const Point& offset); + virtual Rect GetPaddingRect() const; + virtual Rect GetContentRect() const; + void Draw(platform::graphics::IPainter* painter); // Param point must be relative the lefttop of render object including margin. @@ -201,10 +204,11 @@ class RenderObject : public Object { // Lefttop of content_rect should be added when calculated children's offset. virtual void OnLayoutContent(const Rect& content_rect) = 0; - virtual void OnAfterLayout(); + virtual void OnAttachedControlChanged(controls::Control* control) { + CRU_UNUSED(control) + } - virtual Rect GetPaddingRect() const; - virtual Rect GetContentRect() const; + virtual void OnAfterLayout(); private: void SetParent(RenderObject* new_parent); diff --git a/include/cru/ui/render/ScrollBarDelegate.hpp b/include/cru/ui/render/ScrollBarDelegate.hpp new file mode 100644 index 00000000..e5c63f6d --- /dev/null +++ b/include/cru/ui/render/ScrollBarDelegate.hpp @@ -0,0 +1,142 @@ +#pragma once +#include "Base.hpp" +#include "cru/common/Base.hpp" +#include "cru/common/Event.hpp" +#include "cru/platform/graphics/Base.hpp" +#include "cru/platform/graphics/Painter.hpp" +#include "cru/platform/gui/UiApplication.hpp" +#include "cru/ui/controls/Control.hpp" + +#include +#include +#include + +namespace cru::ui::render { +class ScrollRenderObject; + +enum class ScrollBarAreaKind { + UpArrow, // Line up + DownArrow, // Line down + UpThumb, // Page up + DownThumb, // Page down + Thumb +}; + +class ScrollBar : public Object { + public: + explicit ScrollBar(gsl::not_null render_object); + + CRU_DELETE_COPY(ScrollBar) + CRU_DELETE_MOVE(ScrollBar) + + ~ScrollBar() override = default; + + public: + bool IsEnabled() const { return is_enabled_; } + void SetEnabled(bool value); + + void Draw(platform::graphics::IPainter* painter); + + virtual std::optional HitTest(const Point& point) = 0; + + IEvent* ScrollAttemptEvent() { return &scroll_attempt_event_; } + + void InstallHandlers(controls::Control* control); + void UninstallHandlers() { InstallHandlers(nullptr); } + + gsl::not_null> + GetCollapseThumbBrush() const; + + protected: + virtual void OnDraw(platform::graphics::IPainter* painter, bool expand) = 0; + + protected: + gsl::not_null render_object_; + + private: + bool is_enabled_ = true; + + bool is_expanded_ = false; + + std::shared_ptr collapse_thumb_brush_; + + EventRevokerListGuard event_guard_; + + Event scroll_attempt_event_; +}; + +class HorizontalScrollBar : public ScrollBar { + public: + explicit HorizontalScrollBar( + gsl::not_null render_object); + + CRU_DELETE_COPY(HorizontalScrollBar) + CRU_DELETE_MOVE(HorizontalScrollBar) + + ~HorizontalScrollBar() override = default; + + public: + std::optional HitTest(const Point& point) override; + + protected: + void OnDraw(platform::graphics::IPainter* painter, bool expand) override; +}; + +class VerticalScrollBar : public ScrollBar { + public: + explicit VerticalScrollBar(gsl::not_null render_object); + + CRU_DELETE_COPY(VerticalScrollBar) + CRU_DELETE_MOVE(VerticalScrollBar) + + ~VerticalScrollBar() override = default; + + public: + std::optional HitTest(const Point& point) override; + + protected: + void OnDraw(platform::graphics::IPainter* painter, bool expand) override; +}; + +struct ScrollBarScrollAttemptArgs { + float x_offset; + float y_offset; +}; + +// A delegate to draw scrollbar and register related events. +class ScrollBarDelegate : public Object { + public: + explicit ScrollBarDelegate(gsl::not_null render_object); + + CRU_DELETE_COPY(ScrollBarDelegate) + CRU_DELETE_MOVE(ScrollBarDelegate) + + ~ScrollBarDelegate() override = default; + + public: + bool IsHorizontalBarEnabled() const { return horizontal_bar_.IsEnabled(); } + void SetHorizontalBarEnabled(bool value) { + horizontal_bar_.SetEnabled(value); + } + + bool IsVerticalBarEnabled() const { return horizontal_bar_.IsEnabled(); } + void SetVerticalBarEnabled(bool value) { horizontal_bar_.SetEnabled(value); } + + IEvent* ScrollAttemptEvent() { + return &scroll_attempt_event_; + } + + void DrawScrollBar(platform::graphics::IPainter* painter); + + void InstallHandlers(controls::Control* control); + void UninstallHandlers() { InstallHandlers(nullptr); } + + private: + gsl::not_null render_object_; + + HorizontalScrollBar horizontal_bar_; + VerticalScrollBar vertical_bar_; + + Event scroll_attempt_event_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp index 3cc0e4c4..5a431527 100644 --- a/include/cru/ui/render/ScrollRenderObject.hpp +++ b/include/cru/ui/render/ScrollRenderObject.hpp @@ -2,7 +2,9 @@ #include "RenderObject.hpp" #include "cru/platform/graphics/util/Painter.hpp" +#include "cru/ui/render/ScrollBarDelegate.hpp" +#include #include namespace cru::ui::render { @@ -16,7 +18,7 @@ namespace cru::ui::render { // Or layout by scroll state. class ScrollRenderObject : public RenderObject { public: - ScrollRenderObject() : RenderObject(ChildMode::Single) {} + ScrollRenderObject(); CRU_DELETE_COPY(ScrollRenderObject) CRU_DELETE_MOVE(ScrollRenderObject) @@ -54,7 +56,11 @@ class ScrollRenderObject : public RenderObject { const MeasureSize& preferred_size) override; void OnLayoutContent(const Rect& content_rect) override; + void OnAttachedControlChanged(controls::Control* control) override; + private: Point scroll_offset_; + + std::unique_ptr scroll_bar_delegate_; }; } // namespace cru::ui::render diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index d1c1e830..6153bc07 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(cru_ui STATIC render/FlexLayoutRenderObject.cpp render/LayoutHelper.cpp render/RenderObject.cpp + render/ScrollBarDelegate.cpp render/ScrollRenderObject.cpp render/StackLayoutRenderObject.cpp render/TextRenderObject.cpp @@ -77,6 +78,7 @@ target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/render/LayoutRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/MeasureRequirement.hpp ${CRU_UI_INCLUDE_DIR}/render/RenderObject.hpp + ${CRU_UI_INCLUDE_DIR}/render/ScrollBarDelegate.hpp ${CRU_UI_INCLUDE_DIR}/render/ScrollRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/StackLayoutRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/TextRenderObject.hpp diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index a40ce9b8..7cf750cd 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -11,6 +11,11 @@ #include namespace cru::ui::render { +void RenderObject::SetAttachedControl(controls::Control* new_control) { + control_ = new_control; + OnAttachedControlChanged(new_control); +} + void RenderObject::AddChild(RenderObject* render_object, const Index position) { Expects(child_mode_ != ChildMode::None); Expects(!(child_mode_ == ChildMode::Single && children_.size() > 0)); @@ -41,6 +46,15 @@ void RenderObject::RemoveChild(const Index position) { OnRemoveChild(removed_child, position); } +RenderObject* RenderObject::GetFirstChild() const { + const auto& children = GetChildren(); + if (children.empty()) { + return nullptr; + } else { + return children.front(); + } +} + void RenderObject::TraverseDescendants( const std::function& action) { action(this); diff --git a/src/ui/render/ScrollBarDelegate.cpp b/src/ui/render/ScrollBarDelegate.cpp new file mode 100644 index 00000000..2814c567 --- /dev/null +++ b/src/ui/render/ScrollBarDelegate.cpp @@ -0,0 +1,145 @@ +#include "cru/ui/render/ScrollBarDelegate.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](float offset) { + this->scroll_attempt_event_.Raise({offset, 0}); + }); + vertical_bar_.ScrollAttemptEvent()->AddHandler([this](float offset) { + this->scroll_attempt_event_.Raise({0, offset}); + }); +} + +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 diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index 5b9cb627..18b0adbf 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -2,8 +2,11 @@ #include "cru/platform/graphics/Painter.hpp" #include "cru/platform/graphics/util/Painter.hpp" +#include "cru/ui/controls/Control.hpp" +#include "cru/ui/render/ScrollBarDelegate.hpp" #include +#include namespace cru::ui::render { namespace { @@ -31,6 +34,10 @@ Point CoerceScroll(const Point& scroll_offset, const Size& content_size, } } // namespace +ScrollRenderObject::ScrollRenderObject() : RenderObject(ChildMode::Single) { + scroll_bar_delegate_ = std::make_unique(this); +} + RenderObject* ScrollRenderObject::HitTest(const Point& point) { if (const auto child = GetSingleChild()) { const auto offset = child->GetOffset(); @@ -52,6 +59,7 @@ void ScrollRenderObject::OnDrawCore(platform::graphics::IPainter* painter) { [child](platform::graphics::IPainter* p) { child->Draw(p); }); painter->PopLayer(); } + scroll_bar_delegate_->DrawScrollBar(painter); } Point ScrollRenderObject::GetScrollOffset() { @@ -141,4 +149,12 @@ void ScrollRenderObject::OnLayoutContent(const Rect& content_rect) { child->Layout(content_rect.GetLeftTop() - GetScrollOffset()); } } + +void ScrollRenderObject::OnAttachedControlChanged(controls::Control* control) { + if (control) { + scroll_bar_delegate_->InstallHandlers(control); + } else { + scroll_bar_delegate_->UninstallHandlers(); + } +} } // namespace cru::ui::render -- cgit v1.2.3 From 4a0c86d94a06e72be0988062d49a19c05142434a Mon Sep 17 00:00:00 2001 From: crupest Date: Sat, 27 Feb 2021 22:16:28 +0800 Subject: ... --- include/cru/ui/Base.hpp | 4 +- include/cru/ui/render/ScrollBar.hpp | 144 ++++++++++++++++++++++++++ include/cru/ui/render/ScrollBarDelegate.hpp | 142 -------------------------- include/cru/ui/render/ScrollRenderObject.hpp | 2 +- src/ui/CMakeLists.txt | 4 +- src/ui/render/ScrollBar.cpp | 143 ++++++++++++++++++++++++++ src/ui/render/ScrollBarDelegate.cpp | 145 --------------------------- src/ui/render/ScrollRenderObject.cpp | 2 +- 8 files changed, 294 insertions(+), 292 deletions(-) create mode 100644 include/cru/ui/render/ScrollBar.hpp delete mode 100644 include/cru/ui/render/ScrollBarDelegate.hpp create mode 100644 src/ui/render/ScrollBar.cpp delete mode 100644 src/ui/render/ScrollBarDelegate.cpp (limited to 'include/cru/ui/render/ScrollRenderObject.hpp') diff --git a/include/cru/ui/Base.hpp b/include/cru/ui/Base.hpp index b2939a0b..fbdfec77 100644 --- a/include/cru/ui/Base.hpp +++ b/include/cru/ui/Base.hpp @@ -43,9 +43,11 @@ class RenderObject; namespace style { class StyleRuleSet; class StyleRuleSetBind; -} +} // namespace style //-------------------- region: basic types -------------------- +enum class Direction { Horizontal, Vertical }; + namespace internal { constexpr int align_start = 0; constexpr int align_end = align_start + 1; diff --git a/include/cru/ui/render/ScrollBar.hpp b/include/cru/ui/render/ScrollBar.hpp new file mode 100644 index 00000000..a9be49a3 --- /dev/null +++ b/include/cru/ui/render/ScrollBar.hpp @@ -0,0 +1,144 @@ +#pragma once +#include "Base.hpp" +#include "cru/common/Base.hpp" +#include "cru/common/Event.hpp" +#include "cru/platform/graphics/Base.hpp" +#include "cru/platform/graphics/Painter.hpp" +#include "cru/platform/gui/UiApplication.hpp" +#include "cru/ui/Base.hpp" +#include "cru/ui/controls/Control.hpp" + +#include +#include +#include + +namespace cru::ui::render { +class ScrollRenderObject; + +enum class ScrollKind { Absolute, Page, Line }; + +struct Scroll { + Direction direction; + ScrollKind kind; + float offset; +}; + +enum class ScrollBarAreaKind { + UpArrow, // Line up + DownArrow, // Line down + UpThumb, // Page up + DownThumb, // Page down + Thumb +}; + +class ScrollBar : public Object { + public: + explicit ScrollBar(gsl::not_null render_object); + + CRU_DELETE_COPY(ScrollBar) + CRU_DELETE_MOVE(ScrollBar) + + ~ScrollBar() override = default; + + public: + bool IsEnabled() const { return is_enabled_; } + void SetEnabled(bool value); + + void Draw(platform::graphics::IPainter* painter); + + virtual std::optional HitTest(const Point& point) = 0; + + IEvent* ScrollAttemptEvent() { return &scroll_attempt_event_; } + + void InstallHandlers(controls::Control* control); + void UninstallHandlers() { InstallHandlers(nullptr); } + + gsl::not_null> + GetCollapseThumbBrush() const; + + protected: + virtual void OnDraw(platform::graphics::IPainter* painter, bool expand) = 0; + + protected: + gsl::not_null render_object_; + + private: + bool is_enabled_ = true; + + bool is_expanded_ = false; + + std::shared_ptr collapse_thumb_brush_; + + EventRevokerListGuard event_guard_; + + Event scroll_attempt_event_; +}; + +class HorizontalScrollBar : public ScrollBar { + public: + explicit HorizontalScrollBar( + gsl::not_null render_object); + + CRU_DELETE_COPY(HorizontalScrollBar) + CRU_DELETE_MOVE(HorizontalScrollBar) + + ~HorizontalScrollBar() override = default; + + public: + std::optional HitTest(const Point& point) override; + + protected: + void OnDraw(platform::graphics::IPainter* painter, bool expand) override; +}; + +class VerticalScrollBar : public ScrollBar { + public: + explicit VerticalScrollBar(gsl::not_null render_object); + + CRU_DELETE_COPY(VerticalScrollBar) + CRU_DELETE_MOVE(VerticalScrollBar) + + ~VerticalScrollBar() override = default; + + public: + std::optional HitTest(const Point& point) override; + + protected: + void OnDraw(platform::graphics::IPainter* painter, bool expand) override; +}; + +// A delegate to draw scrollbar and register related events. +class ScrollBarDelegate : public Object { + public: + explicit ScrollBarDelegate(gsl::not_null render_object); + + CRU_DELETE_COPY(ScrollBarDelegate) + CRU_DELETE_MOVE(ScrollBarDelegate) + + ~ScrollBarDelegate() override = default; + + public: + bool IsHorizontalBarEnabled() const { return horizontal_bar_.IsEnabled(); } + void SetHorizontalBarEnabled(bool value) { + horizontal_bar_.SetEnabled(value); + } + + bool IsVerticalBarEnabled() const { return horizontal_bar_.IsEnabled(); } + void SetVerticalBarEnabled(bool value) { horizontal_bar_.SetEnabled(value); } + + IEvent* ScrollAttemptEvent() { return &scroll_attempt_event_; } + + void DrawScrollBar(platform::graphics::IPainter* painter); + + void InstallHandlers(controls::Control* control); + void UninstallHandlers() { InstallHandlers(nullptr); } + + private: + gsl::not_null render_object_; + + HorizontalScrollBar horizontal_bar_; + VerticalScrollBar vertical_bar_; + + Event scroll_attempt_event_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollBarDelegate.hpp b/include/cru/ui/render/ScrollBarDelegate.hpp deleted file mode 100644 index e5c63f6d..00000000 --- a/include/cru/ui/render/ScrollBarDelegate.hpp +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once -#include "Base.hpp" -#include "cru/common/Base.hpp" -#include "cru/common/Event.hpp" -#include "cru/platform/graphics/Base.hpp" -#include "cru/platform/graphics/Painter.hpp" -#include "cru/platform/gui/UiApplication.hpp" -#include "cru/ui/controls/Control.hpp" - -#include -#include -#include - -namespace cru::ui::render { -class ScrollRenderObject; - -enum class ScrollBarAreaKind { - UpArrow, // Line up - DownArrow, // Line down - UpThumb, // Page up - DownThumb, // Page down - Thumb -}; - -class ScrollBar : public Object { - public: - explicit ScrollBar(gsl::not_null render_object); - - CRU_DELETE_COPY(ScrollBar) - CRU_DELETE_MOVE(ScrollBar) - - ~ScrollBar() override = default; - - public: - bool IsEnabled() const { return is_enabled_; } - void SetEnabled(bool value); - - void Draw(platform::graphics::IPainter* painter); - - virtual std::optional HitTest(const Point& point) = 0; - - IEvent* ScrollAttemptEvent() { return &scroll_attempt_event_; } - - void InstallHandlers(controls::Control* control); - void UninstallHandlers() { InstallHandlers(nullptr); } - - gsl::not_null> - GetCollapseThumbBrush() const; - - protected: - virtual void OnDraw(platform::graphics::IPainter* painter, bool expand) = 0; - - protected: - gsl::not_null render_object_; - - private: - bool is_enabled_ = true; - - bool is_expanded_ = false; - - std::shared_ptr collapse_thumb_brush_; - - EventRevokerListGuard event_guard_; - - Event scroll_attempt_event_; -}; - -class HorizontalScrollBar : public ScrollBar { - public: - explicit HorizontalScrollBar( - gsl::not_null render_object); - - CRU_DELETE_COPY(HorizontalScrollBar) - CRU_DELETE_MOVE(HorizontalScrollBar) - - ~HorizontalScrollBar() override = default; - - public: - std::optional HitTest(const Point& point) override; - - protected: - void OnDraw(platform::graphics::IPainter* painter, bool expand) override; -}; - -class VerticalScrollBar : public ScrollBar { - public: - explicit VerticalScrollBar(gsl::not_null render_object); - - CRU_DELETE_COPY(VerticalScrollBar) - CRU_DELETE_MOVE(VerticalScrollBar) - - ~VerticalScrollBar() override = default; - - public: - std::optional HitTest(const Point& point) override; - - protected: - void OnDraw(platform::graphics::IPainter* painter, bool expand) override; -}; - -struct ScrollBarScrollAttemptArgs { - float x_offset; - float y_offset; -}; - -// A delegate to draw scrollbar and register related events. -class ScrollBarDelegate : public Object { - public: - explicit ScrollBarDelegate(gsl::not_null render_object); - - CRU_DELETE_COPY(ScrollBarDelegate) - CRU_DELETE_MOVE(ScrollBarDelegate) - - ~ScrollBarDelegate() override = default; - - public: - bool IsHorizontalBarEnabled() const { return horizontal_bar_.IsEnabled(); } - void SetHorizontalBarEnabled(bool value) { - horizontal_bar_.SetEnabled(value); - } - - bool IsVerticalBarEnabled() const { return horizontal_bar_.IsEnabled(); } - void SetVerticalBarEnabled(bool value) { horizontal_bar_.SetEnabled(value); } - - IEvent* ScrollAttemptEvent() { - return &scroll_attempt_event_; - } - - void DrawScrollBar(platform::graphics::IPainter* painter); - - void InstallHandlers(controls::Control* control); - void UninstallHandlers() { InstallHandlers(nullptr); } - - private: - gsl::not_null render_object_; - - HorizontalScrollBar horizontal_bar_; - VerticalScrollBar vertical_bar_; - - Event scroll_attempt_event_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp index 5a431527..6a6ef198 100644 --- a/include/cru/ui/render/ScrollRenderObject.hpp +++ b/include/cru/ui/render/ScrollRenderObject.hpp @@ -2,7 +2,7 @@ #include "RenderObject.hpp" #include "cru/platform/graphics/util/Painter.hpp" -#include "cru/ui/render/ScrollBarDelegate.hpp" +#include "cru/ui/render/ScrollBar.hpp" #include #include diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 6153bc07..7d2792d6 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -33,7 +33,7 @@ add_library(cru_ui STATIC render/FlexLayoutRenderObject.cpp render/LayoutHelper.cpp render/RenderObject.cpp - render/ScrollBarDelegate.cpp + render/ScrollBar.cpp render/ScrollRenderObject.cpp render/StackLayoutRenderObject.cpp render/TextRenderObject.cpp @@ -78,7 +78,7 @@ target_sources(cru_ui PUBLIC ${CRU_UI_INCLUDE_DIR}/render/LayoutRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/MeasureRequirement.hpp ${CRU_UI_INCLUDE_DIR}/render/RenderObject.hpp - ${CRU_UI_INCLUDE_DIR}/render/ScrollBarDelegate.hpp + ${CRU_UI_INCLUDE_DIR}/render/ScrollBar.hpp ${CRU_UI_INCLUDE_DIR}/render/ScrollRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/StackLayoutRenderObject.hpp ${CRU_UI_INCLUDE_DIR}/render/TextRenderObject.hpp 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 diff --git a/src/ui/render/ScrollBarDelegate.cpp b/src/ui/render/ScrollBarDelegate.cpp deleted file mode 100644 index 2814c567..00000000 --- a/src/ui/render/ScrollBarDelegate.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "cru/ui/render/ScrollBarDelegate.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](float offset) { - this->scroll_attempt_event_.Raise({offset, 0}); - }); - vertical_bar_.ScrollAttemptEvent()->AddHandler([this](float offset) { - this->scroll_attempt_event_.Raise({0, offset}); - }); -} - -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 diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index 18b0adbf..a9ec729d 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -3,7 +3,7 @@ #include "cru/platform/graphics/Painter.hpp" #include "cru/platform/graphics/util/Painter.hpp" #include "cru/ui/controls/Control.hpp" -#include "cru/ui/render/ScrollBarDelegate.hpp" +#include "cru/ui/render/ScrollBar.hpp" #include #include -- 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 'include/cru/ui/render/ScrollRenderObject.hpp') 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