Browse Source

agh

pull/13954/head
Jumar Macato 2 years ago
parent
commit
c2744effa3
  1. 2
      samples/Sandbox/App.axaml
  2. 28
      samples/Sandbox/MainWindow.axaml
  3. 5
      samples/Sandbox/Sandbox.csproj
  4. 3
      src/Avalonia.Base/Animation/Animatable.cs
  5. 2
      src/Avalonia.Base/Animation/Animation.cs
  6. 7
      src/Avalonia.Base/Animation/Animators/Animator`1.cs
  7. 69
      src/Avalonia.Base/Animation/DisposeAnimationInstanceSubject.cs
  8. 95
      src/Avalonia.Base/Visual.cs

2
samples/Sandbox/App.axaml

@ -3,6 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Sandbox.App">
<Application.Styles>
<FluentTheme />
<SimpleTheme />
</Application.Styles>
</Application>

28
samples/Sandbox/MainWindow.axaml

@ -1,4 +1,32 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
x:Class="Sandbox.MainWindow">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A progress bar control</TextBlock>
<StackPanel Spacing="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Maximum</TextBlock>
<NumericUpDown x:Name="maximum" Value="100" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Minimum</TextBlock>
<NumericUpDown x:Name="minimum" Value="0" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="5">
<TextBlock VerticalAlignment="Center">Progress Text Format</TextBlock>
<TextBox x:Name="stringFormat" Text="{}{0:0}%" VerticalAlignment="Center"/>
</StackPanel>
<CheckBox x:Name="showProgress" Margin="10,16,0,0" Content="Show Progress Text" />
<CheckBox x:Name="isIndeterminate" Margin="10,16,0,0" Content="Toggle Indeterminate" />
<CheckBox x:Name="isVisible" Margin="10,16,0,0" Content="Toggle Visibility" />
<StackPanel IsVisible="{Binding #isVisible.IsChecked}" Orientation="Horizontal" Margin="0,16,0,0" HorizontalAlignment="Center" Spacing="16">
<StackPanel Spacing="16">
<ProgressBar Height="200" IsIndeterminate="{Binding #isIndeterminate.IsChecked}" ShowProgressText="{Binding #showProgress.IsChecked}" Value="1.0"
Minimum="{Binding #minimum.Value}" Maximum="{Binding #maximum.Value}" ProgressTextFormat="{Binding #stringFormat.Text}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
</Window>

5
samples/Sandbox/Sandbox.csproj

@ -2,9 +2,8 @@
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
<TargetFramework>net8.0</TargetFramework>
<IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
<!-- <AvaloniaXamlIlDebuggerLaunch>true</AvaloniaXamlIlDebuggerLaunch>-->
</PropertyGroup>

3
src/Avalonia.Base/Animation/Animatable.cs

@ -32,8 +32,7 @@ namespace Avalonia.Animation
private Dictionary<ITransition, TransitionState>? _transitionState;
private NotifyCollectionChangedEventHandler? _collectionChanged;
protected bool _animationsEnabled = true;
internal LightweightSubject<bool>? animationsStateSubject;
internal LightweightSubject<bool>? animationsGateSubject;
private NotifyCollectionChangedEventHandler TransitionsCollectionChangedHandler =>
_collectionChanged ??= TransitionsCollectionChanged;

2
src/Avalonia.Base/Animation/Animation.cs

@ -277,7 +277,7 @@ namespace Avalonia.Animation
var (animators, subscriptions) = InterpretKeyframes(control);
var overrideSubject = new LightweightSubject<bool>();
control.animationsStateSubject = overrideSubject;
control.animationsGateSubject = overrideSubject;
if (animators.Count == 1)
{

7
src/Avalonia.Base/Animation/Animators/Animator`1.cs

@ -20,8 +20,11 @@ namespace Avalonia.Animation.Animators
public virtual IDisposable? Apply(Animation animation, Animatable target, IClock? clock,
IObservable<bool> match, IObservable<bool> state, Action? onComplete)
{
var subject = new DisposeAnimationInstanceSubject<T>(this, animation, target, clock, state, onComplete);
return new CompositeDisposable(match.Subscribe(subject), subject);
var disposable = new CompositeDisposable();
var subject = new DisposeAnimationInstanceSubject<T>(this, animation, target, clock, onComplete, state, disposable);
disposable.Add(match.Subscribe(subject));
disposable.Add(subject);
return disposable;
}
protected T InterpolationHandler(double animationTime, T neutralValue)

69
src/Avalonia.Base/Animation/DisposeAnimationInstanceSubject.cs

@ -1,5 +1,7 @@
using System;
using System.Threading;
using Avalonia.Animation.Animators;
using Avalonia.Reactive;
namespace Avalonia.Animation
{
@ -9,29 +11,32 @@ namespace Avalonia.Animation
internal class DisposeAnimationInstanceSubject<T> : IObserver<bool>, IDisposable
{
private IDisposable? _lastInstance;
private bool _lastMatch;
private bool _lastMatch, _lastGate;
private readonly Animator<T> _animator;
private readonly Animation _animation;
private readonly Animatable _control;
private readonly Action? _onComplete;
private readonly IClock? _clock;
private readonly object _lock = new object();
public DisposeAnimationInstanceSubject(Animator<T> animator,
Animation animation, Animatable control, IClock? clock, IObservable<bool> state, Action? onComplete)
public DisposeAnimationInstanceSubject(Animator<T> animator,
Animation animation, Animatable control, IClock? clock, Action? onComplete, IObservable<bool> state, CompositeDisposable disposable)
{
this._animator = animator;
this._animation = animation;
this._control = control;
this._onComplete = onComplete;
this._clock = clock;
// TODO: this causes weird bugs.
state.Subscribe(this);
disposable.Add(state.Subscribe(AnimationStateChange));
}
public void Dispose()
{
_lastInstance?.Dispose();
lock (_lock)
{
_lastInstance?.Dispose();
}
}
public void OnCompleted()
@ -40,27 +45,49 @@ namespace Avalonia.Animation
public void OnError(Exception error)
{
_lastInstance?.Dispose();
_lastInstance = null;
lock (_lock)
{
_lastInstance?.Dispose();
_lastInstance = null;
}
}
public void OnNext(bool matchVal)
private void AnimationStateChange(bool gateState)
{
if (matchVal != _lastMatch)
lock (_lock)
{
_lastInstance?.Dispose();
if(Volatile.Read(ref _lastGate) == gateState) return;
Volatile.Write(ref _lastGate, gateState);
StateChange();
}
}
if (matchVal)
{
_lastInstance = _animator.Run(_animation, _control, _clock, _onComplete);
}
else
{
_lastInstance = null;
}
public void OnNext(bool matchVal)
{
lock (_lock)
{
if (Volatile.Read(ref _lastMatch) == matchVal) return;
Volatile.Write(ref _lastMatch, matchVal);
StateChange();
}
}
void StateChange()
{
bool match = Volatile.Read(ref _lastMatch);
bool gate = Volatile.Read(ref _lastGate);
_lastInstance?.Dispose();
_lastMatch = matchVal;
if (match & gate)
{
_lastInstance = _animator.Run(_animation, _control, _clock, _onComplete);
}
else
{
_lastInstance = null;
}
}
}
}

95
src/Avalonia.Base/Visual.cs

@ -104,6 +104,14 @@ namespace Avalonia
/// </summary>
public static readonly DirectProperty<Visual, Visual?> VisualParentProperty =
AvaloniaProperty.RegisterDirect<Visual, Visual?>(nameof(VisualParent), o => o._visualParent);
/// <summary>
/// Defines the <see cref="IsEffectivelyVisible"/> property.
/// </summary>
public static readonly DirectProperty<Visual, bool> IsEffectivelyVisibleProperty =
AvaloniaProperty.RegisterDirect<Visual, bool>(
nameof(IsEffectivelyVisible),
o => o._isEffectivelyVisible);
/// <summary>
/// Defines the <see cref="ZIndex"/> property.
@ -121,7 +129,9 @@ namespace Avalonia
private Visual? _visualParent;
private bool _hasMirrorTransform;
private TargetWeakEventSubscriber<Visual, EventArgs>? _affectsRenderWeakSubscriber;
private bool _isEffectivelyVisible = true;
/// <summary>
/// Initializes static members of the <see cref="Visual"/> class.
/// </summary>
@ -143,6 +153,7 @@ namespace Avalonia
/// </summary>
public Visual()
{
SetAnimationGateState(false);
// Disable transitions until we're added to the visual tree.
DisableTransitions();
@ -193,25 +204,7 @@ namespace Avalonia
/// <summary>
/// Gets a value indicating whether this control and all its parents are visible.
/// </summary>
public bool IsEffectivelyVisible
{
get
{
Visual? node = this;
while (node != null)
{
if (!node.IsVisible)
{
return false;
}
node = node.VisualParent;
}
return true;
}
}
public bool IsEffectivelyVisible { get; }
/// <summary>
/// Gets or sets a value indicating whether this control is visible.
@ -496,7 +489,7 @@ namespace Avalonia
mutableTransform.Changed += RenderTransformChanged;
}
EnableAnimations();
SetAnimationGateState(true);
EnableTransitions();
if (_visualRoot.Renderer is IRendererWithCompositor compositingRenderer)
@ -540,7 +533,7 @@ namespace Avalonia
mutableTransform.Changed -= RenderTransformChanged;
}
DisableAnimations();
SetAnimationGateState(false);
DisableTransitions();
OnDetachedFromVisualTree(e);
DetachFromCompositor();
@ -668,53 +661,33 @@ namespace Avalonia
/// Called when the <see cref="IsVisible"/> property changes on any control.
/// </summary>
/// <param name="e">The event args.</param>
private static void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
private void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is not Visual visual) return;
if (visual.IsEffectivelyVisible)
if (e.NewValue is bool newVisibility)
{
visual.EnableAnimations();
}
else
{
visual.DisableAnimations();
}
CalculateEffectiveVisibility(newVisibility);
}
}
internal void EnableAnimations()
private void SetAnimationGateState(bool gateState)
{
if (!_animationsEnabled)
if (animationsGateSubject is { } animationsGate)
{
_animationsEnabled = true;
if (animationsStateSubject is { } ds)
{
ds.OnNext(true);
}
foreach (var child in this.GetVisualDescendants())
{
child.EnableAnimations();
}
}
animationsGate.OnNext(gateState);
}
}
internal void DisableAnimations()
internal void CalculateEffectiveVisibility(bool newVisibility)
{
if (_animationsEnabled)
if(_isEffectivelyVisible == newVisibility) return;
SetAndRaise(IsEffectivelyVisibleProperty, ref _isEffectivelyVisible, newVisibility);
SetAnimationGateState(newVisibility);
foreach (var child in this.GetVisualDescendants())
{
_animationsEnabled = false;
if (animationsStateSubject is { } ds)
{
ds.OnNext(false);
}
foreach (var child in this.GetVisualDescendants())
{
child.DisableAnimations();
}
child.CalculateEffectiveVisibility(newVisibility);
}
}

Loading…
Cancel
Save