diff --git a/samples/RenderDemo/Pages/TransitionsPage.xaml b/samples/RenderDemo/Pages/TransitionsPage.xaml index f9f69fb341..1985074b0f 100644 --- a/samples/RenderDemo/Pages/TransitionsPage.xaml +++ b/samples/RenderDemo/Pages/TransitionsPage.xaml @@ -141,6 +141,39 @@ + + + + + + + + @@ -166,6 +199,9 @@ + + + diff --git a/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs b/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs index 0a01767a07..57861163d6 100644 --- a/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs +++ b/src/Avalonia.Controls/ExperimentalAcrylicBorder.cs @@ -3,6 +3,7 @@ using Avalonia.Layout; using Avalonia.Media; using Avalonia.Platform; using System; +using Avalonia.Media.Immutable; namespace Avalonia.Controls { @@ -90,7 +91,7 @@ namespace Avalonia.Controls } else { - _borderRenderHelper.Render(context, Bounds.Size, new Thickness(), CornerRadius, new SolidColorBrush(Material.FallbackColor), null, default); + _borderRenderHelper.Render(context, Bounds.Size, new Thickness(), CornerRadius, new ImmutableSolidColorBrush(Material.FallbackColor), null, default); } } diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index 3bec46a9ac..ff63e5644f 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -6,6 +6,7 @@ using Avalonia.Metadata; using Avalonia.Threading; using Avalonia.VisualTree; using Avalonia.Layout; +using Avalonia.Media.Immutable; namespace Avalonia.Controls.Presenters { @@ -360,32 +361,31 @@ namespace Avalonia.Controls.Presenters RenderInternal(context); - if (selectionStart == selectionEnd) + if (selectionStart == selectionEnd && _caretBlink) { - var caretBrush = CaretBrush; + var caretBrush = CaretBrush?.ToImmutable(); if (caretBrush is null) { - var backgroundColor = (Background as SolidColorBrush)?.Color; + var backgroundColor = (Background as ISolidColorBrush)?.Color; if (backgroundColor.HasValue) { byte red = (byte)~(backgroundColor.Value.R); byte green = (byte)~(backgroundColor.Value.G); byte blue = (byte)~(backgroundColor.Value.B); - caretBrush = new SolidColorBrush(Color.FromRgb(red, green, blue)); + caretBrush = new ImmutableSolidColorBrush(Color.FromRgb(red, green, blue)); } else + { caretBrush = Brushes.Black; + } } - if (_caretBlink) - { - var (p1, p2) = GetCaretPoints(); - context.DrawLine( - new Pen(caretBrush, 1), - p1, p2); - } + var (p1, p2) = GetCaretPoints(); + context.DrawLine( + new ImmutablePen(caretBrush, 1), + p1, p2); } } diff --git a/src/Avalonia.Controls/TickBar.cs b/src/Avalonia.Controls/TickBar.cs index 6ea5277a55..237bc2ce1d 100644 --- a/src/Avalonia.Controls/TickBar.cs +++ b/src/Avalonia.Controls/TickBar.cs @@ -1,6 +1,7 @@ using Avalonia.Collections; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.Media.Immutable; using Avalonia.Utilities; namespace Avalonia.Controls @@ -295,7 +296,7 @@ namespace Avalonia.Controls endPoint = pt; } - var pen = new Pen(Fill, 1.0d); + var pen = new ImmutablePen(Fill?.ToImmutable(), 1.0d); // Is it Vertical? if (Placement == TickBarPlacement.Left || Placement == TickBarPlacement.Right) diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs index 3128753781..f8ab58d46e 100644 --- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs +++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Media; +using Avalonia.Media.Immutable; using Avalonia.Platform; using Avalonia.Utilities; @@ -114,9 +115,9 @@ namespace Avalonia.Controls.Utils var borderThickness = _borderThickness.Top; IPen pen = null; - if (borderThickness > 0) + if (borderBrush != null && borderThickness > 0) { - pen = new Pen(borderBrush, borderThickness); + pen = new ImmutablePen(borderBrush.ToImmutable(), borderThickness); } var rect = new Rect(_size); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs index 63f68501a7..e4c4ca6115 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/AvaloniaPropertyViewModel.cs @@ -26,9 +26,11 @@ namespace Avalonia.Diagnostics.ViewModels public AvaloniaProperty Property { get; } public override object Key => Property; public override string Name { get; } - public bool IsAttached => Property.IsAttached; + public override bool? IsAttached => + Property.IsAttached; - public string Priority => _priority; + public override string Priority => + _priority; public override string Type => _type; @@ -69,7 +71,7 @@ namespace Avalonia.Diagnostics.ViewModels if (val != null) { RaiseAndSetIfChanged(ref _priority, val.Priority.ToString(), nameof(Priority)); - RaiseAndSetIfChanged(ref _group, IsAttached ? "Attached Properties" : "Properties", nameof(Group)); + RaiseAndSetIfChanged(ref _group, IsAttached == true ? "Attached Properties" : "Properties", nameof(Group)); } else { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs index 6b2dbb7bae..65626aeea5 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ClrPropertyViewModel.cs @@ -49,6 +49,12 @@ namespace Avalonia.Diagnostics.ViewModels } } + public override string Priority => + string.Empty; + + public override bool? IsAttached => + default; + // [MemberNotNull(nameof(_type))] public override void Update() { diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs index 1f7205eb16..3790951b0c 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/ControlDetailsViewModel.cs @@ -18,7 +18,7 @@ namespace Avalonia.Diagnostics.ViewModels { private readonly IVisual _control; private readonly IDictionary> _propertyIndex; - private AvaloniaPropertyViewModel? _selectedProperty; + private PropertyViewModel? _selectedProperty; private bool _snapshotStyles; private bool _showInactiveStyles; private string? _styleStatus; @@ -139,7 +139,7 @@ namespace Avalonia.Diagnostics.ViewModels public ObservableCollection PseudoClasses { get; } - public AvaloniaPropertyViewModel? SelectedProperty + public PropertyViewModel? SelectedProperty { get => _selectedProperty; set => RaiseAndSetIfChanged(ref _selectedProperty, value); diff --git a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs index bfd098985a..fdbd8c1aa3 100644 --- a/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs +++ b/src/Avalonia.Diagnostics/Diagnostics/ViewModels/PropertyViewModel.cs @@ -16,7 +16,9 @@ namespace Avalonia.Diagnostics.ViewModels public abstract string Group { get; } public abstract string Type { get; } public abstract string Value { get; set; } - public abstract void Update(); + public abstract string Priority { get; } + public abstract bool? IsAttached { get; } + public abstract void Update(); protected static string ConvertToString(object? value) { diff --git a/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs b/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs new file mode 100644 index 0000000000..cc5af1b4b1 --- /dev/null +++ b/src/Avalonia.Visuals/Animation/Transitions/BrushTransition.cs @@ -0,0 +1,59 @@ +using System; +using Avalonia.Animation.Animators; +using Avalonia.Animation.Easings; +using Avalonia.Media; + +#nullable enable + +namespace Avalonia.Animation +{ + /// + /// Transition class that handles with type. + /// Only values of will transition correctly at the moment. + /// + public class BrushTransition : Transition + { + private static readonly ISolidColorBrushAnimator s_animator = new ISolidColorBrushAnimator(); + + public override IObservable DoTransition(IObservable progress, IBrush? oldValue, IBrush? newValue) + { + var oldSolidColorBrush = TryGetSolidColorBrush(oldValue); + var newSolidColorBrush = TryGetSolidColorBrush(newValue); + + if (oldSolidColorBrush != null && newSolidColorBrush != null) + { + return new AnimatorTransitionObservable( + s_animator, progress, Easing, oldSolidColorBrush, newSolidColorBrush); + } + + return new IncompatibleTransitionObservable(progress, Easing, oldValue, newValue); + } + + private static ISolidColorBrush? TryGetSolidColorBrush(IBrush? brush) + { + if (brush is null) + { + return Brushes.Transparent; + } + + return brush as ISolidColorBrush; + } + + private class IncompatibleTransitionObservable : TransitionObservableBase + { + private readonly IBrush? _from; + private readonly IBrush? _to; + + public IncompatibleTransitionObservable(IObservable progress, Easing easing, IBrush? from, IBrush? to) : base(progress, easing) + { + _from = from; + _to = to; + } + + protected override IBrush? ProduceValue(double progress) + { + return progress < 0.5 ? _from : _to; + } + } + } +} diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs index 32624fbf45..3256f4b11a 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutablePen.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; namespace Avalonia.Media.Immutable { @@ -44,6 +45,8 @@ namespace Avalonia.Media.Immutable PenLineJoin lineJoin = PenLineJoin.Miter, double miterLimit = 10.0) { + Debug.Assert(!(brush is IMutableBrush)); + Brush = brush; Thickness = thickness; LineCap = lineCap; diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs index c2a1a5f9e4..8c25019606 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PenTests.cs @@ -59,9 +59,9 @@ namespace Avalonia.Visuals.UnitTests.Media } [Fact] - public void Equality_Is_Implemented_Between_Immutable_And_Mmutable_Pens() + public void Equality_Is_Implemented_Between_Immutable_And_Mutable_Pens() { - var brush = new SolidColorBrush(Colors.Red); + var brush = new ImmutableSolidColorBrush(Colors.Red); var target1 = new ImmutablePen( brush: brush, thickness: 2, @@ -83,7 +83,7 @@ namespace Avalonia.Visuals.UnitTests.Media [Fact] public void Equality_Is_Implemented_Between_Mutable_And_Immutable_DashStyles() { - var brush = new SolidColorBrush(Colors.Red); + var brush = new ImmutableSolidColorBrush(Colors.Red); var target1 = new ImmutablePen( brush: brush, thickness: 2,