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

MorganKryze / ConsoleAppVisuals / 8158191062

05 Mar 2024 02:47PM UTC coverage: 86.165% (+0.9%) from 85.231%
8158191062

push

github

MorganKryze
📖 (readme) update roadmap

931 of 1144 branches covered (81.38%)

Branch coverage included in aggregate %.

1803 of 2029 relevant lines covered (88.86%)

412.64 hits per line

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

88.76
/src/ConsoleAppVisuals/elements/interactive_elements/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.Elements;
6

7
/// <summary>
8
/// The <see cref="TableSelector{T}"/> class that contains the methods to create a table and display it.
9
/// </summary>
10
/// <remarks>
11
/// For more information, refer to the following resources:
12
/// <list type="bullet">
13
/// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
14
/// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
15
/// </list>
16
/// </remarks>
17
public class TableSelector<T> : InteractiveElement<int>
18
{
19
    #region Fields
20
    private string? _title;
21
    private List<string>? _rawHeaders;
22
    private List<List<T>>? _rawLines;
23
    private bool _excludeHeader;
24
    private bool _excludeFooter;
25
    private string? _footerText;
26
    private string[]? _displayArray;
27
    private bool _roundedCorners = false;
28
    private Placement _placement;
29

30
    #endregion
31

32
    #region Properties
33
    /// <summary>
34
    /// This property returns the title of the table.
35
    /// </summary>
36
    public override Placement Placement => _placement;
10✔
37

38
    /// <summary>
39
    /// This property returns the height of the table.
40
    /// </summary>
41
    public override int Height => _displayArray?.Length ?? 0;
4✔
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;
30✔
47

48
    /// <summary>
49
    /// This property returns the title of the table.
50
    /// </summary>
51
    public string Title => _title ?? "";
10✔
52

53
    /// <summary>
54
    /// This property returns if the header is excluded.
55
    /// </summary>
56
    public bool ExcludeHeader => _excludeHeader;
4✔
57

58
    /// <summary>
59
    /// This property returns if the footer is excluded.
60
    /// </summary>
61
    public bool ExcludeFooter => _excludeFooter;
4✔
62

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

68
    /// <summary>
69
    /// This property returns if the corners are rounded.
70
    /// </summary>
71
    public bool RoundedCorners => _roundedCorners;
6✔
72

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

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

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

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

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

98
    #endregion
99

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

144
    #region Check Methods
145
    private bool CompatibilityCheck()
146
    {
147
        if (_rawHeaders is null)
138✔
148
        {
149
            return CheckRawLines();
34✔
150
        }
151
        else if (_rawLines is null)
104✔
152
        {
153
            return true;
6✔
154
        }
155
        else
156
        {
157
            return CheckRawHeadersAndLines();
98✔
158
        }
159
    }
160

161
    private bool CheckRawLines()
162
    {
163
        if (_rawLines is null || _rawLines.Count == 0)
34✔
164
        {
165
            return false;
24✔
166
        }
167

168
        for (int i = 0; i < _rawLines.Count; i++)
72✔
169
        {
170
            if (_rawLines[i].Count != _rawLines[0].Count)
28✔
171
            {
172
                throw new ArgumentException(
2✔
173
                    "The number of columns in the table is not consistent."
2✔
174
                );
2✔
175
            }
176
        }
177

178
        return true;
8✔
179
    }
180

181
    private bool CheckRawHeadersAndLines()
182
    {
183
        if (_rawLines is null || _rawLines.Count == 0)
98✔
184
        {
185
            return false;
12✔
186
        }
187

188
        for (int i = 0; i < _rawLines.Count; i++)
676✔
189
        {
190
            if (_rawLines[i].Count != _rawHeaders?.Count)
256!
191
            {
192
                throw new ArgumentException(
4✔
193
                    "The number of columns in the table is not consistent(Headers or Lines)."
4✔
194
                );
4✔
195
            }
196
        }
197

198
        return true;
82✔
199
    }
200
    #endregion
201

202
    #region Build Methods
203
    private void BuildTable()
204
    {
205
        if (_rawHeaders is null)
112✔
206
        {
207
            if (_rawLines is not null)
10✔
208
            {
209
                BuildLines();
10✔
210
            }
211
        }
212
        else
213
        {
214
            if (_rawLines is null)
102✔
215
            {
216
                BuildHeaders();
8✔
217
            }
218
            else
219
            {
220
                BuildHeadersAndLines();
94✔
221
            }
222
        }
223
    }
94✔
224

225
    private void BuildHeadersAndLines()
226
    {
227
        if (_rawHeaders is not null && _rawLines is not null)
94✔
228
        {
229
            var stringList = new List<string>();
94✔
230
            var localMax = new int[_rawHeaders.Count];
94✔
231
            for (int i = 0; i < _rawHeaders.Count; i++)
1,128✔
232
            {
233
                if (_rawHeaders[i]?.Length > localMax[i])
470!
234
                {
235
                    localMax[i] = _rawHeaders[i]?.Length ?? 0;
470!
236
                }
237
            }
238

239
            for (int i = 0; i < _rawLines.Count; i++)
732✔
240
            {
241
                for (int j = 0; j < _rawLines[i].Count; j++)
3,264✔
242
                {
243
                    if (_rawLines[i][j]?.ToString()?.Length > localMax[j])
1,360!
244
                    {
245
                        localMax[j] = _rawLines[i][j]?.ToString()?.Length ?? 0;
48!
246
                    }
247
                }
248
            }
249

250
            StringBuilder headerBuilder = new("│ ");
94✔
251
            for (int i = 0; i < _rawHeaders.Count; i++)
1,128✔
252
            {
253
                headerBuilder.Append(_rawHeaders[i]?.PadRight(localMax[i]) ?? "");
470!
254
                if (i != _rawHeaders.Count - 1)
470✔
255
                {
256
                    headerBuilder.Append(" │ ");
376✔
257
                }
258
                else
259
                {
260
                    headerBuilder.Append(" │");
94✔
261
                }
262
            }
263
            stringList.Add(headerBuilder.ToString());
94✔
264

265
            StringBuilder upperBorderBuilder = new(GetCorners[0].ToString());
94✔
266
            for (int i = 0; i < _rawHeaders.Count; i++)
1,128✔
267
            {
268
                upperBorderBuilder.Append(new string('─', localMax[i] + 2));
470✔
269
                upperBorderBuilder.Append(
470✔
270
                    (i != _rawHeaders.Count - 1) ? "┬" : GetCorners[1].ToString()
470✔
271
                );
470✔
272
            }
273
            stringList.Insert(0, upperBorderBuilder.ToString());
94✔
274

275
            StringBuilder intermediateBorderBuilder = new("├");
94✔
276
            for (int i = 0; i < _rawHeaders.Count; i++)
1,128✔
277
            {
278
                intermediateBorderBuilder.Append(new string('─', localMax[i] + 2));
470✔
279
                intermediateBorderBuilder.Append((i != _rawHeaders.Count - 1) ? "┼" : "┤");
470✔
280
            }
281
            stringList.Add(intermediateBorderBuilder.ToString());
94✔
282

283
            for (int i = 0; i < _rawLines.Count; i++)
732✔
284
            {
285
                StringBuilder lineBuilder = new("│ ");
272✔
286
                for (int j = 0; j < _rawLines[i].Count; j++)
3,264✔
287
                {
288
                    lineBuilder.Append(_rawLines[i][j]?.ToString()?.PadRight(localMax[j]) ?? "");
1,360!
289
                    if (j != _rawLines[i].Count - 1)
1,360✔
290
                    {
291
                        lineBuilder.Append(" │ ");
1,088✔
292
                    }
293
                    else
294
                    {
295
                        lineBuilder.Append(" │");
272✔
296
                    }
297
                }
298
                stringList.Add(lineBuilder.ToString());
272✔
299
            }
300

301
            StringBuilder lowerBorderBuilder = new(GetCorners[2].ToString());
94✔
302
            for (int i = 0; i < _rawHeaders.Count; i++)
1,128✔
303
            {
304
                lowerBorderBuilder.Append(new string('─', localMax[i] + 2));
470✔
305
                lowerBorderBuilder.Append(
470✔
306
                    (i != _rawHeaders.Count - 1) ? "┴" : GetCorners[3].ToString()
470✔
307
                );
470✔
308
            }
309
            stringList.Add(lowerBorderBuilder.ToString());
94✔
310

311
            _displayArray = stringList.ToArray();
94✔
312
            BuildTitle();
94✔
313
        }
314
    }
94✔
315

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

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

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

445
    #region Methods: Get, Add, Update, Remove, Clear
446
    /// <summary>
447
    /// This method updates the placement of the table.
448
    /// </summary>
449
    /// <param name="placement">The placement of the table.</param>
450
    /// <remarks>
451
    /// For more information, refer to the following resources:
452
    /// <list type="bullet">
453
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
454
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
455
    /// </list>
456
    /// </remarks>
457
    public void UpdatePlacement(Placement placement)
458
    {
459
        _placement = placement;
×
460
        if (CompatibilityCheck())
×
461
        {
462
            BuildTable();
×
463
        }
464
    }
×
465

466
    /// <summary>
467
    /// This method updates the text of the footer.
468
    /// </summary>
469
    /// <param name="footerText"></param>
470
    public void UpdateFooterText(string footerText)
471
    {
472
        _footerText = footerText;
×
473
        if (CompatibilityCheck())
×
474
        {
475
            BuildTable();
×
476
        }
477
    }
×
478

479
    /// <summary>
480
    /// This method sets the table to exclude the header.
481
    /// </summary>
482
    /// <param name="excludeHeader">Whether to exclude the header or not.</param>
483
    /// <remarks>
484
    /// For more information, refer to the following resources:
485
    /// <list type="bullet">
486
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
487
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
488
    /// </list>
489
    /// </remarks>
490
    public void SetExcludeHeader(bool excludeHeader = true)
491
    {
492
        _excludeHeader = excludeHeader;
×
493
        if (CompatibilityCheck())
×
494
        {
495
            BuildTable();
×
496
        }
497
    }
×
498

499
    /// <summary>
500
    /// This method sets the table to exclude the footer.
501
    /// </summary>
502
    /// <param name="excludeFooter">Whether to exclude the footer or not.</param>
503
    /// <remarks>
504
    /// For more information, refer to the following resources:
505
    /// <list type="bullet">
506
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
507
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
508
    /// </list>
509
    /// </remarks>
510
    public void SetExcludeFooter(bool excludeFooter = true)
511
    {
512
        _excludeFooter = excludeFooter;
×
513
        if (CompatibilityCheck())
×
514
        {
515
            BuildTable();
×
516
        }
517
    }
×
518

519
    /// <summary>
520
    /// This method adds headers to the table.
521
    /// </summary>
522
    /// <param name="headers">The headers to add.</param>
523
    /// <remarks>
524
    /// For more information, refer to the following resources:
525
    /// <list type="bullet">
526
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
527
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
528
    /// </list>
529
    /// </remarks>
530
    public void AddHeaders(List<string> headers)
531
    {
532
        var lastStateOfHeaders = _rawHeaders;
6✔
533
        _rawHeaders = headers;
6✔
534
        if (CompatibilityCheck())
6✔
535
        {
536
            BuildTable();
4✔
537
        }
538
    }
4✔
539

540
    /// <summary>
541
    /// This method updates the headers of the table.
542
    /// </summary>
543
    /// <param name="headers">The headers to update.</param>
544
    /// <remarks>
545
    /// For more information, refer to the following resources:
546
    /// <list type="bullet">
547
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
548
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
549
    /// </list>
550
    /// </remarks>
551
    public void UpdateHeaders(List<string> headers)
552
    {
553
        AddHeaders(headers);
2✔
554
    }
2✔
555

556
    /// <summary>
557
    /// This method adds a title to the table.
558
    /// </summary>
559
    /// <param name="title">The title to add.</param>
560
    /// <remarks>
561
    /// For more information, refer to the following resources:
562
    /// <list type="bullet">
563
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
564
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
565
    /// </list>
566
    /// </remarks>
567
    public void AddTitle(string title)
568
    {
569
        _title = title;
4✔
570
        BuildTable();
4✔
571
    }
4✔
572

573
    /// <summary>
574
    /// This method updates the title of the table.
575
    /// </summary>
576
    /// <param name="title">The title to update.</param>
577
    /// <remarks>
578
    /// For more information, refer to the following resources:
579
    /// <list type="bullet">
580
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
581
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
582
    /// </list>
583
    /// </remarks>
584
    public void UpdateTitle(string title)
585
    {
586
        AddTitle(title);
2✔
587
    }
2✔
588

589
    /// <summary>
590
    /// Toggles the rounded corners of the table.
591
    /// </summary>
592
    /// <param name="rounded">Whether to round the corners or not.</param>
593
    /// <remarks>
594
    /// For more information, refer to the following resources:
595
    /// <list type="bullet">
596
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
597
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
598
    /// </list>
599
    /// </remarks>
600
    public void SetRoundedCorners(bool rounded = true)
601
    {
602
        _roundedCorners = rounded;
2✔
603
        BuildTable();
2✔
604
    }
2✔
605

606
    /// <summary>
607
    /// This property returns the specified line in the table.
608
    /// </summary>
609
    /// <param name="index">The index of the line to return.</param>
610
    /// <returns>The line at the specified index.</returns>
611
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
612
    /// <remarks>
613
    /// For more information, refer to the following resources:
614
    /// <list type="bullet">
615
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
616
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
617
    /// </list>
618
    /// </remarks>
619
    public List<T> GetLine(int index)
620
    {
621
        if (index < 0 || index >= _rawLines?.Count)
8!
622
        {
623
            throw new ArgumentOutOfRangeException(nameof(index), "The index is out of range.");
6✔
624
        }
625
        return _rawLines![index];
2✔
626
    }
627

628
    /// <summary>
629
    /// This method is used to get all the elements from a column given its index.
630
    /// </summary>
631
    /// <param name="index">The index of the column.</param>
632
    /// <returns>The elements of the column.</returns>
633
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
634
    /// <remarks>
635
    /// For more information, refer to the following resources:
636
    /// <list type="bullet">
637
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
638
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
639
    /// </list>
640
    /// </remarks>
641
    public List<T>? GetColumnData(int index)
642
    {
643
        if (_rawLines is null)
12✔
644
        {
645
            return null;
2✔
646
        }
647

648
        if (index < 0 || index >= _rawLines[0].Count)
10✔
649
        {
650
            throw new ArgumentOutOfRangeException(nameof(index), "Invalid column index.");
6✔
651
        }
652

653
        List<T>? list = new();
4✔
654
        for (int i = 0; i < _rawLines.Count; i++)
32✔
655
        {
656
            list.Add(_rawLines[i][index]);
12✔
657
        }
658
        return list;
4✔
659
    }
660

661
    /// <summary>
662
    /// This method is used to get all the elements from a column given its header.
663
    /// </summary>
664
    /// <param name="header">The header of the column.</param>
665
    /// <returns>The elements of the column.</returns>
666
    /// <exception cref="ArgumentException">Is thrown when the header is invalid.</exception>
667
    /// <remarks>
668
    /// For more information, refer to the following resources:
669
    /// <list type="bullet">
670
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
671
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
672
    /// </list>
673
    /// </remarks>
674
    public List<T>? GetColumnData(string header)
675
    {
676
        if (_rawHeaders is null)
8✔
677
        {
678
            return null;
2✔
679
        }
680
        else if (_rawLines is null)
6✔
681
        {
682
            return null;
2✔
683
        }
684
        if (!_rawHeaders.Contains(header))
4✔
685
        {
686
            throw new ArgumentException(nameof(header), "Invalid column header.");
2✔
687
        }
688

689
        return GetColumnData(_rawHeaders.IndexOf(header));
2✔
690
    }
691

692
    /// <summary>
693
    /// This method adds a line to the table.
694
    /// </summary>
695
    /// <param name="line">The line to add.</param>
696
    /// <exception cref="ArgumentException">Is thrown when the number of columns in the table is not consistent with itself or with the headers.</exception>
697
    /// <remarks>
698
    /// For more information, refer to the following resources:
699
    /// <list type="bullet">
700
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
701
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
702
    /// </list>
703
    /// </remarks>
704
    public void AddLine(List<T> line)
705
    {
706
        if (_rawLines?.Count > 0 && line.Count != _rawLines[0].Count)
6!
707
        {
708
            throw new ArgumentException(
2✔
709
                "The number of columns in the table is not consistent with other lines."
2✔
710
            );
2✔
711
        }
712
        if (_rawHeaders is not null && line.Count != _rawHeaders.Count)
4✔
713
        {
714
            throw new ArgumentException(
2✔
715
                "The number of columns in the table is not consistent with the headers."
2✔
716
            );
2✔
717
        }
718
        _rawLines ??= new List<List<T>>();
2!
719
        _rawLines.Add(line);
2✔
720
        BuildTable();
2✔
721
    }
2✔
722

723
    /// <summary>
724
    /// This method removes a line from the table.
725
    /// </summary>
726
    /// <param name="index">The index of the line to remove.</param>
727
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
728
    /// <remarks>
729
    /// For more information, refer to the following resources:
730
    /// <list type="bullet">
731
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
732
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
733
    /// </list>
734
    /// </remarks>
735
    public void RemoveLine(int index)
736
    {
737
        if (_rawLines?.Count > 0 && (index < 0 || index >= _rawLines.Count))
8!
738
        {
739
            throw new ArgumentOutOfRangeException(nameof(index), "The index is out of range.");
4✔
740
        }
741

742
        _rawLines?.RemoveAt(index);
4!
743
        BuildTable();
2✔
744
    }
2✔
745

746
    /// <summary>
747
    /// This method updates a line in the table.
748
    /// </summary>
749
    /// <param name="index">The index of the line to update.</param>
750
    /// <param name="line">The new line.</param>
751
    /// <exception cref="ArgumentOutOfRangeException">Is thrown when the index is out of range.</exception>
752
    /// <exception cref="ArgumentException">Is thrown when the number of columns in the table is not consistent with itself or with the headers.</exception>
753
    /// <remarks>
754
    /// For more information, refer to the following resources:
755
    /// <list type="bullet">
756
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
757
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
758
    /// </list>
759
    /// </remarks>
760
    public void UpdateLine(int index, List<T> line)
761
    {
762
        if (_rawLines?.Count > 0)
10!
763
        {
764
            if (index < 0 || index >= _rawLines.Count)
8✔
765
            {
766
                throw new ArgumentOutOfRangeException(nameof(index), "The index is out of range.");
4✔
767
            }
768

769
            if (line.Count != _rawHeaders?.Count)
4!
770
            {
771
                throw new ArgumentException(
2✔
772
                    "The number of columns in the table is not consistent."
2✔
773
                );
2✔
774
            }
775
        }
776
        _rawLines![index] = line;
4✔
777
        BuildTable();
2✔
778
    }
2✔
779

780
    /// <summary>
781
    /// This method clears the headers of the table.
782
    /// </summary>
783
    /// <remarks>
784
    /// For more information, refer to the following resources:
785
    /// <list type="bullet">
786
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
787
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
788
    /// </list>
789
    /// </remarks>
790
    public void ClearHeaders()
791
    {
792
        _rawHeaders = null;
2✔
793
        BuildTable();
2✔
794
    }
2✔
795

796
    /// <summary>
797
    /// This method clears the lines of the table.
798
    /// </summary>
799
    /// <remarks>
800
    /// For more information, refer to the following resources:
801
    /// <list type="bullet">
802
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
803
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
804
    /// </list>
805
    /// </remarks>
806
    public void ClearLines()
807
    {
808
        _rawLines = null;
2✔
809
        BuildTable();
2✔
810
    }
2✔
811

812
    /// <summary>
813
    /// This method clears the table.
814
    /// </summary>
815
    /// <remarks>
816
    /// For more information, refer to the following resources:
817
    /// <list type="bullet">
818
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
819
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
820
    /// </list>
821
    /// </remarks>
822
    public void Reset()
823
    {
824
        _title = null;
2✔
825
        _rawHeaders?.Clear();
2!
826
        _rawLines?.Clear();
2!
827
        _displayArray = null;
2✔
828
    }
2✔
829
    #endregion
830

831
    #region Display Methods
832
    /// <summary>
833
    /// This method displays the table without interaction.
834
    /// </summary>
835
    [Visual]
836
    protected override void RenderElementActions()
837
    {
838
        int minIndex = GetMinIndex(_excludeHeader);
839
        int maxIndex = GetMaxIndex(_excludeFooter);
840
        int index = minIndex;
841

842
        while (true)
843
        {
844
            string[] array = new string[_displayArray!.Length];
845
            for (int j = 0; j < _displayArray.Length; j++)
846
            {
847
                array[j] = _displayArray[j];
848
                Core.WritePositionedString(array[j], TextAlignment.Center, false, Line + j);
849
                if (j == index)
850
                {
851
                    Core.WritePositionedString(
852
                        j == _displayArray.Length - 1
853
                            ? array[j].InsertString($"┤ {_footerText} ├", Placement.TopCenter)[
854
                                2..^2
855
                            ]
856
                            : array[j][1..^1],
857
                        TextAlignment.Center,
858
                        true,
859
                        Line + j
860
                    );
861
                }
862
            }
863
            switch (Console.ReadKey(intercept: true).Key)
864
            {
865
                case ConsoleKey.UpArrow:
866
                case ConsoleKey.Z:
867
                    index = HandleUpArrowKey(index, minIndex, maxIndex);
868
                    break;
869
                case ConsoleKey.DownArrow:
870
                case ConsoleKey.S:
871
                    index = HandleDownArrowKey(index, minIndex, maxIndex);
872
                    break;
873
                case ConsoleKey.Enter:
874
                    SendResponse(
875
                        this,
876
                        new InteractionEventArgs<int>(Output.Selected, ReturnIndex(index))
877
                    );
878
                    return;
879
                case ConsoleKey.Escape:
880
                    SendResponse(this, new InteractionEventArgs<int>(Output.Escaped, 0));
881
                    return;
882
                case ConsoleKey.Backspace:
883
                    SendResponse(
884
                        this,
885
                        new InteractionEventArgs<int>(Output.Deleted, ReturnIndex(index))
886
                    );
887
                    return;
888
            }
889
        }
890
    }
891

892
    [Visual]
893
    int ReturnIndex(int index)
894
    {
895
        return index - GetMinIndex(_excludeHeader);
896
    }
897

898
    [Visual]
899
    int GetMinIndex(bool excludeHeader)
900
    {
901
        if (excludeHeader)
902
        {
903
            return _rawHeaders is null ? 3 : 5;
904
        }
905
        else
906
        {
907
            return 0;
908
        }
909
    }
910

911
    [Visual]
912
    int GetMaxIndex(bool excludeFooter)
913
    {
914
        return excludeFooter ? _displayArray!.Length - 2 : _displayArray!.Length - 1;
915
    }
916

917
    [Visual]
918
    static int HandleUpArrowKey(int index, int minIndex, int maxIndex)
919
    {
920
        if (index == minIndex)
921
        {
922
            return maxIndex;
923
        }
924
        else if (index > minIndex)
925
        {
926
            return index - 1;
927
        }
928
        return index;
929
    }
930

931
    [Visual]
932
    static int HandleDownArrowKey(int index, int minIndex, int maxIndex)
933
    {
934
        if (index == maxIndex)
935
        {
936
            return minIndex;
937
        }
938
        else if (index < maxIndex)
939
        {
940
            return index + 1;
941
        }
942
        return index;
943
    }
944
    #endregion
945
}
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