aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-02-24 23:01:15 +0800
committercrupest <crupest@outlook.com>2021-02-24 23:01:15 +0800
commit9c2f860ea80310f87b62a2947b4ddea5e7d85587 (patch)
treee912a13eb2e82b6874340fe2833989d62525c761
parent1e1170a89330881c5fad60988bc27c824dfcf454 (diff)
downloadcru-9c2f860ea80310f87b62a2947b4ddea5e7d85587.tar.gz
cru-9c2f860ea80310f87b62a2947b4ddea5e7d85587.tar.bz2
cru-9c2f860ea80310f87b62a2947b4ddea5e7d85587.zip
feat: Scroll bar. Only collapse state.
-rw-r--r--include/cru/platform/GraphBase.hpp6
-rw-r--r--include/cru/ui/render/RenderObject.hpp16
-rw-r--r--include/cru/ui/render/ScrollBarDelegate.hpp142
-rw-r--r--include/cru/ui/render/ScrollRenderObject.hpp8
-rw-r--r--src/ui/CMakeLists.txt2
-rw-r--r--src/ui/render/RenderObject.cpp14
-rw-r--r--src/ui/render/ScrollBarDelegate.cpp145
-rw-r--r--src/ui/render/ScrollRenderObject.cpp16
8 files changed, 342 insertions, 7 deletions
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<void(RenderObject*)>& 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 <gsl/pointers>
+#include <memory>
+#include <optional>
+
+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<ScrollRenderObject*> 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<ScrollBarAreaKind> HitTest(const Point& point) = 0;
+
+ IEvent<float>* ScrollAttemptEvent() { return &scroll_attempt_event_; }
+
+ void InstallHandlers(controls::Control* control);
+ void UninstallHandlers() { InstallHandlers(nullptr); }
+
+ gsl::not_null<std::shared_ptr<platform::graphics::IBrush>>
+ GetCollapseThumbBrush() const;
+
+ protected:
+ virtual void OnDraw(platform::graphics::IPainter* painter, bool expand) = 0;
+
+ protected:
+ gsl::not_null<ScrollRenderObject*> render_object_;
+
+ private:
+ bool is_enabled_ = true;
+
+ bool is_expanded_ = false;
+
+ std::shared_ptr<platform::graphics::IBrush> collapse_thumb_brush_;
+
+ EventRevokerListGuard event_guard_;
+
+ Event<float> scroll_attempt_event_;
+};
+
+class HorizontalScrollBar : public ScrollBar {
+ public:
+ explicit HorizontalScrollBar(
+ gsl::not_null<ScrollRenderObject*> render_object);
+
+ CRU_DELETE_COPY(HorizontalScrollBar)
+ CRU_DELETE_MOVE(HorizontalScrollBar)
+
+ ~HorizontalScrollBar() override = default;
+
+ public:
+ std::optional<ScrollBarAreaKind> 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<ScrollRenderObject*> render_object);
+
+ CRU_DELETE_COPY(VerticalScrollBar)
+ CRU_DELETE_MOVE(VerticalScrollBar)
+
+ ~VerticalScrollBar() override = default;
+
+ public:
+ std::optional<ScrollBarAreaKind> 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<ScrollRenderObject*> 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<ScrollBarScrollAttemptArgs>* ScrollAttemptEvent() {
+ return &scroll_attempt_event_;
+ }
+
+ void DrawScrollBar(platform::graphics::IPainter* painter);
+
+ void InstallHandlers(controls::Control* control);
+ void UninstallHandlers() { InstallHandlers(nullptr); }
+
+ private:
+ gsl::not_null<ScrollRenderObject*> render_object_;
+
+ HorizontalScrollBar horizontal_bar_;
+ VerticalScrollBar vertical_bar_;
+
+ Event<ScrollBarScrollAttemptArgs> 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 <memory>
#include <optional>
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<ScrollBarDelegate> 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 <vector>
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<void(RenderObject*)>& 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 <gsl/pointers>
+#include <optional>
+
+namespace cru::ui::render {
+constexpr float kScrollBarCollapseThumbWidth = 2;
+
+ScrollBar::ScrollBar(gsl::not_null<ScrollRenderObject*> 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<std::shared_ptr<platform::graphics::IBrush>>
+ScrollBar::GetCollapseThumbBrush() const {
+ // TODO: Read theme resource.
+ return collapse_thumb_brush_;
+}
+
+HorizontalScrollBar::HorizontalScrollBar(
+ gsl::not_null<ScrollRenderObject*> render_object)
+ : ScrollBar(render_object) {}
+
+std::optional<ScrollBarAreaKind> 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<ScrollRenderObject*> render_object)
+ : ScrollBar(render_object) {}
+
+std::optional<ScrollBarAreaKind> 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<ScrollRenderObject*> 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 <algorithm>
+#include <memory>
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<ScrollBarDelegate>(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