|
|
@@ -1,8 +1,7 @@
|
|
|
+#nullable enable
|
|
|
using System.IO.Abstractions;
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
-#nullable enable
|
|
|
-
|
|
|
namespace Terminal.Gui.Views;
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -10,10 +9,10 @@ namespace Terminal.Gui.Views;
|
|
|
/// </summary>
|
|
|
public class FileDialog : Dialog, IDesignable
|
|
|
{
|
|
|
- private const int alignmentGroupInput = 32;
|
|
|
- private const int alignmentGroupComplete = 55;
|
|
|
+ private const int ALIGNMENT_GROUP_COMPLETE = 55;
|
|
|
|
|
|
/// <summary>Gets the Path separators for the operating system</summary>
|
|
|
+ // ReSharper disable once InconsistentNaming
|
|
|
internal static char [] Separators =
|
|
|
[
|
|
|
System.IO.Path.AltDirectorySeparatorChar,
|
|
|
@@ -34,23 +33,23 @@ public class FileDialog : Dialog, IDesignable
|
|
|
private readonly Button _btnCancel;
|
|
|
private readonly Button _btnForward;
|
|
|
private readonly Button _btnOk;
|
|
|
- private readonly Button _btnToggleSplitterCollapse;
|
|
|
private readonly Button _btnUp;
|
|
|
- private readonly IFileSystem _fileSystem;
|
|
|
+ private readonly Button _btnTreeToggle;
|
|
|
+ private readonly IFileSystem? _fileSystem;
|
|
|
private readonly FileDialogHistory _history;
|
|
|
private readonly SpinnerView _spinnerView;
|
|
|
- private readonly TileView _splitContainer;
|
|
|
+ private readonly View _tableViewContainer;
|
|
|
private readonly TableView _tableView;
|
|
|
private readonly TextField _tbFind;
|
|
|
private readonly TextField _tbPath;
|
|
|
private readonly TreeView<IFileSystemInfo> _treeView;
|
|
|
- private MenuBarItem _allowedTypeMenu;
|
|
|
- private MenuBar _allowedTypeMenuBar;
|
|
|
- private MenuItem [] _allowedTypeMenuItems;
|
|
|
+ private MenuBarItem? _allowedTypeMenu;
|
|
|
+ private MenuBar? _allowedTypeMenuBar;
|
|
|
+ private MenuItem []? _allowedTypeMenuItems;
|
|
|
private int _currentSortColumn;
|
|
|
private bool _currentSortIsAsc = true;
|
|
|
private bool _disposed;
|
|
|
- private string _feedback;
|
|
|
+ private string? _feedback;
|
|
|
private bool _loaded;
|
|
|
|
|
|
private bool _pushingState;
|
|
|
@@ -61,7 +60,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="FileDialog"/> class with a custom <see cref="IFileSystem"/>.</summary>
|
|
|
/// <remarks>This overload is mainly useful for testing.</remarks>
|
|
|
- internal FileDialog (IFileSystem fileSystem)
|
|
|
+ internal FileDialog (IFileSystem? fileSystem)
|
|
|
{
|
|
|
Height = Dim.Percent (80);
|
|
|
Width = Dim.Percent (80);
|
|
|
@@ -74,7 +73,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
_btnOk = new ()
|
|
|
{
|
|
|
- X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, alignmentGroupComplete),
|
|
|
+ X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, ALIGNMENT_GROUP_COMPLETE),
|
|
|
Y = Pos.AnchorEnd (),
|
|
|
IsDefault = true, Text = Style.OkButtonText
|
|
|
};
|
|
|
@@ -87,11 +86,12 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
|
|
|
Accept (true);
|
|
|
+ e.Handled = true;
|
|
|
};
|
|
|
|
|
|
_btnCancel = new ()
|
|
|
{
|
|
|
- X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, alignmentGroupComplete),
|
|
|
+ X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, ALIGNMENT_GROUP_COMPLETE),
|
|
|
Y = Pos.AnchorEnd (),
|
|
|
Text = Strings.btnCancel
|
|
|
};
|
|
|
@@ -111,6 +111,19 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ // Tree toggle button - shares alignment group with OK/Cancel
|
|
|
+ _btnTreeToggle = new ()
|
|
|
+ {
|
|
|
+ X = 0,//Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, ALIGNMENT_GROUP_COMPLETE),
|
|
|
+ Y = Pos.AnchorEnd (),
|
|
|
+ NoPadding = true
|
|
|
+ };
|
|
|
+ _btnTreeToggle.Accepting += (s, e) =>
|
|
|
+ {
|
|
|
+ e.Handled = true;
|
|
|
+ ToggleTreeVisibility ();
|
|
|
+ };
|
|
|
+
|
|
|
_btnUp = new () { X = 0, Y = 1, NoPadding = true };
|
|
|
_btnUp.Text = GetUpButtonText ();
|
|
|
_btnUp.Accepting += (s, e) =>
|
|
|
@@ -135,7 +148,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
e.Handled = true;
|
|
|
};
|
|
|
|
|
|
- _tbPath = new () { Width = Dim.Fill (), CaptionColor = new (Color.Black) };
|
|
|
+ _tbPath = new () { Width = Dim.Fill (),/* CaptionColor = new (Color.Black)*/ };
|
|
|
|
|
|
_tbPath.KeyDown += (s, k) =>
|
|
|
{
|
|
|
@@ -149,32 +162,40 @@ public class FileDialog : Dialog, IDesignable
|
|
|
_tbPath.Autocomplete = new AppendAutocomplete (_tbPath);
|
|
|
_tbPath.Autocomplete.SuggestionGenerator = new FilepathSuggestionGenerator ();
|
|
|
|
|
|
- _splitContainer = new ()
|
|
|
+ // Create tree view container (left pane)
|
|
|
+ _treeView = new ()
|
|
|
{
|
|
|
X = 0,
|
|
|
Y = Pos.Bottom (_btnBack),
|
|
|
- Width = Dim.Fill (),
|
|
|
- Height = Dim.Fill (Dim.Func (_ => IsInitialized ? _btnOk.Frame.Height : 1))
|
|
|
+ Width = Dim.Fill (Dim.Func (_ => IsInitialized ? _tableViewContainer!.Frame.Width - 30 : 30)),
|
|
|
+ Height = Dim.Fill (Dim.Func (_ => IsInitialized ? _btnOk.Frame.Height : 1)),
|
|
|
+ Visible = false
|
|
|
};
|
|
|
|
|
|
- Initialized += (s, e) =>
|
|
|
- {
|
|
|
- _splitContainer.SetSplitterPos (0, 30);
|
|
|
- _splitContainer.Tiles.ElementAt (0).ContentView.Visible = false;
|
|
|
- };
|
|
|
-
|
|
|
- // this.splitContainer.Border.BorderStyle = BorderStyle.None;
|
|
|
+ // Create table view container (right pane)
|
|
|
+ _tableViewContainer = new ()
|
|
|
+ {
|
|
|
+ X = 0,
|
|
|
+ Y = Pos.Bottom (_btnBack),
|
|
|
+ Width = Dim.Fill (),
|
|
|
+ Height = Dim.Fill (Dim.Func (_ => IsInitialized ? _btnOk.Frame.Height : 1)),
|
|
|
+ Arrangement = ViewArrangement.LeftResizable,
|
|
|
+ BorderStyle = LineStyle.Dashed,
|
|
|
+ SuperViewRendersLineCanvas = true,
|
|
|
+ CanFocus = true,
|
|
|
+ Id = "_tableViewContainer"
|
|
|
+ };
|
|
|
|
|
|
_tableView = new ()
|
|
|
{
|
|
|
Width = Dim.Fill (),
|
|
|
- Height = Dim.Fill (),
|
|
|
+ Height = Dim.Fill (1),
|
|
|
FullRowSelect = true,
|
|
|
+ Id = "_tableView"
|
|
|
};
|
|
|
_tableView.CollectionNavigator = new FileDialogCollectionNavigator (this, _tableView);
|
|
|
_tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
|
|
|
_tableView.MouseClick += OnTableViewMouseClick;
|
|
|
- _tableView.Style.InvertSelectedCellFirstCharacter = true;
|
|
|
Style.TableStyle = _tableView.Style;
|
|
|
|
|
|
ColumnStyle nameStyle = Style.TableStyle.GetOrCreateColumnStyle (0);
|
|
|
@@ -193,8 +214,6 @@ public class FileDialog : Dialog, IDesignable
|
|
|
typeStyle.MinWidth = 6;
|
|
|
typeStyle.ColorGetter = ColorGetter;
|
|
|
|
|
|
- _treeView = new () { Width = Dim.Fill (), Height = Dim.Fill () };
|
|
|
-
|
|
|
var fileDialogTreeBuilder = new FileSystemTreeBuilder ();
|
|
|
_treeView.TreeBuilder = fileDialogTreeBuilder;
|
|
|
_treeView.AspectGetter = AspectGetter;
|
|
|
@@ -202,38 +221,44 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
_treeView.SelectionChanged += TreeView_SelectionChanged;
|
|
|
|
|
|
- _splitContainer.Tiles.ElementAt (0).ContentView.Add (_treeView);
|
|
|
- _splitContainer.Tiles.ElementAt (1).ContentView.Add (_tableView);
|
|
|
+ _tableViewContainer.Add (_tableView);
|
|
|
|
|
|
- _btnToggleSplitterCollapse = new ()
|
|
|
- {
|
|
|
- X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
|
|
|
- Y = Pos.AnchorEnd (), Text = GetToggleSplitterText (false)
|
|
|
- };
|
|
|
+ _tableView.Style.ShowHorizontalHeaderOverline = true;
|
|
|
+ _tableView.Style.ShowVerticalCellLines = true;
|
|
|
+ _tableView.Style.ShowVerticalHeaderLines = true;
|
|
|
+ _tableView.Style.AlwaysShowHeaders = true;
|
|
|
+ _tableView.Style.ShowHorizontalHeaderUnderline = true;
|
|
|
+ _tableView.Style.ShowHorizontalScrollIndicators = true;
|
|
|
|
|
|
- _btnToggleSplitterCollapse.Accepting += (s, e) =>
|
|
|
- {
|
|
|
- // Required otherwise the Save button clicks itself
|
|
|
- e.Handled = true;
|
|
|
- Tile tile = _splitContainer.Tiles.ElementAt (0);
|
|
|
+ _history = new (this);
|
|
|
|
|
|
- bool newState = !tile.ContentView.Visible;
|
|
|
- tile.ContentView.Visible = newState;
|
|
|
- _btnToggleSplitterCollapse.Text = GetToggleSplitterText (newState);
|
|
|
- SetNeedsLayout ();
|
|
|
- };
|
|
|
+ _tbPath.TextChanged += (s, e) => PathChanged ();
|
|
|
+
|
|
|
+ _tableView.CellActivated += CellActivate;
|
|
|
+ _tableView.KeyDown += (s, k) => k.Handled = TableView_KeyUp (k);
|
|
|
+ _tableView.SelectedCellChanged += TableView_SelectedCellChanged;
|
|
|
+
|
|
|
+ _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);
|
|
|
|
|
|
_tbFind = new ()
|
|
|
{
|
|
|
- X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
|
|
|
- CaptionColor = new (Color.Black),
|
|
|
- Width = 30,
|
|
|
- Y = Pos.Top (_btnToggleSplitterCollapse),
|
|
|
- HotKey = Key.F.WithAlt
|
|
|
+ X = 0,
|
|
|
+ Width = Dim.Fill (),
|
|
|
+ Y = Pos.AnchorEnd (),
|
|
|
+ HotKey = Key.F.WithAlt,
|
|
|
+ Id = "_tbFind",
|
|
|
};
|
|
|
|
|
|
_spinnerView = new ()
|
|
|
- { X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput), Y = Pos.AnchorEnd (1), Visible = false };
|
|
|
+ {
|
|
|
+ // The spinner view is positioned over the last column of _tbFind
|
|
|
+ X = Pos.Right (_tbFind) - 1,
|
|
|
+ Y = Pos.Top (_tbFind),
|
|
|
+ Visible = false
|
|
|
+ };
|
|
|
|
|
|
_tbFind.TextChanged += (s, o) => RestartSearch ();
|
|
|
|
|
|
@@ -253,42 +278,26 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
- _tableView.Style.ShowHorizontalHeaderOverline = true;
|
|
|
- _tableView.Style.ShowVerticalCellLines = true;
|
|
|
- _tableView.Style.ShowVerticalHeaderLines = true;
|
|
|
- _tableView.Style.AlwaysShowHeaders = true;
|
|
|
- _tableView.Style.ShowHorizontalHeaderUnderline = true;
|
|
|
- _tableView.Style.ShowHorizontalScrollIndicators = true;
|
|
|
-
|
|
|
- _history = new (this);
|
|
|
-
|
|
|
- _tbPath.TextChanged += (s, e) => PathChanged ();
|
|
|
-
|
|
|
- _tableView.CellActivated += CellActivate;
|
|
|
- _tableView.KeyDown += (s, k) => k.Handled = TableView_KeyUp (k);
|
|
|
- _tableView.SelectedCellChanged += TableView_SelectedCellChanged;
|
|
|
-
|
|
|
- _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);
|
|
|
-
|
|
|
AllowsMultipleSelection = false;
|
|
|
|
|
|
UpdateNavigationVisibility ();
|
|
|
|
|
|
- Add (_tbPath);
|
|
|
- Add (_btnUp);
|
|
|
- Add (_btnBack);
|
|
|
- Add (_btnForward);
|
|
|
- Add (_splitContainer);
|
|
|
- Add (_btnToggleSplitterCollapse);
|
|
|
- Add (_tbFind);
|
|
|
- Add (_spinnerView);
|
|
|
-
|
|
|
- Add (_btnOk);
|
|
|
- Add (_btnCancel);
|
|
|
+ base.Add (_tbPath);
|
|
|
+ base.Add (_btnUp);
|
|
|
+ base.Add (_btnBack);
|
|
|
+ base.Add (_btnForward);
|
|
|
+ base.Add (_treeView);
|
|
|
+ base.Add (_tableViewContainer);
|
|
|
+ _tableViewContainer.Add (_tbFind);
|
|
|
+ _tableViewContainer.Add (_spinnerView);
|
|
|
+
|
|
|
+ // Add the toggle along with OK/Cancel so they align as a group
|
|
|
+ base.Add (_btnTreeToggle);
|
|
|
+ base.Add (_btnOk);
|
|
|
+ base.Add (_btnCancel);
|
|
|
+
|
|
|
+ // Default: Tree hidden and splitter hidden
|
|
|
+ SetTreeVisible (false);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -312,7 +321,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
|
|
|
/// <summary>The UI selected <see cref="IAllowedType"/> from combo box. May be null.</summary>
|
|
|
- public IAllowedType CurrentFilter { get; private set; }
|
|
|
+ public IAllowedType? CurrentFilter { get; private set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// Gets or sets behavior of the <see cref="FileDialog"/> when the user attempts to delete a selected file(s). Set
|
|
|
@@ -375,13 +384,13 @@ public class FileDialog : Dialog, IDesignable
|
|
|
public FileDialogStyle Style { get; }
|
|
|
|
|
|
/// <summary>Gets the currently open directory and known children presented in the dialog.</summary>
|
|
|
- internal FileDialogState State { get; private set; }
|
|
|
+ internal FileDialogState? State { get; private set; }
|
|
|
|
|
|
/// <summary>
|
|
|
/// Event fired when user attempts to confirm a selection (or multi selection). Allows you to cancel the selection
|
|
|
/// or undertake alternative behavior e.g. open a dialog "File already exists, Overwrite? yes/no".
|
|
|
/// </summary>
|
|
|
- public event EventHandler<FilesSelectedEventArgs> FilesSelected;
|
|
|
+ public event EventHandler<FilesSelectedEventArgs>? FilesSelected;
|
|
|
|
|
|
/// <summary>
|
|
|
/// Returns true if there are no <see cref="AllowedTypes"/> or one of them agrees that <paramref name="file"/>
|
|
|
@@ -418,7 +427,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
Move (0, Viewport.Height / 2);
|
|
|
|
|
|
SetAttribute (new (Color.Red, GetAttributeForRole (VisualRole.Normal).Background));
|
|
|
- Driver.AddStr (new (' ', feedbackPadLeft));
|
|
|
+ Driver!.AddStr (new (' ', feedbackPadLeft));
|
|
|
Driver.AddStr (_feedback);
|
|
|
Driver.AddStr (new (' ', feedbackPadRight));
|
|
|
}
|
|
|
@@ -436,6 +445,8 @@ public class FileDialog : Dialog, IDesignable
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ Arrangement |= ViewArrangement.Resizable;
|
|
|
+
|
|
|
_loaded = true;
|
|
|
|
|
|
// May have been updated after instance was constructed
|
|
|
@@ -444,7 +455,6 @@ public class FileDialog : Dialog, IDesignable
|
|
|
_btnUp.Text = GetUpButtonText ();
|
|
|
_btnBack.Text = GetBackButtonText ();
|
|
|
_btnForward.Text = GetForwardButtonText ();
|
|
|
- _btnToggleSplitterCollapse.Text = GetToggleSplitterText (false);
|
|
|
|
|
|
_tbPath.Caption = Style.PathCaption;
|
|
|
_tbFind.Caption = Style.SearchCaption;
|
|
|
@@ -466,7 +476,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
CurrentFilter = AllowedTypes [0];
|
|
|
|
|
|
// Fiddle factor
|
|
|
- int width = AllowedTypes.Max (a => a.ToString ().Length) + 6;
|
|
|
+ int width = AllowedTypes.Max (a => a.ToString ()!.Length) + 6;
|
|
|
|
|
|
_allowedTypeMenu = new (
|
|
|
"<placeholder>",
|
|
|
@@ -497,7 +507,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
_allowedTypeMenuBar.DrawingContent += (s, e) =>
|
|
|
{
|
|
|
_allowedTypeMenuBar.Move (e.NewViewport.Width - 1, 0);
|
|
|
- Driver.AddRune (Glyphs.DownArrow);
|
|
|
+ Driver!.AddRune (Glyphs.DownArrow);
|
|
|
};
|
|
|
|
|
|
Add (_allowedTypeMenuBar);
|
|
|
@@ -506,7 +516,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
// if no path has been provided
|
|
|
if (_tbPath.Text.Length <= 0)
|
|
|
{
|
|
|
- Path = _fileSystem.Directory.GetCurrentDirectory ();
|
|
|
+ Path = _fileSystem!.Directory.GetCurrentDirectory ();
|
|
|
}
|
|
|
|
|
|
// to streamline user experience and allow direct typing of paths
|
|
|
@@ -527,6 +537,9 @@ public class FileDialog : Dialog, IDesignable
|
|
|
MoveSubViewTowardsStart (_btnCancel);
|
|
|
}
|
|
|
|
|
|
+ // Ensure toggle button text matches current state after sizing
|
|
|
+ SetTreeVisible (false);
|
|
|
+
|
|
|
SetNeedsDraw ();
|
|
|
SetNeedsLayout ();
|
|
|
}
|
|
|
@@ -570,7 +583,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
internal void ApplySort ()
|
|
|
{
|
|
|
- FileSystemInfoStats [] stats = State?.Children ?? new FileSystemInfoStats [0];
|
|
|
+ FileSystemInfoStats [] stats = State?.Children ?? [];
|
|
|
|
|
|
// This portion is never reordered (always .. at top then folders)
|
|
|
IOrderedEnumerable<FileSystemInfoStats> forcedOrder = stats
|
|
|
@@ -589,7 +602,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
FileDialogTableSource.GetRawColumnValue (_currentSortColumn, f)
|
|
|
);
|
|
|
|
|
|
- State.Children = ordered.ToArray ();
|
|
|
+ State!.Children = ordered.ToArray ();
|
|
|
|
|
|
_tableView.Update ();
|
|
|
}
|
|
|
@@ -632,7 +645,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
/// <param name="toRestore"></param>
|
|
|
internal void RestoreSelection (IFileSystemInfo toRestore)
|
|
|
{
|
|
|
- _tableView.SelectedRow = State.Children.IndexOf (r => r.FileSystemInfo == toRestore);
|
|
|
+ _tableView.SelectedRow = State!.Children.IndexOf (r => r.FileSystemInfo == toRestore);
|
|
|
_tableView.EnsureSelectedCellIsVisible ();
|
|
|
}
|
|
|
|
|
|
@@ -693,11 +706,8 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
if (!IsCompatibleWithOpenMode (_tbPath.Text, out string reason))
|
|
|
{
|
|
|
- if (reason is { })
|
|
|
- {
|
|
|
- _feedback = reason;
|
|
|
- SetNeedsDraw ();
|
|
|
- }
|
|
|
+ _feedback = reason;
|
|
|
+ SetNeedsDraw ();
|
|
|
|
|
|
return;
|
|
|
}
|
|
|
@@ -724,21 +734,18 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
for (var i = 0; i < AllowedTypes.Count; i++)
|
|
|
{
|
|
|
- _allowedTypeMenuItems [i].Checked = i == idx;
|
|
|
+ _allowedTypeMenuItems! [i].Checked = i == idx;
|
|
|
}
|
|
|
|
|
|
- _allowedTypeMenu.Title = allow.ToString ();
|
|
|
+ _allowedTypeMenu!.Title = allow.ToString ()!;
|
|
|
|
|
|
CurrentFilter = allow;
|
|
|
|
|
|
_tbPath.ClearAllSelection ();
|
|
|
_tbPath.Autocomplete.ClearSuggestions ();
|
|
|
|
|
|
- if (State is { })
|
|
|
- {
|
|
|
- State.RefreshChildren ();
|
|
|
- WriteStateToTableView ();
|
|
|
- }
|
|
|
+ State!.RefreshChildren ();
|
|
|
+ WriteStateToTableView ();
|
|
|
}
|
|
|
|
|
|
private string AspectGetter (object o)
|
|
|
@@ -780,7 +787,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- private void CellActivate (object sender, CellActivatedEventArgs obj)
|
|
|
+ private void CellActivate (object? sender, CellActivatedEventArgs obj)
|
|
|
{
|
|
|
if (TryAcceptMulti ())
|
|
|
{
|
|
|
@@ -833,9 +840,9 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
private void Delete ()
|
|
|
{
|
|
|
- IFileSystemInfo [] toDelete = GetFocusedFiles ();
|
|
|
+ IFileSystemInfo [] toDelete = GetFocusedFiles ()!;
|
|
|
|
|
|
- if (toDelete is { } && FileOperationsHandler.Delete (toDelete))
|
|
|
+ if (FileOperationsHandler.Delete (toDelete))
|
|
|
{
|
|
|
RefreshState ();
|
|
|
}
|
|
|
@@ -871,9 +878,9 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
private string GetBackButtonText () { return Glyphs.LeftArrow + "-"; }
|
|
|
|
|
|
- private IFileSystemInfo [] GetFocusedFiles ()
|
|
|
+ private IFileSystemInfo []? GetFocusedFiles ()
|
|
|
{
|
|
|
- if (!_tableView.HasFocus || !_tableView.CanFocus || FileOperationsHandler is null)
|
|
|
+ if (!_tableView.HasFocus || !_tableView.CanFocus)
|
|
|
{
|
|
|
return null;
|
|
|
}
|
|
|
@@ -911,13 +918,6 @@ public class FileDialog : Dialog, IDesignable
|
|
|
return string.Format (Strings.fdCtxSortAsc, _tableView.Table.ColumnNames [clickedCol]);
|
|
|
}
|
|
|
|
|
|
- private string GetToggleSplitterText (bool isExpanded)
|
|
|
- {
|
|
|
- return isExpanded
|
|
|
- ? new ((char)Glyphs.LeftArrow.Value, 2)
|
|
|
- : new string ((char)Glyphs.RightArrow.Value, 2);
|
|
|
- }
|
|
|
-
|
|
|
private string GetUpButtonText () { return Style.UseUnicodeCharacters ? "◭" : "▲"; }
|
|
|
|
|
|
private void HideColumn (int clickedCol)
|
|
|
@@ -940,7 +940,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
private bool IsCompatibleWithOpenMode (string s, out string reason)
|
|
|
{
|
|
|
- reason = null;
|
|
|
+ reason = string.Empty;
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace (s))
|
|
|
{
|
|
|
@@ -1020,12 +1020,9 @@ public class FileDialog : Dialog, IDesignable
|
|
|
{
|
|
|
foreach (Point p in _tableView.GetAllSelectedCells ())
|
|
|
{
|
|
|
- FileSystemInfoStats add = State?.Children [p.Y];
|
|
|
+ FileSystemInfoStats add = State?.Children [p.Y]!;
|
|
|
|
|
|
- if (add is { })
|
|
|
- {
|
|
|
- toReturn.Add (add);
|
|
|
- }
|
|
|
+ toReturn.Add (add);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1034,9 +1031,8 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
private void New ()
|
|
|
{
|
|
|
- if (State is { })
|
|
|
{
|
|
|
- IFileSystemInfo created = FileOperationsHandler.New (_fileSystem, State.Directory);
|
|
|
+ IFileSystemInfo created = FileOperationsHandler.New (_fileSystem, State!.Directory);
|
|
|
|
|
|
if (created is { })
|
|
|
{
|
|
|
@@ -1046,7 +1042,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void OnTableViewMouseClick (object sender, MouseEventArgs e)
|
|
|
+ private void OnTableViewMouseClick (object? sender, MouseEventArgs e)
|
|
|
{
|
|
|
Point? clickedCell = _tableView.ScreenToCell (e.Position.X, e.Position.Y, out int? clickedCol);
|
|
|
|
|
|
@@ -1167,13 +1163,13 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
private void RefreshState ()
|
|
|
{
|
|
|
- State.RefreshChildren ();
|
|
|
+ State!.RefreshChildren ();
|
|
|
PushState (State, false, false, false);
|
|
|
}
|
|
|
|
|
|
private void Rename ()
|
|
|
{
|
|
|
- IFileSystemInfo [] toRename = GetFocusedFiles ();
|
|
|
+ IFileSystemInfo [] toRename = GetFocusedFiles ()!;
|
|
|
|
|
|
if (toRename?.Length == 1)
|
|
|
{
|
|
|
@@ -1187,26 +1183,6 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // /// <inheritdoc/>
|
|
|
- // public override bool OnHotKey (KeyEventArgs keyEvent)
|
|
|
- // {
|
|
|
- //#if BROKE_IN_2927
|
|
|
- // // BUGBUG: Ctrl-F is forward in a TextField.
|
|
|
- // if (this.NavigateIf (keyEvent, Key.Alt | Key.F, this.tbFind)) {
|
|
|
- // return true;
|
|
|
- // }
|
|
|
- //#endif
|
|
|
-
|
|
|
- // ClearFeedback ();
|
|
|
-
|
|
|
- // if (allowedTypeMenuBar is { } &&
|
|
|
- // keyEvent.ConsoleDriverKey == Key.Tab &&
|
|
|
- // allowedTypeMenuBar.IsMenuOpen) {
|
|
|
- // allowedTypeMenuBar.CloseMenu (false, false, false);
|
|
|
- // }
|
|
|
-
|
|
|
- // return base.OnHotKey (keyEvent);
|
|
|
- // }
|
|
|
private void RestartSearch ()
|
|
|
{
|
|
|
if (_disposed || State?.Directory is null)
|
|
|
@@ -1232,10 +1208,10 @@ public class FileDialog : Dialog, IDesignable
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- PushState (new SearchState (State?.Directory, this, _tbFind.Text), true);
|
|
|
+ PushState (new SearchState (State?.Directory!, this, _tbFind.Text), true);
|
|
|
}
|
|
|
|
|
|
- private FileSystemInfoStats RowToStats (int rowIndex) { return State?.Children [rowIndex]; }
|
|
|
+ private FileSystemInfoStats RowToStats (int rowIndex) { return State?.Children [rowIndex]!; }
|
|
|
|
|
|
private void ShowCellContextMenu (Point? clickedCell, MouseEventArgs e)
|
|
|
{
|
|
|
@@ -1304,10 +1280,10 @@ public class FileDialog : Dialog, IDesignable
|
|
|
// really not what most users would expect
|
|
|
if (Regex.IsMatch (path, "^\\w:$"))
|
|
|
{
|
|
|
- return _fileSystem.DirectoryInfo.New (path + _fileSystem.Path.DirectorySeparatorChar);
|
|
|
+ return _fileSystem!.DirectoryInfo.New (path + _fileSystem.Path.DirectorySeparatorChar);
|
|
|
}
|
|
|
|
|
|
- return _fileSystem.DirectoryInfo.New (path);
|
|
|
+ return _fileSystem!.DirectoryInfo.New (path);
|
|
|
}
|
|
|
|
|
|
private static string StripArrows (string columnName) { return columnName.Replace (" (▼)", string.Empty).Replace (" (▲)", string.Empty); }
|
|
|
@@ -1359,7 +1335,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- private void TableView_SelectedCellChanged (object sender, SelectedCellChangedEventArgs obj)
|
|
|
+ private void TableView_SelectedCellChanged (object? sender, SelectedCellChangedEventArgs obj)
|
|
|
{
|
|
|
if (!_tableView.HasFocus || obj.NewRow == -1 || obj.Table.Rows == 0)
|
|
|
{
|
|
|
@@ -1373,16 +1349,11 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
FileSystemInfoStats stats = RowToStats (obj.NewRow);
|
|
|
|
|
|
- if (stats is null)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
IFileSystemInfo dest;
|
|
|
|
|
|
if (stats.IsParent)
|
|
|
{
|
|
|
- dest = State.Directory;
|
|
|
+ dest = State!.Directory;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
@@ -1394,7 +1365,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
_pushingState = true;
|
|
|
|
|
|
SetPathToSelectedObject (dest);
|
|
|
- State.Selected = stats;
|
|
|
+ State!.Selected = stats;
|
|
|
_tbPath.Autocomplete.ClearSuggestions ();
|
|
|
}
|
|
|
finally
|
|
|
@@ -1403,7 +1374,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void TreeView_SelectionChanged (object sender, SelectionChangedEventArgs<IFileSystemInfo> e)
|
|
|
+ private void TreeView_SelectionChanged (object? sender, SelectionChangedEventArgs<IFileSystemInfo> e)
|
|
|
{
|
|
|
SetPathToSelectedObject (e.NewValue);
|
|
|
}
|
|
|
@@ -1417,7 +1388,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
if (selected is IDirectoryInfo && Style.PreserveFilenameOnDirectoryChanges)
|
|
|
{
|
|
|
- if (!string.IsNullOrWhiteSpace (Path) && !_fileSystem.Directory.Exists (Path))
|
|
|
+ if (!string.IsNullOrWhiteSpace (Path) && !_fileSystem!.Directory.Exists (Path))
|
|
|
{
|
|
|
var currentFile = _fileSystem.Path.GetFileName (Path);
|
|
|
|
|
|
@@ -1436,21 +1407,23 @@ public class FileDialog : Dialog, IDesignable
|
|
|
private bool TryAcceptMulti ()
|
|
|
{
|
|
|
IEnumerable<FileSystemInfoStats> multi = MultiRowToStats ();
|
|
|
- string reason = null;
|
|
|
+ string? reason = null;
|
|
|
+
|
|
|
+ IEnumerable<FileSystemInfoStats> fileSystemInfoStatsEnumerable = multi as FileSystemInfoStats [] ?? multi.ToArray ();
|
|
|
|
|
|
- if (!multi.Any ())
|
|
|
+ if (!fileSystemInfoStatsEnumerable.Any ())
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- if (multi.All (
|
|
|
- m => IsCompatibleWithOpenMode (
|
|
|
- m.FileSystemInfo.FullName,
|
|
|
- out reason
|
|
|
- )
|
|
|
- ))
|
|
|
+ if (fileSystemInfoStatsEnumerable.All (
|
|
|
+ m => IsCompatibleWithOpenMode (
|
|
|
+ m.FileSystemInfo.FullName,
|
|
|
+ out reason
|
|
|
+ )
|
|
|
+ ))
|
|
|
{
|
|
|
- Accept (multi);
|
|
|
+ Accept (fileSystemInfoStatsEnumerable);
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
@@ -1473,11 +1446,6 @@ public class FileDialog : Dialog, IDesignable
|
|
|
|
|
|
private void WriteStateToTableView ()
|
|
|
{
|
|
|
- if (State is null)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
_tableView.Table =
|
|
|
new FileDialogTableSource (this, State, Style, _currentSortColumn, _currentSortIsAsc);
|
|
|
|
|
|
@@ -1485,6 +1453,49 @@ public class FileDialog : Dialog, IDesignable
|
|
|
_tableView.Update ();
|
|
|
}
|
|
|
|
|
|
+ // --- Tree visibility management ---
|
|
|
+
|
|
|
+ private void ToggleTreeVisibility ()
|
|
|
+ {
|
|
|
+ SetTreeVisible (!_treeView.Visible);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void SetTreeVisible (bool visible)
|
|
|
+ {
|
|
|
+ _treeView.Enabled = visible;
|
|
|
+ _treeView.Visible = visible;
|
|
|
+
|
|
|
+ if (visible)
|
|
|
+ {
|
|
|
+ // When visible, the table view's left edge is a splitter next to the tree
|
|
|
+ _treeView.Width = Dim.Fill (Dim.Func (_ => IsInitialized ? _tableViewContainer!.Frame.Width - 30 : 30));
|
|
|
+ _tableViewContainer.X = 30;
|
|
|
+ _tableViewContainer.Arrangement = ViewArrangement.LeftResizable;
|
|
|
+ _tableViewContainer.Border!.Thickness = new (1, 0, 0, 0);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // When hidden, table occupies full width and splitter is hidden/disabled
|
|
|
+ _treeView.Width = 0;
|
|
|
+ _tableViewContainer.X = 0;
|
|
|
+ _tableViewContainer.Width = Dim.Fill ();
|
|
|
+ _tableViewContainer.Arrangement = ViewArrangement.Fixed;
|
|
|
+ _tableViewContainer.Border!.Thickness = new (0, 0, 0, 0);
|
|
|
+ }
|
|
|
+ _btnTreeToggle.Text = GetTreeToggleText (visible);
|
|
|
+
|
|
|
+ SetNeedsLayout ();
|
|
|
+ SetNeedsDraw ();
|
|
|
+ }
|
|
|
+
|
|
|
+ private string GetTreeToggleText (bool visible)
|
|
|
+ {
|
|
|
+ return visible
|
|
|
+ ? $"{Glyphs.LeftArrow}{Strings.fdTree}"
|
|
|
+ : $"{Glyphs.RightArrow}{Strings.fdTree}";
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>State representing a recursive search from <see cref="FileDialogState.Directory"/> downwards.</summary>
|
|
|
internal class SearchState : FileDialogState
|
|
|
{
|
|
|
@@ -1498,7 +1509,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
public SearchState (IDirectoryInfo dir, FileDialog parent, string searchTerms) : base (dir, parent)
|
|
|
{
|
|
|
parent.SearchMatcher.Initialize (searchTerms);
|
|
|
- Children = new FileSystemInfoStats [0];
|
|
|
+ Children = [];
|
|
|
BeginSearch ();
|
|
|
}
|
|
|
|
|
|
@@ -1529,7 +1540,7 @@ public class FileDialog : Dialog, IDesignable
|
|
|
}
|
|
|
);
|
|
|
|
|
|
- Task.Run (() => { UpdateChildren (); });
|
|
|
+ Task.Run (UpdateChildren);
|
|
|
}
|
|
|
|
|
|
private void RecursiveFind (IDirectoryInfo directory)
|