PrivacyRing Preview
DELETE
WalletWasabi.Fluent/Behaviors/ListBoxItemPointerOverSelectedBehavior.cs
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Input;
-using Avalonia.VisualTree;
-using Avalonia.Xaml.Interactions.Custom;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-
-namespace WalletWasabi.Fluent.Behaviors;
-
-public class ListBoxItemPointerOverSelectedBehavior : DisposingBehavior<ListBoxItem>
-{
- protected override void OnAttached(CompositeDisposable disposables)
- {
- if (AssociatedObject is null)
- {
- return;
- }
-
- //var listBox = AssociatedObject.FindAncestorOfType<ListBox>();
-
- //if (listBox is null)
- //{
- // return;
- //}
-
- Observable.FromEventPattern<AvaloniaPropertyChangedEventArgs>(AssociatedObject, nameof(PropertyChanged))
- .Select(x => x.EventArgs)
- .Subscribe(e =>
- {
- if (e.Property == InputElement.IsPointerOverProperty && e.NewValue is true)
- {
- AssociatedObject.IsSelected = true;
- //listBox.SelectedItem = AssociatedObject.DataContext;
- }
- })
- .DisposeWith(disposables);
- }
-}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyControlTileViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyControlTileViewModel.cs
@@ -7,11 +7,12 @@ using NBitcoin;
using ReactiveUI;
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Fluent.ViewModels.Navigation;
+using WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles.PrivacyRing;
using WalletWasabi.Wallets;
namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles;
-public partial class PrivacyControlTileViewModel : TileViewModel
+public partial class PrivacyControlTileViewModel : TileViewModel, IPrivacyRingPreviewItem
{
private readonly WalletViewModel _walletVm;
private readonly IObservable<Unit> _balanceChanged;

ADD
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyRing/IPrivacyRingPreviewItem.cs
+namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles.PrivacyRing;
+
+public interface IPrivacyRingPreviewItem
+{
+}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyRing/PrivacyRingItemViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyRing/PrivacyRingItemViewModel.cs
@@ -3,9 +3,9 @@ using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Fluent.Helpers;
using WalletWasabi.Fluent.ViewModels.Wallets.Advanced.WalletCoins;
-namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles;
+namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles.PrivacyRing;
-public class PrivacyRingItemViewModel : WalletCoinViewModel
+public class PrivacyRingItemViewModel : WalletCoinViewModel, IPrivacyRingPreviewItem
{
private const double TotalAngle = 2d * Math.PI;
private const double UprightAngle = Math.PI / 2d;
@@ -35,6 +35,7 @@ public class PrivacyRingItemViewModel : WalletCoinViewModel
IsPrivate = coin.IsPrivate(parent.Wallet.KeyManager.AnonScoreTarget);
IsSemiPrivate = !IsPrivate && coin.IsSemiPrivate();
IsNonPrivate = !IsPrivate && !IsSemiPrivate;
+ AmountText = $"{Amount.ToFormattedString()} BTC";
}
public double OuterRadius { get; }
@@ -50,6 +51,7 @@ public class PrivacyRingItemViewModel : WalletCoinViewModel
public bool IsPrivate { get; }
public bool IsSemiPrivate { get; }
public bool IsNonPrivate { get; }
+ public string AmountText { get; }
private Point GetAnglePoint(double r, double angle)
{
@@ -58,10 +60,3 @@ public class PrivacyRingItemViewModel : WalletCoinViewModel
return new Point(x, y);
}
}
-
-public enum PrivacyRingItemPrivacyState
-{
- NonPrivate = 1,
- SemiPrivate = 2,
- Private = 3
-}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyRing/PrivacyRingViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Home/Tiles/PrivacyRing/PrivacyRingViewModel.cs
+using Avalonia.Controls;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
@@ -11,7 +12,7 @@ using WalletWasabi.Fluent.Models;
using WalletWasabi.Fluent.ViewModels.Navigation;
using WalletWasabi.Wallets;
-namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles;
+namespace WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles.PrivacyRing;
[NavigationMetaData(
Title = "Wallet Coins",
@@ -20,6 +21,7 @@ public partial class PrivacyRingViewModel : RoutableViewModel
{
private IObservable<Unit> _coinsUpdated;
private readonly SourceList<PrivacyRingItemViewModel> _itemsSourceList = new();
+ [AutoNotify] private PrivacyRingItemViewModel? _selectedItem;
public PrivacyRingViewModel(WalletViewModel walletViewModel, IObservable<Unit> balanceChanged)
{
@@ -30,6 +32,8 @@ public partial class PrivacyRingViewModel : RoutableViewModel
NextCommand = CancelCommand;
+ PreviewItems.Add(walletViewModel.Tiles.OfType<PrivacyControlTileViewModel>().FirstOrDefault());
+
_itemsSourceList
.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
@@ -49,6 +53,7 @@ public partial class PrivacyRingViewModel : RoutableViewModel
}
public ObservableCollectionExtended<PrivacyRingItemViewModel> Items { get; } = new();
+ public ObservableCollectionExtended<IPrivacyRingPreviewItem> PreviewItems { get; } = new();
public double OuterRadius { get; }
public double InnerRadius { get; }
@@ -79,6 +84,9 @@ public partial class PrivacyRingViewModel : RoutableViewModel
start = end;
}
}
+
+ PreviewItems.RemoveRange(1, PreviewItems.Count - 1);
+ PreviewItems.AddRange(list);
});
}

ADD
WalletWasabi.Fluent/Views/Wallets/Home/Tiles/PrivacyRing/PrivacyRingView.axaml
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:i="using:Avalonia.Xaml.Interactivity"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:c="using:WalletWasabi.Fluent.Controls"
+ xmlns:behaviors="using:WalletWasabi.Fluent.Behaviors"
+ xmlns:tiles="using:WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles"
+ xmlns:tilesView="using:WalletWasabi.Fluent.Views.Wallets.Home.Tiles"
+ xmlns:ring="using:WalletWasabi.Fluent.ViewModels.Wallets.Home.Tiles.PrivacyRing"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="WalletWasabi.Fluent.Views.Wallets.Home.Tiles.PrivacyRing.PrivacyRingView">
+ <UserControl.Styles>
+ <Style Selector="Path:pointerover">
+ <Setter Property="StrokeThickness" Value="1" />
+ <Setter Property="Stroke" Value="{DynamicResource SystemAccentColor}" />
+ </Style>
+
+ <Style Selector="Path.private">
+ <Setter Property="Fill" Value="{DynamicResource PrivacyLevelStrongBrush}" />
+ </Style>
+
+ <Style Selector="Path.semiPrivate">
+ <Setter Property="Fill" Value="{DynamicResource PrivacyLevelMediumBrush}" />
+ </Style>
+
+ <Style Selector="Path.nonPrivate">
+ <Setter Property="Fill" Value="{DynamicResource PrivacyLevelNoneBrush}" />
+ </Style>
+
+ <Style Selector="ListBoxItem">
+ <Setter Property="IsSelected" Value="{Binding IsPointerOver, Mode=TwoWay}" />
+ <Setter Property="ClipToBounds" Value="False" />
+ <Setter Property="Margin" Value="0" />
+ <Setter Property="Template">
+ <ControlTemplate TargetType="ListBoxItem">
+ <ContentPresenter Content="{TemplateBinding Content}"
+ ContentTemplate="{TemplateBinding ContentTemplate}" />
+ </ControlTemplate>
+ </Setter>
+ </Style>
+
+ <Style Selector="ListBoxItem:selected">
+ <Setter Property="Background" Value="Red" />
+ </Style>
+ </UserControl.Styles>
+ <c:ContentArea Title="{Binding Title}"
+ EnableNext="True" NextContent="Done"
+ EnableCancel="False"
+ EnableBack="True">
+ <Panel>
+ <ListBox Items="{Binding Items}" ClipToBounds="False" Margin="250 250 0 0" HorizontalAlignment="Center"
+ SelectedItem="{Binding SelectedItem}"
+ x:Name="Ring">
+ <i:Interaction.Behaviors>
+ <behaviors:ListBoxPreviewBehavior PreviewItem="{Binding SelectedItem, Mode=TwoWay}" />
+ </i:Interaction.Behaviors>
+ <ListBox.Template>
+ <ControlTemplate TargetType="ListBox">
+ <ItemsPresenter Name="PART_ItemsPresenter"
+ Items="{TemplateBinding Items}"
+ ItemsPanel="{TemplateBinding ItemsPanel}"
+ ItemTemplate="{TemplateBinding ItemTemplate}" />
+ </ControlTemplate>
+ </ListBox.Template>
+ <ListBox.ItemsPanel>
+ <ItemsPanelTemplate>
+ <Panel />
+ </ItemsPanelTemplate>
+ </ListBox.ItemsPanel>
+ <ListBox.ItemTemplate>
+ <DataTemplate>
+ <Path Stretch="None" Height="{Binding OuterRadius}" Width="{Binding OuterRadius}"
+ Classes.private="{Binding IsPrivate}"
+ Classes.semiPrivate="{Binding IsSemiPrivate}"
+ Classes.nonPrivate="{Binding IsNonPrivate}">
+ <Path.Data>
+ <PathGeometry>
+ <PathFigure StartPoint="{Binding Origin1}" IsClosed="True">
+ <ArcSegment Size="{Binding Arc1Size}" Point="{Binding Arc1}" />
+ <LineSegment Point="{Binding Origin2}" />
+ <ArcSegment Size="{Binding Arc2Size}" Point="{Binding Arc2}" SweepDirection="CounterClockwise" />
+ </PathFigure>
+ </PathGeometry>
+ </Path.Data>
+ </Path>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+ </ListBox>
+
+ <Carousel Items="{Binding PreviewItems}" IsVirtualized="False"
+ SelectedItem="{Binding SelectedItem, Mode=OneWay}"
+ VerticalAlignment="Center" HorizontalAlignment="Center">
+ <Carousel.PageTransition>
+ <CrossFade Duration="0.75" />
+ </Carousel.PageTransition>
+ <Carousel.DataTemplates>
+ <DataTemplate DataType="{x:Type tiles:PrivacyControlTileViewModel}">
+ <tilesView:PrivacyControlTileView Width="300" Height="150" />
+ </DataTemplate>
+ <DataTemplate DataType="{x:Type ring:PrivacyRingItemViewModel}">
+ <c:TileControl TileSize="Medium" Width="300" Height="150">
+ <DockPanel>
+ <c:LabelsListBox Items="{Binding SmartLabel}" DockPanel.Dock="Top" />
+ <StackPanel DockPanel.Dock="Bottom" Height="25" Spacing="8">
+ <Separator />
+ <StackPanel Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center">
+ <StackPanel Opacity="0.8" Orientation="Horizontal" Spacing="10" HorizontalAlignment="Center" IsVisible="{Binding !IsNonPrivate}">
+ <Border VerticalAlignment="Center" BorderThickness="1" CornerRadius="2"
+ BorderBrush="{StaticResource ButtonForeground}">
+ <TextBlock
+ Text="PRIVATE" Classes="bold" Margin="4 2 " VerticalAlignment="Center" HorizontalAlignment="Center" />
+ </Border>
+ </StackPanel>
+ <TextBlock Text="{Binding AnonymitySet, StringFormat='Anonymity Score: {0}'}" Classes="h8" VerticalAlignment="Center" HorizontalAlignment="Center" />
+ </StackPanel>
+ </StackPanel>
+
+ <StackPanel VerticalAlignment="Center" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 3">
+
+ <!--<Button Command="{Binding ShowDetailsCommand}" Classes="plain"
+ ToolTip.Tip="Show Details" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0 0 10 0"
+ Height="36" Width="36">
+ <Image Source="{StaticResource privacy_indicator_good}" />
+ </Button>-->
+
+ <TextBlock
+ VerticalAlignment="Center" HorizontalAlignment="Center"
+ TextAlignment="Center"
+ Text="{Binding AmountText}" Classes="h2" />
+ </StackPanel>
+ </DockPanel>
+ </c:TileControl>
+
+ <!--<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
+
+ <StackPanel Orientation="Horizontal">
+ <TextBlock Text="Amount:" />
+ <TextBlock Text="{Binding Amount}" />
+ </StackPanel>
+
+ <StackPanel Orientation="Horizontal">
+ <TextBlock Text="Anonymity Score:" />
+ <TextBlock Text="{Binding AnonymitySet}" />
+ </StackPanel>
+ </StackPanel>-->
+ </DataTemplate>
+ </Carousel.DataTemplates>
+ </Carousel>
+ </Panel>
+ </c:ContentArea>
+</UserControl>

RENAME
WalletWasabi.Fluent/Views/Wallets/Home/Tiles/PrivacyRingView.axaml.cs
WalletWasabi.Fluent/Views/Wallets/Home/Tiles/PrivacyRing/PrivacyRingView.axaml.cs
-using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
-namespace WalletWasabi.Fluent.Views.Wallets.Home.Tiles;
+namespace WalletWasabi.Fluent.Views.Wallets.Home.Tiles.PrivacyRing;
+
public partial class PrivacyRingView : UserControl
{
public PrivacyRingView()

DELETE
WalletWasabi.Fluent/Views/Wallets/Home/Tiles/PrivacyRingView.axaml
-<UserControl xmlns="https://github.com/avaloniaui"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:i="using:Avalonia.Xaml.Interactivity"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:c="using:WalletWasabi.Fluent.Controls"
- xmlns:behaviors="using:WalletWasabi.Fluent.Behaviors"
- mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="WalletWasabi.Fluent.Views.Wallets.Home.Tiles.PrivacyRingView">
- <UserControl.Styles>
- <Style Selector="Path:pointerover">
- <Setter Property="StrokeThickness" Value="1" />
- <Setter Property="Stroke" Value="{DynamicResource SystemAccentColor}"/>
- </Style>
-
- <Style Selector="Path.private">
- <Setter Property="Fill" Value="{DynamicResource PrivacyLevelStrongBrush}"/>
- </Style>
-
- <Style Selector="Path.semiPrivate">
- <Setter Property="Fill" Value="{DynamicResource PrivacyLevelMediumBrush}"/>
- </Style>
-
- <Style Selector="Path.nonPrivate">
- <Setter Property="Fill" Value="{DynamicResource PrivacyLevelNoneBrush}"/>
- </Style>
-
- <Style Selector="ListBoxItem">
- <Setter Property="IsSelected" Value="{Binding IsPointerOver, Mode=TwoWay}"/>
- <Setter Property="ClipToBounds" Value="False"/>
- <Setter Property="Margin" Value="0"/>
- <Setter Property="Template">
- <ControlTemplate TargetType="ListBoxItem">
- <ContentPresenter Content="{TemplateBinding Content}"
- ContentTemplate="{TemplateBinding ContentTemplate}"/>
- </ControlTemplate>
- </Setter>
-
- <Setter Property="(i:Interaction.Behaviors)">
- <i:BehaviorCollectionTemplate>
- <i:BehaviorCollection>
- <behaviors:ListBoxItemPointerOverSelectedBehavior/>
- </i:BehaviorCollection>
- </i:BehaviorCollectionTemplate>
- </Setter>
- </Style>
-
- <Style Selector="ListBoxItem:selected">
- <Setter Property="Background" Value="Red"/>
- </Style>
- </UserControl.Styles>
- <c:ContentArea Title="{Binding Title}"
- EnableNext="True" NextContent="Done"
- EnableCancel="False"
- EnableBack="True">
- <ListBox Items="{Binding Items}" ClipToBounds="False" Margin="250 250 0 0" HorizontalAlignment="Center">
- <ListBox.Template>
- <ControlTemplate TargetType="ListBox">
- <ItemsPresenter Name="PART_ItemsPresenter"
- Items="{TemplateBinding Items}"
- ItemsPanel="{TemplateBinding ItemsPanel}"
- ItemTemplate="{TemplateBinding ItemTemplate}"/>
- </ControlTemplate>
- </ListBox.Template>
- <ListBox.ItemsPanel>
- <ItemsPanelTemplate>
- <Panel />
- </ItemsPanelTemplate>
- </ListBox.ItemsPanel>
- <ListBox.ItemTemplate>
- <DataTemplate>
- <Path Stretch="None" Height="{Binding OuterRadius}" Width="{Binding OuterRadius}"
- Classes.private="{Binding IsPrivate}"
- Classes.semiPrivate="{Binding IsSemiPrivate}"
- Classes.nonPrivate="{Binding IsNonPrivate}">
- <Path.Data>
- <PathGeometry>
- <PathFigure StartPoint="{Binding Origin1}" IsClosed="True">
- <ArcSegment Size="{Binding Arc1Size}" Point="{Binding Arc1}" />
- <LineSegment Point="{Binding Origin2}" />
- <ArcSegment Size="{Binding Arc2Size}" Point="{Binding Arc2}" SweepDirection="CounterClockwise" />
- </PathFigure>
- </PathGeometry>
- </Path.Data>
- </Path>
- </DataTemplate>
- </ListBox.ItemTemplate>
- </ListBox>
- </c:ContentArea>
-</UserControl>

PrivacyRing Preview