#include "UiDebugBounds.hpp" #include namespace jdi { namespace { /** @brief 顶层组件区域的描边宽度(设备像素)。 */ const int kAreaStroke = 2; /** @brief 内部控件 / 子块的描边宽度(设备像素)。 */ const int kNodeStroke = 1; /** * @brief 用整数像素矩形填充(要求调用方已把像素偏移模式设为 `None`)。 * * 走 GDI+ 而不是 `UiFillRect`:这里需要**精确的 1px 覆盖**,而 `UiFillRect` 的快 * 路径按 `floor/ceil` 取整并可能转交 GDI,混进调试层会带来额外的状态切换开销。 */ void FillPixels(gd::Graphics& g, int x, int y, int width, int height, std::uint32_t argb) { if (width <= 0 || height <= 0) { return; } gd::SolidBrush brush{gd::Color(argb)}; g.FillRectangle(&brush, x, y, width, height); } } // namespace std::uint32_t UiDebugBoundsColor(int depth) { // 与主题无关的固定配色:调试层必须"永远显眼",不能跟随用户主题变成接近底色。 switch (depth) { case 0: return 0xFF29B6F6u; // 青蓝:顶层组件区域 case 1: return 0xFFE040FBu; // 品红:控件节点 case 2: return 0xFFFFB300u; // 琥珀:内部子块 default: return 0xFF66BB6Au; // 绿:更深的子块 } } void UiDebugPaintBounds(gd::Graphics& g, const gd::RectF& rect, int depth, float scale) { (void)scale; // 线宽刻意不随 DPI 变化(见头文件说明),此参数保留以便将来做对齐细分。 if (!(rect.Width > 0.0f) || !(rect.Height > 0.0f)) { return; } // 对齐到整像素:把矩形四边四舍五入,保证 1px 描边正好压住一格像素。 const int left = static_cast(std::lround(rect.X)); const int top = static_cast(std::lround(rect.Y)); int right = static_cast(std::lround(rect.X + rect.Width)) - 1; int bottom = static_cast(std::lround(rect.Y + rect.Height)) - 1; if (right < left) { right = left; } if (bottom < top) { bottom = top; } const int width = right - left + 1; const int height = bottom - top + 1; const int level = (depth < 0) ? 0 : depth; const std::uint32_t color = UiDebugBoundsColor(level); const int stroke = (level == 0) ? kAreaStroke : kNodeStroke; const gd::GraphicsState state = g.Save(); // 描边必须锐利:关抗锯齿 + 关半像素偏移,矩形已对齐整像素 ⇒ 1px 就是 1px。 g.SetSmoothingMode(gd::SmoothingModeNone); g.SetPixelOffsetMode(gd::PixelOffsetModeNone); // ⚠️ **只描边、不填充**(与 Android「Show layout bounds」一致): // 填充会在几十个嵌套矩形上层层叠加,把内容糊成一片,反而看不出真实边界。 // 四条边(用实心矩形而不是 `DrawRectangle`:后者会因笔宽奇偶而偏移半像素)。 const int thick = (stroke * 2 <= width) ? stroke : 1; const int thickY = (stroke * 2 <= height) ? stroke : 1; FillPixels(g, left, top, width, thickY, color); // 上 FillPixels(g, left, bottom - thickY + 1, width, thickY, color); // 下 FillPixels(g, left, top, thick, height, color); // 左 FillPixels(g, right - thick + 1, top, thick, height, color); // 右 g.Restore(state); } } // namespace jdi