From d86a71f79afe0e4dac768f61d6bff690567aca5b Mon Sep 17 00:00:00 2001 From: crupest Date: Sun, 24 May 2020 01:40:02 +0800 Subject: ... --- src/ui/render/BorderRenderObject.cpp | 236 +++++++++++++++++++++++++++ src/ui/render/CanvasRenderObject.cpp | 28 ++++ src/ui/render/FlexLayoutRenderObject.cpp | 197 ++++++++++++++++++++++ src/ui/render/LayoutUtility.cpp | 15 ++ src/ui/render/RenderObject.cpp | 190 +++++++++++++++++++++ src/ui/render/ScrollRenderObject.cpp | 1 + src/ui/render/StackLayoutRenderObject.cpp | 49 ++++++ src/ui/render/TextRenderObject.cpp | 177 ++++++++++++++++++++ src/ui/render/WindowRenderObject.cpp | 45 +++++ src/ui/render/border_render_object.cpp | 236 --------------------------- src/ui/render/canvas_render_object.cpp | 28 ---- src/ui/render/flex_layout_render_object.cpp | 197 ---------------------- src/ui/render/layout_utility.cpp | 15 -- src/ui/render/render_object.cpp | 190 --------------------- src/ui/render/scroll_render_object.cpp | 1 - src/ui/render/stack_layout_render_object.cpp | 49 ------ src/ui/render/text_render_object.cpp | 177 -------------------- src/ui/render/window_render_object.cpp | 45 ----- 18 files changed, 938 insertions(+), 938 deletions(-) create mode 100644 src/ui/render/BorderRenderObject.cpp create mode 100644 src/ui/render/CanvasRenderObject.cpp create mode 100644 src/ui/render/FlexLayoutRenderObject.cpp create mode 100644 src/ui/render/LayoutUtility.cpp create mode 100644 src/ui/render/RenderObject.cpp create mode 100644 src/ui/render/ScrollRenderObject.cpp create mode 100644 src/ui/render/StackLayoutRenderObject.cpp create mode 100644 src/ui/render/TextRenderObject.cpp create mode 100644 src/ui/render/WindowRenderObject.cpp delete mode 100644 src/ui/render/border_render_object.cpp delete mode 100644 src/ui/render/canvas_render_object.cpp delete mode 100644 src/ui/render/flex_layout_render_object.cpp delete mode 100644 src/ui/render/layout_utility.cpp delete mode 100644 src/ui/render/render_object.cpp delete mode 100644 src/ui/render/scroll_render_object.cpp delete mode 100644 src/ui/render/stack_layout_render_object.cpp delete mode 100644 src/ui/render/text_render_object.cpp delete mode 100644 src/ui/render/window_render_object.cpp (limited to 'src/ui/render') diff --git a/src/ui/render/BorderRenderObject.cpp b/src/ui/render/BorderRenderObject.cpp new file mode 100644 index 00000000..a656cb99 --- /dev/null +++ b/src/ui/render/BorderRenderObject.cpp @@ -0,0 +1,236 @@ +#include "cru/ui/render/BorderRenderObject.hpp" + +#include "../Helper.hpp" +#include "cru/common/Logger.hpp" +#include "cru/platform/graph/Factory.hpp" +#include "cru/platform/graph/Geometry.hpp" +#include "cru/platform/graph/util/Painter.hpp" + +#include + +namespace cru::ui::render { +BorderRenderObject::BorderRenderObject() { + SetChildMode(ChildMode::Single); + RecreateGeometry(); +} + +BorderRenderObject::~BorderRenderObject() {} + +void BorderRenderObject::Draw(platform::graph::IPainter* painter) { + if (background_brush_ != nullptr) + painter->FillGeometry(border_inner_geometry_.get(), + background_brush_.get()); + if (is_border_enabled_) { + if (border_brush_ == nullptr) { + log::Warn("Border is enabled but brush is null"); + } else { + painter->FillGeometry(geometry_.get(), border_brush_.get()); + } + } + if (const auto child = GetChild()) { + auto offset = child->GetOffset(); + platform::graph::util::WithTransform( + painter, platform::Matrix::Translation(offset.x, offset.y), + [child](auto p) { child->Draw(p); }); + } + if (foreground_brush_ != nullptr) + painter->FillGeometry(border_inner_geometry_.get(), + foreground_brush_.get()); +} + +void BorderRenderObject::SetBorderStyle(const BorderStyle& style) { + border_brush_ = style.border_brush; + border_thickness_ = style.border_thickness; + border_radius_ = style.border_radius; + foreground_brush_ = style.foreground_brush; + background_brush_ = style.background_brush; + InvalidateLayout(); +} + +RenderObject* BorderRenderObject::HitTest(const Point& point) { + if (const auto child = GetChild()) { + auto offset = child->GetOffset(); + Point p{point.x - offset.x, point.y - offset.y}; + const auto result = child->HitTest(p); + if (result != nullptr) { + return result; + } + } + + if (is_border_enabled_) { + const auto contains = + border_outer_geometry_->FillContains(Point{point.x, point.y}); + return contains ? this : nullptr; + } else { + 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; + } +} + +void BorderRenderObject::OnMeasureCore(const Size& available_size) { + const auto margin = GetMargin(); + const auto padding = GetPadding(); + Size margin_border_padding_size{ + margin.GetHorizontalTotal() + padding.GetHorizontalTotal(), + margin.GetVerticalTotal() + padding.GetVerticalTotal()}; + + if (is_border_enabled_) { + margin_border_padding_size.width += border_thickness_.GetHorizontalTotal(); + margin_border_padding_size.height += border_thickness_.GetVerticalTotal(); + } + + auto coerced_margin_border_padding_size = margin_border_padding_size; + if (coerced_margin_border_padding_size.width > available_size.width) { + log::Warn( + "Measure: horizontal length of padding, border and margin is bigger " + "than available length."); + coerced_margin_border_padding_size.width = available_size.width; + } + if (coerced_margin_border_padding_size.height > available_size.height) { + log::Warn( + "Measure: vertical length of padding, border and margin is bigger " + "than available length."); + coerced_margin_border_padding_size.height = available_size.height; + } + + const auto coerced_content_available_size = + available_size - coerced_margin_border_padding_size; + + const auto actual_content_size = + OnMeasureContent(coerced_content_available_size); + + SetPreferredSize(coerced_margin_border_padding_size + actual_content_size); +} + +void BorderRenderObject::OnLayoutCore(const Rect& rect) { + const auto margin = GetMargin(); + const auto padding = GetPadding(); + Size margin_border_padding_size{ + margin.GetHorizontalTotal() + padding.GetHorizontalTotal(), + margin.GetVerticalTotal() + padding.GetVerticalTotal()}; + + if (is_border_enabled_) { + margin_border_padding_size.width += border_thickness_.GetHorizontalTotal(); + margin_border_padding_size.height += border_thickness_.GetVerticalTotal(); + } + + const auto content_available_size = + rect.GetSize() - margin_border_padding_size; + auto coerced_content_available_size = content_available_size; + + if (coerced_content_available_size.width < 0) { + log::Warn( + "Layout: horizontal length of padding, border and margin is bigger " + "than available length."); + coerced_content_available_size.width = 0; + } + if (coerced_content_available_size.height < 0) { + log::Warn( + "Layout: vertical length of padding, border and margin is bigger " + "than available length."); + coerced_content_available_size.height = 0; + } + + OnLayoutContent( + Rect{margin.left + (is_border_enabled_ ? border_thickness_.left : 0) + + padding.left, + margin.top + (is_border_enabled_ ? border_thickness_.top : 0) + + padding.top, + coerced_content_available_size.width, + coerced_content_available_size.height}); +} + +Size BorderRenderObject::OnMeasureContent(const Size& available_size) { + const auto child = GetChild(); + if (child) { + child->Measure(available_size); + return child->GetPreferredSize(); + } else { + return Size{}; + } +} + +void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { + const auto child = GetChild(); + if (child) { + child->Layout(content_rect); + } +} + +void BorderRenderObject::OnAfterLayout() { RecreateGeometry(); } + +void BorderRenderObject::RecreateGeometry() { + geometry_.reset(); + border_outer_geometry_.reset(); + + Thickness t = border_thickness_; + t.left /= 2.0; + t.top /= 2.0; + t.right /= 2.0; + t.bottom /= 2.0; + const CornerRadius& r = border_radius_; + + CornerRadius outer_radius(r.left_top + Point{t.left, t.top}, + r.right_top + Point{t.right, t.top}, + r.left_bottom + Point{t.left, t.bottom}, + r.right_bottom + Point{t.right, t.bottom}); + + CornerRadius inner_radius(r.left_top - Point{t.left, t.top}, + r.right_top - Point{t.right, t.top}, + r.left_bottom - Point{t.left, t.bottom}, + r.right_bottom - Point{t.right, t.bottom}); + + auto f = [](platform::graph::IGeometryBuilder* builder, const Rect& rect, + const CornerRadius& corner) { + builder->BeginFigure(Point(rect.left + corner.left_top.x, rect.top)); + builder->LineTo(Point(rect.GetRight() - corner.right_top.x, rect.top)); + builder->QuadraticBezierTo( + Point(rect.GetRight(), rect.top), + Point(rect.GetRight(), rect.top + corner.right_top.y)); + builder->LineTo( + Point(rect.GetRight(), rect.GetBottom() - corner.right_bottom.y)); + builder->QuadraticBezierTo( + Point(rect.GetRight(), rect.GetBottom()), + Point(rect.GetRight() - corner.right_bottom.x, rect.GetBottom())); + builder->LineTo(Point(rect.left + corner.left_bottom.x, rect.GetBottom())); + builder->QuadraticBezierTo( + Point(rect.left, rect.GetBottom()), + Point(rect.left, rect.GetBottom() - corner.left_bottom.y)); + builder->LineTo(Point(rect.left, rect.top + corner.left_top.y)); + builder->QuadraticBezierTo(Point(rect.left, rect.top), + Point(rect.left + corner.left_top.x, rect.top)); + builder->CloseFigure(true); + }; + + const auto size = GetSize(); + const auto margin = GetMargin(); + const Rect outer_rect{margin.left, margin.top, + size.width - margin.GetHorizontalTotal(), + size.height - margin.GetVerticalTotal()}; + const auto graph_factory = GetGraphFactory(); + std::unique_ptr builder{ + graph_factory->CreateGeometryBuilder()}; + f(builder.get(), outer_rect, outer_radius); + border_outer_geometry_ = builder->Build(); + builder.reset(); + + const Rect inner_rect = outer_rect.Shrink(border_thickness_); + + builder = graph_factory->CreateGeometryBuilder(); + f(builder.get(), inner_rect, inner_radius); + border_inner_geometry_ = builder->Build(); + builder.reset(); + + builder = graph_factory->CreateGeometryBuilder(); + f(builder.get(), outer_rect, outer_radius); + f(builder.get(), inner_rect, inner_radius); + geometry_ = builder->Build(); + builder.reset(); +} +} // namespace cru::ui::render diff --git a/src/ui/render/CanvasRenderObject.cpp b/src/ui/render/CanvasRenderObject.cpp new file mode 100644 index 00000000..16ac9239 --- /dev/null +++ b/src/ui/render/CanvasRenderObject.cpp @@ -0,0 +1,28 @@ +#include "cru/ui/render/CanvasRenderObject.hpp" + +#include "cru/ui/render/LayoutUtility.hpp" + +namespace cru::ui::render { +CanvasRenderObject::CanvasRenderObject() : RenderObject(ChildMode::None) {} + +CanvasRenderObject::~CanvasRenderObject() = default; + +void CanvasRenderObject::Draw(platform::graph::IPainter* painter) { + const auto rect = GetContentRect(); + CanvasPaintEventArgs args{painter, rect}; + paint_event_.Raise(args); +} + +RenderObject* CanvasRenderObject::HitTest(const Point& point) { + const auto padding_rect = GetPaddingRect(); + return padding_rect.IsPointInside(point) ? this : nullptr; +} + +Size CanvasRenderObject::OnMeasureContent(const Size& available_size) { + return Min(available_size, GetDesiredSize()); +} + +void CanvasRenderObject::OnLayoutContent(const Rect& content_rect) { + CRU_UNUSED(content_rect) +} +} // namespace cru::ui::render diff --git a/src/ui/render/FlexLayoutRenderObject.cpp b/src/ui/render/FlexLayoutRenderObject.cpp new file mode 100644 index 00000000..b609fd97 --- /dev/null +++ b/src/ui/render/FlexLayoutRenderObject.cpp @@ -0,0 +1,197 @@ +#include "cru/ui/render/FlexLayoutRenderObject.hpp" + +#include "cru/platform/graph/util/Painter.hpp" + +#include +#include + +namespace cru::ui::render { +Size FlexLayoutRenderObject::OnMeasureContent(const Size& available_size) { + std::vector has_basis_children; + std::vector no_basis_children; + std::vector grow_children; + std::vector shrink_chilren; + const auto child_count = GetChildCount(); + for (int i = 0; i < child_count; i++) { + const auto& layout_data = *GetChildLayoutData(i); + if (layout_data.flex_basis.has_value()) + has_basis_children.push_back(i); + else + no_basis_children.push_back(i); + if (layout_data.flex_grow > 0) grow_children.push_back(i); + if (layout_data.flex_shrink > 0) shrink_chilren.push_back(i); + } + + std::function get_main_length; + std::function get_cross_length; + std::function create_size; + + if (direction_ == FlexDirection::Horizontal || + direction_ == FlexDirection::HorizontalReverse) { + get_main_length = [](const Size& size) { return size.width; }; + get_cross_length = [](const Size& size) { return size.height; }; + create_size = [](float main, float cross) { return Size(main, cross); }; + } else { + get_main_length = [](const Size& size) { return size.height; }; + get_cross_length = [](const Size& size) { return size.width; }; + create_size = [](float main, float cross) { return Size(cross, main); }; + } + + const auto& children = GetChildren(); + + float remain_main_length = get_main_length(available_size); + float max_cross_length = 0; + + for (const int i : has_basis_children) { + const auto child = children[i]; + const float basis = GetChildLayoutData(i)->flex_basis.value(); + child->Measure(create_size(basis, get_cross_length(available_size))); + remain_main_length -= basis; + const float child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize(create_size(basis, child_preferred_cross_length)); + max_cross_length = std::max(max_cross_length, child_preferred_cross_length); + } + + for (const int i : no_basis_children) { + const auto child = children[i]; + child->Measure(create_size(remain_main_length > 0 ? remain_main_length : 0, + get_cross_length(available_size))); + remain_main_length -= get_main_length(child->GetPreferredSize()); + max_cross_length = + std::max(max_cross_length, get_cross_length(child->GetPreferredSize())); + } + + if (remain_main_length > 0) { + float total_grow = 0; + for (const int i : grow_children) + total_grow += GetChildLayoutData(i)->flex_grow; + + for (const int i : grow_children) { + const float distributed_grow_length = + remain_main_length * (GetChildLayoutData(i)->flex_grow / total_grow); + const auto child = children[i]; + const float new_main_length = + get_main_length(child->GetPreferredSize()) + distributed_grow_length; + child->Measure( + create_size(new_main_length, get_cross_length(available_size))); + const float new_child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize( + create_size(new_main_length, new_child_preferred_cross_length)); + max_cross_length = + std::max(max_cross_length, new_child_preferred_cross_length); + } + } + + if (remain_main_length < 0) { + float total_shrink = 0; + for (const int i : shrink_chilren) + total_shrink += GetChildLayoutData(i)->flex_shrink; + + for (const int i : shrink_chilren) { + const float distributed_shrink_length = // negative + remain_main_length * + (GetChildLayoutData(i)->flex_shrink / total_shrink); + const auto child = children[i]; + float new_main_length = get_main_length(child->GetPreferredSize()) + + distributed_shrink_length; + new_main_length = new_main_length > 0 ? new_main_length : 0; + child->Measure( + create_size(new_main_length, get_cross_length(available_size))); + const float new_child_preferred_cross_length = + get_cross_length(child->GetPreferredSize()); + child->SetPreferredSize( + create_size(new_main_length, new_child_preferred_cross_length)); + max_cross_length = + std::max(max_cross_length, new_child_preferred_cross_length); + } + } + + return create_size(get_main_length(available_size) - + (remain_main_length > 0 ? remain_main_length : 0), + max_cross_length); +} + +void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { + auto calculate_anchor = [](int alignment, float start_point, + float total_length, + float content_length) -> float { + switch (alignment) { + case internal::align_start: + return start_point; + case internal::align_center: + return start_point + (total_length - content_length) / 2.0f; + case internal::align_end: + return start_point + total_length - content_length; + default: + return 0; + } + }; + + const auto& children = GetChildren(); + if (direction_ == FlexDirection::Horizontal || + direction_ == FlexDirection::HorizontalReverse) { + float actual_content_width = 0; + for (const auto child : children) { + actual_content_width += child->GetPreferredSize().width; + } + + const float content_anchor_x = + calculate_anchor(static_cast(content_main_align_), 0, + content_rect.width, actual_content_width); + + float anchor_x = 0; + for (int i = 0; i < static_cast(children.size()); i++) { + const auto child = children[i]; + const auto size = child->GetPreferredSize(); + + float real_anchor_x = anchor_x + content_anchor_x; + if (direction_ == FlexDirection::Horizontal) + real_anchor_x = content_rect.left + real_anchor_x; + else + real_anchor_x = content_rect.GetRight() - real_anchor_x; + child->Layout(Rect{ + real_anchor_x, + calculate_anchor( + static_cast(GetChildLayoutData(i)->cross_alignment.value_or( + this->item_cross_align_)), + content_rect.top, content_rect.height, size.height), + size.width, size.height}); + + anchor_x += size.width; + } + } else { + float actual_content_height = 0; + for (const auto child : children) { + actual_content_height = child->GetPreferredSize().height; + } + + const float content_anchor_y = + calculate_anchor(static_cast(content_main_align_), 0, + content_rect.height, actual_content_height); + + float anchor_y = 0; + for (int i = 0; i < static_cast(children.size()); i++) { + const auto child = children[i]; + const auto size = child->GetPreferredSize(); + + float real_anchor_y = anchor_y + content_anchor_y; + if (direction_ == FlexDirection::Vertical) { + real_anchor_y = content_rect.top + real_anchor_y; + } else { + real_anchor_y = content_rect.GetBottom() - real_anchor_y; + } + child->Layout(Rect{ + real_anchor_y, + calculate_anchor( + static_cast(GetChildLayoutData(i)->cross_alignment.value_or( + this->item_cross_align_)), + content_rect.left, content_rect.width, size.width), + size.width, size.height}); + + anchor_y += size.height; + } + } +} +} // namespace cru::ui::render diff --git a/src/ui/render/LayoutUtility.cpp b/src/ui/render/LayoutUtility.cpp new file mode 100644 index 00000000..03252f1c --- /dev/null +++ b/src/ui/render/LayoutUtility.cpp @@ -0,0 +1,15 @@ +#include "cru/ui/render/LayoutUtility.hpp" + +#include + +namespace cru::ui::render { +Size Min(const Size& left, const Size& right) { + return Size{std::min(left.width, right.width), + std::min(left.height, right.height)}; +} + +Size Max(const Size& left, const Size& right) { + return Size{std::max(left.width, right.width), + std::max(left.height, right.height)}; +} +} // namespace cru::ui::render diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp new file mode 100644 index 00000000..83e306a8 --- /dev/null +++ b/src/ui/render/RenderObject.cpp @@ -0,0 +1,190 @@ +#include "cru/ui/render/RenderObject.hpp" + +#include "cru/common/Logger.hpp" +#include "cru/ui/UiHost.hpp" + +#include + +namespace cru::ui::render { +void RenderObject::AddChild(RenderObject* render_object, const Index position) { + Expects(child_mode_ != ChildMode::None); + Expects(!(child_mode_ == ChildMode::Single && children_.size() > 0)); + + Expects(render_object->GetParent() == + nullptr); // Render object already has a parent. + Expects(position >= 0); // Position index is less than 0. + Expects( + position <= + static_cast(children_.size())); // Position index is out of bound. + + children_.insert(children_.cbegin() + position, render_object); + render_object->SetParent(this); + render_object->SetRenderHostRecursive(GetUiHost()); + OnAddChild(render_object, position); +} + +void RenderObject::RemoveChild(const Index position) { + Expects(position >= 0); // Position index is less than 0. + Expects(position < static_cast( + children_.size())); // Position index is out of bound. + + const auto i = children_.cbegin() + position; + const auto removed_child = *i; + children_.erase(i); + removed_child->SetParent(nullptr); + removed_child->SetRenderHostRecursive(nullptr); + OnRemoveChild(removed_child, position); +} + +Point RenderObject::GetTotalOffset() const { + Point result{}; + const RenderObject* render_object = this; + + while (render_object != nullptr) { + const auto o = render_object->GetOffset(); + result.x += o.x; + result.y += o.y; + render_object = render_object->GetParent(); + } + + return result; +} + +Point RenderObject::FromRootToContent(const Point& point) const { + const auto offset = GetTotalOffset(); + const auto rect = GetContentRect(); + return Point{point.x - (offset.x + rect.left), + point.y - (offset.y + rect.top)}; +} + +void RenderObject::Measure(const Size& available_size) { + OnMeasureCore(available_size); +} + +void RenderObject::Layout(const Rect& rect) { + SetOffset(rect.GetLeftTop()); + SetSize(rect.GetSize()); + OnLayoutCore(Rect{Point{}, rect.GetSize()}); +} + +void RenderObject::OnParentChanged(RenderObject* old_parent, + RenderObject* new_parent) { + CRU_UNUSED(old_parent) + CRU_UNUSED(new_parent) +} + +void RenderObject::OnAddChild(RenderObject* new_child, Index position) { + CRU_UNUSED(new_child) + CRU_UNUSED(position) + + InvalidateLayout(); + InvalidatePaint(); +} + +void RenderObject::OnRemoveChild(RenderObject* removed_child, Index position) { + CRU_UNUSED(removed_child) + CRU_UNUSED(position) + + InvalidateLayout(); + InvalidatePaint(); +} + +void RenderObject::OnMeasureCore(const Size& available_size) { + Size margin_padding_size{ + margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), + margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; + + auto coerced_margin_padding_size = margin_padding_size; + if (coerced_margin_padding_size.width > available_size.width) { + log::Warn( + "Measure: horizontal length of padding and margin is bigger than " + "available length."); + coerced_margin_padding_size.width = available_size.width; + } + if (coerced_margin_padding_size.height > available_size.height) { + log::Warn( + "Measure: vertical length of padding and margin is bigger than " + "available length."); + coerced_margin_padding_size.height = available_size.height; + } + + const auto coerced_content_available_size = + available_size - coerced_margin_padding_size; + const auto actual_content_size = + OnMeasureContent(coerced_content_available_size); + + SetPreferredSize(coerced_margin_padding_size + actual_content_size); +} + +void RenderObject::OnLayoutCore(const Rect& rect) { + Size margin_padding_size{ + margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), + margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; + const auto content_available_size = rect.GetSize() - margin_padding_size; + auto coerced_content_available_size = content_available_size; + + if (coerced_content_available_size.width < 0) { + log::Warn( + "Layout: horizontal length of padding and margin is bigger than " + "available length."); + coerced_content_available_size.width = 0; + } + if (coerced_content_available_size.height < 0) { + log::Warn( + "Layout: vertical length of padding and margin is bigger than " + "available length."); + coerced_content_available_size.height = 0; + } + + OnLayoutContent(Rect{margin_.left + padding_.left, margin_.top + padding_.top, + coerced_content_available_size.width, + coerced_content_available_size.height}); +} + +void RenderObject::OnAfterLayout() {} + +Rect RenderObject::GetPaddingRect() const { + Rect rect{Point{}, GetSize()}; + rect = rect.Shrink(GetMargin()); + rect.width = std::max(rect.width, 0.0f); + rect.height = std::max(rect.height, 0.0f); + return rect; +} + +Rect RenderObject::GetContentRect() const { + Rect rect{Point{}, GetSize()}; + rect = rect.Shrink(GetMargin()); + rect = rect.Shrink(GetPadding()); + rect.width = std::max(rect.width, 0.0f); + rect.height = std::max(rect.height, 0.0f); + return rect; +} + +void RenderObject::SetParent(RenderObject* new_parent) { + const auto old_parent = parent_; + parent_ = new_parent; + OnParentChanged(old_parent, new_parent); +} + +void RenderObject::InvalidateLayout() { + if (ui_host_ != nullptr) ui_host_->InvalidateLayout(); +} + +void RenderObject::InvalidatePaint() { + if (ui_host_ != nullptr) ui_host_->InvalidatePaint(); +} + +void RenderObject::NotifyAfterLayoutRecursive(RenderObject* render_object) { + render_object->OnAfterLayout(); + for (const auto o : render_object->GetChildren()) { + NotifyAfterLayoutRecursive(o); + } +} + +void RenderObject::SetRenderHostRecursive(UiHost* host) { + ui_host_ = host; + for (const auto child : GetChildren()) { + child->SetRenderHostRecursive(host); + } +} +} // namespace cru::ui::render diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp new file mode 100644 index 00000000..33e9e407 --- /dev/null +++ b/src/ui/render/ScrollRenderObject.cpp @@ -0,0 +1 @@ +#include "cru/ui/render/ScrollRenderObject.hpp" diff --git a/src/ui/render/StackLayoutRenderObject.cpp b/src/ui/render/StackLayoutRenderObject.cpp new file mode 100644 index 00000000..d08e136d --- /dev/null +++ b/src/ui/render/StackLayoutRenderObject.cpp @@ -0,0 +1,49 @@ +#include "cru/ui/render/StackLayoutRenderObject.hpp" + +#include + +namespace cru::ui::render { +Size StackLayoutRenderObject::OnMeasureContent(const Size& available_size) { + auto size = Size{}; + for (const auto child : GetChildren()) { + child->Measure(available_size); + const auto& preferred_size = child->GetPreferredSize(); + size.width = std::max(size.width, preferred_size.width); + size.height = std::max(size.height, preferred_size.height); + } + return size; +} + +void StackLayoutRenderObject::OnLayoutContent(const Rect& rect) { + auto calculate_anchor = [](int alignment, float start_point, + float total_length, + float content_length) -> float { + switch (alignment) { + case internal::align_start: + return start_point; + case internal::align_center: + return start_point + (total_length - content_length) / 2.0f; + case internal::align_end: + return start_point + total_length - content_length; + default: + return 0; + } + }; + + const auto count = GetChildCount(); + const auto& children = GetChildren(); + + for (int i = 0; i < count; i++) { + const auto layout_data = GetChildLayoutData(i); + const auto child = children[i]; + const auto& size = child->GetPreferredSize(); + child->Layout( + Rect{calculate_anchor(static_cast(layout_data->horizontal), + rect.left, rect.width, size.width), + calculate_anchor(static_cast(layout_data->vertical), rect.top, + rect.height, size.height), + size.width, size.height}); + } +} + +} // namespace cru::ui::render diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp new file mode 100644 index 00000000..05acd086 --- /dev/null +++ b/src/ui/render/TextRenderObject.cpp @@ -0,0 +1,177 @@ +#include "cru/ui/render/TextRenderObject.hpp" + +#include "../Helper.hpp" +#include "cru/platform/graph/Factory.hpp" +#include "cru/platform/graph/TextLayout.hpp" +#include "cru/platform/graph/util/Painter.hpp" + +#include + +namespace cru::ui::render { +TextRenderObject::TextRenderObject( + std::shared_ptr brush, + std::shared_ptr font, + std::shared_ptr selection_brush, + std::shared_ptr caret_brush) { + Expects(brush); + Expects(font); + Expects(selection_brush); + Expects(caret_brush); + + SetChildMode(ChildMode::None); + + brush.swap(brush_); + font.swap(font_); + selection_brush.swap(selection_brush_); + caret_brush.swap(caret_brush_); + + const auto graph_factory = GetGraphFactory(); + text_layout_ = graph_factory->CreateTextLayout(font_, ""); +} + +TextRenderObject::~TextRenderObject() = default; + +std::string TextRenderObject::GetText() const { + return text_layout_->GetText(); +} + +void TextRenderObject::SetText(std::string new_text) { + text_layout_->SetText(std::move(new_text)); +} + +void TextRenderObject::SetBrush( + std::shared_ptr new_brush) { + Expects(new_brush); + new_brush.swap(brush_); + InvalidatePaint(); +} + +std::shared_ptr TextRenderObject::GetFont() const { + return text_layout_->GetFont(); +} + +void TextRenderObject::SetFont(std::shared_ptr font) { + Expects(font); + text_layout_->SetFont(std::move(font)); +} + +std::vector TextRenderObject::TextRangeRect(const TextRange& text_range) { + return text_layout_->TextRangeRect(text_range); +} + +Point TextRenderObject::TextSinglePoint(gsl::index position, bool trailing) { + return text_layout_->TextSinglePoint(position, trailing); +} + +platform::graph::TextHitTestResult TextRenderObject::TextHitTest( + const Point& point) { + return text_layout_->HitTest(point); +} + +void TextRenderObject::SetSelectionRange(std::optional new_range) { + selection_range_ = std::move(new_range); + InvalidatePaint(); +} + +void TextRenderObject::SetSelectionBrush( + std::shared_ptr new_brush) { + Expects(new_brush); + new_brush.swap(selection_brush_); + if (selection_range_ && selection_range_->count) { + InvalidatePaint(); + } +} + +void TextRenderObject::SetDrawCaret(bool draw_caret) { + if (draw_caret_ != draw_caret) { + draw_caret_ = draw_caret; + InvalidatePaint(); + } +} + +void TextRenderObject::SetCaretPosition(gsl::index position) { + if (position != caret_position_) { + caret_position_ = position; + if (draw_caret_) { + InvalidatePaint(); + } + } +} + +void TextRenderObject::GetCaretBrush( + std::shared_ptr brush) { + Expects(brush); + brush.swap(caret_brush_); + if (draw_caret_) { + InvalidatePaint(); + } +} + +void TextRenderObject::SetCaretWidth(const float width) { + Expects(width >= 0.0f); + + caret_width_ = width; + if (draw_caret_) { + InvalidatePaint(); + } +} + +void TextRenderObject::Draw(platform::graph::IPainter* painter) { + platform::graph::util::WithTransform( + painter, + platform::Matrix::Translation(GetMargin().left + GetPadding().left, + GetMargin().top + GetPadding().top), + [this](platform::graph::IPainter* p) { + if (this->selection_range_.has_value()) { + const auto&& rects = + text_layout_->TextRangeRect(this->selection_range_.value()); + for (const auto& rect : rects) + p->FillRectangle(rect, this->GetSelectionBrush().get()); + } + + p->DrawText(Point{}, text_layout_.get(), brush_.get()); + + if (this->draw_caret_ && this->caret_width_ != 0.0f) { + auto caret_pos = this->caret_position_; + gsl::index text_size = this->GetText().size(); + if (caret_pos < 0) { + caret_pos = 0; + } else if (caret_pos > text_size) { + caret_pos = text_size; + } + + const auto caret_top_center = + this->text_layout_->TextSinglePoint(caret_pos, false); + + const auto font_height = this->font_->GetFontSize(); + const auto caret_width = this->caret_width_; + + p->FillRectangle(Rect{caret_top_center.x - caret_width / 2.0f, + caret_top_center.y, caret_width, font_height}, + this->caret_brush_.get()); + } + }); +} + +RenderObject* TextRenderObject::HitTest(const Point& point) { + const auto padding_rect = GetPaddingRect(); + return padding_rect.IsPointInside(point) ? this : nullptr; +} + +Size TextRenderObject::OnMeasureContent(const Size& available_size) { + text_layout_->SetMaxWidth(available_size.width); + text_layout_->SetMaxHeight(available_size.height); + return text_layout_->GetTextBounds().GetSize(); +} + +void TextRenderObject::OnLayoutContent(const Rect& content_rect) { + CRU_UNUSED(content_rect) +} + +void TextRenderObject::OnAfterLayout() { + const auto&& size = GetContentRect().GetSize(); + text_layout_->SetMaxWidth(size.width); + text_layout_->SetMaxHeight(size.height); +} + +} // namespace cru::ui::render diff --git a/src/ui/render/WindowRenderObject.cpp b/src/ui/render/WindowRenderObject.cpp new file mode 100644 index 00000000..cd1f806f --- /dev/null +++ b/src/ui/render/WindowRenderObject.cpp @@ -0,0 +1,45 @@ +#include "cru/ui/render/WindowRenderObject.hpp" + +#include "../Helper.hpp" +#include "cru/platform/graph/util/Painter.hpp" +#include "cru/ui/UiHost.hpp" + +namespace cru::ui::render { +WindowRenderObject::WindowRenderObject(UiHost* host) { + SetChildMode(ChildMode::Single); + ui_host_ = host; + after_layout_event_guard_.Reset(host->AfterLayoutEvent()->AddHandler( + [this](auto) { NotifyAfterLayoutRecursive(this); })); +} + +void WindowRenderObject::Draw(platform::graph::IPainter* painter) { + painter->Clear(colors::white); + if (const auto child = GetChild()) { + auto offset = child->GetOffset(); + platform::graph::util::WithTransform( + painter, platform::Matrix::Translation(offset.x, offset.y), + [child](platform::graph::IPainter* p) { child->Draw(p); }); + } +} + +RenderObject* WindowRenderObject::HitTest(const Point& point) { + if (const auto child = GetChild()) { + auto offset = child->GetOffset(); + Point p{point.x - offset.x, point.y - offset.y}; + const auto result = child->HitTest(p); + if (result != nullptr) { + return result; + } + } + return Rect{Point{}, GetSize()}.IsPointInside(point) ? this : nullptr; +} + +Size WindowRenderObject::OnMeasureContent(const Size& available_size) { + if (const auto child = GetChild()) child->Measure(available_size); + return available_size; +} + +void WindowRenderObject::OnLayoutContent(const Rect& content_rect) { + if (const auto child = GetChild()) child->Layout(content_rect); +} +} // namespace cru::ui::render diff --git a/src/ui/render/border_render_object.cpp b/src/ui/render/border_render_object.cpp deleted file mode 100644 index 7c96a3b6..00000000 --- a/src/ui/render/border_render_object.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "cru/ui/render/border_render_object.hpp" - -#include "../helper.hpp" -#include "cru/common/logger.hpp" -#include "cru/platform/graph/factory.hpp" -#include "cru/platform/graph/geometry.hpp" -#include "cru/platform/graph/util/painter.hpp" - -#include - -namespace cru::ui::render { -BorderRenderObject::BorderRenderObject() { - SetChildMode(ChildMode::Single); - RecreateGeometry(); -} - -BorderRenderObject::~BorderRenderObject() {} - -void BorderRenderObject::Draw(platform::graph::IPainter* painter) { - if (background_brush_ != nullptr) - painter->FillGeometry(border_inner_geometry_.get(), - background_brush_.get()); - if (is_border_enabled_) { - if (border_brush_ == nullptr) { - log::Warn("Border is enabled but brush is null"); - } else { - painter->FillGeometry(geometry_.get(), border_brush_.get()); - } - } - if (const auto child = GetChild()) { - auto offset = child->GetOffset(); - platform::graph::util::WithTransform( - painter, platform::Matrix::Translation(offset.x, offset.y), - [child](auto p) { child->Draw(p); }); - } - if (foreground_brush_ != nullptr) - painter->FillGeometry(border_inner_geometry_.get(), - foreground_brush_.get()); -} - -void BorderRenderObject::SetBorderStyle(const BorderStyle& style) { - border_brush_ = style.border_brush; - border_thickness_ = style.border_thickness; - border_radius_ = style.border_radius; - foreground_brush_ = style.foreground_brush; - background_brush_ = style.background_brush; - InvalidateLayout(); -} - -RenderObject* BorderRenderObject::HitTest(const Point& point) { - if (const auto child = GetChild()) { - auto offset = child->GetOffset(); - Point p{point.x - offset.x, point.y - offset.y}; - const auto result = child->HitTest(p); - if (result != nullptr) { - return result; - } - } - - if (is_border_enabled_) { - const auto contains = - border_outer_geometry_->FillContains(Point{point.x, point.y}); - return contains ? this : nullptr; - } else { - 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; - } -} - -void BorderRenderObject::OnMeasureCore(const Size& available_size) { - const auto margin = GetMargin(); - const auto padding = GetPadding(); - Size margin_border_padding_size{ - margin.GetHorizontalTotal() + padding.GetHorizontalTotal(), - margin.GetVerticalTotal() + padding.GetVerticalTotal()}; - - if (is_border_enabled_) { - margin_border_padding_size.width += border_thickness_.GetHorizontalTotal(); - margin_border_padding_size.height += border_thickness_.GetVerticalTotal(); - } - - auto coerced_margin_border_padding_size = margin_border_padding_size; - if (coerced_margin_border_padding_size.width > available_size.width) { - log::Warn( - "Measure: horizontal length of padding, border and margin is bigger " - "than available length."); - coerced_margin_border_padding_size.width = available_size.width; - } - if (coerced_margin_border_padding_size.height > available_size.height) { - log::Warn( - "Measure: vertical length of padding, border and margin is bigger " - "than available length."); - coerced_margin_border_padding_size.height = available_size.height; - } - - const auto coerced_content_available_size = - available_size - coerced_margin_border_padding_size; - - const auto actual_content_size = - OnMeasureContent(coerced_content_available_size); - - SetPreferredSize(coerced_margin_border_padding_size + actual_content_size); -} - -void BorderRenderObject::OnLayoutCore(const Rect& rect) { - const auto margin = GetMargin(); - const auto padding = GetPadding(); - Size margin_border_padding_size{ - margin.GetHorizontalTotal() + padding.GetHorizontalTotal(), - margin.GetVerticalTotal() + padding.GetVerticalTotal()}; - - if (is_border_enabled_) { - margin_border_padding_size.width += border_thickness_.GetHorizontalTotal(); - margin_border_padding_size.height += border_thickness_.GetVerticalTotal(); - } - - const auto content_available_size = - rect.GetSize() - margin_border_padding_size; - auto coerced_content_available_size = content_available_size; - - if (coerced_content_available_size.width < 0) { - log::Warn( - "Layout: horizontal length of padding, border and margin is bigger " - "than available length."); - coerced_content_available_size.width = 0; - } - if (coerced_content_available_size.height < 0) { - log::Warn( - "Layout: vertical length of padding, border and margin is bigger " - "than available length."); - coerced_content_available_size.height = 0; - } - - OnLayoutContent( - Rect{margin.left + (is_border_enabled_ ? border_thickness_.left : 0) + - padding.left, - margin.top + (is_border_enabled_ ? border_thickness_.top : 0) + - padding.top, - coerced_content_available_size.width, - coerced_content_available_size.height}); -} - -Size BorderRenderObject::OnMeasureContent(const Size& available_size) { - const auto child = GetChild(); - if (child) { - child->Measure(available_size); - return child->GetPreferredSize(); - } else { - return Size{}; - } -} - -void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { - const auto child = GetChild(); - if (child) { - child->Layout(content_rect); - } -} - -void BorderRenderObject::OnAfterLayout() { RecreateGeometry(); } - -void BorderRenderObject::RecreateGeometry() { - geometry_.reset(); - border_outer_geometry_.reset(); - - Thickness t = border_thickness_; - t.left /= 2.0; - t.top /= 2.0; - t.right /= 2.0; - t.bottom /= 2.0; - const CornerRadius& r = border_radius_; - - CornerRadius outer_radius(r.left_top + Point{t.left, t.top}, - r.right_top + Point{t.right, t.top}, - r.left_bottom + Point{t.left, t.bottom}, - r.right_bottom + Point{t.right, t.bottom}); - - CornerRadius inner_radius(r.left_top - Point{t.left, t.top}, - r.right_top - Point{t.right, t.top}, - r.left_bottom - Point{t.left, t.bottom}, - r.right_bottom - Point{t.right, t.bottom}); - - auto f = [](platform::graph::IGeometryBuilder* builder, const Rect& rect, - const CornerRadius& corner) { - builder->BeginFigure(Point(rect.left + corner.left_top.x, rect.top)); - builder->LineTo(Point(rect.GetRight() - corner.right_top.x, rect.top)); - builder->QuadraticBezierTo( - Point(rect.GetRight(), rect.top), - Point(rect.GetRight(), rect.top + corner.right_top.y)); - builder->LineTo( - Point(rect.GetRight(), rect.GetBottom() - corner.right_bottom.y)); - builder->QuadraticBezierTo( - Point(rect.GetRight(), rect.GetBottom()), - Point(rect.GetRight() - corner.right_bottom.x, rect.GetBottom())); - builder->LineTo(Point(rect.left + corner.left_bottom.x, rect.GetBottom())); - builder->QuadraticBezierTo( - Point(rect.left, rect.GetBottom()), - Point(rect.left, rect.GetBottom() - corner.left_bottom.y)); - builder->LineTo(Point(rect.left, rect.top + corner.left_top.y)); - builder->QuadraticBezierTo(Point(rect.left, rect.top), - Point(rect.left + corner.left_top.x, rect.top)); - builder->CloseFigure(true); - }; - - const auto size = GetSize(); - const auto margin = GetMargin(); - const Rect outer_rect{margin.left, margin.top, - size.width - margin.GetHorizontalTotal(), - size.height - margin.GetVerticalTotal()}; - const auto graph_factory = GetGraphFactory(); - std::unique_ptr builder{ - graph_factory->CreateGeometryBuilder()}; - f(builder.get(), outer_rect, outer_radius); - border_outer_geometry_ = builder->Build(); - builder.reset(); - - const Rect inner_rect = outer_rect.Shrink(border_thickness_); - - builder = graph_factory->CreateGeometryBuilder(); - f(builder.get(), inner_rect, inner_radius); - border_inner_geometry_ = builder->Build(); - builder.reset(); - - builder = graph_factory->CreateGeometryBuilder(); - f(builder.get(), outer_rect, outer_radius); - f(builder.get(), inner_rect, inner_radius); - geometry_ = builder->Build(); - builder.reset(); -} -} // namespace cru::ui::render diff --git a/src/ui/render/canvas_render_object.cpp b/src/ui/render/canvas_render_object.cpp deleted file mode 100644 index 0508e276..00000000 --- a/src/ui/render/canvas_render_object.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "cru/ui/render/canvas_render_object.hpp" - -#include "cru/ui/render/layout_utility.hpp" - -namespace cru::ui::render { -CanvasRenderObject::CanvasRenderObject() : RenderObject(ChildMode::None) {} - -CanvasRenderObject::~CanvasRenderObject() = default; - -void CanvasRenderObject::Draw(platform::graph::IPainter* painter) { - const auto rect = GetContentRect(); - CanvasPaintEventArgs args{painter, rect}; - paint_event_.Raise(args); -} - -RenderObject* CanvasRenderObject::HitTest(const Point& point) { - const auto padding_rect = GetPaddingRect(); - return padding_rect.IsPointInside(point) ? this : nullptr; -} - -Size CanvasRenderObject::OnMeasureContent(const Size& available_size) { - return Min(available_size, GetDesiredSize()); -} - -void CanvasRenderObject::OnLayoutContent(const Rect& content_rect) { - CRU_UNUSED(content_rect) -} -} // namespace cru::ui::render diff --git a/src/ui/render/flex_layout_render_object.cpp b/src/ui/render/flex_layout_render_object.cpp deleted file mode 100644 index f40a9b3e..00000000 --- a/src/ui/render/flex_layout_render_object.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include "cru/ui/render/flex_layout_render_object.hpp" - -#include "cru/platform/graph/util/painter.hpp" - -#include -#include - -namespace cru::ui::render { -Size FlexLayoutRenderObject::OnMeasureContent(const Size& available_size) { - std::vector has_basis_children; - std::vector no_basis_children; - std::vector grow_children; - std::vector shrink_chilren; - const auto child_count = GetChildCount(); - for (int i = 0; i < child_count; i++) { - const auto& layout_data = *GetChildLayoutData(i); - if (layout_data.flex_basis.has_value()) - has_basis_children.push_back(i); - else - no_basis_children.push_back(i); - if (layout_data.flex_grow > 0) grow_children.push_back(i); - if (layout_data.flex_shrink > 0) shrink_chilren.push_back(i); - } - - std::function get_main_length; - std::function get_cross_length; - std::function create_size; - - if (direction_ == FlexDirection::Horizontal || - direction_ == FlexDirection::HorizontalReverse) { - get_main_length = [](const Size& size) { return size.width; }; - get_cross_length = [](const Size& size) { return size.height; }; - create_size = [](float main, float cross) { return Size(main, cross); }; - } else { - get_main_length = [](const Size& size) { return size.height; }; - get_cross_length = [](const Size& size) { return size.width; }; - create_size = [](float main, float cross) { return Size(cross, main); }; - } - - const auto& children = GetChildren(); - - float remain_main_length = get_main_length(available_size); - float max_cross_length = 0; - - for (const int i : has_basis_children) { - const auto child = children[i]; - const float basis = GetChildLayoutData(i)->flex_basis.value(); - child->Measure(create_size(basis, get_cross_length(available_size))); - remain_main_length -= basis; - const float child_preferred_cross_length = - get_cross_length(child->GetPreferredSize()); - child->SetPreferredSize(create_size(basis, child_preferred_cross_length)); - max_cross_length = std::max(max_cross_length, child_preferred_cross_length); - } - - for (const int i : no_basis_children) { - const auto child = children[i]; - child->Measure(create_size(remain_main_length > 0 ? remain_main_length : 0, - get_cross_length(available_size))); - remain_main_length -= get_main_length(child->GetPreferredSize()); - max_cross_length = - std::max(max_cross_length, get_cross_length(child->GetPreferredSize())); - } - - if (remain_main_length > 0) { - float total_grow = 0; - for (const int i : grow_children) - total_grow += GetChildLayoutData(i)->flex_grow; - - for (const int i : grow_children) { - const float distributed_grow_length = - remain_main_length * (GetChildLayoutData(i)->flex_grow / total_grow); - const auto child = children[i]; - const float new_main_length = - get_main_length(child->GetPreferredSize()) + distributed_grow_length; - child->Measure( - create_size(new_main_length, get_cross_length(available_size))); - const float new_child_preferred_cross_length = - get_cross_length(child->GetPreferredSize()); - child->SetPreferredSize( - create_size(new_main_length, new_child_preferred_cross_length)); - max_cross_length = - std::max(max_cross_length, new_child_preferred_cross_length); - } - } - - if (remain_main_length < 0) { - float total_shrink = 0; - for (const int i : shrink_chilren) - total_shrink += GetChildLayoutData(i)->flex_shrink; - - for (const int i : shrink_chilren) { - const float distributed_shrink_length = // negative - remain_main_length * - (GetChildLayoutData(i)->flex_shrink / total_shrink); - const auto child = children[i]; - float new_main_length = get_main_length(child->GetPreferredSize()) + - distributed_shrink_length; - new_main_length = new_main_length > 0 ? new_main_length : 0; - child->Measure( - create_size(new_main_length, get_cross_length(available_size))); - const float new_child_preferred_cross_length = - get_cross_length(child->GetPreferredSize()); - child->SetPreferredSize( - create_size(new_main_length, new_child_preferred_cross_length)); - max_cross_length = - std::max(max_cross_length, new_child_preferred_cross_length); - } - } - - return create_size(get_main_length(available_size) - - (remain_main_length > 0 ? remain_main_length : 0), - max_cross_length); -} - -void FlexLayoutRenderObject::OnLayoutContent(const Rect& content_rect) { - auto calculate_anchor = [](int alignment, float start_point, - float total_length, - float content_length) -> float { - switch (alignment) { - case internal::align_start: - return start_point; - case internal::align_center: - return start_point + (total_length - content_length) / 2.0f; - case internal::align_end: - return start_point + total_length - content_length; - default: - return 0; - } - }; - - const auto& children = GetChildren(); - if (direction_ == FlexDirection::Horizontal || - direction_ == FlexDirection::HorizontalReverse) { - float actual_content_width = 0; - for (const auto child : children) { - actual_content_width += child->GetPreferredSize().width; - } - - const float content_anchor_x = - calculate_anchor(static_cast(content_main_align_), 0, - content_rect.width, actual_content_width); - - float anchor_x = 0; - for (int i = 0; i < static_cast(children.size()); i++) { - const auto child = children[i]; - const auto size = child->GetPreferredSize(); - - float real_anchor_x = anchor_x + content_anchor_x; - if (direction_ == FlexDirection::Horizontal) - real_anchor_x = content_rect.left + real_anchor_x; - else - real_anchor_x = content_rect.GetRight() - real_anchor_x; - child->Layout(Rect{ - real_anchor_x, - calculate_anchor( - static_cast(GetChildLayoutData(i)->cross_alignment.value_or( - this->item_cross_align_)), - content_rect.top, content_rect.height, size.height), - size.width, size.height}); - - anchor_x += size.width; - } - } else { - float actual_content_height = 0; - for (const auto child : children) { - actual_content_height = child->GetPreferredSize().height; - } - - const float content_anchor_y = - calculate_anchor(static_cast(content_main_align_), 0, - content_rect.height, actual_content_height); - - float anchor_y = 0; - for (int i = 0; i < static_cast(children.size()); i++) { - const auto child = children[i]; - const auto size = child->GetPreferredSize(); - - float real_anchor_y = anchor_y + content_anchor_y; - if (direction_ == FlexDirection::Vertical) { - real_anchor_y = content_rect.top + real_anchor_y; - } else { - real_anchor_y = content_rect.GetBottom() - real_anchor_y; - } - child->Layout(Rect{ - real_anchor_y, - calculate_anchor( - static_cast(GetChildLayoutData(i)->cross_alignment.value_or( - this->item_cross_align_)), - content_rect.left, content_rect.width, size.width), - size.width, size.height}); - - anchor_y += size.height; - } - } -} -} // namespace cru::ui::render diff --git a/src/ui/render/layout_utility.cpp b/src/ui/render/layout_utility.cpp deleted file mode 100644 index aae62d35..00000000 --- a/src/ui/render/layout_utility.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "cru/ui/render/layout_utility.hpp" - -#include - -namespace cru::ui::render { -Size Min(const Size& left, const Size& right) { - return Size{std::min(left.width, right.width), - std::min(left.height, right.height)}; -} - -Size Max(const Size& left, const Size& right) { - return Size{std::max(left.width, right.width), - std::max(left.height, right.height)}; -} -} // namespace cru::ui::render diff --git a/src/ui/render/render_object.cpp b/src/ui/render/render_object.cpp deleted file mode 100644 index 4a1bedf3..00000000 --- a/src/ui/render/render_object.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "cru/ui/render/render_object.hpp" - -#include "cru/common/logger.hpp" -#include "cru/ui/ui_host.hpp" - -#include - -namespace cru::ui::render { -void RenderObject::AddChild(RenderObject* render_object, const Index position) { - Expects(child_mode_ != ChildMode::None); - Expects(!(child_mode_ == ChildMode::Single && children_.size() > 0)); - - Expects(render_object->GetParent() == - nullptr); // Render object already has a parent. - Expects(position >= 0); // Position index is less than 0. - Expects( - position <= - static_cast(children_.size())); // Position index is out of bound. - - children_.insert(children_.cbegin() + position, render_object); - render_object->SetParent(this); - render_object->SetRenderHostRecursive(GetUiHost()); - OnAddChild(render_object, position); -} - -void RenderObject::RemoveChild(const Index position) { - Expects(position >= 0); // Position index is less than 0. - Expects(position < static_cast( - children_.size())); // Position index is out of bound. - - const auto i = children_.cbegin() + position; - const auto removed_child = *i; - children_.erase(i); - removed_child->SetParent(nullptr); - removed_child->SetRenderHostRecursive(nullptr); - OnRemoveChild(removed_child, position); -} - -Point RenderObject::GetTotalOffset() const { - Point result{}; - const RenderObject* render_object = this; - - while (render_object != nullptr) { - const auto o = render_object->GetOffset(); - result.x += o.x; - result.y += o.y; - render_object = render_object->GetParent(); - } - - return result; -} - -Point RenderObject::FromRootToContent(const Point& point) const { - const auto offset = GetTotalOffset(); - const auto rect = GetContentRect(); - return Point{point.x - (offset.x + rect.left), - point.y - (offset.y + rect.top)}; -} - -void RenderObject::Measure(const Size& available_size) { - OnMeasureCore(available_size); -} - -void RenderObject::Layout(const Rect& rect) { - SetOffset(rect.GetLeftTop()); - SetSize(rect.GetSize()); - OnLayoutCore(Rect{Point{}, rect.GetSize()}); -} - -void RenderObject::OnParentChanged(RenderObject* old_parent, - RenderObject* new_parent) { - CRU_UNUSED(old_parent) - CRU_UNUSED(new_parent) -} - -void RenderObject::OnAddChild(RenderObject* new_child, Index position) { - CRU_UNUSED(new_child) - CRU_UNUSED(position) - - InvalidateLayout(); - InvalidatePaint(); -} - -void RenderObject::OnRemoveChild(RenderObject* removed_child, Index position) { - CRU_UNUSED(removed_child) - CRU_UNUSED(position) - - InvalidateLayout(); - InvalidatePaint(); -} - -void RenderObject::OnMeasureCore(const Size& available_size) { - Size margin_padding_size{ - margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), - margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; - - auto coerced_margin_padding_size = margin_padding_size; - if (coerced_margin_padding_size.width > available_size.width) { - log::Warn( - "Measure: horizontal length of padding and margin is bigger than " - "available length."); - coerced_margin_padding_size.width = available_size.width; - } - if (coerced_margin_padding_size.height > available_size.height) { - log::Warn( - "Measure: vertical length of padding and margin is bigger than " - "available length."); - coerced_margin_padding_size.height = available_size.height; - } - - const auto coerced_content_available_size = - available_size - coerced_margin_padding_size; - const auto actual_content_size = - OnMeasureContent(coerced_content_available_size); - - SetPreferredSize(coerced_margin_padding_size + actual_content_size); -} - -void RenderObject::OnLayoutCore(const Rect& rect) { - Size margin_padding_size{ - margin_.GetHorizontalTotal() + padding_.GetHorizontalTotal(), - margin_.GetVerticalTotal() + padding_.GetVerticalTotal()}; - const auto content_available_size = rect.GetSize() - margin_padding_size; - auto coerced_content_available_size = content_available_size; - - if (coerced_content_available_size.width < 0) { - log::Warn( - "Layout: horizontal length of padding and margin is bigger than " - "available length."); - coerced_content_available_size.width = 0; - } - if (coerced_content_available_size.height < 0) { - log::Warn( - "Layout: vertical length of padding and margin is bigger than " - "available length."); - coerced_content_available_size.height = 0; - } - - OnLayoutContent(Rect{margin_.left + padding_.left, margin_.top + padding_.top, - coerced_content_available_size.width, - coerced_content_available_size.height}); -} - -void RenderObject::OnAfterLayout() {} - -Rect RenderObject::GetPaddingRect() const { - Rect rect{Point{}, GetSize()}; - rect = rect.Shrink(GetMargin()); - rect.width = std::max(rect.width, 0.0f); - rect.height = std::max(rect.height, 0.0f); - return rect; -} - -Rect RenderObject::GetContentRect() const { - Rect rect{Point{}, GetSize()}; - rect = rect.Shrink(GetMargin()); - rect = rect.Shrink(GetPadding()); - rect.width = std::max(rect.width, 0.0f); - rect.height = std::max(rect.height, 0.0f); - return rect; -} - -void RenderObject::SetParent(RenderObject* new_parent) { - const auto old_parent = parent_; - parent_ = new_parent; - OnParentChanged(old_parent, new_parent); -} - -void RenderObject::InvalidateLayout() { - if (ui_host_ != nullptr) ui_host_->InvalidateLayout(); -} - -void RenderObject::InvalidatePaint() { - if (ui_host_ != nullptr) ui_host_->InvalidatePaint(); -} - -void RenderObject::NotifyAfterLayoutRecursive(RenderObject* render_object) { - render_object->OnAfterLayout(); - for (const auto o : render_object->GetChildren()) { - NotifyAfterLayoutRecursive(o); - } -} - -void RenderObject::SetRenderHostRecursive(UiHost* host) { - ui_host_ = host; - for (const auto child : GetChildren()) { - child->SetRenderHostRecursive(host); - } -} -} // namespace cru::ui::render diff --git a/src/ui/render/scroll_render_object.cpp b/src/ui/render/scroll_render_object.cpp deleted file mode 100644 index f77ee636..00000000 --- a/src/ui/render/scroll_render_object.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "cru/ui/render/scroll_render_object.hpp" diff --git a/src/ui/render/stack_layout_render_object.cpp b/src/ui/render/stack_layout_render_object.cpp deleted file mode 100644 index 1cb31252..00000000 --- a/src/ui/render/stack_layout_render_object.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "cru/ui/render/stack_layout_render_object.hpp" - -#include - -namespace cru::ui::render { -Size StackLayoutRenderObject::OnMeasureContent(const Size& available_size) { - auto size = Size{}; - for (const auto child : GetChildren()) { - child->Measure(available_size); - const auto& preferred_size = child->GetPreferredSize(); - size.width = std::max(size.width, preferred_size.width); - size.height = std::max(size.height, preferred_size.height); - } - return size; -} - -void StackLayoutRenderObject::OnLayoutContent(const Rect& rect) { - auto calculate_anchor = [](int alignment, float start_point, - float total_length, - float content_length) -> float { - switch (alignment) { - case internal::align_start: - return start_point; - case internal::align_center: - return start_point + (total_length - content_length) / 2.0f; - case internal::align_end: - return start_point + total_length - content_length; - default: - return 0; - } - }; - - const auto count = GetChildCount(); - const auto& children = GetChildren(); - - for (int i = 0; i < count; i++) { - const auto layout_data = GetChildLayoutData(i); - const auto child = children[i]; - const auto& size = child->GetPreferredSize(); - child->Layout( - Rect{calculate_anchor(static_cast(layout_data->horizontal), - rect.left, rect.width, size.width), - calculate_anchor(static_cast(layout_data->vertical), rect.top, - rect.height, size.height), - size.width, size.height}); - } -} - -} // namespace cru::ui::render diff --git a/src/ui/render/text_render_object.cpp b/src/ui/render/text_render_object.cpp deleted file mode 100644 index cd670db1..00000000 --- a/src/ui/render/text_render_object.cpp +++ /dev/null @@ -1,177 +0,0 @@ -#include "cru/ui/render/text_render_object.hpp" - -#include "../helper.hpp" -#include "cru/platform/graph/factory.hpp" -#include "cru/platform/graph/text_layout.hpp" -#include "cru/platform/graph/util/painter.hpp" - -#include - -namespace cru::ui::render { -TextRenderObject::TextRenderObject( - std::shared_ptr brush, - std::shared_ptr font, - std::shared_ptr selection_brush, - std::shared_ptr caret_brush) { - Expects(brush); - Expects(font); - Expects(selection_brush); - Expects(caret_brush); - - SetChildMode(ChildMode::None); - - brush.swap(brush_); - font.swap(font_); - selection_brush.swap(selection_brush_); - caret_brush.swap(caret_brush_); - - const auto graph_factory = GetGraphFactory(); - text_layout_ = graph_factory->CreateTextLayout(font_, ""); -} - -TextRenderObject::~TextRenderObject() = default; - -std::string TextRenderObject::GetText() const { - return text_layout_->GetText(); -} - -void TextRenderObject::SetText(std::string new_text) { - text_layout_->SetText(std::move(new_text)); -} - -void TextRenderObject::SetBrush( - std::shared_ptr new_brush) { - Expects(new_brush); - new_brush.swap(brush_); - InvalidatePaint(); -} - -std::shared_ptr TextRenderObject::GetFont() const { - return text_layout_->GetFont(); -} - -void TextRenderObject::SetFont(std::shared_ptr font) { - Expects(font); - text_layout_->SetFont(std::move(font)); -} - -std::vector TextRenderObject::TextRangeRect(const TextRange& text_range) { - return text_layout_->TextRangeRect(text_range); -} - -Point TextRenderObject::TextSinglePoint(gsl::index position, bool trailing) { - return text_layout_->TextSinglePoint(position, trailing); -} - -platform::graph::TextHitTestResult TextRenderObject::TextHitTest( - const Point& point) { - return text_layout_->HitTest(point); -} - -void TextRenderObject::SetSelectionRange(std::optional new_range) { - selection_range_ = std::move(new_range); - InvalidatePaint(); -} - -void TextRenderObject::SetSelectionBrush( - std::shared_ptr new_brush) { - Expects(new_brush); - new_brush.swap(selection_brush_); - if (selection_range_ && selection_range_->count) { - InvalidatePaint(); - } -} - -void TextRenderObject::SetDrawCaret(bool draw_caret) { - if (draw_caret_ != draw_caret) { - draw_caret_ = draw_caret; - InvalidatePaint(); - } -} - -void TextRenderObject::SetCaretPosition(gsl::index position) { - if (position != caret_position_) { - caret_position_ = position; - if (draw_caret_) { - InvalidatePaint(); - } - } -} - -void TextRenderObject::GetCaretBrush( - std::shared_ptr brush) { - Expects(brush); - brush.swap(caret_brush_); - if (draw_caret_) { - InvalidatePaint(); - } -} - -void TextRenderObject::SetCaretWidth(const float width) { - Expects(width >= 0.0f); - - caret_width_ = width; - if (draw_caret_) { - InvalidatePaint(); - } -} - -void TextRenderObject::Draw(platform::graph::IPainter* painter) { - platform::graph::util::WithTransform( - painter, - platform::Matrix::Translation(GetMargin().left + GetPadding().left, - GetMargin().top + GetPadding().top), - [this](platform::graph::IPainter* p) { - if (this->selection_range_.has_value()) { - const auto&& rects = - text_layout_->TextRangeRect(this->selection_range_.value()); - for (const auto& rect : rects) - p->FillRectangle(rect, this->GetSelectionBrush().get()); - } - - p->DrawText(Point{}, text_layout_.get(), brush_.get()); - - if (this->draw_caret_ && this->caret_width_ != 0.0f) { - auto caret_pos = this->caret_position_; - gsl::index text_size = this->GetText().size(); - if (caret_pos < 0) { - caret_pos = 0; - } else if (caret_pos > text_size) { - caret_pos = text_size; - } - - const auto caret_top_center = - this->text_layout_->TextSinglePoint(caret_pos, false); - - const auto font_height = this->font_->GetFontSize(); - const auto caret_width = this->caret_width_; - - p->FillRectangle(Rect{caret_top_center.x - caret_width / 2.0f, - caret_top_center.y, caret_width, font_height}, - this->caret_brush_.get()); - } - }); -} - -RenderObject* TextRenderObject::HitTest(const Point& point) { - const auto padding_rect = GetPaddingRect(); - return padding_rect.IsPointInside(point) ? this : nullptr; -} - -Size TextRenderObject::OnMeasureContent(const Size& available_size) { - text_layout_->SetMaxWidth(available_size.width); - text_layout_->SetMaxHeight(available_size.height); - return text_layout_->GetTextBounds().GetSize(); -} - -void TextRenderObject::OnLayoutContent(const Rect& content_rect) { - CRU_UNUSED(content_rect) -} - -void TextRenderObject::OnAfterLayout() { - const auto&& size = GetContentRect().GetSize(); - text_layout_->SetMaxWidth(size.width); - text_layout_->SetMaxHeight(size.height); -} - -} // namespace cru::ui::render diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp deleted file mode 100644 index abed6fa4..00000000 --- a/src/ui/render/window_render_object.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "cru/ui/render/window_render_object.hpp" - -#include "../helper.hpp" -#include "cru/platform/graph/util/painter.hpp" -#include "cru/ui/ui_host.hpp" - -namespace cru::ui::render { -WindowRenderObject::WindowRenderObject(UiHost* host) { - SetChildMode(ChildMode::Single); - ui_host_ = host; - after_layout_event_guard_.Reset(host->AfterLayoutEvent()->AddHandler( - [this](auto) { NotifyAfterLayoutRecursive(this); })); -} - -void WindowRenderObject::Draw(platform::graph::IPainter* painter) { - painter->Clear(colors::white); - if (const auto child = GetChild()) { - auto offset = child->GetOffset(); - platform::graph::util::WithTransform( - painter, platform::Matrix::Translation(offset.x, offset.y), - [child](platform::graph::IPainter* p) { child->Draw(p); }); - } -} - -RenderObject* WindowRenderObject::HitTest(const Point& point) { - if (const auto child = GetChild()) { - auto offset = child->GetOffset(); - Point p{point.x - offset.x, point.y - offset.y}; - const auto result = child->HitTest(p); - if (result != nullptr) { - return result; - } - } - return Rect{Point{}, GetSize()}.IsPointInside(point) ? this : nullptr; -} - -Size WindowRenderObject::OnMeasureContent(const Size& available_size) { - if (const auto child = GetChild()) child->Measure(available_size); - return available_size; -} - -void WindowRenderObject::OnLayoutContent(const Rect& content_rect) { - if (const auto child = GetChild()) child->Layout(content_rect); -} -} // namespace cru::ui::render -- cgit v1.2.3