Feature: Adds open in background (Shift+Cmd)
This commit is contained in:
@@ -59,9 +59,11 @@ extension BrowserViewController: ShortcutResponder
|
||||
guard let press = presses.first else { return }
|
||||
|
||||
if let key = press.key {
|
||||
if key.modifierFlags == [.command] {
|
||||
let isDown = press.phase == .began || press.phase == .changed || press.phase == .stationary
|
||||
let isDown = press.phase == .began || press.phase == .changed || press.phase == .stationary
|
||||
if key.keyCode == .keyboardLeftGUI || key.keyCode == .keyboardRightGUI {
|
||||
self.commandKeyHeld = isDown
|
||||
} else if key.keyCode == .keyboardLeftShift || key.keyCode == .keyboardRightShift {
|
||||
self.shiftKeyHeld = isDown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,8 +78,7 @@ extension BrowserViewController: WKNavigationDelegate, WKUIDelegate
|
||||
decisionHandler(.cancel, preferences)
|
||||
|
||||
// Start navigation in a new tab
|
||||
let tab = tabController.createNewTab(url: navigationAction.request.url)
|
||||
self.tab = tab
|
||||
createNewTab(withURL: navigationAction.request.url, loadInBackground: self.shiftKeyHeld)
|
||||
|
||||
// Reset this flag.
|
||||
commandKeyHeld = false
|
||||
@@ -140,16 +139,23 @@ extension BrowserViewController: WKNavigationDelegate, WKUIDelegate
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: .off) { [unowned self] _ in
|
||||
let newTab = tabController.createNewTab(url: elementInfo.linkURL)
|
||||
self.tab = newTab
|
||||
self.createNewTab(withURL: elementInfo.linkURL, loadInBackground: false)
|
||||
}
|
||||
|
||||
let openInBackground = UIAction(title: "Open in Background",
|
||||
image: UIImage(systemName: "plus.rectangle.on.rectangle"),
|
||||
identifier: nil,
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: .off) { [unowned self] _ in
|
||||
self.createNewTab(withURL: elementInfo.linkURL, loadInBackground: true)
|
||||
}
|
||||
|
||||
return UIMenu(title: elementInfo.linkURL?.absoluteString ?? "Link",
|
||||
image: nil,
|
||||
identifier: nil,
|
||||
options: .displayInline,
|
||||
children: [ openInNewTab ] + menuElements)
|
||||
children: [ openInNewTab, openInBackground ] + menuElements)
|
||||
}
|
||||
|
||||
completionHandler(menuConfig)
|
||||
|
||||
@@ -26,14 +26,13 @@ class BrowserViewController: UIViewController
|
||||
|
||||
override var canBecomeFirstResponder: Bool { true }
|
||||
|
||||
private var titleObservation: NSKeyValueObservation?
|
||||
private var loadingObservation: NSKeyValueObservation?
|
||||
private var backButtonObservation: NSKeyValueObservation?
|
||||
private var forwardButtonObservation: NSKeyValueObservation?
|
||||
private var hasSecureContentObservation: NSKeyValueObservation?
|
||||
private var activeTabObservation: AnyCancellable?
|
||||
private var faviconObservation: AnyCancellable?
|
||||
|
||||
internal var shiftKeyHeld: Bool = false
|
||||
internal var commandKeyHeld: Bool = false
|
||||
internal var windowButtonHeld: Bool {
|
||||
get { toolbarController.newTabButton.isTracking }
|
||||
@@ -65,6 +64,8 @@ class BrowserViewController: UIViewController
|
||||
self.tabBarViewController = TabBarViewController(tabController: tabController)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
self.tabController.controllerDelegate = self
|
||||
|
||||
addChild(toolbarController)
|
||||
addChild(findOnPageController)
|
||||
addChild(tabBarViewController)
|
||||
@@ -168,11 +169,11 @@ class BrowserViewController: UIViewController
|
||||
alert.dismiss(animated: true, completion: nil)
|
||||
}))
|
||||
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [unowned self] _ in
|
||||
alert.dismiss(animated: true, completion: nil)
|
||||
|
||||
// Clears out the error state
|
||||
toolbarController.urlBar.loadProgress = .complete
|
||||
self.toolbarController.urlBar.loadProgress = .complete
|
||||
}))
|
||||
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
@@ -247,13 +248,13 @@ class BrowserViewController: UIViewController
|
||||
DispatchQueue.main.async {
|
||||
documentControls.dismiss(animated: true, completion: nil)
|
||||
|
||||
let readableViewController = ReaderViewController(readableHTMLString: string, baseURL: tab.bridge.webView.url)
|
||||
readableViewController.title = tab.bridge.webView.title
|
||||
readableViewController.darkModeEnabled = tab.bridge.darkModeEnabled
|
||||
let readableViewController = ReaderViewController(readableHTMLString: string, baseURL: self.tab.bridge.webView.url)
|
||||
readableViewController.title = self.tab.bridge.webView.title
|
||||
readableViewController.darkModeEnabled = self.tab.bridge.darkModeEnabled
|
||||
readableViewController.delegate = self
|
||||
|
||||
let navigationController = UINavigationController(rootViewController: readableViewController)
|
||||
present(navigationController, animated: true, completion: nil)
|
||||
self.present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}, for: .touchUpInside)
|
||||
@@ -310,12 +311,16 @@ class BrowserViewController: UIViewController
|
||||
}
|
||||
|
||||
// Show tab bar view?
|
||||
browserView.tabBarViewVisible = tabController.tabs.count > 1
|
||||
self.updateTabBarVisibility()
|
||||
})
|
||||
|
||||
self.view = browserView
|
||||
}
|
||||
|
||||
internal func updateTabBarVisibility() {
|
||||
browserView.tabBarViewVisible = tabController.tabs.count > 1
|
||||
}
|
||||
|
||||
internal func showShareSheetForCurrentURL(fromViewController: UIViewController?) {
|
||||
guard let url = self.webView.url else { return }
|
||||
|
||||
@@ -455,9 +460,6 @@ class BrowserViewController: UIViewController
|
||||
|
||||
// Title observer
|
||||
updateTitleAndURL(forWebView: webView)
|
||||
titleObservation = webView.observe(\.title, changeHandler: { [unowned self] (webView, observedChange) in
|
||||
self.updateTitleAndURL(forWebView: webView)
|
||||
})
|
||||
|
||||
// Back/forward observer
|
||||
toolbarController.backButton.isEnabled = webView.canGoBack
|
||||
@@ -476,12 +478,6 @@ class BrowserViewController: UIViewController
|
||||
browserView.titlebarView.showsSecurityIndicator = webView.hasOnlySecureContent
|
||||
})
|
||||
|
||||
// Favicon observation
|
||||
faviconObservation = tab.$favicon.receive(on: DispatchQueue.main)
|
||||
.sink { [unowned self] _ in
|
||||
updateTitleAndURL(forWebView: webView)
|
||||
}
|
||||
|
||||
// Script blocker button
|
||||
updateScriptBlockerButton()
|
||||
|
||||
@@ -527,13 +523,26 @@ class BrowserViewController: UIViewController
|
||||
iconView.isEnabled = (webView.url != nil)
|
||||
}
|
||||
|
||||
public func createNewTab(withURL url: URL?) {
|
||||
@discardableResult
|
||||
public func createNewTab(withURL url: URL?, loadInBackground: Bool = false) -> Tab {
|
||||
let newTab = tabController.createNewTab(url: url)
|
||||
self.tab = newTab
|
||||
|
||||
if url == nil && traitCollection.userInterfaceIdiom == .mac {
|
||||
self.toolbarController.urlBar.textField.becomeFirstResponder()
|
||||
if !loadInBackground {
|
||||
self.tab = newTab
|
||||
|
||||
if url == nil && traitCollection.userInterfaceIdiom == .mac {
|
||||
self.toolbarController.urlBar.textField.becomeFirstResponder()
|
||||
}
|
||||
} else {
|
||||
// Send this message to get it to load NOW, instead of waiting for it to show up
|
||||
// in the view hierarchy.
|
||||
tab.webView.didMoveToWindow()
|
||||
|
||||
// Update tab bar now
|
||||
updateTabBarVisibility()
|
||||
}
|
||||
|
||||
return newTab
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,3 +670,19 @@ extension BrowserViewController: MFMailComposeViewControllerDelegate
|
||||
controller.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension BrowserViewController: TabControllerDelegate
|
||||
{
|
||||
func tabController(_ controller: TabController, didUpdateTitle: String, forTab tab: Tab) {
|
||||
updateTitleAndURL(forWebView: tab.webView)
|
||||
|
||||
// Fetch favicon in background, if applicable
|
||||
if tab.favicon == nil, let url = tab.webView.url {
|
||||
tab.updateFaviconForURL(url)
|
||||
}
|
||||
}
|
||||
|
||||
func tabController(_ controller: TabController, didUpdateFavicon: UIImage?, forTab tab: Tab) {
|
||||
updateTitleAndURL(forWebView: tab.webView)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +70,9 @@ class Tab: NSObject, SBRProcessBundleBridgeDelegate
|
||||
public var allowedScriptOrigins = Set<String>()
|
||||
public var blockedScriptOrigins = Set<String>()
|
||||
|
||||
private var titleObservation: NSKeyValueObservation?
|
||||
private var urlObservation: NSKeyValueObservation?
|
||||
public var titleObservation: NSKeyValueObservation?
|
||||
public var urlObservation: NSKeyValueObservation?
|
||||
public var faviconObservation: AnyCancellable?
|
||||
|
||||
convenience init(policyManager: ResourcePolicyManager) {
|
||||
self.init(url: nil, policyManager: policyManager, webViewConfiguration: nil)
|
||||
|
||||
@@ -30,7 +30,7 @@ class TabBarViewController: UIViewController, TabBarViewDataSource, TabBarViewDe
|
||||
tabBarView.reloadTabs()
|
||||
|
||||
tabObserver = tabController.$tabs.sink(receiveValue: { [unowned self] (newTabs: [Tab]) in
|
||||
DispatchQueue.main.async { tabBarView.reloadTabs() }
|
||||
DispatchQueue.main.async { self.tabBarView.reloadTabs() }
|
||||
})
|
||||
|
||||
activeTabIndexObserver = tabController.$activeTabIndex.sink(receiveValue: { [unowned self] (activeIndex: Int) in
|
||||
|
||||
@@ -7,12 +7,18 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol TabControllerDelegate: AnyObject {
|
||||
func tabController(_ controller: TabController, didUpdateTitle: String, forTab: Tab)
|
||||
func tabController(_ controller: TabController, didUpdateFavicon: UIImage?, forTab: Tab)
|
||||
}
|
||||
|
||||
class TabController
|
||||
{
|
||||
@Published var tabs: [Tab] = []
|
||||
@Published var activeTabIndex: Int = 0
|
||||
|
||||
var policyManager = ResourcePolicyManager()
|
||||
weak var controllerDelegate: TabControllerDelegate?
|
||||
|
||||
init() {
|
||||
// TODO: load tabs from disk.
|
||||
@@ -39,6 +45,20 @@ class TabController
|
||||
tabs.append(tab)
|
||||
}
|
||||
|
||||
// Title observation
|
||||
tab.titleObservation = tab.webView.observe(\.title, changeHandler: { [weak tab, weak self] (webView, change) in
|
||||
if let tab = tab, let self = self, let delegate = self.controllerDelegate {
|
||||
delegate.tabController(self, didUpdateTitle: webView.title ?? "", forTab: tab)
|
||||
}
|
||||
})
|
||||
|
||||
// Favicon Observation
|
||||
tab.faviconObservation = tab.$favicon.receive(on: RunLoop.main).sink { [weak tab, weak self] val in
|
||||
if let tab = tab, let self = self, let delegate = self.controllerDelegate {
|
||||
delegate.tabController(self, didUpdateFavicon: val, forTab: tab)
|
||||
}
|
||||
}
|
||||
|
||||
return tab
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user