菜单栏新增双行居中风格:标签比数值宽时数值不再偏到右边
This commit is contained in:
1 parent
2efebb5008
commit
d05b02be4f
8 files changed
+78
-50
No files matched your search
@@ -3,6 +3,14 @@
|
||||
记录 OpenStats 应用的功能新增、样式调整和问题修复,按版本与日期倒序排列。
|
||||
官网、构建与发布脚本等不影响应用本身的改动不在此记录。
|
||||
|
||||
## 未发布
|
||||
|
||||
### 2026-09-21
|
||||
|
||||
#### 新增
|
||||
|
||||
- 菜单栏多一种“双行居中”风格:两行各自居中。原来的“双行文字”是两行一起靠右对齐,标签比数值宽的时候(风扇 0、温度 54°),数值会被推到右边看着偏。居中之后数值正落在标签下面。设置 · 菜单栏里选整体风格,也可以只给某一项单独用。
|
||||
|
||||
## 0.6.2 · 2026-09-21
|
||||
|
||||
### 2026-09-21
|
||||
|
||||
@@ -334,6 +334,7 @@ npm 缓存 npm Cache
|
||||
去批准 Approve
|
||||
去授权 Grant Access
|
||||
双行圆点 Dots
|
||||
双行居中 Stacked, Centered
|
||||
双行文字 Stacked Text
|
||||
双行箭头 Arrows
|
||||
反馈问题 Report a Problem
|
||||
@@ -382,6 +383,7 @@ npm 缓存 npm Cache
|
||||
合盖运行时散热变差,请勿放入包中。使用电池且电量低于 {}% 时会自动关闭。 Cooling is worse with the lid closed, so don't put your Mac in a bag. Turns off automatically on battery below {}%.
|
||||
合盖运行设置 Lid Mode
|
||||
合盖运行需要修改系统睡眠设置,需安装辅助工具(管理员授权一次)。 Lid mode changes system sleep settings and needs the helper (one-time admin approval).
|
||||
同双行文字,但两行各自居中;标签比数值宽时(风扇、温度)不会偏到一边 Same as stacked, but both lines are centered, so a value narrower than its label (fan, temperature) is not pushed to one side
|
||||
同步 Sync
|
||||
同步失败 Sync failed
|
||||
同步的内容:菜单栏项目与风格、刷新频率、外观与语言、详情弹窗的区块、连接探测、通知、快捷键、风扇安全温度、合盖电量下限。当前页面、辅助工具与历史记录留在本机。 What syncs: menu bar items and styles, refresh rate, appearance and language, popover sections, connection probe, notifications, hotkeys, fan safety temperature and lid-mode battery floor. The current page, the helper and history stay on this Mac.
|
||||
@@ -450,7 +452,7 @@ npm 缓存 npm Cache
|
||||
寿命按厂商估算的已用比例计算;写入量越大消耗越快,日常使用通常可用很多年 Life is based on the vendor's wear estimate. Heavy writes wear it faster; typical use lasts many years.
|
||||
将清理 {} 个项目,共 {}。{} {} items, {} total, will be cleaned. {}
|
||||
将移到废纸篓 Will move to Trash
|
||||
小标签在上、数值在下,最紧凑 Small label above the value; most compact
|
||||
小标签在上、数值在下,两行靠右对齐,数值的右边不会跳 Small label on top, value below, both right-aligned so the value's right edge stays put
|
||||
尚未检查 Not checked yet
|
||||
屏幕保持常亮 Keep display on
|
||||
屏幕可按设置关闭,下载、编译等后台任务继续运行 The display can turn off as usual while downloads and builds keep running
|
||||
|
||||
@@ -243,10 +243,11 @@ enum MenuBarRenderer {
|
||||
let text = Format.percent(fraction)
|
||||
let alert = colorizeHighLoad && fraction <= Metrics.lowBatteryLevel
|
||||
let label = MenuBarItem.battery.menuBarLabel
|
||||
let stacked = stackedText(label: label, value: text, sample: "100%", alert: alert)
|
||||
let stacked = stackedText(label: label, value: text, sample: "100%", alert: alert,
|
||||
centered: style == .stackedCenter)
|
||||
var segment: Segment
|
||||
switch style {
|
||||
case .stacked: segment = stacked
|
||||
case .stacked, .stackedCenter: segment = stacked
|
||||
case .inline: segment = inlineText(label: label, value: text, sample: "100%", alert: alert)
|
||||
case .icon:
|
||||
let symbol = reading.batteryCharging ? "battery.100.bolt" : batterySymbol(fraction)
|
||||
@@ -283,10 +284,11 @@ enum MenuBarRenderer {
|
||||
let fraction = min(1, max(0, value ?? 0))
|
||||
let text = value.map { Format.percent($0) } ?? "—"
|
||||
let alert = colorizeHighLoad && fraction >= Metrics.highLevel
|
||||
let stacked = stackedText(label: item.menuBarLabel, value: text, sample: "100%", alert: alert)
|
||||
let stacked = stackedText(label: item.menuBarLabel, value: text, sample: "100%", alert: alert,
|
||||
centered: style == .stackedCenter)
|
||||
|
||||
switch style {
|
||||
case .stacked:
|
||||
case .stacked, .stackedCenter:
|
||||
return stacked
|
||||
case .inline:
|
||||
return inlineText(label: item.menuBarLabel, value: text, sample: "100%", alert: alert)
|
||||
@@ -312,7 +314,8 @@ enum MenuBarRenderer {
|
||||
switch style {
|
||||
case .inline: inlineText(label: item.menuBarLabel, value: value, sample: sample, alert: false)
|
||||
case .icon: combine([symbolSegment(item.symbol), inlineValue(value, sample: sample, alert: false)])
|
||||
default: stackedText(label: item.menuBarLabel, value: value, sample: sample, alert: false)
|
||||
default: stackedText(label: item.menuBarLabel, value: value, sample: sample, alert: false,
|
||||
centered: style == .stackedCenter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,14 +326,24 @@ enum MenuBarRenderer {
|
||||
}
|
||||
|
||||
/// 颜色保持动态(labelColor / secondaryLabelColor),在绘制时才按菜单栏外观解析
|
||||
private static func stackedText(label: String, value: String, sample: String, alert: Bool) -> Segment {
|
||||
/// 两行文字:宽度按标签、数值与样本里最宽的算,所以数值变长也不会挤动旁边的项目。
|
||||
/// `centered` 时两行各自居中,标签比数值宽时(风扇、温度)数值不会贴在右边
|
||||
private static func stackedText(label: String, value: String, sample: String, alert: Bool,
|
||||
centered: Bool = false) -> Segment {
|
||||
let labelAttributes: [NSAttributedString.Key: Any] = [.font: Metrics.labelFont, .foregroundColor: NSColor.secondaryLabelColor]
|
||||
let valueAttributes: [NSAttributedString.Key: Any] = [.font: Metrics.stackedValueFont, .foregroundColor: foreground(alert)]
|
||||
let width = ceil(max(textWidth(label, labelAttributes), textWidth(value, valueAttributes), textWidth(sample, valueAttributes)))
|
||||
return Segment(width: width) { rect in
|
||||
let base = baselineOrigin(in: rect)
|
||||
drawText(label, labelAttributes, rightEdge: rect.maxX, baseline: base + Metrics.upperBaseline)
|
||||
drawText(value, valueAttributes, rightEdge: rect.maxX, baseline: base + Metrics.lowerBaseline)
|
||||
let rows = [(label, labelAttributes, base + Metrics.upperBaseline),
|
||||
(value, valueAttributes, base + Metrics.lowerBaseline)]
|
||||
for (text, attributes, baseline) in rows {
|
||||
if centered {
|
||||
drawText(text, attributes, centerX: rect.midX, baseline: baseline)
|
||||
} else {
|
||||
drawText(text, attributes, rightEdge: rect.maxX, baseline: baseline)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,6 +376,13 @@ enum MenuBarRenderer {
|
||||
string.draw(with: NSRect(x: rightEdge - width, y: baseline, width: width, height: 0))
|
||||
}
|
||||
|
||||
/// 居中绘制:起点取整像素,文字才不会发虚
|
||||
private static func drawText(_ text: String, _ attributes: [NSAttributedString.Key: Any], centerX: CGFloat, baseline: CGFloat) {
|
||||
let string = NSAttributedString(string: text, attributes: attributes)
|
||||
let width = string.size().width
|
||||
string.draw(with: NSRect(x: (centerX - width / 2).rounded(), y: baseline, width: width, height: 0))
|
||||
}
|
||||
|
||||
/// 刘海屏的菜单栏更高,两行内容整体在按钮内垂直居中
|
||||
private static func baselineOrigin(in rect: NSRect) -> CGFloat {
|
||||
rect.midY - Metrics.barHeight / 2
|
||||
|
||||
@@ -472,8 +472,20 @@ private struct StyleCard: View {
|
||||
let action: () -> Void
|
||||
@State private var hovering = false
|
||||
|
||||
/// 两种双行风格的差别只在标签比数值宽时看得出来,预览里用风扇(标签宽、数值短)代替 GPU
|
||||
private var isStacked: Bool { style == .stacked || style == .stackedCenter }
|
||||
|
||||
private var previewItems: [MenuBarItem] { isStacked ? [.cpu, .memory, .fan] : [.cpu, .memory, .gpu] }
|
||||
|
||||
private var previewReading: MenuBarReading {
|
||||
guard isStacked else { return .sample }
|
||||
var reading = MenuBarReading.sample
|
||||
reading.fanRPM = 0
|
||||
return reading
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
let sample = MenuBarRenderer.image(reading: .sample, items: [.cpu, .memory, .gpu],
|
||||
let sample = MenuBarRenderer.image(reading: previewReading, items: previewItems,
|
||||
style: { _ in style }, networkStyle: model.settings.networkStyle,
|
||||
colorizeHighLoad: false, fahrenheit: model.settings.useFahrenheit)
|
||||
Button(action: action) {
|
||||
|
||||
@@ -184,14 +184,14 @@ public enum ProbeTarget: String, CaseIterable, Identifiable, Sendable {
|
||||
|
||||
/// 菜单栏风格:整体选一种套用到所有指标,个别指标可以单独指定(AppSettings.styleOverrides);风格内部的排版保持一致
|
||||
public enum MenuBarStyle: String, CaseIterable, Identifiable, Sendable {
|
||||
case stacked, inline, icon, ring, pie, history, line, meter, dot
|
||||
case stacked, stackedCenter, inline, icon, ring, pie, history, line, meter, dot
|
||||
|
||||
public var id: String { rawValue }
|
||||
|
||||
/// 温度、风扇没有百分比,图形类风格画不出来,只给文字类的三种
|
||||
static func options(for item: MenuBarItem) -> [MenuBarStyle] {
|
||||
switch item {
|
||||
case .temperature, .fan: [.stacked, .inline, .icon]
|
||||
case .temperature, .fan: [.stacked, .stackedCenter, .inline, .icon]
|
||||
default: allCases
|
||||
}
|
||||
}
|
||||
@@ -199,6 +199,7 @@ public enum MenuBarStyle: String, CaseIterable, Identifiable, Sendable {
|
||||
var title: String {
|
||||
switch self {
|
||||
case .stacked: tr("双行文字")
|
||||
case .stackedCenter: tr("双行居中")
|
||||
case .inline: tr("单行文字")
|
||||
case .icon: tr("图标")
|
||||
case .ring: tr("圆环")
|
||||
@@ -212,7 +213,8 @@ public enum MenuBarStyle: String, CaseIterable, Identifiable, Sendable {
|
||||
|
||||
var detail: String {
|
||||
switch self {
|
||||
case .stacked: tr("小标签在上、数值在下,最紧凑")
|
||||
case .stacked: tr("小标签在上、数值在下,两行靠右对齐,数值的右边不会跳")
|
||||
case .stackedCenter: tr("同双行文字,但两行各自居中;标签比数值宽时(风扇、温度)不会偏到一边")
|
||||
case .inline: tr("标签与数值同一行,最易读")
|
||||
case .icon: tr("用系统图标代替文字标签")
|
||||
case .ring: tr("圆环表示当前占用比例")
|
||||
|
||||
@@ -62,9 +62,18 @@ system language by default; switch it in Settings → General → Language.
|
||||
<!-- changelog:start -->
|
||||
<!-- Generated from CHANGELOG.md by Scripts/sync_changelog.py. Do not edit by hand. -->
|
||||
|
||||
Latest release **0.6.2** (2026-09-21) · [full changelog](CHANGELOG.md) (kept in Chinese)
|
||||
Latest release **0.6.2** (2026-09-21) · **1** changes in development · [full changelog](CHANGELOG.md) (kept in Chinese)
|
||||
|
||||
<details open>
|
||||
<summary><b>2026-09-21</b> · Unreleased · 1 added</summary>
|
||||
|
||||
**Added**
|
||||
|
||||
- 菜单栏多一种“双行居中”风格:两行各自居中。原来的“双行文字”是两行一起靠右对齐,标签比数值宽的时候(风扇 0、温度 54°),数值会被推到右边看着偏。居中之后数值正落在标签下面。设置 · 菜单栏里选整体风格,也可以只给某一项单独用。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>2026-09-21</b> · 0.6.2 · 2 fixed</summary>
|
||||
|
||||
**Fixed**
|
||||
@@ -83,23 +92,6 @@ Latest release **0.6.2** (2026-09-21) · [full changelog](CHANGELOG.md) (kept in
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>2026-09-18</b> · 0.6.0 · 5 added · 1 style</summary>
|
||||
|
||||
**Added**
|
||||
|
||||
- 新增出口与分流窗口:网络页顶栏和菜单栏网络弹窗各有一个入口。一次检测约十秒,看清 VPN 与代理有没有生效、各个网站分别从哪个出口出去。
|
||||
- 结论只用一句话说清:未使用代理、代理出口(地区)、代理没有生效、网络不通四种情况。右侧同时给出经代理、直连、连不上的网站数量。
|
||||
- 本机网络:物理网卡的出口 IP、归属地、运营商与 IPv6。归属地数据来自 cleanip.io。
|
||||
- 网站分流:25 个常用网站分别从哪个出口出去。按 AI、影音、社交、开发、国内分类筛选;接入 Cloudflare 的网站能实测出口与边缘节点,其余按国内 / 国际规则推断,每个网站带延迟信号条。
|
||||
- 代理方式一览:VPN / 隧道、系统代理、自动代理配置、DNS 与检测到的代理软件。DNS 被代理接管时会标出来。
|
||||
|
||||
**Style**
|
||||
|
||||
- 菜单栏网络弹窗的 DNS 区块重排:网络服务与线路做成两个徽章(Wi-Fi / 有线、直连或经过哪个代理),走隧道时标题旁有叹号提示 DNS 可能被接管,刷新 DNS 缓存改为图标按钮。
|
||||
|
||||
</details>
|
||||
|
||||
<!-- changelog:end -->
|
||||
|
||||
## Activity
|
||||
|
||||
+10
-18
@@ -57,9 +57,18 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。
|
||||
<!-- changelog:start -->
|
||||
<!-- 由 Scripts/sync_changelog.py 从 CHANGELOG.md 生成,请勿手改。 -->
|
||||
|
||||
最新版本 **0.6.2**(2026-09-21) · [完整更新日志](CHANGELOG.md)
|
||||
最新版本 **0.6.2**(2026-09-21) · 开发中 **1** 项改动尚未发布 · [完整更新日志](CHANGELOG.md)
|
||||
|
||||
<details open>
|
||||
<summary><b>2026-09-21</b> · 未发布 · 新增 1</summary>
|
||||
|
||||
**新增**
|
||||
|
||||
- 菜单栏多一种“双行居中”风格:两行各自居中。原来的“双行文字”是两行一起靠右对齐,标签比数值宽的时候(风扇 0、温度 54°),数值会被推到右边看着偏。居中之后数值正落在标签下面。设置 · 菜单栏里选整体风格,也可以只给某一项单独用。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>2026-09-21</b> · 0.6.2 · 修复 2</summary>
|
||||
|
||||
**修复**
|
||||
@@ -78,23 +87,6 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>2026-09-18</b> · 0.6.0 · 新增 5 · 样式 1</summary>
|
||||
|
||||
**新增**
|
||||
|
||||
- 新增出口与分流窗口:网络页顶栏和菜单栏网络弹窗各有一个入口。一次检测约十秒,看清 VPN 与代理有没有生效、各个网站分别从哪个出口出去。
|
||||
- 结论只用一句话说清:未使用代理、代理出口(地区)、代理没有生效、网络不通四种情况。右侧同时给出经代理、直连、连不上的网站数量。
|
||||
- 本机网络:物理网卡的出口 IP、归属地、运营商与 IPv6。归属地数据来自 cleanip.io。
|
||||
- 网站分流:25 个常用网站分别从哪个出口出去。按 AI、影音、社交、开发、国内分类筛选;接入 Cloudflare 的网站能实测出口与边缘节点,其余按国内 / 国际规则推断,每个网站带延迟信号条。
|
||||
- 代理方式一览:VPN / 隧道、系统代理、自动代理配置、DNS 与检测到的代理软件。DNS 被代理接管时会标出来。
|
||||
|
||||
**样式**
|
||||
|
||||
- 菜单栏网络弹窗的 DNS 区块重排:网络服务与线路做成两个徽章(Wi-Fi / 有线、直连或经过哪个代理),走隧道时标题旁有叹号提示 DNS 可能被接管,刷新 DNS 缓存改为图标按钮。
|
||||
|
||||
</details>
|
||||
|
||||
<!-- changelog:end -->
|
||||
|
||||
## 活跃度
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ settings:
|
||||
SWIFT_VERSION: "6.0"
|
||||
MARKETING_VERSION: "0.6.2"
|
||||
# 构建号四位数字,每次 make build 加一;营销版本只在发布时用 Scripts/version.sh patch / minor 修改
|
||||
CURRENT_PROJECT_VERSION: "0108"
|
||||
CURRENT_PROJECT_VERSION: "0111"
|
||||
MACOSX_DEPLOYMENT_TARGET: "14.0"
|
||||
# 本地开发使用 ad-hoc 签名;发布时改为 Developer ID(见 docs/PLAN.md)
|
||||
CODE_SIGN_STYLE: Manual
|
||||
|
||||
Reference in new issue
Block a user