diff options
-rw-r--r-- | include/cru/ui/render/border_render_object.hpp | 59 | ||||
-rw-r--r-- | include/cru/ui/render/render_object.hpp | 6 | ||||
-rw-r--r-- | include/cru/ui/render/window_render_object.hpp | 4 | ||||
-rw-r--r-- | include/cru/ui/window.hpp | 5 | ||||
-rw-r--r-- | src/ui/render/window_render_object.cpp | 47 | ||||
-rw-r--r-- | src/ui/window.cpp | 26 |
6 files changed, 101 insertions, 46 deletions
diff --git a/include/cru/ui/render/border_render_object.hpp b/include/cru/ui/render/border_render_object.hpp index 22e8ab58..18809f91 100644 --- a/include/cru/ui/render/border_render_object.hpp +++ b/include/cru/ui/render/border_render_object.hpp @@ -32,11 +32,16 @@ struct CornerRadius { Point right_bottom; }; -struct BorderStyle { - std::shared_ptr<platform::graph::Brush> brush{}; - Thickness thickness{}; - CornerRadius corner_radius{}; -}; +inline bool operator==(const CornerRadius& left, const CornerRadius& right) { + return left.left_top == right.left_top && + left.left_bottom == right.left_bottom && + left.right_top == right.right_top && + left.right_bottom == right.right_bottom; +} + +inline bool operator!=(const CornerRadius& left, const CornerRadius& right) { + return !(left == right); +} class BorderRenderObject : public RenderObject { public: @@ -50,8 +55,30 @@ class BorderRenderObject : public RenderObject { bool IsBorderEnabled() const { return is_border_enabled_; } void SetBorderEnabled(bool enabled) { is_border_enabled_ = enabled; } - BorderStyle* GetBorderStyle() { - return &border_style_; + std::shared_ptr<platform::graph::Brush> GetBorderBrush() { + return border_brush_; + } + + void SetBorderBrush(std::shared_ptr<platform::graph::Brush> brush) { + if (brush == border_brush_) return; + border_brush_ = std::move(brush); + InvalidatePaint(); + } + + platform::Thickness GetBorderThickness() { return border_thickness_; } + + void SetBorderThickness(const platform::Thickness thickness) { + if (thickness == border_thickness_) return; + border_thickness_ = thickness; + InvalidateLayout(); + } + + CornerRadius GetBorderRadius() { return border_radius_; } + + void SetBorderRadius(const CornerRadius radius) { + if (radius == border_radius_) return; + border_radius_ = radius; + RecreateGeometry(); } std::shared_ptr<platform::graph::Brush> GetForegroundBrush() { @@ -59,19 +86,21 @@ class BorderRenderObject : public RenderObject { } void SetForegroundBrush(std::shared_ptr<platform::graph::Brush> brush) { + if (brush == foreground_brush_) return; foreground_brush_ = std::move(brush); + InvalidatePaint(); } std::shared_ptr<platform::graph::Brush> GetBackgroundBrush() { - return foreground_brush_; + return background_brush_; } void SetBackgroundBrush(std::shared_ptr<platform::graph::Brush> brush) { - foreground_brush_ = std::move(brush); + if (brush == background_brush_) return; + background_brush_ = std::move(brush); + InvalidatePaint(); } - void Refresh() { RecreateGeometry(); } - void Draw(platform::graph::Painter* painter) override; RenderObject* HitTest(const Point& point) override; @@ -93,14 +122,18 @@ class BorderRenderObject : public RenderObject { private: bool is_border_enabled_ = false; - BorderStyle border_style_; + + std::shared_ptr<platform::graph::Brush> border_brush_; + platform::Thickness border_thickness_; + CornerRadius border_radius_; std::shared_ptr<platform::graph::Brush> foreground_brush_; std::shared_ptr<platform::graph::Brush> background_brush_; // The ring. Used for painting. std::unique_ptr<platform::graph::Geometry> geometry_; - // Area including inner area of the border. Used for painting foreground and background. + // Area including inner area of the border. Used for painting foreground and + // background. std::unique_ptr<platform::graph::Geometry> border_inner_geometry_; // Area including border ring and inner area. Used for hit test. std::unique_ptr<platform::graph::Geometry> border_outer_geometry_; diff --git a/include/cru/ui/render/render_object.hpp b/include/cru/ui/render/render_object.hpp index 8ea7ca79..2394bf97 100644 --- a/include/cru/ui/render/render_object.hpp +++ b/include/cru/ui/render/render_object.hpp @@ -2,6 +2,7 @@ #include "cru/common/base.hpp" #include "../base.hpp" +#include "cru/common/event.hpp" #include <vector> @@ -15,6 +16,9 @@ class Painter; } namespace cru::ui::render { + +struct AfterLayoutEventArgs {}; + struct IRenderHost : Interface { // Mark the layout as invalid, and arrange a re-layout later. // Note this method might be called more than one times in a message cycle. So @@ -25,6 +29,8 @@ struct IRenderHost : Interface { // Note this method might be called more than one times in a message cycle. So // implementation should merge multiple request into once. virtual void InvalidatePaint() = 0; + + virtual IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() = 0; }; class RenderObject : public Object { diff --git a/include/cru/ui/render/window_render_object.hpp b/include/cru/ui/render/window_render_object.hpp index e73a6bfe..431f7025 100644 --- a/include/cru/ui/render/window_render_object.hpp +++ b/include/cru/ui/render/window_render_object.hpp @@ -17,7 +17,9 @@ class WindowRenderObject : public RenderObject { WindowRenderObject& operator=(WindowRenderObject&& other) = delete; ~WindowRenderObject() override = default; - void MeasureAndLayout(); + Window* GetWindow() const { return window_; } + + void Relayout(); void Draw(platform::graph::Painter* painter) override; diff --git a/include/cru/ui/window.hpp b/include/cru/ui/window.hpp index 1325100f..f4cdefaa 100644 --- a/include/cru/ui/window.hpp +++ b/include/cru/ui/window.hpp @@ -53,11 +53,6 @@ class Window final : public ContentControl, public SelfResolvable<Window> { // get more info. Control* GetMouseHoverControl() const { return mouse_hover_control_; } - //*************** region: layout *************** - void Relayout(); - - void InvalidateLayout(); - //*************** region: focus *************** // Request focus for specified control. diff --git a/src/ui/render/window_render_object.cpp b/src/ui/render/window_render_object.cpp index e61dd286..e2e96897 100644 --- a/src/ui/render/window_render_object.cpp +++ b/src/ui/render/window_render_object.cpp @@ -1,33 +1,64 @@ #include "cru/ui/render/window_render_object.hpp" +#include "cru/common/logger.hpp" #include "cru/platform/graph/util/painter_util.hpp" #include "cru/platform/native/native_window.hpp" +#include "cru/platform/native/ui_application.hpp" #include "cru/ui/window.hpp" #include <cassert> namespace cru::ui::render { -class WindowRenderHost : public IRenderHost { +class WindowRenderHost : public IRenderHost, + public SelfResolvable<WindowRenderHost> { public: - WindowRenderHost(Window* window) : window_(window) { - assert(window != nullptr); + WindowRenderHost(WindowRenderObject* render_object) + : render_object_(render_object) { + assert(render_object != nullptr); } - void InvalidateLayout() override { window_->InvalidateLayout(); } + void InvalidateLayout() override; - void InvalidatePaint() override { window_->GetNativeWindow()->Repaint(); } + void InvalidatePaint() override { + render_object_->GetWindow()->GetNativeWindow()->Repaint(); + } + + IEvent<AfterLayoutEventArgs>* AfterLayoutEvent() override { + return &after_layout_event_; + } private: - Window* window_; + WindowRenderObject* render_object_; + + bool need_layout_ = false; + + Event<AfterLayoutEventArgs> after_layout_event_; }; +void WindowRenderHost::InvalidateLayout() { + if (!need_layout_) { + log::Debug(L"A relayout is required."); + platform::native::UiApplication::GetInstance()->InvokeLater( + [resolver = this->CreateResolver()] { + if (const auto host = resolver.Resolve()) { + host->render_object_->Relayout(); + host->need_layout_ = false; + host->after_layout_event_.Raise(AfterLayoutEventArgs{}); + log::Debug(L"A relayout finished."); + host->InvalidatePaint(); + } + }); + need_layout_ = true; + } +} + WindowRenderObject::WindowRenderObject(Window* window) - : window_(window), render_host_(new WindowRenderHost(window)) { + : window_(window), render_host_(new WindowRenderHost(this)) { SetChildMode(ChildMode::Single); SetRenderHost(render_host_.get()); } -void WindowRenderObject::MeasureAndLayout() { +void WindowRenderObject::Relayout() { const auto client_size = window_->GetNativeWindow()->GetClientSize(); Measure(client_size); Layout(Rect{Point{}, client_size}); diff --git a/src/ui/window.cpp b/src/ui/window.cpp index 13784718..25dffe11 100644 --- a/src/ui/window.cpp +++ b/src/ui/window.cpp @@ -135,21 +135,6 @@ render::RenderObject* Window::GetRenderObject() const { return render_object_.get(); } -void Window::Relayout() { this->render_object_->MeasureAndLayout(); } - -void Window::InvalidateLayout() { - if (!need_layout_) { - platform::native::UiApplication::GetInstance()->InvokeLater( - [resolver = this->CreateResolver()] { - if (const auto window = resolver.Resolve()) { - window->Relayout(); - window->need_layout_ = false; - } - }); - need_layout_ = true; - } -} - bool Window::RequestFocusFor(Control* control) { assert(control != nullptr); // The control to request focus can't be null. // You can set it as the window. @@ -216,7 +201,7 @@ void Window::OnNativePaint(std::nullptr_t) { } void Window::OnNativeResize(const Size& size) { - render_object_->MeasureAndLayout(); + render_object_->GetRenderHost()->InvalidateLayout(); } void Window::OnNativeFocus(bool focus) { @@ -241,13 +226,16 @@ void Window::OnNativeMouseMove(const Point& point) { mouse_hover_control_ = new_mouse_hover_control; if (mouse_captured_control_) { - const auto n = FindLowestCommonAncestor(new_mouse_hover_control, mouse_captured_control_); - const auto o = FindLowestCommonAncestor(old_mouse_hover_control, mouse_captured_control_); + const auto n = FindLowestCommonAncestor(new_mouse_hover_control, + mouse_captured_control_); + const auto o = FindLowestCommonAncestor(old_mouse_hover_control, + mouse_captured_control_); bool a = IsAncestor(o, n); if (a) { DispatchEvent(event_names::MouseLeave, o, &Control::MouseLeaveEvent, n); } else { - DispatchEvent(event_names::MouseEnter, n, &Control::MouseEnterEvent, o, point); + DispatchEvent(event_names::MouseEnter, n, &Control::MouseEnterEvent, o, + point); } DispatchEvent(event_names::MouseMove, mouse_captured_control_, &Control::MouseMoveEvent, nullptr, point); |