Răsfoiți Sursa

Merge branch 'v2' of tig:gui-cs/Terminal.Gui into v2

Tigger Kindel 2 ani în urmă
părinte
comite
35fc1411e7

+ 209 - 132
Terminal.Gui/Views/TableView.cs

@@ -99,7 +99,7 @@ namespace Terminal.Gui {
 		/// When <see cref="MultiSelect"/> is enabled this property contain all rectangles of selected cells.  Rectangles describe column/rows selected in <see cref="Table"/> (not screen coordinates)
 		/// </summary>
 		/// <returns></returns>
-		public Stack<TableSelection> MultiSelectedRegions { get; } = new Stack<TableSelection> ();
+		public Stack<TableSelection> MultiSelectedRegions { get; private set; } = new Stack<TableSelection> ();
 
 		/// <summary>
 		/// Horizontal scroll offset.  The index of the first column in <see cref="Table"/> to display when when rendering the view.
@@ -109,7 +109,7 @@ namespace Terminal.Gui {
 			get => columnOffset;
 
 			//try to prevent this being set to an out of bounds column
-			set => columnOffset = TableIsNullOrInvisible() ? 0 : Math.Max (0, Math.Min (Table.Columns.Count - 1, value));
+			set => columnOffset = TableIsNullOrInvisible () ? 0 : Math.Max (0, Math.Min (Table.Columns.Count - 1, value));
 		}
 
 		/// <summary>
@@ -186,7 +186,7 @@ namespace Terminal.Gui {
 			set {
 				if (cellActivationKey != value) {
 					ReplaceKeyBinding (cellActivationKey, value);
-					
+
 					// of API user is mixing and matching old and new methods of keybinding then they may have lost
 					// the old binding (e.g. with ClearKeybindings) so ReplaceKeyBinding alone will fail
 					AddKeyBinding (value, Command.Accept);
@@ -218,9 +218,9 @@ namespace Terminal.Gui {
 			AddCommand (Command.LineDown, () => { ChangeSelectionByOffset (0, 1, false); return true; });
 			AddCommand (Command.PageUp, () => { PageUp (false); return true; });
 			AddCommand (Command.PageDown, () => { PageDown (false); return true; });
-			AddCommand (Command.LeftHome, () => { ChangeSelectionToStartOfRow (false);  return true; });
+			AddCommand (Command.LeftHome, () => { ChangeSelectionToStartOfRow (false); return true; });
 			AddCommand (Command.RightEnd, () => { ChangeSelectionToEndOfRow (false); return true; });
-			AddCommand (Command.TopHome, () => { ChangeSelectionToStartOfTable(false); return true; });
+			AddCommand (Command.TopHome, () => { ChangeSelectionToStartOfTable (false); return true; });
 			AddCommand (Command.BottomEnd, () => { ChangeSelectionToEndOfTable (false); return true; });
 
 			AddCommand (Command.RightExtend, () => { ChangeSelectionByOffset (1, 0, true); return true; });
@@ -234,8 +234,10 @@ namespace Terminal.Gui {
 			AddCommand (Command.TopHomeExtend, () => { ChangeSelectionToStartOfTable (true); return true; });
 			AddCommand (Command.BottomEndExtend, () => { ChangeSelectionToEndOfTable (true); return true; });
 
-			AddCommand (Command.SelectAll, () => { SelectAll(); return true; });
-			AddCommand (Command.Accept, () => { OnCellActivated(new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow)); return true; });
+			AddCommand (Command.SelectAll, () => { SelectAll (); return true; });
+			AddCommand (Command.Accept, () => { OnCellActivated (new CellActivatedEventArgs (Table, SelectedColumn, SelectedRow)); return true; });
+
+			AddCommand (Command.ToggleChecked, () => { ToggleCurrentCellSelection (); return true; });
 
 			// Default keybindings for this view
 			AddKeyBinding (Key.CursorLeft, Command.Left);
@@ -252,7 +254,7 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.CursorLeft | Key.ShiftMask, Command.LeftExtend);
 			AddKeyBinding (Key.CursorRight | Key.ShiftMask, Command.RightExtend);
 			AddKeyBinding (Key.CursorUp | Key.ShiftMask, Command.LineUpExtend);
-			AddKeyBinding (Key.CursorDown| Key.ShiftMask, Command.LineDownExtend);
+			AddKeyBinding (Key.CursorDown | Key.ShiftMask, Command.LineDownExtend);
 			AddKeyBinding (Key.PageUp | Key.ShiftMask, Command.PageUpExtend);
 			AddKeyBinding (Key.PageDown | Key.ShiftMask, Command.PageDownExtend);
 			AddKeyBinding (Key.Home | Key.ShiftMask, Command.LeftHomeExtend);
@@ -264,33 +266,34 @@ namespace Terminal.Gui {
 			AddKeyBinding (CellActivationKey, Command.Accept);
 		}
 
+
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
-			{
-				Move (0, 0);
-				var frame = Frame;
+		{
+			Move (0, 0);
+			var frame = Frame;
 
-				scrollRightPoint = null;
-				scrollLeftPoint = null;
+			scrollRightPoint = null;
+			scrollLeftPoint = null;
 
-				// What columns to render at what X offset in viewport
-				var columnsToRender = CalculateViewport (bounds).ToArray ();
+			// What columns to render at what X offset in viewport
+			var columnsToRender = CalculateViewport (bounds).ToArray ();
 
-				Driver.SetAttribute (GetNormalColor ());
+			Driver.SetAttribute (GetNormalColor ());
 
-				//invalidate current row (prevents scrolling around leaving old characters in the frame
-				Driver.AddStr (new string (' ', bounds.Width));
+			//invalidate current row (prevents scrolling around leaving old characters in the frame
+			Driver.AddStr (new string (' ', bounds.Width));
 
-				int line = 0;
+			int line = 0;
 
-				if (ShouldRenderHeaders ()) {
-					// Render something like:
-					/*
-						┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
-						│ArithmeticComparator│chi       │Healthboard│Interpretation│Labnumber│
-						└────────────────────┴──────────┴───────────┴──────────────┴─────────┘
-					*/
-			if (Style.ShowHorizontalHeaderOverline) {
+			if (ShouldRenderHeaders ()) {
+				// Render something like:
+				/*
+					┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
+					│ArithmeticComparator│chi       │Healthboard│Interpretation│Labnumber│
+					└────────────────────┴──────────┴───────────┴──────────────┴─────────┘
+				*/
+				if (Style.ShowHorizontalHeaderOverline) {
 					RenderHeaderOverline (line, bounds.Width, columnsToRender);
 					line++;
 				}
@@ -436,19 +439,19 @@ namespace Terminal.Gui {
 			bool moreColumnsToLeft = ColumnOffset > 0;
 
 			// if we moved left would we find a new column (or are they all invisible?)
-			if(!TryGetNearestVisibleColumn (ColumnOffset-1, false, false, out _)) {
+			if (!TryGetNearestVisibleColumn (ColumnOffset - 1, false, false, out _)) {
 				moreColumnsToLeft = false;
 			}
 
 			// are there visible columns to the right that have not yet been reached?
 			// lets find out, what is the column index of the last column we are rendering
 			int lastColumnIdxRendered = ColumnOffset + columnsToRender.Length - 1;
-			
+
 			// are there more valid indexes?
 			bool moreColumnsToRight = lastColumnIdxRendered < Table.Columns.Count;
 
 			// if we went right from the last column would we find a new visible column?
-			if(!TryGetNearestVisibleColumn (lastColumnIdxRendered + 1, true, false, out _)) {
+			if (!TryGetNearestVisibleColumn (lastColumnIdxRendered + 1, true, false, out _)) {
 				// no we would not
 				moreColumnsToRight = false;
 			}
@@ -466,7 +469,7 @@ namespace Terminal.Gui {
 				// whole way but update to instead draw a header indicator
 				// or scroll arrow etc
 				var rune = Driver.HLine;
-				
+
 				if (Style.ShowVerticalHeaderLines) {
 					if (c == 0) {
 						// for first character render line
@@ -475,12 +478,11 @@ namespace Terminal.Gui {
 						// unless we have horizontally scrolled along
 						// in which case render an arrow, to indicate user
 						// can scroll left
-						if(Style.ShowHorizontalScrollIndicators && moreColumnsToLeft)
-						{
+						if (Style.ShowHorizontalScrollIndicators && moreColumnsToLeft) {
 							rune = Driver.LeftArrow;
-							scrollLeftPoint = new Point(c,row);
+							scrollLeftPoint = new Point (c, row);
 						}
-							
+
 					}
 					// if the next column is the start of a header
 					else if (columnsToRender.Any (r => r.X == c + 1)) {
@@ -495,10 +497,9 @@ namespace Terminal.Gui {
 						// unless there is more of the table we could horizontally
 						// scroll along to see. In which case render an arrow,
 						// to indicate user can scroll right
-						if(Style.ShowHorizontalScrollIndicators && moreColumnsToRight)
-						{
+						if (Style.ShowHorizontalScrollIndicators && moreColumnsToRight) {
 							rune = Driver.RightArrow;
-							scrollRightPoint = new Point(c,row);
+							scrollRightPoint = new Point (c, row);
 						}
 
 					}
@@ -518,7 +519,7 @@ namespace Terminal.Gui {
 			var focused = HasFocus;
 
 			var rowScheme = (Style.RowColorGetter?.Invoke (
-				new RowColorGetterArgs(Table,rowToRender))) ?? ColorScheme;
+				new RowColorGetterArgs (Table, rowToRender))) ?? ColorScheme;
 
 			//render start of line
 			if (style.ShowVerticalCellLines)
@@ -529,11 +530,9 @@ namespace Terminal.Gui {
 
 			Attribute color;
 
-			if(FullRowSelect && IsSelected (0, rowToRender)) {
+			if (FullRowSelect && IsSelected (0, rowToRender)) {
 				color = focused ? rowScheme.HotFocus : rowScheme.HotNormal;
-			}
-			else 
-			{
+			} else {
 				color = Enabled ? rowScheme.Normal : rowScheme.Disabled;
 			}
 
@@ -562,17 +561,16 @@ namespace Terminal.Gui {
 				var colorSchemeGetter = colStyle?.ColorGetter;
 
 				ColorScheme scheme;
-				if(colorSchemeGetter != null) {
+				if (colorSchemeGetter != null) {
 					// user has a delegate for defining row color per cell, call it
-					scheme = colorSchemeGetter(
-						new CellColorGetterArgs (Table, rowToRender, current.Column.Ordinal, val, representation,rowScheme));
+					scheme = colorSchemeGetter (
+						new CellColorGetterArgs (Table, rowToRender, current.Column.Ordinal, val, representation, rowScheme));
 
 					// if users custom color getter returned null, use the row scheme
-					if(scheme == null) {
+					if (scheme == null) {
 						scheme = rowScheme;
 					}
-				}
-				else {
+				} else {
 					// There is no custom cell coloring delegate so use the scheme for the row
 					scheme = rowScheme;
 				}
@@ -588,16 +586,15 @@ namespace Terminal.Gui {
 
 				// While many cells can be selected (see MultiSelectedRegions) only one cell is the primary (drives navigation etc)
 				bool isPrimaryCell = current.Column.Ordinal == selectedColumn && rowToRender == selectedRow;
-				
-				RenderCell (cellColor,render,isPrimaryCell);
-								
+
+				RenderCell (cellColor, render, isPrimaryCell);
+
 				// Reset color scheme to normal for drawing separators if we drew text with custom scheme
 				if (scheme != rowScheme) {
 
-					if(isSelectedCell) {
+					if (isSelectedCell) {
 						color = focused ? rowScheme.HotFocus : rowScheme.HotNormal;
-					}
-					else {
+					} else {
 						color = Enabled ? rowScheme.Normal : rowScheme.Disabled;
 					}
 					Driver.SetAttribute (color);
@@ -629,7 +626,7 @@ namespace Terminal.Gui {
 		/// <param name="cellColor"></param>
 		/// <param name="render"></param>
 		/// <param name="isPrimaryCell"></param>
-		protected virtual void RenderCell (Attribute cellColor, string render,bool isPrimaryCell)
+		protected virtual void RenderCell (Attribute cellColor, string render, bool isPrimaryCell)
 		{
 			// If the cell is the selected col/row then draw the first rune in inverted colors
 			// this allows the user to track which cell is the active one during a multi cell
@@ -740,12 +737,15 @@ namespace Terminal.Gui {
 
 			col = GetNearestVisibleColumn (col, lookRight, true);
 
-			if (!MultiSelect || !extendExistingSelection)
-				MultiSelectedRegions.Clear ();
+			if (!MultiSelect || !extendExistingSelection) {
+				ClearMultiSelectedRegions (true);
+			}
+
 
 			if (extendExistingSelection) {
+
 				// If we are extending current selection but there isn't one
-				if (MultiSelectedRegions.Count == 0) {
+				if (MultiSelectedRegions.Count == 0 || MultiSelectedRegions.All(m=>m.IsToggled)) {
 					// Create a new region between the old active cell and the new cell
 					var rect = CreateTableSelection (SelectedColumn, SelectedRow, col, row);
 					MultiSelectedRegions.Push (rect);
@@ -761,6 +761,24 @@ namespace Terminal.Gui {
 			SelectedRow = row;
 		}
 
+		private void ClearMultiSelectedRegions (bool keepToggledSelections)
+		{
+			if (!keepToggledSelections) {
+				MultiSelectedRegions.Clear ();
+				return;
+			}
+
+			var oldRegions = MultiSelectedRegions.ToArray ().Reverse ();
+
+			MultiSelectedRegions.Clear ();
+
+			foreach (var region in oldRegions) {
+				if (region.IsToggled) {
+					MultiSelectedRegions.Push (region);
+				}
+			}
+		}
+
 		/// <summary>
 		/// Unions the current selected cell (and/or regions) with the provided cell and makes
 		/// it the active one.
@@ -769,10 +787,10 @@ namespace Terminal.Gui {
 		/// <param name="row"></param>
 		private void UnionSelection (int col, int row)
 		{
-			if (!MultiSelect || TableIsNullOrInvisible()) {
+			if (!MultiSelect || TableIsNullOrInvisible ()) {
 				return;
 			}
-			
+
 			EnsureValidSelection ();
 
 			var oldColumn = SelectedColumn;
@@ -812,7 +830,7 @@ namespace Terminal.Gui {
 		/// Moves the selection up by one page
 		/// </summary>
 		/// <param name="extend">true to extend the current selection (if any) instead of replacing</param>
-		public void PageUp(bool extend)
+		public void PageUp (bool extend)
 		{
 			ChangeSelectionByOffset (0, -(Bounds.Height - GetHeaderHeightIfAny ()), extend);
 			Update ();
@@ -822,7 +840,7 @@ namespace Terminal.Gui {
 		/// Moves the selection down by one page
 		/// </summary>
 		/// <param name="extend">true to extend the current selection (if any) instead of replacing</param>
-		public void PageDown(bool extend)
+		public void PageDown (bool extend)
 		{
 			ChangeSelectionByOffset (0, Bounds.Height - GetHeaderHeightIfAny (), extend);
 			Update ();
@@ -846,7 +864,7 @@ namespace Terminal.Gui {
 		/// to (<see cref="SelectedColumn"/>,nY) i.e. no horizontal scrolling.
 		/// </summary>
 		/// <param name="extend">true to extend the current selection (if any) instead of replacing</param>
-		public void ChangeSelectionToEndOfTable(bool extend)
+		public void ChangeSelectionToEndOfTable (bool extend)
 		{
 			var finalColumn = Table.Columns.Count - 1;
 
@@ -880,10 +898,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void SelectAll ()
 		{
-			if (TableIsNullOrInvisible() || !MultiSelect || Table.Rows.Count == 0)
+			if (TableIsNullOrInvisible () || !MultiSelect || Table.Rows.Count == 0)
 				return;
 
-			MultiSelectedRegions.Clear ();
+			ClearMultiSelectedRegions (true);
 
 			// Create a single region over entire table, set the origin of the selection to the active cell so that a followup spread selection e.g. shift-right behaves properly
 			MultiSelectedRegions.Push (new TableSelection (new Point (SelectedColumn, SelectedRow), new Rect (0, 0, Table.Columns.Count, table.Rows.Count)));
@@ -893,16 +911,18 @@ 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 ()
 		{
 			if (TableIsNullOrInvisible () || Table.Rows.Count == 0)
-				yield break;
+			{
+				return Enumerable.Empty<Point>();				
+			}
 
 			EnsureValidSelection ();
 
+			var toReturn = new HashSet<Point>();
+
 			// If there are one or more rectangular selections
 			if (MultiSelect && MultiSelectedRegions.Any ()) {
 
@@ -916,25 +936,27 @@ namespace Terminal.Gui {
 				for (int y = yMin; y < yMax; y++) {
 					for (int x = xMin; x < xMax; x++) {
 						if (IsSelected (x, y)) {
-							yield return new Point (x, y);
+							toReturn.Add(new Point (x, y));
 						}
 					}
 				}
-			} else {
+			} 
 
-				// if there are no region selections then it is just the active cell
 
-				// if we are selecting the full row
-				if (FullRowSelect) {
-					// all cells in active row are selected
-					for (int x = 0; x < Table.Columns.Count; x++) {
-						yield return new Point (x, SelectedRow);
-					}
-				} else {
-					// Not full row select and no multi selections
-					yield return new Point (SelectedColumn, SelectedRow);
+			// if there are no region selections then it is just the active cell
+
+			// if we are selecting the full row
+			if (FullRowSelect) {
+				// all cells in active row are selected
+				for (int x = 0; x < Table.Columns.Count; x++) {
+					toReturn.Add(new Point (x, SelectedRow));
 				}
+			} else {
+				// Not full row select and no multi selections
+				toReturn.Add(new Point (SelectedColumn, SelectedRow));
 			}
+
+			return toReturn;		
 		}
 
 		/// <summary>
@@ -944,17 +966,60 @@ namespace Terminal.Gui {
 		/// <param name="pt1Y">Origin point for the selection in Y</param>
 		/// <param name="pt2X">End point for the selection in X</param>
 		/// <param name="pt2Y">End point for the selection in Y</param>
+		/// <param name="toggle">True if selection is result of <see cref="Command.ToggleChecked"/></param>
 		/// <returns></returns>
-		private TableSelection CreateTableSelection (int pt1X, int pt1Y, int pt2X, int pt2Y)
+		private TableSelection CreateTableSelection (int pt1X, int pt1Y, int pt2X, int pt2Y, bool toggle = false)
 		{
-			var top = Math.Max(Math.Min (pt1Y, pt2Y), 0);
-			var bot = Math.Max(Math.Max (pt1Y, pt2Y), 0);
+			var top = Math.Max (Math.Min (pt1Y, pt2Y), 0);
+			var bot = Math.Max (Math.Max (pt1Y, pt2Y), 0);
 
-			var left = Math.Max(Math.Min (pt1X, pt2X), 0);
-			var right = Math.Max(Math.Max (pt1X, pt2X), 0);
+			var left = Math.Max (Math.Min (pt1X, pt2X), 0);
+			var right = Math.Max (Math.Max (pt1X, pt2X), 0);
 
 			// Rect class is inclusive of Top Left but exclusive of Bottom Right so extend by 1
-			return new TableSelection (new Point (pt1X, pt1Y), new Rect (left, top, right - left + 1, bot - top + 1));
+			return new TableSelection (new Point (pt1X, pt1Y), new Rect (left, top, right - left + 1, bot - top + 1)) {
+				IsToggled = toggle
+			};
+		}
+
+		private void ToggleCurrentCellSelection ()
+		{
+			if (!MultiSelect) {
+				return;
+			}
+
+			var regions = GetMultiSelectedRegionsContaining(selectedColumn, selectedRow).ToArray();
+			var toggles = regions.Where(s=>s.IsToggled).ToArray ();
+
+			// Toggle it off
+			if (toggles.Any ()) {
+
+				var oldRegions = MultiSelectedRegions.ToArray ().Reverse ();
+				MultiSelectedRegions.Clear ();
+
+				foreach (var region in oldRegions) {
+					if (!toggles.Contains (region))
+						MultiSelectedRegions.Push (region);
+				}
+			} else {
+				
+				// user is toggling selection within a rectangular
+				// select.  So toggle the full region
+				if(regions.Any())
+				{
+					foreach(var r in regions)
+					{
+						r.IsToggled = true;
+					}
+				}
+				else{
+					// Toggle on a single cell selection
+					MultiSelectedRegions.Push (
+					CreateTableSelection (selectedColumn, SelectedRow, selectedColumn, selectedRow, true)
+					);
+				}
+
+			}
 		}
 
 		/// <summary>
@@ -978,22 +1043,36 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public bool IsSelected (int col, int row)
 		{
-			if(!IsColumnVisible(col)) {
+			if (!IsColumnVisible (col)) {
 				return false;
-			}	
-
-			// Cell is also selected if in any multi selection region
-			if (MultiSelect && MultiSelectedRegions.Any (r => r.Rect.Contains (col, row)))
-				return true;
+			}
 
-			// Cell is also selected if Y axis appears in any region (when FullRowSelect is enabled)
-			if (FullRowSelect && MultiSelect && MultiSelectedRegions.Any (r => r.Rect.Bottom > row && r.Rect.Top <= row))
+			if(GetMultiSelectedRegionsContaining(col,row).Any())
+			{
 				return true;
+			}
 
 			return row == SelectedRow &&
 					(col == SelectedColumn || FullRowSelect);
 		}
 
+		private IEnumerable<TableSelection> GetMultiSelectedRegionsContaining(int col, int row)
+		{
+			if(!MultiSelect)
+			{
+				return Enumerable.Empty<TableSelection>();
+			}
+		
+			if(FullRowSelect)
+			{
+				return MultiSelectedRegions.Where (r => r.Rect.Bottom > row && r.Rect.Top <= row);
+			}
+			else
+			{
+				return MultiSelectedRegions.Where (r => r.Rect.Contains (col, row));
+			}
+		}
+
 		/// <summary>
 		/// Returns true if the given <paramref name="columnIndex"/> indexes a visible
 		/// column otherwise false.  Returns false for indexes that are out of bounds.
@@ -1071,19 +1150,17 @@ namespace Terminal.Gui {
 
 			if (me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
 
-				if (scrollLeftPoint != null 
+				if (scrollLeftPoint != null
 					&& scrollLeftPoint.Value.X == me.X
-					&& scrollLeftPoint.Value.Y == me.Y)
-				{
+					&& scrollLeftPoint.Value.Y == me.Y) {
 					ColumnOffset--;
 					EnsureValidScrollOffsets ();
 					SetNeedsDisplay ();
 				}
 
-				if (scrollRightPoint != null 
+				if (scrollRightPoint != null
 					&& scrollRightPoint.Value.X == me.X
-					&& scrollRightPoint.Value.Y == me.Y)
-				{
+					&& scrollRightPoint.Value.Y == me.Y) {
 					ColumnOffset++;
 					EnsureValidScrollOffsets ();
 					SetNeedsDisplay ();
@@ -1092,8 +1169,8 @@ namespace Terminal.Gui {
 				var hit = ScreenToCell (me.X, me.Y);
 				if (hit != null) {
 
-					if(MultiSelect && HasControlOrAlt(me)) {
-						UnionSelection(hit.Value.X, hit.Value.Y);
+					if (MultiSelect && HasControlOrAlt (me)) {
+						UnionSelection (hit.Value.X, hit.Value.Y);
 					} else {
 						SetSelection (hit.Value.X, hit.Value.Y, me.Flags.HasFlag (MouseFlags.ButtonShift));
 					}
@@ -1128,7 +1205,7 @@ namespace Terminal.Gui {
 		/// <returns>Cell clicked or null.</returns>
 		public Point? ScreenToCell (int clientX, int clientY)
 		{
-			return ScreenToCell(clientX, clientY, out _);
+			return ScreenToCell (clientX, clientY, out _);
 		}
 
 		/// <inheritdoc cref="ScreenToCell(int, int)"/>
@@ -1153,7 +1230,7 @@ namespace Terminal.Gui {
 				headerIfAny = col?.Column;
 				return null;
 			}
-				
+
 
 			var rowIdx = RowOffset - headerHeight + clientY;
 
@@ -1161,7 +1238,7 @@ namespace Terminal.Gui {
 			// invalid index back to user!
 			if (rowIdx >= Table.Rows.Count) {
 				return null;
-			}	
+			}
 
 			if (col != null && rowIdx >= 0) {
 
@@ -1242,10 +1319,10 @@ namespace Terminal.Gui {
 		/// <remarks>Changes will not be immediately visible in the display until you call <see cref="View.SetNeedsDisplay()"/></remarks>
 		public void EnsureValidSelection ()
 		{
-			if (TableIsNullOrInvisible()) {
+			if (TableIsNullOrInvisible ()) {
 
 				// Table doesn't exist, we should probably clear those selections
-				MultiSelectedRegions.Clear ();
+				ClearMultiSelectedRegions (false);
 				return;
 			}
 
@@ -1315,8 +1392,7 @@ namespace Terminal.Gui {
 		/// Use false if you are primarily interested in learning about directional column visibility.</param>
 		private int GetNearestVisibleColumn (int columnIndex, bool lookRight, bool allowBumpingInOppositeDirection)
 		{
-			if(TryGetNearestVisibleColumn(columnIndex,lookRight,allowBumpingInOppositeDirection, out var answer))
-			{
+			if (TryGetNearestVisibleColumn (columnIndex, lookRight, allowBumpingInOppositeDirection, out var answer)) {
 				return answer;
 			}
 
@@ -1335,7 +1411,7 @@ namespace Terminal.Gui {
 			// get the column visibility by index (if no style visible is true)
 			bool [] columnVisibility = Table.Columns.Cast<DataColumn> ()
 				.Select (c => this.Style.GetColumnStyleIfAny (c)?.Visible ?? true)
-				.ToArray();
+				.ToArray ();
 
 			// column is visible
 			if (columnVisibility [columnIndex]) {
@@ -1346,10 +1422,9 @@ namespace Terminal.Gui {
 			int increment = lookRight ? 1 : -1;
 
 			// move in that direction
-			for (int i = columnIndex; i >=0 && i < columnVisibility.Length; i += increment) {
+			for (int i = columnIndex; i >= 0 && i < columnVisibility.Length; i += increment) {
 				// if we find a visible column
-				if(columnVisibility [i]) 
-				{
+				if (columnVisibility [i]) {
 					idx = i;
 					return true;
 				}
@@ -1357,7 +1432,7 @@ namespace Terminal.Gui {
 
 			// Caller only wants to look in one direction and we did not find any
 			// visible columns in that direction
-			if(!allowBumpingInOppositeDirection) {
+			if (!allowBumpingInOppositeDirection) {
 				idx = columnIndex;
 				return false;
 			}
@@ -1400,10 +1475,10 @@ namespace Terminal.Gui {
 			//if we have scrolled too far to the right
 			if (SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
 
-				if(Style.SmoothHorizontalScrolling) {
+				if (Style.SmoothHorizontalScrolling) {
 
 					// Scroll right 1 column at a time until the users selected column is visible
-					while(SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
+					while (SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
 
 						ColumnOffset++;
 						columnsToRender = CalculateViewport (Bounds).ToArray ();
@@ -1414,11 +1489,10 @@ namespace Terminal.Gui {
 							break;
 
 					}
-				}
-				else {
+				} else {
 					ColumnOffset = SelectedColumn;
 				}
-				
+
 			}
 
 			//if we have scrolled too far down
@@ -1482,7 +1556,7 @@ namespace Terminal.Gui {
 				int colWidth;
 
 				// if column is not being rendered
-				if(colStyle?.Visible == false) {
+				if (colStyle?.Visible == false) {
 					// do not add it to the returned columns
 					continue;
 				}
@@ -1492,16 +1566,14 @@ namespace Terminal.Gui {
 
 				// there is not enough space for this columns 
 				// visible content
-				if (usedSpace + colWidth > availableHorizontalSpace)
-				{
+				if (usedSpace + colWidth > availableHorizontalSpace) {
 					bool showColumn = false;
 
 					// if this column accepts flexible width rendering and
 					// is therefore happy rendering into less space
-					if ( colStyle != null && colStyle.MinAcceptableWidth > 0 &&
+					if (colStyle != null && colStyle.MinAcceptableWidth > 0 &&
 						// is there enough space to meet the MinAcceptableWidth
-						(availableHorizontalSpace - usedSpace) >= colStyle.MinAcceptableWidth)
-					{
+						(availableHorizontalSpace - usedSpace) >= colStyle.MinAcceptableWidth) {
 						// show column and use use whatever space is 
 						// left for rendering it
 						showColumn = true;
@@ -1510,14 +1582,13 @@ namespace Terminal.Gui {
 
 					// If its the only column we are able to render then
 					// accept it anyway (that must be one massively wide column!)
-					if (first)
-					{
+					if (first) {
 						showColumn = true;
 					}
 
 					// no special exceptions and we are out of space
 					// so stop accepting new columns for the render area
-					if(!showColumn)
+					if (!showColumn)
 						break;
 				}
 
@@ -1771,7 +1842,7 @@ namespace Terminal.Gui {
 			/// Delegate for coloring specific rows in a different color.  For cell color <see cref="ColumnStyle.ColorGetter"/>
 			/// </summary>
 			/// <value></value>
-			public RowColorGetterDelegate RowColorGetter {get;set;}
+			public RowColorGetterDelegate RowColorGetter { get; set; }
 
 			/// <summary>
 			/// Determines rendering when the last column in the table is visible but it's
@@ -1781,7 +1852,7 @@ namespace Terminal.Gui {
 			/// and leave a blank column that cannot be selected in the remaining space.  
 			/// </summary>
 			/// <value></value>
-			public bool ExpandLastColumn {get;set;} = true;
+			public bool ExpandLastColumn { get; set; } = true;
 
 			/// <summary>
 			/// <para>
@@ -1798,7 +1869,7 @@ namespace Terminal.Gui {
 			/// </para>
 			/// </summary>
 			public bool SmoothHorizontalScrolling { get; set; } = true;
-			
+
 			/// <summary>
 			/// Returns the entry from <see cref="ColumnStyles"/> for the given <paramref name="col"/> or null if no custom styling is defined for it
 			/// </summary>
@@ -2003,6 +2074,12 @@ namespace Terminal.Gui {
 			/// <value></value>
 			public Rect Rect { get; set; }
 
+			/// <summary>
+			/// True if the selection was made through <see cref="Command.ToggleChecked"/>
+			/// and therefore should persist even through keyboard navigation.
+			/// </summary>
+			public bool IsToggled { get; set; }
+
 			/// <summary>
 			/// Creates a new selected area starting at the origin corner and covering the provided rectangular area
 			/// </summary>

+ 33 - 17
Terminal.Gui/Views/TileView.cs

@@ -13,6 +13,12 @@ namespace Terminal.Gui {
 	public class TileView : View {
 		TileView parentTileView;
 
+		/// <summary>
+		/// The keyboard key that the user can press to toggle resizing
+		/// of splitter lines.  Mouse drag splitting is always enabled.
+		/// </summary>
+		public Key ToggleResizable { get; set; } = Key.CtrlMask | Key.F10;
+
 		/// <summary>
 		/// A single <see cref="ContentView"/> presented in a <see cref="TileView"/>. To create
 		/// new instances use <see cref="TileView.RebuildForTileCount(int)"/> 
@@ -167,7 +173,6 @@ namespace Terminal.Gui {
 		/// <param name="tiles"></param>
 		public TileView (int tiles)
 		{
-			CanFocus = true;
 			RebuildForTileCount (tiles);
 			IgnoreBorderPropertyOnRedraw = true;
 			Border = new Border () {
@@ -403,15 +408,6 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-		/// <inheritdoc/>
-		public override bool OnEnter (View view)
-		{
-			Driver.SetCursorVisibility (CursorVisibility.Invisible);
-			if (!Tiles.Where (t => t.ContentView.HasFocus).Any ()) {
-				Tiles.FirstOrDefault ()?.ContentView.SetFocus ();
-			}
-			return base.OnEnter (view);
-		}
 
 		/// <inheritdoc/>
 		public override void Redraw (Rect bounds)
@@ -550,6 +546,30 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		/// <inheritdoc/>
+		public override bool ProcessHotKey (KeyEvent keyEvent)
+		{
+			bool focusMoved = false;
+
+			if(keyEvent.Key == ToggleResizable) {
+				foreach(var l in splitterLines) {
+
+					var iniBefore = l.IsInitialized;
+					l.IsInitialized = false;
+					l.CanFocus = !l.CanFocus;
+					l.IsInitialized = iniBefore;
+
+					if (l.CanFocus && !focusMoved) {
+						l.SetFocus ();
+						focusMoved = true;
+					}
+				}
+				return true;
+			}
+
+			return base.ProcessHotKey (keyEvent);
+		}
+
 		private bool IsValidNewSplitterPos (int idx, Pos value, int fullSpace)
 		{
 			int newSize = value.Anchor (fullSpace);
@@ -750,7 +770,7 @@ namespace Terminal.Gui {
 					tile.ContentView.Width = GetTileWidthOrHeight (i, Bounds.Width, visibleTiles, visibleSplitterLines);
 				} else {
 					tile.ContentView.X = bounds.X;
-					tile.ContentView.Y = i == 0 ? 0 : Pos.Bottom (visibleSplitterLines [i - 1]);
+					tile.ContentView.Y = i == 0 ? bounds.Y : Pos.Bottom (visibleSplitterLines [i - 1]);
 					tile.ContentView.Width = bounds.Width;
 					tile.ContentView.Height = GetTileWidthOrHeight (i, Bounds.Height, visibleTiles, visibleSplitterLines);
 				}
@@ -864,7 +884,7 @@ namespace Terminal.Gui {
 
 			public TileViewLineView (TileView parent, int idx)
 			{
-				CanFocus = true;
+				CanFocus = false;
 				TabStop = true;
 
 				this.Parent = parent;
@@ -929,7 +949,7 @@ namespace Terminal.Gui {
 
 			public void DrawSplitterSymbol ()
 			{
-				if (CanFocus && HasFocus) {
+				if (dragPosition != null || CanFocus) {
 					var location = moveRuneRenderLocation ??
 						new Point (Bounds.Width / 2, Bounds.Height / 2);
 
@@ -939,10 +959,6 @@ namespace Terminal.Gui {
 
 			public override bool MouseEvent (MouseEvent mouseEvent)
 			{
-				if (!CanFocus) {
-					return true;
-				}
-
 				if (!dragPosition.HasValue && (mouseEvent.Flags == MouseFlags.Button1Pressed)) {
 
 					// Start a Drag

+ 2 - 0
UICatalog/Scenarios/TableEditor.cs

@@ -148,6 +148,8 @@ namespace UICatalog.Scenarios {
 					}
 				}
 			};
+
+			tableView.AddKeyBinding (Key.Space, Command.ToggleChecked);
 		}
 
 		private void ShowAllColumns ()

+ 4 - 4
UICatalog/Scenarios/TileViewExperiment.cs

@@ -32,7 +32,7 @@ namespace UICatalog.Scenarios {
 				Y = 1,
 				Width = 15, //Dim.Fill (),
 				Height = 15, //Dim.Fill (),
-				//IgnoreBorderPropertyOnRedraw = true
+					     //IgnoreBorderPropertyOnRedraw = true
 
 			};
 			frame1.Border.BorderStyle = BorderStyle.Double;
@@ -60,11 +60,11 @@ namespace UICatalog.Scenarios {
 				Width = 14, //Dim.Percent (30) - 5,
 				Height = 14, //Dim.Percent (50) - 5,
 				ColorScheme = Colors.ColorSchemes ["Dialog"],
-				Border = new Border () { 
-					BorderStyle = BorderStyle.Single, 
+				Border = new Border () {
+					BorderStyle = BorderStyle.Single,
 					//BorderThickness = new Thickness (1), 
 					DrawMarginFrame = true,
-					Padding = new Thickness(1),
+					Padding = new Thickness (1),
 					BorderBrush = Color.BrightMagenta,
 					Title = "Border Title"
 				}

+ 3 - 0
UICatalog/Scenarios/TileViewNesting.cs

@@ -94,6 +94,9 @@ namespace UICatalog.Scenarios {
 			bool? border = cbBorder.Checked;
 			bool? startHorizontal = cbHorizontal.Checked;
 
+			foreach(var sub in workArea.Subviews) {
+				sub.Dispose ();
+			}
 			workArea.RemoveAll ();
 
 			if (numberOfViews <= 0) {

+ 2 - 1
UICatalog/UICatalog.cs

@@ -84,12 +84,13 @@ namespace UICatalog {
 				_selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ());
 				Application.UseSystemConsole = _useSystemConsole;
 				Application.Init ();
-				_selectedScenario.Init (Colors.ColorSchemes [_topLevelColorScheme]);
+				_selectedScenario.Init (Colors.ColorSchemes [_topLevelColorScheme == null ? "Base" : _topLevelColorScheme]);
 				_selectedScenario.Setup ();
 				_selectedScenario.Run ();
 				_selectedScenario.Dispose ();
 				_selectedScenario = null;
 				Application.Shutdown ();
+				VerifyObjectsWereDisposed ();
 				return;
 			}
 

+ 181 - 0
UnitTests/Views/TableViewTests.cs

@@ -321,6 +321,8 @@ namespace Terminal.Gui.ViewTests {
 				Bounds = new Rect (0, 0, 10, 5)
 			};
 
+			tableView.ChangeSelectionToEndOfTable(false);
+
 			// select the last row
 			tableView.MultiSelectedRegions.Clear ();
 			tableView.MultiSelectedRegions.Push (new TableView.TableSelection (new Point (0, 3), new Rect (0, 3, 4, 1)));
@@ -1506,6 +1508,185 @@ namespace Terminal.Gui.ViewTests {
 
 			Assert.DoesNotContain (new Point (1, 0), tableView.GetAllSelectedCells ());
 		}
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn ()
+		{
+			// 2 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			var selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(0,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+
+			// Go Right
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight });
+
+			selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(1,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+
+			// Toggle Select
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+			var m = tableView.MultiSelectedRegions.Single();
+			Assert.True(m.IsToggled);
+			Assert.Equal(1,m.Origin.X);
+			Assert.Equal(0,m.Origin.Y);
+			selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(1,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+
+			// Go Left
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft });
+
+			// Both Toggled and Moved to should be selected
+			Assert.Equal(2,tableView.GetAllSelectedCells().Count());
+			var s1 = tableView.GetAllSelectedCells().ElementAt(0);
+			var s2 = tableView.GetAllSelectedCells().ElementAt(1);
+			Assert.Equal(1,s1.X);
+			Assert.Equal(0,s1.Y);
+			Assert.Equal(0,s2.X);
+			Assert.Equal(0,s2.Y);
+
+			// Go Down
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+
+			// Both Toggled and Moved to should be selected but not 0,0
+			// which we moved down from
+			Assert.Equal(2,tableView.GetAllSelectedCells().Count());
+			s1 = tableView.GetAllSelectedCells().ElementAt(0);
+			s2 = tableView.GetAllSelectedCells().ElementAt(1);
+			Assert.Equal(1,s1.X);
+			Assert.Equal(0,s1.Y);
+			Assert.Equal(0,s2.X);
+			Assert.Equal(1,s2.Y);
+
+
+			// Go back to the toggled cell
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight});
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp});
+
+			// Toggle off 
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			// Go Left
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft});
+
+			selectedCell = tableView.GetAllSelectedCells().Single();
+			Assert.Equal(0,selectedCell.X);
+			Assert.Equal(0,selectedCell.Y);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn_FullRowSelect ()
+		{
+			// 2 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			tableView.FullRowSelect = true;
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			// Toggle Select Cell 0,0
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			// Go Down
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+
+			var m = tableView.MultiSelectedRegions.Single();
+			Assert.True(m.IsToggled);
+			Assert.Equal(0,m.Origin.X);
+			Assert.Equal(0,m.Origin.Y);
+
+			//First row toggled and Second row active = 12 selected cells
+			Assert.Equal(12,tableView.GetAllSelectedCells().Count());
+
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp });
+			
+			Assert.Single(tableView.MultiSelectedRegions.Where(r=>r.IsToggled));
+
+			// Can untoggle at 1,0 even though 0,0 was initial toggle because FullRowSelect is on
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			Assert.Empty(tableView.MultiSelectedRegions.Where(r=>r.IsToggled));
+
+		}
+
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn_SquareSelectToggled ()
+		{
+			// 3 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			// Make a square selection
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown});
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight});
+
+			Assert.Equal(4,tableView.GetAllSelectedCells().Count());
+
+			// Toggle the square selected region on
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+
+			// Go Right
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorRight });
+
+			//Toggled on square + the active cell (x=2,y=1)
+			Assert.Equal(5,tableView.GetAllSelectedCells().Count());
+			Assert.Equal(2,tableView.SelectedColumn);
+			Assert.Equal(1,tableView.SelectedRow);
+
+			// Untoggle the rectangular region by hitting toggle in
+			// any cell in that rect
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorUp });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft });
+
+			Assert.Equal(4,tableView.GetAllSelectedCells().Count());
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space });
+			Assert.Equal(1,tableView.GetAllSelectedCells().Count());
+		}
+
+
+
+		[Fact, AutoInitShutdown]
+		public void TestToggleCells_MultiSelectOn_Two_SquareSelects_BothToggled ()
+		{
+			// 6 row table
+			var tableView = GetABCDEFTableView (out var dt);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			dt.Rows.Add (1, 2, 3, 4, 5, 6);
+			tableView.MultiSelect = true;
+			tableView.AddKeyBinding(Key.Space,Command.ToggleChecked);
+
+			// Make first square selection (0,0 to 1,1)
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown});
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight});
+			tableView.ProcessKey (new KeyEvent { Key = Key.Space});
+			Assert.Equal(4,tableView.GetAllSelectedCells().Count());
+
+			// Make second square selection leaving 1 unselected line between them
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorLeft });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+			tableView.ProcessKey (new KeyEvent { Key = Key.CursorDown });
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorDown});
+			tableView.ProcessKey (new KeyEvent { Key = Key.ShiftMask | Key.CursorRight});
+			
+			// 2 square selections
+			Assert.Equal(8,tableView.GetAllSelectedCells().Count());
+		}
+
 		
 		[Theory, AutoInitShutdown]
 		[InlineData(new object[] { true,true })]

+ 61 - 40
UnitTests/Views/TileViewTests.cs

@@ -1,6 +1,6 @@
 using System;
+using System.ComponentModel;
 using System.Linq;
-using Terminal.Gui;
 using Terminal.Gui.Graphs;
 using Xunit;
 using Xunit.Abstractions;
@@ -60,7 +60,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_Focused ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 
 			tileView.Redraw (tileView.Bounds);
 
@@ -100,7 +100,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_Focused_WithBorder ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 
 			tileView.Redraw (tileView.Bounds);
 
@@ -141,9 +141,10 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_Focused_50PercentSplit ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
 			tileView.SetSplitterPos (0, Pos.Percent (50));
 			Assert.IsType<Pos.PosFactor> (tileView.SplitterDistances.ElementAt (0));
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
+
 			tileView.Redraw (tileView.Bounds);
 
 			string looksLike =
@@ -209,7 +210,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View1MinSize_Absolute ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (0).MinSize = 6;
 
 			// distance is too small (below 6)
@@ -254,7 +255,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View1MinSize_Absolute_WithBorder ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (0).MinSize = 5;
 
 			// distance is too small (below 5)
@@ -298,7 +299,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View2MinSize_Absolute ()
 		{
 			var tileView = Get11By3TileView (out var line);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (1).MinSize = 6;
 
 			// distance leaves too little space for view2 (less than 6 would remain)
@@ -342,7 +343,7 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Vertical_View2MinSize_Absolute_WithBorder ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 			tileView.Tiles.ElementAt (1).MinSize = 5;
 
 			// distance leaves too little space for view2 (less than 5 would remain)
@@ -386,8 +387,6 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_InsertPanelAtStart ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
-
 			tileView.InsertTile (0);
 
 			tileView.Redraw (tileView.Bounds);
@@ -405,8 +404,6 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_InsertPanelMiddle ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
-
 			tileView.InsertTile (1);
 
 			tileView.Redraw (tileView.Bounds);
@@ -424,8 +421,6 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_InsertPanelAtEnd ()
 		{
 			var tileView = Get11By3TileView (out var line, true);
-			SetInputFocusLine (tileView);
-
 			tileView.InsertTile (2);
 
 			tileView.Redraw (tileView.Bounds);
@@ -445,7 +440,9 @@ namespace Terminal.Gui.ViewTests {
 			var tileView = Get11By3TileView (out var line);
 
 			tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal;
-			SetInputFocusLine (tileView);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
+
+			Assert.True (line.HasFocus);
 
 			tileView.Redraw (tileView.Bounds);
 
@@ -485,9 +482,9 @@ namespace Terminal.Gui.ViewTests {
 		public void TestTileView_Horizontal_View1MinSize_Absolute ()
 		{
 			var tileView = Get11By3TileView (out var line);
+			tileView.ProcessHotKey (new KeyEvent (tileView.ToggleResizable, new KeyModifiers ()));
 
 			tileView.Orientation = Terminal.Gui.Graphs.Orientation.Horizontal;
-			SetInputFocusLine (tileView);
 			tileView.Tiles.ElementAt (0).MinSize = 1;
 
 			// 0 should not be allowed because it brings us below minimum size of View1
@@ -2045,6 +2042,45 @@ namespace Terminal.Gui.ViewTests {
 
 		}
 
+		[Fact, AutoInitShutdown]
+		public void Test_SplitTop_WholeBottom()
+		{
+			var tileView = new TileView (2) {
+				Width = 20,
+				Height = 10,
+				Orientation = Orientation.Horizontal,
+			};
+			tileView.Border.BorderStyle = BorderStyle.Single;
+
+			Assert.True (tileView.TrySplitTile (0,2,out TileView top));
+
+			top.Tiles.ElementAt (0).ContentView.Add (new Label ("bleh"));
+			top.Tiles.ElementAt (1).ContentView.Add (new Label ("blah"));
+
+			tileView.Tiles.ElementAt (1).ContentView.Add (new Label ("Hello"));
+			tileView.ColorScheme = new ColorScheme ();
+			top.ColorScheme = new ColorScheme ();
+			tileView.LayoutSubviews ();
+
+			tileView.Redraw (tileView.Bounds);
+
+			string looksLike =
+@"
+┌─────────┬────────┐
+│bleh     │blah    │
+│         │        │
+│         │        │
+│         │        │
+├─────────┴────────┤
+│Hello             │
+│                  │
+│                  │
+└──────────────────┘";
+
+			TestHelpers.AssertDriverContentsAre (looksLike, output);
+
+		}
+
 		[Fact, AutoInitShutdown]
 		public void TestNestedContainer3RightAnd1Down_TitleDoesNotOverspill()
 		{
@@ -2094,12 +2130,10 @@ namespace Terminal.Gui.ViewTests {
 
 			TestHelpers.AssertDriverContentsAre (looksLike, output);
 		}
-
-
-		[Fact,AutoInitShutdown]
+		[Fact, AutoInitShutdown]
 		public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRebuildForTileCount ()
 		{
-			var tv = GetTileView (20,10);
+			var tv = GetTileView (20, 10);
 
 			var myReusableView = new DisposeCounter ();
 
@@ -2113,10 +2147,10 @@ namespace Terminal.Gui.ViewTests {
 			// but I still want my view in the first tile
 			tv.Tiles.ElementAt (0).ContentView.Add (myReusableView);
 			Assert.Multiple (
-				()=>Assert.Equal (0, myReusableView.DisposalCount)
-				,()=> {
+				() => Assert.Equal (0, myReusableView.DisposalCount)
+				, () => {
 					tv.Dispose ();
-					Assert.Equal (1, myReusableView.DisposalCount); 
+					Assert.Equal (1, myReusableView.DisposalCount);
 				});
 		}
 		[Fact, AutoInitShutdown]
@@ -2140,15 +2174,13 @@ namespace Terminal.Gui.ViewTests {
 				() => Assert.Equal (0, myReusableView.DisposalCount)
 				, () => {
 					tv.Dispose ();
-
-					// TODO seems to be double disposed ?!
-					Assert.True (myReusableView.DisposalCount >= 1);
+					Assert.True (myReusableView.DisposalCount>=1);
 				});
 		}
 		[Theory, AutoInitShutdown]
-		[InlineData(0)]
+		[InlineData (0)]
 		[InlineData (1)]
-		public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile(int idx)
+		public void TestDisposal_NoEarlyDisposalsOfUsersViews_DuringRemoveTile (int idx)
 		{
 			var tv = GetTileView (20, 10);
 
@@ -2166,14 +2198,10 @@ namespace Terminal.Gui.ViewTests {
 				() => Assert.Equal (0, myReusableView.DisposalCount)
 				, () => {
 					tv.Dispose ();
-
-					// TODO seems to be double disposed ?!
 					Assert.True (myReusableView.DisposalCount >= 1);
 				});
 		}
-
-		private class DisposeCounter : View
-		{
+		private class DisposeCounter : View {
 			public int DisposalCount;
 			protected override void Dispose (bool disposing)
 			{
@@ -2255,13 +2283,6 @@ namespace Terminal.Gui.ViewTests {
 			return tileView.Subviews.OfType<LineView> ().Single ();
 		}
 
-		private void SetInputFocusLine (TileView tileView)
-		{
-			var line = GetLine (tileView);
-			line.SetFocus ();
-			Assert.True (line.HasFocus);
-		}
-
 
 		private TileView Get5x1TilesView (bool border = true)
 		{