diff --git a/Examples/UICatalog/Scenarios/Notepad.cs b/Examples/UICatalog/Scenarios/Notepad.cs
index c827ca59f..fd3259807 100644
--- a/Examples/UICatalog/Scenarios/Notepad.cs
+++ b/Examples/UICatalog/Scenarios/Notepad.cs
@@ -59,12 +59,12 @@ public class Notepad : Scenario
_tabView.Style.ShowBorder = true;
_tabView.ApplyStyleChanges ();
- // Start with only a single view but support splitting to show side by side
- var split = new TileView (1) { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
- split.Tiles.ElementAt (0).ContentView.Add (_tabView);
- split.LineStyle = LineStyle.None;
+ _tabView.X = 0;
+ _tabView.Y = 1;
+ _tabView.Width = Dim.Fill ();
+ _tabView.Height = Dim.Fill (1);
- top.Add (split);
+ top.Add (_tabView);
LenShortcut = new (Key.Empty, "Len: ", null);
var statusBar = new StatusBar (new [] {
@@ -199,38 +199,10 @@ public class Notepad : Scenario
tab.View.Dispose ();
_focusedTabView = tv;
+ // If last tab is closed, open a new one
if (tv.Tabs.Count == 0)
{
- var split = (TileView)tv.SuperView.SuperView;
-
- // if it is the last TabView on screen don't drop it or we will
- // be unable to open new docs!
- if (split.IsRootTileView () && split.Tiles.Count == 1)
- {
- return;
- }
-
- int tileIndex = split.IndexOf (tv);
- split.RemoveTile (tileIndex);
-
- if (split.Tiles.Count == 0)
- {
- TileView parent = split.GetParentTileView ();
-
- if (parent == null)
- {
- return;
- }
-
- int idx = parent.IndexOf (split);
-
- if (idx == -1)
- {
- return;
- }
-
- parent.RemoveTile (idx);
- }
+ New ();
}
}
@@ -286,37 +258,6 @@ public class Notepad : Scenario
private void Quit () { Application.RequestStop (); }
- private void Split (int offset, Orientation orientation, TabView sender, OpenedFile tab)
- {
- var split = (TileView)sender.SuperView.SuperView;
- int tileIndex = split.IndexOf (sender);
-
- if (tileIndex == -1)
- {
- return;
- }
-
- if (orientation != split.Orientation)
- {
- split.TrySplitTile (tileIndex, 1, out split);
- split.Orientation = orientation;
- tileIndex = 0;
- }
-
- Tile newTile = split.InsertTile (tileIndex + offset);
- TabView newTabView = CreateNewTabView ();
- tab.CloneTo (newTabView);
- newTile.ContentView.Add (newTabView);
-
- newTabView.FocusDeepest (NavigationDirection.Forward, null);
- newTabView.AdvanceFocus (NavigationDirection.Forward, null);
- }
-
- private void SplitDown (TabView sender, OpenedFile tab) { Split (1, Orientation.Horizontal, sender, tab); }
- private void SplitLeft (TabView sender, OpenedFile tab) { Split (0, Orientation.Vertical, sender, tab); }
- private void SplitRight (TabView sender, OpenedFile tab) { Split (1, Orientation.Vertical, sender, tab); }
- private void SplitUp (TabView sender, OpenedFile tab) { Split (0, Orientation.Horizontal, sender, tab); }
-
private void TabView_SelectedTabChanged (object sender, TabChangedEventArgs e)
{
LenShortcut.Title = $"Len:{e.NewTab?.View?.Text?.Length ?? 0}";
@@ -346,12 +287,7 @@ public class Notepad : Scenario
items =
[
new MenuItemv2 ("Save", "", () => Save (_focusedTabView, e.Tab)),
- new MenuItemv2 ("Close", "", () => Close (tv, e.Tab)),
- new Line (),
- new MenuItemv2 ("Split Up", "", () => SplitUp (tv, t)),
- new MenuItemv2 ("Split Down", "", () => SplitDown (tv, t)),
- new MenuItemv2 ("Split Right", "", () => SplitRight (tv, t)),
- new MenuItemv2 ("Split Left", "", () => SplitLeft (tv, t))
+ new MenuItemv2 ("Close", "", () => Close (tv, e.Tab))
];
PopoverMenu? contextMenu = new (items);
diff --git a/Examples/UICatalog/Scenarios/TileViewNesting.cs b/Examples/UICatalog/Scenarios/TileViewNesting.cs
deleted file mode 100644
index 3c4f7e710..000000000
--- a/Examples/UICatalog/Scenarios/TileViewNesting.cs
+++ /dev/null
@@ -1,227 +0,0 @@
-using System.Linq;
-
-namespace UICatalog.Scenarios;
-
-[ScenarioMetadata ("Tile View Nesting", "Demonstrates recursive nesting of TileViews")]
-[ScenarioCategory ("Controls")]
-public class TileViewNesting : Scenario
-{
- private CheckBox _cbBorder;
- private CheckBox _cbHorizontal;
- private CheckBox _cbTitles;
- private CheckBox _cbUseLabels;
- private TextField _textField;
- private int _viewsCreated;
- private int _viewsToCreate;
- private View _workArea;
-
- /// Setup the scenario.
- public override void Main ()
- {
- Application.Init ();
- // Scenario Windows.
- var win = new Window
- {
- Title = GetName (),
- Y = 1
- };
-
- var lblViews = new Label { Text = "Number Of Views:" };
- _textField = new() { X = Pos.Right (lblViews), Width = 10, Text = "2" };
-
- _textField.TextChanged += (s, e) => SetupTileView ();
-
- _cbHorizontal = new() { X = Pos.Right (_textField) + 1, Text = "Horizontal" };
- _cbHorizontal.CheckedStateChanged += (s, e) => SetupTileView ();
-
- _cbBorder = new() { X = Pos.Right (_cbHorizontal) + 1, Text = "Border" };
- _cbBorder.CheckedStateChanged += (s, e) => SetupTileView ();
-
- _cbTitles = new() { X = Pos.Right (_cbBorder) + 1, Text = "Titles" };
- _cbTitles.CheckedStateChanged += (s, e) => SetupTileView ();
-
- _cbUseLabels = new() { X = Pos.Right (_cbTitles) + 1, Text = "Use Labels" };
- _cbUseLabels.CheckedStateChanged += (s, e) => SetupTileView ();
-
- _workArea = new() { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill () };
-
- var menu = new MenuBar
- {
- Menus =
- [
- new ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) })
- ]
- };
-
- win.Add (lblViews);
- win.Add (_textField);
- win.Add (_cbHorizontal);
- win.Add (_cbBorder);
- win.Add (_cbTitles);
- win.Add (_cbUseLabels);
- win.Add (_workArea);
-
- SetupTileView ();
-
- var top = new Toplevel ();
- top.Add (menu);
- top.Add (win);
-
- Application.Run (top);
- top.Dispose ();
- Application.Shutdown ();
- }
-
- private void AddMoreViews (TileView to)
- {
- if (_viewsCreated == _viewsToCreate)
- {
- return;
- }
-
- if (!(to.Tiles.ElementAt (0).ContentView is TileView))
- {
- Split (to, true);
- }
-
- if (!(to.Tiles.ElementAt (1).ContentView is TileView))
- {
- Split (to, false);
- }
-
- if (to.Tiles.ElementAt (0).ContentView is TileView && to.Tiles.ElementAt (1).ContentView is TileView)
- {
- AddMoreViews ((TileView)to.Tiles.ElementAt (0).ContentView);
- AddMoreViews ((TileView)to.Tiles.ElementAt (1).ContentView);
- }
- }
-
- private View CreateContentControl (int number) { return _cbUseLabels.CheckedState == CheckState.Checked ? CreateLabelView (number) : CreateTextView (number); }
-
- private View CreateLabelView (int number)
- {
- return new Label
- {
- Width = Dim.Fill (),
- Height = 1,
-
- Text = number.ToString ().Repeat (1000),
- CanFocus = true
- };
- }
-
- private View CreateTextView (int number)
- {
- return new TextView
- {
- Width = Dim.Fill (), Height = Dim.Fill (), Text = number.ToString ().Repeat (1000), AllowsTab = false
-
- //WordWrap = true, // TODO: This is very slow (like 10s to render with 45 views)
- };
- }
-
- private TileView CreateTileView (int titleNumber, Orientation orientation)
- {
- var toReturn = new TileView
- {
- Width = Dim.Fill (),
- Height = Dim.Fill (),
-
- // flip the orientation
- Orientation = orientation
- };
-
- toReturn.Tiles.ElementAt (0).Title = _cbTitles.CheckedState == CheckState.Checked ? $"View {titleNumber}" : string.Empty;
- toReturn.Tiles.ElementAt (1).Title = _cbTitles.CheckedState == CheckState.Checked ? $"View {titleNumber + 1}" : string.Empty;
-
- return toReturn;
- }
-
- private int GetNumberOfViews ()
- {
- if (int.TryParse (_textField.Text, out int views) && views >= 0)
- {
- return views;
- }
-
- return 0;
- }
-
- private void Quit () { Application.RequestStop (); }
-
- private void SetupTileView ()
- {
- int numberOfViews = GetNumberOfViews ();
-
- CheckState titles = _cbTitles.CheckedState;
- CheckState border = _cbBorder.CheckedState;
- CheckState startHorizontal = _cbHorizontal.CheckedState;
-
- foreach (View sub in _workArea.SubViews)
- {
- sub.Dispose ();
- }
-
- _workArea.RemoveAll ();
-
- if (numberOfViews <= 0)
- {
- return;
- }
-
- TileView root = CreateTileView (1, startHorizontal == CheckState.Checked ? Orientation.Horizontal : Orientation.Vertical);
-
- root.Tiles.ElementAt (0).ContentView.Add (CreateContentControl (1));
- root.Tiles.ElementAt (0).Title = _cbTitles.CheckedState == CheckState.Checked ? "View 1" : string.Empty;
- root.Tiles.ElementAt (1).ContentView.Add (CreateContentControl (2));
- root.Tiles.ElementAt (1).Title = _cbTitles.CheckedState == CheckState.Checked ? "View 2" : string.Empty;
-
- root.LineStyle = border == CheckState.Checked? LineStyle.Rounded : LineStyle.None;
-
- _workArea.Add (root);
-
- if (numberOfViews == 1)
- {
- root.Tiles.ElementAt (1).ContentView.Visible = false;
- }
-
- if (numberOfViews > 2)
- {
- _viewsCreated = 2;
- _viewsToCreate = numberOfViews;
- AddMoreViews (root);
- }
- }
-
- private void Split (TileView to, bool left)
- {
- if (_viewsCreated == _viewsToCreate)
- {
- return;
- }
-
- TileView newView;
-
- if (left)
- {
- to.TrySplitTile (0, 2, out newView);
- }
- else
- {
- to.TrySplitTile (1, 2, out newView);
- }
-
- _viewsCreated++;
-
- // During splitting the old Title will have been migrated to View1 so we only need
- // to set the Title on View2 (the one that gets our new TextView)
- newView.Tiles.ElementAt (1).Title = _cbTitles.CheckedState == CheckState.Checked ? $"View {_viewsCreated}" : string.Empty;
-
- // Flip orientation
- newView.Orientation = to.Orientation == Orientation.Vertical
- ? Orientation.Horizontal
- : Orientation.Vertical;
-
- newView.Tiles.ElementAt (1).ContentView.Add (CreateContentControl (_viewsCreated));
- }
-}
diff --git a/Terminal.Gui/App/Application.cs b/Terminal.Gui/App/Application.cs
index e1012c85c..22b76b706 100644
--- a/Terminal.Gui/App/Application.cs
+++ b/Terminal.Gui/App/Application.cs
@@ -179,8 +179,6 @@ public static partial class Application
// starts running and after Shutdown returns.
internal static void ResetState (bool ignoreDisposed = false)
{
- Navigation = new ();
-
// Shutdown is the bookend for Init. As such it needs to clean up all resources
// Init created. Apps that do any threading will need to code defensively for this.
// e.g. see Issue #537
diff --git a/Terminal.Gui/Resources/Strings.resx b/Terminal.Gui/Resources/Strings.resx
index bb0cd914e..68eb7abcd 100644
--- a/Terminal.Gui/Resources/Strings.resx
+++ b/Terminal.Gui/Resources/Strings.resx
@@ -195,7 +195,7 @@
Enter Path
- Enter Search
+ _Find:
Size
diff --git a/Terminal.Gui/ViewBase/View.Arrangement.cs b/Terminal.Gui/ViewBase/View.Arrangement.cs
index baa1c76ff..dba4f6c83 100644
--- a/Terminal.Gui/ViewBase/View.Arrangement.cs
+++ b/Terminal.Gui/ViewBase/View.Arrangement.cs
@@ -11,5 +11,41 @@ public partial class View
/// See the View Arrangement Deep Dive for more information:
///
///
+ ///
+ ///
+ /// This example demonstrates how to create a resizable splitter between two views using :
+ ///
+ ///
+ /// // Create left pane that fills remaining space
+ /// View leftPane = new ()
+ /// {
+ /// X = 0,
+ /// Y = 0,
+ /// Width = Dim.Fill (Dim.Func (_ => rightPane.Frame.Width)),
+ /// Height = Dim.Fill (),
+ /// CanFocus = true
+ /// };
+ ///
+ /// // Create right pane with resizable left border (acts as splitter)
+ /// View rightPane = new ()
+ /// {
+ /// X = Pos.Right (leftPane) - 1,
+ /// Y = 0,
+ /// Width = Dim.Fill (),
+ /// Height = Dim.Fill (),
+ /// Arrangement = ViewArrangement.LeftResizable,
+ /// BorderStyle = LineStyle.Single,
+ /// SuperViewRendersLineCanvas = true,
+ /// CanFocus = true
+ /// };
+ /// rightPane.Border!.Thickness = new (1, 0, 0, 0); // Only left border
+ ///
+ /// container.Add (leftPane, rightPane);
+ ///
+ ///
+ /// The right pane's left border acts as a draggable splitter. The left pane's width automatically adjusts
+ /// to fill the remaining space using Dim.Fill with a function that subtracts the right pane's width.
+ ///
+ ///
public ViewArrangement Arrangement { get; set; }
}
diff --git a/Terminal.Gui/ViewBase/View.Hierarchy.cs b/Terminal.Gui/ViewBase/View.Hierarchy.cs
index d7510c2b0..a80193c38 100644
--- a/Terminal.Gui/ViewBase/View.Hierarchy.cs
+++ b/Terminal.Gui/ViewBase/View.Hierarchy.cs
@@ -111,7 +111,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
Logging.Warning ($"{view} has already been Added to {this}.");
}
- // TileView likes to add views that were previously added and have HasFocus = true. No bueno.
+ // Ensure views don't have focus when being added
view.HasFocus = false;
// TODO: Make this thread safe
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
index 344399b82..effd921e5 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
@@ -1,8 +1,7 @@
+#nullable enable
using System.IO.Abstractions;
using System.Text.RegularExpressions;
-#nullable enable
-
namespace Terminal.Gui.Views;
///
@@ -10,10 +9,11 @@ namespace Terminal.Gui.Views;
///
public class FileDialog : Dialog, IDesignable
{
- private const int alignmentGroupInput = 32;
- private const int alignmentGroupComplete = 55;
+ private const int ALIGNMENT_GROUP_INPUT = 32;
+ private const int ALIGNMENT_GROUP_COMPLETE = 55;
/// Gets the Path separators for the operating system
+ // ReSharper disable once InconsistentNaming
internal static char [] Separators =
[
System.IO.Path.AltDirectorySeparatorChar,
@@ -34,12 +34,11 @@ 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 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;
@@ -61,7 +60,7 @@ public class FileDialog : Dialog, IDesignable
/// Initializes a new instance of the class with a custom .
/// This overload is mainly useful for testing.
- 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
};
@@ -91,7 +90,7 @@ public class FileDialog : Dialog, IDesignable
_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
};
@@ -149,21 +148,29 @@ public class FileDialog : Dialog, IDesignable
_tbPath.Autocomplete = new AppendAutocomplete (_tbPath);
_tbPath.Autocomplete.SuggestionGenerator = new FilepathSuggestionGenerator ();
- _splitContainer = new ()
+ // Create tree view container (left pane)
+ View treeViewContainer = new ()
+ {
+ X = -1,
+ Y = Pos.Bottom (_btnBack),
+ Width = Dim.Fill (Dim.Func (_ => IsInitialized ? _tableViewContainer!.Frame.Width - 1 : 1)),
+ Height = Dim.Fill (Dim.Func (_ => IsInitialized ? _btnOk.Frame.Height : 1)),
+ CanFocus = true,
+ };
+
+ // 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))
+ Height = Dim.Fill (Dim.Func (_ => IsInitialized ? _btnOk.Frame.Height : 1)),
+ Arrangement = ViewArrangement.LeftResizable,
+ BorderStyle = LineStyle.Dashed,
+ SuperViewRendersLineCanvas = true,
+ CanFocus = true
};
-
- Initialized += (s, e) =>
- {
- _splitContainer.SetSplitterPos (0, 30);
- _splitContainer.Tiles.ElementAt (0).ContentView.Visible = false;
- };
-
- // this.splitContainer.Border.BorderStyle = BorderStyle.None;
+ _tableViewContainer.Border!.Thickness = new (1, 0, 0, 0);
_tableView = new ()
{
@@ -202,38 +209,20 @@ public class FileDialog : Dialog, IDesignable
_treeView.SelectionChanged += TreeView_SelectionChanged;
- _splitContainer.Tiles.ElementAt (0).ContentView.Add (_treeView);
- _splitContainer.Tiles.ElementAt (1).ContentView.Add (_tableView);
-
- _btnToggleSplitterCollapse = new ()
- {
- X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
- Y = Pos.AnchorEnd (), Text = GetToggleSplitterText (false)
- };
-
- _btnToggleSplitterCollapse.Accepting += (s, e) =>
- {
- // Required otherwise the Save button clicks itself
- e.Handled = true;
- Tile tile = _splitContainer.Tiles.ElementAt (0);
-
- bool newState = !tile.ContentView.Visible;
- tile.ContentView.Visible = newState;
- _btnToggleSplitterCollapse.Text = GetToggleSplitterText (newState);
- SetNeedsLayout ();
- };
+ treeViewContainer.Add (_treeView);
+ _tableViewContainer.Add (_tableView);
_tbFind = new ()
{
- X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
+ X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, ALIGNMENT_GROUP_INPUT),
CaptionColor = new (Color.Black),
Width = 30,
- Y = Pos.Top (_btnToggleSplitterCollapse),
+ Y = Pos.Top (_btnOk),
HotKey = Key.F.WithAlt
};
_spinnerView = new ()
- { X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput), Y = Pos.AnchorEnd (1), Visible = false };
+ { X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, ALIGNMENT_GROUP_INPUT), Y = Pos.AnchorEnd (1), Visible = false };
_tbFind.TextChanged += (s, o) => RestartSearch ();
@@ -278,17 +267,17 @@ public class FileDialog : Dialog, IDesignable
UpdateNavigationVisibility ();
- Add (_tbPath);
- Add (_btnUp);
- Add (_btnBack);
- Add (_btnForward);
- Add (_splitContainer);
- Add (_btnToggleSplitterCollapse);
- Add (_tbFind);
- Add (_spinnerView);
+ base.Add (_tbPath);
+ base.Add (_btnUp);
+ base.Add (_btnBack);
+ base.Add (_btnForward);
+ base.Add (treeViewContainer);
+ base.Add (_tableViewContainer);
+ base.Add (_tbFind);
+ base.Add (_spinnerView);
- Add (_btnOk);
- Add (_btnCancel);
+ base.Add (_btnOk);
+ base.Add (_btnCancel);
}
///
@@ -418,7 +407,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));
}
@@ -444,7 +433,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 +454,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 (
"",
@@ -497,7 +485,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 +494,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
@@ -693,11 +681,8 @@ public class FileDialog : Dialog, IDesignable
if (!IsCompatibleWithOpenMode (_tbPath.Text, out string reason))
{
- if (reason is { })
- {
- _feedback = reason;
- SetNeedsDraw ();
- }
+ _feedback = reason;
+ SetNeedsDraw ();
return;
}
@@ -727,18 +712,15 @@ public class FileDialog : Dialog, IDesignable
_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 +762,7 @@ public class FileDialog : Dialog, IDesignable
return false;
}
- private void CellActivate (object sender, CellActivatedEventArgs obj)
+ private void CellActivate (object? sender, CellActivatedEventArgs obj)
{
if (TryAcceptMulti ())
{
@@ -835,7 +817,7 @@ public class FileDialog : Dialog, IDesignable
{
IFileSystemInfo [] toDelete = GetFocusedFiles ();
- if (toDelete is { } && FileOperationsHandler.Delete (toDelete))
+ if (FileOperationsHandler.Delete (toDelete))
{
RefreshState ();
}
@@ -911,13 +893,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)
@@ -1020,12 +995,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,7 +1006,6 @@ public class FileDialog : Dialog, IDesignable
private void New ()
{
- if (State is { })
{
IFileSystemInfo created = FileOperationsHandler.New (_fileSystem, State.Directory);
@@ -1046,7 +1017,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);
@@ -1187,26 +1158,6 @@ public class FileDialog : Dialog, IDesignable
}
}
- // ///
- // 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 +1183,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 +1255,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 +1310,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,11 +1324,6 @@ public class FileDialog : Dialog, IDesignable
FileSystemInfoStats stats = RowToStats (obj.NewRow);
- if (stats is null)
- {
- return;
- }
-
IFileSystemInfo dest;
if (stats.IsParent)
@@ -1403,7 +1349,7 @@ public class FileDialog : Dialog, IDesignable
}
}
- private void TreeView_SelectionChanged (object sender, SelectionChangedEventArgs e)
+ private void TreeView_SelectionChanged (object? sender, SelectionChangedEventArgs e)
{
SetPathToSelectedObject (e.NewValue);
}
@@ -1436,7 +1382,7 @@ public class FileDialog : Dialog, IDesignable
private bool TryAcceptMulti ()
{
IEnumerable multi = MultiRowToStats ();
- string reason = null;
+ string? reason = null;
if (!multi.Any ())
{
@@ -1473,11 +1419,6 @@ public class FileDialog : Dialog, IDesignable
private void WriteStateToTableView ()
{
- if (State is null)
- {
- return;
- }
-
_tableView.Table =
new FileDialogTableSource (this, State, Style, _currentSortColumn, _currentSortIsAsc);
@@ -1498,7 +1439,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 ();
}
diff --git a/Terminal.Gui/Views/SplitterEventArgs.cs b/Terminal.Gui/Views/SplitterEventArgs.cs
deleted file mode 100644
index 7354209e9..000000000
--- a/Terminal.Gui/Views/SplitterEventArgs.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-
-namespace Terminal.Gui.Views;
-
-/// Provides data for events.
-public class SplitterEventArgs : EventArgs
-{
- /// Creates a new instance of the class.
- /// in which splitter is being moved.
- /// Index of the splitter being moved in .
- /// The new of the splitter line.
- public SplitterEventArgs (TileView tileView, int idx, Pos splitterDistance)
- {
- SplitterDistance = splitterDistance;
- TileView = tileView;
- Idx = idx;
- }
-
- ///
- /// Gets the index of the splitter that is being moved. This can be used to index
- ///
- ///
- public int Idx { get; }
-
- /// New position of the splitter line (see ).
- public Pos SplitterDistance { get; }
-
- /// Container (sender) of the event.
- public TileView TileView { get; }
-}
diff --git a/Terminal.Gui/Views/Tile.cs b/Terminal.Gui/Views/Tile.cs
deleted file mode 100644
index 45bcd259b..000000000
--- a/Terminal.Gui/Views/Tile.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-#nullable enable
-using System.ComponentModel;
-
-namespace Terminal.Gui.Views;
-
-///
-/// A single presented in a . To create new instances use
-/// or .
-///
-public class Tile
-{
- private string _title = string.Empty;
-
- /// Creates a new instance of the class.
- public Tile ()
- {
- ContentView = new View
- {
- Width = Dim.Fill (),
- Height = Dim.Fill (),
- CanFocus = true
- };
-#if DEBUG_IDISPOSABLE
- ContentView.Data = "Tile.ContentView";
-#endif
- Title = string.Empty;
- MinSize = 0;
- }
-
- ///
- /// The that is contained in this . Add new child views to this
- /// member for multiple s within the .
- ///
- public View? ContentView { get; internal set; }
-
- ///
- /// Gets or Sets the minimum size you to allow when splitter resizing along parent
- /// direction.
- ///
- public int MinSize { get; set; }
-
- ///
- /// The text that should be displayed above the . This will appear over the splitter line
- /// or border (above the view client area).
- ///
- /// Title are not rendered for root level tiles is .
- public string Title
- {
- get => _title;
- set
- {
- if (!OnTitleChanging (_title, value))
- {
- string old = _title;
- _title = value;
- OnTitleChanged (old, _title);
-
- return;
- }
-
- _title = value;
- }
- }
-
- /// Called when the has been changed. Invokes the event.
- /// The that is/has been replaced.
- /// The new to be replaced.
- public virtual void OnTitleChanged (string oldTitle, string newTitle)
- {
- var args = new EventArgs (in newTitle);
- TitleChanged?.Invoke (this, args);
- }
-
- ///
- /// Called before the changes. Invokes the event, which can be
- /// cancelled.
- ///
- /// The that is/has been replaced.
- /// The new to be replaced.
- /// true if an event handler cancelled the Title change.
- public virtual bool OnTitleChanging (string oldTitle, string newTitle)
- {
- var args = new CancelEventArgs (ref oldTitle, ref newTitle);
- TitleChanging?.Invoke (this, args);
-
- return args.Cancel;
- }
-
- /// Event fired after the has been changed.
- public event EventHandler? TitleChanged;
-
- ///
- /// Event fired when the is changing.
- /// can be set to true to cancel the change.
- ///
- public event EventHandler>? TitleChanging;
-}
diff --git a/Terminal.Gui/Views/TileView.cs b/Terminal.Gui/Views/TileView.cs
deleted file mode 100644
index 13de45e17..000000000
--- a/Terminal.Gui/Views/TileView.cs
+++ /dev/null
@@ -1,1093 +0,0 @@
-#nullable enable
-
-namespace Terminal.Gui.Views;
-
-///
-/// A consisting of a moveable bar that divides the display area into resizeable
-/// .
-///
-public class TileView : View
-{
- private Orientation _orientation = Orientation.Vertical;
- private List? _splitterDistances;
- private List? _splitterLines;
- private List? _tiles;
- private TileView? _parentTileView;
-
- /// Creates a new instance of the class with 2 tiles (i.e. left and right).
- public TileView () : this (2) { }
-
- /// Creates a new instance of the class with number of tiles.
- ///
- public TileView (int tiles)
- {
- CanFocus = true;
- RebuildForTileCount (tiles);
-
- SubViewLayout += (_, _) =>
- {
- Rectangle viewport = Viewport;
-
- if (HasBorder ())
- {
- viewport = new (
- viewport.X + 1,
- viewport.Y + 1,
- Math.Max (0, viewport.Width - 2),
- Math.Max (0, viewport.Height - 2)
- );
- }
-
- Setup (viewport);
- };
- }
-
- /// The line style to use when drawing the splitter lines.
- public LineStyle LineStyle { get; set; } = LineStyle.None;
-
- /// Orientation of the dividing line (Horizontal or Vertical).
- public Orientation Orientation
- {
- get => _orientation;
- set
- {
- if (_orientation == value)
- {
- return;
- }
-
- _orientation = value;
-
- SetNeedsDraw ();
- SetNeedsLayout ();
-
- }
- }
-
- /// The splitter locations. Note that there will be N-1 splitters where N is the number of .
- public IReadOnlyCollection SplitterDistances => _splitterDistances!.AsReadOnly ();
-
- /// The sub sections hosted by the view
- public IReadOnlyCollection Tiles => _tiles!.AsReadOnly ();
-
- // TODO: Update to use Key instead of KeyCode
- ///
- /// The keyboard key that the user can press to toggle resizing of splitter lines. Mouse drag splitting is always
- /// enabled.
- ///
- public KeyCode ToggleResizable { get; set; } = KeyCode.CtrlMask | KeyCode.F10;
-
- ///
- /// Returns the immediate parent of this. Note that in case of deep nesting this might not
- /// be the root . Returns null if this instance is not a nested child (created with
- /// )
- ///
- /// Use to determine if the returned value is the root.
- ///
- public TileView? GetParentTileView () { return _parentTileView; }
-
- ///
- /// Returns the index of the first in which contains
- /// .
- ///
- public int IndexOf (View toFind, bool recursive = false)
- {
- for (var i = 0; i < _tiles!.Count; i++)
- {
- View v = _tiles [i].ContentView!;
-
- if (v == toFind)
- {
- return i;
- }
-
- if (v.SubViews.Contains (toFind))
- {
- return i;
- }
-
- if (recursive)
- {
- if (RecursiveContains (v.SubViews, toFind))
- {
- return i;
- }
- }
- }
-
- return -1;
- }
-
- ///
- /// Adds a new to the collection at . This will also add another splitter
- /// line
- ///
- ///
- public Tile? InsertTile (int idx)
- {
- Tile [] oldTiles = Tiles.ToArray ();
- RebuildForTileCount (oldTiles.Length + 1);
-
- Tile? toReturn = null;
-
- for (var i = 0; i < _tiles?.Count; i++)
- {
- if (i != idx)
- {
- Tile oldTile = oldTiles [i > idx ? i - 1 : i];
-
- // remove the new empty View
- Remove (_tiles [i].ContentView);
- _tiles [i].ContentView?.Dispose ();
- _tiles [i].ContentView = null;
-
- // restore old Tile and View
- _tiles [i] = oldTile;
- _tiles [i].ContentView!.TabStop = TabStop;
- Add (_tiles [i].ContentView);
- }
- else
- {
- toReturn = _tiles [i];
- }
- }
-
- SetNeedsDraw ();
- SetNeedsLayout ();
-
- return toReturn;
- }
-
- ///
- ///
- /// if is nested within a parent e.g. via
- /// the . if it is a root level .
- ///
- ///
- ///
- /// Note that manually adding one to another will not result in a parent/child relationship
- /// and both will still be considered 'root' containers. Always use
- /// if you want to subdivide a .
- ///
- ///
- public bool IsRootTileView () { return _parentTileView == null; }
-
- /// Overridden so no Frames get drawn
- ///
- protected override bool OnDrawingAdornments () { return true; }
-
- ///
- protected override bool OnRenderingLineCanvas () { return false; }
-
- ///
- protected override void OnDrawComplete (DrawContext? context)
- {
- SetAttributeForRole (Enabled ? VisualRole.Normal : VisualRole.Disabled);
-
- var lc = new LineCanvas ();
-
- List allLines = GetAllLineViewsRecursively (this);
- List allTitlesToRender = GetAllTitlesToRenderRecursively (this);
-
- if (IsRootTileView ())
- {
- if (HasBorder ())
- {
- lc.AddLine (Point.Empty, Viewport.Width, Orientation.Horizontal, LineStyle);
- lc.AddLine (Point.Empty, Viewport.Height, Orientation.Vertical, LineStyle);
-
- lc.AddLine (
- new (Viewport.Width - 1, Viewport.Height - 1),
- -Viewport.Width,
- Orientation.Horizontal,
- LineStyle
- );
-
- lc.AddLine (
- new (Viewport.Width - 1, Viewport.Height - 1),
- -Viewport.Height,
- Orientation.Vertical,
- LineStyle
- );
- }
-
- foreach (TileViewLineView line in allLines)
- {
- bool isRoot = _splitterLines!.Contains (line);
-
- Rectangle screen = line.ViewportToScreen (Rectangle.Empty);
- Point origin = ScreenToFrame (screen.Location);
- int length = line.Orientation == Orientation.Horizontal ? line.Frame.Width : line.Frame.Height;
-
- if (!isRoot)
- {
- if (line.Orientation == Orientation.Horizontal)
- {
- origin.X -= 1;
- }
- else
- {
- origin.Y -= 1;
- }
-
- length += 2;
- }
-
- lc.AddLine (origin, length, line.Orientation, LineStyle);
- }
- }
-
- SetAttributeForRole (Enabled ? VisualRole.Normal : VisualRole.Disabled);
-
- foreach (KeyValuePair p in lc.GetMap (Viewport))
- {
- AddRune (p.Key.X, p.Key.Y, p.Value);
- }
-
- // Redraw the lines so that focus/drag symbol renders
- foreach (TileViewLineView line in allLines)
- {
- line.DrawSplitterSymbol ();
- }
-
- // Draw Titles over Border
-
- foreach (TileTitleToRender titleToRender in allTitlesToRender)
- {
- Point renderAt = titleToRender.GetLocalCoordinateForTitle (this);
-
- if (renderAt.Y < 0)
- {
- // If we have no border then root level tiles
- // have nowhere to render their titles.
- continue;
- }
-
- // TODO: Render with focus color if focused
-
- string title = titleToRender.GetTrimmedTitle ();
-
- for (var i = 0; i < title.Length; i++)
- {
- AddRune (renderAt.X + i, renderAt.Y, (Rune)title [i]);
- }
- }
-
- return;
- }
-
- //// BUGBUG: Why is this not handled by a key binding???
- ///
- protected override bool OnKeyDownNotHandled (Key key)
- {
- var focusMoved = false;
-
- if (key.KeyCode == ToggleResizable)
- {
- foreach (TileViewLineView l in _splitterLines!)
- {
- bool iniBefore = l.IsInitialized;
- l.IsInitialized = false;
- l.CanFocus = !l.CanFocus;
- l.IsInitialized = iniBefore;
-
- if (l.CanFocus && !focusMoved)
- {
- l.SetFocus ();
- focusMoved = true;
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- ///
- /// Scraps all and creates new tiles in orientation
- ///
- ///
- ///
- public void RebuildForTileCount (int count)
- {
- _tiles = new ();
- _splitterDistances = new ();
-
- if (_splitterLines is { })
- {
- foreach (TileViewLineView sl in _splitterLines)
- {
- sl.Dispose ();
- }
- }
-
- _splitterLines = new ();
-
- RemoveAll ();
-
- foreach (Tile tile in _tiles)
- {
- tile.ContentView?.Dispose ();
- tile.ContentView = null;
- }
-
- _tiles.Clear ();
- _splitterDistances.Clear ();
-
- if (count == 0)
- {
- return;
- }
-
- for (var i = 0; i < count; i++)
- {
- if (i > 0)
- {
- Pos currentPos = Pos.Percent (100 / count * i);
- _splitterDistances.Add (currentPos);
- var line = new TileViewLineView (this, i - 1);
- Add (line);
- _splitterLines.Add (line);
- }
-
- var tile = new Tile ();
- _tiles.Add (tile);
- tile.ContentView!.Id = $"Tile.ContentView {i}";
- Add (tile.ContentView);
-
- // BUGBUG: This should not be needed:
- tile.TitleChanged += (s, e) => SetNeedsLayout ();
- }
-
- SetNeedsLayout ();
- }
-
- ///
- /// Removes a at the provided from the view. Returns the removed tile
- /// or null if already empty.
- ///
- ///
- ///
- public Tile? RemoveTile (int idx)
- {
- Tile [] oldTiles = Tiles.ToArray ();
-
- if (idx < 0 || idx >= oldTiles.Length)
- {
- return null;
- }
-
- Tile removed = Tiles.ElementAt (idx);
-
- RebuildForTileCount (oldTiles.Length - 1);
-
- for (var i = 0; i < _tiles?.Count; i++)
- {
- int oldIdx = i >= idx ? i + 1 : i;
- Tile oldTile = oldTiles [oldIdx];
-
- // remove the new empty View
- Remove (_tiles [i].ContentView);
- _tiles [i].ContentView?.Dispose ();
- _tiles [i].ContentView = null;
-
- // restore old Tile and View
- _tiles [i] = oldTile;
- Add (_tiles [i].ContentView);
- }
-
- return removed;
- }
-
- ///
- ///
- /// Attempts to update the of line at to the new
- /// . Returns false if the new position is not allowed because of
- /// , location of other splitters etc.
- ///
- ///
- /// Only absolute values (e.g. 10) and percent values (i.e. ) are supported for
- /// this property.
- ///
- ///
- public bool SetSplitterPos (int idx, Pos value)
- {
- if (!(value is PosAbsolute) && !(value is PosPercent))
- {
- throw new ArgumentException (
- $"Only Percent and Absolute values are supported. Passed value was {value.GetType ().Name}"
- );
- }
-
- int fullSpace = _orientation == Orientation.Vertical ? Viewport.Width : Viewport.Height;
-
- if (fullSpace != 0 && !IsValidNewSplitterPos (idx, value, fullSpace))
- {
- return false;
- }
-
- if (_splitterDistances is { })
- {
- _splitterDistances [idx] = value;
- }
-
- OnSplitterMoved (idx);
- SetNeedsDraw ();
- SetNeedsLayout ();
-
- return true;
- }
-
- /// Invoked when any of the is changed.
- public event SplitterEventHandler? SplitterMoved;
-
- ///
- /// Converts of element from a regular to a new
- /// nested the specified . Returns false if the element already
- /// contains a nested view.
- ///
- ///
- /// After successful splitting, the old contents will be moved to the
- /// 's first tile.
- ///
- /// The element of that is to be subdivided.
- /// The number of panels that the should be split into
- /// The new nested .
- ///
- /// if a was converted to a new nested .
- /// if it was already a nested
- ///
- public bool TrySplitTile (int idx, int numberOfPanels, out TileView result)
- {
- // when splitting a view into 2 sub views we will need to migrate
- // the title too
- Tile tile = _tiles! [idx];
-
- string title = tile.Title;
- View? toMove = tile.ContentView;
-
- if (toMove is TileView existing)
- {
- result = existing;
-
- return false;
- }
-
- var newContainer = new TileView (numberOfPanels)
- {
- Width = Dim.Fill (), Height = Dim.Fill (), _parentTileView = this
- };
-
- // Take everything out of the View we are moving
- View [] childViews = toMove!.SubViews.ToArray ();
- toMove.RemoveAll ();
-
- // Remove the view itself and replace it with the new TileView
- Remove (toMove);
- toMove.Dispose ();
- toMove = null;
-
- Add (newContainer);
-
- tile.ContentView = newContainer;
-
- View newTileView1 = newContainer!._tiles? [0].ContentView!;
-
- // Add the original content into the first view of the new container
- foreach (View childView in childViews)
- {
- newTileView1!.Add (childView);
- }
-
- // Move the title across too
- newContainer._tiles! [0].Title = title;
- tile.Title = string.Empty;
-
- result = newContainer;
-
- return true;
- }
-
- ///
- protected override void Dispose (bool disposing)
- {
- foreach (Tile tile in Tiles)
- {
- Remove (tile.ContentView);
- tile.ContentView?.Dispose ();
- }
-
- base.Dispose (disposing);
- }
-
- /// Raises the event
- protected virtual void OnSplitterMoved (int idx) { SplitterMoved?.Invoke (this, new (this, idx, _splitterDistances! [idx])); }
-
- private List GetAllLineViewsRecursively (View v)
- {
- List lines = new ();
-
- foreach (View sub in v.SubViews)
- {
- if (sub is TileViewLineView s)
- {
- if (s.Visible && s.Parent.GetRootTileView () == this)
- {
- lines.Add (s);
- }
- }
- else
- {
- if (sub.Visible)
- {
- lines.AddRange (GetAllLineViewsRecursively (sub));
- }
- }
- }
-
- return lines;
- }
-
- private List GetAllTitlesToRenderRecursively (TileView? v, int depth = 0)
- {
- List titles = new ();
-
- foreach (Tile sub in v!.Tiles)
- {
- // Don't render titles for invisible stuff!
- if (!sub.ContentView!.Visible)
- {
- continue;
- }
-
- if (sub.ContentView is TileView subTileView)
- {
- // Panels with sub split tiles in them can never
- // have their Titles rendered. Instead we dive in
- // and pull up their children as titles
- titles.AddRange (GetAllTitlesToRenderRecursively (subTileView, depth + 1));
- }
- else
- {
- if (sub.Title.Length > 0)
- {
- titles.Add (new (v, sub, depth));
- }
- }
- }
-
- return titles;
- }
-
- private TileView GetRootTileView ()
- {
- TileView root = this;
-
- while (root._parentTileView is { })
- {
- root = root._parentTileView;
- }
-
- return root;
- }
-
- private Dim GetTileWidthOrHeight (int i, int space, Tile? [] visibleTiles, TileViewLineView? [] visibleSplitterLines)
- {
- // last tile
- if (i + 1 >= visibleTiles.Length)
- {
- return Dim.Fill (HasBorder () ? 1 : 0)!;
- }
-
- TileViewLineView? nextSplitter = visibleSplitterLines [i];
- Pos? nextSplitterPos = Orientation == Orientation.Vertical ? nextSplitter!.X : nextSplitter!.Y;
- int nextSplitterDistance = nextSplitterPos.GetAnchor (space);
-
- TileViewLineView? lastSplitter = i >= 1 ? visibleSplitterLines [i - 1] : null;
- Pos? lastSplitterPos = Orientation == Orientation.Vertical ? lastSplitter?.X : lastSplitter?.Y;
- int lastSplitterDistance = lastSplitterPos?.GetAnchor (space) ?? 0;
-
- int distance = nextSplitterDistance - lastSplitterDistance;
-
- if (i > 0)
- {
- return distance - 1;
- }
-
- return distance - (HasBorder () ? 1 : 0);
- }
-
- private bool HasBorder () { return LineStyle != LineStyle.None; }
-
- private void HideSplittersBasedOnTileVisibility ()
- {
- if (_splitterLines is { Count: 0 })
- {
- return;
- }
-
- foreach (TileViewLineView line in _splitterLines!)
- {
- line.Visible = true;
- }
-
- for (var i = 0; i < _tiles!.Count; i++)
- {
- if (!_tiles [i].ContentView!.Visible)
- {
- // when a tile is not visible, prefer hiding
- // the splitter on it's left
- TileViewLineView candidate = _splitterLines [Math.Max (0, i - 1)];
-
- // unless that splitter is already hidden
- // e.g. when hiding panels 0 and 1 of a 3 panel
- // container
- if (candidate.Visible)
- {
- candidate.Visible = false;
- }
- else
- {
- _splitterLines [Math.Min (i, _splitterLines.Count - 1)].Visible = false;
- }
- }
- }
- }
-
- private bool IsValidNewSplitterPos (int idx, Pos value, int fullSpace)
- {
- int newSize = value.GetAnchor (fullSpace);
- bool isGettingBigger = newSize > _splitterDistances! [idx].GetAnchor (fullSpace);
- int lastSplitterOrBorder = HasBorder () ? 1 : 0;
- int nextSplitterOrBorder = HasBorder () ? fullSpace - 1 : fullSpace;
-
- // Cannot move off screen right
- if (newSize >= fullSpace - (HasBorder () ? 1 : 0))
- {
- if (isGettingBigger)
- {
- return false;
- }
- }
-
- // Cannot move off screen left
- if (newSize < (HasBorder () ? 1 : 0))
- {
- if (!isGettingBigger)
- {
- return false;
- }
- }
-
- // Do not allow splitter to move left of the one before
- if (idx > 0)
- {
- int posLeft = _splitterDistances [idx - 1].GetAnchor (fullSpace);
-
- if (newSize <= posLeft)
- {
- return false;
- }
-
- lastSplitterOrBorder = posLeft;
- }
-
- // Do not allow splitter to move right of the one after
- if (idx + 1 < _splitterDistances.Count)
- {
- int posRight = _splitterDistances [idx + 1].GetAnchor (fullSpace);
-
- if (newSize >= posRight)
- {
- return false;
- }
-
- nextSplitterOrBorder = posRight;
- }
-
- if (isGettingBigger)
- {
- int spaceForNext = nextSplitterOrBorder - newSize;
-
- // space required for the last line itself
- if (idx > 0)
- {
- spaceForNext--;
- }
-
- // don't grow if it would take us below min size of right panel
- if (spaceForNext < _tiles! [idx + 1].MinSize)
- {
- return false;
- }
- }
- else
- {
- int spaceForLast = newSize - lastSplitterOrBorder;
-
- // space required for the line itself
- if (idx > 0)
- {
- spaceForLast--;
- }
-
- // don't shrink if it would take us below min size of left panel
- if (spaceForLast < _tiles! [idx].MinSize)
- {
- return false;
- }
- }
-
- return true;
- }
-
- private bool RecursiveContains (IEnumerable haystack, View needle)
- {
- foreach (View v in haystack)
- {
- if (v == needle)
- {
- return true;
- }
-
- if (RecursiveContains (v.SubViews, needle))
- {
- return true;
- }
- }
-
- return false;
- }
-
- private void Setup (Rectangle viewport)
- {
- if (viewport.IsEmpty || viewport.Height <= 0 || viewport.Width <= 0)
- {
- return;
- }
-
- for (var i = 0; i < _splitterLines!.Count; i++)
- {
- TileViewLineView line = _splitterLines [i];
-
- line.Orientation = Orientation;
-
- line.Width = _orientation == Orientation.Vertical
- ? 1
- : Dim.Fill ();
-
- line.Height = _orientation == Orientation.Vertical
- ? Dim.Fill ()
- : 1;
-
- if (_orientation == Orientation.Vertical)
- {
- line.X = _splitterDistances! [i];
- line.Y = 0;
- }
- else
- {
- line.Y = _splitterDistances! [i];
- line.X = 0;
- }
- }
-
- HideSplittersBasedOnTileVisibility ();
-
- Tile [] visibleTiles = _tiles!.Where (t => t.ContentView!.Visible).ToArray ();
- TileViewLineView [] visibleSplitterLines = _splitterLines.Where (l => l.Visible).ToArray ();
-
- for (var i = 0; i < visibleTiles.Length; i++)
- {
- Tile tile = visibleTiles [i];
-
- if (Orientation == Orientation.Vertical)
- {
- tile.ContentView!.X = i == 0 ? viewport.X : Pos.Right (visibleSplitterLines [i - 1]);
- tile.ContentView.Y = viewport.Y;
- tile.ContentView.Height = viewport.Height;
- tile.ContentView.Width = GetTileWidthOrHeight (i, Viewport.Width, visibleTiles, visibleSplitterLines);
- }
- else
- {
- tile.ContentView!.X = viewport.X;
- tile.ContentView.Y = i == 0 ? viewport.Y : Pos.Bottom (visibleSplitterLines [i - 1]);
- tile.ContentView.Width = viewport.Width;
- tile.ContentView.Height = GetTileWidthOrHeight (i, Viewport.Height, visibleTiles, visibleSplitterLines);
- }
-
- // BUGBUG: This should not be needed. If any of the pos/dim setters above actually changed values, NeedsDisplay should have already been set.
- tile.ContentView.SetNeedsDraw ();
- }
- }
-
- private class TileTitleToRender
- {
- public TileTitleToRender (TileView? parent, Tile tile, int depth)
- {
- Parent = parent;
- Tile = tile;
- Depth = depth;
- }
-
- public int Depth { get; }
- public TileView? Parent { get; }
- public Tile? Tile { get; }
-
- ///
- /// Translates the title location from its local coordinate space
- /// .
- ///
- public Point GetLocalCoordinateForTitle (TileView intoCoordinateSpace)
- {
- Rectangle screen = Tile!.ContentView!.ViewportToScreen (Rectangle.Empty);
-
- return intoCoordinateSpace.ScreenToFrame (new (screen.X, screen.Y - 1));
- }
-
- internal string GetTrimmedTitle ()
- {
- Dim? spaceDim = Tile?.ContentView?.Width;
-
- int spaceAbs = spaceDim!.GetAnchor (Parent!.Viewport.Width);
-
- var title = $" {Tile!.Title} ";
-
- if (title.Length > spaceAbs)
- {
- return title!.Substring (0, spaceAbs);
- }
-
- return title;
- }
- }
-
- private class TileViewLineView : Line
- {
- public Point? moveRuneRenderLocation;
-
- private Pos? dragOrignalPos;
- private Point? dragPosition;
-
- public TileViewLineView (TileView parent, int idx)
- {
- CanFocus = false;
- TabStop = TabBehavior.TabStop;
-
- Parent = parent;
- Idx = idx;
- AddCommand (Command.Right, () => MoveSplitter (1, 0));
-
- AddCommand (Command.Left, () => MoveSplitter (-1, 0));
-
- AddCommand (Command.Up, () => MoveSplitter (0, -1));
-
- AddCommand (Command.Down, () => MoveSplitter (0, 1));
-
- KeyBindings.Add (Key.CursorRight, Command.Right);
- KeyBindings.Add (Key.CursorLeft, Command.Left);
- KeyBindings.Add (Key.CursorUp, Command.Up);
- KeyBindings.Add (Key.CursorDown, Command.Down);
- }
-
- public int Idx { get; }
- public TileView Parent { get; }
-
- public void DrawSplitterSymbol ()
- {
- if (dragPosition is { } || CanFocus)
- {
- Point location = moveRuneRenderLocation ?? new Point (Viewport.Width / 2, Viewport.Height / 2);
-
- AddRune (location.X, location.Y, Glyphs.Diamond);
- }
- }
-
- protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
- {
- if (!dragPosition.HasValue && mouseEvent.Flags == MouseFlags.Button1Pressed)
- {
- // Start a Drag
- SetFocus ();
-
- if (mouseEvent.Flags == MouseFlags.Button1Pressed)
- {
- dragPosition = mouseEvent.Position;
- dragOrignalPos = Orientation == Orientation.Horizontal ? Y : X;
- Application.MouseGrabHandler.GrabMouse (this);
-
- if (Orientation == Orientation.Horizontal)
- { }
- else
- {
- moveRuneRenderLocation = new Point (
- 0,
- Math.Max (1, Math.Min (Viewport.Height - 2, mouseEvent.Position.Y))
- );
- }
- }
-
- return true;
- }
-
- if (
- dragPosition.HasValue && mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
- {
- // Continue Drag
-
- // how far has user dragged from original location?
- if (Orientation == Orientation.Horizontal)
- {
- int dy = mouseEvent.Position.Y - dragPosition.Value.Y;
- Parent.SetSplitterPos (Idx, Offset (Y, dy));
- moveRuneRenderLocation = new Point (mouseEvent.Position.X, 0);
- }
- else
- {
- int dx = mouseEvent.Position.X - dragPosition.Value.X;
- Parent.SetSplitterPos (Idx, Offset (X, dx));
- moveRuneRenderLocation = new Point (0, Math.Max (1, Math.Min (Viewport.Height - 2, mouseEvent.Position.Y)));
- }
-
- Parent.SetNeedsLayout ();
-
- return true;
- }
-
- if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Released) && dragPosition.HasValue)
- {
- // End Drag
-
- Application.MouseGrabHandler.UngrabMouse ();
-
- //Driver.UncookMouse ();
- FinalisePosition (
- dragOrignalPos!,
- Orientation == Orientation.Horizontal ? Y : X
- );
- dragPosition = null;
- moveRuneRenderLocation = null;
- }
-
- return false;
- }
-
- ///
- protected override bool OnClearingViewport () { return true; }
-
- protected override bool OnDrawingContent ()
- {
- DrawSplitterSymbol ();
-
- return true;
- }
-
- public override Point? PositionCursor ()
- {
- base.PositionCursor ();
-
- Point location = moveRuneRenderLocation ?? new Point (Viewport.Width / 2, Viewport.Height / 2);
- Move (location.X, location.Y);
-
- return null; // Hide cursor
- }
-
- ///
- ///
- /// Determines the absolute position of and returns a that
- /// describes the percentage of that.
- ///
- ///
- /// Effectively turning any into a (as if created with
- /// )
- ///
- ///
- /// The to convert to
- /// The Height/Width that lies within
- ///
- private Pos ConvertToPosPercent (Pos p, int parentLength)
- {
- // Calculate position in the 'middle' of the cell at p distance along parentLength
- float position = p.GetAnchor (parentLength) + 0.5f;
-
- // Calculate the percentage
- var percent = (int)Math.Round (position / parentLength * 100);
-
- // Return a new PosPercent object
- return Pos.Percent (percent);
- }
-
- ///
- ///
- /// Moves to
- /// preserving format (absolute / relative) that
- /// had.
- ///
- ///
- /// This ensures that if splitter location was e.g. 50% before and you move it to absolute 5 then you end up
- /// with 10% (assuming a parent had 50 width).
- ///
- ///
- ///
- ///
- private bool FinalisePosition (Pos oldValue, Pos newValue)
- {
- SetNeedsDraw ();
-
- SetNeedsLayout ();
-
- if (oldValue is PosPercent)
- {
- if (Orientation == Orientation.Horizontal)
- {
- return Parent.SetSplitterPos (Idx, ConvertToPosPercent (newValue, Parent.Viewport.Height));
- }
-
- return Parent.SetSplitterPos (Idx, ConvertToPosPercent (newValue, Parent.Viewport.Width));
- }
-
- return Parent.SetSplitterPos (Idx, newValue);
- }
-
- private bool MoveSplitter (int distanceX, int distanceY)
- {
- if (Orientation == Orientation.Vertical)
- {
- // Cannot move in this direction
- if (distanceX == 0)
- {
- return false;
- }
-
- Pos oldX = X;
-
- return FinalisePosition (oldX, Offset (X, distanceX));
- }
-
- // Cannot move in this direction
- if (distanceY == 0)
- {
- return false;
- }
-
- Pos oldY = Y;
-
- return FinalisePosition (oldY, Offset (Y, distanceY));
- }
-
- private Pos Offset (Pos pos, int delta)
- {
- int posAbsolute = pos.GetAnchor (
- Orientation == Orientation.Horizontal
- ? Parent.Viewport.Height
- : Parent.Viewport.Width
- );
-
- return posAbsolute + delta;
- }
- }
-}
-
-/// Represents a method that will handle splitter events.
-public delegate void SplitterEventHandler (object? sender, SplitterEventArgs e);
diff --git a/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs b/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs
index 6074e89a7..9ca6384fc 100644
--- a/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs
+++ b/Tests/IntegrationTests/FluentTests/FileDialogFluentTests.cs
@@ -159,7 +159,7 @@ public class FileDialogFluentTests
"/";
}
- [Theory]
+ [Theory (Skip = "New splitter design removes expand button.")]
[ClassData (typeof (TestDrivers))]
public void SaveFileDialog_PressingPopTree_ShouldNotChangeCancel (TestDriver d)
{
@@ -177,7 +177,7 @@ public class FileDialogFluentTests
}
- [Theory]
+ [Theory (Skip = "New splitter design removes expand button.")]
[ClassData (typeof (TestDrivers))]
public void SaveFileDialog_PopTree_AndNavigate (TestDriver d)
{
@@ -224,8 +224,8 @@ public class FileDialogFluentTests
.WaitIteration ()
.ScreenShot ("After typing filename 'hello'", _out)
.AssertEndsWith ("hello", sd.Path)
- .LeftClick