Privacy Ring Improvements
ADD
WalletWasabi.Fluent/Behaviors/FadeInBehavior.cs
+using Avalonia;
+using Avalonia.Animation;
+using Avalonia.Styling;
+using System.Reactive.Disposables;
+
+namespace WalletWasabi.Fluent.Behaviors;
+
+public class FadeInBehavior : AttachedToVisualTreeBehavior<Visual>
+{
+ public static readonly StyledProperty<TimeSpan> InitialDelayProperty =
+ AvaloniaProperty.Register<ItemsControlAnimationBehavior, TimeSpan>(nameof(InitialDelay), TimeSpan.FromMilliseconds(500));
+
+ public static readonly StyledProperty<TimeSpan> DurationProperty =
+ AvaloniaProperty.Register<ItemsControlAnimationBehavior, TimeSpan>(nameof(Duration), TimeSpan.FromMilliseconds(250));
+
+ public TimeSpan InitialDelay
+ {
+ get => GetValue(InitialDelayProperty);
+ set => SetValue(InitialDelayProperty, value);
+ }
+
+ public TimeSpan Duration
+ {
+ get => GetValue(DurationProperty);
+ set => SetValue(DurationProperty, value);
+ }
+
+ protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
+ {
+ if (AssociatedObject is null)
+ {
+ return;
+ }
+
+ var totalDuration = InitialDelay + Duration;
+
+ var animation = new Animation
+ {
+ Duration = totalDuration,
+ Children =
+ {
+ new KeyFrame
+ {
+ KeyTime = TimeSpan.Zero,
+ Setters =
+ {
+ new Setter(Visual.OpacityProperty, 0d),
+ }
+ },
+ new KeyFrame
+ {
+ KeyTime = InitialDelay,
+ Setters =
+ {
+ new Setter(Visual.OpacityProperty, 0d),
+ }
+ },
+ new KeyFrame
+ {
+ KeyTime = Duration,
+ Setters =
+ {
+ new Setter(Visual.OpacityProperty, 1d),
+ }
+ }
+ }
+ };
+ animation.RunAsync(AssociatedObject, null);
+ }
+}

ADD
WalletWasabi.Fluent/Behaviors/ItemsPresenterAnimationBehavior.cs
+using Avalonia;
+using Avalonia.Animation;
+using Avalonia.Controls;
+using Avalonia.Controls.Generators;
+using Avalonia.Styling;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+
+namespace WalletWasabi.Fluent.Behaviors;
+
+public class ItemsControlAnimationBehavior : AttachedToVisualTreeBehavior<ItemsControl>
+{
+ public static readonly StyledProperty<TimeSpan> InitialDelayProperty =
+ AvaloniaProperty.Register<ItemsControlAnimationBehavior, TimeSpan>(nameof(InitialDelay), TimeSpan.FromMilliseconds(500));
+
+ public static readonly StyledProperty<TimeSpan> ItemDurationProperty =
+ AvaloniaProperty.Register<ItemsControlAnimationBehavior, TimeSpan>(nameof(ItemDuration), TimeSpan.FromMilliseconds(10));
+
+ public TimeSpan InitialDelay
+ {
+ get => GetValue(InitialDelayProperty);
+ set => SetValue(InitialDelayProperty, value);
+ }
+
+ public TimeSpan ItemDuration
+ {
+ get => GetValue(ItemDurationProperty);
+ set => SetValue(ItemDurationProperty, value);
+ }
+
+ protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
+ {
+ if (AssociatedObject is null)
+ {
+ return;
+ }
+
+ Observable
+ .FromEventPattern<ItemContainerEventArgs>(AssociatedObject.ItemContainerGenerator, nameof(ItemContainerGenerator.Materialized))
+ .Select(x => x.EventArgs)
+ .Subscribe(e =>
+ {
+ foreach (var c in e.Containers)
+ {
+ if (c.ContainerControl is not Visual v)
+ {
+ continue;
+ }
+
+ var duration = ItemDuration * (c.Index + 1);
+ var totalDuration = InitialDelay + (duration * 2);
+
+ var animation = new Animation
+ {
+ Duration = totalDuration,
+ Children =
+ {
+ new KeyFrame
+ {
+ KeyTime = TimeSpan.Zero,
+ Setters =
+ {
+ new Setter(Visual.OpacityProperty, 0d),
+ }
+ },
+ new KeyFrame
+ {
+ KeyTime = duration + InitialDelay,
+ Setters =
+ {
+ new Setter(Visual.OpacityProperty, 0d),
+ }
+ },
+ new KeyFrame
+ {
+ KeyTime = totalDuration,
+ Setters =
+ {
+ new Setter(Visual.OpacityProperty, 1d),
+ }
+ }
+ }
+ };
+ animation.RunAsync(v, null);
+ }
+ })
+ .DisposeWith(disposable);
+ }
+}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyRing/PrivacyRingViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyRing/PrivacyRingViewModel.cs
@@ -27,8 +27,8 @@ public partial class PrivacyRingViewModel : RoutableViewModel
{
Wallet = walletViewModel.Wallet;
- OuterRadius = 250d;
- InnerRadius = 240d;
+ OuterRadius = 240d;
+ InnerRadius = 230d;
NextCommand = CancelCommand;

MODIFY
WalletWasabi.Fluent/Views/Wallets/Home/Tiles/PrivacyRing/PrivacyRingView.axaml
WalletWasabi.Fluent/Views/Wallets/Home/Tiles/PrivacyRing/PrivacyRingView.axaml
<c:ContentArea Title="{Binding Title}"
EnableNext="True" NextContent="Done"
EnableCancel="False"
- EnableBack="True">
+ EnableBack="True"
+ Caption="Hover over the ring segments to see coin details">
<Panel>
- <ListBox Items="{Binding Items}" ClipToBounds="False" Margin="250 250 0 0" HorizontalAlignment="Center"
+ <ListBox Items="{Binding Items}" ClipToBounds="False" Margin="240 240 0 0" HorizontalAlignment="Center"
SelectedItem="{Binding SelectedItem}"
x:Name="Ring">
<i:Interaction.Behaviors>
<behaviors:ListBoxPreviewBehavior PreviewItem="{Binding SelectedItem, Mode=TwoWay}" Delay="1000" />
+ <behaviors:ItemsControlAnimationBehavior />
</i:Interaction.Behaviors>
<ListBox.Template>
<ControlTemplate TargetType="ListBox">
<Panel>
<!-- "transparent" Border used for MouseMove -->
- <Border Background="#01FFFFFF" Margin="-250 -250 0 0" />
+ <Border Background="White" Margin="-240 -240 0 0" Opacity="0.0001" />
<ItemsPresenter Name="PART_ItemsPresenter"
Items="{TemplateBinding Items}"
ItemsPanel="{TemplateBinding ItemsPanel}"
</Carousel.PageTransition>
<Carousel.DataTemplates>
<DataTemplate DataType="{x:Type tiles:PrivacyControlTileViewModel}">
- <tilesView:PrivacyControlTileView Width="300" Height="150" />
+ <tilesView:PrivacyControlTileView Width="300" Height="150" IsEnabled="False">
+ <i:Interaction.Behaviors>
+ <behaviors:FadeInBehavior InitialDelay="0:0:1" />
+ </i:Interaction.Behaviors>
+ </tilesView:PrivacyControlTileView>
</DataTemplate>
<DataTemplate DataType="{x:Type ring:PrivacyRingItemViewModel}">
<c:TileControl TileSize="Medium" Width="300" Height="150">

Privacy Ring Improvements