fix: 统一输入框编辑能力、修复拖拽合并网格并支持光标吸附数据点
统一文本输入(新增 Utilities/Inc/TextEdit.hpp + TextEdit.cpp) - 一个共享的编辑模型,提供光标/选区/过滤/限长,接入全部输入框: 样式区数值与信号名/分组名、波形图例改名、视图标签页改名、 拾色器 R/G/B 与色号、光标时间与网格上下限。 - 新增能力:Ctrl+A/C/X/V(含剪贴板清洗)、Shift+方向键与 Home/End 选区、 退格与删除清除选区、代理对安全的光标步进。 - 渲染改为选区高亮 + 插入符,文本超出框宽时水平滚动并让插入符始终可见; 初始态为整段选中,首字符键入即整体替换(保持"登录框"式交互)。 - 新增 Utf8Util::PasteTextFromClipboard(换行/制表符折叠为空格)。 修复网格合并(Utilities/Src/DataModel.cpp) - 拖拽信号到其它网格时先固定目标网格的锚信号,再移除源信号并按引用找回目标, 避免空网格回收导致下标错位,从"与原网格交换位置"改为真正合并。 - 自定义划分下未分配网格的可见信号改为各自独占一个新网格, 修复"拖拽合并后再勾选新信号被并入该合并网格"的问题。 绘图区(Components/PlotView) - 网格右键菜单新增"时间轴自适应(匹配本网格)",把时间轴对齐到该网格信号范围。 - 时间轴放大到采样点在屏幕上可分辨(>=6 逻辑像素)时,拖动光标吸附到最近数据点; 缩小到无法分辨时保持自由移动,避免光标"跳格"。 验证:真实输入脚本逐项核对——拖拽合并后单网格含两条曲线、拖拽合并再勾选新信号得 G2 独立网格、 Shift 选区 + Delete 清除、Ctrl+A/C/V 复制粘贴、46 字符文本水平滚动、拾色器色号 1E90FF、 标签页改名 A1、光标时间框复制粘贴、全视图拖动得 0.522213(不吸附)而放大后得 0.28125(= 225 × 0.00125,已吸附)。
This commit is contained in:
1 parent
4e295a2a7d
commit
53768b8b25
16 files changed
+1135
-288
No files matched your search
@@ -118,6 +118,12 @@ namespace jdi
|
||||
*/
|
||||
bool IsEditing() const { return m_editField != EditField::NONE; }
|
||||
|
||||
/**
|
||||
* @brief 设置剪贴板所有者窗口句柄(用于输入框的复制/粘贴)。
|
||||
* @param [IN] owner 宿主窗口句柄,允许为空。
|
||||
*/
|
||||
void SetOwnerWindow(HWND owner) { m_owner = owner; }
|
||||
|
||||
/**
|
||||
* @brief 判断坐标是否落在拾色面板内部。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
@@ -191,6 +197,7 @@ namespace jdi
|
||||
|
||||
const UiFonts* m_fonts = nullptr; /**< 字体资源。 */
|
||||
const Theme* m_theme = nullptr; /**< 主题资源。 */
|
||||
HWND m_owner = nullptr; /**< 剪贴板所有者窗口句柄。 */
|
||||
float m_scale = 1.0f; /**< DPI 缩放比例。 */
|
||||
|
||||
bool m_open = false; /**< 是否处于打开状态。 */
|
||||
@@ -207,8 +214,7 @@ namespace jdi
|
||||
gd::RectF m_hexRect; /**< 十六进制文本矩形。 */
|
||||
|
||||
EditField m_editField = EditField::NONE; /**< 当前处于输入状态的字段。 */
|
||||
bool m_editFresh = false; /**< 输入缓冲是否仍为初始值(首次键入应整体替换)。 */
|
||||
std::wstring m_editText; /**< 输入缓冲。 */
|
||||
TextEdit m_edit; /**< 输入框编辑模型(光标/选区/剪贴板)。 */
|
||||
};
|
||||
|
||||
} // namespace jdi
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "TextEdit.hpp"
|
||||
#include "Theme.hpp"
|
||||
|
||||
namespace jdi
|
||||
@@ -211,20 +212,20 @@ namespace jdi
|
||||
bool active, float scale);
|
||||
|
||||
/**
|
||||
* @brief 绘制内联数值输入框。
|
||||
* @brief 绘制单行内联输入框(含选区高亮、插入符与水平滚动)。
|
||||
*
|
||||
* 处于“首次输入整体替换”状态时以高亮底衬表示整段内容将被覆盖,
|
||||
* 交互观感与常见登录界面的账号/密码输入框一致;已在键入时显示插入符。
|
||||
* 初始状态为“整体选中”时以强调色底衬显示全部内容,首字符键入即整体替换,
|
||||
* 交互观感与常见登录界面的账号/密码输入框一致;文本超出框宽时自动横向滚动,
|
||||
* 保证插入符始终可见。
|
||||
*
|
||||
* @param [IN,OUT] g 绘图上下文。
|
||||
* @param [IN] rect 输入框矩形。
|
||||
* @param [IN] text 当前文本(可为空)。
|
||||
* @param [IN] fresh 是否仍处于“首次输入整体替换”状态。
|
||||
* @param [IN] edit 输入框编辑模型。
|
||||
* @param [IN] font 文本字体。
|
||||
* @param [IN] scale DPI 缩放比例。
|
||||
*/
|
||||
void UiDrawEditField(gd::Graphics& g, const gd::RectF& rect, const std::wstring& text, bool fresh,
|
||||
HFONT font, float scale);
|
||||
void UiDrawEditField(gd::Graphics& g, const gd::RectF& rect, const TextEdit& edit, HFONT font,
|
||||
float scale);
|
||||
|
||||
/**
|
||||
* @brief 绘制一个带边框的复选框。
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
#ifndef __TEXT_EDIT_HPP__
|
||||
#define __TEXT_EDIT_HPP__
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace jdi
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief 输入框的字符过滤策略。
|
||||
*/
|
||||
enum class EditFilter : int
|
||||
{
|
||||
ANY = 0, /**< 接受任意可打印字符(信号名、分组名、视图名)。 */
|
||||
NUMBER = 1, /**< 仅接受数字与科学计数法符号(-+.eE)。 */
|
||||
HEX = 2, /**< 仅接受 0-9a-fA-F 与 #(色号)。 */
|
||||
BYTE = 3 /**< 仅接受整数数字(0-255 分量)。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 单行内联输入框的编辑模型。
|
||||
*
|
||||
* 提供与常规输入框一致的行为:光标移动、Shift 选区、全选、
|
||||
* 退格/删除清除选区、Ctrl+A/C/X/V、粘贴清洗,以及"首次键入整体替换"。
|
||||
* 渲染由 `PaintUtil::UiDrawEditField` 消费本对象完成(含水平滚动)。
|
||||
*/
|
||||
class TextEdit
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief 进入编辑状态。
|
||||
* @param [IN] text 初始文本。
|
||||
* @param [IN] selectAll 是否将初始文本整体选中(首次键入即整体替换)。
|
||||
* @param [IN] filter 字符过滤策略。
|
||||
* @param [IN] maxLength 允许的最大字符数(0 表示不限)。
|
||||
*/
|
||||
void Begin(const std::wstring& text, bool selectAll, EditFilter filter, std::size_t maxLength);
|
||||
|
||||
/**
|
||||
* @brief 退出编辑状态并清空内容。
|
||||
*/
|
||||
void End();
|
||||
|
||||
/**
|
||||
* @brief 判断是否处于编辑状态。
|
||||
* @return 处于编辑状态返回 true。
|
||||
*/
|
||||
bool Active() const { return m_active; }
|
||||
|
||||
/**
|
||||
* @brief 获取当前文本。
|
||||
* @return 文本常量引用。
|
||||
*/
|
||||
const std::wstring& Text() const { return m_text; }
|
||||
|
||||
/**
|
||||
* @brief 获取光标位置(插入符所在偏移)。
|
||||
* @return 字符偏移。
|
||||
*/
|
||||
std::size_t Caret() const { return m_caret; }
|
||||
|
||||
/**
|
||||
* @brief 获取选区锚点位置。
|
||||
* @return 字符偏移。
|
||||
*/
|
||||
std::size_t Anchor() const { return m_anchor; }
|
||||
|
||||
/**
|
||||
* @brief 判断是否存在选区。
|
||||
* @return 存在非空选区返回 true。
|
||||
*/
|
||||
bool HasSelection() const { return m_caret != m_anchor; }
|
||||
|
||||
/**
|
||||
* @brief 获取选区起始偏移。
|
||||
* @return 较小的偏移值。
|
||||
*/
|
||||
std::size_t SelectionBegin() const { return (m_caret < m_anchor) ? m_caret : m_anchor; }
|
||||
|
||||
/**
|
||||
* @brief 获取选区结束偏移。
|
||||
* @return 较大的偏移值。
|
||||
*/
|
||||
std::size_t SelectionEnd() const { return (m_caret < m_anchor) ? m_anchor : m_caret; }
|
||||
|
||||
/**
|
||||
* @brief 判断是否处于"首次输入整体替换"状态。
|
||||
* @return 全部文本处于选中状态返回 true。
|
||||
*/
|
||||
bool Fresh() const { return m_fresh; }
|
||||
|
||||
/**
|
||||
* @brief 在光标处插入一个字符(存在选区时先替换选区)。
|
||||
* @param [IN] character 待插入字符。
|
||||
* @return 内容发生变化返回 true。
|
||||
*/
|
||||
bool Insert(wchar_t character);
|
||||
|
||||
/**
|
||||
* @brief 退格:存在选区时删除选区,否则删除光标前一个字符。
|
||||
* @return 内容发生变化返回 true。
|
||||
*/
|
||||
bool Backspace();
|
||||
|
||||
/**
|
||||
* @brief 删除:存在选区时删除选区,否则删除光标后一个字符。
|
||||
* @return 内容发生变化返回 true。
|
||||
*/
|
||||
bool DeleteForward();
|
||||
|
||||
/**
|
||||
* @brief 移动光标。
|
||||
* @param [IN] delta 位移量(负值向左)。
|
||||
* @param [IN] extendSelection 是否按住 Shift 扩展选区。
|
||||
* @return 光标或选区发生变化返回 true。
|
||||
*/
|
||||
bool MoveCaret(int delta, bool extendSelection);
|
||||
|
||||
/**
|
||||
* @brief 移动光标到行首。
|
||||
* @param [IN] extendSelection 是否按住 Shift 扩展选区。
|
||||
* @return 光标或选区发生变化返回 true。
|
||||
*/
|
||||
bool MoveHome(bool extendSelection);
|
||||
|
||||
/**
|
||||
* @brief 移动光标到行尾。
|
||||
* @param [IN] extendSelection 是否按住 Shift 扩展选区。
|
||||
* @return 光标或选区发生变化返回 true。
|
||||
*/
|
||||
bool MoveEnd(bool extendSelection);
|
||||
|
||||
/**
|
||||
* @brief 全选当前内容。
|
||||
*/
|
||||
void SelectAll();
|
||||
|
||||
/**
|
||||
* @brief 复制当前内容到剪贴板(存在选区时只复制选区)。
|
||||
* @param [IN] owner 剪贴板所有者窗口句柄,允许为空。
|
||||
* @return 复制成功返回 true。
|
||||
*/
|
||||
bool CopyToClipboard(HWND owner) const;
|
||||
|
||||
/**
|
||||
* @brief 剪切当前内容到剪贴板(存在选区时只剪切选区)。
|
||||
* @param [IN] owner 剪贴板所有者窗口句柄,允许为空。
|
||||
* @return 内容发生变化返回 true。
|
||||
*/
|
||||
bool CutToClipboard(HWND owner);
|
||||
|
||||
/**
|
||||
* @brief 从剪贴板粘贴(替换当前选区)。
|
||||
* @param [IN] owner 剪贴板所有者窗口句柄,允许为空。
|
||||
* @return 内容发生变化返回 true。
|
||||
*/
|
||||
bool PasteFromClipboard(HWND owner);
|
||||
|
||||
/**
|
||||
* @brief 判断某字符是否符合指定过滤策略。
|
||||
* @param [IN] filter 过滤策略。
|
||||
* @param [IN] character 待检验字符。
|
||||
* @return 允许输入返回 true。
|
||||
*/
|
||||
static bool AcceptsCharacter(EditFilter filter, wchar_t character);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 用给定文本替换当前选区并更新光标。
|
||||
* @param [IN] replacement 替换文本。
|
||||
* @return 内容发生变化返回 true。
|
||||
*/
|
||||
bool ReplaceSelection(const std::wstring& replacement);
|
||||
|
||||
/**
|
||||
* @brief 把偏移量夹到合法范围并对齐到字符边界。
|
||||
* @param [IN] offset 原始偏移。
|
||||
* @return 合法偏移。
|
||||
*/
|
||||
std::size_t ClampOffset(std::size_t offset) const;
|
||||
|
||||
/**
|
||||
* @brief 向左移动一个字符(避免落在代理对中间)。
|
||||
* @param [IN] offset 起始偏移。
|
||||
* @return 新偏移。
|
||||
*/
|
||||
std::size_t StepLeft(std::size_t offset) const;
|
||||
|
||||
/**
|
||||
* @brief 向右移动一个字符(避免落在代理对中间)。
|
||||
* @param [IN] offset 起始偏移。
|
||||
* @return 新偏移。
|
||||
*/
|
||||
std::size_t StepRight(std::size_t offset) const;
|
||||
|
||||
bool m_active = false; /**< 是否处于编辑状态。 */
|
||||
bool m_fresh = false; /**< 是否处于“首次输入整体替换”状态。 */
|
||||
std::wstring m_text; /**< 当前文本。 */
|
||||
std::size_t m_caret = 0; /**< 光标位置。 */
|
||||
std::size_t m_anchor = 0; /**< 选区锚点。 */
|
||||
EditFilter m_filter = EditFilter::ANY; /**< 字符过滤策略。 */
|
||||
std::size_t m_maxLength = 0; /**< 最大字符数,0 表示不限。 */
|
||||
};
|
||||
|
||||
} // namespace jdi
|
||||
|
||||
#endif // __TEXT_EDIT_HPP__
|
||||
@@ -126,6 +126,14 @@ namespace jdi
|
||||
*/
|
||||
bool CopyTextToClipboard(HWND owner, const std::wstring& text);
|
||||
|
||||
/**
|
||||
* @brief 从系统剪贴板读取文本。
|
||||
* @param [IN] owner 剪贴板所有者窗口句柄,允许为空。
|
||||
* @param [OUT] text 读出的文本,剪贴板无文本时清空。
|
||||
* @return 读到非空文本返回 true。
|
||||
*/
|
||||
bool PasteTextFromClipboard(HWND owner, std::wstring& text);
|
||||
|
||||
} // namespace jdi
|
||||
|
||||
#endif // __UTF8_UTIL_HPP__
|
||||
Reference in new issue
Block a user