Files
Just-DataInspector/Components/Shared/Inc/UiButtonWidget.hpp
T
DeepSeek V4.1 Flash 25264f9074 UI 重构 P3:Toolbar 迁移到控件树(新增 UiButtonWidget,逐像素 0 差异 + 功能断言)
新增 Components/Shared/{Inc,Src}/UiButtonWidget.*:通用「文字按钮」控件,视觉规则与旧 Toolbar
按钮逐像素一致(主按钮强调色实底 / 普通按钮 1px 描边 / 可切换选中态 / 禁用态配色);
交互由控件自己处理:按下即 CaptureMouse 进入按下态,在自身范围内松开才触发 onClick,
指针离开(不会再收到抬起)时 CancelPress。

Toolbar 内部重写,公开 API 一字未改:
- Button 只保留静态配置 + 控件指针;矩形与外观状态归控件。
- 删除 m_hoverIndex / m_pressedIndex 与死字段 Button::hover,改由 UiWidget 的悬停链与 capture 维护。
- Paint 改为「根控件(底色 + 底部分隔线)+ 按钮子节点」,一次 root->Paint(ctx)。
- OnMouseMove/Down/Up/Leave 统一翻译成 UiEvent 后 root->Dispatch;动作经 onClick 回调登记、抬起后执行。

框架约定调整:UiWidget::Paint 的裁剪从「严格等于自身矩形」改为「自身矩形外扩 1px(kUiClipBleed)」。
边框/描边以矩形边界为中心绘制,严格裁剪会切掉外半侧使控件变细、与旧绘制不再像素一致。
uicheck 对应断言改为「裁剪 ⊇ 自身矩形且不超出外扩范围」。

验证:
- make release / make debug 零警告;Target.exe 881,664 B(< 900KB)。
- 逐像素 A/B(8 场景:全览 / 深放大+自由标记+滤波+弱峰+光标 / 平移 / 悬停按钮 / 按下 / 松开 /
  悬停主按钮 / 移出工具栏):迁移前后哈希逐一相同
  A 2166933039626F66 B C444ECFEB18FD73D C 7984446D8E7A4418 D BF855879CA12F289
  E 1C663E4118616016 F 0535292D8E07AE18 G 7A58172C46577729 H 23F390DD76141113。
- 功能断言:locate-cursor-button PASS (1291,73);click-cursor-changes-mode PASS (1 -> 2);
  release-outside-does-not-fire PASS (2 -> 2)。
- Tools/uicheck 62 passed / 0 failed。
2026-10-28 12:29:08 +08:00

85 lines
3.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef __UI_BUTTON_WIDGET_HPP__
#define __UI_BUTTON_WIDGET_HPP__
#include <functional>
#include <string>
#include "UiWidget.hpp"
namespace jdi
{
/**
* @brief 一个"文字按钮"控件(工具栏按钮的原语)。
*
* 视觉规则与旧 `Toolbar` 的按钮**逐像素一致**:
* * 主按钮(`primary`)= 强调色实底 + `textOnAccent` 文字,按下 / 悬停时整体提亮或压暗;
* * 普通按钮 = 透明底 + 1px 描边,可切换按钮选中时用 `accentSoft` 底 + `accent` 描边;
* * 禁用时底色回退为工具栏底色、文字用 `textMuted`、描边与底色混 50%。
*
* 交互:按下即 `CaptureMouse()` 并进入按下态;**在自身范围内**松开才触发 `onClick`
*(与"按住移开再松开 = 放弃"的既有语义一致)。
*/
class UiButtonWidget : public UiWidget
{
public:
UiButtonWidget() = default;
/** @brief 一次性注入绘制资源与外观参数(尺寸由 `OnMeasure` 按文本实测得出)。 */
void Configure(const UiFonts* fonts, const Theme* theme, float scale, std::wstring label,
bool primary, bool toggle, float textPadding, float height);
/** @brief 修改文本(宽度会在下一次 `InvalidateMeasure` 后重新实测)。 */
void SetLabel(std::wstring label);
/** @brief 文本(只读)。 */
const std::wstring& Label() const { return m_label; }
/** @brief 设置可切换按钮的选中态。 */
void SetChecked(bool checked);
/** @brief 选中态。 */
bool Checked() const { return m_checked; }
/** @brief 是否可切换按钮。 */
bool IsToggle() const { return m_toggle; }
/** @brief 设置可用性(禁用时不可点、不可命中)。 */
void SetEnabled(bool enabled);
/** @brief 是否可用。 */
bool Enabled() const { return m_enabled; }
/** @brief 是否处于按下态(按下未松开)。 */
bool Pressed() const { return m_pressed; }
/** @brief 放弃当前按下(指针离开窗口等不会再收到抬起的情形)。 */
void CancelPress();
/** @brief 点击回调(在自身范围内松开时触发)。 */
std::function<void()> onClick;
/** @brief 工具栏底色(普通按钮的底色与禁用配色都以它为基准)。 */
void SetCanvasColor(std::uint32_t color) { m_canvasColor = color; }
protected:
gd::SizeF OnMeasure(const UiMeasureContext& ctx) override;
void OnPaint(UiPaintContext& ctx) override;
UiEventResult OnEvent(UiEvent& event) override;
HCURSOR OnQueryCursor(int x, int y) override;
std::uint64_t ContentKey() const override;
private:
const UiFonts* m_fonts = nullptr; /**< 字体。 */
const Theme* m_theme = nullptr; /**< 主题。 */
float m_scale = 1.0f; /**< 缩放系数。 */
std::wstring m_label; /**< 按钮文本。 */
bool m_primary = false; /**< 主按钮(强调色实底)。 */
bool m_toggle = false; /**< 是否可切换。 */
bool m_checked = false; /**< 可切换按钮的选中态。 */
bool m_enabled = true; /**< 是否可用。 */
bool m_pressed = false; /**< 按下态。 */
float m_textPadding = 14.0f; /**< 左右内边距(逻辑像素)。 */
float m_height = 30.0f; /**< 按钮高度(逻辑像素)。 */
std::uint32_t m_canvasColor = 0; /**< 工具栏底色(配色基准)。 */
};
} // namespace jdi
#endif // __UI_BUTTON_WIDGET_HPP__