tidy up existing utility icons inside a pointer over adorner.
MODIFY
WalletWasabi.Fluent/App.axaml
WalletWasabi.Fluent/App.axaml
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/TileButton.axaml" />
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/QuestionControl.axaml" />
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/PreviewItem.axaml" />
+ <StyleInclude Source="avares://WalletWasabi.Fluent/Controls/AdorningContentControl.axaml" />
</Application.Styles>
<NativeMenu.Menu>
<NativeMenu>

ADD
WalletWasabi.Fluent/Controls/AdorningContentControl.axaml
+<Styles xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:controls="using:WalletWasabi.Fluent.Controls">
+ <Design.PreviewWith>
+ <controls:AdorningContentControl />
+ </Design.PreviewWith>
+
+ <Style Selector="controls|AdorningContentControl">
+ <!-- Set Defaults -->
+ <Setter Property="Background" Value="Transparent" />
+ <Setter Property="Template">
+ <ControlTemplate>
+ <Border Background="{TemplateBinding Background}"
+ BorderBrush="{TemplateBinding BorderBrush}"
+ BorderThickness="{TemplateBinding BorderThickness}"
+ Padding="{TemplateBinding Padding}">
+ <ContentPresenter Name="PART_ContentPresenter"
+ ContentTemplate="{TemplateBinding ContentTemplate}"
+ Content="{TemplateBinding Content}"
+ VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
+ HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
+ </Border>
+ </ControlTemplate>
+ </Setter>
+ </Style>
+</Styles>
\ No newline at end of file

ADD
WalletWasabi.Fluent/Controls/AdorningContentControl.axaml.cs
+using Avalonia;
+using System;
+using Avalonia.Controls;
+using Avalonia.Threading;
+using WalletWasabi.Fluent.Helpers;
+
+namespace WalletWasabi.Fluent.Controls
+{
+ public class AdorningContentControl : ContentControl
+ {
+ private IDisposable? _subscription;
+
+ public static readonly StyledProperty<Control> AdornmentProperty =
+ AvaloniaProperty.Register<ContentArea, Control>(nameof(Adornment));
+
+ public Control Adornment
+ {
+ get => GetValue(AdornmentProperty);
+ set => SetValue(AdornmentProperty, value);
+ }
+
+ protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
+ {
+ base.OnPropertyChanged(change);
+
+ if (change.Property == IsPointerOverProperty)
+ {
+ Dispatcher.UIThread.Post(ShowHideRevealContent);
+ }
+ else if (change.Property == AdornmentProperty)
+ {
+ RevealContentChanged(change.OldValue.GetValueOrDefault<Control?>(), change.NewValue.GetValueOrDefault<Control?>());
+ }
+ }
+
+ protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnDetachedFromVisualTree(e);
+
+ _subscription?.Dispose();
+
+ RevealContentChanged(null, null);
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ RevealContentChanged(null, Adornment);
+ }
+
+ private void RevealContentChanged(Control? oldValue, Control? newValue)
+ {
+ _subscription?.Dispose();
+
+ if (oldValue is { })
+ {
+ AdornerHelper.RemoveAdorner(this, oldValue);
+ }
+
+ if (newValue is { })
+ {
+ _subscription = newValue.GetObservable(IsPointerOverProperty)
+ .Subscribe(_ => Dispatcher.UIThread.Post(ShowHideRevealContent));
+ }
+ }
+
+ private void ShowHideRevealContent()
+ {
+ if (IsPointerOver)
+ {
+ AdornerHelper.AddAdorner(this, Adornment);
+ }
+ else if(!Adornment.IsPointerOver)
+ {
+ AdornerHelper.RemoveAdorner(this, Adornment);
+ }
+ }
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/Controls/AnimatedButton.axaml.cs
WalletWasabi.Fluent/Controls/AnimatedButton.axaml.cs
@@ -31,8 +31,6 @@ namespace WalletWasabi.Fluent.Controls
public static readonly StyledProperty<bool> AnimateIconProperty =
AvaloniaProperty.Register<AnimatedButton, bool>(nameof(AnimateIcon));
- private CompositeDisposable? _compositeDisposable;
-
public ICommand Command
{
get => GetValue(CommandProperty);
@@ -74,5 +72,12 @@ namespace WalletWasabi.Fluent.Controls
get => GetValue(AnimateIconProperty);
set => SetValue(AnimateIconProperty, value);
}
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ AnimateIcon = false;
+ }
}
}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Helpers/AdornerHelper.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+
+namespace WalletWasabi.Fluent.Helpers
+{
+ public class AdornerHelper
+ {
+ public static void AddAdorner(Visual visual, Control adorner)
+ {
+ var layer = AdornerLayer.GetAdornerLayer(visual);
+
+ if (layer is { } && !layer.Children.Contains(adorner))
+ {
+ AdornerLayer.SetAdornedElement(adorner, visual);
+
+ ((ISetLogicalParent) adorner).SetParent(visual);
+
+ layer.Children.Add(adorner);
+ }
+ }
+
+ public static void RemoveAdorner(Visual visual, Control adorner)
+ {
+ var layer = AdornerLayer.GetAdornerLayer(visual);
+
+ if (layer is { } && layer.Children.Contains(adorner))
+ {
+ layer.Children.Remove(adorner);
+ ((ISetLogicalParent) adorner).SetParent(null);
+ }
+ }
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/Views/Wallets/Receive/ReceiveAddressView.axaml
WalletWasabi.Fluent/Views/Wallets/Receive/ReceiveAddressView.axaml
<StackPanel DockPanel.Dock="Bottom" HorizontalAlignment="Stretch" Spacing="30">
<DockPanel HorizontalAlignment="Center">
<DockPanel>
+ <c:AdorningContentControl>
+ <TextBlock Text="{Binding Address}" FontWeight="Normal" FontSize="{StaticResource FontSizeH5}" />
+ <c:AdorningContentControl.Adornment>
+ <StackPanel Orientation="Horizontal" Background="{DynamicResource RegionColor}" HorizontalAlignment="Right" Margin="0 0 -40 0">
<c:AnimatedButton Margin="5 0 0 0" ToolTip.Tip="Show on the hardware wallet" DockPanel.Dock="Right"
IsVisible="{Binding IsHardwareWallet}"
Command="{Binding ShowOnHwWalletCommand}"
NormalIcon="{StaticResource share_regular}"
ClickIcon="{StaticResource copy_confirmed}"/>
- <c:AnimatedButton Margin="10 0 0 0" ToolTip.Tip="Copy" DockPanel.Dock="Right"
+
+ <c:AnimatedButton Margin="10 0 0 0" ToolTip.Tip="Copy"
Command="{Binding CopyAddressCommand}"
NormalIcon="{StaticResource copy_regular}"
ClickIcon="{StaticResource copy_confirmed}"/>
- <TextBlock Text="{Binding Address}" FontWeight="Normal" FontSize="{StaticResource FontSizeH5}" />
+ </StackPanel>
+ </c:AdorningContentControl.Adornment>
+ </c:AdorningContentControl>
</DockPanel>
</DockPanel>
<c:TagsBox Padding="0" IsReadOnly="True" Items="{Binding Labels}" HorizontalAlignment="Center" />
</StackPanel>
- <DockPanel MaxHeight="250" HorizontalAlignment="Center" Margin="0 0 -30 10">
- <Button Command="{Binding SaveQrCodeCommand}" ToolTip.Tip="Save" Classes="plain icon" DockPanel.Dock="Right" VerticalAlignment="Bottom" Margin="10 0 0 0">
+ <c:AdorningContentControl MaxHeight="250" HorizontalAlignment="Center" Margin="0 0 0 10">
+ <c:AdorningContentControl.Adornment>
+ <Button Command="{Binding SaveQrCodeCommand}" ToolTip.Tip="Save" Classes="plain icon"
+ HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0 0 -40 0">
<PathIcon Data="{StaticResource save_regular}"/>
</Button>
+ </c:AdorningContentControl.Adornment>
<Viewbox HorizontalAlignment="Center" VerticalAlignment="Bottom" DockPanel.Dock="Left">
<c:QrCode Height="250" Matrix="{Binding QrCode}" SaveCommand="{Binding QrCodeCommand, Mode=OneWayToSource}" Margin="0 0 0 -18"/>
</Viewbox>
- </DockPanel>
-
+ </c:AdorningContentControl>
</DockPanel>
</c:ContentArea>
</UserControl>

tidy up existing utility icons inside a pointer over adorner.