committed by
GitHub
105 changed files with 5198 additions and 985 deletions
@ -0,0 +1,101 @@ |
|||||
|
<UserControl |
||||
|
xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
x:Class="RenderDemo.Pages.TransitionsPage"> |
||||
|
<UserControl.Styles> |
||||
|
<Styles> |
||||
|
<Styles.Resources> |
||||
|
<Template x:Key="Acorn"> |
||||
|
<Path Fill="White" Stretch="Uniform" |
||||
|
Data="F1 M 16.6309,18.6563C 17.1309, |
||||
|
8.15625 29.8809,14.1563 29.8809, |
||||
|
14.1563C 30.8809,11.1563 34.1308, |
||||
|
11.4063 34.1308,11.4063C 33.5,12 |
||||
|
34.6309,13.1563 34.6309,13.1563C |
||||
|
32.1309,13.1562 31.1309,14.9062 |
||||
|
31.1309,14.9062C 41.1309,23.9062 |
||||
|
32.6309,27.9063 32.6309,27.9062C |
||||
|
24.6309,24.9063 21.1309,22.1562 |
||||
|
16.6309,18.6563 Z M 16.6309,19.9063C |
||||
|
21.6309,24.1563 25.1309,26.1562 |
||||
|
31.6309,28.6562C 31.6309,28.6562 |
||||
|
26.3809,39.1562 18.3809,36.1563C |
||||
|
18.3809,36.1563 18,38 16.3809,36.9063C |
||||
|
15,36 16.3809,34.9063 16.3809,34.9063C |
||||
|
16.3809,34.9063 10.1309,30.9062 16.6309,19.9063 Z"/> |
||||
|
</Template> |
||||
|
<Template x:Key="Heart"> |
||||
|
<Path Fill="Red" Stretch="Uniform" Data=" |
||||
|
M 272.70141,238.71731 |
||||
|
C 206.46141,238.71731 152.70146,292.4773 152.70146,358.71731 |
||||
|
C 152.70146,493.47282 288.63461,528.80461 381.26391,662.02535 |
||||
|
C 468.83815,529.62199 609.82641,489.17075 609.82641,358.71731 |
||||
|
C 609.82641,292.47731 556.06651,238.7173 489.82641,238.71731 |
||||
|
C 441.77851,238.71731 400.42481,267.08774 381.26391,307.90481 |
||||
|
C 362.10311,267.08773 320.74941,238.7173 272.70141,238.71731 z "/> |
||||
|
</Template> |
||||
|
</Styles.Resources> |
||||
|
|
||||
|
<Style Selector="Border.Test"> |
||||
|
<Setter Property="Margin" Value="15"/> |
||||
|
<Setter Property="Width" Value="100"/> |
||||
|
<Setter Property="Height" Value="100"/> |
||||
|
<Setter Property="Child" Value="{StaticResource Acorn}"/> |
||||
|
<Setter Property="Transitions"> |
||||
|
<Transitions> |
||||
|
<TransformOperationsTransition Property="RenderTransform" Duration="0:0:1" /> |
||||
|
</Transitions> |
||||
|
</Setter> |
||||
|
<Setter Property="RenderTransform" Value="none" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border.Rect1:pointerover"> |
||||
|
<Setter Property="RenderTransform" Value="rotate(120deg) scale(1.5)" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border.Rect2:pointerover"> |
||||
|
<Setter Property="RenderTransform" Value="scale(0.8)" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border.Rect3"> |
||||
|
<Setter Property="Child" Value="{StaticResource Heart}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border.Rect3:pointerover"> |
||||
|
<Setter Property="RenderTransform" Value="rotate(1turn)" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border.Rect4:pointerover"> |
||||
|
<Setter Property="RenderTransform" Value="translateY(-100px)" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border.Rect5:pointerover"> |
||||
|
<Setter Property="RenderTransform" Value="skewX(-20deg)" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="Border.Rect5:pointerover"> |
||||
|
<Setter Property="RenderTransform" Value="skewX(-20deg)" /> |
||||
|
</Style> |
||||
|
|
||||
|
</Styles> |
||||
|
</UserControl.Styles> |
||||
|
|
||||
|
<Grid> |
||||
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" ClipToBounds="False"> |
||||
|
<StackPanel.Clock> |
||||
|
<Clock /> |
||||
|
</StackPanel.Clock> |
||||
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"> |
||||
|
<TextBlock VerticalAlignment="Center">Hover to activate Transform Keyframe Animations.</TextBlock> |
||||
|
<Button Content="{Binding PlayStateText}" Command="{Binding TogglePlayState}" Click="ToggleClock" /> |
||||
|
</StackPanel> |
||||
|
<WrapPanel ClipToBounds="False"> |
||||
|
<Border Classes="Test Rect1" Background="DarkRed"/> |
||||
|
<Border Classes="Test Rect2" Background="Magenta"/> |
||||
|
<Border Classes="Test Rect3"/> |
||||
|
<Border Classes="Test Rect4" Background="Navy"/> |
||||
|
<Border Classes="Test Rect5" Background="SeaGreen"/> |
||||
|
</WrapPanel> |
||||
|
</StackPanel> |
||||
|
</Grid> |
||||
|
</UserControl> |
||||
@ -0,0 +1,37 @@ |
|||||
|
using Avalonia.Animation; |
||||
|
using Avalonia.Controls; |
||||
|
using Avalonia.Interactivity; |
||||
|
using Avalonia.Markup.Xaml; |
||||
|
using RenderDemo.ViewModels; |
||||
|
|
||||
|
namespace RenderDemo.Pages |
||||
|
{ |
||||
|
public class TransitionsPage : UserControl |
||||
|
{ |
||||
|
public TransitionsPage() |
||||
|
{ |
||||
|
InitializeComponent(); |
||||
|
this.DataContext = new AnimationsPageViewModel(); |
||||
|
} |
||||
|
|
||||
|
private void InitializeComponent() |
||||
|
{ |
||||
|
AvaloniaXamlLoader.Load(this); |
||||
|
} |
||||
|
|
||||
|
private void ToggleClock(object sender, RoutedEventArgs args) |
||||
|
{ |
||||
|
var button = sender as Button; |
||||
|
var clock = button.Clock; |
||||
|
|
||||
|
if (clock.PlayState == PlayState.Run) |
||||
|
{ |
||||
|
clock.PlayState = PlayState.Pause; |
||||
|
} |
||||
|
else if (clock.PlayState == PlayState.Pause) |
||||
|
{ |
||||
|
clock.PlayState = PlayState.Run; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,136 +0,0 @@ |
|||||
// (c) Copyright Microsoft Corporation.
|
|
||||
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
||||
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
||||
// All other rights reserved.
|
|
||||
|
|
||||
using System; |
|
||||
|
|
||||
namespace Avalonia.Controls.Utils |
|
||||
{ |
|
||||
internal static class DoubleUtil |
|
||||
{ |
|
||||
internal const double DBL_EPSILON = 1e-6; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
|
|
||||
/// not they are within epsilon of each other. Note that this epsilon is proportional
|
|
||||
/// to the numbers themselves to that AreClose survives scalar multiplication.
|
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
|
||||
/// returns false. This is important enough to repeat:
|
|
||||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|
||||
/// used for optimizations *only*.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// bool - the result of the AreClose comparison.
|
|
||||
/// </returns>
|
|
||||
/// <param name="value1"> The first double to compare. </param>
|
|
||||
/// <param name="value2"> The second double to compare. </param>
|
|
||||
public static bool AreClose(double value1, double value2) |
|
||||
{ |
|
||||
//in case they are Infinities (then epsilon check does not work)
|
|
||||
if (value1 == value2) return true; |
|
||||
// This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON
|
|
||||
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON; |
|
||||
double delta = value1 - value2; |
|
||||
return (-eps < delta) && (eps > delta); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// GreaterThan - Returns whether or not the first double is greater than the second double.
|
|
||||
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
|
|
||||
/// the other number. Note that this epsilon is proportional to the numbers themselves
|
|
||||
/// to that AreClose survives scalar multiplication. Note,
|
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
|
||||
/// returns false. This is important enough to repeat:
|
|
||||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|
||||
/// used for optimizations *only*.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// bool - the result of the GreaterThan comparison.
|
|
||||
/// </returns>
|
|
||||
/// <param name="value1"> The first double to compare. </param>
|
|
||||
/// <param name="value2"> The second double to compare. </param>
|
|
||||
public static bool GreaterThan(double value1, double value2) |
|
||||
{ |
|
||||
return (value1 > value2) && !AreClose(value1, value2); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
|
|
||||
/// the second double. That is, whether or not the first is strictly greater than or within
|
|
||||
/// epsilon of the other number. Note that this epsilon is proportional to the numbers
|
|
||||
/// themselves to that AreClose survives scalar multiplication. Note,
|
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
|
||||
/// returns false. This is important enough to repeat:
|
|
||||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|
||||
/// used for optimizations *only*.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// bool - the result of the GreaterThanOrClose comparison.
|
|
||||
/// </returns>
|
|
||||
/// <param name="value1"> The first double to compare. </param>
|
|
||||
/// <param name="value2"> The second double to compare. </param>
|
|
||||
public static bool GreaterThanOrClose(double value1, double value2) |
|
||||
{ |
|
||||
return (value1 > value2) || AreClose(value1, value2); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// IsZero - Returns whether or not the double is "close" to 0. Same as AreClose(double, 0),
|
|
||||
/// but this is faster.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// bool - the result of the IsZero comparison.
|
|
||||
/// </returns>
|
|
||||
/// <param name="value"> The double to compare to 0. </param>
|
|
||||
public static bool IsZero(double value) |
|
||||
{ |
|
||||
return Math.Abs(value) < 10.0 * DBL_EPSILON; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// LessThan - Returns whether or not the first double is less than the second double.
|
|
||||
/// That is, whether or not the first is strictly less than *and* not within epsilon of
|
|
||||
/// the other number. Note that this epsilon is proportional to the numbers themselves
|
|
||||
/// to that AreClose survives scalar multiplication. Note,
|
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
|
||||
/// returns false. This is important enough to repeat:
|
|
||||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|
||||
/// used for optimizations *only*.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// bool - the result of the LessThan comparison.
|
|
||||
/// </returns>
|
|
||||
/// <param name="value1"> The first double to compare. </param>
|
|
||||
/// <param name="value2"> The second double to compare. </param>
|
|
||||
public static bool LessThan(double value1, double value2) |
|
||||
{ |
|
||||
return (value1 < value2) && !AreClose(value1, value2); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// LessThanOrClose - Returns whether or not the first double is less than or close to
|
|
||||
/// the second double. That is, whether or not the first is strictly less than or within
|
|
||||
/// epsilon of the other number. Note that this epsilon is proportional to the numbers
|
|
||||
/// themselves to that AreClose survives scalar multiplication. Note,
|
|
||||
/// There are plenty of ways for this to return false even for numbers which
|
|
||||
/// are theoretically identical, so no code calling this should fail to work if this
|
|
||||
/// returns false. This is important enough to repeat:
|
|
||||
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
|
|
||||
/// used for optimizations *only*.
|
|
||||
/// </summary>
|
|
||||
/// <returns>
|
|
||||
/// bool - the result of the LessThanOrClose comparison.
|
|
||||
/// </returns>
|
|
||||
/// <param name="value1"> The first double to compare. </param>
|
|
||||
/// <param name="value2"> The second double to compare. </param>
|
|
||||
public static bool LessThanOrClose(double value1, double value2) |
|
||||
{ |
|
||||
return (value1 < value2) || AreClose(value1, value2); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,64 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Globalization; |
||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Data.Converters; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Controls.Converters |
||||
|
{ |
||||
|
public class MenuScrollingVisibilityConverter : IMultiValueConverter |
||||
|
{ |
||||
|
public static readonly MenuScrollingVisibilityConverter Instance = new MenuScrollingVisibilityConverter(); |
||||
|
|
||||
|
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture) |
||||
|
{ |
||||
|
if (parameter == null || |
||||
|
values == null || |
||||
|
values.Count != 4 || |
||||
|
!(values[0] is ScrollBarVisibility visiblity) || |
||||
|
!(values[1] is double offset) || |
||||
|
!(values[2] is double extent) || |
||||
|
!(values[3] is double viewport)) |
||||
|
{ |
||||
|
return AvaloniaProperty.UnsetValue; |
||||
|
} |
||||
|
|
||||
|
if (visiblity == ScrollBarVisibility.Auto) |
||||
|
{ |
||||
|
if (extent == viewport) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
double target; |
||||
|
|
||||
|
if (parameter is double d) |
||||
|
{ |
||||
|
target = d; |
||||
|
} |
||||
|
else if (parameter is string s) |
||||
|
{ |
||||
|
target = double.Parse(s, NumberFormatInfo.InvariantInfo); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return AvaloniaProperty.UnsetValue; |
||||
|
} |
||||
|
|
||||
|
// Calculate the percent so that we can see if we are near the edge of the range
|
||||
|
double percent = MathUtilities.Clamp(offset * 100.0 / (extent - viewport), 0, 100); |
||||
|
|
||||
|
if (MathUtilities.AreClose(percent, target)) |
||||
|
{ |
||||
|
// We are at the end of the range, so no need for this button to be shown
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
using Avalonia.Controls.Primitives; |
||||
|
using Avalonia.Controls.Templates; |
||||
|
using Avalonia.LogicalTree; |
||||
|
|
||||
|
namespace Avalonia.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A Toggle Switch control.
|
||||
|
/// </summary>
|
||||
|
public class ToggleSwitch : ToggleButton |
||||
|
{ |
||||
|
static ToggleSwitch() |
||||
|
{ |
||||
|
OffContentProperty.Changed.AddClassHandler<ToggleSwitch>((x, e) => x.OffContentChanged(e)); |
||||
|
OnContentProperty.Changed.AddClassHandler<ToggleSwitch>((x, e) => x.OnContentChanged(e)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="OffContent"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<object> OffContentProperty = |
||||
|
AvaloniaProperty.Register<ToggleSwitch, object>(nameof(OffContent), defaultValue: "Off"); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="OffContentTemplate"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<IDataTemplate> OffContentTemplateProperty = |
||||
|
AvaloniaProperty.Register<ToggleSwitch, IDataTemplate>(nameof(OffContentTemplate)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="OnContent"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<object> OnContentProperty = |
||||
|
AvaloniaProperty.Register<ToggleSwitch, object>(nameof(OnContent), defaultValue: "On"); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Defines the <see cref="OnContentTemplate"/> property.
|
||||
|
/// </summary>
|
||||
|
public static readonly StyledProperty<IDataTemplate> OnContentTemplateProperty = |
||||
|
AvaloniaProperty.Register<ToggleSwitch, IDataTemplate>(nameof(OnContentTemplate)); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or Sets the Content that is displayed when in the On State.
|
||||
|
/// </summary>
|
||||
|
public object OnContent |
||||
|
{ |
||||
|
get { return GetValue(OnContentProperty); } |
||||
|
set { SetValue(OnContentProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or Sets the Content that is displayed when in the Off State.
|
||||
|
/// </summary>
|
||||
|
public object OffContent |
||||
|
{ |
||||
|
get { return GetValue(OffContentProperty); } |
||||
|
set { SetValue(OffContentProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or Sets the <see cref="IDataTemplate"/> used to display the <see cref="OffContent"/>.
|
||||
|
/// </summary>
|
||||
|
public IDataTemplate OffContentTemplate |
||||
|
{ |
||||
|
get { return GetValue(OffContentTemplateProperty); } |
||||
|
set { SetValue(OffContentTemplateProperty, value); } |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets or Sets the <see cref="IDataTemplate"/> used to display the <see cref="OnContent"/>.
|
||||
|
/// </summary>
|
||||
|
public IDataTemplate OnContentTemplate |
||||
|
{ |
||||
|
get { return GetValue(OnContentTemplateProperty); } |
||||
|
set { SetValue(OnContentTemplateProperty, value); } |
||||
|
} |
||||
|
|
||||
|
private void OffContentChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.OldValue is ILogical oldChild) |
||||
|
{ |
||||
|
LogicalChildren.Remove(oldChild); |
||||
|
} |
||||
|
|
||||
|
if (e.NewValue is ILogical newChild) |
||||
|
{ |
||||
|
LogicalChildren.Add(newChild); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void OnContentChanged(AvaloniaPropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.OldValue is ILogical oldChild) |
||||
|
{ |
||||
|
LogicalChildren.Remove(oldChild); |
||||
|
} |
||||
|
|
||||
|
if (e.NewValue is ILogical newChild) |
||||
|
{ |
||||
|
LogicalChildren.Add(newChild); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -1,47 +1,105 @@ |
|||||
<Style xmlns="https://github.com/avaloniaui" Selector="ScrollViewer"> |
<Styles xmlns="https://github.com/avaloniaui" |
||||
<Setter Property="Background" |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
Value="Transparent" /> |
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"> |
||||
<Setter Property="Template"> |
<Style Selector="ScrollViewer"> |
||||
<ControlTemplate> |
<Setter Property="Background" |
||||
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto"> |
Value="Transparent" /> |
||||
<ScrollContentPresenter Name="PART_ContentPresenter" |
<Setter Property="Template"> |
||||
Background="{TemplateBinding Background}" |
<ControlTemplate> |
||||
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}" |
<Grid ColumnDefinitions="*,Auto" RowDefinitions="*,Auto"> |
||||
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" |
<ScrollContentPresenter Name="PART_ContentPresenter" |
||||
Content="{TemplateBinding Content}" |
Background="{TemplateBinding Background}" |
||||
Extent="{TemplateBinding Extent, Mode=TwoWay}" |
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}" |
||||
Margin="{TemplateBinding Padding}" |
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" |
||||
Offset="{TemplateBinding Offset, Mode=TwoWay}" |
Content="{TemplateBinding Content}" |
||||
Viewport="{TemplateBinding Viewport, Mode=TwoWay}"> |
Extent="{TemplateBinding Extent, Mode=TwoWay}" |
||||
<ScrollContentPresenter.GestureRecognizers> |
Margin="{TemplateBinding Padding}" |
||||
<ScrollGestureRecognizer |
Offset="{TemplateBinding Offset, Mode=TwoWay}" |
||||
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}" |
Viewport="{TemplateBinding Viewport, Mode=TwoWay}"> |
||||
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" |
<ScrollContentPresenter.GestureRecognizers> |
||||
|
<ScrollGestureRecognizer |
||||
|
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}" |
||||
|
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" |
||||
/> |
/> |
||||
</ScrollContentPresenter.GestureRecognizers> |
</ScrollContentPresenter.GestureRecognizers> |
||||
</ScrollContentPresenter> |
</ScrollContentPresenter> |
||||
<ScrollBar Name="horizontalScrollBar" |
<ScrollBar Name="horizontalScrollBar" |
||||
Orientation="Horizontal" |
Orientation="Horizontal" |
||||
LargeChange="{Binding LargeChange.Width, RelativeSource={RelativeSource TemplatedParent}}" |
LargeChange="{Binding LargeChange.Width, RelativeSource={RelativeSource TemplatedParent}}" |
||||
SmallChange="{Binding SmallChange.Width, RelativeSource={RelativeSource TemplatedParent}}" |
SmallChange="{Binding SmallChange.Width, RelativeSource={RelativeSource TemplatedParent}}" |
||||
Maximum="{TemplateBinding HorizontalScrollBarMaximum}" |
Maximum="{TemplateBinding HorizontalScrollBarMaximum}" |
||||
Value="{TemplateBinding HorizontalScrollBarValue, Mode=TwoWay}" |
Value="{TemplateBinding HorizontalScrollBarValue, Mode=TwoWay}" |
||||
ViewportSize="{TemplateBinding HorizontalScrollBarViewportSize}" |
ViewportSize="{TemplateBinding HorizontalScrollBarViewportSize}" |
||||
Visibility="{TemplateBinding HorizontalScrollBarVisibility}" |
Visibility="{TemplateBinding HorizontalScrollBarVisibility}" |
||||
Grid.Row="1" |
Grid.Row="1" |
||||
Focusable="False"/> |
Focusable="False"/> |
||||
<ScrollBar Name="verticalScrollBar" |
<ScrollBar Name="verticalScrollBar" |
||||
Orientation="Vertical" |
Orientation="Vertical" |
||||
LargeChange="{Binding LargeChange.Height, RelativeSource={RelativeSource TemplatedParent}}" |
LargeChange="{Binding LargeChange.Height, RelativeSource={RelativeSource TemplatedParent}}" |
||||
SmallChange="{Binding SmallChange.Height, RelativeSource={RelativeSource TemplatedParent}}" |
SmallChange="{Binding SmallChange.Height, RelativeSource={RelativeSource TemplatedParent}}" |
||||
Maximum="{TemplateBinding VerticalScrollBarMaximum}" |
Maximum="{TemplateBinding VerticalScrollBarMaximum}" |
||||
Value="{TemplateBinding VerticalScrollBarValue, Mode=TwoWay}" |
Value="{TemplateBinding VerticalScrollBarValue, Mode=TwoWay}" |
||||
ViewportSize="{TemplateBinding VerticalScrollBarViewportSize}" |
ViewportSize="{TemplateBinding VerticalScrollBarViewportSize}" |
||||
Visibility="{TemplateBinding VerticalScrollBarVisibility}" |
Visibility="{TemplateBinding VerticalScrollBarVisibility}" |
||||
Grid.Column="1" |
Grid.Column="1" |
||||
Focusable="False"/> |
Focusable="False"/> |
||||
<Panel Grid.Row="1" Grid.Column="1" Background="{DynamicResource ThemeControlMidBrush}"/> |
<Panel Grid.Row="1" Grid.Column="1" Background="{DynamicResource ThemeControlMidBrush}"/> |
||||
</Grid> |
</Grid> |
||||
</ControlTemplate> |
</ControlTemplate> |
||||
</Setter> |
</Setter> |
||||
</Style> |
</Style> |
||||
|
|
||||
|
<Style Selector="ScrollViewer.menuscroller"> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<DockPanel> |
||||
|
<RepeatButton DockPanel.Dock="Top" |
||||
|
BorderThickness="0" |
||||
|
Background="Transparent" |
||||
|
Command="{Binding LineUp, RelativeSource={RelativeSource TemplatedParent}}"> |
||||
|
<RepeatButton.IsVisible> |
||||
|
<MultiBinding Converter="{x:Static converters:MenuScrollingVisibilityConverter.Instance}" |
||||
|
ConverterParameter="0"> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="VerticalScrollBarVisibility"/> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Offset.Y"/> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Extent.Height"/> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Viewport.Height"/> |
||||
|
</MultiBinding> |
||||
|
</RepeatButton.IsVisible> |
||||
|
<Path Data="M 0 4 L 8 4 L 4 0 Z"/> |
||||
|
</RepeatButton> |
||||
|
<RepeatButton DockPanel.Dock="Bottom" |
||||
|
BorderThickness="0" |
||||
|
Background="Transparent" |
||||
|
Command="{Binding LineDown, RelativeSource={RelativeSource TemplatedParent}}"> |
||||
|
<RepeatButton.IsVisible> |
||||
|
<MultiBinding Converter="{x:Static converters:MenuScrollingVisibilityConverter.Instance}" |
||||
|
ConverterParameter="100"> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="VerticalScrollBarVisibility"/> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Offset.Y"/> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Extent.Height"/> |
||||
|
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Viewport.Height"/> |
||||
|
</MultiBinding> |
||||
|
</RepeatButton.IsVisible> |
||||
|
<Path Data="M 0 0 L 4 4 L 8 0 Z"/> |
||||
|
</RepeatButton> |
||||
|
<ScrollContentPresenter Name="PART_ContentPresenter" |
||||
|
CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}" |
||||
|
CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" |
||||
|
Content="{TemplateBinding Content}" |
||||
|
Extent="{TemplateBinding Extent, Mode=TwoWay}" |
||||
|
Margin="{TemplateBinding Padding}" |
||||
|
Offset="{TemplateBinding Offset, Mode=TwoWay}" |
||||
|
Viewport="{TemplateBinding Viewport, Mode=TwoWay}"/> |
||||
|
</DockPanel> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
<Style Selector="ScrollViewer.menuscroller /template/ RepeatButton > Path"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ThemeForegroundLowBrush}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ScrollViewer.menuscroller /template/ RepeatButton:pointerover > Path"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ThemeAccentBrush}" /> |
||||
|
</Style> |
||||
|
</Styles> |
||||
|
|||||
@ -0,0 +1,294 @@ |
|||||
|
<Styles xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:sys="clr-namespace:System;assembly=netstandard"> |
||||
|
<Styles.Resources> |
||||
|
<Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness> |
||||
|
<x:Double x:Key="ToggleSwitchPreContentMargin">6</x:Double> |
||||
|
<x:Double x:Key="ToggleSwitchPostContentMargin">6</x:Double> |
||||
|
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double> |
||||
|
<x:Double x:Key="KnobOnPosition">20</x:Double> |
||||
|
<x:Double x:Key="KnobOffPosition">0</x:Double> |
||||
|
</Styles.Resources> |
||||
|
<Design.PreviewWith> |
||||
|
<StackPanel Margin="20" Width="250" Spacing="24" > |
||||
|
<StackPanel Spacing="12" > |
||||
|
<TextBlock |
||||
|
Text="Automatic updates" |
||||
|
Classes="h1"/> |
||||
|
<TextBlock |
||||
|
Text="Updates will be automaticly Downloaded and installed shile the computer is shutting down or restarting" |
||||
|
TextWrapping="Wrap"/> |
||||
|
<ToggleSwitch HorizontalContentAlignment="Left" |
||||
|
Content="Enable automatic Updates?" |
||||
|
OffContent="Uit" |
||||
|
OnContent="Aan" |
||||
|
VerticalAlignment="Bottom"/> |
||||
|
</StackPanel> |
||||
|
|
||||
|
<StackPanel Spacing="12"> |
||||
|
<TextBlock |
||||
|
Text="Previewer" |
||||
|
Classes="h1"/> |
||||
|
<TextBlock |
||||
|
Text="The previewer Shows a preview off your code, this could slow down your system" |
||||
|
TextWrapping="Wrap"/> |
||||
|
<ToggleSwitch |
||||
|
Content="Previewer" |
||||
|
IsChecked="True" /> |
||||
|
</StackPanel> |
||||
|
</StackPanel> |
||||
|
</Design.PreviewWith> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch"> |
||||
|
<Setter Property="Foreground" Value="{DynamicResource ToggleSwitchContentForeground}" /> |
||||
|
<Setter Property="HorizontalAlignment" Value="Left" /> |
||||
|
<Setter Property="VerticalAlignment" Value="Center" /> |
||||
|
<Setter Property="HorizontalContentAlignment" Value="Left" /> |
||||
|
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> |
||||
|
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<Grid Background="{TemplateBinding Background}" |
||||
|
RowDefinitions="Auto,*"> |
||||
|
|
||||
|
<ContentPresenter x:Name="PART_ContentPresenter" |
||||
|
Grid.Row="0" |
||||
|
Content="{TemplateBinding Content}" |
||||
|
ContentTemplate="{TemplateBinding ContentTemplate}" |
||||
|
Margin="{DynamicResource ToggleSwitchTopHeaderMargin}" |
||||
|
VerticalAlignment="Top"/> |
||||
|
|
||||
|
<Grid Grid.Row="1" |
||||
|
MinWidth="{StaticResource ToggleSwitchThemeMinWidth}" |
||||
|
HorizontalAlignment="Left" |
||||
|
VerticalAlignment="Top"> |
||||
|
|
||||
|
<Grid.RowDefinitions> |
||||
|
<RowDefinition Height="{DynamicResource ToggleSwitchPreContentMargin}" /> |
||||
|
<RowDefinition Height="Auto" /> |
||||
|
<RowDefinition Height="{DynamicResource ToggleSwitchPostContentMargin}" /> |
||||
|
</Grid.RowDefinitions> |
||||
|
|
||||
|
<Grid.ColumnDefinitions> |
||||
|
<ColumnDefinition Width="Auto" /> |
||||
|
<ColumnDefinition Width="12" MaxWidth="12" /> |
||||
|
<ColumnDefinition Width="Auto" /> |
||||
|
</Grid.ColumnDefinitions> |
||||
|
|
||||
|
<Grid x:Name="SwitchAreaGrid" |
||||
|
Grid.RowSpan="3" |
||||
|
Grid.ColumnSpan="3" |
||||
|
TemplatedControl.IsTemplateFocusTarget="True" |
||||
|
Margin="0,5" /> |
||||
|
|
||||
|
<ContentPresenter x:Name="PART_OffContentPresenter" |
||||
|
Grid.RowSpan="3" |
||||
|
Grid.Column="2" |
||||
|
Content="{TemplateBinding OffContent}" |
||||
|
ContentTemplate="{TemplateBinding OffContentTemplate}" |
||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> |
||||
|
|
||||
|
<ContentPresenter x:Name="PART_OnContentPresenter" |
||||
|
Grid.RowSpan="3" |
||||
|
Grid.Column="2" |
||||
|
Content="{TemplateBinding OnContent}" |
||||
|
ContentTemplate="{TemplateBinding OnContentTemplate}" |
||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> |
||||
|
|
||||
|
<Border x:Name="OuterBorder" |
||||
|
Grid.Row="1" |
||||
|
Height="20" |
||||
|
Width="40" |
||||
|
CornerRadius="10" |
||||
|
BorderThickness="{DynamicResource ToggleSwitchOuterBorderStrokeThickness}" /> |
||||
|
|
||||
|
<Border x:Name="SwitchKnobBounds" |
||||
|
Grid.Row="1" |
||||
|
Height="20" |
||||
|
Width="40" |
||||
|
CornerRadius="10" |
||||
|
BorderThickness="{DynamicResource ToggleSwitchOnStrokeThickness}"/> |
||||
|
|
||||
|
<Canvas x:Name="SwitchKnob" Grid.Row="1" |
||||
|
HorizontalAlignment="Left" |
||||
|
Width="20" Height="20"> |
||||
|
|
||||
|
<Grid x:Name="MovingKnobs" |
||||
|
Width="20" Height="20"> |
||||
|
|
||||
|
<Ellipse x:Name="SwitchKnobOn" |
||||
|
Width="10" Height="10" /> |
||||
|
|
||||
|
<Ellipse x:Name="SwitchKnobOff" |
||||
|
Width="10" Height="10" /> |
||||
|
</Grid> |
||||
|
</Canvas> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- NormalState --> |
||||
|
<Style Selector="ToggleSwitch /template/ Grid#SwitchAreaGrid"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackground}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Border#OuterBorder"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOff}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOff}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOn}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOn}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOn}"/> |
||||
|
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOn}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOff}"/> |
||||
|
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOff}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Grid#MovingKnobs"> |
||||
|
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOffPosition}"/> |
||||
|
<Setter Property="Transitions"> |
||||
|
<Transitions> |
||||
|
<DoubleTransition Property="Canvas.Left" Duration="0:0:0.2" Easing="CubicEaseOut"/> |
||||
|
</Transitions> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- PointerOverState --> |
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Border#OuterBorder"> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffPointerOver}"/> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnPointerOver}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Grid#SwitchAreaGrid"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackgroundPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- PressedState --> |
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Border#OuterBorder"> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffPressed}"/> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnPressed}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Grid#SwitchAreaGrid"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackgroundPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- DisabledState --> |
||||
|
<Style Selector="ToggleSwitch:disabled"> |
||||
|
<Setter Property="Foreground" Value="{DynamicResource ToggleSwitchHeaderForegroundDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Border#OuterBorder"> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffDisabled}"/> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnDisabled}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- CheckedState --> |
||||
|
<Style Selector="ToggleSwitch:checked /template/ Grid#MovingKnobs"> |
||||
|
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOnPosition}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Border#OuterBorder"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ ContentPresenter#PART_OffContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ ContentPresenter#PART_OnContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!--UncheckedState --> |
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Grid#MovingKnobs"> |
||||
|
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOffPosition}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Border#OuterBorder"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ ContentPresenter#PART_OffContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ ContentPresenter#PART_OnContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
</Styles> |
||||
@ -1,104 +1,140 @@ |
|||||
<Styles xmlns="https://github.com/avaloniaui"> |
<Styles xmlns="https://github.com/avaloniaui" |
||||
<Style Selector="ButtonSpinner"> |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
<Setter Property="Background" Value="Transparent"/> |
xmlns:sys="clr-namespace:System;assembly=netstandard" |
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> |
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"> |
||||
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/> |
<Design.PreviewWith> |
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/> |
<Border Padding="20" |
||||
<Setter Property="VerticalContentAlignment" Value="Center"/> |
Background="Black"> |
||||
|
<StackPanel Spacing="20"> |
||||
|
<ButtonSpinner ButtonSpinnerLocation="Right" |
||||
|
Content="Right disabled inline spinner" |
||||
|
AllowSpin="False" /> |
||||
|
<ButtonSpinner ButtonSpinnerLocation="Left" |
||||
|
Content="Left spinner" /> |
||||
|
<ButtonSpinner ShowButtonSpinner="False" |
||||
|
Content="Hide spinner" /> |
||||
|
</StackPanel> |
||||
|
</Border> |
||||
|
</Design.PreviewWith> |
||||
|
|
||||
|
<Styles.Resources> |
||||
|
<converters:MarginMultiplierConverter x:Key="ButtonSpinner_OnlyLeftThinknessConverter" |
||||
|
Indent="1" |
||||
|
Left="True" /> |
||||
|
<converters:MarginMultiplierConverter x:Key="ButtonSpinner_OnlyRightThinknessConverter" |
||||
|
Indent="1" |
||||
|
Right="True" /> |
||||
|
|
||||
|
<StreamGeometry x:Key="ButtonSpinnerIncreaseButtonIcon">M0,9 L10,0 20,9 19,10 10,2 1,10 z</StreamGeometry> |
||||
|
<StreamGeometry x:Key="ButtonSpinnerDecreaseButtonIcon">M0,1 L10,10 20,1 19,0 10,8 1,0 z</StreamGeometry> |
||||
|
</Styles.Resources> |
||||
|
|
||||
|
<!-- RepeatButton.ButtonSpinnerRepeatButton --> |
||||
|
<Style Selector="RepeatButton.ButtonSpinnerRepeatButton"> |
||||
|
<Setter Property="MinWidth" Value="34" /> |
||||
|
<Setter Property="VerticalAlignment" Value="Stretch" /> |
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner /template/ RepeatButton"> |
|
||||
<Setter Property="Background" Value="Transparent"/> |
<Style Selector="RepeatButton.ButtonSpinnerRepeatButton /template/ ContentPresenter"> |
||||
<Setter Property="BorderBrush" Value="Transparent"/> |
<Setter Property="CornerRadius" Value="0" /> |
||||
|
<Setter Property="BorderBrush" Value="{TemplateBinding BorderBrush}" /> |
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner /template/ RepeatButton:pointerover"> |
<Style Selector="RepeatButton.ButtonSpinnerRepeatButton:disabled"> |
||||
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/> |
<Setter Property="BorderBrush" Value="{TemplateBinding BorderBrush}" /> |
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> |
|
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner /template/ RepeatButton#PART_IncreaseButton"> |
<Style Selector="RepeatButton.ButtonSpinnerRepeatButton:disabled > Path"> |
||||
<Setter Property="Content"> |
<Setter Property="Fill" Value="{DynamicResource RepeatButtonForegroundDisabled}" /> |
||||
<Template> |
|
||||
<Path Fill="{DynamicResource ThemeForegroundBrush}" |
|
||||
Width="8" |
|
||||
Height="4" |
|
||||
Stretch="Uniform" |
|
||||
HorizontalAlignment="Center" |
|
||||
VerticalAlignment="Center" |
|
||||
Data="M0,5 L4.5,.5 9,5 6,5 4.5,3.5 3,5 z"/> |
|
||||
</Template> |
|
||||
</Setter> |
|
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner /template/ RepeatButton#PART_DecreaseButton"> |
|
||||
<Setter Property="Content"> |
<!-- ButtonSpinner --> |
||||
<Template> |
<Style Selector="ButtonSpinner"> |
||||
<Path Fill="{DynamicResource ThemeForegroundBrush}" |
<Setter Property="Background" Value="Transparent" /> |
||||
Width="8" |
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" /> |
||||
Height="4" |
<Setter Property="Padding" Value="10, 0" /> |
||||
Stretch="Uniform" |
<Setter Property="Background" Value="{DynamicResource TextControlBackground}" /> |
||||
HorizontalAlignment="Center" |
<Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrush}" /> |
||||
VerticalAlignment="Center" |
<Setter Property="BorderThickness" Value="{DynamicResource TextControlBorderThemeThickness}" /> |
||||
Data="M0,0 L3,0 4.5,1.5 6,0 9,0 4.5,4.5 z"/> |
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" /> |
||||
</Template> |
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" /> |
||||
</Setter> |
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> |
||||
</Style> |
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> |
||||
<Style Selector="ButtonSpinner:right"> |
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> |
||||
|
<Setter Property="VerticalContentAlignment" Value="Center" /> |
||||
<Setter Property="Template"> |
<Setter Property="Template"> |
||||
<ControlTemplate> |
<ControlTemplate> |
||||
<Border Name="border" |
<Border Background="{TemplateBinding Background}" |
||||
Background="{TemplateBinding Background}" |
|
||||
BorderBrush="{TemplateBinding BorderBrush}" |
BorderBrush="{TemplateBinding BorderBrush}" |
||||
BorderThickness="{TemplateBinding BorderThickness}" |
BorderThickness="{TemplateBinding BorderThickness}" |
||||
Margin="{TemplateBinding Padding}" |
CornerRadius="{DynamicResource ControlCornerRadius}"> |
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" |
<Grid ColumnDefinitions="Auto,*,Auto"> |
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"> |
<ContentPresenter Name="PART_ContentPresenter" |
||||
<Grid ColumnDefinitions="*,Auto"> |
Grid.Column="1" |
||||
<ContentPresenter Name="PART_ContentPresenter" Grid.Column="0" |
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}" |
ContentTemplate="{TemplateBinding ContentTemplate}" |
||||
Content="{TemplateBinding Content}" |
Content="{TemplateBinding Content}" |
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" |
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" |
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" |
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" |
||||
Padding="{TemplateBinding Padding}"/> |
Padding="{TemplateBinding Padding}" /> |
||||
<Grid Grid.Column="1" RowDefinitions="*,*" IsVisible="{TemplateBinding ShowButtonSpinner}"> |
|
||||
<RepeatButton Grid.Row="0" Name="PART_IncreaseButton"/> |
<StackPanel Name="PART_SpinnerPanel" |
||||
<RepeatButton Grid.Row="1" Name="PART_DecreaseButton"/> |
Orientation="Horizontal" |
||||
</Grid> |
IsVisible="{TemplateBinding ShowButtonSpinner}"> |
||||
|
<RepeatButton Name="PART_IncreaseButton" |
||||
|
Classes="ButtonSpinnerRepeatButton" |
||||
|
VerticalContentAlignment="Center" |
||||
|
Foreground="{TemplateBinding Foreground}" |
||||
|
BorderBrush="{TemplateBinding BorderBrush}" |
||||
|
Background="{TemplateBinding Background}" |
||||
|
FontSize="{TemplateBinding FontSize}"> |
||||
|
<Path Fill="{TemplateBinding Foreground}" |
||||
|
Width="16" |
||||
|
Height="8" |
||||
|
Stretch="Uniform" |
||||
|
HorizontalAlignment="Center" |
||||
|
VerticalAlignment="Center" |
||||
|
Data="{StaticResource ButtonSpinnerIncreaseButtonIcon}" /> |
||||
|
</RepeatButton> |
||||
|
|
||||
|
<RepeatButton Name="PART_DecreaseButton" |
||||
|
Classes="ButtonSpinnerRepeatButton" |
||||
|
Foreground="{TemplateBinding Foreground}" |
||||
|
BorderBrush="{TemplateBinding BorderBrush}" |
||||
|
Background="{TemplateBinding Background}" |
||||
|
VerticalContentAlignment="Center" |
||||
|
FontSize="{TemplateBinding FontSize}"> |
||||
|
<Path Fill="{TemplateBinding Foreground}" |
||||
|
Width="16" |
||||
|
Height="8" |
||||
|
Stretch="Uniform" |
||||
|
HorizontalAlignment="Center" |
||||
|
VerticalAlignment="Center" |
||||
|
Data="{StaticResource ButtonSpinnerDecreaseButtonIcon}" /> |
||||
|
</RepeatButton> |
||||
|
</StackPanel> |
||||
</Grid> |
</Grid> |
||||
</Border> |
</Border> |
||||
</ControlTemplate> |
</ControlTemplate> |
||||
</Setter> |
</Setter> |
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner:left"> |
|
||||
<Setter Property="Template"> |
<!-- ButtonSpinnerLocation=Right --> |
||||
<ControlTemplate> |
<Style Selector="ButtonSpinner:right /template/ StackPanel#PART_SpinnerPanel"> |
||||
<Border Name="border" |
<Setter Property="Grid.Column" Value="2" /> |
||||
Background="{TemplateBinding Background}" |
</Style> |
||||
BorderBrush="{TemplateBinding BorderBrush}" |
<Style Selector="ButtonSpinner:right /template/ RepeatButton.ButtonSpinnerRepeatButton"> |
||||
BorderThickness="{TemplateBinding BorderThickness}" |
<Setter Property="BorderThickness" Value="{Binding $parent[ButtonSpinner].BorderThickness, Converter={StaticResource ButtonSpinner_OnlyLeftThinknessConverter}}" /> |
||||
Margin="{TemplateBinding Padding}" |
|
||||
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" |
|
||||
VerticalAlignment="{TemplateBinding VerticalAlignment}"> |
|
||||
<Grid ColumnDefinitions="Auto,*"> |
|
||||
<Grid Grid.Column="0" RowDefinitions="*,*" IsVisible="{TemplateBinding ShowButtonSpinner}"> |
|
||||
<RepeatButton Grid.Row="0" Name="PART_IncreaseButton"/> |
|
||||
<RepeatButton Grid.Row="1" Name="PART_DecreaseButton"/> |
|
||||
</Grid> |
|
||||
<ContentPresenter Name="PART_ContentPresenter" Grid.Column="1" |
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}" |
|
||||
Content="{TemplateBinding Content}" |
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" |
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" |
|
||||
Padding="{TemplateBinding Padding}"/> |
|
||||
</Grid> |
|
||||
</Border> |
|
||||
</ControlTemplate> |
|
||||
</Setter> |
|
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner:pointerover /template/ Border#border"> |
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/> |
<!-- ButtonSpinnerLocation=Left --> |
||||
|
<Style Selector="ButtonSpinner:left /template/ StackPanel#PART_SpinnerPanel"> |
||||
|
<Setter Property="Grid.Column" Value="0" /> |
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner:focus /template/ Border#border"> |
<Style Selector="ButtonSpinner:left /template/ RepeatButton.ButtonSpinnerRepeatButton"> |
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/> |
<Setter Property="BorderThickness" Value="{Binding $parent[ButtonSpinner].BorderThickness, Converter={StaticResource ButtonSpinner_OnlyRightThinknessConverter}}" /> |
||||
</Style> |
</Style> |
||||
<Style Selector="ButtonSpinner:error /template/ Border#border"> |
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}"/> |
<!-- Error state --> |
||||
|
<Style Selector="ButtonSpinner:error"> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}" /> |
||||
</Style> |
</Style> |
||||
|
|
||||
</Styles> |
</Styles> |
||||
|
|||||
@ -0,0 +1,6 @@ |
|||||
|
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
<Style Selector="TextBlock.CaptionTextBlockStyle"> |
||||
|
<Setter Property="FontSize" Value="12" /> |
||||
|
<Setter Property="FontWeight" Value="Normal" /> |
||||
|
</Style> |
||||
|
</Styles> |
||||
@ -1,16 +1,34 @@ |
|||||
<Style xmlns="https://github.com/avaloniaui" Selector="Menu"> |
<Style xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
Selector="Menu"> |
||||
|
<Design.PreviewWith> |
||||
|
<Border Padding="20"> |
||||
|
<Menu> |
||||
|
<MenuItem Header="New" /> |
||||
|
<MenuItem Header="Open" /> |
||||
|
</Menu> |
||||
|
</Border> |
||||
|
</Design.PreviewWith> |
||||
|
|
||||
|
<Style.Resources> |
||||
|
<x:Double x:Key="MenuHeight">32</x:Double> |
||||
|
</Style.Resources> |
||||
|
<Setter Property="Background" Value="Transparent" /> |
||||
|
<Setter Property="Height" Value="{StaticResource MenuHeight}" /> |
||||
<Setter Property="Template"> |
<Setter Property="Template"> |
||||
<ControlTemplate> |
<ControlTemplate> |
||||
<Border Background="{TemplateBinding Background}" |
<Border Background="{TemplateBinding Background}" |
||||
BorderBrush="{TemplateBinding BorderBrush}" |
BorderBrush="{TemplateBinding BorderBrush}" |
||||
BorderThickness="{TemplateBinding BorderThickness}" |
BorderThickness="{TemplateBinding BorderThickness}" |
||||
|
HorizontalAlignment="Stretch" |
||||
Padding="{TemplateBinding Padding}"> |
Padding="{TemplateBinding Padding}"> |
||||
<ItemsPresenter Name="PART_ItemsPresenter" |
<ItemsPresenter Name="PART_ItemsPresenter" |
||||
Items="{TemplateBinding Items}" |
Items="{TemplateBinding Items}" |
||||
ItemsPanel="{TemplateBinding ItemsPanel}" |
ItemsPanel="{TemplateBinding ItemsPanel}" |
||||
ItemTemplate="{TemplateBinding ItemTemplate}" |
ItemTemplate="{TemplateBinding ItemTemplate}" |
||||
KeyboardNavigation.TabNavigation="Continue"/> |
VerticalAlignment="Stretch" |
||||
|
KeyboardNavigation.TabNavigation="Continue" /> |
||||
</Border> |
</Border> |
||||
</ControlTemplate> |
</ControlTemplate> |
||||
</Setter> |
</Setter> |
||||
</Style> |
</Style> |
||||
|
|||||
@ -1,38 +1,60 @@ |
|||||
<Styles xmlns="https://github.com/avaloniaui"> |
<Styles xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
<Design.PreviewWith> |
||||
|
<Border Padding="20" |
||||
|
Background="Black"> |
||||
|
<StackPanel Spacing="20"> |
||||
|
<NumericUpDown Minimum="0" |
||||
|
Maximum="10" |
||||
|
Increment="0.5" |
||||
|
Width="150" |
||||
|
Watermark="Enter text" /> |
||||
|
<NumericUpDown Minimum="0" |
||||
|
Maximum="10" |
||||
|
Increment="0.5" |
||||
|
Width="150" |
||||
|
ButtonSpinnerLocation="Left" |
||||
|
Watermark="Enter text" /> |
||||
|
</StackPanel> |
||||
|
</Border> |
||||
|
</Design.PreviewWith> |
||||
|
|
||||
<Style Selector="NumericUpDown"> |
<Style Selector="NumericUpDown"> |
||||
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" /> |
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" /> |
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> |
<Setter Property="Background" Value="{DynamicResource TextControlBackground}" /> |
||||
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/> |
<Setter Property="BorderThickness" Value="{DynamicResource TextControlBorderThemeThickness}" /> |
||||
<Setter Property="Padding" Value="4"/> |
<Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrush}" /> |
||||
|
<Setter Property="MinHeight" Value="{DynamicResource TextControlThemeMinHeight}" /> |
||||
|
<Setter Property="MinWidth" Value="{DynamicResource TextControlThemeMinWidth}" /> |
||||
|
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> |
||||
|
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> |
||||
|
<Setter Property="Padding" Value="{DynamicResource TextControlThemePadding}" /> |
||||
<Setter Property="Template"> |
<Setter Property="Template"> |
||||
<ControlTemplate> |
<ControlTemplate> |
||||
<ButtonSpinner Name="PART_Spinner" |
<ButtonSpinner Name="PART_Spinner" |
||||
Background="{TemplateBinding Background}" |
Background="{TemplateBinding Background}" |
||||
BorderThickness="{TemplateBinding BorderThickness}" |
BorderThickness="{TemplateBinding BorderThickness}" |
||||
BorderBrush="{TemplateBinding BorderBrush}" |
BorderBrush="{TemplateBinding BorderBrush}" |
||||
|
Padding="0" |
||||
HorizontalContentAlignment="Stretch" |
HorizontalContentAlignment="Stretch" |
||||
VerticalContentAlignment="Stretch" |
VerticalContentAlignment="Stretch" |
||||
AllowSpin="{TemplateBinding AllowSpin}" |
AllowSpin="{TemplateBinding AllowSpin}" |
||||
ShowButtonSpinner="{TemplateBinding ShowButtonSpinner}" |
ShowButtonSpinner="{TemplateBinding ShowButtonSpinner}" |
||||
ButtonSpinnerLocation="{TemplateBinding ButtonSpinnerLocation}"> |
ButtonSpinnerLocation="{TemplateBinding ButtonSpinnerLocation}"> |
||||
<TextBox Name="PART_TextBox" |
<TextBox Name="PART_TextBox" |
||||
BorderThickness="0" |
Background="Transparent" |
||||
Background="{TemplateBinding Background}" |
BorderBrush="Transparent" |
||||
BorderBrush="{TemplateBinding BorderBrush}" |
Margin="-2" |
||||
Padding="{TemplateBinding Padding}" |
Padding="{TemplateBinding Padding}" |
||||
Watermark="{TemplateBinding Watermark}" |
Watermark="{TemplateBinding Watermark}" |
||||
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}" |
DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}" |
||||
IsReadOnly="{TemplateBinding IsReadOnly}" |
IsReadOnly="{TemplateBinding IsReadOnly}" |
||||
Text="{TemplateBinding Text}" |
Text="{TemplateBinding Text}" |
||||
AcceptsReturn="False" |
AcceptsReturn="False" |
||||
TextWrapping="NoWrap"> |
TextWrapping="NoWrap" /> |
||||
</TextBox> |
|
||||
</ButtonSpinner> |
</ButtonSpinner> |
||||
</ControlTemplate> |
</ControlTemplate> |
||||
</Setter> |
</Setter> |
||||
</Style> |
</Style> |
||||
<Style Selector="NumericUpDown /template/ TextBox#PART_TextBox"> |
|
||||
<Setter Property="Margin" Value="4"/> |
</Styles> |
||||
<Setter Property="MinWidth" Value="20"/> |
|
||||
</Style> |
|
||||
</Styles> |
|
||||
|
|||||
@ -0,0 +1,62 @@ |
|||||
|
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
<Design.PreviewWith> |
||||
|
<Border Padding="20"> |
||||
|
<StackPanel Spacing="20"> |
||||
|
<RepeatButton Content="Click Me!" /> |
||||
|
</StackPanel> |
||||
|
</Border> |
||||
|
</Design.PreviewWith> |
||||
|
<Styles.Resources> |
||||
|
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness> |
||||
|
</Styles.Resources> |
||||
|
<Style Selector="RepeatButton"> |
||||
|
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackground}" /> |
||||
|
<!--<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />--> |
||||
|
<Setter Property="Foreground" Value="{DynamicResource RepeatButtonForeground}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrush}" /> |
||||
|
<Setter Property="BorderThickness" Value="{DynamicResource ButtonBorderThemeThickness}" /> |
||||
|
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" /> |
||||
|
<Setter Property="HorizontalAlignment" Value="Left" /> |
||||
|
<Setter Property="VerticalAlignment" Value="Center" /> |
||||
|
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> |
||||
|
<Setter Property="FontWeight" Value="Normal" /> |
||||
|
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> |
||||
|
<!--<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" /> |
||||
|
<Setter Property="FocusVisualMargin" Value="-3" />--> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<ContentPresenter x:Name="PART_ContentPresenter" |
||||
|
Background="{TemplateBinding Background}" |
||||
|
BorderBrush="{TemplateBinding BorderBrush}" |
||||
|
BorderThickness="{TemplateBinding BorderThickness}" |
||||
|
Content="{TemplateBinding Content}" |
||||
|
ContentTemplate="{TemplateBinding ContentTemplate}" |
||||
|
Padding="{TemplateBinding Padding}" |
||||
|
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" |
||||
|
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" /> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="RepeatButton /template/ ContentPresenter"> |
||||
|
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="RepeatButton:pointerover /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPointerOver}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushPointerOver}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundPointerOver}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="RepeatButton:pressed /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPressed}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushPressed}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundPressed}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="RepeatButton:disabled /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundDisabled}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushDisabled}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundDisabled}" /> |
||||
|
</Style> |
||||
|
</Styles> |
||||
@ -1,38 +1,108 @@ |
|||||
<Styles xmlns="https://github.com/avaloniaui"> |
<Styles xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
||||
|
<Design.PreviewWith> |
||||
|
<Border Padding="20"> |
||||
|
<StackPanel Spacing="20"> |
||||
|
<ToggleButton Content="Click Me!" /> |
||||
|
<ToggleButton Content="Disabled" IsEnabled="False" /> |
||||
|
<ToggleButton Content="Three state" IsThreeState="True" /> |
||||
|
</StackPanel> |
||||
|
</Border> |
||||
|
</Design.PreviewWith> |
||||
|
|
||||
|
<Styles.Resources> |
||||
|
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness> |
||||
|
</Styles.Resources> |
||||
<Style Selector="ToggleButton"> |
<Style Selector="ToggleButton"> |
||||
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/> |
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackground}" /> |
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/> |
<Setter Property="Foreground" Value="{DynamicResource ToggleButtonForeground}" /> |
||||
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/> |
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrush}" /> |
||||
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/> |
<Setter Property="BorderThickness" Value="{DynamicResource ToggleButtonBorderThemeThickness}" /> |
||||
<Setter Property="Padding" Value="4"/> |
<Setter Property="Padding" Value="{DynamicResource ButtonPadding}" /> |
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/> |
<Setter Property="HorizontalAlignment" Value="Left" /> |
||||
<Setter Property="VerticalContentAlignment" Value="Center"/> |
<Setter Property="VerticalAlignment" Value="Center" /> |
||||
|
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> |
||||
|
<Setter Property="FontWeight" Value="Normal" /> |
||||
|
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> |
||||
<Setter Property="Template"> |
<Setter Property="Template"> |
||||
<ControlTemplate> |
<ControlTemplate> |
||||
<ContentPresenter Name="PART_ContentPresenter" |
<ContentPresenter x:Name="PART_ContentPresenter" |
||||
Background="{TemplateBinding Background}" |
Background="{TemplateBinding Background}" |
||||
BorderBrush="{TemplateBinding BorderBrush}" |
BorderBrush="{TemplateBinding BorderBrush}" |
||||
BorderThickness="{TemplateBinding BorderThickness}" |
BorderThickness="{TemplateBinding BorderThickness}" |
||||
ContentTemplate="{TemplateBinding ContentTemplate}" |
CornerRadius="{DynamicResource ControlCornerRadius}" |
||||
Content="{TemplateBinding Content}" |
Content="{TemplateBinding Content}" |
||||
|
ContentTemplate="{TemplateBinding ContentTemplate}" |
||||
Padding="{TemplateBinding Padding}" |
Padding="{TemplateBinding Padding}" |
||||
TextBlock.Foreground="{TemplateBinding Foreground}" |
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" |
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" |
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/> |
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" /> |
||||
</ControlTemplate> |
</ControlTemplate> |
||||
</Setter> |
</Setter> |
||||
</Style> |
</Style> |
||||
<Style Selector="ToggleButton:checked /template/ ContentPresenter"> |
|
||||
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/> |
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> |
|
||||
</Style> |
|
||||
<Style Selector="ToggleButton:pointerover /template/ ContentPresenter"> |
<Style Selector="ToggleButton:pointerover /template/ ContentPresenter"> |
||||
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/> |
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundPointerOver}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushPointerOver}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundPointerOver}" /> |
||||
</Style> |
</Style> |
||||
|
|
||||
<Style Selector="ToggleButton:pressed /template/ ContentPresenter"> |
<Style Selector="ToggleButton:pressed /template/ ContentPresenter"> |
||||
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/> |
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundPressed}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushPressed}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundPressed}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:disabled /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundDisabled}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushDisabled}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundDisabled}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:checked /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundChecked}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushChecked}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundChecked}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:checked:pointerover /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedPointerOver}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedPointerOver}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPointerOver}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:checked:pressed /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedPressed}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedPressed}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPressed}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:checked:disabled /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedDisabled}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedDisabled}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedDisabled}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:indeterminate /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminate}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminate}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminate}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:indeterminate:pointerover /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminatePointerOver}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminatePointerOver}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePointerOver}" /> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleButton:indeterminate:pressed /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminatePressed}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminatePressed}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePressed}" /> |
||||
</Style> |
</Style> |
||||
<Style Selector="ToggleButton:disabled"> |
|
||||
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/> |
<Style Selector="ToggleButton:indeterminate:disabled /template/ ContentPresenter"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminateDisabled}" /> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminateDisabled}" /> |
||||
|
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminateDisabled}" /> |
||||
</Style> |
</Style> |
||||
</Styles> |
</Styles> |
||||
|
|||||
@ -0,0 +1,294 @@ |
|||||
|
<Styles xmlns="https://github.com/avaloniaui" |
||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
||||
|
xmlns:sys="clr-namespace:System;assembly=netstandard"> |
||||
|
<Styles.Resources> |
||||
|
<Thickness x:Key="ToggleSwitchTopHeaderMargin">0,0,0,6</Thickness> |
||||
|
<x:Double x:Key="ToggleSwitchPreContentMargin">6</x:Double> |
||||
|
<x:Double x:Key="ToggleSwitchPostContentMargin">6</x:Double> |
||||
|
<x:Double x:Key="ToggleSwitchThemeMinWidth">154</x:Double> |
||||
|
<x:Double x:Key="KnobOnPosition">20</x:Double> |
||||
|
<x:Double x:Key="KnobOffPosition">0</x:Double> |
||||
|
</Styles.Resources> |
||||
|
<Design.PreviewWith> |
||||
|
<StackPanel Margin="20" Width="250" Spacing="24" > |
||||
|
<StackPanel Spacing="12" > |
||||
|
<TextBlock |
||||
|
Text="Automatic updates" |
||||
|
Classes="h1"/> |
||||
|
<TextBlock |
||||
|
Text="Updates will be automaticly Downloaded and installed shile the computer is shutting down or restarting" |
||||
|
TextWrapping="Wrap"/> |
||||
|
<ToggleSwitch HorizontalContentAlignment="Left" |
||||
|
Content="Enable automatic Updates?" |
||||
|
OffContent="Uit" |
||||
|
OnContent="Aan" |
||||
|
VerticalAlignment="Bottom"/> |
||||
|
</StackPanel> |
||||
|
|
||||
|
<StackPanel Spacing="12"> |
||||
|
<TextBlock |
||||
|
Text="Previewer" |
||||
|
Classes="h1"/> |
||||
|
<TextBlock |
||||
|
Text="The previewer Shows a preview off your code, this could slow down your system" |
||||
|
TextWrapping="Wrap"/> |
||||
|
<ToggleSwitch |
||||
|
Content="Previewer" |
||||
|
IsChecked="True" /> |
||||
|
</StackPanel> |
||||
|
</StackPanel> |
||||
|
</Design.PreviewWith> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch"> |
||||
|
<Setter Property="Foreground" Value="{DynamicResource ToggleSwitchContentForeground}" /> |
||||
|
<Setter Property="HorizontalAlignment" Value="Left" /> |
||||
|
<Setter Property="VerticalAlignment" Value="Center" /> |
||||
|
<Setter Property="HorizontalContentAlignment" Value="Left" /> |
||||
|
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> |
||||
|
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> |
||||
|
<Setter Property="Template"> |
||||
|
<ControlTemplate> |
||||
|
<Grid Background="{TemplateBinding Background}" |
||||
|
RowDefinitions="Auto,*"> |
||||
|
|
||||
|
<ContentPresenter x:Name="PART_ContentPresenter" |
||||
|
Grid.Row="0" |
||||
|
Content="{TemplateBinding Content}" |
||||
|
ContentTemplate="{TemplateBinding ContentTemplate}" |
||||
|
Margin="{DynamicResource ToggleSwitchTopHeaderMargin}" |
||||
|
VerticalAlignment="Top"/> |
||||
|
|
||||
|
<Grid Grid.Row="1" |
||||
|
MinWidth="{StaticResource ToggleSwitchThemeMinWidth}" |
||||
|
HorizontalAlignment="Left" |
||||
|
VerticalAlignment="Top"> |
||||
|
|
||||
|
<Grid.RowDefinitions> |
||||
|
<RowDefinition Height="{DynamicResource ToggleSwitchPreContentMargin}" /> |
||||
|
<RowDefinition Height="Auto" /> |
||||
|
<RowDefinition Height="{DynamicResource ToggleSwitchPostContentMargin}" /> |
||||
|
</Grid.RowDefinitions> |
||||
|
|
||||
|
<Grid.ColumnDefinitions> |
||||
|
<ColumnDefinition Width="Auto" /> |
||||
|
<ColumnDefinition Width="12" MaxWidth="12" /> |
||||
|
<ColumnDefinition Width="Auto" /> |
||||
|
</Grid.ColumnDefinitions> |
||||
|
|
||||
|
<Grid x:Name="SwitchAreaGrid" |
||||
|
Grid.RowSpan="3" |
||||
|
Grid.ColumnSpan="3" |
||||
|
TemplatedControl.IsTemplateFocusTarget="True" |
||||
|
Margin="0,5" /> |
||||
|
|
||||
|
<ContentPresenter x:Name="PART_OffContentPresenter" |
||||
|
Grid.RowSpan="3" |
||||
|
Grid.Column="2" |
||||
|
Content="{TemplateBinding OffContent}" |
||||
|
ContentTemplate="{TemplateBinding OffContentTemplate}" |
||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> |
||||
|
|
||||
|
<ContentPresenter x:Name="PART_OnContentPresenter" |
||||
|
Grid.RowSpan="3" |
||||
|
Grid.Column="2" |
||||
|
Content="{TemplateBinding OnContent}" |
||||
|
ContentTemplate="{TemplateBinding OnContentTemplate}" |
||||
|
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" |
||||
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> |
||||
|
|
||||
|
<Border x:Name="OuterBorder" |
||||
|
Grid.Row="1" |
||||
|
Height="20" |
||||
|
Width="40" |
||||
|
CornerRadius="10" |
||||
|
BorderThickness="{DynamicResource ToggleSwitchOuterBorderStrokeThickness}" /> |
||||
|
|
||||
|
<Border x:Name="SwitchKnobBounds" |
||||
|
Grid.Row="1" |
||||
|
Height="20" |
||||
|
Width="40" |
||||
|
CornerRadius="10" |
||||
|
BorderThickness="{DynamicResource ToggleSwitchOnStrokeThickness}"/> |
||||
|
|
||||
|
<Canvas x:Name="SwitchKnob" Grid.Row="1" |
||||
|
HorizontalAlignment="Left" |
||||
|
Width="20" Height="20"> |
||||
|
|
||||
|
<Grid x:Name="MovingKnobs" |
||||
|
Width="20" Height="20"> |
||||
|
|
||||
|
<Ellipse x:Name="SwitchKnobOn" |
||||
|
Width="10" Height="10" /> |
||||
|
|
||||
|
<Ellipse x:Name="SwitchKnobOff" |
||||
|
Width="10" Height="10" /> |
||||
|
</Grid> |
||||
|
</Canvas> |
||||
|
</Grid> |
||||
|
</Grid> |
||||
|
</ControlTemplate> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- NormalState --> |
||||
|
<Style Selector="ToggleSwitch /template/ Grid#SwitchAreaGrid"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackground}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Border#OuterBorder"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOff}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOff}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOn}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOn}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOn}"/> |
||||
|
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOn}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOff}"/> |
||||
|
<Setter Property="Stroke" Value="{DynamicResource ToggleSwitchKnobStrokeOff}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch /template/ Grid#MovingKnobs"> |
||||
|
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOffPosition}"/> |
||||
|
<Setter Property="Transitions"> |
||||
|
<Transitions> |
||||
|
<DoubleTransition Property="Canvas.Left" Duration="0:0:0.2" Easing="CubicEaseOut"/> |
||||
|
</Transitions> |
||||
|
</Setter> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- PointerOverState --> |
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Border#OuterBorder"> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffPointerOver}"/> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnPointerOver}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pointerover /template/ Grid#SwitchAreaGrid"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackgroundPointerOver}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- PressedState --> |
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Border#OuterBorder"> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffPressed}"/> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnPressed}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:pressed /template/ Grid#SwitchAreaGrid"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchContainerBackgroundPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- DisabledState --> |
||||
|
<Style Selector="ToggleSwitch:disabled"> |
||||
|
<Setter Property="Foreground" Value="{DynamicResource ToggleSwitchHeaderForegroundDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Border#OuterBorder"> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOffDisabled}"/> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOffPressed}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOffDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Fill" Value="{DynamicResource ToggleSwitchKnobFillOnDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:disabled /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Background" Value="{DynamicResource ToggleSwitchFillOnDisabled}"/> |
||||
|
<Setter Property="BorderBrush" Value="{DynamicResource ToggleSwitchStrokeOnDisabled}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!-- CheckedState --> |
||||
|
<Style Selector="ToggleSwitch:checked /template/ Grid#MovingKnobs"> |
||||
|
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOnPosition}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Border#OuterBorder"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ ContentPresenter#PART_OffContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:checked /template/ ContentPresenter#PART_OnContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<!--UncheckedState --> |
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Grid#MovingKnobs"> |
||||
|
<Setter Property="Canvas.Left" Value="{DynamicResource KnobOffPosition}"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Border#OuterBorder"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Ellipse#SwitchKnobOff"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Ellipse#SwitchKnobOn"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ Border#SwitchKnobBounds"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ ContentPresenter#PART_OffContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="1"/> |
||||
|
</Style> |
||||
|
|
||||
|
<Style Selector="ToggleSwitch:unchecked /template/ ContentPresenter#PART_OnContentPresenter"> |
||||
|
<Setter Property="Opacity" Value="0"/> |
||||
|
</Style> |
||||
|
</Styles> |
||||
@ -0,0 +1,35 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Media; |
||||
|
using Avalonia.Media.Transformation; |
||||
|
|
||||
|
namespace Avalonia.Animation.Animators |
||||
|
{ |
||||
|
public class TransformOperationsAnimator : Animator<TransformOperations> |
||||
|
{ |
||||
|
public TransformOperationsAnimator() |
||||
|
{ |
||||
|
Validate = ValidateTransform; |
||||
|
} |
||||
|
|
||||
|
public override TransformOperations Interpolate(double progress, TransformOperations oldValue, TransformOperations newValue) |
||||
|
{ |
||||
|
var oldTransform = EnsureOperations(oldValue); |
||||
|
var newTransform = EnsureOperations(newValue); |
||||
|
|
||||
|
return TransformOperations.Interpolate(oldTransform, newTransform, progress); |
||||
|
} |
||||
|
|
||||
|
internal static TransformOperations EnsureOperations(ITransform value) |
||||
|
{ |
||||
|
return value as TransformOperations ?? TransformOperations.Identity; |
||||
|
} |
||||
|
|
||||
|
private void ValidateTransform(AnimatorKeyFrame kf) |
||||
|
{ |
||||
|
if (!(kf.Value is TransformOperations)) |
||||
|
{ |
||||
|
throw new InvalidOperationException($"All keyframes must be of type {typeof(TransformOperations)}."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using System; |
||||
|
using System.Reactive.Linq; |
||||
|
using Avalonia.Animation.Animators; |
||||
|
using Avalonia.Media; |
||||
|
|
||||
|
namespace Avalonia.Animation |
||||
|
{ |
||||
|
public class TransformOperationsTransition : Transition<ITransform> |
||||
|
{ |
||||
|
private static readonly TransformOperationsAnimator _operationsAnimator = new TransformOperationsAnimator(); |
||||
|
|
||||
|
public override IObservable<ITransform> DoTransition(IObservable<double> progress, |
||||
|
ITransform oldValue, |
||||
|
ITransform newValue) |
||||
|
{ |
||||
|
var oldTransform = TransformOperationsAnimator.EnsureOperations(oldValue); |
||||
|
var newTransform = TransformOperationsAnimator.EnsureOperations(newValue); |
||||
|
|
||||
|
return progress |
||||
|
.Select(p => |
||||
|
{ |
||||
|
var f = Easing.Ease(p); |
||||
|
|
||||
|
return _operationsAnimator.Interpolate(f, oldTransform, newTransform); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Avalonia.Media |
||||
|
{ |
||||
|
public interface IMutableTransform : ITransform |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Raised when the transform changes.
|
||||
|
/// </summary>
|
||||
|
event EventHandler Changed; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using System.ComponentModel; |
||||
|
|
||||
|
namespace Avalonia.Media |
||||
|
{ |
||||
|
[TypeConverter(typeof(TransformConverter))] |
||||
|
public interface ITransform |
||||
|
{ |
||||
|
Matrix Value { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Globalization; |
||||
|
using Avalonia.Media.Transformation; |
||||
|
|
||||
|
namespace Avalonia.Media |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Creates an <see cref="ITransform"/> from a string representation.
|
||||
|
/// </summary>
|
||||
|
public class TransformConverter : TypeConverter |
||||
|
{ |
||||
|
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) |
||||
|
{ |
||||
|
return sourceType == typeof(string); |
||||
|
} |
||||
|
|
||||
|
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) |
||||
|
{ |
||||
|
return TransformOperations.Parse((string)value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
namespace Avalonia.Media.Transformation |
||||
|
{ |
||||
|
internal static class InterpolationUtilities |
||||
|
{ |
||||
|
public static double InterpolateScalars(double from, double to, double progress) |
||||
|
{ |
||||
|
return from * (1d - progress) + to * progress; |
||||
|
} |
||||
|
|
||||
|
public static Vector InterpolateVectors(Vector from, Vector to, double progress) |
||||
|
{ |
||||
|
var x = InterpolateScalars(from.X, to.X, progress); |
||||
|
var y = InterpolateScalars(from.Y, to.Y, progress); |
||||
|
|
||||
|
return new Vector(x, y); |
||||
|
} |
||||
|
|
||||
|
public static Matrix ComposeTransform(Matrix.Decomposed decomposed) |
||||
|
{ |
||||
|
// According to https://www.w3.org/TR/css-transforms-1/#recomposing-to-a-2d-matrix
|
||||
|
|
||||
|
return Matrix.CreateTranslation(decomposed.Translate) * |
||||
|
Matrix.CreateRotation(decomposed.Angle) * |
||||
|
Matrix.CreateSkew(decomposed.Skew.X, decomposed.Skew.Y) * |
||||
|
Matrix.CreateScale(decomposed.Scale); |
||||
|
} |
||||
|
|
||||
|
public static Matrix.Decomposed InterpolateDecomposedTransforms(ref Matrix.Decomposed from, ref Matrix.Decomposed to, double progres) |
||||
|
{ |
||||
|
Matrix.Decomposed result = default; |
||||
|
|
||||
|
result.Translate = InterpolateVectors(from.Translate, to.Translate, progres); |
||||
|
result.Scale = InterpolateVectors(from.Scale, to.Scale, progres); |
||||
|
result.Skew = InterpolateVectors(from.Skew, to.Skew, progres); |
||||
|
result.Angle = InterpolateScalars(from.Angle, to.Angle, progres); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,230 @@ |
|||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace Avalonia.Media.Transformation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents a single primitive transform (like translation, rotation, scale, etc.).
|
||||
|
/// </summary>
|
||||
|
public struct TransformOperation |
||||
|
{ |
||||
|
public OperationType Type; |
||||
|
public Matrix Matrix; |
||||
|
public DataLayout Data; |
||||
|
|
||||
|
public enum OperationType |
||||
|
{ |
||||
|
Translate, |
||||
|
Rotate, |
||||
|
Scale, |
||||
|
Skew, |
||||
|
Matrix, |
||||
|
Identity |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns whether operation produces the identity matrix.
|
||||
|
/// </summary>
|
||||
|
public bool IsIdentity => Matrix.IsIdentity; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Bakes this operation to a transform matrix.
|
||||
|
/// </summary>
|
||||
|
public void Bake() |
||||
|
{ |
||||
|
Matrix = Matrix.Identity; |
||||
|
|
||||
|
switch (Type) |
||||
|
{ |
||||
|
case OperationType.Translate: |
||||
|
{ |
||||
|
Matrix = Matrix.CreateTranslation(Data.Translate.X, Data.Translate.Y); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Rotate: |
||||
|
{ |
||||
|
Matrix = Matrix.CreateRotation(Data.Rotate.Angle); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Scale: |
||||
|
{ |
||||
|
Matrix = Matrix.CreateScale(Data.Scale.X, Data.Scale.Y); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Skew: |
||||
|
{ |
||||
|
Matrix = Matrix.CreateSkew(Data.Skew.X, Data.Skew.Y); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns new identity transform operation.
|
||||
|
/// </summary>
|
||||
|
public static TransformOperation Identity => |
||||
|
new TransformOperation { Matrix = Matrix.Identity, Type = OperationType.Identity }; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Attempts to interpolate between two transform operations.
|
||||
|
/// </summary>
|
||||
|
/// <param name="from">Source operation.</param>
|
||||
|
/// <param name="to">Target operation.</param>
|
||||
|
/// <param name="progress">Interpolation progress.</param>
|
||||
|
/// <param name="result">Interpolation result that will be filled in when operation was successful.</param>
|
||||
|
/// <remarks>
|
||||
|
/// Based upon https://www.w3.org/TR/css-transforms-1/#interpolation-of-transform-functions.
|
||||
|
/// </remarks>
|
||||
|
public static bool TryInterpolate(TransformOperation? from, TransformOperation? to, double progress, |
||||
|
ref TransformOperation result) |
||||
|
{ |
||||
|
bool fromIdentity = IsOperationIdentity(ref from); |
||||
|
bool toIdentity = IsOperationIdentity(ref to); |
||||
|
|
||||
|
if (fromIdentity && toIdentity) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// ReSharper disable PossibleInvalidOperationException
|
||||
|
TransformOperation fromValue = fromIdentity ? Identity : from.Value; |
||||
|
TransformOperation toValue = toIdentity ? Identity : to.Value; |
||||
|
// ReSharper restore PossibleInvalidOperationException
|
||||
|
|
||||
|
var interpolationType = toIdentity ? fromValue.Type : toValue.Type; |
||||
|
|
||||
|
result.Type = interpolationType; |
||||
|
|
||||
|
switch (interpolationType) |
||||
|
{ |
||||
|
case OperationType.Translate: |
||||
|
{ |
||||
|
double fromX = fromIdentity ? 0 : fromValue.Data.Translate.X; |
||||
|
double fromY = fromIdentity ? 0 : fromValue.Data.Translate.Y; |
||||
|
|
||||
|
double toX = toIdentity ? 0 : toValue.Data.Translate.X; |
||||
|
double toY = toIdentity ? 0 : toValue.Data.Translate.Y; |
||||
|
|
||||
|
result.Data.Translate.X = InterpolationUtilities.InterpolateScalars(fromX, toX, progress); |
||||
|
result.Data.Translate.Y = InterpolationUtilities.InterpolateScalars(fromY, toY, progress); |
||||
|
|
||||
|
result.Bake(); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Rotate: |
||||
|
{ |
||||
|
double fromAngle = fromIdentity ? 0 : fromValue.Data.Rotate.Angle; |
||||
|
|
||||
|
double toAngle = toIdentity ? 0 : toValue.Data.Rotate.Angle; |
||||
|
|
||||
|
result.Data.Rotate.Angle = InterpolationUtilities.InterpolateScalars(fromAngle, toAngle, progress); |
||||
|
|
||||
|
result.Bake(); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Scale: |
||||
|
{ |
||||
|
double fromX = fromIdentity ? 1 : fromValue.Data.Scale.X; |
||||
|
double fromY = fromIdentity ? 1 : fromValue.Data.Scale.Y; |
||||
|
|
||||
|
double toX = toIdentity ? 1 : toValue.Data.Scale.X; |
||||
|
double toY = toIdentity ? 1 : toValue.Data.Scale.Y; |
||||
|
|
||||
|
result.Data.Scale.X = InterpolationUtilities.InterpolateScalars(fromX, toX, progress); |
||||
|
result.Data.Scale.Y = InterpolationUtilities.InterpolateScalars(fromY, toY, progress); |
||||
|
|
||||
|
result.Bake(); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Skew: |
||||
|
{ |
||||
|
double fromX = fromIdentity ? 0 : fromValue.Data.Skew.X; |
||||
|
double fromY = fromIdentity ? 0 : fromValue.Data.Skew.Y; |
||||
|
|
||||
|
double toX = toIdentity ? 0 : toValue.Data.Skew.X; |
||||
|
double toY = toIdentity ? 0 : toValue.Data.Skew.Y; |
||||
|
|
||||
|
result.Data.Skew.X = InterpolationUtilities.InterpolateScalars(fromX, toX, progress); |
||||
|
result.Data.Skew.Y = InterpolationUtilities.InterpolateScalars(fromY, toY, progress); |
||||
|
|
||||
|
result.Bake(); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Matrix: |
||||
|
{ |
||||
|
var fromMatrix = fromIdentity ? Matrix.Identity : fromValue.Matrix; |
||||
|
var toMatrix = toIdentity ? Matrix.Identity : toValue.Matrix; |
||||
|
|
||||
|
if (!Matrix.TryDecomposeTransform(fromMatrix, out Matrix.Decomposed fromDecomposed) || |
||||
|
!Matrix.TryDecomposeTransform(toMatrix, out Matrix.Decomposed toDecomposed)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var interpolated = |
||||
|
InterpolationUtilities.InterpolateDecomposedTransforms( |
||||
|
ref fromDecomposed, ref toDecomposed, |
||||
|
progress); |
||||
|
|
||||
|
result.Matrix = InterpolationUtilities.ComposeTransform(interpolated); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case OperationType.Identity: |
||||
|
{ |
||||
|
// Do nothing.
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private static bool IsOperationIdentity(ref TransformOperation? operation) |
||||
|
{ |
||||
|
return !operation.HasValue || operation.Value.IsIdentity; |
||||
|
} |
||||
|
|
||||
|
[StructLayout(LayoutKind.Explicit)] |
||||
|
public struct DataLayout |
||||
|
{ |
||||
|
[FieldOffset(0)] public SkewLayout Skew; |
||||
|
|
||||
|
[FieldOffset(0)] public ScaleLayout Scale; |
||||
|
|
||||
|
[FieldOffset(0)] public TranslateLayout Translate; |
||||
|
|
||||
|
[FieldOffset(0)] public RotateLayout Rotate; |
||||
|
|
||||
|
public struct SkewLayout |
||||
|
{ |
||||
|
public double X; |
||||
|
public double Y; |
||||
|
} |
||||
|
|
||||
|
public struct ScaleLayout |
||||
|
{ |
||||
|
public double X; |
||||
|
public double Y; |
||||
|
} |
||||
|
|
||||
|
public struct TranslateLayout |
||||
|
{ |
||||
|
public double X; |
||||
|
public double Y; |
||||
|
} |
||||
|
|
||||
|
public struct RotateLayout |
||||
|
{ |
||||
|
public double Angle; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,258 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Avalonia.Media.Transformation |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Contains a list of <see cref="TransformOperation"/> that represent primitive transforms that will be
|
||||
|
/// applied in declared order.
|
||||
|
/// </summary>
|
||||
|
public sealed class TransformOperations : ITransform |
||||
|
{ |
||||
|
public static TransformOperations Identity { get; } = new TransformOperations(new List<TransformOperation>()); |
||||
|
|
||||
|
private readonly List<TransformOperation> _operations; |
||||
|
|
||||
|
private TransformOperations(List<TransformOperation> operations) |
||||
|
{ |
||||
|
_operations = operations ?? throw new ArgumentNullException(nameof(operations)); |
||||
|
|
||||
|
IsIdentity = CheckIsIdentity(); |
||||
|
|
||||
|
Value = ApplyTransforms(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns whether all operations combined together produce the identity matrix.
|
||||
|
/// </summary>
|
||||
|
public bool IsIdentity { get; } |
||||
|
|
||||
|
public IReadOnlyList<TransformOperation> Operations => _operations; |
||||
|
|
||||
|
public Matrix Value { get; } |
||||
|
|
||||
|
public static TransformOperations Parse(string s) |
||||
|
{ |
||||
|
return TransformParser.Parse(s); |
||||
|
} |
||||
|
|
||||
|
public static Builder CreateBuilder(int capacity) |
||||
|
{ |
||||
|
return new Builder(capacity); |
||||
|
} |
||||
|
|
||||
|
public static TransformOperations Interpolate(TransformOperations from, TransformOperations to, double progress) |
||||
|
{ |
||||
|
TransformOperations result = Identity; |
||||
|
|
||||
|
if (!TryInterpolate(from, to, progress, ref result)) |
||||
|
{ |
||||
|
// If the matrices cannot be interpolated, fallback to discrete animation logic.
|
||||
|
// See https://drafts.csswg.org/css-transforms/#matrix-interpolation
|
||||
|
result = progress < 0.5 ? from : to; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private Matrix ApplyTransforms(int startOffset = 0) |
||||
|
{ |
||||
|
Matrix matrix = Matrix.Identity; |
||||
|
|
||||
|
for (var i = startOffset; i < _operations.Count; i++) |
||||
|
{ |
||||
|
TransformOperation operation = _operations[i]; |
||||
|
matrix *= operation.Matrix; |
||||
|
} |
||||
|
|
||||
|
return matrix; |
||||
|
} |
||||
|
|
||||
|
private bool CheckIsIdentity() |
||||
|
{ |
||||
|
foreach (TransformOperation operation in _operations) |
||||
|
{ |
||||
|
if (!operation.IsIdentity) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private static bool TryInterpolate(TransformOperations from, TransformOperations to, double progress, ref TransformOperations result) |
||||
|
{ |
||||
|
bool fromIdentity = from.IsIdentity; |
||||
|
bool toIdentity = to.IsIdentity; |
||||
|
|
||||
|
if (fromIdentity && toIdentity) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
int matchingPrefixLength = ComputeMatchingPrefixLength(from, to); |
||||
|
int fromSize = fromIdentity ? 0 : from._operations.Count; |
||||
|
int toSize = toIdentity ? 0 : to._operations.Count; |
||||
|
int numOperations = Math.Max(fromSize, toSize); |
||||
|
|
||||
|
var builder = new Builder(matchingPrefixLength); |
||||
|
|
||||
|
for (int i = 0; i < matchingPrefixLength; i++) |
||||
|
{ |
||||
|
TransformOperation interpolated = new TransformOperation |
||||
|
{ |
||||
|
Type = TransformOperation.OperationType.Identity |
||||
|
}; |
||||
|
|
||||
|
if (!TransformOperation.TryInterpolate( |
||||
|
i >= fromSize ? default(TransformOperation?) : from._operations[i], |
||||
|
i >= toSize ? default(TransformOperation?) : to._operations[i], |
||||
|
progress, |
||||
|
ref interpolated)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
builder.Append(interpolated); |
||||
|
} |
||||
|
|
||||
|
if (matchingPrefixLength < numOperations) |
||||
|
{ |
||||
|
if (!ComputeDecomposedTransform(from, matchingPrefixLength, out Matrix.Decomposed fromDecomposed) || |
||||
|
!ComputeDecomposedTransform(to, matchingPrefixLength, out Matrix.Decomposed toDecomposed)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var transform = InterpolationUtilities.InterpolateDecomposedTransforms(ref fromDecomposed, ref toDecomposed, progress); |
||||
|
|
||||
|
builder.AppendMatrix(InterpolationUtilities.ComposeTransform(transform)); |
||||
|
} |
||||
|
|
||||
|
result = builder.Build(); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private static bool ComputeDecomposedTransform(TransformOperations operations, int startOffset, out Matrix.Decomposed decomposed) |
||||
|
{ |
||||
|
Matrix transform = operations.ApplyTransforms(startOffset); |
||||
|
|
||||
|
if (!Matrix.TryDecomposeTransform(transform, out decomposed)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
private static int ComputeMatchingPrefixLength(TransformOperations from, TransformOperations to) |
||||
|
{ |
||||
|
int numOperations = Math.Min(from._operations.Count, to._operations.Count); |
||||
|
|
||||
|
for (int i = 0; i < numOperations; i++) |
||||
|
{ |
||||
|
if (from._operations[i].Type != to._operations[i].Type) |
||||
|
{ |
||||
|
return i; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// If the operations match to the length of the shorter list, then pad its
|
||||
|
// length with the matching identity operations.
|
||||
|
// https://drafts.csswg.org/css-transforms/#transform-function-lists
|
||||
|
return Math.Max(from._operations.Count, to._operations.Count); |
||||
|
} |
||||
|
|
||||
|
public readonly struct Builder |
||||
|
{ |
||||
|
private readonly List<TransformOperation> _operations; |
||||
|
|
||||
|
public Builder(int capacity) |
||||
|
{ |
||||
|
_operations = new List<TransformOperation>(capacity); |
||||
|
} |
||||
|
|
||||
|
public void AppendTranslate(double x, double y) |
||||
|
{ |
||||
|
var toAdd = new TransformOperation(); |
||||
|
|
||||
|
toAdd.Type = TransformOperation.OperationType.Translate; |
||||
|
toAdd.Data.Translate.X = x; |
||||
|
toAdd.Data.Translate.Y = y; |
||||
|
|
||||
|
toAdd.Bake(); |
||||
|
|
||||
|
_operations.Add(toAdd); |
||||
|
} |
||||
|
|
||||
|
public void AppendRotate(double angle) |
||||
|
{ |
||||
|
var toAdd = new TransformOperation(); |
||||
|
|
||||
|
toAdd.Type = TransformOperation.OperationType.Rotate; |
||||
|
toAdd.Data.Rotate.Angle = angle; |
||||
|
|
||||
|
toAdd.Bake(); |
||||
|
|
||||
|
_operations.Add(toAdd); |
||||
|
} |
||||
|
|
||||
|
public void AppendScale(double x, double y) |
||||
|
{ |
||||
|
var toAdd = new TransformOperation(); |
||||
|
|
||||
|
toAdd.Type = TransformOperation.OperationType.Scale; |
||||
|
toAdd.Data.Scale.X = x; |
||||
|
toAdd.Data.Scale.Y = y; |
||||
|
|
||||
|
toAdd.Bake(); |
||||
|
|
||||
|
_operations.Add(toAdd); |
||||
|
} |
||||
|
|
||||
|
public void AppendSkew(double x, double y) |
||||
|
{ |
||||
|
var toAdd = new TransformOperation(); |
||||
|
|
||||
|
toAdd.Type = TransformOperation.OperationType.Skew; |
||||
|
toAdd.Data.Skew.X = x; |
||||
|
toAdd.Data.Skew.Y = y; |
||||
|
|
||||
|
toAdd.Bake(); |
||||
|
|
||||
|
_operations.Add(toAdd); |
||||
|
} |
||||
|
|
||||
|
public void AppendMatrix(Matrix matrix) |
||||
|
{ |
||||
|
var toAdd = new TransformOperation(); |
||||
|
|
||||
|
toAdd.Type = TransformOperation.OperationType.Matrix; |
||||
|
toAdd.Matrix = matrix; |
||||
|
|
||||
|
_operations.Add(toAdd); |
||||
|
} |
||||
|
|
||||
|
public void AppendIdentity() |
||||
|
{ |
||||
|
var toAdd = new TransformOperation(); |
||||
|
|
||||
|
toAdd.Type = TransformOperation.OperationType.Identity; |
||||
|
|
||||
|
_operations.Add(toAdd); |
||||
|
} |
||||
|
|
||||
|
public void Append(TransformOperation toAdd) |
||||
|
{ |
||||
|
_operations.Add(toAdd); |
||||
|
} |
||||
|
|
||||
|
public TransformOperations Build() |
||||
|
{ |
||||
|
return new TransformOperations(_operations); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,444 @@ |
|||||
|
using System; |
||||
|
using System.Globalization; |
||||
|
using Avalonia.Utilities; |
||||
|
|
||||
|
namespace Avalonia.Media.Transformation |
||||
|
{ |
||||
|
public static class TransformParser |
||||
|
{ |
||||
|
private static readonly (string, TransformFunction)[] s_functionMapping = |
||||
|
{ |
||||
|
("translate", TransformFunction.Translate), |
||||
|
("translateX", TransformFunction.TranslateX), |
||||
|
("translateY", TransformFunction.TranslateY), |
||||
|
("scale", TransformFunction.Scale), |
||||
|
("scaleX", TransformFunction.ScaleX), |
||||
|
("scaleY", TransformFunction.ScaleY), |
||||
|
("skew", TransformFunction.Skew), |
||||
|
("skewX", TransformFunction.SkewX), |
||||
|
("skewY", TransformFunction.SkewY), |
||||
|
("rotate", TransformFunction.Rotate), |
||||
|
("matrix", TransformFunction.Matrix) |
||||
|
}; |
||||
|
|
||||
|
private static readonly (string, Unit)[] s_unitMapping = |
||||
|
{ |
||||
|
("deg", Unit.Degree), |
||||
|
("grad", Unit.Gradian), |
||||
|
("rad", Unit.Radian), |
||||
|
("turn", Unit.Turn), |
||||
|
("px", Unit.Pixel) |
||||
|
}; |
||||
|
|
||||
|
public static TransformOperations Parse(string s) |
||||
|
{ |
||||
|
void ThrowInvalidFormat() |
||||
|
{ |
||||
|
throw new FormatException($"Invalid transform string: '{s}'."); |
||||
|
} |
||||
|
|
||||
|
if (string.IsNullOrEmpty(s)) |
||||
|
{ |
||||
|
throw new ArgumentException(nameof(s)); |
||||
|
} |
||||
|
|
||||
|
var span = s.AsSpan().Trim(); |
||||
|
|
||||
|
if (span.Equals("none".AsSpan(), StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
return TransformOperations.Identity; |
||||
|
} |
||||
|
|
||||
|
var builder = TransformOperations.CreateBuilder(0); |
||||
|
|
||||
|
while (true) |
||||
|
{ |
||||
|
var beginIndex = span.IndexOf('('); |
||||
|
var endIndex = span.IndexOf(')'); |
||||
|
|
||||
|
if (beginIndex == -1 || endIndex == -1) |
||||
|
{ |
||||
|
ThrowInvalidFormat(); |
||||
|
} |
||||
|
|
||||
|
var namePart = span.Slice(0, beginIndex).Trim(); |
||||
|
|
||||
|
var function = ParseTransformFunction(in namePart); |
||||
|
|
||||
|
if (function == TransformFunction.Invalid) |
||||
|
{ |
||||
|
ThrowInvalidFormat(); |
||||
|
} |
||||
|
|
||||
|
var valuePart = span.Slice(beginIndex + 1, endIndex - beginIndex - 1).Trim(); |
||||
|
|
||||
|
ParseFunction(in valuePart, function, in builder); |
||||
|
|
||||
|
span = span.Slice(endIndex + 1); |
||||
|
|
||||
|
if (span.IsWhiteSpace()) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return builder.Build(); |
||||
|
} |
||||
|
|
||||
|
private static void ParseFunction( |
||||
|
in ReadOnlySpan<char> functionPart, |
||||
|
TransformFunction function, |
||||
|
in TransformOperations.Builder builder) |
||||
|
{ |
||||
|
static UnitValue ParseValue(ReadOnlySpan<char> part) |
||||
|
{ |
||||
|
int unitIndex = -1; |
||||
|
|
||||
|
for (int i = 0; i < part.Length; i++) |
||||
|
{ |
||||
|
char c = part[i]; |
||||
|
|
||||
|
if (char.IsDigit(c) || c == '-' || c == '.') |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
unitIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
Unit unit = Unit.None; |
||||
|
|
||||
|
if (unitIndex != -1) |
||||
|
{ |
||||
|
var unitPart = part.Slice(unitIndex, part.Length - unitIndex); |
||||
|
|
||||
|
unit = ParseUnit(unitPart); |
||||
|
|
||||
|
part = part.Slice(0, unitIndex); |
||||
|
} |
||||
|
|
||||
|
var value = double.Parse(part.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture); |
||||
|
|
||||
|
return new UnitValue(unit, value); |
||||
|
} |
||||
|
|
||||
|
static int ParseValuePair( |
||||
|
in ReadOnlySpan<char> part, |
||||
|
ref UnitValue leftValue, |
||||
|
ref UnitValue rightValue) |
||||
|
{ |
||||
|
var commaIndex = part.IndexOf(','); |
||||
|
|
||||
|
if (commaIndex != -1) |
||||
|
{ |
||||
|
var leftPart = part.Slice(0, commaIndex).Trim(); |
||||
|
var rightPart = part.Slice(commaIndex + 1, part.Length - commaIndex - 1).Trim(); |
||||
|
|
||||
|
leftValue = ParseValue(leftPart); |
||||
|
rightValue = ParseValue(rightPart); |
||||
|
|
||||
|
return 2; |
||||
|
} |
||||
|
|
||||
|
leftValue = ParseValue(part); |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
static int ParseCommaDelimitedValues(ReadOnlySpan<char> part, in Span<UnitValue> outValues) |
||||
|
{ |
||||
|
int valueIndex = 0; |
||||
|
|
||||
|
while (true) |
||||
|
{ |
||||
|
if (valueIndex >= outValues.Length) |
||||
|
{ |
||||
|
throw new FormatException("Too many provided values."); |
||||
|
} |
||||
|
|
||||
|
var commaIndex = part.IndexOf(','); |
||||
|
|
||||
|
if (commaIndex == -1) |
||||
|
{ |
||||
|
if (!part.IsWhiteSpace()) |
||||
|
{ |
||||
|
outValues[valueIndex++] = ParseValue(part); |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
var valuePart = part.Slice(0, commaIndex).Trim(); |
||||
|
|
||||
|
outValues[valueIndex++] = ParseValue(valuePart); |
||||
|
|
||||
|
part = part.Slice(commaIndex + 1, part.Length - commaIndex - 1); |
||||
|
} |
||||
|
|
||||
|
return valueIndex; |
||||
|
} |
||||
|
|
||||
|
switch (function) |
||||
|
{ |
||||
|
case TransformFunction.Scale: |
||||
|
case TransformFunction.ScaleX: |
||||
|
case TransformFunction.ScaleY: |
||||
|
{ |
||||
|
var scaleX = UnitValue.One; |
||||
|
var scaleY = UnitValue.One; |
||||
|
|
||||
|
int count = ParseValuePair(functionPart, ref scaleX, ref scaleY); |
||||
|
|
||||
|
if (count != 1 && (function == TransformFunction.ScaleX || function == TransformFunction.ScaleY)) |
||||
|
{ |
||||
|
ThrowFormatInvalidValueCount(function, 1); |
||||
|
} |
||||
|
|
||||
|
VerifyZeroOrUnit(function, in scaleX, Unit.None); |
||||
|
VerifyZeroOrUnit(function, in scaleY, Unit.None); |
||||
|
|
||||
|
if (function == TransformFunction.ScaleY) |
||||
|
{ |
||||
|
scaleY = scaleX; |
||||
|
scaleX = UnitValue.One; |
||||
|
} |
||||
|
else if (function == TransformFunction.Scale && count == 1) |
||||
|
{ |
||||
|
scaleY = scaleX; |
||||
|
} |
||||
|
|
||||
|
builder.AppendScale(scaleX.Value, scaleY.Value); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case TransformFunction.Skew: |
||||
|
case TransformFunction.SkewX: |
||||
|
case TransformFunction.SkewY: |
||||
|
{ |
||||
|
var skewX = UnitValue.Zero; |
||||
|
var skewY = UnitValue.Zero; |
||||
|
|
||||
|
int count = ParseValuePair(functionPart, ref skewX, ref skewY); |
||||
|
|
||||
|
if (count != 1 && (function == TransformFunction.SkewX || function == TransformFunction.SkewY)) |
||||
|
{ |
||||
|
ThrowFormatInvalidValueCount(function, 1); |
||||
|
} |
||||
|
|
||||
|
VerifyZeroOrAngle(function, in skewX); |
||||
|
VerifyZeroOrAngle(function, in skewY); |
||||
|
|
||||
|
if (function == TransformFunction.SkewY) |
||||
|
{ |
||||
|
skewY = skewX; |
||||
|
skewX = UnitValue.Zero; |
||||
|
} |
||||
|
|
||||
|
builder.AppendSkew(ToRadians(in skewX), ToRadians(in skewY)); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case TransformFunction.Rotate: |
||||
|
{ |
||||
|
var angle = UnitValue.Zero; |
||||
|
UnitValue _ = default; |
||||
|
|
||||
|
int count = ParseValuePair(functionPart, ref angle, ref _); |
||||
|
|
||||
|
if (count != 1) |
||||
|
{ |
||||
|
ThrowFormatInvalidValueCount(function, 1); |
||||
|
} |
||||
|
|
||||
|
VerifyZeroOrAngle(function, in angle); |
||||
|
|
||||
|
builder.AppendRotate(ToRadians(in angle)); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case TransformFunction.Translate: |
||||
|
case TransformFunction.TranslateX: |
||||
|
case TransformFunction.TranslateY: |
||||
|
{ |
||||
|
var translateX = UnitValue.Zero; |
||||
|
var translateY = UnitValue.Zero; |
||||
|
|
||||
|
int count = ParseValuePair(functionPart, ref translateX, ref translateY); |
||||
|
|
||||
|
if (count != 1 && (function == TransformFunction.TranslateX || function == TransformFunction.TranslateY)) |
||||
|
{ |
||||
|
ThrowFormatInvalidValueCount(function, 1); |
||||
|
} |
||||
|
|
||||
|
VerifyZeroOrUnit(function, in translateX, Unit.Pixel); |
||||
|
VerifyZeroOrUnit(function, in translateY, Unit.Pixel); |
||||
|
|
||||
|
if (function == TransformFunction.TranslateY) |
||||
|
{ |
||||
|
translateY = translateX; |
||||
|
translateX = UnitValue.Zero; |
||||
|
} |
||||
|
|
||||
|
builder.AppendTranslate(translateX.Value, translateY.Value); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
case TransformFunction.Matrix: |
||||
|
{ |
||||
|
Span<UnitValue> values = stackalloc UnitValue[6]; |
||||
|
|
||||
|
int count = ParseCommaDelimitedValues(functionPart, in values); |
||||
|
|
||||
|
if (count != 6) |
||||
|
{ |
||||
|
ThrowFormatInvalidValueCount(function, 6); |
||||
|
} |
||||
|
|
||||
|
foreach (UnitValue value in values) |
||||
|
{ |
||||
|
VerifyZeroOrUnit(function, value, Unit.None); |
||||
|
} |
||||
|
|
||||
|
var matrix = new Matrix( |
||||
|
values[0].Value, |
||||
|
values[1].Value, |
||||
|
values[2].Value, |
||||
|
values[3].Value, |
||||
|
values[4].Value, |
||||
|
values[5].Value); |
||||
|
|
||||
|
builder.AppendMatrix(matrix); |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void VerifyZeroOrUnit(TransformFunction function, in UnitValue value, Unit unit) |
||||
|
{ |
||||
|
bool isZero = value.Unit == Unit.None && value.Value == 0d; |
||||
|
|
||||
|
if (!isZero && value.Unit != unit) |
||||
|
{ |
||||
|
ThrowFormatInvalidValue(function, in value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void VerifyZeroOrAngle(TransformFunction function, in UnitValue value) |
||||
|
{ |
||||
|
if (value.Value != 0d && !IsAngleUnit(value.Unit)) |
||||
|
{ |
||||
|
ThrowFormatInvalidValue(function, in value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static bool IsAngleUnit(Unit unit) |
||||
|
{ |
||||
|
switch (unit) |
||||
|
{ |
||||
|
case Unit.Radian: |
||||
|
case Unit.Gradian: |
||||
|
case Unit.Degree: |
||||
|
case Unit.Turn: |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private static void ThrowFormatInvalidValue(TransformFunction function, in UnitValue value) |
||||
|
{ |
||||
|
var unitString = value.Unit == Unit.None ? string.Empty : value.Unit.ToString(); |
||||
|
|
||||
|
throw new FormatException($"Invalid value {value.Value} {unitString} for {function}"); |
||||
|
} |
||||
|
|
||||
|
private static void ThrowFormatInvalidValueCount(TransformFunction function, int count) |
||||
|
{ |
||||
|
throw new FormatException($"Invalid format. {function} expects {count} value(s)."); |
||||
|
} |
||||
|
|
||||
|
private static Unit ParseUnit(in ReadOnlySpan<char> part) |
||||
|
{ |
||||
|
foreach (var (name, unit) in s_unitMapping) |
||||
|
{ |
||||
|
if (part.Equals(name.AsSpan(), StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
return unit; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
throw new FormatException($"Invalid unit: {part.ToString()}"); |
||||
|
} |
||||
|
|
||||
|
private static TransformFunction ParseTransformFunction(in ReadOnlySpan<char> part) |
||||
|
{ |
||||
|
foreach (var (name, transformFunction) in s_functionMapping) |
||||
|
{ |
||||
|
if (part.Equals(name.AsSpan(), StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
return transformFunction; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return TransformFunction.Invalid; |
||||
|
} |
||||
|
|
||||
|
private static double ToRadians(in UnitValue value) |
||||
|
{ |
||||
|
return value.Unit switch |
||||
|
{ |
||||
|
Unit.Radian => value.Value, |
||||
|
Unit.Gradian => MathUtilities.Grad2Rad(value.Value), |
||||
|
Unit.Degree => MathUtilities.Deg2Rad(value.Value), |
||||
|
Unit.Turn => MathUtilities.Turn2Rad(value.Value), |
||||
|
_ => value.Value |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private enum Unit |
||||
|
{ |
||||
|
None, |
||||
|
Pixel, |
||||
|
Radian, |
||||
|
Gradian, |
||||
|
Degree, |
||||
|
Turn |
||||
|
} |
||||
|
|
||||
|
private readonly struct UnitValue |
||||
|
{ |
||||
|
public readonly Unit Unit; |
||||
|
public readonly double Value; |
||||
|
|
||||
|
public UnitValue(Unit unit, double value) |
||||
|
{ |
||||
|
Unit = unit; |
||||
|
Value = value; |
||||
|
} |
||||
|
|
||||
|
public static UnitValue Zero => new UnitValue(Unit.None, 0); |
||||
|
|
||||
|
public static UnitValue One => new UnitValue(Unit.None, 1); |
||||
|
} |
||||
|
|
||||
|
private enum TransformFunction |
||||
|
{ |
||||
|
Invalid, |
||||
|
Translate, |
||||
|
TranslateX, |
||||
|
TranslateY, |
||||
|
Scale, |
||||
|
ScaleX, |
||||
|
ScaleY, |
||||
|
Skew, |
||||
|
SkewX, |
||||
|
SkewY, |
||||
|
Rotate, |
||||
|
Matrix |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,222 @@ |
|||||
|
using System; |
||||
|
using Avalonia.Utilities; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Avalonia.Base.UnitTests.Utilities |
||||
|
{ |
||||
|
public class MathUtilitiesTests |
||||
|
{ |
||||
|
private const double AnyValue = 42.42; |
||||
|
private readonly double _calculatedAnyValue; |
||||
|
private readonly double _one; |
||||
|
private readonly double _zero; |
||||
|
|
||||
|
public MathUtilitiesTests() |
||||
|
{ |
||||
|
_calculatedAnyValue = 0.0; |
||||
|
_one = 0.0; |
||||
|
_zero = 1.0; |
||||
|
|
||||
|
const int N = 10; |
||||
|
var dxAny = AnyValue / N; |
||||
|
var dxOne = 1.0 / N; |
||||
|
var dxZero = _zero / N; |
||||
|
|
||||
|
for (var i = 0; i < N; ++i) |
||||
|
{ |
||||
|
_calculatedAnyValue += dxAny; |
||||
|
_one += dxOne; |
||||
|
_zero -= dxZero; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Two_Equivalent_Double_Values_Are_Close() |
||||
|
{ |
||||
|
var actual = MathUtilities.AreClose(AnyValue, _calculatedAnyValue); |
||||
|
|
||||
|
Assert.True(actual); |
||||
|
Assert.Equal(AnyValue, Math.Round(_calculatedAnyValue, 14)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Two_Equivalent_Single_Values_Are_Close() |
||||
|
{ |
||||
|
var expectedValue = (float)AnyValue; |
||||
|
var actualValue = (float)_calculatedAnyValue; |
||||
|
|
||||
|
var actual = MathUtilities.AreClose(expectedValue, actualValue); |
||||
|
|
||||
|
Assert.True(actual); |
||||
|
Assert.Equal((float) Math.Round(expectedValue, 5), (float) Math.Round(actualValue, 4)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Calculated_Double_One_Is_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.IsOne(_one); |
||||
|
|
||||
|
Assert.True(actual); |
||||
|
Assert.Equal(1.0, Math.Round(_one, 15)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Calculated_Single_One_Is_One() |
||||
|
{ |
||||
|
var actualValue = (float)_one; |
||||
|
|
||||
|
var actual = MathUtilities.IsOne(actualValue); |
||||
|
|
||||
|
Assert.True(actual); |
||||
|
Assert.Equal(1.0f, (float) Math.Round(actualValue, 7)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Calculated_Double_Zero_Is_Zero() |
||||
|
{ |
||||
|
var actual = MathUtilities.IsZero(_zero); |
||||
|
|
||||
|
Assert.True(actual); |
||||
|
Assert.Equal(0.0, Math.Round(_zero, 15)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Calculated_Single_Zero_Is_Zero() |
||||
|
{ |
||||
|
var actualValue = (float)_zero; |
||||
|
|
||||
|
var actual = MathUtilities.IsZero(actualValue); |
||||
|
|
||||
|
Assert.True(actual); |
||||
|
Assert.Equal(0.0f, (float) Math.Round(actualValue, 7)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Float_Clamp_Input_NaN_Return_NaN() |
||||
|
{ |
||||
|
var clamp = MathUtilities.Clamp(double.NaN, 0.0, 1.0); |
||||
|
Assert.True(double.IsNaN(clamp)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Float_Clamp_Input_NegativeInfinity_Return_Min() |
||||
|
{ |
||||
|
const double min = 0.0; |
||||
|
const double max = 1.0; |
||||
|
var actual = MathUtilities.Clamp(double.NegativeInfinity, min, max); |
||||
|
Assert.Equal(min, actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Float_Clamp_Input_PositiveInfinity_Return_Max() |
||||
|
{ |
||||
|
const double min = 0.0; |
||||
|
const double max = 1.0; |
||||
|
var actual = MathUtilities.Clamp(double.PositiveInfinity, min, max); |
||||
|
Assert.Equal(max, actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Double_Float_Zero_Less_Than_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.LessThan(0d, 1d); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Single_Float_Zero_Less_Than_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.LessThan(0f, 1f); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Double_Float_One_Not_Less_Than_Zero() |
||||
|
{ |
||||
|
var actual = MathUtilities.LessThan(1d, 0d); |
||||
|
Assert.False(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Single_Float_One_Not_Less_Than_Zero() |
||||
|
{ |
||||
|
var actual = MathUtilities.LessThan(1f, 0f); |
||||
|
Assert.False(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Double_Float_Zero_Not_Greater_Than_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.GreaterThan(0d, 1d); |
||||
|
Assert.False(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Single_Float_Zero_Not_Greater_Than_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.GreaterThan(0f, 1f); |
||||
|
Assert.False(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Double_Float_One_Greater_Than_Zero() |
||||
|
{ |
||||
|
var actual = MathUtilities.GreaterThan(1d, 0d); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Single_Float_One_Greater_Than_Zero() |
||||
|
{ |
||||
|
var actual = MathUtilities.GreaterThan(1f, 0f); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Double_Float_One_Less_Than_Or_Close_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.LessThanOrClose(1d, 1d); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Single_Float_One_Less_Than_Or_Close_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.LessThanOrClose(1f, 1f); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Double_Float_One_Greater_Than_Or_Close_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.GreaterThanOrClose(1d, 1d); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Single_Float_One_Greater_Than_Or_Close_One() |
||||
|
{ |
||||
|
var actual = MathUtilities.GreaterThanOrClose(1f, 1f); |
||||
|
Assert.True(actual); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Round_Layout_Value_Without_DPI_Aware() |
||||
|
{ |
||||
|
const double value = 42.5; |
||||
|
var expectedValue = Math.Round(value); |
||||
|
var actualValue = MathUtilities.RoundLayoutValue(value, 1.0); |
||||
|
Assert.Equal(expectedValue, actualValue); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Round_Layout_Value_With_DPI_Aware() |
||||
|
{ |
||||
|
const double dpiScale = 1.25; |
||||
|
const double value = 42.5; |
||||
|
var expectedValue = Math.Round(value * dpiScale) / dpiScale; |
||||
|
var actualValue = MathUtilities.RoundLayoutValue(value, dpiScale); |
||||
|
Assert.Equal(expectedValue, actualValue); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
using System.ComponentModel; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Avalonia.Benchmarks.Data |
||||
|
{ |
||||
|
internal class AccessorTestObject : INotifyPropertyChanged |
||||
|
{ |
||||
|
private string _test; |
||||
|
|
||||
|
public string Test |
||||
|
{ |
||||
|
get => _test; |
||||
|
set |
||||
|
{ |
||||
|
if (_test == value) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
_test = value; |
||||
|
|
||||
|
OnPropertyChanged(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public event PropertyChangedEventHandler PropertyChanged; |
||||
|
|
||||
|
public void Execute() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void Execute(object p0) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public void Execute(object p0, object p1) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
[NotifyPropertyChangedInvocator] |
||||
|
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) |
||||
|
{ |
||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Avalonia.Data.Core.Plugins; |
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
|
||||
|
namespace Avalonia.Benchmarks.Data |
||||
|
{ |
||||
|
[MemoryDiagnoser, InProcess] |
||||
|
public class PropertyAccessorPluginBenchmarks |
||||
|
{ |
||||
|
private readonly AccessorTestObject _targetStrongRef = new AccessorTestObject(); |
||||
|
|
||||
|
private readonly List<IPropertyAccessorPlugin> _oldPlugins; |
||||
|
private readonly List<IPropertyAccessorPlugin> _newPlugins; |
||||
|
|
||||
|
public PropertyAccessorPluginBenchmarks() |
||||
|
{ |
||||
|
_oldPlugins = new List<IPropertyAccessorPlugin> |
||||
|
{ |
||||
|
new AvaloniaPropertyAccessorPlugin(), |
||||
|
new MethodAccessorPlugin(), |
||||
|
new InpcPropertyAccessorPlugin() |
||||
|
}; |
||||
|
|
||||
|
_newPlugins = new List<IPropertyAccessorPlugin> |
||||
|
{ |
||||
|
new AvaloniaPropertyAccessorPlugin(), |
||||
|
new InpcPropertyAccessorPlugin(), |
||||
|
new MethodAccessorPlugin() |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void MatchAccessorOld() |
||||
|
{ |
||||
|
var propertyName = nameof(AccessorTestObject.Test); |
||||
|
|
||||
|
foreach (IPropertyAccessorPlugin x in _oldPlugins) |
||||
|
{ |
||||
|
if (x.Match(_targetStrongRef, propertyName)) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Benchmark] |
||||
|
public void MatchAccessorNew() |
||||
|
{ |
||||
|
var propertyName = nameof(AccessorTestObject.Test); |
||||
|
|
||||
|
foreach (IPropertyAccessorPlugin x in _newPlugins) |
||||
|
{ |
||||
|
if (x.Match(_targetStrongRef, propertyName)) |
||||
|
{ |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue