Snapshot before repaint phase 2
This commit is contained in:
+97
-122
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user