| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- import Cocoa
- enum SidebarDestination: Int, CaseIterable {
- case home
- case scan
- case print
- case scanAndHome
- case settings
- }
- final class SidebarView: NSView, AppearanceRefreshable {
- private var appNameLabel: NSTextField!
- private var premiumCard: SidebarPremiumCardView!
- var onDestinationSelected: ((SidebarDestination) -> Void)?
- var onPremiumUpgradeTapped: (() -> Void)?
- var onManageSubscriptionTapped: (() -> Void)?
- private var navItems: [SidebarNavItem] = []
- init() {
- super.init(frame: .zero)
- wantsLayer = true
- layer?.backgroundColor = AppTheme.sidebarBackground.cgColor
- translatesAutoresizingMaskIntoConstraints = false
- setup()
- }
- @available(*, unavailable)
- required init?(coder: NSCoder) { nil }
- func select(_ destination: SidebarDestination) {
- for (index, item) in navItems.enumerated() {
- item.isSelected = navDestination(at: index) == destination
- }
- }
- func refreshPremiumState() {
- premiumCard?.update(isPremium: StoreManager.shared.isPremium)
- }
- func refreshAppearance() {
- layer?.backgroundColor = AppTheme.sidebarBackground.cgColor
- appNameLabel?.refreshThemeLabelColor()
- premiumCard?.refreshAppearance()
- }
- private func navDestination(at index: Int) -> SidebarDestination {
- switch index {
- case 0: return .home
- case 1: return .scan
- case 2: return .print
- case 3: return .settings
- default: return .home
- }
- }
- private func setup() {
- let logoContainer = NSView()
- logoContainer.translatesAutoresizingMaskIntoConstraints = false
- let logoIcon = NSImageView()
- logoIcon.translatesAutoresizingMaskIntoConstraints = false
- logoIcon.image = NSImage(named: "AppLogo")
- logoIcon.imageScaling = .scaleProportionallyUpOrDown
- appNameLabel = NSTextField.themeLabel("Smart Printer", style: .primary, font: AppTheme.semiboldFont(size: 15))
- appNameLabel.alignment = .center
- appNameLabel.translatesAutoresizingMaskIntoConstraints = false
- let navStack = NSStackView()
- navStack.orientation = .vertical
- navStack.spacing = 4
- navStack.alignment = .leading
- navStack.translatesAutoresizingMaskIntoConstraints = false
- let bottomNavStack = NSStackView()
- bottomNavStack.orientation = .vertical
- bottomNavStack.spacing = 12
- bottomNavStack.alignment = .leading
- bottomNavStack.translatesAutoresizingMaskIntoConstraints = false
- let homeItem = SidebarNavItem(title: "Home", symbolName: "house.fill", isSelected: true)
- let scanItem = SidebarNavItem(title: "Scan", symbolName: "viewfinder")
- let printItem = SidebarNavItem(title: "Print", symbolName: "printer")
- let settingsItem = SidebarNavItem(title: "Settings", symbolName: "gearshape")
- premiumCard = SidebarPremiumCardView()
- premiumCard.onUpgradeTapped = { [weak self] in
- self?.onPremiumUpgradeTapped?()
- }
- premiumCard.onManageSubscriptionTapped = { [weak self] in
- self?.onManageSubscriptionTapped?()
- }
- navItems = [homeItem, scanItem, printItem, settingsItem]
- for (index, item) in navItems.enumerated() {
- item.onClick = { [weak self] in
- guard let self else { return }
- let destination = self.navDestination(at: index)
- self.select(destination)
- self.onDestinationSelected?(destination)
- }
- }
- for item in navItems {
- navStack.addArrangedSubview(item)
- }
- bottomNavStack.addArrangedSubview(premiumCard)
- addSubview(logoContainer)
- logoContainer.addSubview(logoIcon)
- addSubview(appNameLabel)
- addSubview(navStack)
- addSubview(bottomNavStack)
- let divider = NSBox()
- divider.boxType = .separator
- divider.translatesAutoresizingMaskIntoConstraints = false
- addSubview(divider)
- NSLayoutConstraint.activate([
- widthAnchor.constraint(equalToConstant: AppTheme.sidebarWidth),
- logoContainer.topAnchor.constraint(equalTo: topAnchor, constant: 52),
- logoContainer.centerXAnchor.constraint(equalTo: centerXAnchor),
- logoContainer.widthAnchor.constraint(equalToConstant: 56),
- logoContainer.heightAnchor.constraint(equalToConstant: 56),
- logoIcon.centerXAnchor.constraint(equalTo: logoContainer.centerXAnchor),
- logoIcon.centerYAnchor.constraint(equalTo: logoContainer.centerYAnchor),
- logoIcon.widthAnchor.constraint(equalToConstant: 48),
- logoIcon.heightAnchor.constraint(equalToConstant: 48),
- appNameLabel.topAnchor.constraint(equalTo: logoContainer.bottomAnchor, constant: 8),
- appNameLabel.centerXAnchor.constraint(equalTo: centerXAnchor),
- navStack.topAnchor.constraint(equalTo: appNameLabel.bottomAnchor, constant: 32),
- navStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
- navStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -12),
- bottomNavStack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
- bottomNavStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
- bottomNavStack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20),
- homeItem.widthAnchor.constraint(equalTo: navStack.widthAnchor),
- scanItem.widthAnchor.constraint(equalTo: navStack.widthAnchor),
- printItem.widthAnchor.constraint(equalTo: navStack.widthAnchor),
- settingsItem.widthAnchor.constraint(equalTo: navStack.widthAnchor),
- premiumCard.widthAnchor.constraint(equalTo: bottomNavStack.widthAnchor),
- divider.trailingAnchor.constraint(equalTo: trailingAnchor),
- divider.topAnchor.constraint(equalTo: topAnchor),
- divider.bottomAnchor.constraint(equalTo: bottomAnchor),
- divider.widthAnchor.constraint(equalToConstant: 1),
- ])
- }
- }
|