Files
OpenNest/OpenNest.Avalonia/ConfirmDialog.cs
T
ajandClaude Opus 5.5 16635b7791 feat(avalonia): save, save as and export nests, and ask to save on close
File > Save (Ctrl+S), Save As and Export .nest, with Save and Save As on
the toolbar. Save writes to the file saved to this session, else asks
for one in the nest's folder; a failed save says why, keeps the nest
unsaved and points to Save As. One save or export runs at a time, a
nest cannot be closed while one is under way, and the nest Auto Nest
is working on is not written, checked again after the file picker.

Closing a nest with unsaved changes asks Save / Don't Save / Cancel, as
the WinForms nest window does; Exit asks about each unsaved nest in
turn, showing it, and reads the list again after each answer, since
Auto Nest can finish while a question is open. Cancel, or a save that
does not happen, keeps the app open; an unsaved nest still being
nested stops Exit with how to stop Auto Nest.

Nest load warnings are shown after opening, one dialog at a time and
scrolling when long; for nests opened from the command line they appear
once the window opens, and --snapshot only notes them in the status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 17:35:22 -05:00

73 lines
2.9 KiB
C#

using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
namespace OpenNest.Avalonia
{
/// <summary>
/// A message with a row of buttons ending in Cancel (OK for a plain message). That last
/// button is the default and Esc, so a stray Enter never discards work, and closing the
/// dialog counts as it.
/// </summary>
internal sealed class ConfirmDialog : Window
{
private ConfirmDialog(string title, string message, string[] choices, string cancelText)
{
Title = title;
Width = 460;
SizeToContent = SizeToContent.Height;
CanResize = false;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
var buttons = new StackPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Right,
Spacing = 8,
};
for (var i = 0; i < choices.Length; i++)
{
var index = i;
var choice = new Button { Content = choices[i] };
choice.Click += (_, _) => Close(index);
buttons.Children.Add(choice);
}
var cancel = new Button { Content = cancelText, IsDefault = true, IsCancel = true };
cancel.Click += (_, _) => Close(null);
buttons.Children.Add(cancel);
var panel = new StackPanel { Margin = new Thickness(18), Spacing = 18 };
// A long message (many load warnings) scrolls rather than pushing the buttons away.
panel.Children.Add(new ScrollViewer
{
MaxHeight = 420,
Content = new TextBlock { Text = message, TextWrapping = TextWrapping.Wrap },
});
panel.Children.Add(buttons);
Content = panel;
Opened += (_, _) => cancel.Focus();
}
/// <summary>True when the user chose <paramref name="confirmText"/>.</summary>
public static async Task<bool> AskAsync(
Window owner, string title, string message, string confirmText) =>
await ChooseAsync(owner, title, message, confirmText) == 0;
/// <summary>
/// The index of the choice the user clicked, or -1 for Cancel (also Esc, Enter and
/// closing the dialog).
/// </summary>
public static async Task<int> ChooseAsync(
Window owner, string title, string message, params string[] choices) =>
await new ConfirmDialog(title, message, choices, "Cancel")
.ShowDialog<int?>(owner) ?? -1;
/// <summary>A message with only an OK button.</summary>
public static Task ShowAsync(Window owner, string title, string message) =>
new ConfirmDialog(title, message, System.Array.Empty<string>(), "OK")
.ShowDialog<int?>(owner);
}
}