diff --git a/Terminal.Gui/Views/TableView/TableStyle.cs b/Terminal.Gui/Views/TableView/TableStyle.cs
index 35479ed979..84db12bb00 100644
--- a/Terminal.Gui/Views/TableView/TableStyle.cs
+++ b/Terminal.Gui/Views/TableView/TableStyle.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
namespace Terminal.Gui;
@@ -9,6 +10,36 @@ namespace Terminal.Gui;
///
public class TableStyle {
+ ///
+ /// Gets or sets the LineStyle for the borders surrounding header rows of a .
+ /// Defaults to .
+ ///
+ public LineStyle OuterHeaderBorderStyle { get; set; } = LineStyle.Single;
+
+ ///
+ /// Gets or sets the LineStyle for the vertical lines separating header items in a .
+ /// Defaults to .
+ ///
+ public LineStyle InnerHeaderBorderStyle { get; set; } = LineStyle.Single;
+
+ ///
+ /// Gets or sets the LineStyle for the borders surrounding the regular (non-header) portion of a .
+ /// Defaults to .
+ ///
+ public LineStyle OuterBorderStyle { get; set; } = LineStyle.Single;
+
+ ///
+ /// Gets or sets the LineStyle for the lines separating regular (non-header) items in a .
+ /// Defaults to .
+ ///
+ public LineStyle InnerBorderStyle { get; set; } = LineStyle.Single;
+
+ ///
+ /// Gets or sets the color Attribute of the inner and outer borders of a .
+ /// Defaults to Attribute(-1, -1) which results in .
+ ///
+ public Attribute BorderColor { get; set; } = new Attribute(-1, -1);
+
///
/// Gets or sets a flag indicating whether to render headers of a .
/// Defaults to .
@@ -32,6 +63,11 @@ public class TableStyle {
///
public bool ShowHorizontalHeaderUnderline { get; set; } = true;
+ ///
+ /// True to render a solid line through the headers (only when Overline and/or Underline are )
+ ///
+ public bool ShowHorizontalHeaderThroughline { get; set; } = false;
+
///
/// True to render a solid line vertical line between cells
///
@@ -57,6 +93,13 @@ public class TableStyle {
///
public bool ShowHorizontalBottomline { get; set; } = false;
+ ///
+ /// True to invert the colors of the entire selected cell in the .
+ /// Helpful for when is on, especially when the doesn't show
+ /// the cursor
+ ///
+ public bool InvertSelectedCell { get; set; } = false;
+
///
/// True to invert the colors of the first symbol of the selected cell in the .
/// This gives the appearance of a cursor for when the doesn't otherwise show
@@ -64,11 +107,48 @@ public class TableStyle {
///
public bool InvertSelectedCellFirstCharacter { get; set; } = false;
+ // NOTE: This is equivalent to True by default after change to LineCanvas borders and can't be turned off
+ // without disabling ShowVerticalCellLines, however SeparatorSymbol and HeaderSeparatorSymbol could be
+ // used to approximate the previous default behavior with FullRowSelect
+ // TODO: Explore ways of changing this without a workaround
///
/// Gets or sets a flag indicating whether to force use when rendering
/// vertical cell lines (even when is on).
///
- public bool AlwaysUseNormalColorForVerticalCellLines { get; set; } = false;
+ //public bool AlwaysUseNormalColorForVerticalCellLines { get; set; } = false;
+
+ ///
+ /// The symbol to add after each header value to visually seperate values (if not using vertical gridlines)
+ /// CM.Glyphs.VLine can be used to emulate vertical grindlines that highlight with
+ ///
+ public Rune HeaderSeparatorSymbol { get; set; } = (Rune)' ';
+
+ ///
+ /// The symbol to add after each cell value to visually seperate values (if not using vertical gridlines)
+ /// CM.Glyphs.VLine can be used to emulate vertical grindlines that highlight with
+ ///
+ public Rune SeparatorSymbol { get; set; } = (Rune)' ';
+
+ ///
+ /// The text representation that should be rendered for cells with the value
+ ///
+ public string NullSymbol { get; set; } = "-";
+
+ ///
+ /// The symbol to pad around values (between separators) in the header line
+ ///
+ public char HeaderPaddingSymbol { get; set; } = ' ';
+
+ ///
+ /// The symbol to pad around values (between separators)
+ ///
+ public char CellPaddingSymbol { get; set; } = ' ';
+
+ ///
+ /// The symbol to pad outside table (if both and
+ /// are False)
+ ///
+ public Rune BackgroundSymbol { get; set; } = (Rune)' ';
///
/// Collection of columns for which you want special rendering (e.g. custom column lengths, text alignment etc)
@@ -82,15 +162,25 @@ public class TableStyle {
public RowColorGetterDelegate RowColorGetter { get; set; }
///
- /// Determines rendering when the last column in the table is visible but it's
+ /// Determines rendering when the last column in the table is visible but its
/// content or is less than the remaining
/// space in the control. True (the default) will expand the column to fill
- /// the remaining bounds of the control. False will draw a column ending line
- /// and leave a blank column that cannot be selected in the remaining space.
+ /// the remaining bounds of the control. If false,
+ /// determines the behavior of the remaining space.
///
///
public bool ExpandLastColumn { get; set; } = true;
+ ///
+ /// Determines rendering when the last column in the table is visible but its
+ /// content or is less than the remaining
+ /// space in the control *and* is False. True (the default)
+ /// will add a blank column that cannot be selected in the remaining space.
+ /// False will fill the remaining space with .
+ ///
+ ///
+ public bool AddEmptyColumn { get; set; } = true;
+
///
///
/// Determines how is updated when scrolling
diff --git a/Terminal.Gui/Views/TableView/TableView.cs b/Terminal.Gui/Views/TableView/TableView.cs
index c04d1252bd..c7364f9fb4 100644
--- a/Terminal.Gui/Views/TableView/TableView.cs
+++ b/Terminal.Gui/Views/TableView/TableView.cs
@@ -28,6 +28,7 @@ namespace Terminal.Gui {
///
public class TableView : View {
+ private LineCanvas grid = new LineCanvas ();
private int columnOffset;
private int rowOffset;
private int selectedRow;
@@ -145,16 +146,6 @@ public int SelectedRow {
///
public int MaxCellWidth { get; set; } = DefaultMaxCellWidth;
- ///
- /// The text representation that should be rendered for cells with the value
- ///
- public string NullSymbol { get; set; } = "-";
-
- ///
- /// The symbol to add after each cell value and header value to visually seperate values (if not using vertical gridlines)
- ///
- public char SeparatorSymbol { get; set; } = ' ';
-
///
/// This event is raised when the selected cell in the table changes.
///
@@ -273,9 +264,7 @@ public override void OnDrawContent (Rect contentArea)
base.OnDrawContent (contentArea);
Move (0, 0);
-
- scrollRightPoint = null;
- scrollLeftPoint = null;
+ var frame = Frame;
// What columns to render at what X offset in viewport
var columnsToRender = CalculateViewport (Bounds).ToArray ();
@@ -285,40 +274,124 @@ public override void OnDrawContent (Rect contentArea)
//invalidate current row (prevents scrolling around leaving old characters in the frame
Driver.AddStr (new string (' ', Bounds.Width));
- int line = 0;
+ if (Table == null || columnsToRender.Length < 1) {
+ return;
+ }
- if (ShouldRenderHeaders ()) {
- // Render something like:
- /*
- ┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
- │ArithmeticComparator│chi │Healthboard│Interpretation│Labnumber│
- └────────────────────┴──────────┴───────────┴──────────────┴─────────┘
- */
- if (Style.ShowHorizontalHeaderOverline) {
- RenderHeaderOverline (line, Bounds.Width, columnsToRender);
- line++;
- }
+ var lastCol = columnsToRender [columnsToRender.Length - 1];
+ var width = Bounds.Width;
+ if (!Style.ExpandLastColumn) {
+ width = lastCol.X + lastCol.Width;
+ }
- if (Style.ShowHeaders) {
- RenderHeaderMidline (line, columnsToRender);
- line++;
+ // render the cell lines
+ grid.Clear ();
+ Color fg;
+ Color bg;
+ if (Style.BorderColor.Foreground == -1) {
+ fg = this.Border.ColorScheme.Normal.Foreground;
+ } else {
+ fg = Style.BorderColor.Foreground;
+ }
+ if (Style.BorderColor.Background == -1) {
+ bg = this.Border.ColorScheme.Normal.Background;
+ } else {
+ bg = Style.BorderColor.Background;
+ }
+ Driver.SetAttribute (new Attribute(fg, bg));
+
+ var lineWidth = width;
+
+ if (!Style.ExpandLastColumn && Style.AddEmptyColumn) {
+ lineWidth = contentArea.Width;
+ }
+ RenderCellLines (lineWidth, Table.Rows, columnsToRender);
+
+ foreach (var p in grid.GetMap (Bounds)) {
+ this.AddRune (p.Key.X, p.Key.Y, p.Value);
+ }
+
+ int hh = GetHeaderHeightIfAny ();
+
+ // render arrows
+ if (Style.ShowHorizontalScrollIndicators) {
+ if (hh > 0 && MoreColumnsToLeft ()) {
+ scrollLeftPoint = new Point (0, hh);
+ AddRuneAt (Driver, 0, scrollLeftPoint.Value.Y - 1, CM.Glyphs.LeftArrow);
}
+ if (hh > 0 && MoreColumnsToRight (columnsToRender)) {
+ scrollRightPoint = new Point (lineWidth - 1, hh);
+ AddRuneAt (Driver, scrollRightPoint.Value.X, scrollRightPoint.Value.Y - 1, CM.Glyphs.RightArrow);
+ }
+ }
+
+ // render the header contents
+ if (Style.ShowHeaders && hh > 0) {
+ var padChar = Style.HeaderPaddingSymbol;
+ var yh = hh - 1;
if (Style.ShowHorizontalHeaderUnderline) {
- RenderHeaderUnderline (line, Bounds.Width, columnsToRender);
- line++;
+ yh--;
}
- }
- int headerLinesConsumed = line;
+ for (var i = 0; i < columnsToRender.Length; i++) {
+
+ var current = columnsToRender [i];
- //render the cells
- for (; line < Bounds.Height; line++) {
+ var colStyle = Style.GetColumnStyleIfAny (current.Column);
+ var colName = table.ColumnNames [current.Column];
- ClearLine (line, Bounds.Width);
+ if (!Style.ShowVerticalHeaderLines && current.X > 0) {
+ AddRune (current.X - 1, yh, (Rune)Style.HeaderSeparatorSymbol);
+ }
+
+ Move (current.X, yh);
+
+ if (current.Width > colName.Length && Style.ShowHorizontalHeaderThroughline) {
+
+ if (colName.Sum (c => ((Rune)c).GetColumns ()) < current.Width) {
+ Driver.AddStr (colName);
+ } else {
+ Driver.AddStr (new string (colName.TakeWhile (h => (current.Width -= ((Rune)h).GetColumns ()) > 0).ToArray ()));
+ }
+ } else {
+ Driver.AddStr (TruncateOrPad (colName, colName, current.Width, colStyle, padChar));
+ }
+
+ if (!Style.ShowVerticalHeaderLines
+ && current.Column == columnsToRender.First().Column + columnsToRender.Length - 1
+ && current.X + current.Width - 1 <= lineWidth) {
+ AddRune (current.X + current.Width - 1, yh, (Rune)Style.HeaderSeparatorSymbol);
+ }
+
+ if (!Style.ExpandLastColumn) {
+ if (!Style.AddEmptyColumn) {
+ if (i == columnsToRender.Length - 1) {
+ for (int j = current.X + current.Width; j < Bounds.Width; j++) {
+ Driver.SetAttribute (GetNormalColor ());
+ AddRune (j, yh, (Rune)Style.BackgroundSymbol);
+ if (Style.ShowHorizontalHeaderOverline) {
+ AddRune (j, yh - 1, (Rune)Style.BackgroundSymbol);
+ }
+ if (Style.ShowHorizontalHeaderUnderline) {
+ AddRune (j, yh + 1, (Rune)Style.BackgroundSymbol);
+ }
+ }
+ }
+ } else if (!Style.ShowVerticalHeaderLines) {
+ AddRune (Bounds.Width - 1, yh, (Rune)Style.HeaderSeparatorSymbol);
+ }
+ }
+ }
+ }
+
+ // render the cell contents
+ for (var line = hh; line < frame.Height; line++) {
+
+ var padChar = Style.CellPaddingSymbol;
//work out what Row to render
- var rowToRender = RowOffset + (line - headerLinesConsumed);
+ var rowToRender = RowOffset + (line - hh);
//if we have run off the end of the table
if (TableIsNullOrInvisible () || rowToRender < 0)
@@ -326,30 +399,23 @@ public override void OnDrawContent (Rect contentArea)
// No more data
if (rowToRender >= Table.Rows) {
-
- if (rowToRender == Table.Rows && Style.ShowHorizontalBottomline) {
- RenderBottomLine (line, Bounds.Width, columnsToRender);
+ if (rowToRender == Table.Rows
+ && Style.ShowHorizontalBottomline
+ && !Style.ExpandLastColumn
+ && !Style.AddEmptyColumn) {
+ var start = columnsToRender [^1];
+ for (int i = start.X + start.Width; i < Bounds.Width; i++) {
+ AddRune (i, Table.Rows + hh, (Rune)Style.BackgroundSymbol);
+ }
}
continue;
}
- RenderRow (line, rowToRender, columnsToRender);
+ RenderRow (line, rowToRender, columnsToRender, padChar);
}
}
- ///
- /// Clears a line of the console by filling it with spaces
- ///
- ///
- ///
- private void ClearLine (int row, int width)
- {
- Move (0, row);
- Driver.SetAttribute (GetNormalColor ());
- Driver.AddStr (new string (' ', width));
- }
-
///
/// Returns the amount of vertical space currently occupied by the header or 0 if it is not visible.
///
@@ -376,86 +442,100 @@ internal int GetHeaderHeight ()
return heightRequired;
}
- private void RenderHeaderOverline (int row, int availableWidth, ColumnToRender [] columnsToRender)
+ private void RenderCellLines (int width, int height, ColumnToRender [] columnsToRender)
{
- // Renders a line above table headers (when visible) like:
- // ┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
-
- for (int c = 0; c < availableWidth; c++) {
-
- var rune = CM.Glyphs.HLine;
+ var row = 0;
+ int hh = GetHeaderHeightIfAny ();
- if (Style.ShowVerticalHeaderLines) {
+ // First render the header, something like:
+ /*
+ ┌────────────────────┬──────────┬───────────┬──────────────┬─────────┐
+ │ArithmeticComparator│chi │Healthboard│Interpretation│Labnumber│
+ └────────────────────┴──────────┴───────────┴──────────────┴─────────┘
+ */
- if (c == 0) {
- rune = CM.Glyphs.ULCorner;
- }
- // if the next column is the start of a header
- else if (columnsToRender.Any (r => r.X == c + 1)) {
- rune = CM.Glyphs.TopTee;
- } else if (c == availableWidth - 1) {
- rune = CM.Glyphs.URCorner;
- }
- // if the next console column is the lastcolumns end
- else if (Style.ExpandLastColumn == false &&
- columnsToRender.Any (r => r.IsVeryLast && r.X + r.Width - 1 == c)) {
- rune = CM.Glyphs.TopTee;
+ if (hh > 0) {
+ if (Style.ShowHorizontalHeaderOverline) {
+ grid.AddLine (new Point (0, row), width, Orientation.Horizontal, Style.OuterHeaderBorderStyle);
+ row++;
+ }
+ if (Style.ShowHeaders) {
+ if (Style.ShowHorizontalHeaderThroughline) {
+ grid.AddLine (new Point (0, row), width, Orientation.Horizontal, Style.InnerHeaderBorderStyle);
}
+ row++;
+ }
+ if (Style.ShowHorizontalHeaderUnderline) {
+ grid.AddLine (new Point (0, row), width, Orientation.Horizontal, Style.OuterHeaderBorderStyle);
+ row++;
}
- AddRuneAt (Driver, c, row, rune);
- }
- }
-
- private void RenderHeaderMidline (int row, ColumnToRender [] columnsToRender)
- {
- // Renders something like:
- // │ArithmeticComparator│chi │Healthboard│Interpretation│Labnumber│
-
- ClearLine (row, Bounds.Width);
-
- //render start of line
- if (style.ShowVerticalHeaderLines)
- AddRune (0, row, CM.Glyphs.VLine);
-
- for (int i = 0; i < columnsToRender.Length; i++) {
-
- var current = columnsToRender [i];
+ if (row > 1 && Style.ShowVerticalHeaderLines && Style.InnerHeaderBorderStyle != LineStyle.None) {
+ foreach (var col in columnsToRender) {
+ var lineStyle = Style.InnerHeaderBorderStyle;
+ if (col.X - 1 == 0) {
+ lineStyle = Style.OuterHeaderBorderStyle;
+ }
+ grid.AddLine (new Point (col.X - 1, 0), row, Orientation.Vertical, lineStyle);
- var colStyle = Style.GetColumnStyleIfAny (current.Column);
- var colName = table.ColumnNames [current.Column];
+ // left side of empty column
+ if (col.Column == columnsToRender.First().Column + columnsToRender.Length - 1
+ && !Style.ExpandLastColumn
+ && Style.AddEmptyColumn) {
+ grid.AddLine (new Point (col.X + col.Width - 1, 0), row, Orientation.Vertical, lineStyle);
+ }
+ }
- RenderSeparator (current.X - 1, row, true);
+ // right side
+ grid.AddLine (new Point (width - 1, 0), row, Orientation.Vertical, Style.OuterHeaderBorderStyle);
+ }
+ }
- Move (current.X, row);
+ if (Style.ShowHorizontalBottomline) {
+ height++;
+ }
- Driver.AddStr (TruncateOrPad (colName, colName, current.Width, colStyle));
+ // render the vertical cell lines
+ if (Style.ShowVerticalCellLines) {
+ foreach (var col in columnsToRender) {
+ var lineStyle = Style.InnerBorderStyle;
+ if (col.X - 1 == 0) {
+ lineStyle = Style.OuterBorderStyle;
+ }
+ grid.AddLine (new Point (col.X - 1, row - 1), height - RowOffset + 1, Orientation.Vertical, lineStyle);
- if (Style.ExpandLastColumn == false && current.IsVeryLast) {
- RenderSeparator (current.X + current.Width - 1, row, true);
+ // left side of empty column
+ if (col.Column == columnsToRender.First().Column + columnsToRender.Length - 1
+ && !Style.ExpandLastColumn
+ && Style.AddEmptyColumn) {
+ grid.AddLine (new Point (col.X + col.Width - 1, row - 1), height - RowOffset + 1, Orientation.Vertical, lineStyle);
+ }
}
+ grid.AddLine (new Point (width - 1, row - 1), height - RowOffset + 1, Orientation.Vertical, Style.OuterBorderStyle);
}
- //render end of line
- if (style.ShowVerticalHeaderLines)
- AddRune (Bounds.Width - 1, row, CM.Glyphs.VLine);
+ // render the bottom line
+ if (Style.ShowHorizontalBottomline) {
+ grid.AddLine (new Point (0, height - RowOffset + hh - 1), width, Orientation.Horizontal, Style.OuterBorderStyle);
+ }
}
- private void RenderHeaderUnderline (int row, int availableWidth, ColumnToRender [] columnsToRender)
+ private bool MoreColumnsToLeft ()
{
- /*
- * First lets work out if we should be rendering scroll indicators
- */
-
// are there are visible columns to the left that have been pushed
// off the screen due to horizontal scrolling?
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 _)) {
- moreColumnsToLeft = false;
+ return false;
}
+ return moreColumnsToLeft;
+ }
+
+ private bool MoreColumnsToRight (ColumnToRender [] columnsToRender)
+ {
// 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;
@@ -466,102 +546,13 @@ private void RenderHeaderUnderline (int row, int availableWidth, ColumnToRender
// if we went right from the last column would we find a new visible column?
if (!TryGetNearestVisibleColumn (lastColumnIdxRendered + 1, true, false, out _)) {
// no we would not
- moreColumnsToRight = false;
- }
-
- /*
- * Now lets draw the line itself
- */
-
- // Renders a line below the table headers (when visible) like:
- // ├──────────┼───────────┼───────────────────┼──────────┼────────┼─────────────┤
-
- for (int c = 0; c < availableWidth; c++) {
-
- // Start by assuming we just draw a straight line the
- // whole way but update to instead draw a header indicator
- // or scroll arrow etc
- var rune = CM.Glyphs.HLine;
-
- if (Style.ShowVerticalHeaderLines) {
- if (c == 0) {
- // for first character render line
- rune = Style.ShowVerticalCellLines ? CM.Glyphs.LeftTee : CM.Glyphs.LLCorner;
-
- // unless we have horizontally scrolled along
- // in which case render an arrow, to indicate user
- // can scroll left
- if (Style.ShowHorizontalScrollIndicators && moreColumnsToLeft) {
- rune = CM.Glyphs.LeftArrow;
- scrollLeftPoint = new Point (c, row);
- }
-
- }
- // if the next column is the start of a header
- else if (columnsToRender.Any (r => r.X == c + 1)) {
-
- /*TODO: is ┼ symbol in Driver?*/
- rune = Style.ShowVerticalCellLines ? CM.Glyphs.Cross : CM.Glyphs.BottomTee;
- } else if (c == availableWidth - 1) {
-
- // for the last character in the table
- rune = Style.ShowVerticalCellLines ? CM.Glyphs.RightTee : CM.Glyphs.LRCorner;
-
- // 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) {
- rune = CM.Glyphs.RightArrow;
- scrollRightPoint = new Point (c, row);
- }
-
- }
- // if the next console column is the lastcolumns end
- else if (Style.ExpandLastColumn == false &&
- columnsToRender.Any (r => r.IsVeryLast && r.X + r.Width - 1 == c)) {
- rune = Style.ShowVerticalCellLines ? CM.Glyphs.Cross : CM.Glyphs.BottomTee;
- }
- }
-
- AddRuneAt (Driver, c, row, rune);
+ return false;
}
+ return moreColumnsToRight;
}
- private void RenderBottomLine (int row, int availableWidth, ColumnToRender [] columnsToRender)
- {
- // Renders a line at the bottom of the table after all the data like:
- // └─────────────────────────────────┴──────────┴──────┴──────────┴────────┴────────────────────────────────────────────┘
-
- for (int c = 0; c < availableWidth; c++) {
-
- // Start by assuming we just draw a straight line the
- // whole way but update to instead draw BottomTee / Corner etc
- var rune = CM.Glyphs.HLine;
-
- if (Style.ShowVerticalCellLines) {
- if (c == 0) {
- // for first character render line
- rune = CM.Glyphs.LLCorner;
-
- } else if (columnsToRender.Any (r => r.X == c + 1)) {
- // if the next column is the start of a header
- rune = CM.Glyphs.BottomTee;
- } else if (c == availableWidth - 1) {
- // for the last character in the table
- rune = CM.Glyphs.LRCorner;
-
- } else if (Style.ExpandLastColumn == false &&
- columnsToRender.Any (r => r.IsVeryLast && r.X + r.Width - 1 == c)) {
- // if the next console column is the lastcolumns end
- rune = CM.Glyphs.BottomTee;
- }
- }
-
- AddRuneAt (Driver, c, row, rune);
- }
- }
- private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRender)
+ private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRender, char padChar)
{
var focused = HasFocus;
@@ -579,9 +570,6 @@ private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRen
color = Enabled ? rowScheme.Normal : rowScheme.Disabled;
}
- Driver.SetAttribute (color);
- Driver.AddStr (new string (' ', Bounds.Width));
-
// Render cells for each visible header for the current row
for (int i = 0; i < columnsToRender.Length; i++) {
@@ -625,49 +613,53 @@ private void RenderRow (int row, int rowToRender, ColumnToRender [] columnsToRen
cellColor = Enabled ? scheme.Normal : scheme.Disabled;
}
- var render = TruncateOrPad (val, representation, current.Width, colStyle);
+ var render = TruncateOrPad (val, representation, current.Width, colStyle, padChar);
// While many cells can be selected (see MultiSelectedRegions) only one cell is the primary (drives navigation etc)
bool isPrimaryCell = current.Column == selectedColumn && rowToRender == selectedRow;
RenderCell (cellColor, render, isPrimaryCell);
- // Reset color scheme to normal for drawing separators if we drew text with custom scheme
- if (scheme != rowScheme) {
+ // Style.AlwaysUseNormalColorForVerticalCellLines is no longer possible after switch to LineCanvas
+ // except when vertical cell lines are disabled (though a Style.SeparatorSymbol could be used)
- if (isSelectedCell) {
+ if (!Style.ShowVerticalCellLines) {
+ if (isSelectedCell && FullRowSelect) {
color = focused ? rowScheme.Focus : rowScheme.HotNormal;
} else {
color = Enabled ? rowScheme.Normal : rowScheme.Disabled;
}
Driver.SetAttribute (color);
- }
-
- // If not in full row select mode always, reset color scheme to normal and render the vertical line (or space) at the end of the cell
- if (!FullRowSelect)
- Driver.SetAttribute (Enabled ? rowScheme.Normal : rowScheme.Disabled);
- if (style.AlwaysUseNormalColorForVerticalCellLines && style.ShowVerticalCellLines) {
+ if (current.X > 0) {
+ AddRune (current.X - 1, row, (Rune)Style.SeparatorSymbol);
+ }
+ if (current.X + current.Width - 1 < Bounds.Width) {
+ AddRune (current.X + current.Width - 1, row, (Rune)Style.SeparatorSymbol);
+ }
- Driver.SetAttribute (rowScheme.Normal);
+ if (isSelectedCell) {
+ color = focused ? rowScheme.Focus : rowScheme.HotNormal;
+ } else {
+ color = Enabled ? rowScheme.Normal : rowScheme.Disabled;
+ }
+ Driver.SetAttribute (color);
}
- RenderSeparator (current.X - 1, row, false);
-
- if (Style.ExpandLastColumn == false && current.IsVeryLast) {
- RenderSeparator (current.X + current.Width - 1, row, false);
+ if (!Style.ExpandLastColumn) {
+ if (!Style.AddEmptyColumn) {
+ if (i == columnsToRender.Length - 1) {
+ for (int j = current.X + current.Width; j < Bounds.Width; j++) {
+ Driver.SetAttribute (GetNormalColor ());
+ AddRune (j, row, (Rune)Style.BackgroundSymbol);
+ }
+ }
+ } else if (!Style.ShowVerticalCellLines) {
+ Driver.SetAttribute (Enabled ? rowScheme.Normal : rowScheme.Disabled);
+ AddRune (Bounds.Width - 1, row, (Rune)Style.SeparatorSymbol);
+ }
}
}
-
- if (style.ShowVerticalCellLines) {
-
- Driver.SetAttribute (rowScheme.Normal);
-
- //render start and end of line
- AddRune (0, row, CM.Glyphs.VLine);
- AddRune (Bounds.Width - 1, row, CM.Glyphs.VLine);
- }
-
}
///
@@ -685,7 +677,7 @@ protected virtual void RenderCell (Attribute cellColor, string render, bool isPr
// 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
// selection or in full row select mode
- if (Style.InvertSelectedCellFirstCharacter && isPrimaryCell) {
+ if ((Style.InvertSelectedCell || Style.InvertSelectedCellFirstCharacter) && isPrimaryCell) {
if (render.Length > 0) {
// invert the color of the current cell for the first character
@@ -693,8 +685,10 @@ protected virtual void RenderCell (Attribute cellColor, string render, bool isPr
Driver.AddRune ((Rune)render [0]);
if (render.Length > 1) {
- Driver.SetAttribute (cellColor);
- Driver.AddStr (render.Substring (1));
+ if (!Style.InvertSelectedCell) {
+ Driver.SetAttribute (cellColor);
+ }
+ Driver.AddStr (render[1..]);
}
}
} else {
@@ -703,17 +697,6 @@ protected virtual void RenderCell (Attribute cellColor, string render, bool isPr
}
}
- private void RenderSeparator (int col, int row, bool isHeader)
- {
- if (col < 0)
- return;
-
- var renderLines = isHeader ? style.ShowVerticalHeaderLines : style.ShowVerticalCellLines;
-
- Rune symbol = renderLines ? CM.Glyphs.VLine : (Rune)SeparatorSymbol;
- AddRune (col, row, symbol);
- }
-
void AddRuneAt (ConsoleDriver d, int col, int row, Rune ch)
{
Move (col, row);
@@ -727,11 +710,12 @@ void AddRuneAt (ConsoleDriver d, int col, int row, Rune ch)
/// The string representation of
///
/// Optional style indicating custom alignment for the cell
+ /// Character used to pad string (defaults to space)
///
- private string TruncateOrPad (object originalCellValue, string representation, int availableHorizontalSpace, ColumnStyle colStyle)
+ private string TruncateOrPad (object originalCellValue, string representation, int availableHorizontalSpace, ColumnStyle colStyle, char padChar = ' ')
{
if (string.IsNullOrEmpty (representation))
- return new string (' ', availableHorizontalSpace);
+ return new string (padChar, availableHorizontalSpace);
// if value is not wide enough
if (representation.EnumerateRunes ().Sum (c => c.GetColumns ()) < availableHorizontalSpace) {
@@ -742,17 +726,17 @@ private string TruncateOrPad (object originalCellValue, string representation, i
switch (colStyle?.GetAlignment (originalCellValue) ?? TextAlignment.Left) {
case TextAlignment.Left:
- return representation + new string (' ', toPad);
+ return representation + new string (padChar, toPad);
case TextAlignment.Right:
- return new string (' ', toPad) + representation;
+ return new string (padChar, toPad) + representation;
// TODO: With single line cells, centered and justified are the same right?
case TextAlignment.Centered:
case TextAlignment.Justified:
return
- new string (' ', (int)Math.Floor (toPad / 2.0)) + // round down
+ new string (padChar, (int)Math.Floor (toPad / 2.0)) + // round down
representation +
- new string (' ', (int)Math.Ceiling (toPad / 2.0)); // round up
+ new string (padChar, (int)Math.Ceiling (toPad / 2.0)); // round up
}
}
@@ -1792,15 +1776,13 @@ private int CalculateMaxCellWidth (int col, int rowsToRender, ColumnStyle colSty
///
private string GetRepresentation (object value, ColumnStyle colStyle)
{
- if (value == null || value == DBNull.Value) {
- return NullSymbol;
+ if (value is null || value == DBNull.Value) {
+ return string.IsNullOrEmpty(Style.NullSymbol) ? " " : Style.NullSymbol;
}
return colStyle != null ? colStyle.GetRepresentation (value) : value.ToString ();
}
-
-
///
/// Describes a desire to render a column at a given horizontal position in the UI
///
diff --git a/UICatalog/Scenarios/ListColumns.cs b/UICatalog/Scenarios/ListColumns.cs
index 5e8983d673..ac66eb50f3 100644
--- a/UICatalog/Scenarios/ListColumns.cs
+++ b/UICatalog/Scenarios/ListColumns.cs
@@ -73,12 +73,14 @@ public override void Setup ()
Checked = _listColView.Style.ExpandLastColumn,
CheckType = MenuItemCheckStyle.Checked
},
+ /*
_miAlwaysUseNormalColorForVerticalCellLines =
new MenuItem ("_AlwaysUseNormalColorForVerticalCellLines", "",
() => ToggleAlwaysUseNormalColorForVerticalCellLines ()) {
Checked = _listColView.Style.AlwaysUseNormalColorForVerticalCellLines,
CheckType = MenuItemCheckStyle.Checked
},
+ */
_miSmoothScrolling = new MenuItem ("_SmoothHorizontalScrolling", "", () => ToggleSmoothScrolling ()) {
Checked = _listColView.Style.SmoothHorizontalScrolling,
CheckType = MenuItemCheckStyle.Checked
@@ -218,6 +220,7 @@ void ToggleExpandLastColumn ()
}
+ /*
void ToggleAlwaysUseNormalColorForVerticalCellLines ()
{
_miAlwaysUseNormalColorForVerticalCellLines.Checked = !_miAlwaysUseNormalColorForVerticalCellLines.Checked;
@@ -225,6 +228,7 @@ void ToggleAlwaysUseNormalColorForVerticalCellLines ()
_listColView.Update ();
}
+ */
void ToggleSmoothScrolling ()
{
diff --git a/UICatalog/Scenarios/TableEditor.cs b/UICatalog/Scenarios/TableEditor.cs
index 7d0ccc9ece..cf49f4c9c0 100644
--- a/UICatalog/Scenarios/TableEditor.cs
+++ b/UICatalog/Scenarios/TableEditor.cs
@@ -22,15 +22,18 @@ public class TableEditor : Scenario {
private MenuItem _miShowHeaders;
private MenuItem _miAlwaysShowHeaders;
private MenuItem _miHeaderOverline;
- private MenuItem _miHeaderMidline;
+ private MenuItem _miHeaderThruline;
private MenuItem _miHeaderUnderline;
+ private MenuItem _miHeaderVertline;
private MenuItem _miShowHorizontalScrollIndicators;
private MenuItem _miCellLines;
private MenuItem _miFullRowSelect;
private MenuItem _miExpandLastColumn;
- private MenuItem _miAlwaysUseNormalColorForVerticalCellLines;
+ private MenuItem _miAddEmptyColumn;
+ //private MenuItem _miAlwaysUseNormalColorForVerticalCellLines;
private MenuItem _miSmoothScrolling;
private MenuItem _miAlternatingColors;
+ private MenuItem _miInvert;
private MenuItem _miCursor;
private MenuItem _miBottomline;
private MenuItem _miCheckboxes;
@@ -42,6 +45,14 @@ public class TableEditor : Scenario {
ColorScheme redColorSchemeAlt;
ColorScheme alternatingColorScheme;
+ enum TableBorderStyleType
+ {
+ OuterHeaderBorderStyle,
+ InnerHeaderBorderStyle,
+ OuterBorderStyle,
+ InnerBorderStyle,
+ }
+
HashSet _checkedFileSystemInfos = new HashSet ();
public override void Setup ()
@@ -59,40 +70,45 @@ public override void Setup ()
var menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_File", new MenuItem [] {
- new MenuItem ("_OpenBigExample", "", () => OpenExample(true)),
- new MenuItem ("_OpenSmallExample", "", () => OpenExample(false)),
+ new MenuItem ("Open_BigExample", "", () => OpenExample(true)),
+ new MenuItem ("Open_SmallExample", "", () => OpenExample(false)),
new MenuItem ("OpenCharacter_Map","",()=>OpenUnicodeMap()),
new MenuItem ("OpenTreeExample","",()=>OpenTreeExample()),
new MenuItem ("_CloseExample", "", () => CloseExample()),
new MenuItem ("_Quit", "", () => Quit()),
}),
new MenuBarItem ("_View", new MenuItem [] {
- _miShowHeaders = new MenuItem ("_ShowHeaders", "", () => ToggleShowHeaders()){Checked = tableView.Style.ShowHeaders, CheckType = MenuItemCheckStyle.Checked },
+ _miShowHeaders = new MenuItem ("Show_Headers", "", () => ToggleShowHeaders()){Checked = tableView.Style.ShowHeaders, CheckType = MenuItemCheckStyle.Checked },
_miAlwaysShowHeaders = new MenuItem ("_AlwaysShowHeaders", "", () => ToggleAlwaysShowHeaders()){Checked = tableView.Style.AlwaysShowHeaders, CheckType = MenuItemCheckStyle.Checked },
- _miHeaderOverline = new MenuItem ("_HeaderOverLine", "", () => ToggleOverline()){Checked = tableView.Style.ShowHorizontalHeaderOverline, CheckType = MenuItemCheckStyle.Checked },
- _miHeaderMidline = new MenuItem ("_HeaderMidLine", "", () => ToggleHeaderMidline()){Checked = tableView.Style.ShowVerticalHeaderLines, CheckType = MenuItemCheckStyle.Checked },
- _miHeaderUnderline = new MenuItem ("_HeaderUnderLine", "", () => ToggleUnderline()){Checked = tableView.Style.ShowHorizontalHeaderUnderline, CheckType = MenuItemCheckStyle.Checked },
+ _miHeaderOverline = new MenuItem ("Header_OverLine", "", () => ToggleOverline()){Checked = tableView.Style.ShowHorizontalHeaderOverline, CheckType = MenuItemCheckStyle.Checked },
+ _miHeaderThruline = new MenuItem ("Header_ThroughLine", "", () => ToggleThruline()){Checked = tableView.Style.ShowHorizontalHeaderThroughline, CheckType = MenuItemCheckStyle.Checked },
+ _miHeaderUnderline = new MenuItem ("Header_UnderLine", "", () => ToggleUnderline()){Checked = tableView.Style.ShowHorizontalHeaderUnderline, CheckType = MenuItemCheckStyle.Checked },
+ _miHeaderVertline = new MenuItem ("_VerticalHeaderLines", "", () => ToggleHeaderVertline()){Checked = tableView.Style.ShowVerticalHeaderLines, CheckType = MenuItemCheckStyle.Checked },
_miBottomline = new MenuItem ("_BottomLine", "", () => ToggleBottomline()){Checked = tableView.Style.ShowHorizontalBottomline, CheckType = MenuItemCheckStyle.Checked },
- _miShowHorizontalScrollIndicators = new MenuItem ("_HorizontalScrollIndicators", "", () => ToggleHorizontalScrollIndicators()){Checked = tableView.Style.ShowHorizontalScrollIndicators, CheckType = MenuItemCheckStyle.Checked },
+ _miShowHorizontalScrollIndicators = new MenuItem ("Horizontal_ScrollIndicators", "", () => ToggleHorizontalScrollIndicators()){Checked = tableView.Style.ShowHorizontalScrollIndicators, CheckType = MenuItemCheckStyle.Checked },
_miFullRowSelect =new MenuItem ("_FullRowSelect", "", () => ToggleFullRowSelect()){Checked = tableView.FullRowSelect, CheckType = MenuItemCheckStyle.Checked },
- _miCellLines =new MenuItem ("_CellLines", "", () => ToggleCellLines()){Checked = tableView.Style.ShowVerticalCellLines, CheckType = MenuItemCheckStyle.Checked },
+ _miCellLines =new MenuItem ("Cell_Lines", "", () => ToggleCellLines()){Checked = tableView.Style.ShowVerticalCellLines, CheckType = MenuItemCheckStyle.Checked },
+ //_miAlwaysUseNormalColorForVerticalCellLines = new MenuItem ("AlwaysUse_NormalColorForVerticalCellLines", "", () => ToggleAlwaysUseNormalColorForVerticalCellLines()){Checked = tableView.Style.AlwaysUseNormalColorForVerticalCellLines, CheckType = MenuItemCheckStyle.Checked },
+ new MenuItem ("_BorderStyles", "", () => SetBorderStyles()),
+ new MenuItem ("_BorderColor", "", () => SetBorderColor()),
+ new MenuItem ("AllBorders", "", () => ToggleAllCellLines()),
+ new MenuItem ("NoBorders", "", () => ToggleNoCellLines()),
_miExpandLastColumn = new MenuItem ("_ExpandLastColumn", "", () => ToggleExpandLastColumn()){Checked = tableView.Style.ExpandLastColumn, CheckType = MenuItemCheckStyle.Checked },
- _miAlwaysUseNormalColorForVerticalCellLines = new MenuItem ("_AlwaysUseNormalColorForVerticalCellLines", "", () => ToggleAlwaysUseNormalColorForVerticalCellLines()){Checked = tableView.Style.AlwaysUseNormalColorForVerticalCellLines, CheckType = MenuItemCheckStyle.Checked },
- _miSmoothScrolling = new MenuItem ("_SmoothHorizontalScrolling", "", () => ToggleSmoothScrolling()){Checked = tableView.Style.SmoothHorizontalScrolling, CheckType = MenuItemCheckStyle.Checked },
- new MenuItem ("_AllLines", "", () => ToggleAllCellLines()),
- new MenuItem ("_NoLines", "", () => ToggleNoCellLines()),
+ _miAddEmptyColumn = new MenuItem ("A_ddEmptyColumn", "", () => ToggleAddEmptyColumn()){Checked = tableView.Style.AddEmptyColumn, CheckType = MenuItemCheckStyle.Checked },
+ _miSmoothScrolling = new MenuItem ("S_moothHorizontalScrolling", "", () => ToggleSmoothScrolling()){Checked = tableView.Style.SmoothHorizontalScrolling, CheckType = MenuItemCheckStyle.Checked },
+ _miAlternatingColors = new MenuItem ("Alte_rnatingColors", "", () => ToggleAlternatingColors()){CheckType = MenuItemCheckStyle.Checked},
+ _miInvert = new MenuItem ("InvertSelectedCell", "", () => ToggleInvertSelectedCell()){Checked = tableView.Style.InvertSelectedCell,CheckType = MenuItemCheckStyle.Checked},
+ _miCursor = new MenuItem ("_InvertSelectedCellFirstCharacter", "", () => ToggleInvertSelectedCellFirstCharacter()){Checked = tableView.Style.InvertSelectedCellFirstCharacter,CheckType = MenuItemCheckStyle.Checked},
+ new MenuItem ("ClearColumnSt_yles", "", () => ClearColumnStyles()),
+ new MenuItem ("Sho_wAllColumns", "", ()=>ShowAllColumns()),
_miCheckboxes = new MenuItem ("_Checkboxes", "", () => ToggleCheckboxes(false)){Checked = false, CheckType = MenuItemCheckStyle.Checked },
_miRadioboxes = new MenuItem ("_Radioboxes", "", () => ToggleCheckboxes(true)){Checked = false, CheckType = MenuItemCheckStyle.Checked },
- _miAlternatingColors = new MenuItem ("Alternating Colors", "", () => ToggleAlternatingColors()){CheckType = MenuItemCheckStyle.Checked},
- _miCursor = new MenuItem ("Invert Selected Cell First Character", "", () => ToggleInvertSelectedCellFirstCharacter()){Checked = tableView.Style.InvertSelectedCellFirstCharacter,CheckType = MenuItemCheckStyle.Checked},
- new MenuItem ("_ClearColumnStyles", "", () => ClearColumnStyles()),
- new MenuItem ("Sho_w All Columns", "", ()=>ShowAllColumns())
}),
new MenuBarItem ("_Column", new MenuItem [] {
- new MenuItem ("_Set Max Width", "", SetMaxWidth),
- new MenuItem ("_Set Min Width", "", SetMinWidth),
- new MenuItem ("_Set MinAcceptableWidth", "",SetMinAcceptableWidth),
- new MenuItem ("_Set All MinAcceptableWidth=1", "",SetMinAcceptableWidthToOne),
+ new MenuItem ("Set Ma_x Width", "", SetMaxWidth),
+ new MenuItem ("Set Mi_n Width", "", SetMinWidth),
+ new MenuItem ("Set MinAcceptable_Width", "",SetMinAcceptableWidth),
+ new MenuItem ("Set _All MinAcceptableWidth=1", "",SetMinAcceptableWidthToOne),
}),
});
@@ -276,11 +292,13 @@ private void HideColumn (int clickedCol)
private int? GetColumn ()
{
- if (tableView.Table == null)
+ if (tableView.Table == null) {
return null;
+ }
- if (tableView.SelectedColumn < 0 || tableView.SelectedColumn > tableView.Table.Columns)
+ if (tableView.SelectedColumn < 0 || tableView.SelectedColumn > tableView.Table.Columns) {
return null;
+ }
return tableView.SelectedColumn;
}
@@ -355,6 +373,168 @@ private void RunColumnWidthDialog (int? col, string prompt, Action e.ToString ()).ToArray ()) {
+ X = 0,
+ Y = 0,
+ };
+
+ var accepted = false;
+ var ok = new Button ("Ok", is_default: true);
+ ok.Clicked += (s, e) => { accepted = true; Application.RequestStop (); };
+ var cancel = new Button ("Cancel");
+ cancel.Clicked += (s, e) => { Application.RequestStop (); };
+ var d = new Dialog (ok, cancel) { Title = "BorderStyles" };
+
+ var borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast ().ToList ();
+ var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
+ e => e.ToString ()).ToArray ()) {
+ X = Pos.Right (rbBorderType) + 2,
+ Y = 0,
+ SelectedItem = (int) chosenStyle
+ };
+
+ // Change selection to existing style value for given type
+ rbBorderType.SelectedItemChanged += (s, e) => {
+ chosenType = (TableBorderStyleType)e.SelectedItem;
+ var str = chosenType.ToString ();
+ switch (e.SelectedItem) {
+ case 0:
+ rbBorderStyle.SelectedItem = (int) (TableBorderStyleType) style.OuterHeaderBorderStyle;
+ break;
+ case 1:
+ rbBorderStyle.SelectedItem = (int) (TableBorderStyleType) style.InnerHeaderBorderStyle;
+ break;
+ case 2:
+ rbBorderStyle.SelectedItem = (int) (TableBorderStyleType) style.OuterBorderStyle;
+ break;
+ case 3:
+ rbBorderStyle.SelectedItem = (int) (TableBorderStyleType) style.InnerBorderStyle;
+ break;
+ }
+ };
+ rbBorderStyle.SelectedItemChanged += (s, e) => {
+ chosenStyle = (LineStyle)e.SelectedItem;
+ };
+
+ d.Add (rbBorderType, rbBorderStyle);
+
+ Application.Run (d);
+
+ if (accepted) {
+
+ try {
+ switch ((int)chosenType) {
+ case 0:
+ Action setterHdrOut = (s, v) => s.OuterHeaderBorderStyle = v;
+ setterHdrOut (style, chosenStyle);
+ break;
+ case 1:
+ Action setterHdrIn = (s, v) => s.InnerHeaderBorderStyle = v;
+ setterHdrIn (style, chosenStyle);
+ break;
+ case 2:
+ Action setterOut = (s, v) => s.OuterBorderStyle = v;
+ setterOut (style, chosenStyle);
+ break;
+ case 3:
+ Action setterIn = (s, v) => s.InnerBorderStyle = v;
+ setterIn (style, chosenStyle);
+ break;
+ }
+ } catch (Exception ex) {
+ MessageBox.ErrorQuery (60, 20, "Failed to set", ex.Message, "Ok");
+ }
+
+ tableView.Update ();
+ }
+ }
+
+ private void SetBorderColor ()
+ {
+ var style = tableView.Style;
+ Color chosenForeground;
+ Color chosenBackground;
+ if (style.BorderColor.Foreground != -1) {
+ chosenForeground = style.BorderColor.Foreground;
+ } else {
+ chosenForeground = tableView.Border.ColorScheme.Normal.Foreground;
+ }
+ if (style.BorderColor.Background != -1) {
+ chosenBackground = style.BorderColor.Background;
+ } else {
+ chosenBackground = tableView.Border.ColorScheme.Normal.Background;
+ }
+
+ var accepted = false;
+ var ok = new Button ("Ok", is_default: true);
+ ok.Clicked += (s, e) => { accepted = true; Application.RequestStop (); };
+ var cancel = new Button ("Cancel");
+ cancel.Clicked += (s, e) => { Application.RequestStop (); };
+ var d = new Dialog (ok, cancel) { Title = "BorderColor" };
+
+ var colorEnum = Enum.GetNames (typeof (ColorName));
+ var rbBorderFgTitle = new Label ("Foreground") {
+ X = 0,
+ Y = 0
+ };
+ var rbBorderForeground = new RadioGroup (colorEnum.Select (
+ e => e.ToString ()).ToArray ()) {
+ X = 0,
+ Y = Pos.Bottom(rbBorderFgTitle),
+ SelectedItem = (int)chosenForeground
+ };
+ var rbBorderBgTitle = new Label ("Background") {
+ X = Pos.Right (rbBorderForeground) + 2,
+ Y = 0
+ };
+ var rbBorderBackground = new RadioGroup (colorEnum.Select (
+ e => e.ToString ()).ToArray ()) {
+ X = Pos.Right (rbBorderForeground) + 2,
+ Y = Pos.Bottom(rbBorderBgTitle),
+ SelectedItem = (int)chosenBackground
+ };
+
+ rbBorderForeground.SelectedItemChanged += (s, e) => {
+ if (Color.TryParse (colorEnum [e.SelectedItem], out var c)) {
+ chosenForeground = (Color)c;
+ }
+ };
+ rbBorderBackground.SelectedItemChanged += (s, e) => {
+ if (Color.TryParse (colorEnum [e.SelectedItem], out var c)) {
+ chosenBackground = (Color)c;
+ }
+ };
+
+ d.Add (rbBorderFgTitle, rbBorderForeground, rbBorderBgTitle, rbBorderBackground);
+
+ Application.Run (d);
+
+ if (accepted) {
+
+ try {
+ Action setterCol = (s, v) => s.BorderColor = v;
+ setterCol (style, new Attribute (chosenForeground, chosenBackground));
+ } catch (Exception ex) {
+ MessageBox.ErrorQuery (60, 20, "Failed to set", ex.Message, "Ok");
+ }
+
+ tableView.Update ();
+ }
+ }
+
private void SetupScrollBar ()
{
var scrollBar = new ScrollBarView (tableView, true);
@@ -437,10 +617,10 @@ private void ToggleOverline ()
tableView.Style.ShowHorizontalHeaderOverline = (bool)_miHeaderOverline.Checked;
tableView.Update ();
}
- private void ToggleHeaderMidline ()
+ private void ToggleThruline ()
{
- _miHeaderMidline.Checked = !_miHeaderMidline.Checked;
- tableView.Style.ShowVerticalHeaderLines = (bool)_miHeaderMidline.Checked;
+ _miHeaderThruline.Checked = !_miHeaderThruline.Checked;
+ tableView.Style.ShowHorizontalHeaderThroughline = (bool)_miHeaderThruline.Checked;
tableView.Update ();
}
private void ToggleUnderline ()
@@ -449,6 +629,12 @@ private void ToggleUnderline ()
tableView.Style.ShowHorizontalHeaderUnderline = (bool)_miHeaderUnderline.Checked;
tableView.Update ();
}
+ private void ToggleHeaderVertline ()
+ {
+ _miHeaderVertline.Checked = !_miHeaderVertline.Checked;
+ tableView.Style.ShowVerticalHeaderLines = (bool)_miHeaderVertline.Checked;
+ tableView.Update ();
+ }
private void ToggleBottomline ()
{
_miBottomline.Checked = !_miBottomline.Checked;
@@ -472,7 +658,14 @@ private void ToggleExpandLastColumn ()
{
_miExpandLastColumn.Checked = !_miExpandLastColumn.Checked;
tableView.Style.ExpandLastColumn = (bool)_miExpandLastColumn.Checked;
+ tableView.Update ();
+ }
+
+ private void ToggleAddEmptyColumn ()
+ {
+ _miAddEmptyColumn.Checked = !_miAddEmptyColumn.Checked;
+ tableView.Style.AddEmptyColumn = (bool)_miAddEmptyColumn.Checked;
tableView.Update ();
}
@@ -532,6 +725,7 @@ private void CheckOrUncheckFile (FileSystemInfo info, bool check)
}
}
+ /*
private void ToggleAlwaysUseNormalColorForVerticalCellLines ()
{
_miAlwaysUseNormalColorForVerticalCellLines.Checked = !_miAlwaysUseNormalColorForVerticalCellLines.Checked;
@@ -539,6 +733,8 @@ private void ToggleAlwaysUseNormalColorForVerticalCellLines ()
tableView.Update ();
}
+ */
+
private void ToggleSmoothScrolling ()
{
_miSmoothScrolling.Checked = !_miSmoothScrolling.Checked;
@@ -556,28 +752,36 @@ private void ToggleCellLines ()
private void ToggleAllCellLines ()
{
tableView.Style.ShowHorizontalHeaderOverline = true;
- tableView.Style.ShowVerticalHeaderLines = true;
+ tableView.Style.ShowHorizontalHeaderThroughline = true;
tableView.Style.ShowHorizontalHeaderUnderline = true;
+ tableView.Style.ShowVerticalHeaderLines = true;
tableView.Style.ShowVerticalCellLines = true;
+ tableView.Style.ShowHorizontalBottomline = true;
_miHeaderOverline.Checked = true;
- _miHeaderMidline.Checked = true;
+ _miHeaderThruline.Checked = true;
_miHeaderUnderline.Checked = true;
+ _miHeaderVertline.Checked = true;
_miCellLines.Checked = true;
+ _miBottomline.Checked = true;
tableView.Update ();
}
private void ToggleNoCellLines ()
{
tableView.Style.ShowHorizontalHeaderOverline = false;
- tableView.Style.ShowVerticalHeaderLines = false;
+ tableView.Style.ShowHorizontalHeaderThroughline = false;
tableView.Style.ShowHorizontalHeaderUnderline = false;
+ tableView.Style.ShowVerticalHeaderLines = false;
tableView.Style.ShowVerticalCellLines = false;
+ tableView.Style.ShowHorizontalBottomline = false;
_miHeaderOverline.Checked = false;
- _miHeaderMidline.Checked = false;
+ _miHeaderThruline.Checked = false;
_miHeaderUnderline.Checked = false;
+ _miHeaderVertline.Checked = false;
_miCellLines.Checked = false;
+ _miBottomline.Checked = false;
tableView.Update ();
}
@@ -595,6 +799,13 @@ private void ToggleAlternatingColors ()
tableView.SetNeedsDisplay ();
}
+ private void ToggleInvertSelectedCell ()
+ {
+ //toggle menu item
+ _miInvert.Checked = !_miInvert.Checked;
+ tableView.Style.InvertSelectedCell = (bool)_miInvert.Checked;
+ tableView.SetNeedsDisplay ();
+ }
private void ToggleInvertSelectedCellFirstCharacter ()
{
//toggle menu item
diff --git a/UnitTests/Views/TableViewTests.cs b/UnitTests/Views/TableViewTests.cs
index 4a58ea70d5..5239895607 100644
--- a/UnitTests/Views/TableViewTests.cs
+++ b/UnitTests/Views/TableViewTests.cs
@@ -457,6 +457,7 @@ public void TableView_ShowHeadersFalse_AndNoHeaderLines ()
tv.Style.ShowHeaders = false;
tv.Style.ShowHorizontalHeaderOverline = false;
+ tv.Style.ShowHorizontalHeaderThroughline = false;
tv.Style.ShowHorizontalHeaderUnderline = false;
tv.Draw ();
@@ -474,12 +475,13 @@ public void TableView_ShowHeadersFalse_OverlineTrue ()
tv.Style.ShowHeaders = false;
tv.Style.ShowHorizontalHeaderOverline = true;
+ tv.Style.ShowHorizontalHeaderThroughline = false;
tv.Style.ShowHorizontalHeaderUnderline = false;
tv.Draw ();
string expected = @"
-┌─┬─┐
+┌─┬─►
│1│2│
";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -492,15 +494,16 @@ public void TableView_ShowHeadersFalse_UnderlineTrue ()
tv.Style.ShowHeaders = false;
tv.Style.ShowHorizontalHeaderOverline = false;
+ tv.Style.ShowHorizontalHeaderThroughline = false;
tv.Style.ShowHorizontalHeaderUnderline = true;
- // Horizontal scrolling option is part of the underline
+ // Horizontal scrolling option is the lowest header line
tv.Style.ShowHorizontalScrollIndicators = true;
tv.Draw ();
string expected = @"
-├─┼─►
+┌─┬─►
│1│2│
";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -514,8 +517,9 @@ public void TableView_ShowHeadersFalse_AllLines ()
tv.Style.ShowHeaders = false;
tv.Style.ShowHorizontalHeaderOverline = true;
+ tv.Style.ShowHorizontalHeaderThroughline = false;
tv.Style.ShowHorizontalHeaderUnderline = true;
- // Horizontal scrolling option is part of the underline
+ // Horizontal scrolling option is the lowest header line
tv.Style.ShowHorizontalScrollIndicators = true;
@@ -552,12 +556,36 @@ public void TableView_ExpandLastColumn_True ()
}
[Fact, AutoInitShutdown]
- public void TableView_ExpandLastColumn_False ()
+ public void TableView_ExpandLastColumn_False_AddEmptyColumn_False ()
{
var tv = SetUpMiniTable ();
// the thing we are testing
tv.Style.ExpandLastColumn = false;
+ tv.Style.AddEmptyColumn = false;
+
+ tv.Draw ();
+
+ string expected = @"
+┌─┬─┐
+│A│B│
+├─┼─┤
+│1│2│
+";
+ TestHelpers.AssertDriverContentsAre (expected, output);
+
+ // Shutdown must be called to safely clean up Application if Init has been called
+ Application.Shutdown ();
+ }
+
+ [Fact, AutoInitShutdown]
+ public void TableView_ExpandLastColumn_False_AddEmptyColumn_True ()
+ {
+ var tv = SetUpMiniTable ();
+
+ // the thing we are testing
+ tv.Style.ExpandLastColumn = false;
+ tv.Style.AddEmptyColumn = true;
tv.Draw ();
@@ -846,6 +874,137 @@ public void TableView_ColorTests_FocusedOrNot (bool focused)
Application.Shutdown ();
}
+ [Fact, AutoInitShutdown]
+ public void TableView_NullSymbol_Is_EmptyString ()
+ {
+ var tv = new TableView () {
+ Width = 20,
+ Height = 4
+ };
+
+ var dt = new DataTable ();
+ dt.Columns.Add ("C1");
+ dt.Columns.Add ("C2");
+ dt.Columns.Add ("C3");
+
+ dt.Rows.Add ("Hello", DBNull.Value, "f");
+
+ tv.Table = new DataTableSource (dt);
+ tv.Style.NullSymbol = string.Empty;
+
+ Application.Top.Add (tv);
+ Application.Begin (Application.Top);
+
+ tv.Draw ();
+
+ var expected =
+@"
+┌─────┬──┬─────────┐
+│C1 │C2│C3 │
+├─────┼──┼─────────┤
+│Hello│ │f │
+";
+
+ TestHelpers.AssertDriverContentsAre (expected, output);
+
+ Application.Shutdown ();
+ }
+
+ [Theory, AutoInitShutdown]
+ [InlineData (false)]
+ [InlineData (true)]
+ public void TableView_ColorTests_InvertSelectedCell_and_InvertSelectedCellFirstCharacter (bool focused)
+ {
+ var tv = new TableView () {
+ Width = 20,
+ Height = 4
+ };
+
+ var dt = new DataTable ();
+ dt.Columns.Add ("C1");
+ dt.Columns.Add ("C2");
+ dt.Columns.Add ("C3");
+
+ dt.Rows.Add ("Hello", DBNull.Value, "f");
+
+ tv.Table = new DataTableSource (dt);
+ tv.Style.NullSymbol = "-";
+
+ Application.Top.Add (tv);
+ Application.Begin (Application.Top);
+
+ // private method for forcing the view to be focused/not focused
+ var setFocusMethod = typeof (View).GetMethod ("SetHasFocus", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ // when the view is/isn't focused
+ setFocusMethod.Invoke (tv, new object [] { focused, tv, true });
+
+ tv.Draw ();
+
+ var expected =
+@"
+┌─────┬──┬─────────┐
+│C1 │C2│C3 │
+├─────┼──┼─────────┤
+│Hello│- │f │
+";
+
+ TestHelpers.AssertDriverContentsAre (expected, output);
+
+ // Now the thing we really want to test are the styles
+ // InvertSelectedCell and InvertSelectedCellFirstCharacter!
+
+ tv.FullRowSelect = true;
+ tv.Style.InvertSelectedCell = true;
+
+ tv.Draw ();
+ string expectedColors =
+@"
+00000000000000000000
+00000000000000000000
+00000000000000000000
+02222011011111111110
+";
+ var invertFocus = new Attribute (tv.ColorScheme.Focus.Background, tv.ColorScheme.Focus.Foreground);
+ var invertHotFocus = new Attribute (tv.ColorScheme.HotFocus.Background, tv.ColorScheme.HotFocus.Foreground);
+ var testColors = new Attribute [] {
+ // 0
+ tv.ColorScheme.Normal,
+ // 1
+ focused? tv.ColorScheme.Focus : tv.ColorScheme.HotFocus,
+ // 2
+ focused? invertFocus : invertHotFocus};
+
+ TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, testColors);
+
+ tv.Style.InvertSelectedCell = false;
+ tv.Style.InvertSelectedCellFirstCharacter = true;
+
+ tv.Draw ();
+ expectedColors =
+@"
+00000000000000000000
+00000000000000000000
+00000000000000000000
+02111011011111111110
+";
+ TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, testColors);
+
+ tv.Style.ShowVerticalCellLines = false;
+
+ tv.Draw ();
+ expectedColors =
+@"
+00000000000000000000
+00000000000000000000
+00000000000000000000
+12111111111111111111
+";
+ TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, testColors);
+
+ Application.Shutdown ();
+ }
+
[Theory, AutoInitShutdown]
[InlineData (false)]
[InlineData (true)]
@@ -936,7 +1095,7 @@ public void TableView_ColorsTest_RowColorGetter (bool focused)
00000
00000
00000
-21222
+01020
";
TestHelpers.AssertDriverAttributesAre (expectedColors, driver: Application.Driver, new Attribute [] {
@@ -1139,6 +1298,7 @@ public void ScrollRight_SmoothScrolling ()
// 3 columns are visibile
tableView.Bounds = new Rect (0, 0, 7, 5);
tableView.Style.ShowHorizontalHeaderUnderline = false;
+ tableView.Style.ShowHorizontalHeaderThroughline = false;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = true;
@@ -1162,7 +1322,7 @@ public void ScrollRight_SmoothScrolling ()
string expected =
@"
-│A│B│C│
+│A│B│C►
│1│2│3│";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -1181,7 +1341,7 @@ public void ScrollRight_SmoothScrolling ()
expected =
@"
-│B│C│D│
+◄B│C│D►
│2│3│4│";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -1200,6 +1360,7 @@ public void ScrollRight_WithoutSmoothScrolling ()
// 3 columns are visibile
tableView.Bounds = new Rect (0, 0, 7, 5);
tableView.Style.ShowHorizontalHeaderUnderline = false;
+ tableView.Style.ShowHorizontalHeaderThroughline = false;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = false;
@@ -1223,7 +1384,7 @@ public void ScrollRight_WithoutSmoothScrolling ()
string expected =
@"
-│A│B│C│
+│A│B│C►
│1│2│3│";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -1241,7 +1402,7 @@ public void ScrollRight_WithoutSmoothScrolling ()
expected =
@"
-│D│E│F│
+◄D│E│F│
│4│5│6│";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -1260,6 +1421,7 @@ private TableView GetABCDEFTableView (out DataTable dt)
// 3 columns are visible
tableView.Bounds = new Rect (0, 0, 7, 5);
tableView.Style.ShowHorizontalHeaderUnderline = false;
+ tableView.Style.ShowHorizontalHeaderThroughline = false;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = false;
@@ -1289,7 +1451,7 @@ public void TestColumnStyle_VisibleFalse_IsNotRendered ()
string expected =
@"
-│A│C│D│
+│A│C│D►
│1│3│4│";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -1809,6 +1971,7 @@ public void LongColumnTest ()
// 25 characters can be printed into table
tableView.Bounds = new Rect (0, 0, 25, 5);
tableView.Style.ShowHorizontalHeaderUnderline = true;
+ tableView.Style.ShowHorizontalHeaderThroughline = false;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = true;
@@ -1957,6 +2120,19 @@ public void LongColumnTest ()
";
TestHelpers.AssertDriverContentsAre (expected, output);
+ tableView.Style.AddEmptyColumn = false;
+
+ tableView.LayoutSubviews ();
+ tableView.Draw ();
+ expected =
+@"
+│A │B │Very Long │
+├───┼───┼──────────┤
+│1 │2 │aaaaaaaaaa│
+│1 │2 │aaa │
+";
+ TestHelpers.AssertDriverContentsAre (expected, output);
+
// MaxCellWidth limits MinCellWidth
tableView.MaxCellWidth = 5;
tableView.MinCellWidth = 10;
@@ -1965,10 +2141,10 @@ public void LongColumnTest ()
tableView.Draw ();
expected =
@"
-│A │B │Very │ │
-├─────┼─────┼─────┼─────┤
-│1 │2 │aaaaa│ │
-│1 │2 │aaa │ │
+│A │B │Very │
+├─────┼─────┼─────┤
+│1 │2 │aaaaa│
+│1 │2 │aaa │
";
TestHelpers.AssertDriverContentsAre (expected, output);
@@ -1986,6 +2162,7 @@ public void ScrollIndicators ()
// 3 columns are visibile
tableView.Bounds = new Rect (0, 0, 7, 5);
tableView.Style.ShowHorizontalHeaderUnderline = true;
+ tableView.Style.ShowHorizontalHeaderThroughline = false;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = true;
@@ -2066,7 +2243,7 @@ public void CellEventsBackgroundFill ()
dt.Rows.Add ("Hello", DBNull.Value, "f");
tv.Table = new DataTableSource (dt);
- tv.NullSymbol = string.Empty;
+ tv.Style.NullSymbol = string.Empty;
Application.Top.Add (tv);
Application.Begin (Application.Top);
@@ -2129,6 +2306,7 @@ public void ShowHorizontalBottomLine_WithVerticalCellLines ()
// 3 columns are visibile
tableView.Bounds = new Rect (0, 0, 7, 5);
tableView.Style.ShowHorizontalHeaderUnderline = true;
+ tableView.Style.ShowHorizontalHeaderThroughline = true;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = true;
@@ -2140,7 +2318,7 @@ public void ShowHorizontalBottomLine_WithVerticalCellLines ()
// Because first column in table is A
string expected =
@"
-│A│B│C│
+┌A┬B┬C┐
├─┼─┼─►
│1│2│3│
└─┴─┴─┘";
@@ -2158,6 +2336,7 @@ public void ShowHorizontalBottomLine_NoCellLines ()
// 3 columns are visibile
tableView.Bounds = new Rect (0, 0, 7, 5);
tableView.Style.ShowHorizontalHeaderUnderline = true;
+ tableView.Style.ShowHorizontalHeaderThroughline = true;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = true;
@@ -2170,7 +2349,7 @@ public void ShowHorizontalBottomLine_NoCellLines ()
// Because first column in table is A
string expected =
@"
-│A│B│C│
+┌A┬B┬C┐
└─┴─┴─►
1 2 3
───────";
@@ -2234,12 +2413,13 @@ public void TestFullRowSelect_SelectionColorStopsAtTableEdge_WithCellLines ()
0000000
0000000
0000000
-0111110
+0101010
0000000";
TestHelpers.AssertDriverAttributesAre (expected, driver: Application.Driver, normal, focus);
}
+ /*
[Fact, AutoInitShutdown]
public void TestFullRowSelect_AlwaysUseNormalColorForVerticalCellLines ()
{
@@ -2299,6 +2479,7 @@ public void TestFullRowSelect_AlwaysUseNormalColorForVerticalCellLines ()
TestHelpers.AssertDriverAttributesAre (expected, driver: Application.Driver, normal, focus);
}
+ */
[Fact, AutoInitShutdown]
public void TestTableViewCheckboxes_Simple ()
@@ -2769,7 +2950,7 @@ public void TestFullRowSelect_SelectionColorDoesNotStop_WhenShowVerticalCellLine
string expected =
@"
A B C
-───────
+──────►
1 2 3
1 2 3
1 2 3";
@@ -3196,6 +3377,7 @@ private TableView GetTwoRowSixColumnTable (out DataTable dt)
// 3 columns are visible
tableView.Bounds = new Rect (0, 0, 7, 5);
tableView.Style.ShowHorizontalHeaderUnderline = true;
+ tableView.Style.ShowHorizontalHeaderThroughline = false;
tableView.Style.ShowHorizontalHeaderOverline = false;
tableView.Style.AlwaysShowHeaders = true;
tableView.Style.SmoothHorizontalScrolling = true;
diff --git a/UnitTests/Views/TreeTableSourceTests.cs b/UnitTests/Views/TreeTableSourceTests.cs
index b5563984fe..8007b7330e 100644
--- a/UnitTests/Views/TreeTableSourceTests.cs
+++ b/UnitTests/Views/TreeTableSourceTests.cs
@@ -28,6 +28,7 @@ public void TestTreeTableSource_BasicExpanding_WithKeyboard ()
{
var tv = GetTreeTable (out _);
+ tv.Style.ShowHorizontalHeaderThroughline = false;
tv.Style.GetOrCreateColumnStyle (1).MinAcceptableWidth = 1;
tv.Draw ();
@@ -85,6 +86,7 @@ public void TestTreeTableSource_BasicExpanding_WithMouse ()
{
var tv = GetTreeTable (out _);
+ tv.Style.ShowHorizontalHeaderThroughline = false;
tv.Style.GetOrCreateColumnStyle (1).MinAcceptableWidth = 1;
tv.Draw ();
@@ -150,6 +152,7 @@ public void TestTreeTableSource_CombinedWithCheckboxes ()
CheckBoxTableSourceWrapperByIndex checkSource;
tv.Table = checkSource = new CheckBoxTableSourceWrapperByIndex (tv, tv.Table);
+ tv.Style.ShowHorizontalHeaderThroughline = false;
tv.Style.GetOrCreateColumnStyle (2).MinAcceptableWidth = 1;
tv.Draw ();