diff options
| author | Yuqian Yang <crupest@crupest.life> | 2025-12-11 23:51:58 +0800 |
|---|---|---|
| committer | Yuqian Yang <crupest@crupest.life> | 2025-12-11 23:51:58 +0800 |
| commit | c0c0106cf47fdff397756913b8646541f3bb9928 (patch) | |
| tree | 945d0a3662ac92577c966f49578e46ba743ea84b | |
| parent | e833e810aaa324d7de1630c2fb0528564b182742 (diff) | |
| download | cru-c0c0106cf47fdff397756913b8646541f3bb9928.tar.gz cru-c0c0106cf47fdff397756913b8646541f3bb9928.tar.bz2 cru-c0c0106cf47fdff397756913b8646541f3bb9928.zip | |
Add paint invalid area.
| -rw-r--r-- | include/cru/platform/GraphicsBase.h | 70 | ||||
| -rw-r--r-- | include/cru/ui/controls/ControlHost.h | 7 | ||||
| -rw-r--r-- | include/cru/ui/render/BorderRenderObject.h | 2 | ||||
| -rw-r--r-- | include/cru/ui/render/CanvasRenderObject.h | 3 | ||||
| -rw-r--r-- | include/cru/ui/render/GeometryRenderObject.h | 2 | ||||
| -rw-r--r-- | include/cru/ui/render/LayoutRenderObject.h | 20 | ||||
| -rw-r--r-- | include/cru/ui/render/RenderObject.h | 42 | ||||
| -rw-r--r-- | include/cru/ui/render/ScrollRenderObject.h | 3 | ||||
| -rw-r--r-- | include/cru/ui/render/TextRenderObject.h | 3 | ||||
| -rw-r--r-- | include/cru/ui/render/TreeRenderObject.h | 3 | ||||
| -rw-r--r-- | src/platform/gui/xcb/Window.cpp | 2 | ||||
| -rw-r--r-- | src/ui/controls/ControlHost.cpp | 35 | ||||
| -rw-r--r-- | src/ui/render/BorderRenderObject.cpp | 47 | ||||
| -rw-r--r-- | src/ui/render/CanvasRenderObject.cpp | 12 | ||||
| -rw-r--r-- | src/ui/render/GeometryRenderObject.cpp | 27 | ||||
| -rw-r--r-- | src/ui/render/RenderObject.cpp | 40 | ||||
| -rw-r--r-- | src/ui/render/ScrollRenderObject.cpp | 23 | ||||
| -rw-r--r-- | src/ui/render/TextRenderObject.cpp | 42 | ||||
| -rw-r--r-- | src/ui/render/TreeRenderObject.cpp | 18 |
19 files changed, 207 insertions, 194 deletions
diff --git a/include/cru/platform/GraphicsBase.h b/include/cru/platform/GraphicsBase.h index 81c44cb6..14c5d926 100644 --- a/include/cru/platform/GraphicsBase.h +++ b/include/cru/platform/GraphicsBase.h @@ -152,37 +152,38 @@ struct Rect final { constexpr static Rect FromVertices(const float left, const float top, const float right, const float bottom) { - return Rect(left, top, right - left, bottom - top); + return {left, top, right - left, bottom - top}; } constexpr static Rect FromVertices(const Point& lefttop, const Point& rightbottom) { - return Rect(lefttop.x, lefttop.y, rightbottom.x - lefttop.x, - rightbottom.y - lefttop.y); + return {lefttop.x, lefttop.y, rightbottom.x - lefttop.x, + rightbottom.y - lefttop.y}; } constexpr static Rect FromCenter(const Point& center, const float width, const float height) { - return Rect(center.x - width / 2.0f, center.y - height / 2.0f, width, - height); + return {center.x - width / 2.0f, center.y - height / 2.0f, width, height}; } + constexpr bool HasNoSize() const { return width == 0.f || height == 0.f; } + constexpr float GetRight() const { return left + width; } constexpr float GetBottom() const { return top + height; } - constexpr Point GetLeftTop() const { return Point(left, top); } + constexpr Point GetLeftTop() const { return {left, top}; } constexpr Point GetRightBottom() const { - return Point(left + width, top + height); + return {left + width, top + height}; } - constexpr Point GetLeftBottom() const { return Point(left, top + height); } + constexpr Point GetLeftBottom() const { return {left, top + height}; } - constexpr Point GetRightTop() const { return Point(left + width, top); } + constexpr Point GetRightTop() const { return {left + width, top}; } constexpr Point GetCenter() const { - return Point(left + width / 2.0f, top + height / 2.0f); + return {left + width / 2.0f, top + height / 2.0f}; } constexpr void SetSize(const Size& size) { @@ -192,28 +193,21 @@ struct Rect final { constexpr Size GetSize() const { return Size(width, height); } + constexpr Rect WithOffset(const Point& offset) const { + return {left + offset.x, top + offset.y, width, height}; + } + constexpr Rect Expand(const Thickness& thickness) const { - return Rect(left - thickness.left, top - thickness.top, - width + thickness.GetHorizontalTotal(), - height + thickness.GetVerticalTotal()); + return {left - thickness.left, top - thickness.top, + width + thickness.GetHorizontalTotal(), + height + thickness.GetVerticalTotal()}; } constexpr Rect Shrink(const Thickness& thickness, bool normalize = false) const { - Rect result(left + thickness.left, top + thickness.top, - width - thickness.GetHorizontalTotal(), - height - thickness.GetVerticalTotal()); - - if (normalize) { - if (result.width < 0) { - result.width = 0; - } - if (result.height < 0) { - result.height = 0; - } - } - - return result; + return {left + thickness.left, top + thickness.top, + width - thickness.GetHorizontalTotal(), + height - thickness.GetVerticalTotal()}; } constexpr Rect Scale(float scale) const { @@ -238,6 +232,28 @@ struct Rect final { return result; } + constexpr bool IsIntersect(const Rect& other) const { + if (HasNoSize() || other.HasNoSize()) { + return false; + } + + if (left >= other.GetRight() || GetRight() <= other.left) { + return false; + } + + if (top >= other.GetBottom() || GetBottom() <= other.top) { + return false; + } + + return true; + } + + constexpr Rect Union(const Rect& other) const { + return FromVertices(std::min(left, other.left), std::min(top, other.top), + std::max(GetRight(), other.GetRight()), + std::max(GetBottom(), other.GetBottom())); + } + std::string ToString() const { return std::format("Rect(left: {}, top: {}, width: {}, height: {})", left, top, width, height); diff --git a/include/cru/ui/controls/ControlHost.h b/include/cru/ui/controls/ControlHost.h index 5d0b7947..e6eb472d 100644 --- a/include/cru/ui/controls/ControlHost.h +++ b/include/cru/ui/controls/ControlHost.h @@ -23,6 +23,9 @@ class CRU_UI_API ControlHost : public Object { void ScheduleRepaint(); void ScheduleRelayout(); + Rect GetPaintInvalidArea(); + void AddPaintInvalidArea(const Rect& area); + void Repaint(); void Relayout(); void RelayoutWithSize(const Size& available_size = Size::Infinite(), @@ -56,7 +59,7 @@ class CRU_UI_API ControlHost : public Object { std::unique_ptr<platform::gui::INativeWindow> CreateNativeWindow(); void OnNativeDestroy(std::nullptr_t); - void OnNativePaint(std::nullptr_t); + void OnNativePaint1(const cru::platform::gui::NativePaintEventArgs& args); void OnNativeResize(const Size& size); void OnNativeFocus(cru::platform::gui::FocusChangeType focus); void OnNativeMouseEnterLeave(cru::platform::gui::MouseEnterLeaveType enter); @@ -190,6 +193,8 @@ class CRU_UI_API ControlHost : public Object { bool layout_prefer_to_fill_window_; + Rect paint_invalid_area_; + platform::gui::TimerAutoCanceler relayout_schedule_canceler_; }; } // namespace cru::ui::controls diff --git a/include/cru/ui/render/BorderRenderObject.h b/include/cru/ui/render/BorderRenderObject.h index 73fc85c1..c011cda0 100644 --- a/include/cru/ui/render/BorderRenderObject.h +++ b/include/cru/ui/render/BorderRenderObject.h @@ -43,7 +43,6 @@ class CRU_UI_API BorderRenderObject : public SingleChildRenderObject { void ApplyBorderStyle(const style::ApplyBorderStyleInfo& style); RenderObject* HitTest(const Point& point) override; - void Draw(platform::graphics::IPainter* painter) override; Thickness GetTotalSpaceThickness() override; Thickness GetInnerSpaceThickness() override; @@ -53,6 +52,7 @@ class CRU_UI_API BorderRenderObject : public SingleChildRenderObject { protected: Size OnMeasureContent(const MeasureRequirement& requirement) override; void OnLayoutContent(const Rect& content_rect) override; + void OnDraw(RenderObjectDrawContext& context) override; void OnResize(const Size& new_size) override; diff --git a/include/cru/ui/render/CanvasRenderObject.h b/include/cru/ui/render/CanvasRenderObject.h index 2e44fcc2..b2da3207 100644 --- a/include/cru/ui/render/CanvasRenderObject.h +++ b/include/cru/ui/render/CanvasRenderObject.h @@ -33,12 +33,11 @@ class CRU_UI_API CanvasRenderObject : public RenderObject { public: RenderObject* HitTest(const Point& point) override; - void Draw(platform::graphics::IPainter* painter) override; - CRU_DEFINE_EVENT(Paint, const CanvasPaintEventArgs&) protected: Size OnMeasureContent(const MeasureRequirement& requirement) override; void OnLayoutContent(const Rect& content_rect) override; + void OnDraw(RenderObjectDrawContext& context) override; }; } // namespace cru::ui::render diff --git a/include/cru/ui/render/GeometryRenderObject.h b/include/cru/ui/render/GeometryRenderObject.h index 1994daf3..455af6b8 100644 --- a/include/cru/ui/render/GeometryRenderObject.h +++ b/include/cru/ui/render/GeometryRenderObject.h @@ -15,7 +15,6 @@ class GeometryRenderObject : public RenderObject { GeometryRenderObject(); public: - void Draw(platform::graphics::IPainter* painter) override; RenderObject* HitTest(const Point& point) override; std::shared_ptr<platform::graphics::IGeometry> GetGeometry(); @@ -37,6 +36,7 @@ class GeometryRenderObject : public RenderObject { protected: Size OnMeasureContent(const MeasureRequirement& requirement) override; void OnLayoutContent(const Rect& content_rect) override; + void OnDraw(RenderObjectDrawContext& context) override; private: std::shared_ptr<platform::graphics::IGeometry> geometry_ = nullptr; diff --git a/include/cru/ui/render/LayoutRenderObject.h b/include/cru/ui/render/LayoutRenderObject.h index 5cb70680..99dd8057 100644 --- a/include/cru/ui/render/LayoutRenderObject.h +++ b/include/cru/ui/render/LayoutRenderObject.h @@ -1,8 +1,6 @@ #pragma once #include "RenderObject.h" -#include "cru/platform/graphics/Painter.h" - namespace cru::ui::render { template <typename TChildLayoutData> class LayoutRenderObject : public RenderObject { @@ -81,16 +79,6 @@ class LayoutRenderObject : public RenderObject { InvalidateLayout(); } - void Draw(platform::graphics::IPainter* painter) override { - for (const auto& child : children_) { - painter->PushState(); - painter->ConcatTransform( - Matrix::Translation(child.render_object->GetOffset())); - child.render_object->Draw(painter); - painter->PopState(); - } - } - RenderObject* HitTest(const Point& point) override { const auto child_count = GetChildCount(); for (auto i = child_count - 1; i >= 0; --i) { @@ -104,6 +92,14 @@ class LayoutRenderObject : public RenderObject { return GetPaddingRect().IsPointInside(point) ? this : nullptr; } + protected: + void OnDraw(RenderObjectDrawContext& context) override { + auto painter = context.painter; + for (const auto& child : children_) { + context.DrawChild(child.render_object); + } + } + private: std::vector<ChildData> children_; }; diff --git a/include/cru/ui/render/RenderObject.h b/include/cru/ui/render/RenderObject.h index e696aefe..85cb7990 100644 --- a/include/cru/ui/render/RenderObject.h +++ b/include/cru/ui/render/RenderObject.h @@ -1,33 +1,24 @@ #pragma once #include "../Base.h" - #include "MeasureRequirement.h" #include <cru/base/Event.h> #include <cru/platform/graphics/Painter.h> + #include <string> namespace cru::ui::render { -struct BoxConstraint { - static const BoxConstraint kNotLimit; - - Size max; - Size min; +class RenderObject; - constexpr bool Validate() const { - return max.width >= min.width && max.height >= min.height && - min.width >= 0 && min.height >= 0; - } - - constexpr bool Satisfy(const Size& size) const { - return size.width <= max.width && size.height <= max.height && - size.width >= min.width && size.height >= min.height; - } +struct CRU_UI_API RenderObjectDrawContext { + /** + * Coordinates are related to the render object to be painted. So parent + * render object should adjust this before passing to children. + */ + Rect paint_invalid_area; + platform::graphics::IPainter* painter; - constexpr Size Coerce(const Size& size) const { - return Size{std::min(std::max(size.width, min.width), max.width), - std::min(std::max(size.height, min.height), max.height)}; - } + void DrawChild(RenderObject* render_object); }; /** @@ -37,11 +28,11 @@ struct BoxConstraint { * 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; + * void OnDraw(RenderObjectDrawContext& context) override; */ class CRU_UI_API RenderObject : public Object { private: @@ -124,10 +115,12 @@ class CRU_UI_API RenderObject : public Object { virtual Rect GetPaddingRect(); virtual Rect GetContentRect(); - virtual void Draw(platform::graphics::IPainter* painter) = 0; + virtual Rect GetRenderRect(); - // Param point must be relative the lefttop of render object including margin. - // Add offset before pass point to children. + void Draw(RenderObjectDrawContext& context); + + // 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; public: @@ -160,13 +153,14 @@ class CRU_UI_API RenderObject : public Object { // 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) = 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 OnDraw(RenderObjectDrawContext& context) = 0; + virtual void OnAttachedControlChanged(controls::Control* old_control, controls::Control* new_control) {} diff --git a/include/cru/ui/render/ScrollRenderObject.h b/include/cru/ui/render/ScrollRenderObject.h index ae11f361..ed67331d 100644 --- a/include/cru/ui/render/ScrollRenderObject.h +++ b/include/cru/ui/render/ScrollRenderObject.h @@ -62,8 +62,6 @@ class CRU_UI_API ScrollRenderObject : public SingleChildRenderObject { bool VerticalCanScrollUp(); bool VerticalCanScrollDown(); - void Draw(platform::graphics::IPainter* painter) override; - protected: // Logic: // If available size is bigger than child's preferred size, then child's @@ -71,6 +69,7 @@ class CRU_UI_API ScrollRenderObject : public SingleChildRenderObject { // If not, all available size is taken while forming a scroll area. Size OnMeasureContent(const MeasureRequirement& requirement) override; void OnLayoutContent(const Rect& content_rect) override; + void OnDraw(RenderObjectDrawContext& context) override; void OnAttachedControlChanged(controls::Control* old_control, controls::Control* new_control) override; diff --git a/include/cru/ui/render/TextRenderObject.h b/include/cru/ui/render/TextRenderObject.h index 0d17853b..eefb3264 100644 --- a/include/cru/ui/render/TextRenderObject.h +++ b/include/cru/ui/render/TextRenderObject.h @@ -88,12 +88,11 @@ class CRU_UI_API TextRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; - void Draw(platform::graphics::IPainter* painter) override; - protected: // See remarks of this class. Size OnMeasureContent(const MeasureRequirement& requirement) override; void OnLayoutContent(const Rect& content_rect) override; + void OnDraw(RenderObjectDrawContext& context) override; private: std::shared_ptr<platform::graphics::IBrush> brush_; diff --git a/include/cru/ui/render/TreeRenderObject.h b/include/cru/ui/render/TreeRenderObject.h index 599e0f65..425eee23 100644 --- a/include/cru/ui/render/TreeRenderObject.h +++ b/include/cru/ui/render/TreeRenderObject.h @@ -62,11 +62,10 @@ class CRU_UI_API TreeRenderObject : public RenderObject { RenderObject* HitTest(const Point& point) override; - void Draw(platform::graphics::IPainter* painter) override; - protected: Size OnMeasureContent(const MeasureRequirement& requirement) override; void OnLayoutContent(const Rect& content_rect) override; + void OnDraw(RenderObjectDrawContext& context) override; private: float tab_width_ = 12.f; diff --git a/src/platform/gui/xcb/Window.cpp b/src/platform/gui/xcb/Window.cpp index 06d82de9..c708973f 100644 --- a/src/platform/gui/xcb/Window.cpp +++ b/src/platform/gui/xcb/Window.cpp @@ -224,7 +224,7 @@ void XcbWindow::SetWindowRect(const Rect& rect) { auto frame_properties = Get_NET_FRAME_EXTENTS(*xcb_window_); if (frame_properties.has_value()) { - real_rect = real_rect.Shrink(*frame_properties, true); + real_rect = real_rect.Shrink(*frame_properties); } SetClientRect(real_rect); diff --git a/src/ui/controls/ControlHost.cpp b/src/ui/controls/ControlHost.cpp index f66d3293..290602d4 100644 --- a/src/ui/controls/ControlHost.cpp +++ b/src/ui/controls/ControlHost.cpp @@ -3,6 +3,7 @@ #include "cru/platform/gui/UiApplication.h" #include "cru/platform/gui/Window.h" #include "cru/ui/Base.h" +#include "cru/ui/render/RenderObject.h" #include <cassert> @@ -102,8 +103,8 @@ ControlHost::CreateNativeWindow() { BindNativeEvent(this, native_window, native_window->DestroyEvent(), &ControlHost::OnNativeDestroy); - BindNativeEvent(this, native_window, native_window->PaintEvent(), - &ControlHost::OnNativePaint); + BindNativeEvent(this, native_window, native_window->Paint1Event(), + &ControlHost::OnNativePaint1); BindNativeEvent(this, native_window, native_window->ResizeEvent(), &ControlHost::OnNativeResize); BindNativeEvent(this, native_window, native_window->FocusEvent(), @@ -134,21 +135,19 @@ void ControlHost::ScheduleRelayout() { [this] { Relayout(); })); } -bool ControlHost::IsLayoutPreferToFillWindow() const { - return layout_prefer_to_fill_window_; -} +Rect ControlHost::GetPaintInvalidArea() { return paint_invalid_area_; } -void ControlHost::SetLayoutPreferToFillWindow(bool value) { - if (value == layout_prefer_to_fill_window_) return; - layout_prefer_to_fill_window_ = value; - ScheduleRelayout(); +void ControlHost::AddPaintInvalidArea(const Rect& area) { + paint_invalid_area_ = paint_invalid_area_.Union(area); } void ControlHost::Repaint() { auto painter = native_window_->BeginPaint(); painter->Clear(colors::white); - root_control_->GetRenderObject()->Draw(painter.get()); + render::RenderObjectDrawContext context{paint_invalid_area_, painter.get()}; + root_control_->GetRenderObject()->Draw(context); painter->EndDraw(); + paint_invalid_area_ = {}; } void ControlHost::Relayout() { @@ -177,6 +176,16 @@ void ControlHost::RelayoutWithSize(const Size& available_size, ScheduleRepaint(); } +bool ControlHost::IsLayoutPreferToFillWindow() const { + return layout_prefer_to_fill_window_; +} + +void ControlHost::SetLayoutPreferToFillWindow(bool value) { + if (value == layout_prefer_to_fill_window_) return; + layout_prefer_to_fill_window_ = value; + ScheduleRelayout(); +} + Control* ControlHost::GetFocusControl() { return focus_control_; } void ControlHost::SetFocusControl(Control* control) { @@ -243,7 +252,11 @@ void ControlHost::OnNativeDestroy(std::nullptr_t) { mouse_captured_control_ = nullptr; } -void ControlHost::OnNativePaint(std::nullptr_t) { Repaint(); } +void ControlHost::OnNativePaint1( + const platform::gui::NativePaintEventArgs& args) { + AddPaintInvalidArea(args.repaint_area); + Repaint(); +} void ControlHost::OnNativeResize([[maybe_unused]] const Size& size) { ScheduleRelayout(); diff --git a/src/ui/render/BorderRenderObject.cpp b/src/ui/render/BorderRenderObject.cpp index 080bcc4f..1fc120f1 100644 --- a/src/ui/render/BorderRenderObject.cpp +++ b/src/ui/render/BorderRenderObject.cpp @@ -5,7 +5,6 @@ #include "cru/platform/graphics/Geometry.h" #include "cru/platform/graphics/Painter.h" #include "cru/platform/gui/UiApplication.h" -#include "cru/ui/DebugFlags.h" #include "cru/ui/render/RenderObject.h" #include <algorithm> @@ -80,15 +79,24 @@ RenderObject* BorderRenderObject::HitTest(const Point& point) { } } -void BorderRenderObject::Draw(platform::graphics::IPainter* painter) { - if constexpr (debug_flags::draw) { - CruLogDebug( - kLogTag, "BorderRenderObject draw, background: {}, foreground: {}.", - background_brush_ == nullptr ? "NONE" - : background_brush_->GetDebugString(), - foreground_brush_ == nullptr ? "NONE" - : foreground_brush_->GetDebugString()); +Size BorderRenderObject::OnMeasureContent( + const MeasureRequirement& requirement) { + if (auto child = GetChild()) { + child->Measure(requirement); + return child->GetMeasureResultSize(); + } else { + return requirement.suggest.GetSizeOr0(); + } +} + +void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { + if (auto child = GetChild()) { + child->Layout(content_rect.GetLeftTop()); } +} + +void BorderRenderObject::OnDraw(RenderObjectDrawContext& context) { + auto painter = context.painter; if (background_brush_ != nullptr) painter->FillGeometry(border_inner_geometry_.get(), @@ -103,10 +111,7 @@ void BorderRenderObject::Draw(platform::graphics::IPainter* painter) { } if (auto child = GetChild()) { - painter->PushState(); - painter->ConcatTransform(Matrix::Translation(child->GetOffset())); - child->Draw(painter); - painter->PopState(); + context.DrawChild(child); } if (foreground_brush_ != nullptr) @@ -114,22 +119,6 @@ void BorderRenderObject::Draw(platform::graphics::IPainter* painter) { foreground_brush_.get()); } -Size BorderRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { - if (auto child = GetChild()) { - child->Measure(requirement); - return child->GetMeasureResultSize(); - } else { - return requirement.suggest.GetSizeOr0(); - } -} - -void BorderRenderObject::OnLayoutContent(const Rect& content_rect) { - if (auto child = GetChild()) { - child->Layout(content_rect.GetLeftTop()); - } -} - void BorderRenderObject::OnResize(const Size& new_size) { RecreateGeometry(); } Thickness BorderRenderObject::GetTotalSpaceThickness() { diff --git a/src/ui/render/CanvasRenderObject.cpp b/src/ui/render/CanvasRenderObject.cpp index 0c5ca3ed..9c516477 100644 --- a/src/ui/render/CanvasRenderObject.cpp +++ b/src/ui/render/CanvasRenderObject.cpp @@ -9,12 +9,6 @@ RenderObject* CanvasRenderObject::HitTest(const Point& point) { return padding_rect.IsPointInside(point) ? this : nullptr; } -void CanvasRenderObject::Draw(platform::graphics::IPainter* painter) { - const auto rect = GetContentRect(); - CanvasPaintEventArgs args{painter, rect.GetSize()}; - PaintEvent_.Raise(args); -} - Size CanvasRenderObject::OnMeasureContent( const MeasureRequirement& requirement) { return requirement.Coerce(requirement.suggest.GetSizeOr({100, 100})); @@ -23,4 +17,10 @@ Size CanvasRenderObject::OnMeasureContent( void CanvasRenderObject::OnLayoutContent(const Rect& content_rect) { CRU_UNUSED(content_rect) } + +void CanvasRenderObject::OnDraw(RenderObjectDrawContext& context) { + const auto rect = GetContentRect(); + CanvasPaintEventArgs args{context.painter, rect.GetSize()}; + PaintEvent_.Raise(args); +} } // namespace cru::ui::render diff --git a/src/ui/render/GeometryRenderObject.cpp b/src/ui/render/GeometryRenderObject.cpp index 84379b39..1ab0d8ed 100644 --- a/src/ui/render/GeometryRenderObject.cpp +++ b/src/ui/render/GeometryRenderObject.cpp @@ -60,9 +60,23 @@ void GeometryRenderObject::SetStrokeWidth(float width) { InvalidatePaint(); } -void GeometryRenderObject::Draw(platform::graphics::IPainter* painter) { +RenderObject* GeometryRenderObject::HitTest(const Point& point) { + return GetPaddingRect().IsPointInside(point) ? this : nullptr; +} + +Size GeometryRenderObject::OnMeasureContent( + const MeasureRequirement& requirement) { + Size result = GetViewPort().GetSize(); + return requirement.ExpandToSuggestAndCoerce(result); +} + +void GeometryRenderObject::OnLayoutContent(const Rect& content_rect) {} + +void GeometryRenderObject::OnDraw(RenderObjectDrawContext& context) { if (!geometry_) return; + auto painter = context.painter; + painter->PushState(); auto content_rect = GetContentRect(); @@ -86,15 +100,4 @@ void GeometryRenderObject::Draw(platform::graphics::IPainter* painter) { painter->PopState(); } -RenderObject* GeometryRenderObject::HitTest(const Point& point) { - return GetPaddingRect().IsPointInside(point) ? this : nullptr; -} - -Size GeometryRenderObject::OnMeasureContent( - const MeasureRequirement& requirement) { - Size result = GetViewPort().GetSize(); - return requirement.ExpandToSuggestAndCoerce(result); -} - -void GeometryRenderObject::OnLayoutContent(const Rect& content_rect) {} } // namespace cru::ui::render diff --git a/src/ui/render/RenderObject.cpp b/src/ui/render/RenderObject.cpp index f553ea6d..a84bd8c1 100644 --- a/src/ui/render/RenderObject.cpp +++ b/src/ui/render/RenderObject.cpp @@ -6,7 +6,17 @@ #include "cru/ui/controls/ControlHost.h" namespace cru::ui::render { -const BoxConstraint BoxConstraint::kNotLimit{Size::kMax, Size::kZero}; +void RenderObjectDrawContext::DrawChild(RenderObject* render_object) { + auto offset = render_object->GetOffset(); + paint_invalid_area.left -= offset.x; + paint_invalid_area.top -= offset.y; + painter->PushState(); + painter->ConcatTransform(Matrix::Translation(offset)); + render_object->Draw(*this); + painter->PopState(); + paint_invalid_area.left += offset.x; + paint_invalid_area.top += offset.y; +} RenderObject::RenderObject(std::string name) : name_(std::move(name)), @@ -139,28 +149,35 @@ void RenderObject::OnLayoutCore(const Rect& rect) { } Rect RenderObject::GetPaddingRect() { - const auto size = GetMeasureResultSize(); - Rect rect{Point{}, size}; + Rect rect{Point{}, size_}; rect = rect.Shrink(GetMargin()); - rect.left = std::min(rect.left, size.width); - rect.top = std::min(rect.top, size.height); + rect.left = std::min(rect.left, size_.width); + rect.top = std::min(rect.top, size_.height); rect.width = std::max(rect.width, 0.0f); rect.height = std::max(rect.height, 0.0f); return rect; } Rect RenderObject::GetContentRect() { - const auto size = GetMeasureResultSize(); - Rect rect{Point{}, size}; + Rect rect{Point{}, size_}; rect = rect.Shrink(GetMargin()); rect = rect.Shrink(GetPadding()); - rect.left = std::min(rect.left, size.width); - rect.top = std::min(rect.top, size.height); + rect.left = std::min(rect.left, size_.width); + rect.top = std::min(rect.top, size_.height); rect.width = std::max(rect.width, 0.0f); rect.height = std::max(rect.height, 0.0f); return rect; } +Rect RenderObject::GetRenderRect() { return GetContentRect(); } + +void RenderObject::Draw(RenderObjectDrawContext& context) { + if (!context.paint_invalid_area.IsIntersect(GetRenderRect())) { + return; + } + OnDraw(context); +} + controls::ControlHost* RenderObject::GetControlHost() { if (control_) { return control_->GetControlHost(); @@ -176,8 +193,9 @@ void RenderObject::InvalidateLayout() { } void RenderObject::InvalidatePaint() { - if (auto window = GetControlHost()) { - window->ScheduleRepaint(); + if (auto host = GetControlHost()) { + host->AddPaintInvalidArea(GetRenderRect().WithOffset(GetTotalOffset())); + host->ScheduleRepaint(); } } diff --git a/src/ui/render/ScrollRenderObject.cpp b/src/ui/render/ScrollRenderObject.cpp index 4d359a86..1d12c7d0 100644 --- a/src/ui/render/ScrollRenderObject.cpp +++ b/src/ui/render/ScrollRenderObject.cpp @@ -82,19 +82,6 @@ RenderObject* ScrollRenderObject::HitTest(const Point& point) { return rect.IsPointInside(point) ? this : nullptr; } // namespace cru::ui::render -void ScrollRenderObject::Draw(platform::graphics::IPainter* painter) { - if (auto child = GetChild()) { - painter->PushLayer(this->GetContentRect()); - const auto offset = child->GetOffset(); - painter->PushState(); - painter->ConcatTransform(Matrix::Translation(offset)); - child->Draw(painter); - painter->PopState(); - painter->PopLayer(); - } - scroll_bar_delegate_->DrawScrollBar(painter); -} - Point ScrollRenderObject::GetScrollOffset() { if (auto child = GetChild()) { return CoerceScroll(scroll_offset_, GetContentRect().GetSize(), @@ -184,6 +171,16 @@ void ScrollRenderObject::OnLayoutContent(const Rect& content_rect) { } } +void ScrollRenderObject::OnDraw(RenderObjectDrawContext& context) { + auto painter = context.painter; + if (auto child = GetChild()) { + painter->PushLayer(this->GetContentRect()); + context.DrawChild(child); + painter->PopLayer(); + } + scroll_bar_delegate_->DrawScrollBar(painter); +} + void ScrollRenderObject::OnAttachedControlChanged( controls::Control* old_control, controls::Control* new_control) { if (new_control) { diff --git a/src/ui/render/TextRenderObject.cpp b/src/ui/render/TextRenderObject.cpp index 12d77d0d..032adb0f 100644 --- a/src/ui/render/TextRenderObject.cpp +++ b/src/ui/render/TextRenderObject.cpp @@ -1,11 +1,8 @@ #include "cru/ui/render/TextRenderObject.h" - -#include "cru/base/log/Logger.h" #include "cru/platform/graphics/Factory.h" #include "cru/platform/graphics/Painter.h" #include "cru/platform/graphics/TextLayout.h" #include "cru/platform/gui/UiApplication.h" -#include "cru/ui/DebugFlags.h" #include "cru/ui/render/RenderObject.h" #include <limits> @@ -176,29 +173,6 @@ RenderObject* TextRenderObject::HitTest(const Point& point) { return padding_rect.IsPointInside(point) ? this : nullptr; } -void TextRenderObject::Draw(platform::graphics::IPainter* painter) { - if constexpr (debug_flags::draw) { - CruLogDebug(kLogTag, - "Begin to paint, total_offset: {}, size: {}, text_layout: " - "{}, brush: {}.", - this->GetTotalOffset(), this->GetMeasureResultSize(), - this->text_layout_->GetDebugString(), - this->brush_->GetDebugString()); - } - - if (this->selection_range_.has_value()) { - const auto&& rects = text_layout_->TextRangeRect(*this->selection_range_); - for (const auto& rect : rects) - painter->FillRectangle(rect, this->GetSelectionBrush().get()); - } - - painter->DrawText(Point{}, text_layout_.get(), brush_.get()); - - if (this->draw_caret_ && this->caret_width_ != 0.0f) { - painter->FillRectangle(GetCaretRectInContent(), this->caret_brush_.get()); - } -} - Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement) { float measure_width = requirement.suggest.width.GetLengthOr( requirement.max.width.GetLengthOrMaxFloat()); @@ -215,4 +189,20 @@ Size TextRenderObject::OnMeasureContent(const MeasureRequirement& requirement) { void TextRenderObject::OnLayoutContent(const Rect& content_rect) { CRU_UNUSED(content_rect) } + +void TextRenderObject::OnDraw(RenderObjectDrawContext& context) { + auto painter = context.painter; + + if (this->selection_range_.has_value()) { + const auto&& rects = text_layout_->TextRangeRect(*this->selection_range_); + for (const auto& rect : rects) + painter->FillRectangle(rect, this->GetSelectionBrush().get()); + } + + painter->DrawText(Point{}, text_layout_.get(), brush_.get()); + + if (this->draw_caret_ && this->caret_width_ != 0.0f) { + painter->FillRectangle(GetCaretRectInContent(), this->caret_brush_.get()); + } +} } // namespace cru::ui::render diff --git a/src/ui/render/TreeRenderObject.cpp b/src/ui/render/TreeRenderObject.cpp index 758108f6..7df3d5b6 100644 --- a/src/ui/render/TreeRenderObject.cpp +++ b/src/ui/render/TreeRenderObject.cpp @@ -1,5 +1,4 @@ #include "cru/ui/render/TreeRenderObject.h" -#include "cru/platform/graphics/Painter.h" #include "cru/ui/render/MeasureRequirement.h" #include "cru/ui/render/RenderObject.h" @@ -78,24 +77,17 @@ RenderObject* TreeRenderObject::HitTest(const Point& point) { } void TreeRenderObjectItemDraw(TreeRenderObjectItem* item, - platform::graphics::IPainter* painter) { + RenderObjectDrawContext& context) { auto render_object = item->GetRenderObject(); if (render_object) { - painter->PushState(); - painter->ConcatTransform(Matrix::Translation(render_object->GetOffset())); - render_object->Draw(painter); - painter->PopState(); + context.DrawChild(render_object); } for (auto child : item->GetChildren()) { - TreeRenderObjectItemDraw(child, painter); + TreeRenderObjectItemDraw(child, context); } } -void TreeRenderObject::Draw(platform::graphics::IPainter* painter) { - TreeRenderObjectItemDraw(root_item_, painter); -} - static Size MeasureTreeRenderObjectItem(MeasureSize max_size, TreeRenderObjectItem* item, float tab_width) { @@ -162,4 +154,8 @@ static void LayoutTreeRenderObjectItem(Rect rect, TreeRenderObjectItem* item, void TreeRenderObject::OnLayoutContent(const Rect& content_rect) { LayoutTreeRenderObjectItem(content_rect, root_item_, tab_width_); } + +void TreeRenderObject::OnDraw(RenderObjectDrawContext& context) { + TreeRenderObjectItemDraw(root_item_, context); +} } // namespace cru::ui::render |
