Save pre-batched-redraw snapshot
This commit is contained in:
+11
-9
@@ -276,9 +276,11 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
{
|
{
|
||||||
if (!show)
|
if (!show)
|
||||||
return false;
|
return false;
|
||||||
|
resetEventVisualChanged();
|
||||||
|
|
||||||
bool oldHover = hover;// 注意:只在状态变化时记录,避免 WM_MOUSEMOVE 刷屏
|
bool oldHover = hover;// 注意:只在状态变化时记录,避免 WM_MOUSEMOVE 刷屏
|
||||||
bool oldClick = click;
|
bool oldClick = click;
|
||||||
|
const bool oldTipVisible = tipVisible;
|
||||||
|
|
||||||
bool consume = false;//是否消耗事件
|
bool consume = false;//是否消耗事件
|
||||||
const bool isMouseMessage =
|
const bool isMouseMessage =
|
||||||
@@ -312,12 +314,6 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
break;
|
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)
|
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)
|
if (GetTickCount64() - tipHoverTick >= (ULONGLONG)tipDelayMs)
|
||||||
{
|
{
|
||||||
SX_LOG_TRACE("Button") << SX_T("提示信息显示: ","tooltip show:")<<" id = " << id <<SX_T("延时时间: ", " delayMs = ") << tipDelayMs;
|
|
||||||
|
|
||||||
tipVisible = true;
|
tipVisible = true;
|
||||||
|
|
||||||
// 定位(跟随鼠标 or 相对按钮)
|
// 定位(跟随鼠标 or 相对按钮)
|
||||||
@@ -424,8 +418,16 @@ bool Button::handleEvent(const ExMessage& msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果状态发生变化,标记需要重绘
|
// 如果状态发生变化,标记需要重绘
|
||||||
if (hover != oldHover || click != oldClick)
|
const bool stateChanged = (hover != oldHover || click != oldClick);
|
||||||
|
if (stateChanged)
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
const bool tipVisibilityChanged = (tipVisible != oldTipVisible);
|
||||||
|
|
||||||
|
// 鼠标命中按钮区域,或按钮自身状态因此发生变化时,吞掉该事件。
|
||||||
|
// 这样可以避免被遮挡/重叠的下层控件继续收到同一事件并把自己重绘到上层。
|
||||||
|
if (isMouseMessage && (hover || oldHover || click != oldClick))
|
||||||
|
consume = true;
|
||||||
|
markEventVisualChanged(stateChanged || tipVisibilityChanged);
|
||||||
|
|
||||||
// 如果需要重绘,立即执行
|
// 如果需要重绘,立即执行
|
||||||
if (dirty)
|
if (dirty)
|
||||||
|
|||||||
+20
-2
@@ -6,6 +6,19 @@ static bool SxIsNoisyMsg(UINT m)
|
|||||||
return m == WM_MOUSEMOVE;
|
return m == WM_MOUSEMOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* SxCanvasMsgName(UINT m)
|
||||||
|
{
|
||||||
|
switch (m)
|
||||||
|
{
|
||||||
|
case WM_MOUSEMOVE: return "WM_MOUSEMOVE";
|
||||||
|
case WM_LBUTTONDOWN: return "WM_LBUTTONDOWN";
|
||||||
|
case WM_LBUTTONUP: return "WM_LBUTTONUP";
|
||||||
|
case WM_KEYDOWN: return "WM_KEYDOWN";
|
||||||
|
case WM_KEYUP: return "WM_KEYUP";
|
||||||
|
default: return "WM_UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Canvas::Canvas()
|
Canvas::Canvas()
|
||||||
:Control(0, 0, 100, 100)
|
:Control(0, 0, 100, 100)
|
||||||
{
|
{
|
||||||
@@ -113,9 +126,11 @@ void Canvas::draw()
|
|||||||
bool Canvas::handleEvent(const ExMessage& msg)
|
bool Canvas::handleEvent(const ExMessage& msg)
|
||||||
{
|
{
|
||||||
if (!show) return false;
|
if (!show) return false;
|
||||||
|
resetEventVisualChanged();
|
||||||
|
|
||||||
bool consumed = false;
|
bool consumed = false;
|
||||||
bool anyDirty = false;
|
bool anyDirty = false;
|
||||||
|
bool anyVisualChanged = false;
|
||||||
Control* firstConsumer = nullptr;
|
Control* firstConsumer = nullptr;
|
||||||
|
|
||||||
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
||||||
@@ -124,6 +139,7 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
|||||||
bool cConsumed = c->handleEvent(msg);
|
bool cConsumed = c->handleEvent(msg);
|
||||||
|
|
||||||
if (c->isDirty()) anyDirty = true;
|
if (c->isDirty()) anyDirty = true;
|
||||||
|
if (c->didEventAffectVisual()) anyVisualChanged = true;
|
||||||
|
|
||||||
if (cConsumed)
|
if (cConsumed)
|
||||||
{
|
{
|
||||||
@@ -135,8 +151,9 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
|||||||
|
|
||||||
if (firstConsumer && !SxIsNoisyMsg(msg.message))
|
if (firstConsumer && !SxIsNoisyMsg(msg.message))
|
||||||
{
|
{
|
||||||
SX_LOGD("Event") << SX_T("Canvas 消耗消息: ","Canvas consumed: msg=") << msg.message
|
SX_LOGD("Event") << SX_T("Canvas 消耗消息: ","Canvas consumed: ")
|
||||||
<< SX_T("子控件"," by child")<<" id=" << firstConsumer->getId();
|
<< SxCanvasMsgName(msg.message)
|
||||||
|
<< SX_T(" 子控件 id=", " childId=") << firstConsumer->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anyDirty)
|
if (anyDirty)
|
||||||
@@ -145,6 +162,7 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
|||||||
SX_LOGD("Dirty") << SX_T("Canvas检测有控件为脏状态 -> 请求重绘, ","Canvas anyDirty -> requestRepaint, ")<<"id = " << id;
|
SX_LOGD("Dirty") << SX_T("Canvas检测有控件为脏状态 -> 请求重绘, ","Canvas anyDirty -> requestRepaint, ")<<"id = " << id;
|
||||||
requestRepaint(parent);
|
requestRepaint(parent);
|
||||||
}
|
}
|
||||||
|
markEventVisualChanged(anyVisualChanged);
|
||||||
|
|
||||||
return consumed;
|
return consumed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ protected:
|
|||||||
Control* parent = nullptr; // 父控件
|
Control* parent = nullptr; // 父控件
|
||||||
bool dirty = true; // 是否重绘
|
bool dirty = true; // 是否重绘
|
||||||
bool show = true; // 是否显示
|
bool show = true; // 是否显示
|
||||||
|
bool eventVisualChanged = false; // 最近一次 handleEvent 是否真的引发了视觉变化
|
||||||
|
|
||||||
/* == 布局模式 == */
|
/* == 布局模式 == */
|
||||||
StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式
|
StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式
|
||||||
@@ -128,6 +129,7 @@ public:
|
|||||||
std::string getId() const { return id; }
|
std::string getId() const { return id; }
|
||||||
//检查是否为脏
|
//检查是否为脏
|
||||||
bool isDirty() { return dirty; }
|
bool isDirty() { return dirty; }
|
||||||
|
bool didEventAffectVisual() const { return eventVisualChanged; }
|
||||||
//用来检查对话框是否模态,其他控件不用实现
|
//用来检查对话框是否模态,其他控件不用实现
|
||||||
virtual bool model()const = 0;
|
virtual bool model()const = 0;
|
||||||
//布局
|
//布局
|
||||||
@@ -139,4 +141,6 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void saveStyle();
|
void saveStyle();
|
||||||
void restoreStyle();
|
void restoreStyle();
|
||||||
|
void resetEventVisualChanged() { eventVisualChanged = false; }
|
||||||
|
void markEventVisualChanged(bool changed = true) { eventVisualChanged = changed; }
|
||||||
};
|
};
|
||||||
|
|||||||
+52
-24
@@ -44,12 +44,15 @@ void Dialog::draw()
|
|||||||
|
|
||||||
//绘制消息文本
|
//绘制消息文本
|
||||||
settextcolor(textStyle.color);
|
settextcolor(textStyle.color);
|
||||||
|
setbkmode(TRANSPARENT);
|
||||||
|
|
||||||
//设置字体样式
|
//设置字体样式
|
||||||
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
settextstyle(textStyle.nHeight, textStyle.nWidth, textStyle.lpszFace,
|
||||||
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
textStyle.nEscapement, textStyle.nOrientation, textStyle.nWeight,
|
||||||
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
textStyle.bItalic, textStyle.bUnderline, textStyle.bStrikeOut);
|
||||||
|
|
||||||
|
outtextxy(x + 5, y + 5, LPCTSTR(titleText.c_str()));
|
||||||
|
|
||||||
int ty = y + closeButtonHeight + titleToTextMargin; // 文本起始Y坐标
|
int ty = y + closeButtonHeight + titleToTextMargin; // 文本起始Y坐标
|
||||||
for (auto& line : lines)
|
for (auto& line : lines)
|
||||||
{
|
{
|
||||||
@@ -68,6 +71,7 @@ void Dialog::draw()
|
|||||||
bool Dialog::handleEvent(const ExMessage& msg)
|
bool Dialog::handleEvent(const ExMessage& msg)
|
||||||
{
|
{
|
||||||
bool consume = false;
|
bool consume = false;
|
||||||
|
resetEventVisualChanged();
|
||||||
if (!show)
|
if (!show)
|
||||||
{
|
{
|
||||||
if (pendingCleanup && !isCleaning)
|
if (pendingCleanup && !isCleaning)
|
||||||
@@ -80,11 +84,19 @@ bool Dialog::handleEvent(const ExMessage& msg)
|
|||||||
// 如果正在清理或标记为待清理,则不处理事件
|
// 如果正在清理或标记为待清理,则不处理事件
|
||||||
if (pendingCleanup || isCleaning)
|
if (pendingCleanup || isCleaning)
|
||||||
return false;
|
return false;
|
||||||
// 模态对话框不允许点击外部区域
|
|
||||||
// 模态对话框:点击对话框外部区域时,发出提示音(\a)并吞噬该事件,不允许操作背景内容。
|
const bool isMouseMessage =
|
||||||
if (modal && msg.message == WM_LBUTTONUP &&
|
msg.message == WM_MOUSEMOVE ||
|
||||||
(msg.x < x || msg.x > x + width || msg.y < y || msg.y > y + height))
|
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)
|
||||||
{
|
{
|
||||||
|
if (msg.message == WM_LBUTTONUP)
|
||||||
std::cout << "\a" << std::endl;
|
std::cout << "\a" << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -93,6 +105,10 @@ bool Dialog::handleEvent(const ExMessage& msg)
|
|||||||
if (!consume)
|
if (!consume)
|
||||||
consume = Canvas::handleEvent(msg);
|
consume = Canvas::handleEvent(msg);
|
||||||
|
|
||||||
|
// 对话框矩形范围内的鼠标事件一律由对话框吞掉,避免穿透到底层控件。
|
||||||
|
if (isMouseMessage && insideDialog)
|
||||||
|
consume = true;
|
||||||
|
|
||||||
// 每次事件处理后检查是否需要执行延迟清理
|
// 每次事件处理后检查是否需要执行延迟清理
|
||||||
if (pendingCleanup && !isCleaning)
|
if (pendingCleanup && !isCleaning)
|
||||||
performDelayedCleanup();
|
performDelayedCleanup();
|
||||||
@@ -182,9 +198,8 @@ void Dialog::Show()
|
|||||||
// 立即统一收口:父窗重绘 背景+普通控件(不会画到这只模态)
|
// 立即统一收口:父窗重绘 背景+普通控件(不会画到这只模态)
|
||||||
hWnd.pumpResizeIfNeeded();
|
hWnd.pumpResizeIfNeeded();
|
||||||
|
|
||||||
// 这只模态在新尺寸下重建布局 / 重抓背景 → 本帧要画自己
|
// 这只模态只重新居中,不参与拉伸;背景快照需要在新位置重抓。
|
||||||
setInitialization(true);
|
recenterInHostWindow();
|
||||||
setDirty(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ② 处理这只对话框的鼠标/键盘(沿用原来 EX_MOUSE | EX_KEY)
|
// ② 处理这只对话框的鼠标/键盘(沿用原来 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()
|
void Dialog::initButtons()
|
||||||
{
|
{
|
||||||
controls.clear();
|
|
||||||
|
|
||||||
switch (this->type)
|
switch (this->type)
|
||||||
{
|
{
|
||||||
case StellarX::MessageBoxType::OK: // 只有确定按钮
|
case StellarX::MessageBoxType::OK: // 只有确定按钮
|
||||||
@@ -489,16 +524,6 @@ void Dialog::initCloseButton()
|
|||||||
this->addControl(std::move(but));
|
this->addControl(std::move(but));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialog::initTitle()
|
|
||||||
{
|
|
||||||
auto titleLabel = std::make_unique<Label>(this->x + 5, this->y + 5, titleText, textStyle.color);
|
|
||||||
titleLabel->setTextdisap(true);
|
|
||||||
titleLabel->textStyle = this->textStyle;
|
|
||||||
this->title = titleLabel.get();
|
|
||||||
|
|
||||||
this->addControl(std::move(titleLabel));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dialog::splitMessageLines()
|
void Dialog::splitMessageLines()
|
||||||
{
|
{
|
||||||
lines.clear(); // 先清空现有的行
|
lines.clear(); // 先清空现有的行
|
||||||
@@ -570,6 +595,13 @@ void Dialog::invalidateLayout(bool clearChildren)
|
|||||||
this->dirty = true;
|
this->dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Dialog::rebuildChrome()
|
||||||
|
{
|
||||||
|
clearControls();
|
||||||
|
initButtons();
|
||||||
|
initCloseButton();
|
||||||
|
}
|
||||||
|
|
||||||
// 计算逻辑:对话框宽度取【文本区域最大宽度】和【按钮区域总宽度】中的较大值。
|
// 计算逻辑:对话框宽度取【文本区域最大宽度】和【按钮区域总宽度】中的较大值。
|
||||||
// 对话框高度 = 标题栏 + 文本区 + 按钮区 + 各种间距。
|
// 对话框高度 = 标题栏 + 文本区 + 按钮区 + 各种间距。
|
||||||
void Dialog::initDialogSize()
|
void Dialog::initDialogSize()
|
||||||
@@ -626,9 +658,7 @@ void Dialog::initDialogSize()
|
|||||||
this->x = (hWnd.getWidth() - this->width) / 2;
|
this->x = (hWnd.getWidth() - this->width) / 2;
|
||||||
this->y = (hWnd.getHeight() - this->height) / 2;
|
this->y = (hWnd.getHeight() - this->height) / 2;
|
||||||
|
|
||||||
initButtons(); // 初始化按钮
|
rebuildChrome();
|
||||||
initTitle(); // 初始化标题标签
|
|
||||||
initCloseButton(); // 初始化关闭按钮
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dialog::addControl(std::unique_ptr<Control> control)
|
void Dialog::addControl(std::unique_ptr<Control> control)
|
||||||
@@ -655,7 +685,6 @@ void Dialog::performDelayedCleanup()
|
|||||||
|
|
||||||
// 重置指针
|
// 重置指针
|
||||||
closeButton = nullptr;
|
closeButton = nullptr;
|
||||||
title = nullptr;
|
|
||||||
// 释放背景图像资源
|
// 释放背景图像资源
|
||||||
if (saveBkImage && hasSnap)
|
if (saveBkImage && hasSnap)
|
||||||
{
|
{
|
||||||
@@ -709,7 +738,6 @@ void Dialog::clearControls()
|
|||||||
controls.clear();
|
controls.clear();
|
||||||
// 重置按钮指针
|
// 重置按钮指针
|
||||||
closeButton = nullptr;
|
closeButton = nullptr;
|
||||||
title = nullptr; // 释放标题资源
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Button> Dialog::createDialogButton(int x, int y, const std::string& text)
|
std::unique_ptr<Button> Dialog::createDialogButton(int x, int y, const std::string& text)
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class Dialog : public Canvas
|
|||||||
|
|
||||||
StellarX::MessageBoxType type = StellarX::MessageBoxType::OK; //对话框类型
|
StellarX::MessageBoxType type = StellarX::MessageBoxType::OK; //对话框类型
|
||||||
std::string titleText = "提示"; //标题文本
|
std::string titleText = "提示"; //标题文本
|
||||||
Label* title = nullptr; //标题标签(由 controls 持有)
|
|
||||||
|
|
||||||
std::string message; //提示信息
|
std::string message; //提示信息
|
||||||
std::vector<std::string> lines; //消息内容按行分割
|
std::vector<std::string> lines; //消息内容按行分割
|
||||||
@@ -105,14 +104,14 @@ public:
|
|||||||
void Close();
|
void Close();
|
||||||
//初始化
|
//初始化
|
||||||
void setInitialization(bool init);
|
void setInitialization(bool init);
|
||||||
|
// 宿主窗口变化时仅重新居中,不拉伸 Dialog 自身
|
||||||
|
void recenterInHostWindow();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 初始化按钮
|
// 初始化按钮
|
||||||
void initButtons();
|
void initButtons();
|
||||||
// 初始化关闭按钮
|
// 初始化关闭按钮
|
||||||
void initCloseButton();
|
void initCloseButton();
|
||||||
// 初始化标题
|
|
||||||
void initTitle();
|
|
||||||
// 按行分割消息内容
|
// 按行分割消息内容
|
||||||
void splitMessageLines();
|
void splitMessageLines();
|
||||||
// 获取文本大小
|
// 获取文本大小
|
||||||
@@ -121,6 +120,8 @@ private:
|
|||||||
void invalidateLayout(bool clearChildren);
|
void invalidateLayout(bool clearChildren);
|
||||||
//初始化对话框尺寸
|
//初始化对话框尺寸
|
||||||
void initDialogSize();
|
void initDialogSize();
|
||||||
|
// 依据当前 Dialog 的 x/y/width/height 重新创建标题和按钮
|
||||||
|
void rebuildChrome();
|
||||||
void addControl(std::unique_ptr<Control> control);
|
void addControl(std::unique_ptr<Control> control);
|
||||||
|
|
||||||
// 清除所有控件
|
// 清除所有控件
|
||||||
|
|||||||
@@ -99,6 +99,7 @@ void TextBox::draw()
|
|||||||
bool TextBox::handleEvent(const ExMessage& msg)
|
bool TextBox::handleEvent(const ExMessage& msg)
|
||||||
{
|
{
|
||||||
if (!show) return false;
|
if (!show) return false;
|
||||||
|
resetEventVisualChanged();
|
||||||
|
|
||||||
bool hover = false;
|
bool hover = false;
|
||||||
bool oldClick = click;
|
bool oldClick = click;
|
||||||
@@ -165,6 +166,7 @@ bool TextBox::handleEvent(const ExMessage& msg)
|
|||||||
|
|
||||||
if (dirty)
|
if (dirty)
|
||||||
requestRepaint(parent);
|
requestRepaint(parent);
|
||||||
|
markEventVisualChanged(dirty);
|
||||||
|
|
||||||
if (click)
|
if (click)
|
||||||
click = false;
|
click = false;
|
||||||
|
|||||||
+35
-14
@@ -3,6 +3,8 @@
|
|||||||
#include"SxLog.h"
|
#include"SxLog.h"
|
||||||
#include <easyx.h>
|
#include <easyx.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
// 可能频繁出现且对调试信息干扰较大的消息(例如鼠标移动),
|
||||||
|
// 可以在日志输出时特殊处理以减少干扰。
|
||||||
static bool SxIsNoisyMsg(UINT m)
|
static bool SxIsNoisyMsg(UINT m)
|
||||||
{
|
{
|
||||||
return m == WM_MOUSEMOVE;
|
return m == WM_MOUSEMOVE;
|
||||||
@@ -425,6 +427,17 @@ int Window::runEventLoop()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasVisibleNonModalDialog = false;
|
||||||
|
for (const auto& d : dialogs)
|
||||||
|
{
|
||||||
|
if (d->IsVisible() && !d->model())
|
||||||
|
{
|
||||||
|
hasVisibleNonModalDialog = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool controlVisualChanged = false;
|
||||||
|
|
||||||
// 输入优先:先给顶层“非模态对话框”,再传给普通控件
|
// 输入优先:先给顶层“非模态对话框”,再传给普通控件
|
||||||
for (auto it = dialogs.rbegin(); it != dialogs.rend(); ++it)
|
for (auto it = dialogs.rbegin(); it != dialogs.rend(); ++it)
|
||||||
{
|
{
|
||||||
@@ -433,7 +446,9 @@ int Window::runEventLoop()
|
|||||||
consume = d->handleEvent(msg);
|
consume = d->handleEvent(msg);
|
||||||
if (consume)
|
if (consume)
|
||||||
{
|
{
|
||||||
SX_LOGD("Event") << SX_T("事件被非模态对话框处理","Event consumed by non-modal dialog");
|
if (!SxIsNoisyMsg(msg.message))
|
||||||
|
SX_LOGD("Event") << SX_T("事件被非模态对话框处理:", "Event consumed by non-modal dialog: ")
|
||||||
|
<< SxMsgName(msg.message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,15 +456,21 @@ int Window::runEventLoop()
|
|||||||
{
|
{
|
||||||
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
for (auto it = controls.rbegin(); it != controls.rend(); ++it)
|
||||||
{
|
{
|
||||||
consume = (*it)->handleEvent(msg);
|
Control* current = it->get();
|
||||||
|
consume = current->handleEvent(msg);
|
||||||
|
if (current->didEventAffectVisual())
|
||||||
|
controlVisualChanged = true;
|
||||||
if (consume)
|
if (consume)
|
||||||
{
|
{
|
||||||
SX_LOGD("Event") << SX_T("事件被控件处理 id=", "Event consumed by control id=") << (*it)->getId();
|
if (!SxIsNoisyMsg(msg.message))
|
||||||
redrawDialogs = true; // 控件事件可能引起对话框状态变化,标记需要重绘对话框
|
SX_LOGD("Event") << SX_T("事件被控件处理:", "Event consumed by control: ")
|
||||||
|
<< SxMsgName(msg.message)
|
||||||
|
<< SX_T(" id=", " id=") << current->getId();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (hasVisibleNonModalDialog && controlVisualChanged)
|
||||||
|
redrawDialogs = true; // 只有普通控件真的发生视觉变化时,才补画非模态对话框
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 关键点⑦:事件处理后,如果对话框状态可能变化(例如控件事件引起的控件重绘可能导致对话框被覆盖),优先重绘对话框以确保界面响应及时;后续再根据 needResizeDirty 进行一次性重绘。
|
// 关键点⑦:事件处理后,如果对话框状态可能变化(例如控件事件引起的控件重绘可能导致对话框被覆盖),优先重绘对话框以确保界面响应及时;后续再根据 needResizeDirty 进行一次性重绘。
|
||||||
@@ -550,14 +571,6 @@ int Window::runEventLoop()
|
|||||||
// 批量通知控件“窗口尺寸变化”,并标记重绘
|
// 批量通知控件“窗口尺寸变化”,并标记重绘
|
||||||
for (auto& c : controls)
|
for (auto& c : controls)
|
||||||
adaptiveLayout(c, finalH, finalW);
|
adaptiveLayout(c, finalH, finalW);
|
||||||
for (auto& d : dialogs)
|
|
||||||
{
|
|
||||||
if (auto dd = dynamic_cast<Dialog*>(d.get()))
|
|
||||||
{
|
|
||||||
dd->setDirty(true);
|
|
||||||
dd->setInitialization(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//重绘窗口
|
//重绘窗口
|
||||||
Resize(nullptr, finalW, finalH);
|
Resize(nullptr, finalW, finalH);
|
||||||
|
|
||||||
@@ -585,6 +598,14 @@ int Window::runEventLoop()
|
|||||||
// 最终提交“当前已应用尺寸”(用于外部查询/下次比较)
|
// 最终提交“当前已应用尺寸”(用于外部查询/下次比较)
|
||||||
width = renderWidth;
|
width = renderWidth;
|
||||||
height = renderHeight;
|
height = renderHeight;
|
||||||
|
|
||||||
|
for (auto& d : dialogs)
|
||||||
|
{
|
||||||
|
if (auto dd = dynamic_cast<Dialog*>(d.get()))
|
||||||
|
{
|
||||||
|
dd->recenterInHostWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 统一批量绘制
|
// 统一批量绘制
|
||||||
@@ -782,7 +803,7 @@ void Window::pumpResizeIfNeeded()
|
|||||||
}
|
}
|
||||||
for (auto& d : dialogs)
|
for (auto& d : dialogs)
|
||||||
if (auto* dd = dynamic_cast<Dialog*>(d.get()))
|
if (auto* dd = dynamic_cast<Dialog*>(d.get()))
|
||||||
dd->setInitialization(true); // 强制对话框在新尺寸下重建布局/快照
|
dd->recenterInHostWindow(); // 窗口变化时仅重新居中,不拉伸 Dialog 自身
|
||||||
|
|
||||||
// 重绘
|
// 重绘
|
||||||
for (auto& c : controls) c->draw();
|
for (auto& c : controls) c->draw();
|
||||||
|
|||||||
+13
-1
@@ -195,11 +195,22 @@ int main()
|
|||||||
os.clear();
|
os.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectionAreaLabel->setAnchor(StellarX::Anchor::Right, StellarX::Anchor::Left);
|
||||||
|
selectionAreaLabel->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
|
||||||
|
|
||||||
selectionArea->addControl(std::move(selectionAreaLabel));
|
selectionArea->addControl(std::move(selectionAreaLabel));
|
||||||
for (auto& s : selectionAreaButton)
|
for (auto& s : selectionAreaButton)
|
||||||
|
{
|
||||||
|
s->setAnchor(StellarX::Anchor::Right, StellarX::Anchor::Left);
|
||||||
|
s->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
|
||||||
selectionArea->addControl(std::move(s));
|
selectionArea->addControl(std::move(s));
|
||||||
|
}
|
||||||
for (auto& s : selectionAreaButtonLabel)
|
for (auto& s : selectionAreaButtonLabel)
|
||||||
|
{
|
||||||
|
s->setAnchor(StellarX::Anchor::Right, StellarX::Anchor::Left);
|
||||||
|
s->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
|
||||||
selectionArea->addControl(std::move(s));
|
selectionArea->addControl(std::move(s));
|
||||||
|
}
|
||||||
//功能区控件
|
//功能区控件
|
||||||
//功能区总容器
|
//功能区总容器
|
||||||
auto function = std::make_unique<Canvas>(10, 170, 680, 70);
|
auto function = std::make_unique<Canvas>(10, 170, 680, 70);
|
||||||
@@ -536,7 +547,7 @@ int main()
|
|||||||
signedTogglePtr->setOnToggleOnListener([&]() {
|
signedTogglePtr->setOnToggleOnListener([&]() {
|
||||||
gSigned = true;
|
gSigned = true;
|
||||||
signedTogglePtr->setButtonText("有符号");
|
signedTogglePtr->setButtonText("有符号");
|
||||||
StellarX::MessageBox::showAsync(mainWindow, "有符号模式下,\n最高位为符号位,\n其余位为数值位。", "有符号模式");
|
StellarX::MessageBox::showModal(mainWindow, "有符号模式下,\n最高位为符号位,\n其余位为数值位。", "有符号模式");
|
||||||
// 立即刷新十进制显示:用当前位图算出新值,仅改 dec
|
// 立即刷新十进制显示:用当前位图算出新值,仅改 dec
|
||||||
auto cur = snapshotBits();
|
auto cur = snapshotBits();
|
||||||
const uint32_t u = [&] { uint32_t v = 0; for (int b = 0; b < 32; ++b) if (cur[b]) v |= (1u << b); return v; }();
|
const uint32_t u = [&] { uint32_t v = 0; for (int b = 0; b < 32; ++b) if (cur[b]) v |= (1u << b); return v; }();
|
||||||
@@ -562,6 +573,7 @@ int main()
|
|||||||
|
|
||||||
selectionArea->setAnchor(StellarX::Anchor::Right, StellarX::Anchor::Left);
|
selectionArea->setAnchor(StellarX::Anchor::Right, StellarX::Anchor::Left);
|
||||||
selectionArea->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
|
selectionArea->setLayoutMode(StellarX::LayoutMode::AnchorToEdges);
|
||||||
|
|
||||||
mainWindow.addControl(std::move(selectionArea));
|
mainWindow.addControl(std::move(selectionArea));
|
||||||
mainWindow.addControl(std::move(function));
|
mainWindow.addControl(std::move(function));
|
||||||
mainWindow.addControl(std::move(NumericalDisplayArea));
|
mainWindow.addControl(std::move(NumericalDisplayArea));
|
||||||
|
|||||||
Reference in New Issue
Block a user