diff --git a/Terminal.Gui/Views/TableView.cs b/Terminal.Gui/Views/TableView.cs
index 0c04d5061..5a674f3e7 100644
--- a/Terminal.Gui/Views/TableView.cs
+++ b/Terminal.Gui/Views/TableView.cs
@@ -761,6 +761,41 @@ 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 || TableIsNullOrInvisible()) {
+ return;
+ }
+
+ EnsureValidSelection ();
+
+ 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 +887,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 +951,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 +1086,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 +1107,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)]