Browse Source

Merge branch 'master' into use-esbuild

pull/8934/head
Dan Walmsley 4 years ago
committed by GitHub
parent
commit
c1605c68bc
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs
  2. 2
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  3. 1
      src/Avalonia.Base/Input/PointerEventArgs.cs
  4. 17
      src/Avalonia.Base/Input/PointerOverPreProcessor.cs
  5. 2
      src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs
  6. 10
      src/Avalonia.Base/Media/TextTrimming.cs
  7. 2
      src/Avalonia.Base/Media/UnicodeRange.cs
  8. 2
      src/Avalonia.Base/Utilities/IWeakSubscriber.cs
  9. 2
      src/Avalonia.Controls/Window.cs
  10. 2
      src/Avalonia.MicroCom/MicroComProxyBase.cs
  11. 6
      src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs
  12. 16
      src/Linux/Avalonia.LinuxFramebuffer/Input/NullInput/NullInputBackend.cs
  13. 40
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  14. 4
      src/tools/DevGenerators/CompositionGenerator/Generator.cs
  15. 58
      tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs
  16. 2
      tests/Avalonia.Base.UnitTests/Input/PointerTests.cs
  17. 2
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

2
src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs

@ -42,7 +42,7 @@ namespace Avalonia.Data.Core.Plugins
if (target is IObservable<object?> result)
{
return result;
};
}
// If the observable returns a value type then we need to call Observable.Select on it.
// First get the type of T in `IObservable<T>`.

2
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@ -71,7 +71,7 @@ namespace Avalonia.Input.GestureRecognizers
{
EndGesture();
_tracking = e.Pointer;
_gestureId = ScrollGestureEventArgs.GetNextFreeId();;
_gestureId = ScrollGestureEventArgs.GetNextFreeId();
_trackedRootPoint = e.GetPosition(_target);
}
}

1
src/Avalonia.Base/Input/PointerEventArgs.cs

@ -65,6 +65,7 @@ namespace Avalonia.Input
return default;
if (relativeTo == null)
return pt;
return pt * _rootVisual.TransformToVisual(relativeTo) ?? default;
}

17
src/Avalonia.Base/Input/PointerOverPreProcessor.cs

@ -68,27 +68,29 @@ namespace Avalonia.Input
if (dirtyRect.Contains(clientPoint))
{
SetPointerOver(pointer, _inputRoot, _inputRoot.InputHitTest(clientPoint), 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
var element = pointer.Captured ?? _inputRoot.InputHitTest(clientPoint);
SetPointerOver(pointer, _inputRoot, element, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
else if (!_inputRoot.Bounds.Contains(clientPoint))
{
ClearPointerOver(pointer, _inputRoot, 0, new Point(-1, -1), PointerPointProperties.None, KeyModifiers.None);
ClearPointerOver(pointer, _inputRoot, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
}
}
private void ClearPointerOver()
{
if (_lastPointer is (var pointer, var _))
if (_lastPointer is (var pointer, var position))
{
ClearPointerOver(pointer, _inputRoot, 0, new Point(-1, -1), PointerPointProperties.None, KeyModifiers.None);
var clientPoint = _inputRoot.PointToClient(position);
ClearPointerOver(pointer, _inputRoot, 0, clientPoint, PointerPointProperties.None, KeyModifiers.None);
}
_lastPointer = null;
_lastActivePointerDevice = null;
}
private void ClearPointerOver(IPointer pointer, IInputRoot root,
ulong timestamp, Point position, PointerPointProperties properties, KeyModifiers inputModifiers)
ulong timestamp, Point? position, PointerPointProperties properties, KeyModifiers inputModifiers)
{
var element = root.PointerOverElement;
if (element is null)
@ -96,11 +98,10 @@ namespace Avalonia.Input
return;
}
// Do not pass rootVisual, when we have unknown (negative) position,
// Do not pass rootVisual, when we have unknown position,
// so GetPosition won't return invalid values.
var hasPosition = position.X >= 0 && position.Y >= 0;
var e = new PointerEventArgs(InputElement.PointerExitedEvent, element, pointer,
hasPosition ? root : null, hasPosition ? position : default,
position.HasValue ? root : null, position.HasValue ? position.Value : default,
timestamp, properties, inputModifiers);
if (element != null && !element.IsAttachedToVisualTree)

2
src/Avalonia.Base/Layout/FlowLayoutAlgorithm.cs

@ -523,7 +523,7 @@ namespace Avalonia.Layout
{
firstRealizedElement = _elementManager.GetAt(0);
firstBounds = _elementManager.GetLayoutBoundsForRealizedIndex(0);
firstDataIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);;
firstDataIndex = _elementManager.GetDataIndexFromRealizedRangeIndex(0);
int last = _elementManager.GetRealizedElementCount() - 1;
lastRealizedElement = _elementManager.GetAt(last);

10
src/Avalonia.Base/Media/TextTrimming.cs

@ -8,7 +8,7 @@ namespace Avalonia.Media
/// </summary>
public abstract class TextTrimming
{
public static char s_defaultEllipsisChar = '\u2026';
internal const char DefaultEllipsisChar = '\u2026';
/// <summary>
/// Text is not trimmed.
@ -18,22 +18,22 @@ namespace Avalonia.Media
/// <summary>
/// Text is trimmed at a character boundary. An ellipsis (...) is drawn in place of remaining text.
/// </summary>
public static TextTrimming CharacterEllipsis { get; } = new TextTrailingTrimming(s_defaultEllipsisChar, false);
public static TextTrimming CharacterEllipsis { get; } = new TextTrailingTrimming(DefaultEllipsisChar, false);
/// <summary>
/// Text is trimmed at a word boundary. An ellipsis (...) is drawn in place of remaining text.
/// </summary>
public static TextTrimming WordEllipsis { get; } = new TextTrailingTrimming(s_defaultEllipsisChar, true);
public static TextTrimming WordEllipsis { get; } = new TextTrailingTrimming(DefaultEllipsisChar, true);
/// <summary>
/// Text is trimmed after a given prefix length. An ellipsis (...) is drawn in between prefix and suffix and represents remaining text.
/// </summary>
public static TextTrimming PrefixCharacterEllipsis { get; } = new TextLeadingPrefixTrimming(s_defaultEllipsisChar, 8);
public static TextTrimming PrefixCharacterEllipsis { get; } = new TextLeadingPrefixTrimming(DefaultEllipsisChar, 8);
/// <summary>
/// Text is trimmed at a character boundary starting from the beginning. An ellipsis (...) is drawn in place of remaining text.
/// </summary>
public static TextTrimming LeadingCharacterEllipsis { get; } = new TextLeadingPrefixTrimming(s_defaultEllipsisChar, 0);
public static TextTrimming LeadingCharacterEllipsis { get; } = new TextLeadingPrefixTrimming(DefaultEllipsisChar, 0);
/// <summary>
/// Creates properties that will be used for collapsing lines of text.

2
src/Avalonia.Base/Media/UnicodeRange.cs

@ -9,7 +9,7 @@ namespace Avalonia.Media
/// </summary>
public readonly struct UnicodeRange
{
public static UnicodeRange Default = Parse("0-10FFFD");
public readonly static UnicodeRange Default = Parse("0-10FFFD");
private readonly UnicodeRangeSegment _single;
private readonly IReadOnlyList<UnicodeRangeSegment>? _segments = null;

2
src/Avalonia.Base/Utilities/IWeakSubscriber.cs

@ -3,7 +3,7 @@ using System;
namespace Avalonia.Utilities
{
/// <summary>
/// Defines a listener to a event subscribed vis the <see cref="WeakSubscriptionManager"/>.
/// Defines a listener to a event subscribed vis the <see cref="WeakObservable"/>.
/// </summary>
/// <typeparam name="T">The type of the event arguments.</typeparam>
public interface IWeakSubscriber<T> where T : EventArgs

2
src/Avalonia.Controls/Window.cs

@ -378,8 +378,6 @@ namespace Avalonia.Controls
/// <summary>
/// Enables or disables resizing of the window.
/// Note that if <see cref="HasSystemDecorations"/> is set to False then this property
/// has no effect and should be treated as a recommendation for the user setting HasSystemDecorations.
/// </summary>
public bool CanResize
{

2
src/Avalonia.MicroCom/MicroComProxyBase.cs

@ -65,7 +65,7 @@ namespace Avalonia.MicroCom
protected virtual void Dispose(bool disposing)
{
if(_nativePointer == null)
if(_nativePointer == IntPtr.Zero)
return;
if (_ownsHandle)
{

6
src/Linux/Avalonia.LinuxFramebuffer/Input/LibInput/LibInputBackend.cs

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Threading;
using static Avalonia.LinuxFramebuffer.Input.LibInput.LibInputNativeUnsafeMethods;
namespace Avalonia.LinuxFramebuffer.Input.LibInput
{
@ -29,8 +27,6 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput
new Thread(()=>InputThread(ctx)).Start();
}
private unsafe void InputThread(IntPtr ctx)
{
var fd = libinput_get_fd(ctx);
@ -143,8 +139,6 @@ namespace Avalonia.LinuxFramebuffer.Input.LibInput
}
}
public void Initialize(IScreenInfoProvider screen, Action<RawInputEventArgs> onInput)
{

16
src/Linux/Avalonia.LinuxFramebuffer/Input/NullInput/NullInputBackend.cs

@ -0,0 +1,16 @@
using System;
using Avalonia.Input;
using Avalonia.Input.Raw;
namespace Avalonia.LinuxFramebuffer.Input.NullInput;
public class NullInputBackend : IInputBackend
{
public void Initialize(IScreenInfoProvider screen, Action<RawInputEventArgs> onInput)
{
}
public void SetInputRoot(IInputRoot root)
{
}
}

40
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -42,9 +42,9 @@ namespace Avalonia.LinuxFramebuffer
Threading = new InternalPlatformThreadingInterface();
if (_fb is IGlOutputBackend gl)
AvaloniaLocator.CurrentMutable.Bind<IPlatformOpenGlInterface>().ToConstant(gl.PlatformOpenGlInterface);
var opts = AvaloniaLocator.Current.GetService<LinuxFramebufferPlatformOptions>() ?? new LinuxFramebufferPlatformOptions();
AvaloniaLocator.CurrentMutable
.Bind<IPlatformThreadingInterface>().ToConstant(Threading)
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(opts.Fps))
@ -59,12 +59,12 @@ namespace Avalonia.LinuxFramebuffer
AvaloniaLocator.Current.GetService<IPlatformOpenGlInterface>());
}
internal static LinuxFramebufferLifetime Initialize<T>(T builder, IOutputBackend outputBackend) where T : AppBuilderBase<T>, new()
internal static LinuxFramebufferLifetime Initialize<T>(T builder, IOutputBackend outputBackend, IInputBackend inputBackend) where T : AppBuilderBase<T>, new()
{
var platform = new LinuxFramebufferPlatform(outputBackend);
builder.UseSkia().UseWindowingSubsystem(platform.Initialize, "fbdev");
return new LinuxFramebufferLifetime(platform._fb);
return new LinuxFramebufferLifetime(platform._fb, inputBackend);
}
}
@ -80,13 +80,13 @@ namespace Avalonia.LinuxFramebuffer
{
_fb = fb;
}
public LinuxFramebufferLifetime(IOutputBackend fb, IInputBackend input)
{
_fb = fb;
_inputBackend = input;
}
public Control MainView
{
get => (Control)_topLevel?.Content;
@ -126,7 +126,7 @@ namespace Avalonia.LinuxFramebuffer
{
Startup?.Invoke(this, new ControlledApplicationLifetimeStartupEventArgs(args));
}
public void Shutdown(int exitCode)
{
ExitCode = exitCode;
@ -140,22 +140,22 @@ namespace Avalonia.LinuxFramebuffer
public static class LinuxFramebufferPlatformExtensions
{
public static int StartLinuxFbDev<T>(this T builder, string[] args, string fbdev = null, double scaling = 1)
public static int StartLinuxFbDev<T>(this T builder, string[] args, string fbdev = null, double scaling = 1, IInputBackend inputBackend = default)
where T : AppBuilderBase<T>, new() =>
StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: null) { Scaling = scaling });
public static int StartLinuxFbDev<T>(this T builder, string[] args, string fbdev, PixelFormat? format, double scaling)
StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: null) { Scaling = scaling }, inputBackend);
public static int StartLinuxFbDev<T>(this T builder, string[] args, string fbdev, PixelFormat? format, double scaling, IInputBackend inputBackend = default)
where T : AppBuilderBase<T>, new() =>
StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: format) { Scaling = scaling });
public static int StartLinuxDrm<T>(this T builder, string[] args, string card = null, double scaling = 1)
where T : AppBuilderBase<T>, new() => StartLinuxDirect(builder, args, new DrmOutput(card) {Scaling = scaling});
public static int StartLinuxDrm<T>(this T builder, string[] args, string card = null, bool connectorsForceProbe = false, [CanBeNull] DrmOutputOptions options = null)
where T : AppBuilderBase<T>, new() => StartLinuxDirect(builder, args, new DrmOutput(card, connectorsForceProbe, options));
public static int StartLinuxDirect<T>(this T builder, string[] args, IOutputBackend backend)
StartLinuxDirect(builder, args, new FbdevOutput(fileName: fbdev, format: format) { Scaling = scaling }, inputBackend);
public static int StartLinuxDrm<T>(this T builder, string[] args, string card = null, double scaling = 1, IInputBackend inputBackend = default)
where T : AppBuilderBase<T>, new() => StartLinuxDirect(builder, args, new DrmOutput(card) { Scaling = scaling }, inputBackend);
public static int StartLinuxDrm<T>(this T builder, string[] args, string card = null, bool connectorsForceProbe = false, [CanBeNull] DrmOutputOptions options = null, IInputBackend inputBackend = default)
where T : AppBuilderBase<T>, new() => StartLinuxDirect(builder, args, new DrmOutput(card, connectorsForceProbe, options), inputBackend);
public static int StartLinuxDirect<T>(this T builder, string[] args, IOutputBackend outputBackend, IInputBackend inputBackend = default)
where T : AppBuilderBase<T>, new()
{
var lifetime = LinuxFramebufferPlatform.Initialize(builder, backend);
var lifetime = LinuxFramebufferPlatform.Initialize(builder, outputBackend, inputBackend);
builder.SetupWithLifetime(lifetime);
lifetime.Start(args);
builder.Instance.Run(lifetime.Token);

4
src/tools/DevGenerators/CompositionGenerator/Generator.cs

@ -133,10 +133,6 @@ namespace Avalonia.SourceGenerator.CompositionGenerator
MethodDeclaration(ParseTypeName("void"), "Initialize")
.AddModifiers(SyntaxKind.PartialKeyword).WithSemicolonToken(Semicolon()));
var changesVarName = "c";
var changesVar = IdentifierName(changesVarName);
server = server.AddMembers(
MethodDeclaration(ParseTypeName("void"), "DeserializeChangesExtra")
.AddParameterListParameters(Parameter(Identifier("c")).WithType(ParseTypeName("BatchStreamReader")))

58
tests/Avalonia.Base.UnitTests/Input/PointerOverTests.cs

@ -356,20 +356,28 @@ namespace Avalonia.Base.UnitTests.Input
var impl = CreateTopLevelImplMock(renderer.Object);
var invalidateRect = new Rect(0, 0, 15, 15);
var lastClientPosition = new Point(1, 5);
var result = new List<(object?, string, Point)>();
void HandleEvent(object? sender, PointerEventArgs e)
{
result.Add((sender, e.RoutedEvent!.Name, e.GetPosition(null)));
}
Canvas canvas;
var root = CreateInputRoot(impl.Object, new Panel
var root = (Window)CreateInputRoot(impl.Object, new Panel
{
Children =
{
(canvas = new Canvas())
}
});
AddEnteredExitedHandlers(HandleEvent, root, canvas);
// Let input know about latest device.
SetHit(renderer, canvas);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root));
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root, lastClientPosition));
Assert.True(canvas.IsPointerOver);
SetHit(renderer, canvas);
@ -380,6 +388,52 @@ namespace Avalonia.Base.UnitTests.Input
SetHit(renderer, null);
renderer.Raise(r => r.SceneInvalidated += null, new SceneInvalidatedEventArgs((IRenderRoot)root, invalidateRect));
Assert.False(canvas.IsPointerOver);
Assert.Equal(
new[]
{
((object?)canvas, nameof(InputElement.PointerEntered), lastClientPosition),
(root, nameof(InputElement.PointerEntered), lastClientPosition),
(canvas, nameof(InputElement.PointerExited), lastClientPosition),
(root, nameof(InputElement.PointerExited), lastClientPosition),
},
result);
}
[Fact]
public void PointerOver_Invalidation_Should_Use_Previously_Captured_Element()
{
using var app = UnitTestApplication.Start(new TestServices(inputManager: new InputManager()));
var renderer = new Mock<IRenderer>();
var deviceMock = CreatePointerDeviceMock();
var impl = CreateTopLevelImplMock(renderer.Object);
var invalidateRect = new Rect(0, 0, 15, 15);
Canvas canvas1, canvas2;
var root = CreateInputRoot(impl.Object, new Panel
{
Children =
{
(canvas1 = new Canvas()),
(canvas2 = new Canvas())
}
});
canvas1.PointerMoved += (s, a) => a.Pointer.Capture(canvas1);
// Let input know about latest device.
SetHit(renderer, canvas1);
impl.Object.Input!(CreateRawPointerMovedArgs(deviceMock.Object, root));
Assert.True(canvas1.IsPointerOver);
Assert.False(canvas2.IsPointerOver);
SetHit(renderer, canvas2);
renderer.Raise(r => r.SceneInvalidated += null, new SceneInvalidatedEventArgs((IRenderRoot)root, invalidateRect));
Assert.False(canvas1.IsPointerOver);
Assert.True(canvas2.IsPointerOver);
}
// https://github.com/AvaloniaUI/Avalonia/issues/7748

2
tests/Avalonia.Base.UnitTests/Input/PointerTests.cs

@ -8,7 +8,7 @@ using Xunit;
namespace Avalonia.Base.UnitTests.Input
{
public class PointerTests
public class PointerTests : PointerTestsBase
{
[Fact]
public void On_Capture_Transfer_PointerCaptureLost_Should_Propagate_Up_To_The_Common_Parent()

2
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextFormatterTests.cs

@ -389,7 +389,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
if (textLine.Width > 300 || currentHeight + textLine.Height > 240)
{
textLine = textLine.Collapse(new TextTrailingWordEllipsis(new ReadOnlySlice<char>(new[] { TextTrimming.s_defaultEllipsisChar }), 300, defaultProperties));
textLine = textLine.Collapse(new TextTrailingWordEllipsis(new ReadOnlySlice<char>(new[] { TextTrimming.DefaultEllipsisChar }), 300, defaultProperties));
}
currentHeight += textLine.Height;

Loading…
Cancel
Save