diff --git a/Examples/UICatalog/Scenarios/TableViewTest.cs b/Examples/UICatalog/Scenarios/TableViewTest.cs
index 9654819cf3..ca78731b9e 100644
--- a/Examples/UICatalog/Scenarios/TableViewTest.cs
+++ b/Examples/UICatalog/Scenarios/TableViewTest.cs
@@ -89,6 +89,10 @@ public override void Main ()
("ShowVerticalHeaderLines", () => tableView.Style.ShowVerticalHeaderLines, b => tableView.Style.ShowVerticalHeaderLines = b),
("ShowHorizontalHeaderUnderline", () => tableView.Style.ShowHorizontalHeaderUnderline, b => tableView.Style.ShowHorizontalHeaderUnderline = b),
("ShowVerticalCellLines", () => tableView.Style.ShowVerticalCellLines, b => tableView.Style.ShowVerticalCellLines = b),
+ ("ShowVerticalCellLineForFirstColumn", () => tableView.Style.ShowVerticalCellLineForFirstColumn,
+ b => tableView.Style.ShowVerticalCellLineForFirstColumn = b),
+ ("ShowVerticalCellLineForLastColumn", () => tableView.Style.ShowVerticalCellLineForLastColumn,
+ b => tableView.Style.ShowVerticalCellLineForLastColumn = b),
("InvertSelectedCellFirstCharacter", () => tableView.Style.InvertSelectedCellFirstCharacter,
b => tableView.Style.InvertSelectedCellFirstCharacter = b),
("ShowHorizontalBottomline", () => tableView.Style.ShowHorizontalBottomLine, b => tableView.Style.ShowHorizontalBottomLine = b),
diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
index 82ce7817d9..5850dcae31 100644
--- a/Terminal.Gui/Views/FileDialogs/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialogs/FileDialog.cs
@@ -207,8 +207,10 @@ internal FileDialog (IFileSystem? fileSystem)
_tableViewContainer.Add (_tableView);
_tableView.Style.ShowHorizontalHeaderOverline = false;
- _tableView.Style.ShowVerticalCellLines = false;
- _tableView.Style.ShowVerticalHeaderLines = false;
+ _tableView.Style.ShowVerticalCellLines = true;
+ _tableView.Style.ShowVerticalCellLineForFirstColumn = false;
+ _tableView.Style.ShowVerticalCellLineForLastColumn = false;
+ _tableView.Style.ShowVerticalHeaderLines = true;
_tableView.Style.AlwaysShowHeaders = true;
_tableView.Style.ShowHorizontalHeaderUnderline = false;
_tableView.Style.ShowHorizontalBottomLine = false;
diff --git a/Terminal.Gui/Views/TableView/TableStyle.cs b/Terminal.Gui/Views/TableView/TableStyle.cs
index cee6900dd0..cd193df22f 100644
--- a/Terminal.Gui/Views/TableView/TableStyle.cs
+++ b/Terminal.Gui/Views/TableView/TableStyle.cs
@@ -72,6 +72,18 @@ public class TableStyle
/// True to render a solid line vertical line between cells
public bool ShowVerticalCellLines { get; set; } = true;
+ ///
+ /// Gets or sets whether the left-most visible column renders a vertical line on its left side when vertical
+ /// lines are enabled.
+ ///
+ public bool ShowVerticalCellLineForFirstColumn { get; set; } = true;
+
+ ///
+ /// Gets or sets whether the right-most visible column renders a vertical line on its right side when vertical
+ /// lines are enabled.
+ ///
+ public bool ShowVerticalCellLineForLastColumn { get; set; } = true;
+
/// True to render a solid line vertical line between headers
public bool ShowVerticalHeaderLines { get; set; } = true;
diff --git a/Terminal.Gui/Views/TableView/TableView.Content.cs b/Terminal.Gui/Views/TableView/TableView.Content.cs
index a725c0b110..dec5623ea6 100644
--- a/Terminal.Gui/Views/TableView/TableView.Content.cs
+++ b/Terminal.Gui/Views/TableView/TableView.Content.cs
@@ -161,6 +161,8 @@ public void EnsureValidScrollOffsets ()
{
int headerHeight = GetHeaderHeightIfAny ();
int headerHeightVisible = CurrentHeaderHeightVisible ();
+ int leftOuterBorderWidth = ShouldRenderFirstOuterVerticalLine () ? 1 : 0;
+ int rightOuterBorderWidth = ShouldRenderLastOuterVerticalLine () ? 1 : 0;
contentSize.Height += headerHeight + Table?.Rows ?? 0;
if (Style.ShowHorizontalBottomLine)
@@ -200,8 +202,7 @@ public void EnsureValidScrollOffsets ()
reservedFromIndex [i] = minWidths [i] + separator + reservedFromIndex [i + 1];
}
- //right border
- contentSize.Width += Style.ShowVerticalHeaderLines || Style.ShowVerticalCellLines ? 1 : 0;
+ contentSize.Width += leftOuterBorderWidth;
var startRow = 0;
int rowsToRender = Table.Rows;
@@ -238,7 +239,7 @@ public void EnsureValidScrollOffsets ()
if (isVeryLast)
{
//remaining space for last column
- int remainingSpace = Viewport.Width - contentSize.Width - (Style.ShowVerticalHeaderLines || Style.ShowVerticalCellLines ? 1 : 0);
+ int remainingSpace = Viewport.Width - contentSize.Width - rightOuterBorderWidth;
if (Style.ExpandLastColumn && colWidth < remainingSpace)
{
@@ -250,8 +251,7 @@ public void EnsureValidScrollOffsets ()
// Reserve at least the header width for each subsequent visible column so that a wide
// column does not consume all viewport space and push later columns off-screen.
int reservedForRemaining = reservedFromIndex [columnIndex + 1];
- int borderWidth = Style.ShowVerticalHeaderLines || Style.ShowVerticalCellLines ? 1 : 0;
- int availableForThisCol = Viewport.Width - contentSize.Width - reservedForRemaining - borderWidth - 1; // -1 for this column's separator
+ int availableForThisCol = Viewport.Width - contentSize.Width - reservedForRemaining - rightOuterBorderWidth - 1; // -1 for this column's separator
// Don't shrink below this column's own minimum (header width or configured minimum)
int thisColMin = minWidths [columnIndex];
@@ -275,8 +275,7 @@ public void EnsureValidScrollOffsets ()
columnIndex++;
}
- // for left border
- contentSize.Width += Style.ShowVerticalHeaderLines || Style.ShowVerticalCellLines ? 1 : 0;
+ contentSize.Width += rightOuterBorderWidth;
}
else
{
@@ -349,4 +348,24 @@ private int MinimumWidthFor (int colIdx, ColumnStyle? colStyle)
return min;
}
+
+ private bool ShouldRenderFirstOuterVerticalLine ()
+ {
+ if (!Style.ShowVerticalCellLineForFirstColumn)
+ {
+ return false;
+ }
+
+ return Style.ShowVerticalHeaderLines || Style.ShowVerticalCellLines;
+ }
+
+ private bool ShouldRenderLastOuterVerticalLine ()
+ {
+ if (!Style.ShowVerticalCellLineForLastColumn)
+ {
+ return false;
+ }
+
+ return Style.ShowVerticalHeaderLines || Style.ShowVerticalCellLines;
+ }
}
diff --git a/Terminal.Gui/Views/TableView/TableView.Drawing.cs b/Terminal.Gui/Views/TableView/TableView.Drawing.cs
index 38668fd975..d704a843a2 100644
--- a/Terminal.Gui/Views/TableView/TableView.Drawing.cs
+++ b/Terminal.Gui/Views/TableView/TableView.Drawing.cs
@@ -185,6 +185,9 @@ private void RenderBottomLine (int row, int availableWidth, ColumnToRender [] co
{
// Renders a line at the bottom of the table after all the data like:
// └─────────────────────────────────┴──────────┴──────┴──────────┴────────┴────────────────────────────────────────────┘
+ bool renderFirstOuterVerticalLine = Style.ShowVerticalCellLines && Style.ShowVerticalCellLineForFirstColumn;
+ bool renderLastOuterVerticalLine = Style.ShowVerticalCellLines && Style.ShowVerticalCellLineForLastColumn;
+
for (var c = 0; c < availableWidth; c++)
{
// Start by assuming we just draw a straight line the
@@ -195,8 +198,10 @@ private void RenderBottomLine (int row, int availableWidth, ColumnToRender [] co
{
if (c == 0)
{
- // for first character render line
- rune = Glyphs.LLCorner;
+ if (renderFirstOuterVerticalLine)
+ {
+ rune = Glyphs.LLCorner;
+ }
}
else if (columnsToRender.Any (r => r.X == c + 1))
{
@@ -205,8 +210,10 @@ private void RenderBottomLine (int row, int availableWidth, ColumnToRender [] co
}
else if (c == availableWidth - 1)
{
- // for the last character in the table
- rune = Glyphs.LRCorner;
+ if (renderLastOuterVerticalLine)
+ {
+ rune = Glyphs.LRCorner;
+ }
}
else if (!Style.ExpandLastColumn && columnsToRender.Any (r => r.IsVeryLast && r.X + r.Width - 1 == c))
{
@@ -234,7 +241,7 @@ private void RenderHeaderMidline (int row, int availableWidth, ColumnToRender []
ClearLine (row, Viewport.Width);
// render start of line
- if (_style.ShowVerticalHeaderLines)
+ if (_style.ShowVerticalHeaderLines && Style.ShowVerticalCellLineForFirstColumn)
{
RenderRune (0, row, Glyphs.VLine);
}
@@ -259,7 +266,7 @@ private void RenderHeaderMidline (int row, int availableWidth, ColumnToRender []
Move (current.X - Viewport.X, row);
AddStr (TruncateOrPad (colName, colName, current.Width, colStyle));
- if (!Style.ExpandLastColumn && current.IsVeryLast)
+ if (!Style.ExpandLastColumn && current.IsVeryLast && Style.ShowVerticalCellLineForLastColumn)
{
SetAttribute (GetAttributeForRole (VisualRole.Normal));
RenderSeparator (current.X + current.Width - 1, row, true);
@@ -270,7 +277,7 @@ private void RenderHeaderMidline (int row, int availableWidth, ColumnToRender []
SetAttribute (GetAttributeForRole (VisualRole.Normal));
// render end of line
- if (_style.ShowVerticalHeaderLines)
+ if (_style.ShowVerticalHeaderLines && Style.ShowVerticalCellLineForLastColumn)
{
RenderRune (availableWidth - 1, row, Glyphs.VLine);
}
@@ -280,6 +287,9 @@ private void RenderHeaderOverline (int row, int availableWidth, ColumnToRender [
{
// Renders a line above table headers (when visible) like:
// ┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
+ bool renderFirstOuterVerticalLine = Style.ShowVerticalHeaderLines && Style.ShowVerticalCellLineForFirstColumn;
+ bool renderLastOuterVerticalLine = Style.ShowVerticalHeaderLines && Style.ShowVerticalCellLineForLastColumn;
+
for (var c = 0; c < availableWidth; c++)
{
Rune rune = Glyphs.HLine;
@@ -288,7 +298,10 @@ private void RenderHeaderOverline (int row, int availableWidth, ColumnToRender [
{
if (c == 0)
{
- rune = Glyphs.ULCorner;
+ if (renderFirstOuterVerticalLine)
+ {
+ rune = Glyphs.ULCorner;
+ }
}
// if the next column is the start of a header
@@ -298,7 +311,10 @@ private void RenderHeaderOverline (int row, int availableWidth, ColumnToRender [
}
else if (c == availableWidth - 1)
{
- rune = Glyphs.URCorner;
+ if (renderLastOuterVerticalLine)
+ {
+ rune = Glyphs.URCorner;
+ }
}
// if the next console column is the last column's end
@@ -322,6 +338,9 @@ private void RenderHeaderUnderline (int row, int availableWidth, ColumnToRender
*/
// Renders a line below the table headers (when visible) like:
// ├──────────┼───────────┼───────────────────┼──────────┼────────┼─────────────┤
+ bool renderFirstOuterVerticalLine = Style.ShowVerticalHeaderLines && Style.ShowVerticalCellLineForFirstColumn;
+ bool renderLastOuterVerticalLine = Style.ShowVerticalHeaderLines && Style.ShowVerticalCellLineForLastColumn;
+
for (var c = 0; c < availableWidth; c++)
{
// Start by assuming we just draw a straight line the
@@ -333,8 +352,10 @@ private void RenderHeaderUnderline (int row, int availableWidth, ColumnToRender
{
if (c == 0)
{
- // for first character render line
- rune = Style.ShowVerticalCellLines ? Glyphs.LeftTee : Glyphs.LLCorner;
+ if (renderFirstOuterVerticalLine)
+ {
+ rune = Style.ShowVerticalCellLines ? Glyphs.LeftTee : Glyphs.LLCorner;
+ }
}
// if the next column is the start of a header
@@ -344,8 +365,10 @@ private void RenderHeaderUnderline (int row, int availableWidth, ColumnToRender
}
else if (c == availableWidth - 1)
{
- // for the last character in the table
- rune = Style.ShowVerticalCellLines ? Glyphs.RightTee : Glyphs.LRCorner;
+ if (renderLastOuterVerticalLine)
+ {
+ rune = Style.ShowVerticalCellLines ? Glyphs.RightTee : Glyphs.LRCorner;
+ }
}
// if the next console column is the last column's end
@@ -446,7 +469,7 @@ private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRen
RenderSeparator (current.X - 1, row, false);
- if (!Style.ExpandLastColumn && current.IsVeryLast)
+ if (!Style.ExpandLastColumn && current.IsVeryLast && Style.ShowVerticalCellLineForLastColumn)
{
RenderSeparator (current.X + current.Width - 1, row, false);
}
@@ -477,11 +500,14 @@ private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRen
SetAttribute (rowScheme.Normal);
// render start and end of line
- RenderRune (0, row, Glyphs.VLine);
+ if (Style.ShowVerticalCellLineForFirstColumn)
+ {
+ RenderRune (0, row, Glyphs.VLine);
+ }
ColumnToRender? lastCol = columnsToRender.LastOrDefault ();
- if (lastCol != null)
+ if (lastCol != null && Style.ShowVerticalCellLineForLastColumn)
{
RenderRune (lastCol.X + lastCol.Width - 1, row, Glyphs.VLine);
}
diff --git a/Tests/UnitTestsParallelizable/Views/FileDialogResultTests.cs b/Tests/UnitTestsParallelizable/Views/FileDialogResultTests.cs
index acb1d037b2..69befbdf15 100644
--- a/Tests/UnitTestsParallelizable/Views/FileDialogResultTests.cs
+++ b/Tests/UnitTestsParallelizable/Views/FileDialogResultTests.cs
@@ -1,5 +1,6 @@
// Copilot
+using System.Reflection;
using System.IO.Abstractions.TestingHelpers;
namespace UnitTests.Views;
@@ -153,6 +154,22 @@ public void FileDialog_Cancel_ClearsResult_WhenPreviouslyAccepted ()
Assert.True (sd.Canceled);
}
+ [Fact]
+ public void OpenDialog_UsesInnerTableSeparatorsWithoutOuterBorders ()
+ {
+ using OpenDialog od = new TestableOpenDialog ();
+
+ FieldInfo? tableViewField = typeof (FileDialog).GetField ("_tableView", BindingFlags.Instance | BindingFlags.NonPublic);
+ Assert.NotNull (tableViewField);
+
+ TableView tableView = Assert.IsType (tableViewField!.GetValue (od));
+
+ Assert.True (tableView.Style.ShowVerticalCellLines);
+ Assert.True (tableView.Style.ShowVerticalHeaderLines);
+ Assert.False (tableView.Style.ShowVerticalCellLineForFirstColumn);
+ Assert.False (tableView.Style.ShowVerticalCellLineForLastColumn);
+ }
+
/// Testable subclass that exposes the internal file-system constructor.
private sealed class TestableFileDialog : FileDialog
{
diff --git a/Tests/UnitTestsParallelizable/Views/TableViewTests.cs b/Tests/UnitTestsParallelizable/Views/TableViewTests.cs
index 504ba06b98..ef23713055 100644
--- a/Tests/UnitTestsParallelizable/Views/TableViewTests.cs
+++ b/Tests/UnitTestsParallelizable/Views/TableViewTests.cs
@@ -1190,6 +1190,58 @@ public void HeaderSeparatorLines_DoNotUseFocusAttribute_WhenTableHasFocus ()
tableView.Dispose ();
}
+ // Copilot
+ [Fact]
+ public void ShowVerticalCellLines_CanHideOuterBorders_AndPreserveInnerSeparators ()
+ {
+ IDriver driver = CreateTestDriver (20, 5);
+
+ TableView tableView = new () { Driver = driver };
+ tableView.BeginInit ();
+ tableView.EndInit ();
+ tableView.SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Base);
+ tableView.Viewport = new Rectangle (0, 0, 20, 5);
+
+ tableView.Style.ShowHeaders = true;
+ tableView.Style.ShowHorizontalHeaderUnderline = false;
+ tableView.Style.ShowHorizontalHeaderOverline = false;
+ tableView.Style.AlwaysShowHeaders = true;
+ tableView.Style.ShowVerticalCellLines = true;
+ tableView.Style.ShowVerticalHeaderLines = true;
+ tableView.Style.ExpandLastColumn = false;
+ tableView.Style.ShowVerticalCellLineForFirstColumn = false;
+ tableView.Style.ShowVerticalCellLineForLastColumn = false;
+
+ DataTable dt = new ();
+ dt.Columns.Add ("Name");
+ dt.Columns.Add ("Value");
+ dt.Rows.Add ("A", "B");
+ tableView.Table = new DataTableSource (dt);
+
+ tableView.Layout ();
+ tableView.SetClipToScreen ();
+ tableView.Draw ();
+
+ Cell [,] contents = driver.Contents!;
+ TableView.ColumnToRender [] columns = GetColumnsToRender (tableView);
+
+ Assert.Equal (2, columns.Length);
+
+ int leftBorderCol = 0;
+ int innerSeparatorCol = columns [1].X - 1;
+ int rightBorderCol = columns [1].X + columns [1].Width - 1;
+
+ Assert.NotEqual (Glyphs.VLine.ToString (), contents [0, leftBorderCol].Grapheme);
+ Assert.Equal (Glyphs.VLine.ToString (), contents [0, innerSeparatorCol].Grapheme);
+ Assert.NotEqual (Glyphs.VLine.ToString (), contents [0, rightBorderCol].Grapheme);
+
+ Assert.NotEqual (Glyphs.VLine.ToString (), contents [1, leftBorderCol].Grapheme);
+ Assert.Equal (Glyphs.VLine.ToString (), contents [1, innerSeparatorCol].Grapheme);
+ Assert.NotEqual (Glyphs.VLine.ToString (), contents [1, rightBorderCol].Grapheme);
+
+ tableView.Dispose ();
+ }
+
// Claude - Opus 4.7
// Regression test for https://github.com/gui-cs/Terminal.Gui/issues/5126
// Clicking outside the row area of a TableView (e.g. below the last row) must
@@ -1220,6 +1272,46 @@ public void Click_OutsideRows_DoesNotRaise_Activating ()
tableView.Dispose ();
}
+ // Copilot
+ [Fact]
+ public void CalculateContentSize_HidingOuterVerticalCellLines_ReclaimsBothOuterColumns ()
+ {
+ DataTable dt = new ();
+ dt.Columns.Add ("Name");
+ dt.Columns.Add ("Value");
+ dt.Rows.Add ("A", "B");
+
+ using TableView tableViewWithOuterBorders = new ()
+ {
+ Table = new DataTableSource (dt),
+ Viewport = new Rectangle (0, 0, 20, 5)
+ };
+ tableViewWithOuterBorders.BeginInit ();
+ tableViewWithOuterBorders.EndInit ();
+ tableViewWithOuterBorders.Style.ShowHeaders = true;
+ tableViewWithOuterBorders.Style.ShowVerticalCellLines = true;
+ tableViewWithOuterBorders.Style.ShowVerticalHeaderLines = true;
+ tableViewWithOuterBorders.Style.ExpandLastColumn = false;
+ tableViewWithOuterBorders.RefreshContentSize ();
+
+ using TableView tableViewWithoutOuterBorders = new ()
+ {
+ Table = new DataTableSource (dt),
+ Viewport = new Rectangle (0, 0, 20, 5)
+ };
+ tableViewWithoutOuterBorders.BeginInit ();
+ tableViewWithoutOuterBorders.EndInit ();
+ tableViewWithoutOuterBorders.Style.ShowHeaders = true;
+ tableViewWithoutOuterBorders.Style.ShowVerticalCellLines = true;
+ tableViewWithoutOuterBorders.Style.ShowVerticalHeaderLines = true;
+ tableViewWithoutOuterBorders.Style.ExpandLastColumn = false;
+ tableViewWithoutOuterBorders.Style.ShowVerticalCellLineForFirstColumn = false;
+ tableViewWithoutOuterBorders.Style.ShowVerticalCellLineForLastColumn = false;
+ tableViewWithoutOuterBorders.RefreshContentSize ();
+
+ Assert.Equal (tableViewWithOuterBorders.GetContentSize ().Width - 2, tableViewWithoutOuterBorders.GetContentSize ().Width);
+ }
+
// Claude - Opus 4.7
// Companion to Click_OutsideRows_DoesNotRaise_Activating: clicking on a real
// cell still raises Activating as before.