|
|
@@ -26,8 +26,6 @@ class ViewController: NSViewController {
|
|
|
private let chromeUnifiedBackground = NSColor(calibratedRed: 22 / 255, green: 23 / 255, blue: 26 / 255, alpha: 1)
|
|
|
private let searchPillBackground = NSColor.white.withAlphaComponent(0.06)
|
|
|
private let meetingCardBackground = NSColor(calibratedRed: 30 / 255, green: 34 / 255, blue: 42 / 255, alpha: 1)
|
|
|
- private let titleBarControlBackground = NSColor.white.withAlphaComponent(0.04)
|
|
|
- private let titleBarLightControlBackground = NSColor.white.withAlphaComponent(0.02)
|
|
|
private let appShellCornerRadius: CGFloat = 20
|
|
|
private let homeChromeHeaderHeight: CGFloat = 56
|
|
|
private let nativeTrafficLightsLeading: CGFloat = 14
|
|
|
@@ -747,22 +745,35 @@ class ViewController: NSViewController {
|
|
|
searchPill.layer?.cornerRadius = 10
|
|
|
let search = makeLabel("Search (\u{2318}E)", size: 13, color: mutedText, weight: .regular, centered: true)
|
|
|
|
|
|
+ let backForwardCluster = NSStackView()
|
|
|
+ backForwardCluster.orientation = .horizontal
|
|
|
+ backForwardCluster.spacing = 4
|
|
|
+ backForwardCluster.alignment = .centerY
|
|
|
+ let backButton = makeNavGlyphButton(symbol: "chevron.left", action: #selector(topBarPlaceholderTapped), dimension: 14, pointSize: 7, toolTip: "Back")
|
|
|
+ let forwardButton = makeNavGlyphButton(symbol: "chevron.right", action: #selector(topBarPlaceholderTapped), dimension: 14, pointSize: 7, toolTip: "Forward")
|
|
|
+ [backButton, forwardButton].forEach { backForwardCluster.addArrangedSubview($0) }
|
|
|
+
|
|
|
let leftTopBarCluster = NSStackView()
|
|
|
leftTopBarCluster.orientation = .horizontal
|
|
|
- leftTopBarCluster.spacing = 10
|
|
|
+ leftTopBarCluster.spacing = 0
|
|
|
leftTopBarCluster.alignment = .centerY
|
|
|
- let backButton = makeTopBarGlyphButton(symbol: "chevron.left", action: #selector(topBarPlaceholderTapped))
|
|
|
- let forwardButton = makeTopBarGlyphButton(symbol: "chevron.right", action: #selector(topBarPlaceholderTapped))
|
|
|
- let historyButton = makeTopBarGlyphButton(symbol: "clock.arrow.circlepath", action: #selector(topBarPlaceholderTapped))
|
|
|
- [backButton, forwardButton, historyButton].forEach { leftTopBarCluster.addArrangedSubview($0) }
|
|
|
-
|
|
|
+ let historyButton = makeNavGlyphButton(symbol: "clock.arrow.circlepath", action: #selector(topBarPlaceholderTapped), toolTip: "History")
|
|
|
+ let navHistoryGap = NSView()
|
|
|
+ navHistoryGap.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ navHistoryGap.widthAnchor.constraint(equalToConstant: 12).isActive = true
|
|
|
+ [backForwardCluster, navHistoryGap, historyButton].forEach { leftTopBarCluster.addArrangedSubview($0) }
|
|
|
+
|
|
|
+ let searchRow = NSStackView()
|
|
|
+ searchRow.orientation = .horizontal
|
|
|
+ searchRow.spacing = 14
|
|
|
+ searchRow.alignment = .centerY
|
|
|
+ [leftTopBarCluster, searchPill].forEach { searchRow.addArrangedSubview($0) }
|
|
|
+
|
|
|
let rightTopBarCluster = NSStackView()
|
|
|
rightTopBarCluster.orientation = .horizontal
|
|
|
- rightTopBarCluster.spacing = 8
|
|
|
+ rightTopBarCluster.spacing = 10
|
|
|
rightTopBarCluster.alignment = .centerY
|
|
|
- let plusButton = makeTopBarGlyphButton(symbol: "plus", action: #selector(topBarPlaceholderTapped))
|
|
|
- let notificationButton = makeTopBarGlyphButton(symbol: "bell", action: #selector(topBarPlaceholderTapped))
|
|
|
- let appsButton = makeTopBarIconButton(symbol: "square.grid.2x2", action: #selector(topBarPlaceholderTapped))
|
|
|
+ let upgradeToProButton = makeUpgradeToProButton(action: #selector(topBarPlaceholderTapped))
|
|
|
|
|
|
let profileChip = NSButton(title: String((profile?.name ?? "H").prefix(1)).uppercased(), target: self, action: #selector(logoutTapped))
|
|
|
profileChip.isBordered = false
|
|
|
@@ -772,7 +783,7 @@ class ViewController: NSViewController {
|
|
|
profileChip.contentTintColor = primaryText
|
|
|
profileChip.font = .systemFont(ofSize: 14, weight: .bold)
|
|
|
profileChip.toolTip = "Profile (click to logout)"
|
|
|
- [plusButton, notificationButton, appsButton, profileChip].forEach { rightTopBarCluster.addArrangedSubview($0) }
|
|
|
+ [upgradeToProButton, profileChip].forEach { rightTopBarCluster.addArrangedSubview($0) }
|
|
|
|
|
|
let welcome = makeLabel("Home", size: 15, color: secondaryText, weight: .medium, centered: false)
|
|
|
let timeTitle = makeLabel("--:--", size: 56, color: primaryText, weight: .bold, centered: true)
|
|
|
@@ -829,14 +840,13 @@ class ViewController: NSViewController {
|
|
|
content.addSubview(topBarDivider)
|
|
|
content.addSubview(contentColumn)
|
|
|
|
|
|
- [brandStack, leftTopBarCluster, rightTopBarCluster, searchPill, search].forEach {
|
|
|
+ [brandStack, searchRow, backForwardCluster, leftTopBarCluster, rightTopBarCluster, searchPill, search].forEach {
|
|
|
$0.translatesAutoresizingMaskIntoConstraints = false
|
|
|
}
|
|
|
[brandStack].forEach {
|
|
|
shell.addSubview($0)
|
|
|
}
|
|
|
- [leftTopBarCluster, rightTopBarCluster, searchPill, search].forEach {
|
|
|
- $0.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ [searchRow, rightTopBarCluster, search].forEach {
|
|
|
topBar.addSubview($0)
|
|
|
}
|
|
|
[welcome, timeTitle, dateTitle, actions, panel, panelHeader, meetingsStatus, noMeeting, meetingsScrollView, openRecordings].forEach {
|
|
|
@@ -850,9 +860,12 @@ class ViewController: NSViewController {
|
|
|
meetingsScrollView.documentView = meetingsDocument
|
|
|
meetingsDocument.addSubview(meetingsStack)
|
|
|
|
|
|
+ let searchRowCenterX = searchRow.centerXAnchor.constraint(equalTo: topBar.centerXAnchor)
|
|
|
+ searchRowCenterX.priority = .defaultHigh
|
|
|
+
|
|
|
NSLayoutConstraint.activate([
|
|
|
brandStack.leadingAnchor.constraint(equalTo: shell.leadingAnchor, constant: brandLeadingInset),
|
|
|
- brandStack.trailingAnchor.constraint(lessThanOrEqualTo: leftTopBarCluster.leadingAnchor, constant: -12),
|
|
|
+ brandStack.trailingAnchor.constraint(lessThanOrEqualTo: searchRow.leadingAnchor, constant: -12),
|
|
|
brandStack.centerYAnchor.constraint(equalTo: chromeHeader.centerYAnchor, constant: -1),
|
|
|
|
|
|
topBar.topAnchor.constraint(equalTo: content.topAnchor),
|
|
|
@@ -869,18 +882,17 @@ class ViewController: NSViewController {
|
|
|
contentColumn.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8),
|
|
|
contentColumn.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -8),
|
|
|
|
|
|
- leftTopBarCluster.leadingAnchor.constraint(greaterThanOrEqualTo: topBar.leadingAnchor, constant: 40),
|
|
|
- leftTopBarCluster.leadingAnchor.constraint(greaterThanOrEqualTo: brandStack.trailingAnchor, constant: 16),
|
|
|
- leftTopBarCluster.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
|
+ searchRowCenterX,
|
|
|
+ searchRow.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
|
+ searchRow.leadingAnchor.constraint(greaterThanOrEqualTo: topBar.leadingAnchor, constant: 40),
|
|
|
+ searchRow.leadingAnchor.constraint(greaterThanOrEqualTo: brandStack.trailingAnchor, constant: 16),
|
|
|
+ searchRow.trailingAnchor.constraint(lessThanOrEqualTo: rightTopBarCluster.leadingAnchor, constant: -12),
|
|
|
+
|
|
|
rightTopBarCluster.trailingAnchor.constraint(equalTo: topBar.trailingAnchor, constant: -12),
|
|
|
rightTopBarCluster.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
|
|
|
|
- searchPill.centerXAnchor.constraint(equalTo: topBar.centerXAnchor),
|
|
|
- searchPill.centerYAnchor.constraint(equalTo: topBar.centerYAnchor),
|
|
|
searchPill.heightAnchor.constraint(equalToConstant: 32),
|
|
|
searchPill.widthAnchor.constraint(equalToConstant: 320),
|
|
|
- searchPill.leadingAnchor.constraint(greaterThanOrEqualTo: leftTopBarCluster.trailingAnchor, constant: 12),
|
|
|
- searchPill.trailingAnchor.constraint(lessThanOrEqualTo: rightTopBarCluster.leadingAnchor, constant: -12),
|
|
|
search.leadingAnchor.constraint(equalTo: searchPill.leadingAnchor, constant: 12),
|
|
|
search.trailingAnchor.constraint(equalTo: searchPill.trailingAnchor, constant: -12),
|
|
|
search.centerYAnchor.constraint(equalTo: searchPill.centerYAnchor),
|
|
|
@@ -1061,37 +1073,47 @@ class ViewController: NSViewController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private func makeTopBarIconButton(symbol: String, action: Selector?) -> NSButton {
|
|
|
- let button = NSButton(title: "", target: action == nil ? nil : self, action: action)
|
|
|
+ private func makeUpgradeToProButton(action: Selector?) -> NSButton {
|
|
|
+ let title = "Upgrade to Pro"
|
|
|
+ let button = NSButton(title: title, target: action == nil ? nil : self, action: action)
|
|
|
button.isBordered = false
|
|
|
+ button.focusRingType = .none
|
|
|
button.wantsLayer = true
|
|
|
- button.layer?.backgroundColor = titleBarControlBackground.cgColor
|
|
|
- button.layer?.cornerRadius = 10
|
|
|
- button.layer?.borderWidth = 1
|
|
|
- button.layer?.borderColor = NSColor.white.withAlphaComponent(0.05).cgColor
|
|
|
- button.contentTintColor = secondaryText
|
|
|
- button.image = NSImage(systemSymbolName: symbol, accessibilityDescription: symbol)
|
|
|
- button.imageScaling = .scaleProportionallyUpOrDown
|
|
|
+ button.layer?.backgroundColor = accentBlue.cgColor
|
|
|
+ button.layer?.cornerRadius = 17
|
|
|
+ let font = NSFont.systemFont(ofSize: 13, weight: .semibold)
|
|
|
+ button.attributedTitle = NSAttributedString(string: title, attributes: [
|
|
|
+ .foregroundColor: NSColor.white,
|
|
|
+ .font: font
|
|
|
+ ])
|
|
|
+ button.toolTip = title
|
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- button.widthAnchor.constraint(equalToConstant: 30).isActive = true
|
|
|
- button.heightAnchor.constraint(equalToConstant: 30).isActive = true
|
|
|
+ button.heightAnchor.constraint(equalToConstant: 34).isActive = true
|
|
|
+ button.widthAnchor.constraint(greaterThanOrEqualToConstant: 152).isActive = true
|
|
|
return button
|
|
|
}
|
|
|
-
|
|
|
- private func makeTopBarGlyphButton(symbol: String, action: Selector?) -> NSButton {
|
|
|
+
|
|
|
+ /// Back / forward / history: icon-only, no background or border. Back/forward use smaller `dimension` / `pointSize` than history.
|
|
|
+ private func makeNavGlyphButton(symbol: String, action: Selector?, dimension: CGFloat = 18, pointSize: CGFloat = 9, toolTip: String? = nil) -> NSButton {
|
|
|
let button = NSButton(title: "", target: action == nil ? nil : self, action: action)
|
|
|
button.isBordered = false
|
|
|
- button.wantsLayer = true
|
|
|
- button.layer?.backgroundColor = titleBarLightControlBackground.cgColor
|
|
|
- button.layer?.cornerRadius = 9
|
|
|
- button.layer?.borderWidth = 1
|
|
|
- button.layer?.borderColor = NSColor.white.withAlphaComponent(0.03).cgColor
|
|
|
- button.contentTintColor = secondaryText
|
|
|
- button.image = NSImage(systemSymbolName: symbol, accessibilityDescription: symbol)
|
|
|
+ button.bezelStyle = .shadowlessSquare
|
|
|
+ button.focusRingType = .none
|
|
|
+ button.contentTintColor = NSColor(calibratedWhite: 0.84, alpha: 1)
|
|
|
+ if let toolTip {
|
|
|
+ button.toolTip = toolTip
|
|
|
+ }
|
|
|
+ let symbolConfig = NSImage.SymbolConfiguration(pointSize: pointSize, weight: .medium)
|
|
|
+ if let base = NSImage(systemSymbolName: symbol, accessibilityDescription: symbol),
|
|
|
+ let image = base.withSymbolConfiguration(symbolConfig) {
|
|
|
+ image.isTemplate = true
|
|
|
+ button.image = image
|
|
|
+ }
|
|
|
button.imageScaling = .scaleProportionallyUpOrDown
|
|
|
+ button.imagePosition = .imageOnly
|
|
|
button.translatesAutoresizingMaskIntoConstraints = false
|
|
|
- button.widthAnchor.constraint(equalToConstant: 26).isActive = true
|
|
|
- button.heightAnchor.constraint(equalToConstant: 26).isActive = true
|
|
|
+ button.widthAnchor.constraint(equalToConstant: dimension).isActive = true
|
|
|
+ button.heightAnchor.constraint(equalToConstant: dimension).isActive = true
|
|
|
return button
|
|
|
}
|
|
|
|