新增 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。
85 lines
3.3 KiB
C++
85 lines
3.3 KiB
C++
#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__
|