소스 검색

Merge pull request #2260 from tznind/table-multi-select

Fixes #1842 - Add support for Ctrl/Alt + click to TableView
Tig 2 년 전
부모
커밋
b213afca41
2개의 변경된 파일127개의 추가작업 그리고 1개의 파일을 삭제
  1. 58 1
      Terminal.Gui/Views/TableView.cs
  2. 69 0
      UnitTests/TableViewTests.cs

+ 58 - 1
Terminal.Gui/Views/TableView.cs

@@ -761,6 +761,41 @@ namespace Terminal.Gui {
 			SelectedRow = row;
 		}
 
+		/// <summary>
+		/// Unions the current selected cell (and/or regions) with the provided cell and makes
+		/// it the active one.
+		/// </summary>
+		/// <param name="col"></param>
+		/// <param name="row"></param>
+		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)
+					);
+			}
+		}
+
+
 		/// <summary>
 		/// Moves the <see cref="SelectedRow"/> and <see cref="SelectedColumn"/> by the provided offsets. Optionally starting a box selection (see <see cref="MultiSelect"/>)
 		/// </summary>
@@ -852,6 +887,8 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Returns all cells in any <see cref="MultiSelectedRegions"/> (if <see cref="MultiSelect"/> is enabled) and the selected cell
 		/// </summary>
+		/// <remarks>Return value is not affected by <see cref="FullRowSelect"/> (i.e. returned <see cref="Point"/>s are not expanded to 
+		/// include all points on row).</remarks>
 		/// <returns></returns>
 		public IEnumerable<Point> 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));
 		}
 
+		/// <summary>
+		/// Returns a single point as a <see cref="TableSelection"/>
+		/// </summary>
+		/// <param name="x"></param>
+		/// <param name="y"></param>
+		/// <returns></returns>
+		private TableSelection CreateTableSelection (int x, int y)
+		{
+			return CreateTableSelection (x, y, x, y);
+		}
 		/// <summary>
 		/// <para>
 		/// Returns true if the given cell is selected either because it is the active cell or part of a multi cell selection (e.g. <see cref="FullRowSelect"/>).
@@ -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);
+		}
+
 		/// <summary>.
 		/// Returns the column and row of <see cref="Table"/> that corresponds to a given point 
 		/// on the screen (relative to the control client area).  Returns null if the point is

+ 69 - 0
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)]