aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuqian Yang <crupest@crupest.life>2025-12-11 23:51:58 +0800
committerYuqian Yang <crupest@crupest.life>2025-12-11 23:51:58 +0800
commitc0c0106cf47fdff397756913b8646541f3bb9928 (patch)
tree945d0a3662ac92577c966f49578e46ba743ea84b
parente833e810aaa324d7de1630c2fb0528564b182742 (diff)
downloadcru-c0c0106cf47fdff397756913b8646541f3bb9928.tar.gz
cru-c0c0106cf47fdff397756913b8646541f3bb9928.tar.bz2
cru-c0c0106cf47fdff397756913b8646541f3bb9928.zip
Add paint invalid area.
-rw-r--r--include/cru/platform/GraphicsBase.h70
-rw-r--r--include/cru/ui/controls/ControlHost.h7
-rw-r--r--include/cru/ui/render/BorderRenderObject.h2
-rw-r--r--include/cru/ui/render/CanvasRenderObject.h3
-rw-r--r--include/cru/ui/render/GeometryRenderObject.h2
-rw-r--r--include/cru/ui/render/LayoutRenderObject.h20
-rw-r--r--include/cru/ui/render/RenderObject.h42
-rw-r--r--include/cru/ui/render/ScrollRenderObject.h3
-rw-r--r--include/cru/ui/render/TextRenderObject.h3
-rw-r--r--include/cru/ui/render/TreeRenderObject.h3
-rw-r--r--src/platform/gui/xcb/Window.cpp2
-rw-r--r--src/ui/controls/ControlHost.cpp35
-rw-r--r--src/ui/render/BorderRenderObject.cpp47
-rw-r--r--src/ui/render/CanvasRenderObject.cpp12
-rw-r--r--src/ui/render/GeometryRenderObject.cpp27
-rw-r--r--src/ui/render/RenderObject.cpp40
-rw-r--r--src/ui/render/ScrollRenderObject.cpp23
-rw-r--r--src/ui/render/TextRenderObject.cpp42
-rw-r--r--src/ui/render/TreeRenderObject.cpp18
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