fix(launcher): close LA4 review findings

This commit is contained in:
Erik 2026-08-14 19:02:20 +02:00
parent d0a9c65d85
commit 10a712d66b
19 changed files with 1631 additions and 134 deletions

View file

@ -1,5 +1,7 @@
using System.ComponentModel;
using AcDream.Launcher.ViewModels;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
@ -8,6 +10,9 @@ namespace AcDream.Launcher;
public sealed partial class MainWindow : Window
{
private readonly DispatcherTimer _statusTimer;
private LauncherWindowViewModel? _observedViewModel;
private Control? _focusBeforeModal;
private bool _wasModalOpen;
public MainWindow()
{
@ -17,6 +22,7 @@ public sealed partial class MainWindow : Window
Interval = TimeSpan.FromMilliseconds(250),
};
_statusTimer.Tick += OnStatusTimerTick;
DataContextChanged += OnDataContextChanged;
Opened += OnOpened;
Closed += OnClosed;
}
@ -27,6 +33,8 @@ public sealed partial class MainWindow : Window
{
_statusTimer.Stop();
_statusTimer.Tick -= OnStatusTimerTick;
DataContextChanged -= OnDataContextChanged;
ObserveViewModel(null);
Opened -= OnOpened;
Closed -= OnClosed;
}
@ -38,4 +46,92 @@ public sealed partial class MainWindow : Window
viewModel.PollStatus();
}
}
private void OnDataContextChanged(object? sender, EventArgs e) =>
ObserveViewModel(DataContext as LauncherWindowViewModel);
private void ObserveViewModel(LauncherWindowViewModel? viewModel)
{
if (ReferenceEquals(_observedViewModel, viewModel))
{
return;
}
if (_observedViewModel is not null)
{
_observedViewModel.PropertyChanged -= OnViewModelPropertyChanged;
}
_observedViewModel = viewModel;
if (_observedViewModel is not null)
{
_observedViewModel.PropertyChanged += OnViewModelPropertyChanged;
}
_wasModalOpen = viewModel?.IsModalOpen == true;
}
private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(LauncherWindowViewModel.IsModalOpen)
|| sender is not LauncherWindowViewModel viewModel)
{
return;
}
bool isModalOpen = viewModel.IsModalOpen;
if (isModalOpen && !_wasModalOpen)
{
_focusBeforeModal = TopLevel.GetTopLevel(this)?.FocusManager?.GetFocusedElement() as Control;
Dispatcher.UIThread.Post(() => FocusActiveModal(viewModel));
}
else if (!isModalOpen && _wasModalOpen)
{
Control? focusToRestore = _focusBeforeModal;
_focusBeforeModal = null;
Dispatcher.UIThread.Post(() =>
{
if (focusToRestore?.Focus() != true)
{
ProfilesTree.Focus();
}
});
}
_wasModalOpen = isModalOpen;
}
private void FocusActiveModal(LauncherWindowViewModel viewModel)
{
if (viewModel.EditorDialog.IsOpen)
{
Control target = viewModel.EditorDialog.Kind switch
{
ProfileEditorKind.AddServer or ProfileEditorKind.EditServer => ServerNameTextBox,
ProfileEditorKind.AddAccount or ProfileEditorKind.EditAccount => AccountNameTextBox,
ProfileEditorKind.AddCharacter or ProfileEditorKind.EditCharacter => CharacterNameTextBox,
_ => EditorSubmitButton,
};
target.Focus();
}
else if (viewModel.FirstRunWizardShell.IsOpen)
{
FirstRunCloseButton.Focus();
}
else if (viewModel.UpdatePromptShell.IsOpen)
{
UpdateCloseButton.Focus();
}
}
private void OnModalKeyDown(object? sender, KeyEventArgs e)
{
if (e.Key != Key.Escape || DataContext is not LauncherWindowViewModel viewModel)
{
return;
}
viewModel.CloseActiveModal();
e.Handled = true;
}
}