Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
6af1903
Fixed #58: Multi-line commands rendering wrong
tig Mar 11, 2020
a69373e
Fixed #58 - Newlines in commands render incorrectly
tig Mar 11, 2020
f155262
Added debug instructions to readme
tig Mar 11, 2020
440833e
Update src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs
tig Mar 11, 2020
dd2e765
simplified stripping of newline/linefeed
tig Mar 12, 2020
e66f614
Merge branch 'master' of tig:tig/GraphicalTools
tig Mar 14, 2020
ebcb92f
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Apr 12, 2020
dce01c8
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Apr 17, 2020
a2c0238
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Apr 19, 2020
2184386
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Apr 19, 2020
668c5c5
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Apr 20, 2020
9d2c85a
Merge branch 'master' of tig:tig/GraphicalTools
tig May 13, 2020
d15fb38
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Aug 24, 2020
49b9559
Merge branch 'master' of tig:tig/GraphicalTools into master
tig Sep 25, 2020
4175dc0
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Sep 27, 2020
53b1203
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Sep 27, 2020
941cef7
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Sep 29, 2020
06c8a1f
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Sep 29, 2020
474826e
made column spacing tigher
tig Sep 29, 2020
3112550
update to new Terminal.gui package; no code changes
tig Sep 29, 2020
67911d3
tweaked widths
tig Sep 29, 2020
86a72b5
removed excess padding on right
tig Sep 29, 2020
c95abc5
removed excess rows at bottom
tig Sep 29, 2020
869b578
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Sep 29, 2020
bed1c3d
Merge branch 'master' into tighter_columns
tig Sep 29, 2020
142ffe5
status bar wsa occluding window
tig Sep 29, 2020
65c3c5a
tweaks
tig Sep 30, 2020
fd934e1
fixed build scripts to only build ocgv
tig Oct 1, 2020
f58d592
Merge branch 'tighter_columns' into minui
tig Oct 1, 2020
8e89181
refactored to make logic more obvious
tig Oct 1, 2020
1137d7d
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Oct 3, 2020
e965afd
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Oct 3, 2020
967b503
merged
tig Oct 3, 2020
e530f37
removed orig files
tig Oct 3, 2020
4188094
Merge branch 'master' of tig:PowerShell/GraphicalTools into master
tig Oct 5, 2020
797de5e
Merge branch 'master' of tig:tig/GraphicalTools into master
tig Oct 11, 2020
be4bea2
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Oct 21, 2020
85d8578
Merge branch 'master' of tig:tig/GraphicalTools
tig Oct 21, 2020
a03fc9b
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Nov 18, 2020
cec80a4
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Jun 3, 2022
c172968
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Jul 30, 2022
25204bc
Upgrade to Terminal.Gui v1.7
tig Aug 3, 2022
5e0cdcc
Merge branch 'master' into minui
tig Aug 3, 2022
b474f84
removed border when minui is enabled
tig Aug 3, 2022
75ca4d1
re-implemented feature post merge screw up
tig Aug 3, 2022
fd6483e
tweaks
tig Aug 3, 2022
7c4bec7
Merge branch 'master' of tig:PowerShell/GraphicalTools
tig Aug 4, 2022
15ab348
Merge branch 'master' into select_all
tig Aug 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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-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"
pwsh -noprofile -command "Import-Module -verbose '$PSScriptRoot/module/Microsoft.PowerShell.ConsoleGuiTools'; Get-Module -all | Out-ConsoleGridView -OutputMode Single -Title 'Imported Modules' -Filter power"
258 changes: 162 additions & 96 deletions src/Microsoft.PowerShell.ConsoleGuiTools/ConsoleGui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -37,22 +37,36 @@ public HashSet<int> 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<string> gridHeaders = _applicationData.DataTable.DataColumns.Select((c) => c.Label).ToList();
CalculateColumnWidths(gridHeaders);

AddFilter(win);
AddHeaders(win, 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);

// GridView row logic
LoadData();
AddRows(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();
Expand All @@ -75,6 +89,41 @@ public HashSet<int> Start(ApplicationData applicationData)
return selectedIndexes;
}

private GridViewDataSource LoadData()
{
var items = new List<GridViewRow>();
int newIndex = 0;
for (int i = 0; i < _applicationData.DataTable.Data.Count; i++)
{
var dataTableRow = _applicationData.DataTable.Data[i];
var valueList = new List<string>();
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<GridViewRow> 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();
Expand All @@ -86,58 +135,85 @@ private void Close()
Application.RequestStop();
}

private Window AddTopLevelWindow()
private Window CreateTopLevelWindow()
{
// 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)
};

if (_applicationData.MinUI)
{
win.Border.BorderStyle = BorderStyle.None;
}

Application.Top.Add(win);
return win;
}

private void AddStatusBar()
private void AddStatusBar(bool visible)
{
var statusBar = new StatusBar(
_applicationData.OutputMode != OutputModeOption.None
? new StatusItem[]
var statusItems = new List<StatusItem>();
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, "~CTRL-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();
}));

// 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;
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);
}

Expand All @@ -164,7 +240,6 @@ private void CalculateColumnWidths(List<string> gridHeaders)
{
listViewColumnWidths[index] = len;
}

index++;
}
}
Expand Down Expand Up @@ -194,17 +269,25 @@ 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),
CanFocus = true,
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,
Expand All @@ -222,9 +305,9 @@ private void AddFilter(Window win)
filterErrorLabel.Text = " ";
filterErrorLabel.ColorScheme = Colors.Base;
filterErrorLabel.Redraw(filterErrorLabel.Bounds);
_applicationData.Filter = filterText;
ApplyFilter();

List<GridViewRow> itemList = GridViewHelpers.FilterData(_itemSource.GridViewRowList, filterText);
_listView.Source = new GridViewDataSource(itemList);
}
catch (Exception ex)
{
Expand All @@ -243,12 +326,16 @@ private void AddHeaders(Window win, List<string> 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
Expand All @@ -266,54 +353,33 @@ private void AddHeaders(Window win, List<string> gridHeaders)
}
}

var headerLine = new Label(headerLineText.ToString())
{
X = 0,
Y = 3
};

win.Add(headerLine);
}

private void LoadData()
{
var items = new List<GridViewRow>();
int newIndex = 0;
for (int i = 0; i < _applicationData.DataTable.Data.Count; i++)
if (!_applicationData.MinUI)
{
var dataTableRow = _applicationData.DataTable.Data[i];
var valueList = new List<string>();
foreach (var dataTableColumn in _applicationData.DataTable.DataColumns)
var headerLine = new Label(headerLineText.ToString())
{
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++;
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ public class OutConsoleGridViewCmdletCommand : PSCmdlet, IDisposable
[Parameter()]
public string Filter { set; get; }

/// <summary>
/// gets or sets the whether "minimum UI" mode will be enabled
/// </summary>
[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
Expand Down Expand Up @@ -133,6 +139,7 @@ protected override void EndProcessing()
Title = Title ?? "Out-ConsoleGridView",
OutputMode = OutputMode,
Filter = Filter,
MinUI = MinUI,
DataTable = dataTable
};

Expand Down
Loading