mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-09-13 06:07:30 -04:00
Warn if accounts dialog is dirty when user attempts to close it. This includes editing items from the Marketplaces child-dialog
This commit is contained in:
1 parent
3e7191adc7
commit
129486ce89
2 files changed
+94
-3
No files matched your search
@@ -18,10 +18,16 @@ namespace LibationAvalonia.Dialogs;
|
||||
public partial class AccountsDialog : DialogWindow
|
||||
{
|
||||
public AvaloniaList<AccountDto> Accounts { get; } = new();
|
||||
private bool _isDirty;
|
||||
private bool _closeConfirmed;
|
||||
public class AccountDto : ViewModels.ViewModelBase
|
||||
{
|
||||
public IReadOnlyList<Locale> Locales => AccountsDialog.Locales;
|
||||
public bool LibraryScan { get; set; } = true;
|
||||
public bool LibraryScan
|
||||
{
|
||||
get => field;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
} = true;
|
||||
public string? AccountId
|
||||
{
|
||||
get => field;
|
||||
@@ -43,7 +49,11 @@ public partial class AccountsDialog : DialogWindow
|
||||
}
|
||||
}
|
||||
|
||||
public string? AccountName { get; set; }
|
||||
public string? AccountName
|
||||
{
|
||||
get => field;
|
||||
set => this.RaiseAndSetIfChanged(ref field, value);
|
||||
}
|
||||
public bool IsDefault => string.IsNullOrEmpty(AccountId);
|
||||
|
||||
/// <summary>
|
||||
@@ -153,7 +163,11 @@ public partial class AccountsDialog : DialogWindow
|
||||
else if (e.Action is NotifyCollectionChangedAction.Remove && e.OldItems?.Count > 0)
|
||||
{
|
||||
foreach (var oldItem in e.OldItems.OfType<AccountDto>())
|
||||
{
|
||||
oldItem.PropertyChanged -= AccountDto_PropertyChanged;
|
||||
if (!oldItem.IsDefault)
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +175,13 @@ public partial class AccountsDialog : DialogWindow
|
||||
|
||||
private void AccountDto_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
// only user-editable fields; skip derived props like CanExport / button tooltips
|
||||
if (e.PropertyName is nameof(AccountDto.LibraryScan)
|
||||
or nameof(AccountDto.AccountId)
|
||||
or nameof(AccountDto.SelectedLocale)
|
||||
or nameof(AccountDto.AccountName))
|
||||
_isDirty = true;
|
||||
|
||||
if (!Accounts.Any(a => a.IsDefault))
|
||||
addBlankAccount();
|
||||
}
|
||||
@@ -214,7 +235,10 @@ public partial class AccountsDialog : DialogWindow
|
||||
}
|
||||
|
||||
if (importResult.Account is { } account)
|
||||
{
|
||||
Accounts.Add(new AccountDto(account));
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -252,7 +276,10 @@ public partial class AccountsDialog : DialogWindow
|
||||
var dialog = new MarketplacesDialog(account, persister.AccountsSettings, acc.AdditionalLocaleNames);
|
||||
|
||||
if (await dialog.ShowDialog<DialogResult>(this) == DialogResult.OK)
|
||||
{
|
||||
acc.SetAdditionalLocaleNames(dialog.SelectedAdditionalLocaleNames);
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task SaveAndCloseAsync()
|
||||
@@ -282,6 +309,30 @@ public partial class AccountsDialog : DialogWindow
|
||||
public async void SaveButton_Clicked(object sender, Avalonia.Interactivity.RoutedEventArgs e)
|
||||
=> await SaveAndCloseAsync();
|
||||
|
||||
protected override async void OnClosing(WindowClosingEventArgs e)
|
||||
{
|
||||
if (!_closeConfirmed && _isDirty && DialogResult != DialogResult.OK)
|
||||
{
|
||||
e.Cancel = true;
|
||||
|
||||
var result = await MessageBox.Show(
|
||||
this,
|
||||
"You have unsaved changes. Close without saving?",
|
||||
"Unsaved Changes",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning,
|
||||
MessageBoxDefaultButton.Button2);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
_closeConfirmed = true;
|
||||
Close(DialogResult.Cancel);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnClosing(e);
|
||||
}
|
||||
|
||||
private void persist(AccountsSettings accountsSettings)
|
||||
{
|
||||
var existingAccounts = accountsSettings.Accounts;
|
||||
|
||||
@@ -21,6 +21,8 @@ public partial class AccountsDialog : Form
|
||||
private const string COL_Locale = nameof(Locale);
|
||||
private const string COL_Marketplaces = nameof(Marketplaces);
|
||||
|
||||
private bool _isDirty;
|
||||
|
||||
public AccountsDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -29,6 +31,7 @@ public partial class AccountsDialog : Form
|
||||
|
||||
dataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
|
||||
dataGridView1.CurrentCellDirtyStateChanged += DataGridView1_CurrentCellDirtyStateChanged;
|
||||
dataGridView1.UserAddedRow += DataGridView1_UserAddedRow;
|
||||
|
||||
populateDropDown();
|
||||
|
||||
@@ -36,6 +39,31 @@ public partial class AccountsDialog : Form
|
||||
this.SetLibationIcon();
|
||||
}
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||
{
|
||||
// commit in-progress edits so CellValueChanged can mark dirty before we decide
|
||||
if (dataGridView1.IsCurrentCellInEditMode)
|
||||
dataGridView1.EndEdit();
|
||||
if (dataGridView1.IsCurrentCellDirty)
|
||||
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
|
||||
if (_isDirty && DialogResult != DialogResult.OK)
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
this,
|
||||
"You have unsaved changes. Close without saving?",
|
||||
"Unsaved Changes",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning,
|
||||
MessageBoxDefaultButton.Button2);
|
||||
|
||||
if (result == DialogResult.No)
|
||||
e.Cancel = true;
|
||||
}
|
||||
|
||||
base.OnFormClosing(e);
|
||||
}
|
||||
|
||||
private void populateDropDown()
|
||||
=> (dataGridView1.Columns[COL_Locale] as DataGridViewComboBoxColumn)?.DataSource
|
||||
= Localization.Locales
|
||||
@@ -90,6 +118,7 @@ public partial class AccountsDialog : Form
|
||||
{
|
||||
if (e.RowIndex < 0 || e.ColumnIndex < 0)
|
||||
return;
|
||||
_isDirty = true;
|
||||
var colName = dataGridView1.Columns[e.ColumnIndex].Name;
|
||||
if (colName is COL_AccountId or COL_Locale)
|
||||
UpdateExportCellState(dataGridView1.Rows[e.RowIndex]);
|
||||
@@ -100,10 +129,14 @@ public partial class AccountsDialog : Form
|
||||
if (!dataGridView1.IsCurrentCellDirty || dataGridView1.CurrentCell is null)
|
||||
return;
|
||||
var colName = dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex].Name;
|
||||
if (colName == COL_Locale)
|
||||
// combo and checkbox do not commit until leave-cell unless we force it here
|
||||
if (colName is COL_Locale or COL_LibraryScan)
|
||||
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
}
|
||||
|
||||
private void DataGridView1_UserAddedRow(object? sender, DataGridViewRowEventArgs e)
|
||||
=> _isDirty = true;
|
||||
|
||||
private static bool AccountRowCanExport(string? accountId, string? localeName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(accountId) || string.IsNullOrWhiteSpace(localeName))
|
||||
@@ -150,7 +183,10 @@ public partial class AccountsDialog : Form
|
||||
case COL_Delete:
|
||||
// if final/edit row: do nothing
|
||||
if (e.RowIndex < dgv.RowCount - 1)
|
||||
{
|
||||
dgv.Rows.Remove(row);
|
||||
_isDirty = true;
|
||||
}
|
||||
break;
|
||||
case COL_Export:
|
||||
// if final/edit row: do nothing
|
||||
@@ -222,6 +258,7 @@ public partial class AccountsDialog : Form
|
||||
var selected = dialog.SelectedAdditionalLocaleNames.ToList();
|
||||
row.Tag = selected;
|
||||
row.Cells[COL_Marketplaces].Value = MarketplacesUi.ButtonText(selected.Count + 1);
|
||||
_isDirty = true;
|
||||
}
|
||||
|
||||
private void saveBtn_Click(object sender, EventArgs e)
|
||||
@@ -416,7 +453,10 @@ public partial class AccountsDialog : Form
|
||||
}
|
||||
|
||||
if (importResult.Account is { } account)
|
||||
{
|
||||
AddAccountToGrid(account);
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user