From 82884f65b78cda8b176d3f9fe0974ced29c0d526 Mon Sep 17 00:00:00 2001 From: zqq-dora Date: Wed, 30 Sep 2026 16:08:41 +0800 Subject: [PATCH] feat(MenuItem): add 1px inset shadow bevel on hover highlight Add a subtle inner-shadow bevel to MenuItem hover/highlight state, matching the ToolButton checked-state approach using BoxInsetShadow. FlowStyle.qml: - Add itemHighlightInnerShadow palette (bottom): 20% black (light) / 24% black (dark) - Add itemHighlightInnerShadowTop palette (top): transparent (light) / 10% white (dark) HighlightPanel.qml: - Extend with bevelShadowColor1/2 (D.Palette, default null) for top + bottom bevel inner shadows, inactive by default so existing callers are unaffected - Resolve bevel colors via panel.D.ColorSelector.bevelShadowColor1/2, following the same pattern as innerShadowColor - Restore the legacy innerShadowColor Loader (managed by Loader for performance) MenuItem.qml: - Pass DS.Style.menu.itemHighlightInnerShadowTop/InnerShadow directly to HighlightPanel's bevelShadowColor1/2; colors resolve via HighlightPanel's ColorSelector and follow theme changes automatically Menu.md: - Document MenuItem hover-highlight rendering README.md: - Add Menu.md to the docs index, covering bottom inner shadow and dark-theme top highlight Light mode: only bottom 1px inner shadow (20% black). Dark mode: top 1px white 10% highlight + bottom 1px black 24% inner shadow. Color values use DTK palettes so they follow theme changes automatically. --- qt6/src/docs/Menu.md | 66 ++++++++++++++++++++++++++++++++++ qt6/src/docs/README.md | 1 + qt6/src/qml/FlowStyle.qml | 10 ++++++ qt6/src/qml/HighlightPanel.qml | 55 ++++++++++++++++++++++------ qt6/src/qml/MenuItem.qml | 2 ++ 5 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 qt6/src/docs/Menu.md diff --git a/qt6/src/docs/Menu.md b/qt6/src/docs/Menu.md new file mode 100644 index 000000000..21db19c4c --- /dev/null +++ b/qt6/src/docs/Menu.md @@ -0,0 +1,66 @@ +# Menu 菜单项悬浮高亮渲染说明 + +[公共规范与控件索引](README.md) + +## 适用范围与依据 + +本文档说明 v25 flow style 下 `MenuItem` 的悬浮(高亮)背景渲染,即菜单项被指针悬停或其子菜单展开时的背景效果。设计依据为本分支已验证实现:在既有高亮填充之上叠加 1px 内阴影,做法参照 [ToolButton](../qml/ToolButton.qml) 的 checked 状态内阴影(同样的 `BoxInsetShadow` 参数)。菜单容器面板、分隔符、页眉页脚等其它部分不在本文档范围。 + +普通模式、亮色主题与暗色主题下的悬浮内阴影均已定稿。 + +## 外形与内容布局 + +| 项目 | 默认值 | +|---|---| +| 菜单项背景圆角 | 6 个逻辑像素 | +| 菜单项隐式尺寸 | 180 × 26(背景建议尺寸,非控件固定宽高)| +| 菜单项内边距 | 3 | +| 内容左留白 | 取内容留白 30 与指示器/箭头占位中的较大值 | +| 图标尺寸 | 14 × 14 | +| 指示器边距 | 10 | + +文字与图标由调用方提供;存在已选中项时为指示器预留空间,子菜单项为箭头预留空间。 + +## 渲染元素与显示条件 + +悬浮背景由三层叠加:底层 `HighlightPanel` 圆角填充,之上两道 1px 内阴影——顶部高光与底部阴影,均用 `BoxInsetShadow` 实现——共同形成内斜面,模仿 Button checked 状态的凸起边缘效果。三层均在菜单项处于高亮状态(指针悬停,或其子菜单可见)时显示,其余情况不显示。 + +| 窗口模式与主题 | 高亮填充 | 顶部内阴影 | 底部内阴影 | +|---|---|---|---| +| 普通模式、亮色 | 显示系统高亮色填充 | 不显示(透明) | 显示 1px 底部内阴影,黑色 20% 不透明度 | +| 普通模式、暗色 | 显示系统高亮色填充 | 显示 1px 顶部内阴影,白色 10% 不透明度 | 显示 1px 底部内阴影,黑色 24% 不透明度 | +| 水晶模式、亮色 | 显示系统高亮色填充 | 不显示(透明) | 显示 1px 底部内阴影,黑色 20% 不透明度 | +| 水晶模式、暗色 | 显示系统高亮色填充 | 显示 1px 顶部内阴影,白色 10% 不透明度 | 显示 1px 底部内阴影,黑色 24% 不透明度 | + +> 内阴影颜色取自 `menu.itemHighlightInnerShadow`(底部)与 `menu.itemHighlightInnerShadowTop`(顶部),调色板仅定义普通状态;悬浮时按公共回退规则取普通状态值。浅色模式顶部内阴影设为透明,故不可见。暗色底部取值(24%)略高于浅色(20%),因暗色主题高亮填充更亮,需略强对比以保持底部内阴影可读。高亮填充使用 `HighlightPanel` 默认背景(`highlightPanel.background`,即系统高亮色),由既有实现提供,本文档不改变其取色。 + +## 色值与几何参数 + +两道内阴影均用 `BoxInsetShadow` 实现,`shadowBlur` 设为 1 以保证圆角处自然过渡。取与高亮面板相同的尺寸与圆角(6)。顶部 `shadowOffsetY: 1`,底部 `shadowOffsetY: -1`。顶部为白色 10% 不透明度(仅暗色),底部为黑色 20%(亮色)或 24%(暗色)不透明度。两道阴影共同形成内斜面,模仿 Button checked 状态的凸起边缘。 + +| 窗口模式与主题 | 顶部内阴影颜色 | 底部内阴影颜色 | +|---|---|---| +| 普通模式、亮色 | `transparent` | `rgba(0,0,0,0.2)` | +| 普通模式、暗色 | `rgba(255,255,255,0.1)` | `rgba(0,0,0,0.24)` | +| 水晶模式、亮色 | `transparent` | `rgba(0,0,0,0.2)` | +| 水晶模式、暗色 | `rgba(255,255,255,0.1)` | `rgba(0,0,0,0.24)` | + +色值定义以 [FlowStyle.qml](../qml/FlowStyle.qml) 的 `menu.itemHighlightInnerShadow`(底部)与 `menu.itemHighlightInnerShadowTop`(顶部)为准。 + +## 实现对应关系 + +| 内容 | 实现来源 | +|---|---| +| 菜单项背景装配、悬浮显示条件 | [MenuItem.qml](../qml/MenuItem.qml) | +| 高亮填充面板 | [HighlightPanel.qml](../qml/HighlightPanel.qml) | +| 顶部与底部内阴影渲染 | `BoxInsetShadow`(`shadowBlur: 1`),经 [HighlightPanel.qml](../qml/HighlightPanel.qml) 的 `bevelShadowColor1/2` 触发 | +| 内阴影颜色、菜单项几何与圆角 | [FlowStyle.qml](../qml/FlowStyle.qml) 的 `menu.itemHighlightInnerShadow` 与 `menu.item` | +| 设计参照(内斜面效果)| [ToolButton.qml](../qml/ToolButton.qml) 的 checked 状态 | + +## 复现与验收 + +- 普通模式、亮色:菜单项悬浮时,在系统高亮色填充上出现底部 1px 黑色 20% 内阴影,跟随圆角过渡;顶部无内阴影。 +- 暗色主题:顶部 1px 白色 10% 高光 + 底部 1px 黑色 24% 内阴影,共同形成内斜面效果。 +- 菜单容器面板、分隔符、页眉页脚等不在本次范围。 + +验证范围:已核对普通模式亮色与暗色悬浮内阴影的颜色、几何参数与显示条件,通过 exhibition 示例程序做视觉验收。 diff --git a/qt6/src/docs/README.md b/qt6/src/docs/README.md index 6e41d32ea..4a785f478 100644 --- a/qt6/src/docs/README.md +++ b/qt6/src/docs/README.md @@ -14,6 +14,7 @@ |---|---| | 本主文档 | 统一目的、文档结构、状态维度、效果术语、颜色规则、维护和验收要求 | | [Button.md](Button.md) | 普通 Button 的外形、内容、各渲染元素的显示条件、颜色和参数;不覆盖选中、高亮变体 | +| [Menu.md](Menu.md) | MenuItem 悬浮高亮背景的填充与 1px 底部内阴影、暗色主题 1px 顶部高光;不含菜单容器面板、分隔符等 | | [QML 维护约束](../qml/.AGENTS) | 简短约束代码与文档同步,引用本规范,不重复详细规则 | 每个控件使用同名子文档,例如 Button.qml 对应 Button.md。子文档按以下顺序组织;不适用项明确说明,无需创建空章节。 diff --git a/qt6/src/qml/FlowStyle.qml b/qt6/src/qml/FlowStyle.qml index e8ed922c5..8c8f6fc7c 100644 --- a/qt6/src/qml/FlowStyle.qml +++ b/qt6/src/qml/FlowStyle.qml @@ -910,6 +910,16 @@ QtObject { normal: Qt.rgba(0, 0, 0, 0.2) normalDark: Qt.rgba(1, 1, 1, 0.1) } + property D.Palette itemHighlightInnerShadow: D.Palette { + // Light: 20% black; Dark: 24% black. + normal: Qt.rgba(0, 0, 0, 0.2) + normalDark: Qt.rgba(0, 0, 0, 0.24) + } + property D.Palette itemHighlightInnerShadowTop: D.Palette { + // Light: no top highlight; Dark: 1px white 10% top inset highlight. + normal: ("transparent") + normalDark: Qt.rgba(1, 1, 1, 0.1) + } property D.Palette itemText: D.Palette { normal: ("black") diff --git a/qt6/src/qml/HighlightPanel.qml b/qt6/src/qml/HighlightPanel.qml index b5f76e187..b042afec5 100644 --- a/qt6/src/qml/HighlightPanel.qml +++ b/qt6/src/qml/HighlightPanel.qml @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. +// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd. // // SPDX-License-Identifier: LGPL-3.0-or-later @@ -12,6 +12,11 @@ Item { property D.Palette backgroundColor: DS.Style.highlightPanel.background property D.Palette outerShadowColor: DS.Style.highlightPanel.dropShadow property D.Palette innerShadowColor: DS.Style.highlightPanel.innerShadow + // Dual-direction inner shadow palettes for consumers that need a + // top + bottom bevel (e.g. MenuItem hover). Default to null so the + // bevel Loaders stay inactive and existing callers are unaffected. + property D.Palette bevelShadowColor1: null + property D.Palette bevelShadowColor2: null property int radius: DS.Style.highlightPanel.radius implicitWidth: DS.Style.highlightPanel.width @@ -33,13 +38,43 @@ Item { radius: panel.radius } - // BoxInsetShadow { - // anchors.fill: backgroundRect - // visible: panel.innerShadowColor - // shadowColor: panel.D.ColorSelector.innerShadowColor - // shadowOffsetY: -1 - // shadowBlur: 2 - // spread: 1 - // cornerRadius: backgroundRect.radius - // } + // Bottom inner shadow (legacy single-direction API). + Loader { + anchors.fill: backgroundRect + active: panel.innerShadowColor + + sourceComponent: BoxInsetShadow { + shadowColor: panel.D.ColorSelector.innerShadowColor + shadowOffsetY: -1 + shadowBlur: 2 + spread: 1 + cornerRadius: backgroundRect.radius + } + } + + // Top inner shadow (bevel highlight). + Loader { + anchors.fill: backgroundRect + active: panel.bevelShadowColor1 + + sourceComponent: BoxInsetShadow { + shadowColor: panel.D.ColorSelector.bevelShadowColor1 + shadowOffsetY: 1 + shadowBlur: 1 + cornerRadius: backgroundRect.radius + } + } + + // Bottom inner shadow (bevel shadow). + Loader { + anchors.fill: backgroundRect + active: panel.bevelShadowColor2 + + sourceComponent: BoxInsetShadow { + shadowColor: panel.D.ColorSelector.bevelShadowColor2 + shadowOffsetY: -1 + shadowBlur: 1 + cornerRadius: backgroundRect.radius + } + } } diff --git a/qt6/src/qml/MenuItem.qml b/qt6/src/qml/MenuItem.qml index 68f4df05a..52077609a 100644 --- a/qt6/src/qml/MenuItem.qml +++ b/qt6/src/qml/MenuItem.qml @@ -108,6 +108,8 @@ T.MenuItem { sourceComponent: HighlightPanel { outerShadowColor: null innerShadowColor: null + bevelShadowColor1: DS.Style.menu.itemHighlightInnerShadowTop + bevelShadowColor2: DS.Style.menu.itemHighlightInnerShadow radius: DS.Style.menu.item.radius } }