|
@@ -1,4 +1,5 @@
|
|
|
-using System;
|
|
|
+using NStack;
|
|
|
+using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Data;
|
|
|
using System.Linq;
|
|
@@ -12,6 +13,8 @@ namespace Terminal.Gui.Views {
|
|
|
|
|
|
private int columnOffset;
|
|
|
private int rowOffset;
|
|
|
+ private int selectedRow;
|
|
|
+ private int selectedColumn;
|
|
|
|
|
|
public DataTable Table { get; private set; }
|
|
|
|
|
@@ -20,42 +23,37 @@ namespace Terminal.Gui.Views {
|
|
|
/// </summary>
|
|
|
/// <remarks>This property allows very wide tables to be rendered with horizontal scrolling</remarks>
|
|
|
public int ColumnOffset {
|
|
|
- get {
|
|
|
- return columnOffset;
|
|
|
- }
|
|
|
+ get => columnOffset;
|
|
|
|
|
|
//try to prevent this being set to an out of bounds column
|
|
|
- set {
|
|
|
- //the value before we changed it
|
|
|
- var origValue = columnOffset;
|
|
|
-
|
|
|
- columnOffset = Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
|
|
|
-
|
|
|
- //if value actually changed we must update UI
|
|
|
- if(columnOffset != origValue)
|
|
|
- SetNeedsDisplay();
|
|
|
- }
|
|
|
+ set => columnOffset = Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Zero indexed offset for the <see cref="DataRow"/> to display in <see cref="Table"/> on line 2 of the control (first line being headers)
|
|
|
/// </summary>
|
|
|
/// <remarks>This property allows very wide tables to be rendered with horizontal scrolling</remarks>
|
|
|
public int RowOffset {
|
|
|
- get {
|
|
|
- return rowOffset;
|
|
|
- }
|
|
|
- set {
|
|
|
- //the value before we changed it
|
|
|
- var origValue = rowOffset;
|
|
|
+ get => rowOffset;
|
|
|
+ set => rowOffset = Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
|
|
|
+ }
|
|
|
|
|
|
- rowOffset = Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
|
|
|
+ /// <summary>
|
|
|
+ /// The index of <see cref="DataTable.Columns"/> in <see cref="Table"/> that the user has currently selected
|
|
|
+ /// </summary>
|
|
|
+ public int SelectedColumn {
|
|
|
+ get => selectedColumn;
|
|
|
|
|
|
- //if value actually changed we must update UI
|
|
|
- if(rowOffset != origValue)
|
|
|
- SetNeedsDisplay();
|
|
|
- }
|
|
|
+ //try to prevent this being set to an out of bounds column
|
|
|
+ set => selectedColumn = Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// The index of <see cref="DataTable.Rows"/> in <see cref="Table"/> that the user has currently selected
|
|
|
+ /// </summary>
|
|
|
+ public int SelectedRow {
|
|
|
+ get => selectedRow;
|
|
|
+ set => selectedRow = Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
@@ -91,22 +89,19 @@ namespace Terminal.Gui.Views {
|
|
|
|
|
|
var frame = Frame;
|
|
|
|
|
|
- int activeColor = ColorScheme.HotNormal;
|
|
|
- int trackingColor = ColorScheme.HotFocus;
|
|
|
-
|
|
|
// What columns to render at what X offset in viewport
|
|
|
Dictionary<DataColumn, int> columnsToRender = CalculateViewport(bounds);
|
|
|
|
|
|
- Driver.SetAttribute (ColorScheme.HotNormal);
|
|
|
+ Driver.SetAttribute (ColorScheme.Normal);
|
|
|
|
|
|
//invalidate current row (prevents scrolling around leaving old characters in the frame
|
|
|
Driver.AddStr(new string (' ',bounds.Width));
|
|
|
-
|
|
|
+
|
|
|
// Render the headers
|
|
|
foreach(var kvp in columnsToRender) {
|
|
|
|
|
|
Move (kvp.Value,0);
|
|
|
- Driver.AddStr(kvp.Key.ColumnName+ SeparatorSymbol);
|
|
|
+ Driver.AddStr(Truncate(kvp.Key.ColumnName+ SeparatorSymbol,bounds.Width - kvp.Value));
|
|
|
}
|
|
|
|
|
|
//render the cells
|
|
@@ -114,6 +109,7 @@ namespace Terminal.Gui.Views {
|
|
|
|
|
|
//invalidate current row (prevents scrolling around leaving old characters in the frame
|
|
|
Move (0,line);
|
|
|
+ Driver.SetAttribute(ColorScheme.Normal);
|
|
|
Driver.AddStr(new string (' ',bounds.Width));
|
|
|
|
|
|
//work out what Row to render
|
|
@@ -125,7 +121,14 @@ namespace Terminal.Gui.Views {
|
|
|
|
|
|
foreach(var kvp in columnsToRender) {
|
|
|
Move (kvp.Value,line);
|
|
|
- Driver.AddStr(GetRenderedVal(Table.Rows[rowToRender][kvp.Key]) + SeparatorSymbol);
|
|
|
+
|
|
|
+ bool isSelectedCell = rowToRender == SelectedRow && kvp.Key.Ordinal == SelectedColumn;
|
|
|
+
|
|
|
+ Driver.SetAttribute(isSelectedCell? ColorScheme.HotFocus: ColorScheme.Normal);
|
|
|
+
|
|
|
+
|
|
|
+ var valueToRender = GetRenderedVal(Table.Rows[rowToRender][kvp.Key]) + SeparatorSymbol;
|
|
|
+ Driver.AddStr(Truncate(valueToRender,bounds.Width - kvp.Value ));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -138,50 +141,101 @@ namespace Terminal.Gui.Views {
|
|
|
}
|
|
|
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ private ustring Truncate (string valueToRender, int availableHorizontalSpace)
|
|
|
+ {
|
|
|
+ if(string.IsNullOrEmpty(valueToRender) || valueToRender.Length < availableHorizontalSpace)
|
|
|
+ return valueToRender;
|
|
|
+
|
|
|
+ return valueToRender.Substring(0,availableHorizontalSpace);
|
|
|
+ }
|
|
|
+
|
|
|
/// <inheritdoc/>
|
|
|
public override bool ProcessKey (KeyEvent keyEvent)
|
|
|
{
|
|
|
switch (keyEvent.Key) {
|
|
|
case Key.CursorLeft:
|
|
|
- ColumnOffset--;
|
|
|
+ SelectedColumn--;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.CursorRight:
|
|
|
- ColumnOffset++;
|
|
|
+ SelectedColumn++;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.CursorDown:
|
|
|
- RowOffset++;
|
|
|
+ SelectedRow++;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.CursorUp:
|
|
|
- RowOffset--;
|
|
|
+ SelectedRow--;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.PageUp:
|
|
|
- RowOffset -= Frame.Height;
|
|
|
+ SelectedRow -= Frame.Height;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
- case Key.V | Key.CtrlMask:
|
|
|
case Key.PageDown:
|
|
|
- RowOffset += Frame.Height;
|
|
|
+ SelectedRow += Frame.Height;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.Home | Key.CtrlMask:
|
|
|
- RowOffset = 0;
|
|
|
- ColumnOffset = 0;
|
|
|
+ SelectedRow = 0;
|
|
|
+ SelectedColumn = 0;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.Home:
|
|
|
- ColumnOffset = 0;
|
|
|
+ SelectedColumn = 0;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.End | Key.CtrlMask:
|
|
|
//jump to end of table
|
|
|
- RowOffset = Table.Rows.Count-1;
|
|
|
- ColumnOffset = Table.Columns.Count-1;
|
|
|
+ SelectedRow = Table.Rows.Count-1;
|
|
|
+ SelectedColumn = Table.Columns.Count-1;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
case Key.End:
|
|
|
//jump to end of row
|
|
|
- ColumnOffset = Table.Columns.Count-1;
|
|
|
+ SelectedColumn = Table.Columns.Count-1;
|
|
|
+ RefreshViewport();
|
|
|
break;
|
|
|
}
|
|
|
PositionCursor ();
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Updates the viewport (<see cref="ColumnOffset"/> / <see cref="RowOffset"/>) to ensure that the users selected cell is visible and redraws control
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>This always calls <see cref="View.SetNeedsDisplay()"/></remarks>
|
|
|
+ public void RefreshViewport ()
|
|
|
+ {
|
|
|
+ //TODO: implement
|
|
|
+
|
|
|
+ Dictionary<DataColumn, int> columnsToRender = CalculateViewport(Bounds);
|
|
|
+
|
|
|
+
|
|
|
+ //if we have scrolled too far to the left
|
|
|
+ if(SelectedColumn < columnsToRender.Keys.Min(col=>col.Ordinal)) {
|
|
|
+ ColumnOffset = SelectedColumn;
|
|
|
+ }
|
|
|
+
|
|
|
+ //if we have scrolled too far to the right
|
|
|
+ if(SelectedColumn > columnsToRender.Keys.Max(col=>col.Ordinal)) {
|
|
|
+ ColumnOffset = SelectedColumn;
|
|
|
+ }
|
|
|
+
|
|
|
+ //if we have scrolled too far down
|
|
|
+ if(SelectedRow > RowOffset + Bounds.Height-1) {
|
|
|
+ RowOffset = SelectedRow;
|
|
|
+ }
|
|
|
+ //if we have scrolled too far up
|
|
|
+ if(SelectedRow < RowOffset) {
|
|
|
+ RowOffset = SelectedRow;
|
|
|
+ }
|
|
|
+
|
|
|
+ SetNeedsDisplay();
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Calculates which columns should be rendered given the <paramref name="bounds"/> in which to display and the <see cref="ColumnOffset"/>
|
|
|
/// </summary>
|