|
@@ -86,6 +86,7 @@ final class ViewController: NSViewController {
|
|
|
private weak var scheduleFilterDropdown: NSPopUpButton?
|
|
private weak var scheduleFilterDropdown: NSPopUpButton?
|
|
|
private weak var scheduleGoogleAuthButton: NSButton?
|
|
private weak var scheduleGoogleAuthButton: NSButton?
|
|
|
private var scheduleGoogleAuthButtonWidthConstraint: NSLayoutConstraint?
|
|
private var scheduleGoogleAuthButtonWidthConstraint: NSLayoutConstraint?
|
|
|
|
|
+ private var scheduleGoogleAuthHovering = false
|
|
|
private var scheduleCurrentProfile: GoogleProfileDisplay?
|
|
private var scheduleCurrentProfile: GoogleProfileDisplay?
|
|
|
private var scheduleProfileImageTask: Task<Void, Never>?
|
|
private var scheduleProfileImageTask: Task<Void, Never>?
|
|
|
|
|
|
|
@@ -1895,7 +1896,7 @@ private extension ViewController {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func makeGoogleAuthButton() -> NSButton {
|
|
private func makeGoogleAuthButton() -> NSButton {
|
|
|
- let button = NSButton(title: "", target: self, action: #selector(scheduleConnectButtonPressed(_:)))
|
|
|
|
|
|
|
+ let button = HoverButton(title: "", target: self, action: #selector(scheduleConnectButtonPressed(_:)))
|
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
|
button.isBordered = false
|
|
button.isBordered = false
|
|
|
button.bezelStyle = .regularSquare
|
|
button.bezelStyle = .regularSquare
|
|
@@ -1904,8 +1905,8 @@ private extension ViewController {
|
|
|
button.layer?.borderWidth = 1
|
|
button.layer?.borderWidth = 1
|
|
|
button.font = NSFont.systemFont(ofSize: 14, weight: .semibold)
|
|
button.font = NSFont.systemFont(ofSize: 14, weight: .semibold)
|
|
|
button.imagePosition = .imageLeading
|
|
button.imagePosition = .imageLeading
|
|
|
- button.alignment = .left
|
|
|
|
|
- button.imageHugsTitle = false
|
|
|
|
|
|
|
+ button.alignment = .center
|
|
|
|
|
+ button.imageHugsTitle = true
|
|
|
button.lineBreakMode = .byTruncatingTail
|
|
button.lineBreakMode = .byTruncatingTail
|
|
|
button.contentTintColor = palette.textPrimary
|
|
button.contentTintColor = palette.textPrimary
|
|
|
button.imageScaling = .scaleNone
|
|
button.imageScaling = .scaleNone
|
|
@@ -1913,6 +1914,11 @@ private extension ViewController {
|
|
|
let widthConstraint = button.widthAnchor.constraint(equalToConstant: 248)
|
|
let widthConstraint = button.widthAnchor.constraint(equalToConstant: 248)
|
|
|
widthConstraint.isActive = true
|
|
widthConstraint.isActive = true
|
|
|
scheduleGoogleAuthButtonWidthConstraint = widthConstraint
|
|
scheduleGoogleAuthButtonWidthConstraint = widthConstraint
|
|
|
|
|
+ button.onHoverChanged = { [weak self] hovering in
|
|
|
|
|
+ self?.scheduleGoogleAuthHovering = hovering
|
|
|
|
|
+ self?.applyGoogleAuthButtonSurface()
|
|
|
|
|
+ }
|
|
|
|
|
+ button.onHoverChanged?(false)
|
|
|
return button
|
|
return button
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -2951,7 +2957,6 @@ private extension ViewController {
|
|
|
private func updateGoogleAuthButtonTitle() {
|
|
private func updateGoogleAuthButtonTitle() {
|
|
|
let signedIn = (googleOAuth.loadTokens() != nil)
|
|
let signedIn = (googleOAuth.loadTokens() != nil)
|
|
|
guard let button = scheduleGoogleAuthButton else { return }
|
|
guard let button = scheduleGoogleAuthButton else { return }
|
|
|
- let iconLeftInset: CGFloat = 12
|
|
|
|
|
|
|
|
|
|
let profileName = scheduleCurrentProfile?.name ?? "Google account"
|
|
let profileName = scheduleCurrentProfile?.name ?? "Google account"
|
|
|
let profileEmail = scheduleCurrentProfile?.email ?? "Sign in with Google"
|
|
let profileEmail = scheduleCurrentProfile?.email ?? "Sign in with Google"
|
|
@@ -2968,12 +2973,12 @@ private extension ViewController {
|
|
|
|
|
|
|
|
if signedIn {
|
|
if signedIn {
|
|
|
let symbol = NSImage(systemSymbolName: "person.crop.circle.fill", accessibilityDescription: "Profile")
|
|
let symbol = NSImage(systemSymbolName: "person.crop.circle.fill", accessibilityDescription: "Profile")
|
|
|
- button.image = symbol.flatMap { paddedImage($0, iconSize: NSSize(width: 16, height: 16), leftInset: iconLeftInset) }
|
|
|
|
|
- button.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 16, weight: .regular)
|
|
|
|
|
|
|
+ button.image = symbol.flatMap { resizedImage($0, to: NSSize(width: 20, height: 20)) }
|
|
|
|
|
+ button.symbolConfiguration = NSImage.SymbolConfiguration(pointSize: 20, weight: .regular)
|
|
|
button.contentTintColor = palette.textPrimary
|
|
button.contentTintColor = palette.textPrimary
|
|
|
} else {
|
|
} else {
|
|
|
if let g = NSImage(named: "GoogleGLogo") {
|
|
if let g = NSImage(named: "GoogleGLogo") {
|
|
|
- button.image = paddedImage(g, iconSize: NSSize(width: 16, height: 16), leftInset: iconLeftInset)
|
|
|
|
|
|
|
+ button.image = resizedImage(g, to: NSSize(width: 20, height: 20))
|
|
|
} else {
|
|
} else {
|
|
|
button.image = nil
|
|
button.image = nil
|
|
|
}
|
|
}
|
|
@@ -2981,9 +2986,7 @@ private extension ViewController {
|
|
|
}
|
|
}
|
|
|
button.contentTintColor = signedIn ? palette.textPrimary : nil
|
|
button.contentTintColor = signedIn ? palette.textPrimary : nil
|
|
|
|
|
|
|
|
- let isDark = darkModeEnabled
|
|
|
|
|
- button.layer?.backgroundColor = isDark ? NSColor(calibratedRed: 8.0 / 255.0, green: 14.0 / 255.0, blue: 24.0 / 255.0, alpha: 1).cgColor : NSColor.white.cgColor
|
|
|
|
|
- button.layer?.borderColor = (isDark ? NSColor(calibratedWhite: 0.50, alpha: 1) : NSColor(calibratedWhite: 0.72, alpha: 1)).cgColor
|
|
|
|
|
|
|
+ applyGoogleAuthButtonSurface()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private func makeGoogleProfileDisplay(from profile: GoogleUserProfile) -> GoogleProfileDisplay {
|
|
private func makeGoogleProfileDisplay(from profile: GoogleUserProfile) -> GoogleProfileDisplay {
|
|
@@ -3010,7 +3013,7 @@ private extension ViewController {
|
|
|
if Task.isCancelled { return }
|
|
if Task.isCancelled { return }
|
|
|
guard let image = NSImage(data: data) else { return }
|
|
guard let image = NSImage(data: data) else { return }
|
|
|
await MainActor.run {
|
|
await MainActor.run {
|
|
|
- self?.scheduleGoogleAuthButton?.image = self?.paddedImage(image, iconSize: NSSize(width: 16, height: 16), leftInset: 12)
|
|
|
|
|
|
|
+ self?.scheduleGoogleAuthButton?.image = self?.resizedImage(image, to: NSSize(width: 20, height: 20))
|
|
|
self?.scheduleGoogleAuthButton?.contentTintColor = nil
|
|
self?.scheduleGoogleAuthButton?.contentTintColor = nil
|
|
|
}
|
|
}
|
|
|
} catch {
|
|
} catch {
|
|
@@ -3031,17 +3034,22 @@ private extension ViewController {
|
|
|
return result
|
|
return result
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private func paddedImage(_ image: NSImage, iconSize: NSSize, leftInset: CGFloat) -> NSImage {
|
|
|
|
|
- let canvasSize = NSSize(width: iconSize.width + leftInset, height: iconSize.height)
|
|
|
|
|
- let result = NSImage(size: canvasSize)
|
|
|
|
|
- result.lockFocus()
|
|
|
|
|
- image.draw(in: NSRect(x: leftInset, y: 0, width: iconSize.width, height: iconSize.height),
|
|
|
|
|
- from: NSRect(origin: .zero, size: image.size),
|
|
|
|
|
- operation: .copy,
|
|
|
|
|
- fraction: 1.0)
|
|
|
|
|
- result.unlockFocus()
|
|
|
|
|
- result.isTemplate = false
|
|
|
|
|
- return result
|
|
|
|
|
|
|
+ private func applyGoogleAuthButtonSurface() {
|
|
|
|
|
+ guard let button = scheduleGoogleAuthButton else { return }
|
|
|
|
|
+ let isDark = darkModeEnabled
|
|
|
|
|
+ let baseBackground = isDark
|
|
|
|
|
+ ? NSColor(calibratedRed: 8.0 / 255.0, green: 14.0 / 255.0, blue: 24.0 / 255.0, alpha: 1)
|
|
|
|
|
+ : NSColor.white
|
|
|
|
|
+ let hoverBlend = isDark ? NSColor.white : NSColor.black
|
|
|
|
|
+ let hoverBackground = baseBackground.blended(withFraction: 0.07, of: hoverBlend) ?? baseBackground
|
|
|
|
|
+ let baseBorder = isDark
|
|
|
|
|
+ ? NSColor(calibratedWhite: 0.50, alpha: 1)
|
|
|
|
|
+ : NSColor(calibratedWhite: 0.72, alpha: 1)
|
|
|
|
|
+ let hoverBorder = isDark
|
|
|
|
|
+ ? NSColor(calibratedWhite: 0.62, alpha: 1)
|
|
|
|
|
+ : NSColor(calibratedWhite: 0.56, alpha: 1)
|
|
|
|
|
+ button.layer?.backgroundColor = (scheduleGoogleAuthHovering ? hoverBackground : baseBackground).cgColor
|
|
|
|
|
+ button.layer?.borderColor = (scheduleGoogleAuthHovering ? hoverBorder : baseBorder).cgColor
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@MainActor
|
|
@MainActor
|