Finalize layout stage 2 fixes and refresh regression scenes
This commit is contained in:
+178
-21
@@ -33,6 +33,9 @@ static bool SxRectsIntersect(const RECT& a, const RECT& b)
|
||||
a.top < b.bottom && a.bottom > b.top;
|
||||
}
|
||||
|
||||
static void collectManagedControlOverlays(const std::vector<std::unique_ptr<Control>>& controls,
|
||||
Control* repaintRoot, const RECT& coverage, std::vector<Control*>& overlays);
|
||||
|
||||
bool Window::isManagedDispatchActive() const
|
||||
{
|
||||
return managedDispatchActive;
|
||||
@@ -56,9 +59,20 @@ void Window::requestManagedRepaint(Control* source)
|
||||
if (!root)
|
||||
return;
|
||||
|
||||
RECT coverage = root->getBoundsRect();
|
||||
RECT coverage = root->getManagedRepaintCoverageRect();
|
||||
if (root->canCommitManagedPartialRepaint())
|
||||
coverage = source->getBoundsRect();
|
||||
{
|
||||
// 对支持局部提交的 root,coverage 不能再盯着最深处的 source;
|
||||
// 否则像“三层 Canvas 里的按钮变色”这种情况,只会登记成一个很小的叶子矩形,
|
||||
// 顶层 root 提交时既容易漏掉那条直接脏分支,也会低估后续 overlay 补画范围。
|
||||
Control* branch = source->getManagedRepaintDirectBranch(root);
|
||||
coverage = branch ? branch->getManagedRepaintCoverageRect() : source->getManagedRepaintCoverageRect();
|
||||
const RECT sourceCoverage = source->getManagedRepaintCoverageRect();
|
||||
coverage.left = (std::min)(coverage.left, sourceCoverage.left);
|
||||
coverage.top = (std::min)(coverage.top, sourceCoverage.top);
|
||||
coverage.right = (std::max)(coverage.right, sourceCoverage.right);
|
||||
coverage.bottom = (std::max)(coverage.bottom, sourceCoverage.bottom);
|
||||
}
|
||||
|
||||
for (auto& item : managedRepaintItems)
|
||||
{
|
||||
@@ -138,29 +152,108 @@ void Window::flushManagedRepaint()
|
||||
return;
|
||||
|
||||
BeginBatchDraw();
|
||||
std::vector<Control*> overlayDialogs;
|
||||
auto unionCoverage = [](RECT& lhs, const RECT& rhs)
|
||||
{
|
||||
lhs.left = (std::min)(lhs.left, rhs.left);
|
||||
lhs.top = (std::min)(lhs.top, rhs.top);
|
||||
lhs.right = (std::max)(lhs.right, rhs.right);
|
||||
lhs.bottom = (std::max)(lhs.bottom, rhs.bottom);
|
||||
};
|
||||
|
||||
for (auto& item : managedRepaintItems)
|
||||
collectManagedDialogOverlays(item.root, item.coverage, overlayDialogs);
|
||||
auto redrawOverlayUnit = [](Control* unit)
|
||||
{
|
||||
if (!unit || !unit->IsVisible())
|
||||
return;
|
||||
// overlay 补画不是“沿用旧快照再贴回去”,而是重新站到当前顶层场景上合成一遍。
|
||||
unit->invalidateBackgroundSnapshot();
|
||||
unit->setDirty(true);
|
||||
unit->draw();
|
||||
};
|
||||
|
||||
auto processManagedRoot = [&](Control* root, const RECT& initialCoverage)
|
||||
{
|
||||
if (!root || !root->IsVisible())
|
||||
return;
|
||||
|
||||
root->commitManagedRepaint();
|
||||
RECT workingCoverage = initialCoverage;
|
||||
|
||||
size_t controlStartIdx = controls.size();
|
||||
for (size_t i = 0; i < controls.size(); ++i)
|
||||
{
|
||||
if (controls[i].get() == root)
|
||||
{
|
||||
controlStartIdx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 顶层普通控件的 overlay 补画必须是“传递式”的:
|
||||
// 如果 A 重画后把上层按钮 B 也重画出来,而 B 自己又伸进更上层的 C,
|
||||
// 那么 C 也必须继续补画回来,而不能只看最初 root 的 coverage 一跳收集。
|
||||
if (controlStartIdx < controls.size())
|
||||
{
|
||||
for (size_t i = controlStartIdx; i < controls.size(); ++i)
|
||||
{
|
||||
Control* current = controls[i].get();
|
||||
if (!current || !current->IsVisible())
|
||||
continue;
|
||||
const RECT currentCoverage = current->getManagedRepaintCoverageRect();
|
||||
if (!SxRectsIntersect(currentCoverage, workingCoverage))
|
||||
continue;
|
||||
|
||||
redrawOverlayUnit(current);
|
||||
unionCoverage(workingCoverage, current->getManagedRepaintCoverageRect());
|
||||
}
|
||||
}
|
||||
|
||||
size_t dialogStartIdx = 0;
|
||||
for (size_t i = 0; i < dialogs.size(); ++i)
|
||||
{
|
||||
if (dialogs[i].get() == root)
|
||||
{
|
||||
dialogStartIdx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Dialog 永远位于普通顶层控件之上,也要使用扩张后的 coverage 做传递式补画。
|
||||
for (size_t i = dialogStartIdx; i < dialogs.size(); ++i)
|
||||
{
|
||||
Control* dialog = dialogs[i].get();
|
||||
if (!dialog || !dialog->IsVisible())
|
||||
continue;
|
||||
const RECT dialogCoverage = dialog->getManagedRepaintCoverageRect();
|
||||
if (!SxRectsIntersect(dialogCoverage, workingCoverage))
|
||||
continue;
|
||||
|
||||
redrawOverlayUnit(dialog);
|
||||
unionCoverage(workingCoverage, dialog->getManagedRepaintCoverageRect());
|
||||
}
|
||||
};
|
||||
|
||||
for (auto& control : controls)
|
||||
{
|
||||
for (auto& item : managedRepaintItems)
|
||||
{
|
||||
if (item.root == control.get() && item.root && item.root->IsVisible())
|
||||
if (item.root == control.get())
|
||||
{
|
||||
item.root->commitManagedRepaint();
|
||||
processManagedRoot(item.root, item.coverage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& dialog : overlayDialogs)
|
||||
for (auto& dialog : dialogs)
|
||||
{
|
||||
if (!dialog || !dialog->IsVisible())
|
||||
continue;
|
||||
dialog->setDirty(true);
|
||||
dialog->draw();
|
||||
for (auto& item : managedRepaintItems)
|
||||
{
|
||||
if (item.root == dialog.get())
|
||||
{
|
||||
processManagedRoot(item.root, item.coverage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EndBatchDraw();
|
||||
@@ -214,6 +307,44 @@ void Window::collectManagedDialogOverlays(Control* repaintRoot, const RECT& cove
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* collectManagedControlOverlays(repaintRoot, coverage, overlays)
|
||||
* 作用:找出在本轮 root 提交后,需要重新补画回上层的普通顶层控件。
|
||||
* 规则:
|
||||
* - 只处理 Window::controls 这一层的直接兄弟;
|
||||
* - 从 repaintRoot 在 controls 中的位置之后开始收集;
|
||||
* - 仅收集可见且与 coverage 相交的控件。
|
||||
*/
|
||||
static void collectManagedControlOverlays(const std::vector<std::unique_ptr<Control>>& controls,
|
||||
Control* repaintRoot, const RECT& coverage, std::vector<Control*>& overlays)
|
||||
{
|
||||
size_t startIdx = controls.size();
|
||||
for (size_t i = 0; i < controls.size(); ++i)
|
||||
{
|
||||
if (controls[i].get() == repaintRoot)
|
||||
{
|
||||
startIdx = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startIdx > controls.size())
|
||||
return;
|
||||
|
||||
for (size_t i = startIdx; i < controls.size(); ++i)
|
||||
{
|
||||
Control* control = controls[i].get();
|
||||
if (!control || !control->IsVisible())
|
||||
continue;
|
||||
|
||||
if (SxRectsIntersect(control->getBoundsRect(), coverage))
|
||||
{
|
||||
if (std::find(overlays.begin(), overlays.end(), control) == overlays.end())
|
||||
overlays.push_back(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplyResizableStyle
|
||||
* 作用:统一设置可拉伸/裁剪样式,并按开关使用 WS_EX_COMPOSITED(合成双缓冲)。
|
||||
@@ -621,19 +752,45 @@ int Window::runEventLoop()
|
||||
}
|
||||
if (!consume)
|
||||
{
|
||||
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
||||
Control* firstConsumer = nullptr;
|
||||
if (msg.message == WM_MOUSEMOVE)
|
||||
{
|
||||
Control* current = it->get();
|
||||
consume = current->handleEvent(msg);
|
||||
if (consume)
|
||||
// 顶层普通控件的 hover/tooltip 清理规则:
|
||||
// - 第一个命中的兄弟分支收到真实 WM_MOUSEMOVE;
|
||||
// - 后续兄弟不再重新命中,只清理旧 hover / tooltip 等瞬时鼠标状态。
|
||||
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
||||
{
|
||||
if (!SxIsNoisyMsg(msg.message))
|
||||
SX_LOGD("Event") << SX_T("事件被控件处理:", "Event consumed by control: ")
|
||||
<< SxMsgName(msg.message)
|
||||
<< SX_T(" id=", " id=") << current->getId();
|
||||
break;
|
||||
Control* current = it->get();
|
||||
if (!consume)
|
||||
{
|
||||
consume = current->handleEvent(msg);
|
||||
if (consume)
|
||||
firstConsumer = current;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->clearTransientMouseState();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
||||
{
|
||||
Control* current = it->get();
|
||||
consume = current->handleEvent(msg);
|
||||
if (consume)
|
||||
{
|
||||
firstConsumer = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstConsumer && !SxIsNoisyMsg(msg.message))
|
||||
SX_LOGD("Event") << SX_T("事件被控件处理:", "Event consumed by control: ")
|
||||
<< SxMsgName(msg.message)
|
||||
<< SX_T(" id=", " id=") << firstConsumer->getId();
|
||||
}
|
||||
managedDispatchActive = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user