use a behavior to manage focus on recovery word confirmation screen.
MODIFY
WalletWasabi.Fluent/AddWallet/CreateWallet/ConfirmRecoveryWordsView.axaml
WalletWasabi.Fluent/AddWallet/CreateWallet/ConfirmRecoveryWordsView.axaml
<StackPanel Margin="0 0 0 20">
<TextBlock Text="{Binding Index, StringFormat=Recovery word {0}}" Margin="0 0 0 8" />
<StackPanel Orientation="Horizontal">
- <TextBox Watermark="Type the word here..." Text="{Binding Input}" IsEnabled="{Binding !IsConfirmed}" Width="280" FontSize="12" />
+ <TextBox Watermark="Type the word here..." Text="{Binding Input}" IsEnabled="{Binding !IsConfirmed}" Width="280" FontSize="12">
+ <i:Interaction.Behaviors>
+ <behaviors:FocusNextItemBehavior IsFocused="{Binding !IsConfirmed}" />
+ </i:Interaction.Behaviors>
+ </TextBox>
<PathIcon Foreground="{StaticResource SystemAccentColor}" IsVisible="{Binding IsConfirmed}" Data="{StaticResource checkmark_filled}" VerticalAlignment="Center" Width="12" Height="12" Margin="15 0 0 0" />
</StackPanel>
</StackPanel>

MODIFY
WalletWasabi.Fluent/AddWallet/CreateWallet/ConfirmRecoveryWordsViewModel.cs
WalletWasabi.Fluent/AddWallet/CreateWallet/ConfirmRecoveryWordsViewModel.cs
@@ -16,23 +16,22 @@ using WalletWasabi.Gui.ViewModels;
namespace WalletWasabi.Fluent.AddWallet.CreateWallet
{
- public class ConfirmRecoveryWordsViewModel : ViewModelBase, IDisposable, IRoutableViewModel
+ public class ConfirmRecoveryWordsViewModel : ViewModelBase, IRoutableViewModel
{
private bool _isConfirmationFinished;
- private bool _disposedValue;
public ConfirmRecoveryWordsViewModel(IScreen screen, List<RecoveryWord> mnemonicWords, KeyManager keyManager, Global global)
{
HostScreen = screen;
ConfirmationWords = new ObservableCollection<RecoveryWord>();
- ConfirmationWords.CollectionChanged += OnConfirmationWordCollectionChanged;
FinishCommand = ReactiveCommand.Create(() =>
{
global.WalletManager.AddWallet(keyManager);
screen.Router.NavigationStack.Clear();
});
+
CancelCommand = ReactiveCommand.Create(() => HostScreen.Router.NavigateAndReset.Execute(new SettingsPageViewModel(screen)));
SetConfirmationWords(mnemonicWords);
@@ -49,50 +48,16 @@ namespace WalletWasabi.Fluent.AddWallet.CreateWallet
}
public string UrlPathSegment { get; } = null!;
+
public IScreen HostScreen { get; }
- public ObservableCollection<RecoveryWord> ConfirmationWords { get; }
- public ICommand FinishCommand { get; }
- public ICommand CancelCommand { get; }
- public ICommand GoBackCommand => HostScreen.Router.NavigateBack;
- private void OnConfirmationWordCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
- {
- if (e.Action == NotifyCollectionChangedAction.Add)
- {
- foreach (var item in e.NewItems)
- {
- if (item is RecoveryWord recoveryWord)
- {
- recoveryWord.PropertyChanged += RecoveryWordOnPropertyChanged;
- }
- }
- }
- }
+ public ObservableCollection<RecoveryWord> ConfirmationWords { get; }
- private void RecoveryWordOnPropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- if (e.PropertyName == nameof(RecoveryWord.IsConfirmed) && FocusManager.Instance.Current is { } focusManager && sender is RecoveryWord recoveryWord)
- {
- var nextToFocus = KeyboardNavigationHandler.GetNext(focusManager, NavigationDirection.Next);
- var prevToFocus = KeyboardNavigationHandler.GetNext(focusManager, NavigationDirection.Previous);
+ public ICommand FinishCommand { get; }
- var currentWordIndex = recoveryWord.Index;
+ public ICommand CancelCommand { get; }
- if (ConfirmationWords.Where(x => !x.IsConfirmed).Any(x => x.Index < currentWordIndex))
- {
- prevToFocus?.Focus();
- }
- else if (ConfirmationWords.Where(x => !x.IsConfirmed).Any(x => x.Index > currentWordIndex))
- {
- nextToFocus?.Focus();
- }
- else
- {
- IsConfirmationFinished = true;
- nextToFocus?.Focus();
- }
- }
- }
+ public ICommand GoBackCommand => HostScreen.Router.NavigateBack;
private void SetConfirmationWords(List<RecoveryWord> mnemonicWords)
{
@@ -121,30 +86,5 @@ namespace WalletWasabi.Fluent.AddWallet.CreateWallet
ConfirmationWords.Add(item);
}
}
-
- protected virtual void Dispose(bool disposing)
- {
- if (!_disposedValue)
- {
- if (disposing)
- {
- foreach (RecoveryWord item in ConfirmationWords)
- {
- item.PropertyChanged -= RecoveryWordOnPropertyChanged;
- }
-
- ConfirmationWords.CollectionChanged -= OnConfirmationWordCollectionChanged;
- }
-
- _disposedValue = true;
- }
- }
-
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- Dispose(disposing: true);
- GC.SuppressFinalize(this);
- }
}
}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/Behaviors/FocusFirstTextBoxInItemsControlBehavior.cs
WalletWasabi.Fluent/Behaviors/FocusFirstTextBoxInItemsControlBehavior.cs
@@ -23,6 +23,7 @@ namespace WalletWasabi.Fluent.Behaviors
private void OnLayoutUpdated(object? sender, EventArgs e)
{
+ AssociatedObject!.LayoutUpdated -= OnLayoutUpdated;
AssociatedObject.FindLogicalDescendantOfType<TextBox>().Focus();
}
}

ADD
WalletWasabi.Fluent/Behaviors/FocusNextItemBehavior.cs
+using System;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Xaml.Interactivity;
+using ReactiveUI;
+
+namespace WalletWasabi.Fluent.Behaviors
+{
+ internal class FocusNextItemBehavior : Behavior<Control>
+ {
+ private IDisposable _disposable;
+
+ public static readonly StyledProperty<bool> IsFocusedProperty =
+ AvaloniaProperty.Register<FocusNextItemBehavior, bool>(nameof(IsFocused), true);
+
+ public bool IsFocused
+ {
+ get => GetValue(IsFocusedProperty);
+ set => SetValue(IsFocusedProperty, value);
+ }
+
+ protected override void OnAttached()
+ {
+ base.OnAttached();
+
+ _disposable?.Dispose();
+
+ _disposable = this.WhenAnyValue(x => x.IsFocused)
+ .Subscribe(x =>
+ {
+ if (!x)
+ {
+ var nextToFocus = KeyboardNavigationHandler.GetNext(AssociatedObject, NavigationDirection.Next);
+ nextToFocus.Focus();
+ }
+ });
+ }
+
+ protected override void OnDetaching()
+ {
+ base.OnDetaching();
+
+ _disposable?.Dispose();
+ }
+ }
+}
\ No newline at end of file

use a behavior to manage focus on recovery word confirmation screen.