committed by
GitHub
127 changed files with 4175 additions and 1237 deletions
@ -1,6 +1,6 @@ |
|||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="SkiaSharp" Version="1.68.1" /> |
<PackageReference Include="SkiaSharp" Version="1.68.2" /> |
||||
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="1.68.1" /> |
<PackageReference Condition="'$(IncludeLinuxSkia)' == 'true'" Include="SkiaSharp.NativeAssets.Linux" Version="1.68.2" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
</Project> |
</Project> |
||||
|
|||||
@ -1,8 +1,8 @@ |
|||||
#!/usr/bin/env bash |
#!/usr/bin/env bash |
||||
|
|
||||
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp2.0/ |
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp3.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netcoreapp3.1/ |
||||
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/ |
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp3.1/Avalonia**.dll ~/.nuget/packages/avalonia/$1/lib/netstandard2.0/ |
||||
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.skia/$1/lib/netstandard2.0/ |
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp3.1/Avalonia**.dll ~/.nuget/packages/avalonia.skia/$1/lib/netstandard2.0/ |
||||
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp2.0/Avalonia**.dll ~/.nuget/packages/avalonia.native/$1/lib/netstandard2.0/ |
cp ../samples/ControlCatalog.NetCore/bin/Debug/netcoreapp3.1/Avalonia**.dll ~/.nuget/packages/avalonia.native/$1/lib/netstandard2.0/ |
||||
|
|
||||
|
|
||||
|
|||||
@ -0,0 +1,349 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel; |
||||
|
using System.Globalization; |
||||
|
using System.Text; |
||||
|
using Avalonia; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
// Ported from WPF open-source code.
|
||||
|
// https://github.com/dotnet/wpf/blob/ae1790531c3b993b56eba8b1f0dd395a3ed7de75/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Media/Animation/KeySpline.cs
|
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Determines how an animation is used based on a cubic bezier curve.
|
||||
|
/// X1 and X2 must be between 0.0 and 1.0, inclusive.
|
||||
|
/// See https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.animation.keyspline
|
||||
|
/// </summary>
|
||||
|
[TypeConverter(typeof(KeySplineTypeConverter))] |
||||
|
public class KeySpline : AvaloniaObject |
||||
|
{ |
||||
|
// Control points
|
||||
|
private double _controlPointX1; |
||||
|
private double _controlPointY1; |
||||
|
private double _controlPointX2; |
||||
|
private double _controlPointY2; |
||||
|
private bool _isSpecified; |
||||
|
private bool _isDirty; |
||||
|
|
||||
|
// The parameter that corresponds to the most recent time
|
||||
|
private double _parameter; |
||||
|
|
||||
|
// Cached coefficients
|
||||
|
private double _Bx; // 3*points[0].X
|
||||
|
private double _Cx; // 3*points[1].X
|
||||
|
private double _Cx_Bx; // 2*(Cx - Bx)
|
||||
|
private double _three_Cx; // 3 - Cx
|
||||
|
|
||||
|
private double _By; // 3*points[0].Y
|
||||
|
private double _Cy; // 3*points[1].Y
|
||||
|
|
||||
|
// constants
|
||||
|
private const double _accuracy = .001; // 1/3 the desired accuracy in X
|
||||
|
private const double _fuzz = .000001; // computational zero
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a <see cref="KeySpline"/> with X1 = Y1 = 0 and X2 = Y2 = 1.
|
||||
|
/// </summary>
|
||||
|
public KeySpline() |
||||
|
{ |
||||
|
_controlPointX1 = 0.0; |
||||
|
_controlPointY1 = 0.0; |
||||
|
_controlPointX2 = 1.0; |
||||
|
_controlPointY2 = 1.0; |
||||
|
_isDirty = true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Create a <see cref="KeySpline"/> with the given parameters
|
||||
|
/// </summary>
|
||||
|
/// <param name="x1">X coordinate for the first control point</param>
|
||||
|
/// <param name="y1">Y coordinate for the first control point</param>
|
||||
|
/// <param name="x2">X coordinate for the second control point</param>
|
||||
|
/// <param name="y2">Y coordinate for the second control point</param>
|
||||
|
public KeySpline(double x1, double y1, double x2, double y2) |
||||
|
{ |
||||
|
_controlPointX1 = x1; |
||||
|
_controlPointY1 = y1; |
||||
|
_controlPointX2 = x2; |
||||
|
_controlPointY2 = y2; |
||||
|
_isDirty = true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Parse a <see cref="KeySpline"/> from a string. The string
|
||||
|
/// needs to contain 4 values in it for the 2 control points.
|
||||
|
/// </summary>
|
||||
|
/// <param name="value">string with 4 values in it</param>
|
||||
|
/// <param name="culture">culture of the string</param>
|
||||
|
/// <exception cref="FormatException">Thrown if the string does not have 4 values</exception>
|
||||
|
/// <returns>A <see cref="KeySpline"/> with the appropriate values set</returns>
|
||||
|
public static KeySpline Parse(string value, CultureInfo culture) |
||||
|
{ |
||||
|
using (var tokenizer = new StringTokenizer((string)value, CultureInfo.InvariantCulture, exceptionMessage: "Invalid KeySpline.")) |
||||
|
{ |
||||
|
return new KeySpline(tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble(), tokenizer.ReadDouble()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// X coordinate of the first control point
|
||||
|
/// </summary>
|
||||
|
public double ControlPointX1 |
||||
|
{ |
||||
|
get => _controlPointX1; |
||||
|
set |
||||
|
{ |
||||
|
if (IsValidXValue(value)) |
||||
|
{ |
||||
|
_controlPointX1 = value; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw new ArgumentException("Invalid KeySpline X1 value. Must be >= 0.0 and <= 1.0."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Y coordinate of the first control point
|
||||
|
/// </summary>
|
||||
|
public double ControlPointY1 |
||||
|
{ |
||||
|
get => _controlPointY1; |
||||
|
set => _controlPointY1 = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// X coordinate of the second control point
|
||||
|
/// </summary>
|
||||
|
public double ControlPointX2 |
||||
|
{ |
||||
|
get => _controlPointX2; |
||||
|
set |
||||
|
{ |
||||
|
if (IsValidXValue(value)) |
||||
|
{ |
||||
|
_controlPointX2 = value; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw new ArgumentException("Invalid KeySpline X2 value. Must be >= 0.0 and <= 1.0."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Y coordinate of the second control point
|
||||
|
/// </summary>
|
||||
|
public double ControlPointY2 |
||||
|
{ |
||||
|
get => _controlPointY2; |
||||
|
set => _controlPointY2 = value; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Calculates spline progress from a linear progress.
|
||||
|
/// </summary>
|
||||
|
/// <param name="linearProgress">the linear progress</param>
|
||||
|
/// <returns>the spline progress</returns>
|
||||
|
public double GetSplineProgress(double linearProgress) |
||||
|
{ |
||||
|
if (_isDirty) |
||||
|
{ |
||||
|
Build(); |
||||
|
} |
||||
|
|
||||
|
if (!_isSpecified) |
||||
|
{ |
||||
|
return linearProgress; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
SetParameterFromX(linearProgress); |
||||
|
|
||||
|
return GetBezierValue(_By, _Cy, _parameter); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Check to see whether the <see cref="KeySpline"/> is valid by looking
|
||||
|
/// at its X values.
|
||||
|
/// </summary>
|
||||
|
/// <returns>true if the X values for this <see cref="KeySpline"/> fall in
|
||||
|
/// acceptable range; false otherwise.</returns>
|
||||
|
public bool IsValid() |
||||
|
{ |
||||
|
return IsValidXValue(_controlPointX1) && IsValidXValue(_controlPointX2); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
///
|
||||
|
/// </summary>
|
||||
|
/// <param name="value"></param>
|
||||
|
/// <returns></returns>
|
||||
|
private bool IsValidXValue(double value) |
||||
|
{ |
||||
|
return value >= 0.0 && value <= 1.0; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compute cached coefficients.
|
||||
|
/// </summary>
|
||||
|
private void Build() |
||||
|
{ |
||||
|
if (_controlPointX1 == 0 && _controlPointY1 == 0 && _controlPointX2 == 1 && _controlPointY2 == 1) |
||||
|
{ |
||||
|
// This KeySpline would have no effect on the progress.
|
||||
|
_isSpecified = false; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_isSpecified = true; |
||||
|
|
||||
|
_parameter = 0; |
||||
|
|
||||
|
// X coefficients
|
||||
|
_Bx = 3 * _controlPointX1; |
||||
|
_Cx = 3 * _controlPointX2; |
||||
|
_Cx_Bx = 2 * (_Cx - _Bx); |
||||
|
_three_Cx = 3 - _Cx; |
||||
|
|
||||
|
// Y coefficients
|
||||
|
_By = 3 * _controlPointY1; |
||||
|
_Cy = 3 * _controlPointY2; |
||||
|
} |
||||
|
|
||||
|
_isDirty = false; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Get an X or Y value with the Bezier formula.
|
||||
|
/// </summary>
|
||||
|
/// <param name="b">the second Bezier coefficient</param>
|
||||
|
/// <param name="c">the third Bezier coefficient</param>
|
||||
|
/// <param name="t">the parameter value to evaluate at</param>
|
||||
|
/// <returns>the value of the Bezier function at the given parameter</returns>
|
||||
|
static private double GetBezierValue(double b, double c, double t) |
||||
|
{ |
||||
|
double s = 1.0 - t; |
||||
|
double t2 = t * t; |
||||
|
|
||||
|
return b * t * s * s + c * t2 * s + t2 * t; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Get X and dX/dt at a given parameter
|
||||
|
/// </summary>
|
||||
|
/// <param name="t">the parameter value to evaluate at</param>
|
||||
|
/// <param name="x">the value of x there</param>
|
||||
|
/// <param name="dx">the value of dx/dt there</param>
|
||||
|
private void GetXAndDx(double t, out double x, out double dx) |
||||
|
{ |
||||
|
double s = 1.0 - t; |
||||
|
double t2 = t * t; |
||||
|
double s2 = s * s; |
||||
|
|
||||
|
x = _Bx * t * s2 + _Cx * t2 * s + t2 * t; |
||||
|
dx = _Bx * s2 + _Cx_Bx * s * t + _three_Cx * t2; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Compute the parameter value that corresponds to a given X value, using a modified
|
||||
|
/// clamped Newton-Raphson algorithm to solve the equation X(t) - time = 0. We make
|
||||
|
/// use of some known properties of this particular function:
|
||||
|
/// * We are only interested in solutions in the interval [0,1]
|
||||
|
/// * X(t) is increasing, so we can assume that if X(t) > time t > solution. We use
|
||||
|
/// that to clamp down the search interval with every probe.
|
||||
|
/// * The derivative of X and Y are between 0 and 3.
|
||||
|
/// </summary>
|
||||
|
/// <param name="time">the time, scaled to fit in [0,1]</param>
|
||||
|
private void SetParameterFromX(double time) |
||||
|
{ |
||||
|
// Dynamic search interval to clamp with
|
||||
|
double bottom = 0; |
||||
|
double top = 1; |
||||
|
|
||||
|
if (time == 0) |
||||
|
{ |
||||
|
_parameter = 0; |
||||
|
} |
||||
|
else if (time == 1) |
||||
|
{ |
||||
|
_parameter = 1; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Loop while improving the guess
|
||||
|
while (top - bottom > _fuzz) |
||||
|
{ |
||||
|
double x, dx, absdx; |
||||
|
|
||||
|
// Get x and dx/dt at the current parameter
|
||||
|
GetXAndDx(_parameter, out x, out dx); |
||||
|
absdx = Math.Abs(dx); |
||||
|
|
||||
|
// Clamp down the search interval, relying on the monotonicity of X(t)
|
||||
|
if (x > time) |
||||
|
{ |
||||
|
top = _parameter; // because parameter > solution
|
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
bottom = _parameter; // because parameter < solution
|
||||
|
} |
||||
|
|
||||
|
// The desired accuracy is in ultimately in y, not in x, so the
|
||||
|
// accuracy needs to be multiplied by dx/dy = (dx/dt) / (dy/dt).
|
||||
|
// But dy/dt <=3, so we omit that
|
||||
|
if (Math.Abs(x - time) < _accuracy * absdx) |
||||
|
{ |
||||
|
break; // We're there
|
||||
|
} |
||||
|
|
||||
|
if (absdx > _fuzz) |
||||
|
{ |
||||
|
// Nonzero derivative, use Newton-Raphson to obtain the next guess
|
||||
|
double next = _parameter - (x - time) / dx; |
||||
|
|
||||
|
// If next guess is out of the search interval then clamp it in
|
||||
|
if (next >= top) |
||||
|
{ |
||||
|
_parameter = (_parameter + top) / 2; |
||||
|
} |
||||
|
else if (next <= bottom) |
||||
|
{ |
||||
|
_parameter = (_parameter + bottom) / 2; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// Next guess is inside the search interval, accept it
|
||||
|
_parameter = next; |
||||
|
} |
||||
|
} |
||||
|
else // Zero derivative, halve the search interval
|
||||
|
{ |
||||
|
_parameter = (bottom + top) / 2; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Converts string values to <see cref="KeySpline"/> values
|
||||
|
/// </summary>
|
||||
|
public class KeySplineTypeConverter : TypeConverter |
||||
|
{ |
||||
|
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
||||
|
{ |
||||
|
return sourceType == typeof(string); |
||||
|
} |
||||
|
|
||||
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
||||
|
{ |
||||
|
return KeySpline.Parse((string)value, culture); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public interface INativeMenuExporterEventsImplBridge |
||||
|
{ |
||||
|
void RaiseNeedsUpdate (); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
public interface INativeMenuItemExporterEventsImplBridge |
||||
|
{ |
||||
|
void RaiseClicked (); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Interactivity; |
||||
|
|
||||
|
#nullable enable |
||||
|
|
||||
|
namespace Avalonia.Controls.Primitives |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Holds data for the <see cref="Popup.Closed"/> event.
|
||||
|
/// </summary>
|
||||
|
public class PopupClosedEventArgs : EventArgs |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="PopupClosedEventArgs"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="closeEvent"></param>
|
||||
|
public PopupClosedEventArgs(EventArgs? closeEvent) |
||||
|
{ |
||||
|
CloseEvent = closeEvent; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the event that closed the popup, if any.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// If <see cref="Popup.StaysOpen"/> is false, then this property will hold details of the
|
||||
|
/// interaction that caused the popup to close if the close was caused by e.g. a pointer press
|
||||
|
/// outside the popup. It can be used to mark the event as handled if the event should not
|
||||
|
/// be propagated.
|
||||
|
/// </remarks>
|
||||
|
public EventArgs? CloseEvent { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
using Avalonia.Interactivity; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Describes a change in scrolling state.
|
||||
|
/// </summary>
|
||||
|
public class ScrollChangedEventArgs : RoutedEventArgs |
||||
|
{ |
||||
|
public ScrollChangedEventArgs( |
||||
|
Vector extentDelta, |
||||
|
Vector offsetDelta, |
||||
|
Vector viewportDelta) |
||||
|
: this(ScrollViewer.ScrollChangedEvent, extentDelta, offsetDelta, viewportDelta) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public ScrollChangedEventArgs( |
||||
|
RoutedEvent routedEvent, |
||||
|
Vector extentDelta, |
||||
|
Vector offsetDelta, |
||||
|
Vector viewportDelta) |
||||
|
: base(routedEvent) |
||||
|
{ |
||||
|
ExtentDelta = extentDelta; |
||||
|
OffsetDelta = offsetDelta; |
||||
|
ViewportDelta = viewportDelta; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the change to the value of <see cref="ScrollViewer.Extent"/>.
|
||||
|
/// </summary>
|
||||
|
public Vector ExtentDelta { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the change to the value of <see cref="ScrollViewer.Offset"/>.
|
||||
|
/// </summary>
|
||||
|
public Vector OffsetDelta { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the change to the value of <see cref="ScrollViewer.Viewport"/>.
|
||||
|
/// </summary>
|
||||
|
public Vector ViewportDelta { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
// This source file is adapted from the WinUI project.
|
||||
|
// (https://github.com/microsoft/microsoft-ui-xaml)
|
||||
|
//
|
||||
|
// Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Layout |
||||
|
{ |
||||
|
internal class LayoutContextAdapter : VirtualizingLayoutContext |
||||
|
{ |
||||
|
private readonly NonVirtualizingLayoutContext _nonVirtualizingContext; |
||||
|
|
||||
|
public LayoutContextAdapter(NonVirtualizingLayoutContext nonVirtualizingContext) |
||||
|
{ |
||||
|
_nonVirtualizingContext = nonVirtualizingContext; |
||||
|
} |
||||
|
|
||||
|
protected override object LayoutStateCore |
||||
|
{ |
||||
|
get => _nonVirtualizingContext.LayoutState; |
||||
|
set => _nonVirtualizingContext.LayoutState = value; |
||||
|
} |
||||
|
|
||||
|
protected override Point LayoutOriginCore |
||||
|
{ |
||||
|
get => default; |
||||
|
set |
||||
|
{ |
||||
|
if (value != default) |
||||
|
{ |
||||
|
throw new InvalidOperationException("LayoutOrigin must be at (0,0) when RealizationRect is infinite sized."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override Rect RealizationRectCore() => new Rect(Size.Infinity); |
||||
|
|
||||
|
protected override int ItemCountCore() => _nonVirtualizingContext.Children.Count; |
||||
|
protected override object GetItemAtCore(int index) => _nonVirtualizingContext.Children[index]; |
||||
|
protected override ILayoutable GetOrCreateElementAtCore(int index, ElementRealizationOptions options) => |
||||
|
_nonVirtualizingContext.Children[index]; |
||||
|
protected override void RecycleElementCore(ILayoutable element) { } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,160 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
using Avalonia.Data; |
||||
|
|
||||
|
namespace Avalonia.Layout |
||||
|
{ |
||||
|
public class NonVirtualizingStackLayout : NonVirtualizingLayout |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Orientation"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<Orientation> OrientationProperty = |
||||
|
StackLayout.OrientationProperty.AddOwner<NonVirtualizingStackLayout>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="Spacing"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<double> SpacingProperty = |
||||
|
StackLayout.SpacingProperty.AddOwner<NonVirtualizingStackLayout>(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets the axis along which items are laid out.
|
||||
|
/// </summary>
|
||||
|
/// <value>
|
||||
|
/// One of the enumeration values that specifies the axis along which items are laid out.
|
||||
|
/// The default is Vertical.
|
||||
|
/// </value>
|
||||
|
public Orientation Orientation |
||||
|
{ |
||||
|
get => GetValue(OrientationProperty); |
||||
|
set => SetValue(OrientationProperty, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or sets a uniform distance (in pixels) between stacked items. It is applied in the
|
||||
|
/// direction of the StackLayout's Orientation.
|
||||
|
/// </summary>
|
||||
|
public double Spacing |
||||
|
{ |
||||
|
get => GetValue(SpacingProperty); |
||||
|
set => SetValue(SpacingProperty, value); |
||||
|
} |
||||
|
|
||||
|
protected internal override Size MeasureOverride( |
||||
|
NonVirtualizingLayoutContext context, |
||||
|
Size availableSize) |
||||
|
{ |
||||
|
var extentU = 0.0; |
||||
|
var extentV = 0.0; |
||||
|
var childCount = context.Children.Count; |
||||
|
var isVertical = Orientation == Orientation.Vertical; |
||||
|
var spacing = Spacing; |
||||
|
var constraint = isVertical ? |
||||
|
availableSize.WithHeight(double.PositiveInfinity) : |
||||
|
availableSize.WithWidth(double.PositiveInfinity); |
||||
|
|
||||
|
for (var i = 0; i < childCount; ++i) |
||||
|
{ |
||||
|
var element = context.Children[i]; |
||||
|
|
||||
|
if (!element.IsVisible) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
element.Measure(constraint); |
||||
|
|
||||
|
if (isVertical) |
||||
|
{ |
||||
|
extentU += element.DesiredSize.Height; |
||||
|
extentV = Math.Max(extentV, element.DesiredSize.Width); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
extentU += element.DesiredSize.Width; |
||||
|
extentV = Math.Max(extentV, element.DesiredSize.Height); |
||||
|
} |
||||
|
|
||||
|
if (i < childCount - 1) |
||||
|
{ |
||||
|
extentU += spacing; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return isVertical ? new Size(extentV, extentU) : new Size(extentU, extentV); |
||||
|
} |
||||
|
|
||||
|
protected internal override Size ArrangeOverride( |
||||
|
NonVirtualizingLayoutContext context, |
||||
|
Size finalSize) |
||||
|
{ |
||||
|
var u = 0.0; |
||||
|
var childCount = context.Children.Count; |
||||
|
var isVertical = Orientation == Orientation.Vertical; |
||||
|
var spacing = Spacing; |
||||
|
var bounds = new Rect(); |
||||
|
|
||||
|
for (var i = 0; i < childCount; ++i) |
||||
|
{ |
||||
|
var element = context.Children[i]; |
||||
|
|
||||
|
if (!element.IsVisible) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
bounds = isVertical ? |
||||
|
LayoutVertical(element, u, finalSize) : |
||||
|
LayoutHorizontal(element, u, finalSize); |
||||
|
element.Arrange(bounds); |
||||
|
u = (isVertical ? bounds.Bottom : bounds.Right) + spacing; |
||||
|
} |
||||
|
|
||||
|
return new Size(bounds.Right, bounds.Bottom); |
||||
|
} |
||||
|
|
||||
|
private static Rect LayoutVertical(ILayoutable element, double y, Size constraint) |
||||
|
{ |
||||
|
var x = 0.0; |
||||
|
var width = element.DesiredSize.Width; |
||||
|
|
||||
|
switch (element.HorizontalAlignment) |
||||
|
{ |
||||
|
case HorizontalAlignment.Center: |
||||
|
x += (constraint.Width - element.DesiredSize.Width) / 2; |
||||
|
break; |
||||
|
case HorizontalAlignment.Right: |
||||
|
x += constraint.Width - element.DesiredSize.Width; |
||||
|
break; |
||||
|
case HorizontalAlignment.Stretch: |
||||
|
width = constraint.Width; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return new Rect(x, y, width, element.DesiredSize.Height); |
||||
|
} |
||||
|
|
||||
|
private static Rect LayoutHorizontal(ILayoutable element, double x, Size constraint) |
||||
|
{ |
||||
|
var y = 0.0; |
||||
|
var height = element.DesiredSize.Height; |
||||
|
|
||||
|
switch (element.VerticalAlignment) |
||||
|
{ |
||||
|
case VerticalAlignment.Center: |
||||
|
y += (constraint.Height - element.DesiredSize.Height) / 2; |
||||
|
break; |
||||
|
case VerticalAlignment.Bottom: |
||||
|
y += constraint.Height - element.DesiredSize.Height; |
||||
|
break; |
||||
|
case VerticalAlignment.Stretch: |
||||
|
height = constraint.Height; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return new Rect(x, y, element.DesiredSize.Width, height); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Avalonia.Layout |
||||
|
{ |
||||
|
public class VirtualLayoutContextAdapter : NonVirtualizingLayoutContext |
||||
|
{ |
||||
|
private readonly VirtualizingLayoutContext _virtualizingContext; |
||||
|
private ChildrenCollection _children; |
||||
|
|
||||
|
public VirtualLayoutContextAdapter(VirtualizingLayoutContext virtualizingContext) |
||||
|
{ |
||||
|
_virtualizingContext = virtualizingContext; |
||||
|
} |
||||
|
|
||||
|
protected override object LayoutStateCore |
||||
|
{ |
||||
|
get => _virtualizingContext.LayoutState; |
||||
|
set => _virtualizingContext.LayoutState = value; |
||||
|
} |
||||
|
|
||||
|
protected override IReadOnlyList<ILayoutable> ChildrenCore => |
||||
|
_children ?? (_children = new ChildrenCollection(_virtualizingContext)); |
||||
|
|
||||
|
private class ChildrenCollection : IReadOnlyList<ILayoutable> |
||||
|
{ |
||||
|
private readonly VirtualizingLayoutContext _context; |
||||
|
public ChildrenCollection(VirtualizingLayoutContext context) => _context = context; |
||||
|
public ILayoutable this[int index] => _context.GetOrCreateElementAt(index); |
||||
|
public int Count => _context.ItemCount; |
||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); |
||||
|
|
||||
|
public IEnumerator<ILayoutable> GetEnumerator() |
||||
|
{ |
||||
|
for (var i = 0; i < Count; ++i) |
||||
|
{ |
||||
|
yield return this[i]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,176 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Specialized; |
||||
|
using System.Reactive.Disposables; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Platform.Interop; |
||||
|
|
||||
|
namespace Avalonia.Native.Interop |
||||
|
{ |
||||
|
class MenuEvents : CallbackBase, IAvnMenuEvents |
||||
|
{ |
||||
|
private IAvnMenu _parent; |
||||
|
|
||||
|
public void Initialise(IAvnMenu parent) |
||||
|
{ |
||||
|
_parent = parent; |
||||
|
} |
||||
|
|
||||
|
public void NeedsUpdate() |
||||
|
{ |
||||
|
_parent?.RaiseNeedsUpdate(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public partial class IAvnMenu |
||||
|
{ |
||||
|
private MenuEvents _events; |
||||
|
private AvaloniaNativeMenuExporter _exporter; |
||||
|
private List<IAvnMenuItem> _menuItems = new List<IAvnMenuItem>(); |
||||
|
private Dictionary<NativeMenuItemBase, IAvnMenuItem> _menuItemLookup = new Dictionary<NativeMenuItemBase, IAvnMenuItem>(); |
||||
|
private CompositeDisposable _propertyDisposables = new CompositeDisposable(); |
||||
|
|
||||
|
internal void RaiseNeedsUpdate() |
||||
|
{ |
||||
|
(ManagedMenu as INativeMenuExporterEventsImplBridge).RaiseNeedsUpdate(); |
||||
|
|
||||
|
_exporter.UpdateIfNeeded(); |
||||
|
} |
||||
|
|
||||
|
internal NativeMenu ManagedMenu { get; private set; } |
||||
|
|
||||
|
public static IAvnMenu Create(IAvaloniaNativeFactory factory) |
||||
|
{ |
||||
|
var events = new MenuEvents(); |
||||
|
|
||||
|
var menu = factory.CreateMenu(events); |
||||
|
|
||||
|
events.Initialise(menu); |
||||
|
|
||||
|
menu._events = events; |
||||
|
|
||||
|
return menu; |
||||
|
} |
||||
|
|
||||
|
protected override void Dispose(bool disposing) |
||||
|
{ |
||||
|
if (disposing) |
||||
|
{ |
||||
|
_events.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void RemoveAndDispose(IAvnMenuItem item) |
||||
|
{ |
||||
|
_menuItemLookup.Remove(item.ManagedMenuItem); |
||||
|
_menuItems.Remove(item); |
||||
|
RemoveItem(item); |
||||
|
|
||||
|
item.Deinitialise(); |
||||
|
item.Dispose(); |
||||
|
} |
||||
|
|
||||
|
private void MoveExistingTo(int index, IAvnMenuItem item) |
||||
|
{ |
||||
|
_menuItems.Remove(item); |
||||
|
_menuItems.Insert(index, item); |
||||
|
|
||||
|
RemoveItem(item); |
||||
|
InsertItem(index, item); |
||||
|
} |
||||
|
|
||||
|
private IAvnMenuItem CreateNewAt(IAvaloniaNativeFactory factory, int index, NativeMenuItemBase item) |
||||
|
{ |
||||
|
var result = CreateNew(factory, item); |
||||
|
|
||||
|
result.Initialise(item); |
||||
|
|
||||
|
_menuItemLookup.Add(result.ManagedMenuItem, result); |
||||
|
_menuItems.Insert(index, result); |
||||
|
|
||||
|
InsertItem(index, result); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private IAvnMenuItem CreateNew(IAvaloniaNativeFactory factory, NativeMenuItemBase item) |
||||
|
{ |
||||
|
var nativeItem = item is NativeMenuItemSeperator ? factory.CreateMenuItemSeperator() : factory.CreateMenuItem(); |
||||
|
nativeItem.ManagedMenuItem = item; |
||||
|
|
||||
|
return nativeItem; |
||||
|
} |
||||
|
|
||||
|
internal void Initialise(AvaloniaNativeMenuExporter exporter, NativeMenu managedMenu, string title) |
||||
|
{ |
||||
|
_exporter = exporter; |
||||
|
ManagedMenu = managedMenu; |
||||
|
|
||||
|
((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged += OnMenuItemsChanged; |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(title)) |
||||
|
{ |
||||
|
using (var buffer = new Utf8Buffer(title)) |
||||
|
{ |
||||
|
Title = buffer.DangerousGetHandle(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal void Deinitialise() |
||||
|
{ |
||||
|
((INotifyCollectionChanged)ManagedMenu.Items).CollectionChanged -= OnMenuItemsChanged; |
||||
|
|
||||
|
foreach (var item in _menuItems) |
||||
|
{ |
||||
|
item.Deinitialise(); |
||||
|
item.Dispose(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal void Update(IAvaloniaNativeFactory factory, NativeMenu menu) |
||||
|
{ |
||||
|
if (menu != ManagedMenu) |
||||
|
{ |
||||
|
throw new ArgumentException("The menu being updated does not match.", nameof(menu)); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i < menu.Items.Count; i++) |
||||
|
{ |
||||
|
IAvnMenuItem nativeItem; |
||||
|
|
||||
|
if (i >= _menuItems.Count) |
||||
|
{ |
||||
|
nativeItem = CreateNewAt(factory, i, menu.Items[i]); |
||||
|
} |
||||
|
else if (menu.Items[i] == _menuItems[i].ManagedMenuItem) |
||||
|
{ |
||||
|
nativeItem = _menuItems[i]; |
||||
|
} |
||||
|
else if (_menuItemLookup.TryGetValue(menu.Items[i], out nativeItem)) |
||||
|
{ |
||||
|
MoveExistingTo(i, nativeItem); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
nativeItem = CreateNewAt(factory, i, menu.Items[i]); |
||||
|
} |
||||
|
|
||||
|
if (menu.Items[i] is NativeMenuItem nmi) |
||||
|
{ |
||||
|
nativeItem.Update(_exporter, factory, nmi); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
while (_menuItems.Count > menu.Items.Count) |
||||
|
{ |
||||
|
RemoveAndDispose(_menuItems[_menuItems.Count - 1]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void OnMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs e) |
||||
|
{ |
||||
|
_exporter.QueueReset(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,175 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Reactive.Disposables; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Media.Imaging; |
||||
|
using Avalonia.Platform.Interop; |
||||
|
|
||||
|
namespace Avalonia.Native.Interop |
||||
|
{ |
||||
|
public partial class IAvnMenuItem |
||||
|
{ |
||||
|
private IAvnMenu _subMenu; |
||||
|
private CompositeDisposable _propertyDisposables = new CompositeDisposable(); |
||||
|
private IDisposable _currentActionDisposable; |
||||
|
|
||||
|
public NativeMenuItemBase ManagedMenuItem { get; set; } |
||||
|
|
||||
|
private void UpdateTitle(string title) |
||||
|
{ |
||||
|
using (var buffer = new Utf8Buffer(string.IsNullOrWhiteSpace(title) ? "" : title)) |
||||
|
{ |
||||
|
Title = buffer.DangerousGetHandle(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void UpdateIsChecked(bool isChecked) |
||||
|
{ |
||||
|
IsChecked = isChecked; |
||||
|
} |
||||
|
|
||||
|
private void UpdateToggleType(NativeMenuItemToggleType toggleType) |
||||
|
{ |
||||
|
ToggleType = (AvnMenuItemToggleType)toggleType; |
||||
|
} |
||||
|
|
||||
|
private unsafe void UpdateIcon (IBitmap icon) |
||||
|
{ |
||||
|
if(icon is null) |
||||
|
{ |
||||
|
SetIcon(IntPtr.Zero, 0); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
using(var ms = new MemoryStream()) |
||||
|
{ |
||||
|
icon.Save(ms); |
||||
|
|
||||
|
var imageData = ms.ToArray(); |
||||
|
|
||||
|
fixed(void* ptr = imageData) |
||||
|
{ |
||||
|
SetIcon(new IntPtr(ptr), imageData.Length); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void UpdateGesture(Input.KeyGesture gesture) |
||||
|
{ |
||||
|
// todo ensure backend can cope with setting null gesture.
|
||||
|
using (var buffer = new Utf8Buffer(gesture == null ? "" : OsxUnicodeKeys.ConvertOSXSpecialKeyCodes(gesture.Key))) |
||||
|
{ |
||||
|
var modifiers = gesture == null ? AvnInputModifiers.AvnInputModifiersNone : (AvnInputModifiers)gesture.KeyModifiers; |
||||
|
SetGesture(buffer.DangerousGetHandle(), modifiers); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void UpdateAction(NativeMenuItem item) |
||||
|
{ |
||||
|
_currentActionDisposable?.Dispose(); |
||||
|
|
||||
|
var action = new PredicateCallback(() => |
||||
|
{ |
||||
|
if (item.Command != null || item.HasClickHandlers) |
||||
|
{ |
||||
|
return item.IsEnabled; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
}); |
||||
|
|
||||
|
var callback = new MenuActionCallback(() => { (item as INativeMenuItemExporterEventsImplBridge)?.RaiseClicked(); }); |
||||
|
|
||||
|
_currentActionDisposable = Disposable.Create(() => |
||||
|
{ |
||||
|
action.Dispose(); |
||||
|
callback.Dispose(); |
||||
|
}); |
||||
|
|
||||
|
SetAction(action, callback); |
||||
|
} |
||||
|
|
||||
|
internal void Initialise(NativeMenuItemBase nativeMenuItem) |
||||
|
{ |
||||
|
ManagedMenuItem = nativeMenuItem; |
||||
|
|
||||
|
if (ManagedMenuItem is NativeMenuItem item) |
||||
|
{ |
||||
|
UpdateTitle(item.Header); |
||||
|
|
||||
|
UpdateGesture(item.Gesture); |
||||
|
|
||||
|
UpdateAction(ManagedMenuItem as NativeMenuItem); |
||||
|
|
||||
|
UpdateToggleType(item.ToggleType); |
||||
|
|
||||
|
UpdateIcon(item.Icon); |
||||
|
|
||||
|
UpdateIsChecked(item.IsChecked); |
||||
|
|
||||
|
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.HeaderProperty) |
||||
|
.Subscribe(x => UpdateTitle(x))); |
||||
|
|
||||
|
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.GestureProperty) |
||||
|
.Subscribe(x => UpdateGesture(x))); |
||||
|
|
||||
|
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.CommandProperty) |
||||
|
.Subscribe(x => UpdateAction(ManagedMenuItem as NativeMenuItem))); |
||||
|
|
||||
|
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.ToggleTypeProperty) |
||||
|
.Subscribe(x => UpdateToggleType(x))); |
||||
|
|
||||
|
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.IsCheckedProperty) |
||||
|
.Subscribe(x => UpdateIsChecked(x))); |
||||
|
|
||||
|
_propertyDisposables.Add(ManagedMenuItem.GetObservable(NativeMenuItem.IconProperty) |
||||
|
.Subscribe(x => UpdateIcon(x))); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal void Deinitialise() |
||||
|
{ |
||||
|
if (_subMenu != null) |
||||
|
{ |
||||
|
SetSubMenu(null); |
||||
|
_subMenu.Deinitialise(); |
||||
|
_subMenu.Dispose(); |
||||
|
_subMenu = null; |
||||
|
} |
||||
|
|
||||
|
_propertyDisposables?.Dispose(); |
||||
|
_currentActionDisposable?.Dispose(); |
||||
|
} |
||||
|
|
||||
|
internal void Update(AvaloniaNativeMenuExporter exporter, IAvaloniaNativeFactory factory, NativeMenuItem item) |
||||
|
{ |
||||
|
if (item != ManagedMenuItem) |
||||
|
{ |
||||
|
throw new ArgumentException("The item does not match the menuitem being updated.", nameof(item)); |
||||
|
} |
||||
|
|
||||
|
if (item.Menu != null) |
||||
|
{ |
||||
|
if (_subMenu == null) |
||||
|
{ |
||||
|
_subMenu = IAvnMenu.Create(factory); |
||||
|
|
||||
|
_subMenu.Initialise(exporter, item.Menu, item.Header); |
||||
|
|
||||
|
SetSubMenu(_subMenu); |
||||
|
} |
||||
|
|
||||
|
_subMenu.Update(factory, item.Menu); |
||||
|
} |
||||
|
|
||||
|
if (item.Menu == null && _subMenu != null) |
||||
|
{ |
||||
|
_subMenu.Deinitialise(); |
||||
|
_subMenu.Dispose(); |
||||
|
|
||||
|
SetSubMenu(null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Native.Interop; |
||||
|
|
||||
|
namespace Avalonia.Native |
||||
|
{ |
||||
|
public class MenuActionCallback : CallbackBase, IAvnActionCallback |
||||
|
{ |
||||
|
private Action _action; |
||||
|
|
||||
|
public MenuActionCallback(Action action) |
||||
|
{ |
||||
|
_action = action; |
||||
|
} |
||||
|
|
||||
|
void IAvnActionCallback.Run() |
||||
|
{ |
||||
|
_action?.Invoke(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,147 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Input; |
||||
|
|
||||
|
namespace Avalonia.Native.Interop |
||||
|
{ |
||||
|
internal static class OsxUnicodeKeys |
||||
|
{ |
||||
|
enum OsxUnicodeSpecialKey |
||||
|
{ |
||||
|
NSUpArrowFunctionKey = 0xF700, |
||||
|
NSDownArrowFunctionKey = 0xF701, |
||||
|
NSLeftArrowFunctionKey = 0xF702, |
||||
|
NSRightArrowFunctionKey = 0xF703, |
||||
|
NSF1FunctionKey = 0xF704, |
||||
|
NSF2FunctionKey = 0xF705, |
||||
|
NSF3FunctionKey = 0xF706, |
||||
|
NSF4FunctionKey = 0xF707, |
||||
|
NSF5FunctionKey = 0xF708, |
||||
|
NSF6FunctionKey = 0xF709, |
||||
|
NSF7FunctionKey = 0xF70A, |
||||
|
NSF8FunctionKey = 0xF70B, |
||||
|
NSF9FunctionKey = 0xF70C, |
||||
|
NSF10FunctionKey = 0xF70D, |
||||
|
NSF11FunctionKey = 0xF70E, |
||||
|
NSF12FunctionKey = 0xF70F, |
||||
|
NSF13FunctionKey = 0xF710, |
||||
|
NSF14FunctionKey = 0xF711, |
||||
|
NSF15FunctionKey = 0xF712, |
||||
|
NSF16FunctionKey = 0xF713, |
||||
|
NSF17FunctionKey = 0xF714, |
||||
|
NSF18FunctionKey = 0xF715, |
||||
|
NSF19FunctionKey = 0xF716, |
||||
|
NSF20FunctionKey = 0xF717, |
||||
|
NSF21FunctionKey = 0xF718, |
||||
|
NSF22FunctionKey = 0xF719, |
||||
|
NSF23FunctionKey = 0xF71A, |
||||
|
NSF24FunctionKey = 0xF71B, |
||||
|
NSF25FunctionKey = 0xF71C, |
||||
|
NSF26FunctionKey = 0xF71D, |
||||
|
NSF27FunctionKey = 0xF71E, |
||||
|
NSF28FunctionKey = 0xF71F, |
||||
|
NSF29FunctionKey = 0xF720, |
||||
|
NSF30FunctionKey = 0xF721, |
||||
|
NSF31FunctionKey = 0xF722, |
||||
|
NSF32FunctionKey = 0xF723, |
||||
|
NSF33FunctionKey = 0xF724, |
||||
|
NSF34FunctionKey = 0xF725, |
||||
|
NSF35FunctionKey = 0xF726, |
||||
|
NSInsertFunctionKey = 0xF727, |
||||
|
NSDeleteFunctionKey = 0xF728, |
||||
|
NSHomeFunctionKey = 0xF729, |
||||
|
NSBeginFunctionKey = 0xF72A, |
||||
|
NSEndFunctionKey = 0xF72B, |
||||
|
NSPageUpFunctionKey = 0xF72C, |
||||
|
NSPageDownFunctionKey = 0xF72D, |
||||
|
NSPrintScreenFunctionKey = 0xF72E, |
||||
|
NSScrollLockFunctionKey = 0xF72F, |
||||
|
NSPauseFunctionKey = 0xF730, |
||||
|
NSSysReqFunctionKey = 0xF731, |
||||
|
NSBreakFunctionKey = 0xF732, |
||||
|
NSResetFunctionKey = 0xF733, |
||||
|
NSStopFunctionKey = 0xF734, |
||||
|
NSMenuFunctionKey = 0xF735, |
||||
|
NSUserFunctionKey = 0xF736, |
||||
|
NSSystemFunctionKey = 0xF737, |
||||
|
NSPrintFunctionKey = 0xF738, |
||||
|
NSClearLineFunctionKey = 0xF739, |
||||
|
NSClearDisplayFunctionKey = 0xF73A, |
||||
|
NSInsertLineFunctionKey = 0xF73B, |
||||
|
NSDeleteLineFunctionKey = 0xF73C, |
||||
|
NSInsertCharFunctionKey = 0xF73D, |
||||
|
NSDeleteCharFunctionKey = 0xF73E, |
||||
|
NSPrevFunctionKey = 0xF73F, |
||||
|
NSNextFunctionKey = 0xF740, |
||||
|
NSSelectFunctionKey = 0xF741, |
||||
|
NSExecuteFunctionKey = 0xF742, |
||||
|
NSUndoFunctionKey = 0xF743, |
||||
|
NSRedoFunctionKey = 0xF744, |
||||
|
NSFindFunctionKey = 0xF745, |
||||
|
NSHelpFunctionKey = 0xF746, |
||||
|
NSModeSwitchFunctionKey = 0xF747 |
||||
|
} |
||||
|
|
||||
|
private static Dictionary<Key, OsxUnicodeSpecialKey> s_osxKeys = new Dictionary<Key, OsxUnicodeSpecialKey> |
||||
|
{ |
||||
|
{Key.Up, OsxUnicodeSpecialKey.NSUpArrowFunctionKey }, |
||||
|
{Key.Down, OsxUnicodeSpecialKey.NSDownArrowFunctionKey }, |
||||
|
{Key.Left, OsxUnicodeSpecialKey.NSLeftArrowFunctionKey }, |
||||
|
{Key.Right, OsxUnicodeSpecialKey.NSRightArrowFunctionKey }, |
||||
|
{ Key.F1, OsxUnicodeSpecialKey.NSF1FunctionKey }, |
||||
|
{ Key.F2, OsxUnicodeSpecialKey.NSF2FunctionKey }, |
||||
|
{ Key.F3, OsxUnicodeSpecialKey.NSF3FunctionKey }, |
||||
|
{ Key.F4, OsxUnicodeSpecialKey.NSF4FunctionKey }, |
||||
|
{ Key.F5, OsxUnicodeSpecialKey.NSF5FunctionKey }, |
||||
|
{ Key.F6, OsxUnicodeSpecialKey.NSF6FunctionKey }, |
||||
|
{ Key.F7, OsxUnicodeSpecialKey.NSF7FunctionKey }, |
||||
|
{ Key.F8, OsxUnicodeSpecialKey.NSF8FunctionKey }, |
||||
|
{ Key.F9, OsxUnicodeSpecialKey.NSF9FunctionKey }, |
||||
|
{ Key.F10, OsxUnicodeSpecialKey.NSF10FunctionKey }, |
||||
|
{ Key.F11, OsxUnicodeSpecialKey.NSF11FunctionKey }, |
||||
|
{ Key.F12, OsxUnicodeSpecialKey.NSF12FunctionKey }, |
||||
|
{ Key.F13, OsxUnicodeSpecialKey.NSF13FunctionKey }, |
||||
|
{ Key.F14, OsxUnicodeSpecialKey.NSF14FunctionKey }, |
||||
|
{ Key.F15, OsxUnicodeSpecialKey.NSF15FunctionKey }, |
||||
|
{ Key.F16, OsxUnicodeSpecialKey.NSF16FunctionKey }, |
||||
|
{ Key.F17, OsxUnicodeSpecialKey.NSF17FunctionKey }, |
||||
|
{ Key.F18, OsxUnicodeSpecialKey.NSF18FunctionKey }, |
||||
|
{ Key.F19, OsxUnicodeSpecialKey.NSF19FunctionKey }, |
||||
|
{ Key.F20, OsxUnicodeSpecialKey.NSF20FunctionKey }, |
||||
|
{ Key.F21, OsxUnicodeSpecialKey.NSF21FunctionKey }, |
||||
|
{ Key.F22, OsxUnicodeSpecialKey.NSF22FunctionKey }, |
||||
|
{ Key.F23, OsxUnicodeSpecialKey.NSF23FunctionKey }, |
||||
|
{ Key.F24, OsxUnicodeSpecialKey.NSF24FunctionKey }, |
||||
|
{ Key.Insert, OsxUnicodeSpecialKey.NSInsertFunctionKey }, |
||||
|
{ Key.Delete, OsxUnicodeSpecialKey.NSDeleteFunctionKey }, |
||||
|
{ Key.Home, OsxUnicodeSpecialKey.NSHomeFunctionKey }, |
||||
|
//{ Key.Begin, OsxUnicodeSpecialKey.NSBeginFunctionKey },
|
||||
|
{ Key.End, OsxUnicodeSpecialKey.NSEndFunctionKey }, |
||||
|
{ Key.PageUp, OsxUnicodeSpecialKey.NSPageUpFunctionKey }, |
||||
|
{ Key.PageDown, OsxUnicodeSpecialKey.NSPageDownFunctionKey }, |
||||
|
{ Key.PrintScreen, OsxUnicodeSpecialKey.NSPrintScreenFunctionKey }, |
||||
|
{ Key.Scroll, OsxUnicodeSpecialKey.NSScrollLockFunctionKey }, |
||||
|
//{ Key.SysReq, OsxUnicodeSpecialKey.NSSysReqFunctionKey },
|
||||
|
//{ Key.Break, OsxUnicodeSpecialKey.NSBreakFunctionKey },
|
||||
|
//{ Key.Reset, OsxUnicodeSpecialKey.NSResetFunctionKey },
|
||||
|
//{ Key.Stop, OsxUnicodeSpecialKey.NSStopFunctionKey },
|
||||
|
//{ Key.Menu, OsxUnicodeSpecialKey.NSMenuFunctionKey },
|
||||
|
//{ Key.UserFunction, OsxUnicodeSpecialKey.NSUserFunctionKey },
|
||||
|
//{ Key.SystemFunction, OsxUnicodeSpecialKey.NSSystemFunctionKey },
|
||||
|
{ Key.Print, OsxUnicodeSpecialKey.NSPrintFunctionKey }, |
||||
|
//{ Key.ClearLine, OsxUnicodeSpecialKey.NSClearLineFunctionKey },
|
||||
|
//{ Key.ClearDisplay, OsxUnicodeSpecialKey.NSClearDisplayFunctionKey },
|
||||
|
}; |
||||
|
|
||||
|
public static string ConvertOSXSpecialKeyCodes(Key key) |
||||
|
{ |
||||
|
if (s_osxKeys.ContainsKey(key)) |
||||
|
{ |
||||
|
return ((char)s_osxKeys[key]).ToString(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return key.ToString().ToLower(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Native.Interop; |
||||
|
|
||||
|
namespace Avalonia.Native |
||||
|
{ |
||||
|
public class PredicateCallback : CallbackBase, IAvnPredicateCallback |
||||
|
{ |
||||
|
private Func<bool> _predicate; |
||||
|
|
||||
|
public PredicateCallback(Func<bool> predicate) |
||||
|
{ |
||||
|
_predicate = predicate; |
||||
|
} |
||||
|
|
||||
|
bool IAvnPredicateCallback.Evaluate() |
||||
|
{ |
||||
|
return _predicate(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,44 +0,0 @@ |
|||||
namespace Avalonia.Media.TextFormatting.Unicode |
|
||||
{ |
|
||||
public enum UnicodeGeneralCategory : byte |
|
||||
{ |
|
||||
Other, //C# Cc | Cf | Cn | Co | Cs
|
|
||||
Control, //Cc
|
|
||||
Format, //Cf
|
|
||||
Unassigned, //Cn
|
|
||||
PrivateUse, //Co
|
|
||||
Surrogate, //Cs
|
|
||||
Letter, //L# Ll | Lm | Lo | Lt | Lu
|
|
||||
CasedLetter, //LC# Ll | Lt | Lu
|
|
||||
LowercaseLetter, //Ll
|
|
||||
ModifierLetter, //Lm
|
|
||||
OtherLetter, //Lo
|
|
||||
TitlecaseLetter, //Lt
|
|
||||
UppercaseLetter, //Lu
|
|
||||
Mark, //M
|
|
||||
SpacingMark, //Mc
|
|
||||
EnclosingMark, //Me
|
|
||||
NonspacingMark, //Mn
|
|
||||
Number, //N# Nd | Nl | No
|
|
||||
DecimalNumber, //Nd
|
|
||||
LetterNumber, //Nl
|
|
||||
OtherNumber, //No
|
|
||||
Punctuation, //P
|
|
||||
ConnectorPunctuation, //Pc
|
|
||||
DashPunctuation, //Pd
|
|
||||
ClosePunctuation, //Pe
|
|
||||
FinalPunctuation, //Pf
|
|
||||
InitialPunctuation, //Pi
|
|
||||
OtherPunctuation, //Po
|
|
||||
OpenPunctuation, //Ps
|
|
||||
Symbol, //S# Sc | Sk | Sm | So
|
|
||||
CurrencySymbol, //Sc
|
|
||||
ModifierSymbol, //Sk
|
|
||||
MathSymbol, //Sm
|
|
||||
OtherSymbol, //So
|
|
||||
Separator, //Z# Zl | Zp | Zs
|
|
||||
LineSeparator, //Zl
|
|
||||
ParagraphSeparator, //Zp
|
|
||||
SpaceSeparator, //Zs
|
|
||||
} |
|
||||
} |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue