diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs
index ba45c59d7..e738d6d1d 100644
--- a/Terminal.Gui/Views/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialog.cs
@@ -10,6 +10,9 @@ namespace Terminal.Gui;
///
public class FileDialog : Dialog
{
+ private const int alignmentGroupInput = 32;
+ private const int alignmentGroupComplete = 55;
+
/// Gets the Path separators for the operating system
internal static char [] Separators =
[
@@ -71,24 +74,20 @@ public class FileDialog : Dialog
_btnOk = new Button
{
- Y = Pos.AnchorEnd (1), X = Pos.Func (CalculateOkButtonPosX), IsDefault = true, Text = Style.OkButtonText
+ X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, alignmentGroupComplete),
+ Y = Pos.AnchorEnd (),
+ IsDefault = true, Text = Style.OkButtonText
};
_btnOk.Accept += (s, e) => Accept (true);
- _btnOk.KeyDown += (s, k) =>
- {
- NavigateIf (k, KeyCode.CursorLeft, _btnCancel);
- NavigateIf (k, KeyCode.CursorUp, _tableView);
- };
- _btnCancel = new Button { Y = Pos.AnchorEnd (1), X = Pos.Right (_btnOk) + 1, Text = Strings.btnCancel };
+ _btnCancel = new Button
+ {
+ X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, alignmentGroupComplete),
+ Y = Pos.AnchorEnd(),
+ Text = Strings.btnCancel
+ };
- _btnCancel.KeyDown += (s, k) =>
- {
- NavigateIf (k, KeyCode.CursorLeft, _btnToggleSplitterCollapse);
- NavigateIf (k, KeyCode.CursorUp, _tableView);
- NavigateIf (k, KeyCode.CursorRight, _btnOk);
- };
_btnCancel.Accept += (s, e) =>
{
Canceled = true;
@@ -121,7 +120,13 @@ public class FileDialog : Dialog
_tbPath.Autocomplete = new AppendAutocomplete (_tbPath);
_tbPath.Autocomplete.SuggestionGenerator = new FilepathSuggestionGenerator ();
- _splitContainer = new TileView { X = 0, Y = 2, Width = Dim.Fill (), Height = Dim.Fill (1) };
+ _splitContainer = new TileView
+ {
+ X = 0,
+ Y = Pos.Bottom (_btnBack),
+ Width = Dim.Fill (),
+ Height = Dim.Fill (Dim.Func (() => IsInitialized ? _btnOk.Frame.Height : 1)),
+ };
Initialized += (s, e) =>
{
@@ -129,7 +134,7 @@ public class FileDialog : Dialog
_splitContainer.Tiles.ElementAt (0).ContentView.Visible = false;
};
- // this.splitContainer.Border.BorderStyle = BorderStyle.None;
+ // this.splitContainer.Border.BorderStyle = BorderStyle.None;
_tableView = new TableView
{
@@ -158,28 +163,7 @@ public class FileDialog : Dialog
ColumnStyle typeStyle = Style.TableStyle.GetOrCreateColumnStyle (3);
typeStyle.MinWidth = 6;
typeStyle.ColorGetter = ColorGetter;
-
- _tableView.KeyDown += (s, k) =>
- {
- if (_tableView.SelectedRow <= 0)
- {
- NavigateIf (k, KeyCode.CursorUp, _tbPath);
- }
-
- if (_tableView.SelectedRow == _tableView.Table.Rows - 1)
- {
- NavigateIf (k, KeyCode.CursorDown, _btnToggleSplitterCollapse);
- }
-
- if (_splitContainer.Tiles.First ().ContentView.Visible && _tableView.SelectedColumn == 0)
- {
- NavigateIf (k, KeyCode.CursorLeft, _treeView);
- }
-
- if (k.Handled)
- { }
- };
-
+
_treeView = new TreeView { Width = Dim.Fill (), Height = Dim.Fill () };
var fileDialogTreeBuilder = new FileSystemTreeBuilder ();
@@ -192,7 +176,11 @@ public class FileDialog : Dialog
_splitContainer.Tiles.ElementAt (0).ContentView.Add (_treeView);
_splitContainer.Tiles.ElementAt (1).ContentView.Add (_tableView);
- _btnToggleSplitterCollapse = new Button { Y = Pos.AnchorEnd (1), Text = GetToggleSplitterText (false) };
+ _btnToggleSplitterCollapse = new Button
+ {
+ X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
+ Y = Pos.AnchorEnd (), Text = GetToggleSplitterText (false)
+ };
_btnToggleSplitterCollapse.Accept += (s, e) =>
{
@@ -206,13 +194,13 @@ public class FileDialog : Dialog
_tbFind = new TextField
{
- X = Pos.Right (_btnToggleSplitterCollapse) + 1,
+ X = Pos.Align (Alignment.Start,AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
CaptionColor = new Color (Color.Black),
Width = 30,
- Y = Pos.AnchorEnd (1),
+ Y = Pos.Top (_btnToggleSplitterCollapse),
HotKey = Key.F.WithAlt
};
- _spinnerView = new SpinnerView { X = Pos.Right (_tbFind) + 1, Y = Pos.AnchorEnd (1), Visible = false };
+ _spinnerView = new SpinnerView { X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput), Y = Pos.AnchorEnd (1), Visible = false };
_tbFind.TextChanged += (s, o) => RestartSearch ();
@@ -231,16 +219,6 @@ public class FileDialog : Dialog
o.Handled = true;
}
}
-
- if (_tbFind.CursorIsAtEnd ())
- {
- NavigateIf (o, KeyCode.CursorRight, _btnCancel);
- }
-
- if (_tbFind.CursorIsAtStart ())
- {
- NavigateIf (o, KeyCode.CursorLeft, _btnToggleSplitterCollapse);
- }
};
_tableView.Style.ShowHorizontalHeaderOverline = true;
@@ -262,48 +240,22 @@ public class FileDialog : Dialog
_tableView.KeyBindings.ReplaceCommands (Key.End, Command.BottomEnd);
_tableView.KeyBindings.ReplaceCommands (Key.Home.WithShift, Command.TopHomeExtend);
_tableView.KeyBindings.ReplaceCommands (Key.End.WithShift, Command.BottomEndExtend);
-
- _treeView.KeyDown += (s, k) =>
- {
- IFileSystemInfo selected = _treeView.SelectedObject;
-
- if (selected is { })
- {
- if (!_treeView.CanExpand (selected) || _treeView.IsExpanded (selected))
- {
- NavigateIf (k, KeyCode.CursorRight, _tableView);
- }
- else if (_treeView.GetObjectRow (selected) == 0)
- {
- NavigateIf (k, KeyCode.CursorUp, _tbPath);
- }
- }
-
- if (k.Handled)
- {
- return;
- }
-
- k.Handled = TreeView_KeyDown (k);
- };
-
+
AllowsMultipleSelection = false;
UpdateNavigationVisibility ();
- // BUGBUG: This TabOrder is counter-intuitive. The tab order for a dialog should match the
- // order the Views' are presented, left to right, top to bottom.
- // Determines tab order
- Add (_btnToggleSplitterCollapse);
- Add (_tbFind);
- Add (_spinnerView);
- Add (_btnOk);
- Add (_btnCancel);
+ Add (_tbPath);
Add (_btnUp);
Add (_btnBack);
Add (_btnForward);
- Add (_tbPath);
Add (_splitContainer);
+ Add (_btnToggleSplitterCollapse);
+ Add (_tbFind);
+ Add (_spinnerView);
+
+ Add(_btnOk);
+ Add(_btnCancel);
}
///
@@ -1041,23 +993,6 @@ public class FileDialog : Dialog
return toReturn;
}
- private bool NavigateIf (Key keyEvent, KeyCode isKey, View to)
- {
- if (keyEvent.KeyCode == isKey)
- {
- to.FocusDeepest (NavigationDirection.Forward, null);
-
- if (to == _tbPath)
- {
- _tbPath.MoveEnd ();
- }
-
- return true;
- }
-
- return false;
- }
-
private void New ()
{
if (State is { })
@@ -1430,19 +1365,6 @@ public class FileDialog : Dialog
}
}
- private bool TreeView_KeyDown (Key keyEvent)
- {
- if (_treeView.HasFocus && Separators.Contains ((char)keyEvent))
- {
- _tbPath.FocusDeepest (NavigationDirection.Forward, null);
-
- // let that keystroke go through on the tbPath instead
- return true;
- }
-
- return false;
- }
-
private void TreeView_SelectionChanged (object sender, SelectionChangedEventArgs e)
{
if (e.NewValue is null)
diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs
index 10c53513d..ac9ea1113 100644
--- a/Terminal.Gui/Views/TableView/TableView.cs
+++ b/Terminal.Gui/Views/TableView/TableView.cs
@@ -54,47 +54,19 @@ public class TableView : View
// Things this view knows how to do
AddCommand (
Command.Right,
- () =>
- {
- // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
- ChangeSelectionByOffset (1, 0, false);
-
- return true;
- }
- );
+ () => ChangeSelectionByOffsetWithReturn (1, 0));
AddCommand (
Command.Left,
- () =>
- {
- // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
- ChangeSelectionByOffset (-1, 0, false);
-
- return true;
- }
- );
+ () => ChangeSelectionByOffsetWithReturn (-1, 0));
AddCommand (
Command.LineUp,
- () =>
- {
- // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
- ChangeSelectionByOffset (0, -1, false);
-
- return true;
- }
- );
+ () => ChangeSelectionByOffsetWithReturn (0, -1));
AddCommand (
Command.LineDown,
- () =>
- {
- // BUGBUG: SHould return false if selectokn doesn't change (to support nav to next view)
- ChangeSelectionByOffset (0, 1, false);
-
- return true;
- }
- );
+ () => ChangeSelectionByOffsetWithReturn (0, 1));
AddCommand (
Command.PageUp,
@@ -519,6 +491,41 @@ public class TableView : View
return new Point (colHit.X, tableRow + headerHeight - RowOffset);
}
+ ///
+ /// Private override of that returns true if the selection has
+ /// changed as a result of moving the selection. Used by key handling logic to determine whether e.g.
+ /// the cursor right resulted in a change or should be forwarded on to toggle logic handling.
+ ///
+ ///
+ ///
+ ///
+ private bool ChangeSelectionByOffsetWithReturn (int offsetX, int offsetY)
+ {
+ var oldSelection = GetSelectionSnapshot ();
+ SetSelection (SelectedColumn + offsetX, SelectedRow + offsetY, false);
+ Update ();
+
+ return !SelectionIsSame (oldSelection);
+ }
+
+ private TableViewSelectionSnapshot GetSelectionSnapshot ()
+ {
+ return new (
+ SelectedColumn,
+ SelectedRow,
+ MultiSelectedRegions.Select (s => s.Rectangle).ToArray ());
+ }
+
+ private bool SelectionIsSame (TableViewSelectionSnapshot oldSelection)
+ {
+ var newSelection = GetSelectionSnapshot ();
+
+ return oldSelection.SelectedColumn == newSelection.SelectedColumn
+ && oldSelection.SelectedRow == newSelection.SelectedRow
+ && oldSelection.multiSelection.SequenceEqual (newSelection.multiSelection);
+ }
+ private record TableViewSelectionSnapshot (int SelectedColumn, int SelectedRow, Rectangle [] multiSelection);
+
///
/// Moves the and by the provided offsets. Optionally
/// starting a box selection (see )
diff --git a/UnitTests/FileServices/FileDialogTests.cs b/UnitTests/FileServices/FileDialogTests.cs
index 8b8b2e19b..afa6c1b63 100644
--- a/UnitTests/FileServices/FileDialogTests.cs
+++ b/UnitTests/FileServices/FileDialogTests.cs
@@ -99,12 +99,13 @@ public class FileDialogTests (ITestOutputHelper output)
string openIn = Path.Combine (Environment.CurrentDirectory, "zz");
Directory.CreateDirectory (openIn);
dlg.Path = openIn + Path.DirectorySeparatorChar;
- Application.OnKeyDown (Key.Tab);
- Application.OnKeyDown (Key.Tab);
- Application.OnKeyDown (Key.Tab);
+
+ var tf = GetTextField (dlg, FileDialogPart.SearchField);
+ tf.SetFocus ();
Assert.IsType (dlg.MostFocused);
- var tf = (TextField)dlg.MostFocused;
+ Assert.Same (tf, dlg.MostFocused);
+
Assert.Equal ("Enter Search", tf.Caption);
// Dialog has not yet been confirmed with a choice
@@ -140,6 +141,10 @@ public class FileDialogTests (ITestOutputHelper output)
Assert.IsType (dlg.MostFocused);
Send ('v', ConsoleKey.DownArrow);
+
+ var tv = GetTableView(dlg);
+ tv.SetFocus ();
+
Assert.IsType (dlg.MostFocused);
// ".." should be the first thing selected
@@ -177,8 +182,10 @@ public class FileDialogTests (ITestOutputHelper output)
IReadOnlyCollection eventMultiSelected = null;
dlg.FilesSelected += (s, e) => { eventMultiSelected = e.Dialog.MultiSelected; };
- Assert.IsType (dlg.MostFocused);
- Send ('v', ConsoleKey.DownArrow);
+
+ var tv = GetTableView (dlg);
+ tv.SetFocus ();
+
Assert.IsType (dlg.MostFocused);
// Try to toggle '..'
@@ -232,8 +239,9 @@ public class FileDialogTests (ITestOutputHelper output)
IReadOnlyCollection eventMultiSelected = null;
dlg.FilesSelected += (s, e) => { eventMultiSelected = e.Dialog.MultiSelected; };
- Assert.IsType (dlg.MostFocused);
- Send ('v', ConsoleKey.DownArrow);
+ var tv = GetTableView (dlg);
+ tv.SetFocus ();
+
Assert.IsType (dlg.MostFocused);
// Move selection to subfolder
@@ -284,8 +292,9 @@ public class FileDialogTests (ITestOutputHelper output)
IReadOnlyCollection eventMultiSelected = null;
dlg.FilesSelected += (s, e) => { eventMultiSelected = e.Dialog.MultiSelected; };
- Assert.IsType (dlg.MostFocused);
- Send ('v', ConsoleKey.DownArrow);
+ var tv = GetTableView (dlg);
+ tv.SetFocus ();
+
Assert.IsType (dlg.MostFocused);
// Move selection to subfolder
@@ -327,8 +336,9 @@ public class FileDialogTests (ITestOutputHelper output)
dlg.OpenMode = openModeMixed ? OpenMode.Mixed : OpenMode.Directory;
dlg.AllowsMultipleSelection = multiple;
- Assert.IsType (dlg.MostFocused);
- Send ('v', ConsoleKey.DownArrow);
+ var tv = GetTableView (dlg);
+ tv.SetFocus ();
+
Assert.IsType (dlg.MostFocused);
// Should be selecting ..
@@ -421,45 +431,60 @@ public class FileDialogTests (ITestOutputHelper output)
fd.Draw ();
- var expected =
- @$"
-┌─────────────────────────────────────────────────────────────────────────┐
-│/demo/ │
-│{
- CM.Glyphs.LeftBracket
-}▲{
- CM.Glyphs.RightBracket
-} │
-│┌────────────┬──────────┬──────────────────────────────┬────────────────┐│
-││Filename (▲)│Size │Modified │Type ││
-│├────────────┼──────────┼──────────────────────────────┼────────────────┤│
-││.. │ │ │ ││
-││/subfolder │ │2002-01-01T22:42:10 │ ││
-││image.gif │4.00 B │2002-01-01T22:42:10 │.gif ││
-││jQuery.js │7.00 B │2001-01-01T11:44:42 │.js ││
-│ │
-│ │
-│ │
-│{
- CM.Glyphs.LeftBracket
-} ►► {
- CM.Glyphs.RightBracket
-} Enter Search {
- CM.Glyphs.LeftBracket
-}{
- CM.Glyphs.LeftDefaultIndicator
-} OK {
- CM.Glyphs.RightDefaultIndicator
-}{
- CM.Glyphs.RightBracket
-} {
- CM.Glyphs.LeftBracket
-} Cancel {
- CM.Glyphs.RightBracket
-} │
-└─────────────────────────────────────────────────────────────────────────┘
-";
- TestHelpers.AssertDriverContentsAre (expected, output, ignoreLeadingWhitespace: true);
+ /*
+ *
+ *
+ ┌─────────────────────────────────────────────────────────────────────────┐
+ │/demo/ │
+ │⟦▲⟧ │
+ │┌────────────┬──────────┬──────────────────────────────┬────────────────┐│
+ ││Filename (▲)│Size │Modified │Type ││
+ │├────────────┼──────────┼──────────────────────────────┼────────────────┤│
+ ││.. │ │ │ ││
+ ││/subfolder │ │2002-01-01T22:42:10 │ ││
+ ││image.gif │4.00 B │2002-01-01T22:42:10 │.gif ││
+ ││jQuery.js │7.00 B │2001-01-01T11:44:42 │.js ││
+ │ │
+ │ │
+ │ │
+ │⟦ ►► ⟧ Enter Search ⟦► OK ◄⟧ ⟦ Cancel ⟧ │
+ └─────────────────────────────────────────────────────────────────────────┘
+
+ *
+ */
+
+ var path = GetTextField (fd, FileDialogPart.Path);
+ Assert.Equal ("/demo/", path.Text);
+
+ var tv = GetTableView (fd);
+
+ // Asserting the headers
+ Assert.Equal ("Filename (▲)", tv.Table.ColumnNames.ElementAt (0));
+ Assert.Equal ("Size", tv.Table.ColumnNames.ElementAt (1));
+ Assert.Equal ("Modified", tv.Table.ColumnNames.ElementAt (2));
+ Assert.Equal ("Type", tv.Table.ColumnNames.ElementAt (3));
+
+ // Asserting the table contents
+ Assert.Equal ("..", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [0, 0]));
+ Assert.Equal ("/subfolder", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [1, 0]));
+ Assert.Equal ("image.gif", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [2, 0]));
+ Assert.Equal ("jQuery.js", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [3, 0]));
+
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [0, 1]));
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [1, 1]));
+ Assert.Equal ("4.00 B", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [2, 1]));
+ Assert.Equal ("7.00 B", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [3, 1]));
+
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [0, 2]));
+ Assert.Equal ("2002-01-01T22:42:10", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [1, 2]));
+ Assert.Equal ("2002-01-01T22:42:10", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [2, 2]));
+ Assert.Equal ("2001-01-01T11:44:42", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [3, 2]));
+
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [0, 3]));
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [1, 3]));
+ Assert.Equal (".gif", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [2, 3]));
+ Assert.Equal (".js", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [3, 3]));
+
fd.Dispose ();
}
@@ -479,45 +504,64 @@ public class FileDialogTests (ITestOutputHelper output)
fd.Draw ();
- var expected =
- @$"
-┌─────────────────────────────────────────────────────────────────────────┐
-│c:\demo\ │
-│{
- CM.Glyphs.LeftBracket
-}▲{
- CM.Glyphs.RightBracket
-} │
-│┌────────────┬──────────┬──────────────────────────────┬────────────────┐│
-││Filename (▲)│Size │Modified │Type ││
-│├────────────┼──────────┼──────────────────────────────┼────────────────┤│
-││.. │ │ │ ││
-││\subfolder │ │2002-01-01T22:42:10 │ ││
-││image.gif │4.00 B │2002-01-01T22:42:10 │.gif ││
-││jQuery.js │7.00 B │2001-01-01T11:44:42 │.js ││
-││mybinary.exe│7.00 B │2001-01-01T11:44:42 │.exe ││
-│ │
-│ │
-│{
- CM.Glyphs.LeftBracket
-} ►► {
- CM.Glyphs.RightBracket
-} Enter Search {
- CM.Glyphs.LeftBracket
-}{
- CM.Glyphs.LeftDefaultIndicator
-} OK {
- CM.Glyphs.RightDefaultIndicator
-}{
- CM.Glyphs.RightBracket
-} {
- CM.Glyphs.LeftBracket
-} Cancel {
- CM.Glyphs.RightBracket
-} │
-└─────────────────────────────────────────────────────────────────────────┘
-";
- TestHelpers.AssertDriverContentsAre (expected, output, ignoreLeadingWhitespace: true);
+ /*
+ *
+ *
+ ┌─────────────────────────────────────────────────────────────────────────┐
+ │c:\demo\ │
+ │⟦▲⟧ │
+ │┌────────────┬──────────┬──────────────────────────────┬────────────────┐│
+ ││Filename (▲)│Size │Modified │Type ││
+ │├────────────┼──────────┼──────────────────────────────┼────────────────┤│
+ ││.. │ │ │ ││
+ ││\subfolder │ │2002-01-01T22:42:10 │ ││
+ ││image.gif │4.00 B │2002-01-01T22:42:10 │.gif ││
+ ││jQuery.js │7.00 B │2001-01-01T11:44:42 │.js ││
+ ││mybinary.exe│7.00 B │2001-01-01T11:44:42 │.exe ││
+ │ │
+ │ │
+ │⟦ ►► ⟧ Enter Search ⟦► OK ◄⟧ ⟦ Cancel ⟧ │
+ └─────────────────────────────────────────────────────────────────────────┘
+
+ *
+ */
+
+ var path = GetTextField (fd, FileDialogPart.Path);
+ Assert.Equal ("c:\\demo\\",path.Text);
+
+ var tv = GetTableView (fd);
+
+ // Asserting the headers
+ Assert.Equal ("Filename (▲)", tv.Table.ColumnNames.ElementAt (0));
+ Assert.Equal ("Size", tv.Table.ColumnNames.ElementAt (1));
+ Assert.Equal ("Modified", tv.Table.ColumnNames.ElementAt (2));
+ Assert.Equal ("Type", tv.Table.ColumnNames.ElementAt (3));
+
+ // Asserting the table contents
+ Assert.Equal ("..", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [0, 0]));
+ Assert.Equal (@"\subfolder", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [1, 0]));
+ Assert.Equal ("image.gif", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [2, 0]));
+ Assert.Equal ("jQuery.js", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [3, 0]));
+ Assert.Equal ("mybinary.exe", tv.Style.GetOrCreateColumnStyle (0).GetRepresentation (tv.Table [4, 0]));
+
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [0, 1]));
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [1, 1]));
+ Assert.Equal ("4.00 B", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [2, 1]));
+ Assert.Equal ("7.00 B", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [3, 1]));
+ Assert.Equal ("7.00 B", tv.Style.GetOrCreateColumnStyle (1).GetRepresentation (tv.Table [4, 1]));
+
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [0, 2]));
+ Assert.Equal ("2002-01-01T22:42:10", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [1, 2]));
+ Assert.Equal ("2002-01-01T22:42:10", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [2, 2]));
+ Assert.Equal ("2001-01-01T11:44:42", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [3, 2]));
+ Assert.Equal ("2001-01-01T11:44:42", tv.Style.GetOrCreateColumnStyle (2).GetRepresentation (tv.Table [4, 2]));
+
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [0, 3]));
+ Assert.Equal ("", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [1, 3]));
+ Assert.Equal (".gif", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [2, 3]));
+ Assert.Equal (".js", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [3, 3]));
+ Assert.Equal (".exe", tv.Style.GetOrCreateColumnStyle (3).GetRepresentation (tv.Table [4, 3]));
+
fd.Dispose ();
}
@@ -734,4 +778,30 @@ public class FileDialogTests (ITestOutputHelper output)
Send ('\\', ConsoleKey.Separator);
}
}
+
+ private TextField GetTextField (FileDialog dlg, FileDialogPart part)
+ {
+ switch (part)
+ {
+ case FileDialogPart.Path:
+ return dlg.Subviews.OfType ().ElementAt (0);
+ case FileDialogPart.SearchField:
+ return dlg.Subviews.OfType ().ElementAt (1);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException (nameof (part), part, null);
+ }
+ }
+
+ private TableView GetTableView (FileDialog dlg)
+ {
+ var tile = dlg.Subviews.OfType ().Single ();
+ return (TableView)tile.Tiles.ElementAt (1).ContentView.Subviews.ElementAt(0);
+ }
+
+ private enum FileDialogPart
+ {
+ Path,
+ SearchField,
+ }
}
diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs
index f5d03f7bc..33e3ba6f3 100644
--- a/UnitTests/Views/TableViewTests.cs
+++ b/UnitTests/Views/TableViewTests.cs
@@ -1068,7 +1068,7 @@ public class TableViewTests (ITestOutputHelper output)
Application.Begin (top);
tv.HasFocus = focused;
- Assert.Equal(focused, tv.HasFocus);
+ Assert.Equal (focused, tv.HasFocus);
tv.Draw ();
@@ -1155,7 +1155,7 @@ public class TableViewTests (ITestOutputHelper output)
// when B is 2 use the custom highlight color for the row
tv.Style.RowColorGetter += e => Convert.ToInt32 (e.Table [e.RowIndex, 1]) == 2 ? rowHighlight : null;
-
+
var top = new Toplevel ();
top.Add (tv);
Application.Begin (top);
@@ -3169,7 +3169,7 @@ A B C
}
[Fact]
- public void TestDataColumnCaption()
+ public void TestDataColumnCaption ()
{
var tableView = new TableView ();
@@ -3191,6 +3191,175 @@ A B C
Assert.Equal ("Column Name 2", cn [1]);
}
+
+ [Fact]
+ public void CanTabOutOfTableViewUsingCursor_Left ()
+ {
+ GetTableViewWithSiblings (out var tf1, out var tableView, out var tf2);
+
+ // Make the selected cell one in
+ tableView.SelectedColumn = 1;
+
+ // Pressing left should move us to the first column without changing focus
+ Application.OnKeyDown (Key.CursorLeft);
+ Assert.Same (tableView, Application.Current.MostFocused);
+ Assert.True (tableView.HasFocus);
+
+ // Because we are now on the leftmost cell a further left press should move focus
+ Application.OnKeyDown (Key.CursorLeft);
+
+ Assert.NotSame (tableView, Application.Current.MostFocused);
+ Assert.False (tableView.HasFocus);
+
+ Assert.Same (tf1, Application.Current.MostFocused);
+ Assert.True (tf1.HasFocus);
+
+ Application.Current.Dispose ();
+ }
+
+ [Fact]
+ public void CanTabOutOfTableViewUsingCursor_Up ()
+ {
+ GetTableViewWithSiblings (out var tf1, out var tableView, out var tf2);
+
+ // Make the selected cell one in
+ tableView.SelectedRow = 1;
+
+ // First press should move us up
+ Application.OnKeyDown (Key.CursorUp);
+ Assert.Same (tableView, Application.Current.MostFocused);
+ Assert.True (tableView.HasFocus);
+
+ // Because we are now on the top row a further press should move focus
+ Application.OnKeyDown (Key.CursorUp);
+
+ Assert.NotSame (tableView, Application.Current.MostFocused);
+ Assert.False (tableView.HasFocus);
+
+ Assert.Same (tf1, Application.Current.MostFocused);
+ Assert.True (tf1.HasFocus);
+
+ Application.Current.Dispose ();
+ }
+ [Fact]
+ public void CanTabOutOfTableViewUsingCursor_Right ()
+ {
+ GetTableViewWithSiblings (out var tf1, out var tableView, out var tf2);
+
+ // Make the selected cell one in from the rightmost column
+ tableView.SelectedColumn = tableView.Table.Columns - 2;
+
+ // First press should move us to the rightmost column without changing focus
+ Application.OnKeyDown (Key.CursorRight);
+ Assert.Same (tableView, Application.Current.MostFocused);
+ Assert.True (tableView.HasFocus);
+
+ // Because we are now on the rightmost cell, a further right press should move focus
+ Application.OnKeyDown (Key.CursorRight);
+
+ Assert.NotSame (tableView, Application.Current.MostFocused);
+ Assert.False (tableView.HasFocus);
+
+ Assert.Same (tf2, Application.Current.MostFocused);
+ Assert.True (tf2.HasFocus);
+
+ Application.Current.Dispose ();
+ }
+
+ [Fact]
+ public void CanTabOutOfTableViewUsingCursor_Down ()
+ {
+ GetTableViewWithSiblings (out var tf1, out var tableView, out var tf2);
+
+ // Make the selected cell one in from the bottommost row
+ tableView.SelectedRow = tableView.Table.Rows - 2;
+
+ // First press should move us to the bottommost row without changing focus
+ Application.OnKeyDown (Key.CursorDown);
+ Assert.Same (tableView, Application.Current.MostFocused);
+ Assert.True (tableView.HasFocus);
+
+ // Because we are now on the bottommost cell, a further down press should move focus
+ Application.OnKeyDown (Key.CursorDown);
+
+ Assert.NotSame (tableView, Application.Current.MostFocused);
+ Assert.False (tableView.HasFocus);
+
+ Assert.Same (tf2, Application.Current.MostFocused);
+ Assert.True (tf2.HasFocus);
+
+ Application.Current.Dispose ();
+ }
+
+
+ [Fact]
+ public void CanTabOutOfTableViewUsingCursor_Left_ClearsSelectionFirst ()
+ {
+ GetTableViewWithSiblings (out var tf1, out var tableView, out var tf2);
+
+ // Make the selected cell one in
+ tableView.SelectedColumn = 1;
+
+ // Pressing shift-left should give us a multi selection
+ Application.OnKeyDown (Key.CursorLeft.WithShift);
+ Assert.Same (tableView, Application.Current.MostFocused);
+ Assert.True (tableView.HasFocus);
+ Assert.Equal (2, tableView.GetAllSelectedCells ().Count ());
+
+ // Because we are now on the leftmost cell a further left press would normally move focus
+ // However there is an ongoing selection so instead the operation clears the selection and
+ // gets swallowed (not resulting in a focus change)
+ Application.OnKeyDown (Key.CursorLeft);
+
+ // Selection 'clears' just to the single cell and we remain focused
+ Assert.Single (tableView.GetAllSelectedCells ());
+ Assert.Same (tableView, Application.Current.MostFocused);
+ Assert.True (tableView.HasFocus);
+
+ // A further left will switch focus
+ Application.OnKeyDown (Key.CursorLeft);
+
+ Assert.NotSame (tableView, Application.Current.MostFocused);
+ Assert.False (tableView.HasFocus);
+
+ Assert.Same (tf1, Application.Current.MostFocused);
+ Assert.True (tf1.HasFocus);
+
+ Application.Current.Dispose ();
+ }
+
+ ///
+ /// Creates 3 views on with the focus in the
+ /// . This is a helper method to setup tests that want to
+ /// explore moving input focus out of a tableview.
+ ///
+ ///
+ ///
+ ///
+ private void GetTableViewWithSiblings (out TextField tf1, out TableView tableView, out TextField tf2)
+ {
+ tableView = new TableView ();
+ tableView.BeginInit ();
+ tableView.EndInit ();
+
+ Application.Navigation = new ();
+ Application.Current = new ();
+ tf1 = new TextField ();
+ tf2 = new TextField ();
+ Application.Current.Add (tf1);
+ Application.Current.Add (tableView);
+ Application.Current.Add (tf2);
+
+ tableView.SetFocus ();
+
+ Assert.Same (tableView, Application.Current.MostFocused);
+ Assert.True (tableView.HasFocus);
+
+
+ // Set big table
+ tableView.Table = BuildTable (25, 50);
+ }
+
private TableView GetABCDEFTableView (out DataTable dt)
{
var tableView = new TableView ();