using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Terminal.Gui.Views {
///
/// View for tabular data based on a
///
public class TableView : View {
private int columnOffset;
private int rowOffset;
public DataTable Table { get; private set; }
///
/// Zero indexed offset for the upper left to display in .
///
/// This property allows very wide tables to be rendered with horizontal scrolling
public int ColumnOffset {
get => columnOffset;
//try to prevent this being set to an out of bounds column
set => columnOffset = Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
}
///
/// Zero indexed offset for the to display in on line 2 of the control (first line being headers)
///
/// This property allows very wide tables to be rendered with horizontal scrolling
public int RowOffset {
get => rowOffset;
set => rowOffset = Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
}
///
/// The maximum number of characters to render in any given column. This prevents one long column from pushing out all the others
///
public int MaximumCellWidth {get;set;} = 100;
///
/// The text representation that should be rendered for cells with the value
///
public string NullSymbol {get;set;} = "-";
///
/// Initialzies a class using layout.
///
/// The table to display in the control
public TableView (DataTable table) : base ()
{
this.Table = table ?? throw new ArgumentNullException (nameof (table));
}
///
public override void Redraw (Rect bounds)
{
Attribute currentAttribute;
var current = ColorScheme.Focus;
Driver.SetAttribute (current);
Move (0, 0);
var frame = Frame;
int activeColor = ColorScheme.HotNormal;
int trackingColor = ColorScheme.HotFocus;
// What columns to render at what X offset in viewport
Dictionary columnsToRender = CalculateViewport(bounds);
Driver.SetAttribute (ColorScheme.HotNormal);
// Render the headers
foreach(var kvp in columnsToRender) {
Move (kvp.Value,0);
Driver.AddStr(kvp.Key.ColumnName);
}
//render the cells
for (int line = 1; line < frame.Height; line++) {
//work out what Row to render
var rowToRender = RowOffset + (line-1);
if(rowToRender >= Table.Rows.Count)
break;
foreach(var kvp in columnsToRender) {
Move (kvp.Value,line);
Driver.AddStr(GetRenderedVal(Table.Rows[rowToRender][kvp.Key]));
}
}
/*
for (int line = 1; line < frame.Height; line++) {
var lineRect = new Rect (0, line, frame.Width, 1);
if (!bounds.Contains (lineRect))
continue;
Move (0, line);
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.AddStr ("test");
currentAttribute = ColorScheme.HotNormal;
SetAttribute (ColorScheme.Normal);
}*/
void SetAttribute (Attribute attribute)
{
if (currentAttribute != attribute) {
currentAttribute = attribute;
Driver.SetAttribute (attribute);
}
}
}
///
/// Calculates which columns should be rendered given the in which to display and the
///
///
///
///
private Dictionary CalculateViewport(Rect bounds, int padding = 1)
{
Dictionary toReturn = new Dictionary();
int usedSpace = 0;
int availableHorizontalSpace = bounds.Width;
int rowsToRender = bounds.Height-1; //1 reserved for the headers row
foreach(var col in Table.Columns.Cast().Skip(ColumnOffset)) {
toReturn.Add(col,usedSpace);
usedSpace += CalculateMaxRowSize(col,rowsToRender) + padding;
if(usedSpace > availableHorizontalSpace)
return toReturn;
}
return toReturn;
}
///
/// Returns the maximum of the name and the maximum length of data that will be rendered starting at and rendering
///
///
///
///
private int CalculateMaxRowSize (DataColumn col, int rowsToRender)
{
int spaceRequired = col.ColumnName.Length;
for(int i = RowOffset; i
/// Returns the value that should be rendered to best represent a strongly typed read from
///
///
///
private string GetRenderedVal (object value)
{
if(value == null || value == DBNull.Value)
{
return NullSymbol;
}
var representation = value.ToString();
//if it is too long to fit
if(representation.Length > MaximumCellWidth)
return representation.Substring(0,MaximumCellWidth);
return representation;
}
}
}