Snapshot before repaint phase 2

This commit is contained in:
Codex
2026-04-07 21:37:49 +08:00
parent b07a4ec370
commit 77a8fe568a
10 changed files with 395 additions and 131 deletions
+97 -122
View File
@@ -27,6 +27,73 @@ static const char* SxMsgName(UINT m)
}
}
bool Window::isManagedDispatchActive() const
{
return managedDispatchActive;
}
void Window::requestManagedRepaint()
{
managedSceneDirty = true;
}
void Window::drawWindowBackground()
{
if (!bkImageFile.empty())
{
if (!background || background->getwidth() != width || background->getheight() != height)
{
background = std::make_unique<IMAGE>();
loadimage(background.get(), bkImageFile.c_str(), width, height, true);
}
putimage(0, 0, background.get());
}
else
{
setbkcolor(wBkcolor);
cleardevice();
}
}
void Window::redrawScene(bool forceControlsDirty, bool forceDialogsDirty)
{
drawWindowBackground();
for (auto& c : controls)
{
if (forceControlsDirty)
c->setDirty(true);
c->draw();
}
for (auto& d : dialogs)
{
if (forceDialogsDirty && d->IsVisible())
d->setDirty(true);
d->draw();
}
}
void Window::flushManagedRepaint()
{
if (!managedSceneDirty || !hWnd)
return;
BeginBatchDraw();
redrawScene(true, true);
EndBatchDraw();
managedSceneDirty = false;
}
void Window::dispatchSyntheticMouseMoveToControls(short x, short y)
{
ExMessage mm{};
mm.message = WM_MOUSEMOVE;
mm.x = x;
mm.y = y;
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
(*it)->handleEvent(mm);
}
/**
* ApplyResizableStyle
* 作用:统一设置可拉伸/裁剪样式,并按开关使用 WS_EX_COMPOSITED(合成双缓冲)。
@@ -294,18 +361,8 @@ void Window::draw()
cls &= ~(CS_HREDRAW | CS_VREDRAW);
SetClassLongPtr(hWnd, GCL_STYLE, cls);
setbkcolor(wBkcolor);
cleardevice();
BeginBatchDraw();
for (auto& c : controls)
{
c->draw();
}
for (auto& d : dialogs)
{
d->draw();
}
redrawScene(true, true);
EndBatchDraw();
}
@@ -345,19 +402,9 @@ void Window::draw(std::string imagePath)
background.reset();
}
background = std::make_unique<IMAGE>();
loadimage(background.get(), bkImageFile.c_str(), width, height, true);
putimage(0, 0, background.get());
BeginBatchDraw();
for (auto& c : controls)
{
c->setDirty(true);
c->draw();
}
for (auto& d : dialogs)
{
d->draw();
}
redrawScene(true, true);
EndBatchDraw();
}
@@ -382,7 +429,6 @@ int Window::runEventLoop()
{
bool consume = false; // 事件是否被消费的标志(用于输入事件分发)
bool redrawDialogs = false; // 是否需要重绘对话框(控件事件可能引起对话框状态变化)
if (peekmessage(&msg, EX_MOUSE | EX_KEY | EX_WINDOW, true))
{
@@ -427,18 +473,8 @@ int Window::runEventLoop()
continue;
}
bool hasVisibleNonModalDialog = false;
for (const auto& d : dialogs)
{
if (d->IsVisible() && !d->model())
{
hasVisibleNonModalDialog = true;
break;
}
}
bool controlVisualChanged = false;
// 输入优先:先给顶层“非模态对话框”,再传给普通控件
managedDispatchActive = true;
for (auto it = dialogs.rbegin(); it != dialogs.rend(); ++it)
{
auto& d = *it;
@@ -449,6 +485,12 @@ int Window::runEventLoop()
if (!SxIsNoisyMsg(msg.message))
SX_LOGD("Event") << SX_T("事件被非模态对话框处理:", "Event consumed by non-modal dialog: ")
<< SxMsgName(msg.message);
// 非模态对话框吞掉自己的区域内鼠标移动后,底层普通控件收不到“离开”消息,
// 会残留 hover。这里补一条落在窗口外的合成移动,只用于清理底层 hover,
// 不会让底层控件重新命中。
if (msg.message == WM_MOUSEMOVE)
dispatchSyntheticMouseMoveToControls(-32768, -32768);
break;
}
}
@@ -458,8 +500,6 @@ int Window::runEventLoop()
{
Control* current = it->get();
consume = current->handleEvent(msg);
if (current->didEventAffectVisual())
controlVisualChanged = true;
if (consume)
{
if (!SxIsNoisyMsg(msg.message))
@@ -469,20 +509,8 @@ int Window::runEventLoop()
break;
}
}
if (hasVisibleNonModalDialog && controlVisualChanged)
redrawDialogs = true; // 只有普通控件真的发生视觉变化时,才补画非模态对话框
}
}
// 关键点⑦:事件处理后,如果对话框状态可能变化(例如控件事件引起的控件重绘可能导致对话框被覆盖),优先重绘对话框以确保界面响应及时;后续再根据 needResizeDirty 进行一次性重绘。
if (redrawDialogs)
{
for (auto& d : dialogs)
{
if (!d->model() && d->IsVisible())
d->setDirty(true);
d->draw();
}
redrawDialogs = false; // 重置标志
managedDispatchActive = false;
}
//如果有对话框打开或者关闭强制重绘
@@ -508,31 +536,21 @@ int Window::runEventLoop()
{
ScreenToClient(this->hWnd, &pt);
ExMessage mm;
mm.message = WM_MOUSEMOVE;
mm.x = (short)pt.x;
mm.y = (short)pt.y;
// 只分发给 window 层控件(因为 dialog 已经关闭或即将关闭)
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
(*it)->handleEvent(mm);
managedDispatchActive = true;
dispatchSyntheticMouseMoveToControls((short)pt.x, (short)pt.y);
managedDispatchActive = false;
}
dialogClose = false; // 重置标志
}
BeginBatchDraw();
SX_LOGD("Event") << SX_T("对话框打开/关闭,触发全量重绘", "The dialog box opens/closes, triggering a full redraw");
// 先绘制普通控件
for (auto& c : controls)
c->draw();
// 然后绘制对话框(确保对话框在最上层)
for (auto& d : dialogs)
{
if (!d->model() && d->IsVisible())
d->setDirty(true);
d->draw();
}
redrawScene(true, true);
EndBatchDraw();
needredraw = false;
dialogOpen = false;
managedSceneDirty = false;
}
// —— 统一收口(needResizeDirty 为真时执行一次性重绘)——
@@ -579,25 +597,9 @@ int Window::runEventLoop()
int confirmedWidth = clientRect.right - clientRect.left;
int confirmedHeight = clientRect.bottom - clientRect.top;
int renderWidth = confirmedWidth;
int renderHeight = confirmedHeight;
// 背景:若设置了背景图则重载并铺满;否则清屏为纯色
if (background && !bkImageFile.empty())
{
background = std::make_unique<IMAGE>();
loadimage(background.get(), bkImageFile.c_str(), renderWidth, renderHeight, true);
putimage(0, 0, background.get());
}
else
{
setbkcolor(wBkcolor);
cleardevice();
}
// 最终提交“当前已应用尺寸”(用于外部查询/下次比较)
width = renderWidth;
height = renderHeight;
width = confirmedWidth;
height = confirmedHeight;
for (auto& d : dialogs)
{
@@ -609,8 +611,7 @@ int Window::runEventLoop()
}
// 统一批量绘制
for (auto& c : controls) c->draw();
for (auto& d : dialogs) d->draw();
redrawScene(true, true);
EndBatchDraw();
@@ -621,8 +622,12 @@ int Window::runEventLoop()
SX_LOGI("Resize") << SX_T("尺寸调整已完成:width=","Resize settle done: width=") << width << " height=" << height;
needResizeDirty = false; // 收口完成,清标志
managedSceneDirty = false;
}
if (!needResizeDirty && !dialogOpen && !dialogClose)
flushManagedRepaint();
// 轻微睡眠,削峰填谷(不阻塞拖拽体验)
Sleep(10);
}
@@ -638,20 +643,8 @@ void Window::setBkImage(std::string pImgFile)
background = std::make_unique<IMAGE>();
bkImageFile = std::move(pImgFile);
loadimage(background.get(), bkImageFile.c_str(), width, height, true);
putimage(0, 0, background.get());
BeginBatchDraw();
for (auto& c : controls)
{
c->setDirty(true);
c->draw();
}
for (auto& d : dialogs)
{
d->setDirty(true);
d->draw();
}
redrawScene(true, true);
EndBatchDraw();
}
@@ -659,20 +652,11 @@ void Window::setBkcolor(COLORREF c)
{
// 更换纯色背景:立即清屏并批量重绘控件/对话框
wBkcolor = c;
setbkcolor(wBkcolor);
cleardevice();
background.reset();
bkImageFile.clear();
BeginBatchDraw();
for (auto& c : controls)
{
c->setDirty(true);
c->draw();
}
for (auto& d : dialogs)
{
d->setDirty(true);
d->draw();
}
redrawScene(true, true);
EndBatchDraw();
}
@@ -687,12 +671,14 @@ void Window::setHeadline(std::string title)
void Window::addControl(std::unique_ptr<Control> control)
{
// 新增控件:仅加入管理容器,具体绘制在 draw()/收口时统一进行
control->setHostWindow(this);
controls.push_back(std::move(control));
}
void Window::addDialog(std::unique_ptr<Control> dlg)
{
// 新增非模态对话框:管理顺序决定事件优先级(顶层从后往前)
dlg->setHostWindow(this);
dialogs.push_back(std::move(dlg));
}
@@ -783,17 +769,6 @@ void Window::pumpResizeIfNeeded()
// Resize + 背景
Resize(nullptr, finalW, finalH);
GetClientRect(hWnd, &rc);
if (background && !bkImageFile.empty())
{
background = std::make_unique<IMAGE>();
loadimage(background.get(), bkImageFile.c_str(), rc.right - rc.left, rc.bottom - rc.top, true);
putimage(0, 0, background.get());
}
else
{
setbkcolor(wBkcolor);
cleardevice();
}
width = rc.right - rc.left; height = rc.bottom - rc.top;
// 通知控件/对话框
@@ -806,8 +781,7 @@ void Window::pumpResizeIfNeeded()
dd->recenterInHostWindow(); // 窗口变化时仅重新居中,不拉伸 Dialog 自身
// 重绘
for (auto& c : controls) c->draw();
for (auto& d : dialogs) d->draw();
redrawScene(true, true);
EndBatchDraw();
SendMessage(hWnd, WM_SETREDRAW, TRUE, 0);
@@ -818,6 +792,7 @@ void Window::pumpResizeIfNeeded()
ValidateRect(hWnd, nullptr);
needResizeDirty = false;
managedSceneDirty = false;
}
void Window::scheduleResizeFromModal(int w, int h)
{