浏览代码

Standardisation (blank constructor, menu in example etc)
- Added blank constructor (Table is now optional and can be null, in which case control will be blank)
- Moved edit to be an F key and follow pattern of open/close seen in HexEditor

tznind 4 年之前
父节点
当前提交
cdbc37ca90
共有 2 个文件被更改,包括 126 次插入88 次删除
  1. 90 77
      Terminal.Gui/Views/TableView.cs
  2. 36 11
      UICatalog/Scenarios/TableEditor.cs

+ 90 - 77
Terminal.Gui/Views/TableView.cs

@@ -15,8 +15,9 @@ namespace Terminal.Gui.Views {
 		private int rowOffset;
 		private int rowOffset;
 		private int selectedRow;
 		private int selectedRow;
 		private int selectedColumn;
 		private int selectedColumn;
+		private DataTable table;
 
 
-		public DataTable Table { get; private set; }
+		public DataTable Table { get => table; set {table = value; Update(); } }
 
 
 		/// <summary>
 		/// <summary>
 		/// Zero indexed offset for the upper left <see cref="DataColumn"/> to display in <see cref="Table"/>.
 		/// Zero indexed offset for the upper left <see cref="DataColumn"/> to display in <see cref="Table"/>.
@@ -26,16 +27,16 @@ namespace Terminal.Gui.Views {
 			get => columnOffset;
 			get => columnOffset;
 
 
 			//try to prevent this being set to an out of bounds column
 			//try to prevent this being set to an out of bounds column
-			set  => columnOffset = Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
+			set => columnOffset = Table == null ? 0 : Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
 		}
 		}
 
 
 		/// <summary>
 		/// <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)
 		/// 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>
 		/// </summary>
 		/// <remarks>This property allows very wide tables to be rendered with horizontal scrolling</remarks>
 		/// <remarks>This property allows very wide tables to be rendered with horizontal scrolling</remarks>
-		public int RowOffset { 
-			get => rowOffset; 
-			set => rowOffset = Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
+		public int RowOffset {
+			get => rowOffset;
+			set => rowOffset = Table == null ? 0 : Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
@@ -45,31 +46,31 @@ namespace Terminal.Gui.Views {
 			get => selectedColumn;
 			get => selectedColumn;
 
 
 			//try to prevent this being set to an out of bounds column
 			//try to prevent this being set to an out of bounds column
-			set  => selectedColumn = Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
+			set => selectedColumn = Table == null ? 0 :  Math.Min (Table.Columns.Count - 1, Math.Max (0, value));
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
 		/// The index of <see cref="DataTable.Rows"/> in <see cref="Table"/> that the user has currently selected
 		/// The index of <see cref="DataTable.Rows"/> in <see cref="Table"/> that the user has currently selected
 		/// </summary>
 		/// </summary>
-		public int SelectedRow { 
-			get => selectedRow; 
-			set => selectedRow = Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
+		public int SelectedRow {
+			get => selectedRow;
+			set => selectedRow =  Table == null ? 0 : Math.Min (Table.Rows.Count - 1, Math.Max (0, value));
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
 		/// The maximum number of characters to render in any given column.  This prevents one long column from pushing out all the others
 		/// The maximum number of characters to render in any given column.  This prevents one long column from pushing out all the others
 		/// </summary>
 		/// </summary>
-		public int MaximumCellWidth {get;set;} = 100;
+		public int MaximumCellWidth { get; set; } = 100;
 
 
 		/// <summary>
 		/// <summary>
 		/// The text representation that should be rendered for cells with the value <see cref="DBNull.Value"/>
 		/// The text representation that should be rendered for cells with the value <see cref="DBNull.Value"/>
 		/// </summary>
 		/// </summary>
-		public string NullSymbol {get;set;} = "-";
+		public string NullSymbol { get; set; } = "-";
 
 
 		/// <summary>
 		/// <summary>
 		/// The symbol to add after each cell value and header value to visually seperate values
 		/// The symbol to add after each cell value and header value to visually seperate values
 		/// </summary>
 		/// </summary>
-		public char SeparatorSymbol {get;set; } = ' ';
+		public char SeparatorSymbol { get; set; } = ' ';
 
 
 		/// <summary>
 		/// <summary>
 		/// Initialzies a <see cref="TableView"/> class using <see cref="LayoutStyle.Computed"/> layout. 
 		/// Initialzies a <see cref="TableView"/> class using <see cref="LayoutStyle.Computed"/> layout. 
@@ -77,8 +78,16 @@ namespace Terminal.Gui.Views {
 		/// <param name="table">The table to display in the control</param>
 		/// <param name="table">The table to display in the control</param>
 		public TableView (DataTable table) : base ()
 		public TableView (DataTable table) : base ()
 		{
 		{
-			this.Table = table ?? throw new ArgumentNullException (nameof (table));
+			this.Table = table;
 		}
 		}
+
+		/// <summary>
+		/// Initialzies a <see cref="TableView"/> class using <see cref="LayoutStyle.Computed"/> layout. Set the <see cref="Table"/> property to begin editing
+		/// </summary>
+		public TableView () : base ()
+		{
+		}
+
 		///<inheritdoc/>
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		public override void Redraw (Rect bounds)
 		{
 		{
@@ -90,45 +99,45 @@ namespace Terminal.Gui.Views {
 			var frame = Frame;
 			var frame = Frame;
 
 
 			// What columns to render at what X offset in viewport
 			// What columns to render at what X offset in viewport
-			Dictionary<DataColumn, int> columnsToRender = CalculateViewport(bounds);
+			Dictionary<DataColumn, int> columnsToRender = CalculateViewport (bounds);
 
 
 			Driver.SetAttribute (ColorScheme.Normal);
 			Driver.SetAttribute (ColorScheme.Normal);
 
 
 			//invalidate current row (prevents scrolling around leaving old characters in the frame
 			//invalidate current row (prevents scrolling around leaving old characters in the frame
-			Driver.AddStr(new string (' ',bounds.Width));
-			
+			Driver.AddStr (new string (' ', bounds.Width));
+
 			// Render the headers
 			// Render the headers
-			foreach(var kvp in columnsToRender) {
-				
-				Move (kvp.Value,0);
-				Driver.AddStr(Truncate(kvp.Key.ColumnName+ SeparatorSymbol,bounds.Width - kvp.Value));
+			foreach (var kvp in columnsToRender) {
+
+				Move (kvp.Value, 0);
+				Driver.AddStr (Truncate (kvp.Key.ColumnName + SeparatorSymbol, bounds.Width - kvp.Value));
 			}
 			}
 
 
 			//render the cells
 			//render the cells
 			for (int line = 1; line < frame.Height; line++) {
 			for (int line = 1; line < frame.Height; line++) {
-				
+
 				//invalidate current row (prevents scrolling around leaving old characters in the frame
 				//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));
+				Move (0, line);
+				Driver.SetAttribute (ColorScheme.Normal);
+				Driver.AddStr (new string (' ', bounds.Width));
 
 
 				//work out what Row to render
 				//work out what Row to render
-				var rowToRender = RowOffset + (line-1);
+				var rowToRender = RowOffset + (line - 1);
 
 
 				//if we have run off the end of the table
 				//if we have run off the end of the table
-				if(rowToRender >= Table.Rows.Count)
+				if ( Table == null || rowToRender >= Table.Rows.Count)
 					continue;
 					continue;
 
 
-				foreach(var kvp in columnsToRender) {
-					Move (kvp.Value,line);
+				foreach (var kvp in columnsToRender) {
+					Move (kvp.Value, line);
 
 
 					bool isSelectedCell = rowToRender == SelectedRow && kvp.Key.Ordinal == SelectedColumn;
 					bool isSelectedCell = rowToRender == SelectedRow && kvp.Key.Ordinal == SelectedColumn;
 
 
-					Driver.SetAttribute(isSelectedCell? ColorScheme.HotFocus: ColorScheme.Normal);
+					Driver.SetAttribute (isSelectedCell ? ColorScheme.HotFocus : ColorScheme.Normal);
+
 
 
-					
-					var valueToRender = GetRenderedVal(Table.Rows[rowToRender][kvp.Key]) + SeparatorSymbol;
-					Driver.AddStr(Truncate(valueToRender,bounds.Width - kvp.Value ));
+					var valueToRender = GetRenderedVal (Table.Rows [rowToRender] [kvp.Key]) + SeparatorSymbol;
+					Driver.AddStr (Truncate (valueToRender, bounds.Width - kvp.Value));
 				}
 				}
 			}
 			}
 
 
@@ -144,10 +153,10 @@ namespace Terminal.Gui.Views {
 
 
 		private ustring Truncate (string valueToRender, int availableHorizontalSpace)
 		private ustring Truncate (string valueToRender, int availableHorizontalSpace)
 		{
 		{
-			if(string.IsNullOrEmpty(valueToRender) || valueToRender.Length < availableHorizontalSpace)
+			if (string.IsNullOrEmpty (valueToRender) || valueToRender.Length < availableHorizontalSpace)
 				return valueToRender;
 				return valueToRender;
 
 
-			return valueToRender.Substring(0,availableHorizontalSpace);
+			return valueToRender.Substring (0, availableHorizontalSpace);
 		}
 		}
 
 
 		/// <inheritdoc/>
 		/// <inheritdoc/>
@@ -156,47 +165,47 @@ namespace Terminal.Gui.Views {
 			switch (keyEvent.Key) {
 			switch (keyEvent.Key) {
 			case Key.CursorLeft:
 			case Key.CursorLeft:
 				SelectedColumn--;
 				SelectedColumn--;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.CursorRight:
 			case Key.CursorRight:
 				SelectedColumn++;
 				SelectedColumn++;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.CursorDown:
 			case Key.CursorDown:
 				SelectedRow++;
 				SelectedRow++;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.CursorUp:
 			case Key.CursorUp:
 				SelectedRow--;
 				SelectedRow--;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.PageUp:
 			case Key.PageUp:
 				SelectedRow -= Frame.Height;
 				SelectedRow -= Frame.Height;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.PageDown:
 			case Key.PageDown:
 				SelectedRow += Frame.Height;
 				SelectedRow += Frame.Height;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.Home | Key.CtrlMask:
 			case Key.Home | Key.CtrlMask:
 				SelectedRow = 0;
 				SelectedRow = 0;
 				SelectedColumn = 0;
 				SelectedColumn = 0;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.Home:
 			case Key.Home:
 				SelectedColumn = 0;
 				SelectedColumn = 0;
-				RefreshViewport();
+				Update ();
 				break;
 				break;
 			case Key.End | Key.CtrlMask:
 			case Key.End | Key.CtrlMask:
 				//jump to end of table
 				//jump to end of table
-				SelectedRow = Table.Rows.Count-1;
-				SelectedColumn = Table.Columns.Count-1;
-				RefreshViewport();
+				SelectedRow =  Table == null ? 0 : Table.Rows.Count - 1;
+				SelectedColumn =  Table == null ? 0 : Table.Columns.Count - 1;
+				Update ();
 				break;
 				break;
 			case Key.End:
 			case Key.End:
 				//jump to end of row
 				//jump to end of row
-				SelectedColumn = Table.Columns.Count-1;
-				RefreshViewport();		
+				SelectedColumn =  Table == null ? 0 : Table.Columns.Count - 1;
+				Update ();
 				break;
 				break;
 			}
 			}
 			PositionCursor ();
 			PositionCursor ();
@@ -204,36 +213,38 @@ namespace Terminal.Gui.Views {
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Updates the viewport (<see cref="ColumnOffset"/> / <see cref="RowOffset"/>) to ensure that the users selected cell is visible and redraws control
+		/// Updates the view to reflect changes to <see cref="Table"/> and to (<see cref="ColumnOffset"/> / <see cref="RowOffset"/>) etc
 		/// </summary>
 		/// </summary>
 		/// <remarks>This always calls <see cref="View.SetNeedsDisplay()"/></remarks>
 		/// <remarks>This always calls <see cref="View.SetNeedsDisplay()"/></remarks>
-		public void RefreshViewport ()
+		public void Update()
 		{
 		{
-			//TODO: implement
-
-			Dictionary<DataColumn, int> columnsToRender = CalculateViewport(Bounds);
+			if(Table == null) {
+				SetNeedsDisplay ();
+				return;
+			}
 
 
+			Dictionary<DataColumn, int> columnsToRender = CalculateViewport (Bounds);
 
 
 			//if we have scrolled too far to the left 
 			//if we have scrolled too far to the left 
-			if(SelectedColumn < columnsToRender.Keys.Min(col=>col.Ordinal)) {
+			if (SelectedColumn < columnsToRender.Keys.Min (col => col.Ordinal)) {
 				ColumnOffset = SelectedColumn;
 				ColumnOffset = SelectedColumn;
 			}
 			}
 
 
 			//if we have scrolled too far to the right
 			//if we have scrolled too far to the right
-			if(SelectedColumn > columnsToRender.Keys.Max(col=>col.Ordinal)) {
+			if (SelectedColumn > columnsToRender.Keys.Max (col => col.Ordinal)) {
 				ColumnOffset = SelectedColumn;
 				ColumnOffset = SelectedColumn;
 			}
 			}
 
 
 			//if we have scrolled too far down
 			//if we have scrolled too far down
-			if(SelectedRow > RowOffset + Bounds.Height-1) {
+			if (SelectedRow > RowOffset + Bounds.Height - 1) {
 				RowOffset = SelectedRow;
 				RowOffset = SelectedRow;
 			}
 			}
 			//if we have scrolled too far up
 			//if we have scrolled too far up
-			if(SelectedRow < RowOffset) {
+			if (SelectedRow < RowOffset) {
 				RowOffset = SelectedRow;
 				RowOffset = SelectedRow;
 			}
 			}
 
 
-			SetNeedsDisplay();
+			SetNeedsDisplay ();
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
@@ -242,24 +253,27 @@ namespace Terminal.Gui.Views {
 		/// <param name="bounds"></param>
 		/// <param name="bounds"></param>
 		/// <param name="padding"></param>
 		/// <param name="padding"></param>
 		/// <returns></returns>
 		/// <returns></returns>
-		private Dictionary<DataColumn,int> CalculateViewport(Rect bounds, int padding = 1)
+		private Dictionary<DataColumn, int> CalculateViewport (Rect bounds, int padding = 1)
 		{
 		{
-			Dictionary<DataColumn,int> toReturn = new Dictionary<DataColumn, int>();
+			Dictionary<DataColumn, int> toReturn = new Dictionary<DataColumn, int> ();
+
+			if(Table == null)
+				return toReturn;
 
 
 			int usedSpace = 0;
 			int usedSpace = 0;
 			int availableHorizontalSpace = bounds.Width;
 			int availableHorizontalSpace = bounds.Width;
-			int rowsToRender = bounds.Height-1; //1 reserved for the headers row
-			
-			foreach(var col in Table.Columns.Cast<DataColumn>().Skip(ColumnOffset)) {
-				
-				toReturn.Add(col,usedSpace);
-				usedSpace += CalculateMaxRowSize(col,rowsToRender) + padding;
-
-				if(usedSpace > availableHorizontalSpace)
+			int rowsToRender = bounds.Height - 1; //1 reserved for the headers row
+
+			foreach (var col in Table.Columns.Cast<DataColumn> ().Skip (ColumnOffset)) {
+
+				toReturn.Add (col, usedSpace);
+				usedSpace += CalculateMaxRowSize (col, rowsToRender) + padding;
+
+				if (usedSpace > availableHorizontalSpace)
 					return toReturn;
 					return toReturn;
-				
+
 			}
 			}
-			
+
 			return toReturn;
 			return toReturn;
 		}
 		}
 
 
@@ -273,10 +287,10 @@ namespace Terminal.Gui.Views {
 		{
 		{
 			int spaceRequired = col.ColumnName.Length;
 			int spaceRequired = col.ColumnName.Length;
 
 
-			for(int i = RowOffset; i<RowOffset + rowsToRender && i<Table.Rows.Count;i++) {
+			for (int i = RowOffset; i < RowOffset + rowsToRender && i < Table.Rows.Count; i++) {
 
 
 				//expand required space if cell is bigger than the last biggest cell or header
 				//expand required space if cell is bigger than the last biggest cell or header
-				spaceRequired = Math.Max(spaceRequired,GetRenderedVal(Table.Rows[i][col]).Length);
+				spaceRequired = Math.Max (spaceRequired, GetRenderedVal (Table.Rows [i] [col]).Length);
 			}
 			}
 
 
 			return spaceRequired;
 			return spaceRequired;
@@ -289,16 +303,15 @@ namespace Terminal.Gui.Views {
 		/// <returns></returns>
 		/// <returns></returns>
 		private string GetRenderedVal (object value)
 		private string GetRenderedVal (object value)
 		{
 		{
-			if(value == null || value == DBNull.Value) 
-			{
+			if (value == null || value == DBNull.Value) {
 				return NullSymbol;
 				return NullSymbol;
 			}
 			}
-			
-			var representation = value.ToString();
+
+			var representation = value.ToString ();
 
 
 			//if it is too long to fit
 			//if it is too long to fit
-			if(representation.Length > MaximumCellWidth)
-				return representation.Substring(0,MaximumCellWidth);
+			if (representation.Length > MaximumCellWidth)
+				return representation.Substring (0, MaximumCellWidth);
 
 
 			return representation;
 			return representation;
 		}
 		}

+ 36 - 11
UICatalog/Scenarios/TableEditor.cs

@@ -18,14 +18,30 @@ namespace UICatalog.Scenarios {
 
 
 		public override void Setup ()
 		public override void Setup ()
 		{
 		{
-			var dt = BuildDemoDataTable(30,1000);
-
-			Win.Title = this.GetName() + "-" + dt.TableName ?? "Untitled";
+			Win.Title = this.GetName();
 			Win.Y = 1; // menu
 			Win.Y = 1; // menu
 			Win.Height = Dim.Fill (1); // status bar
 			Win.Height = Dim.Fill (1); // status bar
 			Top.LayoutSubviews ();
 			Top.LayoutSubviews ();
 
 
-			this.tableView = new TableView (dt) {
+			var menu = new MenuBar (new MenuBarItem [] {
+				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_OpenExample", "", () => OpenExample()),
+					new MenuItem ("_CloseExample", "", () => CloseExample()),
+					new MenuItem ("_Quit", "", () => Quit()),
+				}),
+			});
+			Top.Add (menu);
+
+			var statusBar = new StatusBar (new StatusItem [] {
+				//new StatusItem(Key.Enter, "~ENTER~ ApplyEdits", () => { _hexView.ApplyEdits(); }),
+				new StatusItem(Key.F2, "~F2~ OpenExample", () => OpenExample()),
+				new StatusItem(Key.F3, "~F3~ EditCell", () => EditCurrentCell()),
+				new StatusItem(Key.F4, "~F4~ CloseExample", () => CloseExample()),
+				new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
+			});
+			Top.Add (statusBar);
+
+			this.tableView = new TableView () {
 				X = 0,
 				X = 0,
 				Y = 0,
 				Y = 0,
 				Width = Dim.Fill (),
 				Width = Dim.Fill (),
@@ -33,21 +49,30 @@ namespace UICatalog.Scenarios {
 			};
 			};
 			tableView.CanFocus = true;
 			tableView.CanFocus = true;
 
 
-			tableView.KeyPress += KeyPressed; 
 
 
 			Win.Add (tableView);
 			Win.Add (tableView);
 		}
 		}
 
 
-		private void KeyPressed (View.KeyEventEventArgs obj)
+		private void CloseExample ()
 		{
 		{
-			if(obj.KeyEvent.Key == Key.Enter) {
-				EditCurrentCell();
-			}
-			
+			tableView.Table = null;
+		}
+
+		private void Quit ()
+		{
+			Application.RequestStop ();
+		}
+
+		private void OpenExample ()
+		{
+			tableView.Table = BuildDemoDataTable(30,1000);
 		}
 		}
 
 
 		private void EditCurrentCell ()
 		private void EditCurrentCell ()
 		{
 		{
+			if(tableView.Table == null)
+				return;
+
 			var oldValue = tableView.Table.Rows[tableView.SelectedRow][tableView.SelectedColumn].ToString();
 			var oldValue = tableView.Table.Rows[tableView.SelectedRow][tableView.SelectedColumn].ToString();
 			bool okPressed = false;
 			bool okPressed = false;
 
 
@@ -85,7 +110,7 @@ namespace UICatalog.Scenarios {
 					MessageBox.ErrorQuery(60,20,"Failed to set text", ex.Message,"Ok");
 					MessageBox.ErrorQuery(60,20,"Failed to set text", ex.Message,"Ok");
 				}
 				}
 				
 				
-				tableView.RefreshViewport();
+				tableView.Update();
 			}
 			}
 		}
 		}