#ifndef __TOOLBAR_HPP__ #define __TOOLBAR_HPP__ #include #include #include #include #include #include #include #include "PaintUtil.hpp" #include "Theme.hpp" #include "UiButtonWidget.hpp" #include "UiComponent.hpp" #include "UiWidget.hpp" namespace jdi { /** * @brief 工具栏按钮触发的动作。 */ enum class ToolbarAction : int { NONE = 0, /**< 未触发任何动作。 */ SETTINGS, /**< **展开应用设置菜单**(齿轮图标,位于「导入文件」左侧)。 */ OPEN_FILE, /**< 打开数据文件。 */ FIT_ALL, /**< 视图自适应到可见信号范围(**不**退出选区放大模式)。 */ AUTO_FIT_Y, /**< 切换「选区 Y 轴自适应」全局开关:时间轴选区放大后自动匹配各网格 Y 轴。 */ ZOOM_SELECT, /**< 切换「选区放大」模式(横向拖拽放大时间轴,纵向拖拽放大该网格幅值)。 */ CURSOR, /**< 三态循环切换时间光标:无 → 单光标 → 双光标。 */ MERGE_LANES, /**< 合并为单一网格。 */ SPLIT_LANES, /**< 按信号拆分为多网格。 */ CLEAR_ALL /**< 清空当前标签页的全部网格与信号勾选。 */ }; /** * @brief 顶部工具栏:文件操作与视图控制的入口。 */ class Toolbar : public UiComponent { 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) override; /** * @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] action 目标按钮动作。 * @param [OUT] rect 按钮矩形(客户区坐标)。 * @return 找到且已摆放返回 true。 */ bool ButtonRect(ToolbarAction action, gd::RectF& rect) const; /** * @brief 按给定区域重新排布按钮。 * @param [IN] area 工具栏可用区域。 */ void Layout(const gd::RectF& area) override; /** @brief 工具栏占用的整条区域(与 `Layout` 同源)。 */ gd::RectF Area() const override { return m_area; } /** * @brief 绘制工具栏。 * @param [IN,OUT] g 绘图上下文。 */ void Paint(gd::Graphics& g) override; /** * @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() override; /** * @brief 判断坐标是否落在工具栏区域内。 * @param [IN] x 客户区横坐标。 * @param [IN] y 客户区纵坐标。 * @return 落在区域内返回 true。 */ bool HitTest(int x, int y) const override; /** * @brief 收集工具栏区域、**每个按钮**与按钮内容(文本/图标)的布局边界(调试用)。 * @param [IN,OUT] boxes 收集器。 */ void CollectDebugBoxes(UiDebugBoxList& boxes) override; private: /** * @brief 单个工具栏按钮的**静态配置**(矩形与外观状态由控件自己维护)。 */ struct Button { ToolbarAction action = ToolbarAction::NONE; /**< 按钮动作。 */ UiButtonWidget* widget = nullptr; /**< 对应控件(由 `m_root` 拥有)。 */ UiButtonIcon icon = UiButtonIcon::NONE; /**< 图标(非 `NONE` 时按钮只画图标)。 */ 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 按固定规格表重建控件树。 * * ⚠️ 只在 `m_treeDirty` 为真时由 `Layout` 调用:规格表、字体、主题、缩放、动态文本 * 任何一项变化都会置脏,而**纯粹的尺寸变化不会**(按钮几何由 `Arrange` 决定)。 * 旧实现每次 `Layout` 都无条件重建(拖动面板分隔条时每帧一次)⇒ 每帧 9 次堆分配 * 加 8 次测量缓存失效。 */ 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