Move GeometryReader from wrapping the entire VStack to wrapping only the ScrollView so proxy.size.height reflects available height (minus pill), preventing unnecessary scrollability that triggered macOS horizontal insets. Also clamp update pill text width with maxWidth instead of fixed width so it truncates gracefully at narrow sidebar widths and grows when wider, add horizontal padding, left-align truncated text, and add debug menu item for testing with long nightly version strings.
80 lines
2.4 KiB
Swift
80 lines
2.4 KiB
Swift
import AppKit
|
|
import Foundation
|
|
import SwiftUI
|
|
|
|
/// A pill-shaped button that displays update status and provides access to update actions.
|
|
struct UpdatePill: View {
|
|
@ObservedObject var model: UpdateViewModel
|
|
@State private var showPopover = false
|
|
|
|
private let textFont = NSFont.systemFont(ofSize: 11, weight: .medium)
|
|
|
|
var body: some View {
|
|
let state = model.effectiveState
|
|
if !state.isIdle {
|
|
pillButton
|
|
.popover(
|
|
isPresented: $showPopover,
|
|
attachmentAnchor: .rect(.bounds),
|
|
arrowEdge: .top
|
|
) {
|
|
UpdatePopoverView(model: model)
|
|
}
|
|
.transition(.opacity.combined(with: .scale(scale: 0.95)))
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var pillButton: some View {
|
|
Button(action: {
|
|
if case .notFound(let notFound) = model.state {
|
|
model.state = .idle
|
|
notFound.acknowledgement()
|
|
} else {
|
|
showPopover.toggle()
|
|
}
|
|
}) {
|
|
HStack(spacing: 6) {
|
|
UpdateBadge(model: model)
|
|
.frame(width: 14, height: 14)
|
|
|
|
Text(model.text)
|
|
.font(Font(textFont))
|
|
.lineLimit(1)
|
|
.truncationMode(.tail)
|
|
.frame(maxWidth: textWidth, alignment: .leading)
|
|
}
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(
|
|
Capsule()
|
|
.fill(model.backgroundColor)
|
|
)
|
|
.foregroundColor(model.foregroundColor)
|
|
.contentShape(Capsule())
|
|
}
|
|
.buttonStyle(.plain)
|
|
.help(model.text)
|
|
.accessibilityLabel(model.text)
|
|
.accessibilityIdentifier("UpdatePill")
|
|
}
|
|
|
|
private var textWidth: CGFloat? {
|
|
let attributes: [NSAttributedString.Key: Any] = [.font: textFont]
|
|
let size = (model.maxWidthText as NSString).size(withAttributes: attributes)
|
|
return size.width
|
|
}
|
|
}
|
|
|
|
/// Menu item that shows "Install Update and Relaunch" when an update is ready.
|
|
struct InstallUpdateMenuItem: View {
|
|
@ObservedObject var model: UpdateViewModel
|
|
|
|
var body: some View {
|
|
if model.state.isInstallable {
|
|
Button("Install Update and Relaunch") {
|
|
model.state.confirm()
|
|
}
|
|
}
|
|
}
|
|
}
|