A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

268 lines
8.8 KiB

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Versioning;
using Avalonia.Browser.Skia;
using Avalonia.Browser.Storage;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.Rendering;
using Avalonia.Rendering.Composition;
[assembly: SupportedOSPlatform("browser")]
namespace Avalonia.Browser
{
internal class BrowserTopLevelImpl : ITopLevelImpl
{
private Size _clientSize;
private IInputRoot? _inputRoot;
private readonly Stopwatch _sw = Stopwatch.StartNew();
private readonly AvaloniaView _avaloniaView;
private readonly TouchDevice _touchDevice;
private readonly PenDevice _penDevice;
private string _currentCursor = CssCursor.Default;
private readonly INativeControlHostImpl _nativeControlHost;
private readonly IStorageProvider _storageProvider;
private readonly ISystemNavigationManagerImpl _systemNavigationManager;
public BrowserTopLevelImpl(AvaloniaView avaloniaView)
{
Surfaces = Enumerable.Empty<object>();
_avaloniaView = avaloniaView;
TransparencyLevel = WindowTransparencyLevel.None;
AcrylicCompensationLevels = new AcrylicPlatformCompensationLevels(1, 1, 1);
_touchDevice = new TouchDevice();
_penDevice = new PenDevice();
_nativeControlHost = _avaloniaView.GetNativeControlHostImpl();
_storageProvider = new BrowserStorageProvider();
_systemNavigationManager = new BrowserSystemNavigationManagerImpl();
}
public ulong Timestamp => (ulong)_sw.ElapsedMilliseconds;
public void SetClientSize(Size newSize, double dpi)
{
if (Math.Abs(RenderScaling - dpi) > 0.0001)
{
if (Surfaces.FirstOrDefault() is BrowserSkiaSurface surface)
{
surface.Scaling = dpi;
}
ScalingChanged?.Invoke(dpi);
}
if (newSize != _clientSize)
{
_clientSize = newSize;
if (Surfaces.FirstOrDefault() is BrowserSkiaSurface surface)
{
surface.Size = new PixelSize((int)newSize.Width, (int)newSize.Height);
}
Resized?.Invoke(newSize, PlatformResizeReason.User);
}
}
public bool RawPointerEvent(
RawPointerEventType eventType, string pointerType,
RawPointerPoint p, RawInputModifiers modifiers, long touchPointId,
Lazy<IReadOnlyList<RawPointerPoint>?>? intermediatePoints = null)
{
if (_inputRoot is { }
&& Input is { } input)
{
var device = GetPointerDevice(pointerType);
var args = device is TouchDevice ?
new RawTouchEventArgs(device, Timestamp, _inputRoot, eventType, p, modifiers, touchPointId)
{
IntermediatePoints = intermediatePoints
} :
new RawPointerEventArgs(device, Timestamp, _inputRoot, eventType, p, modifiers)
{
RawPointerId = touchPointId,
IntermediatePoints = intermediatePoints
};
input.Invoke(args);
return args.Handled;
}
return false;
}
private IPointerDevice GetPointerDevice(string pointerType)
{
return pointerType switch
{
"touch" => _touchDevice,
"pen" => _penDevice,
_ => MouseDevice
};
}
public bool RawMouseWheelEvent(Point p, Vector v, RawInputModifiers modifiers)
{
if (_inputRoot is { })
{
var args = new RawMouseWheelEventArgs(MouseDevice, Timestamp, _inputRoot, p, v, modifiers);
Input?.Invoke(args);
return args.Handled;
}
return false;
}
public bool RawKeyboardEvent(RawKeyEventType type, string code, string key, RawInputModifiers modifiers)
{
if (Keycodes.KeyCodes.TryGetValue(code, out var avkey))
{
if (_inputRoot is { })
{
var args = new RawKeyEventArgs(KeyboardDevice, Timestamp, _inputRoot, type, avkey, modifiers);
Input?.Invoke(args);
return args.Handled;
}
}
else if (Keycodes.KeyCodes.TryGetValue(key, out avkey))
{
if (_inputRoot is { })
{
var args = new RawKeyEventArgs(KeyboardDevice, Timestamp, _inputRoot, type, avkey, modifiers);
Input?.Invoke(args);
return args.Handled;
}
}
return false;
}
public bool RawTextEvent(string text)
{
if (_inputRoot is { })
{
var args = new RawTextInputEventArgs(KeyboardDevice, Timestamp, _inputRoot, text);
Input?.Invoke(args);
return args.Handled;
}
return false;
}
public void Dispose()
{
}
public IRenderer CreateRenderer(IRenderRoot root)
{
var loop = AvaloniaLocator.Current.GetRequiredService<IRenderLoop>();
return new CompositingRenderer(root,
new Compositor(loop, AvaloniaLocator.Current.GetRequiredService<IPlatformGraphics>()), () => Surfaces);
}
public void Invalidate(Rect rect)
{
//Console.WriteLine("invalidate rect called");
}
public void SetInputRoot(IInputRoot inputRoot)
{
_inputRoot = inputRoot;
}
public Point PointToClient(PixelPoint point) => new Point(point.X, point.Y);
public PixelPoint PointToScreen(Point point) => new PixelPoint((int)point.X, (int)point.Y);
public void SetCursor(ICursorImpl? cursor)
{
var val = (cursor as CssCursor)?.Value ?? CssCursor.Default;
if (_currentCursor != val)
{
SetCssCursor?.Invoke(val);
_currentCursor = val;
}
}
public IPopupImpl? CreatePopup()
{
return null;
}
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
{
if (transparencyLevel == WindowTransparencyLevel.None
|| transparencyLevel == WindowTransparencyLevel.Transparent)
{
TransparencyLevel = transparencyLevel;
}
}
public Size ClientSize => _clientSize;
public Size? FrameSize => null;
public double RenderScaling => (Surfaces.FirstOrDefault() as BrowserSkiaSurface)?.Scaling ?? 1;
public IEnumerable<object> Surfaces { get; set; }
public Action<string>? SetCssCursor { get; set; }
public Action<RawInputEventArgs>? Input { get; set; }
public Action<Rect>? Paint { get; set; }
public Action<Size, PlatformResizeReason>? Resized { get; set; }
public Action<double>? ScalingChanged { get; set; }
public Action<WindowTransparencyLevel>? TransparencyLevelChanged { get; set; }
public Action? Closed { get; set; }
public Action? LostFocus { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice();
public IKeyboardDevice KeyboardDevice { get; } = BrowserWindowingPlatform.Keyboard;
public WindowTransparencyLevel TransparencyLevel { get; private set; }
public void SetFrameThemeVariant(PlatformThemeVariant themeVariant)
{
// not in the standard, but we potentially can use "apple-mobile-web-app-status-bar-style" for iOS and "theme-color" for android.
}
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; }
public object? TryGetFeature(Type featureType)
{
if (featureType == typeof(IStorageProvider))
{
return _storageProvider;
}
if (featureType == typeof(ITextInputMethodImpl))
{
return _avaloniaView;
}
if (featureType == typeof(ISystemNavigationManagerImpl))
{
return _systemNavigationManager;
}
if (featureType == typeof(INativeControlHostImpl))
{
return _nativeControlHost;
}
return null;
}
}
}