|
|
@@ -15,13 +15,6 @@ private enum SidebarPage: Int {
|
|
15
|
15
|
case settings = 4
|
|
16
|
16
|
}
|
|
17
|
17
|
|
|
18
|
|
-private enum MeetingProvider: Int {
|
|
19
|
|
- case meet = 0
|
|
20
|
|
- case zoom = 1
|
|
21
|
|
- case teams = 2
|
|
22
|
|
- case zoho = 3
|
|
23
|
|
-}
|
|
24
|
|
-
|
|
25
|
18
|
private enum ZoomJoinMode: Int {
|
|
26
|
19
|
case id = 0
|
|
27
|
20
|
case url = 1
|
|
|
@@ -50,13 +43,10 @@ final class ViewController: NSViewController {
|
|
50
|
43
|
|
|
51
|
44
|
private var mainContentHost: NSView?
|
|
52
|
45
|
private var sidebarRowViews: [SidebarPage: NSView] = [:]
|
|
53
|
|
- private var tabViews: [MeetingProvider: NSView] = [:]
|
|
54
|
46
|
private var selectedSidebarPage: SidebarPage = .joinMeetings
|
|
55
|
|
- private var selectedMeetingProvider: MeetingProvider = .meet
|
|
56
|
47
|
private var selectedZoomJoinMode: ZoomJoinMode = .id
|
|
57
|
48
|
private var pageCache: [SidebarPage: NSView] = [:]
|
|
58
|
49
|
private var sidebarPageByView = [ObjectIdentifier: SidebarPage]()
|
|
59
|
|
- private var meetingProviderByView = [ObjectIdentifier: MeetingProvider]()
|
|
60
|
50
|
private var zoomJoinModeByView = [ObjectIdentifier: ZoomJoinMode]()
|
|
61
|
51
|
private var zoomJoinModeViews: [ZoomJoinMode: NSView] = [:]
|
|
62
|
52
|
private var settingsActionByView = [ObjectIdentifier: SettingsAction]()
|
|
|
@@ -169,28 +159,13 @@ private extension ViewController {
|
|
169
|
159
|
showSidebarPage(page)
|
|
170
|
160
|
}
|
|
171
|
161
|
|
|
172
|
|
- @objc private func meetingTabClicked(_ sender: NSClickGestureRecognizer) {
|
|
173
|
|
- guard let view = sender.view,
|
|
174
|
|
- let provider = meetingProviderByView[ObjectIdentifier(view)],
|
|
175
|
|
- provider != selectedMeetingProvider else { return }
|
|
176
|
|
- selectedMeetingProvider = provider
|
|
177
|
|
- if provider == .zoom {
|
|
178
|
|
- selectedZoomJoinMode = .id
|
|
179
|
|
- }
|
|
180
|
|
- updateTabAppearance()
|
|
181
|
|
- if selectedSidebarPage == .joinMeetings {
|
|
182
|
|
- pageCache[.joinMeetings] = nil
|
|
183
|
|
- showSidebarPage(.joinMeetings)
|
|
184
|
|
- }
|
|
185
|
|
- }
|
|
186
|
|
-
|
|
187
|
162
|
@objc private func zoomJoinModeClicked(_ sender: NSClickGestureRecognizer) {
|
|
188
|
163
|
guard let view = sender.view,
|
|
189
|
164
|
let mode = zoomJoinModeByView[ObjectIdentifier(view)],
|
|
190
|
165
|
mode != selectedZoomJoinMode else { return }
|
|
191
|
166
|
selectedZoomJoinMode = mode
|
|
192
|
167
|
updateZoomJoinModeAppearance()
|
|
193
|
|
- if selectedSidebarPage == .joinMeetings, selectedMeetingProvider == .zoom {
|
|
|
168
|
+ if selectedSidebarPage == .joinMeetings {
|
|
194
|
169
|
pageCache[.joinMeetings] = nil
|
|
195
|
170
|
showSidebarPage(.joinMeetings)
|
|
196
|
171
|
}
|
|
|
@@ -420,12 +395,6 @@ private extension ViewController {
|
|
420
|
395
|
}
|
|
421
|
396
|
}
|
|
422
|
397
|
|
|
423
|
|
- private func updateTabAppearance() {
|
|
424
|
|
- for (provider, tab) in tabViews {
|
|
425
|
|
- applyTabStyle(tab, provider: provider, logoTemplate: logoTemplateForMeetingProvider(provider))
|
|
426
|
|
- }
|
|
427
|
|
- }
|
|
428
|
|
-
|
|
429
|
398
|
private func logoTemplateForSidebarPage(_ page: SidebarPage) -> Bool {
|
|
430
|
399
|
switch page {
|
|
431
|
400
|
case .photo, .tutorials: return false
|
|
|
@@ -433,13 +402,6 @@ private extension ViewController {
|
|
433
|
402
|
}
|
|
434
|
403
|
}
|
|
435
|
404
|
|
|
436
|
|
- private func logoTemplateForMeetingProvider(_ provider: MeetingProvider) -> Bool {
|
|
437
|
|
- switch provider {
|
|
438
|
|
- case .teams: return false
|
|
439
|
|
- case .meet, .zoom, .zoho: return true
|
|
440
|
|
- }
|
|
441
|
|
- }
|
|
442
|
|
-
|
|
443
|
405
|
func makeSidebar() -> NSView {
|
|
444
|
406
|
let sidebar = NSView()
|
|
445
|
407
|
sidebar.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -580,19 +542,11 @@ private extension ViewController {
|
|
580
|
542
|
contentStack.spacing = 14
|
|
581
|
543
|
contentStack.alignment = .leading
|
|
582
|
544
|
|
|
|
545
|
+ let joinActions = meetJoinActionsRow()
|
|
583
|
546
|
contentStack.addArrangedSubview(textLabel("Join Meetings", font: typography.pageTitle, color: palette.textPrimary))
|
|
584
|
|
- contentStack.addArrangedSubview(meetingTypeTabs())
|
|
585
|
|
- if selectedMeetingProvider == .zoom {
|
|
586
|
|
- contentStack.addArrangedSubview(zoomJoinModeTabs())
|
|
587
|
|
- if selectedZoomJoinMode == .id {
|
|
588
|
|
- contentStack.addArrangedSubview(zoomMeetingIDSection())
|
|
589
|
|
- } else {
|
|
590
|
|
- contentStack.addArrangedSubview(meetingUrlSection())
|
|
591
|
|
- }
|
|
592
|
|
- } else {
|
|
593
|
|
- contentStack.addArrangedSubview(joinWithURLHeading())
|
|
594
|
|
- contentStack.addArrangedSubview(meetingUrlSection())
|
|
595
|
|
- }
|
|
|
547
|
+ contentStack.addArrangedSubview(meetJoinSectionRow())
|
|
|
548
|
+ contentStack.addArrangedSubview(joinActions)
|
|
|
549
|
+ contentStack.setCustomSpacing(26, after: joinActions)
|
|
596
|
550
|
contentStack.addArrangedSubview(scheduleHeader())
|
|
597
|
551
|
contentStack.addArrangedSubview(textLabel("Tuesday, 14 Apr", font: typography.dateHeading, color: palette.textSecondary))
|
|
598
|
552
|
contentStack.addArrangedSubview(scheduleCardsRow())
|
|
|
@@ -608,6 +562,123 @@ private extension ViewController {
|
|
608
|
562
|
return panel
|
|
609
|
563
|
}
|
|
610
|
564
|
|
|
|
565
|
+ func meetJoinSectionRow() -> NSView {
|
|
|
566
|
+ let row = NSStackView()
|
|
|
567
|
+ row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
568
|
+ row.orientation = .horizontal
|
|
|
569
|
+ row.spacing = 12
|
|
|
570
|
+ row.alignment = .top
|
|
|
571
|
+ row.distribution = .fillEqually
|
|
|
572
|
+ row.widthAnchor.constraint(greaterThanOrEqualToConstant: 880).isActive = true
|
|
|
573
|
+ row.heightAnchor.constraint(equalToConstant: 140).isActive = true
|
|
|
574
|
+
|
|
|
575
|
+ let instant = HoverSurfaceView()
|
|
|
576
|
+ instant.translatesAutoresizingMaskIntoConstraints = false
|
|
|
577
|
+ instant.wantsLayer = true
|
|
|
578
|
+ instant.layer?.cornerRadius = 14
|
|
|
579
|
+ instant.layer?.backgroundColor = palette.sectionCard.cgColor
|
|
|
580
|
+ styleSurface(instant, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
581
|
+
|
|
|
582
|
+ let iconWrap = roundedContainer(cornerRadius: 12, color: NSColor.clear)
|
|
|
583
|
+ iconWrap.translatesAutoresizingMaskIntoConstraints = false
|
|
|
584
|
+ iconWrap.widthAnchor.constraint(equalToConstant: 58).isActive = true
|
|
|
585
|
+ iconWrap.heightAnchor.constraint(equalToConstant: 58).isActive = true
|
|
|
586
|
+ iconWrap.layer?.borderWidth = 0
|
|
|
587
|
+ let meetLogoImage = NSImage(named: "MeetLogo") ?? NSImage()
|
|
|
588
|
+ meetLogoImage.isTemplate = false
|
|
|
589
|
+ let meetLogo = NSImageView(image: meetLogoImage)
|
|
|
590
|
+ meetLogo.translatesAutoresizingMaskIntoConstraints = false
|
|
|
591
|
+ meetLogo.imageScaling = .scaleProportionallyDown
|
|
|
592
|
+ meetLogo.contentTintColor = nil
|
|
|
593
|
+ iconWrap.addSubview(meetLogo)
|
|
|
594
|
+
|
|
|
595
|
+ let instantTitle = textLabel("New Instant Meet", font: NSFont.systemFont(ofSize: 40 / 2, weight: .semibold), color: palette.textPrimary)
|
|
|
596
|
+ let instantSub = textLabel("Start instant Meet in more section with\nGoogle Meet meet.", font: NSFont.systemFont(ofSize: 16 / 2, weight: .medium), color: palette.textSecondary)
|
|
|
597
|
+ instantSub.maximumNumberOfLines = 2
|
|
|
598
|
+ instant.addSubview(iconWrap)
|
|
|
599
|
+ instant.addSubview(instantTitle)
|
|
|
600
|
+ instant.addSubview(instantSub)
|
|
|
601
|
+
|
|
|
602
|
+ let codeCard = HoverSurfaceView()
|
|
|
603
|
+ codeCard.translatesAutoresizingMaskIntoConstraints = false
|
|
|
604
|
+ codeCard.wantsLayer = true
|
|
|
605
|
+ codeCard.layer?.cornerRadius = 14
|
|
|
606
|
+ codeCard.layer?.backgroundColor = palette.sectionCard.cgColor
|
|
|
607
|
+ styleSurface(codeCard, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
608
|
+ let codeTitle = textLabel("Join with Link", font: NSFont.systemFont(ofSize: 40 / 2, weight: .semibold), color: palette.textPrimary)
|
|
|
609
|
+ let codeInputShell = roundedContainer(cornerRadius: 8, color: palette.inputBackground)
|
|
|
610
|
+ codeInputShell.translatesAutoresizingMaskIntoConstraints = false
|
|
|
611
|
+ codeInputShell.heightAnchor.constraint(equalToConstant: 52).isActive = true
|
|
|
612
|
+ styleSurface(codeInputShell, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
|
|
|
613
|
+ let codeField = NSTextField(string: "")
|
|
|
614
|
+ codeField.translatesAutoresizingMaskIntoConstraints = false
|
|
|
615
|
+ codeField.isEditable = true
|
|
|
616
|
+ codeField.isBordered = false
|
|
|
617
|
+ codeField.drawsBackground = false
|
|
|
618
|
+ codeField.focusRingType = .none
|
|
|
619
|
+ codeField.font = NSFont.systemFont(ofSize: 36 / 2, weight: .regular)
|
|
|
620
|
+ codeField.textColor = palette.textPrimary
|
|
|
621
|
+ codeField.placeholderString = "Enter Link"
|
|
|
622
|
+ codeInputShell.addSubview(codeField)
|
|
|
623
|
+ codeCard.addSubview(codeTitle)
|
|
|
624
|
+ codeCard.addSubview(codeInputShell)
|
|
|
625
|
+
|
|
|
626
|
+ NSLayoutConstraint.activate([
|
|
|
627
|
+ meetLogo.centerXAnchor.constraint(equalTo: iconWrap.centerXAnchor),
|
|
|
628
|
+ meetLogo.centerYAnchor.constraint(equalTo: iconWrap.centerYAnchor),
|
|
|
629
|
+ meetLogo.widthAnchor.constraint(equalToConstant: 46),
|
|
|
630
|
+ meetLogo.heightAnchor.constraint(equalToConstant: 46),
|
|
|
631
|
+
|
|
|
632
|
+ iconWrap.leadingAnchor.constraint(equalTo: instant.leadingAnchor, constant: 18),
|
|
|
633
|
+ iconWrap.topAnchor.constraint(equalTo: instant.topAnchor, constant: 22),
|
|
|
634
|
+ instantTitle.leadingAnchor.constraint(equalTo: iconWrap.trailingAnchor, constant: 14),
|
|
|
635
|
+ instantTitle.topAnchor.constraint(equalTo: instant.topAnchor, constant: 24),
|
|
|
636
|
+ instantSub.leadingAnchor.constraint(equalTo: instantTitle.leadingAnchor),
|
|
|
637
|
+ instantSub.topAnchor.constraint(equalTo: instantTitle.bottomAnchor, constant: 6),
|
|
|
638
|
+ instantSub.trailingAnchor.constraint(lessThanOrEqualTo: instant.trailingAnchor, constant: -16),
|
|
|
639
|
+
|
|
|
640
|
+ codeTitle.leadingAnchor.constraint(equalTo: codeCard.leadingAnchor, constant: 18),
|
|
|
641
|
+ codeTitle.topAnchor.constraint(equalTo: codeCard.topAnchor, constant: 22),
|
|
|
642
|
+ codeInputShell.leadingAnchor.constraint(equalTo: codeCard.leadingAnchor, constant: 18),
|
|
|
643
|
+ codeInputShell.trailingAnchor.constraint(equalTo: codeCard.trailingAnchor, constant: -18),
|
|
|
644
|
+ codeInputShell.topAnchor.constraint(equalTo: codeTitle.bottomAnchor, constant: 12),
|
|
|
645
|
+ codeField.leadingAnchor.constraint(equalTo: codeInputShell.leadingAnchor, constant: 14),
|
|
|
646
|
+ codeField.trailingAnchor.constraint(equalTo: codeInputShell.trailingAnchor, constant: -14),
|
|
|
647
|
+ codeField.centerYAnchor.constraint(equalTo: codeInputShell.centerYAnchor)
|
|
|
648
|
+ ])
|
|
|
649
|
+
|
|
|
650
|
+ let baseColor = palette.sectionCard
|
|
|
651
|
+ let hoverColor = baseColor.blended(withFraction: 0.10, of: NSColor.white) ?? baseColor
|
|
|
652
|
+ instant.onHoverChanged = { hovering in
|
|
|
653
|
+ instant.layer?.backgroundColor = (hovering ? hoverColor : baseColor).cgColor
|
|
|
654
|
+ }
|
|
|
655
|
+ codeCard.onHoverChanged = { hovering in
|
|
|
656
|
+ codeCard.layer?.backgroundColor = (hovering ? hoverColor : baseColor).cgColor
|
|
|
657
|
+ }
|
|
|
658
|
+ instant.onHoverChanged?(false)
|
|
|
659
|
+ codeCard.onHoverChanged?(false)
|
|
|
660
|
+
|
|
|
661
|
+ row.addArrangedSubview(instant)
|
|
|
662
|
+ row.addArrangedSubview(codeCard)
|
|
|
663
|
+ return row
|
|
|
664
|
+ }
|
|
|
665
|
+
|
|
|
666
|
+ func meetJoinActionsRow() -> NSView {
|
|
|
667
|
+ let row = NSStackView()
|
|
|
668
|
+ row.translatesAutoresizingMaskIntoConstraints = false
|
|
|
669
|
+ row.orientation = .horizontal
|
|
|
670
|
+ row.spacing = 12
|
|
|
671
|
+ row.alignment = .centerY
|
|
|
672
|
+ row.widthAnchor.constraint(greaterThanOrEqualToConstant: 880).isActive = true
|
|
|
673
|
+
|
|
|
674
|
+ let spacer = NSView()
|
|
|
675
|
+ spacer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
676
|
+ row.addArrangedSubview(spacer)
|
|
|
677
|
+ row.addArrangedSubview(actionButton(title: "Cancel", color: palette.cancelButton, textColor: palette.textSecondary, width: 110))
|
|
|
678
|
+ row.addArrangedSubview(actionButton(title: "Join", color: palette.primaryBlue, textColor: .white, width: 116))
|
|
|
679
|
+ return row
|
|
|
680
|
+ }
|
|
|
681
|
+
|
|
611
|
682
|
func makePaywallContent() -> NSView {
|
|
612
|
683
|
paywallPlanViews.removeAll()
|
|
613
|
684
|
premiumPlanByView.removeAll()
|
|
|
@@ -1170,51 +1241,6 @@ private extension ViewController {
|
|
1170
|
1241
|
return container
|
|
1171
|
1242
|
}
|
|
1172
|
1243
|
|
|
1173
|
|
- func meetingTypeTabs() -> NSView {
|
|
1174
|
|
- let wrapper = NSView()
|
|
1175
|
|
- wrapper.translatesAutoresizingMaskIntoConstraints = false
|
|
1176
|
|
-
|
|
1177
|
|
- let shell = roundedContainer(cornerRadius: 24, color: palette.tabBarBackground)
|
|
1178
|
|
- shell.translatesAutoresizingMaskIntoConstraints = false
|
|
1179
|
|
- shell.heightAnchor.constraint(equalToConstant: 48).isActive = true
|
|
1180
|
|
-
|
|
1181
|
|
- let stack = NSStackView()
|
|
1182
|
|
- stack.translatesAutoresizingMaskIntoConstraints = false
|
|
1183
|
|
- stack.orientation = .horizontal
|
|
1184
|
|
- stack.distribution = .fillEqually
|
|
1185
|
|
- stack.spacing = 4
|
|
1186
|
|
-
|
|
1187
|
|
- let meetTab = topTab("Meet", icon: "", provider: .meet, logoImageName: "MeetLogo")
|
|
1188
|
|
- stack.addArrangedSubview(meetTab)
|
|
1189
|
|
- tabViews[.meet] = meetTab
|
|
1190
|
|
- let zoomTab = topTab("Zoom", icon: "", provider: .zoom, logoImageName: "ZoomLogo", logoPointSize: 34)
|
|
1191
|
|
- stack.addArrangedSubview(zoomTab)
|
|
1192
|
|
- tabViews[.zoom] = zoomTab
|
|
1193
|
|
- let teamsTab = topTab("Teams", icon: "", provider: .teams, logoImageName: "TeamsLogo", logoPointSize: 26, logoHeightMultiplier: 62.0 / 50.0, logoTemplate: false)
|
|
1194
|
|
- stack.addArrangedSubview(teamsTab)
|
|
1195
|
|
- tabViews[.teams] = teamsTab
|
|
1196
|
|
- let zohoTab = topTab("Zoho", icon: "", provider: .zoho, logoImageName: "ZohoLogo", logoPointSize: 28)
|
|
1197
|
|
- stack.addArrangedSubview(zohoTab)
|
|
1198
|
|
- tabViews[.zoho] = zohoTab
|
|
1199
|
|
-
|
|
1200
|
|
- shell.addSubview(stack)
|
|
1201
|
|
- wrapper.addSubview(shell)
|
|
1202
|
|
- NSLayoutConstraint.activate([
|
|
1203
|
|
- wrapper.widthAnchor.constraint(greaterThanOrEqualToConstant: 780),
|
|
1204
|
|
- shell.leadingAnchor.constraint(equalTo: wrapper.leadingAnchor),
|
|
1205
|
|
- shell.trailingAnchor.constraint(equalTo: wrapper.trailingAnchor),
|
|
1206
|
|
- shell.topAnchor.constraint(equalTo: wrapper.topAnchor),
|
|
1207
|
|
- shell.bottomAnchor.constraint(equalTo: wrapper.bottomAnchor),
|
|
1208
|
|
- // More horizontal breathing room inside the bar.
|
|
1209
|
|
- stack.leadingAnchor.constraint(equalTo: shell.leadingAnchor, constant: 64),
|
|
1210
|
|
- stack.trailingAnchor.constraint(equalTo: shell.trailingAnchor, constant: -44),
|
|
1211
|
|
- stack.topAnchor.constraint(equalTo: shell.topAnchor, constant: 6),
|
|
1212
|
|
- stack.bottomAnchor.constraint(equalTo: shell.bottomAnchor, constant: -6)
|
|
1213
|
|
- ])
|
|
1214
|
|
-
|
|
1215
|
|
- return wrapper
|
|
1216
|
|
- }
|
|
1217
|
|
-
|
|
1218
|
1244
|
func meetingUrlSection() -> NSView {
|
|
1219
|
1245
|
let wrapper = NSView()
|
|
1220
|
1246
|
wrapper.translatesAutoresizingMaskIntoConstraints = false
|
|
|
@@ -1430,6 +1456,44 @@ private final class HoverTrackingView: RowHitTestView {
|
|
1430
|
1456
|
}
|
|
1431
|
1457
|
}
|
|
1432
|
1458
|
|
|
|
1459
|
+/// Hover tracking without overriding hit-testing; keeps controls like text fields interactive.
|
|
|
1460
|
+private final class HoverSurfaceView: NSView {
|
|
|
1461
|
+ var onHoverChanged: ((Bool) -> Void)?
|
|
|
1462
|
+
|
|
|
1463
|
+ private var trackingAreaRef: NSTrackingArea?
|
|
|
1464
|
+ private var isHovering = false {
|
|
|
1465
|
+ didSet {
|
|
|
1466
|
+ guard isHovering != oldValue else { return }
|
|
|
1467
|
+ onHoverChanged?(isHovering)
|
|
|
1468
|
+ }
|
|
|
1469
|
+ }
|
|
|
1470
|
+
|
|
|
1471
|
+ override func updateTrackingAreas() {
|
|
|
1472
|
+ super.updateTrackingAreas()
|
|
|
1473
|
+ if let trackingAreaRef {
|
|
|
1474
|
+ removeTrackingArea(trackingAreaRef)
|
|
|
1475
|
+ }
|
|
|
1476
|
+ let options: NSTrackingArea.Options = [
|
|
|
1477
|
+ .activeInKeyWindow,
|
|
|
1478
|
+ .inVisibleRect,
|
|
|
1479
|
+ .mouseEnteredAndExited
|
|
|
1480
|
+ ]
|
|
|
1481
|
+ let area = NSTrackingArea(rect: bounds, options: options, owner: self, userInfo: nil)
|
|
|
1482
|
+ addTrackingArea(area)
|
|
|
1483
|
+ trackingAreaRef = area
|
|
|
1484
|
+ }
|
|
|
1485
|
+
|
|
|
1486
|
+ override func mouseEntered(with event: NSEvent) {
|
|
|
1487
|
+ super.mouseEntered(with: event)
|
|
|
1488
|
+ isHovering = true
|
|
|
1489
|
+ }
|
|
|
1490
|
+
|
|
|
1491
|
+ override func mouseExited(with event: NSEvent) {
|
|
|
1492
|
+ super.mouseExited(with: event)
|
|
|
1493
|
+ isHovering = false
|
|
|
1494
|
+ }
|
|
|
1495
|
+}
|
|
|
1496
|
+
|
|
1433
|
1497
|
private final class SettingsMenuViewController: NSViewController {
|
|
1434
|
1498
|
private let palette: Palette
|
|
1435
|
1499
|
private let typography: Typography
|
|
|
@@ -1748,79 +1812,6 @@ private extension ViewController {
|
|
1748
|
1812
|
}
|
|
1749
|
1813
|
}
|
|
1750
|
1814
|
|
|
1751
|
|
- func topTab(_ title: String, icon: String, provider: MeetingProvider, logoImageName: String? = nil, logoPointSize: CGFloat = 26, logoHeightMultiplier: CGFloat = 1, logoTemplate: Bool = true) -> NSView {
|
|
1752
|
|
- let tab = HoverTrackingView()
|
|
1753
|
|
- tab.wantsLayer = true
|
|
1754
|
|
- tab.layer?.cornerRadius = 19
|
|
1755
|
|
- tab.layer?.backgroundColor = NSColor.clear.cgColor
|
|
1756
|
|
- tab.translatesAutoresizingMaskIntoConstraints = false
|
|
1757
|
|
- meetingProviderByView[ObjectIdentifier(tab)] = provider
|
|
1758
|
|
-
|
|
1759
|
|
- let leadingView: NSView
|
|
1760
|
|
- if let name = logoImageName, let logo = NSImage(named: name) {
|
|
1761
|
|
- let imageView = NSImageView(image: logo)
|
|
1762
|
|
- imageView.translatesAutoresizingMaskIntoConstraints = false
|
|
1763
|
|
- imageView.imageScaling = .scaleProportionallyDown
|
|
1764
|
|
- imageView.imageAlignment = .alignCenter
|
|
1765
|
|
- imageView.isEditable = false
|
|
1766
|
|
- if logoTemplate {
|
|
1767
|
|
- imageView.contentTintColor = palette.textPrimary
|
|
1768
|
|
- }
|
|
1769
|
|
- leadingView = imageView
|
|
1770
|
|
- } else {
|
|
1771
|
|
- leadingView = textLabel(icon, font: typography.tabIcon, color: palette.textPrimary)
|
|
1772
|
|
- }
|
|
1773
|
|
-
|
|
1774
|
|
- let titleLabel = textLabel(title, font: typography.tabTitle, color: palette.textPrimary)
|
|
1775
|
|
-
|
|
1776
|
|
- tab.addSubview(leadingView)
|
|
1777
|
|
- tab.addSubview(titleLabel)
|
|
1778
|
|
-
|
|
1779
|
|
- var constraints: [NSLayoutConstraint] = [
|
|
1780
|
|
- leadingView.leadingAnchor.constraint(equalTo: tab.leadingAnchor, constant: 18),
|
|
1781
|
|
- leadingView.centerYAnchor.constraint(equalTo: tab.centerYAnchor),
|
|
1782
|
|
- titleLabel.leadingAnchor.constraint(equalTo: leadingView.trailingAnchor, constant: 8),
|
|
1783
|
|
- titleLabel.centerYAnchor.constraint(equalTo: tab.centerYAnchor),
|
|
1784
|
|
- titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: tab.trailingAnchor, constant: -18)
|
|
1785
|
|
- ]
|
|
1786
|
|
- if logoImageName != nil {
|
|
1787
|
|
- constraints.append(contentsOf: [
|
|
1788
|
|
- leadingView.widthAnchor.constraint(equalToConstant: logoPointSize),
|
|
1789
|
|
- leadingView.heightAnchor.constraint(equalToConstant: logoPointSize * logoHeightMultiplier)
|
|
1790
|
|
- ])
|
|
1791
|
|
- }
|
|
1792
|
|
- NSLayoutConstraint.activate(constraints)
|
|
1793
|
|
-
|
|
1794
|
|
- applyTabStyle(tab, provider: provider, logoTemplate: logoTemplate)
|
|
1795
|
|
- tab.onHoverChanged = { [weak self, weak tab] hovering in
|
|
1796
|
|
- guard let self, let tab else { return }
|
|
1797
|
|
- self.applyTabStyle(tab, provider: provider, logoTemplate: logoTemplate, hovering: hovering)
|
|
1798
|
|
- }
|
|
1799
|
|
-
|
|
1800
|
|
- let click = NSClickGestureRecognizer(target: self, action: #selector(meetingTabClicked(_:)))
|
|
1801
|
|
- tab.addGestureRecognizer(click)
|
|
1802
|
|
-
|
|
1803
|
|
- return tab
|
|
1804
|
|
- }
|
|
1805
|
|
-
|
|
1806
|
|
- func applyTabStyle(_ tab: NSView, provider: MeetingProvider, logoTemplate: Bool, hovering: Bool = false) {
|
|
1807
|
|
- let selected = (provider == selectedMeetingProvider)
|
|
1808
|
|
- let hoverColor = NSColor(calibratedWhite: 1, alpha: 0.07)
|
|
1809
|
|
- tab.layer?.backgroundColor = (selected ? palette.primaryBlue : (hovering ? hoverColor : NSColor.clear)).cgColor
|
|
1810
|
|
- guard tab.subviews.count >= 2 else { return }
|
|
1811
|
|
- let leading = tab.subviews[0]
|
|
1812
|
|
- let title = tab.subviews[1] as? NSTextField
|
|
1813
|
|
- let textColor = palette.textPrimary
|
|
1814
|
|
- title?.textColor = textColor
|
|
1815
|
|
- if let imageView = leading as? NSImageView {
|
|
1816
|
|
- if logoTemplate {
|
|
1817
|
|
- imageView.contentTintColor = textColor
|
|
1818
|
|
- }
|
|
1819
|
|
- } else if let iconField = leading as? NSTextField {
|
|
1820
|
|
- iconField.textColor = textColor
|
|
1821
|
|
- }
|
|
1822
|
|
- }
|
|
1823
|
|
-
|
|
1824
|
1815
|
func actionButton(title: String, color: NSColor, textColor: NSColor, width: CGFloat) -> NSView {
|
|
1825
|
1816
|
let button = HoverTrackingView()
|
|
1826
|
1817
|
button.wantsLayer = true
|