// // BrowserWindowController.swift // Gemini // // Created by Shadowfacts on 7/15/20. // import Cocoa import SwiftUI import Combine import BrowserCore class BrowserWindowController: NSWindowController { @IBOutlet weak var toolbar: NSToolbar! private var locationTextField: NSTextField? private var urlUpdater: Cancellable? let navigator = NavigationManager(url: AppDelegate.homePage) convenience init() { self.init(windowNibName: "BrowserWindowController") } override func windowDidLoad() { super.windowDidLoad() contentViewController = NSHostingController(rootView: ContentView(navigator: navigator)) urlUpdater = navigator.$currentURL.sink(receiveValue: self.currentURLChanged(_:)) } private func currentURLChanged(_ newValue: URL) { locationTextField?.stringValue = newValue.absoluteString window?.title = newValue.absoluteString } override func newWindowForTab(_ sender: Any?) { (NSApp.delegate as! AppDelegate).createNewWindowTab(attachedTo: window!) } } extension NSToolbarItem.Identifier { static let locationField = NSToolbarItem.Identifier("Gemini.locationField") static let goBack = NSToolbarItem.Identifier("Gemini.goBack") static let goForward = NSToolbarItem.Identifier("Gemini.goForward") } extension BrowserWindowController: NSToolbarDelegate { func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return [ .space, .flexibleSpace, .locationField, .goBack, .goForward ] } func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { return [ .goBack, .goForward, .flexibleSpace, .locationField, .flexibleSpace, ] } func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { if itemIdentifier == .locationField { return createLocationFieldToolbarItem() } else if itemIdentifier == .goBack { return createBackToolbarItem() } else if itemIdentifier == .goForward { return createForwardToolbarItem() } return NSToolbarItem(itemIdentifier: itemIdentifier) } private func createLocationFieldToolbarItem() -> NSToolbarItem { let item = NSToolbarItem(itemIdentifier: .locationField) item.label = "Location" item.paletteLabel = "Location" let textField = NSTextField(string: navigator.currentURL.absoluteString) textField.delegate = self NSLayoutConstraint.activate([ textField.heightAnchor.constraint(equalToConstant: 22), textField.widthAnchor.constraint(lessThanOrEqualToConstant: 500), textField.widthAnchor.constraint(greaterThanOrEqualToConstant: 250) ]) locationTextField = textField item.view = textField return item } private func createBackToolbarItem() -> NSToolbarItem { let item = NSToolbarItem(itemIdentifier: .goBack) if #available(macOS 10.16, *) { item.image = NSImage(systemSymbolName: "chevron.left", accessibilityDescription: "Go Back") } else { item.image = NSImage(named: NSImage.goBackTemplateName) } item.label = "Go Back" item.paletteLabel = "Go Back" item.toolTip = "Go to the previous page" item.target = navigator item.action = #selector(NavigationManager.back) item.isBordered = true if #available(macOS 10.16, *) { item.isNavigational = true } return item } private func createForwardToolbarItem() -> NSToolbarItem { let item = NSToolbarItem(itemIdentifier: .goForward) if #available(macOS 10.16, *) { item.image = NSImage(systemSymbolName: "chevron.right", accessibilityDescription: "Go Forward") } else { item.image = NSImage(named: NSImage.goForwardTemplateName) } item.label = "Go Forward" item.paletteLabel = "Go Forward" item.toolTip = "Go to the next page" item.target = navigator item.action = #selector(NavigationManager.forward) item.isBordered = true if #available(macOS 10.16, *) { item.isNavigational = true } return item } } extension NavigationManager: NSToolbarItemValidation { public func validateToolbarItem(_ item: NSToolbarItem) -> Bool { if item.itemIdentifier == .goBack { return !backStack.isEmpty } else if item.itemIdentifier == .goForward { return !forwardStack.isEmpty } else { return true } } } extension BrowserWindowController: NSTextFieldDelegate { func controlTextDidEndEditing(_ obj: Notification) { guard let textField = locationTextField else { return } guard let newURL = URL(string: textField.stringValue) else { let alert = NSAlert() alert.alertStyle = .warning alert.messageText = "You specified a malformed URL" alert.addButton(withTitle: "OK") return } navigator.changeURL(newURL) } }