123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- using System;
- using System.Threading;
- using Avalonia;
- using Avalonia.Animation;
- using Avalonia.Controls;
- using Avalonia.Controls.Presenters;
- using Avalonia.Controls.Primitives;
- using Avalonia.Interactivity;
- using Avalonia.Media;
- using Avalonia.Styling;
- using Avalonia.Threading;
- namespace SukiUI.Controls
- {
- // TODO: This needs fairly significant work to make a bit more bomb proof
- // There are probably some more gains that can be made in terms of performance.
- // Unfortunately we're still bound by the arrange of controls having to happen on the main thread.
- public class SukiTransitioningContentControl : TemplatedControl
- {
- internal static readonly StyledProperty<object?> FirstBufferProperty =
- AvaloniaProperty.Register<SukiTransitioningContentControl, object?>(nameof(FirstBuffer));
- internal object? FirstBuffer
- {
- get => GetValue(FirstBufferProperty);
- set => SetValue(FirstBufferProperty, value);
- }
- internal static readonly StyledProperty<object?> SecondBufferProperty =
- AvaloniaProperty.Register<SukiTransitioningContentControl, object?>(nameof(SecondBuffer));
- internal object? SecondBuffer
- {
- get => GetValue(SecondBufferProperty);
- set => SetValue(SecondBufferProperty, value);
- }
- public static readonly StyledProperty<object?> ContentProperty = AvaloniaProperty.Register<SukiTransitioningContentControl, object?>(nameof(Content));
- public object? Content
- {
- get => GetValue(ContentProperty);
- set => SetValue(ContentProperty, value);
- }
- private bool _isFirstBufferActive;
- private ContentPresenter? _firstBuffer = null;
- private ContentPresenter? _secondBuffer = null;
- private static readonly Animation FadeIn;
- private static readonly Animation FadeOut;
-
- private ContentPresenter? To => _isFirstBufferActive ? _firstBuffer : _secondBuffer;
- private ContentPresenter? From => _isFirstBufferActive ? _secondBuffer : _firstBuffer;
- private object? _contentBeforeApplied;
- static SukiTransitioningContentControl()
- {
- FadeIn = new Animation
- {
- Duration = TimeSpan.FromMilliseconds(400),
- Children =
- {
- new KeyFrame()
- {
- Setters =
- {
- new Setter
- {
- Property = OpacityProperty,
- Value = 0d
- }
- },
- Cue = new Cue(0d)
- },
- new KeyFrame()
- {
- Setters =
- {
- new Setter
- {
- Property = OpacityProperty,
- Value = 1d
- }
- },
- Cue = new Cue(1d)
- }
- },
- FillMode = FillMode.Forward
- };
- FadeOut = new Animation
- {
- Duration = TimeSpan.FromMilliseconds(400),
- Children =
- {
- new KeyFrame()
- {
- Setters =
- {
- new Setter
- {
- Property = OpacityProperty,
- Value = 1d
- }
- },
- Cue = new Cue(0d)
- },
- new KeyFrame()
- {
- Setters =
- {
- new Setter
- {
- Property = OpacityProperty,
- Value = 0d
- }
- },
- Cue = new Cue(1d)
- }
- },
- FillMode = FillMode.Forward
- };
- FadeIn.Duration = FadeOut.Duration = TimeSpan.FromMilliseconds(250);
- }
- private CancellationTokenSource _animCancellationToken = new();
-
- protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
- {
- base.OnPropertyChanged(change);
- if(change.Property == ContentProperty)
- PushContent(change.NewValue);
- }
- protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
- {
- base.OnApplyTemplate(e);
- if (e.NameScope.Get<ContentPresenter>("PART_FirstBufferControl") is { } fBuff)
- _firstBuffer = fBuff;
- if (e.NameScope.Get<ContentPresenter>("PART_SecondBufferControl") is { } sBuff)
- _secondBuffer = sBuff;
- if (_contentBeforeApplied != null)
- {
- PushContent(_contentBeforeApplied);
- _contentBeforeApplied = null;
- }
- }
- private void PushContent(object? content)
- {
- if (To is null || From is null)
- {
- _contentBeforeApplied = content;
- return;
- }
- try
- {
- _animCancellationToken.Cancel();
- _animCancellationToken.Dispose();
- }
- catch
- {
- }
- _animCancellationToken = new CancellationTokenSource();
-
- if (_isFirstBufferActive) SecondBuffer = content;
- else FirstBuffer = content;
- _isFirstBufferActive = !_isFirstBufferActive;
- try
- {
- FadeOut.RunAsync(From, _animCancellationToken.Token).ContinueWith(_ =>
- {
- Dispatcher.UIThread.Invoke(() =>
- {
- From.IsHitTestVisible = false;
- if (_isFirstBufferActive) SecondBuffer = null;
- else FirstBuffer = null;
- });
- });
- FadeIn.RunAsync(To, _animCancellationToken.Token).ContinueWith(_ =>
- Dispatcher.UIThread.Invoke(() => To.IsHitTestVisible = true));
- }
- catch
- {
- // ignored
- }
- }
- protected override void OnUnloaded(RoutedEventArgs e)
- {
- base.OnUnloaded(e);
- _animCancellationToken.Dispose();
- }
- }
- }
|