From 6af1903d8c461a02cd94d461a756164d0a3bc888 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 11 Mar 2020 15:55:02 -0700 Subject: [PATCH 01/26] Fixed #58: Multi-line commands rendering wrong --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index e0e27cd..07eb4bd 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using OutGridView.Models; using Terminal.Gui; @@ -164,6 +165,11 @@ private static string GetPaddedString(List strings, int[] colWidths, int builder.Append(' '); } + // Replace any newlines with encoded newline (`n) + // Note we can't use Environment.Newline because we don't know that the + // Command honors that. + strings[i] = strings[i].Replace("\n", "`n"); + // If the string won't fit in the column, append an ellipsis. if (strings[i].Length > colWidths[i]) { From a69373eae9534aaf1a771f372982b5ea03ecc6de Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 11 Mar 2020 16:00:25 -0700 Subject: [PATCH 02/26] Fixed #58 - Newlines in commands render incorrectly --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 07eb4bd..19deced 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Text.RegularExpressions; using OutGridView.Models; using Terminal.Gui; From f155262ca88adaab5c49c16fd232b789d6c32abe Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 11 Mar 2020 16:30:01 -0700 Subject: [PATCH 03/26] Added debug instructions to readme --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 073a375..5672e70 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,35 @@ Get-Process | Out-ConsoleGridView > NOTE: If you change the code and rebuild the project, you'll need to launch a > _new_ PowerShell process since the dll is already loaded and can't be unloaded. +### Debugging in Visual Studio Code + + +```powershell +PS C:\path\to\GraphicalTools> code . +``` + +Build by hitting `Ctrl-Shift-b` in VS Code. + +To debug: + +In a Powershell session in the `c:\path\to\GraphicalTools` directory, run `pwsh` (thus nesting powershell). + +Then do the folowing: + +```powershell +Import-Module .\module\Microsoft.PowerShell.ConsoleGuiTools +$pid +``` + +This will import the latest built DLL and output the process ID you'll need for debugging. Copy this ID to the clipboard. + +In VScode, set your breakpoints, etc... Then hit `F5`. In the VScode search box, paste the value printed by `$pid`. You'll see something like `pwsh.exe 18328`. Click that and the debug session will start. + +In the Powershell session run your commands; breakpoints will be hit, etc... + +When done, run `exit` to exit the nested PowerShell and run `pwsh` again. This unloads the DLL. Repeat. + + ## Contributions Welcome! We would love to incorporate community contributions into this project. If you would like to From 440833ea33563e47efbb1c2558be67fd125291ef Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 11 Mar 2020 16:31:28 -0700 Subject: [PATCH 04/26] Update src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs Co-Authored-By: Tyler James Leonhardt --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 19deced..e784836 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -167,6 +167,7 @@ private static string GetPaddedString(List strings, int[] colWidths, int // Replace any newlines with encoded newline (`n) // Note we can't use Environment.Newline because we don't know that the // Command honors that. + strings[i] = strings[i].Replace("\r\n", "`r`n"); strings[i] = strings[i].Replace("\n", "`n"); // If the string won't fit in the column, append an ellipsis. From dd2e765f705d9914aee7ad1b4e6cde2eeafcc432 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 11 Mar 2020 19:45:23 -0700 Subject: [PATCH 05/26] simplified stripping of newline/linefeed --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index e784836..2692d5b 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -164,10 +164,10 @@ private static string GetPaddedString(List strings, int[] colWidths, int builder.Append(' '); } - // Replace any newlines with encoded newline (`n) + // Replace any newlines with encoded newline/linefeed (`n or `r) // Note we can't use Environment.Newline because we don't know that the // Command honors that. - strings[i] = strings[i].Replace("\r\n", "`r`n"); + strings[i] = strings[i].Replace("\r", "`r"); strings[i] = strings[i].Replace("\n", "`n"); // If the string won't fit in the column, append an ellipsis. From 474826e6dd3987041e94692f8ea4b1cfca758afb Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Tue, 29 Sep 2020 10:39:32 -0700 Subject: [PATCH 06/26] made column spacing tigher --- .../ConsoleGui.cs | 34 ++++++++++--------- .../GridViewDetails.cs | 2 +- ...icrosoft.PowerShell.ConsoleGuiTools.csproj | 2 +- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 78e76ad..0dd3ac5 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -14,8 +14,11 @@ namespace OutGridView.Cmdlet internal class ConsoleGui : IDisposable { private const string FILTER_LABEL = "Filter"; + // This adjusts the left margin of all controls + private const int FILTER_LABEL_X = 2; private bool _cancelled; private GridViewDataSource _itemSource; + private Label _filterLabel; private TextField _filterField; private ListView _listView; private ApplicationData _applicationData; @@ -28,9 +31,8 @@ public HashSet Start(ApplicationData applicationData) _gridViewDetails = new GridViewDetails { // If OutputMode is Single or Multiple, then we make items selectable. If we make them selectable, - // they have a 8 character addition of a checkbox (".....[ ]" or ".....( )") - // that we have to factor in. - ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? 8 : 4 + // 2 columns are required for the check/selection indicator and space. + ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? FILTER_LABEL_X + 2 : FILTER_LABEL_X }; Window win = AddTopLevelWindow(); @@ -189,25 +191,25 @@ private void CalculateColumnWidths(List gridHeaders) private void AddFilter(Window win) { - var filterLabel = new Label(FILTER_LABEL) + _filterLabel = new Label(FILTER_LABEL) { - X = 2 + X = FILTER_LABEL_X }; _filterField = new TextField(string.Empty) { - X = Pos.Right(filterLabel) + 1, - Y = Pos.Top(filterLabel), + X = Pos.Right(_filterLabel) + 1, + Y = Pos.Top(_filterLabel), CanFocus = true, - Width = Dim.Fill() - filterLabel.Text.Length + Width = Dim.Fill() - _filterLabel.Text.Length }; var filterErrorLabel = new Label(string.Empty) { - X = Pos.Right(filterLabel) + 1, - Y = Pos.Top(filterLabel) + 1, + X = Pos.Right(_filterLabel) + 1, + Y = Pos.Top(_filterLabel) + 1, ColorScheme = Colors.Base, - Width = Dim.Fill() - filterLabel.Text.Length + Width = Dim.Fill() - _filterLabel.Text.Length }; _filterField.TextChanged += (str) => @@ -232,14 +234,14 @@ private void AddFilter(Window win) } }; - win.Add(filterLabel, _filterField, filterErrorLabel); + win.Add(_filterLabel, _filterField, filterErrorLabel); } private void AddHeaders(Window win, List gridHeaders) { var header = new Label(GridViewHelpers.GetPaddedString( gridHeaders, - _gridViewDetails.ListViewOffset + _gridViewDetails.ListViewOffset - 1, + _gridViewDetails.ListViewOffset, _gridViewDetails.ListViewColumnWidths)) { X = 0, @@ -286,7 +288,7 @@ private void LoadData() valueList.Add(dataValue); } - string displayString = GridViewHelpers.GetPaddedString(valueList, _gridViewDetails.ListViewOffset, _gridViewDetails.ListViewColumnWidths); + string displayString = GridViewHelpers.GetPaddedString(valueList, 0, _gridViewDetails.ListViewColumnWidths); items.Add(new GridViewRow { @@ -304,8 +306,8 @@ private void AddRows(Window win) { _listView = new ListView(_itemSource) { - X = 3, - Y = 4, + X = Pos.Left(_filterLabel), + Y = Pos.Bottom(_filterLabel) + 3, // 1 for space, 1 for header, 1 for header underline Width = Dim.Fill(2), Height = Dim.Fill(2), AllowsMarking = _applicationData.OutputMode != OutputModeOption.None, diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDetails.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDetails.cs index 2b4a6c7..4841b7f 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDetails.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDetails.cs @@ -8,7 +8,7 @@ internal class GridViewDetails // Contains the width of each column in the grid view. public int[] ListViewColumnWidths { get; set; } - // Dictates where the grid should actually start considering + // Dictates where the header should actually start considering // some offset is needed to factor in the checkboxes public int ListViewOffset { get; set; } diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj index 9aa0f20..edb4ee5 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj @@ -6,7 +6,7 @@ - + From 3112550bf3cd4b97d13812d742952cd0e4f7fa6a Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Tue, 29 Sep 2020 11:06:48 -0700 Subject: [PATCH 07/26] update to new Terminal.gui package; no code changes --- .../Microsoft.PowerShell.ConsoleGuiTools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj index 9aa0f20..897219a 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj @@ -6,7 +6,7 @@ - + From 86a72b558dc36f6e242362300c648e3e506b1999 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Tue, 29 Sep 2020 11:40:27 -0700 Subject: [PATCH 08/26] removed excess padding on right --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 7ec8296..29bb520 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -171,7 +171,7 @@ private void CalculateColumnWidths(List gridHeaders) // if the total width is wider than the usable width, remove 1 from widest column until it fits // the gui loses 3 chars on the left and 2 chars on the right - _gridViewDetails.UsableWidth = Application.Top.Frame.Width - 3 - listViewColumnWidths.Length - _gridViewDetails.ListViewOffset - 2; + _gridViewDetails.UsableWidth = Application.Top.Frame.Width - 3 - listViewColumnWidths.Length - _gridViewDetails.ListViewOffset; int columnWidthsSum = listViewColumnWidths.Sum(); while (columnWidthsSum >= _gridViewDetails.UsableWidth) { From c95abc569c2bcf4e0fcfbfeec4a13d88a052306c Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Tue, 29 Sep 2020 11:47:43 -0700 Subject: [PATCH 09/26] removed excess rows at bottom --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 5 ++--- .../GridViewDataSource.cs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 29bb520..db866d1 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -170,8 +170,7 @@ private void CalculateColumnWidths(List gridHeaders) } // if the total width is wider than the usable width, remove 1 from widest column until it fits - // the gui loses 3 chars on the left and 2 chars on the right - _gridViewDetails.UsableWidth = Application.Top.Frame.Width - 3 - listViewColumnWidths.Length - _gridViewDetails.ListViewOffset; + _gridViewDetails.UsableWidth = Application.Top.Frame.Width - MARGIN_LEFT - listViewColumnWidths.Length - _gridViewDetails.ListViewOffset; int columnWidthsSum = listViewColumnWidths.Sum(); while (columnWidthsSum >= _gridViewDetails.UsableWidth) { @@ -311,7 +310,7 @@ private void AddRows(Window win) X = Pos.Left(_filterLabel), Y = Pos.Bottom(_filterLabel) + 3, // 1 for space, 1 for header, 1 for header underline Width = Dim.Fill(2), - Height = Dim.Fill(2), + Height = Dim.Fill(), AllowsMarking = _applicationData.OutputMode != OutputModeOption.None, AllowsMultipleSelection = _applicationData.OutputMode == OutputModeOption.Multiple, }; diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDataSource.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDataSource.cs index ff82fb5..f8047ce 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDataSource.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/GridViewDataSource.cs @@ -47,7 +47,7 @@ private void RenderUstr(ConsoleDriver driver, ustring ustr, int col, int line, i { (var rune, var size) = Utf8.DecodeRune(ustr, index, index - ustr.Length); var count = Rune.ColumnWidth(rune); - if (used + count >= width) break; + if (used + count > width) break; driver.AddRune(rune); used += count; index += size; From 142ffe5aaf95555c86a7bc0e8ca0e47e8c715e88 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Tue, 29 Sep 2020 13:33:48 -0700 Subject: [PATCH 10/26] status bar wsa occluding window --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index db866d1..00be36f 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -95,7 +95,7 @@ private Window AddTopLevelWindow() Y = 0, // By using Dim.Fill(), it will automatically resize without manual intervention Width = Dim.Fill(), - Height = Dim.Fill() + Height = Dim.Fill(1) }; Application.Top.Add(win); From 65c3c5ae6d8698bd7a07dfadd942d7c860c5ea39 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 30 Sep 2020 11:05:00 -0700 Subject: [PATCH 11/26] tweaks --- .../ConsoleGui.cs | 18 +++++++++++++----- .../OutConsoleGridviewCmdletCommand.cs | 7 +++++++ src/OutGridView.Models/ApplicationData.cs | 1 + 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 00be36f..92a1602 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -44,7 +44,10 @@ public HashSet Start(ApplicationData applicationData) List gridHeaders = _applicationData.DataTable.DataColumns.Select((c) => c.Label).ToList(); CalculateColumnWidths(gridHeaders); - AddFilter(win); + if (!_applicationData.MinUI) + { + AddFilter(win); + } AddHeaders(win, gridHeaders); // GridView row logic @@ -91,11 +94,11 @@ private Window AddTopLevelWindow() // Creates the top-level window to show var win = new Window(_applicationData.Title) { - X = 0, - Y = 0, + X = _applicationData.MinUI ? -1 : 0, + Y = _applicationData.MinUI ? -1 : 0, // By using Dim.Fill(), it will automatically resize without manual intervention - Width = Dim.Fill(), - Height = Dim.Fill(1) + Width = Dim.Fill(_applicationData.MinUI ? -1 : 0), + Height = Dim.Fill(_applicationData.MinUI ? -1 : 1) }; Application.Top.Add(win); @@ -138,6 +141,11 @@ private void AddStatusBar() } ); + if (_applicationData.MinUI) + { + statusBar.Visible = false; + } + Application.Top.Add(statusBar); } diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/OutConsoleGridviewCmdletCommand.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/OutConsoleGridviewCmdletCommand.cs index 3f8a623..f4f5a9b 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/OutConsoleGridviewCmdletCommand.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/OutConsoleGridviewCmdletCommand.cs @@ -52,6 +52,12 @@ public class OutConsoleGridViewCmdletCommand : PSCmdlet, IDisposable [Parameter()] public string Filter { set; get; } + /// + /// gets or sets the whether "minimum UI" mode will be enabled + /// + [Parameter()] + public SwitchParameter MinUI { set; get; } + #endregion Input Parameters // This method gets called once for each cmdlet in the pipeline when the pipeline starts executing @@ -133,6 +139,7 @@ protected override void EndProcessing() Title = Title ?? "Out-ConsoleGridView", OutputMode = OutputMode, Filter = Filter, + MinUI = MinUI, DataTable = dataTable }; diff --git a/src/OutGridView.Models/ApplicationData.cs b/src/OutGridView.Models/ApplicationData.cs index 954c9dd..d193e5a 100644 --- a/src/OutGridView.Models/ApplicationData.cs +++ b/src/OutGridView.Models/ApplicationData.cs @@ -12,6 +12,7 @@ public class ApplicationData public OutputModeOption OutputMode { get; set; } public bool PassThru { get; set; } public string Filter { get; set; } + public bool MinUI { get; set; } public DataTable DataTable { get; set; } } } From fd934e168ce6454164be2b7357c4e5c49f14886b Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Thu, 1 Oct 2020 10:54:12 -0700 Subject: [PATCH 12/26] fixed build scripts to only build ocgv --- .vscode/tasks.json | 7 ++++++- Build.ps1 | 12 ++++++++++-- GraphicalTools.build.ps1 | 4 +++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8af661d..0056896 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -16,7 +16,12 @@ "args": [ "-c", "Invoke-Build", - "Build" + // Build both modules + //"Build -ModuleName Microsoft.PowerShell.GraphicalTools, Microsoft.PowerShell.ConsoleGuiTools", + // Build only Out-GridView + //"Build -ModuleName Microsoft.PowerShell.GraphicalTools", + // Build only Out-ConsoleGridView + "Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools", ], "problemMatcher": "$msCompile", "group": { diff --git a/Build.ps1 b/Build.ps1 index 1398d79..e514a57 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -1,3 +1,11 @@ -Invoke-Build Build -pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.GraphicalTools'; Get-Process | Out-GridView -PassThru" +# Build script for buildling/testing from the commnad line. See tasks.json for how build is invoked within VS Code +# GraphicalTools includes two modules: Microsoft.PowerShell.GraphicalTools and Microsoft.PowerShell.ConsoleGuiTools +# To build them all leave -ModuleName off the `InvokeBuild` command (e.g. Invoke-Build Build). +# To build only one, specify it using the -ModuleName paramater (e.g. Invoke-Build Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools). + +# Build... +Invoke-Build Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools + +# Run what was built... +# pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.GraphicalTools'; Get-Process | Out-GridView -PassThru" pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-PSProfile | Out-ConsoleGridView -OutputMode Single -Title 'PS Profiles' -Filter power" \ No newline at end of file diff --git a/GraphicalTools.build.ps1 b/GraphicalTools.build.ps1 index dd3d3c7..dbbeef9 100644 --- a/GraphicalTools.build.ps1 +++ b/GraphicalTools.build.ps1 @@ -3,7 +3,9 @@ param( [ValidateSet("Debug", "Release")] [string]$Configuration = "Debug", - [string[]]$ModuleName = @( "Microsoft.PowerShell.GraphicalTools", "Microsoft.PowerShell.ConsoleGuiTools" ) + [string[]]$ModuleName = @( + "Microsoft.PowerShell.GraphicalTools", + "Microsoft.PowerShell.ConsoleGuiTools" ) ) $script:IsUnix = $PSVersionTable.PSEdition -and $PSVersionTable.PSEdition -eq "Core" -and !$IsWindows From 8e89181c487cf0a3ef20e8a2368f2c37f9cd17b1 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Thu, 1 Oct 2020 12:57:32 -0700 Subject: [PATCH 13/26] refactored to make logic more obvious --- Build.ps1 | 2 +- .../ConsoleGui.cs | 161 ++++++++++-------- 2 files changed, 93 insertions(+), 70 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index e514a57..b07618a 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -8,4 +8,4 @@ Invoke-Build Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools # Run what was built... # pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.GraphicalTools'; Get-Process | Out-GridView -PassThru" -pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-PSProfile | Out-ConsoleGridView -OutputMode Single -Title 'PS Profiles' -Filter power" \ No newline at end of file +pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-PSProfile | Out-ConsoleGridView -OutputMode Single -Title 'PS Profiles' -Filter ISE" \ No newline at end of file diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 92a1602..67cd7a2 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -19,10 +19,10 @@ internal class ConsoleGui : IDisposable // Width of Terminal.Gui ListView selection/check UI elements (old == 4, new == 2) private const int CHECK_WIDTH = 4; private bool _cancelled; - private GridViewDataSource _itemSource; private Label _filterLabel; private TextField _filterField; private ListView _listView; + private GridViewDataSource _itemSource; private ApplicationData _applicationData; private GridViewDetails _gridViewDetails; @@ -37,25 +37,36 @@ public HashSet Start(ApplicationData applicationData) ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? MARGIN_LEFT + CHECK_WIDTH : MARGIN_LEFT }; - Window win = AddTopLevelWindow(); - AddStatusBar(); + Window win = CreateTopLevelWindow(); - // GridView header logic + // Create the headers and calculate column widths based on the DataTable List gridHeaders = _applicationData.DataTable.DataColumns.Select((c) => c.Label).ToList(); CalculateColumnWidths(gridHeaders); + // Copy DataTable into the ListView's DataSource + _itemSource = LoadData(); + + // Add Filter UI if (!_applicationData.MinUI) { AddFilter(win); } - AddHeaders(win, gridHeaders); - // GridView row logic - LoadData(); - AddRows(win); + // Add Header UI + if (!_applicationData.MinUI) + { + AddHeaders(win, gridHeaders); + } + + // Add ListView + AddListView(win); + + // Status bar is where our key-bindings are handled + AddStatusBar(!_applicationData.MinUI); + + // If -Filter parameter is set, apply it. + ApplyFilter(); - _filterField.Text = _applicationData.Filter ?? string.Empty; - _filterField.CursorPosition = _filterField.Text.Length; // Run the GUI. Application.Run(); Application.Shutdown(); @@ -78,6 +89,40 @@ public HashSet Start(ApplicationData applicationData) return selectedIndexes; } + private GridViewDataSource LoadData() + { + var items = new List(); + int newIndex = 0; + for (int i = 0; i < _applicationData.DataTable.Data.Count; i++) + { + var dataTableRow = _applicationData.DataTable.Data[i]; + var valueList = new List(); + foreach (var dataTableColumn in _applicationData.DataTable.DataColumns) + { + string dataValue = dataTableRow.Values[dataTableColumn.ToString()].DisplayValue; + valueList.Add(dataValue); + } + + string displayString = GridViewHelpers.GetPaddedString(valueList, 0, _gridViewDetails.ListViewColumnWidths); + + items.Add(new GridViewRow + { + DisplayString = displayString, + OriginalIndex = i + }); + + newIndex++; + } + + return new GridViewDataSource(items); + } + + private void ApplyFilter(){ + List itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, _applicationData.Filter ?? string.Empty); + // Set the ListView to show only the subset defined by the filter + _listView.Source = new GridViewDataSource(itemList); + } + private void Accept() { Application.RequestStop(); @@ -89,7 +134,7 @@ private void Close() Application.RequestStop(); } - private Window AddTopLevelWindow() + private Window CreateTopLevelWindow() { // Creates the top-level window to show var win = new Window(_applicationData.Title) @@ -105,7 +150,7 @@ private Window AddTopLevelWindow() return win; } - private void AddStatusBar() + private void AddStatusBar(bool visible) { var statusBar = new StatusBar( _applicationData.OutputMode != OutputModeOption.None @@ -140,12 +185,7 @@ private void AddStatusBar() new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) } ); - - if (_applicationData.MinUI) - { - statusBar.Visible = false; - } - + statusBar.Visible = visible; Application.Top.Add(statusBar); } @@ -202,10 +242,11 @@ private void AddFilter(Window win) { _filterLabel = new Label(FILTER_LABEL) { - X = MARGIN_LEFT + X = MARGIN_LEFT, + Y = 0 }; - _filterField = new TextField(string.Empty) + _filterField = new TextField(_applicationData.Filter ?? string.Empty) { X = Pos.Right(_filterLabel) + 1, Y = Pos.Top(_filterLabel), @@ -230,9 +271,9 @@ private void AddFilter(Window win) filterErrorLabel.Text = " "; filterErrorLabel.ColorScheme = Colors.Base; filterErrorLabel.Redraw(filterErrorLabel.Bounds); + _applicationData.Filter = filterText; + ApplyFilter(); - List itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, filterText); - _listView.Source = new GridViewDataSource(itemList); } catch (Exception ex) { @@ -251,12 +292,16 @@ private void AddHeaders(Window win, List gridHeaders) var header = new Label(GridViewHelpers.GetPaddedString( gridHeaders, _gridViewDetails.ListViewOffset, - _gridViewDetails.ListViewColumnWidths)) + _gridViewDetails.ListViewColumnWidths)); + header.X = 0; + if (_applicationData.MinUI) { - X = 0, - Y = 2 - }; - + header.Y = 0; + } + else + { + header.Y = 2; + } win.Add(header); // This renders dashes under the header to make it more clear what is header and what is data @@ -274,54 +319,32 @@ private void AddHeaders(Window win, List gridHeaders) } } - var headerLine = new Label(headerLineText.ToString()) - { - X = 0, - Y = 3 - }; - - win.Add(headerLine); - } - - private void LoadData() - { - var items = new List(); - int newIndex = 0; - for (int i = 0; i < _applicationData.DataTable.Data.Count; i++) - { - var dataTableRow = _applicationData.DataTable.Data[i]; - var valueList = new List(); - foreach (var dataTableColumn in _applicationData.DataTable.DataColumns) - { - string dataValue = dataTableRow.Values[dataTableColumn.ToString()].DisplayValue; - valueList.Add(dataValue); - } - - string displayString = GridViewHelpers.GetPaddedString(valueList, 0, _gridViewDetails.ListViewColumnWidths); - - items.Add(new GridViewRow + if (!_applicationData.MinUI){ + var headerLine = new Label(headerLineText.ToString()) { - DisplayString = displayString, - OriginalIndex = i - }); - - newIndex++; + X = 0, + Y = Pos.Bottom(header) + }; + win.Add(headerLine); } - - _itemSource = new GridViewDataSource(items); } - private void AddRows(Window win) + private void AddListView(Window win) { - _listView = new ListView(_itemSource) + _listView = new ListView(_itemSource); + _listView.X = MARGIN_LEFT; + if (!_applicationData.MinUI) { - X = Pos.Left(_filterLabel), - Y = Pos.Bottom(_filterLabel) + 3, // 1 for space, 1 for header, 1 for header underline - Width = Dim.Fill(2), - Height = Dim.Fill(), - AllowsMarking = _applicationData.OutputMode != OutputModeOption.None, - AllowsMultipleSelection = _applicationData.OutputMode == OutputModeOption.Multiple, - }; + _listView.Y = Pos.Bottom(_filterLabel) + 3; // 1 for space, 1 for header, 1 for header underline + } + else + { + _listView.Y = 1; // 1 for space, 1 for header, 1 for header underline + } + _listView.Width = Dim.Fill(2); + _listView.Height = Dim.Fill(); + _listView.AllowsMarking = _applicationData.OutputMode != OutputModeOption.None; + _listView.AllowsMultipleSelection = _applicationData.OutputMode == OutputModeOption.Multiple; win.Add(_listView); } From e530f373e5c3be405ff42c007e2073eb9169512b Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Sat, 3 Oct 2020 09:41:31 -0700 Subject: [PATCH 14/26] removed orig files --- Build.ps1.orig | 16 - .../ConsoleGui.cs.orig | 417 ------------------ 2 files changed, 433 deletions(-) delete mode 100644 Build.ps1.orig delete mode 100644 src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig diff --git a/Build.ps1.orig b/Build.ps1.orig deleted file mode 100644 index e26b262..0000000 --- a/Build.ps1.orig +++ /dev/null @@ -1,16 +0,0 @@ -# Build script for buildling/testing from the commnad line. See tasks.json for how build is invoked within VS Code -# GraphicalTools includes two modules: Microsoft.PowerShell.GraphicalTools and Microsoft.PowerShell.ConsoleGuiTools -# To build them all leave -ModuleName off the `InvokeBuild` command (e.g. Invoke-Build Build). -# To build only one, specify it using the -ModuleName paramater (e.g. Invoke-Build Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools). - -# Build... -Invoke-Build Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools - -# Run what was built... -<<<<<<< HEAD -# pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.GraphicalTools'; Get-Process | Out-GridView -PassThru" -pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-PSProfile | Out-ConsoleGridView -OutputMode Single -Title 'PS Profiles' -Filter ISE" -======= -# pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.GraphicalTools'; Get-Module -all | Out-GridView -OutputMode Single -Title 'Imported Modules' -pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-Module -all | Out-ConsoleGridView -OutputMode Single -Title 'Imported Modules' -Filter power" ->>>>>>> master diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig deleted file mode 100644 index 7ebb5eb..0000000 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NStack; -using OutGridView.Models; -using Terminal.Gui; - -namespace OutGridView.Cmdlet -{ - internal class ConsoleGui : IDisposable - { - private const string FILTER_LABEL = "Filter"; - // This adjusts the left margin of all controls - private const int MARGIN_LEFT = 2; - // Width of Terminal.Gui ListView selection/check UI elements (old == 4, new == 2) - private const int CHECK_WIDTH = 4; - private bool _cancelled; -<<<<<<< HEAD -======= - private GridViewDataSource _itemSource; ->>>>>>> master - private Label _filterLabel; - private TextField _filterField; - private ListView _listView; - private GridViewDataSource _itemSource; - private ApplicationData _applicationData; - private GridViewDetails _gridViewDetails; - - public HashSet Start(ApplicationData applicationData) - { - Application.Init(); - _applicationData = applicationData; - _gridViewDetails = new GridViewDetails - { - // If OutputMode is Single or Multiple, then we make items selectable. If we make them selectable, - // 2 columns are required for the check/selection indicator and space. - ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? MARGIN_LEFT + CHECK_WIDTH : MARGIN_LEFT - }; - - Window win = CreateTopLevelWindow(); - - // Create the headers and calculate column widths based on the DataTable - List gridHeaders = _applicationData.DataTable.DataColumns.Select((c) => c.Label).ToList(); - CalculateColumnWidths(gridHeaders); - - // Copy DataTable into the ListView's DataSource - _itemSource = LoadData(); - - // Add Filter UI - if (!_applicationData.MinUI) - { - AddFilter(win); - } - - // Add Header UI - if (!_applicationData.MinUI) - { - AddHeaders(win, gridHeaders); - } - - // Add ListView - AddListView(win); - - // Status bar is where our key-bindings are handled - AddStatusBar(!_applicationData.MinUI); - - // If -Filter parameter is set, apply it. - ApplyFilter(); - - // Run the GUI. - Application.Run(); - Application.Shutdown(); - - // Return results of selection if required. - HashSet selectedIndexes = new HashSet(); - if (_cancelled) - { - return selectedIndexes; - } - - foreach (GridViewRow gvr in _itemSource.GridViewRowList) - { - if (gvr.IsMarked) - { - selectedIndexes.Add(gvr.OriginalIndex); - } - } - - return selectedIndexes; - } - - private GridViewDataSource LoadData() - { - var items = new List(); - int newIndex = 0; - for (int i = 0; i < _applicationData.DataTable.Data.Count; i++) - { - var dataTableRow = _applicationData.DataTable.Data[i]; - var valueList = new List(); - foreach (var dataTableColumn in _applicationData.DataTable.DataColumns) - { - string dataValue = dataTableRow.Values[dataTableColumn.ToString()].DisplayValue; - valueList.Add(dataValue); - } - - string displayString = GridViewHelpers.GetPaddedString(valueList, 0, _gridViewDetails.ListViewColumnWidths); - - items.Add(new GridViewRow - { - DisplayString = displayString, - OriginalIndex = i - }); - - newIndex++; - } - - return new GridViewDataSource(items); - } - - private void ApplyFilter(){ - List itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, _applicationData.Filter ?? string.Empty); - // Set the ListView to show only the subset defined by the filter - _listView.Source = new GridViewDataSource(itemList); - } - - private void Accept() - { - Application.RequestStop(); - } - - private void Close() - { - _cancelled = true; - Application.RequestStop(); - } - - private Window CreateTopLevelWindow() - { - // Creates the top-level window to show - var win = new Window(_applicationData.Title) - { - X = _applicationData.MinUI ? -1 : 0, - Y = _applicationData.MinUI ? -1 : 0, - // By using Dim.Fill(), it will automatically resize without manual intervention -<<<<<<< HEAD - Width = Dim.Fill(_applicationData.MinUI ? -1 : 0), - Height = Dim.Fill(_applicationData.MinUI ? -1 : 1) -======= - Width = Dim.Fill(), - Height = Dim.Fill(1) ->>>>>>> master - }; - - Application.Top.Add(win); - return win; - } - - private void AddStatusBar(bool visible) - { - var statusBar = new StatusBar( - _applicationData.OutputMode != OutputModeOption.None - ? new StatusItem[] - { - // Use Key.Unknown for SPACE with no delegate because ListView already - // handles SPACE - new StatusItem(Key.Unknown, "~SPACE~ Mark Item", null), - new StatusItem(Key.Enter, "~ENTER~ Accept", () => - { - if (Application.Top.MostFocused == _listView) - { - // If nothing was explicitly marked, we return the item that was selected - // when ENTER is pressed in Single mode. If something was previously selected - // (using SPACE) then honor that as the single item to return - if (_applicationData.OutputMode == OutputModeOption.Single && - _itemSource.GridViewRowList.Find(i => i.IsMarked) == null) - { - _listView.MarkUnmarkRow(); - } - Accept(); - } - else if (Application.Top.MostFocused == _filterField) - { - _listView.SetFocus(); - } - }), - new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) - } - : new StatusItem[] - { - new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) - } - ); - statusBar.Visible = visible; - Application.Top.Add(statusBar); - } - - private void CalculateColumnWidths(List gridHeaders) - { - _gridViewDetails.ListViewColumnWidths = new int[gridHeaders.Count]; - var listViewColumnWidths = _gridViewDetails.ListViewColumnWidths; - - for (int i = 0; i < gridHeaders.Count; i++) - { - listViewColumnWidths[i] = gridHeaders[i].Length; - } - - // calculate the width of each column based on longest string in each column for each row - foreach (var row in _applicationData.DataTable.Data) - { - int index = 0; - - // use half of the visible buffer height for the number of objects to inspect to calculate widths - foreach (var col in row.Values.Take(Application.Top.Frame.Height / 2)) - { - var len = col.Value.DisplayValue.Length; - if (len > listViewColumnWidths[index]) - { - listViewColumnWidths[index] = len; - } - - index++; - } - } - - // if the total width is wider than the usable width, remove 1 from widest column until it fits - _gridViewDetails.UsableWidth = Application.Top.Frame.Width - MARGIN_LEFT - listViewColumnWidths.Length - _gridViewDetails.ListViewOffset; - int columnWidthsSum = listViewColumnWidths.Sum(); - while (columnWidthsSum >= _gridViewDetails.UsableWidth) - { - int maxWidth = 0; - int maxIndex = 0; - for (int i = 0; i < listViewColumnWidths.Length; i++) - { - if (listViewColumnWidths[i] > maxWidth) - { - maxWidth = listViewColumnWidths[i]; - maxIndex = i; - } - } - - listViewColumnWidths[maxIndex]--; - columnWidthsSum--; - } - } - - private void AddFilter(Window win) - { - _filterLabel = new Label(FILTER_LABEL) - { -<<<<<<< HEAD - X = MARGIN_LEFT, - Y = 0 -======= - X = MARGIN_LEFT ->>>>>>> master - }; - - _filterField = new TextField(_applicationData.Filter ?? string.Empty) - { - X = Pos.Right(_filterLabel) + 1, - Y = Pos.Top(_filterLabel), - CanFocus = true, - Width = Dim.Fill() - _filterLabel.Text.Length - }; - - var filterErrorLabel = new Label(string.Empty) - { - X = Pos.Right(_filterLabel) + 1, - Y = Pos.Top(_filterLabel) + 1, - ColorScheme = Colors.Base, - Width = Dim.Fill() - _filterLabel.Text.Length - }; - - _filterField.TextChanged += (str) => - { - // str is the OLD value - string filterText = _filterField.Text?.ToString(); - try - { - filterErrorLabel.Text = " "; - filterErrorLabel.ColorScheme = Colors.Base; - filterErrorLabel.Redraw(filterErrorLabel.Bounds); - _applicationData.Filter = filterText; - ApplyFilter(); - - } - catch (Exception ex) - { - filterErrorLabel.Text = ex.Message; - filterErrorLabel.ColorScheme = Colors.Error; - filterErrorLabel.Redraw(filterErrorLabel.Bounds); - _listView.Source = _itemSource; - } - }; - - win.Add(_filterLabel, _filterField, filterErrorLabel); - } - - private void AddHeaders(Window win, List gridHeaders) - { - var header = new Label(GridViewHelpers.GetPaddedString( - gridHeaders, - _gridViewDetails.ListViewOffset, -<<<<<<< HEAD - _gridViewDetails.ListViewColumnWidths)); - header.X = 0; - if (_applicationData.MinUI) -======= - _gridViewDetails.ListViewColumnWidths)) ->>>>>>> master - { - header.Y = 0; - } - else - { - header.Y = 2; - } - win.Add(header); - - // This renders dashes under the header to make it more clear what is header and what is data - var headerLineText = new StringBuilder(); - foreach (char c in header.Text) - { - if (c.Equals(' ')) - { - headerLineText.Append(' '); - } - else - { - // When gui.cs supports text decorations, should replace this with just underlining the header - headerLineText.Append('-'); - } - } - -<<<<<<< HEAD - if (!_applicationData.MinUI){ - var headerLine = new Label(headerLineText.ToString()) -======= - var headerLine = new Label(headerLineText.ToString()) - { - X = 0, - Y = 3 - }; - - win.Add(headerLine); - } - - private void LoadData() - { - var items = new List(); - int newIndex = 0; - for (int i = 0; i < _applicationData.DataTable.Data.Count; i++) - { - var dataTableRow = _applicationData.DataTable.Data[i]; - var valueList = new List(); - foreach (var dataTableColumn in _applicationData.DataTable.DataColumns) - { - string dataValue = dataTableRow.Values[dataTableColumn.ToString()].DisplayValue; - valueList.Add(dataValue); - } - - string displayString = GridViewHelpers.GetPaddedString(valueList, 0, _gridViewDetails.ListViewColumnWidths); - - items.Add(new GridViewRow ->>>>>>> master - { - X = 0, - Y = Pos.Bottom(header) - }; - win.Add(headerLine); - } - } - - private void AddListView(Window win) - { - _listView = new ListView(_itemSource); - _listView.X = MARGIN_LEFT; - if (!_applicationData.MinUI) - { -<<<<<<< HEAD - _listView.Y = Pos.Bottom(_filterLabel) + 3; // 1 for space, 1 for header, 1 for header underline - } - else - { - _listView.Y = 1; // 1 for space, 1 for header, 1 for header underline - } - _listView.Width = Dim.Fill(2); - _listView.Height = Dim.Fill(); - _listView.AllowsMarking = _applicationData.OutputMode != OutputModeOption.None; - _listView.AllowsMultipleSelection = _applicationData.OutputMode == OutputModeOption.Multiple; -======= - X = Pos.Left(_filterLabel), - Y = Pos.Bottom(_filterLabel) + 3, // 1 for space, 1 for header, 1 for header underline - Width = Dim.Fill(2), - Height = Dim.Fill(), - AllowsMarking = _applicationData.OutputMode != OutputModeOption.None, - AllowsMultipleSelection = _applicationData.OutputMode == OutputModeOption.Multiple, - }; ->>>>>>> master - - win.Add(_listView); - } - - public void Dispose() - { - // By emitting this, we fix an issue where arrow keys don't work in the console - // because .NET requires application mode to support Arrow key escape sequences - // Esc[?1h - Set cursor key to application mode - // See http://ascii-table.com/ansi-escape-sequences-vt-100.php - Console.Write("\u001b[?1h"); - } - } -} From 25204bce067be424dc098550c5b4fa52b07ffa4b Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 3 Aug 2022 14:20:17 -0700 Subject: [PATCH 15/26] Upgrade to Terminal.Gui v1.7 --- .../Microsoft.PowerShell.ConsoleGuiTools.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj index 2bd6ac6..adeabd1 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj @@ -6,7 +6,7 @@ - + From b474f840307b3f4fd6cae4c07812821ca0909429 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 3 Aug 2022 15:22:15 -0700 Subject: [PATCH 16/26] removed border when minui is enabled --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 4464b42..5876fb9 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -141,11 +141,16 @@ private Window CreateTopLevelWindow() { X = _applicationData.MinUI ? -1 : 0, Y = _applicationData.MinUI ? -1 : 0, + // By using Dim.Fill(), it will automatically resize without manual intervention Width = Dim.Fill(_applicationData.MinUI ? -1 : 0), Height = Dim.Fill(_applicationData.MinUI ? -1 : 1) }; - + + if (_applicationData.MinUI) { + win.Border.BorderStyle = BorderStyle.None; + } + Application.Top.Add(win); return win; } From 75ca4d110b0075d8cc2e3b07f74c7c6c89576a96 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 3 Aug 2022 15:58:59 -0700 Subject: [PATCH 17/26] re-implemented feature post merge screw up --- .../ConsoleGui.cs | 96 ++++++++++++------- 1 file changed, 60 insertions(+), 36 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 5876fb9..73c90f0 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -62,7 +62,7 @@ public HashSet Start(ApplicationData applicationData) AddListView(win); // Status bar is where our key-bindings are handled - AddStatusBar(!_applicationData.MinUI); + AddStatusBar(); // If -Filter parameter is set, apply it. ApplyFilter(); @@ -117,7 +117,8 @@ private GridViewDataSource LoadData() return new GridViewDataSource(items); } - private void ApplyFilter(){ + private void ApplyFilter() + { List itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, _applicationData.Filter ?? string.Empty); // Set the ListView to show only the subset defined by the filter _listView.Source = new GridViewDataSource(itemList); @@ -146,50 +147,72 @@ private Window CreateTopLevelWindow() Width = Dim.Fill(_applicationData.MinUI ? -1 : 0), Height = Dim.Fill(_applicationData.MinUI ? -1 : 1) }; - - if (_applicationData.MinUI) { + + if (_applicationData.MinUI) + { win.Border.BorderStyle = BorderStyle.None; - } - + } + Application.Top.Add(win); return win; } private void AddStatusBar(bool visible) { - var statusBar = new StatusBar( - _applicationData.OutputMode != OutputModeOption.None - ? new StatusItem[] + var statusItems = new List(); + if (_applicationData.OutputMode != OutputModeOption.None) + { + // Use Key.Unknown for SPACE with no delegate because ListView already + // handles SPACE + statusItems.Add(new StatusItem(Key.Unknown, "~SPACE~ Select Item", null)); + } + + if (_applicationData.OutputMode == OutputModeOption.Multiple) + { + statusItems.Add(new StatusItem(Key.A | Key.CtrlMask, "~^A~ Select All", () => + { + // This selects only the items that match the Filter + var gvds = _listView.Source as GridViewDataSource; + gvds.GridViewRowList.ForEach(i => i.IsMarked = true); + _listView.SetNeedsDisplay(); + })); + + // Use Ctrl-N until Terminal.Gui supports ctrl-shift chords + statusItems.Add(new StatusItem(Key.N | Key.CtrlMask, "~^N~ Select None", () => + { + // This un-selects only the items that match the Filter + var gvds = _listView.Source as GridViewDataSource; + gvds.GridViewRowList.ForEach(i => i.IsMarked = false); + _listView.SetNeedsDisplay(); + })); + } + + if (_applicationData.OutputMode != OutputModeOption.None) + { + statusItems.Add(new StatusItem(Key.Enter, "~ENTER~ Accept", () => + { + if (Application.Top.MostFocused == _listView) { - // Use Key.Unknown for SPACE with no delegate because ListView already - // handles SPACE - new StatusItem(Key.Unknown, "~SPACE~ Mark Item", null), - new StatusItem(Key.Enter, "~ENTER~ Accept", () => + // If nothing was explicitly marked, we return the item that was selected + // when ENTER is pressed in Single mode. If something was previously selected + // (using SPACE) then honor that as the single item to return + if (_applicationData.OutputMode == OutputModeOption.Single && + _itemSource.GridViewRowList.Find(i => i.IsMarked) == null) { - if (Application.Top.MostFocused == _listView) - { - // If nothing was explicitly marked, we return the item that was selected - // when ENTER is pressed in Single mode. If something was previously selected - // (using SPACE) then honor that as the single item to return - if (_applicationData.OutputMode == OutputModeOption.Single && - _itemSource.GridViewRowList.Find(i => i.IsMarked) == null) - { - _listView.MarkUnmarkRow(); - } - Accept(); - } - else if (Application.Top.MostFocused == _filterField) - { - _listView.SetFocus(); - } - }), - new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) + _listView.MarkUnmarkRow(); + } + Accept(); } - : new StatusItem[] + else if (Application.Top.MostFocused == _filterField) { - new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) + _listView.SetFocus(); } - ); + })); + } + + statusItems.Add(new StatusItem(Key.Esc, "~ESC~ Close", () => Close())); + + var statusBar = new StatusBar(statusItems.ToArray()); statusBar.Visible = visible; Application.Top.Add(statusBar); } @@ -324,7 +347,8 @@ private void AddHeaders(Window win, List gridHeaders) } } - if (!_applicationData.MinUI){ + if (!_applicationData.MinUI) + { var headerLine = new Label(headerLineText.ToString()) { X = 0, @@ -341,7 +365,7 @@ private void AddListView(Window win) if (!_applicationData.MinUI) { _listView.Y = Pos.Bottom(_filterLabel) + 3; // 1 for space, 1 for header, 1 for header underline - } + } else { _listView.Y = 1; // 1 for space, 1 for header, 1 for header underline From fd6483e57f170fac14bdd6812b2a2efaebb788f2 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 3 Aug 2022 16:20:43 -0700 Subject: [PATCH 18/26] tweaks --- .../ConsoleGui.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 73c90f0..5642e96 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -62,7 +62,7 @@ public HashSet Start(ApplicationData applicationData) AddListView(win); // Status bar is where our key-bindings are handled - AddStatusBar(); + AddStatusBar(!_applicationData.MinUI); // If -Filter parameter is set, apply it. ApplyFilter(); @@ -169,7 +169,7 @@ private void AddStatusBar(bool visible) if (_applicationData.OutputMode == OutputModeOption.Multiple) { - statusItems.Add(new StatusItem(Key.A | Key.CtrlMask, "~^A~ Select All", () => + statusItems.Add(new StatusItem(Key.A | Key.CtrlMask, "~CTRL-A~ Select All", () => { // This selects only the items that match the Filter var gvds = _listView.Source as GridViewDataSource; @@ -177,8 +177,8 @@ private void AddStatusBar(bool visible) _listView.SetNeedsDisplay(); })); - // Use Ctrl-N until Terminal.Gui supports ctrl-shift chords - statusItems.Add(new StatusItem(Key.N | Key.CtrlMask, "~^N~ Select None", () => + // Ctrl-D is commonly used in GUIs for select-none + statusItems.Add(new StatusItem(Key.D | Key.CtrlMask, "~CTRL-D~ Select None", () => { // This un-selects only the items that match the Filter var gvds = _listView.Source as GridViewDataSource; @@ -240,7 +240,6 @@ private void CalculateColumnWidths(List gridHeaders) { listViewColumnWidths[index] = len; } - index++; } } @@ -282,6 +281,13 @@ private void AddFilter(Window win) Width = Dim.Fill() - _filterLabel.Text.Length }; + // TextField captures Ctrl-A (select all text) and Ctrl-D (delete backwards) + // In OCGV these are used for select-all/none of items. Selecting items is more + // common than editing the filter field so we turn them off in the filter textview. + // BACKSPACE still works for delete backwards + _filterField.ClearKeybinding(Key.A | Key.CtrlMask); + _filterField.ClearKeybinding(Key.D | Key.CtrlMask); + var filterErrorLabel = new Label(string.Empty) { X = Pos.Right(_filterLabel) + 1, From c06641e18487d2a8c4514ac09f3c425103508391 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 3 Aug 2022 16:54:38 -0700 Subject: [PATCH 19/26] improve VS code build and debug support --- .vscode/launch.json | 29 ++++++++++++----------------- .vscode/settings.json | 2 +- .vscode/tasks.json | 39 ++++++++++++++------------------------- README.md | 26 ++++++-------------------- 4 files changed, 33 insertions(+), 63 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 17f330a..fcc366c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,25 +6,20 @@ "configurations": [ { "name": ".NET Core Launch (console)", + "preLaunchTask": "build ConsoleGuiTools", "type": "coreclr", "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/Cmdlet/bin/Debug/net6.0/win10-x64/OutGridViewCmdlet.dll", - "args": [], - "cwd": "${workspaceFolder}/Cmdlet", - "console": "internalConsole", - "stopAtEntry": false - }, - { - "name": ".NET Core Launch (application)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/Application/bin/Debug/net6.0/win10-x64/OutGridViewApplication.dll", - "args": [], - "cwd": "${workspaceFolder}/Application", - "console": "internalConsole", - "stopAtEntry": false + "program": "pwsh", + "args": [ + "-NoExit", + "-NoProfile", + "-Command", + "Import-Module ${workspaceFolder}/module/Microsoft.PowerShell.ConsoleGuiTools" + ], + "cwd": "${workspaceFolder}", + "console": "integratedTerminal", + "stopAtEntry": false, + "justMyCode": false }, { "name": ".NET Core Attach", diff --git a/.vscode/settings.json b/.vscode/settings.json index 351aacc..d528a62 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { "files.associations": { - "**/.vsts-ci/**/*.yml":"azure-pipelines" + "**/.vsts-ci/**/*.yml":"azure-pipelines", } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0056896..e0a35c4 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,33 +1,22 @@ -{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + { "version": "2.0.0", "tasks": [ { - "label": "build", - "osx": { - "command": "/usr/local/bin/pwsh" - }, - "windows": { - "command": "pwsh.exe" - }, - "linux": { - "command": "/usr/local/bin/pwsh" - }, - "type": "process", + "label": "build ConsoleGuiTools", + "command": "Invoke-Build", + "type": "shell", "args": [ - "-c", - "Invoke-Build", - // Build both modules - //"Build -ModuleName Microsoft.PowerShell.GraphicalTools, Microsoft.PowerShell.ConsoleGuiTools", - // Build only Out-GridView - //"Build -ModuleName Microsoft.PowerShell.GraphicalTools", - // Build only Out-ConsoleGridView - "Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools", + "Build", + "-Module", + "Microsoft.PowerShell.ConsoleGuiTools" ], - "problemMatcher": "$msCompile", - "group": { - "kind": "build", - "isDefault": true - } + "group": "build", + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" } ] } \ No newline at end of file diff --git a/README.md b/README.md index d762d96..5a3fcd9 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,9 @@ to view and filter objects graphically. ## Development -### 1. Install PowerShell 7.1+ +### 1. Install PowerShell 7.2+ -Install PowerShell 7.1+ with [these instructions](https://github.com/PowerShell/PowerShell#get-powershell). +Install PowerShell 7.2+ with [these instructions](https://github.com/PowerShell/PowerShell#get-powershell). ### 2. Clone the GitHub repository @@ -72,29 +72,15 @@ PS ./GraphicalTools> code . Build by hitting `Ctrl-Shift-B` in VS Code. -To debug: +Set a breakpoint and hit `F5` to start the debugger. -In a PowerShell session in the `./GraphicalTools` directory, run `pwsh` (thus -nesting PowerShell). - -Then do the folowing: +Click on the VS Code "TERMINAL" tab and type your command that starts `Out-ConsoleGridView`, e.g. ```powershell -Import-Module ./module/Microsoft.PowerShell.ConsoleGuiTools -$pid +ls | ocgv ``` -This will import the latest built DLL and output the process ID you'll need -for debugging. Copy this ID to the clipboard. - -In VScode, set your breakpoints, etc. Then hit `F5`. In the VScode search -box, paste the value printed by `$pid`. You'll see something like `pwsh.exe -18328`. Click that and the debug session will start. - -In the PowerShell session run your commands; breakpoints will be hit, etc. - -When done, run `exit` to exit the nested PowerShell and run `pwsh` again. -This unloads the DLL. Repeat. +Your breakpoint should be hit. ## Contributions Welcome From 6833168b04951c54741b6796f8bb2d16bb7c8919 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 3 Aug 2022 17:40:25 -0700 Subject: [PATCH 20/26] On exit, ensure only visible marked items are output --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 2ad6f0d..dd42dc6 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -64,7 +64,10 @@ public HashSet Start(ApplicationData applicationData) return selectedIndexes; } - foreach (GridViewRow gvr in _itemSource.GridViewRowList) + // Ensure that only items that are marked AND not filtered out + // get returned (See Issue #121) + List itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, _filterField.Text.ToString()); + foreach (GridViewRow gvr in itemList) { if (gvr.IsMarked) { From ac54e61e43f64ed000223449a261f1deeb44e51b Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Wed, 3 Aug 2022 20:10:29 -0600 Subject: [PATCH 21/26] Fixes 87 --- src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs index 2ad6f0d..c600b9d 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs @@ -53,6 +53,9 @@ public HashSet Start(ApplicationData applicationData) _filterField.Text = _applicationData.Filter ?? string.Empty; _filterField.CursorPosition = _filterField.Text.Length; + + _listView.SetFocus(); + // Run the GUI. Application.Run(); Application.Shutdown(); From 3efc919b6f8eca936ebd29f45ef2b6a30e3f90a9 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 17 Aug 2022 14:17:17 -0700 Subject: [PATCH 22/26] Tweaked build to have a better test at end --- Build.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index 2789206..aac77f1 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -6,6 +6,8 @@ # Build... Invoke-Build Build -ModuleName Microsoft.PowerShell.ConsoleGuiTools -# Run what was built... -# pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.GraphicalTools'; Get-Module -all | Out-GridView -OutputMode Single -Title 'Imported Modules' -pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-Module -all | Out-ConsoleGridView -OutputMode Single -Title 'Imported Modules' -Filter power" +# Run what was built as a bit of test of: +# - Scale: recursive ls of the project +# - Filter: proving regex works +# - SelectMultiple +pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-ChildItem -Recurse | Out-ConsoleGridView -OutputMode Multiple -Title 'Imported Modules' -Filter \.xml" From 59e1cf7c4d3c03287d8d178bc63f533b4b7de817 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 17 Aug 2022 14:31:02 -0700 Subject: [PATCH 23/26] merge --- .../ConsoleGui.cs.orig | 349 ++++++++++++++++++ ...oft.PowerShell.ConsoleGuiTools.csproj.orig | 26 ++ 2 files changed, 375 insertions(+) create mode 100644 src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig create mode 100644 src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig new file mode 100644 index 0000000..ff5a97b --- /dev/null +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig @@ -0,0 +1,349 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NStack; +using OutGridView.Models; +using Terminal.Gui; + +namespace OutGridView.Cmdlet +{ + internal class ConsoleGui : IDisposable + { + private const string FILTER_LABEL = "Filter"; + // This adjusts the left margin of all controls +<<<<<<< HEAD + private const int MARGIN_LEFT = 2; + // Width of Terminal.Gui ListView selection/check UI elements (old == 4, new == 2) + private const int CHECK_WIDTH = 4; +======= + private const int FILTER_LABEL_X = 2; +>>>>>>> 474826e (made column spacing tigher) + private bool _cancelled; + private GridViewDataSource _itemSource; + private Label _filterLabel; + private TextField _filterField; + private ListView _listView; + private ApplicationData _applicationData; + private GridViewDetails _gridViewDetails; + + public HashSet Start(ApplicationData applicationData) + { + Application.Init(); + _applicationData = applicationData; + _gridViewDetails = new GridViewDetails + { + // If OutputMode is Single or Multiple, then we make items selectable. If we make them selectable, + // 2 columns are required for the check/selection indicator and space. +<<<<<<< HEAD + ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? MARGIN_LEFT + CHECK_WIDTH : MARGIN_LEFT +======= + ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? FILTER_LABEL_X + 2 : FILTER_LABEL_X +>>>>>>> 474826e (made column spacing tigher) + }; + + Window win = AddTopLevelWindow(); + AddStatusBar(); + + // GridView header logic + List gridHeaders = _applicationData.DataTable.DataColumns.Select((c) => c.Label).ToList(); + CalculateColumnWidths(gridHeaders); + + AddFilter(win); + AddHeaders(win, gridHeaders); + + // GridView row logic + LoadData(); + AddRows(win); + + _filterField.Text = _applicationData.Filter ?? string.Empty; + _filterField.CursorPosition = _filterField.Text.Length; + // Run the GUI. + Application.Run(); + Application.Shutdown(); + + // Return results of selection if required. + HashSet selectedIndexes = new HashSet(); + if (_cancelled) + { + return selectedIndexes; + } + + foreach (GridViewRow gvr in _itemSource.GridViewRowList) + { + if (gvr.IsMarked) + { + selectedIndexes.Add(gvr.OriginalIndex); + } + } + + return selectedIndexes; + } + + private void Accept() + { + Application.RequestStop(); + } + + private void Close() + { + _cancelled = true; + Application.RequestStop(); + } + + private Window AddTopLevelWindow() + { + // Creates the top-level window to show + var win = new Window(_applicationData.Title) + { + X = 0, + Y = 0, + // By using Dim.Fill(), it will automatically resize without manual intervention + Width = Dim.Fill(), + Height = Dim.Fill(1) + }; + + Application.Top.Add(win); + return win; + } + + private void AddStatusBar() + { + var statusBar = new StatusBar( + _applicationData.OutputMode != OutputModeOption.None + ? new StatusItem[] + { + // Use Key.Unknown for SPACE with no delegate because ListView already + // handles SPACE + new StatusItem(Key.Unknown, "~SPACE~ Mark Item", null), + new StatusItem(Key.Enter, "~ENTER~ Accept", () => + { + if (Application.Top.MostFocused == _listView) + { + // If nothing was explicitly marked, we return the item that was selected + // when ENTER is pressed in Single mode. If something was previously selected + // (using SPACE) then honor that as the single item to return + if (_applicationData.OutputMode == OutputModeOption.Single && + _itemSource.GridViewRowList.Find(i => i.IsMarked) == null) + { + _listView.MarkUnmarkRow(); + } + Accept(); + } + else if (Application.Top.MostFocused == _filterField) + { + _listView.SetFocus(); + } + }), + new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) + } + : new StatusItem[] + { + new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) + } + ); + + Application.Top.Add(statusBar); + } + + private void CalculateColumnWidths(List gridHeaders) + { + _gridViewDetails.ListViewColumnWidths = new int[gridHeaders.Count]; + var listViewColumnWidths = _gridViewDetails.ListViewColumnWidths; + + for (int i = 0; i < gridHeaders.Count; i++) + { + listViewColumnWidths[i] = gridHeaders[i].Length; + } + + // calculate the width of each column based on longest string in each column for each row + foreach (var row in _applicationData.DataTable.Data) + { + int index = 0; + + // use half of the visible buffer height for the number of objects to inspect to calculate widths + foreach (var col in row.Values.Take(Application.Top.Frame.Height / 2)) + { + var len = col.Value.DisplayValue.Length; + if (len > listViewColumnWidths[index]) + { + listViewColumnWidths[index] = len; + } + + index++; + } + } + + // if the total width is wider than the usable width, remove 1 from widest column until it fits + _gridViewDetails.UsableWidth = Application.Top.Frame.Width - MARGIN_LEFT - listViewColumnWidths.Length - _gridViewDetails.ListViewOffset; + int columnWidthsSum = listViewColumnWidths.Sum(); + while (columnWidthsSum >= _gridViewDetails.UsableWidth) + { + int maxWidth = 0; + int maxIndex = 0; + for (int i = 0; i < listViewColumnWidths.Length; i++) + { + if (listViewColumnWidths[i] > maxWidth) + { + maxWidth = listViewColumnWidths[i]; + maxIndex = i; + } + } + + listViewColumnWidths[maxIndex]--; + columnWidthsSum--; + } + } + + private void AddFilter(Window win) + { + _filterLabel = new Label(FILTER_LABEL) + { +<<<<<<< HEAD + X = MARGIN_LEFT +======= + X = FILTER_LABEL_X +>>>>>>> 474826e (made column spacing tigher) + }; + + _filterField = new TextField(string.Empty) + { + X = Pos.Right(_filterLabel) + 1, + Y = Pos.Top(_filterLabel), + CanFocus = true, + Width = Dim.Fill() - _filterLabel.Text.Length + }; + + var filterErrorLabel = new Label(string.Empty) + { + X = Pos.Right(_filterLabel) + 1, + Y = Pos.Top(_filterLabel) + 1, + ColorScheme = Colors.Base, + Width = Dim.Fill() - _filterLabel.Text.Length + }; + + _filterField.TextChanged += (str) => + { + // str is the OLD value + string filterText = _filterField.Text?.ToString(); + try + { + filterErrorLabel.Text = " "; + filterErrorLabel.ColorScheme = Colors.Base; + filterErrorLabel.Redraw(filterErrorLabel.Bounds); + + List itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, filterText); + _listView.Source = new GridViewDataSource(itemList); + } + catch (Exception ex) + { + filterErrorLabel.Text = ex.Message; + filterErrorLabel.ColorScheme = Colors.Error; + filterErrorLabel.Redraw(filterErrorLabel.Bounds); + _listView.Source = _itemSource; + } + }; + + win.Add(_filterLabel, _filterField, filterErrorLabel); + } + + private void AddHeaders(Window win, List gridHeaders) + { + var header = new Label(GridViewHelpers.GetPaddedString( + gridHeaders, + _gridViewDetails.ListViewOffset, + _gridViewDetails.ListViewColumnWidths)) + { + X = 0, + Y = 2 + }; + + win.Add(header); + + // This renders dashes under the header to make it more clear what is header and what is data + var headerLineText = new StringBuilder(); + foreach (char c in header.Text) + { + if (c.Equals(' ')) + { + headerLineText.Append(' '); + } + else + { + // When gui.cs supports text decorations, should replace this with just underlining the header + headerLineText.Append('-'); + } + } + + var headerLine = new Label(headerLineText.ToString()) + { + X = 0, + Y = 3 + }; + + win.Add(headerLine); + } + + private void LoadData() + { + var items = new List(); + int newIndex = 0; + for (int i = 0; i < _applicationData.DataTable.Data.Count; i++) + { + var dataTableRow = _applicationData.DataTable.Data[i]; + var valueList = new List(); + foreach (var dataTableColumn in _applicationData.DataTable.DataColumns) + { + string dataValue = dataTableRow.Values[dataTableColumn.ToString()].DisplayValue; + valueList.Add(dataValue); + } + + string displayString = GridViewHelpers.GetPaddedString(valueList, 0, _gridViewDetails.ListViewColumnWidths); + + items.Add(new GridViewRow + { + DisplayString = displayString, + OriginalIndex = i + }); + + newIndex++; + } + + _itemSource = new GridViewDataSource(items); + } + + private void AddRows(Window win) + { + _listView = new ListView(_itemSource) + { + X = Pos.Left(_filterLabel), + Y = Pos.Bottom(_filterLabel) + 3, // 1 for space, 1 for header, 1 for header underline + Width = Dim.Fill(2), + Height = Dim.Fill(), + AllowsMarking = _applicationData.OutputMode != OutputModeOption.None, + AllowsMultipleSelection = _applicationData.OutputMode == OutputModeOption.Multiple, + }; + + win.Add(_listView); + } + + public void Dispose() + { + if (!Console.IsInputRedirected) + { + // By emitting this, we fix two issues: + // 1. An issue where arrow keys don't work in the console because .NET + // requires application mode to support Arrow key escape sequences. + // Esc[?1h sets the cursor key to application mode + // See http://ascii-table.com/ansi-escape-sequences-vt-100.php + // 2. An issue where moving the mouse causes characters to show up because + // mouse tracking is still on. Esc[?1003l turns it off. + // See https://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking + Console.Write("\u001b[?1h\u001b[?1003l"); + } + } + } +} diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig new file mode 100644 index 0000000..6f48f24 --- /dev/null +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig @@ -0,0 +1,26 @@ + + + + net6.0 + + + +<<<<<<< HEAD + + + +======= + + + +>>>>>>> 474826e (made column spacing tigher) + + + + + + + + + + From ef2c3847f96d1a76b237412f2490718e597a18fd Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 17 Aug 2022 14:33:09 -0700 Subject: [PATCH 24/26] removed merge artifacts --- .../ConsoleGui.cs.orig | 349 ------------------ ...oft.PowerShell.ConsoleGuiTools.csproj.orig | 26 -- 2 files changed, 375 deletions(-) delete mode 100644 src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig delete mode 100644 src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig b/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig deleted file mode 100644 index ff5a97b..0000000 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs.orig +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using NStack; -using OutGridView.Models; -using Terminal.Gui; - -namespace OutGridView.Cmdlet -{ - internal class ConsoleGui : IDisposable - { - private const string FILTER_LABEL = "Filter"; - // This adjusts the left margin of all controls -<<<<<<< HEAD - private const int MARGIN_LEFT = 2; - // Width of Terminal.Gui ListView selection/check UI elements (old == 4, new == 2) - private const int CHECK_WIDTH = 4; -======= - private const int FILTER_LABEL_X = 2; ->>>>>>> 474826e (made column spacing tigher) - private bool _cancelled; - private GridViewDataSource _itemSource; - private Label _filterLabel; - private TextField _filterField; - private ListView _listView; - private ApplicationData _applicationData; - private GridViewDetails _gridViewDetails; - - public HashSet Start(ApplicationData applicationData) - { - Application.Init(); - _applicationData = applicationData; - _gridViewDetails = new GridViewDetails - { - // If OutputMode is Single or Multiple, then we make items selectable. If we make them selectable, - // 2 columns are required for the check/selection indicator and space. -<<<<<<< HEAD - ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? MARGIN_LEFT + CHECK_WIDTH : MARGIN_LEFT -======= - ListViewOffset = _applicationData.OutputMode != OutputModeOption.None ? FILTER_LABEL_X + 2 : FILTER_LABEL_X ->>>>>>> 474826e (made column spacing tigher) - }; - - Window win = AddTopLevelWindow(); - AddStatusBar(); - - // GridView header logic - List gridHeaders = _applicationData.DataTable.DataColumns.Select((c) => c.Label).ToList(); - CalculateColumnWidths(gridHeaders); - - AddFilter(win); - AddHeaders(win, gridHeaders); - - // GridView row logic - LoadData(); - AddRows(win); - - _filterField.Text = _applicationData.Filter ?? string.Empty; - _filterField.CursorPosition = _filterField.Text.Length; - // Run the GUI. - Application.Run(); - Application.Shutdown(); - - // Return results of selection if required. - HashSet selectedIndexes = new HashSet(); - if (_cancelled) - { - return selectedIndexes; - } - - foreach (GridViewRow gvr in _itemSource.GridViewRowList) - { - if (gvr.IsMarked) - { - selectedIndexes.Add(gvr.OriginalIndex); - } - } - - return selectedIndexes; - } - - private void Accept() - { - Application.RequestStop(); - } - - private void Close() - { - _cancelled = true; - Application.RequestStop(); - } - - private Window AddTopLevelWindow() - { - // Creates the top-level window to show - var win = new Window(_applicationData.Title) - { - X = 0, - Y = 0, - // By using Dim.Fill(), it will automatically resize without manual intervention - Width = Dim.Fill(), - Height = Dim.Fill(1) - }; - - Application.Top.Add(win); - return win; - } - - private void AddStatusBar() - { - var statusBar = new StatusBar( - _applicationData.OutputMode != OutputModeOption.None - ? new StatusItem[] - { - // Use Key.Unknown for SPACE with no delegate because ListView already - // handles SPACE - new StatusItem(Key.Unknown, "~SPACE~ Mark Item", null), - new StatusItem(Key.Enter, "~ENTER~ Accept", () => - { - if (Application.Top.MostFocused == _listView) - { - // If nothing was explicitly marked, we return the item that was selected - // when ENTER is pressed in Single mode. If something was previously selected - // (using SPACE) then honor that as the single item to return - if (_applicationData.OutputMode == OutputModeOption.Single && - _itemSource.GridViewRowList.Find(i => i.IsMarked) == null) - { - _listView.MarkUnmarkRow(); - } - Accept(); - } - else if (Application.Top.MostFocused == _filterField) - { - _listView.SetFocus(); - } - }), - new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) - } - : new StatusItem[] - { - new StatusItem(Key.Esc, "~ESC~ Close", () => Close()) - } - ); - - Application.Top.Add(statusBar); - } - - private void CalculateColumnWidths(List gridHeaders) - { - _gridViewDetails.ListViewColumnWidths = new int[gridHeaders.Count]; - var listViewColumnWidths = _gridViewDetails.ListViewColumnWidths; - - for (int i = 0; i < gridHeaders.Count; i++) - { - listViewColumnWidths[i] = gridHeaders[i].Length; - } - - // calculate the width of each column based on longest string in each column for each row - foreach (var row in _applicationData.DataTable.Data) - { - int index = 0; - - // use half of the visible buffer height for the number of objects to inspect to calculate widths - foreach (var col in row.Values.Take(Application.Top.Frame.Height / 2)) - { - var len = col.Value.DisplayValue.Length; - if (len > listViewColumnWidths[index]) - { - listViewColumnWidths[index] = len; - } - - index++; - } - } - - // if the total width is wider than the usable width, remove 1 from widest column until it fits - _gridViewDetails.UsableWidth = Application.Top.Frame.Width - MARGIN_LEFT - listViewColumnWidths.Length - _gridViewDetails.ListViewOffset; - int columnWidthsSum = listViewColumnWidths.Sum(); - while (columnWidthsSum >= _gridViewDetails.UsableWidth) - { - int maxWidth = 0; - int maxIndex = 0; - for (int i = 0; i < listViewColumnWidths.Length; i++) - { - if (listViewColumnWidths[i] > maxWidth) - { - maxWidth = listViewColumnWidths[i]; - maxIndex = i; - } - } - - listViewColumnWidths[maxIndex]--; - columnWidthsSum--; - } - } - - private void AddFilter(Window win) - { - _filterLabel = new Label(FILTER_LABEL) - { -<<<<<<< HEAD - X = MARGIN_LEFT -======= - X = FILTER_LABEL_X ->>>>>>> 474826e (made column spacing tigher) - }; - - _filterField = new TextField(string.Empty) - { - X = Pos.Right(_filterLabel) + 1, - Y = Pos.Top(_filterLabel), - CanFocus = true, - Width = Dim.Fill() - _filterLabel.Text.Length - }; - - var filterErrorLabel = new Label(string.Empty) - { - X = Pos.Right(_filterLabel) + 1, - Y = Pos.Top(_filterLabel) + 1, - ColorScheme = Colors.Base, - Width = Dim.Fill() - _filterLabel.Text.Length - }; - - _filterField.TextChanged += (str) => - { - // str is the OLD value - string filterText = _filterField.Text?.ToString(); - try - { - filterErrorLabel.Text = " "; - filterErrorLabel.ColorScheme = Colors.Base; - filterErrorLabel.Redraw(filterErrorLabel.Bounds); - - List itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, filterText); - _listView.Source = new GridViewDataSource(itemList); - } - catch (Exception ex) - { - filterErrorLabel.Text = ex.Message; - filterErrorLabel.ColorScheme = Colors.Error; - filterErrorLabel.Redraw(filterErrorLabel.Bounds); - _listView.Source = _itemSource; - } - }; - - win.Add(_filterLabel, _filterField, filterErrorLabel); - } - - private void AddHeaders(Window win, List gridHeaders) - { - var header = new Label(GridViewHelpers.GetPaddedString( - gridHeaders, - _gridViewDetails.ListViewOffset, - _gridViewDetails.ListViewColumnWidths)) - { - X = 0, - Y = 2 - }; - - win.Add(header); - - // This renders dashes under the header to make it more clear what is header and what is data - var headerLineText = new StringBuilder(); - foreach (char c in header.Text) - { - if (c.Equals(' ')) - { - headerLineText.Append(' '); - } - else - { - // When gui.cs supports text decorations, should replace this with just underlining the header - headerLineText.Append('-'); - } - } - - var headerLine = new Label(headerLineText.ToString()) - { - X = 0, - Y = 3 - }; - - win.Add(headerLine); - } - - private void LoadData() - { - var items = new List(); - int newIndex = 0; - for (int i = 0; i < _applicationData.DataTable.Data.Count; i++) - { - var dataTableRow = _applicationData.DataTable.Data[i]; - var valueList = new List(); - foreach (var dataTableColumn in _applicationData.DataTable.DataColumns) - { - string dataValue = dataTableRow.Values[dataTableColumn.ToString()].DisplayValue; - valueList.Add(dataValue); - } - - string displayString = GridViewHelpers.GetPaddedString(valueList, 0, _gridViewDetails.ListViewColumnWidths); - - items.Add(new GridViewRow - { - DisplayString = displayString, - OriginalIndex = i - }); - - newIndex++; - } - - _itemSource = new GridViewDataSource(items); - } - - private void AddRows(Window win) - { - _listView = new ListView(_itemSource) - { - X = Pos.Left(_filterLabel), - Y = Pos.Bottom(_filterLabel) + 3, // 1 for space, 1 for header, 1 for header underline - Width = Dim.Fill(2), - Height = Dim.Fill(), - AllowsMarking = _applicationData.OutputMode != OutputModeOption.None, - AllowsMultipleSelection = _applicationData.OutputMode == OutputModeOption.Multiple, - }; - - win.Add(_listView); - } - - public void Dispose() - { - if (!Console.IsInputRedirected) - { - // By emitting this, we fix two issues: - // 1. An issue where arrow keys don't work in the console because .NET - // requires application mode to support Arrow key escape sequences. - // Esc[?1h sets the cursor key to application mode - // See http://ascii-table.com/ansi-escape-sequences-vt-100.php - // 2. An issue where moving the mouse causes characters to show up because - // mouse tracking is still on. Esc[?1003l turns it off. - // See https://www.xfree86.org/current/ctlseqs.html#Mouse%20Tracking - Console.Write("\u001b[?1h\u001b[?1003l"); - } - } - } -} diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig deleted file mode 100644 index 6f48f24..0000000 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj.orig +++ /dev/null @@ -1,26 +0,0 @@ - - - - net6.0 - - - -<<<<<<< HEAD - - - -======= - - - ->>>>>>> 474826e (made column spacing tigher) - - - - - - - - - - From 20ce94567b54124ecdcd27adcf8c0d9a564459a5 Mon Sep 17 00:00:00 2001 From: Charlie Kindel Date: Wed, 17 Aug 2022 14:33:42 -0700 Subject: [PATCH 25/26] exclude .orig files (merge artifacts) --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index cacf0ef..995941e 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ module/ # Ignore package Microsoft.PowerShell.GraphicalTools.zip Microsoft.PowerShell.ConsoleGuiTools.zip + +# git artifacts +*.orig \ No newline at end of file From 04ee196070e09ee33a3718c665d33fa7b5912231 Mon Sep 17 00:00:00 2001 From: Tig Kindel Date: Fri, 28 Apr 2023 17:09:11 +0200 Subject: [PATCH 26/26] Updates the project to the latest Terminal.Gui release (v1.10). --- .../Microsoft.PowerShell.ConsoleGuiTools.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj index 92854d2..577e6fe 100644 --- a/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj +++ b/src/Microsoft.PowerShell.ConsoleGuiTools/Microsoft.PowerShell.ConsoleGuiTools.csproj @@ -5,8 +5,8 @@ - - + +