SidebarView.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import Cocoa
  2. enum SidebarDestination: Int, CaseIterable {
  3. case home
  4. case scan
  5. case scanAndHome
  6. case settings
  7. }
  8. final class SidebarView: NSView {
  9. var onDestinationSelected: ((SidebarDestination) -> Void)?
  10. private var navItems: [SidebarNavItem] = []
  11. init() {
  12. super.init(frame: .zero)
  13. wantsLayer = true
  14. layer?.backgroundColor = AppTheme.sidebarBackground.cgColor
  15. translatesAutoresizingMaskIntoConstraints = false
  16. setup()
  17. }
  18. @available(*, unavailable)
  19. required init?(coder: NSCoder) { nil }
  20. func select(_ destination: SidebarDestination) {
  21. for (index, item) in navItems.enumerated() {
  22. item.isSelected = index == destination.rawValue
  23. }
  24. }
  25. private func setup() {
  26. let logoContainer = NSView()
  27. logoContainer.translatesAutoresizingMaskIntoConstraints = false
  28. let logoIcon = NSImageView()
  29. logoIcon.translatesAutoresizingMaskIntoConstraints = false
  30. if let image = NSImage(systemSymbolName: "printer.fill", accessibilityDescription: "Smart Printer") {
  31. let config = NSImage.SymbolConfiguration(pointSize: 28, weight: .medium)
  32. logoIcon.image = image.withSymbolConfiguration(config)
  33. }
  34. logoIcon.contentTintColor = AppTheme.purple
  35. let appNameLabel = NSTextField(labelWithString: "Smart Printer")
  36. appNameLabel.font = AppTheme.semiboldFont(size: 15)
  37. appNameLabel.textColor = AppTheme.textPrimary
  38. appNameLabel.alignment = .center
  39. appNameLabel.translatesAutoresizingMaskIntoConstraints = false
  40. let navStack = NSStackView()
  41. navStack.orientation = .vertical
  42. navStack.spacing = 4
  43. navStack.alignment = .leading
  44. navStack.translatesAutoresizingMaskIntoConstraints = false
  45. let bottomNavStack = NSStackView()
  46. bottomNavStack.orientation = .vertical
  47. bottomNavStack.spacing = 4
  48. bottomNavStack.alignment = .leading
  49. bottomNavStack.translatesAutoresizingMaskIntoConstraints = false
  50. let homeItem = SidebarNavItem(title: "Home", symbolName: "house.fill", isSelected: true)
  51. let scanItem = SidebarNavItem(title: "Scan", symbolName: "viewfinder")
  52. let scanAndHomeItem = SidebarNavItem(title: "Premium", symbolName: "diamond.fill", accent: .premium)
  53. let settingsItem = SidebarNavItem(title: "Settings", symbolName: "gearshape")
  54. let mainNavItems = [homeItem, scanItem, scanAndHomeItem]
  55. navItems = mainNavItems + [settingsItem]
  56. for (index, item) in navItems.enumerated() {
  57. item.onClick = { [weak self] in
  58. guard let self, let destination = SidebarDestination(rawValue: index) else { return }
  59. self.select(destination)
  60. self.onDestinationSelected?(destination)
  61. }
  62. }
  63. for item in mainNavItems {
  64. navStack.addArrangedSubview(item)
  65. }
  66. bottomNavStack.addArrangedSubview(settingsItem)
  67. addSubview(logoContainer)
  68. logoContainer.addSubview(logoIcon)
  69. addSubview(appNameLabel)
  70. addSubview(navStack)
  71. addSubview(bottomNavStack)
  72. let divider = NSBox()
  73. divider.boxType = .separator
  74. divider.translatesAutoresizingMaskIntoConstraints = false
  75. addSubview(divider)
  76. NSLayoutConstraint.activate([
  77. widthAnchor.constraint(equalToConstant: AppTheme.sidebarWidth),
  78. logoContainer.topAnchor.constraint(equalTo: topAnchor, constant: 52),
  79. logoContainer.centerXAnchor.constraint(equalTo: centerXAnchor),
  80. logoContainer.widthAnchor.constraint(equalToConstant: 48),
  81. logoContainer.heightAnchor.constraint(equalToConstant: 48),
  82. logoIcon.centerXAnchor.constraint(equalTo: logoContainer.centerXAnchor),
  83. logoIcon.centerYAnchor.constraint(equalTo: logoContainer.centerYAnchor),
  84. logoIcon.widthAnchor.constraint(equalToConstant: 36),
  85. logoIcon.heightAnchor.constraint(equalToConstant: 36),
  86. appNameLabel.topAnchor.constraint(equalTo: logoContainer.bottomAnchor, constant: 8),
  87. appNameLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
  88. navStack.topAnchor.constraint(equalTo: appNameLabel.bottomAnchor, constant: 32),
  89. navStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
  90. navStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
  91. bottomNavStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
  92. bottomNavStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
  93. bottomNavStack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -24),
  94. homeItem.widthAnchor.constraint(equalTo: navStack.widthAnchor),
  95. scanItem.widthAnchor.constraint(equalTo: navStack.widthAnchor),
  96. scanAndHomeItem.widthAnchor.constraint(equalTo: navStack.widthAnchor),
  97. settingsItem.widthAnchor.constraint(equalTo: bottomNavStack.widthAnchor),
  98. divider.trailingAnchor.constraint(equalTo: trailingAnchor),
  99. divider.topAnchor.constraint(equalTo: topAnchor),
  100. divider.bottomAnchor.constraint(equalTo: bottomAnchor),
  101. divider.widthAnchor.constraint(equalToConstant: 1),
  102. ])
  103. }
  104. }