菜单栏新增双行居中风格:标签比数值宽时数值不再偏到右边
This commit is contained in:
1 parent
2efebb5008
commit
d05b02be4f
8 files changed
+78
-50
No files matched your search
@@ -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("圆环表示当前占用比例")
|
||||
|
||||
Reference in new issue
Block a user