• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MorganKryze / ConsoleAppVisuals / 7972444880

pending completion
7972444880

push

github

MorganKryze
📖 add legacy docs

827 of 916 branches covered (90.28%)

Branch coverage included in aggregate %.

1676 of 1694 relevant lines covered (98.94%)

105.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.64
/src/ConsoleAppVisuals/elements/interactive/TableSelector.cs
1
/*
2
    GNU GPL License 2024 MorganKryze(Yann Vidamment)
3
    For full license information, please visit: https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/LICENSE
4
*/
5
namespace ConsoleAppVisuals;
6

7
/// <summary>
8
/// The <see cref="TableSelector{T}"/> class that contains the methods to create a table and display it.
9
/// </summary>
10
///
11
public class TableSelector<T> : InteractiveElement<int>
12
{
13
    #region Fields: title, headers, lines, display array, rounded corners
14
    private string? _title;
15
    private List<string>? _rawHeaders;
16
    private List<List<T>>? _rawLines;
17
    private readonly bool _excludeHeader;
18
    private readonly bool _excludeFooter;
19
    private readonly string? _footerText;
20
    private string[]? _displayArray;
21
    private bool _roundedCorners = false;
22
    private readonly int _line;
23
    private readonly Placement _placement;
24

25
    #endregion
26

27
    #region Properties: get headers, get lines
28
    /// <summary>
29
    /// This property returns the title of the table.
30
    /// </summary>
31
    public override Placement Placement => _placement;
9✔
32

33
    /// <summary>
34
    /// This property returns the line to display the table on.
35
    /// </summary>
36
    public override int Line => _line;
6✔
37

38
    /// <summary>
39
    /// This property returns the height of the table.
40
    /// </summary>
41
    public override int Height => _displayArray?.Length ?? 0;
6✔
42

43
    /// <summary>
44
    /// This property returns the width of the table.
45
    /// </summary>
46
    public override int Width => _displayArray?.Max(x => x.Length) ?? 0;
45✔
47
    #endregion
48

49
    #region Properties: get corners, count
50
    /// <summary>
51
    /// This property returns the title of the table.
52
    /// </summary>
53
    public string Title => _title ?? "";
15✔
54

55
    /// <summary>
56
    /// This property returns if the header is excluded.
57
    /// </summary>
58
    public bool ExcludeHeader => _excludeHeader;
6✔
59

60
    /// <summary>
61
    /// This property returns if the footer is excluded.
62
    /// </summary>
63
    public bool ExcludeFooter => _excludeFooter;
6✔
64

65
    /// <summary>
66
    /// This property returns the text of the footer.
67
    /// </summary>
68
    public string FooterText => _footerText ?? "";
6✔
69

70
    /// <summary>
71
    /// This property returns if the corners are rounded.
72
    /// </summary>
73
    public bool RoundedCorners => _roundedCorners;
9✔
74

75
    /// <summary>
76
    /// This property returns the corners of the table.
77
    /// </summary>
78
    public string GetCorners => _roundedCorners ? "╭╮╰╯" : "┌┐└┘";
816!
79

80
    /// <summary>
81
    /// This property returns the headers of the table.
82
    /// </summary>
83
    public List<string>? GetRawHeaders => _rawHeaders;
18✔
84

85
    /// <summary>
86
    /// This property returns the lines of the table.
87
    /// </summary>
88
    public List<List<T>>? GetRawLines => _rawLines;
21✔
89

90
    /// <summary>
91
    /// This property returns the number of lines in the table.
92
    /// </summary>
93
    public int Count => _rawLines?.Count ?? 0;
12✔
94

95
    /// <summary>
96
    /// This property returns the display array of the table.
97
    /// </summary>
98
    public string[]? DisplayArray => _displayArray;
6✔
99

100
    #endregion
101

102
    #region Constructor
103
    /// <summary>
104
    /// The <see cref="TableSelector{T}"/> natural constructor.
105
    /// </summary>
106
    /// <param name="title">The title of the table.</param>
107
    /// <param name="lines">The lines of the table.</param>
108
    /// <param name="headers">The headers of the table.</param>
109
    /// <param name="excludeHeader">Whether to exclude the header from selectable elements.</param>
110
    /// <param name="excludeFooter">Whether to exclude the footer from selectable elements.</param>
111
    /// <param name="footerText">The text to display in the footer.</param>
112
    /// <param name="placement">The placement of the table.</param>
113
    /// <param name="line">The line to display the table on.</param>
114
    /// <exception cref="ArgumentException">Is thrown when the number of columns in the table is not consistent with itself or with the headers.</exception>
115
    /// <exception cref="NullReferenceException">Is thrown when no body lines were provided.</exception>
116
    /// <remarks>
117
    /// For more information, refer to the following resources:
118
    /// <list type="bullet">
119
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
120
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
121
    /// </list>
122
    /// </remarks>
123
    public TableSelector(
198✔
124
        string? title = null,
198✔
125
        List<string>? headers = null,
198✔
126
        List<List<T>>? lines = null,
198✔
127
        bool excludeHeader = true,
198✔
128
        bool excludeFooter = true,
198✔
129
        string? footerText = null,
198✔
130
        Placement placement = Placement.TopCenter,
198✔
131
        int? line = null
198✔
132
    )
198✔
133
    {
134
        _title = title;
198✔
135
        _rawHeaders = headers;
198✔
136
        _rawLines = lines;
198✔
137
        _excludeHeader = excludeHeader;
198✔
138
        _excludeFooter = excludeFooter;
198✔
139
        _footerText = footerText;
198✔
140
        _placement = placement;
198✔
141
        _line = Window.CheckLine(line) ?? Window.GetLineAvailable(placement);
198✔
142
        if (CompatibilityCheck())
198✔
143
        {
144
            BuildTable();
138✔
145
        }
146
    }
192✔
147
    #endregion
148

149
    #region Check Methods
150
    private bool CompatibilityCheck()
151
    {
152
        if (_rawHeaders is null)
207✔
153
        {
154
            return CheckRawLines();
51✔
155
        }
156
        else if (_rawLines is null)
156✔
157
        {
158
            return true;
9✔
159
        }
160
        else
161
        {
162
            return CheckRawHeadersAndLines();
147✔
163
        }
164
    }
165

166
    private bool CheckRawLines()
167
    {
168
        if (_rawLines is null || _rawLines.Count == 0)
51✔
169
        {
170
            return false;
36✔
171
        }
172

173
        for (int i = 0; i < _rawLines.Count; i++)
108✔
174
        {
175
            if (_rawLines[i].Count != _rawLines[0].Count)
42✔
176
            {
177
                throw new ArgumentException(
3✔
178
                    "The number of columns in the table is not consistent."
3✔
179
                );
3✔
180
            }
181
        }
182

183
        return true;
12✔
184
    }
185

186
    private bool CheckRawHeadersAndLines()
187
    {
188
        if (_rawLines is null || _rawLines.Count == 0)
147✔
189
        {
190
            return false;
18✔
191
        }
192

193
        for (int i = 0; i < _rawLines.Count; i++)
1,014✔
194
        {
195
            if (_rawLines[i].Count != _rawHeaders?.Count)
384!
196
            {
197
                throw new ArgumentException(
6✔
198
                    "The number of columns in the table is not consistent(Headers or Lines)."
6✔
199
                );
6✔
200
            }
201
        }
202

203
        return true;
123✔
204
    }
205
    #endregion
206

207
    #region Build Methods
208
    private void BuildTable()
209
    {
210
        if (_rawHeaders is null)
168✔
211
        {
212
            if (_rawLines is not null)
15✔
213
            {
214
                BuildLines();
15✔
215
            }
216
        }
217
        else
218
        {
219
            if (_rawLines is null)
153✔
220
            {
221
                BuildHeaders();
12✔
222
            }
223
            else
224
            {
225
                BuildHeadersAndLines();
141✔
226
            }
227
        }
228
    }
141✔
229

230
    private void BuildHeadersAndLines()
231
    {
232
        if (_rawHeaders is not null && _rawLines is not null)
141✔
233
        {
234
            var stringList = new List<string>();
141✔
235
            var localMax = new int[_rawHeaders.Count];
141✔
236
            for (int i = 0; i < _rawHeaders.Count; i++)
1,692✔
237
            {
238
                if (_rawHeaders[i]?.Length > localMax[i])
705!
239
                {
240
                    localMax[i] = _rawHeaders[i]?.Length ?? 0;
705!
241
                }
242
            }
243

244
            for (int i = 0; i < _rawLines.Count; i++)
1,098✔
245
            {
246
                for (int j = 0; j < _rawLines[i].Count; j++)
4,896✔
247
                {
248
                    if (_rawLines[i][j]?.ToString()?.Length > localMax[j])
2,040!
249
                    {
250
                        localMax[j] = _rawLines[i][j]?.ToString()?.Length ?? 0;
72!
251
                    }
252
                }
253
            }
254

255
            StringBuilder headerBuilder = new("│ ");
141✔
256
            for (int i = 0; i < _rawHeaders.Count; i++)
1,692✔
257
            {
258
                headerBuilder.Append(_rawHeaders[i]?.PadRight(localMax[i]) ?? "");
705!
259
                if (i != _rawHeaders.Count - 1)
705✔
260
                {
261
                    headerBuilder.Append(" │ ");
564✔
262
                }
263
                else
264
                {
265
                    headerBuilder.Append(" │");
141✔
266
                }
267
            }
268
            stringList.Add(headerBuilder.ToString());
141✔
269

270
            StringBuilder upperBorderBuilder = new(GetCorners[0].ToString());
141✔
271
            for (int i = 0; i < _rawHeaders.Count; i++)
1,692✔
272
            {
273
                upperBorderBuilder.Append(new string('─', localMax[i] + 2));
705✔
274
                upperBorderBuilder.Append(
705✔
275
                    (i != _rawHeaders.Count - 1) ? "┬" : GetCorners[1].ToString()
705✔
276
                );
705✔
277
            }
278
            stringList.Insert(0, upperBorderBuilder.ToString());
141✔
279

280
            StringBuilder intermediateBorderBuilder = new("├");
141✔
281
            for (int i = 0; i < _rawHeaders.Count; i++)
1,692✔
282
            {
283
                intermediateBorderBuilder.Append(new string('─', localMax[i] + 2));
705✔
284
                intermediateBorderBuilder.Append((i != _rawHeaders.Count - 1) ? "┼" : "┤");
705✔
285
            }
286
            stringList.Add(intermediateBorderBuilder.ToString());
141✔
287

288
            for (int i = 0; i < _rawLines.Count; i++)
1,098✔
289
            {
290
                StringBuilder lineBuilder = new("│ ");
408✔
291
                for (int j = 0; j < _rawLines[i].Count; j++)
4,896✔
292
                {
293
                    lineBuilder.Append(_rawLines[i][j]?.ToString()?.PadRight(localMax[j]) ?? "");
2,040!
294
                    if (j != _rawLines[i].Count - 1)
2,040✔
295
                    {
296
                        lineBuilder.Append(" │ ");
1,632✔
297
                    }
298
                    else
299
                    {
300
                        lineBuilder.Append(" │");
408✔
301
                    }
302
                }
303
                stringList.Add(lineBuilder.ToString());
408✔
304
            }
305

306
            StringBuilder lowerBorderBuilder = new(GetCorners[2].ToString());
141✔
307
            for (int i = 0; i < _rawHeaders.Count; i++)
1,692✔
308
            {
309
                lowerBorderBuilder.Append(new string('─', localMax[i] + 2));
705✔
310
                lowerBorderBuilder.Append(
705✔
311
                    (i != _rawHeaders.Count - 1) ? "┴" : GetCorners[3].ToString()
705✔
312
                );
705✔
313
            }
314
            stringList.Add(lowerBorderBuilder.ToString());
141✔
315

316
            _displayArray = stringList.ToArray();
141✔
317
            BuildTitle();
141✔
318
        }
319
    }
141✔
320

321
    private void BuildHeaders()
322
    {
323
        if (_rawHeaders is not null)
12✔
324
        {
325
            var stringList = new List<string>();
12✔
326
            var localMax = new int[_rawHeaders.Count];
12✔
327
            for (int i = 0; i < _rawHeaders.Count; i++)
144✔
328
            {
329
                if (_rawHeaders[i]?.Length > localMax[i])
60!
330
                {
331
                    localMax[i] = _rawHeaders[i]?.Length ?? 0;
60!
332
                }
333
            }
334
            StringBuilder headerBuilder = new("│ ");
12✔
335
            for (int i = 0; i < _rawHeaders.Count; i++)
144✔
336
            {
337
                headerBuilder.Append(_rawHeaders[i]?.PadRight(localMax[i]) ?? "");
60!
338
                if (i != _rawHeaders.Count - 1)
60✔
339
                {
340
                    headerBuilder.Append(" │ ");
48✔
341
                }
342
                else
343
                {
344
                    headerBuilder.Append(" │");
12✔
345
                }
346
            }
347
            stringList.Add(headerBuilder.ToString());
12✔
348
            StringBuilder upperBorderBuilder = new(GetCorners[0].ToString());
12✔
349
            for (int i = 0; i < _rawHeaders.Count; i++)
144✔
350
            {
351
                upperBorderBuilder.Append(new string('─', localMax[i] + 2));
60✔
352
                upperBorderBuilder.Append(
60✔
353
                    (i != _rawHeaders.Count - 1) ? "┬" : GetCorners[1].ToString()
60✔
354
                );
60✔
355
            }
356
            stringList.Insert(0, upperBorderBuilder.ToString());
12✔
357
            StringBuilder lowerBorderBuilder = new(GetCorners[2].ToString());
12✔
358
            for (int i = 0; i < _rawHeaders.Count; i++)
144✔
359
            {
360
                lowerBorderBuilder.Append(new string('─', localMax[i] + 2));
60✔
361
                lowerBorderBuilder.Append(
60✔
362
                    (i != _rawHeaders.Count - 1) ? "┴" : GetCorners[3].ToString()
60✔
363
                );
60✔
364
            }
365
            stringList.Add(lowerBorderBuilder.ToString());
12✔
366
            _displayArray = stringList.ToArray();
12✔
367
            BuildTitle();
12✔
368
        }
369
    }
12✔
370

371
    private void BuildLines()
372
    {
373
        if (_rawLines is not null)
15✔
374
        {
375
            var stringList = new List<string>();
15✔
376
            var localMax = new int[_rawLines[0].Count];
15✔
377
            for (int i = 0; i < _rawLines.Count; i++)
120✔
378
            {
379
                for (int j = 0; j < _rawLines[i].Count; j++)
540✔
380
                {
381
                    if (_rawLines[i][j]?.ToString()?.Length > localMax[j])
225!
382
                    {
383
                        localMax[j] = _rawLines[i][j]?.ToString()?.Length ?? 0;
105!
384
                    }
385
                }
386
            }
387
            for (int i = 0; i < _rawLines.Count; i++)
120✔
388
            {
389
                StringBuilder line = new("│ ");
45✔
390
                for (int j = 0; j < _rawLines[i].Count; j++)
540✔
391
                {
392
                    line.Append(_rawLines[i][j]?.ToString()?.PadRight(localMax[j]) ?? "");
225!
393
                    if (j != _rawLines[i].Count - 1)
225✔
394
                    {
395
                        line.Append(" │ ");
180✔
396
                    }
397
                    else
398
                    {
399
                        line.Append(" │");
45✔
400
                    }
401
                }
402
                stringList.Add(line.ToString());
45✔
403
            }
404
            StringBuilder upperBorderBuilder = new(GetCorners[0].ToString());
15✔
405
            for (int i = 0; i < _rawLines.Count; i++)
120✔
406
            {
407
                upperBorderBuilder.Append(new string('─', localMax[i] + 2));
45✔
408
                upperBorderBuilder.Append(
45✔
409
                    (i != _rawLines.Count - 1) ? "┬" : GetCorners[1].ToString()
45✔
410
                );
45✔
411
            }
412
            stringList.Insert(0, upperBorderBuilder.ToString());
15✔
413
            StringBuilder lowerBorderBuilder = new(GetCorners[2].ToString());
15✔
414
            for (int i = 0; i < _rawLines.Count; i++)
120✔
415
            {
416
                lowerBorderBuilder.Append(new string('─', localMax[i] + 2));
45✔
417
                lowerBorderBuilder.Append(
45✔
418
                    (i != _rawLines.Count - 1) ? "┴" : GetCorners[3].ToString()
45✔
419
                );
45✔
420
            }
421
            stringList.Add(lowerBorderBuilder.ToString());
15✔
422
            _displayArray = stringList.ToArray();
15✔
423
            BuildTitle();
15✔
424
        }
425
    }
15✔
426

427
    private void BuildTitle()
428
    {
429
        if (_title is not null)
168✔
430
        {
431
            var len = _displayArray![0].Length;
69✔
432
            var title = _title.ResizeString(len - 4);
69✔
433
            title = $"│ {title} │";
69✔
434
            var upperBorderBuilder = new StringBuilder(GetCorners[0].ToString());
69✔
435
            upperBorderBuilder.Append(new string('─', len - 2));
69✔
436
            upperBorderBuilder.Append(GetCorners[1].ToString());
69✔
437
            var display = _displayArray.ToList();
69✔
438
            display[0] = display[0]
69✔
439
                .Remove(0, 1)
69✔
440
                .Insert(0, "├")
69✔
441
                .Remove(display[1].Length - 1, 1)
69✔
442
                .Insert(display[1].Length - 1, "┤");
69✔
443
            display.Insert(0, title);
69✔
444
            display.Insert(0, upperBorderBuilder.ToString());
69✔
445
            _displayArray = display.ToArray();
69✔
446
        }
447
    }
168✔
448
    #endregion
449

450
    #region Methods: Get, Add, Update, Remove, Clear
451
    /// <summary>
452
    /// This method adds headers to the table.
453
    /// </summary>
454
    /// <param name="headers">The headers to add.</param>
455
    /// <remarks>
456
    /// For more information, refer to the following resources:
457
    /// <list type="bullet">
458
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
459
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
460
    /// </list>
461
    /// </remarks>
462
    public void AddHeaders(List<string> headers)
463
    {
464
        var lastStateOfHeaders = _rawHeaders;
9✔
465
        _rawHeaders = headers;
9✔
466
        if (CompatibilityCheck())
9!
467
        {
468
            BuildTable();
6✔
469
        }
470
        else
471
        {
472
            _rawHeaders = lastStateOfHeaders;
×
473
        }
474
    }
×
475

476
    /// <summary>
477
    /// This method updates the headers of the table.
478
    /// </summary>
479
    /// <param name="headers">The headers to update.</param>
480
    /// <remarks>
481
    /// For more information, refer to the following resources:
482
    /// <list type="bullet">
483
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
484
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
485
    /// </list>
486
    /// </remarks>
487
    public void UpdateHeaders(List<string> headers)
488
    {
489
        AddHeaders(headers);
3✔
490
    }
3✔
491

492
    /// <summary>
493
    /// This method adds a title to the table.
494
    /// </summary>
495
    /// <param name="title">The title to add.</param>
496
    /// <remarks>
497
    /// For more information, refer to the following resources:
498
    /// <list type="bullet">
499
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
500
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
501
    /// </list>
502
    /// </remarks>
503
    public void AddTitle(string title)
504
    {
505
        _title = title;
6✔
506
        BuildTable();
6✔
507
    }
6✔
508

509
    /// <summary>
510
    /// This method updates the title of the table.
511
    /// </summary>
512
    /// <param name="title">The title to update.</param>
513
    /// <remarks>
514
    /// For more information, refer to the following resources:
515
    /// <list type="bullet">
516
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
517
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
518
    /// </list>
519
    /// </remarks>
520
    public void UpdateTitle(string title)
521
    {
522
        AddTitle(title);
3✔
523
    }
3✔
524

525
    /// <summary>
526
    /// Toggles the rounded corners of the table.
527
    /// </summary>
528
    /// <param name="rounded">Whether to round the corners or not.</param>
529
    /// <remarks>
530
    /// For more information, refer to the following resources:
531
    /// <list type="bullet">
532
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
533
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
534
    /// </list>
535
    /// </remarks>
536
    public void SetRoundedCorners(bool rounded = true)
537
    {
538
        _roundedCorners = rounded;
3✔
539
        BuildTable();
3✔
540
    }
3✔
541

542
    /// <summary>
543
    /// This property returns the specified line in the table.
544
    /// </summary>
545
    /// <param name="index">The index of the line to return.</param>
546
    /// <returns>The line at the specified index.</returns>
547
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
548
    /// <remarks>
549
    /// For more information, refer to the following resources:
550
    /// <list type="bullet">
551
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
552
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
553
    /// </list>
554
    /// </remarks>
555
    public List<T> GetLine(int index)
556
    {
557
        if (index < 0 || index >= _rawLines?.Count)
12!
558
        {
559
            throw new ArgumentOutOfRangeException(nameof(index), "The index is out of range.");
9✔
560
        }
561
        return _rawLines![index];
3✔
562
    }
563

564
    /// <summary>
565
    /// This method is used to get all the elements from a column given its index.
566
    /// </summary>
567
    /// <param name="index">The index of the column.</param>
568
    /// <returns>The elements of the column.</returns>
569
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
570
    /// <remarks>
571
    /// For more information, refer to the following resources:
572
    /// <list type="bullet">
573
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
574
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
575
    /// </list>
576
    /// </remarks>
577
    public List<T>? GetColumnData(int index)
578
    {
579
        if (_rawLines is null)
18✔
580
        {
581
            return null;
3✔
582
        }
583

584
        if (index < 0 || index >= _rawLines[0].Count)
15✔
585
        {
586
            throw new ArgumentOutOfRangeException(nameof(index), "Invalid column index.");
9✔
587
        }
588

589
        List<T>? list = new();
6✔
590
        for (int i = 0; i < _rawLines.Count; i++)
48✔
591
        {
592
            list.Add(_rawLines[i][index]);
18✔
593
        }
594
        return list;
6✔
595
    }
596

597
    /// <summary>
598
    /// This method is used to get all the elements from a column given its header.
599
    /// </summary>
600
    /// <param name="header">The header of the column.</param>
601
    /// <returns>The elements of the column.</returns>
602
    /// <exception cref="ArgumentException">Is thrown when the header is invalid.</exception>
603
    /// <remarks>
604
    /// For more information, refer to the following resources:
605
    /// <list type="bullet">
606
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
607
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
608
    /// </list>
609
    /// </remarks>
610
    public List<T>? GetColumnData(string header)
611
    {
612
        if (_rawHeaders is null)
12✔
613
        {
614
            return null;
3✔
615
        }
616
        else if (_rawLines is null)
9✔
617
        {
618
            return null;
3✔
619
        }
620
        if (!_rawHeaders.Contains(header))
6✔
621
        {
622
            throw new ArgumentException(nameof(header), "Invalid column header.");
3✔
623
        }
624

625
        return GetColumnData(_rawHeaders.IndexOf(header));
3✔
626
    }
627

628
    /// <summary>
629
    /// This method adds a line to the table.
630
    /// </summary>
631
    /// <param name="line">The line to add.</param>
632
    /// <exception cref="ArgumentException">Is thrown when the number of columns in the table is not consistent with itself or with the headers.</exception>
633
    /// <remarks>
634
    /// For more information, refer to the following resources:
635
    /// <list type="bullet">
636
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
637
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
638
    /// </list>
639
    /// </remarks>
640
    public void AddLine(List<T> line)
641
    {
642
        if (_rawLines?.Count > 0 && line.Count != _rawLines[0].Count)
9!
643
        {
644
            throw new ArgumentException(
3✔
645
                "The number of columns in the table is not consistent with other lines."
3✔
646
            );
3✔
647
        }
648
        if (_rawHeaders is not null && line.Count != _rawHeaders.Count)
6✔
649
        {
650
            throw new ArgumentException(
3✔
651
                "The number of columns in the table is not consistent with the headers."
3✔
652
            );
3✔
653
        }
654
        _rawLines ??= new List<List<T>>();
3!
655
        _rawLines.Add(line);
3✔
656
        BuildTable();
3✔
657
    }
3✔
658

659
    /// <summary>
660
    /// This method removes a line from the table.
661
    /// </summary>
662
    /// <param name="index">The index of the line to remove.</param>
663
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
664
    /// <remarks>
665
    /// For more information, refer to the following resources:
666
    /// <list type="bullet">
667
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
668
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
669
    /// </list>
670
    /// </remarks>
671
    public void RemoveLine(int index)
672
    {
673
        if (_rawLines?.Count > 0 && (index < 0 || index >= _rawLines.Count))
12!
674
        {
675
            throw new ArgumentOutOfRangeException(nameof(index), "The index is out of range.");
6✔
676
        }
677

678
        _rawLines?.RemoveAt(index);
6!
679
        BuildTable();
3✔
680
    }
3✔
681

682
    /// <summary>
683
    /// This method updates a line in the table.
684
    /// </summary>
685
    /// <param name="index">The index of the line to update.</param>
686
    /// <param name="line">The new line.</param>
687
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
688
    /// <exception cref="ArgumentException">Is thrown when the number of columns in the table is not consistent with itself or with the headers.</exception>
689
    /// <remarks>
690
    /// For more information, refer to the following resources:
691
    /// <list type="bullet">
692
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
693
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
694
    /// </list>
695
    /// </remarks>
696
    public void UpdateLine(int index, List<T> line)
697
    {
698
        if (_rawLines?.Count > 0)
15!
699
        {
700
            if (index < 0 || index >= _rawLines.Count)
12✔
701
            {
702
                throw new ArgumentOutOfRangeException(nameof(index), "The index is out of range.");
6✔
703
            }
704

705
            if (line.Count != _rawHeaders?.Count)
6!
706
            {
707
                throw new ArgumentException(
3✔
708
                    "The number of columns in the table is not consistent."
3✔
709
                );
3✔
710
            }
711
        }
712
        _rawLines![index] = line;
6✔
713
        BuildTable();
3✔
714
    }
3✔
715

716
    /// <summary>
717
    /// This method clears the headers of the table.
718
    /// </summary>
719
    /// <remarks>
720
    /// For more information, refer to the following resources:
721
    /// <list type="bullet">
722
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
723
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
724
    /// </list>
725
    /// </remarks>
726
    public void ClearHeaders()
727
    {
728
        _rawHeaders = null;
3✔
729
        BuildTable();
3✔
730
    }
3✔
731

732
    /// <summary>
733
    /// This method clears the lines of the table.
734
    /// </summary>
735
    /// <remarks>
736
    /// For more information, refer to the following resources:
737
    /// <list type="bullet">
738
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
739
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
740
    /// </list>
741
    /// </remarks>
742
    public void ClearLines()
743
    {
744
        _rawLines = null;
3✔
745
        BuildTable();
3✔
746
    }
3✔
747

748
    /// <summary>
749
    /// This method clears the table.
750
    /// </summary>
751
    /// <remarks>
752
    /// For more information, refer to the following resources:
753
    /// <list type="bullet">
754
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
755
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/Program.cs">Example Project</a></description></item>
756
    /// </list>
757
    /// </remarks>
758
    public void Reset()
759
    {
760
        _title = null;
3✔
761
        _rawHeaders?.Clear();
3!
762
        _rawLines?.Clear();
3!
763
        _displayArray = null;
3✔
764
    }
3✔
765
    #endregion
766

767
    #region Display Methods
768
    /// <summary>
769
    /// This method displays the table without interaction.
770
    /// </summary>
771
    [Visual]
772
    protected override void RenderElementActions()
773
    {
774
        int minIndex = GetMinIndex(_excludeHeader);
775
        int maxIndex = GetMaxIndex(_excludeFooter);
776
        int index = minIndex;
777

778
        while (true)
779
        {
780
            string[] array = new string[_displayArray!.Length];
781
            for (int j = 0; j < _displayArray.Length; j++)
782
            {
783
                array[j] = _displayArray[j];
784
                Core.WritePositionedString(array[j], TextAlignment.Center, false, _line + j);
785
                if (j == index)
786
                {
787
                    Core.WritePositionedString(
788
                        j == _displayArray.Length - 1
789
                            ? array[j].InsertString($"┤ {_footerText} ├", Placement.TopCenter)[
790
                                2..^2
791
                            ]
792
                            : array[j][1..^1],
793
                        TextAlignment.Center,
794
                        true,
795
                        _line + j
796
                    );
797
                }
798
            }
799
            switch (Console.ReadKey(intercept: true).Key)
800
            {
801
                case ConsoleKey.UpArrow:
802
                case ConsoleKey.Z:
803
                    index = HandleUpArrowKey(index, minIndex, maxIndex);
804
                    break;
805
                case ConsoleKey.DownArrow:
806
                case ConsoleKey.S:
807
                    index = HandleDownArrowKey(index, minIndex, maxIndex);
808
                    break;
809
                case ConsoleKey.Enter:
810
                    SendResponse(
811
                        this,
812
                        new InteractionEventArgs<int>(Output.Select, ReturnIndex(index))
813
                    );
814
                    return;
815
                case ConsoleKey.Escape:
816
                    SendResponse(this, new InteractionEventArgs<int>(Output.Exit, 0));
817
                    return;
818
                case ConsoleKey.Backspace:
819
                    SendResponse(
820
                        this,
821
                        new InteractionEventArgs<int>(Output.Delete, ReturnIndex(index))
822
                    );
823
                    return;
824
            }
825
        }
826
    }
827

828
    [Visual]
829
    int ReturnIndex(int index)
830
    {
831
        return index - GetMinIndex(_excludeHeader);
832
    }
833

834
    [Visual]
835
    int GetMinIndex(bool excludeHeader)
836
    {
837
        if (excludeHeader)
838
        {
839
            return _rawHeaders is null ? 3 : 5;
840
        }
841
        else
842
        {
843
            return 0;
844
        }
845
    }
846

847
    [Visual]
848
    int GetMaxIndex(bool excludeFooter)
849
    {
850
        return excludeFooter ? _displayArray!.Length - 2 : _displayArray!.Length - 1;
851
    }
852

853
    [Visual]
854
    static int HandleUpArrowKey(int index, int minIndex, int maxIndex)
855
    {
856
        if (index == minIndex)
857
        {
858
            return maxIndex;
859
        }
860
        else if (index > minIndex)
861
        {
862
            return index - 1;
863
        }
864
        return index;
865
    }
866

867
    [Visual]
868
    static int HandleDownArrowKey(int index, int minIndex, int maxIndex)
869
    {
870
        if (index == maxIndex)
871
        {
872
            return minIndex;
873
        }
874
        else if (index < maxIndex)
875
        {
876
            return index + 1;
877
        }
878
        return index;
879
    }
880
    #endregion
881
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc