Handle multi-button mouse events in the browser panel's WKWebView: - Mouse back button (button 3) triggers goBack(), forward button (button 4) triggers goForward(), enabling side-button navigation on mice like Logitech - Middle-click (button 2) on a link opens it in a new browser tab by hit-testing the click position via JavaScript and routing through the existing openLinkInNewTab mechanism
This commit is contained in:
parent
463c6baabb
commit
5b2be45f3a
3 changed files with 68 additions and 0 deletions
|
|
@ -107,6 +107,14 @@ struct BrowserPanelView: View {
|
|||
}) { _ in
|
||||
onRequestPanelFocus()
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: .webViewMiddleClickedLink).filter { [weak panel] note in
|
||||
guard let webView = note.object as? CmuxWebView else { return false }
|
||||
return webView === panel?.webView
|
||||
}) { note in
|
||||
if let url = note.userInfo?["url"] as? URL {
|
||||
panel.openLinkInNewTab(url: url)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
UserDefaults.standard.register(defaults: [
|
||||
BrowserSearchSettings.searchEngineKey: BrowserSearchSettings.defaultSearchEngine.rawValue,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,65 @@ final class CmuxWebView: WKWebView {
|
|||
super.mouseDown(with: event)
|
||||
}
|
||||
|
||||
// MARK: - Mouse back/forward buttons & middle-click
|
||||
|
||||
override func otherMouseDown(with event: NSEvent) {
|
||||
// Button 3 = back, button 4 = forward (multi-button mice like Logitech).
|
||||
// Consume the event so WebKit doesn't handle it.
|
||||
switch event.buttonNumber {
|
||||
case 3:
|
||||
goBack()
|
||||
return
|
||||
case 4:
|
||||
goForward()
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
super.otherMouseDown(with: event)
|
||||
}
|
||||
|
||||
override func otherMouseUp(with event: NSEvent) {
|
||||
// Middle-click (button 2) on a link opens it in a new tab.
|
||||
if event.buttonNumber == 2 {
|
||||
let point = convert(event.locationInWindow, from: nil)
|
||||
findLinkAtPoint(point) { [weak self] url in
|
||||
guard let self, let url else { return }
|
||||
NotificationCenter.default.post(
|
||||
name: .webViewMiddleClickedLink,
|
||||
object: self,
|
||||
userInfo: ["url": url]
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
super.otherMouseUp(with: event)
|
||||
}
|
||||
|
||||
/// Use JavaScript to find the nearest anchor element at the given view-local point.
|
||||
private func findLinkAtPoint(_ point: NSPoint, completion: @escaping (URL?) -> Void) {
|
||||
// WKWebView's coordinate system is flipped (origin top-left for web content).
|
||||
let flippedY = bounds.height - point.y
|
||||
let js = """
|
||||
(() => {
|
||||
let el = document.elementFromPoint(\(point.x), \(flippedY));
|
||||
while (el) {
|
||||
if (el.tagName === 'A' && el.href) return el.href;
|
||||
el = el.parentElement;
|
||||
}
|
||||
return '';
|
||||
})();
|
||||
"""
|
||||
evaluateJavaScript(js) { result, _ in
|
||||
guard let href = result as? String, !href.isEmpty,
|
||||
let url = URL(string: href) else {
|
||||
completion(nil)
|
||||
return
|
||||
}
|
||||
completion(url)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Drag-and-drop passthrough
|
||||
|
||||
// WKWebView inherently calls registerForDraggedTypes with public.text (and others).
|
||||
|
|
|
|||
|
|
@ -2489,4 +2489,5 @@ extension Notification.Name {
|
|||
static let browserDidFocusAddressBar = Notification.Name("browserDidFocusAddressBar")
|
||||
static let browserDidBlurAddressBar = Notification.Name("browserDidBlurAddressBar")
|
||||
static let webViewDidReceiveClick = Notification.Name("webViewDidReceiveClick")
|
||||
static let webViewMiddleClickedLink = Notification.Name("webViewMiddleClickedLink")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue