发布前托管重绘与布局封版收口
收口 Dialog/overlay 后鼠标状态同步、Tooltip 临时 coverage 与持久 coverage 拆分、跨 root 脏区补提交、TextBox/Button 绘制副作用修复,并补充 KEY6 回归用例和 BUG/Fix/Feature 开发记录。
This commit is contained in:
+62
-15
@@ -22,6 +22,12 @@ static const char* SxCanvasMsgName(UINT m)
|
||||
|
||||
namespace
|
||||
{
|
||||
enum class SxCanvasOverlayRedrawMode
|
||||
{
|
||||
None,
|
||||
RefreshSnapshot
|
||||
};
|
||||
|
||||
bool SxCanvasRectValid(const RECT& rc)
|
||||
{
|
||||
return rc.right > rc.left && rc.bottom > rc.top;
|
||||
@@ -209,9 +215,18 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
else if (c->clearTransientMouseState())
|
||||
else
|
||||
{
|
||||
anyVisualChanged = true;
|
||||
// 后续兄弟只走临时状态清理,不会再进入自己的 handleEvent()。
|
||||
// Tooltip 隐藏会先回贴旧快照,再改变 coverage;因此必须先保存旧覆盖范围,
|
||||
// 避免登记重绘时丢失旧 Tooltip 区域,导致上层 overlay 补画判断不完整。
|
||||
const RECT previousCoverage = c->getManagedRepaintCoverageRect();
|
||||
if (c->clearTransientMouseState())
|
||||
{
|
||||
if (Window* host = getHostWindow())
|
||||
host->requestManagedRepaint(c, previousCoverage);
|
||||
anyVisualChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,9 +418,11 @@ void Canvas::requestRepaint(Control* parent)
|
||||
|
||||
SX_LOG_TRACE("Dirty") << SX_T("Canvas 请求局部重绘:id=", "Canvas::requestRepaint(partial): id=") << id;
|
||||
|
||||
RECT coverage{};
|
||||
bool hasCoverage = false;
|
||||
auto commitManagedChild = [&](Control* child, bool forceOverlayRedraw)
|
||||
RECT paintCoverage{};
|
||||
bool hasPaintCoverage = false;
|
||||
RECT persistentCoverage{};
|
||||
bool hasPersistentCoverage = false;
|
||||
auto commitManagedChild = [&](Control* child, SxCanvasOverlayRedrawMode overlayMode)
|
||||
{
|
||||
if (!child || !child->IsVisible())
|
||||
return;
|
||||
@@ -413,10 +430,10 @@ void Canvas::requestRepaint(Control* parent)
|
||||
const bool directDirty = child->isDirty();
|
||||
const bool subtreeDirty = child->hasManagedDirtySubtree();
|
||||
|
||||
if (forceOverlayRedraw)
|
||||
if (overlayMode == SxCanvasOverlayRedrawMode::RefreshSnapshot)
|
||||
{
|
||||
// overlay 补画必须先作废旧快照:
|
||||
// 下层兄弟刚刚已经写过像素,若继续沿用旧快照,会把旧背景再贴回来。
|
||||
// 下层兄弟的持久内容刚刚已经写过像素,若继续沿用旧快照,会把旧背景再贴回来。
|
||||
child->invalidateBackgroundSnapshot();
|
||||
child->setDirty(true);
|
||||
child->draw();
|
||||
@@ -437,15 +454,26 @@ void Canvas::requestRepaint(Control* parent)
|
||||
return;
|
||||
}
|
||||
|
||||
const RECT childRect = child->getManagedRepaintCoverageRect();
|
||||
if (!hasCoverage)
|
||||
const RECT childPaintRect = child->getManagedRepaintCoverageRect();
|
||||
if (!hasPaintCoverage)
|
||||
{
|
||||
coverage = childRect;
|
||||
hasCoverage = true;
|
||||
paintCoverage = childPaintRect;
|
||||
hasPaintCoverage = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
coverage = SxCanvasUnionRect(coverage, childRect);
|
||||
paintCoverage = SxCanvasUnionRect(paintCoverage, childPaintRect);
|
||||
}
|
||||
|
||||
const RECT childPersistentRect = child->getManagedRepaintPersistentCoverageRect();
|
||||
if (!hasPersistentCoverage)
|
||||
{
|
||||
persistentCoverage = childPersistentRect;
|
||||
hasPersistentCoverage = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
persistentCoverage = SxCanvasUnionRect(persistentCoverage, childPersistentRect);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -457,12 +485,17 @@ void Canvas::requestRepaint(Control* parent)
|
||||
|
||||
if (child->hasManagedDirtySubtree())
|
||||
{
|
||||
commitManagedChild(child, false);
|
||||
commitManagedChild(child, SxCanvasOverlayRedrawMode::None);
|
||||
}
|
||||
else if (hasCoverage && SxCanvasRectsIntersect(child->getManagedRepaintCoverageRect(), coverage))
|
||||
else if (hasPaintCoverage && SxCanvasRectsIntersect(child->getManagedRepaintCoverageRect(), paintCoverage))
|
||||
{
|
||||
// 位于本次累计 coverage 上方、且发生相交的兄弟控件,需要补画回最上层。
|
||||
commitManagedChild(child, true);
|
||||
// 但只有下层“持久内容”影响到它时,才允许作废并重新抓背景快照;
|
||||
// 如果只是被 Tooltip 等临时浮层覆盖,则跳过兄弟补画,避免透明控件回贴旧快照擦掉 Tooltip。
|
||||
const bool persistentHit = hasPersistentCoverage &&
|
||||
SxCanvasRectsIntersect(child->getManagedRepaintPersistentCoverageRect(), persistentCoverage);
|
||||
if (persistentHit)
|
||||
commitManagedChild(child, SxCanvasOverlayRedrawMode::RefreshSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,6 +535,20 @@ RECT Canvas::getManagedRepaintCoverageRect() const
|
||||
return coverage;
|
||||
}
|
||||
|
||||
RECT Canvas::getManagedRepaintPersistentCoverageRect() const
|
||||
{
|
||||
// 持久 coverage 只描述会进入背景快照语义的范围。
|
||||
// 子控件 Tooltip 等临时浮层不会被并入,避免兄弟控件补画时抓到临时像素。
|
||||
RECT coverage = getBoundsRect();
|
||||
for (const auto& child : controls)
|
||||
{
|
||||
if (!child->IsVisible())
|
||||
continue;
|
||||
coverage = SxCanvasUnionRect(coverage, child->getManagedRepaintPersistentCoverageRect());
|
||||
}
|
||||
return coverage;
|
||||
}
|
||||
|
||||
bool Canvas::canCommitManagedPartialRepaint() const
|
||||
{
|
||||
// Canvas 只有在“自己本体不脏 + 仍持有有效背景快照”时,
|
||||
|
||||
Reference in New Issue
Block a user