Add parent-child relationship of the control tree and bubble-style invalidation propagation.

This commit is contained in:
fanlusky committed 2026-06-14 15:17:44 +08:00
1 parent df881f81bf
commit db59ca7365
4 files changed
+80 -16

No files matched your search

+2
View File
@@ -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 和更完整的命中/焦点管理
- 引入资源字典、主题和声明式界面描述
+5
View File
@@ -95,8 +95,12 @@ class Visual
const RectF &GetBounds() const;
protected:
void AdoptChild(const std::shared_ptr<Visual> &child);
void ReleaseChild(const std::shared_ptr<Visual> &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;
};
+10 -8
View File
@@ -1082,16 +1082,17 @@ TabControl::TabControl(float headerHeight) : headerHeight_(headerHeight)
void TabControl::AddTab(std::wstring title, std::shared_ptr<Visual> 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<Visual> 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 &section : sections_)
{
ReleaseChild(section.content);
}
sections_.clear();
headerRects_.clear();
contentRects_.clear();
+63 -8
View File
@@ -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<Visual> &child)
{
if (!child)
{
return;
}
child->SetParent(this);
if (window_)
{
child->Attach(window_);
}
}
void Visual::ReleaseChild(const std::shared_ptr<Visual> &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<Visual> 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<Visual> 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<Visual> 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<size_t>(rowSpan, 1), std::max<size_t>(columnSpan, 1)}, {}});
InvalidateMeasure();
}
@@ -1197,15 +1252,15 @@ std::vector<float> Grid::ResolveTrackSizes(const std::vector<GridLength> &defini
Container::Container(std::shared_ptr<Visual> child) : child_(std::move(child))
{
AdoptChild(child_);
}
void Container::SetChild(std::shared_ptr<Visual> child)
{
ReleaseChild(child_);
child_ = std::move(child);
if (child_ && window_)
{
child_->Attach(window_);
}
AdoptChild(child_);
InvalidateMeasure();
}