#ifndef __TAB_BAR_HPP__ #define __TAB_BAR_HPP__ #include #include #include #include #include #include "PaintUtil.hpp" #include "Theme.hpp" namespace jdi { /** * @brief 标签栏交互结果。 */ struct TabBarResult { /** * @brief 交互动作类型。 */ enum class Type : int { NONE = 0, /**< 未触发任何动作。 */ SELECT, /**< 切换到指定标签页。 */ CLOSE, /**< 关闭指定标签页。 */ ADD, /**< 新建标签页。 */ CONTEXT, /**< 请求弹出标签页上下文菜单。 */ REORDER, /**< 拖拽调整标签页顺序。 */ RENAME /**< 提交标签页重命名。 */ }; Type type = Type::NONE; /**< 动作类型。 */ int index = -1; /**< 标签页下标(ADD 时无意义)。 */ int targetIndex = -1; /**< REORDER 的目标位置。 */ std::wstring text; /**< RENAME 的新名称。 */ int screenX = 0; /**< 上下文菜单请求的屏幕横坐标。 */ int screenY = 0; /**< 上下文菜单请求的屏幕纵坐标。 */ }; /** * @brief 顶部标签栏:支持多视图切换、新建与关闭。 */ class TabBar { public: TabBar(); /** * @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] names 标签页名称列表。 */ void SetTabs(const std::vector& names); /** * @brief 设置当前激活的标签页下标。 * @param [IN] index 标签页下标,-1 表示无激活页。 */ void SetActiveIndex(int index); /** * @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 客户区纵坐标。 * @return 命中可点击元素返回 true。 */ bool OnLButtonDown(int x, int y); /** * @brief 处理鼠标左键抬起并给出交互结果。 * @param [IN] x 客户区横坐标。 * @param [IN] y 客户区纵坐标。 * @param [OUT] result 交互结果。 * @return 视觉状态发生变化返回 true。 */ bool OnLButtonUp(int x, int y, TabBarResult& result); /** * @brief 处理鼠标右键按下(请求标签页上下文菜单)。 * @param [IN] x 客户区横坐标。 * @param [IN] y 客户区纵坐标。 * @param [IN] screenX 屏幕横坐标。 * @param [IN] screenY 屏幕纵坐标。 * @param [OUT] result 交互结果。 * @return 命中标签页返回 true。 */ bool OnRButtonDown(int x, int y, int screenX, int screenY, TabBarResult& result); /** * @brief 处理鼠标左键双击(进入标签页重命名)。 * @param [IN] x 客户区横坐标。 * @param [IN] y 客户区纵坐标。 * @return 进入编辑态返回 true。 */ bool OnDoubleClick(int x, int y); /** * @brief 判断标签栏是否处于重命名编辑态。 * @return 处于编辑态返回 true。 */ bool IsEditing() const { return m_editIndex >= 0; } /** * @brief 处理编辑态下的字符输入。 * @param [IN] character 输入字符。 * @return 界面状态发生变化返回 true。 */ bool OnEditChar(wchar_t character); /** * @brief 处理编辑态下的按键。 * @param [IN] key 虚拟键码。 * @param [OUT] result 提交重命名时返回结果。 * @return 事件已被消费返回 true。 */ bool OnEditKey(WPARAM key, TabBarResult& result); /** * @brief 编辑态下按下鼠标右键:取消选区并把插入符移到文本末尾。 * @return 发生改变返回 true;不在编辑态返回 false。 */ bool CollapseEditSelectionToEnd(); /** * @brief 编辑态下在输入框内按下鼠标左键:定位插入符并开始拖拽框选。 * @param [IN] x 客户区横坐标。 * @param [IN] y 客户区纵坐标。 * @return 命中输入框返回 true。 */ bool OnEditMouseDown(int x, int y); /** * @brief 编辑态下拖拽鼠标:扩展选区。 * @param [IN] x 客户区横坐标。 * @return 选区发生变化返回 true。 */ bool OnEditMouseMove(int x); /** * @brief 编辑态下松开鼠标左键:结束拖拽框选。 * @return 之前处于拖拽中返回 true。 */ bool OnEditMouseUp(); /** * @brief 计算重命名输入框的矩形(绘制与命中测试共用同一套几何)。 * @return 输入框矩形;不在编辑态时返回空矩形。 */ gd::RectF EditRect() const; /** * @brief 查询拖拽排序时应当插入的位置。 * @param [IN] x 客户区横坐标。 * @return 插入下标(0..标签数量)。 */ int DropIndexAt(int x) const; /** * @brief 处理鼠标离开标签栏。 * @return 视觉状态发生变化返回 true。 */ bool OnMouseLeave(); /** * @brief 设置宿主窗口句柄(用于剪贴板所有者)。 * @param [IN] host 宿主窗口句柄,允许为空。 */ void SetHostWindow(HWND host) { m_host = host; } /** * @brief 判断坐标是否落在标签栏区域内。 * @param [IN] x 客户区横坐标。 * @param [IN] y 客户区纵坐标。 * @return 落在区域内返回 true。 */ bool HitTest(int x, int y) const; private: /** * @brief 单个标签页的布局信息。 */ struct TabItem { std::wstring name; /**< 标签页名称。 */ gd::RectF rect; /**< 标签整体矩形。 */ gd::RectF closeRect; /**< 关闭按钮矩形。 */ }; /** * @brief 判断坐标命中的标签页下标。 * @param [IN] x 客户区横坐标。 * @param [IN] y 客户区纵坐标。 * @return 标签页下标,未命中返回 -1。 */ int TabAtPoint(int x, int y) const; const UiFonts* m_fonts = nullptr; /**< 字体资源。 */ const Theme* m_theme = nullptr; /**< 主题资源。 */ float m_scale = 1.0f; /**< DPI 缩放比例。 */ std::vector m_tabs; /**< 标签页布局集合。 */ gd::RectF m_addRect; /**< 新建按钮矩形。 */ gd::RectF m_area; /**< 标签栏整体区域。 */ int m_activeIndex = -1; /**< 当前激活标签页下标。 */ int m_hoverIndex = -1; /**< 当前悬停标签页下标。 */ bool m_hoverClose = false; /**< 是否悬停在关闭按钮上。 */ bool m_hoverAdd = false; /**< 是否悬停在新建按钮上。 */ int m_pressedIndex = -1; /**< 当前按下的标签页下标。 */ bool m_pressedClose = false; /**< 是否按在关闭按钮上。 */ bool m_pressedAdd = false; /**< 是否按在新建按钮上。 */ int m_dragIndex = -1; /**< 正在拖动排序的标签页下标,-1 表示未拖动。 */ int m_dragMouseX = 0; /**< 拖动过程中鼠标横坐标。 */ bool m_dragMoved = false; /**< 拖动过程中是否发生了位移。 */ int m_pressedX = 0; /**< 按下时的横坐标(用于判定拖动阈值)。 */ int m_editIndex = -1; /**< 正在重命名的标签页下标,-1 表示未编辑。 */ TextEdit m_edit; /**< 输入框编辑模型(光标/选区/剪贴板)。 */ HWND m_host = nullptr; /**< 宿主窗口句柄(惰性绑定,用于剪贴板)。 */ int m_lastClickIndex = -1; /**< 上一次单击的标签页下标(用于自行判定双击)。 */ DWORD m_lastClickTick = 0; /**< 上一次单击的时刻。 */ }; } // namespace jdi #endif // __TAB_BAR_HPP__