发布前托管重绘与布局封版收口

收口 Dialog/overlay 后鼠标状态同步、Tooltip 临时 coverage 与持久 coverage 拆分、跨 root 脏区补提交、TextBox/Button 绘制副作用修复,并补充 KEY6 回归用例和 BUG/Fix/Feature 开发记录。
This commit is contained in:
Codex
2026-05-17 00:26:08 +08:00
parent 2388f22c99
commit 9155a86a8a
26 changed files with 1355 additions and 175 deletions
+57 -17
View File
@@ -4,6 +4,12 @@
namespace
{
enum class SxTabOverlayRedrawMode
{
None,
RefreshSnapshot
};
bool SxTabRectsIntersect(const RECT& a, const RECT& b)
{
return a.left < b.right && a.right > b.left &&
@@ -473,7 +479,7 @@ int TabControl::indexOf(const std::string& tabText) const
return idx;
}
return idx;
return -1;
}
void TabControl::setDirty(bool dirty)
@@ -498,9 +504,11 @@ void TabControl::requestRepaint(Control* parent)
if (this == parent)
{
RECT coverage{};
bool hasCoverage = false;
auto commitTabUnit = [&](Control* unit, bool forceOverlayRedraw)
RECT paintCoverage{};
bool hasPaintCoverage = false;
RECT persistentCoverage{};
bool hasPersistentCoverage = false;
auto commitTabUnit = [&](Control* unit, SxTabOverlayRedrawMode overlayMode)
{
if (!unit || !unit->IsVisible())
return;
@@ -508,9 +516,9 @@ void TabControl::requestRepaint(Control* parent)
const bool directDirty = unit->isDirty();
const bool subtreeDirty = unit->hasManagedDirtySubtree();
if (forceOverlayRedraw)
if (overlayMode == SxTabOverlayRedrawMode::RefreshSnapshot)
{
// 下层单元已经写过像素,上层页签/页面作为 overlay 补画时,
// 下层单元的持久内容已经写过像素,上层页签/页面作为 overlay 补画时,
// 必须先丢掉旧快照,重新抓取当前背景后再画,否则会把旧背景再贴回来。
unit->invalidateBackgroundSnapshot();
unit->setDirty(true);
@@ -529,15 +537,26 @@ void TabControl::requestRepaint(Control* parent)
return;
}
const RECT rc = unit->getManagedRepaintCoverageRect();
if (!hasCoverage)
const RECT paintRect = unit->getManagedRepaintCoverageRect();
if (!hasPaintCoverage)
{
coverage = rc;
hasCoverage = true;
paintCoverage = paintRect;
hasPaintCoverage = true;
}
else
{
coverage = SxTabUnionRect(coverage, rc);
paintCoverage = SxTabUnionRect(paintCoverage, paintRect);
}
const RECT persistentRect = unit->getManagedRepaintPersistentCoverageRect();
if (!hasPersistentCoverage)
{
persistentCoverage = persistentRect;
hasPersistentCoverage = true;
}
else
{
persistentCoverage = SxTabUnionRect(persistentCoverage, persistentRect);
}
};
@@ -553,11 +572,14 @@ void TabControl::requestRepaint(Control* parent)
if (page->hasManagedDirtySubtree())
{
commitTabUnit(page, false);
commitTabUnit(page, SxTabOverlayRedrawMode::None);
}
else if (hasCoverage && SxTabRectsIntersect(page->getManagedRepaintCoverageRect(), coverage))
else if (hasPaintCoverage && SxTabRectsIntersect(page->getManagedRepaintCoverageRect(), paintCoverage))
{
commitTabUnit(page, true);
const bool persistentHit = hasPersistentCoverage &&
SxTabRectsIntersect(page->getManagedRepaintPersistentCoverageRect(), persistentCoverage);
if (persistentHit)
commitTabUnit(page, SxTabOverlayRedrawMode::RefreshSnapshot);
}
}
@@ -569,11 +591,14 @@ void TabControl::requestRepaint(Control* parent)
if (button->hasManagedDirtySubtree())
{
commitTabUnit(button, false);
commitTabUnit(button, SxTabOverlayRedrawMode::None);
}
else if (hasCoverage && SxTabRectsIntersect(button->getManagedRepaintCoverageRect(), coverage))
else if (hasPaintCoverage && SxTabRectsIntersect(button->getManagedRepaintCoverageRect(), paintCoverage))
{
commitTabUnit(button, true);
const bool persistentHit = hasPersistentCoverage &&
SxTabRectsIntersect(button->getManagedRepaintPersistentCoverageRect(), persistentCoverage);
if (persistentHit)
commitTabUnit(button, SxTabOverlayRedrawMode::RefreshSnapshot);
}
}
return;
@@ -612,6 +637,21 @@ RECT TabControl::getManagedRepaintCoverageRect() const
return coverage;
}
RECT TabControl::getManagedRepaintPersistentCoverageRect() const
{
// 持久 coverage 排除页签按钮 Tooltip 等临时浮层,
// 用于判断上层页签/页面补画时是否允许刷新背景快照。
RECT coverage = getBoundsRect();
for (const auto& control : controls)
{
if (control.first->IsVisible())
coverage = SxTabUnionRect(coverage, control.first->getManagedRepaintPersistentCoverageRect());
if (control.second->IsVisible())
coverage = SxTabUnionRect(coverage, control.second->getManagedRepaintPersistentCoverageRect());
}
return coverage;
}
bool TabControl::canCommitManagedPartialRepaint() const
{
// TabControl 只有在自己本体不脏且背景快照有效时,才允许只更新脏页签/脏页面。