Browse Source

Merge branch 'master' into listbox_control_selection

pull/9144/head
Max Katz 4 years ago
committed by GitHub
parent
commit
3ced7a35bc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      src/Avalonia.Controls/TreeViewItem.cs
  2. 1
      src/Web/Avalonia.Web/Avalonia.Web.csproj
  3. 76
      src/Web/Avalonia.Web/AvaloniaView.cs
  4. 11
      src/Web/Avalonia.Web/BrowserTopLevelImpl.cs
  5. 6
      src/Web/Avalonia.Web/Interop/InputHelper.cs
  6. 12
      src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts

30
src/Avalonia.Controls/TreeViewItem.cs

@ -6,6 +6,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Threading;
namespace Avalonia.Controls
{
@ -45,6 +46,8 @@ namespace Avalonia.Controls
private IControl? _header;
private bool _isExpanded;
private int _level;
private bool _templateApplied;
private bool _deferredBringIntoViewFlag;
/// <summary>
/// Initializes static members of the <see cref="TreeViewItem"/> class.
@ -136,15 +139,24 @@ namespace Avalonia.Controls
protected virtual void OnRequestBringIntoView(RequestBringIntoViewEventArgs e)
{
if (e.TargetObject == this && _header != null)
if (e.TargetObject == this)
{
var m = _header.TransformToVisual(this);
if (!_templateApplied)
{
_deferredBringIntoViewFlag = true;
return;
}
if (m.HasValue)
if (_header != null)
{
var bounds = new Rect(_header.Bounds.Size);
var rect = bounds.TransformToAABB(m.Value);
e.TargetRect = rect;
var m = _header.TransformToVisual(this);
if (m.HasValue)
{
var bounds = new Rect(_header.Bounds.Size);
var rect = bounds.TransformToAABB(m.Value);
e.TargetRect = rect;
}
}
}
}
@ -187,6 +199,12 @@ namespace Avalonia.Controls
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
_header = e.NameScope.Find<IControl>("PART_Header");
_templateApplied = true;
if (_deferredBringIntoViewFlag)
{
_deferredBringIntoViewFlag = false;
Dispatcher.UIThread.Post(this.BringIntoView); // must use the Dispatcher, otherwise the TreeView doesn't scroll
}
}
private static int CalculateDistanceFromLogicalParent<T>(ILogical? logical, int @default = -1) where T : class

1
src/Web/Avalonia.Web/Avalonia.Web.csproj

@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Remove="@(SupportedPlatform)" />
<SupportedPlatform Include="browser" />
</ItemGroup>

76
src/Web/Avalonia.Web/AvaloniaView.cs

@ -1,5 +1,9 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices.JavaScript;
using Avalonia.Collections.Pooled;
using Avalonia.Controls;
using Avalonia.Controls.Embedding;
using Avalonia.Controls.Platform;
@ -18,6 +22,7 @@ namespace Avalonia.Web
[System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings
public partial class AvaloniaView : ITextInputMethodImpl
{
private static readonly PooledList<RawPointerPoint> s_intermediatePointsPooledList = new(ClearMode.Never);
private readonly BrowserTopLevelImpl _topLevelImpl;
private EmbeddableControlRoot _topLevel;
@ -52,13 +57,13 @@ namespace Avalonia.Web
}
_containerElement = hostContent.GetPropertyAsJSObject("host")
?? throw new InvalidOperationException("Host cannot be null");
?? throw new InvalidOperationException("Host cannot be null");
_canvas = hostContent.GetPropertyAsJSObject("canvas")
?? throw new InvalidOperationException("Canvas cannot be null");
?? throw new InvalidOperationException("Canvas cannot be null");
_nativeControlsContainer = hostContent.GetPropertyAsJSObject("nativeHost")
?? throw new InvalidOperationException("NativeHost cannot be null");
?? throw new InvalidOperationException("NativeHost cannot be null");
_inputElement = hostContent.GetPropertyAsJSObject("inputElement")
?? throw new InvalidOperationException("InputElement cannot be null");
?? throw new InvalidOperationException("InputElement cannot be null");
_splash = DomHelper.GetElementById("avalonia-splash");
@ -96,7 +101,8 @@ namespace Avalonia.Web
OnCompositionUpdate,
OnCompositionEnd);
InputHelper.SubscribePointerEvents(_containerElement, OnPointerMove, OnPointerDown, OnPointerUp, OnWheel);
InputHelper.SubscribePointerEvents(_containerElement, OnPointerMove, OnPointerDown, OnPointerUp,
OnPointerCancel, OnWheel);
var skiaOptions = AvaloniaLocator.Current.GetService<SkiaOptions>();
@ -117,7 +123,12 @@ namespace Avalonia.Web
_context.SetResourceCacheLimit(skiaOptions?.MaxGpuResourceSizeBytes ?? 32 * 1024 * 1024);
}
_topLevelImpl.Surfaces = new[] { new BrowserSkiaSurface(_context, _jsGlInfo, ColorType, new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi, GRSurfaceOrigin.BottomLeft) };
_topLevelImpl.Surfaces = new[]
{
new BrowserSkiaSurface(_context, _jsGlInfo, ColorType,
new PixelSize((int)_canvasSize.Width, (int)_canvasSize.Height), _dpi,
GRSurfaceOrigin.BottomLeft)
};
}
else
{
@ -135,7 +146,7 @@ namespace Avalonia.Web
DomHelper.ObserveSize(host, null, OnSizeChanged);
CanvasHelper.RequestAnimationFrame(_canvas, true);
InputHelper.FocusElement(_containerElement);
}
@ -155,17 +166,36 @@ namespace Avalonia.Web
private bool OnPointerMove(JSObject args)
{
var type = args.GetPropertyAsString("pointertype");
var pointerType = args.GetPropertyAsString("pointerType");
var point = ExtractRawPointerFromJSArgs(args);
var type = pointerType switch
{
"touch" => RawPointerEventType.TouchUpdate,
_ => RawPointerEventType.Move
};
var coalescedEvents = new Lazy<IReadOnlyList<RawPointerPoint>?>(() =>
{
var points = InputHelper.GetCoalescedEvents(args);
s_intermediatePointsPooledList.Clear();
s_intermediatePointsPooledList.Capacity = points.Length - 1;
// Skip the last one, as it is already processed point.
for (var i = 0; i < points.Length - 1; i++)
{
var point = points[i];
s_intermediatePointsPooledList.Add(ExtractRawPointerFromJSArgs(point));
}
return s_intermediatePointsPooledList;
});
return _topLevelImpl.RawPointerEvent(RawPointerEventType.Move, type!, point, GetModifiers(args), args.GetPropertyAsInt32("pointerId"));
return _topLevelImpl.RawPointerEvent(type, pointerType!, point, GetModifiers(args), args.GetPropertyAsInt32("pointerId"), coalescedEvents);
}
private bool OnPointerDown(JSObject args)
{
var pointerType = args.GetPropertyAsString("pointerType");
var pointerType = args.GetPropertyAsString("pointerType") ?? "mouse";
var type = pointerType switch
{
"touch" => RawPointerEventType.TouchBegin,
@ -176,20 +206,18 @@ namespace Avalonia.Web
2 => RawPointerEventType.RightButtonDown,
3 => RawPointerEventType.XButton1Down,
4 => RawPointerEventType.XButton2Down,
// 5 => Pen eraser button,
5 => RawPointerEventType.XButton1Down, // should be pen eraser button,
_ => RawPointerEventType.Move
}
};
var point = ExtractRawPointerFromJSArgs(args);
return _topLevelImpl.RawPointerEvent(type, pointerType!, point, GetModifiers(args), args.GetPropertyAsInt32("pointerId"));
return _topLevelImpl.RawPointerEvent(type, pointerType, point, GetModifiers(args), args.GetPropertyAsInt32("pointerId"));
}
private bool OnPointerUp(JSObject args)
{
var pointerType = args.GetPropertyAsString("pointerType") ?? "mouse";
var type = pointerType switch
{
"touch" => RawPointerEventType.TouchEnd,
@ -200,15 +228,27 @@ namespace Avalonia.Web
2 => RawPointerEventType.RightButtonUp,
3 => RawPointerEventType.XButton1Up,
4 => RawPointerEventType.XButton2Up,
// 5 => Pen eraser button,
5 => RawPointerEventType.XButton1Up, // should be pen eraser button,
_ => RawPointerEventType.Move
}
};
var point = ExtractRawPointerFromJSArgs(args);
return _topLevelImpl.RawPointerEvent(type, pointerType, point, GetModifiers(args), args.GetPropertyAsInt32("pointerId"));
}
private bool OnPointerCancel(JSObject args)
{
var pointerType = args.GetPropertyAsString("pointerType") ?? "mouse";
if (pointerType == "touch")
{
var point = ExtractRawPointerFromJSArgs(args);
_topLevelImpl.RawPointerEvent(RawPointerEventType.TouchCancel, pointerType, point,
GetModifiers(args), args.GetPropertyAsInt32("pointerId"));
}
return false;
}
private bool OnWheel(JSObject args)
{

11
src/Web/Avalonia.Web/BrowserTopLevelImpl.cs

@ -67,17 +67,22 @@ namespace Avalonia.Web
public bool RawPointerEvent(
RawPointerEventType eventType, string pointerType,
RawPointerPoint p, RawInputModifiers modifiers, long touchPointId)
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) :
new RawTouchEventArgs(device, Timestamp, _inputRoot, eventType, p, modifiers, touchPointId)
{
IntermediatePoints = intermediatePoints
} :
new RawPointerEventArgs(device, Timestamp, _inputRoot, eventType, p, modifiers)
{
RawPointerId = touchPointId
RawPointerId = touchPointId,
IntermediatePoints = intermediatePoints
};
input.Invoke(args);

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

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.JavaScript;
using System.Threading.Tasks;
@ -36,6 +37,8 @@ internal static partial class InputHelper
[JSMarshalAs<JSType.Function<JSType.Object, JSType.Boolean>>]
Func<JSObject, bool> pointerUp,
[JSMarshalAs<JSType.Function<JSType.Object, JSType.Boolean>>]
Func<JSObject, bool> pointerCancel,
[JSMarshalAs<JSType.Function<JSType.Object, JSType.Boolean>>]
Func<JSObject, bool> wheel);
@ -45,6 +48,9 @@ internal static partial class InputHelper
[JSMarshalAs<JSType.Function<JSType.String, JSType.Boolean>>]
Func<string, bool> input);
[JSImport("InputHelper.getCoalescedEvents", AvaloniaModule.MainModuleName)]
[return: JSMarshalAs<JSType.Array<JSType.Object>>]
public static partial JSObject[] GetCoalescedEvents(JSObject pointerEvent);
[JSImport("InputHelper.clearInput", AvaloniaModule.MainModuleName)]
public static partial void ClearInputElement(JSObject htmlElement);

12
src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts

@ -95,6 +95,7 @@ export class InputHelper {
pointerMoveCallback: (args: PointerEvent) => boolean,
pointerDownCallback: (args: PointerEvent) => boolean,
pointerUpCallback: (args: PointerEvent) => boolean,
pointerCancelCallback: (args: PointerEvent) => boolean,
wheelCallback: (args: WheelEvent) => boolean
) {
const pointerMoveHandler = (args: PointerEvent) => {
@ -112,6 +113,11 @@ export class InputHelper {
args.preventDefault();
};
const pointerCancelHandler = (args: PointerEvent) => {
pointerCancelCallback(args);
args.preventDefault();
};
const wheelHandler = (args: WheelEvent) => {
wheelCallback(args);
args.preventDefault();
@ -121,11 +127,13 @@ export class InputHelper {
element.addEventListener("pointerdown", pointerDownHandler);
element.addEventListener("pointerup", pointerUpHandler);
element.addEventListener("wheel", wheelHandler);
element.addEventListener("pointercancel", pointerCancelHandler);
return () => {
element.removeEventListener("pointerover", pointerMoveHandler);
element.removeEventListener("pointerdown", pointerDownHandler);
element.removeEventListener("pointerup", pointerUpHandler);
element.removeEventListener("pointercancel", pointerCancelHandler);
element.removeEventListener("wheel", wheelHandler);
};
}
@ -146,6 +154,10 @@ export class InputHelper {
};
}
public static getCoalescedEvents(pointerEvent: PointerEvent): PointerEvent[] {
return pointerEvent.getCoalescedEvents();
}
public static clearInput(inputElement: HTMLInputElement) {
inputElement.value = "";
}

Loading…
Cancel
Save