fix: 重构样式区交互与布局,修复拾色浮窗穿透与自定义颜色记忆
样式编辑区统一为固定四行网格(颜色 / 样式 / 透明度 / 线宽): - 四行共用同一内容列宽度,宽度带上下限(面板拉宽时不再无限延伸), 标签列与内容列作为整体在面板内居中,行标签统一按整行垂直居中, 修复“颜色”字样与色块存在高低差的问题。 - 颜色行的色块间距按剩余空间自适应,使该行与其它三行等宽对齐。 - 线宽改为与透明度同款的滑块(0.5–10 px),从原先的“预设 + 数值旋钮” 调整为置于样式区最下方的滑块,保留双击数值直接键入的能力。 自定义颜色浮窗(ColorPickerPopup): - 拖动滑块时不再与下方图层(尤其是绘图区平移/框选)产生交互: 拾色浮窗内的按下会被完整消费并抓取鼠标,不再下传。 - 打开浮窗时光标正确显示为横向调整(IDC_SIZEWE),其余区域为箭头。 - 新增自定义颜色记忆:SignalStyle 记录 customColor,从自定义色切换到 预设色后再次点击加号方格即可直接回到上次的自定义颜色; 加号方格始终以该颜色填充(与预设色块同尺寸、同外框)。 - 打开浮窗时以记忆色为初始取值,不再重新从预设色起步。 透明度: - 预设色块与自定义颜色方格同样叠加不透明度显示, 修复此前透明度只影响自定义颜色预览的问题。 其他: - 左侧面板宽度上限由 560 收紧至 400,避免样式控件被拉得过长。 - 移除只写不读的色块尺寸缓存与已废弃的线宽步进接口。 验证:真实鼠标输入逐项核对(拾色浮窗拖拽后绘图区视图范围与时间轴完全 不变、滑块光标 SIZEWE、自定义色 #0071C4 在切换预设后仍可一键恢复、 透明度 21% 时全部预设色块同步变淡、线宽滑块 8.1 px / 双击键入 5 设为 5.0 px、面板最窄与最宽两种极限下四行均不溢出且等宽)。 make clean && make all 零错误零警告。
This commit is contained in:
1 parent
2d09935063
commit
dbe9b904ab
6 files changed
+271
-284
No files matched your search
@@ -27,8 +27,8 @@ namespace jdi
|
||||
const wchar_t* kWindowTitle = L"Just DataInspector"; /**< 默认窗口标题。 */
|
||||
const float kStatusBarHeight = 26.0f; /**< 状态栏高度(逻辑像素)。 */
|
||||
const float kSplitterWidth = 4.0f; /**< 分隔条宽度(逻辑像素)。 */
|
||||
const float kMinPanelWidth = 220.0f; /**< 面板最小宽度(逻辑像素)。 */
|
||||
const float kMaxPanelWidth = 560.0f; /**< 面板最大宽度(逻辑像素)。 */
|
||||
const float kMinPanelWidth = 260.0f; /**< 面板最小宽度(逻辑像素)。 */
|
||||
const float kMaxPanelWidth = 400.0f; /**< 面板最大宽度(逻辑像素)。 */
|
||||
const int kMinClientWidth = 860; /**< 窗口最小客户区宽度。 */
|
||||
const int kMinClientHeight = 540; /**< 窗口最小客户区高度。 */
|
||||
const UINT kMessageRemoveFile = WM_APP + 1; /**< 面板请求移除文件的自定义消息。 */
|
||||
@@ -1178,31 +1178,51 @@ namespace jdi
|
||||
consume = true;
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
{
|
||||
const int pickX = GET_X_LPARAM(lParam);
|
||||
const int pickY = GET_Y_LPARAM(lParam);
|
||||
|
||||
// 拾色器接管输入后,取消底层组件可能仍在进行的拖拽。
|
||||
m_panel.OnLButtonUp(0, 0);
|
||||
m_plot.OnLButtonUp(0, 0);
|
||||
m_activeTarget = Target::NONE;
|
||||
m_draggingSplitter = false;
|
||||
|
||||
// 点击拾色器外部时收起;若落点落在信号面板内则放行本次点击,
|
||||
// 便于用户在展开状态下直接改选预设色、线宽等其它样式。
|
||||
if (m_colorPicker.ContainsPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)))
|
||||
if (m_colorPicker.ContainsPoint(pickX, pickY))
|
||||
{
|
||||
m_colorPicker.OnLButtonDown(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
||||
// 面板内部:开始拖动滑块,并吃掉本次点击,避免穿透到下方图层。
|
||||
m_colorPicker.OnLButtonDown(pickX, pickY);
|
||||
SetCapture(hwnd);
|
||||
consume = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 面板外部:收起面板;若落点在信号面板内则放行本次点击,
|
||||
// 便于用户在展开状态下直接改选预设色、样式或拖动滑块。
|
||||
m_colorPicker.Close();
|
||||
if (!m_panel.HitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)))
|
||||
if (!m_panel.HitTest(pickX, pickY))
|
||||
{
|
||||
InvalidateRect(hwnd, nullptr, FALSE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
repaint = true;
|
||||
consume = false;
|
||||
break;
|
||||
}
|
||||
case WM_SETCURSOR:
|
||||
{
|
||||
if (LOWORD(lParam) == HTCLIENT)
|
||||
{
|
||||
POINT point = {};
|
||||
GetCursorPos(&point);
|
||||
ScreenToClient(hwnd, &point);
|
||||
|
||||
HCURSOR cursor = m_colorPicker.QueryCursor(point.x, point.y);
|
||||
SetCursor(cursor != nullptr ? cursor : LoadCursorW(nullptr, IDC_ARROW));
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONUP:
|
||||
m_colorPicker.OnLButtonUp();
|
||||
ReleaseCapture();
|
||||
|
||||
@@ -246,12 +246,10 @@ namespace jdi
|
||||
NONE = 0, /**< 未命中。 */
|
||||
COLOR, /**< 预设颜色色块。 */
|
||||
CUSTOM_CHIP, /**< 自定义颜色入口。 */
|
||||
WIDTH_PRESET, /**< 线宽预设按钮(细/粗)。 */
|
||||
WIDTH_MINUS, /**< 线宽减号。 */
|
||||
WIDTH_PLUS, /**< 线宽加号。 */
|
||||
WIDTH_VALUE, /**< 线宽数值框。 */
|
||||
MODE, /**< 绘制样式按钮。 */
|
||||
OPACITY, /**< 不透明度滑块。 */
|
||||
OPACITY_SLIDER, /**< 不透明度滑块。 */
|
||||
WIDTH_SLIDER, /**< 线宽滑块。 */
|
||||
WIDTH_VALUE, /**< 线宽数值框(可双击输入)。 */
|
||||
TOGGLE_ALL /**< 全部显示/隐藏按钮。 */
|
||||
};
|
||||
|
||||
@@ -408,13 +406,6 @@ namespace jdi
|
||||
*/
|
||||
int TotalSignalCount() const;
|
||||
|
||||
/**
|
||||
* @brief 调整当前信号线宽。
|
||||
* @param [IN] delta 增量,正值加粗。
|
||||
* @return 取值发生变化返回 true。
|
||||
*/
|
||||
bool StepLineWidth(float delta);
|
||||
|
||||
/**
|
||||
* @brief 进入线宽数值输入状态。
|
||||
* @return 状态发生变化返回 true。
|
||||
@@ -435,6 +426,13 @@ namespace jdi
|
||||
*/
|
||||
bool ApplyOpacityFromX(float x);
|
||||
|
||||
/**
|
||||
* @brief 依据线宽滑块横坐标调整当前信号线宽。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
* @return 取值发生变化返回 true。
|
||||
*/
|
||||
bool ApplyWidthFromX(float x);
|
||||
|
||||
HWND m_host = nullptr; /**< 宿主窗口句柄。 */
|
||||
const UiFonts* m_fonts = nullptr; /**< 字体资源。 */
|
||||
const Theme* m_theme = nullptr; /**< 主题资源。 */
|
||||
@@ -455,19 +453,17 @@ namespace jdi
|
||||
std::array<gd::RectF, 12> m_colorRects; /**< 预设颜色色块矩形。 */
|
||||
int m_colorCount = 0; /**< 实际可用的预设颜色数量。 */
|
||||
gd::RectF m_customChipRect; /**< 自定义颜色入口矩形(与预设色块同尺寸)。 */
|
||||
float m_swatchSize = 18.0f; /**< 颜色色块边长(逻辑像素)。 */
|
||||
|
||||
gd::RectF m_widthThinRect; /**< 最细线宽预设矩形。 */
|
||||
gd::RectF m_widthThickRect; /**< 最粗线宽预设矩形。 */
|
||||
gd::RectF m_widthMinusRect; /**< 线宽减号矩形。 */
|
||||
gd::RectF m_widthPlusRect; /**< 线宽加号矩形。 */
|
||||
gd::RectF m_widthValueRect; /**< 线宽数值显示矩形。 */
|
||||
gd::RectF m_widthSpinnerRect; /**< 线宽数值输入框整体矩形。 */
|
||||
|
||||
std::array<gd::RectF, 3> m_modeRects; /**< 绘制样式按钮矩形。 */
|
||||
|
||||
// 样式区固定四行:颜色 / 样式 / 透明度 / 线宽。各行控件共用同一内容列宽度。
|
||||
std::array<gd::RectF, 4> m_styleRowRects; /**< 四行整体矩形(含标签列)。 */
|
||||
gd::RectF m_styleContentRect; /**< 四行共用的内容列矩形。 */
|
||||
|
||||
gd::RectF m_opacityTrackRect; /**< 不透明度滑块轨道矩形。 */
|
||||
gd::RectF m_opacityValueRect; /**< 不透明度数值文本矩形。 */
|
||||
gd::RectF m_widthTrackRect; /**< 线宽滑块轨道矩形。 */
|
||||
gd::RectF m_widthValueRect; /**< 线宽数值文本矩形(双击可输入)。 */
|
||||
|
||||
std::vector<ListRow> m_rows; /**< 列表行集合(惰性缓存)。 */
|
||||
bool m_rowsDirty = true; /**< 列表行集合是否需要重建。 */
|
||||
@@ -479,6 +475,7 @@ namespace jdi
|
||||
bool m_draggingScrollBar = false; /**< 是否正在拖拽滚动条。 */
|
||||
int m_scrollGrabOffset = 0; /**< 拖拽时鼠标相对滑块顶部的偏移。 */
|
||||
bool m_draggingOpacity = false; /**< 是否正在拖拽不透明度滑块。 */
|
||||
bool m_draggingWidth = false; /**< 是否正在拖拽线宽滑块。 */
|
||||
int m_hoverRow = -1; /**< 当前悬停行。 */
|
||||
RowPart m_hoverPart = RowPart::NONE; /**< 当前悬停的行内区域。 */
|
||||
StyleHit m_hoverStyle = StyleHit::NONE; /**< 当前悬停的样式条目类型。 */
|
||||
|
||||
@@ -25,26 +25,26 @@ namespace jdi
|
||||
const float kScrollBarThickness = 8.0f; /**< 滚动条宽度。 */
|
||||
const float kCloseButtonSize = 18.0f; /**< 文件行关闭按钮尺寸。 */
|
||||
|
||||
// 样式编辑区:固定四行网格,行高与间距恒定,保证面板宽度变化时控件不散架。
|
||||
// 样式编辑区:固定四行网格(颜色 / 样式 / 透明度 / 线宽),行高、间距与内容列宽度统一,
|
||||
// 保证面板宽度变化时四行控件始终对齐,且不会被无限制拉伸。
|
||||
const float kStylePadH = 12.0f; /**< 样式区左右内边距。 */
|
||||
const float kStylePadV = 12.0f; /**< 样式区上下内边距。 */
|
||||
const float kStyleRowHeight = 26.0f; /**< 样式区单行高度。 */
|
||||
const float kStyleRowGap = 10.0f; /**< 样式区行间距。 */
|
||||
const float kStyleLabelWidth = 44.0f; /**< 样式行标签列宽度。 */
|
||||
const float kStyleLabelGap = 8.0f; /**< 标签列与内容列间距。 */
|
||||
const float kStyleRowCount = 4.0f; /**< 样式区固定行数。 */
|
||||
const float kSwatchEdge = 18.0f; /**< 颜色色块边长(含自定义颜色入口)。 */
|
||||
const float kSwatchGap = 5.0f; /**< 颜色色块间距。 */
|
||||
const float kWidthPresetWidth = 46.0f; /**< 线宽预设按钮宽度。 */
|
||||
const float kWidthSpinButton = 22.0f; /**< 线宽旋钮按钮宽度。 */
|
||||
const float kWidthValueWidth = 62.0f; /**< 线宽数值框宽度。 */
|
||||
const float kOpacityValueWidth = 52.0f; /**< 不透明度数值宽度。 */
|
||||
const float kStyleContentMax = 260.0f; /**< 内容列最大宽度(再拉宽面板也不再延伸)。 */
|
||||
const float kSwatchEdge = 18.0f; /**< 颜色色块边长上限(含自定义颜色入口)。 */
|
||||
const float kSwatchMin = 11.0f; /**< 颜色色块边长下限。 */
|
||||
const float kSwatchGap = 4.0f; /**< 颜色色块最小间距。 */
|
||||
const float kSwatchMaxGap = 10.0f; /**< 颜色色块最大间距(用于铺满内容列)。 */
|
||||
const float kSliderValueWidth = 52.0f; /**< 滑块行数值文本宽度(透明度与线宽一致)。 */
|
||||
const float kSliderValueGap = 8.0f; /**< 滑块轨道与数值文本间距。 */
|
||||
const float kTrackHeight = 6.0f; /**< 滑块轨道高度。 */
|
||||
|
||||
const float kWidthMin = 0.5f; /**< 允许的最小线宽。 */
|
||||
const float kWidthMax = 6.0f; /**< 允许的最大线宽。 */
|
||||
const float kWidthStep = 0.2f; /**< 线宽增减步进。 */
|
||||
const float kWidthThinPreset = 1.0f; /**< 最细线宽预设值。 */
|
||||
const float kWidthThickPreset = 3.0f; /**< 最粗线宽预设值。 */
|
||||
const float kWidthMin = 0.5f; /**< 允许的最小线宽(px)。 */
|
||||
const float kWidthMax = 10.0f; /**< 允许的最大线宽(px)。 */
|
||||
|
||||
/** @brief 可选绘制样式顺序(不含阶梯方式)。 */
|
||||
const DrawMode kModeOptions[] = {DrawMode::LINE, DrawMode::SCATTER, DrawMode::LINE_SCATTER};
|
||||
@@ -154,6 +154,7 @@ namespace jdi
|
||||
m_rowsDirty = true;
|
||||
m_scrollOffset = 0.0f;
|
||||
m_draggingOpacity = false;
|
||||
m_draggingWidth = false;
|
||||
m_widthEditing = false;
|
||||
m_widthEditText.clear();
|
||||
m_pendingLaneMenu = false;
|
||||
@@ -435,102 +436,104 @@ namespace jdi
|
||||
|
||||
void SignalPanel::LayoutStyleControls()
|
||||
{
|
||||
const float padH = kStylePadH * m_scale;
|
||||
const float labelWidth = kStyleLabelWidth * m_scale;
|
||||
const float contentLeft = m_styleRect.X + kStylePadH * m_scale + labelWidth;
|
||||
const float contentWidth = std::max(60.0f, m_styleRect.Width - kStylePadH * 2.0f * m_scale - labelWidth);
|
||||
|
||||
const float labelGap = kStyleLabelGap * m_scale;
|
||||
const float rowHeight = kStyleRowHeight * m_scale;
|
||||
const float row0Top = m_styleRect.Y + kStylePadV * m_scale;
|
||||
const float rowStep = (kStyleRowHeight + kStyleRowGap) * m_scale;
|
||||
const float firstTop = m_styleRect.Y + kStylePadV * m_scale;
|
||||
|
||||
// ---- 第一行:颜色(预设色块 + 同尺寸自定义颜色入口)----
|
||||
// 内容列宽度带上下限:面板变窄时收缩,面板变宽时不再延伸,四行始终共用同一宽度。
|
||||
const float available = std::max(40.0f, m_styleRect.Width - padH * 2.0f - labelWidth - labelGap);
|
||||
const float contentWidth = std::min(available, kStyleContentMax * m_scale);
|
||||
|
||||
// 标签列 + 内容列作为整体在面板内居中,避免拉宽后控件偏向一侧。
|
||||
const float groupWidth = labelWidth + labelGap + contentWidth;
|
||||
const float groupLeft = m_styleRect.X + std::max(padH, (m_styleRect.Width - groupWidth) * 0.5f);
|
||||
const float contentLeft = groupLeft + labelWidth + labelGap;
|
||||
|
||||
m_styleContentRect = gd::RectF(contentLeft, firstTop, contentWidth,
|
||||
kStyleRowCount * rowHeight + (kStyleRowCount - 1.0f) *
|
||||
(kStyleRowGap * m_scale));
|
||||
for (int index = 0; index < static_cast<int>(kStyleRowCount); ++index)
|
||||
{
|
||||
m_styleRowRects[static_cast<std::size_t>(index)] =
|
||||
gd::RectF(groupLeft, firstTop + static_cast<float>(index) * rowStep, groupWidth, rowHeight);
|
||||
}
|
||||
|
||||
const gd::RectF& colorRow = m_styleRowRects[0];
|
||||
const gd::RectF& modeRow = m_styleRowRects[1];
|
||||
const gd::RectF& opacityRow = m_styleRowRects[2];
|
||||
const gd::RectF& widthRow = m_styleRowRects[3];
|
||||
|
||||
// ---- 颜色行:预设色块 + 同尺寸自定义颜色入口(合计恰好铺满内容列宽度)----
|
||||
int paletteCount = 0;
|
||||
SignalPalette(paletteCount);
|
||||
|
||||
float swatchSize = kSwatchEdge * m_scale;
|
||||
const float swatchGap = kSwatchGap * m_scale;
|
||||
const float swatchMaxGap = kSwatchMaxGap * m_scale;
|
||||
const float swatchMin = kSwatchMin * m_scale;
|
||||
int count = std::min(paletteCount, static_cast<int>(m_colorRects.size()));
|
||||
|
||||
// 为自定义颜色入口预留一个同尺寸格位,优先整体缩小色块以保留全部预设颜色。
|
||||
if (count > 0)
|
||||
float swatchSize = kSwatchEdge * m_scale;
|
||||
float gap = swatchGap;
|
||||
while (count >= 1)
|
||||
{
|
||||
const float fit = (contentWidth - swatchGap * static_cast<float>(count)) /
|
||||
static_cast<float>(count + 1);
|
||||
if (fit < swatchSize)
|
||||
const float slots = static_cast<float>(count + 1);
|
||||
const float fit = (contentWidth - swatchGap * (slots - 1.0f)) / slots;
|
||||
if (count == 1 || fit >= swatchMin)
|
||||
{
|
||||
swatchSize = fit;
|
||||
}
|
||||
// 色块小于可用下限时再从末尾裁剪预设,避免尺寸不可用。
|
||||
const float minSwatch = 14.0f * m_scale;
|
||||
while (count > 1 && swatchSize < minSwatch)
|
||||
{
|
||||
--count;
|
||||
swatchSize = (contentWidth - swatchGap * static_cast<float>(count)) /
|
||||
static_cast<float>(count + 1);
|
||||
swatchSize = std::max(swatchMin, std::min(kSwatchEdge * m_scale, fit));
|
||||
// 用实际色块尺寸反算间距,让颜色行与透明度、线宽、样式各行同宽。
|
||||
gap = (contentWidth - swatchSize * slots) / static_cast<float>(count);
|
||||
gap = std::max(swatchGap, std::min(gap, swatchMaxGap));
|
||||
break;
|
||||
}
|
||||
--count;
|
||||
}
|
||||
m_colorCount = std::max(0, count);
|
||||
m_swatchSize = swatchSize / m_scale;
|
||||
m_colorCount = std::max(1, count);
|
||||
|
||||
const float swatchTop = row0Top + (rowHeight - swatchSize) * 0.5f;
|
||||
// 若受色块尺寸上限约束仍有剩余空间,则整行在内容列内居中。
|
||||
const float colorRowWidth = swatchSize * static_cast<float>(m_colorCount + 1) +
|
||||
gap * static_cast<float>(m_colorCount);
|
||||
const float colorRowLeft = contentLeft + std::max(0.0f, (contentWidth - colorRowWidth) * 0.5f);
|
||||
|
||||
const float swatchTop = colorRow.Y + (rowHeight - swatchSize) * 0.5f;
|
||||
for (int index = 0; index < m_colorCount; ++index)
|
||||
{
|
||||
const float x = contentLeft + static_cast<float>(index) * (swatchSize + swatchGap);
|
||||
m_colorRects[static_cast<std::size_t>(index)] = gd::RectF(x, swatchTop, swatchSize, swatchSize);
|
||||
m_colorRects[static_cast<std::size_t>(index)] =
|
||||
gd::RectF(colorRowLeft + static_cast<float>(index) * (swatchSize + gap), swatchTop,
|
||||
swatchSize, swatchSize);
|
||||
}
|
||||
m_customChipRect =
|
||||
gd::RectF(contentLeft + static_cast<float>(m_colorCount) * (swatchSize + swatchGap), swatchTop,
|
||||
gd::RectF(colorRowLeft + static_cast<float>(m_colorCount) * (swatchSize + gap), swatchTop,
|
||||
swatchSize, swatchSize);
|
||||
|
||||
// ---- 第二行:线宽(最细 / 数值旋钮 / 最粗)----
|
||||
// 先按设计尺寸排布,仅在可用宽度不足时整体等比缩小,保证不溢出也不被拉伸变形。
|
||||
const float row1Top = row0Top + (kStyleRowHeight + kStyleRowGap) * m_scale;
|
||||
const float naturalGap = 8.0f * m_scale;
|
||||
const float naturalPreset = kWidthPresetWidth * m_scale;
|
||||
const float naturalSpinner = (kWidthSpinButton * 2.0f + kWidthValueWidth) * m_scale;
|
||||
|
||||
const float naturalTotal = naturalPreset * 2.0f + naturalSpinner + naturalGap * 2.0f;
|
||||
const float widthFactor =
|
||||
(naturalTotal > contentWidth && naturalTotal > 0.0f) ? (contentWidth / naturalTotal) : 1.0f;
|
||||
|
||||
const float presetWidth = naturalPreset * widthFactor;
|
||||
const float spinnerWidth = naturalSpinner * widthFactor;
|
||||
const float rowGap = std::max(2.0f * m_scale, naturalGap * widthFactor);
|
||||
|
||||
const float spinButtonWidth = std::min(kWidthSpinButton * m_scale, spinnerWidth * 0.26f);
|
||||
const float innerValueWidth = std::max(12.0f * m_scale, spinnerWidth - spinButtonWidth * 2.0f);
|
||||
|
||||
const float groupWidth = presetWidth * 2.0f + spinnerWidth + rowGap * 2.0f;
|
||||
const float groupLeft = contentLeft + std::max(0.0f, (contentWidth - groupWidth) * 0.5f);
|
||||
|
||||
m_widthThinRect = gd::RectF(groupLeft, row1Top, presetWidth, rowHeight);
|
||||
m_widthSpinnerRect = gd::RectF(groupLeft + presetWidth + rowGap, row1Top, spinnerWidth, rowHeight);
|
||||
m_widthMinusRect = gd::RectF(m_widthSpinnerRect.X, row1Top, spinButtonWidth, rowHeight);
|
||||
m_widthValueRect =
|
||||
gd::RectF(m_widthMinusRect.X + spinButtonWidth, row1Top, innerValueWidth, rowHeight);
|
||||
m_widthPlusRect =
|
||||
gd::RectF(m_widthValueRect.X + innerValueWidth, row1Top, spinButtonWidth, rowHeight);
|
||||
m_widthThickRect =
|
||||
gd::RectF(m_widthSpinnerRect.X + spinnerWidth + rowGap, row1Top, presetWidth, rowHeight);
|
||||
|
||||
// ---- 第三行:样式(线段 / 散点 / 点线)----
|
||||
const float row2Top = row1Top + (kStyleRowHeight + kStyleRowGap) * m_scale;
|
||||
// ---- 样式行:三个等宽按钮铺满内容列 ----
|
||||
const float modeGap = 6.0f * m_scale;
|
||||
const float modeWidth = (contentWidth - modeGap * static_cast<float>(kModeOptionCount - 1)) /
|
||||
static_cast<float>(kModeOptionCount);
|
||||
const float modeWidth =
|
||||
std::max(20.0f, (contentWidth - modeGap * static_cast<float>(kModeOptionCount - 1)) /
|
||||
static_cast<float>(kModeOptionCount));
|
||||
for (int index = 0; index < kModeOptionCount; ++index)
|
||||
{
|
||||
const float x = contentLeft + static_cast<float>(index) * (modeWidth + modeGap);
|
||||
m_modeRects[static_cast<std::size_t>(index)] = gd::RectF(x, row2Top, modeWidth, rowHeight);
|
||||
m_modeRects[static_cast<std::size_t>(index)] =
|
||||
gd::RectF(contentLeft + static_cast<float>(index) * (modeWidth + modeGap), modeRow.Y,
|
||||
modeWidth, rowHeight);
|
||||
}
|
||||
|
||||
// ---- 第四行:不透明度 ----
|
||||
const float row3Top = row2Top + (kStyleRowHeight + kStyleRowGap) * m_scale;
|
||||
const float opacityValueWidth = kOpacityValueWidth * m_scale;
|
||||
const float trackWidth = std::max(40.0f, contentWidth - opacityValueWidth - 8.0f * m_scale);
|
||||
m_opacityTrackRect = gd::RectF(contentLeft, row3Top + (rowHeight - kTrackHeight * m_scale) * 0.5f,
|
||||
trackWidth, kTrackHeight * m_scale);
|
||||
m_opacityValueRect = gd::RectF(contentLeft + trackWidth + 8.0f * m_scale, row3Top,
|
||||
opacityValueWidth - 8.0f * m_scale, rowHeight);
|
||||
// ---- 透明度行与线宽行:几何完全一致,保证纵向宽度对齐 ----
|
||||
const float valueWidth = std::min(kSliderValueWidth * m_scale, contentWidth * 0.4f);
|
||||
const float trackWidth = std::max(24.0f, contentWidth - valueWidth - kSliderValueGap * m_scale);
|
||||
const float trackHeight = kTrackHeight * m_scale;
|
||||
|
||||
m_opacityTrackRect = gd::RectF(contentLeft, opacityRow.Y + (rowHeight - trackHeight) * 0.5f,
|
||||
trackWidth, trackHeight);
|
||||
m_opacityValueRect = gd::RectF(contentLeft + trackWidth + kSliderValueGap * m_scale, opacityRow.Y,
|
||||
valueWidth, rowHeight);
|
||||
|
||||
m_widthTrackRect =
|
||||
gd::RectF(contentLeft, widthRow.Y + (rowHeight - trackHeight) * 0.5f, trackWidth, trackHeight);
|
||||
m_widthValueRect = gd::RectF(contentLeft + trackWidth + kSliderValueGap * m_scale, widthRow.Y,
|
||||
valueWidth, rowHeight);
|
||||
}
|
||||
|
||||
void SignalPanel::Layout(const gd::RectF& area)
|
||||
@@ -835,27 +838,35 @@ namespace jdi
|
||||
return;
|
||||
}
|
||||
|
||||
const float labelX = m_styleRect.X + kStylePadH * m_scale;
|
||||
const float labelWidth = kStyleLabelWidth * m_scale;
|
||||
const float rowHeight = kStyleRowHeight * m_scale;
|
||||
|
||||
// ---- 第一行:颜色 ----
|
||||
// 行标签统一以整行为基准垂直居中,确保与同行控件纵向对齐。
|
||||
auto paintRowLabel = [&](int rowIndex, const wchar_t* text)
|
||||
{
|
||||
UiText(g, L"颜色", m_fonts->uiSmall, gd::RectF(labelX, m_customChipRect.Y, labelWidth, rowHeight),
|
||||
const gd::RectF& row = m_styleRowRects[static_cast<std::size_t>(rowIndex)];
|
||||
UiText(g, text, m_fonts->uiSmall, gd::RectF(row.X, row.Y, labelWidth, row.Height),
|
||||
m_theme->textMuted, TextAlign::LEFT);
|
||||
};
|
||||
|
||||
// ---- 第一行:颜色(预设色块 + 自定义颜色入口)----
|
||||
{
|
||||
paintRowLabel(0, L"颜色");
|
||||
|
||||
int paletteCount = 0;
|
||||
const std::uint32_t* palette = SignalPalette(paletteCount);
|
||||
bool presetActive = false;
|
||||
|
||||
// 当自定义颜色与某个预设色恰好相同时,优先视为自定义颜色,避免两处同时高亮。
|
||||
const bool customActive = (style->color == style->customColor);
|
||||
for (int index = 0; index < m_colorCount; ++index)
|
||||
{
|
||||
const gd::RectF& rect = m_colorRects[static_cast<std::size_t>(index)];
|
||||
const std::uint32_t color = palette[index % paletteCount];
|
||||
const bool hovered = (m_hoverStyle == StyleHit::COLOR && m_hoverStyleIndex == index);
|
||||
const bool active = (style->color == color);
|
||||
presetActive = presetActive || active;
|
||||
const bool active = !customActive && (style->color == color);
|
||||
|
||||
UiFillRoundRect(g, rect, kCorner * m_scale, color);
|
||||
// 预设色块同样叠加不透明度,直观反映实际显示效果。
|
||||
UiFillRoundRect(g, rect, kCorner * m_scale, ApplyOpacity(color, style->opacity));
|
||||
if (active || hovered)
|
||||
{
|
||||
const gd::RectF ring(rect.X - 2.0f * m_scale, rect.Y - 2.0f * m_scale,
|
||||
@@ -867,125 +878,32 @@ namespace jdi
|
||||
}
|
||||
|
||||
// 自定义颜色入口:与预设色块完全同尺寸、同外框样式的小方格,仅多一个加号图标。
|
||||
// 当前颜色不在预设中时,方格直接填充该颜色,与预设色块的表现保持一致。
|
||||
// 方格始终显示记忆的自定义颜色,因此从任意预设色再次点击它都能立即回到上次的自定义色。
|
||||
const bool chipHot = (m_hoverStyle == StyleHit::CUSTOM_CHIP);
|
||||
|
||||
std::uint32_t chipColor = m_theme->cardBg;
|
||||
std::uint32_t iconColor = chipHot ? m_theme->accent : m_theme->text;
|
||||
if (!presetActive)
|
||||
{
|
||||
chipColor = ApplyOpacity(style->color, style->opacity);
|
||||
iconColor = IsDarkColor(chipColor) ? 0xFFFDF6E3u : 0xFF073642u;
|
||||
}
|
||||
const std::uint32_t chipColor = ApplyOpacity(style->customColor, style->opacity);
|
||||
const std::uint32_t iconColor = IsDarkColor(chipColor) ? 0xFFFDF6E3u : 0xFF073642u;
|
||||
|
||||
UiFillRoundRect(g, m_customChipRect, kCorner * m_scale, chipColor);
|
||||
UiDrawRoundRect(g, m_customChipRect, kCorner * m_scale,
|
||||
chipHot ? m_theme->accent : m_theme->borderStrong, 1.0f);
|
||||
customActive ? m_theme->textStrong : (chipHot ? m_theme->accent : m_theme->borderStrong),
|
||||
customActive ? 1.6f : 1.0f);
|
||||
UiDrawPlusIcon(g, m_customChipRect, iconColor, 1.6f * m_scale);
|
||||
|
||||
// 选中外框与预设色块完全一致(当前正在使用自定义颜色或被悬停时显示)。
|
||||
if (!presetActive || chipHot)
|
||||
// 当前正在使用自定义颜色、或鼠标悬停时,显示与预设色块一致的外框。
|
||||
if (customActive || chipHot)
|
||||
{
|
||||
const gd::RectF& chip = m_customChipRect;
|
||||
const gd::RectF ring(chip.X - 2.0f * m_scale, chip.Y - 2.0f * m_scale,
|
||||
chip.Width + 4.0f * m_scale, chip.Height + 4.0f * m_scale);
|
||||
UiDrawRoundRect(g, ring, 5.0f * m_scale,
|
||||
!presetActive ? m_theme->textStrong : m_theme->borderStrong,
|
||||
!presetActive ? 1.6f : 1.0f);
|
||||
customActive ? m_theme->textStrong : m_theme->borderStrong,
|
||||
customActive ? 1.6f : 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 第二行:线宽 ----
|
||||
// ---- 第二行:样式(线段 / 散点 / 点线)----
|
||||
{
|
||||
UiText(g, L"线宽", m_fonts->uiSmall,
|
||||
gd::RectF(labelX, m_widthThinRect.Y, labelWidth, rowHeight), m_theme->textMuted,
|
||||
TextAlign::LEFT);
|
||||
|
||||
const gd::RectF presetRects[2] = {m_widthThinRect, m_widthThickRect};
|
||||
const float presetWidths[2] = {kWidthThinPreset, kWidthThickPreset};
|
||||
for (int index = 0; index < 2; ++index)
|
||||
{
|
||||
const gd::RectF& rect = presetRects[index];
|
||||
const bool active = std::fabs(style->lineWidth - presetWidths[index]) < 0.01f;
|
||||
const bool hovered =
|
||||
(m_hoverStyle == StyleHit::WIDTH_PRESET && m_hoverStyleIndex == index);
|
||||
|
||||
UiFillRoundRect(g, rect, kCorner * m_scale,
|
||||
active ? m_theme->accentSoft : (hovered ? m_theme->hover : m_theme->cardBg));
|
||||
UiDrawRoundRect(g, rect, kCorner * m_scale, active ? m_theme->accent : m_theme->border, 1.0f);
|
||||
|
||||
const float centerY = rect.Y + rect.Height * 0.5f;
|
||||
UiDrawLine(g, rect.X + 9.0f * m_scale, centerY, rect.X + rect.Width - 9.0f * m_scale,
|
||||
centerY, active ? m_theme->accent : m_theme->text,
|
||||
presetWidths[index] * m_scale);
|
||||
}
|
||||
|
||||
// 数值旋钮(线宽输入框)。
|
||||
const bool valueHot = (m_hoverStyle == StyleHit::WIDTH_VALUE);
|
||||
UiFillRoundRect(g, m_widthSpinnerRect, kCorner * m_scale, m_theme->cardBg);
|
||||
UiDrawRoundRect(g, m_widthSpinnerRect, kCorner * m_scale,
|
||||
m_widthEditing ? m_theme->accent
|
||||
: (valueHot ? m_theme->borderStrong : m_theme->border),
|
||||
m_widthEditing ? 1.6f : 1.0f);
|
||||
|
||||
if (m_widthEditing)
|
||||
{
|
||||
const std::wstring editText =
|
||||
m_widthEditText.empty() ? std::wstring(L" ") : m_widthEditText;
|
||||
// 面板极窄时省略单位,保证数值本身完整可读。
|
||||
const std::wstring fullText = editText + L" px";
|
||||
const std::wstring shown =
|
||||
(UiTextWidth(g, fullText, m_fonts->monoSmall) > m_widthValueRect.Width - 6.0f * m_scale)
|
||||
? editText
|
||||
: fullText;
|
||||
const float textWidth = UiTextWidth(g, shown, m_fonts->monoSmall);
|
||||
const float caretX =
|
||||
m_widthValueRect.X + m_widthValueRect.Width - 6.0f * m_scale - textWidth;
|
||||
UiText(g, shown, m_fonts->monoSmall, m_widthValueRect, m_theme->textStrong,
|
||||
TextAlign::RIGHT);
|
||||
if (caretX > m_widthValueRect.X)
|
||||
{
|
||||
const float caretY = m_widthValueRect.Y + 5.0f * m_scale;
|
||||
UiDrawLine(g, caretX, caretY, caretX, m_widthValueRect.Y + rowHeight - 5.0f * m_scale,
|
||||
m_theme->accent, 1.4f * m_scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::wstring numberText = FormatNumber(style->lineWidth, 1);
|
||||
const std::wstring fullText = numberText + L" px";
|
||||
const std::wstring shown =
|
||||
(UiTextWidth(g, fullText, m_fonts->monoSmall) > m_widthValueRect.Width - 6.0f * m_scale)
|
||||
? numberText
|
||||
: fullText;
|
||||
UiText(g, shown, m_fonts->monoSmall, m_widthValueRect, m_theme->textStrong,
|
||||
TextAlign::CENTER);
|
||||
}
|
||||
|
||||
const bool minusHot = (m_hoverStyle == StyleHit::WIDTH_MINUS);
|
||||
const bool plusHot = (m_hoverStyle == StyleHit::WIDTH_PLUS);
|
||||
const bool minusEnabled = style->lineWidth > (kWidthMin + 1.0e-4f);
|
||||
const bool plusEnabled = style->lineWidth < (kWidthMax - 1.0e-4f);
|
||||
|
||||
if (minusHot && minusEnabled)
|
||||
{
|
||||
UiFillRoundRect(g, m_widthMinusRect, kCorner * m_scale, m_theme->hover);
|
||||
}
|
||||
if (plusHot && plusEnabled)
|
||||
{
|
||||
UiFillRoundRect(g, m_widthPlusRect, kCorner * m_scale, m_theme->hover);
|
||||
}
|
||||
|
||||
UiDrawArrowIcon(g, m_widthMinusRect, minusEnabled ? m_theme->text : m_theme->textMuted,
|
||||
1.6f * m_scale, IconDirection::LEFT);
|
||||
UiDrawArrowIcon(g, m_widthPlusRect, plusEnabled ? m_theme->text : m_theme->textMuted,
|
||||
1.6f * m_scale, IconDirection::RIGHT);
|
||||
}
|
||||
|
||||
// ---- 第三行:样式 ----
|
||||
{
|
||||
UiText(g, L"样式", m_fonts->uiSmall, gd::RectF(labelX, m_modeRects[0].Y, labelWidth, rowHeight),
|
||||
m_theme->textMuted, TextAlign::LEFT);
|
||||
paintRowLabel(1, L"样式");
|
||||
|
||||
for (int index = 0; index < kModeOptionCount; ++index)
|
||||
{
|
||||
@@ -1001,13 +919,11 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 第四行:不透明度 ----
|
||||
// ---- 第三行:透明度 ----
|
||||
{
|
||||
UiText(g, L"透明度", m_fonts->uiSmall,
|
||||
gd::RectF(labelX, m_opacityValueRect.Y, labelWidth, rowHeight), m_theme->textMuted,
|
||||
TextAlign::LEFT);
|
||||
paintRowLabel(2, L"透明度");
|
||||
|
||||
const bool active = m_draggingOpacity || (m_hoverStyle == StyleHit::OPACITY);
|
||||
const bool active = m_draggingOpacity || (m_hoverStyle == StyleHit::OPACITY_SLIDER);
|
||||
DrawSlider(g, m_opacityTrackRect, static_cast<double>(style->opacity), m_theme->accent, active,
|
||||
m_scale);
|
||||
|
||||
@@ -1015,6 +931,47 @@ namespace jdi
|
||||
UiText(g, std::to_wstring(percent) + L"%", m_fonts->monoSmall, m_opacityValueRect,
|
||||
m_theme->textStrong, TextAlign::RIGHT);
|
||||
}
|
||||
|
||||
// ---- 第四行:线宽(与透明度行几何一致)----
|
||||
{
|
||||
paintRowLabel(3, L"线宽");
|
||||
|
||||
const double ratio =
|
||||
(static_cast<double>(style->lineWidth) - kWidthMin) / (kWidthMax - kWidthMin);
|
||||
const bool active = m_draggingWidth || (m_hoverStyle == StyleHit::WIDTH_SLIDER);
|
||||
DrawSlider(g, m_widthTrackRect, ratio, m_theme->accent, active, m_scale);
|
||||
|
||||
if (m_widthEditing)
|
||||
{
|
||||
// 输入状态:右对齐并显示插入符,便于用户看到键入内容。
|
||||
const std::wstring editText = m_widthEditText.empty() ? std::wstring(L" ") : m_widthEditText;
|
||||
UiFillRoundRect(g, m_widthValueRect, kCorner * m_scale, m_theme->cardBg);
|
||||
UiDrawRoundRect(g, m_widthValueRect, kCorner * m_scale, m_theme->accent, 1.6f);
|
||||
|
||||
const float textWidth = UiTextWidth(g, editText, m_fonts->monoSmall);
|
||||
UiText(g, editText, m_fonts->monoSmall, m_widthValueRect, m_theme->textStrong,
|
||||
TextAlign::RIGHT);
|
||||
|
||||
const float caretX = m_widthValueRect.X + m_widthValueRect.Width - 6.0f * m_scale - textWidth;
|
||||
if (caretX > m_widthValueRect.X)
|
||||
{
|
||||
UiDrawLine(g, caretX, m_widthValueRect.Y + 5.0f * m_scale, caretX,
|
||||
m_widthValueRect.Y + rowHeight - 5.0f * m_scale, m_theme->accent,
|
||||
1.4f * m_scale);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::wstring numberText = FormatNumber(style->lineWidth, 1);
|
||||
const std::wstring fullText = numberText + L" px";
|
||||
// 空间不足时省略单位,保证数值本身完整可读。
|
||||
const std::wstring shown =
|
||||
(UiTextWidth(g, fullText, m_fonts->monoSmall) > m_widthValueRect.Width - 6.0f * m_scale)
|
||||
? numberText
|
||||
: fullText;
|
||||
UiText(g, shown, m_fonts->monoSmall, m_widthValueRect, m_theme->textStrong, TextAlign::RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SignalPanel::AreAllSignalsVisible() const
|
||||
@@ -1026,7 +983,7 @@ namespace jdi
|
||||
return m_tab->VisibleCount() == TotalSignalCount();
|
||||
}
|
||||
|
||||
bool SignalPanel::StepLineWidth(float delta)
|
||||
bool SignalPanel::ApplyWidthFromX(float x)
|
||||
{
|
||||
if (m_tab == nullptr)
|
||||
{
|
||||
@@ -1038,8 +995,10 @@ namespace jdi
|
||||
return false;
|
||||
}
|
||||
|
||||
float value = ClampFloat(style->lineWidth + delta, kWidthMin, kWidthMax);
|
||||
const double ratio = UiSliderRatioFromX(m_widthTrackRect, x);
|
||||
float value = static_cast<float>(kWidthMin + ratio * (kWidthMax - kWidthMin));
|
||||
value = std::round(value * 10.0f) / 10.0f;
|
||||
value = ClampFloat(value, kWidthMin, kWidthMax);
|
||||
if (std::fabs(value - style->lineWidth) < 0.001f)
|
||||
{
|
||||
return false;
|
||||
@@ -1191,12 +1150,12 @@ namespace jdi
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (style->color == color)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 同时记住最后一次自定义取值,便于切换到预设色后一键回到该颜色。
|
||||
const bool changed = (style->color != color) || (style->customColor != color);
|
||||
style->color = color;
|
||||
return true;
|
||||
style->customColor = color;
|
||||
return changed;
|
||||
}
|
||||
|
||||
bool SignalPanel::ApplyOpacityFromX(float x)
|
||||
@@ -1228,7 +1187,8 @@ namespace jdi
|
||||
}
|
||||
|
||||
const gd::RectF bounds = (m_clientBounds.Width > 1.0f) ? m_clientBounds : m_area;
|
||||
m_colorPicker->Open(bounds, m_customChipRect, style.color);
|
||||
// 以记忆的自定义颜色作为初始取值,实现“再次打开即恢复上次取色”。
|
||||
m_colorPicker->Open(bounds, m_customChipRect, style.customColor);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1254,6 +1214,10 @@ namespace jdi
|
||||
{
|
||||
return ApplyOpacityFromX(static_cast<float>(x));
|
||||
}
|
||||
if (m_draggingWidth)
|
||||
{
|
||||
return ApplyWidthFromX(static_cast<float>(x));
|
||||
}
|
||||
if (m_draggingScrollBar)
|
||||
{
|
||||
const gd::RectF thumb = ScrollThumbRect();
|
||||
@@ -1296,28 +1260,6 @@ namespace jdi
|
||||
{
|
||||
hoverStyle = StyleHit::CUSTOM_CHIP;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE && UiPointInRect(m_widthThinRect, fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::WIDTH_PRESET;
|
||||
hoverStyleIndex = 0;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE && UiPointInRect(m_widthThickRect, fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::WIDTH_PRESET;
|
||||
hoverStyleIndex = 1;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE && UiPointInRect(m_widthMinusRect, fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::WIDTH_MINUS;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE && UiPointInRect(m_widthPlusRect, fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::WIDTH_PLUS;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE && UiPointInRect(m_widthValueRect, fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::WIDTH_VALUE;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE)
|
||||
{
|
||||
for (int index = 0; index < kModeOptionCount; ++index)
|
||||
@@ -1333,7 +1275,16 @@ namespace jdi
|
||||
if (hoverStyle == StyleHit::NONE &&
|
||||
UiPointInRect(SliderHitRect(m_opacityTrackRect, m_scale), fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::OPACITY;
|
||||
hoverStyle = StyleHit::OPACITY_SLIDER;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE &&
|
||||
UiPointInRect(SliderHitRect(m_widthTrackRect, m_scale), fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::WIDTH_SLIDER;
|
||||
}
|
||||
if (hoverStyle == StyleHit::NONE && UiPointInRect(m_widthValueRect, fx, fy))
|
||||
{
|
||||
hoverStyle = StyleHit::WIDTH_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1416,33 +1367,11 @@ namespace jdi
|
||||
}
|
||||
if (UiPointInRect(m_customChipRect, fx, fy))
|
||||
{
|
||||
// 在左键抬起时展开,避免按下即被后续消息立即收起。
|
||||
// 先恢复到记忆的自定义颜色(即便随后收起浮窗也会生效),再在抬起时展开浮窗。
|
||||
style->color = style->customColor;
|
||||
m_pendingColorPicker = true;
|
||||
return true;
|
||||
}
|
||||
if (UiPointInRect(m_widthThinRect, fx, fy))
|
||||
{
|
||||
style->lineWidth = kWidthThinPreset;
|
||||
return true;
|
||||
}
|
||||
if (UiPointInRect(m_widthThickRect, fx, fy))
|
||||
{
|
||||
style->lineWidth = kWidthThickPreset;
|
||||
return true;
|
||||
}
|
||||
if (UiPointInRect(m_widthMinusRect, fx, fy))
|
||||
{
|
||||
return StepLineWidth(-kWidthStep);
|
||||
}
|
||||
if (UiPointInRect(m_widthPlusRect, fx, fy))
|
||||
{
|
||||
return StepLineWidth(kWidthStep);
|
||||
}
|
||||
if (UiPointInRect(m_widthValueRect, fx, fy))
|
||||
{
|
||||
// 单击即进入输入状态,双击亦等效。
|
||||
return BeginWidthEdit();
|
||||
}
|
||||
for (int index = 0; index < kModeOptionCount; ++index)
|
||||
{
|
||||
if (UiPointInRect(m_modeRects[static_cast<std::size_t>(index)], fx, fy))
|
||||
@@ -1454,7 +1383,19 @@ namespace jdi
|
||||
if (UiPointInRect(SliderHitRect(m_opacityTrackRect, m_scale), fx, fy))
|
||||
{
|
||||
m_draggingOpacity = true;
|
||||
return ApplyOpacityFromX(fx) || true;
|
||||
ApplyOpacityFromX(fx);
|
||||
return true;
|
||||
}
|
||||
if (UiPointInRect(SliderHitRect(m_widthTrackRect, m_scale), fx, fy))
|
||||
{
|
||||
m_draggingWidth = true;
|
||||
ApplyWidthFromX(fx);
|
||||
return true;
|
||||
}
|
||||
if (UiPointInRect(m_widthValueRect, fx, fy))
|
||||
{
|
||||
// 单击即进入输入状态,双击亦等效。
|
||||
return BeginWidthEdit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1518,6 +1459,11 @@ namespace jdi
|
||||
m_draggingOpacity = false;
|
||||
changed = true;
|
||||
}
|
||||
if (m_draggingWidth)
|
||||
{
|
||||
m_draggingWidth = false;
|
||||
changed = true;
|
||||
}
|
||||
if (m_draggingScrollBar)
|
||||
{
|
||||
m_draggingScrollBar = false;
|
||||
|
||||
@@ -113,6 +113,14 @@ namespace jdi
|
||||
*/
|
||||
bool ContainsPoint(int x, int y) const;
|
||||
|
||||
/**
|
||||
* @brief 查询该坐标应使用的系统光标。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
* @param [IN] y 客户区纵坐标。
|
||||
* @return 系统光标句柄;面板未打开时返回空指针。
|
||||
*/
|
||||
HCURSOR QueryCursor(int x, int y) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 依据锚点矩形与客户区计算面板矩形与内部控件矩形。
|
||||
|
||||
@@ -195,6 +195,7 @@ namespace jdi
|
||||
{
|
||||
bool visible = false; /**< 是否参与绘制。默认关闭,由用户自行勾选。 */
|
||||
std::uint32_t color = 0xFF268BD2; /**< 波形颜色,格式 0xAARRGGBB。 */
|
||||
std::uint32_t customColor = 0xFF6C71C4; /**< 自定义颜色记忆值(切换预设色后仍保留)。 */
|
||||
float opacity = 1.0f; /**< 不透明度,取值 0.0-1.0,默认 1.0。 */
|
||||
float lineWidth = 1.6f; /**< 线宽(逻辑像素,未乘 DPI 缩放)。 */
|
||||
DrawMode mode = DrawMode::LINE; /**< 绘制方式。 */
|
||||
|
||||
@@ -193,6 +193,21 @@ namespace jdi
|
||||
return UiPointInRect(m_rect, static_cast<float>(x), static_cast<float>(y));
|
||||
}
|
||||
|
||||
HCURSOR ColorPickerPopup::QueryCursor(int x, int y) const
|
||||
{
|
||||
if (!m_open)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 拖拽过程中始终保持横向调整光标,避免指针移出轨道后光标跳变。
|
||||
if (m_draggingSlider >= 0 || SliderAtPoint(x, y) >= 0)
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_SIZEWE);
|
||||
}
|
||||
return LoadCursorW(nullptr, IDC_ARROW);
|
||||
}
|
||||
|
||||
int ColorPickerPopup::SliderAtPoint(int x, int y) const
|
||||
{
|
||||
if (!m_open)
|
||||
|
||||
Reference in new issue
Block a user