-
Notifications
You must be signed in to change notification settings - Fork 63
/
ConsoleGui.cs
191 lines (165 loc) · 6.9 KB
/
ConsoleGui.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OutGridView.Models;
using Terminal.Gui;
namespace OutGridView.Cmdlet
{
internal class ConsoleGui : IDisposable
{
private const string ACCEPT_TEXT = "Are you sure you want to select\nthese items to send down the pipeline?";
private const string CANCEL_TEXT = "Are you sure you want to cancel?\nNothing will be emitted to the pipeline.";
private const string CLOSE_TEXT = "Are you sure you want to close?";
private bool _cancelled;
internal HashSet<int> SelectedIndexes { get; private set; } = new HashSet<int>();
public void Start(ApplicationData applicationData)
{
Application.Init();
var top = Application.Top;
// Creates the top-level window to show
var win = new Window(applicationData.Title ?? "Out-ConsoleGridView")
{
X = 0,
Y = 1, // Leave one row for the toplevel menu
// By using Dim.Fill(), it will automatically resize without manual intervention
Width = Dim.Fill(),
Height = Dim.Fill()
};
top.Add(win);
// Creates a menubar, the item "New" has a help menu.
var menu = new MenuBar(new MenuBarItem []
{
new MenuBarItem("_Actions (F9)",
applicationData.PassThru
? new MenuItem []
{
new MenuItem("_Accept", "", () => { if (Quit("Accept", ACCEPT_TEXT)) Application.RequestStop(); }),
new MenuItem("_Cancel", "", () =>{ if (Quit("Cancel", CANCEL_TEXT)) _cancelled = true; Application.RequestStop(); })
}
: new MenuItem []
{
new MenuItem("_Close", "", () =>{ if (Quit("Close", CLOSE_TEXT)) Application.RequestStop(); })
})
});
top.Add(menu);
var gridHeaders = applicationData.DataTable.DataColumns.Select((c) => c.Label).ToList();
var columnWidths = new int[gridHeaders.Count];
for (int i = 0; i < gridHeaders.Count; i++)
{
columnWidths[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(top.Frame.Height / 2))
{
var len = col.Value.DisplayValue.Length;
if (len > columnWidths[index])
{
columnWidths[index] = len;
}
index++;
}
}
// If we have PassThru, then we want to make them selectable. If we make them selectable,
// they have a 8 character addition of a checkbox (" [ ]") that we have to factor in.
int offset = applicationData.PassThru ? 8 : 4;
// 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
int usableWidth = top.Frame.Width - 3 - columnWidths.Length - offset - 2;
int columnWidthsSum = columnWidths.Sum();
while (columnWidthsSum >= usableWidth)
{
int maxWidth = 0;
int maxIndex = 0;
for (int i = 0; i < columnWidths.Length; i++)
{
if (columnWidths[i] > maxWidth)
{
maxWidth = columnWidths[i];
maxIndex = i;
}
}
columnWidths[maxIndex]--;
columnWidthsSum--;
}
win.Add(new Label(GetPaddedString(gridHeaders, columnWidths, offset + offset - 1)));
var items = new List<string>();
foreach (DataTableRow dataTableRow in applicationData.DataTable.Data)
{
var valueList = new List<string>();
foreach (var dataTableColumn in applicationData.DataTable.DataColumns)
{
valueList.Add(dataTableRow.Values[dataTableColumn.ToString()].DisplayValue);
}
items.Add(GetPaddedString(valueList, columnWidths, offset));
}
var list = new ListView(items)
{
X = 3,
Y = 3,
Width = Dim.Fill(2),
Height = Dim.Fill(2),
AllowsMarking = applicationData.PassThru
};
win.Add(list);
Application.Run();
if (_cancelled)
{
return;
}
for (int i = 0; i < applicationData.DataTable.Data.Count; i++)
{
if(list.Source.IsMarked(i))
{
SelectedIndexes.Add(i);
}
}
}
private static bool Quit(string title, string text)
{
var n = MessageBox.Query(50, 7, title, text, "Yes", "No");
return n == 0;
}
private static string GetPaddedString(List<string> strings, int[] colWidths, int offset = 0)
{
var builder = new StringBuilder();
if (offset > 0)
{
builder.Append(string.Empty.PadRight(offset));
}
for (int i = 0; i < strings.Count; i++)
{
if (i > 0)
{
// Add a space between columns
builder.Append(' ');
}
// If the string won't fit in the column, append an ellipsis.
if (strings[i].Length > colWidths[i])
{
builder.Append(strings[i].Substring(0, colWidths[i] - 4));
builder.Append("...");
}
else
{
builder.Append(strings[i].PadRight(colWidths[i]));
}
}
return builder.ToString();
}
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");
}
}
}