#ifndef __UI_DEBUG_BOUNDS_HPP__ #define __UI_DEBUG_BOUNDS_HPP__ #include #include #include #include #include #include #include #include "Theme.hpp" namespace jdi { /** * @brief 布局边界描边颜色(按层级取色)。 * * 层级只用于**区分归属**,不代表重要性:层级越深说明越靠近叶子控件。 * * @param [IN] depth 层级(0 = 顶层组件区域,≥1 = 内部控件)。 * @return ARGB 颜色(不透明)。 */ std::uint32_t UiDebugBoundsColor(int depth); /** * @brief 一个待显示的布局边界 box。 * * 这里刻意用**值类型**而不是控件基类:`PlotView` 的 4 层位图缓存、`SignalPanel` 的 * 行位图缓存都要求"绘制由组件自己负责",把几何也塞进继承体系会和缓存体系打架。 * box 只描述"哪里有一个矩形、属于第几层、叫什么名字",因此**几何可以被绘制、命中与 * 调试共用**,也不需要任何虚函数或生命周期管理。 */ struct UiDebugBox { gd::RectF rect; /**< 矩形(客户区坐标)。 */ int depth = 0; /**< 层级(决定配色与角的粗细)。 */ std::wstring tag; /**< 可选短标签(空 = 不画标签)。 */ }; /** * @brief box 收集器(值语义容器)。 * * 由宿主每帧构造一次、交给各组件填充、再一次性画完 —— 全程只在「显示布局边界」打开时执行。 */ class UiDebugBoxList { public: /** @brief 清空(复用同一实例时调用)。 */ void Clear() { m_boxes.clear(); } /** @brief 追加一个 box(空矩形自动忽略)。 */ void Add(const gd::RectF& rect, int depth, std::wstring tag = std::wstring()); /** @brief 追加一组同层级的 box(空矩形逐个忽略)。 */ void AddAll(const std::vector& rects, int depth); /** @brief 是否为空。 */ bool Empty() const { return m_boxes.empty(); } /** @brief box 数量。 */ std::size_t Count() const { return m_boxes.size(); } /** @brief 只读访问。 */ const std::vector& Items() const { return m_boxes; } private: std::vector m_boxes; }; /** @brief 绘制 box 列表所需的附加资源。 */ struct UiDebugPaintStyle { HFONT font = nullptr; /**< 标签字体(`nullptr` = 一律不画标签)。 */ float scale = 1.0f; /**< DPI 缩放(只用于标签排版,不用于线宽)。 */ }; /** * @brief 绘制一个矩形的布局边界。 * * 视觉规则(**四角粗、四边细**,便于一眼定位元素的实际边界): * * 四条边固定 1 设备像素; * * 四个角额外加粗(层级 0 为 4px,其余 3px),每个角沿水平与垂直方向各延伸一段 * `cornerLen`,形成 `⌜ ⌝ ⌞ ⌟` 的包角; * * 只描边、不填充 —— 填充会在几十个嵌套矩形上层层叠加,把内容糊成一片; * * 层级 > 0 的矩形整体内缩 1 像素,避免与父矩形共边时叠成一条"更粗的边"。 * * ⚠️ **线宽按设备像素固定**(不乘 `scale`):调试线一旦随 DPI 变粗就会盖住内容。 * ⚠️ 描绘边时临时切到 `PixelOffsetModeNone` + 无抗锯齿,并把矩形对齐到整像素, * 否则 1px 线会被抗锯齿摊成 2px 灰边(无法判断真实边界)。 * * @param [IN,OUT] g 绘图上下文。 * @param [IN] rect 目标矩形(客户区坐标)。 * @param [IN] depth 层级(决定配色、角的粗细与内缩)。 */ void UiDebugPaintBounds(gd::Graphics& g, const gd::RectF& rect, int depth); /** * @brief 一次性画完整个 box 列表(在宿主绘制的最后一步调用)。 * * 按层级从浅到深排序后绘制:深层的彩色包角因此不会被父级的边压住。 * * @param [IN,OUT] g 绘图上下文。 * @param [IN] boxes box 列表。 * @param [IN] style 字体与缩放(标签用)。 */ void UiDebugPaintBoxList(gd::Graphics& g, const UiDebugBoxList& boxes, const UiDebugPaintStyle& style); } // namespace jdi #endif // __UI_DEBUG_BOUNDS_HPP__