implement re-labeling of rx-addresses.
MODIFY
WalletWasabi.Gui/App.xaml
WalletWasabi.Gui/App.xaml
<StyleInclude Source="resm:WalletWasabi.Gui.Controls.NoparaPasswordBox.xaml?assembly=WalletWasabi.Gui" />
<StyleInclude Source="resm:WalletWasabi.Gui.Controls.MultiTextBox.xaml?assembly=WalletWasabi.Gui" />
<StyleInclude Source="resm:WalletWasabi.Gui.Controls.BusyIndicator.xaml?assembly=WalletWasabi.Gui" />
+ <StyleInclude Source="resm:WalletWasabi.Gui.Controls.EditableTextBlock.xaml?assembly=WalletWasabi.Gui" />
</Application.Styles>
<Application.DataTemplates>
<DataTemplate DataType="Views:MainView">

ADD
WalletWasabi.Gui/Controls/EditableTextBlock.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Metadata;
+using Avalonia.Threading;
+using System;
+
+namespace WalletWasabi.Gui.Controls
+{
+ public class EditableTextBlock : TemplatedControl
+ {
+ private string _text;
+ private string _editText;
+ private TextBox _textBox;
+ private DispatcherTimer _editClickTimer;
+ private IInputRoot _root;
+
+ static EditableTextBlock()
+ {
+ PseudoClass<EditableTextBlock>(InEditModeProperty, ":editing");
+ }
+
+ public EditableTextBlock()
+ {
+ _editClickTimer = new DispatcherTimer
+ {
+ Interval = TimeSpan.FromMilliseconds(500),
+ };
+
+ _editClickTimer.Tick += (sender, e) =>
+ {
+ _editClickTimer.Stop();
+
+ if(IsFocused && !InEditMode)
+ {
+ EnterEditMode();
+ }
+ };
+
+ this.GetObservable(TextProperty).Subscribe(t =>
+ {
+ EditText = t;
+ });
+
+ this.GetObservable(InEditModeProperty).Subscribe(mode =>
+ {
+ if (mode && _textBox != null)
+ {
+ EnterEditMode();
+ }
+ });
+
+ AddHandler(PointerPressedEvent, (sender, e)=>
+ {
+ _editClickTimer.Stop();
+
+ if (!InEditMode)
+ {
+ if (e.ClickCount == 1 && e.InputModifiers == InputModifiers.LeftMouseButton && IsFocused)
+ {
+ _editClickTimer.Start();
+ }
+ }
+ else
+ {
+ var hit = this.InputHitTest(e.GetPosition(this));
+
+ if (hit == null)
+ {
+ ExitEditMode();
+ }
+ }
+ }, RoutingStrategies.Tunnel);
+ }
+
+ public static readonly DirectProperty<EditableTextBlock, string> TextProperty = TextBlock.TextProperty.AddOwner<EditableTextBlock>(
+ o => o.Text,
+ (o, v) => o.Text = v,
+ defaultBindingMode: BindingMode.TwoWay,
+ enableDataValidation: true);
+
+ [Content]
+ public string Text
+ {
+ get { return _text; }
+ set
+ {
+ SetAndRaise(TextProperty, ref _text, value);
+ }
+ }
+
+ public string EditText
+ {
+ get => _editText;
+ set
+ {
+ SetAndRaise(EditTextProperty, ref _editText, value);
+ }
+ }
+
+ public static readonly DirectProperty<EditableTextBlock, string> EditTextProperty =
+ AvaloniaProperty.RegisterDirect<EditableTextBlock, string>(nameof(EditText), o => o.EditText, (o, v) => o.EditText = v);
+
+ public static readonly StyledProperty<bool> InEditModeProperty =
+ AvaloniaProperty.Register<EditableTextBlock, bool>(nameof(InEditMode), defaultBindingMode: BindingMode.TwoWay);
+
+ public bool InEditMode
+ {
+ get { return GetValue(InEditModeProperty); }
+ set { SetValue(InEditModeProperty, value); }
+ }
+
+ protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
+ {
+ base.OnTemplateApplied(e);
+
+ _textBox = e.NameScope.Find<TextBox>("PART_TextBox");
+
+ if (InEditMode)
+ {
+ EnterEditMode();
+ }
+ }
+
+ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
+ {
+ base.OnAttachedToVisualTree(e);
+
+ _root = e.Root as IInputRoot;
+ }
+
+ protected override void OnKeyUp(KeyEventArgs e)
+ {
+ switch (e.Key)
+ {
+ case Key.Enter:
+ ExitEditMode();
+ e.Handled = true;
+ break;
+
+ case Key.Escape:
+ ExitEditMode(true);
+ e.Handled = true;
+ break;
+ }
+
+ base.OnKeyUp(e);
+ }
+
+ private void EnterEditMode()
+ {
+ EditText = Text;
+ InEditMode = true;
+
+ _root.MouseDevice.Capture(_textBox);
+ _textBox.CaretIndex = Text.Length;
+ _textBox.SelectionStart = 0;
+ _textBox.SelectionEnd = Text.Length;
+
+ Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ _textBox.Focus();
+ });
+ }
+
+ private void ExitEditMode(bool restore = false)
+ {
+ if (!restore)
+ {
+ Text = EditText;
+ }
+
+ InEditMode = false;
+ _root.MouseDevice.Capture(null);
+ }
+ }
+}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabView.xaml
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabView.xaml
Height="16" Width="16" Stretch="Fill" />
</MenuItem.Icon>
</MenuItem>
+
+ <MenuItem Header="Re-label" Command="{Binding ReLabelCommand}" />
</ContextMenu>
</controls:ExtendedListBox.ContextMenu>
<controls:ExtendedListBox.ItemTemplate>
</Grid>
</Grid>
- <TextBlock Text="{Binding Label, ConverterParameter=11, Converter={StaticResource LurkingWifeModeStringConverter}, Mode=OneWay}" Grid.Column="1" />
+ <controls:EditableTextBlock Text="{Binding Label, Mode=TwoWay}" InEditMode="{Binding InEditMode}" Grid.Column="1" />
</Grid>
</Grid>
</DataTemplate>

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
@@ -31,6 +31,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public ReactiveCommand<Unit, Unit> CopyAddress { get; }
public ReactiveCommand<Unit, Unit> CopyLabel { get; }
public ReactiveCommand<Unit, Unit> ShowQrCode { get; }
+ public ReactiveCommand<Unit, Unit> ReLabelCommand { get; }
public ReceiveTabViewModel(WalletViewModel walletViewModel)
: base("Receive", walletViewModel)
@@ -135,6 +136,11 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{ }
}, isCoinListItemSelected);
+ ReLabelCommand = ReactiveCommand.Create(() =>
+ {
+ SelectedAddress.InEditMode = true;
+ }, isCoinListItemSelected);
+
_suggestions = new ObservableCollection<SuggestionViewModel>();
}

MODIFY
WalletWasabi.Gui/ViewModels/AddressViewModel.cs
WalletWasabi.Gui/ViewModels/AddressViewModel.cs
@@ -3,6 +3,8 @@ using Avalonia.Threading;
using Gma.QrCodeNet.Encoding;
using ReactiveUI;
using System;
+using System.Linq;
+using System.Reactive;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;
@@ -18,6 +20,8 @@ namespace WalletWasabi.Gui.ViewModels
private bool[,] _qrCode;
private bool _clipboardNotificationVisible;
private double _clipboardNotificationOpacity;
+ private string _label;
+ private bool _inEditMode;
public HdPubKey Model { get; }
public Global Global { get; }
@@ -28,6 +32,7 @@ namespace WalletWasabi.Gui.ViewModels
Model = model;
ClipboardNotificationVisible = false;
ClipboardNotificationOpacity = 0;
+ Label = model.Label;
// TODO fix this performance issue this should only be generated when accessed.
Task.Run(() =>
@@ -46,6 +51,21 @@ namespace WalletWasabi.Gui.ViewModels
this.RaisePropertyChanged(nameof(Address));
this.RaisePropertyChanged(nameof(Label));
}).DisposeWith(Disposables);
+
+ this.WhenAnyValue(x => x.Label)
+ .Subscribe(newLabel =>
+ {
+ if(InEditMode)
+ {
+ KeyManager keyManager = Global.WalletService.KeyManager;
+ HdPubKey hdPubKey = keyManager.GetKeys(x => Model == x).FirstOrDefault();
+
+ if (hdPubKey != default)
+ {
+ hdPubKey.SetLabel(newLabel, kmToFile: keyManager);
+ }
+ }
+ });
}
public bool ClipboardNotificationVisible
@@ -66,7 +86,18 @@ namespace WalletWasabi.Gui.ViewModels
set => this.RaiseAndSetIfChanged(ref _isExpanded, value);
}
- public string Label => Model.Label;
+ public string Label
+ {
+ get { return _label; }
+ set { this.RaiseAndSetIfChanged(ref _label, value); }
+ }
+
+ public bool InEditMode
+ {
+ get { return _inEditMode; }
+ set { this.RaiseAndSetIfChanged(ref _inEditMode, value); }
+ }
+
public string Address => Model.GetP2wpkhAddress(Global.Network).ToString();

implement re-labeling of rx-addresses.