diff --git a/Button.cpp b/Button.cpp index b766731..0781231 100644 --- a/Button.cpp +++ b/Button.cpp @@ -276,9 +276,11 @@ bool Button::handleEvent(const ExMessage& msg) { if (!show) return false; + resetEventVisualChanged(); bool oldHover = hover;// 注意:只在状态变化时记录,避免 WM_MOUSEMOVE 刷屏 bool oldClick = click; + const bool oldTipVisible = tipVisible; bool consume = false;//是否消耗事件 const bool isMouseMessage = @@ -312,12 +314,6 @@ bool Button::handleEvent(const ExMessage& msg) break; } } - if (hover != oldHover) - { - SX_LOG_TRACE("Button") << SX_T("悬停变化: ","hover change: ") << "id=" << id - << " text= " << text - << " " << (oldHover ? 1 : 0) << "->" << (hover ? 1 : 0); - } // 处理鼠标点击事件 if (msg.message == WM_LBUTTONDOWN && hover && mode != StellarX::ButtonMode::DISABLED) { @@ -396,8 +392,6 @@ bool Button::handleEvent(const ExMessage& msg) // 到点就显示 if (GetTickCount64() - tipHoverTick >= (ULONGLONG)tipDelayMs) { - SX_LOG_TRACE("Button") << SX_T("提示信息显示: ","tooltip show:")<<" id = " << id <handleEvent(msg); if (c->isDirty()) anyDirty = true; + if (c->didEventAffectVisual()) anyVisualChanged = true; if (cConsumed) { @@ -135,8 +151,9 @@ bool Canvas::handleEvent(const ExMessage& msg) if (firstConsumer && !SxIsNoisyMsg(msg.message)) { - SX_LOGD("Event") << SX_T("Canvas 消耗消息: ","Canvas consumed: msg=") << msg.message - << SX_T("子控件"," by child")<<" id=" << firstConsumer->getId(); + SX_LOGD("Event") << SX_T("Canvas 消耗消息: ","Canvas consumed: ") + << SxCanvasMsgName(msg.message) + << SX_T(" 子控件 id=", " childId=") << firstConsumer->getId(); } if (anyDirty) @@ -145,6 +162,7 @@ bool Canvas::handleEvent(const ExMessage& msg) SX_LOGD("Dirty") << SX_T("Canvas检测有控件为脏状态 -> 请求重绘, ","Canvas anyDirty -> requestRepaint, ")<<"id = " << id; requestRepaint(parent); } + markEventVisualChanged(anyVisualChanged); return consumed; } diff --git a/Control.h b/Control.h index c0a8f1e..b378dc8 100644 --- a/Control.h +++ b/Control.h @@ -41,6 +41,7 @@ protected: Control* parent = nullptr; // 父控件 bool dirty = true; // 是否重绘 bool show = true; // 是否显示 + bool eventVisualChanged = false; // 最近一次 handleEvent 是否真的引发了视觉变化 /* == 布局模式 == */ StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式 @@ -128,6 +129,7 @@ public: std::string getId() const { return id; } //检查是否为脏 bool isDirty() { return dirty; } + bool didEventAffectVisual() const { return eventVisualChanged; } //用来检查对话框是否模态,其他控件不用实现 virtual bool model()const = 0; //布局 @@ -139,4 +141,6 @@ public: protected: void saveStyle(); void restoreStyle(); + void resetEventVisualChanged() { eventVisualChanged = false; } + void markEventVisualChanged(bool changed = true) { eventVisualChanged = changed; } }; diff --git a/Dialog.cpp b/Dialog.cpp index 80049f4..792befa 100644 --- a/Dialog.cpp +++ b/Dialog.cpp @@ -44,12 +44,15 @@ void Dialog::draw() //绘制消息文本 settextcolor(textStyle.color); + setbkmode(TRANSPARENT); //设置字体样式 settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace, textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight, textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut); + outtextxy(x + 5, y + 5, LPCTSTR(titleText.c_str())); + int ty = y + closeButtonHeight + titleToTextMargin; // 文本起始Y坐标 for (auto& line : lines) { @@ -68,6 +71,7 @@ void Dialog::draw() bool Dialog::handleEvent(const ExMessage& msg) { bool consume = false; + resetEventVisualChanged(); if (!show) { if (pendingCleanup && !isCleaning) @@ -80,12 +84,20 @@ bool Dialog::handleEvent(const ExMessage& msg) // 如果正在清理或标记为待清理,则不处理事件 if (pendingCleanup || isCleaning) return false; - // 模态对话框不允许点击外部区域 - // 模态对话框:点击对话框外部区域时,发出提示音(\a)并吞噬该事件,不允许操作背景内容。 - if (modal && msg.message == WM_LBUTTONUP && - (msg.x < x || msg.x > x + width || msg.y < y || msg.y > y + height)) + + const bool isMouseMessage = + msg.message == WM_MOUSEMOVE || + msg.message == WM_LBUTTONDOWN || + msg.message == WM_LBUTTONUP; + const bool insideDialog = + msg.x >= x && msg.x <= x + width && + msg.y >= y && msg.y <= y + height; + + // 模态对话框区域外鼠标事件不允许落到底层背景。 + if (modal && isMouseMessage && !insideDialog) { - std::cout << "\a" << std::endl; + if (msg.message == WM_LBUTTONUP) + std::cout << "\a" << std::endl; return true; } @@ -93,6 +105,10 @@ bool Dialog::handleEvent(const ExMessage& msg) if (!consume) consume = Canvas::handleEvent(msg); + // 对话框矩形范围内的鼠标事件一律由对话框吞掉,避免穿透到底层控件。 + if (isMouseMessage && insideDialog) + consume = true; + // 每次事件处理后检查是否需要执行延迟清理 if (pendingCleanup && !isCleaning) performDelayedCleanup(); @@ -182,9 +198,8 @@ void Dialog::Show() // 立即统一收口:父窗重绘 背景+普通控件(不会画到这只模态) hWnd.pumpResizeIfNeeded(); - // 这只模态在新尺寸下重建布局 / 重抓背景 → 本帧要画自己 - setInitialization(true); - setDirty(true); + // 这只模态只重新居中,不参与拉伸;背景快照需要在新位置重抓。 + recenterInHostWindow(); } // ② 处理这只对话框的鼠标/键盘(沿用原来 EX_MOUSE | EX_KEY) @@ -257,10 +272,30 @@ void Dialog::setInitialization(bool init) } } +void Dialog::recenterInHostWindow() +{ + if (!show) + return; + + // 尚未完成首次初始化时,保持“延迟初始化”语义,由首次 draw 统一创建子控件。 + if (needsInitialization || width <= 0 || height <= 0) + { + dirty = true; + return; + } + + const int newX = (hWnd.getWidth() - width) / 2; + const int newY = (hWnd.getHeight() - height) / 2; + invalidateBackgroundSnapshot(); + + x = newX; + y = newY; + rebuildChrome(); + dirty = true; +} + void Dialog::initButtons() { - controls.clear(); - switch (this->type) { case StellarX::MessageBoxType::OK: // 只有确定按钮 @@ -489,16 +524,6 @@ void Dialog::initCloseButton() this->addControl(std::move(but)); } -void Dialog::initTitle() -{ - auto titleLabel = std::make_unique