Menüleiste: Breite mitwachsend, Symbole unverzerrt, Sensor wählbar
Die Netzwerkkachel war auf „↓ 999,9 MB/s" dimensioniert und stand damit neben „↓ 13 KB/s" zur Hälfte leer. Auf den schlimmsten Fall auszulegen ist für einen Wert, der sich um den Faktor tausend ändert, die falsche Antwort. Gemessen wird jetzt, was dasteht — mit unsymmetrischer Hysterese: sofort wachsen, denn abgeschnittene Messwerte sind falsche Messwerte, und erst nach zehn ruhigeren Messungen schrumpfen, damit ein Lastausschlag die Leiste nicht zum Pumpen bringt. Das Akkusymbol war zerquetscht. Ein Batteriesymbol ist doppelt so breit wie hoch und wurde in ein Quadrat gezeichnet. Symbole behalten jetzt ihr Seitenverhältnis, und die Breite des Elements richtet sich nach dem, was das Symbol tatsächlich braucht — sonst hätte ein breites Glyph dieselbe Spalte wie ein schmales. Bei den Sensoren stand stumm das Maximum über alle Punkte: eine Zahl, von der niemand weiß, woher sie kommt. Jetzt wählbar, und der gewählte Sensor gibt sein eigenes Kürzel — „Temp 46°" sagt weniger als „Heatpipe 46°". Voreinstellung bleibt der wärmste Punkt, weil das die häufigste Frage ist. Und die Lüfterkachel zeigte „2500" ohne Einheit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@ public final class NetworkMenuBarModule: MenuBarModule {
|
||||
}
|
||||
}
|
||||
|
||||
final class NetworkStatusView: NSView {
|
||||
final class NetworkStatusView: NSView, WidthReporting {
|
||||
|
||||
private let presentation: MenuBarPresentation
|
||||
private var snapshot = NetworkSnapshot()
|
||||
@@ -67,7 +67,7 @@ final class NetworkStatusView: NSView {
|
||||
|
||||
init(presentation: MenuBarPresentation) {
|
||||
self.presentation = presentation
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: Self.width(presentation), height: 22))
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: MenuBarText.minimumWidth, height: 22))
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
@@ -75,38 +75,60 @@ final class NetworkStatusView: NSView {
|
||||
|
||||
/// Breiter als die Hardwaremodule: „↓ 2,4 MB/s" braucht schlicht mehr Platz
|
||||
/// als „46 %".
|
||||
/// Gemessen an der breitesten Zeichenfolge, die vorkommen kann.
|
||||
/// Der Platz für die **aktuellen** Raten.
|
||||
///
|
||||
/// „↓ 999,9 MB/s" ist der Extremfall — meistens steht dort „↓ 12 KB/s".
|
||||
/// Vorher waren 76 Punkte fest verdrahtet, und daneben blieb dauerhaft
|
||||
/// Platz für ein weiteres Symbol frei, in dem nichts stand.
|
||||
static func width(_ presentation: MenuBarPresentation) -> CGFloat {
|
||||
/// Auf „↓ 999,9 MB/s" zu dimensionieren hieße, neben „↓ 13 KB/s" dauerhaft
|
||||
/// die halbe Breite leer zu lassen — und genau so sah es aus. Gemessen wird
|
||||
/// deshalb, was gerade dasteht; `AdaptiveWidth` sorgt dafür, dass die
|
||||
/// Leiste dabei nicht zappelt.
|
||||
private static func ratesWidth(_ snapshot: NetworkSnapshot) -> CGFloat {
|
||||
let font = NSFont.monospacedDigitSystemFont(ofSize: 9, weight: .regular)
|
||||
let lines = ["↓ " + Throughput.formatted(snapshot.downloadRate),
|
||||
"↑ " + Throughput.formatted(snapshot.uploadRate)]
|
||||
let widest = lines
|
||||
.map { NSAttributedString(string: $0, attributes: [.font: font]).size().width }
|
||||
.max() ?? 0
|
||||
return ceil(widest) + 2 * MenuBarText.sidePadding
|
||||
}
|
||||
|
||||
private func neededWidth() -> CGFloat {
|
||||
switch presentation {
|
||||
case .symbol: MenuBarText.minimumWidth
|
||||
case .symbol:
|
||||
max(MenuBarText.minimumWidth,
|
||||
MenuBarText.symbolWidth(snapshot.interfaceKind.symbolName, height: 15) + 4)
|
||||
case .graph, .bars: 28
|
||||
case .value: ratesWidth
|
||||
case .valueAndGraph: ratesWidth + 26
|
||||
// Die Raten tragen mit ↓ und ↑ ihre Beschriftung schon in sich.
|
||||
case .labelAndValue, .symbolAndValue: ratesWidth + MenuBarText.symbolWidth + 2
|
||||
case .value: Self.ratesWidth(snapshot)
|
||||
case .valueAndGraph: Self.ratesWidth(snapshot) + 26
|
||||
// Die Raten tragen mit ↓ und ↑ ihre Beschriftung schon in sich; ein
|
||||
// Kürzel davor wäre doppelt gemoppelt, ein Symbol dagegen nützlich —
|
||||
// es zeigt WLAN, Ethernet oder VPN.
|
||||
case .labelAndValue, .symbolAndValue:
|
||||
Self.ratesWidth(snapshot) + symbolSlot + MenuBarText.innerGap
|
||||
}
|
||||
}
|
||||
|
||||
/// Die Raten stehen zweizeilig in 9 pt — dafür misst `MenuBarText` mit
|
||||
/// seiner 11-pt-Schrift zu breit, also hier eigens gemessen.
|
||||
private static var ratesWidth: CGFloat {
|
||||
let font = NSFont.monospacedDigitSystemFont(ofSize: 9, weight: .regular)
|
||||
let widest = NSAttributedString(string: "↓ 999,9 MB/s", attributes: [.font: font])
|
||||
return ceil(widest.size().width) + 2 * MenuBarText.sidePadding
|
||||
private var symbolSlot: CGFloat {
|
||||
MenuBarText.symbolWidth(snapshot.interfaceKind.symbolName, height: 13)
|
||||
}
|
||||
|
||||
private var adaptive = AdaptiveWidth(minimum: MenuBarText.minimumWidth)
|
||||
private var width: CGFloat = MenuBarText.minimumWidth
|
||||
var onWidthChange: ((CGFloat) -> Void)?
|
||||
|
||||
override var intrinsicContentSize: NSSize {
|
||||
NSSize(width: Self.width(presentation), height: 22)
|
||||
NSSize(width: width, height: 22)
|
||||
}
|
||||
|
||||
func update(_ snapshot: NetworkSnapshot, history: [Double]) {
|
||||
self.snapshot = snapshot
|
||||
self.history = history
|
||||
needsDisplay = true
|
||||
|
||||
let next = adaptive.update(needed: neededWidth())
|
||||
guard next != width else { return }
|
||||
width = next
|
||||
invalidateIntrinsicContentSize()
|
||||
onWidthChange?(next)
|
||||
}
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
@@ -122,10 +144,14 @@ final class NetworkStatusView: NSView {
|
||||
drawGraph(color: color, in: bounds.insetBy(dx: 2, dy: 5))
|
||||
|
||||
case .labelAndValue, .symbolAndValue:
|
||||
let glyph = symbolSlot
|
||||
MenuBarText.drawSymbol(snapshot.interfaceKind.symbolName, color: color,
|
||||
in: NSRect(x: 0, y: bounds.midY - 6.5, width: 13, height: 13))
|
||||
in: NSRect(x: 0, y: bounds.midY - 6.5,
|
||||
width: glyph, height: 13))
|
||||
drawRates(color: color,
|
||||
in: NSRect(x: 15, y: 0, width: bounds.width - 15, height: bounds.height))
|
||||
in: NSRect(x: glyph + MenuBarText.innerGap, y: 0,
|
||||
width: bounds.width - glyph - MenuBarText.innerGap,
|
||||
height: bounds.height))
|
||||
|
||||
case .value, .valueAndGraph:
|
||||
drawRates(color: color,
|
||||
|
||||
Reference in New Issue
Block a user