Adapt HistoryTable to new behavior
MODIFY
WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml.cs
WalletWasabi.Fluent/Controls/PrivacyContentControl.axaml.cs
@@ -15,8 +15,6 @@ public enum ReplacementMode
public class PrivacyContentControl : ContentControl
{
- private static readonly TimeSpan RevealDelay = TimeSpan.FromSeconds(0.75);
-
public static readonly StyledProperty<uint> NumberOfPrivacyCharsProperty =
AvaloniaProperty.Register<PrivacyContentControl, uint>(nameof(NumberOfPrivacyChars), 5);
@@ -33,21 +31,10 @@ public class PrivacyContentControl : ContentControl
return;
}
- var isPrivacyModeEnabled = Services.UiConfig.WhenAnyValue(x => x.PrivacyMode);
- var isForced = this.WhenAnyValue(x => x.ForceShow);
- var isPointerOver = this.WhenAnyValue(x => x.IsPointerOver)
- .Select(
- isTrue => isTrue
- ? Observable.Return(true).Delay(RevealDelay)
- .Concat(Observable.Return(false).Delay(TimeSpan.FromSeconds(5)))
- : Observable.Return(false)
- )
- .Switch();
-
- var displayContent = isPrivacyModeEnabled.CombineLatest(
- isPointerOver,
- isForced,
- (privacyModeEnabled, pointerOver, forced) => !privacyModeEnabled || pointerOver || forced);
+ var displayContent = ObservableMixin.DelayedRevealAndHide(
+ this.WhenAnyValue(x => x.IsPointerOver),
+ Services.UiConfig.WhenAnyValue(x => x.PrivacyMode),
+ this.WhenAnyValue(x => x.ForceShow));
IsContentRevealed = displayContent
.ObserveOn(RxApp.MainThreadScheduler)

MODIFY
WalletWasabi.Fluent/Extensions/ObservableMixin.cs
WalletWasabi.Fluent/Extensions/ObservableMixin.cs
@@ -4,10 +4,48 @@ namespace WalletWasabi.Fluent.Extensions;
public static class ObservableMixin
{
- public static IObservable<bool> DelayTrue(this IObservable<bool> obs, TimeSpan ts)
+ private static readonly TimeSpan RevealDelay = TimeSpan.FromSeconds(0.75);
+ private static readonly TimeSpan HideDelay = TimeSpan.FromSeconds(10);
+
+ public static IObservable<bool> DelayedRevealAndHide(
+ IObservable<bool> pointerOver,
+ IObservable<bool> isPrivacyModeEnabled,
+ IObservable<bool>? isForced = null)
{
- return obs
- .Select(isTrue => isTrue ? Observable.Return(true).Delay(ts) : Observable.Return(false))
+ isForced ??= Observable.Return(false);
+ var isPointerOver = pointerOver
+ .Select(
+ isTrue => isTrue
+ ? PointerOverObs()
+ : PointerOutObs())
.Switch();
+
+ var displayContent = isPrivacyModeEnabled
+ .CombineLatest(
+ isPointerOver,
+ isForced,
+ (privacyModeEnabled, pointerOver, forced) => !privacyModeEnabled || pointerOver || forced);
+
+ return displayContent;
+ }
+
+ private static IObservable<bool> PointerOutObs()
+ {
+ return Observable.Return(false);
+ }
+
+ private static IObservable<bool> PointerOverObs()
+ {
+ var hideObs =
+ Observable
+ .Return(false)
+ .Delay(HideDelay);
+
+ var showObs =
+ Observable
+ .Return(true)
+ .Delay(RevealDelay);
+
+ return showObs.Concat(hideObs);
}
}

MODIFY
WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPrivacyTextCell.cs
WalletWasabi.Fluent/TreeDataGrid/TreeDataGridPrivacyTextCell.cs
-using System.Collections.Generic;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.Controls;
@@ -6,19 +5,31 @@ using Avalonia.Controls.Models.TreeDataGrid;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using ReactiveUI;
+using WalletWasabi.Fluent.Extensions;
namespace WalletWasabi.Fluent.TreeDataGrid;
internal class TreeDataGridPrivacyTextCell : TreeDataGridCell
{
- private static readonly List<TreeDataGridPrivacyTextCell> Realized = new();
- private static IDisposable? Subscription;
- private static bool IsContentVisible = true;
- private string? _value;
+ private readonly IDisposable _subscription;
private FormattedText? _formattedText;
+ private bool _isContentVisible = true;
private int _numberOfPrivacyChars;
+ private string? _value;
+
+ public TreeDataGridPrivacyTextCell()
+ {
+ var displayContent = ObservableMixin.DelayedRevealAndHide(
+ this.WhenAnyValue(x => x.IsPointerOver),
+ Services.UiConfig.WhenAnyValue(x => x.PrivacyMode));
- public string? Text => IsContentVisible ? _value : new string('#', _value is not null ? _numberOfPrivacyChars : 0);
+ _subscription = displayContent
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Do(SetContentVisible)
+ .Subscribe();
+ }
+
+ private string? Text => _isContentVisible ? _value : new string('#', _value is not null ? _numberOfPrivacyChars : 0);
public override void Realize(IElementFactory factory, ICell model, int columnIndex, int rowIndex)
{
@@ -45,30 +56,9 @@ internal class TreeDataGridPrivacyTextCell : TreeDataGridCell
}
}
- protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
- {
- base.OnAttachedToVisualTree(e);
-
- if (Realized.Count == 0)
- {
- Subscription = Services.UiConfig
- .WhenAnyValue(x => x.PrivacyMode)
- .ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(x => SetContentVisible(!x));
- }
-
- Realized.Add(this);
- }
-
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- Realized.Remove(this);
-
- if (Realized.Count == 0)
- {
- Subscription?.Dispose();
- Subscription = null;
- }
+ _subscription.Dispose();
}
protected override Size MeasureOverride(Size availableSize)
@@ -92,14 +82,10 @@ internal class TreeDataGridPrivacyTextCell : TreeDataGridCell
return _formattedText.Bounds.Size;
}
- private static void SetContentVisible(bool value)
- {
- IsContentVisible = value;
-
- foreach (var c in Realized)
+ private void SetContentVisible(bool value)
{
- c._formattedText = null;
- c.InvalidateMeasure();
- }
+ _isContentVisible = value;
+ _formattedText = null;
+ InvalidateMeasure();
}
}

Adapt HistoryTable to new behavior