diff --git a/Examples/AI/AI.csproj b/Examples/AI/AI.csproj
deleted file mode 100644
index 6361cd36df..0000000000
--- a/Examples/AI/AI.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- Exe
- enable
- latest
-
-
-
-
-
-
-
-
-
diff --git a/Examples/AI/ChatView.cs b/Examples/AI/ChatView.cs
deleted file mode 100644
index d6b30f244b..0000000000
--- a/Examples/AI/ChatView.cs
+++ /dev/null
@@ -1,283 +0,0 @@
-using System.Text;
-using GitHub.Copilot.SDK;
-using Terminal.Gui.App;
-using Terminal.Gui.Drawing;
-using Terminal.Gui.Input;
-using Terminal.Gui.ViewBase;
-using Terminal.Gui.Views;
-
-namespace AI;
-
-///
-/// An inline Window with conversation history, input field, status bar, and slash commands.
-///
-internal sealed class ChatView : Window
-{
- private readonly IApplication _app;
- private readonly CopilotClient _client;
- private string _model;
- private readonly Markdown _conversationView;
- private readonly StringBuilder _conversationText = new ();
- private readonly TextField _inputField;
- private readonly View _inputIndicator;
- private readonly SpinnerView _spinner;
- private readonly StatusBar _statusBar;
- private CopilotSession? _session;
- private bool _isStreaming;
-
- public int ExitCode { get; } = 0;
-
- public ChatView (IApplication app, CopilotClient client, string model)
- {
- _app = app;
- _client = client;
- _model = model;
-
- Title = $"Copilot Chat ({model})";
- Width = Dim.Fill ();
- Height = Dim.Auto ();
- Border.LineStyle = LineStyle.Rounded;
- Border.Thickness = new Thickness (0, 3, 0, 0);
- Border.Settings = BorderSettings.Gradient | BorderSettings.Title;
-
- _spinner = new SpinnerView
- {
- AutoSpin = false,
- Style = new SpinnerStyle.FingerDance (),
- SyncWithTerminal = true,
- Height = 1,
- Width = 5,
- Visible = false
- };
- Shortcut spinnerShortcut = new () { CommandView = _spinner, MouseHighlightStates = MouseState.None, Enabled = false };
- Shortcut quitShortcut = new (Application.GetDefaultKey (Command.Quit), "Quit", RequestStop);
-
- _statusBar = new StatusBar { AlignmentModes = AlignmentModes.IgnoreFirstOrLast, SchemeName = SchemeName, BorderStyle = LineStyle.None };
- _statusBar.Add (spinnerShortcut, quitShortcut);
-
- _conversationView = new Markdown
- {
- Width = Dim.Fill (), Height = Dim.Auto (minimumContentDim: 1, maximumContentDim: Dim.Func (_ => GetMaxConversationHeight ()))
- };
-
- _inputIndicator = new View
- {
- Text = $"{Glyphs.RightArrow}",
- Y = Pos.Bottom (_conversationView),
- Width = 2,
- Height = 3,
- Enabled = false
- };
- _inputIndicator.Border.LineStyle = LineStyle.Dotted;
- _inputIndicator.Border.Thickness = new Thickness (0, 1, 0, 1);
-
- _inputField = new TextField { X = Pos.Right (_inputIndicator), Y = Pos.Top (_inputIndicator), Width = Dim.Fill () };
- _inputField.Border.LineStyle = _inputIndicator.Border.LineStyle;
- _inputField.Border.Thickness = new Thickness (0, 1, 0, 1);
-
- _inputField.Autocomplete?.SuggestionGenerator = new SlashCommandSuggestionGenerator ();
- _inputField.Accepted += OnInputAccepted;
-
- Add (_conversationView, _inputIndicator, _inputField, _statusBar);
- _inputField.SetFocus ();
-
- return;
-
- int GetMaxConversationHeight ()
- {
- int screenHeight = _app.Driver?.Screen.Height ?? 40;
- int windowAdornments = GetAdornmentsThickness ().Vertical;
- int siblingHeight = _inputIndicator!.Frame.Height + _statusBar.Frame.Height;
-
- return Math.Max (1, screenHeight - windowAdornments - siblingHeight);
- }
- }
-
- private async void OnInputAccepted (object? sender, EventArgs e)
- {
- string text = _inputField.Text.Trim ();
-
- if (string.IsNullOrEmpty (text) || _isStreaming)
- {
- return;
- }
-
- _inputField.Text = string.Empty;
-
- if (text.StartsWith ('/'))
- {
- HandleSlashCommand (text);
-
- return;
- }
-
- _isStreaming = true;
- _spinner.AutoSpin = true;
- _spinner.Visible = true;
- _inputField.Enabled = false;
-
- AppendToConversation ($"\n{Glyphs.BlackCircle} You: {text}\n\n{Glyphs.Diamond} Copilot: ");
-
- try
- {
- _session ??= await _client.CreateSessionAsync (new SessionConfig
- {
- Model = _model, Streaming = true, OnPermissionRequest = PermissionHandler.ApproveAll
- });
-
- TaskCompletionSource done = new ();
-
- using IDisposable subscription = _session.On (evt =>
- {
- switch (evt)
- {
- case AssistantMessageDeltaEvent delta:
- _app.Invoke (() => AppendToConversation (delta.Data.DeltaContent));
-
- break;
-
- case SessionIdleEvent:
- _app.Invoke (() => AppendToConversation ("\n"));
- done.TrySetResult ();
-
- break;
-
- case SessionErrorEvent err:
- _app.Invoke (() => AppendToConversation ($"\n[Error: {err.Data.Message}]\n"));
- done.TrySetResult ();
-
- break;
- }
- });
-
- await _session.SendAsync (new MessageOptions { Prompt = text });
- await done.Task;
- }
- catch (Exception ex)
- {
- AppendToConversation ($"\n[Error: {ex.Message}]\n");
- }
- finally
- {
- _isStreaming = false;
- _spinner.Visible = false;
- _spinner.AutoSpin = false;
- _inputField.Enabled = true;
- _inputField.SetFocus ();
- }
- }
-
- private async Task ValidateAndSwitchModel (string newModel)
- {
- _inputField.Enabled = false;
-
- try
- {
- CopilotSession testSession = await _client.CreateSessionAsync (new SessionConfig
- {
- Model = newModel,
- Streaming = true,
- OnPermissionRequest = PermissionHandler.ApproveAll
- });
-
- if (_session is { })
- {
- await _session.DisposeAsync ();
- }
-
- _session = testSession;
- _model = newModel;
-
- _app.Invoke (() =>
- {
- Title = $"Copilot Chat ({newModel})";
- AppendToConversation (" \u2713\n");
- _inputField.Enabled = true;
- _inputField.SetFocus ();
- });
- }
- catch (Exception ex)
- {
- _app.Invoke (() =>
- {
- AppendToConversation ($"\n{Glyphs.Diamond} Failed: {ex.Message}\n Keeping model: {_model}\n");
- _inputField.Enabled = true;
- _inputField.SetFocus ();
- });
- }
- }
-
- private void AppendToConversation (string text)
- {
- _conversationText.Append (text);
- _conversationView.Text = _conversationText.ToString ();
- }
-
- private void HandleSlashCommand (string command)
- {
- string cmd = command.TrimStart ('/').ToLowerInvariant ();
- string [] parts = cmd.Split (' ', 2);
-
- switch (parts [0])
- {
- case "quit" or "exit":
- RequestStop ();
-
- break;
-
- case "clear":
- _conversationText.Clear ();
- _conversationView.Text = string.Empty;
- AppendToConversation ($"{Glyphs.Diamond} Conversation cleared.\n");
-
- break;
-
- case "model":
- if (parts.Length > 1 && !string.IsNullOrWhiteSpace (parts [1]))
- {
- string newModel = parts [1].Trim ();
- AppendToConversation ($"\n{Glyphs.Diamond} Switching to {newModel}...");
- _ = ValidateAndSwitchModel (newModel);
- }
- else
- {
- AppendToConversation ($"\n{Glyphs.Diamond} Current model: {_model}\n Usage: /model \n");
- }
-
- break;
-
- case "help":
- AppendToConversation ($"\n{Glyphs.Diamond} Commands:\n"
- + " /help Show this help\n"
- + " /clear Clear conversation\n"
- + " /model Switch model\n"
- + " /compact Summarize conversation\n"
- + " /quit Exit chat\n");
-
- break;
-
- case "compact":
- AppendToConversation ($"\n{Glyphs.Diamond} Compacting conversation...\n");
-
- // TODO: Send conversation summary request to model
-
- break;
-
- default:
- AppendToConversation ($"\n{Glyphs.Diamond} Unknown command: /{parts [0]}. Type /help for commands.\n");
-
- break;
- }
- }
-
- ///
- protected override void Dispose (bool disposing)
- {
- if (disposing)
- {
- _session?.DisposeAsync ().AsTask ().GetAwaiter ().GetResult ();
- }
-
- base.Dispose (disposing);
- }
-}
\ No newline at end of file
diff --git a/Examples/AI/Program.cs b/Examples/AI/Program.cs
deleted file mode 100644
index 7b20494068..0000000000
--- a/Examples/AI/Program.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-// AI — A Terminal.Gui inline-mode CLI powered by the GitHub Copilot SDK.
-//
-// Requires: GitHub Copilot CLI installed and authenticated (gh extension install github/gh-copilot)
-
-using System.CommandLine;
-using System.CommandLine.Help;
-using AI;
-using GitHub.Copilot.SDK;
-using Terminal.Gui.App;
-
-Option modelOption = new ("--model") { Description = "The Copilot model to use.", DefaultValueFactory = _ => "claude-opus-4.6" };
-modelOption.Aliases.Add ("-m");
-
-Argument promptArgument = new ("prompt") { Description = "Prompt text. If omitted, interactive chat mode starts.", DefaultValueFactory = _ => null };
-promptArgument.Arity = ArgumentArity.ZeroOrOne;
-
-RootCommand rootCommand = new ("Terminal.Gui inline-mode Copilot chat") { modelOption, promptArgument };
-
-// Capture parsed values — SetAction runs synchronously, so we store and act after.
-var parsedModel = "claude-opus-4.6";
-string? parsedPrompt = null;
-
-rootCommand.SetAction (context =>
- {
- parsedModel = context.GetRequiredValue (modelOption);
- parsedPrompt = context.GetValue (promptArgument);
- });
-
-ParseResult parseResult = rootCommand.Parse (args);
-
-if (parseResult.Errors.Count > 0 || parseResult.Action is HelpAction)
-{
- parseResult.Invoke ();
-
- return parseResult.Errors.Count > 0 ? 1 : 0;
-}
-
-parseResult.Invoke ();
-
-// ── Start Copilot SDK and run ────────────────────────────────────────────────
-
-await using CopilotClient client = new ();
-await client.StartAsync ();
-
-Application.AppModel = AppModel.Inline;
-IApplication app = Application.Create ().Init ();
-
-if (parsedPrompt is { })
-{
- return SingleTurnView.Run (app, client, parsedModel, parsedPrompt);
-}
-
-ChatView chatView = new (app, client, parsedModel);
-app.Run (chatView);
-app.Dispose ();
-
-return chatView.ExitCode;
diff --git a/Examples/AI/SingleTurnView.cs b/Examples/AI/SingleTurnView.cs
deleted file mode 100644
index 1fb7970870..0000000000
--- a/Examples/AI/SingleTurnView.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System.Text;
-using GitHub.Copilot.SDK;
-using Terminal.Gui.App;
-using Terminal.Gui.Drawing;
-using Terminal.Gui.ViewBase;
-using Terminal.Gui.Views;
-
-namespace AI;
-
-///
-/// Handles single-turn mode: streams one answer inline, then exits.
-///
-internal sealed class SingleTurnView : Window
-{
- private readonly IApplication _app;
- private readonly CopilotClient _client;
- private readonly string _model;
- private readonly string _prompt;
- private readonly Markdown _responseView;
-
- public SingleTurnView (IApplication app, CopilotClient client, string model, string prompt)
- {
- _app = app;
- _client = client;
- _model = model;
- _prompt = prompt;
-
- Title = $"Copilot ({model})";
- Width = Dim.Fill ();
- Height = Dim.Auto ();
- Border.LineStyle = LineStyle.Rounded;
-
- _responseView = new Markdown { Width = Dim.Fill (), Height = Dim.Auto () };
-
- Add (_responseView);
- }
-
- ///
- protected override void OnIsRunningChanged (bool newIsRunning)
- {
- base.OnIsRunningChanged (newIsRunning);
-
- if (newIsRunning)
- {
- _ = streamResponse ();
- }
- }
-
- private async Task streamResponse ()
- {
- try
- {
- await using CopilotSession session = await _client.CreateSessionAsync (new SessionConfig
- {
- Model = _model,
- Streaming = true,
- OnPermissionRequest = PermissionHandler.ApproveAll
- });
-
- StringBuilder responseText = new ();
- TaskCompletionSource done = new ();
-
- session.On (evt =>
- {
- switch (evt)
- {
- case AssistantMessageDeltaEvent delta:
- responseText.Append (delta.Data.DeltaContent);
-
- _app.Invoke (() => { _responseView.Text = responseText.ToString (); });
-
- break;
-
- case SessionIdleEvent:
- done.TrySetResult ();
-
- break;
-
- case SessionErrorEvent err:
- _app.Invoke (() => { _responseView.Text = $"Error: {err.Data.Message}"; });
- done.TrySetResult ();
-
- break;
- }
- });
-
- await session.SendAsync (new MessageOptions { Prompt = _prompt });
- await done.Task;
- }
- catch (Exception ex)
- {
- _app.Invoke (() => _responseView.Text = $"Error: {ex.Message}");
- }
-
- // Give the UI a moment to render the final update, then exit
- _app.Invoke (RequestStop);
- }
-
- public static int Run (IApplication app, CopilotClient client, string model, string prompt)
- {
- SingleTurnView view = new (app, client, model, prompt);
- app.Run (view);
- app.Dispose ();
-
- return 0;
- }
-}
\ No newline at end of file
diff --git a/Examples/AI/SlashCommandSuggestionGenerator.cs b/Examples/AI/SlashCommandSuggestionGenerator.cs
deleted file mode 100644
index 729c0a9318..0000000000
--- a/Examples/AI/SlashCommandSuggestionGenerator.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.Text;
-using Terminal.Gui.Views;
-
-namespace AI;
-
-///
-/// Suggestion generator that provides slash command completions when the input starts with '/'.
-///
-internal sealed class SlashCommandSuggestionGenerator : ISuggestionGenerator
-{
- private static readonly List _commands = ["/help", "/clear", "/model", "/compact", "/quit", "/exit"];
-
- ///
- public IEnumerable GenerateSuggestions (AutocompleteContext context)
- {
- List line = context.CurrentLine.Select (c => c.Grapheme).ToList ();
- string lineText = string.Join ("", line).TrimEnd ();
-
- if (lineText.Length == 0 || lineText [0] != '/')
- {
- return [];
- }
-
- int typedLength = Math.Min (context.CursorPosition, lineText.Length);
- string typed = lineText [..typedLength];
-
- context.CursorPosition = 0;
-
- return _commands.Where (c => c.StartsWith (typed, StringComparison.OrdinalIgnoreCase) && !c.Equals (typed, StringComparison.OrdinalIgnoreCase))
- .Select (c => new Suggestion (typed.Length, c))
- .ToList ();
- }
-
- ///
- public bool IsWordChar (string text)
- {
- if (string.IsNullOrEmpty (text))
- {
- return false;
- }
-
- Rune r = text.EnumerateRunes ().First ();
-
- return Rune.IsLetterOrDigit (r) || r == new Rune ('/');
- }
-}
\ No newline at end of file
diff --git a/Examples/UICatalog/Scenarios/Editor.cs b/Examples/UICatalog/Scenarios/Editor.cs
index 4408a09725..e9b4a3fd64 100644
--- a/Examples/UICatalog/Scenarios/Editor.cs
+++ b/Examples/UICatalog/Scenarios/Editor.cs
@@ -365,7 +365,7 @@ private bool SaveAs ()
_app?.Run (sd);
bool canceled = sd.Canceled;
string path = sd.Path;
- string fileName = sd.FileName;
+ string fileName = sd.FileName ?? string.Empty;
sd.Dispose ();
if (canceled)
diff --git a/Examples/UICatalog/Scenarios/Notepad.cs b/Examples/UICatalog/Scenarios/Notepad.cs
index b1a579a72a..988e4764d5 100644
--- a/Examples/UICatalog/Scenarios/Notepad.cs
+++ b/Examples/UICatalog/Scenarios/Notepad.cs
@@ -1,4 +1,5 @@
// ReSharper disable AccessToDisposedClosure
+
#nullable enable
namespace UICatalog.Scenarios;
@@ -146,7 +147,7 @@ public bool SaveAs ()
_lastDirectory = Path.GetDirectoryName (Path.GetFullPath (fd.Path));
tab.File = new FileInfo (fd.Path);
- tab.Title = fd.FileName;
+ tab.Title = fd.FileName ?? throw new InvalidOperationException ();
tab.Save ();
fd.Dispose ();
@@ -205,7 +206,20 @@ private void Close (Tabs tabs, OpenedFile tabToClose)
private void Open ()
{
- OpenDialog open = new () { Title = "Open", AllowsMultipleSelection = true };
+ OpenDialog open = new ()
+ {
+ Title = "Open",
+ AllowsMultipleSelection = true,
+ AllowedTypes =
+ [
+ new AllowedType ("Markdown", ".md", ".markdown"),
+ new AllowedType ("Text", ".txt", ".csv", ".tsv"),
+ new AllowedType ("Code", ".c", ".h", ".js", ".cs", ".json", ".yml"),
+ new AllowedTypeAny ()
+ ],
+ MustExist = true,
+ OpenMode = OpenMode.File
+ };
if (_lastDirectory is { })
{
diff --git a/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs b/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs
index 46ce46f338..e6237a1569 100644
--- a/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs
+++ b/Terminal.Gui/FileServices/FileSystemTreeBuilder.cs
@@ -1,5 +1,4 @@
-#nullable disable
-using System.IO.Abstractions;
+using System.IO.Abstractions;
namespace Terminal.Gui.FileServices;
@@ -16,7 +15,7 @@ public class FileSystemTreeBuilder : ITreeBuilder, IComparer Sorter { get; set; }
///
- public int Compare (IFileSystemInfo x, IFileSystemInfo y)
+ public int Compare (IFileSystemInfo? x, IFileSystemInfo? y)
{
if (x is IDirectoryInfo && y is not IDirectoryInfo)
{
@@ -28,12 +27,7 @@ public int Compare (IFileSystemInfo x, IFileSystemInfo y)
return 1;
}
- if (x is { } && y is { })
- {
- return string.Compare (x.Name, y.Name, StringComparison.Ordinal);
- }
-
- return 0;
+ return string.Compare (x?.Name, y?.Name, StringComparison.Ordinal);
}
///
@@ -47,12 +41,7 @@ public bool CanExpand (IFileSystemInfo toExpand)
return false;
}
- if (IsReparsePoint (toExpand))
- {
- return false;
- }
-
- return TryGetChildren (toExpand).Any ();
+ return !IsReparsePoint (toExpand) && TryGetChildren (toExpand).Any ();
}
///
@@ -62,16 +51,16 @@ private IEnumerable TryGetChildren (IFileSystemInfo entry)
{
if (entry is IFileInfo)
{
- return Enumerable.Empty ();
+ return [];
}
// Prevent traversal cycles through symlinks/junctions/mount points.
if (IsReparsePoint (entry))
{
- return Enumerable.Empty ();
+ return [];
}
- var dir = (IDirectoryInfo)entry;
+ IDirectoryInfo dir = (IDirectoryInfo)entry;
try
{
diff --git a/Terminal.Gui/FileServices/IFileOperations.cs b/Terminal.Gui/FileServices/IFileOperations.cs
index 920f3e947f..06fd639fd0 100644
--- a/Terminal.Gui/FileServices/IFileOperations.cs
+++ b/Terminal.Gui/FileServices/IFileOperations.cs
@@ -25,7 +25,7 @@ public interface IFileOperations
///
/// Ensure you use a try/catch block with appropriate error handling (e.g. showing a
///
- IFileSystemInfo New (IApplication? app, IFileSystem fileSystem, IDirectoryInfo inDirectory);
+ IFileSystemInfo? New (IApplication? app, IFileSystem fileSystem, IDirectoryInfo inDirectory);
/// Specifies how to handle file/directory rename attempts in .
///
@@ -35,5 +35,5 @@ public interface IFileOperations
///
/// Ensure you use a try/catch block with appropriate error handling (e.g. showing a
///
- IFileSystemInfo Rename (IApplication? app, IFileSystem fileSystem, IFileSystemInfo toRename);
+ IFileSystemInfo? Rename (IApplication? app, IFileSystem fileSystem, IFileSystemInfo toRename);
}
diff --git a/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs b/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs
index e76788029a..3df4d43d0d 100644
--- a/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs
+++ b/Terminal.Gui/ViewBase/Helpers/StackExtensions.cs
@@ -1,244 +1,160 @@
-#nullable disable
-namespace Terminal.Gui.ViewBase;
+namespace Terminal.Gui.ViewBase;
/// Extension of helper to work with specific
public static class StackExtensions
{
- /// Check if the stack object contains the value to find.
- /// The stack object type.
/// The stack object.
- /// Value to find.
- /// The comparison object.
- /// true If the value was found.false otherwise.
- public static bool Contains (this Stack stack, T valueToFind, IEqualityComparer comparer = null)
- {
- comparer = comparer ?? EqualityComparer.Default;
-
- foreach (T obj in stack)
- {
- if (comparer.Equals (obj, valueToFind))
- {
- return true;
- }
- }
-
- return false;
- }
-
- /// Find all duplicates stack objects values.
/// The stack object type.
- /// The stack object.
- /// The comparison object.
- /// The duplicates stack object.
- public static Stack FindDuplicates (this Stack stack, IEqualityComparer comparer = null)
+ extension (Stack stack)
{
- comparer = comparer ?? EqualityComparer.Default;
-
- Stack dup = new ();
- T [] stackArr = stack.ToArray ();
-
- for (var i = 0; i < stackArr.Length; i++)
+ /// Check if the stack object contains the value to find.
+ /// Value to find.
+ /// The comparison object.
+ /// true If the value was found.false otherwise.
+ public bool Contains (T valueToFind, IEqualityComparer? comparer = null)
{
- T value = stackArr [i];
+ comparer ??= EqualityComparer.Default;
- for (int j = i + 1; j < stackArr.Length; j++)
+ foreach (T obj in stack)
{
- T valueToFind = stackArr [j];
-
- if (comparer.Equals (value, valueToFind) && !Contains (dup, valueToFind))
+ if (comparer.Equals (obj, valueToFind))
{
- dup.Push (value);
+ return true;
}
}
- }
- return dup;
- }
-
- /// Move the first stack object value to the end.
- /// The stack object type.
- /// The stack object.
- public static void MoveNext (this Stack stack)
- {
- Stack temp = new ();
- T last = stack.Pop ();
-
- while (stack.Count > 0)
- {
- T value = stack.Pop ();
- temp.Push (value);
+ return false;
}
- temp.Push (last);
-
- while (temp.Count > 0)
+ /// Move the first stack object value to the end.
+ public void MoveNext ()
{
- stack.Push (temp.Pop ());
- }
- }
+ Stack temp = new ();
+ T last = stack.Pop ();
- /// Move the last stack object value to the top.
- /// The stack object type.
- /// The stack object.
- public static void MovePrevious (this Stack stack)
- {
- Stack temp = new ();
- T first = default;
+ while (stack.Count > 0)
+ {
+ T value = stack.Pop ();
+ temp.Push (value);
+ }
- while (stack.Count > 0)
- {
- T value = stack.Pop ();
- temp.Push (value);
+ temp.Push (last);
- if (stack.Count == 1)
+ while (temp.Count > 0)
{
- first = stack.Pop ();
+ stack.Push (temp.Pop ());
}
}
- while (temp.Count > 0)
- {
- stack.Push (temp.Pop ());
- }
-
- stack.Push (first);
- }
-
- /// Move the stack object value to the index.
- /// The stack object type.
- /// The stack object.
- /// Value to move.
- /// The index where to move.
- /// The comparison object.
- public static void MoveTo (
- this Stack stack,
- T valueToMove,
- int index = 0,
- IEqualityComparer comparer = null
- )
- {
- if (index < 0)
+ /// Move the last stack object value to the top.
+ public void MovePrevious ()
{
- return;
- }
-
- comparer = comparer ?? EqualityComparer.Default;
+ Stack temp = new ();
+ T? first = default;
- Stack temp = new ();
- var toMove = default (T);
- int stackCount = stack.Count;
- var count = 0;
+ while (stack.Count > 0)
+ {
+ T value = stack.Pop ();
+ temp.Push (value);
- while (stack.Count > 0)
- {
- T value = stack.Pop ();
+ if (stack.Count == 1)
+ {
+ first = stack.Pop ();
+ }
+ }
- if (comparer.Equals (value, valueToMove))
+ while (temp.Count > 0)
{
- toMove = value;
-
- break;
+ stack.Push (temp.Pop ());
}
- temp.Push (value);
- count++;
+ if (first is { })
+ {
+ stack.Push (first);
+ }
}
- var idx = 0;
-
- while (stack.Count < stackCount)
+ /// Move the stack object value to the index.
+ /// Value to move.
+ /// The index where to move.
+ /// The comparison object.
+ public void MoveTo (T valueToMove, int index = 0, IEqualityComparer? comparer = null)
{
- if (count - idx == index)
+ if (index < 0)
{
- stack.Push (toMove);
+ return;
}
- else
+
+ comparer ??= EqualityComparer.Default;
+
+ Stack temp = new ();
+ var toMove = default (T);
+ int stackCount = stack.Count;
+ var count = 0;
+
+ while (stack.Count > 0)
{
- stack.Push (temp.Pop ());
- }
+ T value = stack.Pop ();
- idx++;
- }
- }
+ if (comparer.Equals (value, valueToMove))
+ {
+ toMove = value;
- /// Replaces a stack object values that match with the value to replace.
- /// The stack object type.
- /// The stack object.
- /// Value to replace.
- /// Value to replace with to what matches the value to replace.
- /// The comparison object.
- public static void Replace (
- this Stack stack,
- T valueToReplace,
- T valueToReplaceWith,
- IEqualityComparer comparer = null
- )
- {
- comparer = comparer ?? EqualityComparer.Default;
+ break;
+ }
- Stack temp = new ();
+ temp.Push (value);
+ count++;
+ }
- while (stack.Count > 0)
- {
- T value = stack.Pop ();
+ var idx = 0;
- if (comparer.Equals (value, valueToReplace))
+ while (stack.Count < stackCount)
{
- stack.Push (valueToReplaceWith);
+ if (count - idx == index)
+ {
+ if (toMove is { })
+ {
+ stack.Push (toMove);
+ }
+ }
+ else
+ {
+ stack.Push (temp.Pop ());
+ }
- break;
+ idx++;
}
-
- temp.Push (value);
}
- while (temp.Count > 0)
+ /// Replaces a stack object values that match with the value to replace.
+ /// Value to replace.
+ /// Value to replace with to what matches the value to replace.
+ /// The comparison object.
+ public void Replace (T valueToReplace, T valueToReplaceWith, IEqualityComparer? comparer = null)
{
- stack.Push (temp.Pop ());
- }
- }
+ comparer ??= EqualityComparer.Default;
- /// Swap two stack objects values that matches with the both values.
- /// The stack object type.
- /// The stack object.
- /// Value to swap from.
- /// Value to swap to.
- /// The comparison object.
- public static void Swap (
- this Stack stack,
- T valueToSwapFrom,
- T valueToSwapTo,
- IEqualityComparer comparer = null
- )
- {
- comparer = comparer ?? EqualityComparer.Default;
+ Stack temp = new ();
- int index = stack.Count - 1;
- T [] stackArr = new T [stack.Count];
+ while (stack.Count > 0)
+ {
+ T value = stack.Pop ();
- while (stack.Count > 0)
- {
- T value = stack.Pop ();
+ if (comparer.Equals (value, valueToReplace))
+ {
+ stack.Push (valueToReplaceWith);
- if (comparer.Equals (value, valueToSwapFrom))
- {
- stackArr [index] = valueToSwapTo;
- }
- else if (comparer.Equals (value, valueToSwapTo))
- {
- stackArr [index] = valueToSwapFrom;
+ break;
+ }
+
+ temp.Push (value);
}
- else
+
+ while (temp.Count > 0)
{
- stackArr [index] = value;
+ stack.Push (temp.Pop ());
}
-
- index--;
- }
-
- for (var i = 0; i < stackArr.Length; i++)
- {
- stack.Push (stackArr [i]);
}
}
}
diff --git a/Terminal.Gui/ViewBase/Layout/Aligner.cs b/Terminal.Gui/ViewBase/Layout/Aligner.cs
index 637765ba11..082581aba4 100644
--- a/Terminal.Gui/ViewBase/Layout/Aligner.cs
+++ b/Terminal.Gui/ViewBase/Layout/Aligner.cs
@@ -1,4 +1,3 @@
-#nullable disable
using System.ComponentModel;
namespace Terminal.Gui.ViewBase;
@@ -9,8 +8,6 @@ namespace Terminal.Gui.ViewBase;
///
public class Aligner : INotifyPropertyChanged
{
- private Alignment _alignment;
-
///
/// Gets or sets how the aligns items within a container.
///
@@ -21,11 +18,11 @@ public class Aligner : INotifyPropertyChanged
///
public Alignment Alignment
{
- get => _alignment;
+ get;
set
{
- _alignment = value;
- PropertyChanged?.Invoke (this, new (nameof (Alignment)));
+ field = value;
+ PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (nameof (Alignment)));
}
}
@@ -38,7 +35,7 @@ public AlignmentModes AlignmentModes
set
{
field = value;
- PropertyChanged?.Invoke (this, new (nameof (AlignmentModes)));
+ PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (nameof (AlignmentModes)));
}
} = AlignmentModes.StartToEnd;
@@ -51,12 +48,12 @@ public int ContainerSize
set
{
field = value;
- PropertyChanged?.Invoke (this, new (nameof (ContainerSize)));
+ PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (nameof (ContainerSize)));
}
}
///
- public event PropertyChangedEventHandler PropertyChanged;
+ public event PropertyChangedEventHandler? PropertyChanged;
///
/// Takes a list of item sizes and returns a list of the positions of those items when aligned within
diff --git a/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs b/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs
index 12384e45b1..0753889ea5 100644
--- a/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs
+++ b/Terminal.Gui/Views/Autocomplete/AutocompleteFilepathContext.cs
@@ -1,4 +1,3 @@
-#nullable disable
using System.IO.Abstractions;
using System.Runtime.InteropServices;
@@ -12,7 +11,7 @@ internal class AutocompleteFilepathContext (string currentLine, int cursorPositi
internal class FilepathSuggestionGenerator : ISuggestionGenerator
{
- private FileDialogState _state;
+ private FileDialogState? _state;
public IEnumerable GenerateSuggestions (AutocompleteContext context)
{
@@ -21,74 +20,51 @@ public IEnumerable GenerateSuggestions (AutocompleteContext context)
_state = fileState.State;
}
- if (_state is null)
- {
- return Enumerable.Empty ();
- }
-
var path = Cell.ToString (context.CurrentLine);
int last = path.LastIndexOfAny (FileDialog.Separators);
- if (string.IsNullOrWhiteSpace (path) || !Path.IsPathRooted (path))
+ if (string.IsNullOrWhiteSpace (path) || !Path.IsPathRooted (path) || _state is null)
{
- return Enumerable.Empty ();
+ return [];
}
- string term = path.Substring (last + 1);
+ string term = path [(last + 1)..];
// If path is /tmp/ then don't just list everything in it
if (string.IsNullOrWhiteSpace (term))
{
- return Enumerable.Empty ();
+ return [];
}
if (term.Equals (_state?.Directory?.Name))
{
// Clear suggestions
- return Enumerable.Empty ();
+ return [];
}
bool isWindows = RuntimeInformation.IsOSPlatform (OSPlatform.Windows);
- string [] suggestions = _state!.Children.Where (d => !d.IsParent)
- .Select (
- e => e.FileSystemInfo is IDirectoryInfo d
- ? d.Name + Path.DirectorySeparatorChar
- : e.FileSystemInfo.Name
- )
- .ToArray ();
+ string? [] suggestions = _state?.Children.Where (d => !d.IsParent)
+ .Select (e => e.FileSystemInfo is IDirectoryInfo d ? d.Name + Path.DirectorySeparatorChar : e.FileSystemInfo?.Name)
+ .ToArray ()
+ ?? [];
string [] validSuggestions = suggestions
- .Where (
- s => s.StartsWith (
- term,
- isWindows
- ? StringComparison.InvariantCultureIgnoreCase
- : StringComparison.InvariantCulture
- )
- )
- .OrderBy (m => m.Length)
- .ToArray ();
+ .Where (s => s?.StartsWith (term,
+ isWindows ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture)
+ == true)
+ .OfType ()
+ .OrderBy (m => m.Length)
+ .ToArray ();
// nothing to suggest
if (validSuggestions.Length == 0 || validSuggestions [0].Length == term.Length)
{
- return Enumerable.Empty ();
+ return [];
}
- return validSuggestions.Select (
- f => new Suggestion (term.Length, f, f)
- )
- .ToList ();
+ return validSuggestions.Select (f => new Suggestion (term.Length, f, f)).ToList ();
}
- public bool IsWordChar (string text)
- {
- if (text == "\n")
- {
- return false;
- }
-
- return true;
- }
+ public bool IsWordChar (string text) => text != "\n";
}
diff --git a/Terminal.Gui/Views/DialogTResult.cs b/Terminal.Gui/Views/DialogTResult.cs
index 81d4a37fd6..3a0ab97cb2 100644
--- a/Terminal.Gui/Views/DialogTResult.cs
+++ b/Terminal.Gui/Views/DialogTResult.cs
@@ -114,27 +114,11 @@ public Dialog ()
SetStyle ();
}
- private Size _minimumSubViewsSize;
-
///
public override void EndInit ()
{
base.EndInit ();
UpdateSizes ();
-
-#if DIALOG_SCROLLBARS
- // Don't enable scrollbars until after initialized; otherwise they get created before
- // our frame has dimensions.
- ViewportSettings |= ViewportSettingsFlags.HasScrollBars;
-#endif
- }
-
- ///
- protected override void OnSubViewAdded (View view)
- {
- _minimumSubViewsSize = new Size (GetWidthRequiredForSubViews (), GetHeightRequiredForSubViews ());
- UpdateSizes ();
- base.OnSubViewAdded (view);
}
///
@@ -196,13 +180,8 @@ private void UpdateSizes ()
}
// Always floor at Viewport size — the content area should never be smaller
- // than what's visible. For DimAuto dialogs, the Frame may be larger than
- // _minimumSubViewsSize (e.g. due to title width), so the content area
- // should reflect the actual available space.
- int subViewsWidth = Math.Max (_minimumSubViewsSize.Width, Viewport.Width);
- int subViewsHeight = Math.Max (_minimumSubViewsSize.Height, Viewport.Height);
-
- SetContentSize (new Size (Math.Max (_minimumButtonsSize.Width, subViewsWidth), Math.Max (_minimumButtonsSize.Height, subViewsHeight)));
+ // than what's visible.
+ SetContentSize (new Size (Math.Max (_minimumButtonsSize.Width, Viewport.Width), Math.Max (_minimumButtonsSize.Height, Viewport.Height)));
}
///
@@ -212,10 +191,10 @@ private void UpdateSizes ()
///
private int GetMinimumDialogWidth ()
{
- int minSize = Math.Max (Math.Max (_minimumSubViewsSize.Width,
+ int minSize = Math.Max (
- // Ensure space for title + borders
- Title.GetColumns () + 4),
+ // Ensure space for title + borders
+ Title.GetColumns () + 4,
_minimumButtonsSize.Width);
return minSize;
@@ -228,7 +207,7 @@ private int GetMinimumDialogWidth ()
///
private int GetMinimumDialogHeight ()
{
- int minSize = Math.Max (_minimumSubViewsSize.Height, _minimumButtonsSize.Height - Border.Thickness.Vertical - Margin.Thickness.Vertical);
+ int minSize = _minimumButtonsSize.Height - Border.Thickness.Vertical - Margin.Thickness.Vertical;
return minSize;
}
diff --git a/Terminal.Gui/Views/FileDialogs/AllowedType.cs b/Terminal.Gui/Views/FileDialogs/AllowedType.cs
index 2bcdfbe4a0..05116a32d4 100644
--- a/Terminal.Gui/Views/FileDialogs/AllowedType.cs
+++ b/Terminal.Gui/Views/FileDialogs/AllowedType.cs
@@ -1,30 +1,5 @@
-#nullable disable
-
namespace Terminal.Gui.Views;
-/// Interface for restrictions on which file type(s) the user is allowed to select/enter.
-public interface IAllowedType
-{
- ///
- /// Returns true if the file at is compatible with this allow option. Note that the file
- /// may not exist (e.g. in the case of saving).
- ///
- ///
- ///
- bool IsAllowed (string path);
-}
-
-/// that allows selection of any types (*.*).
-public class AllowedTypeAny : IAllowedType
-{
- ///
- public bool IsAllowed (string path) { return true; }
-
- /// Returns a string representation of this .
- ///
- public override string ToString () { return Strings.fdAnyFiles + "(*.*)"; }
-}
-
///
/// Describes a requirement on what can be selected. This can be combined with other
/// in a to for example show only .csv files but let user change to
@@ -34,7 +9,7 @@ public class AllowedType : IAllowedType
{
/// Initializes a new instance of the class.
/// The human-readable text to display.
- /// Extension(s) to match e.g. .csv.
+ /// Extension(s) to match e.g. ".csv".
public AllowedType (string description, params string [] extensions)
{
if (extensions.Length == 0)
@@ -79,13 +54,13 @@ public bool IsAllowed (string path)
/// Returns plus all separated by semicolons.
public override string ToString ()
{
- const int maxLength = 30;
+ const int MAX_LENGTH = 30;
var desc = $"{Description} ({string.Join (";", Extensions.Select (e => '*' + e).ToArray ())})";
- if (desc.Length > maxLength)
+ if (desc.Length > MAX_LENGTH)
{
- return desc.Substring (0, maxLength - 2) + "…";
+ return desc [..(MAX_LENGTH - 2)] + "…";
}
return desc;
diff --git a/Terminal.Gui/Views/FileDialogs/AllowedTypeAny.cs b/Terminal.Gui/Views/FileDialogs/AllowedTypeAny.cs
new file mode 100644
index 0000000000..91ea037771
--- /dev/null
+++ b/Terminal.Gui/Views/FileDialogs/AllowedTypeAny.cs
@@ -0,0 +1,12 @@
+namespace Terminal.Gui.Views;
+
+/// that allows selection of any types (*.*).
+public class AllowedTypeAny : IAllowedType
+{
+ ///
+ public bool IsAllowed (string path) => true;
+
+ /// Returns a string representation of this .
+ ///
+ public override string ToString () => Strings.fdAnyFiles + " (*.*)";
+}
diff --git a/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs b/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs
index 1c74914035..09570a9716 100644
--- a/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs
+++ b/Terminal.Gui/Views/FileDialogs/DefaultFileOperations.cs
@@ -1,4 +1,3 @@
-#nullable disable
using System.IO.Abstractions;
namespace Terminal.Gui.Views;
@@ -7,18 +6,20 @@ namespace Terminal.Gui.Views;
public class DefaultFileOperations : IFileOperations
{
///
- public bool Delete (IApplication app, IEnumerable toDelete)
+ public bool Delete (IApplication? app, IEnumerable toDelete)
{
// Default implementation does not allow deleting multiple files
- if (toDelete.Count () != 1)
+ IEnumerable fileSystemInfos = toDelete as IFileSystemInfo [] ?? toDelete.ToArray ();
+
+ if (fileSystemInfos.Count () != 1)
{
return false;
}
- IFileSystemInfo d = toDelete.Single ();
+ IFileSystemInfo d = fileSystemInfos.Single ();
string adjective = d.Name;
- int? result = MessageBox.Query (app,
+ int? result = MessageBox.Query (app ?? throw new ArgumentNullException (nameof (app)),
string.Format (Strings.fdDeleteTitle, adjective),
string.Format (Strings.fdDeleteBody, adjective),
Strings.btnYes,
@@ -49,52 +50,61 @@ public bool Delete (IApplication app, IEnumerable toDelete)
}
///
- public IFileSystemInfo Rename (IApplication app, IFileSystem fileSystem, IFileSystemInfo toRename)
+ public IFileSystemInfo? Rename (IApplication? app, IFileSystem fileSystem, IFileSystemInfo toRename)
{
// Don't allow renaming C: or D: or / (on linux) etc
- if (toRename is IDirectoryInfo dir && dir.Parent is null)
+ if (toRename is IDirectoryInfo { Parent: null })
+ {
+ return null;
+ }
+
+ if (!Prompt (app ?? throw new ArgumentNullException (nameof (app)), Strings.fdRenameTitle, toRename.Name, out string newName))
+ {
+ return null;
+ }
+
+ if (string.IsNullOrWhiteSpace (newName))
{
return null;
}
- if (Prompt (app, Strings.fdRenameTitle, toRename.Name, out string newName))
+ try
{
- if (!string.IsNullOrWhiteSpace (newName))
+ if (toRename is IFileInfo f)
{
- try
- {
- if (toRename is IFileInfo f)
- {
- IFileInfo newLocation = fileSystem.FileInfo.New (Path.Combine (f.Directory.FullName, newName));
- f.MoveTo (newLocation.FullName);
-
- return newLocation;
- }
- else
- {
- var d = (IDirectoryInfo)toRename;
-
- IDirectoryInfo newLocation = fileSystem.DirectoryInfo.New (Path.Combine (d.Parent.FullName, newName));
- d.MoveTo (newLocation.FullName);
-
- return newLocation;
- }
- }
- catch (Exception ex)
- {
- MessageBox.ErrorQuery (app, Strings.fdRenameFailedTitle, ex.Message, Strings.btnOk);
- }
+ IFileInfo newLocation = fileSystem.FileInfo.New (Path.Combine (f.Directory?.FullName ?? throw new InvalidOperationException (), newName));
+ f.MoveTo (newLocation.FullName);
+
+ return newLocation;
+ }
+ else
+ {
+ var d = (IDirectoryInfo)toRename;
+
+ IDirectoryInfo newLocation =
+ fileSystem.DirectoryInfo.New (Path.Combine (d.Parent?.FullName ?? throw new InvalidOperationException (), newName));
+ d.MoveTo (newLocation.FullName);
+
+ return newLocation;
}
}
+ catch (Exception ex)
+ {
+ MessageBox.ErrorQuery (app, Strings.fdRenameFailedTitle, ex.Message, Strings.btnOk);
+ }
return null;
}
///
- public IFileSystemInfo New (IApplication app, IFileSystem fileSystem, IDirectoryInfo inDirectory)
+ public IFileSystemInfo? New (IApplication? app, IFileSystem fileSystem, IDirectoryInfo inDirectory)
{
+ if (app is null)
+ {
+ ArgumentNullException.ThrowIfNull (app);
+ }
var tv = new TextField { Width = Dim.Fill (0, 50), Height = 1 };
- string result = app?.TopRunnable?.Prompt (tv, beginInitHandler: prompt => { prompt.Title = Strings.fdNewTitle; });
+ string? result = app.TopRunnable?.Prompt (tv, beginInitHandler: prompt => { prompt.Title = Strings.fdNewTitle; });
if (string.IsNullOrWhiteSpace (result))
{
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.Commands.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.Commands.cs
index 7e61853e16..77786871c2 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.Commands.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.Commands.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel;
using System.IO.Abstractions;
namespace Terminal.Gui.Views;
@@ -10,18 +11,10 @@ public partial class FileDialog
///
///
///
- public bool IsCompatibleWithAllowedExtensions (IFileInfo file) => !AllowedTypes.Any () || MatchesAllowedTypes (file);
+ public bool IsCompatibleWithAllowedExtensions (IFileInfo file) => AllowedTypes.Count == 0 || MatchesAllowedTypes (file);
///
- protected override bool OnAccepting (CommandEventArgs args)
- {
- if (Accept (true))
- {
- return base.OnAccepting (args);
- }
-
- return false;
- }
+ protected override bool OnAccepting (CommandEventArgs args) => Accept (true) && base.OnAccepting (args);
private void Accept (IEnumerable toMultiAccept)
{
@@ -154,16 +147,10 @@ private bool FinishAccept ()
.ToArray ();
}
- private bool IsCompatibleWithAllowedExtensions (string path)
- {
- // no restrictions
- if (!AllowedTypes.Any ())
- {
- return true;
- }
+ private bool IsCompatibleWithAllowedExtensions (string path) =>
- return AllowedTypes.Any (t => t.IsAllowed (path));
- }
+ // no restrictions
+ AllowedTypes.Count == 0 || AllowedTypes.Any (t => t.IsAllowed (path));
private bool IsCompatibleWithOpenMode (string s, out string reason)
{
@@ -225,7 +212,7 @@ private bool IsCompatibleWithOpenMode (string s, out string reason)
return false;
- default: throw new ArgumentOutOfRangeException (nameof (OpenMode));
+ default: throw new InvalidEnumArgumentException (nameof (OpenMode), (int)OpenMode, typeof (OpenMode));
}
}
@@ -241,9 +228,9 @@ private bool IsCompatibleWithOpenMode (string s, out string reason)
///
private IEnumerable MultiRowToStats ()
{
- HashSet toReturn = new ();
+ HashSet toReturn = [];
- if (!AllowsMultipleSelection || !_tableView.MultiSelectedRegions.Any ())
+ if (!AllowsMultipleSelection || _tableView.MultiSelectedRegions.Count == 0)
{
return toReturn;
}
@@ -260,7 +247,11 @@ private IEnumerable MultiRowToStats ()
private void New ()
{
- IFileSystemInfo created = FileOperationsHandler.New (App, _fileSystem!, State!.Directory);
+ if (_fileSystem is null)
+ {
+ return;
+ }
+ IFileSystemInfo? created = FileOperationsHandler.New (App, _fileSystem, State!.Directory);
RefreshState ();
RestoreSelection (created);
@@ -275,7 +266,7 @@ private void Rename (IApplication? app)
return;
}
- IFileSystemInfo newNamed = FileOperationsHandler.Rename (app, _fileSystem!, toRename.Single ()!);
+ IFileSystemInfo? newNamed = FileOperationsHandler.Rename (app, _fileSystem!, toRename.Single ()!);
RefreshState ();
RestoreSelection (newNamed);
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.Navigation.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.Navigation.cs
index 80377139c3..1b8e68d773 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.Navigation.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.Navigation.cs
@@ -29,7 +29,7 @@ internal void PushState (IDirectoryInfo d, bool addCurrentStateToHistory, bool s
/// Select in the table view (if present)
///
- internal void RestoreSelection (IFileSystemInfo toRestore)
+ internal void RestoreSelection (IFileSystemInfo? toRestore)
{
int row = State!.Children.IndexOf (r => r.FileSystemInfo == toRestore);
_tableView.SetSelection (0, row >= 0 ? row : 0, false);
@@ -78,7 +78,10 @@ private void PathChanged ()
PushState (dir.Parent, true, false);
}
- _tbPath.Autocomplete?.GenerateSuggestions (new AutocompleteFilepathContext (_tbPath.Text, _tbPath.InsertionPoint, State));
+ if (State is { })
+ {
+ _tbPath.Autocomplete?.GenerateSuggestions (new AutocompleteFilepathContext (_tbPath.Text, _tbPath.InsertionPoint, State));
+ }
}
private void PushState (FileDialogState newState, bool addCurrentStateToHistory, bool setPathText = true, bool clearForward = true, string? pathText = null)
@@ -189,7 +192,7 @@ private IDirectoryInfo StringToDirectoryInfo (string path)
return _fileSystem!.DirectoryInfo.New (path);
}
- private void SuppressIfBadChar (Key k)
+ private static void SuppressIfBadChar (Key k)
{
// don't let user type bad letters
var ch = (char)k;
@@ -211,7 +214,7 @@ private void SetPathToSelectedObject (IFileSystemInfo? selected)
if (selected is IDirectoryInfo && Style.PreserveFilenameOnDirectoryChanges)
{
- if (!string.IsNullOrWhiteSpace (Path) && !_fileSystem!.Directory.Exists (Path))
+ if (!string.IsNullOrWhiteSpace (Path) && _fileSystem is { } && !_fileSystem.Directory.Exists (Path))
{
string currentFile = _fileSystem.Path.GetFileName (Path);
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.TableView.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.TableView.cs
index 63ceee5180..2480e9fb5b 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.TableView.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.TableView.cs
@@ -11,7 +11,7 @@ private void TableViewHandleCommandNotBound (object? sender, CommandEventArgs e)
return;
}
- if (e.Context.Binding is MouseBinding { MouseEvent: { } mouse })
+ if (e.Context.Binding is MouseBinding { MouseEvent: { Flags: MouseFlags.RightButtonClicked } mouse })
{
Point? clickedCell = _tableView.ScreenToCell (mouse.Position!.Value.X, mouse.Position!.Value.Y, out int? clickedCol);
@@ -258,6 +258,7 @@ private void SortColumn (int clickedCol)
private static string StripArrows (string columnName) => columnName.Replace (" (▼)", string.Empty).Replace (" (▲)", string.Empty);
+ // TODO: Port this to use key bindings
private bool TableView_KeyDown (Key keyEvent)
{
switch (keyEvent.KeyCode)
@@ -285,7 +286,7 @@ private bool TableView_KeyDown (Key keyEvent)
}
}
- // private void TableViewOnActivated (object? sender, EventArgs e)
+ // BUGBUG: See https://github.com/gui-cs/Terminal.Gui/issues/5087#issuecomment-4328093883
private void TableViewOnValueChanged (object? sender, ValueChangedEventArgs e)
{
if (!_tableView.HasFocus || _tableView.Value is null || _tableView.Table?.Rows == 0)
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
index aaa0a9a2f7..23eda2db1d 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
@@ -27,13 +27,16 @@ public partial class FileDialog : Dialog, IDesignable
private readonly Button _btnBack;
+ private readonly Button _btnForward;
+ private readonly Button _btnCancel;
+
///
- /// Gets the cancel button for the dialog. This is useful for checking if the user canceled the dialog by comparing
+ /// Gets the index of the cancel button for the dialog. This is useful for checking if the user canceled the dialog by
+ /// comparing
/// the to the index of this button in the array.
///
- public Button CancelButton { get; }
+ public int CancelButtonIndex => Buttons.IndexOf (_btnCancel);
- private readonly Button _btnForward;
private readonly Button _btnOk;
private readonly Button _btnUp;
private readonly FileDialogHistory _history;
@@ -45,7 +48,7 @@ public partial class FileDialog : Dialog, IDesignable
private readonly Button _btnTreeToggle;
private readonly TreeView _treeView;
private Dictionary _treeRoots = new ();
- private DropDownList? _typeFilterDropDown;
+ private readonly DropDownList? _typeFilterDropDown;
private int _currentSortColumn;
private bool _currentSortIsAsc = true;
private bool _disposed;
@@ -71,7 +74,7 @@ internal FileDialog (IFileSystem? fileSystem)
// Ensure we get Accept for any subviews; esp TreeView
CommandsToBubbleUp = [Command.Accept];
- CancelButton = new Button { Text = Strings.btnCancel };
+ _btnCancel = new Button { Text = Strings.btnCancel };
_btnOk = new Button { Text = Style.OkButtonText };
@@ -120,6 +123,14 @@ internal FileDialog (IFileSystem? fileSystem)
_tbPath.Autocomplete = new AppendAutocomplete (_tbPath);
_tbPath.Autocomplete.SuggestionGenerator = new FilepathSuggestionGenerator ();
+ _typeFilterDropDown = new DropDownList
+ {
+ X = Pos.AnchorEnd (),
+ Y = 1,
+ Visible = false
+ };
+ Add (_typeFilterDropDown);
+
// Create table view container (right pane)
_tableViewContainer = new View
{
@@ -201,18 +212,19 @@ internal FileDialog (IFileSystem? fileSystem)
_tableView.KeyBindings.ReplaceCommands (Key.Home, Command.Start);
_tableView.KeyBindings.ReplaceCommands (Key.End, Command.End);
_tableView.KeyBindings.ReplaceCommands (Key.Home.WithShift, Command.StartExtend);
- _tableView.KeyBindings.ReplaceCommands (Key.End.WithShift, Command.EndExtend); _history = new FileDialogHistory (this);
+ _tableView.KeyBindings.ReplaceCommands (Key.End.WithShift, Command.EndExtend);
+ _history = new FileDialogHistory (this);
// Changing the key-bindings of a View is not allowed, however,
// by default, Runnable doesn't bind to Command.Context, so
// we can take advantage of the CommandNotBound event to handle it
_tableView.CommandNotBound += TableViewHandleCommandNotBound;
- _tableView.KeyBindings.Add (Key.Space.WithCtrl, Command.Context);
+ _tableView.KeyBindings.Add (PopoverMenu.DefaultKey, Command.Context);
_tableView.MouseBindings.Add (MouseFlags.RightButtonClicked, Command.Context);
_tbPath.TextChanged += (_, _) => PathChanged ();
- _tbFind = new TextField { X = 1, Width = Dim.Width (_tableView) - 1, Y = Pos.AnchorEnd (), Id = "_tbFind" };
+ _tbFind = new TextField { X = 0, Width = Dim.Width (_tableView) - 1, Y = Pos.AnchorEnd (), Id = "_tbFind" };
_spinnerView = new SpinnerView
{
@@ -247,7 +259,7 @@ internal FileDialog (IFileSystem? fileSystem)
// Add the toggle along with OK/Cancel so they align as a group
AddButton (_btnTreeToggle);
- AddButton (CancelButton);
+ AddButton (_btnCancel);
AddButton (_btnOk);
Add (_tbPath);
@@ -256,12 +268,58 @@ internal FileDialog (IFileSystem? fileSystem)
Add (_btnForward);
Add (_treeView);
- // Default: Tree hidden and splitter hidden
- SetTreeVisible (false);
-
Add (_tableViewContainer);
_tableViewContainer.Add (_tbFind);
_tableViewContainer.Add (_spinnerView);
+
+ // to streamline user experience and allow direct typing of paths
+ // with zero navigation we start with focus in the text box and any
+ // default/current path fully selected and ready to be overwritten
+ _tbPath.SetFocus ();
+
+ SetTreeVisible (false);
+ }
+
+ ///
+ public override void EndInit ()
+ {
+ base.EndInit ();
+
+ // Style may have been updated after instance was constructed
+ _btnOk.Text = Style.OkButtonText;
+ _btnCancel.Text = Style.CancelButtonText;
+ _btnUp.Text = GetUpButtonText ();
+ _btnBack.Text = GetBackButtonText ();
+ _btnForward.Text = GetForwardButtonText ();
+ _tbPath.Title = Style.PathCaption;
+ _tbFind.Title = Style.SearchCaption;
+ _treeRoots = Style.TreeRootGetter ();
+ Style.IconProvider.IsOpenGetter = _treeView.IsExpanded;
+ _treeView.AddObjects (_treeRoots.Keys);
+
+ // if filtering on file type is configured then create the DropDownList and establish
+ // initial filtering by extension(s)
+ if (AllowedTypes.Count > 0)
+ {
+ CurrentFilter = AllowedTypes [0];
+
+ _typeFilterDropDown?.Visible = true;
+ _typeFilterDropDown?.Source = new ListWrapper (new ObservableCollection (AllowedTypes.Select (a => a.ToString ()!).ToList ()));
+ _typeFilterDropDown?.Value = AllowedTypes [0].ToString () ?? string.Empty;
+ }
+
+ // if no path has been provided
+ if (Path.Length <= 0)
+ {
+ Path = _fileSystem!.Directory.GetCurrentDirectory ();
+ }
+
+ _tbPath.SelectAll ();
+
+ if (string.IsNullOrEmpty (Title))
+ {
+ Title = GetDefaultTitle ();
+ }
}
///
@@ -327,17 +385,6 @@ public string Path
{
_tbPath.Text = value;
_tbPath.MoveEnd ();
-
- //IDirectoryInfo dir = StringToDirectoryInfo (value);
-
- //StringComparison comparison = OperatingSystem.IsWindows () ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
-
- //if (_treeView.ExpandParents (dir, (left, right) => string.Equals (left.FullName, right.FullName, comparison), out IFileSystemInfo? matched)
- // && matched is { })
- //{
- // // _treeView.EnsureVisible (matched);
- // // _treeView.SelectedObject = matched;
- //}
}
}
@@ -364,86 +411,6 @@ public string Path
// TODO: Refactor to use CWP
public event EventHandler? FilesSelected;
- ///
- protected override void OnIsRunningChanged (bool newIsRunning)
- {
- base.OnIsRunningChanged (newIsRunning);
-
- if (!newIsRunning)
- {
- return;
- }
-
- Arrangement |= ViewArrangement.Resizable;
-
- // May have been updated after instance was constructed
- _btnOk.Text = Style.OkButtonText;
- CancelButton.Text = Style.CancelButtonText;
- _btnUp.Text = GetUpButtonText ();
- _btnBack.Text = GetBackButtonText ();
- _btnForward.Text = GetForwardButtonText ();
-
- _tbPath.Title = Style.PathCaption;
- _tbFind.Title = Style.SearchCaption;
-
- _tbPath.Autocomplete?.Scheme = new Scheme (_tbPath.GetScheme ())
- {
- Normal = new Attribute (Color.Black, _tbPath.GetAttributeForRole (VisualRole.Normal).Background)
- };
-
- _treeRoots = Style.TreeRootGetter ();
- Style.IconProvider.IsOpenGetter = _treeView.IsExpanded;
- _treeView.AddObjects (_treeRoots.Keys);
-
- // if filtering on file type is configured then create the DropDownList and establish
- // initial filtering by extension(s)
- if (AllowedTypes.Any ())
- {
- CurrentFilter = AllowedTypes [0];
-
- _typeFilterDropDown = new DropDownList
- {
- X = Pos.AnchorEnd (),
- Y = 1,
- Source = new ListWrapper (new ObservableCollection (AllowedTypes.Select (a => a.ToString ()!).ToList ())),
- Value = AllowedTypes [0].ToString () ?? string.Empty
- };
- Add (_typeFilterDropDown);
- }
-
- // if no path has been provided
- if (_tbPath.Text.Length <= 0)
- {
- Path = _fileSystem!.Directory.GetCurrentDirectory ();
- }
-
- // to streamline user experience and allow direct typing of paths
- // with zero navigation we start with focus in the text box and any
- // default/current path fully selected and ready to be overwritten
- _tbPath.SetFocus ();
- _tbPath.SelectAll ();
-
- if (string.IsNullOrEmpty (Title))
- {
- Title = GetDefaultTitle ();
- }
-
- // Ensure toggle button text matches current state after sizing
- SetTreeVisible (false);
-
- SetNeedsDraw ();
- SetNeedsLayout ();
- }
-
- ///
- protected override void Dispose (bool disposing)
- {
- _disposed = true;
- base.Dispose (disposing);
-
- CancelSearch ();
- }
-
///
/// Gets a default dialog title, when is not set or empty, result of the function will be
/// shown.
@@ -474,7 +441,7 @@ protected virtual string GetDefaultTitle ()
}
/// State representing a recursive search from downwards.
- internal class SearchState : FileDialogState
+ internal sealed class SearchState : FileDialogState
{
// TODO: Add thread safe child adding
private readonly List _found = [];
@@ -488,6 +455,7 @@ public SearchState (IDirectoryInfo dir, FileDialog parent, string searchTerms) :
parent.SearchMatcher.Initialize (searchTerms);
Children = [];
BeginSearch ();
+ RefreshChildren ();
}
///
@@ -505,8 +473,6 @@ internal bool Cancel ()
return !alreadyCancelled;
}
- internal override void RefreshChildren () { }
-
private void BeginSearch ()
{
Task.Run (() =>
@@ -615,4 +581,13 @@ bool IDesignable.EnableForDesign ()
return true;
}
+
+ ///
+ protected override void Dispose (bool disposing)
+ {
+ _disposed = true;
+ base.Dispose (disposing);
+
+ CancelSearch ();
+ }
}
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs b/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs
index 535a19777e..7da69c9509 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialogHistory.cs
@@ -1,24 +1,21 @@
-#nullable disable
-using System.IO.Abstractions;
+using System.IO.Abstractions;
namespace Terminal.Gui.Views;
-internal class FileDialogHistory
+internal class FileDialogHistory (FileDialog dlg)
{
- private readonly Stack back = new ();
- private readonly FileDialog dlg;
- private readonly Stack forward = new ();
- public FileDialogHistory (FileDialog dlg) { this.dlg = dlg; }
+ private readonly Stack _back = new ();
+ private readonly Stack _forward = new ();
public bool Back ()
{
- IDirectoryInfo goTo = null;
- FileSystemInfoStats restoreSelection = null;
- string restorePath = null;
+ IDirectoryInfo? goTo = null;
+ FileSystemInfoStats? restoreSelection = null;
+ string? restorePath = null;
if (CanBack ())
{
- FileDialogState backTo = back.Pop ();
+ FileDialogState backTo = _back.Pop ();
goTo = backTo.Directory;
restoreSelection = backTo.Selected;
restorePath = backTo.Path;
@@ -34,7 +31,7 @@ public bool Back ()
return false;
}
- forward.Push (dlg.State);
+ _forward.Push (dlg.State ?? throw new InvalidOperationException ());
dlg.PushState (goTo, false, true, false, restorePath);
if (restoreSelection is { })
@@ -45,24 +42,23 @@ public bool Back ()
return true;
}
- internal bool CanBack () { return back.Count > 0; }
- internal bool CanForward () { return forward.Count > 0; }
- internal bool CanUp () { return dlg.State?.Directory.Parent != null; }
- internal void ClearForward () { forward.Clear (); }
+ internal bool CanBack () => _back.Count > 0;
+ internal bool CanForward () => _forward.Count > 0;
+ internal bool CanUp () => dlg.State?.Directory.Parent != null;
+ internal void ClearForward () => _forward.Clear ();
internal bool Forward ()
{
- if (forward.Count > 0)
+ if (_forward.Count <= 0)
{
- dlg.PushState (forward.Pop ().Directory, true, true, false);
-
- return true;
+ return false;
}
+ dlg.PushState (_forward.Pop ().Directory, true, true, false);
- return false;
+ return true;
}
- internal void Push (FileDialogState state, bool clearForward)
+ internal void Push (FileDialogState? state, bool clearForward)
{
if (state is null)
{
@@ -70,29 +66,29 @@ internal void Push (FileDialogState state, bool clearForward)
}
// if changing to a new directory push onto the Back history
- if (back.Count == 0 || back.Peek ().Directory.FullName != state.Directory.FullName)
+ if (_back.Count != 0 && _back.Peek ().Directory.FullName == state.Directory.FullName)
{
- back.Push (state);
+ return;
+ }
+ _back.Push (state);
- if (clearForward)
- {
- ClearForward ();
- }
+ if (clearForward)
+ {
+ ClearForward ();
}
}
internal bool Up ()
{
- IDirectoryInfo parent = dlg.State?.Directory.Parent;
+ IDirectoryInfo? parent = dlg.State?.Directory.Parent;
- if (parent is { })
+ if (parent is null)
{
- back.Push (new FileDialogState (parent, dlg));
- dlg.PushState (parent, false);
-
- return true;
+ return false;
}
+ _back.Push (new FileDialogState (parent, dlg));
+ dlg.PushState (parent, false);
- return false;
+ return true;
}
}
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogState.cs b/Terminal.Gui/Views/FileDialogs/FileDialogState.cs
index aec6818175..4c267624cc 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialogState.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialogState.cs
@@ -1,30 +1,28 @@
-#nullable disable
-using System.IO.Abstractions;
+using System.IO.Abstractions;
namespace Terminal.Gui.Views;
internal class FileDialogState
{
- protected readonly FileDialog Parent;
-
public FileDialogState (IDirectoryInfo dir, FileDialog parent)
{
- Directory = dir;
Parent = parent;
+ Directory = dir;
+ Children = GetChildren (Directory).ToArray ();
Path = parent.Path;
-
- RefreshChildren ();
}
+ protected FileDialog Parent { get; }
+
public FileSystemInfoStats [] Children { get; internal set; }
public IDirectoryInfo Directory { get; }
/// Gets what was entered in the path text box of the dialog when the state was active.
public string Path { get; }
- public FileSystemInfoStats Selected { get; set; }
+ public FileSystemInfoStats? Selected { get; set; }
- protected virtual IEnumerable GetChildren (IDirectoryInfo dir)
+ protected IEnumerable GetChildren (IDirectoryInfo dir)
{
try
{
@@ -33,26 +31,17 @@ protected virtual IEnumerable GetChildren (IDirectoryInfo d
// if directories only
if (Parent.OpenMode == OpenMode.Directory)
{
- children = dir.GetDirectories ()
- .Select (e => new FileSystemInfoStats (e, Parent.Style.Culture))
- .ToList ();
+ children = dir.GetDirectories ().Select (e => new FileSystemInfoStats (e, Parent.Style.Culture)).ToList ();
}
else
{
- children = dir.GetFileSystemInfos ()
- .Select (e => new FileSystemInfoStats (e, Parent.Style.Culture))
- .ToList ();
+ children = dir.GetFileSystemInfos ().Select (e => new FileSystemInfoStats (e, Parent.Style.Culture)).ToList ();
}
// if only allowing specific file types
- if (Parent.AllowedTypes.Any () && Parent.OpenMode == OpenMode.File)
+ if (Parent.AllowedTypes.Count > 0 && Parent.OpenMode == OpenMode.File)
{
- children = children.Where (
- c => c.IsDir
- || (c.FileSystemInfo is IFileInfo f
- && Parent.IsCompatibleWithAllowedExtensions (f))
- )
- .ToList ();
+ children = children.Where (c => c.IsDir || (c.FileSystemInfo is IFileInfo f && Parent.IsCompatibleWithAllowedExtensions (f))).ToList ();
}
// if there's a UI filter in place too
@@ -72,20 +61,12 @@ protected virtual IEnumerable GetChildren (IDirectoryInfo d
catch (Exception)
{
// Access permissions Exceptions, Dir not exists etc
- return Enumerable.Empty ();
+ return [];
}
}
- protected bool MatchesApiFilter (FileSystemInfoStats arg)
- {
- return arg.IsDir
- || (arg.FileSystemInfo is IFileInfo f
- && Parent.CurrentFilter.IsAllowed (f.FullName));
- }
+ protected bool MatchesApiFilter (FileSystemInfoStats arg) =>
+ Parent.CurrentFilter is { } && (arg.IsDir || (arg.FileSystemInfo is IFileInfo f && Parent.CurrentFilter.IsAllowed (f.FullName)));
- internal virtual void RefreshChildren ()
- {
- IDirectoryInfo dir = Directory;
- Children = GetChildren (dir).ToArray ();
- }
+ internal virtual void RefreshChildren () => Children = GetChildren (Directory).ToArray ();
}
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs b/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs
index bf15d3bac0..d29fd14175 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialogTableSource.cs
@@ -1,26 +1,9 @@
-
namespace Terminal.Gui.Views;
-internal class FileDialogTableSource (
- FileDialog? dlg,
- FileDialogState? state,
- FileDialogStyle? style,
- int currentSortColumn,
- bool currentSortIsAsc)
+internal class FileDialogTableSource (FileDialog? dlg, FileDialogState? state, FileDialogStyle? style, int currentSortColumn, bool currentSortIsAsc)
: ITableSource
{
- public object this [int row, int col]
- {
- get
- {
- if (state is { })
- {
- return GetColumnValue (col, state.Children [row]);
- }
-
- return string.Empty;
- }
- }
+ public object this [int row, int col] => state is { } ? GetColumnValue (col, state.Children [row]) : string.Empty;
public int Rows => state is { } ? state.Children.Count () : 0;
@@ -39,8 +22,11 @@ internal static object GetRawColumnValue (int col, FileSystemInfoStats? stats)
switch (col)
{
case 0: return stats!.FileSystemInfo!.Name;
+
case 1: return stats!.MachineReadableLength;
+
case 2: return stats!.LastWriteTime ?? default (DateTime);
+
case 3: return stats!.Type;
}
@@ -60,9 +46,11 @@ private object GetColumnValue (int col, FileSystemInfoStats? stats)
string icon = dlg!.Style.IconProvider.GetIconWithOptionalSpace (stats!.FileSystemInfo);
- return (icon + (stats?.Name ?? string.Empty)).Trim ();
+ return (icon + stats.Name).Trim ();
+
case 1:
return stats?.HumanReadableLength ?? string.Empty;
+
case 2:
if (stats is null || stats.IsParent || stats.LastWriteTime is null)
{
@@ -70,8 +58,10 @@ private object GetColumnValue (int col, FileSystemInfoStats? stats)
}
return stats.LastWriteTime.Value.ToString (style!.DateFormat);
+
case 3:
return stats?.Type ?? string.Empty;
+
default:
throw new ArgumentOutOfRangeException (nameof (col));
}
diff --git a/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs b/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs
index f8629e31a4..a219c95e24 100644
--- a/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs
+++ b/Terminal.Gui/Views/FileDialogs/FilesSelectedEventArgs.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
namespace Terminal.Gui.Views;
/// Event args for the event
@@ -7,7 +5,7 @@ public class FilesSelectedEventArgs : EventArgs
{
/// Creates a new instance of the
///
- public FilesSelectedEventArgs (FileDialog dialog) { Dialog = dialog; }
+ public FilesSelectedEventArgs (FileDialog dialog) => Dialog = dialog;
///
/// Set to true if you want to prevent the selection going ahead (this will leave the
diff --git a/Terminal.Gui/Views/FileDialogs/IAllowedType.cs b/Terminal.Gui/Views/FileDialogs/IAllowedType.cs
new file mode 100644
index 0000000000..6c51cc2a4a
--- /dev/null
+++ b/Terminal.Gui/Views/FileDialogs/IAllowedType.cs
@@ -0,0 +1,13 @@
+namespace Terminal.Gui.Views;
+
+/// Interface for restrictions on which file type(s) the user is allowed to select/enter.
+public interface IAllowedType
+{
+ ///
+ /// Returns true if the file at is compatible with this allow option. Note that the file
+ /// may not exist (e.g. in the case of saving).
+ ///
+ ///
+ ///
+ bool IsAllowed (string path);
+}
diff --git a/Terminal.Gui/Views/FileDialogs/OpenDialog.cs b/Terminal.Gui/Views/FileDialogs/OpenDialog.cs
index 669635cb44..c6df91ee9b 100644
--- a/Terminal.Gui/Views/FileDialogs/OpenDialog.cs
+++ b/Terminal.Gui/Views/FileDialogs/OpenDialog.cs
@@ -1,15 +1,3 @@
-#nullable disable
-//
-// FileDialog.cs: File system dialogs for open and save
-//
-// TODO:
-// * Add directory selector
-// * Implement subclasses
-// * Figure out why message text does not show
-// * Remove the extra space when message does not show
-// * Use a line separator to show the file listing, so we can use same colors as the rest
-// * DirListView: Add mouse support
-
using System.Collections.ObjectModel;
namespace Terminal.Gui.Views;
@@ -26,7 +14,7 @@ namespace Terminal.Gui.Views;
/// . This will run the dialog modally, and when this returns,
/// the list of files will be available on the property.
///
-/// To select more than one file, users can use the space key, or CTRL-T.
+/// Use `Ctrl-click` or `Space` to select multiple files. `Alt-Click` extends the selection.
///
public class OpenDialog : FileDialog
{
@@ -36,7 +24,7 @@ public OpenDialog () { }
/// Returns the selected files, or an empty list if nothing has been selected
/// The file paths.
public IReadOnlyList FilePaths =>
- ((IRunnable)this).Result is null || Result == Buttons.IndexOf (CancelButton) ? Enumerable.Empty ().ToList ().AsReadOnly () :
+ ((IRunnable)this).Result is null || Result == CancelButtonIndex ? Enumerable.Empty ().ToList ().AsReadOnly () :
AllowsMultipleSelection ? MultiSelected : new ReadOnlyCollection ([Path]);
///
diff --git a/Terminal.Gui/Views/FileDialogs/OpenMode.cs b/Terminal.Gui/Views/FileDialogs/OpenMode.cs
index e3e62d60f4..494d82e553 100644
--- a/Terminal.Gui/Views/FileDialogs/OpenMode.cs
+++ b/Terminal.Gui/Views/FileDialogs/OpenMode.cs
@@ -1,5 +1,4 @@
-#nullable disable
-namespace Terminal.Gui.Views;
+namespace Terminal.Gui.Views;
/// Determine which type to open.
public enum OpenMode
diff --git a/Terminal.Gui/Views/FileDialogs/SaveDialog.cs b/Terminal.Gui/Views/FileDialogs/SaveDialog.cs
index 1940895ed9..b0af109e7c 100644
--- a/Terminal.Gui/Views/FileDialogs/SaveDialog.cs
+++ b/Terminal.Gui/Views/FileDialogs/SaveDialog.cs
@@ -1,13 +1,4 @@
//
-// FileDialog.cs: File system dialogs for open and save
-//
-// TODO:
-// * Add directory selector
-// * Implement subclasses
-// * Figure out why message text does not show
-// * Remove the extra space when message does not show
-// * Use a line separator to show the file listing, so we can use same colors as the rest
-// * DirListView: Add mouse support
using System.IO.Abstractions;
@@ -34,7 +25,7 @@ public class SaveDialog : FileDialog
/// .
///
/// The name of the file.
- public string? FileName => (this as IRunnable).Result is null || Result == Buttons.IndexOf (CancelButton) ? null : Path;
+ public string? FileName => (this as IRunnable).Result is null || Result == CancelButtonIndex ? null : Path;
/// Gets the default title for the .
///
diff --git a/Terminal.sln b/Terminal.sln
index 8196f36ae1..16239ed1db 100644
--- a/Terminal.sln
+++ b/Terminal.sln
@@ -159,8 +159,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InlineSelect", "Examples\In
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui.Analyzers.Internal", "Terminal.Gui.Analyzers.Internal\Terminal.Gui.Analyzers.Internal.csproj", "{927CCC07-F00C-409C-BE42-458EB03DD4E8}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AI", "Examples\AI\AI.csproj", "{1737CFE6-456F-B41B-70D0-2F9EC6BE554F}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -459,19 +457,6 @@ Global
{927CCC07-F00C-409C-BE42-458EB03DD4E8}.Release|x64.Build.0 = Release|Any CPU
{927CCC07-F00C-409C-BE42-458EB03DD4E8}.Release|x86.ActiveCfg = Release|Any CPU
{927CCC07-F00C-409C-BE42-458EB03DD4E8}.Release|x86.Build.0 = Release|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Debug|x64.ActiveCfg = Debug|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Debug|x64.Build.0 = Debug|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Debug|x86.ActiveCfg = Debug|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Debug|x86.Build.0 = Debug|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Release|Any CPU.Build.0 = Release|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Release|x64.ActiveCfg = Release|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Release|x64.Build.0 = Release|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Release|x86.ActiveCfg = Release|Any CPU
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F}.Release|x86.Build.0 = Release|Any CPU
-
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -502,7 +487,6 @@ Global
{90A42AE4-301D-4B05-8892-60BE5209C1B5} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
{70802F77-F259-44C6-9522-46FCE2FD754E} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
{3116547F-A8F2-4189-BC22-0B47C757164C} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
- {1737CFE6-456F-B41B-70D0-2F9EC6BE554F} = {3DD033C0-E023-47BF-A808-9CCE30873C3E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F8F8A4D-7B8D-4C2A-AC5E-CD7117F74C03}