Snapshot before max-resize threshold diagnosis
This commit is contained in:
+28
-1
@@ -159,6 +159,8 @@ bool Canvas::handleEvent(const ExMessage& msg)
|
|||||||
|
|
||||||
if (anyDirty)
|
if (anyDirty)
|
||||||
{
|
{
|
||||||
|
// 只要任一子控件因本次事件进入 dirty,就把这笔重绘继续向上汇报。
|
||||||
|
// 在托管模式下,这不会立即绘制,而是登记为 Canvas 对应的重绘 root。
|
||||||
if (!SxIsNoisyMsg(msg.message))
|
if (!SxIsNoisyMsg(msg.message))
|
||||||
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);
|
||||||
@@ -435,8 +437,9 @@ void Canvas::requestRepaint(Control* parent)
|
|||||||
{
|
{
|
||||||
if (shouldDeferManagedRepaint())
|
if (shouldDeferManagedRepaint())
|
||||||
{
|
{
|
||||||
|
// 托管路径:由 Window 统一决定这次是否只重画本 Canvas,还是升级为补画 Dialog / 整体场景。
|
||||||
if (auto* host = getHostWindow())
|
if (auto* host = getHostWindow())
|
||||||
host->requestManagedRepaint();
|
host->requestManagedRepaint(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,3 +477,27 @@ void Canvas::requestRepaint(Control* parent)
|
|||||||
onRequestRepaintAsRoot();
|
onRequestRepaintAsRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Canvas::canCommitManagedPartialRepaint() const
|
||||||
|
{
|
||||||
|
// Canvas 只有在“自己本体不脏 + 仍持有有效背景快照”时,
|
||||||
|
// 才能安全地做局部提交(即只更新内部脏子控件)。
|
||||||
|
return show && !dirty && hasValidBackgroundSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Canvas::commitManagedRepaint()
|
||||||
|
{
|
||||||
|
if (!show)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (canCommitManagedPartialRepaint())
|
||||||
|
{
|
||||||
|
// 快照完好:沿用 Canvas 自己已有的局部重绘逻辑。
|
||||||
|
requestRepaint(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自身已经脏了,或快照失效:必须升级为整 Canvas 重画。
|
||||||
|
this->dirty = true;
|
||||||
|
onRequestRepaintAsRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* @描述:
|
* @描述:
|
||||||
* 作为其他控件的父容器,提供统一的背景和边框样式。
|
* 作为其他控件的父容器,提供统一的背景和边框样式。
|
||||||
* 负责将事件传递给子控件并管理它们的绘制顺序。
|
* 负责将事件传递给子控件并管理它们的绘制顺序。
|
||||||
|
* 在托管重绘模式下,Canvas 通常作为一组子控件的安全重绘 root。
|
||||||
*
|
*
|
||||||
* @特性:
|
* @特性:
|
||||||
* - 支持四种矩形形状(普通、圆角,各有边框和无边框版本)
|
* - 支持四种矩形形状(普通、圆角,各有边框和无边框版本)
|
||||||
@@ -64,7 +65,12 @@ public:
|
|||||||
void setIsVisible(bool visible) override;
|
void setIsVisible(bool visible) override;
|
||||||
void setDirty(bool dirty) override;
|
void setDirty(bool dirty) override;
|
||||||
void onWindowResize() override;
|
void onWindowResize() override;
|
||||||
void requestRepaint(Control* parent)override;
|
// 托管模式下登记为 root;非托管模式下走局部或根级重绘
|
||||||
|
void requestRepaint(Control* parent)override;
|
||||||
|
// 判断当前 Canvas 是否可安全做局部提交
|
||||||
|
bool canCommitManagedPartialRepaint() const override;
|
||||||
|
// 托管收口阶段执行 Canvas 的真正重绘
|
||||||
|
void commitManagedRepaint() override;
|
||||||
//获取子控件列表
|
//获取子控件列表
|
||||||
std::vector<std::unique_ptr<Control>>& getControls() { return controls; }
|
std::vector<std::unique_ptr<Control>>& getControls() { return controls; }
|
||||||
private:
|
private:
|
||||||
|
|||||||
+37
-2
@@ -126,8 +126,10 @@ void Control::requestRepaint(Control* parent)
|
|||||||
{
|
{
|
||||||
if (shouldDeferManagedRepaint())
|
if (shouldDeferManagedRepaint())
|
||||||
{
|
{
|
||||||
|
// 托管路径:当前正在 Window 的事件分发阶段,不能立即绘制;
|
||||||
|
// 这里只登记 source,真正的 root 选择由 Window 在 requestManagedRepaint 中完成。
|
||||||
if (auto* host = getHostWindow())
|
if (auto* host = getHostWindow())
|
||||||
host->requestManagedRepaint();
|
host->requestManagedRepaint(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,8 +159,10 @@ void Control::onRequestRepaintAsRoot()
|
|||||||
{
|
{
|
||||||
if (shouldDeferManagedRepaint())
|
if (shouldDeferManagedRepaint())
|
||||||
{
|
{
|
||||||
|
// 即使已经冒泡到 root,只要还在托管分发期,也不能直接绘制;
|
||||||
|
// 仍然回到 Window 做统一提交。
|
||||||
if (auto* host = getHostWindow())
|
if (auto* host = getHostWindow())
|
||||||
host->requestManagedRepaint();
|
host->requestManagedRepaint(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +182,9 @@ bool Control::shouldDeferManagedRepaint() const
|
|||||||
return host && host->isManagedDispatchActive();
|
return host && host->isManagedDispatchActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取宿主 Window:
|
||||||
|
// - 顶层控件由 Window/addDialog 直接注入;
|
||||||
|
// - 子控件没有直接注入时,沿 parent 链向上回溯即可。
|
||||||
Window* Control::getHostWindow() const
|
Window* Control::getHostWindow() const
|
||||||
{
|
{
|
||||||
if (hostWindow)
|
if (hostWindow)
|
||||||
@@ -185,6 +192,18 @@ Window* Control::getHostWindow() const
|
|||||||
return parent ? parent->getHostWindow() : nullptr;
|
return parent ? parent->getHostWindow() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 托管重绘 root 选择规则:
|
||||||
|
// - 对于直接挂在 Window 下的控件,root 就是它自己;
|
||||||
|
// - 对于嵌套在 Canvas/TabControl/Dialog 内的子控件,沿 parent 向上找到与宿主 Window 同属一棵树的最上层控件。
|
||||||
|
Control* Control::getManagedRepaintRoot()
|
||||||
|
{
|
||||||
|
Control* root = this;
|
||||||
|
Window* host = getHostWindow();
|
||||||
|
while (root->parent && root->parent->getHostWindow() == host)
|
||||||
|
root = root->parent;
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
RECT Control::getBoundsRect() const
|
RECT Control::getBoundsRect() const
|
||||||
{
|
{
|
||||||
RECT rc{};
|
RECT rc{};
|
||||||
@@ -195,6 +214,22 @@ RECT Control::getBoundsRect() const
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Control::canCommitManagedPartialRepaint() const
|
||||||
|
{
|
||||||
|
// 基类默认不承诺自己能安全做局部提交;
|
||||||
|
// 只有 Canvas / TabControl / Dialog 这类“拥有完整背景语义”的 root 才会 override 为 true。
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Control::commitManagedRepaint()
|
||||||
|
{
|
||||||
|
if (!show)
|
||||||
|
return;
|
||||||
|
// 基类兜底:如果没有更具体的容器实现,就按根级重绘处理。
|
||||||
|
if (dirty)
|
||||||
|
onRequestRepaintAsRoot();
|
||||||
|
}
|
||||||
|
|
||||||
void Control::saveBackground(int x, int y, int w, int h)
|
void Control::saveBackground(int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* @描述:
|
* @描述:
|
||||||
* 提供控件的基本属性和方法,包括位置、尺寸、重绘标记等。
|
* 提供控件的基本属性和方法,包括位置、尺寸、重绘标记等。
|
||||||
* 实现绘图状态保存和恢复机制,确保控件绘制不影响全局状态。
|
* 实现绘图状态保存和恢复机制,确保控件绘制不影响全局状态。
|
||||||
|
* 同时提供“事件阶段登记、收口阶段统一提交”的托管重绘基础接口。
|
||||||
*
|
*
|
||||||
* @特性:
|
* @特性:
|
||||||
* - 定义控件基本属性(坐标、尺寸、脏标记)
|
* - 定义控件基本属性(坐标、尺寸、脏标记)
|
||||||
@@ -44,7 +45,7 @@ protected:
|
|||||||
Window* hostWindow = nullptr; // 宿主窗口(顶层由 Window 注入,子控件可沿 parent 回溯)
|
Window* hostWindow = nullptr; // 宿主窗口(顶层由 Window 注入,子控件可沿 parent 回溯)
|
||||||
bool dirty = true; // 是否重绘
|
bool dirty = true; // 是否重绘
|
||||||
bool show = true; // 是否显示
|
bool show = true; // 是否显示
|
||||||
bool eventVisualChanged = false; // 最近一次 handleEvent 是否真的引发了视觉变化
|
bool eventVisualChanged = false; // 最近一次 handleEvent 是否真的引发了视觉变化(用于上层判断是否需要登记重绘)
|
||||||
|
|
||||||
/* == 布局模式 == */
|
/* == 布局模式 == */
|
||||||
StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式
|
StellarX::LayoutMode layoutMode = StellarX::LayoutMode::Fixed; // 布局模式
|
||||||
@@ -81,10 +82,11 @@ public:
|
|||||||
discardBackground();
|
discardBackground();
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
//向上请求重绘
|
// 向上请求重绘:普通路径交给父容器,托管路径则登记到 Window
|
||||||
virtual void requestRepaint(Control* parent);
|
virtual void requestRepaint(Control* parent);
|
||||||
//根控件/无父时触发重绘
|
// 根控件/无父时触发重绘
|
||||||
virtual void onRequestRepaintAsRoot();
|
virtual void onRequestRepaintAsRoot();
|
||||||
|
// 当前是否处于 Window 托管分发阶段;若为真,则不应立即画
|
||||||
bool shouldDeferManagedRepaint() const;
|
bool shouldDeferManagedRepaint() const;
|
||||||
protected:
|
protected:
|
||||||
//保存背景快照
|
//保存背景快照
|
||||||
@@ -127,8 +129,12 @@ public:
|
|||||||
void setParent(Control* parent) { this->parent = parent; }
|
void setParent(Control* parent) { this->parent = parent; }
|
||||||
//设置宿主窗口(通常仅由顶层 Window/对话框注入)
|
//设置宿主窗口(通常仅由顶层 Window/对话框注入)
|
||||||
virtual void setHostWindow(Window* host) { this->hostWindow = host; }
|
virtual void setHostWindow(Window* host) { this->hostWindow = host; }
|
||||||
Window* getHostWindow() const;
|
Window* getHostWindow() const; // 获取宿主 Window;子控件可沿 parent 向上回溯
|
||||||
RECT getBoundsRect() const;
|
RECT getBoundsRect() const; // 获取当前控件外接矩形,用于覆盖/相交判断
|
||||||
|
Control* getManagedRepaintRoot(); // 找到本控件对应的托管重绘 root
|
||||||
|
bool hasValidBackgroundSnapshot() const { return hasSnap && saveBkImage != nullptr; } // 当前是否持有可用于局部恢复的快照
|
||||||
|
virtual bool canCommitManagedPartialRepaint() const; // 当前 root 是否可安全做“局部提交”而非整 root 重画
|
||||||
|
virtual void commitManagedRepaint(); // 托管收口阶段真正执行绘制的入口
|
||||||
//设置是否重绘
|
//设置是否重绘
|
||||||
virtual void setDirty(bool dirty) { this->dirty = dirty; }
|
virtual void setDirty(bool dirty) { this->dirty = dirty; }
|
||||||
//检查控件是否可见
|
//检查控件是否可见
|
||||||
@@ -137,6 +143,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; }
|
bool didEventAffectVisual() const { return eventVisualChanged; }
|
||||||
//用来检查对话框是否模态,其他控件不用实现
|
//用来检查对话框是否模态,其他控件不用实现
|
||||||
virtual bool model()const = 0;
|
virtual bool model()const = 0;
|
||||||
|
|||||||
+27
-1
@@ -760,8 +760,10 @@ void Dialog::requestRepaint(Control* parent)
|
|||||||
{
|
{
|
||||||
if (shouldDeferManagedRepaint())
|
if (shouldDeferManagedRepaint())
|
||||||
{
|
{
|
||||||
|
// 非模态 Dialog 在 Window 主循环中也走托管提交;
|
||||||
|
// 这样底层控件和对话框的绘制顺序由 Window 统一收口控制。
|
||||||
if (auto* host = getHostWindow())
|
if (auto* host = getHostWindow())
|
||||||
host->requestManagedRepaint();
|
host->requestManagedRepaint(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -774,3 +776,27 @@ void Dialog::requestRepaint(Control* parent)
|
|||||||
else
|
else
|
||||||
onRequestRepaintAsRoot();
|
onRequestRepaintAsRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Dialog::canCommitManagedPartialRepaint() const
|
||||||
|
{
|
||||||
|
// Dialog 只有在“自身底板不脏 + 仍持有有效背景快照”时,
|
||||||
|
// 才能安全地只更新内部按钮,而不重画整个对话框底板。
|
||||||
|
return show && !dirty && hasValidBackgroundSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dialog::commitManagedRepaint()
|
||||||
|
{
|
||||||
|
if (!show)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (canCommitManagedPartialRepaint())
|
||||||
|
{
|
||||||
|
// 背景快照完好:沿用 Dialog 自己已有的局部重绘路径。
|
||||||
|
requestRepaint(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对话框底板本身已脏,或快照失效:必须整 Dialog 重画。
|
||||||
|
this->dirty = true;
|
||||||
|
onRequestRepaintAsRoot();
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* @描述:
|
* @描述:
|
||||||
* 实现完整的对话框功能,支持多种按钮组合和异步结果回调。
|
* 实现完整的对话框功能,支持多种按钮组合和异步结果回调。
|
||||||
* 自动处理布局、背景保存恢复和生命周期管理。
|
* 自动处理布局、背景保存恢复和生命周期管理。
|
||||||
|
* 在窗口托管重绘模式下,Dialog 自身也是一个独立的重绘 root。
|
||||||
*
|
*
|
||||||
* @特性:
|
* @特性:
|
||||||
* - 支持六种标准消息框类型(OK、YesNo、YesNoCancel等)
|
* - 支持六种标准消息框类型(OK、YesNo、YesNoCancel等)
|
||||||
@@ -103,7 +104,7 @@ public:
|
|||||||
// 关闭对话框
|
// 关闭对话框
|
||||||
void Close();
|
void Close();
|
||||||
//初始化
|
//初始化
|
||||||
void setInitialization(bool init);
|
void setInitialization(bool init); // 历史接口:当前语义更接近“请求重新布局/重建”
|
||||||
// 宿主窗口变化时仅重新居中,不拉伸 Dialog 自身
|
// 宿主窗口变化时仅重新居中,不拉伸 Dialog 自身
|
||||||
void recenterInHostWindow();
|
void recenterInHostWindow();
|
||||||
|
|
||||||
@@ -123,10 +124,12 @@ private:
|
|||||||
// 依据当前 Dialog 的 x/y/width/height 重新创建标题和按钮
|
// 依据当前 Dialog 的 x/y/width/height 重新创建标题和按钮
|
||||||
void rebuildChrome();
|
void rebuildChrome();
|
||||||
void addControl(std::unique_ptr<Control> control);
|
void addControl(std::unique_ptr<Control> control);
|
||||||
|
bool canCommitManagedPartialRepaint() const override; // 判断当前 Dialog 是否可安全做局部提交
|
||||||
|
void commitManagedRepaint() override; // 托管收口阶段执行 Dialog 的真正重绘
|
||||||
|
|
||||||
// 清除所有控件
|
// 清除所有控件
|
||||||
void clearControls();
|
void clearControls();
|
||||||
//创建对话框按钮
|
//创建对话框按钮
|
||||||
std::unique_ptr<Button> createDialogButton(int x, int y, const std::string& text);
|
std::unique_ptr<Button> createDialogButton(int x, int y, const std::string& text);
|
||||||
void requestRepaint(Control* parent) override;
|
void requestRepaint(Control* parent) override; // 托管模式下登记为 Dialog root;非托管模式下立即更新内部按钮
|
||||||
};
|
};
|
||||||
|
|||||||
+25
-1
@@ -418,8 +418,9 @@ void TabControl::requestRepaint(Control* parent)
|
|||||||
{
|
{
|
||||||
if (shouldDeferManagedRepaint())
|
if (shouldDeferManagedRepaint())
|
||||||
{
|
{
|
||||||
|
// 托管路径:TabControl 作为“页签栏 + 当前页面”的统一重绘 root 登记到 Window。
|
||||||
if (auto* host = getHostWindow())
|
if (auto* host = getHostWindow())
|
||||||
host->requestManagedRepaint();
|
host->requestManagedRepaint(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -437,3 +438,26 @@ void TabControl::requestRepaint(Control* parent)
|
|||||||
else
|
else
|
||||||
onRequestRepaintAsRoot();
|
onRequestRepaintAsRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TabControl::canCommitManagedPartialRepaint() const
|
||||||
|
{
|
||||||
|
// TabControl 只有在自己本体不脏且背景快照有效时,才允许只更新脏页签/脏页面。
|
||||||
|
return show && !dirty && hasValidBackgroundSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TabControl::commitManagedRepaint()
|
||||||
|
{
|
||||||
|
if (!show)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (canCommitManagedPartialRepaint())
|
||||||
|
{
|
||||||
|
// 页签栏和页面基线都还有效:沿用原有局部重绘逻辑。
|
||||||
|
requestRepaint(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自身布局或背景已经变化:升级为整 TabControl 重画。
|
||||||
|
this->dirty = true;
|
||||||
|
onRequestRepaintAsRoot();
|
||||||
|
}
|
||||||
|
|||||||
+4
-1
@@ -4,6 +4,7 @@
|
|||||||
* @描述:
|
* @描述:
|
||||||
* 提供页签栏布局(上/下/左/右)、选中切换、页内容区域定位;
|
* 提供页签栏布局(上/下/左/右)、选中切换、页内容区域定位;
|
||||||
* 与 Button 一起工作,支持窗口大小变化、可见性联动与脏区重绘。
|
* 与 Button 一起工作,支持窗口大小变化、可见性联动与脏区重绘。
|
||||||
|
* 在托管重绘模式下,TabControl 作为“页签栏 + 当前页面”的统一重绘 root。
|
||||||
*
|
*
|
||||||
* @特性:
|
* @特性:
|
||||||
* - 页签栏四向排列(Top / Bottom / Left / Right)
|
* - 页签栏四向排列(Top / Bottom / Left / Right)
|
||||||
@@ -72,5 +73,7 @@ public:
|
|||||||
//设置脏区并请求重绘
|
//设置脏区并请求重绘
|
||||||
void setDirty(bool dirty) override;
|
void setDirty(bool dirty) override;
|
||||||
//请求父控件重绘
|
//请求父控件重绘
|
||||||
void requestRepaint(Control* parent)override;
|
void requestRepaint(Control* parent)override; // 托管模式下登记为 root;非托管模式下局部更新脏按钮/脏页面
|
||||||
|
bool canCommitManagedPartialRepaint() const override; // 判断当前 TabControl 是否可安全做局部提交
|
||||||
|
void commitManagedRepaint() override; // 托管收口阶段执行 TabControl 的真正重绘
|
||||||
};
|
};
|
||||||
|
|||||||
+137
-9
@@ -27,14 +27,62 @@ static const char* SxMsgName(UINT m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool SxRectsIntersect(const RECT& a, const RECT& b)
|
||||||
|
{
|
||||||
|
return a.left < b.right && a.right > b.left &&
|
||||||
|
a.top < b.bottom && a.bottom > b.top;
|
||||||
|
}
|
||||||
|
|
||||||
bool Window::isManagedDispatchActive() const
|
bool Window::isManagedDispatchActive() const
|
||||||
{
|
{
|
||||||
return managedDispatchActive;
|
return managedDispatchActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::requestManagedRepaint()
|
/**
|
||||||
|
* requestManagedRepaint(source)
|
||||||
|
* 作用:在“事件分发期”登记一笔托管重绘请求,而不是立即绘制。
|
||||||
|
* 关键点:
|
||||||
|
* - source 是真正发生视觉变化的控件;
|
||||||
|
* - root 是后续真正安全重绘的最小层级(通常是顶层控件/容器,或 Dialog 自身);
|
||||||
|
* - coverage 记录这次变化影响的范围,用于判断哪些上层 Dialog 需要补画。
|
||||||
|
*/
|
||||||
|
void Window::requestManagedRepaint(Control* source)
|
||||||
{
|
{
|
||||||
|
if (!source)
|
||||||
|
return;
|
||||||
|
|
||||||
managedSceneDirty = true;
|
managedSceneDirty = true;
|
||||||
|
Control* root = source->getManagedRepaintRoot();
|
||||||
|
if (!root)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RECT coverage = root->getBoundsRect();
|
||||||
|
if (root->canCommitManagedPartialRepaint())
|
||||||
|
coverage = source->getBoundsRect();
|
||||||
|
|
||||||
|
for (auto& item : managedRepaintItems)
|
||||||
|
{
|
||||||
|
if (item.root == root)
|
||||||
|
{
|
||||||
|
item.coverage.left = (std::min)(item.coverage.left, coverage.left);
|
||||||
|
item.coverage.top = (std::min)(item.coverage.top, coverage.top);
|
||||||
|
item.coverage.right = (std::max)(item.coverage.right, coverage.right);
|
||||||
|
item.coverage.bottom = (std::max)(item.coverage.bottom, coverage.bottom);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedRepaintItem item;
|
||||||
|
item.root = root;
|
||||||
|
item.coverage = coverage;
|
||||||
|
managedRepaintItems.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空本轮托管重绘状态;通常在 flush/全场景重绘/resize 收口后调用
|
||||||
|
void Window::clearManagedRepaintState()
|
||||||
|
{
|
||||||
|
managedSceneDirty = false;
|
||||||
|
managedRepaintItems.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::drawWindowBackground()
|
void Window::drawWindowBackground()
|
||||||
@@ -73,17 +121,53 @@ void Window::redrawScene(bool forceControlsDirty, bool forceDialogsDirty)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* flushManagedRepaint()
|
||||||
|
* 作用:提交当前事件分发阶段累计的托管重绘请求。
|
||||||
|
* 提交顺序:
|
||||||
|
* 1)先根据 coverage 找出需要补画的非模态 Dialog;
|
||||||
|
* 2)再按 Window::controls 的层级顺序提交受影响的普通 root;
|
||||||
|
* 3)最后把相交的 Dialog 补画回最上层。
|
||||||
|
* 说明:
|
||||||
|
* - 这里不做整场景重画,而是只画本轮登记的 root;
|
||||||
|
* - 之所以按 controls 顺序提交,而不是按登记顺序,是为了保持顶层控件原有的 z-order。
|
||||||
|
*/
|
||||||
void Window::flushManagedRepaint()
|
void Window::flushManagedRepaint()
|
||||||
{
|
{
|
||||||
if (!managedSceneDirty || !hWnd)
|
if (!managedSceneDirty || !hWnd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BeginBatchDraw();
|
BeginBatchDraw();
|
||||||
redrawScene(true, true);
|
std::vector<Control*> overlayDialogs;
|
||||||
|
|
||||||
|
for (auto& item : managedRepaintItems)
|
||||||
|
collectManagedDialogOverlays(item.root, item.coverage, overlayDialogs);
|
||||||
|
|
||||||
|
for (auto& control : controls)
|
||||||
|
{
|
||||||
|
for (auto& item : managedRepaintItems)
|
||||||
|
{
|
||||||
|
if (item.root == control.get() && item.root && item.root->IsVisible())
|
||||||
|
{
|
||||||
|
item.root->commitManagedRepaint();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& dialog : overlayDialogs)
|
||||||
|
{
|
||||||
|
if (!dialog || !dialog->IsVisible())
|
||||||
|
continue;
|
||||||
|
dialog->setDirty(true);
|
||||||
|
dialog->draw();
|
||||||
|
}
|
||||||
|
|
||||||
EndBatchDraw();
|
EndBatchDraw();
|
||||||
managedSceneDirty = false;
|
clearManagedRepaintState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 合成一条 WM_MOUSEMOVE 并直接分发给 Window 顶层控件;常用于同步 hover 状态
|
||||||
void Window::dispatchSyntheticMouseMoveToControls(short x, short y)
|
void Window::dispatchSyntheticMouseMoveToControls(short x, short y)
|
||||||
{
|
{
|
||||||
ExMessage mm{};
|
ExMessage mm{};
|
||||||
@@ -94,6 +178,42 @@ void Window::dispatchSyntheticMouseMoveToControls(short x, short y)
|
|||||||
(*it)->handleEvent(mm);
|
(*it)->handleEvent(mm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* collectManagedDialogOverlays(repaintRoot, coverage, overlays)
|
||||||
|
* 作用:找出在本轮提交后需要重新盖到最上层的非模态 Dialog。
|
||||||
|
* 规则:
|
||||||
|
* - 如果 repaintRoot 本身就是 Dialog,则从它自己开始往上层 Dialog 收集;
|
||||||
|
* - 如果 repaintRoot 是普通控件,则收集所有与 coverage 相交的可见 Dialog。
|
||||||
|
*/
|
||||||
|
void Window::collectManagedDialogOverlays(Control* repaintRoot, const RECT& coverage, std::vector<Control*>& overlays)
|
||||||
|
{
|
||||||
|
size_t startIdx = 0;
|
||||||
|
if (auto* dialogRoot = dynamic_cast<Dialog*>(repaintRoot))
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < dialogs.size(); ++i)
|
||||||
|
{
|
||||||
|
if (dialogs[i].get() == dialogRoot)
|
||||||
|
{
|
||||||
|
startIdx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = startIdx; i < dialogs.size(); ++i)
|
||||||
|
{
|
||||||
|
Control* dialog = dialogs[i].get();
|
||||||
|
if (!dialog || !dialog->IsVisible())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (dialog == repaintRoot || SxRectsIntersect(dialog->getBoundsRect(), coverage))
|
||||||
|
{
|
||||||
|
if (std::find(overlays.begin(), overlays.end(), dialog) == overlays.end())
|
||||||
|
overlays.push_back(dialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ApplyResizableStyle
|
* ApplyResizableStyle
|
||||||
* 作用:统一设置可拉伸/裁剪样式,并按开关使用 WS_EX_COMPOSITED(合成双缓冲)。
|
* 作用:统一设置可拉伸/裁剪样式,并按开关使用 WS_EX_COMPOSITED(合成双缓冲)。
|
||||||
@@ -364,6 +484,7 @@ void Window::draw()
|
|||||||
BeginBatchDraw();
|
BeginBatchDraw();
|
||||||
redrawScene(true, true);
|
redrawScene(true, true);
|
||||||
EndBatchDraw();
|
EndBatchDraw();
|
||||||
|
clearManagedRepaintState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -406,6 +527,7 @@ void Window::draw(std::string imagePath)
|
|||||||
BeginBatchDraw();
|
BeginBatchDraw();
|
||||||
redrawScene(true, true);
|
redrawScene(true, true);
|
||||||
EndBatchDraw();
|
EndBatchDraw();
|
||||||
|
clearManagedRepaintState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------- 事件循环 ----------------
|
// ---------------- 事件循环 ----------------
|
||||||
@@ -416,7 +538,9 @@ void Window::draw(std::string imagePath)
|
|||||||
* 关键策略:
|
* 关键策略:
|
||||||
* - WM_SIZE:始终更新 pendingW/H(即使在拉伸中也只记录不立即绘制);
|
* - WM_SIZE:始终更新 pendingW/H(即使在拉伸中也只记录不立即绘制);
|
||||||
* - needResizeDirty:当尺寸确实变化时置位,随后在循环尾进行一次性重绘;
|
* - needResizeDirty:当尺寸确实变化时置位,随后在循环尾进行一次性重绘;
|
||||||
* - 非模态对话框优先消费事件(顶层从后往前);再交给普通控件。
|
* - 非模态对话框优先消费事件(顶层从后往前);再交给普通控件;
|
||||||
|
* - managedDispatchActive=true 期间,控件 requestRepaint 不会立即画,而是登记到 managedRepaintItems;
|
||||||
|
* - 事件尾通过 flushManagedRepaint 提交本轮 root 重绘,再按需补画 Dialog。
|
||||||
*/
|
*/
|
||||||
int Window::runEventLoop()
|
int Window::runEventLoop()
|
||||||
{
|
{
|
||||||
@@ -473,7 +597,8 @@ int Window::runEventLoop()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 输入优先:先给顶层“非模态对话框”,再传给普通控件
|
// 输入优先:先给顶层“非模态对话框”,再传给普通控件。
|
||||||
|
// 在 managedDispatchActive 期间,控件只改状态并登记重绘 root,不直接画。
|
||||||
managedDispatchActive = true;
|
managedDispatchActive = true;
|
||||||
for (auto it = dialogs.rbegin(); it != dialogs.rend(); ++it)
|
for (auto it = dialogs.rbegin(); it != dialogs.rend(); ++it)
|
||||||
{
|
{
|
||||||
@@ -513,7 +638,8 @@ int Window::runEventLoop()
|
|||||||
managedDispatchActive = false;
|
managedDispatchActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//如果有对话框打开或者关闭强制重绘
|
// 对话框打开/关闭属于全局层级变化:这里仍然使用整场景重绘兜底,
|
||||||
|
// 并在结束后清空本轮托管重绘登记,避免旧的 root 请求延后提交。
|
||||||
bool needredraw = false;
|
bool needredraw = false;
|
||||||
if(dialogOpen)
|
if(dialogOpen)
|
||||||
{
|
{
|
||||||
@@ -550,10 +676,11 @@ int Window::runEventLoop()
|
|||||||
EndBatchDraw();
|
EndBatchDraw();
|
||||||
needredraw = false;
|
needredraw = false;
|
||||||
dialogOpen = false;
|
dialogOpen = false;
|
||||||
managedSceneDirty = false;
|
clearManagedRepaintState();
|
||||||
|
|
||||||
}
|
}
|
||||||
// —— 统一收口(needResizeDirty 为真时执行一次性重绘)——
|
// —— 统一收口(needResizeDirty 为真时执行一次性重绘)——
|
||||||
|
// resize 会改变布局和背景基线,因此仍然走整场景重绘,而不是局部 root 提交。
|
||||||
if (needResizeDirty)
|
if (needResizeDirty)
|
||||||
{
|
{
|
||||||
SX_LOGI("Resize") << SX_T("调整窗口尺寸开始:width=","Resize settle start: width=") << width << " height=" << height;
|
SX_LOGI("Resize") << SX_T("调整窗口尺寸开始:width=","Resize settle start: width=") << width << " height=" << height;
|
||||||
@@ -622,9 +749,10 @@ int Window::runEventLoop()
|
|||||||
SX_LOGI("Resize") << SX_T("尺寸调整已完成:width=","Resize settle done: width=") << width << " height=" << height;
|
SX_LOGI("Resize") << SX_T("尺寸调整已完成:width=","Resize settle done: width=") << width << " height=" << height;
|
||||||
|
|
||||||
needResizeDirty = false; // 收口完成,清标志
|
needResizeDirty = false; // 收口完成,清标志
|
||||||
managedSceneDirty = false;
|
clearManagedRepaintState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 普通输入事件收口:只在没有 resize / 对话框开关这种全局变化时,才提交本轮托管重绘。
|
||||||
if (!needResizeDirty && !dialogOpen && !dialogClose)
|
if (!needResizeDirty && !dialogOpen && !dialogClose)
|
||||||
flushManagedRepaint();
|
flushManagedRepaint();
|
||||||
|
|
||||||
@@ -792,7 +920,7 @@ void Window::pumpResizeIfNeeded()
|
|||||||
ValidateRect(hWnd, nullptr);
|
ValidateRect(hWnd, nullptr);
|
||||||
|
|
||||||
needResizeDirty = false;
|
needResizeDirty = false;
|
||||||
managedSceneDirty = false;
|
clearManagedRepaintState();
|
||||||
}
|
}
|
||||||
void Window::scheduleResizeFromModal(int w, int h)
|
void Window::scheduleResizeFromModal(int w, int h)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
* - 提供一个基于 Win32 + EasyX 的“可拉伸且稳定不抖”的窗口容器。
|
* - 提供一个基于 Win32 + EasyX 的“可拉伸且稳定不抖”的窗口容器。
|
||||||
* - 通过消息过程子类化(WndProcThunk)接管关键消息(WM_SIZING/WM_SIZE/...)。
|
* - 通过消息过程子类化(WndProcThunk)接管关键消息(WM_SIZING/WM_SIZE/...)。
|
||||||
* - 将“几何变化记录(pendingW/H)”与“统一收口重绘(needResizeDirty)”解耦。
|
* - 将“几何变化记录(pendingW/H)”与“统一收口重绘(needResizeDirty)”解耦。
|
||||||
|
* - 在事件分发阶段只改状态并登记重绘 root,在事件尾统一提交,减少分离绘制造成的闪烁。
|
||||||
*
|
*
|
||||||
* 关键点(与 .cpp 中实现对应):
|
* 关键点(与 .cpp 中实现对应):
|
||||||
* - isSizing:处于交互拉伸阶段时,冻结重绘;松手后统一重绘,防止抖动。
|
* - isSizing:处于交互拉伸阶段时,冻结重绘;松手后统一重绘,防止抖动。
|
||||||
* - WM_SIZING:只做“最小尺寸夹紧”,不回滚矩形、不做对齐;把其余交给系统。
|
* - WM_SIZING:只做“最小尺寸夹紧”,不回滚矩形、不做对齐;把其余交给系统。
|
||||||
* - WM_GETMINMAXINFO:按最小“客户区”换算到“窗口矩形”,提供系统层最小轨迹值。
|
* - WM_GETMINMAXINFO:按最小“客户区”换算到“窗口矩形”,提供系统层最小轨迹值。
|
||||||
* - runEventLoop:只记录 WM_SIZE 的新尺寸;真正绘制放在 needResizeDirty 时集中处理。
|
* - runEventLoop:输入事件先分发给对话框/控件,控件只登记重绘请求;真正绘制在分发或 resize 收口时统一处理。
|
||||||
*/
|
*/
|
||||||
//fuck windows
|
//fuck windows
|
||||||
//fuck win32
|
//fuck win32
|
||||||
@@ -52,14 +53,20 @@ class Window
|
|||||||
std::string bkImageFile; // 背景图文件路径(loadimage 用)
|
std::string bkImageFile; // 背景图文件路径(loadimage 用)
|
||||||
|
|
||||||
// —— 控件/对话框 ——(容器内的普通控件与非模态对话框)
|
// —— 控件/对话框 ——(容器内的普通控件与非模态对话框)
|
||||||
std::vector<std::unique_ptr<Control>> controls;
|
std::vector<std::unique_ptr<Control>> controls; // 普通顶层控件;绘制顺序也决定层级顺序
|
||||||
std::vector<std::unique_ptr<Control>> dialogs;
|
std::vector<std::unique_ptr<Control>> dialogs; // 非模态对话框;始终位于普通控件之上
|
||||||
bool managedDispatchActive = false; // 事件分发期:控件只改状态,不立即画
|
bool managedDispatchActive = false; // 事件分发期:控件只改状态并登记重绘,不立即画
|
||||||
bool managedSceneDirty = false; // 本轮分发是否登记了统一重绘请求
|
bool managedSceneDirty = false; // 当前分发轮次是否已经登记了至少一笔托管重绘请求
|
||||||
|
struct ManagedRepaintItem // 托管重绘项:记录由哪个控件发起、需要重绘的根控件和覆盖范围(用于后续判断哪些对话框需要补画)
|
||||||
|
{
|
||||||
|
Control* root = nullptr; // 顶层重绘根(直接挂在 Window 下的控件,或 Dialog 自身)
|
||||||
|
RECT coverage{}; // 本轮脏区覆盖范围;用于判断哪些上层 Dialog 需要补画
|
||||||
|
};
|
||||||
|
std::vector<ManagedRepaintItem> managedRepaintItems; // 本轮事件分发累计的重绘项
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool dialogClose = false; // 项目内使用的状态位,对话框关闭标志
|
bool dialogClose = false; // 项目内使用的状态位,对话框关闭标志
|
||||||
mutable bool dialogOpen = false; // 项目内使用的状态位,对话框打开标志
|
mutable bool dialogOpen = false; // 项目内使用的状态位,对话框打开标志
|
||||||
|
|
||||||
// —— 构造/析构 ——(仅初始化成员;实际样式与子类化在 draw() 中完成)
|
// —— 构造/析构 ——(仅初始化成员;实际样式与子类化在 draw() 中完成)
|
||||||
Window(int width, int height, int mode);
|
Window(int width, int height, int mode);
|
||||||
@@ -94,15 +101,27 @@ public:
|
|||||||
std::string getBkImageFile() const;
|
std::string getBkImageFile() const;
|
||||||
std::vector<std::unique_ptr<Control>>& getControls();
|
std::vector<std::unique_ptr<Control>>& getControls();
|
||||||
|
|
||||||
// —— 尺寸调整 ——(供内部与外部调用的尺寸变化处理)
|
// —— 尺寸调整 / 托管重绘 ——(事件阶段登记,收口阶段提交)
|
||||||
void pumpResizeIfNeeded(); // 执行一次统一收口重绘
|
|
||||||
void scheduleResizeFromModal(int w, int h);
|
// 执行一次 resize 收口 + 统一重绘
|
||||||
bool isManagedDispatchActive() const;
|
void pumpResizeIfNeeded();
|
||||||
void requestManagedRepaint();
|
// 供模态 Dialog 上报宿主窗口尺寸变化
|
||||||
void flushManagedRepaint();
|
void scheduleResizeFromModal(int w, int h);
|
||||||
|
// 当前是否处于“事件只改状态,不立即画”的阶段
|
||||||
|
bool isManagedDispatchActive() const;
|
||||||
|
// 记录一笔由 source 发起的托管重绘请求
|
||||||
|
void requestManagedRepaint(Control* source);
|
||||||
|
// 在事件收口阶段提交本轮登记的 root 重绘
|
||||||
|
void flushManagedRepaint();
|
||||||
private:
|
private:
|
||||||
void adaptiveLayout(std::unique_ptr<Control>& c, const int finalH, const int finalW);
|
void adaptiveLayout(std::unique_ptr<Control>& c, const int finalH, const int finalW);
|
||||||
|
// resize / 初次绘制 / 对话框开关这类全局场景的整场景重绘
|
||||||
void redrawScene(bool forceControlsDirty, bool forceDialogsDirty);
|
void redrawScene(bool forceControlsDirty, bool forceDialogsDirty);
|
||||||
void drawWindowBackground();
|
void drawWindowBackground();
|
||||||
void dispatchSyntheticMouseMoveToControls(short x, short y);
|
// 合成 WM_MOUSEMOVE,用于同步底层 hover 状态
|
||||||
|
void dispatchSyntheticMouseMoveToControls(short x, short y);
|
||||||
|
// 清空本轮托管重绘登记
|
||||||
|
void clearManagedRepaintState();
|
||||||
|
// 找出需要补画到最上层的对话框
|
||||||
|
void collectManagedDialogOverlays(Control* repaintRoot, const RECT& coverage, std::vector<Control*>& overlays);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
# 修改总览-20260409
|
||||||
|
|
||||||
|
## 说明
|
||||||
|
|
||||||
|
- 本目录用于记录本轮协作中对框架做过的修改。
|
||||||
|
- 记录范围覆盖:
|
||||||
|
- 早期未建 Git 基线前的修改
|
||||||
|
- 已提交到仓库的阶段性修改
|
||||||
|
- 当前工作区中尚未提交的第二阶段重绘架构与注释整理
|
||||||
|
- 其中相对最原始版本、但未落到 Git 基线的那部分内容,依据协作过程与当前代码状态回溯整理。
|
||||||
|
|
||||||
|
## 阶段概览
|
||||||
|
|
||||||
|
1. 初始基线
|
||||||
|
- 提交:`dde570a`
|
||||||
|
- 含义:建立仓库后的第一版基线
|
||||||
|
|
||||||
|
2. 第一阶段:基础治理与确定性问题修复
|
||||||
|
- 主要内容:
|
||||||
|
- 资源所有权收紧
|
||||||
|
- 析构顺序修正
|
||||||
|
- `Table / Label / Dialog / Button / TextBox` 等基础问题修复
|
||||||
|
|
||||||
|
3. 第二阶段:快照与对话框行为收口
|
||||||
|
- 提交:`4a6e153`
|
||||||
|
- 提交:`7f8431a`
|
||||||
|
- 主要内容:
|
||||||
|
- 快照“作废”和“回贴”语义拆分
|
||||||
|
- `Dialog` 在窗口变化时只重新居中
|
||||||
|
- 模态/非模态对话框的残影、穿透、关闭后 hover 清理等问题修复
|
||||||
|
|
||||||
|
4. 第三阶段:统一提交重绘(第一版)
|
||||||
|
- 提交:`b07a4ec`
|
||||||
|
- 主要内容:
|
||||||
|
- 将事件阶段的“改状态”与绘制阶段的“提交”分离
|
||||||
|
- 窗口开始托管控件在事件分发期间的重绘请求
|
||||||
|
|
||||||
|
5. 第四阶段:统一提交重绘(第二版,当前工作区)
|
||||||
|
- 状态:未提交
|
||||||
|
- 主要内容:
|
||||||
|
- 从“整场景统一重绘”推进到“按 root 登记并选择性补画对话框”
|
||||||
|
- 为 `Canvas / TabControl / Dialog` 建立托管重绘提交语义
|
||||||
|
- 补充相关注释
|
||||||
|
|
||||||
|
6. 测试与文档
|
||||||
|
- 主要内容:
|
||||||
|
- `KEY == 4` 综合回归用例
|
||||||
|
- 日志降噪
|
||||||
|
- 模板体系与开发记录目录建立
|
||||||
|
|
||||||
|
## 记录索引
|
||||||
|
|
||||||
|
### BUG
|
||||||
|
|
||||||
|
- [BUG-20260409-0001 对话框重绘、快照残留与遮挡交互异常](./BUG/BUG-20260409-0001-对话框重绘快照与遮挡交互异常.md)
|
||||||
|
- [BUG-20260409-0002 基础控件生命周期与边界条件问题](./BUG/BUG-20260409-0002-基础控件生命周期与边界条件问题.md)
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- [Fix-BUG-20260409-0001 对话框重绘、快照残留与遮挡交互异常](./Fix/Fix-BUG-20260409-0001-对话框重绘快照与遮挡交互异常.md)
|
||||||
|
- [Fix-BUG-20260409-0002 基础控件生命周期与边界条件问题](./Fix/Fix-BUG-20260409-0002-基础控件生命周期与边界条件问题.md)
|
||||||
|
|
||||||
|
### 功能变更
|
||||||
|
|
||||||
|
- [Feature-20260409-0001 基础资源所有权与生命周期收口](./功能变更/Feature-20260409-0001-基础资源所有权与生命周期收口.md)
|
||||||
|
- [Feature-20260409-0002 Dialog 与 MessageBox 行为调整](./功能变更/Feature-20260409-0002-Dialog与MessageBox行为调整.md)
|
||||||
|
- [Feature-20260409-0003 输入事件、hover 与遮挡交互调整](./功能变更/Feature-20260409-0003-输入事件与遮挡交互调整.md)
|
||||||
|
- [Feature-20260409-0004 测试用例与可观测性调整](./功能变更/Feature-20260409-0004-测试用例与可观测性调整.md)
|
||||||
|
- [Feature-20260409-0005 开发记录与模板体系整理](./功能变更/Feature-20260409-0005-开发记录与模板体系整理.md)
|
||||||
|
|
||||||
|
### 模块
|
||||||
|
|
||||||
|
- [Module-20260409-0001 Window 托管重绘与覆盖合成机制](./模块/Module-20260409-0001-Window托管重绘与覆盖合成机制.md)
|
||||||
|
|
||||||
|
## 覆盖关系
|
||||||
|
|
||||||
|
### 核心代码文件覆盖
|
||||||
|
|
||||||
|
- `Window.h / Window.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `BUG-20260409-0001`
|
||||||
|
- `Fix-BUG-20260409-0001`
|
||||||
|
- `Feature-20260409-0002`
|
||||||
|
- `Feature-20260409-0003`
|
||||||
|
- `Module-20260409-0001`
|
||||||
|
|
||||||
|
- `Control.h / Control.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `BUG-20260409-0002`
|
||||||
|
- `Fix-BUG-20260409-0002`
|
||||||
|
- `Feature-20260409-0001`
|
||||||
|
- `Module-20260409-0001`
|
||||||
|
|
||||||
|
- `Canvas.h / Canvas.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `BUG-20260409-0001`
|
||||||
|
- `Fix-BUG-20260409-0001`
|
||||||
|
- `Feature-20260409-0003`
|
||||||
|
- `Module-20260409-0001`
|
||||||
|
|
||||||
|
- `TabControl.h / TabControl.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `Feature-20260409-0003`
|
||||||
|
- `Feature-20260409-0004`
|
||||||
|
- `Module-20260409-0001`
|
||||||
|
|
||||||
|
- `Dialog.h / Dialog.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `BUG-20260409-0001`
|
||||||
|
- `Fix-BUG-20260409-0001`
|
||||||
|
- `Feature-20260409-0002`
|
||||||
|
- `Module-20260409-0001`
|
||||||
|
|
||||||
|
- `Button.cpp / TextBox.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `BUG-20260409-0001`
|
||||||
|
- `Fix-BUG-20260409-0001`
|
||||||
|
- `Feature-20260409-0003`
|
||||||
|
|
||||||
|
- `Table.cpp / Table.h / Label.cpp / MessageBox.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `BUG-20260409-0002`
|
||||||
|
- `Fix-BUG-20260409-0002`
|
||||||
|
- `Feature-20260409-0001`
|
||||||
|
- `Feature-20260409-0002`
|
||||||
|
|
||||||
|
- `z-testDome.cpp`
|
||||||
|
- 覆盖于:
|
||||||
|
- `Feature-20260409-0004`
|
||||||
|
|
||||||
|
## 当前状态
|
||||||
|
|
||||||
|
- 当前工作区在 `77a8fe5` 之后还有未提交修改。
|
||||||
|
- 这些未提交修改主要是:
|
||||||
|
- 第二阶段托管重绘
|
||||||
|
- 注释补充与校正
|
||||||
|
- 记录与模板体系整理
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# BUG-20260409-0001
|
||||||
|
|
||||||
|
> 适用场景:记录问题本身,不展开完整修复方案。修复内容写入对应的 Fix 文档。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID: BUG-20260409-0001
|
||||||
|
- 标题: 对话框重绘、快照残留与遮挡交互异常
|
||||||
|
- 状态:已修复 / 待持续回归
|
||||||
|
- 严重性:S2
|
||||||
|
- 优先级:P0
|
||||||
|
- 模块:Window / Dialog / Canvas / Button / TextBox
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:Windows + EasyX + VS2022
|
||||||
|
- 发现人:协作过程静态审查与测试用例回归
|
||||||
|
- 关联 Fix ID:Fix-BUG-20260409-0001
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
- 现象:
|
||||||
|
- 非模态对话框打开后,底层被遮挡控件仍可能收到鼠标事件。
|
||||||
|
- 模态或非模态对话框在窗口尺寸变化后,可能出现背景快照残留。
|
||||||
|
- 模态对话框标题区域曾出现抓到底层背景的问题。
|
||||||
|
- 对话框关闭后,底层按钮 hover 状态可能不能立即恢复。
|
||||||
|
- 多个按钮快速 hover 切换时,对话框补画链容易闪烁。
|
||||||
|
- 影响范围:
|
||||||
|
- 对话框交互正确性
|
||||||
|
- 重绘层级稳定性
|
||||||
|
- hover 与点击行为一致性
|
||||||
|
- 期望结果:
|
||||||
|
- 对话框始终位于正确层级。
|
||||||
|
- 被对话框覆盖的底层控件不会误交互。
|
||||||
|
- resize / 打开 / 关闭过程中无残影、无错位。
|
||||||
|
- 实际结果:
|
||||||
|
- 曾出现穿透、残影、错误快照、关闭后 hover 残留、频繁补画闪烁等问题。
|
||||||
|
|
||||||
|
## 复现信息
|
||||||
|
|
||||||
|
- 前置条件:存在非模态或模态对话框,且下层有可交互控件
|
||||||
|
- 复现步骤:
|
||||||
|
|
||||||
|
1. 打开包含按钮、Canvas、TabControl 的测试窗口。
|
||||||
|
2. 弹出非模态或模态对话框。
|
||||||
|
3. 在对话框遮挡区域附近快速移动鼠标,或拖动窗口大小。
|
||||||
|
|
||||||
|
- 复现概率:高概率 / 偶现(不同问题不同)
|
||||||
|
- 最小复现 Demo:`z-testDome.cpp` 中 `KEY == 2 / 3 / 4`
|
||||||
|
- 证据:日志、静态推演、用户回归观察
|
||||||
|
|
||||||
|
## 初步分析
|
||||||
|
|
||||||
|
- 疑似位置:
|
||||||
|
- `Dialog::handleEvent`
|
||||||
|
- `Dialog::draw / recenterInHostWindow / clearControls`
|
||||||
|
- `Window::runEventLoop / pumpResizeIfNeeded / flushManagedRepaint`
|
||||||
|
- `Button::handleEvent`
|
||||||
|
- 触发条件:
|
||||||
|
- 对话框覆盖底层控件
|
||||||
|
- 窗口 resize
|
||||||
|
- hover 高频切换
|
||||||
|
- 对话框打开/关闭后的层级切换
|
||||||
|
- 相关线索:
|
||||||
|
- “快照作废”和“回贴旧背景”语义混用
|
||||||
|
- 事件阶段与绘制阶段原先分离不彻底
|
||||||
|
- 非模态对话框区域内鼠标事件吞掉后,底层控件拿不到离开消息
|
||||||
|
|
||||||
|
## 跟踪信息
|
||||||
|
|
||||||
|
- 首次发现时间:本轮协作初期
|
||||||
|
- 最后更新时间:2026-04-09
|
||||||
|
- 修复版本:[当前工作区]
|
||||||
|
- 验证版本:[当前工作区]
|
||||||
|
- 备注:快速划过多个按钮时一帧内偶发双高亮的问题暂未彻底消除,属于已接受的底层限制
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# BUG-20260409-0002
|
||||||
|
|
||||||
|
> 适用场景:记录问题本身,不展开完整修复方案。修复内容写入对应的 Fix 文档。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID: BUG-20260409-0002
|
||||||
|
- 标题: 基础控件生命周期与边界条件问题
|
||||||
|
- 状态:已修复 / 待持续回归
|
||||||
|
- 严重性:S2
|
||||||
|
- 优先级:P1
|
||||||
|
- 模块:Control / Window / Table / Label / Button / TextBox / MessageBox
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:Windows + EasyX + VS2022
|
||||||
|
- 发现人:静态审查
|
||||||
|
- 关联 Fix ID:Fix-BUG-20260409-0002
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
- 现象:
|
||||||
|
- `Window` 析构时可能先关闭图形环境,再让控件析构访问绘图接口。
|
||||||
|
- `Table` 在空表头、空数据、列数不一致时存在越界或空状态问题。
|
||||||
|
- `Label::setText()` 后尺寸与背景快照不能及时同步。
|
||||||
|
- `MessageBox` 外层曾直接干预 `Dialog` 初始化流程。
|
||||||
|
- 一些资源所有权和析构职责不够清晰。
|
||||||
|
- 影响范围:
|
||||||
|
- 基础稳定性
|
||||||
|
- 边界输入正确性
|
||||||
|
- 维护成本
|
||||||
|
- 期望结果:
|
||||||
|
- 生命周期安全、边界输入可防御、接口语义清晰
|
||||||
|
- 实际结果:
|
||||||
|
- 存在潜在崩溃、越界、状态不一致和维护负担
|
||||||
|
|
||||||
|
## 复现信息
|
||||||
|
|
||||||
|
- 前置条件:特定表格输入、动态修改文本、窗口关闭、MessageBox 使用链
|
||||||
|
- 复现步骤:
|
||||||
|
|
||||||
|
1. 静态审查相关代码路径。
|
||||||
|
2. 构造空表头、长于表头的数据行、动态变更 Label 文本。
|
||||||
|
3. 观察关闭窗口或销毁相关对象时的析构顺序。
|
||||||
|
|
||||||
|
- 复现概率:高概率 / 必现(不同问题不同)
|
||||||
|
- 最小复现 Demo:[可选]
|
||||||
|
- 证据:静态分析、日志、编译验证
|
||||||
|
|
||||||
|
## 初步分析
|
||||||
|
|
||||||
|
- 疑似位置:
|
||||||
|
- `Window::~Window`
|
||||||
|
- `Control` 析构与背景快照恢复链
|
||||||
|
- `Table::initTextWaH / setData / drawHeader / drawTable`
|
||||||
|
- `Label::setText`
|
||||||
|
- `MessageBox::showAsync / showModal`
|
||||||
|
- 触发条件:
|
||||||
|
- 边界数据输入
|
||||||
|
- 动态文本变化
|
||||||
|
- 对象销毁 / 窗口关闭
|
||||||
|
- 相关线索:
|
||||||
|
- 所有权层次混乱
|
||||||
|
- 空状态防御不足
|
||||||
|
- 接口暴露超出真实职责
|
||||||
|
|
||||||
|
## 跟踪信息
|
||||||
|
|
||||||
|
- 首次发现时间:本轮协作初期
|
||||||
|
- 最后更新时间:2026-04-09
|
||||||
|
- 修复版本:[当前工作区]
|
||||||
|
- 验证版本:[当前工作区]
|
||||||
|
- 备注:字符集相关问题暂未处理,仍按 MBCS 假设工作
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Fix-BUG-20260409-0001
|
||||||
|
|
||||||
|
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
|
||||||
|
|
||||||
|
## 关联信息
|
||||||
|
|
||||||
|
- Fix ID: Fix-BUG-20260409-0001
|
||||||
|
- 关联 BUG ID: BUG-20260409-0001
|
||||||
|
- 修复目标: 收紧对话框快照、遮挡、事件分发和统一提交重绘链
|
||||||
|
- 状态:已完成 / 待持续回归
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
- 分支 / 版本:`master`
|
||||||
|
|
||||||
|
## 根因分析
|
||||||
|
|
||||||
|
- 根因:
|
||||||
|
- 旧实现中“作废快照”和“回贴旧背景”混在一起,resize 时容易把旧画面贴回屏幕。
|
||||||
|
- 非模态对话框与底层控件之间缺少明确的事件阻断和 hover 清理机制。
|
||||||
|
- 重绘长期依赖控件即时局部绘制,导致底层先画、对话框后补,容易闪烁或层级错乱。
|
||||||
|
- 标题曾作为独立 `Label` 存在,导致额外一层背景快照,时序敏感。
|
||||||
|
- 触发条件:
|
||||||
|
- 非模态遮挡下的 hover / click
|
||||||
|
- 模态或非模态 resize
|
||||||
|
- 对话框打开 / 关闭
|
||||||
|
- 为什么之前没发现:
|
||||||
|
- 问题主要集中在“高频交互 + 对话框遮挡 + resize”组合路径
|
||||||
|
|
||||||
|
## 修复方案
|
||||||
|
|
||||||
|
- 修复思路:
|
||||||
|
- 拆分快照语义
|
||||||
|
- 收紧对话框事件消费
|
||||||
|
- 让 `Dialog` 在窗口变化时只重新居中
|
||||||
|
- 将标题绘制并入 `Dialog`
|
||||||
|
- 把重绘推进到窗口统一收口
|
||||||
|
- 关键改动:
|
||||||
|
- `discardBackground()` 与 `invalidateBackgroundSnapshot()` 分离
|
||||||
|
- `Dialog` 标题改为直接绘制,不再使用独立 `Label`
|
||||||
|
- `Dialog::handleEvent()` 对自身区域内鼠标事件统一吞掉
|
||||||
|
- 对话框关闭及遮挡场景补发合成 `WM_MOUSEMOVE`,清理底层 hover
|
||||||
|
- `Window` 引入托管重绘,先重绘受影响 root,再补画相交对话框
|
||||||
|
- 涉及文件 / 类 / 函数:
|
||||||
|
- `Window.h / Window.cpp`
|
||||||
|
- `Dialog.h / Dialog.cpp`
|
||||||
|
- `Control.h / Control.cpp`
|
||||||
|
- `Canvas.cpp`
|
||||||
|
- `TabControl.cpp`
|
||||||
|
- `Button.cpp`
|
||||||
|
- `TextBox.cpp`
|
||||||
|
- 影响的 API / 行为:
|
||||||
|
- `Dialog` 在窗口变化时的语义固定为“只重新居中,不拉伸”
|
||||||
|
- 非模态对话框覆盖区域内不再允许鼠标事件穿透到底层
|
||||||
|
- 关键约束 / 不变量:
|
||||||
|
- 对话框始终绘制在普通控件之上
|
||||||
|
- 托管分发期间控件不直接提交绘制
|
||||||
|
- resize 和对话框开关仍走整场景重绘兜底
|
||||||
|
|
||||||
|
## 影响评估
|
||||||
|
|
||||||
|
- 影响范围:
|
||||||
|
- 所有包含 `Dialog`、按钮 hover、遮挡重绘的场景
|
||||||
|
- 兼容性影响:有(行为更严格,底层控件不再穿透响应)
|
||||||
|
- 行为变化:有(对话框 resize 只居中;遮挡区域 hover 不再透传)
|
||||||
|
- 性能影响:有正向变化(减少无意义整场景补画);同时在某些兜底场景仍保留整场景重绘
|
||||||
|
- 回归风险:
|
||||||
|
- 托管重绘 root 选择不当可能导致嵌套容器局部重绘异常
|
||||||
|
- 需持续回归 `Canvas / TabControl / Table / Dialog` 组合场景
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 验证步骤:
|
||||||
|
|
||||||
|
1. 在 `KEY == 2 / 4` 中打开非模态对话框,快速在遮挡区域附近 hover 和点击。
|
||||||
|
2. 在 `KEY == 3 / 4` 中打开模态对话框并拖动窗口大小。
|
||||||
|
3. 关闭对话框后观察底层按钮 hover 恢复。
|
||||||
|
|
||||||
|
- 验证结果:
|
||||||
|
- 穿透、残影、关闭后 hover 不恢复等主问题已压住。
|
||||||
|
- 回归检查:
|
||||||
|
- 保留对“快速划过多个按钮时偶发一帧双高亮”的已知限制记录。
|
||||||
|
- 验证证据:
|
||||||
|
- 静态推演 + 多轮编译验证 + 用户回归反馈
|
||||||
|
|
||||||
|
## 落地信息
|
||||||
|
|
||||||
|
- Commit:
|
||||||
|
- `4a6e153`
|
||||||
|
- `7f8431a`
|
||||||
|
- `b07a4ec`
|
||||||
|
- 当前工作区未提交阶段
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:该修复跨多个阶段逐步收敛,不是一次性完成
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# Fix-BUG-20260409-0002
|
||||||
|
|
||||||
|
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
|
||||||
|
|
||||||
|
## 关联信息
|
||||||
|
|
||||||
|
- Fix ID: Fix-BUG-20260409-0002
|
||||||
|
- 关联 BUG ID: BUG-20260409-0002
|
||||||
|
- 修复目标: 收紧资源所有权,补齐基础边界防御,修正生命周期与接口语义问题
|
||||||
|
- 状态:已完成 / 待持续回归
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
- 分支 / 版本:`master`
|
||||||
|
|
||||||
|
## 根因分析
|
||||||
|
|
||||||
|
- 根因:
|
||||||
|
- 部分对象持有关系不清晰,析构顺序对 EasyX 上下文敏感。
|
||||||
|
- `Table / Label` 等基础控件对空状态和动态变化处理不足。
|
||||||
|
- `MessageBox` 曾通过历史接口越权干预 `Dialog` 初始化。
|
||||||
|
- 触发条件:
|
||||||
|
- 析构时机
|
||||||
|
- 空数据 / 列数不一致
|
||||||
|
- 动态变更文本
|
||||||
|
- 为什么之前没发现:
|
||||||
|
- 很多问题集中在边界输入和静态结构设计,而非主路径功能
|
||||||
|
|
||||||
|
## 修复方案
|
||||||
|
|
||||||
|
- 修复思路:
|
||||||
|
- 收紧资源所有权
|
||||||
|
- 明确析构顺序
|
||||||
|
- 给空状态和列数不一致加防御
|
||||||
|
- 让接口更贴合真实职责
|
||||||
|
- 关键改动:
|
||||||
|
- `Window` 析构先清空控件树,再 `closegraph()`
|
||||||
|
- `Control / Window / Button / Table` 中一批资源改为明确所有权
|
||||||
|
- `Table` 增加空状态、列数规范化、分页下限保护、表高计算
|
||||||
|
- `Label::setText()` 重新计算尺寸并处理旧快照
|
||||||
|
- `MessageBox` 移除外层 `setInitialization(true)` 调用
|
||||||
|
- 涉及文件 / 类 / 函数:
|
||||||
|
- `Control.h / Control.cpp`
|
||||||
|
- `Window.h / Window.cpp`
|
||||||
|
- `Button.h / Button.cpp`
|
||||||
|
- `Table.h / Table.cpp`
|
||||||
|
- `Label.cpp`
|
||||||
|
- `MessageBox.cpp`
|
||||||
|
- `TextBox.cpp`
|
||||||
|
- 影响的 API / 行为:
|
||||||
|
- `Button` 回调 setter 语义收正常
|
||||||
|
- `MessageBox` 不再依赖外层强制初始化 `Dialog`
|
||||||
|
- 关键约束 / 不变量:
|
||||||
|
- 宿主图形上下文必须在控件销毁后再关闭
|
||||||
|
- 表格行数据必须与表头列数对齐
|
||||||
|
|
||||||
|
## 影响评估
|
||||||
|
|
||||||
|
- 影响范围:
|
||||||
|
- 基础控件稳定性与维护性
|
||||||
|
- 兼容性影响:低
|
||||||
|
- 行为变化:有(边界输入会被规范化,不再放任异常状态继续传播)
|
||||||
|
- 性能影响:无明显负担
|
||||||
|
- 回归风险:
|
||||||
|
- 主要集中在表格布局与分页按钮链路
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 验证步骤:
|
||||||
|
|
||||||
|
1. 编译 `Window / Control / Table / Label / MessageBox / TextBox` 相关文件。
|
||||||
|
2. 逻辑推演空表头、长行数据、动态文本变化和窗口关闭链路。
|
||||||
|
3. 在现有测试用例中回归表格和消息框基本功能。
|
||||||
|
|
||||||
|
- 验证结果:
|
||||||
|
- 已消除确定性越界和明显生命周期风险。
|
||||||
|
- 回归检查:
|
||||||
|
- 字符集相关问题暂不在本次修复范围内。
|
||||||
|
- 验证证据:
|
||||||
|
- 静态审查 + 编译通过
|
||||||
|
|
||||||
|
## 落地信息
|
||||||
|
|
||||||
|
- Commit:
|
||||||
|
- `dde570a` 之后的早期工作区修改
|
||||||
|
- `4a6e153`
|
||||||
|
- `7f8431a`
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:这部分修复多为基础治理,部分早期修改发生在仓库初始化之前
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# 功能变更 ID: Feature-20260409-0001
|
||||||
|
|
||||||
|
> 适用场景:记录小到中等规模的功能、接口、行为、默认值或配置变化。
|
||||||
|
> 不适用场景:新增核心模块、重大模块重构、架构级设计,请使用“新增功能模块”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID: Feature-20260409-0001
|
||||||
|
- 标题: 基础资源所有权与生命周期收口
|
||||||
|
- 状态:已完成
|
||||||
|
- 类型:修改
|
||||||
|
- 级别:L2 中等
|
||||||
|
- 模块:Control / Window / Button / Table / Label
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:Windows + EasyX + VS2022
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
|
||||||
|
## 变更背景
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 初始版本中一部分资源持有关系不明确,析构顺序和边界输入处理存在隐患。
|
||||||
|
- 目标:
|
||||||
|
- 提高基础稳定性与维护性。
|
||||||
|
- 不做什么:[可选]
|
||||||
|
- 不处理字符集体系切换问题。
|
||||||
|
|
||||||
|
## 变更内容
|
||||||
|
|
||||||
|
- 变更摘要:
|
||||||
|
- 收紧资源所有权,调整析构顺序,补齐基础控件边界防御。
|
||||||
|
- 新增项:[可选]
|
||||||
|
- `Control` 提供更明确的快照有效性查询和宿主窗口回溯能力。
|
||||||
|
- 修改项:[可选]
|
||||||
|
- `Window` 析构顺序调整
|
||||||
|
- `Table` 行数据规范化、空状态防御、页数保护
|
||||||
|
- `Label` 动态文本变化处理
|
||||||
|
- 删除 / 废弃项:[可选]
|
||||||
|
- `MessageBox` 外层强制 `Dialog` 初始化的做法被移除
|
||||||
|
- 受影响的文件 / 类 / 函数:
|
||||||
|
- `Control.h / Control.cpp`
|
||||||
|
- `Window.h / Window.cpp`
|
||||||
|
- `Button.h / Button.cpp`
|
||||||
|
- `Table.h / Table.cpp`
|
||||||
|
- `Label.cpp`
|
||||||
|
- `MessageBox.cpp`
|
||||||
|
- 对外 API / 属性变化:[可选]
|
||||||
|
- `Button` 回调设置接口语义更正常
|
||||||
|
|
||||||
|
## 行为对照
|
||||||
|
|
||||||
|
- 变更前:
|
||||||
|
- 生命周期和边界输入更依赖使用者自觉,异常输入容易直接进入绘制或析构链。
|
||||||
|
- 变更后:
|
||||||
|
- 基础控件会主动规范化一部分输入,生命周期顺序更安全。
|
||||||
|
- 兼容性说明:兼容
|
||||||
|
- 迁移说明:[可选]
|
||||||
|
- 无强制迁移要求
|
||||||
|
|
||||||
|
## 验证与落地
|
||||||
|
|
||||||
|
- 验证方式:
|
||||||
|
- 静态审查 + 编译验证 + 现有测试用例回归
|
||||||
|
- 验证结果:
|
||||||
|
- 主要基础问题已收敛
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- `BUG-20260409-0002`
|
||||||
|
- `Fix-BUG-20260409-0002`
|
||||||
|
- Commit:
|
||||||
|
- 早期工作区修改 + `4a6e153` + `7f8431a`
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
|
- 其中一部分修改早于仓库基线
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
# 功能变更 ID: Feature-20260409-0002
|
||||||
|
|
||||||
|
> 适用场景:记录小到中等规模的功能、接口、行为、默认值或配置变化。
|
||||||
|
> 不适用场景:新增核心模块、重大模块重构、架构级设计,请使用“新增功能模块”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID: Feature-20260409-0002
|
||||||
|
- 标题: Dialog 与 MessageBox 行为调整
|
||||||
|
- 状态:已完成
|
||||||
|
- 类型:修改
|
||||||
|
- 级别:L2 中等
|
||||||
|
- 模块:Dialog / MessageBox / Window
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:Windows + EasyX + VS2022
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
|
||||||
|
## 变更背景
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 初始版本中 `Dialog` 的初始化、resize、标题绘制和 `MessageBox` 外层调用关系较松散。
|
||||||
|
- 目标:
|
||||||
|
- 明确 `Dialog` 行为边界,让其在 resize、显示、关闭、标题更新等场景更稳定。
|
||||||
|
- 不做什么:[可选]
|
||||||
|
- 暂不重命名 `setInitialization()` 这类历史接口。
|
||||||
|
|
||||||
|
## 变更内容
|
||||||
|
|
||||||
|
- 变更摘要:
|
||||||
|
- `Dialog` 在窗口变化时只重新居中;标题直接由 `Dialog` 自身绘制;`MessageBox` 不再外层强制初始化。
|
||||||
|
- 新增项:[可选]
|
||||||
|
- `Dialog::recenterInHostWindow()`
|
||||||
|
- 修改项:[可选]
|
||||||
|
- `SetTitle / SetMessage / SetType` 触发重新布局
|
||||||
|
- `Dialog` 标题不再使用独立 `Label`
|
||||||
|
- `Dialog` 在模态和非模态场景中的重绘语义被收紧
|
||||||
|
- 删除 / 废弃项:[可选]
|
||||||
|
- `MessageBox::showAsync/showModal()` 中对 `setInitialization(true)` 的直接调用已移除
|
||||||
|
- 受影响的文件 / 类 / 函数:
|
||||||
|
- `Dialog.h / Dialog.cpp`
|
||||||
|
- `MessageBox.cpp`
|
||||||
|
- `Window.cpp`
|
||||||
|
- 对外 API / 属性变化:[可选]
|
||||||
|
- `Dialog` 在窗口变化时的对外可观察行为改为“只居中,不拉伸”
|
||||||
|
|
||||||
|
## 行为对照
|
||||||
|
|
||||||
|
- 变更前:
|
||||||
|
- `Dialog` resize 时可能重建行为与快照行为混杂,标题区域额外依赖子控件快照。
|
||||||
|
- 变更后:
|
||||||
|
- `Dialog` 尺寸由内容决定;窗口变化只重新居中;标题直接绘制,减少一层快照时序问题。
|
||||||
|
- 兼容性说明:部分兼容
|
||||||
|
- 迁移说明:[可选]
|
||||||
|
- 如果外部逻辑曾假设 `Dialog` 会跟随窗口拉伸,现在需要改按“仅居中”理解
|
||||||
|
|
||||||
|
## 验证与落地
|
||||||
|
|
||||||
|
- 验证方式:
|
||||||
|
- `KEY == 2 / 3 / 4` 相关场景静态推演与回归
|
||||||
|
- 验证结果:
|
||||||
|
- 标题更新、消息框初始化链、resize 后位置语义更稳定
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- `BUG-20260409-0001`
|
||||||
|
- `Fix-BUG-20260409-0001`
|
||||||
|
- Commit:
|
||||||
|
- `4a6e153`
|
||||||
|
- `7f8431a`
|
||||||
|
- 当前工作区部分未提交整理
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# 功能变更 ID: Feature-20260409-0003
|
||||||
|
|
||||||
|
> 适用场景:记录小到中等规模的功能、接口、行为、默认值或配置变化。
|
||||||
|
> 不适用场景:新增核心模块、重大模块重构、架构级设计,请使用“新增功能模块”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID: Feature-20260409-0003
|
||||||
|
- 标题: 输入事件、hover 与遮挡交互调整
|
||||||
|
- 状态:已完成
|
||||||
|
- 类型:修改
|
||||||
|
- 级别:L2 中等
|
||||||
|
- 模块:Window / Canvas / Button / TextBox / Dialog / TabControl
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:Windows + EasyX + VS2022
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
|
||||||
|
## 变更背景
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 初始版本中,hover、点击、遮挡与重叠控件之间的事件传递不够稳定。
|
||||||
|
- 目标:
|
||||||
|
- 建立更一致的鼠标事件消费规则,减少穿透、残留 hover 和无意义重绘。
|
||||||
|
- 不做什么:[可选]
|
||||||
|
- 暂不彻底解决“快速划过多按钮偶发一帧双高亮”的底层限制问题。
|
||||||
|
|
||||||
|
## 变更内容
|
||||||
|
|
||||||
|
- 变更摘要:
|
||||||
|
- 收紧 `Button / TextBox / Dialog` 的事件消费语义,并补上对话框关闭后及遮挡场景下的 hover 清理。
|
||||||
|
- 新增项:[可选]
|
||||||
|
- `Window::dispatchSyntheticMouseMoveToControls(...)`
|
||||||
|
- 修改项:[可选]
|
||||||
|
- `Button` 只在合理命中路径上吞掉鼠标事件
|
||||||
|
- `TextBox` 纯 hover 不再处理
|
||||||
|
- 非模态对话框覆盖区域内阻断底层穿透
|
||||||
|
- 对话框关闭后补发合成鼠标移动同步 hover
|
||||||
|
- 删除 / 废弃项:[可选]
|
||||||
|
- 大量 hover DEBUG 日志已降噪
|
||||||
|
- 受影响的文件 / 类 / 函数:
|
||||||
|
- `Window.cpp`
|
||||||
|
- `Canvas.cpp`
|
||||||
|
- `Button.cpp`
|
||||||
|
- `TextBox.cpp`
|
||||||
|
- `Dialog.cpp`
|
||||||
|
- `TabControl.cpp`
|
||||||
|
- 对外 API / 属性变化:[可选]
|
||||||
|
- 无新增公开 API,主要是行为语义变化
|
||||||
|
|
||||||
|
## 行为对照
|
||||||
|
|
||||||
|
- 变更前:
|
||||||
|
- 遮挡区域 hover/click 可能穿透;对话框关闭后底层 hover 可能残留。
|
||||||
|
- 变更后:
|
||||||
|
- 交互更偏向“命中即消费,关闭后同步清理 hover,遮挡区域不透传”。
|
||||||
|
- 兼容性说明:部分兼容
|
||||||
|
- 迁移说明:[可选]
|
||||||
|
- 若旧代码依赖非模态对话框下方控件还能收到事件,这种行为已被收紧
|
||||||
|
|
||||||
|
## 验证与落地
|
||||||
|
|
||||||
|
- 验证方式:
|
||||||
|
- `KEY == 2 / 3 / 4` 中对按钮、Tab、Table 分页、对话框关闭链做回归
|
||||||
|
- 验证结果:
|
||||||
|
- 主要交互一致性问题已收敛
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- `BUG-20260409-0001`
|
||||||
|
- `Fix-BUG-20260409-0001`
|
||||||
|
- Commit:
|
||||||
|
- `7f8431a`
|
||||||
|
- `b07a4ec`
|
||||||
|
- 当前工作区未提交阶段
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# 功能变更 ID: Feature-20260409-0004
|
||||||
|
|
||||||
|
> 适用场景:记录小到中等规模的功能、接口、行为、默认值或配置变化。
|
||||||
|
> 不适用场景:新增核心模块、重大模块重构、架构级设计,请使用“新增功能模块”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID: Feature-20260409-0004
|
||||||
|
- 标题: 测试用例与可观测性调整
|
||||||
|
- 状态:已完成
|
||||||
|
- 类型:修改
|
||||||
|
- 级别:L1 轻量
|
||||||
|
- 模块:z-testDome / 日志
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:Windows + EasyX + VS2022
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
|
||||||
|
## 变更背景
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 对话框、遮挡、hover、分页和页签的组合问题需要更有针对性的回归入口。
|
||||||
|
- 目标:
|
||||||
|
- 增加一组综合用例,并降低 DEBUG 日志噪音。
|
||||||
|
- 不做什么:[可选]
|
||||||
|
- 不改变原有 `KEY == 1 / 2 / 3` 的默认运行逻辑
|
||||||
|
|
||||||
|
## 变更内容
|
||||||
|
|
||||||
|
- 变更摘要:
|
||||||
|
- 新增 `KEY == 4` 的综合回归用例,覆盖对话框遮挡、Tab、Table、模态 resize 等场景。
|
||||||
|
- 新增项:[可选]
|
||||||
|
- `KEY == 4` 测试分支
|
||||||
|
- 修改项:[可选]
|
||||||
|
- hover 相关日志降噪
|
||||||
|
- 事件日志补充更清晰的事件名
|
||||||
|
- 删除 / 废弃项:[可选]
|
||||||
|
- 无
|
||||||
|
- 受影响的文件 / 类 / 函数:
|
||||||
|
- `z-testDome.cpp`
|
||||||
|
- `Window.cpp`
|
||||||
|
- `Canvas.cpp`
|
||||||
|
- `Button.cpp`
|
||||||
|
- 对外 API / 属性变化:[可选]
|
||||||
|
- 无
|
||||||
|
|
||||||
|
## 行为对照
|
||||||
|
|
||||||
|
- 变更前:
|
||||||
|
- 回归更多依赖手工拼场景,hover 日志容易刷屏。
|
||||||
|
- 变更后:
|
||||||
|
- 有固定综合回归入口,日志更适合看关键路径。
|
||||||
|
- 兼容性说明:兼容
|
||||||
|
- 迁移说明:[可选]
|
||||||
|
- 测试 `KEY == 4` 需手动切换宏值
|
||||||
|
|
||||||
|
## 验证与落地
|
||||||
|
|
||||||
|
- 验证方式:
|
||||||
|
- 编译 `z-testDome.cpp`
|
||||||
|
- 静态推演 `KEY == 4` 控件创建、显示、对话框触发和关闭链
|
||||||
|
- 验证结果:
|
||||||
|
- 当前默认 `KEY == 2` 未被带坏,`KEY == 4` 逻辑链自洽
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- 可作为 `BUG-20260409-0001` 相关问题的回归入口
|
||||||
|
- Commit:
|
||||||
|
- 当前工作区未提交阶段
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# 功能变更 ID: Feature-20260409-0005
|
||||||
|
|
||||||
|
> 适用场景:记录小到中等规模的功能、接口、行为、默认值或配置变化。
|
||||||
|
> 不适用场景:新增核心模块、重大模块重构、架构级设计,请使用“新增功能模块”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID: Feature-20260409-0005
|
||||||
|
- 标题: 开发记录与模板体系整理
|
||||||
|
- 状态:已完成
|
||||||
|
- 类型:修改
|
||||||
|
- 级别:L1 轻量
|
||||||
|
- 模块:文档模板 / 开发记录
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:本地工作区
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
|
||||||
|
## 变更背景
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 需要对本轮协作中的大量修改建立可持续维护的记录体系。
|
||||||
|
- 目标:
|
||||||
|
- 将“问题、修复、功能变化、模块设计”这四类信息分层记录,避免后续继续靠聊天上下文回忆。
|
||||||
|
- 不做什么:[可选]
|
||||||
|
- 不把模板设计成覆盖所有场景的一份大而全文档
|
||||||
|
|
||||||
|
## 变更内容
|
||||||
|
|
||||||
|
- 变更摘要:
|
||||||
|
- 重写四个文档模板,修正编码内容,并建立统一的开发记录目录。
|
||||||
|
- 新增项:[可选]
|
||||||
|
- `开发记录/BUG`
|
||||||
|
- `开发记录/Fix`
|
||||||
|
- `开发记录/功能变更`
|
||||||
|
- `开发记录/模块`
|
||||||
|
- 修改项:[可选]
|
||||||
|
- `文档模板` 目录下四个模板重写并重新划分职责
|
||||||
|
- 删除 / 废弃项:[可选]
|
||||||
|
- 原模板中“功能变更”和“模块说明”职责重叠的写法已废弃
|
||||||
|
- 受影响的文件 / 类 / 函数:
|
||||||
|
- `文档模板/*.md`
|
||||||
|
- `开发记录/*.md`
|
||||||
|
- 对外 API / 属性变化:[可选]
|
||||||
|
- 无
|
||||||
|
|
||||||
|
## 行为对照
|
||||||
|
|
||||||
|
- 变更前:
|
||||||
|
- 模板存在乱码,功能变更和模块说明边界不清,小改动记录成本过高。
|
||||||
|
- 变更后:
|
||||||
|
- 模板职责明确,记录粒度更合理,可以持续沉淀协作历史。
|
||||||
|
- 兼容性说明:兼容
|
||||||
|
- 迁移说明:[可选]
|
||||||
|
- 后续新增记录优先按新模板写,不再沿用旧模板内容
|
||||||
|
|
||||||
|
## 验证与落地
|
||||||
|
|
||||||
|
- 验证方式:
|
||||||
|
- 读取模板文件确认编码与内容正常
|
||||||
|
- 创建第一批记录文件验证模板可用
|
||||||
|
- 验证结果:
|
||||||
|
- 模板可正常使用,第一批记录已按新体系落地
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- 无
|
||||||
|
- Commit:
|
||||||
|
- 当前工作区未提交阶段
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
# 新增功能模块 / 模块重构
|
||||||
|
|
||||||
|
> 适用场景:新增模块、重大模块重构、核心架构能力演进。
|
||||||
|
> 不适用场景:小接口或轻量功能变更,请使用“功能变更”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- 模块 ID:Module-20260409-0001
|
||||||
|
- 模块名称:Window 托管重绘与覆盖合成机制
|
||||||
|
- 状态:已完成 / 待持续回归
|
||||||
|
- 类型:架构演进
|
||||||
|
- 所属系统 / 子系统:Window / Control / Dialog 重绘链
|
||||||
|
- 版本 / 分支:`master`
|
||||||
|
- 环境:Windows + EasyX + VS2022
|
||||||
|
- 负责人:Codex 协作修改
|
||||||
|
|
||||||
|
## 背景与目标
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 初始框架以控件局部即时重绘为主,非模态对话框与底层控件重叠时,容易出现底层先画、对话框后补的层级问题。
|
||||||
|
- 当前痛点:
|
||||||
|
- 对话框遮挡下的 hover / click / tooltip / 分页等交互容易闪烁
|
||||||
|
- 底层控件和上层对话框在事件里分离绘制,顺序不可控
|
||||||
|
- 目标:
|
||||||
|
- 将“事件改状态”和“绘制提交”拆开,由 `Window` 在事件尾统一提交重绘。
|
||||||
|
- 非目标:[可选]
|
||||||
|
- 暂不做真正像素级脏区裁剪
|
||||||
|
- 暂不统一布局系统
|
||||||
|
|
||||||
|
## 模块边界
|
||||||
|
|
||||||
|
- 职责:
|
||||||
|
- 在 `Window` 主循环里托管控件重绘请求
|
||||||
|
- 选择安全的重绘 root
|
||||||
|
- 在提交阶段维护普通控件与非模态 `Dialog` 的正确层级
|
||||||
|
- 不负责什么:
|
||||||
|
- 模态 `Dialog` 自己的内部事件循环
|
||||||
|
- 布局语义统一
|
||||||
|
- 字符集问题
|
||||||
|
- 外部依赖:
|
||||||
|
- EasyX 绘制接口
|
||||||
|
- 各控件自身的快照恢复能力
|
||||||
|
- 对外能力 / API:
|
||||||
|
- `Window::requestManagedRepaint(Control* source)`
|
||||||
|
- `Window::flushManagedRepaint()`
|
||||||
|
- `Control::getManagedRepaintRoot()`
|
||||||
|
- `Control::commitManagedRepaint()`
|
||||||
|
- 关键数据 / 状态:
|
||||||
|
- `managedDispatchActive`
|
||||||
|
- `managedSceneDirty`
|
||||||
|
- `managedRepaintItems`
|
||||||
|
|
||||||
|
## 设计说明
|
||||||
|
|
||||||
|
- 核心流程:
|
||||||
|
1. `Window::runEventLoop()` 开始输入分发前,进入 `managedDispatchActive = true`
|
||||||
|
2. 控件在 `handleEvent()` 中只改状态并通过 `requestRepaint()` 上报
|
||||||
|
3. `Window::requestManagedRepaint(source)` 把 source 转换成 `root + coverage`
|
||||||
|
4. 事件收口时 `flushManagedRepaint()` 按层级提交 root
|
||||||
|
5. 最后补画和脏区相交的非模态 `Dialog`
|
||||||
|
- 关键对象 / 类关系:
|
||||||
|
- `Window`:重绘调度中心
|
||||||
|
- `Control`:提供宿主窗口回溯与 root 选择语义
|
||||||
|
- `Canvas / TabControl / Dialog`:作为安全重绘 root 的主要容器
|
||||||
|
- 生命周期:
|
||||||
|
- 托管重绘项只存在于当前分发轮次
|
||||||
|
- `clearManagedRepaintState()` 在 flush、resize 收口、对话框开关后的整场景重绘后清空
|
||||||
|
- 事件 / 渲染 / 数据流:
|
||||||
|
- 事件:Dialog 优先,普通控件其次
|
||||||
|
- 渲染:受影响普通 root 先画,Dialog 后补
|
||||||
|
- 数据流:leaf control -> requestRepaint -> Window registry -> flush
|
||||||
|
- 关键不变量:
|
||||||
|
- 分发期不直接提交托管重绘
|
||||||
|
- 顶层普通控件的提交顺序必须服从 `Window::controls` 的 z-order
|
||||||
|
- 对话框始终绘制在普通控件之上
|
||||||
|
- 降级 / 回退策略:[可选]
|
||||||
|
- `resize`、对话框打开/关闭等全局变化仍走整场景重绘
|
||||||
|
|
||||||
|
## 实现与影响
|
||||||
|
|
||||||
|
- 关键实现点:
|
||||||
|
- `Control` 增加 `hostWindow`、`getManagedRepaintRoot()`、`commitManagedRepaint()`
|
||||||
|
- `Window` 增加 `ManagedRepaintItem` 队列
|
||||||
|
- `Canvas / TabControl / Dialog` 提供局部可提交与升级为整 root 重画的判断
|
||||||
|
- 涉及文件 / 类 / 函数:
|
||||||
|
- `Window.h / Window.cpp`
|
||||||
|
- `Control.h / Control.cpp`
|
||||||
|
- `Canvas.h / Canvas.cpp`
|
||||||
|
- `TabControl.h / TabControl.cpp`
|
||||||
|
- `Dialog.h / Dialog.cpp`
|
||||||
|
- 兼容性影响:
|
||||||
|
- 行为层面更严格,底层控件不再依赖旧的“即时局部重绘”副作用
|
||||||
|
- 性能影响:
|
||||||
|
- 相比整场景重绘更细
|
||||||
|
- 相比纯即时局部重绘更稳定
|
||||||
|
- 在全局变化场景仍保留整场景兜底
|
||||||
|
- 风险点:
|
||||||
|
- root 选择错误会导致局部重绘不完整
|
||||||
|
- 嵌套容器组合场景需持续回归
|
||||||
|
|
||||||
|
## 测试与验证
|
||||||
|
|
||||||
|
- 测试范围:
|
||||||
|
- 非模态遮挡下的按钮 hover / click
|
||||||
|
- `Canvas / TabControl / Table` 组合场景
|
||||||
|
- 模态与非模态对话框 coexist 场景
|
||||||
|
- 验证步骤:
|
||||||
|
|
||||||
|
1. 编译 `Control / Canvas / TabControl / Dialog / Window` 相关源文件。
|
||||||
|
2. 回归 `KEY == 2 / 3 / 4` 的对话框与遮挡场景。
|
||||||
|
3. 检查对话框关闭、resize、hover 清理和局部重绘顺序。
|
||||||
|
|
||||||
|
- 验证结果:
|
||||||
|
- 第二阶段架构已落地,核心编译通过,主问题链路已被压住。
|
||||||
|
- 已知限制 / 遗留问题:[可选]
|
||||||
|
- 快速划过多个按钮时偶发一帧双高亮,当前接受为底层限制
|
||||||
|
- 仍未推进到真正像素级脏区裁剪
|
||||||
|
|
||||||
|
## 落地信息
|
||||||
|
|
||||||
|
- 关联功能变更 ID:[可选]
|
||||||
|
- `Feature-20260409-0002`
|
||||||
|
- `Feature-20260409-0003`
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- `BUG-20260409-0001`
|
||||||
|
- `Fix-BUG-20260409-0001`
|
||||||
|
- Commit:
|
||||||
|
- `b07a4ec`
|
||||||
|
- `77a8fe5`
|
||||||
|
- 当前工作区未提交阶段
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 相关文档:[可选]
|
||||||
|
- `00-修改总览-20260409.md`
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# BUG-YYYYMMDD-XXXX
|
||||||
|
|
||||||
|
> 适用场景:记录问题本身,不展开完整修复方案。修复内容写入对应的 Fix 文档。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID:
|
||||||
|
- 标题:
|
||||||
|
- 状态:待修复 / 修复中 / 已修复 / 已验证 / 已关闭
|
||||||
|
- 严重性:S1 / S2 / S3 / S4
|
||||||
|
- 优先级:P0 / P1 / P2 / P3
|
||||||
|
- 模块:
|
||||||
|
- 版本 / 分支:
|
||||||
|
- 环境:
|
||||||
|
- 发现人:
|
||||||
|
- 关联 Fix ID:[可选]
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
- 现象:
|
||||||
|
- 影响范围:
|
||||||
|
- 期望结果:
|
||||||
|
- 实际结果:
|
||||||
|
|
||||||
|
## 复现信息
|
||||||
|
|
||||||
|
- 前置条件:[可选]
|
||||||
|
- 复现步骤:
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
- 复现概率:必现 / 高概率 / 偶现 / 低概率
|
||||||
|
- 最小复现 Demo:[可选]
|
||||||
|
- 证据:截图 / 日志 / 调用栈 / 录屏 / 断点观察
|
||||||
|
|
||||||
|
## 初步分析
|
||||||
|
|
||||||
|
- 疑似位置:
|
||||||
|
- 触发条件:
|
||||||
|
- 相关线索:
|
||||||
|
- 最近相关改动:[可选]
|
||||||
|
|
||||||
|
## 跟踪信息
|
||||||
|
|
||||||
|
- 首次发现时间:
|
||||||
|
- 最后更新时间:
|
||||||
|
- 修复版本:[可选]
|
||||||
|
- 验证版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Fix-BUG-YYYYMMDD-XXXX
|
||||||
|
|
||||||
|
> 适用场景:记录某个 BUG 的修复方案、影响评估、验证结果与落地信息。
|
||||||
|
|
||||||
|
## 关联信息
|
||||||
|
|
||||||
|
- Fix ID:
|
||||||
|
- 关联 BUG ID:
|
||||||
|
- 修复目标:
|
||||||
|
- 状态:设计中 / 开发中 / 已完成 / 已验证 / 已回滚
|
||||||
|
- 负责人:
|
||||||
|
- 分支 / 版本:
|
||||||
|
|
||||||
|
## 根因分析
|
||||||
|
|
||||||
|
- 根因:
|
||||||
|
- 触发条件:
|
||||||
|
- 为什么之前没发现:[可选]
|
||||||
|
- 关键证据:[可选]
|
||||||
|
|
||||||
|
## 修复方案
|
||||||
|
|
||||||
|
- 修复思路:
|
||||||
|
- 关键改动:
|
||||||
|
- 涉及文件 / 类 / 函数:
|
||||||
|
- 影响的 API / 行为:[可选]
|
||||||
|
- 关键约束 / 不变量:[可选]
|
||||||
|
- 回滚点 / 开关:[可选]
|
||||||
|
|
||||||
|
## 影响评估
|
||||||
|
|
||||||
|
- 影响范围:
|
||||||
|
- 兼容性影响:无 / 有(说明)
|
||||||
|
- 行为变化:无 / 有(说明)
|
||||||
|
- 性能影响:无 / 有(说明)
|
||||||
|
- 回归风险:
|
||||||
|
|
||||||
|
## 验证结果
|
||||||
|
|
||||||
|
- 验证步骤:
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
- 验证结果:
|
||||||
|
- 回归检查:[可选]
|
||||||
|
- 验证证据:[可选]
|
||||||
|
|
||||||
|
## 落地信息
|
||||||
|
|
||||||
|
- Commit:
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# 功能变更 ID: Feature-YYYYMMDD-XXXXX
|
||||||
|
|
||||||
|
> 适用场景:记录小到中等规模的功能、接口、行为、默认值或配置变化。
|
||||||
|
> 不适用场景:新增核心模块、重大模块重构、架构级设计,请使用“新增功能模块”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- ID:
|
||||||
|
- 标题:
|
||||||
|
- 状态:设计中 / 开发中 / 已完成 / 已验证 / 已发布
|
||||||
|
- 类型:新增 / 修改 / 删除 / 废弃 / 重命名
|
||||||
|
- 级别:L1 轻量 / L2 中等 / L3 重大
|
||||||
|
- 模块:
|
||||||
|
- 版本 / 分支:
|
||||||
|
- 环境:
|
||||||
|
- 负责人:
|
||||||
|
|
||||||
|
## 变更背景
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 目标:
|
||||||
|
- 不做什么:[可选]
|
||||||
|
|
||||||
|
## 变更内容
|
||||||
|
|
||||||
|
- 变更摘要:
|
||||||
|
- 新增项:[可选]
|
||||||
|
- 修改项:[可选]
|
||||||
|
- 删除 / 废弃项:[可选]
|
||||||
|
- 受影响的文件 / 类 / 函数:
|
||||||
|
- 对外 API / 属性变化:[可选]
|
||||||
|
|
||||||
|
## 行为对照
|
||||||
|
|
||||||
|
- 变更前:
|
||||||
|
- 变更后:
|
||||||
|
- 兼容性说明:兼容 / 部分兼容 / 不兼容
|
||||||
|
- 迁移说明:[可选]
|
||||||
|
|
||||||
|
## 验证与落地
|
||||||
|
|
||||||
|
- 验证方式:
|
||||||
|
- 验证结果:
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- Commit:
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 备注:[可选]
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# 新增功能模块 / 模块重构
|
||||||
|
|
||||||
|
> 适用场景:新增模块、重大模块重构、核心架构能力演进。
|
||||||
|
> 不适用场景:小接口或轻量功能变更,请使用“功能变更”模板。
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- 模块 ID:Module-YYYYMMDD-XXXXX
|
||||||
|
- 模块名称:
|
||||||
|
- 状态:设计中 / 开发中 / 已完成 / 已验证 / 已发布
|
||||||
|
- 类型:新增模块 / 模块重构 / 架构演进
|
||||||
|
- 所属系统 / 子系统:
|
||||||
|
- 版本 / 分支:
|
||||||
|
- 环境:
|
||||||
|
- 负责人:
|
||||||
|
|
||||||
|
## 背景与目标
|
||||||
|
|
||||||
|
- 背景:
|
||||||
|
- 当前痛点:
|
||||||
|
- 目标:
|
||||||
|
- 非目标:[可选]
|
||||||
|
|
||||||
|
## 模块边界
|
||||||
|
|
||||||
|
- 职责:
|
||||||
|
- 不负责什么:
|
||||||
|
- 外部依赖:
|
||||||
|
- 对外能力 / API:
|
||||||
|
- 关键数据 / 状态:
|
||||||
|
|
||||||
|
## 设计说明
|
||||||
|
|
||||||
|
- 核心流程:
|
||||||
|
- 关键对象 / 类关系:
|
||||||
|
- 生命周期:
|
||||||
|
- 事件 / 渲染 / 数据流:[按模块类型填写]
|
||||||
|
- 关键不变量:
|
||||||
|
- 降级 / 回退策略:[可选]
|
||||||
|
|
||||||
|
## 实现与影响
|
||||||
|
|
||||||
|
- 关键实现点:
|
||||||
|
- 涉及文件 / 类 / 函数:
|
||||||
|
- 兼容性影响:
|
||||||
|
- 性能影响:
|
||||||
|
- 风险点:
|
||||||
|
|
||||||
|
## 测试与验证
|
||||||
|
|
||||||
|
- 测试范围:
|
||||||
|
- 验证步骤:
|
||||||
|
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
- 验证结果:
|
||||||
|
- 已知限制 / 遗留问题:[可选]
|
||||||
|
|
||||||
|
## 落地信息
|
||||||
|
|
||||||
|
- 关联功能变更 ID:[可选]
|
||||||
|
- 关联 BUG / Fix:[可选]
|
||||||
|
- Commit:
|
||||||
|
- PR:[可选]
|
||||||
|
- 发布版本:[可选]
|
||||||
|
- 相关文档:[可选]
|
||||||
Reference in New Issue
Block a user