diff --git a/ui/README.md b/ui/README.md index 27fc2204..1d273740 100644 --- a/ui/README.md +++ b/ui/README.md @@ -41,6 +41,7 @@ - 修复了单行 `TextBox`(如 demo 中的 Search Box)输入光标和文本区域未垂直居中的问题 - 修复了单行 `TextBox` 在垂直居中后输入法候选框未同步跟随文本区域下移的问题 - 新增一版场景级 invalidation 机制:控件现可通过 `InvalidateMeasure` / `InvalidateArrange` / `InvalidateVisual` 统一声明布局或重绘失效,`Scene` / `Window` 会在下一次绘制前自动补做布局,减少到处手动 `Relayout()` 的耦合 +- 补充了控件树父子关系和冒泡式失效传播:容器在收养子控件时会建立 parent 链,子控件的布局/重绘失效会先沿控件树向上汇聚,再交给根场景统一处理,为后续做局部布局优化打基础 ## 构建 @@ -107,6 +108,7 @@ build/bin/Debug/msimeui-demo.exe ## 后续工作 - 继续把场景级 invalidation 细化为局部布局和局部重绘,减少全树重排成本 +- 基于现有 parent 链继续引入更细粒度的 subtree dirty 标记和可缓存测量结果 - 继续完善 `TextBox` 的输入、样式和布局能力 - 增加菜单、弹出层、overlay 和更完整的命中/焦点管理 - 引入资源字典、主题和声明式界面描述 diff --git a/ui/include/msimeui/Layout.h b/ui/include/msimeui/Layout.h index dc884765..1013d07d 100644 --- a/ui/include/msimeui/Layout.h +++ b/ui/include/msimeui/Layout.h @@ -95,8 +95,12 @@ class Visual const RectF &GetBounds() const; protected: + void AdoptChild(const std::shared_ptr &child); + void ReleaseChild(const std::shared_ptr &child); bool HasExplicitWidth() const; bool HasExplicitHeight() const; + void SetParent(Visual *parent); + Visual *GetParent() const; Thickness margin_ = {}; Thickness padding_ = {}; @@ -110,6 +114,7 @@ class Visual VerticalAlignment verticalAlignment_ = VerticalAlignment::Leading; SizeF desiredSize_ = {}; RectF bounds_ = {}; + Visual *parent_ = nullptr; Window *window_ = nullptr; }; diff --git a/ui/src/Controls.cpp b/ui/src/Controls.cpp index 8634ab89..3392050c 100644 --- a/ui/src/Controls.cpp +++ b/ui/src/Controls.cpp @@ -1082,16 +1082,17 @@ TabControl::TabControl(float headerHeight) : headerHeight_(headerHeight) void TabControl::AddTab(std::wstring title, std::shared_ptr content) { + AdoptChild(content); tabs_.push_back({std::move(title), std::move(content)}); - if (tabs_.back().content && window_) - { - tabs_.back().content->Attach(window_); - } InvalidateMeasure(); } void TabControl::ClearTabs() { + for (auto &tab : tabs_) + { + ReleaseChild(tab.content); + } tabs_.clear(); headerRects_.clear(); selectedIndex_ = 0; @@ -1328,20 +1329,21 @@ Accordion::Accordion(float headerHeight) : headerHeight_(headerHeight) void Accordion::AddSection(std::wstring title, std::shared_ptr content, bool expanded) { + AdoptChild(content); sections_.push_back({std::move(title), std::move(content), expanded}); if (!allowMultipleExpanded_ && expanded) { CollapseOtherSections(sections_.size() - 1); } - if (sections_.back().content && window_) - { - sections_.back().content->Attach(window_); - } InvalidateMeasure(); } void Accordion::ClearSections() { + for (auto §ion : sections_) + { + ReleaseChild(section.content); + } sections_.clear(); headerRects_.clear(); contentRects_.clear(); diff --git a/ui/src/Layout.cpp b/ui/src/Layout.cpp index 984b55c2..561d85a3 100644 --- a/ui/src/Layout.cpp +++ b/ui/src/Layout.cpp @@ -183,6 +183,12 @@ HCURSOR Visual::GetCursor() const void Visual::InvalidateMeasure() { + if (parent_) + { + parent_->InvalidateMeasure(); + return; + } + if (window_) { window_->InvalidateMeasure(); @@ -191,6 +197,12 @@ void Visual::InvalidateMeasure() void Visual::InvalidateArrange() { + if (parent_) + { + parent_->InvalidateArrange(); + return; + } + if (window_) { window_->InvalidateArrange(); @@ -199,6 +211,12 @@ void Visual::InvalidateArrange() void Visual::InvalidateVisual() { + if (parent_) + { + parent_->InvalidateVisual(); + return; + } + if (window_) { window_->Invalidate(); @@ -411,17 +429,51 @@ bool Visual::HasExplicitWidth() const return explicitWidth_ >= 0.0f; } +void Visual::AdoptChild(const std::shared_ptr &child) +{ + if (!child) + { + return; + } + + child->SetParent(this); + if (window_) + { + child->Attach(window_); + } +} + +void Visual::ReleaseChild(const std::shared_ptr &child) +{ + if (child && child->GetParent() == this) + { + child->SetParent(nullptr); + } +} + bool Visual::HasExplicitHeight() const { return explicitHeight_ >= 0.0f; } +void Visual::SetParent(Visual *parent) +{ + parent_ = parent; +} + +Visual *Visual::GetParent() const +{ + return parent_; +} + void Panel::AddChild(std::shared_ptr child) { - if (child && window_) + if (!child) { - child->Attach(window_); + return; } + + AdoptChild(child); children_.push_back(std::move(child)); InvalidateMeasure(); } @@ -686,6 +738,7 @@ void WrapPanel::Render(DeviceResources &deviceResources) ScrollViewer::ScrollViewer(std::shared_ptr content) : content_(std::move(content)) { + AdoptChild(content_); } SizeF ScrollViewer::Measure(const SizeF &availableSize) @@ -963,10 +1016,12 @@ void Grid::AddColumn(GridLength length) void Grid::AddChild(std::shared_ptr child, size_t row, size_t column, size_t rowSpan, size_t columnSpan) { - if (child && window_) + if (!child) { - child->Attach(window_); + return; } + + AdoptChild(child); children_.push_back({std::move(child), {row, column, std::max(rowSpan, 1), std::max(columnSpan, 1)}, {}}); InvalidateMeasure(); } @@ -1197,15 +1252,15 @@ std::vector Grid::ResolveTrackSizes(const std::vector &defini Container::Container(std::shared_ptr child) : child_(std::move(child)) { + AdoptChild(child_); } void Container::SetChild(std::shared_ptr child) { + ReleaseChild(child_); + child_ = std::move(child); - if (child_ && window_) - { - child_->Attach(window_); - } + AdoptChild(child_); InvalidateMeasure(); }