Browse Source

Fix for browser pointer capture not released when pointer up outside of browser (#15103)

* init

* rework
pull/15190/head
Johan Polson 2 years ago
committed by GitHub
parent
commit
392cd874e0
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 36
      src/Browser/Avalonia.Browser/BrowserMouseDevice.cs
  2. 31
      src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs
  3. 6
      src/Browser/Avalonia.Browser/Interop/InputHelper.cs
  4. 10
      src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts

36
src/Browser/Avalonia.Browser/BrowserMouseDevice.cs

@ -0,0 +1,36 @@
using System.Runtime.InteropServices.JavaScript;
using Avalonia.Input;
using Avalonia.Browser.Interop;
namespace Avalonia.Browser
{
internal class BrowserMouseDevice : MouseDevice
{
internal long PointerId { get; }
public BrowserMouseDevice(long pointerId, JSObject container) : base( new BrowserMousePointer(pointerId, container))
{
PointerId = pointerId;
}
internal class BrowserMousePointer : Pointer
{
private readonly JSObject _container;
private readonly long _pointerId;
internal BrowserMousePointer(long pointerId, JSObject container) : base(GetNextFreeId(),PointerType.Mouse, true)
{
_pointerId = pointerId;
_container = container;
}
protected override void PlatformCapture(IInputElement? element)
{
if (element is { })
InputHelper.SetPointerCapture(_container, _pointerId);
else
InputHelper.ReleasePointerCapture(_container, _pointerId);
}
}
}
}

31
src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs

@ -35,6 +35,9 @@ namespace Avalonia.Browser
private readonly ClipboardImpl _clipboard;
private readonly IInsetsManager? _insetsManager;
private readonly IInputPane _inputPane;
private readonly List<BrowserMouseDevice> _mouseDevices;
private readonly JSObject _container;
public BrowserTopLevelImpl(AvaloniaView avaloniaView, JSObject container)
{
@ -50,6 +53,9 @@ namespace Avalonia.Browser
_systemNavigationManager = new BrowserSystemNavigationManagerImpl();
_clipboard = new ClipboardImpl();
_inputPane = new BrowserInputPane(container);
_mouseDevices = new();
_container = container;
}
public ulong Timestamp => (ulong)_sw.ElapsedMilliseconds;
@ -90,7 +96,7 @@ namespace Avalonia.Browser
if (_inputRoot is { }
&& Input is { } input)
{
var device = GetPointerDevice(pointerType);
var device = GetPointerDevice(pointerType, touchPointId);
var args = device is TouchDevice ?
new RawTouchEventArgs(device, Timestamp, _inputRoot, eventType, p, modifiers, touchPointId)
{
@ -110,21 +116,28 @@ namespace Avalonia.Browser
return false;
}
private IPointerDevice GetPointerDevice(string pointerType)
private IPointerDevice GetPointerDevice(string pointerType, long pointerId)
{
return pointerType switch
if (pointerType == "touch")
return _touchDevice;
else if (pointerType == "pen")
return _penDevice;
foreach (var mouseDevice in _mouseDevices)
{
"touch" => _touchDevice,
"pen" => _penDevice,
_ => MouseDevice
};
if (mouseDevice.PointerId == pointerId)
return mouseDevice;
}
var newMouseDevice = new BrowserMouseDevice(pointerId, _container);
_mouseDevices.Add(newMouseDevice);
return newMouseDevice;
}
public bool RawMouseWheelEvent(Point p, Vector v, RawInputModifiers modifiers)
{
if (_inputRoot is { })
{
var args = new RawMouseWheelEventArgs(MouseDevice, Timestamp, _inputRoot, p, v, modifiers);
var args = new RawMouseWheelEventArgs(WheelMouseDevice, Timestamp, _inputRoot, p, v, modifiers);
Input?.Invoke(args);
@ -235,7 +248,7 @@ namespace Avalonia.Browser
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public Action? Closed { get; set; }
public Action? LostFocus { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice();
public IMouseDevice WheelMouseDevice { get; } = new MouseDevice();
public IKeyboardDevice KeyboardDevice { get; } = BrowserWindowingPlatform.Keyboard;
public WindowTransparencyLevel TransparencyLevel => WindowTransparencyLevel.None;

6
src/Browser/Avalonia.Browser/Interop/InputHelper.cs

@ -92,6 +92,12 @@ internal static partial class InputHelper
[JSImport("InputHelper.readClipboardText", AvaloniaModule.MainModuleName)]
public static partial Task<string> ReadClipboardTextAsync();
[JSImport("InputHelper.setPointerCapture", AvaloniaModule.MainModuleName)]
public static partial void SetPointerCapture(JSObject containerElement, [JSMarshalAs<JSType.Number>] long pointerId);
[JSImport("InputHelper.releasePointerCapture", AvaloniaModule.MainModuleName)]
public static partial void ReleasePointerCapture(JSObject containerElement, [JSMarshalAs<JSType.Number>] long pointerId);
[JSImport("globalThis.navigator.clipboard.writeText")]
public static partial Task WriteClipboardTextAsync(string text);
}

10
src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts

@ -326,4 +326,14 @@ export class InputHelper {
return modifiers;
}
public static setPointerCapture(containerElement: HTMLInputElement, pointerId: number): void {
containerElement.setPointerCapture(pointerId);
}
public static releasePointerCapture(containerElement: HTMLInputElement, pointerId: number): void {
if (containerElement.hasPointerCapture(pointerId)) {
containerElement.releasePointerCapture(pointerId);
}
}
}

Loading…
Cancel
Save