From a2736353449fa67dba17c89223ae65810a681eee Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Mon, 4 Nov 2024 17:32:01 +0100 Subject: [PATCH] updated requestFocus implementation, macos: implemented requestFocus and clearFocus WebView methods, implemented workaround for macos #2380 --- flutter_inappwebview/CHANGELOG.md | 5 + .../lib/in_app_webiew_example.screen.dart | 3 +- .../in_app_webview_controller.dart | 12 +- flutter_inappwebview_android/CHANGELOG.md | 1 + .../types/InAppWebViewRect.java | 107 +++++++++++++++ .../webview/WebViewChannelDelegate.java | 17 ++- .../in_app_webview_controller.dart | 8 +- flutter_inappwebview_ios/CHANGELOG.md | 1 + .../Classes/InAppWebView/InAppWebView.swift | 8 +- .../InAppWebView/WebViewChannelDelegate.swift | 6 +- .../in_app_webview_controller.dart | 8 +- flutter_inappwebview_macos/CHANGELOG.md | 2 + .../src/in_app_webview/in_app_webview.dart | 2 +- .../in_app_webview_controller.dart | 24 ++++ .../Classes/InAppWebView/InAppWebView.swift | 44 ++++++ .../InAppWebView/WebViewChannelDelegate.swift | 16 +++ .../WebViewChannelDelegateMethods.swift | 2 + .../platform_inappwebview_controller.dart | 20 ++- .../lib/src/types/focus_direction.dart | 54 ++++++++ .../lib/src/types/focus_direction.g.dart | 125 ++++++++++++++++++ .../lib/src/types/main.dart | 1 + 21 files changed, 443 insertions(+), 23 deletions(-) create mode 100644 flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/types/InAppWebViewRect.java create mode 100644 flutter_inappwebview_platform_interface/lib/src/types/focus_direction.dart create mode 100644 flutter_inappwebview_platform_interface/lib/src/types/focus_direction.g.dart diff --git a/flutter_inappwebview/CHANGELOG.md b/flutter_inappwebview/CHANGELOG.md index 0adc91e0a..8eb2eff6c 100755 --- a/flutter_inappwebview/CHANGELOG.md +++ b/flutter_inappwebview/CHANGELOG.md @@ -16,6 +16,7 @@ Implemented security features to better manage access to the native javascript b - Added `PlatformInAppLocalhostServer.onData` parameter to set a custom on data server callback - Added `javaScriptBridgeEnabled`, `javaScriptBridgeOriginAllowList`, `javaScriptBridgeForMainFrameOnly`, `pluginScriptsOriginAllowList`, `pluginScriptsForMainFrameOnly`, `javaScriptHandlersOriginAllowList`, `javaScriptHandlersForMainFrameOnly`, `scrollMultiplier` InAppWebViewSettings parameters - Added `setJavaScriptBridgeName`, `getJavaScriptBridgeName` static WebView controller methods +- Added `requestFocus` WebView method - Added `onProcessFailed` WebView event - Added `JavaScriptHandlerFunctionData` type - Deprecated `JavaScriptHandlerCallback` type in favor of `JavaScriptHandlerFunction` type @@ -27,6 +28,7 @@ Implemented security features to better manage access to the native javascript b - Added `InAppWebViewController.enableSlowWholeDocumentDraw` static method - Added `CookieManager.flush` method - Added support for `UserScript.forMainFrameOnly` parameter +- Implemented `requestFocus` WebView method - Updated UserScript at document end implementation - Updated `InAppWebViewController.takeScreenshot` implementation to support screenshot out of visible viewport when `InAppWebViewController.enableSlowWholeDocumentDraw` is called - Fixed "After dispose a InAppWebViewKeepAlive using InAppWebViewController.disposeKeepAlive. NullPointerException is thrown when main activity enter destroyed state." [#2025](https://github.com/pichillilorenzo/flutter_inappwebview/issues/2025) @@ -35,10 +37,13 @@ Implemented security features to better manage access to the native javascript b - Merged "Update Android Cookie Expiration date format to 24-hour format (HH)" [#2389](https://github.com/pichillilorenzo/flutter_inappwebview/pull/2389) (thanks to [takuyaaaaaaahaaaaaa](https://github.com/takuyaaaaaaahaaaaaa)) #### macOS and iOS Platforms +- Implemented `requestFocus` WebView method - Updated ConsoleLogJS internal PluginScript to main-frame only as using it on non-main frames could cause issues such as [#1738](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1738) - Moved `WKUserContentController` initialization on `preWKWebViewConfiguration` to fix possible `undefined is not an object (evaluating 'window.webkit.messageHandlers')` javascript error - Added support for `UserScript.allowedOriginRules` parameter - Merged "change priority of DispatchQueue" [#2322](https://github.com/pichillilorenzo/flutter_inappwebview/pull/2322) (thanks to [nnnlog](https://github.com/nnnlog)) +- macOS: Implemented also `clearFocus` WebView method +- macOS: Implemented workaround for "[macOS] Copy Shortcut does not work if TextField outside of WebView has focus" [#2380](https://github.com/pichillilorenzo/flutter_inappwebview/issues/2380) #### Windows Platform - Updated `scrollMultiplier` default value from 6 to 1 diff --git a/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart b/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart index 0a53f086c..82d17b658 100755 --- a/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart +++ b/flutter_inappwebview/example/lib/in_app_webiew_example.screen.dart @@ -1,4 +1,5 @@ import 'dart:collection'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; @@ -116,7 +117,7 @@ class _InAppWebViewExampleScreenState extends State { key: webViewKey, webViewEnvironment: webViewEnvironment, initialUrlRequest: - URLRequest(url: WebUri('https://flutter.dev')), + URLRequest(url: WebUri('https://flutter.dev')), // initialUrlRequest: // URLRequest(url: WebUri(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')), // initialFile: "assets/index.html", diff --git a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart index 0920b9685..72e9dab13 100644 --- a/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview/lib/src/in_app_webview/in_app_webview_controller.dart @@ -1,18 +1,14 @@ import 'dart:core'; -import 'dart:typed_data'; -import 'dart:ui'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; +import '../print_job/main.dart'; import '../web_message/main.dart'; import '../web_storage/web_storage.dart'; import 'android/in_app_webview_controller.dart'; import 'apple/in_app_webview_controller.dart'; -import '../print_job/main.dart'; - ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController} class InAppWebViewController { ///Use [InAppWebViewController] instead. @@ -289,7 +285,11 @@ class InAppWebViewController { platform.getHitTestResult(); ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.requestFocus} - Future requestFocus() => platform.requestFocus(); + Future requestFocus( + {FocusDirection? direction, + InAppWebViewRect? previouslyFocusedRect}) => + platform.requestFocus( + direction: direction, previouslyFocusedRect: previouslyFocusedRect); ///{@macro flutter_inappwebview_platform_interface.PlatformInAppWebViewController.clearFocus} Future clearFocus() => platform.clearFocus(); diff --git a/flutter_inappwebview_android/CHANGELOG.md b/flutter_inappwebview_android/CHANGELOG.md index f5d4e25aa..23e8baffd 100644 --- a/flutter_inappwebview_android/CHANGELOG.md +++ b/flutter_inappwebview_android/CHANGELOG.md @@ -4,6 +4,7 @@ - Added `InAppWebViewController.enableSlowWholeDocumentDraw` static method - Added `CookieManager.flush` method - Added support for `UserScript.forMainFrameOnly` parameter +- Implemented `requestFocus` WebView method - Updated UserScript at document end implementation - Updated `InAppWebViewController.takeScreenshot` implementation to support screenshot out of visible viewport when `InAppWebViewController.enableSlowWholeDocumentDraw` is called - Fixed "After dispose a InAppWebViewKeepAlive using InAppWebViewController.disposeKeepAlive. NullPointerException is thrown when main activity enter destroyed state." [#2025](https://github.com/pichillilorenzo/flutter_inappwebview/issues/2025) diff --git a/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/types/InAppWebViewRect.java b/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/types/InAppWebViewRect.java new file mode 100644 index 000000000..b297899ff --- /dev/null +++ b/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/types/InAppWebViewRect.java @@ -0,0 +1,107 @@ +package com.pichillilorenzo.flutter_inappwebview_android.types; + +import android.graphics.Rect; + +import androidx.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; + +public class InAppWebViewRect { + private double height; + private double width; + private double x; + private double y; + + public InAppWebViewRect(double height, double width, double x, double y) { + this.height = height; + this.width = width; + this.x = x; + this.y = y; + } + + @Nullable + public static InAppWebViewRect fromMap(@Nullable Map map) { + if (map == null) { + return null; + } + double height = (double) map.get("height"); + double width = (double) map.get("width"); + double x = (double) map.get("x"); + double y = (double) map.get("y"); + return new InAppWebViewRect(height, width, x, y); + } + + public Map toMap() { + Map map = new HashMap<>(); + map.put("height", height); + map.put("width", width); + map.put("x", x); + map.put("y", y); + return map; + } + + public Rect toRect() { + return new Rect((int) x, (int) y, (int) (x + width), (int) (y + height)); + } + + public double getHeight() { + return height; + } + + public void setHeight(double height) { + this.height = height; + } + + public double getWidth() { + return width; + } + + public void setWidth(double width) { + this.width = width; + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InAppWebViewRect that = (InAppWebViewRect) o; + return Double.compare(height, that.height) == 0 && Double.compare(width, that.width) == 0 && Double.compare(x, that.x) == 0 && Double.compare(y, that.y) == 0; + } + + @Override + public int hashCode() { + int result = Double.hashCode(height); + result = 31 * result + Double.hashCode(width); + result = 31 * result + Double.hashCode(x); + result = 31 * result + Double.hashCode(y); + return result; + } + + @Override + public String toString() { + return "InAppWebViewRect{" + + "height=" + height + + ", width=" + width + + ", x=" + x + + ", y=" + y + + '}'; + } +} diff --git a/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/webview/WebViewChannelDelegate.java b/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/webview/WebViewChannelDelegate.java index e4b12a976..e8e64a166 100644 --- a/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/webview/WebViewChannelDelegate.java +++ b/flutter_inappwebview_android/android/src/main/java/com/pichillilorenzo/flutter_inappwebview_android/webview/WebViewChannelDelegate.java @@ -1,5 +1,6 @@ package com.pichillilorenzo.flutter_inappwebview_android.webview; +import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.webkit.ValueCallback; @@ -29,6 +30,7 @@ import com.pichillilorenzo.flutter_inappwebview_android.types.HitTestResult; import com.pichillilorenzo.flutter_inappwebview_android.types.HttpAuthResponse; import com.pichillilorenzo.flutter_inappwebview_android.types.HttpAuthenticationChallenge; +import com.pichillilorenzo.flutter_inappwebview_android.types.InAppWebViewRect; import com.pichillilorenzo.flutter_inappwebview_android.types.JavaScriptHandlerFunctionData; import com.pichillilorenzo.flutter_inappwebview_android.types.JsAlertResponse; import com.pichillilorenzo.flutter_inappwebview_android.types.JsBeforeUnloadResponse; @@ -477,9 +479,20 @@ public void onReceiveValue(String value) { break; case requestFocus: if (webView != null) { - webView.requestFocus(); + boolean resultValue = false; + Integer direction = (Integer) call.argument("direction"); + InAppWebViewRect previouslyFocusedRect = InAppWebViewRect.fromMap((Map) call.argument("previouslyFocusedRect")); + if (direction != null && previouslyFocusedRect != null) { + resultValue = webView.requestFocus(direction, previouslyFocusedRect.toRect()); + } else if (direction != null) { + resultValue = webView.requestFocus(direction); + } else { + resultValue = webView.requestFocus(); + } + result.success(resultValue); + } else { + result.success(false); } - result.success(true); break; case setContextMenu: if (webView != null) { diff --git a/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview_controller.dart index c9cbc155c..4e3794b53 100644 --- a/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_android/lib/src/in_app_webview/in_app_webview_controller.dart @@ -2240,9 +2240,13 @@ class AndroidInAppWebViewController extends PlatformInAppWebViewController } @override - Future requestFocus() async { + Future requestFocus( + {FocusDirection? direction, + InAppWebViewRect? previouslyFocusedRect}) async { Map args = {}; - return await channel?.invokeMethod('requestFocus', args); + args.putIfAbsent("direction", () => direction?.toNativeValue()); + args.putIfAbsent("previouslyFocusedRect", () => previouslyFocusedRect?.toMap()); + return await channel?.invokeMethod('requestFocus', args); } @override diff --git a/flutter_inappwebview_ios/CHANGELOG.md b/flutter_inappwebview_ios/CHANGELOG.md index 1041fe279..2c52d0bf4 100644 --- a/flutter_inappwebview_ios/CHANGELOG.md +++ b/flutter_inappwebview_ios/CHANGELOG.md @@ -1,6 +1,7 @@ ## 1.2.0-beta.1 - Updated flutter_inappwebview_platform_interface version to ^1.4.0 +- Implemented `requestFocus` WebView method - Updated ConsoleLogJS internal PluginScript to main-frame only as using it on non-main frames could cause issues such as [#1738](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1738) - Added support for `UserScript.allowedOriginRules` parameter - Moved `WKUserContentController` initialization on `preWKWebViewConfiguration` to fix possible `undefined is not an object (evaluating 'window.webkit.messageHandlers')` javascript error diff --git a/flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebView.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebView.swift index 98862b282..3a9d3ca8d 100755 --- a/flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebView.swift +++ b/flutter_inappwebview_ios/ios/Classes/InAppWebView/InAppWebView.swift @@ -3259,12 +3259,12 @@ if(window.\(JavaScriptBridgeJS.get_JAVASCRIPT_BRIDGE_NAME())[\(_callHandlerID)] } } - public func clearFocus() { - self.scrollView.subviews.first?.resignFirstResponder() + public func clearFocus() -> Bool { + return self.scrollView.subviews.first?.resignFirstResponder() ?? false } - public func requestFocus() { - self.scrollView.subviews.first?.becomeFirstResponder() + public func requestFocus() -> Bool { + return self.scrollView.subviews.first?.becomeFirstResponder() ?? false } public func getCertificate() -> SslCertificate? { diff --git a/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegate.swift b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegate.swift index 0e8c19670..b0d88ebcc 100644 --- a/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegate.swift +++ b/flutter_inappwebview_ios/ios/Classes/InAppWebView/WebViewChannelDelegate.swift @@ -345,12 +345,10 @@ public class WebViewChannelDelegate: ChannelDelegate { } break case .clearFocus: - webView?.clearFocus() - result(true) + result(webView?.clearFocus()) break case .requestFocus: - webView?.requestFocus() - result(true) + result(webView?.requestFocus()) break case .setContextMenu: if let webView = webView { diff --git a/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview_controller.dart index d37e14602..7fe686162 100644 --- a/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_ios/lib/src/in_app_webview/in_app_webview_controller.dart @@ -2233,9 +2233,13 @@ class IOSInAppWebViewController extends PlatformInAppWebViewController } @override - Future requestFocus() async { + Future requestFocus( + {FocusDirection? direction, + InAppWebViewRect? previouslyFocusedRect}) async { Map args = {}; - return await channel?.invokeMethod('requestFocus', args); + args.putIfAbsent("direction", () => direction?.toNativeValue()); + args.putIfAbsent("previouslyFocusedRect", () => previouslyFocusedRect?.toMap()); + return await channel?.invokeMethod('requestFocus', args); } @override diff --git a/flutter_inappwebview_macos/CHANGELOG.md b/flutter_inappwebview_macos/CHANGELOG.md index a07283b49..b245ed568 100644 --- a/flutter_inappwebview_macos/CHANGELOG.md +++ b/flutter_inappwebview_macos/CHANGELOG.md @@ -1,10 +1,12 @@ ## 1.2.0-beta.1 - Updated flutter_inappwebview_platform_interface version to ^1.4.0 +- Implemented `requestFocus`, `clearFocus` WebView methods - Updated ConsoleLogJS internal PluginScript to main-frame only as using it on non-main frames could cause issues such as [#1738](https://github.com/pichillilorenzo/flutter_inappwebview/issues/1738) - Added support for `UserScript.allowedOriginRules` parameter - Moved `WKUserContentController` initialization on `preWKWebViewConfiguration` to fix possible `undefined is not an object (evaluating 'window.webkit.messageHandlers')` javascript error - Merged "change priority of DispatchQueue" [#2322](https://github.com/pichillilorenzo/flutter_inappwebview/pull/2322) (thanks to [nnnlog](https://github.com/nnnlog)) +- Implemented workaround for "[macOS] Copy Shortcut does not work if TextField outside of WebView has focus" [#2380](https://github.com/pichillilorenzo/flutter_inappwebview/issues/2380) ## 1.1.2 diff --git a/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview.dart b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview.dart index f2800a949..0d0499c69 100755 --- a/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview.dart +++ b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; -import 'headless_in_app_webview.dart'; import '../find_interaction/find_interaction_controller.dart'; +import 'headless_in_app_webview.dart'; import 'in_app_webview_controller.dart'; /// Object specifying creation parameters for creating a [PlatformInAppWebViewWidget]. diff --git a/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview_controller.dart b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview_controller.dart index ec7cd242a..add535e8a 100644 --- a/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/flutter_inappwebview_macos/lib/src/in_app_webview/in_app_webview_controller.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_inappwebview_platform_interface/flutter_inappwebview_platform_interface.dart'; import '../in_app_browser/in_app_browser.dart'; @@ -188,6 +189,13 @@ class MacOSInAppWebViewController extends PlatformInAppWebViewController } Future _handleMethod(MethodCall call) async { + if (call.method == "_onMouseDown") { + // Workaround for https://github.com/pichillilorenzo/flutter_inappwebview/issues/2380 + // TODO: remove when Flutter fixes this + FocusManager.instance.primaryFocus?.unfocus(); + return; + } + if (PlatformInAppWebViewController.debugLoggingSettings.enabled && call.method != "onCallJsHandler") { _debugLog(call.method, call.arguments); @@ -2202,6 +2210,22 @@ class MacOSInAppWebViewController extends PlatformInAppWebViewController return await channel?.invokeMethod('getSelectedText', args); } + @override + Future requestFocus( + {FocusDirection? direction, + InAppWebViewRect? previouslyFocusedRect}) async { + Map args = {}; + args.putIfAbsent("direction", () => direction?.toNativeValue()); + args.putIfAbsent("previouslyFocusedRect", () => previouslyFocusedRect?.toMap()); + return await channel?.invokeMethod('requestFocus', args); + } + + @override + Future clearFocus() async { + Map args = {}; + return await channel?.invokeMethod('clearFocus', args); + } + @override Future> getMetaTags() async { List metaTags = []; diff --git a/flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebView.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebView.swift index ce7f505f4..18c493cca 100755 --- a/flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebView.swift +++ b/flutter_inappwebview_macos/macos/Classes/InAppWebView/InAppWebView.swift @@ -53,6 +53,8 @@ public class InAppWebView: WKWebView, WKUIDelegate, private var exceptedBridgeSecret = NSUUID().uuidString private var javaScriptBridgeEnabled = true + public override var acceptsFirstResponder: Bool { return true } + init(id: Any?, plugin: InAppWebViewFlutterPlugin?, frame: CGRect, configuration: WKWebViewConfiguration, userScripts: [UserScript] = []) { super.init(frame: frame, configuration: configuration) @@ -2604,6 +2606,48 @@ if(window.\(JavaScriptBridgeJS.get_JAVASCRIPT_BRIDGE_NAME())[\(_callHandlerID)] } } + public func clearFocus() -> Bool { + return (self.superview?.window ?? self.window)?.makeFirstResponder(nil) ?? false + } + + public func requestFocus() -> Bool { + return (self.superview?.window ?? self.window)?.makeFirstResponder(self) ?? false + } + + // Workaround for https://github.com/pichillilorenzo/flutter_inappwebview/issues/2380 + // TODO: remove when Flutter fixes this + private var _isFirstResponder = true + override open func becomeFirstResponder() -> Bool { + _isFirstResponder = true + return super.becomeFirstResponder() + } + private func _fixFocus(callback: @escaping () -> Void) { + if _isFirstResponder, let channelDelegate = channelDelegate { + _isFirstResponder = false + channelDelegate._onMouseDown(callback: { [weak self] in + let _ = self?.requestFocus() + callback() + }) + } else { + callback() + } + } + override public func mouseDown(with event: NSEvent) { + _fixFocus { + super.mouseDown(with: event) + } + } + override public func rightMouseDown(with event: NSEvent) { + _fixFocus { + super.rightMouseDown(with: event) + } + } + override public func otherMouseDown(with event: NSEvent) { + _fixFocus { + super.otherMouseDown(with: event) + } + } + public func getCertificate() -> SslCertificate? { guard let scheme = url?.scheme, scheme == "https", diff --git a/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegate.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegate.swift index 671083388..cd8a5358a 100644 --- a/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegate.swift +++ b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegate.swift @@ -370,6 +370,12 @@ public class WebViewChannelDelegate: ChannelDelegate { result(nil) } break + case .clearFocus: + result(webView?.clearFocus()) + break + case .requestFocus: + result(webView?.requestFocus()) + break case .getCertificate: result(webView?.getCertificate()?.toMap()) break @@ -1166,6 +1172,16 @@ public class WebViewChannelDelegate: ChannelDelegate { channel?.invokeMethod("onPrintRequest", arguments: arguments, callback: callback) } + internal func _onMouseDown(callback: @escaping () -> Void) { + if channel == nil { + return + } + let arguments: [String:Any] = [:]; + channel?.invokeMethod("_onMouseDown", arguments: arguments) {(result) -> Void in + callback() + } + } + public override func dispose() { super.dispose() webView = nil diff --git a/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift index 4810bae3b..8dac64e48 100644 --- a/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift +++ b/flutter_inappwebview_macos/macos/Classes/InAppWebView/WebViewChannelDelegateMethods.swift @@ -58,6 +58,8 @@ public enum WebViewChannelDelegateMethods: String { case getSelectedText = "getSelectedText" case getScrollX = "getScrollX" case getScrollY = "getScrollY" + case clearFocus = "clearFocus" + case requestFocus = "requestFocus" case getCertificate = "getCertificate" case addUserScript = "addUserScript" case removeUserScript = "removeUserScript" diff --git a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart index 43aa98baf..6a7502052 100644 --- a/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart +++ b/flutter_inappwebview_platform_interface/lib/src/in_app_webview/platform_inappwebview_controller.dart @@ -1071,7 +1071,24 @@ abstract class PlatformInAppWebViewController extends PlatformInterface 'getHitTestResult is not implemented on the current platform'); } - Future requestFocus() { + ///{@template flutter_inappwebview_platform_interface.PlatformInAppWebViewController.requestFocus} + ///Call this method when you want to try the WebView to be the first responder. + /// + ///On Android, call this to try to give focus to the WebView and + ///give it hints about the [direction] and a specific [previouslyFocusedRect] that the focus is coming from. + ///The [previouslyFocusedRect] can help give larger views a finer grained hint about where focus is coming from, + ///and therefore, where to show selection, or forward focus change internally. + /// + ///Returns `true` whether this WebView actually took focus; otherwise, `false`. + /// + ///**NOTE**: [direction] and [previouslyFocusedRect] are available only on Android. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebView.requestFocus](https://developer.android.com/reference/android/webkit/WebView#requestFocus(int,%20android.graphics.Rect))) + ///- iOS ([Official API - UIResponder.becomeFirstResponder](https://developer.apple.com/documentation/uikit/uiresponder/1621113-becomefirstresponder)) + ///- MacOS ([Official API - NSWindow.makeFirstResponder](https://developer.apple.com/documentation/appkit/nswindow/1419366-makefirstresponder)) + ///{@endtemplate} + Future requestFocus({FocusDirection? direction, InAppWebViewRect? previouslyFocusedRect}) { throw UnimplementedError( 'requestFocus is not implemented on the current platform'); } @@ -1082,6 +1099,7 @@ abstract class PlatformInAppWebViewController extends PlatformInterface ///**Officially Supported Platforms/Implementations**: ///- Android native WebView ([Official API - ViewGroup.clearFocus](https://developer.android.com/reference/android/view/ViewGroup#clearFocus())) ///- iOS ([Official API - UIResponder.resignFirstResponder](https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder)) + ///- MacOS ([Official API - NSWindow.makeFirstResponder](https://developer.apple.com/documentation/appkit/nswindow/1419366-makefirstresponder)) ///{@endtemplate} Future clearFocus() { throw UnimplementedError( diff --git a/flutter_inappwebview_platform_interface/lib/src/types/focus_direction.dart b/flutter_inappwebview_platform_interface/lib/src/types/focus_direction.dart new file mode 100644 index 000000000..c3f593b8b --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/types/focus_direction.dart @@ -0,0 +1,54 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_inappwebview_internal_annotations/flutter_inappwebview_internal_annotations.dart'; + +part 'focus_direction.g.dart'; + +///Class used to indicate the force dark mode. +@ExchangeableEnum() +class FocusDirection_ { + // ignore: unused_field + final String _value; + // ignore: unused_field + final dynamic _nativeValue = null; + const FocusDirection_._internal(this._value); + + ///Move focus up. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform( + apiName: 'FOCUS_UP', + apiUrl: 'https://developer.android.com/reference/android/view/View#FOCUS_UP', + value: 33 + ), + ]) + static const UP = const FocusDirection_._internal('UP'); + + ///Move focus down. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform( + apiName: 'FOCUS_DOWN', + apiUrl: 'https://developer.android.com/reference/android/view/View#FOCUS_DOWN', + value: 130 + ), + ]) + static const DOWN = const FocusDirection_._internal('DOWN'); + + ///Move focus to the left. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform( + apiName: 'FOCUS_LEFT', + apiUrl: 'https://developer.android.com/reference/android/view/View#FOCUS_LEFT', + value: 130 + ), + ]) + static const LEFT = const FocusDirection_._internal('LEFT'); + + ///Move focus to the right. + @EnumSupportedPlatforms(platforms: [ + EnumAndroidPlatform( + apiName: 'FOCUS_RIGHT', + apiUrl: 'https://developer.android.com/reference/android/view/View#FOCUS_RIGHT', + value: 130 + ), + ]) + static const RIGHT = const FocusDirection_._internal('RIGHT'); +} \ No newline at end of file diff --git a/flutter_inappwebview_platform_interface/lib/src/types/focus_direction.g.dart b/flutter_inappwebview_platform_interface/lib/src/types/focus_direction.g.dart new file mode 100644 index 000000000..76684e551 --- /dev/null +++ b/flutter_inappwebview_platform_interface/lib/src/types/focus_direction.g.dart @@ -0,0 +1,125 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'focus_direction.dart'; + +// ************************************************************************** +// ExchangeableEnumGenerator +// ************************************************************************** + +///Class used to indicate the force dark mode. +class FocusDirection { + final String _value; + final dynamic _nativeValue; + const FocusDirection._internal(this._value, this._nativeValue); +// ignore: unused_element + factory FocusDirection._internalMultiPlatform( + String value, Function nativeValue) => + FocusDirection._internal(value, nativeValue()); + + ///Move focus down. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - FOCUS_DOWN](https://developer.android.com/reference/android/view/View#FOCUS_DOWN)) + static final DOWN = FocusDirection._internalMultiPlatform('DOWN', () { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return 130; + default: + break; + } + return null; + }); + + ///Move focus to the left. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - FOCUS_LEFT](https://developer.android.com/reference/android/view/View#FOCUS_LEFT)) + static final LEFT = FocusDirection._internalMultiPlatform('LEFT', () { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return 130; + default: + break; + } + return null; + }); + + ///Move focus to the right. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - FOCUS_RIGHT](https://developer.android.com/reference/android/view/View#FOCUS_RIGHT)) + static final RIGHT = FocusDirection._internalMultiPlatform('RIGHT', () { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return 130; + default: + break; + } + return null; + }); + + ///Move focus up. + /// + ///**Officially Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - FOCUS_UP](https://developer.android.com/reference/android/view/View#FOCUS_UP)) + static final UP = FocusDirection._internalMultiPlatform('UP', () { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return 33; + default: + break; + } + return null; + }); + + ///Set of all values of [FocusDirection]. + static final Set values = [ + FocusDirection.DOWN, + FocusDirection.LEFT, + FocusDirection.RIGHT, + FocusDirection.UP, + ].toSet(); + + ///Gets a possible [FocusDirection] instance from [String] value. + static FocusDirection? fromValue(String? value) { + if (value != null) { + try { + return FocusDirection.values + .firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets a possible [FocusDirection] instance from a native value. + static FocusDirection? fromNativeValue(dynamic value) { + if (value != null) { + try { + return FocusDirection.values + .firstWhere((element) => element.toNativeValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets [String] value. + String toValue() => _value; + + ///Gets [dynamic] native value. + dynamic toNativeValue() => _nativeValue; + + @override + int get hashCode => _value.hashCode; + + @override + bool operator ==(value) => value == _value; + + @override + String toString() { + return _value; + } +} diff --git a/flutter_inappwebview_platform_interface/lib/src/types/main.dart b/flutter_inappwebview_platform_interface/lib/src/types/main.dart index 738df7950..d6d4c78cb 100644 --- a/flutter_inappwebview_platform_interface/lib/src/types/main.dart +++ b/flutter_inappwebview_platform_interface/lib/src/types/main.dart @@ -235,3 +235,4 @@ export 'frame_kind.dart' show FrameKind; export 'process_failed_kind.dart' show ProcessFailedKind; export 'process_failed_reason.dart' show ProcessFailedReason; export 'process_failed_detail.dart' show ProcessFailedDetail; +export 'focus_direction.dart' show FocusDirection;