Add Slider of CustonFee logic
ADD
WalletWasabi.Gui/Behaviors/CommandOnKeyDownBehavior.cs
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using System.Reactive.Disposables;
+
+namespace WalletWasabi.Gui.Behaviors
+{
+ public class CommandOnKeyDownBehavior : CommandBasedBehavior<TextBox>
+ {
+ private CompositeDisposable Disposables { get; set; }
+
+ protected override void OnAttached()
+ {
+ Disposables = new CompositeDisposable();
+
+ base.OnAttached();
+
+ Disposables.Add(AssociatedObject.AddHandler(InputElement.KeyDownEvent, (sender, e) =>
+ {
+ CommandParameter = e;
+ e.Handled = ExecuteCommand();
+ }, RoutingStrategies.Tunnel));
+ }
+
+ protected override void OnDetaching()
+ {
+ base.OnDetaching();
+
+ Disposables?.Dispose();
+ }
+ }
+}

ADD
WalletWasabi.Gui/Behaviors/CommandOnKeyUpBehavior.cs
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using System.Reactive.Disposables;
+
+namespace WalletWasabi.Gui.Behaviors
+{
+ public class CommandOnKeyUpBehavior : CommandBasedBehavior<TextBox>
+ {
+ private CompositeDisposable Disposables { get; set; }
+
+ protected override void OnAttached()
+ {
+ Disposables = new CompositeDisposable();
+
+ base.OnAttached();
+
+ Disposables.Add(AssociatedObject.AddHandler(InputElement.KeyUpEvent, (sender, e) =>
+ {
+ CommandParameter = e;
+ e.Handled = ExecuteCommand();
+ }, RoutingStrategies.Tunnel));
+ }
+
+ protected override void OnDetaching()
+ {
+ base.OnDetaching();
+
+ Disposables?.Dispose();
+ }
+ }
+}

ADD
WalletWasabi.Gui/Behaviors/GreyOutOnDisabledBehavior.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Xaml.Interactivity;
+using System;
+using System.Reactive.Disposables;
+
+namespace WalletWasabi.Gui.Behaviors
+{
+ public class GreyOutOnDisabledBehavior : Behavior<Control>
+ {
+ private CompositeDisposable Disposables { get; set; }
+
+ private double? OriginalOpacity { get; set; }
+
+ protected override void OnAttached()
+ {
+ Disposables?.Dispose();
+
+ Disposables = new CompositeDisposable
+ {
+ AssociatedObject.GetObservable(InputElement.IsEnabledProperty).Subscribe(enabled =>
+ {
+ if (enabled)
+ {
+ AssociatedObject.Opacity = OriginalOpacity.HasValue ? OriginalOpacity.Value : 1;
+ }
+ else
+ {
+ OriginalOpacity = AssociatedObject.Opacity;
+ AssociatedObject.Opacity = 0.5;
+ }
+ })
+ };
+ }
+
+ protected override void OnDetaching()
+ {
+ base.OnDetaching();
+
+ Disposables?.Dispose();
+ }
+ }
+}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabView.xaml
WalletWasabi.Gui/Controls/WalletExplorer/SendTabView.xaml
UseFloatingWatermark="True"
MinWidth="124"
MinHeight="46" />
- <StackPanel>
+ <StackPanel Opacity="{Binding FeeControlOpacity}">
<StackPanel IsVisible="{Binding !MinMaxFeeTargetsEqual}" Orientation="Horizontal" Spacing="10">
<TextBlock Text="Fee:" />
<Grid Height="10" Width="10">
<DrawingPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Drawing="{DynamicResource SendTabView_Lightning}" />
</Grid>
- <Slider Minimum="{Binding MinimumFeeTarget}" Maximum="{Binding MaximumFeeTarget}" Value="{Binding FeeTarget}" Width="270" />
+ <Slider Minimum="{Binding MinimumFeeTarget}" Maximum="{Binding MaximumFeeTarget}" Value="{Binding FeeTarget}" Width="270">
+ </Slider>
<Grid Height="10" Width="10">
<DrawingPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Drawing="{DynamicResource SendTabView_Clock}" />
</Grid>
<DrawingPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Drawing="{DynamicResource SendTabView_Smiley}" />
</Grid>
</StackPanel>
- <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Spacing="4">
+ <StackPanel IsVisible="{Binding IsSliderFeeUsed}" Orientation="Horizontal" VerticalAlignment="Center" Spacing="4">
<TextBlock Text="Confirmation Expected In:" VerticalAlignment="Center" />
<TextBlock Foreground="YellowGreen" Text="{Binding ConfirmationExpectedText}" Width="80" VerticalAlignment="Center" />
<Button Content="{Binding FeeText}" ToolTip.Tip="{Binding FeeToolTip}" Command="{Binding FeeRateCommand}" Padding="0" Background="{DynamicResource EditorBackgroundBrush}" BorderBrush="{DynamicResource EditorBackgroundBrush}" VerticalAlignment="Top" />
</StackPanel>
<StackPanel IsVisible="{Binding IsCustomFee}" VerticalAlignment="Center" Orientation="Horizontal" Spacing="10">
<TextBlock Text="or set Fee:" />
- <controls:ExtendedTextBox Text="{Binding UserFeeText, Mode=TwoWay}" Watermark="Fee (sat/byte)" UseFloatingWatermark="True" MinWidth="124" MaxWidth="124" MinHeight="46" />
+ <controls:ExtendedTextBox Text="{Binding UserFeeText, Mode=TwoWay}" Watermark="Fee (sat/byte)" UseFloatingWatermark="True" MinWidth="124" MaxWidth="124" MinHeight="46">
+ <i:Interaction.Behaviors>
+ <behaviors:CommandOnKeyUpBehavior Command="{Binding UserFeeTextKeyUpCommand}" />
+ </i:Interaction.Behaviors>
+ </controls:ExtendedTextBox>
</StackPanel>
</StackPanel>
<StackPanel Spacing="10" Orientation="Horizontal">

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
-using Avalonia.Threading;
+using Avalonia.Input;
using AvalonStudio.Extensibility;
using AvalonStudio.Shell;
using NBitcoin;
@@ -69,6 +69,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private ObservableCollection<SuggestionViewModel> _suggestions;
private FeeDisplayFormat _feeDisplayFormat;
+ private bool _isSliderFeeUsed = true;
+ private double _feeControlOpacity;
private FeeDisplayFormat FeeDisplayFormat
{
@@ -205,7 +207,20 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
this.WhenAnyValue(x => x.FeeTarget)
.ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(_ => SetFeesAndTexts());
+ .Subscribe(_ =>
+ {
+ IsSliderFeeUsed = true;
+ SetFeesAndTexts();
+ }
+ );
+
+ this.WhenAnyValue(x => x.IsSliderFeeUsed)
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(enabled =>
+ {
+ FeeControlOpacity = enabled ? 1 : 0.5; // Give the control the disabled feeling. Real Disable it not a solution as we have to detect if the slider is moved.
+ }
+ );
MaxCommand = ReactiveCommand.Create(() => { IsMax = !IsMax; }, outputScheduler: RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.IsMax)
@@ -293,7 +308,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
}
- bool useCustomFee = IsCustomFee && !string.IsNullOrEmpty(UserFeeText);
+ bool useCustomFee = !IsSliderFeeUsed;
FeeRate feeRate = null;
if (useCustomFee)
{
@@ -438,6 +453,25 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
this.WhenAny(x => x.IsMax, x => x.AmountText, x => x.Address, x => x.IsBusy,
(isMax, amount, address, busy) => (isMax.Value || !string.IsNullOrWhiteSpace(amount.Value)) && !string.IsNullOrWhiteSpace(Address) && !IsBusy)
.ObserveOn(RxApp.MainThreadScheduler));
+
+ UserFeeTextKeyUpCommand = ReactiveCommand.Create((KeyEventArgs key) =>
+ {
+ IsSliderFeeUsed = string.IsNullOrEmpty(UserFeeText);
+ });
+
+ FeeSliderClickedCommand = ReactiveCommand.Create((PointerPressedEventArgs mouse) =>
+ {
+ IsSliderFeeUsed = true;
+ });
+
+ Observable
+ .Merge(MaxCommand.ThrownExceptions)
+ .Merge(FeeRateCommand.ThrownExceptions)
+ .Merge(OnAddressPasteCommand.ThrownExceptions)
+ .Merge(BuildTransactionCommand.ThrownExceptions)
+ .Merge(UserFeeTextKeyUpCommand.ThrownExceptions)
+ .Merge(FeeSliderClickedCommand.ThrownExceptions)
+ .Subscribe(ex => SetWarningMessage(ex.ToTypeMessageString()));
}
private async Task<(bool success, string error)> TryRefreshHardwareWalletInfoAsync(KeyManager keyManager)
@@ -1035,6 +1069,18 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
set => this.RaiseAndSetIfChanged(ref _amountWaterMarkText, value);
}
+ public bool IsSliderFeeUsed
+ {
+ get => _isSliderFeeUsed;
+ set => this.RaiseAndSetIfChanged(ref _isSliderFeeUsed, value);
+ }
+
+ public double FeeControlOpacity
+ {
+ get => _feeControlOpacity;
+ set => this.RaiseAndSetIfChanged(ref _feeControlOpacity, value);
+ }
+
public bool IsCustomFee => _isCustomFee?.Value ?? false;
public ReactiveCommand<Unit, Unit> BuildTransactionCommand { get; }
@@ -1045,6 +1091,10 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public ReactiveCommand<BitcoinUrlBuilder, Unit> OnAddressPasteCommand { get; }
+ public ReactiveCommand<KeyEventArgs, Unit> UserFeeTextKeyUpCommand { get; }
+
+ public ReactiveCommand<PointerPressedEventArgs, Unit> FeeSliderClickedCommand { get; }
+
public bool IsTransactionBuilder { get; }
public override void OnOpen()
@@ -1080,6 +1130,16 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
.ToProperty(this, x => x.IsCustomFee, scheduler: RxApp.MainThreadScheduler)
.DisposeWith(Disposables);
+ this.WhenAnyValue(x => x.IsCustomFee)
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(x =>
+ {
+ if (!IsCustomFee)
+ {
+ IsSliderFeeUsed = true;
+ }
+ });
+
base.OnOpen();
}

Add Slider of CustonFee logic