From 8dd77f249dd180f18cee6394cc1c9393805b619b Mon Sep 17 00:00:00 2001 From: tznind Date: Fri, 30 Dec 2022 09:55:05 +0000 Subject: [PATCH 1/2] Add support for Ctrl/Alt + click to TableView --- Terminal.Gui/Views/TableView.cs | 57 ++++++++++++++++++++++++++- UnitTests/TableViewTests.cs | 69 +++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TableView.cs b/Terminal.Gui/Views/TableView.cs index 0c04d5061..2a4735ceb 100644 --- a/Terminal.Gui/Views/TableView.cs +++ b/Terminal.Gui/Views/TableView.cs @@ -761,6 +761,39 @@ namespace Terminal.Gui { SelectedRow = row; } + /// + /// Unions the current selected cell (and/or regions) with the provided cell and makes + /// it the active one. + /// + /// + /// + private void UnionSelection (int col, int row) + { + if(!MultiSelect) { + return; + } + + var oldColumn = SelectedColumn; + var oldRow = SelectedRow; + + // move us to the new cell + SelectedColumn = col; + SelectedRow = row; + MultiSelectedRegions.Push ( + CreateTableSelection (col, row) + ); + + // if the old cell was not part of a rectangular select + // or otherwise selected we need to retain it in the selection + + if (!IsSelected (oldColumn, oldRow)) { + MultiSelectedRegions.Push ( + CreateTableSelection (oldColumn, oldRow) + ); + } + } + + /// /// Moves the and by the provided offsets. Optionally starting a box selection (see ) /// @@ -852,6 +885,8 @@ namespace Terminal.Gui { /// /// Returns all cells in any (if is enabled) and the selected cell /// + /// Return value is not affected by (i.e. returned s are not expanded to + /// include all points on row). /// public IEnumerable GetAllSelectedCells () { @@ -914,6 +949,16 @@ namespace Terminal.Gui { return new TableSelection (new Point (pt1X, pt1Y), new Rect (left, top, right - left + 1, bot - top + 1)); } + /// + /// Returns a single point as a + /// + /// + /// + /// + private TableSelection CreateTableSelection (int x, int y) + { + return CreateTableSelection (x, y, x, y); + } /// /// /// Returns true if the given cell is selected either because it is the active cell or part of a multi cell selection (e.g. ). @@ -1039,7 +1084,12 @@ namespace Terminal.Gui { var hit = ScreenToCell (me.X, me.Y); if (hit != null) { - SetSelection (hit.Value.X, hit.Value.Y, me.Flags.HasFlag (MouseFlags.ButtonShift)); + if(MultiSelect && HasControlOrAlt(me)) { + UnionSelection(hit.Value.X, hit.Value.Y); + } else { + SetSelection (hit.Value.X, hit.Value.Y, me.Flags.HasFlag (MouseFlags.ButtonShift)); + } + Update (); } } @@ -1055,6 +1105,11 @@ namespace Terminal.Gui { return false; } + private bool HasControlOrAlt (MouseEvent me) + { + return me.Flags.HasFlag (MouseFlags.ButtonAlt) || me.Flags.HasFlag (MouseFlags.ButtonCtrl); + } + /// . /// Returns the column and row of that corresponds to a given point /// on the screen (relative to the control client area). Returns null if the point is diff --git a/UnitTests/TableViewTests.cs b/UnitTests/TableViewTests.cs index f29bfd25d..3ef1bfee7 100644 --- a/UnitTests/TableViewTests.cs +++ b/UnitTests/TableViewTests.cs @@ -646,6 +646,75 @@ namespace Terminal.Gui.Views { Application.Shutdown (); } + [Fact, AutoInitShutdown] + public void TestShiftClick_MultiSelect_TwoRowTable_FullRowSelect() + { + var tv = GetTwoRowSixColumnTable (); + + tv.MultiSelect = true; + + // Clicking in bottom row + tv.MouseEvent (new MouseEvent { + X = 1, + Y = 3, + Flags = MouseFlags.Button1Clicked + }); + + // should select that row + Assert.Equal (1, tv.SelectedRow); + + // shift clicking top row + tv.MouseEvent (new MouseEvent { + X = 1, + Y = 2, + Flags = MouseFlags.Button1Clicked | MouseFlags.ButtonShift + }); + + // should extend the selection + Assert.Equal (0, tv.SelectedRow); + + var selected = tv.GetAllSelectedCells ().ToArray(); + + Assert.Contains (new Point(0,0), selected); + Assert.Contains (new Point (0, 1), selected); + } + + [Fact, AutoInitShutdown] + public void TestControlClick_MultiSelect_ThreeRowTable_FullRowSelect () + { + var tv = GetTwoRowSixColumnTable (); + tv.Table.Rows.Add (1, 2, 3, 4, 5, 6); + + tv.MultiSelect = true; + + // Clicking in bottom row + tv.MouseEvent (new MouseEvent { + X = 1, + Y = 4, + Flags = MouseFlags.Button1Clicked + }); + + // should select that row + Assert.Equal (2, tv.SelectedRow); + + // shift clicking top row + tv.MouseEvent (new MouseEvent { + X = 1, + Y = 2, + Flags = MouseFlags.Button1Clicked | MouseFlags.ButtonCtrl + }); + + // should extend the selection + // to include bottom and top row but not middle + Assert.Equal (0, tv.SelectedRow); + + var selected = tv.GetAllSelectedCells ().ToArray (); + + Assert.Contains (new Point (0, 0), selected); + Assert.DoesNotContain (new Point (0, 1), selected); + Assert.Contains (new Point (0, 2), selected); + } + [Theory] [InlineData (false)] [InlineData (true)] From c3cebbdd3ab986eeb6ed14261e63f57ec00b54f1 Mon Sep 17 00:00:00 2001 From: tznind Date: Fri, 30 Dec 2022 10:15:00 +0000 Subject: [PATCH 2/2] Add additional guards to UnionSelection method --- Terminal.Gui/Views/TableView.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TableView.cs b/Terminal.Gui/Views/TableView.cs index 2a4735ceb..5a674f3e7 100644 --- a/Terminal.Gui/Views/TableView.cs +++ b/Terminal.Gui/Views/TableView.cs @@ -769,9 +769,11 @@ namespace Terminal.Gui { /// private void UnionSelection (int col, int row) { - if(!MultiSelect) { + if (!MultiSelect || TableIsNullOrInvisible()) { return; } + + EnsureValidSelection (); var oldColumn = SelectedColumn; var oldRow = SelectedRow;