|
|
@@ -27,6 +27,31 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
private let initialURL: URL
|
|
|
private var didLoadInitialURL = false
|
|
|
|
|
|
+ // Toolbar UI (top navigation bar)
|
|
|
+ private var backButton: NSButton!
|
|
|
+ private var forwardButton: NSButton!
|
|
|
+ private var reloadButton: NSButton!
|
|
|
+ private var pinButton: NSButton!
|
|
|
+ private var homeButton: NSButton!
|
|
|
+ private var gridButton: NSButton!
|
|
|
+ private var openInBrowserButton: NSButton!
|
|
|
+ private var handButton: NSButton!
|
|
|
+ private var downloadButton: NSButton!
|
|
|
+ private var moreButton: NSButton!
|
|
|
+ private var urlField: NSTextField!
|
|
|
+ private var isUpdatingURLField = false
|
|
|
+ private var isPinnedOnTop = false
|
|
|
+ private var isGestureModeEnabled = true
|
|
|
+
|
|
|
+ private func setToggledStyle(_ button: NSButton, isOn: Bool) {
|
|
|
+ button.layer?.backgroundColor = isOn
|
|
|
+ ? NSColor.systemBlue.withAlphaComponent(0.28).cgColor
|
|
|
+ : NSColor.white.withAlphaComponent(0.06).cgColor
|
|
|
+ button.layer?.borderColor = isOn
|
|
|
+ ? NSColor.systemBlue.withAlphaComponent(0.55).cgColor
|
|
|
+ : NSColor.white.withAlphaComponent(0.10).cgColor
|
|
|
+ }
|
|
|
+
|
|
|
init(initialURL: URL, windowTitle: String?) {
|
|
|
self.initialURL = initialURL
|
|
|
|
|
|
@@ -42,15 +67,242 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
)
|
|
|
window.title = windowTitle ?? "Browser"
|
|
|
window.isReleasedWhenClosed = false
|
|
|
- window.contentView = webView
|
|
|
+
|
|
|
+ // Build UI: top toolbar + web view underneath.
|
|
|
+ let containerView = NSView(frame: .zero)
|
|
|
+ containerView.wantsLayer = true
|
|
|
+ containerView.layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
+ window.contentView = containerView
|
|
|
+
|
|
|
+ let toolbarHeight: CGFloat = 46
|
|
|
+ let toolbarContainer = NSView()
|
|
|
+ toolbarContainer.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ toolbarContainer.wantsLayer = true
|
|
|
+ toolbarContainer.layer?.cornerRadius = 14
|
|
|
+ toolbarContainer.layer?.masksToBounds = true
|
|
|
+ containerView.addSubview(toolbarContainer)
|
|
|
+
|
|
|
+ // Blurred background like a modern browser toolbar.
|
|
|
+ let toolbarBlur = NSVisualEffectView()
|
|
|
+ toolbarBlur.material = .hudWindow
|
|
|
+ toolbarBlur.blendingMode = .behindWindow
|
|
|
+ toolbarBlur.state = .active
|
|
|
+ toolbarBlur.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ toolbarContainer.addSubview(toolbarBlur)
|
|
|
+
|
|
|
+ let toolbarView = NSStackView()
|
|
|
+ toolbarView.orientation = .horizontal
|
|
|
+ toolbarView.alignment = .centerY
|
|
|
+ toolbarView.spacing = 10
|
|
|
+ toolbarView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ toolbarContainer.addSubview(toolbarView)
|
|
|
+
|
|
|
+ toolbarBlur.wantsLayer = true
|
|
|
+ toolbarBlur.layer?.cornerRadius = 14
|
|
|
+ toolbarBlur.layer?.masksToBounds = true
|
|
|
+
|
|
|
+ func makeIconButton(symbolName: String, accessibility: String, action: Selector) -> NSButton {
|
|
|
+ let button = NSButton()
|
|
|
+ button.image = NSImage(
|
|
|
+ systemSymbolName: symbolName,
|
|
|
+ accessibilityDescription: accessibility
|
|
|
+ )
|
|
|
+ button.image?.isTemplate = true
|
|
|
+ button.isBordered = false
|
|
|
+ button.contentTintColor = .white
|
|
|
+ button.setButtonType(.momentaryChange)
|
|
|
+ button.action = action
|
|
|
+ button.setButtonType(.momentaryChange)
|
|
|
+
|
|
|
+ // Subtle circular hit target like the screenshot.
|
|
|
+ button.wantsLayer = true
|
|
|
+ button.layer?.cornerRadius = 11
|
|
|
+ button.layer?.masksToBounds = true
|
|
|
+ button.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.06).cgColor
|
|
|
+ button.layer?.borderColor = NSColor.white.withAlphaComponent(0.10).cgColor
|
|
|
+ button.layer?.borderWidth = 1
|
|
|
+
|
|
|
+ button.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ button.widthAnchor.constraint(equalToConstant: 22).isActive = true
|
|
|
+ button.heightAnchor.constraint(equalToConstant: 22).isActive = true
|
|
|
+
|
|
|
+ button.toolTip = accessibility
|
|
|
+ return button
|
|
|
+ }
|
|
|
+
|
|
|
+ // Back
|
|
|
+ backButton = makeIconButton(
|
|
|
+ symbolName: "chevron.left",
|
|
|
+ accessibility: "Back",
|
|
|
+ action: #selector(goBack)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Forward
|
|
|
+ forwardButton = makeIconButton(
|
|
|
+ symbolName: "chevron.right",
|
|
|
+ accessibility: "Forward",
|
|
|
+ action: #selector(goForward)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Reload
|
|
|
+ reloadButton = makeIconButton(
|
|
|
+ symbolName: "arrow.clockwise",
|
|
|
+ accessibility: "Reload",
|
|
|
+ action: #selector(reloadPage)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Pin (always on top)
|
|
|
+ pinButton = makeIconButton(
|
|
|
+ symbolName: "pin",
|
|
|
+ accessibility: "Pin (Always on Top)",
|
|
|
+ action: #selector(togglePinOnTop)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Home
|
|
|
+ homeButton = makeIconButton(
|
|
|
+ symbolName: "house",
|
|
|
+ accessibility: "Home",
|
|
|
+ action: #selector(goHome)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Grid (show launcher)
|
|
|
+ gridButton = makeIconButton(
|
|
|
+ symbolName: "square.grid.2x2",
|
|
|
+ accessibility: "Show Launcher",
|
|
|
+ action: #selector(showLauncher)
|
|
|
+ )
|
|
|
+
|
|
|
+ // URL field
|
|
|
+ urlField = NSTextField()
|
|
|
+ urlField.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ urlField.action = #selector(urlFieldDidSubmit)
|
|
|
+ urlField.font = NSFont.systemFont(ofSize: 13, weight: .regular)
|
|
|
+ urlField.textColor = .white
|
|
|
+ urlField.alignment = .left
|
|
|
+ urlField.isEditable = true
|
|
|
+ urlField.isBordered = false
|
|
|
+ urlField.drawsBackground = false
|
|
|
+ urlField.wantsLayer = true
|
|
|
+ urlField.layer?.backgroundColor = NSColor.clear.cgColor
|
|
|
+
|
|
|
+ // URL "pill" wrapper.
|
|
|
+ let urlWrapperView = NSView()
|
|
|
+ urlWrapperView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ urlWrapperView.wantsLayer = true
|
|
|
+ urlWrapperView.layer?.cornerRadius = 10
|
|
|
+ urlWrapperView.layer?.masksToBounds = true
|
|
|
+ urlWrapperView.layer?.backgroundColor = NSColor.white.withAlphaComponent(0.06).cgColor
|
|
|
+ urlWrapperView.layer?.borderColor = NSColor.white.withAlphaComponent(0.12).cgColor
|
|
|
+ urlWrapperView.layer?.borderWidth = 1
|
|
|
+
|
|
|
+ // Open in external browser
|
|
|
+ openInBrowserButton = makeIconButton(
|
|
|
+ symbolName: "arrow.up.right.square",
|
|
|
+ accessibility: "Open in Browser",
|
|
|
+ action: #selector(openInBrowser)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Hand tool (toggle navigation gestures)
|
|
|
+ handButton = makeIconButton(
|
|
|
+ symbolName: "hand.raised",
|
|
|
+ accessibility: "Gesture Mode",
|
|
|
+ action: #selector(toggleGestureMode)
|
|
|
+ )
|
|
|
+
|
|
|
+ // Download (save as PDF)
|
|
|
+ downloadButton = makeIconButton(
|
|
|
+ symbolName: "arrow.down.circle",
|
|
|
+ accessibility: "Download PDF",
|
|
|
+ action: #selector(downloadPDF)
|
|
|
+ )
|
|
|
+
|
|
|
+ // More menu
|
|
|
+ moreButton = makeIconButton(
|
|
|
+ symbolName: "ellipsis.vertical",
|
|
|
+ accessibility: "More",
|
|
|
+ action: #selector(showMoreMenu)
|
|
|
+ )
|
|
|
+
|
|
|
+ toolbarView.addArrangedSubview(backButton)
|
|
|
+ toolbarView.addArrangedSubview(forwardButton)
|
|
|
+ toolbarView.addArrangedSubview(reloadButton)
|
|
|
+ toolbarView.addArrangedSubview(urlWrapperView)
|
|
|
+ toolbarView.addArrangedSubview(pinButton)
|
|
|
+ toolbarView.addArrangedSubview(homeButton)
|
|
|
+ toolbarView.addArrangedSubview(gridButton)
|
|
|
+ toolbarView.addArrangedSubview(openInBrowserButton)
|
|
|
+ toolbarView.addArrangedSubview(handButton)
|
|
|
+ toolbarView.addArrangedSubview(downloadButton)
|
|
|
+ toolbarView.addArrangedSubview(moreButton)
|
|
|
+
|
|
|
+ // Give the URL field more space than the icon buttons.
|
|
|
+ urlWrapperView.setContentHuggingPriority(.defaultLow, for: .horizontal)
|
|
|
+ urlWrapperView.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
|
|
+
|
|
|
+ urlWrapperView.addSubview(urlField)
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
+ urlField.leadingAnchor.constraint(equalTo: urlWrapperView.leadingAnchor, constant: 12),
|
|
|
+ urlField.trailingAnchor.constraint(equalTo: urlWrapperView.trailingAnchor, constant: -12),
|
|
|
+ urlField.topAnchor.constraint(equalTo: urlWrapperView.topAnchor, constant: 6),
|
|
|
+ urlField.bottomAnchor.constraint(equalTo: urlWrapperView.bottomAnchor, constant: -6),
|
|
|
+ urlWrapperView.heightAnchor.constraint(equalToConstant: 28),
|
|
|
+ ])
|
|
|
+
|
|
|
+ // Web view below toolbar.
|
|
|
+ webView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
+ webView.setValue(false, forKey: "drawsBackground")
|
|
|
+
|
|
|
+ containerView.addSubview(webView)
|
|
|
+
|
|
|
+ NSLayoutConstraint.activate([
|
|
|
+ toolbarContainer.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 10),
|
|
|
+ toolbarContainer.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -10),
|
|
|
+ toolbarContainer.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10),
|
|
|
+ toolbarContainer.heightAnchor.constraint(equalToConstant: toolbarHeight),
|
|
|
+
|
|
|
+ toolbarBlur.leadingAnchor.constraint(equalTo: toolbarContainer.leadingAnchor),
|
|
|
+ toolbarBlur.trailingAnchor.constraint(equalTo: toolbarContainer.trailingAnchor),
|
|
|
+ toolbarBlur.topAnchor.constraint(equalTo: toolbarContainer.topAnchor),
|
|
|
+ toolbarBlur.bottomAnchor.constraint(equalTo: toolbarContainer.bottomAnchor),
|
|
|
+
|
|
|
+ toolbarView.leadingAnchor.constraint(equalTo: toolbarContainer.leadingAnchor, constant: 12),
|
|
|
+ toolbarView.trailingAnchor.constraint(equalTo: toolbarContainer.trailingAnchor, constant: -12),
|
|
|
+ toolbarView.topAnchor.constraint(equalTo: toolbarContainer.topAnchor, constant: 6),
|
|
|
+ toolbarView.bottomAnchor.constraint(equalTo: toolbarContainer.bottomAnchor, constant: -6),
|
|
|
+
|
|
|
+ webView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
|
|
|
+ webView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
|
|
|
+ webView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
|
|
+ webView.topAnchor.constraint(equalTo: toolbarContainer.bottomAnchor, constant: 0),
|
|
|
+ ])
|
|
|
|
|
|
super.init(window: window)
|
|
|
|
|
|
window.delegate = self
|
|
|
+
|
|
|
+ // Safe to reference `self` only after super.init.
|
|
|
+ backButton.target = self
|
|
|
+ forwardButton.target = self
|
|
|
+ reloadButton.target = self
|
|
|
+ pinButton.target = self
|
|
|
+ homeButton.target = self
|
|
|
+ gridButton.target = self
|
|
|
+ openInBrowserButton.target = self
|
|
|
+ handButton.target = self
|
|
|
+ downloadButton.target = self
|
|
|
+ moreButton.target = self
|
|
|
+ urlField.delegate = self
|
|
|
+ urlField.target = self
|
|
|
+
|
|
|
webView.uiDelegate = self
|
|
|
webView.navigationDelegate = self
|
|
|
webView.allowsBackForwardNavigationGestures = true
|
|
|
- webView.setValue(false, forKey: "drawsBackground")
|
|
|
+ isGestureModeEnabled = true
|
|
|
+
|
|
|
+ // Initialize toolbar state.
|
|
|
+ setToggledStyle(pinButton, isOn: isPinnedOnTop)
|
|
|
+ setToggledStyle(handButton, isOn: isGestureModeEnabled)
|
|
|
+ updateToolbarState()
|
|
|
}
|
|
|
|
|
|
@available(*, unavailable)
|
|
|
@@ -68,6 +320,202 @@ final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
|
|
|
func windowWillClose(_ notification: Notification) {
|
|
|
onClose?()
|
|
|
}
|
|
|
+
|
|
|
+ @objc private func goBack() {
|
|
|
+ webView.goBack()
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func goForward() {
|
|
|
+ webView.goForward()
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func reloadPage() {
|
|
|
+ webView.reload()
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func openInBrowser() {
|
|
|
+ guard let url = webView.url else { return }
|
|
|
+ NSWorkspace.shared.open(url)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func goHome() {
|
|
|
+ webView.load(URLRequest(url: initialURL))
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func showLauncher() {
|
|
|
+ // Bring the main app window (launcher) to front.
|
|
|
+ if let main = NSApp.windows.first {
|
|
|
+ main.makeKeyAndOrderFront(nil)
|
|
|
+ }
|
|
|
+ NSApp.activate(ignoringOtherApps: true)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func togglePinOnTop() {
|
|
|
+ isPinnedOnTop.toggle()
|
|
|
+ window?.level = isPinnedOnTop ? .floating : .normal
|
|
|
+ if let button = pinButton {
|
|
|
+ let symbol = isPinnedOnTop ? "pin.fill" : "pin"
|
|
|
+ button.image = NSImage(systemSymbolName: symbol, accessibilityDescription: "Pin (Always on Top)")
|
|
|
+ button.image?.isTemplate = true
|
|
|
+ setToggledStyle(button, isOn: isPinnedOnTop)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func toggleGestureMode() {
|
|
|
+ isGestureModeEnabled.toggle()
|
|
|
+ webView.allowsBackForwardNavigationGestures = isGestureModeEnabled
|
|
|
+ if let button = handButton {
|
|
|
+ let symbol = isGestureModeEnabled ? "hand.raised.fill" : "hand.raised"
|
|
|
+ button.image = NSImage(systemSymbolName: symbol, accessibilityDescription: "Gesture Mode")
|
|
|
+ button.image?.isTemplate = true
|
|
|
+ setToggledStyle(button, isOn: isGestureModeEnabled)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func downloadPDF() {
|
|
|
+ guard let url = webView.url else { return }
|
|
|
+
|
|
|
+ let host = (url.host?.isEmpty == false) ? (url.host ?? "page") : "page"
|
|
|
+ let dateFormatter = DateFormatter()
|
|
|
+ dateFormatter.dateFormat = "yyyy-MM-dd_HH-mm-ss"
|
|
|
+ let fileName = "\(host)_\(dateFormatter.string(from: Date())).pdf"
|
|
|
+
|
|
|
+ guard let downloads = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first else {
|
|
|
+ showAlert(title: "Download Failed", message: "Couldn’t find your Downloads folder.")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ let targetURL = downloads.appendingPathComponent(fileName)
|
|
|
+
|
|
|
+ if #available(macOS 11.0, *) {
|
|
|
+ webView.createPDF(configuration: WKPDFConfiguration()) { [weak self] result in
|
|
|
+ guard let self else { return }
|
|
|
+ switch result {
|
|
|
+ case .success(let data):
|
|
|
+ do {
|
|
|
+ try data.write(to: targetURL, options: .atomic)
|
|
|
+ self.showAlert(title: "Downloaded", message: "Saved PDF to Downloads:\n\(fileName)")
|
|
|
+ } catch {
|
|
|
+ self.showAlert(title: "Download Failed", message: "Couldn’t save the PDF.")
|
|
|
+ }
|
|
|
+ case .failure:
|
|
|
+ self.showAlert(title: "Download Failed", message: "Couldn’t generate a PDF for this page.")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ showAlert(title: "Unavailable", message: "PDF download requires macOS 11 or newer.")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func showMoreMenu() {
|
|
|
+ let menu = NSMenu()
|
|
|
+
|
|
|
+ let copyURL = NSMenuItem(title: "Copy URL", action: #selector(copyCurrentURL), keyEquivalent: "c")
|
|
|
+ copyURL.keyEquivalentModifierMask = [.command]
|
|
|
+ menu.addItem(copyURL)
|
|
|
+
|
|
|
+ menu.addItem(NSMenuItem.separator())
|
|
|
+
|
|
|
+ let reload = NSMenuItem(title: "Reload", action: #selector(reloadPage), keyEquivalent: "r")
|
|
|
+ reload.keyEquivalentModifierMask = [.command]
|
|
|
+ menu.addItem(reload)
|
|
|
+
|
|
|
+ let home = NSMenuItem(title: "Home", action: #selector(goHome), keyEquivalent: "h")
|
|
|
+ home.keyEquivalentModifierMask = [.command]
|
|
|
+ menu.addItem(home)
|
|
|
+
|
|
|
+ let openExternal = NSMenuItem(title: "Open in Default Browser", action: #selector(openInBrowser), keyEquivalent: "o")
|
|
|
+ openExternal.keyEquivalentModifierMask = [.command]
|
|
|
+ menu.addItem(openExternal)
|
|
|
+
|
|
|
+ menu.addItem(NSMenuItem.separator())
|
|
|
+
|
|
|
+ let close = NSMenuItem(title: "Close Window", action: #selector(closeWindowFromMenu), keyEquivalent: "w")
|
|
|
+ close.keyEquivalentModifierMask = [.command]
|
|
|
+ menu.addItem(close)
|
|
|
+
|
|
|
+ menu.items.forEach { $0.target = self }
|
|
|
+
|
|
|
+ let location = NSPoint(x: moreButton.bounds.midX, y: moreButton.bounds.minY - 4)
|
|
|
+ menu.popUp(positioning: nil, at: location, in: moreButton)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func copyCurrentURL() {
|
|
|
+ guard let s = webView.url?.absoluteString, !s.isEmpty else { return }
|
|
|
+ NSPasteboard.general.clearContents()
|
|
|
+ NSPasteboard.general.setString(s, forType: .string)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func closeWindowFromMenu() {
|
|
|
+ window?.performClose(nil)
|
|
|
+ }
|
|
|
+
|
|
|
+ @objc private func urlFieldDidSubmit() {
|
|
|
+ loadFromURLField()
|
|
|
+ }
|
|
|
+
|
|
|
+ private func loadFromURLField() {
|
|
|
+ let raw = urlField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
+ guard !raw.isEmpty else { return }
|
|
|
+
|
|
|
+ let normalized: String
|
|
|
+ if raw.lowercased().hasPrefix("http://") || raw.lowercased().hasPrefix("https://") {
|
|
|
+ normalized = raw
|
|
|
+ } else {
|
|
|
+ normalized = "https://\(raw)"
|
|
|
+ }
|
|
|
+
|
|
|
+ guard let url = URL(string: normalized) else {
|
|
|
+ revertURLFieldToCurrentWebURL()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // Avoid the URL field update triggering other edits while we load.
|
|
|
+ isUpdatingURLField = true
|
|
|
+ webView.load(URLRequest(url: url))
|
|
|
+ isUpdatingURLField = false
|
|
|
+ }
|
|
|
+
|
|
|
+ private func revertURLFieldToCurrentWebURL() {
|
|
|
+ guard let url = webView.url?.absoluteString else { return }
|
|
|
+ isUpdatingURLField = true
|
|
|
+ urlField.stringValue = url
|
|
|
+ isUpdatingURLField = false
|
|
|
+ }
|
|
|
+
|
|
|
+ private var isURLFieldBeingEdited: Bool {
|
|
|
+ urlField.currentEditor() != nil
|
|
|
+ }
|
|
|
+
|
|
|
+ private func setURLFieldToCurrentWebURL() {
|
|
|
+ guard let url = webView.url?.absoluteString else { return }
|
|
|
+ guard !isURLFieldBeingEdited else { return }
|
|
|
+ guard !isUpdatingURLField else { return }
|
|
|
+
|
|
|
+ isUpdatingURLField = true
|
|
|
+ urlField.stringValue = url
|
|
|
+ isUpdatingURLField = false
|
|
|
+ }
|
|
|
+
|
|
|
+ private func updateToolbarState() {
|
|
|
+ backButton.isEnabled = webView.canGoBack
|
|
|
+ forwardButton.isEnabled = webView.canGoForward
|
|
|
+
|
|
|
+ // Keep reload enabled; it matches typical browser behavior.
|
|
|
+ reloadButton.isEnabled = true
|
|
|
+
|
|
|
+ // Sync URL field and window title.
|
|
|
+ setURLFieldToCurrentWebURL()
|
|
|
+ }
|
|
|
+
|
|
|
+ private func showAlert(title: String, message: String) {
|
|
|
+ let alert = NSAlert()
|
|
|
+ alert.messageText = title
|
|
|
+ alert.informativeText = message
|
|
|
+ alert.addButton(withTitle: "OK")
|
|
|
+ alert.alertStyle = .informational
|
|
|
+ alert.runModal()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
extension InAppBrowserWindowController: WKNavigationDelegate {
|
|
|
@@ -89,6 +537,8 @@ extension InAppBrowserWindowController: WKNavigationDelegate {
|
|
|
if let host = webView.url?.host, !host.isEmpty {
|
|
|
window?.title = host
|
|
|
}
|
|
|
+
|
|
|
+ updateToolbarState()
|
|
|
}
|
|
|
|
|
|
func webView(
|
|
|
@@ -118,6 +568,14 @@ extension InAppBrowserWindowController: WKNavigationDelegate {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+extension InAppBrowserWindowController: NSTextFieldDelegate {
|
|
|
+ func controlTextDidEndEditing(_ obj: Notification) {
|
|
|
+ guard obj.object as? NSTextField === urlField else { return }
|
|
|
+ guard !isUpdatingURLField else { return }
|
|
|
+ loadFromURLField()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
extension InAppBrowserWindowController: WKUIDelegate {
|
|
|
func webView(
|
|
|
_ webView: WKWebView,
|