新增 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。
182 lines
5.9 KiB
C++
182 lines
5.9 KiB
C++
#ifndef __TOOLBAR_HPP__
|
|
#define __TOOLBAR_HPP__
|
|
|
|
#include <windows.h>
|
|
#include <objidl.h>
|
|
#include <gdiplus.h>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "PaintUtil.hpp"
|
|
#include "Theme.hpp"
|
|
#include "UiButtonWidget.hpp"
|
|
#include "UiWidget.hpp"
|
|
|
|
namespace jdi
|
|
{
|
|
|
|
/**
|
|
* @brief 工具栏按钮触发的动作。
|
|
*/
|
|
enum class ToolbarAction : int
|
|
{
|
|
NONE = 0, /**< 未触发任何动作。 */
|
|
OPEN_FILE, /**< 打开数据文件。 */
|
|
FIT_ALL, /**< 视图自适应到可见信号范围(**不**退出选区放大模式)。 */
|
|
AUTO_FIT_Y, /**< 切换「选区 Y 轴自适应」全局开关:时间轴选区放大后自动匹配各网格 Y 轴。 */
|
|
ZOOM_SELECT, /**< 切换「选区放大」模式(横向拖拽放大时间轴,纵向拖拽放大该网格幅值)。 */
|
|
CURSOR, /**< 三态循环切换时间光标:无 → 单光标 → 双光标。 */
|
|
MERGE_LANES, /**< 合并为单一网格。 */
|
|
SPLIT_LANES, /**< 按信号拆分为多网格。 */
|
|
CLEAR_ALL /**< 清空当前标签页的全部网格与信号勾选。 */
|
|
};
|
|
|
|
/**
|
|
* @brief 顶部工具栏:文件操作与视图控制的入口。
|
|
*/
|
|
class Toolbar
|
|
{
|
|
public:
|
|
Toolbar();
|
|
~Toolbar();
|
|
|
|
/**
|
|
* @brief 绑定字体与主题资源。
|
|
* @param [IN] fonts 字体集合,需在工具栏生命周期内保持有效。
|
|
* @param [IN] theme 主题配色,需在工具栏生命周期内保持有效。
|
|
*/
|
|
void Setup(const UiFonts* fonts, const Theme* theme);
|
|
|
|
/**
|
|
* @brief 设置 DPI 缩放比例。
|
|
* @param [IN] scale 缩放比例(1.0 对应 96 DPI)。
|
|
*/
|
|
void SetScale(float scale);
|
|
|
|
/**
|
|
* @brief 设置是否已加载数据(未加载时大部分按钮被禁用)。
|
|
* @param [IN] hasDocument 是否已加载数据。
|
|
*/
|
|
void SetDocumentLoaded(bool hasDocument);
|
|
|
|
/**
|
|
* @brief 设置可切换按钮的选中状态。
|
|
* @param [IN] action 目标按钮动作。
|
|
* @param [IN] checked 是否处于选中(生效)状态。
|
|
*/
|
|
void SetToggleState(ToolbarAction action, bool checked);
|
|
|
|
/**
|
|
* @brief 覆盖某个按钮的显示文本(用于状态相关的动态标签,例如「光标」)。
|
|
*
|
|
* 调用后需要由宿主重新排布(`Layout`)才能让按钮宽度与新文本匹配。
|
|
*
|
|
* @param [IN] action 目标按钮动作。
|
|
* @param [IN] label 新文本。
|
|
* @return 文本发生变化返回 true(此时宿主应重新排布)。
|
|
*/
|
|
bool SetActionLabel(ToolbarAction action, const std::wstring& label);
|
|
|
|
/**
|
|
* @brief 计算工具栏在指定缩放下的期望高度。
|
|
* @return 期望高度(像素)。
|
|
*/
|
|
float DesiredHeight() const;
|
|
|
|
/**
|
|
* @brief 按给定区域重新排布按钮。
|
|
* @param [IN] area 工具栏可用区域。
|
|
*/
|
|
void Layout(const gd::RectF& area);
|
|
|
|
/**
|
|
* @brief 绘制工具栏。
|
|
* @param [IN,OUT] g 绘图上下文。
|
|
*/
|
|
void Paint(gd::Graphics& g);
|
|
|
|
/**
|
|
* @brief 处理鼠标移动,更新悬停状态。
|
|
* @param [IN] x 客户区横坐标。
|
|
* @param [IN] y 客户区纵坐标。
|
|
* @return 悬停状态发生变化返回 true。
|
|
*/
|
|
bool OnMouseMove(int x, int y);
|
|
|
|
/**
|
|
* @brief 处理鼠标左键按下。
|
|
* @param [IN] x 客户区横坐标。
|
|
* @param [IN] y 客户区纵坐标。
|
|
* @param [OUT] action 命中的按钮动作。
|
|
* @return 命中按钮返回 true。
|
|
*/
|
|
bool OnLButtonDown(int x, int y, ToolbarAction& action);
|
|
|
|
/**
|
|
* @brief 处理鼠标左键抬起。
|
|
* @param [IN] x 客户区横坐标。
|
|
* @param [IN] y 客户区纵坐标。
|
|
* @param [OUT] action 抬起时命中的按钮动作(仅在按钮上抬起时有效)。
|
|
* @return 视觉状态发生变化返回 true。
|
|
*/
|
|
bool OnLButtonUp(int x, int y, ToolbarAction& action);
|
|
|
|
/**
|
|
* @brief 处理鼠标离开工具栏。
|
|
* @return 视觉状态发生变化返回 true。
|
|
*/
|
|
bool OnMouseLeave();
|
|
|
|
/**
|
|
* @brief 判断坐标是否落在工具栏区域内。
|
|
* @param [IN] x 客户区横坐标。
|
|
* @param [IN] y 客户区纵坐标。
|
|
* @return 落在区域内返回 true。
|
|
*/
|
|
bool HitTest(int x, int y) const;
|
|
|
|
private:
|
|
/**
|
|
* @brief 单个工具栏按钮的**静态配置**(矩形与外观状态由控件自己维护)。
|
|
*/
|
|
struct Button
|
|
{
|
|
ToolbarAction action = ToolbarAction::NONE; /**< 按钮动作。 */
|
|
UiButtonWidget* widget = nullptr; /**< 对应控件(由 `m_root` 拥有)。 */
|
|
bool primary = false; /**< 是否为主按钮(强调色填充)。 */
|
|
bool needsDocument = true; /**< 是否依赖已加载数据。 */
|
|
bool toggle = false; /**< 是否为可切换按钮。 */
|
|
bool checked = false; /**< 可切换按钮的选中状态。 */
|
|
float width = 0.0f; /**< 按钮宽度(由控件实测得出)。 */
|
|
};
|
|
|
|
/**
|
|
* @brief 判断按钮当前是否可用。
|
|
* @param [IN] button 目标按钮。
|
|
* @return 可用返回 true。
|
|
*/
|
|
bool IsEnabled(const Button& button) const;
|
|
|
|
/** @brief 按固定规格表重建控件树(文本变化/首次布局时调用)。 */
|
|
void RebuildWidgets();
|
|
/** @brief 把可用性同步到控件(未加载数据时禁用依赖数据的按钮)。 */
|
|
void SyncEnabledState();
|
|
|
|
const UiFonts* m_fonts = nullptr; /**< 字体资源。 */
|
|
const Theme* m_theme = nullptr; /**< 主题资源。 */
|
|
float m_scale = 1.0f; /**< DPI 缩放比例。 */
|
|
bool m_documentLoaded = false; /**< 是否已加载数据。 */
|
|
std::vector<Button> m_buttons; /**< 按钮配置集合。 */
|
|
std::vector<std::pair<ToolbarAction, std::wstring>> m_labelOverrides; /**< 动态文本覆盖表。 */
|
|
gd::RectF m_area; /**< 工具栏整体区域。 */
|
|
std::unique_ptr<UiWidget> m_root; /**< 控件树根(负责底色与底部分隔线)。 */
|
|
ToolbarAction m_pendingAction = ToolbarAction::NONE; /**< 最近一次成功点击的动作。 */
|
|
};
|
|
|
|
} // namespace jdi
|
|
|
|
#endif // __TOOLBAR_HPP__
|