windows: fixed custom platform view context menu and window position

This commit is contained in:
unknown 2024-01-22 16:11:43 +01:00
parent ed6283d3b3
commit e4bfd68313
6 changed files with 92 additions and 88 deletions

View File

@ -220,6 +220,16 @@ class CustomPlatformViewController
return _methodChannel return _methodChannel
.invokeMethod('setSize', [size.width, size.height, scaleFactor]); .invokeMethod('setSize', [size.width, size.height, scaleFactor]);
} }
/// Sets the surface size to the provided [size].
Future<void> _setPosition(Offset position, double scaleFactor) async {
if (_isDisposed) {
return;
}
assert(value.isInitialized);
return _methodChannel
.invokeMethod('setPosition', [position.dx, position.dy, scaleFactor]);
}
} }
class CustomPlatformView extends StatefulWidget { class CustomPlatformView extends StatefulWidget {
@ -273,8 +283,11 @@ class _CustomPlatformViewState extends State<CustomPlatformView> {
}, },
arguments: widget.creationParams); arguments: widget.creationParams);
// Report initial surface size // Report initial surface size and widget position
WidgetsBinding.instance.addPostFrameCallback((_) => _reportSurfaceSize()); WidgetsBinding.instance.addPostFrameCallback((_) {
_reportSurfaceSize();
_reportWidgetPosition();
});
_cursorSubscription = _controller._cursor.listen((cursor) { _cursorSubscription = _controller._cursor.listen((cursor) {
setState(() { setState(() {
@ -301,6 +314,7 @@ class _CustomPlatformViewState extends State<CustomPlatformView> {
return NotificationListener<SizeChangedLayoutNotification>( return NotificationListener<SizeChangedLayoutNotification>(
onNotification: (notification) { onNotification: (notification) {
_reportSurfaceSize(); _reportSurfaceSize();
_reportWidgetPosition();
return true; return true;
}, },
child: SizeChangedLayoutNotifier( child: SizeChangedLayoutNotifier(
@ -316,6 +330,9 @@ class _CustomPlatformViewState extends State<CustomPlatformView> {
_controller._setCursorPos(ev.localPosition); _controller._setCursorPos(ev.localPosition);
}, },
onPointerDown: (ev) { onPointerDown: (ev) {
_reportSurfaceSize();
_reportWidgetPosition();
if (!_focusNode.hasFocus) { if (!_focusNode.hasFocus) {
_focusNode.requestFocus(); _focusNode.requestFocus();
Future.delayed(const Duration(milliseconds: 50), () { Future.delayed(const Duration(milliseconds: 50), () {
@ -401,6 +418,17 @@ class _CustomPlatformViewState extends State<CustomPlatformView> {
await _controller.ready; await _controller.ready;
unawaited(_controller._setSize( unawaited(_controller._setSize(
box.size, widget.scaleFactor ?? window.devicePixelRatio)); box.size, widget.scaleFactor ?? window.devicePixelRatio));
}
}
void _reportWidgetPosition() async {
final box = _key.currentContext?.findRenderObject() as RenderBox?;
if (box != null) {
await _controller.ready;
final position = box.localToGlobal(Offset.zero);
unawaited(_controller._setPosition(
position, widget.scaleFactor ?? window.devicePixelRatio));
} }
} }

View File

@ -15,6 +15,7 @@ namespace flutter_inappwebview_plugin
constexpr auto kErrorInvalidArgs = "invalidArguments"; constexpr auto kErrorInvalidArgs = "invalidArguments";
constexpr auto kMethodSetSize = "setSize"; constexpr auto kMethodSetSize = "setSize";
constexpr auto kMethodSetPosition = "setPosition";
constexpr auto kMethodSetCursorPos = "setCursorPos"; constexpr auto kMethodSetCursorPos = "setCursorPos";
constexpr auto kMethodSetPointerUpdate = "setPointerUpdate"; constexpr auto kMethodSetPointerUpdate = "setPointerUpdate";
constexpr auto kMethodSetPointerButton = "setPointerButton"; constexpr auto kMethodSetPointerButton = "setPointerButton";
@ -299,8 +300,20 @@ namespace flutter_inappwebview_plugin
} }
return result->Error(kErrorInvalidArgs); return result->Error(kErrorInvalidArgs);
} }
else if (method_name.compare(kMethodSetPosition) == 0) {
auto position = GetPointAndScaleFactorFromArgs(method_call.arguments());
if (position && view) {
const auto [x, y, scale_factor] = position.value();
if (method_name.compare(kMethodSetFpsLimit) == 0) { view->setPosition(static_cast<size_t>(x),
static_cast<size_t>(y),
static_cast<float>(scale_factor));
return result->Success();
}
return result->Error(kErrorInvalidArgs);
}
else if (method_name.compare(kMethodSetFpsLimit) == 0) {
if (const auto value = std::get_if<int32_t>(method_call.arguments())) { if (const auto value = std::get_if<int32_t>(method_call.arguments())) {
texture_bridge_->SetFpsLimit(*value == 0 ? std::nullopt texture_bridge_->SetFpsLimit(*value == 0 ? std::nullopt
: std::make_optional(*value)); : std::make_optional(*value));

View File

@ -14,6 +14,7 @@
#include "../utils/strconv.h" #include "../utils/strconv.h"
#include "../utils/string.h" #include "../utils/string.h"
#include "in_app_webview.h" #include "in_app_webview.h"
#include "in_app_webview_manager.h" #include "in_app_webview_manager.h"
namespace flutter_inappwebview_plugin namespace flutter_inappwebview_plugin
@ -872,6 +873,38 @@ namespace flutter_inappwebview_plugin
} }
} }
void InAppWebView::setPosition(size_t x, size_t y, float scale_factor)
{
if (!webViewController || !plugin || !plugin->registrar) {
return;
}
if (x >= 0 && y >= 0) {
scaleFactor_ = scale_factor;
auto scaled_x = static_cast<int>(x * scale_factor);
auto scaled_y = static_cast<int>(y * scale_factor);
auto titleBarHeight = ((GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME)) * scale_factor) + GetSystemMetrics(SM_CXPADDEDBORDER);
auto borderWidth = (GetSystemMetrics(SM_CXBORDER) + GetSystemMetrics(SM_CXPADDEDBORDER)) * scale_factor;
RECT flutterWindowRect;
HWND flutterWindowHWnd = plugin->registrar->GetView()->GetNativeWindow();
GetWindowRect(flutterWindowHWnd, &flutterWindowRect);
HWND webViewHWnd;
if (succeededOrLog(webViewController->get_ParentWindow(&webViewHWnd))) {
::SetWindowPos(webViewHWnd,
nullptr,
static_cast<int>(flutterWindowRect.left + scaled_x - borderWidth),
static_cast<int>(flutterWindowRect.top + scaled_y - titleBarHeight),
0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
}
void InAppWebView::setCursorPos(double x, double y) void InAppWebView::setCursorPos(double x, double y)
{ {
if (!webViewCompositionController) { if (!webViewCompositionController) {
@ -999,92 +1032,16 @@ namespace flutter_inappwebview_plugin
auto offset = static_cast<short>(delta * kScrollMultiplier); auto offset = static_cast<short>(delta * kScrollMultiplier);
/*
// For some reason,
// setting the point other than (x: 0, y: 0)
// will not make the scroll work.
// Unfortunately, this will break the scroll event
// for nested HTML scrollable elements.
POINT point;
point.x = 0;
point.y = 0;
if (horizontal) { if (horizontal) {
webViewCompositionController->SendMouseInput( webViewCompositionController->SendMouseInput(
COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtual_keys_.state(), COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtualKeys_.state(),
offset, point); offset, lastCursorPos_);
} }
else { else {
webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL, webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL,
virtual_keys_.state(), offset, virtualKeys_.state(), offset,
point); lastCursorPos_);
} }
*/
// Workaround for scroll events
auto workaroundScrollJS = "(function(horizontal, offset, x, y) { \
function elemCanScrollY(elem) { \
if (elem.scrollTop > 0) { \
return elem; \
} else { \
elem.scrollTop++; \
const top = elem.scrollTop; \
top && (elem.scrollTop = 0); \
if (top > 0) { \
return elem; \
} else { \
return elemCanScrollY(elem.parentElement); \
} \
} \
} \
function elemCanScrollX(elem) { \
if (elem.scrollLeft > 0) { \
return elem; \
} else { \
elem.scrollLeft++; \
const left = elem.scrollLeft; \
left && (elem.scrollLeft = 0); \
if (left > 0) { \
return elem; \
} else { \
return elemCanScrollX(elem.parentElement); \
} \
} \
} \
const elem = document.elementFromPoint(x, y); \
const elem2 = horizontal ? elemCanScrollX(elem) : elemCanScrollY(elem); \
const handled = elem2 != null && elem2 != document.documentElement && elem2 != document.body; \
if (handled) { \
elem2.scrollBy({left: horizontal ? offset : 0, top: horizontal ? 0 : offset}); \
} \
return handled; \
})(" + std::to_string(horizontal) + ", " + std::to_string(offset) + ", " + std::to_string(lastCursorPos_.x) + ", " + std::to_string(lastCursorPos_.y) + ");";
webView->ExecuteScript(utf8_to_wide(workaroundScrollJS).c_str(), Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
[this, horizontal, offset](HRESULT error, PCWSTR result) -> HRESULT
{
if (webViewCompositionController && (error != S_OK || wide_to_utf8(result).compare("false") == 0)) {
// try to use native mouse wheel handler
POINT point;
point.x = 0;
point.y = 0;
if (horizontal) {
webViewCompositionController->SendMouseInput(
COREWEBVIEW2_MOUSE_EVENT_KIND_HORIZONTAL_WHEEL, virtualKeys_.state(),
offset, point);
}
else {
webViewCompositionController->SendMouseInput(COREWEBVIEW2_MOUSE_EVENT_KIND_WHEEL,
virtualKeys_.state(), offset,
point);
}
}
return S_OK;
}).Get());
} }
void InAppWebView::setScrollDelta(double delta_x, double delta_y) void InAppWebView::setScrollDelta(double delta_x, double delta_y)

View File

@ -112,6 +112,7 @@ namespace flutter_inappwebview_plugin
return surface_.get(); return surface_.get();
} }
void setSurfaceSize(size_t width, size_t height, float scale_factor); void setSurfaceSize(size_t width, size_t height, float scale_factor);
void setPosition(size_t x, size_t y, float scale_factor);
void setCursorPos(double x, double y); void setCursorPos(double x, double y);
void setPointerUpdate(int32_t pointer, InAppWebViewPointerEventKind eventKind, void setPointerUpdate(int32_t pointer, InAppWebViewPointerEventKind eventKind,
double x, double y, double size, double pressure); double x, double y, double size, double pressure);

View File

@ -56,7 +56,7 @@ namespace flutter_inappwebview_plugin
if (string_equals(methodName, "createInAppWebView")) { if (string_equals(methodName, "createInAppWebView")) {
if (isSupported()) { if (isSupported()) {
createInAppBrowser(arguments, std::move(result)); createInAppWebView(arguments, std::move(result));
} }
else { else {
result->Error("0", "Creating an InAppWebView instance is not supported! Graphics Context is not valid!"); result->Error("0", "Creating an InAppWebView instance is not supported! Graphics Context is not valid!");
@ -74,7 +74,7 @@ namespace flutter_inappwebview_plugin
} }
} }
void InAppWebViewManager::createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) void InAppWebViewManager::createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)
{ {
auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result)); auto result_ = std::shared_ptr<flutter::MethodResult<flutter::EncodableValue>>(std::move(result));
@ -84,8 +84,13 @@ namespace flutter_inappwebview_plugin
auto initialDataMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "initialData"); auto initialDataMap = get_optional_fl_map_value<flutter::EncodableMap>(*arguments, "initialData");
auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(*arguments, "initialUserScripts"); auto initialUserScriptList = get_optional_fl_map_value<flutter::EncodableList>(*arguments, "initialUserScripts");
auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, CW_DEFAULT, RECT bounds;
CW_DEFAULT, 0, 0, HWND_MESSAGE, nullptr, GetClientRect(plugin->registrar->GetView()->GetNativeWindow(), &bounds);
auto hwnd = CreateWindowEx(0, windowClass_.lpszClassName, L"", 0, 0,
0, bounds.right - bounds.left, bounds.bottom - bounds.top,
plugin->registrar->GetView()->GetNativeWindow(), // HWND_MESSAGE,
nullptr,
windowClass_.hInstance, nullptr); windowClass_.hInstance, nullptr);
InAppWebView::createInAppWebViewEnv(hwnd, true, InAppWebView::createInAppWebViewEnv(hwnd, true,

View File

@ -44,7 +44,7 @@ namespace flutter_inappwebview_plugin
const flutter::MethodCall<flutter::EncodableValue>& method_call, const flutter::MethodCall<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result); std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
void createInAppBrowser(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result); void createInAppWebView(const flutter::EncodableMap* arguments, std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);
private: private:
std::unique_ptr<rx::RoHelper> rohelper_; std::unique_ptr<rx::RoHelper> rohelper_;
winrt::com_ptr<ABI::Windows::System::IDispatcherQueueController> winrt::com_ptr<ABI::Windows::System::IDispatcherQueueController>