From bd48fc8dbb1d5e0c6967116d4e4642167b96cd76 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Wed, 15 Jul 2020 18:30:12 -0400 Subject: [PATCH] Use toolbar for navigation controls --- Gemini.xcodeproj/project.pbxproj | 31 +- .../xcschemes/xcschememanagement.plist | 4 +- Gemini/AppDelegate.swift | 93 ++- Gemini/Base.lproj/Main.storyboard | 683 ------------------ Gemini/BrowserWindowController.swift | 149 ++++ Gemini/BrowserWindowController.xib | 38 + Gemini/ContentView.swift | 17 +- Gemini/Info.plist | 4 +- Gemini/MainMenu.xib | 423 +++++++++++ Gemini/NavigationManager.swift | 6 +- GeminiRenderer/DocumentView.swift | 2 +- 11 files changed, 710 insertions(+), 740 deletions(-) delete mode 100644 Gemini/Base.lproj/Main.storyboard create mode 100644 Gemini/BrowserWindowController.swift create mode 100644 Gemini/BrowserWindowController.xib create mode 100644 Gemini/MainMenu.xib diff --git a/Gemini.xcodeproj/project.pbxproj b/Gemini.xcodeproj/project.pbxproj index cfc8f7a..e7fadb2 100644 --- a/Gemini.xcodeproj/project.pbxproj +++ b/Gemini.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ D626646124BBF1C200DF9B88 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626646024BBF1C200DF9B88 /* ContentView.swift */; }; D626646324BBF1C300DF9B88 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D626646224BBF1C300DF9B88 /* Assets.xcassets */; }; D626646624BBF1C300DF9B88 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D626646524BBF1C300DF9B88 /* Preview Assets.xcassets */; }; - D626646924BBF1C300DF9B88 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D626646724BBF1C300DF9B88 /* Main.storyboard */; }; D626648024BBF22E00DF9B88 /* GeminiProtocol.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D626647724BBF22E00DF9B88 /* GeminiProtocol.framework */; }; D626648724BBF22E00DF9B88 /* GeminiProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D626648624BBF22E00DF9B88 /* GeminiProtocolTests.swift */; }; D626648924BBF22E00DF9B88 /* GeminiProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D626647924BBF22E00DF9B88 /* GeminiProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -46,6 +45,9 @@ D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */; }; D69F00AE24BEA29100E37622 /* GeminiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AD24BEA29100E37622 /* GeminiResponse.swift */; }; D69F00B024BEA84D00E37622 /* NavigationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AF24BEA84D00E37622 /* NavigationManager.swift */; }; + D6E1529824BFAAA400FDF9D3 /* BrowserWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E1529624BFAAA400FDF9D3 /* BrowserWindowController.swift */; }; + D6E1529924BFAAA400FDF9D3 /* BrowserWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6E1529724BFAAA400FDF9D3 /* BrowserWindowController.xib */; }; + D6E1529B24BFAEC700FDF9D3 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6E1529A24BFAEC700FDF9D3 /* MainMenu.xib */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -154,7 +156,6 @@ D626646024BBF1C200DF9B88 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; D626646224BBF1C300DF9B88 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D626646524BBF1C300DF9B88 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - D626646824BBF1C300DF9B88 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; D626646A24BBF1C300DF9B88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D626646B24BBF1C300DF9B88 /* Gemini.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Gemini.entitlements; sourceTree = ""; }; D626647724BBF22E00DF9B88 /* GeminiProtocol.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GeminiProtocol.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -192,6 +193,9 @@ D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiDataTask.swift; sourceTree = ""; }; D69F00AD24BEA29100E37622 /* GeminiResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiResponse.swift; sourceTree = ""; }; D69F00AF24BEA84D00E37622 /* NavigationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationManager.swift; sourceTree = ""; }; + D6E1529624BFAAA400FDF9D3 /* BrowserWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserWindowController.swift; sourceTree = ""; }; + D6E1529724BFAAA400FDF9D3 /* BrowserWindowController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = BrowserWindowController.xib; sourceTree = ""; }; + D6E1529A24BFAEC700FDF9D3 /* MainMenu.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -289,8 +293,10 @@ D626645E24BBF1C200DF9B88 /* AppDelegate.swift */, D626646024BBF1C200DF9B88 /* ContentView.swift */, D69F00AF24BEA84D00E37622 /* NavigationManager.swift */, + D6E1529A24BFAEC700FDF9D3 /* MainMenu.xib */, + D6E1529624BFAAA400FDF9D3 /* BrowserWindowController.swift */, + D6E1529724BFAAA400FDF9D3 /* BrowserWindowController.xib */, D626646224BBF1C300DF9B88 /* Assets.xcassets */, - D626646724BBF1C300DF9B88 /* Main.storyboard */, D626646A24BBF1C300DF9B88 /* Info.plist */, D626646B24BBF1C300DF9B88 /* Gemini.entitlements */, D626646424BBF1C300DF9B88 /* Preview Content */, @@ -614,9 +620,10 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - D626646924BBF1C300DF9B88 /* Main.storyboard in Resources */, + D6E1529924BFAAA400FDF9D3 /* BrowserWindowController.xib in Resources */, D626646624BBF1C300DF9B88 /* Preview Assets.xcassets in Resources */, D626646324BBF1C300DF9B88 /* Assets.xcassets in Resources */, + D6E1529B24BFAEC700FDF9D3 /* MainMenu.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -669,6 +676,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D6E1529824BFAAA400FDF9D3 /* BrowserWindowController.swift in Sources */, D626646124BBF1C200DF9B88 /* ContentView.swift in Sources */, D626645F24BBF1C200DF9B88 /* AppDelegate.swift in Sources */, D69F00B024BEA84D00E37622 /* NavigationManager.swift in Sources */, @@ -791,17 +799,6 @@ }; /* End PBXTargetDependency section */ -/* Begin PBXVariantGroup section */ - D626646724BBF1C300DF9B88 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - D626646824BBF1C300DF9B88 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ D626646C24BBF1C300DF9B88 /* Debug */ = { isa = XCBuildConfiguration; @@ -936,7 +933,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 10.16; PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.Gemini; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -961,7 +958,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MACOSX_DEPLOYMENT_TARGET = 10.15; + MACOSX_DEPLOYMENT_TARGET = 10.16; PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.Gemini; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; diff --git a/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist b/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist index 4404ed6..990a051 100644 --- a/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Gemini.xcodeproj/xcuserdata/shadowfacts.xcuserdatad/xcschemes/xcschememanagement.plist @@ -17,12 +17,12 @@ GeminiProtocol.xcscheme_^#shared#^_ orderHint - 3 + 2 GeminiRenderer.xcscheme_^#shared#^_ orderHint - 2 + 3 SuppressBuildableAutocreation diff --git a/Gemini/AppDelegate.swift b/Gemini/AppDelegate.swift index 20551c5..d3d8b85 100644 --- a/Gemini/AppDelegate.swift +++ b/Gemini/AppDelegate.swift @@ -8,30 +8,53 @@ import Cocoa import SwiftUI import GeminiProtocol +import Combine @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { - var window: NSWindow! +// var window: NSWindow! + + var windowControllers = [BrowserWindowController]() let homePage = URL(string: "gemini://gemini.circumlunar.space/")! - private(set) lazy var navigationManager = NavigationManager(url: homePage) +// private(set) lazy var navigationManager = NavigationManager(url: homePage) + +// private var toolbarTextField: NSTextField! +// private var toolbarUpdater: Cancellable? func applicationDidFinishLaunching(_ aNotification: Notification) { - // Create the SwiftUI view that provides the window contents. - let contentView = ContentView(navigator: navigationManager) - - // Create the window and set the content view. - window = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), - styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], - backing: .buffered, defer: false) - window.isReleasedWhenClosed = false - window.center() - window.setFrameAutosaveName("Main Window") - window.contentView = NSHostingView(rootView: contentView) - window.title = "Gemini" - window.makeKeyAndOrderFront(nil) +// // Create the SwiftUI view that provides the window contents. +// let contentView = ContentView(navigator: navigationManager) +// +// // Create the window and set the content view. +// window = NSWindow( +// contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), +// styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], +// backing: .buffered, defer: false) +// window.isReleasedWhenClosed = false +// window.center() +// window.setFrameAutosaveName("Main Window") +// window.contentView = NSHostingView(rootView: contentView) +//// window.title = "Gemini" +//// let toolbar = NSToolbar() +//// toolbar.delegate = self +//// toolbar.displayMode = .iconAndLabel +//// window.toolbar = toolbar +// window.makeKeyAndOrderFront(nil) +// +// toolbarTextField = NSTextField(string: navigationManager.currentURL.absoluteString) +// toolbarTextField.delegate = self +// +// toolbarTextField.widthAnchor.constraint(equalToConstant: 250).isActive = true +// +// toolbarUpdater = navigationManager.$currentURL.sink(receiveValue: { (newValue) in +// self.toolbarTextField.stringValue = newValue.absoluteString +// }) + + let wc = BrowserWindowController() + windowControllers.append(wc) + wc.showWindow(nil) } func applicationWillTerminate(_ aNotification: Notification) { @@ -39,3 +62,41 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + +//extension AppDelegate: NSToolbarDelegate { +// func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { +// return [ +// .flexibleSpace, +// .urlField, +// ] +// } +// +// func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { +// return [ +// .flexibleSpace, +// .urlField, +// .flexibleSpace, +// ] +// } +// +// func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? { +// if itemIdentifier == .urlField { +// let item = NSToolbarItem(itemIdentifier: .urlField) +// item.label = "URL" +// item.view = toolbarTextField +// return item +// } else { +// return NSToolbarItem(itemIdentifier: .urlField) +// } +// } +//} +// +//extension AppDelegate: NSTextFieldDelegate { +// func controlTextDidEndEditing(_ obj: Notification) { +// print("did end edting, new text: '\(toolbarTextField.stringValue)'") +// } +//} +// +//extension NSToolbarItem.Identifier { +// static let urlField = NSToolbarItem.Identifier("GeminiUrlField") +//} diff --git a/Gemini/Base.lproj/Main.storyboard b/Gemini/Base.lproj/Main.storyboard deleted file mode 100644 index 3e6934d..0000000 --- a/Gemini/Base.lproj/Main.storyboard +++ /dev/null @@ -1,683 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - Default - - - - - - - Left to Right - - - - - - - Right to Left - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Gemini/BrowserWindowController.swift b/Gemini/BrowserWindowController.swift new file mode 100644 index 0000000..219351c --- /dev/null +++ b/Gemini/BrowserWindowController.swift @@ -0,0 +1,149 @@ +// +// BrowserWindowController.swift +// Gemini +// +// Created by Shadowfacts on 7/15/20. +// + +import Cocoa +import SwiftUI +import Combine + +class BrowserWindowController: NSWindowController { + + @IBOutlet weak var toolbar: NSToolbar! + + private var locationTextField: NSTextField? + private var locationUpdater: Cancellable? + + let navigator = NavigationManager(url: URL(string: "gemini://gemini.circumlunar.space/")!) + + convenience init() { + self.init(windowNibName: "BrowserWindowController") + } + + override func windowDidLoad() { + super.windowDidLoad() + + contentViewController = NSHostingController(rootView: ContentView(navigator: navigator)) + + locationUpdater = navigator.$currentURL.sink(receiveValue: self.updateLocationField(_:)) + } + + private func updateLocationField(_ newValue: URL) { + locationTextField?.stringValue = newValue.absoluteString + } + +} + +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) + item.image = NSImage(systemSymbolName: "chevron.left", accessibilityDescription: "Go Back") + 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) + item.image = NSImage(systemSymbolName: "chevron.right", accessibilityDescription: "Go Forward") + 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 { + 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) + } +} diff --git a/Gemini/BrowserWindowController.xib b/Gemini/BrowserWindowController.xib new file mode 100644 index 0000000..4e89cda --- /dev/null +++ b/Gemini/BrowserWindowController.xib @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gemini/ContentView.swift b/Gemini/ContentView.swift index fe8a64a..96db521 100644 --- a/Gemini/ContentView.swift +++ b/Gemini/ContentView.swift @@ -17,22 +17,7 @@ struct ContentView: View { @State var errorMessage: String? var body: some View { - VStack { - HStack { - Button(action: navigator.back) { - Image(systemName: "chevron.left") - }.disabled(navigator.backStack.isEmpty) - Button(action: navigator.forward) { - Image(systemName: "chevron.right") - }.disabled(navigator.forwardStack.isEmpty) - TextField("URL", text: Binding(get: { - self.navigator.currentURL.absoluteString - }, set: { (newValue) in - self.navigator.currentURL = URL(string: newValue)! - })) - }.padding([.leading, .trailing]) - mainView.frame(maxWidth: .infinity, maxHeight: .infinity) - } + mainView .frame(minWidth: 480, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity) .onReceive(navigator.$currentURL, perform: self.urlChanged) } diff --git a/Gemini/Info.plist b/Gemini/Info.plist index cfbbdb7..e817063 100644 --- a/Gemini/Info.plist +++ b/Gemini/Info.plist @@ -2,6 +2,8 @@ + NSMainNibFile + MainMenu CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -22,8 +24,6 @@ 1 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) - NSMainStoryboardFile - Main NSPrincipalClass NSApplication diff --git a/Gemini/MainMenu.xib b/Gemini/MainMenu.xib new file mode 100644 index 0000000..b14f475 --- /dev/null +++ b/Gemini/MainMenu.xib @@ -0,0 +1,423 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gemini/NavigationManager.swift b/Gemini/NavigationManager.swift index be0731f..9c608f2 100644 --- a/Gemini/NavigationManager.swift +++ b/Gemini/NavigationManager.swift @@ -7,7 +7,7 @@ import Foundation -class NavigationManager: ObservableObject { +class NavigationManager: NSObject, ObservableObject { @Published var currentURL: URL @Published var backStack = [URL]() @@ -23,13 +23,13 @@ class NavigationManager: ObservableObject { forwardStack = [] } - func back() { + @objc func back() { guard !backStack.isEmpty else { return } forwardStack.insert(currentURL, at: 0) currentURL = backStack.removeLast() } - func forward() { + @objc func forward() { guard !forwardStack.isEmpty else { return } backStack.append(currentURL) currentURL = forwardStack.removeFirst() diff --git a/GeminiRenderer/DocumentView.swift b/GeminiRenderer/DocumentView.swift index ef85aef..070799b 100644 --- a/GeminiRenderer/DocumentView.swift +++ b/GeminiRenderer/DocumentView.swift @@ -25,7 +25,7 @@ public struct DocumentView: View { ForEach(blocks.indices) { (index) in RenderingBlockView(block: blocks[index], changeURL: changeURL) } - }.padding() + }.padding([.leading, .trailing, .bottom]) } } }