From 74bb9cd27242b9320f99ff4d2b50c3051576cc14 Mon Sep 17 00:00:00 2001 From: crupest Date: Tue, 8 Feb 2022 16:53:51 +0800 Subject: ... --- include/cru/ui/render/Base.h | 12 + include/cru/ui/render/Base.hpp | 12 - include/cru/ui/render/BorderRenderObject.h | 109 +++++++++ include/cru/ui/render/BorderRenderObject.hpp | 109 --------- include/cru/ui/render/CanvasRenderObject.h | 36 +++ include/cru/ui/render/CanvasRenderObject.hpp | 36 --- include/cru/ui/render/FlexLayoutRenderObject.h | 120 ++++++++++ include/cru/ui/render/FlexLayoutRenderObject.hpp | 120 ---------- include/cru/ui/render/LayoutHelper.h | 14 ++ include/cru/ui/render/LayoutHelper.hpp | 14 -- include/cru/ui/render/LayoutRenderObject.h | 86 ++++++++ include/cru/ui/render/LayoutRenderObject.hpp | 86 -------- include/cru/ui/render/MeasureRequirement.h | 251 +++++++++++++++++++++ include/cru/ui/render/MeasureRequirement.hpp | 251 --------------------- include/cru/ui/render/RenderObject.h | 240 ++++++++++++++++++++ include/cru/ui/render/RenderObject.hpp | 240 -------------------- include/cru/ui/render/ScrollBar.h | 255 ++++++++++++++++++++++ include/cru/ui/render/ScrollBar.hpp | 255 ---------------------- include/cru/ui/render/ScrollRenderObject.h | 98 +++++++++ include/cru/ui/render/ScrollRenderObject.hpp | 98 --------- include/cru/ui/render/StackLayoutRenderObject.h | 57 +++++ include/cru/ui/render/StackLayoutRenderObject.hpp | 57 ----- include/cru/ui/render/TextRenderObject.h | 121 ++++++++++ include/cru/ui/render/TextRenderObject.hpp | 121 ---------- 24 files changed, 1399 insertions(+), 1399 deletions(-) create mode 100644 include/cru/ui/render/Base.h delete mode 100644 include/cru/ui/render/Base.hpp create mode 100644 include/cru/ui/render/BorderRenderObject.h delete mode 100644 include/cru/ui/render/BorderRenderObject.hpp create mode 100644 include/cru/ui/render/CanvasRenderObject.h delete mode 100644 include/cru/ui/render/CanvasRenderObject.hpp create mode 100644 include/cru/ui/render/FlexLayoutRenderObject.h delete mode 100644 include/cru/ui/render/FlexLayoutRenderObject.hpp create mode 100644 include/cru/ui/render/LayoutHelper.h delete mode 100644 include/cru/ui/render/LayoutHelper.hpp create mode 100644 include/cru/ui/render/LayoutRenderObject.h delete mode 100644 include/cru/ui/render/LayoutRenderObject.hpp create mode 100644 include/cru/ui/render/MeasureRequirement.h delete mode 100644 include/cru/ui/render/MeasureRequirement.hpp create mode 100644 include/cru/ui/render/RenderObject.h delete mode 100644 include/cru/ui/render/RenderObject.hpp create mode 100644 include/cru/ui/render/ScrollBar.h delete mode 100644 include/cru/ui/render/ScrollBar.hpp create mode 100644 include/cru/ui/render/ScrollRenderObject.h delete mode 100644 include/cru/ui/render/ScrollRenderObject.hpp create mode 100644 include/cru/ui/render/StackLayoutRenderObject.h delete mode 100644 include/cru/ui/render/StackLayoutRenderObject.hpp create mode 100644 include/cru/ui/render/TextRenderObject.h delete mode 100644 include/cru/ui/render/TextRenderObject.hpp (limited to 'include/cru/ui/render') diff --git a/include/cru/ui/render/Base.h b/include/cru/ui/render/Base.h new file mode 100644 index 00000000..fca67086 --- /dev/null +++ b/include/cru/ui/render/Base.h @@ -0,0 +1,12 @@ +#pragma once +#include "../Base.h" + +namespace cru::ui::render { +class RenderObject; +class BorderRenderObject; +class CanvasRenderObject; +class FlexLayoutRenderObject; +class ScrollRenderObject; +class StackLayoutRenderObject; +class TextRenderObject; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/Base.hpp b/include/cru/ui/render/Base.hpp deleted file mode 100644 index ac67349e..00000000 --- a/include/cru/ui/render/Base.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "../Base.hpp" - -namespace cru::ui::render { -class RenderObject; -class BorderRenderObject; -class CanvasRenderObject; -class FlexLayoutRenderObject; -class ScrollRenderObject; -class StackLayoutRenderObject; -class TextRenderObject; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/BorderRenderObject.h b/include/cru/ui/render/BorderRenderObject.h new file mode 100644 index 00000000..d75a979f --- /dev/null +++ b/include/cru/ui/render/BorderRenderObject.h @@ -0,0 +1,109 @@ +#pragma once +#include +#include "../style/ApplyBorderStyleInfo.h" +#include "RenderObject.h" +#include "cru/platform/GraphicsBase.h" +#include "cru/ui/Base.h" + +namespace cru::ui::render { +class CRU_UI_API BorderRenderObject : public RenderObject { + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::BorderRenderObject") + + public: + BorderRenderObject(); + BorderRenderObject(const BorderRenderObject& other) = delete; + BorderRenderObject(BorderRenderObject&& other) = delete; + BorderRenderObject& operator=(const BorderRenderObject& other) = delete; + BorderRenderObject& operator=(BorderRenderObject&& other) = delete; + ~BorderRenderObject() override; + + bool IsBorderEnabled() const { return is_border_enabled_; } + void SetBorderEnabled(bool enabled) { is_border_enabled_ = enabled; } + + std::shared_ptr GetBorderBrush() { + return border_brush_; + } + + void SetBorderBrush(std::shared_ptr brush) { + if (brush == border_brush_) return; + border_brush_ = std::move(brush); + InvalidatePaint(); + } + + Thickness GetBorderThickness() const { return border_thickness_; } + + void SetBorderThickness(const Thickness thickness) { + if (thickness == border_thickness_) return; + border_thickness_ = thickness; + InvalidateLayout(); + } + + CornerRadius GetBorderRadius() { return border_radius_; } + + void SetBorderRadius(const CornerRadius radius) { + if (radius == border_radius_) return; + border_radius_ = radius; + RecreateGeometry(); + } + + std::shared_ptr GetForegroundBrush() { + return foreground_brush_; + } + + void SetForegroundBrush(std::shared_ptr brush) { + if (brush == foreground_brush_) return; + foreground_brush_ = std::move(brush); + InvalidatePaint(); + } + + std::shared_ptr GetBackgroundBrush() { + return background_brush_; + } + + void SetBackgroundBrush(std::shared_ptr brush) { + if (brush == background_brush_) return; + background_brush_ = std::move(brush); + InvalidatePaint(); + } + + void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style); + + RenderObject* HitTest(const Point& point) override; + + Thickness GetOuterSpaceThickness() const override; + Rect GetPaddingRect() const override; + Rect GetContentRect() const override; + + std::u16string_view GetName() const override { return u"BorderRenderObject"; } + + protected: + void OnDrawCore(platform::graphics::IPainter* painter) override; + + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + void OnAfterLayout() override; + + private: + void RecreateGeometry(); + + private: + bool is_border_enabled_ = false; + + std::shared_ptr border_brush_; + Thickness border_thickness_; + CornerRadius border_radius_; + + std::shared_ptr foreground_brush_; + std::shared_ptr background_brush_; + + // The ring. Used for painting. + std::unique_ptr geometry_; + // Area including inner area of the border. Used for painting foreground and + // background. + std::unique_ptr border_inner_geometry_; + // Area including border ring and inner area. Used for hit test. + std::unique_ptr border_outer_geometry_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/BorderRenderObject.hpp b/include/cru/ui/render/BorderRenderObject.hpp deleted file mode 100644 index bf4b27a1..00000000 --- a/include/cru/ui/render/BorderRenderObject.hpp +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once -#include -#include "../style/ApplyBorderStyleInfo.hpp" -#include "RenderObject.hpp" -#include "cru/platform/GraphicsBase.hpp" -#include "cru/ui/Base.hpp" - -namespace cru::ui::render { -class CRU_UI_API BorderRenderObject : public RenderObject { - CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::BorderRenderObject") - - public: - BorderRenderObject(); - BorderRenderObject(const BorderRenderObject& other) = delete; - BorderRenderObject(BorderRenderObject&& other) = delete; - BorderRenderObject& operator=(const BorderRenderObject& other) = delete; - BorderRenderObject& operator=(BorderRenderObject&& other) = delete; - ~BorderRenderObject() override; - - bool IsBorderEnabled() const { return is_border_enabled_; } - void SetBorderEnabled(bool enabled) { is_border_enabled_ = enabled; } - - std::shared_ptr GetBorderBrush() { - return border_brush_; - } - - void SetBorderBrush(std::shared_ptr brush) { - if (brush == border_brush_) return; - border_brush_ = std::move(brush); - InvalidatePaint(); - } - - Thickness GetBorderThickness() const { return border_thickness_; } - - void SetBorderThickness(const Thickness thickness) { - if (thickness == border_thickness_) return; - border_thickness_ = thickness; - InvalidateLayout(); - } - - CornerRadius GetBorderRadius() { return border_radius_; } - - void SetBorderRadius(const CornerRadius radius) { - if (radius == border_radius_) return; - border_radius_ = radius; - RecreateGeometry(); - } - - std::shared_ptr GetForegroundBrush() { - return foreground_brush_; - } - - void SetForegroundBrush(std::shared_ptr brush) { - if (brush == foreground_brush_) return; - foreground_brush_ = std::move(brush); - InvalidatePaint(); - } - - std::shared_ptr GetBackgroundBrush() { - return background_brush_; - } - - void SetBackgroundBrush(std::shared_ptr brush) { - if (brush == background_brush_) return; - background_brush_ = std::move(brush); - InvalidatePaint(); - } - - void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style); - - RenderObject* HitTest(const Point& point) override; - - Thickness GetOuterSpaceThickness() const override; - Rect GetPaddingRect() const override; - Rect GetContentRect() const override; - - std::u16string_view GetName() const override { return u"BorderRenderObject"; } - - protected: - void OnDrawCore(platform::graphics::IPainter* painter) override; - - Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - void OnAfterLayout() override; - - private: - void RecreateGeometry(); - - private: - bool is_border_enabled_ = false; - - std::shared_ptr border_brush_; - Thickness border_thickness_; - CornerRadius border_radius_; - - std::shared_ptr foreground_brush_; - std::shared_ptr background_brush_; - - // The ring. Used for painting. - std::unique_ptr geometry_; - // Area including inner area of the border. Used for painting foreground and - // background. - std::unique_ptr border_inner_geometry_; - // Area including border ring and inner area. Used for hit test. - std::unique_ptr border_outer_geometry_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/CanvasRenderObject.h b/include/cru/ui/render/CanvasRenderObject.h new file mode 100644 index 00000000..ca55ebc6 --- /dev/null +++ b/include/cru/ui/render/CanvasRenderObject.h @@ -0,0 +1,36 @@ +#pragma once +#include "RenderObject.h" + +namespace cru::ui::render { +// Layout logic: +// If no preferred size is set. Then (100, 100) is used and then coerced to +// required range. +class CRU_UI_API CanvasRenderObject : public RenderObject { + public: + CanvasRenderObject(); + + CRU_DELETE_COPY(CanvasRenderObject) + CRU_DELETE_MOVE(CanvasRenderObject) + + ~CanvasRenderObject(); + + public: + RenderObject* HitTest(const Point& point) override; + + Size GetDesiredSize() const { return desired_size_; } + + IEvent* PaintEvent() { return &paint_event_; } + + protected: + void OnDrawContent(platform::graphics::IPainter* painter) override; + + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + Size desired_size_{}; + + Event paint_event_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/CanvasRenderObject.hpp b/include/cru/ui/render/CanvasRenderObject.hpp deleted file mode 100644 index 68400271..00000000 --- a/include/cru/ui/render/CanvasRenderObject.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include "RenderObject.hpp" - -namespace cru::ui::render { -// Layout logic: -// If no preferred size is set. Then (100, 100) is used and then coerced to -// required range. -class CRU_UI_API CanvasRenderObject : public RenderObject { - public: - CanvasRenderObject(); - - CRU_DELETE_COPY(CanvasRenderObject) - CRU_DELETE_MOVE(CanvasRenderObject) - - ~CanvasRenderObject(); - - public: - RenderObject* HitTest(const Point& point) override; - - Size GetDesiredSize() const { return desired_size_; } - - IEvent* PaintEvent() { return &paint_event_; } - - protected: - void OnDrawContent(platform::graphics::IPainter* painter) override; - - Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - private: - Size desired_size_{}; - - Event paint_event_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/FlexLayoutRenderObject.h b/include/cru/ui/render/FlexLayoutRenderObject.h new file mode 100644 index 00000000..3a8348f6 --- /dev/null +++ b/include/cru/ui/render/FlexLayoutRenderObject.h @@ -0,0 +1,120 @@ +#pragma once +#include "LayoutRenderObject.h" + +#include + +namespace cru::ui::render { +// Measure Logic (v0.1): +// Cross axis measure logic is the same as stack layout. +// +// 1. Layout all children with unspecified(infinate) max main axis length. +// +// 2. Add up main axis length of children to get total main length. +// +// 2.1. If preferred main axis length of parent is specified, then compare +// total length with it. If bigger, shrink is performed. If smaller, expand is +// performed. And preferred main axis length is used as target length. +// +// 2.2. If preferred main axis length of parent is not specified. +// +// 2.2.1. If max main axis length is specified and total length is bigger +// than max main axis length, shrink is performed and max main axis length is +// used as target length. +// +// 2.2.2. Or if max main axis length is specified and total length is smaller +// than max main axis length and any child has a positive expand factor, then +// expand is performed and max main axis length is used as target length. +// +// 2.2.3. Or if min main axis length is specified and total length is smaller +// than min main axis length, expand is performed and min main axis length is +// used as target length. +// +// 3. If shrink or expand is needed, then +// +// 3.1. Shrink: +// +// 3.1.1. Find out all shrink_factor > 0 children to form a adjusting list. +// +// 3.1.2. Use total main length minus target length to get the total shrink +// length. Add up all shrink_factor in adjusting list to get total shrink +// factor. +// +// 3.1.3. Iterate all children in adjusting list: +// +// 3.1.3.1. Calculate its own shrink length as +// shrink_factor / total_shrink_factor * total_shrink_length . +// Use current main axis length of child minus shrink length to get new +// measure length. +// +// 3.1.3.2. If min main axis length of the child is specified and new +// measure length is less than it, then min main axis length is used as new +// measure length. +// +// 3.1.3.3. Or if new measure length is less than 0, then it is coerced to +// 0. +// +// 3.1.3.4. Call measure with max and preferred main axis length set to new +// measure length. Cross axis length requirement is the same as step 1. +// +// 3.1.3.5. After measure, if it has min main axis length specified and +// actual main axis length is equal to it or its actual main axis length is +// 0, then remove it from adjusting list. +// +// 3.1.4. Add up main axis length of children to update total main length. If +// total main length is less than or equal to target length, goto step 4. +// +// 3.1.4. If adjusting list is not empty, go to step 3.1.2. +// +// 3.2. Expand: +// The same as shrink after you exchange related things except some minor +// things like do not do special things on 0. +// +// 4. If final total main axis length exceeeds the max main axis length (if +// specified), then report an error. And result main axis length is the coerced +// length. If final total main axis length is smaller than min main axis length +// (if specified), then coerce the length to the min value but not report error +// and just fill the rest space with blank. +// +class CRU_UI_API FlexLayoutRenderObject : public LayoutRenderObject { + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::FlexLayoutRenderObject") + + public: + FlexLayoutRenderObject() = default; + FlexLayoutRenderObject(const FlexLayoutRenderObject& other) = delete; + FlexLayoutRenderObject& operator=(const FlexLayoutRenderObject& other) = + delete; + FlexLayoutRenderObject(FlexLayoutRenderObject&& other) = delete; + FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete; + ~FlexLayoutRenderObject() override = default; + + std::u16string_view GetName() const override; + + FlexDirection GetFlexDirection() const { return direction_; } + void SetFlexDirection(FlexDirection direction) { + direction_ = direction; + InvalidateLayout(); + } + + FlexMainAlignment GetContentMainAlign() const { return content_main_align_; } + void SetContentMainAlign(FlexMainAlignment align) { + content_main_align_ = align; + InvalidateLayout(); + } + + FlexCrossAlignment GetItemCrossAlign() const { return item_cross_align_; } + void SetItemCrossAlign(FlexCrossAlignment align) { + item_cross_align_ = align; + InvalidateLayout(); + } + + protected: + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + FlexDirection direction_ = FlexDirection::Horizontal; + FlexMainAlignment content_main_align_ = FlexMainAlignment::Start; + FlexCrossAlignment item_cross_align_ = FlexCrossAlignment::Center; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/FlexLayoutRenderObject.hpp b/include/cru/ui/render/FlexLayoutRenderObject.hpp deleted file mode 100644 index c6fb7211..00000000 --- a/include/cru/ui/render/FlexLayoutRenderObject.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -#include "LayoutRenderObject.hpp" - -#include - -namespace cru::ui::render { -// Measure Logic (v0.1): -// Cross axis measure logic is the same as stack layout. -// -// 1. Layout all children with unspecified(infinate) max main axis length. -// -// 2. Add up main axis length of children to get total main length. -// -// 2.1. If preferred main axis length of parent is specified, then compare -// total length with it. If bigger, shrink is performed. If smaller, expand is -// performed. And preferred main axis length is used as target length. -// -// 2.2. If preferred main axis length of parent is not specified. -// -// 2.2.1. If max main axis length is specified and total length is bigger -// than max main axis length, shrink is performed and max main axis length is -// used as target length. -// -// 2.2.2. Or if max main axis length is specified and total length is smaller -// than max main axis length and any child has a positive expand factor, then -// expand is performed and max main axis length is used as target length. -// -// 2.2.3. Or if min main axis length is specified and total length is smaller -// than min main axis length, expand is performed and min main axis length is -// used as target length. -// -// 3. If shrink or expand is needed, then -// -// 3.1. Shrink: -// -// 3.1.1. Find out all shrink_factor > 0 children to form a adjusting list. -// -// 3.1.2. Use total main length minus target length to get the total shrink -// length. Add up all shrink_factor in adjusting list to get total shrink -// factor. -// -// 3.1.3. Iterate all children in adjusting list: -// -// 3.1.3.1. Calculate its own shrink length as -// shrink_factor / total_shrink_factor * total_shrink_length . -// Use current main axis length of child minus shrink length to get new -// measure length. -// -// 3.1.3.2. If min main axis length of the child is specified and new -// measure length is less than it, then min main axis length is used as new -// measure length. -// -// 3.1.3.3. Or if new measure length is less than 0, then it is coerced to -// 0. -// -// 3.1.3.4. Call measure with max and preferred main axis length set to new -// measure length. Cross axis length requirement is the same as step 1. -// -// 3.1.3.5. After measure, if it has min main axis length specified and -// actual main axis length is equal to it or its actual main axis length is -// 0, then remove it from adjusting list. -// -// 3.1.4. Add up main axis length of children to update total main length. If -// total main length is less than or equal to target length, goto step 4. -// -// 3.1.4. If adjusting list is not empty, go to step 3.1.2. -// -// 3.2. Expand: -// The same as shrink after you exchange related things except some minor -// things like do not do special things on 0. -// -// 4. If final total main axis length exceeeds the max main axis length (if -// specified), then report an error. And result main axis length is the coerced -// length. If final total main axis length is smaller than min main axis length -// (if specified), then coerce the length to the min value but not report error -// and just fill the rest space with blank. -// -class CRU_UI_API FlexLayoutRenderObject : public LayoutRenderObject { - CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::FlexLayoutRenderObject") - - public: - FlexLayoutRenderObject() = default; - FlexLayoutRenderObject(const FlexLayoutRenderObject& other) = delete; - FlexLayoutRenderObject& operator=(const FlexLayoutRenderObject& other) = - delete; - FlexLayoutRenderObject(FlexLayoutRenderObject&& other) = delete; - FlexLayoutRenderObject& operator=(FlexLayoutRenderObject&& other) = delete; - ~FlexLayoutRenderObject() override = default; - - std::u16string_view GetName() const override; - - FlexDirection GetFlexDirection() const { return direction_; } - void SetFlexDirection(FlexDirection direction) { - direction_ = direction; - InvalidateLayout(); - } - - FlexMainAlignment GetContentMainAlign() const { return content_main_align_; } - void SetContentMainAlign(FlexMainAlignment align) { - content_main_align_ = align; - InvalidateLayout(); - } - - FlexCrossAlignment GetItemCrossAlign() const { return item_cross_align_; } - void SetItemCrossAlign(FlexCrossAlignment align) { - item_cross_align_ = align; - InvalidateLayout(); - } - - protected: - Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - private: - FlexDirection direction_ = FlexDirection::Horizontal; - FlexMainAlignment content_main_align_ = FlexMainAlignment::Start; - FlexCrossAlignment item_cross_align_ = FlexCrossAlignment::Center; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/LayoutHelper.h b/include/cru/ui/render/LayoutHelper.h new file mode 100644 index 00000000..c2377066 --- /dev/null +++ b/include/cru/ui/render/LayoutHelper.h @@ -0,0 +1,14 @@ +#pragma once +#include "Base.h" + +#include "MeasureRequirement.h" + +namespace cru::ui::render { +float CalculateAnchorByAlignment(Alignment alignment, float start_point, + float content_length, float child_length); + +MeasureLength StackLayoutCalculateChildMaxLength( + MeasureLength parent_preferred_size, MeasureLength parent_max_size, + MeasureLength child_min_size, std::u16string_view log_tag, + std::u16string_view exceeds_message); +} // namespace cru::ui::render diff --git a/include/cru/ui/render/LayoutHelper.hpp b/include/cru/ui/render/LayoutHelper.hpp deleted file mode 100644 index 518dc5a3..00000000 --- a/include/cru/ui/render/LayoutHelper.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include "MeasureRequirement.hpp" - -namespace cru::ui::render { -float CalculateAnchorByAlignment(Alignment alignment, float start_point, - float content_length, float child_length); - -MeasureLength StackLayoutCalculateChildMaxLength( - MeasureLength parent_preferred_size, MeasureLength parent_max_size, - MeasureLength child_min_size, std::u16string_view log_tag, - std::u16string_view exceeds_message); -} // namespace cru::ui::render diff --git a/include/cru/ui/render/LayoutRenderObject.h b/include/cru/ui/render/LayoutRenderObject.h new file mode 100644 index 00000000..42a3aa55 --- /dev/null +++ b/include/cru/ui/render/LayoutRenderObject.h @@ -0,0 +1,86 @@ +#pragma once +#include "RenderObject.h" + +#include "cru/platform/graphics/util/Painter.h" + +namespace cru::ui::render { +template +class CRU_UI_API LayoutRenderObject : public RenderObject { + public: + using ChildLayoutData = TChildLayoutData; + + protected: + LayoutRenderObject() : RenderObject(ChildMode::Multiple) {} + + public: + CRU_DELETE_COPY(LayoutRenderObject) + CRU_DELETE_MOVE(LayoutRenderObject) + + ~LayoutRenderObject() override = default; + + const std::vector& GetChildLayoutDataList() const { + return this->child_layout_data_; + } + + void SetChildLayoutData(Index position, ChildLayoutData data) { + Expects(position >= 0 && + position < static_cast(this->child_layout_data_.size())); + this->child_layout_data_[position] = std::move(data); + this->InvalidateLayout(); + } + + const ChildLayoutData& GetChildLayoutData(Index position) const { + Expects(position >= 0 && + position < static_cast(this->child_layout_data_.size())); + return this->child_layout_data_[position]; + } + + RenderObject* HitTest(const Point& point) override; + + protected: + void OnAddChild(RenderObject* new_child, Index position) override; + void OnRemoveChild(RenderObject* removed_child, Index position) override; + + private: + std::vector child_layout_data_{}; +}; + +template +RenderObject* LayoutRenderObject::HitTest( + const Point& point) { + const auto& children = GetChildren(); + for (auto i = children.crbegin(); i != children.crend(); ++i) { + auto offset = (*i)->GetOffset(); + Point p{point.x - offset.x, point.y - offset.y}; + const auto result = (*i)->HitTest(p); + if (result != nullptr) { + return result; + } + } + + const auto margin = GetMargin(); + const auto size = GetSize(); + return Rect{margin.left, margin.top, + std::max(size.width - margin.GetHorizontalTotal(), 0.0f), + std::max(size.height - margin.GetVerticalTotal(), 0.0f)} + .IsPointInside(point) + ? this + : nullptr; +} // namespace cru::ui::render + +template +void LayoutRenderObject::OnAddChild(RenderObject* new_child, + const Index position) { + CRU_UNUSED(new_child) + + child_layout_data_.emplace(child_layout_data_.cbegin() + position); +} + +template +void LayoutRenderObject::OnRemoveChild( + RenderObject* removed_child, const Index position) { + CRU_UNUSED(removed_child) + + child_layout_data_.erase(child_layout_data_.cbegin() + position); +} +} // namespace cru::ui::render diff --git a/include/cru/ui/render/LayoutRenderObject.hpp b/include/cru/ui/render/LayoutRenderObject.hpp deleted file mode 100644 index 424a5831..00000000 --- a/include/cru/ui/render/LayoutRenderObject.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once -#include "RenderObject.hpp" - -#include "cru/platform/graphics/util/Painter.hpp" - -namespace cru::ui::render { -template -class CRU_UI_API LayoutRenderObject : public RenderObject { - public: - using ChildLayoutData = TChildLayoutData; - - protected: - LayoutRenderObject() : RenderObject(ChildMode::Multiple) {} - - public: - CRU_DELETE_COPY(LayoutRenderObject) - CRU_DELETE_MOVE(LayoutRenderObject) - - ~LayoutRenderObject() override = default; - - const std::vector& GetChildLayoutDataList() const { - return this->child_layout_data_; - } - - void SetChildLayoutData(Index position, ChildLayoutData data) { - Expects(position >= 0 && - position < static_cast(this->child_layout_data_.size())); - this->child_layout_data_[position] = std::move(data); - this->InvalidateLayout(); - } - - const ChildLayoutData& GetChildLayoutData(Index position) const { - Expects(position >= 0 && - position < static_cast(this->child_layout_data_.size())); - return this->child_layout_data_[position]; - } - - RenderObject* HitTest(const Point& point) override; - - protected: - void OnAddChild(RenderObject* new_child, Index position) override; - void OnRemoveChild(RenderObject* removed_child, Index position) override; - - private: - std::vector child_layout_data_{}; -}; - -template -RenderObject* LayoutRenderObject::HitTest( - const Point& point) { - const auto& children = GetChildren(); - for (auto i = children.crbegin(); i != children.crend(); ++i) { - auto offset = (*i)->GetOffset(); - Point p{point.x - offset.x, point.y - offset.y}; - const auto result = (*i)->HitTest(p); - if (result != nullptr) { - return result; - } - } - - const auto margin = GetMargin(); - const auto size = GetSize(); - return Rect{margin.left, margin.top, - std::max(size.width - margin.GetHorizontalTotal(), 0.0f), - std::max(size.height - margin.GetVerticalTotal(), 0.0f)} - .IsPointInside(point) - ? this - : nullptr; -} // namespace cru::ui::render - -template -void LayoutRenderObject::OnAddChild(RenderObject* new_child, - const Index position) { - CRU_UNUSED(new_child) - - child_layout_data_.emplace(child_layout_data_.cbegin() + position); -} - -template -void LayoutRenderObject::OnRemoveChild( - RenderObject* removed_child, const Index position) { - CRU_UNUSED(removed_child) - - child_layout_data_.erase(child_layout_data_.cbegin() + position); -} -} // namespace cru::ui::render diff --git a/include/cru/ui/render/MeasureRequirement.h b/include/cru/ui/render/MeasureRequirement.h new file mode 100644 index 00000000..c740385b --- /dev/null +++ b/include/cru/ui/render/MeasureRequirement.h @@ -0,0 +1,251 @@ +#pragma once +#include "Base.h" + +#include "cru/common/String.h" + +#include +#include +#include + +namespace cru::ui::render { +constexpr Size Min(const Size& left, const Size& right) { + return Size{std::min(left.width, right.width), + std::min(left.height, right.height)}; +} + +constexpr Size Max(const Size& left, const Size& right) { + return Size{std::max(left.width, right.width), + std::max(left.height, right.height)}; +} + +class MeasureLength final { + public: + struct tag_not_specify_t {}; + constexpr static tag_not_specify_t tag_not_specify{}; + + constexpr MeasureLength() : MeasureLength(tag_not_specify) {} + constexpr MeasureLength(tag_not_specify_t) : length_(-1) {} + constexpr MeasureLength(float length) : length_(length) { + Expects(length >= 0); + } + + MeasureLength(const MeasureLength& other) = default; + constexpr MeasureLength& operator=(const MeasureLength& other) = default; + constexpr MeasureLength& operator=(float length) { + Expects(length >= 0); + length_ = length; + return *this; + } + + ~MeasureLength() = default; + + constexpr static MeasureLength NotSpecified() { + return MeasureLength{tag_not_specify}; + } + + constexpr bool IsSpecified() const { return length_ >= 0.0f; } + + // What not specified means depends on situation. + constexpr bool IsNotSpecified() const { return length_ < 0.0f; } + + constexpr float GetLengthOr(float value) const { + return length_ < 0 ? value : length_; + } + + // If not specify max value of float is returned. + constexpr float GetLengthOrMax() const { + return GetLengthOr(std::numeric_limits::max()); + } + + // If not specify 0 is returned. + constexpr float GetLengthOr0() const { return GetLengthOr(0.f); } + + // If not specify, return value is undefined. + constexpr float GetLengthOrUndefined() const { return length_; } + + // Not operator overload because this semantics is not very clear. + constexpr MeasureLength Plus(float length) const { + if (IsSpecified()) + return length_ + length; + else + return NotSpecified(); + } + + // Not operator overload because this semantics is not very clear. + constexpr MeasureLength Minus(float length) const { + if (IsSpecified()) + return std::max(length_ - length, 0.f); + else + return NotSpecified(); + } + + constexpr bool operator==(MeasureLength other) const { + return (length_ < 0 && other.length_ < 0) || length_ == other.length_; + } + + constexpr bool operator!=(MeasureLength other) const { + return !operator==(other); + } + + constexpr static MeasureLength Min(MeasureLength left, MeasureLength right) { + if (left.IsNotSpecified()) { + if (right.IsNotSpecified()) + return NotSpecified(); + else + return right; + } else { + if (right.IsNotSpecified()) + return left; + else + return std::min(left.length_, right.length_); + } + } + + constexpr static MeasureLength Max(MeasureLength left, MeasureLength right) { + if (left.IsNotSpecified()) { + if (right.IsNotSpecified()) + return NotSpecified(); + else + return right; + } else { + if (left.IsNotSpecified()) + return left; + else + return std::max(left.length_, right.length_); + } + } + + String ToDebugString() const { + return IsSpecified() ? ToString(GetLengthOrUndefined()) : u"UNSPECIFIED"; + } + + private: + // -1 for not specify + float length_; +}; + +struct MeasureSize { + MeasureLength width; + MeasureLength height; + + constexpr MeasureSize() = default; + constexpr MeasureSize(MeasureLength width, MeasureLength height) + : width(width), height(height) {} + constexpr MeasureSize(const Size& size) + : width(size.width), height(size.height) {} + constexpr MeasureSize(const MeasureSize& other) = default; + + MeasureSize& operator=(const MeasureSize& other) = default; + constexpr MeasureSize& operator=(const Size& other) { + width = other.width; + height = other.height; + return *this; + } + + constexpr Size GetSizeOrMax() const { + return Size{width.GetLengthOrMax(), height.GetLengthOrMax()}; + } + + constexpr Size GetSizeOr0() const { + return Size{width.GetLengthOr0(), height.GetLengthOr0()}; + } + + constexpr MeasureSize Plus(const Size& size) const { + return MeasureSize{width.Plus(size.width), height.Plus(size.height)}; + } + + constexpr MeasureSize Minus(const Size& size) const { + return MeasureSize{width.Minus(size.width), height.Minus(size.height)}; + } + + constexpr MeasureSize OverrideBy(const MeasureSize& size) const { + return MeasureSize{ + size.width.IsSpecified() ? size.width.GetLengthOrUndefined() + : this->width, + size.height.IsSpecified() ? size.height.GetLengthOrUndefined() + : this->height, + }; + } + + String ToDebugString() const { + return Format(u"({}, {})", width.ToDebugString(), height.ToDebugString()); + } + + constexpr static MeasureSize NotSpecified() { + return MeasureSize{MeasureLength::NotSpecified(), + MeasureLength::NotSpecified()}; + } + + constexpr static MeasureSize Min(const MeasureSize& left, + const MeasureSize& right) { + return MeasureSize{MeasureLength::Min(left.width, right.width), + MeasureLength::Min(left.height, right.height)}; + } + + constexpr static MeasureSize Max(const MeasureSize& left, + const MeasureSize& right) { + return MeasureSize{MeasureLength::Max(left.width, right.width), + MeasureLength::Max(left.height, right.height)}; + } +}; + +struct MeasureRequirement { + MeasureSize max; + MeasureSize min; + + constexpr MeasureRequirement() = default; + constexpr MeasureRequirement(const MeasureSize& max, const MeasureSize& min) + : max(max), min(min) {} + + constexpr bool Satisfy(const Size& size) const { + auto normalized = Normalize(); + return normalized.max.width.GetLengthOrMax() >= size.width && + normalized.max.height.GetLengthOrMax() >= size.height && + normalized.min.width.GetLengthOr0() <= size.width && + normalized.min.height.GetLengthOr0() <= size.height; + } + + constexpr MeasureRequirement Normalize() const { + MeasureRequirement result = *this; + if (result.min.width.GetLengthOr0() > result.max.width.GetLengthOrMax()) + result.min.width = result.max.width; + + if (result.min.height.GetLengthOr0() > result.max.height.GetLengthOrMax()) + result.min.height = result.max.height; + return result; + } + + constexpr Size Coerce(const Size& size) const { + // This order guarentees result is the same as normalized. + return Min(Max(size, min.GetSizeOr0()), max.GetSizeOrMax()); + } + + constexpr MeasureSize Coerce(const MeasureSize& size) const { + MeasureSize result = size; + if (result.width.IsSpecified()) + // This order guarentees result is the same as normalized. + result.width = std::min(std::max(min.width.GetLengthOr0(), + result.width.GetLengthOrUndefined()), + max.width.GetLengthOrMax()); + + if (result.height.IsSpecified()) + // This order guarentees result is the same as normalized. + result.height = std::min(std::max(min.height.GetLengthOr0(), + result.height.GetLengthOrUndefined()), + max.height.GetLengthOrMax()); + + return result; + } + + String ToDebugString() const { + return Format(u"{{min: {}, max: {}}}", min.ToDebugString(), + max.ToDebugString()); + } + + constexpr static MeasureRequirement Merge(const MeasureRequirement& left, + const MeasureRequirement& right) { + return MeasureRequirement{MeasureSize::Min(left.max, right.max), + MeasureSize::Max(left.min, right.min)}; + } +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/MeasureRequirement.hpp b/include/cru/ui/render/MeasureRequirement.hpp deleted file mode 100644 index 90d02a02..00000000 --- a/include/cru/ui/render/MeasureRequirement.hpp +++ /dev/null @@ -1,251 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include "cru/common/String.hpp" - -#include -#include -#include - -namespace cru::ui::render { -constexpr Size Min(const Size& left, const Size& right) { - return Size{std::min(left.width, right.width), - std::min(left.height, right.height)}; -} - -constexpr Size Max(const Size& left, const Size& right) { - return Size{std::max(left.width, right.width), - std::max(left.height, right.height)}; -} - -class MeasureLength final { - public: - struct tag_not_specify_t {}; - constexpr static tag_not_specify_t tag_not_specify{}; - - constexpr MeasureLength() : MeasureLength(tag_not_specify) {} - constexpr MeasureLength(tag_not_specify_t) : length_(-1) {} - constexpr MeasureLength(float length) : length_(length) { - Expects(length >= 0); - } - - MeasureLength(const MeasureLength& other) = default; - constexpr MeasureLength& operator=(const MeasureLength& other) = default; - constexpr MeasureLength& operator=(float length) { - Expects(length >= 0); - length_ = length; - return *this; - } - - ~MeasureLength() = default; - - constexpr static MeasureLength NotSpecified() { - return MeasureLength{tag_not_specify}; - } - - constexpr bool IsSpecified() const { return length_ >= 0.0f; } - - // What not specified means depends on situation. - constexpr bool IsNotSpecified() const { return length_ < 0.0f; } - - constexpr float GetLengthOr(float value) const { - return length_ < 0 ? value : length_; - } - - // If not specify max value of float is returned. - constexpr float GetLengthOrMax() const { - return GetLengthOr(std::numeric_limits::max()); - } - - // If not specify 0 is returned. - constexpr float GetLengthOr0() const { return GetLengthOr(0.f); } - - // If not specify, return value is undefined. - constexpr float GetLengthOrUndefined() const { return length_; } - - // Not operator overload because this semantics is not very clear. - constexpr MeasureLength Plus(float length) const { - if (IsSpecified()) - return length_ + length; - else - return NotSpecified(); - } - - // Not operator overload because this semantics is not very clear. - constexpr MeasureLength Minus(float length) const { - if (IsSpecified()) - return std::max(length_ - length, 0.f); - else - return NotSpecified(); - } - - constexpr bool operator==(MeasureLength other) const { - return (length_ < 0 && other.length_ < 0) || length_ == other.length_; - } - - constexpr bool operator!=(MeasureLength other) const { - return !operator==(other); - } - - constexpr static MeasureLength Min(MeasureLength left, MeasureLength right) { - if (left.IsNotSpecified()) { - if (right.IsNotSpecified()) - return NotSpecified(); - else - return right; - } else { - if (right.IsNotSpecified()) - return left; - else - return std::min(left.length_, right.length_); - } - } - - constexpr static MeasureLength Max(MeasureLength left, MeasureLength right) { - if (left.IsNotSpecified()) { - if (right.IsNotSpecified()) - return NotSpecified(); - else - return right; - } else { - if (left.IsNotSpecified()) - return left; - else - return std::max(left.length_, right.length_); - } - } - - String ToDebugString() const { - return IsSpecified() ? ToString(GetLengthOrUndefined()) : u"UNSPECIFIED"; - } - - private: - // -1 for not specify - float length_; -}; - -struct MeasureSize { - MeasureLength width; - MeasureLength height; - - constexpr MeasureSize() = default; - constexpr MeasureSize(MeasureLength width, MeasureLength height) - : width(width), height(height) {} - constexpr MeasureSize(const Size& size) - : width(size.width), height(size.height) {} - constexpr MeasureSize(const MeasureSize& other) = default; - - MeasureSize& operator=(const MeasureSize& other) = default; - constexpr MeasureSize& operator=(const Size& other) { - width = other.width; - height = other.height; - return *this; - } - - constexpr Size GetSizeOrMax() const { - return Size{width.GetLengthOrMax(), height.GetLengthOrMax()}; - } - - constexpr Size GetSizeOr0() const { - return Size{width.GetLengthOr0(), height.GetLengthOr0()}; - } - - constexpr MeasureSize Plus(const Size& size) const { - return MeasureSize{width.Plus(size.width), height.Plus(size.height)}; - } - - constexpr MeasureSize Minus(const Size& size) const { - return MeasureSize{width.Minus(size.width), height.Minus(size.height)}; - } - - constexpr MeasureSize OverrideBy(const MeasureSize& size) const { - return MeasureSize{ - size.width.IsSpecified() ? size.width.GetLengthOrUndefined() - : this->width, - size.height.IsSpecified() ? size.height.GetLengthOrUndefined() - : this->height, - }; - } - - String ToDebugString() const { - return Format(u"({}, {})", width.ToDebugString(), height.ToDebugString()); - } - - constexpr static MeasureSize NotSpecified() { - return MeasureSize{MeasureLength::NotSpecified(), - MeasureLength::NotSpecified()}; - } - - constexpr static MeasureSize Min(const MeasureSize& left, - const MeasureSize& right) { - return MeasureSize{MeasureLength::Min(left.width, right.width), - MeasureLength::Min(left.height, right.height)}; - } - - constexpr static MeasureSize Max(const MeasureSize& left, - const MeasureSize& right) { - return MeasureSize{MeasureLength::Max(left.width, right.width), - MeasureLength::Max(left.height, right.height)}; - } -}; - -struct MeasureRequirement { - MeasureSize max; - MeasureSize min; - - constexpr MeasureRequirement() = default; - constexpr MeasureRequirement(const MeasureSize& max, const MeasureSize& min) - : max(max), min(min) {} - - constexpr bool Satisfy(const Size& size) const { - auto normalized = Normalize(); - return normalized.max.width.GetLengthOrMax() >= size.width && - normalized.max.height.GetLengthOrMax() >= size.height && - normalized.min.width.GetLengthOr0() <= size.width && - normalized.min.height.GetLengthOr0() <= size.height; - } - - constexpr MeasureRequirement Normalize() const { - MeasureRequirement result = *this; - if (result.min.width.GetLengthOr0() > result.max.width.GetLengthOrMax()) - result.min.width = result.max.width; - - if (result.min.height.GetLengthOr0() > result.max.height.GetLengthOrMax()) - result.min.height = result.max.height; - return result; - } - - constexpr Size Coerce(const Size& size) const { - // This order guarentees result is the same as normalized. - return Min(Max(size, min.GetSizeOr0()), max.GetSizeOrMax()); - } - - constexpr MeasureSize Coerce(const MeasureSize& size) const { - MeasureSize result = size; - if (result.width.IsSpecified()) - // This order guarentees result is the same as normalized. - result.width = std::min(std::max(min.width.GetLengthOr0(), - result.width.GetLengthOrUndefined()), - max.width.GetLengthOrMax()); - - if (result.height.IsSpecified()) - // This order guarentees result is the same as normalized. - result.height = std::min(std::max(min.height.GetLengthOr0(), - result.height.GetLengthOrUndefined()), - max.height.GetLengthOrMax()); - - return result; - } - - String ToDebugString() const { - return Format(u"{{min: {}, max: {}}}", min.ToDebugString(), - max.ToDebugString()); - } - - constexpr static MeasureRequirement Merge(const MeasureRequirement& left, - const MeasureRequirement& right) { - return MeasureRequirement{MeasureSize::Min(left.max, right.max), - MeasureSize::Max(left.min, right.min)}; - } -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h new file mode 100644 index 00000000..1e47e7fc --- /dev/null +++ b/include/cru/ui/render/RenderObject.h @@ -0,0 +1,240 @@ +#pragma once +#include "Base.h" + +#include "MeasureRequirement.h" +#include "cru/common/Base.h" +#include "cru/common/Event.h" +#include "cru/ui/Base.h" + +#include +#include +#include + +namespace cru::ui::render { +// Render object will not destroy its children when destroyed. Control must +// manage lifecycle of its render objects. Since control will destroy its +// children when destroyed, render objects will be destroyed along with it. +// +// About layout: +// Render object should be able to deal with arbitrary size as the result of +// measure and layout. +// +// Parent may pass calculated preferred size down. But the preferred size set on +// child itself takes precedence. +// +// Each render object should obey the measure requirement to set size and report +// a warning when that requirement can't be satisfied with probably bigger size +// of children than that of itself and optional visual effect to indicate that. +// +// If size of chilren are less than min size requirement, then render object +// should try to fill the rest area. If size of children are more than max size +// requirement, then render object should display a warning of the layout +// problem and use the max size of itself with children exceeding its bound. +// (Or better you could use some visual effect to indicate that.) +// +// To write a custom RenderObject, override following methods: +// public: +// void Draw(platform::graphics::IPainter* painter) override; +// RenderObject* HitTest(const Point& point) override; +// protected: +// Size OnMeasureContent(const MeasureRequirement& requirement) override; +// void OnLayoutContent(const Rect& content_rect) override; +class CRU_UI_API RenderObject : public Object { + friend host::WindowHost; + + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::RenderObject") + + protected: + enum class ChildMode { + None, + Single, + Multiple, + }; + + RenderObject() = default; + RenderObject(ChildMode child_mode) : RenderObject() { + SetChildMode(child_mode); + } + + public: + RenderObject(const RenderObject& other) = delete; + RenderObject(RenderObject&& other) = delete; + RenderObject& operator=(const RenderObject& other) = delete; + RenderObject& operator=(RenderObject&& other) = delete; + ~RenderObject() override = default; + + controls::Control* GetAttachedControl() const { return control_; } + void SetAttachedControl(controls::Control* new_control); + + host::WindowHost* GetWindowHost() const { return window_host_; } + + RenderObject* GetParent() const { return parent_; } + + const std::vector& GetChildren() const { return children_; } + Index GetChildCount() const { return static_cast(children_.size()); } + 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 + // accounted for. + Point GetOffset() const { return offset_; } + Size GetSize() const { return size_; } + Point GetTotalOffset() const; + Point FromRootToContent(const Point& point) const; + + Thickness GetMargin() const { return margin_; } + void SetMargin(const Thickness& margin) { + margin_ = margin; + InvalidateLayout(); + } + + Thickness GetPadding() const { return padding_; } + void SetPadding(const Thickness& padding) { + padding_ = padding; + InvalidateLayout(); + } + + MeasureSize GetPreferredSize() const { return preferred_size_; } + void SetPreferredSize(const MeasureSize& preferred_size) { + preferred_size_ = preferred_size; + InvalidateLayout(); + } + + MeasureSize GetMinSize() const { return custom_measure_requirement_.min; } + void SetMinSize(const MeasureSize& min_size) { + custom_measure_requirement_.min = min_size; + InvalidateLayout(); + } + + MeasureSize GetMaxSize() const { return custom_measure_requirement_.max; } + void SetMaxSize(const MeasureSize& max_size) { + custom_measure_requirement_.max = max_size; + InvalidateLayout(); + } + + MeasureRequirement GetCustomMeasureRequirement() const { + return custom_measure_requirement_; + } + + // This method will merge requirement passed by argument and requirement of + // the render object using MeasureRequirement::Merge and then call + // MeasureRequirement::Normalize on it. And it will use preferred size of the + // render object to override the one passed by argument. Then pass the two to + // OnMeasureCore and use the return value of it to set the size of this render + // object. This can be called multiple times on children during measure to + // adjust for better size. + void Measure(const MeasureRequirement& requirement, + const MeasureSize& preferred_size); + // This will set offset of this render object and call OnLayoutCore. + void Layout(const Point& offset); + + virtual Thickness GetOuterSpaceThickness() const; + 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. + // Add offset before pass point to children. + virtual RenderObject* HitTest(const Point& point) = 0; + + IEvent* AttachToHostEvent() { + return &attach_to_host_event_; + } + IEvent* DetachFromHostEvent() { + return &detach_from_host_event_; + } + + public: + void InvalidateLayout(); + void InvalidatePaint(); + + public: + virtual std::u16string_view GetName() const; + std::u16string GetDebugPathInTree() const; + + protected: + void SetChildMode(ChildMode mode) { child_mode_ = mode; } + + protected: + RenderObject* GetSingleChild() const; + + virtual void OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent); + + // default is to invalidate both layout and paint + virtual void OnAddChild(RenderObject* new_child, Index position); + // default is to invalidate both layout and paint + virtual void OnRemoveChild(RenderObject* removed_child, Index position); + + // Draw all children with offset. + void DefaultDrawChildren(platform::graphics::IPainter* painter); + + // Draw all children with translation of content rect lefttop. + void DefaultDrawContent(platform::graphics::IPainter* painter); + + // Call DefaultDrawContent. Then call DefaultDrawChildren. + virtual void OnDrawCore(platform::graphics::IPainter* painter); + + virtual void OnDrawContent(platform::graphics::IPainter* painter); + + // Size measure including margin and padding. Please reduce margin and padding + // or other custom things and pass the result content measure requirement and + // preferred size to OnMeasureContent. Return value must not be negative and + // must obey requirement. + // Note: Implementation should coerce the preferred size into the requirement + // when pass them to OnMeasureContent. + virtual Size OnMeasureCore(const MeasureRequirement& requirement, + const MeasureSize& preferred_size); + + // Please reduce margin and padding or other custom things and pass the result + // content rect to OnLayoutContent. + virtual void OnLayoutCore(); + + // Override this function to measure content and children(Call Measure on + // them). Do not consider margin or padding in this method because they are + // already considered in OnMeasureCore. Returned size must obey requirement. + // Caller should guarantee preferred_size is corerced into required range. + virtual Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) = 0; + + // Layout all content and children(Call Layout on them). + // Lefttop of content_rect should be added when calculated children's offset. + virtual void OnLayoutContent(const Rect& content_rect) = 0; + + virtual void OnAttachedControlChanged(controls::Control* control) { + CRU_UNUSED(control) + } + + virtual void OnAfterLayout(); + + private: + void SetParent(RenderObject* new_parent); + + void SetWindowHostRecursive(host::WindowHost* host); + + private: + controls::Control* control_ = nullptr; + host::WindowHost* window_host_ = nullptr; + + RenderObject* parent_ = nullptr; + std::vector children_{}; + + ChildMode child_mode_ = ChildMode::None; + + Point offset_{}; + Size size_{}; + + MeasureSize preferred_size_; + MeasureRequirement custom_measure_requirement_; + + Thickness margin_{}; + Thickness padding_{}; + + Event attach_to_host_event_; + Event detach_from_host_event_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/RenderObject.hpp b/include/cru/ui/render/RenderObject.hpp deleted file mode 100644 index bac97640..00000000 --- a/include/cru/ui/render/RenderObject.hpp +++ /dev/null @@ -1,240 +0,0 @@ -#pragma once -#include "Base.hpp" - -#include "MeasureRequirement.hpp" -#include "cru/common/Base.hpp" -#include "cru/common/Event.hpp" -#include "cru/ui/Base.hpp" - -#include -#include -#include - -namespace cru::ui::render { -// Render object will not destroy its children when destroyed. Control must -// manage lifecycle of its render objects. Since control will destroy its -// children when destroyed, render objects will be destroyed along with it. -// -// About layout: -// Render object should be able to deal with arbitrary size as the result of -// measure and layout. -// -// Parent may pass calculated preferred size down. But the preferred size set on -// child itself takes precedence. -// -// Each render object should obey the measure requirement to set size and report -// a warning when that requirement can't be satisfied with probably bigger size -// of children than that of itself and optional visual effect to indicate that. -// -// If size of chilren are less than min size requirement, then render object -// should try to fill the rest area. If size of children are more than max size -// requirement, then render object should display a warning of the layout -// problem and use the max size of itself with children exceeding its bound. -// (Or better you could use some visual effect to indicate that.) -// -// To write a custom RenderObject, override following methods: -// public: -// void Draw(platform::graphics::IPainter* painter) override; -// RenderObject* HitTest(const Point& point) override; -// protected: -// Size OnMeasureContent(const MeasureRequirement& requirement) override; -// void OnLayoutContent(const Rect& content_rect) override; -class CRU_UI_API RenderObject : public Object { - friend host::WindowHost; - - CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::RenderObject") - - protected: - enum class ChildMode { - None, - Single, - Multiple, - }; - - RenderObject() = default; - RenderObject(ChildMode child_mode) : RenderObject() { - SetChildMode(child_mode); - } - - public: - RenderObject(const RenderObject& other) = delete; - RenderObject(RenderObject&& other) = delete; - RenderObject& operator=(const RenderObject& other) = delete; - RenderObject& operator=(RenderObject&& other) = delete; - ~RenderObject() override = default; - - controls::Control* GetAttachedControl() const { return control_; } - void SetAttachedControl(controls::Control* new_control); - - host::WindowHost* GetWindowHost() const { return window_host_; } - - RenderObject* GetParent() const { return parent_; } - - const std::vector& GetChildren() const { return children_; } - Index GetChildCount() const { return static_cast(children_.size()); } - 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 - // accounted for. - Point GetOffset() const { return offset_; } - Size GetSize() const { return size_; } - Point GetTotalOffset() const; - Point FromRootToContent(const Point& point) const; - - Thickness GetMargin() const { return margin_; } - void SetMargin(const Thickness& margin) { - margin_ = margin; - InvalidateLayout(); - } - - Thickness GetPadding() const { return padding_; } - void SetPadding(const Thickness& padding) { - padding_ = padding; - InvalidateLayout(); - } - - MeasureSize GetPreferredSize() const { return preferred_size_; } - void SetPreferredSize(const MeasureSize& preferred_size) { - preferred_size_ = preferred_size; - InvalidateLayout(); - } - - MeasureSize GetMinSize() const { return custom_measure_requirement_.min; } - void SetMinSize(const MeasureSize& min_size) { - custom_measure_requirement_.min = min_size; - InvalidateLayout(); - } - - MeasureSize GetMaxSize() const { return custom_measure_requirement_.max; } - void SetMaxSize(const MeasureSize& max_size) { - custom_measure_requirement_.max = max_size; - InvalidateLayout(); - } - - MeasureRequirement GetCustomMeasureRequirement() const { - return custom_measure_requirement_; - } - - // This method will merge requirement passed by argument and requirement of - // the render object using MeasureRequirement::Merge and then call - // MeasureRequirement::Normalize on it. And it will use preferred size of the - // render object to override the one passed by argument. Then pass the two to - // OnMeasureCore and use the return value of it to set the size of this render - // object. This can be called multiple times on children during measure to - // adjust for better size. - void Measure(const MeasureRequirement& requirement, - const MeasureSize& preferred_size); - // This will set offset of this render object and call OnLayoutCore. - void Layout(const Point& offset); - - virtual Thickness GetOuterSpaceThickness() const; - 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. - // Add offset before pass point to children. - virtual RenderObject* HitTest(const Point& point) = 0; - - IEvent* AttachToHostEvent() { - return &attach_to_host_event_; - } - IEvent* DetachFromHostEvent() { - return &detach_from_host_event_; - } - - public: - void InvalidateLayout(); - void InvalidatePaint(); - - public: - virtual std::u16string_view GetName() const; - std::u16string GetDebugPathInTree() const; - - protected: - void SetChildMode(ChildMode mode) { child_mode_ = mode; } - - protected: - RenderObject* GetSingleChild() const; - - virtual void OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent); - - // default is to invalidate both layout and paint - virtual void OnAddChild(RenderObject* new_child, Index position); - // default is to invalidate both layout and paint - virtual void OnRemoveChild(RenderObject* removed_child, Index position); - - // Draw all children with offset. - void DefaultDrawChildren(platform::graphics::IPainter* painter); - - // Draw all children with translation of content rect lefttop. - void DefaultDrawContent(platform::graphics::IPainter* painter); - - // Call DefaultDrawContent. Then call DefaultDrawChildren. - virtual void OnDrawCore(platform::graphics::IPainter* painter); - - virtual void OnDrawContent(platform::graphics::IPainter* painter); - - // Size measure including margin and padding. Please reduce margin and padding - // or other custom things and pass the result content measure requirement and - // preferred size to OnMeasureContent. Return value must not be negative and - // must obey requirement. - // Note: Implementation should coerce the preferred size into the requirement - // when pass them to OnMeasureContent. - virtual Size OnMeasureCore(const MeasureRequirement& requirement, - const MeasureSize& preferred_size); - - // Please reduce margin and padding or other custom things and pass the result - // content rect to OnLayoutContent. - virtual void OnLayoutCore(); - - // Override this function to measure content and children(Call Measure on - // them). Do not consider margin or padding in this method because they are - // already considered in OnMeasureCore. Returned size must obey requirement. - // Caller should guarantee preferred_size is corerced into required range. - virtual Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) = 0; - - // Layout all content and children(Call Layout on them). - // Lefttop of content_rect should be added when calculated children's offset. - virtual void OnLayoutContent(const Rect& content_rect) = 0; - - virtual void OnAttachedControlChanged(controls::Control* control) { - CRU_UNUSED(control) - } - - virtual void OnAfterLayout(); - - private: - void SetParent(RenderObject* new_parent); - - void SetWindowHostRecursive(host::WindowHost* host); - - private: - controls::Control* control_ = nullptr; - host::WindowHost* window_host_ = nullptr; - - RenderObject* parent_ = nullptr; - std::vector children_{}; - - ChildMode child_mode_ = ChildMode::None; - - Point offset_{}; - Size size_{}; - - MeasureSize preferred_size_; - MeasureRequirement custom_measure_requirement_; - - Thickness margin_{}; - Thickness padding_{}; - - Event attach_to_host_event_; - Event detach_from_host_event_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollBar.h b/include/cru/ui/render/ScrollBar.h new file mode 100644 index 00000000..84d4375b --- /dev/null +++ b/include/cru/ui/render/ScrollBar.h @@ -0,0 +1,255 @@ +#pragma once +#include "Base.h" +#include "cru/common/Base.h" +#include "cru/common/Event.h" +#include "cru/platform/graphics/Base.h" +#include "cru/platform/graphics/Brush.h" +#include "cru/platform/graphics/Geometry.h" +#include "cru/platform/graphics/Painter.h" +#include "cru/platform/gui/Cursor.h" +#include "cru/platform/gui/TimerHelper.h" +#include "cru/platform/gui/UiApplication.h" +#include "cru/ui/Base.h" +#include "cru/ui/controls/Control.h" +#include "cru/ui/helper/ClickDetector.h" + +#include +#include +#include +#include + +namespace cru::ui::render { +class ScrollRenderObject; + +enum class ScrollKind { Absolute, Relative, Page, Line }; + +struct Scroll { + Direction direction; + ScrollKind kind; + // For absolute, the new scroll position. Otherwise, offset. + float value; +}; + +enum class ScrollBarAreaKind { + UpArrow, // Line up + DownArrow, // Line down + UpSlot, // Page up + DownSlot, // Page down + Thumb +}; + +enum class ScrollBarBrushUsageKind { Arrow, ArrowBackground, Slot, Thumb }; +enum class ScrollBarBrushStateKind { Normal, Hover, Press, Disable }; + +String CRU_UI_API GenerateScrollBarThemeColorKey(ScrollBarBrushUsageKind usage, + ScrollBarBrushStateKind state); + +class CRU_UI_API ScrollBar : public Object { + public: + ScrollBar(gsl::not_null render_object, + Direction direction); + + CRU_DELETE_COPY(ScrollBar) + CRU_DELETE_MOVE(ScrollBar) + + ~ScrollBar() override; + + public: + Direction GetDirection() const { return direction_; } + + bool IsEnabled() const { return is_enabled_; } + void SetEnabled(bool value); + + bool IsExpanded() const { return is_expanded_; } + void SetExpanded(bool value); + + void Draw(platform::graphics::IPainter* painter); + + IEvent* ScrollAttemptEvent() { return &scroll_attempt_event_; } + + void InstallHandlers(controls::Control* control); + void UninstallHandlers() { InstallHandlers(nullptr); } + + gsl::not_null> + GetCollapsedThumbBrush(); + // Brush could be nullptr to use the theme brush. + void SetCollapsedThumbBrush( + std::shared_ptr brush); + gsl::not_null> GetBrush( + ScrollBarBrushUsageKind usage, ScrollBarBrushStateKind state); + // Brush could be nullptr to use the theme brush. + void SetBrush(ScrollBarBrushUsageKind usage, ScrollBarBrushStateKind state, + std::shared_ptr brush); + + protected: + void OnDraw(platform::graphics::IPainter* painter, bool expand); + + virtual void DrawUpArrow( + platform::graphics::IPainter* painter, const Rect& area, + gsl::not_null arrow_brush, + gsl::not_null background_brush) = 0; + virtual void DrawDownArrow( + platform::graphics::IPainter* painter, const Rect& area, + gsl::not_null arrow_brush, + gsl::not_null background_brush) = 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; + + virtual bool CanScrollUp() = 0; + virtual bool CanScrollDown() = 0; + + private: + void SetCursor(); + void RestoreCursor(); + + void BeginAutoCollapseTimer(); + void StopAutoCollapseTimer(); + + void OnMouseLeave(); + + ScrollBarBrushStateKind GetState(ScrollBarAreaKind area); + + protected: + gsl::not_null render_object_; + + std::unique_ptr arrow_geometry_; + + private: + Direction direction_; + + bool is_enabled_ = true; + + bool is_expanded_ = false; + + std::shared_ptr collapsed_thumb_brush_; + std::unordered_map< + ScrollBarBrushUsageKind, + std::unordered_map>> + brushes_; + + Rect move_thumb_thumb_original_rect_; + std::optional move_thumb_start_; + + std::optional mouse_hover_; + std::optional mouse_press_; + + EventRevokerListGuard event_guard_; + + Event scroll_attempt_event_; + + bool cursor_overrided_ = false; + + platform::gui::TimerAutoCanceler auto_collapse_timer_canceler_; +}; + +class CRU_UI_API HorizontalScrollBar : public ScrollBar { + public: + explicit HorizontalScrollBar( + gsl::not_null render_object); + + CRU_DELETE_COPY(HorizontalScrollBar) + CRU_DELETE_MOVE(HorizontalScrollBar) + + ~HorizontalScrollBar() override = default; + + protected: + void DrawUpArrow( + platform::graphics::IPainter* painter, const Rect& area, + gsl::not_null arrow_brush, + gsl::not_null background_brush) override; + void DrawDownArrow( + platform::graphics::IPainter* painter, const Rect& area, + gsl::not_null arrow_brush, + gsl::not_null background_brush) 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; + + bool CanScrollUp() override; + bool CanScrollDown() override; +}; + +class CRU_UI_API VerticalScrollBar : public ScrollBar { + public: + explicit VerticalScrollBar(gsl::not_null render_object); + + CRU_DELETE_COPY(VerticalScrollBar) + CRU_DELETE_MOVE(VerticalScrollBar) + + ~VerticalScrollBar() override = default; + + protected: + void DrawUpArrow( + platform::graphics::IPainter* painter, const Rect& area, + gsl::not_null arrow_brush, + gsl::not_null background_brush) override; + void DrawDownArrow( + platform::graphics::IPainter* painter, const Rect& area, + gsl::not_null arrow_brush, + gsl::not_null background_brush) 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; + + bool CanScrollUp() override; + bool CanScrollDown() override; +}; + +// A delegate to draw scrollbar and register related events. +class CRU_UI_API 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/ScrollBar.hpp b/include/cru/ui/render/ScrollBar.hpp deleted file mode 100644 index f1007d4d..00000000 --- a/include/cru/ui/render/ScrollBar.hpp +++ /dev/null @@ -1,255 +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/Brush.hpp" -#include "cru/platform/graphics/Geometry.hpp" -#include "cru/platform/graphics/Painter.hpp" -#include "cru/platform/gui/Cursor.hpp" -#include "cru/platform/gui/TimerHelper.hpp" -#include "cru/platform/gui/UiApplication.hpp" -#include "cru/ui/Base.hpp" -#include "cru/ui/controls/Control.hpp" -#include "cru/ui/helper/ClickDetector.hpp" - -#include -#include -#include -#include - -namespace cru::ui::render { -class ScrollRenderObject; - -enum class ScrollKind { Absolute, Relative, Page, Line }; - -struct Scroll { - Direction direction; - ScrollKind kind; - // For absolute, the new scroll position. Otherwise, offset. - float value; -}; - -enum class ScrollBarAreaKind { - UpArrow, // Line up - DownArrow, // Line down - UpSlot, // Page up - DownSlot, // Page down - Thumb -}; - -enum class ScrollBarBrushUsageKind { Arrow, ArrowBackground, Slot, Thumb }; -enum class ScrollBarBrushStateKind { Normal, Hover, Press, Disable }; - -String CRU_UI_API GenerateScrollBarThemeColorKey(ScrollBarBrushUsageKind usage, - ScrollBarBrushStateKind state); - -class CRU_UI_API ScrollBar : public Object { - public: - ScrollBar(gsl::not_null render_object, - Direction direction); - - CRU_DELETE_COPY(ScrollBar) - CRU_DELETE_MOVE(ScrollBar) - - ~ScrollBar() override; - - public: - Direction GetDirection() const { return direction_; } - - bool IsEnabled() const { return is_enabled_; } - void SetEnabled(bool value); - - bool IsExpanded() const { return is_expanded_; } - void SetExpanded(bool value); - - void Draw(platform::graphics::IPainter* painter); - - IEvent* ScrollAttemptEvent() { return &scroll_attempt_event_; } - - void InstallHandlers(controls::Control* control); - void UninstallHandlers() { InstallHandlers(nullptr); } - - gsl::not_null> - GetCollapsedThumbBrush(); - // Brush could be nullptr to use the theme brush. - void SetCollapsedThumbBrush( - std::shared_ptr brush); - gsl::not_null> GetBrush( - ScrollBarBrushUsageKind usage, ScrollBarBrushStateKind state); - // Brush could be nullptr to use the theme brush. - void SetBrush(ScrollBarBrushUsageKind usage, ScrollBarBrushStateKind state, - std::shared_ptr brush); - - protected: - void OnDraw(platform::graphics::IPainter* painter, bool expand); - - virtual void DrawUpArrow( - platform::graphics::IPainter* painter, const Rect& area, - gsl::not_null arrow_brush, - gsl::not_null background_brush) = 0; - virtual void DrawDownArrow( - platform::graphics::IPainter* painter, const Rect& area, - gsl::not_null arrow_brush, - gsl::not_null background_brush) = 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; - - virtual bool CanScrollUp() = 0; - virtual bool CanScrollDown() = 0; - - private: - void SetCursor(); - void RestoreCursor(); - - void BeginAutoCollapseTimer(); - void StopAutoCollapseTimer(); - - void OnMouseLeave(); - - ScrollBarBrushStateKind GetState(ScrollBarAreaKind area); - - protected: - gsl::not_null render_object_; - - std::unique_ptr arrow_geometry_; - - private: - Direction direction_; - - bool is_enabled_ = true; - - bool is_expanded_ = false; - - std::shared_ptr collapsed_thumb_brush_; - std::unordered_map< - ScrollBarBrushUsageKind, - std::unordered_map>> - brushes_; - - Rect move_thumb_thumb_original_rect_; - std::optional move_thumb_start_; - - std::optional mouse_hover_; - std::optional mouse_press_; - - EventRevokerListGuard event_guard_; - - Event scroll_attempt_event_; - - bool cursor_overrided_ = false; - - platform::gui::TimerAutoCanceler auto_collapse_timer_canceler_; -}; - -class CRU_UI_API HorizontalScrollBar : public ScrollBar { - public: - explicit HorizontalScrollBar( - gsl::not_null render_object); - - CRU_DELETE_COPY(HorizontalScrollBar) - CRU_DELETE_MOVE(HorizontalScrollBar) - - ~HorizontalScrollBar() override = default; - - protected: - void DrawUpArrow( - platform::graphics::IPainter* painter, const Rect& area, - gsl::not_null arrow_brush, - gsl::not_null background_brush) override; - void DrawDownArrow( - platform::graphics::IPainter* painter, const Rect& area, - gsl::not_null arrow_brush, - gsl::not_null background_brush) 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; - - bool CanScrollUp() override; - bool CanScrollDown() override; -}; - -class CRU_UI_API VerticalScrollBar : public ScrollBar { - public: - explicit VerticalScrollBar(gsl::not_null render_object); - - CRU_DELETE_COPY(VerticalScrollBar) - CRU_DELETE_MOVE(VerticalScrollBar) - - ~VerticalScrollBar() override = default; - - protected: - void DrawUpArrow( - platform::graphics::IPainter* painter, const Rect& area, - gsl::not_null arrow_brush, - gsl::not_null background_brush) override; - void DrawDownArrow( - platform::graphics::IPainter* painter, const Rect& area, - gsl::not_null arrow_brush, - gsl::not_null background_brush) 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; - - bool CanScrollUp() override; - bool CanScrollDown() override; -}; - -// A delegate to draw scrollbar and register related events. -class CRU_UI_API 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.h b/include/cru/ui/render/ScrollRenderObject.h new file mode 100644 index 00000000..bb282953 --- /dev/null +++ b/include/cru/ui/render/ScrollRenderObject.h @@ -0,0 +1,98 @@ +#pragma once +#include "RenderObject.h" + +#include "cru/common/Event.h" +#include "cru/platform/graphics/util/Painter.h" +#include "cru/ui/Base.h" +#include "cru/ui/render/ScrollBar.h" + +#include +#include + +namespace cru::ui::render { +// Measure logic: +// Measure child with unspecified min and max size. +// If parent's preferred size is specified, then it is used as measure result. +// Or child's size is coerced into requirement and then used as result. +// If no child, then use the preferred size if set or min size if set or 0. +// Layout logic: +// If child is smaller than content area, layout at lefttop. +// Or layout by scroll state. +class CRU_UI_API ScrollRenderObject : public RenderObject { + public: + ScrollRenderObject(); + + CRU_DELETE_COPY(ScrollRenderObject) + CRU_DELETE_MOVE(ScrollRenderObject) + + ~ScrollRenderObject() override = default; + + RenderObject* HitTest(const Point& point) override; + + // 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 ApplyScroll(const Scroll& scroll); + + Point GetRawScrollOffset() const { return scroll_offset_; } + + // Return the viewable area rect. + // Lefttop is scroll offset. Size is content size. + // If size exceeds view area, left and top is more important when calculate + // new scroll offset. + Rect GetViewRect() { + return Rect{GetScrollOffset(), GetContentRect().GetSize()}; + } + + // Rect lefttop relative to content rect. + // Param margin is just for convenience and it will just add to the rect. + void ScrollToContain(const Rect& rect, const Thickness& margin = Thickness{}); + + std::u16string_view GetName() const override { return u"ScrollRenderObject"; } + + bool IsMouseWheelScrollEnabled() const { return is_mouse_wheel_enabled_; } + void SetMouseWheelScrollEnabled(bool enable); + + bool HorizontalCanScrollUp(); + bool HorizontalCanScrollDown(); + bool VerticalCanScrollUp(); + bool VerticalCanScrollDown(); + + protected: + void OnDrawCore(platform::graphics::IPainter* painter) override; + + // Logic: + // If available size is bigger than child's preferred size, then child's + // preferred size is taken. + // If not, all available size is taken while forming a scroll area. + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + void OnAttachedControlChanged(controls::Control* control) override; + + void InstallMouseWheelHandler(controls::Control* control); + + private: + Point scroll_offset_; + + std::unique_ptr scroll_bar_delegate_; + + bool is_mouse_wheel_enabled_ = true; + + EventRevokerListGuard guard_; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/ScrollRenderObject.hpp b/include/cru/ui/render/ScrollRenderObject.hpp deleted file mode 100644 index 19814c51..00000000 --- a/include/cru/ui/render/ScrollRenderObject.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#include "RenderObject.hpp" - -#include "cru/common/Event.hpp" -#include "cru/platform/graphics/util/Painter.hpp" -#include "cru/ui/Base.hpp" -#include "cru/ui/render/ScrollBar.hpp" - -#include -#include - -namespace cru::ui::render { -// Measure logic: -// Measure child with unspecified min and max size. -// If parent's preferred size is specified, then it is used as measure result. -// Or child's size is coerced into requirement and then used as result. -// If no child, then use the preferred size if set or min size if set or 0. -// Layout logic: -// If child is smaller than content area, layout at lefttop. -// Or layout by scroll state. -class CRU_UI_API ScrollRenderObject : public RenderObject { - public: - ScrollRenderObject(); - - CRU_DELETE_COPY(ScrollRenderObject) - CRU_DELETE_MOVE(ScrollRenderObject) - - ~ScrollRenderObject() override = default; - - RenderObject* HitTest(const Point& point) override; - - // 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 ApplyScroll(const Scroll& scroll); - - Point GetRawScrollOffset() const { return scroll_offset_; } - - // Return the viewable area rect. - // Lefttop is scroll offset. Size is content size. - // If size exceeds view area, left and top is more important when calculate - // new scroll offset. - Rect GetViewRect() { - return Rect{GetScrollOffset(), GetContentRect().GetSize()}; - } - - // Rect lefttop relative to content rect. - // Param margin is just for convenience and it will just add to the rect. - void ScrollToContain(const Rect& rect, const Thickness& margin = Thickness{}); - - std::u16string_view GetName() const override { return u"ScrollRenderObject"; } - - bool IsMouseWheelScrollEnabled() const { return is_mouse_wheel_enabled_; } - void SetMouseWheelScrollEnabled(bool enable); - - bool HorizontalCanScrollUp(); - bool HorizontalCanScrollDown(); - bool VerticalCanScrollUp(); - bool VerticalCanScrollDown(); - - protected: - void OnDrawCore(platform::graphics::IPainter* painter) override; - - // Logic: - // If available size is bigger than child's preferred size, then child's - // preferred size is taken. - // If not, all available size is taken while forming a scroll area. - Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - void OnAttachedControlChanged(controls::Control* control) override; - - void InstallMouseWheelHandler(controls::Control* control); - - private: - Point scroll_offset_; - - std::unique_ptr scroll_bar_delegate_; - - bool is_mouse_wheel_enabled_ = true; - - EventRevokerListGuard guard_; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/StackLayoutRenderObject.h b/include/cru/ui/render/StackLayoutRenderObject.h new file mode 100644 index 00000000..e141d16e --- /dev/null +++ b/include/cru/ui/render/StackLayoutRenderObject.h @@ -0,0 +1,57 @@ +#pragma once +#include "LayoutRenderObject.h" +#include "cru/ui/Base.h" + +namespace cru::ui::render { +// Measure Logic: +// Following rules are applied both horizontally and vertically. +// +// 1. Measure each children with min size not specified and max size as +// following rules: +// +// 1.1. If parent's preferred size is set and it is not less than child's min +// size, then it is used as child's max size. +// +// 1.2. Or if parent's max size is set, then it is used as child's max size. +// If it is less than child's min size, then log an warning about that. +// +// 2. Result size is children's max size. +// +// 3. If preferred size is specified and result size is smaller than it, coerce +// result size to preferred size. +// +// 4. If result size is smaller than min size (if specified), coerce result size +// to min size. +class CRU_UI_API StackLayoutRenderObject + : public LayoutRenderObject { + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render:StackLayoutRenderObject") + + public: + StackLayoutRenderObject() = default; + CRU_DELETE_COPY(StackLayoutRenderObject) + CRU_DELETE_MOVE(StackLayoutRenderObject) + ~StackLayoutRenderObject() = default; + + std::u16string_view GetName() const override { + return u"StackLayoutRenderObject"; + } + + Alignment GetDefaultHorizontalAlignment() const { + return default_vertical_alignment_; + } + void SetDefaultHorizontalAlignment(Alignment alignment); + Alignment GetDefaultVerticalAlignment() { + return default_horizontal_alignment_; + } + void SetDefaultVertialAlignment(Alignment alignment); + + protected: + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + private: + Alignment default_horizontal_alignment_ = Alignment::Start; + Alignment default_vertical_alignment_ = Alignment::Start; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/StackLayoutRenderObject.hpp b/include/cru/ui/render/StackLayoutRenderObject.hpp deleted file mode 100644 index 2f832e55..00000000 --- a/include/cru/ui/render/StackLayoutRenderObject.hpp +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once -#include "LayoutRenderObject.hpp" -#include "cru/ui/Base.hpp" - -namespace cru::ui::render { -// Measure Logic: -// Following rules are applied both horizontally and vertically. -// -// 1. Measure each children with min size not specified and max size as -// following rules: -// -// 1.1. If parent's preferred size is set and it is not less than child's min -// size, then it is used as child's max size. -// -// 1.2. Or if parent's max size is set, then it is used as child's max size. -// If it is less than child's min size, then log an warning about that. -// -// 2. Result size is children's max size. -// -// 3. If preferred size is specified and result size is smaller than it, coerce -// result size to preferred size. -// -// 4. If result size is smaller than min size (if specified), coerce result size -// to min size. -class CRU_UI_API StackLayoutRenderObject - : public LayoutRenderObject { - CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render:StackLayoutRenderObject") - - public: - StackLayoutRenderObject() = default; - CRU_DELETE_COPY(StackLayoutRenderObject) - CRU_DELETE_MOVE(StackLayoutRenderObject) - ~StackLayoutRenderObject() = default; - - std::u16string_view GetName() const override { - return u"StackLayoutRenderObject"; - } - - Alignment GetDefaultHorizontalAlignment() const { - return default_vertical_alignment_; - } - void SetDefaultHorizontalAlignment(Alignment alignment); - Alignment GetDefaultVerticalAlignment() { - return default_horizontal_alignment_; - } - void SetDefaultVertialAlignment(Alignment alignment); - - protected: - Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - private: - Alignment default_horizontal_alignment_ = Alignment::Start; - Alignment default_vertical_alignment_ = Alignment::Start; -}; -} // namespace cru::ui::render diff --git a/include/cru/ui/render/TextRenderObject.h b/include/cru/ui/render/TextRenderObject.h new file mode 100644 index 00000000..3b5f581a --- /dev/null +++ b/include/cru/ui/render/TextRenderObject.h @@ -0,0 +1,121 @@ +#pragma once +#include "RenderObject.h" + +#include + +namespace cru::ui::render { +// Layout logic: +// 1. If preferred width is set then it is taken to do a text measure. If not +// set, then max width is taken to do that. +// 2. If the actual width of text after measure exceeds the required width, +// coerced value is returned and an error is reported. If preferred width is set +// and actual width is smaller than that, then preferred width is used. If +// preferred width is not set, then the same thing is applied to min width. +// 3. If actual height of text is bigger than max height, an error is reported +// and coerced value is returned. Or height is lifted up to be at least +// preferred size if set or min height. +// +// If the result layout box is bigger than actual text box, then text is center +// aligned. +class CRU_UI_API TextRenderObject : public RenderObject { + CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::TextRenderObject") + + public: + constexpr static float default_caret_width = 2; + + public: + TextRenderObject(std::shared_ptr brush, + std::shared_ptr font, + std::shared_ptr selection_brush, + std::shared_ptr caret_brush); + TextRenderObject(const TextRenderObject& other) = delete; + TextRenderObject(TextRenderObject&& other) = delete; + TextRenderObject& operator=(const TextRenderObject& other) = delete; + TextRenderObject& operator=(TextRenderObject&& other) = delete; + ~TextRenderObject() override; + + String GetText() const; + void SetText(String new_text); + + void SetBrush(std::shared_ptr new_brush); + + std::shared_ptr GetFont() const; + void SetFont(std::shared_ptr font); + + bool IsEditMode(); + void SetEditMode(bool enable); + + Index GetLineCount(); + Index GetLineIndexFromCharIndex(Index char_index); + float GetLineHeight(Index line_index); + std::vector TextRangeRect(const TextRange& text_range); + Rect TextSinglePoint(gsl::index position, bool trailing); + platform::graphics::TextHitTestResult TextHitTest(const Point& point); + + std::optional GetSelectionRange() const { + return selection_range_; + } + void SetSelectionRange(std::optional new_range); + + std::shared_ptr GetSelectionBrush() const { + return selection_brush_; + } + void SetSelectionBrush(std::shared_ptr new_brush); + + bool IsDrawCaret() const { return draw_caret_; } + void SetDrawCaret(bool draw_caret); + void ToggleDrawCaret() { SetDrawCaret(!IsDrawCaret()); } + + // Caret position can be any value. When it is negative, 0 is used. When it + // exceeds the size of the string, the size of the string is used. + gsl::index GetCaretPosition() const { return caret_position_; } + void SetCaretPosition(gsl::index position); + + // Lefttop relative to content lefttop. + Rect GetCaretRectInContent(); + // Lefttop relative to render object lefttop. + Rect GetCaretRect(); + + std::shared_ptr GetCaretBrush() const { + return caret_brush_; + } + void GetCaretBrush(std::shared_ptr brush); + + float GetCaretWidth() const { return caret_width_; } + void SetCaretWidth(float width); + + bool IsMeasureIncludingTrailingSpace() const { + return is_measure_including_trailing_space_; + } + void SetMeasureIncludingTrailingSpace(bool including); + + RenderObject* HitTest(const Point& point) override; + + std::u16string_view GetName() const override { return u"TextRenderObject"; } + + protected: + void OnDrawContent(platform::graphics::IPainter* painter) override; + + // See remarks of this class. + Size OnMeasureContent(const MeasureRequirement& requirement, + const MeasureSize& preferred_size) override; + void OnLayoutContent(const Rect& content_rect) override; + + void OnAfterLayout() override; + + private: + std::shared_ptr brush_; + std::shared_ptr font_; + std::unique_ptr text_layout_; + + std::optional selection_range_ = std::nullopt; + std::shared_ptr selection_brush_; + + bool draw_caret_ = false; + gsl::index caret_position_ = 0; + std::shared_ptr caret_brush_; + float caret_width_ = default_caret_width; + + bool is_measure_including_trailing_space_ = false; +}; +} // namespace cru::ui::render diff --git a/include/cru/ui/render/TextRenderObject.hpp b/include/cru/ui/render/TextRenderObject.hpp deleted file mode 100644 index 601bd0a8..00000000 --- a/include/cru/ui/render/TextRenderObject.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once -#include "RenderObject.hpp" - -#include - -namespace cru::ui::render { -// Layout logic: -// 1. If preferred width is set then it is taken to do a text measure. If not -// set, then max width is taken to do that. -// 2. If the actual width of text after measure exceeds the required width, -// coerced value is returned and an error is reported. If preferred width is set -// and actual width is smaller than that, then preferred width is used. If -// preferred width is not set, then the same thing is applied to min width. -// 3. If actual height of text is bigger than max height, an error is reported -// and coerced value is returned. Or height is lifted up to be at least -// preferred size if set or min height. -// -// If the result layout box is bigger than actual text box, then text is center -// aligned. -class CRU_UI_API TextRenderObject : public RenderObject { - CRU_DEFINE_CLASS_LOG_TAG(u"cru::ui::render::TextRenderObject") - - public: - constexpr static float default_caret_width = 2; - - public: - TextRenderObject(std::shared_ptr brush, - std::shared_ptr font, - std::shared_ptr selection_brush, - std::shared_ptr caret_brush); - TextRenderObject(const TextRenderObject& other) = delete; - TextRenderObject(TextRenderObject&& other) = delete; - TextRenderObject& operator=(const TextRenderObject& other) = delete; - TextRenderObject& operator=(TextRenderObject&& other) = delete; - ~TextRenderObject() override; - - String GetText() const; - void SetText(String new_text); - - void SetBrush(std::shared_ptr new_brush); - - std::shared_ptr GetFont() const; - void SetFont(std::shared_ptr font); - - bool IsEditMode(); - void SetEditMode(bool enable); - - Index GetLineCount(); - Index GetLineIndexFromCharIndex(Index char_index); - float GetLineHeight(Index line_index); - std::vector TextRangeRect(const TextRange& text_range); - Rect TextSinglePoint(gsl::index position, bool trailing); - platform::graphics::TextHitTestResult TextHitTest(const Point& point); - - std::optional GetSelectionRange() const { - return selection_range_; - } - void SetSelectionRange(std::optional new_range); - - std::shared_ptr GetSelectionBrush() const { - return selection_brush_; - } - void SetSelectionBrush(std::shared_ptr new_brush); - - bool IsDrawCaret() const { return draw_caret_; } - void SetDrawCaret(bool draw_caret); - void ToggleDrawCaret() { SetDrawCaret(!IsDrawCaret()); } - - // Caret position can be any value. When it is negative, 0 is used. When it - // exceeds the size of the string, the size of the string is used. - gsl::index GetCaretPosition() const { return caret_position_; } - void SetCaretPosition(gsl::index position); - - // Lefttop relative to content lefttop. - Rect GetCaretRectInContent(); - // Lefttop relative to render object lefttop. - Rect GetCaretRect(); - - std::shared_ptr GetCaretBrush() const { - return caret_brush_; - } - void GetCaretBrush(std::shared_ptr brush); - - float GetCaretWidth() const { return caret_width_; } - void SetCaretWidth(float width); - - bool IsMeasureIncludingTrailingSpace() const { - return is_measure_including_trailing_space_; - } - void SetMeasureIncludingTrailingSpace(bool including); - - RenderObject* HitTest(const Point& point) override; - - std::u16string_view GetName() const override { return u"TextRenderObject"; } - - protected: - void OnDrawContent(platform::graphics::IPainter* painter) override; - - // See remarks of this class. - Size OnMeasureContent(const MeasureRequirement& requirement, - const MeasureSize& preferred_size) override; - void OnLayoutContent(const Rect& content_rect) override; - - void OnAfterLayout() override; - - private: - std::shared_ptr brush_; - std::shared_ptr font_; - std::unique_ptr text_layout_; - - std::optional selection_range_ = std::nullopt; - std::shared_ptr selection_brush_; - - bool draw_caret_ = false; - gsl::index caret_position_ = 0; - std::shared_ptr caret_brush_; - float caret_width_ = default_caret_width; - - bool is_measure_including_trailing_space_ = false; -}; -} // namespace cru::ui::render -- cgit v1.2.3