iosWebViewFix/flutter_inappwebview_macos/macos/Classes/InAppBrowser/InAppBrowserWindow.swift

388 lines
17 KiB
Swift

//
// InAppBrowserNavigationController.swift
// flutter_inappwebview
//
// Created by Lorenzo Pichilli on 14/02/21.
//
import Foundation
struct ToolbarIdentifiers {
static let searchBar = NSToolbarItem.Identifier(rawValue: "SearchBar")
static let backButton = NSToolbarItem.Identifier(rawValue: "BackButton")
static let forwardButton = NSToolbarItem.Identifier(rawValue: "ForwardButton")
static let reloadButton = NSToolbarItem.Identifier(rawValue: "ReloadButton")
static let menuButton = NSToolbarItem.Identifier(rawValue: "MenuButton")
}
public class InAppBrowserWindow: NSWindow, NSWindowDelegate, NSToolbarDelegate, NSSearchFieldDelegate {
var searchItem: NSToolbarItem?
var backItem: NSToolbarItem?
var forwardItem: NSToolbarItem?
var reloadItem: NSToolbarItem?
var menuItem: NSToolbarItem?
var actionItems: [NSToolbarItem] = []
var reloadButton: NSButton? {
get {
return reloadItem?.view as? NSButton
}
}
var backButton: NSButton? {
get {
return backItem?.view as? NSButton
}
}
var forwardButton: NSButton? {
get {
return forwardItem?.view as? NSButton
}
}
var searchBar: NSSearchField? {
get {
if #available(macOS 11.0, *), let searchItem = searchItem as? NSSearchToolbarItem {
return searchItem.searchField
} else {
return searchItem?.view as? NSSearchField
}
}
}
var menuButton: NSPopUpButton? {
get {
return menuItem?.view as? NSPopUpButton
}
}
var browserSettings: InAppBrowserSettings?
var menuItems: [InAppBrowserMenuItem] = []
public func prepare() {
title = ""
collectionBehavior = .fullScreenPrimary
delegate = self
NotificationCenter.default.addObserver(self,
selector: #selector(onMainWindowWillClose(_:)),
name: NSWindow.willCloseNotification,
object: NSApplication.shared.mainWindow)
if #available(macOS 10.13, *) {
let windowToolbar = NSToolbar()
windowToolbar.delegate = self
if #available(macOS 11.0, *) {
searchItem = NSSearchToolbarItem(itemIdentifier: ToolbarIdentifiers.searchBar)
(searchItem as! NSSearchToolbarItem).searchField.delegate = self
toolbarStyle = .expanded
} else {
searchItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.searchBar)
let textField = NSSearchField()
textField.usesSingleLineMode = true
textField.delegate = self
searchItem?.view = textField
}
searchItem?.label = ""
windowToolbar.displayMode = .default
backItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.backButton)
backItem?.label = ""
if let webViewController = contentViewController as? InAppBrowserWebViewController {
if #available(macOS 11.0, *) {
backItem?.view = NSButton(image: NSImage(systemSymbolName: "chevron.left",
accessibilityDescription: "Go Back")!,
target: webViewController,
action: #selector(InAppBrowserWebViewController.goBack))
} else {
backItem?.view = NSButton(title: "\u{2039}",
target: webViewController,
action: #selector(InAppBrowserWebViewController.goBack))
}
}
forwardItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.forwardButton)
forwardItem?.label = ""
if let webViewController = contentViewController as? InAppBrowserWebViewController {
if #available(macOS 11.0, *) {
forwardItem?.view = NSButton(image: NSImage(systemSymbolName: "chevron.right",
accessibilityDescription: "Go Forward")!,
target: webViewController,
action: #selector(InAppBrowserWebViewController.goForward))
} else {
forwardItem?.view = NSButton(title: "\u{203A}",
target: webViewController,
action: #selector(InAppBrowserWebViewController.goForward))
}
}
reloadItem = NSToolbarItem(itemIdentifier: ToolbarIdentifiers.reloadButton)
reloadItem?.label = ""
if let webViewController = contentViewController as? InAppBrowserWebViewController {
if #available(macOS 11.0, *) {
reloadItem?.view = NSButton(image: NSImage(systemSymbolName: "arrow.counterclockwise",
accessibilityDescription: "Reload")!,
target: webViewController,
action: #selector(InAppBrowserWebViewController.reload))
} else {
reloadItem?.view = NSButton(title: "Reload",
target: webViewController,
action: #selector(InAppBrowserWebViewController.reload))
}
}
if #available(macOS 10.15, *), !menuItems.isEmpty {
menuItem = NSMenuToolbarItem(itemIdentifier: ToolbarIdentifiers.menuButton)
if let menuItem = menuItem as? NSMenuToolbarItem {
menuItem.label = ""
if #available(macOS 11.0, *) {
menuItem.image = NSImage(systemSymbolName: "ellipsis.circle",
accessibilityDescription: "Options")
menuItem.showsIndicator = true
menuItem.isBordered = true
} else {
menuItem.title = "Options"
}
let menu = NSMenu()
menuItems = menuItems.sorted(by: {$0.order ?? 0 < $1.order ?? 0})
for item in menuItems {
if !item.showAsAction {
let nsItem = NSMenuItem(title: item.title, action: #selector(InAppBrowserWebViewController.onMenuItemClicked), keyEquivalent: "")
nsItem.identifier = NSUserInterfaceItemIdentifier.init(String(item.id))
nsItem.image = item.icon
menu.addItem(nsItem)
} else {
let actionItem = NSMenuToolbarItem(itemIdentifier: NSToolbarItem.Identifier(rawValue: String(item.id)))
actionItem.label = ""
if let webViewController = contentViewController as? InAppBrowserWebViewController {
let actionButton = NSButton(title: item.title,
target: webViewController,
action: #selector(InAppBrowserWebViewController.onMenuItemClicked))
actionButton.identifier = NSUserInterfaceItemIdentifier.init(String(item.id))
actionButton.image = item.icon
actionItem.view = actionButton
}
actionItems.append(actionItem)
}
}
menuItem.menu = menu
}
}
if #available(macOS 10.14, *) {
windowToolbar.centeredItemIdentifier = ToolbarIdentifiers.searchBar
}
toolbar = windowToolbar
}
forwardButton?.isEnabled = false
backButton?.isEnabled = false
if let browserSettings = browserSettings {
if let toolbarTopFixedTitle = browserSettings.toolbarTopFixedTitle {
title = toolbarTopFixedTitle
}
if !browserSettings.hideToolbarTop {
toolbar?.isVisible = true
if browserSettings.hideUrlBar {
if #available(macOS 11.0, *) {
(searchItem as! NSSearchToolbarItem).searchField.isHidden = true
} else {
searchItem?.view?.isHidden = true
}
}
if let bgColor = browserSettings.toolbarTopBackgroundColor, !bgColor.isEmpty {
backgroundColor = NSColor(hexString: bgColor)
}
}
else {
toolbar?.isVisible = false
}
if #available(macOS 11.0, *), let windowTitlebarSeparatorStyle = browserSettings.windowTitlebarSeparatorStyle {
titlebarSeparatorStyle = windowTitlebarSeparatorStyle
}
alphaValue = browserSettings.windowAlphaValue
if let windowStyleMask = browserSettings.windowStyleMask {
styleMask = windowStyleMask
}
if let windowFrame = browserSettings.windowFrame {
setFrame(windowFrame, display: true)
}
reloadItem?.view?.isHidden = browserSettings.hideDefaultMenuItems
backItem?.view?.isHidden = browserSettings.hideDefaultMenuItems
forwardItem?.view?.isHidden = browserSettings.hideDefaultMenuItems
}
}
public func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [[ ToolbarIdentifiers.menuButton,
ToolbarIdentifiers.searchBar,
ToolbarIdentifiers.backButton,
ToolbarIdentifiers.forwardButton,
ToolbarIdentifiers.reloadButton,
.flexibleSpace ], actionItems.compactMap({ item in
return item.itemIdentifier
})].flatMap { $0 }
}
public func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return [[.flexibleSpace,
ToolbarIdentifiers.searchBar,
.flexibleSpace,
ToolbarIdentifiers.reloadButton,
ToolbarIdentifiers.backButton,
ToolbarIdentifiers.forwardButton],
actionItems.compactMap({ item in
return item.itemIdentifier
}),
[ToolbarIdentifiers.menuButton]].flatMap { $0 }
}
public func toolbar(_ toolbar: NSToolbar, itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier, willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
switch(itemIdentifier) {
case ToolbarIdentifiers.searchBar:
return searchItem
case ToolbarIdentifiers.backButton:
return backItem
case ToolbarIdentifiers.forwardButton:
return forwardItem
case ToolbarIdentifiers.reloadButton:
return reloadItem
case ToolbarIdentifiers.menuButton:
return menuItem
default:
let actionItem = actionItems.first { item in
return item.itemIdentifier == itemIdentifier
}
return actionItem
}
}
public func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
if (commandSelector == #selector(NSResponder.insertNewline(_:))) {
// ENTER key
var searchField: NSSearchField? = nil
if #available(macOS 11.0, *), let searchBar = searchItem as? NSSearchToolbarItem {
searchField = searchBar.searchField
} else if let searchBar = searchItem {
searchField = searchBar.view as? NSSearchField
}
guard let searchField,
let urlEncoded = searchField.stringValue.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let url = URL(string: urlEncoded) else {
return false
}
let request = URLRequest(url: url)
(contentViewController as? InAppBrowserWebViewController)?.webView?.load(request)
return true
}
return false
}
public func hide() {
orderOut(self)
}
public func show() {
let mainWindow = parent ?? NSApplication.shared.mainWindow
if #available(macOS 10.12, *),
!(mainWindow?.tabbedWindows?.contains(self) ?? false),
browserSettings?.windowType == .tabbed {
mainWindow?.addTabbedWindow(self, ordered: .above)
} else if !(mainWindow?.childWindows?.contains(self) ?? false) {
mainWindow?.addChildWindow(self, ordered: .above)
}
makeKeyAndOrderFront(self)
NSApplication.shared.activate(ignoringOtherApps: true)
}
public func setSettings(newSettings: InAppBrowserSettings, newSettingsMap: [String: Any]) {
if newSettingsMap["hidden"] != nil, browserSettings?.hidden != newSettings.hidden {
if newSettings.hidden {
hide()
}
else {
show()
}
}
if newSettingsMap["hideUrlBar"] != nil, browserSettings?.hideUrlBar != newSettings.hideUrlBar {
searchBar?.isHidden = newSettings.hideUrlBar
}
if newSettingsMap["hideToolbarTop"] != nil, browserSettings?.hideToolbarTop != newSettings.hideToolbarTop {
toolbar?.isVisible = !newSettings.hideToolbarTop
}
if newSettingsMap["toolbarTopBackgroundColor"] != nil, browserSettings?.toolbarTopBackgroundColor != newSettings.toolbarTopBackgroundColor {
if let bgColor = newSettings.toolbarTopBackgroundColor, !bgColor.isEmpty {
backgroundColor = NSColor(hexString: bgColor)
} else {
backgroundColor = nil
}
}
if #available(macOS 11.0, *), newSettingsMap["windowTitlebarSeparatorStyle"] != nil,
browserSettings?.windowTitlebarSeparatorStyle != newSettings.windowTitlebarSeparatorStyle {
titlebarSeparatorStyle = newSettings.windowTitlebarSeparatorStyle!
}
if newSettingsMap["windowAlphaValue"] != nil, browserSettings?.windowAlphaValue != newSettings.windowAlphaValue {
alphaValue = newSettings.windowAlphaValue
}
if newSettingsMap["windowStyleMask"] != nil, browserSettings?.windowStyleMask != newSettings.windowStyleMask {
styleMask = newSettings.windowStyleMask!
}
if newSettingsMap["windowFrame"] != nil, browserSettings?.windowFrame != newSettings.windowFrame {
setFrame(newSettings.windowFrame!, display: true)
}
if newSettingsMap["hideDefaultMenuItems"] != nil, browserSettings?.hideDefaultMenuItems != newSettings.hideDefaultMenuItems {
reloadItem?.view?.isHidden = newSettings.hideDefaultMenuItems
backItem?.view?.isHidden = newSettings.hideDefaultMenuItems
forwardItem?.view?.isHidden = newSettings.hideDefaultMenuItems
}
browserSettings = newSettings
}
public func windowWillClose(_ notification: Notification) {
dispose()
}
@objc func onMainWindowWillClose(_ notification: Notification) {
if let webViewController = contentViewController as? InAppBrowserWebViewController {
webViewController.channelDelegate?.onMainWindowWillClose()
}
}
public func dispose() {
delegate = nil
NotificationCenter.default.removeObserver(self,
name: NSWindow.willCloseNotification,
object: NSApplication.shared.mainWindow)
if let webViewController = contentViewController as? InAppBrowserWebViewController {
webViewController.dispose()
}
if #available(macOS 11.0, *) {
(searchItem as? NSSearchToolbarItem)?.searchField.delegate = nil
} else {
(searchItem?.view as? NSTextField)?.delegate = nil
searchItem?.view = nil
}
searchItem = nil
(backItem?.view as? NSButton)?.target = nil
backItem?.view = nil
backItem = nil
(forwardItem?.view as? NSButton)?.target = nil
forwardItem?.view = nil
forwardItem = nil
(reloadItem?.view as? NSButton)?.target = nil
reloadItem?.view = nil
reloadItem = nil
}
deinit {
debugPrint("InAppBrowserWindow - dealloc")
dispose()
}
}