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

MorganKryze / ConsoleAppVisuals / 8418737641

25 Mar 2024 10:35AM UTC coverage: 93.788%. Remained the same
8418737641

push

github

MorganKryze
🚑 (TableSelector) fix render issue with first display not rendering properly

917 of 1042 branches covered (88.0%)

Branch coverage included in aggregate %.

2012 of 2081 relevant lines covered (96.68%)

252.64 hits per line

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

93.69
/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.InteractiveElements;
6

7
/// <summary>
8
/// A <see cref="TableSelector"/> is an interactive element that displays a table with selectable elements.
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 : InteractiveElement<int>
18
{
19
    #region Fields
20
    private string? _title;
21
    private List<string>? _rawHeaders;
22
    private List<List<string>>? _rawLines;
23
    private bool _excludeHeader;
24
    private bool _excludeFooter;
25
    private string? _footerText;
26
    private string[]? _displayArray;
27
    private readonly Borders _borders;
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;
7✔
37

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

48
    /// <summary>
49
    /// This property returns the title of the table.
50
    /// </summary>
51
    public string Title => _title ?? "";
5✔
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 ?? "New";
4✔
67

68
    /// <summary>
69
    /// This property returns the borders manager of the table.
70
    /// </summary>
71
    public Borders Borders => _borders;
320✔
72

73
    /// <summary>
74
    /// This property returns the type of the borders of the table.
75
    /// </summary>
76
    public BordersType BordersType => _borders.Type;
2✔
77

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

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

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

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

98
    #endregion
99

100
    #region Constructor
101
    /// <summary>
102
    /// A <see cref="TableSelector"/> is an interactive element that displays a table with selectable elements.
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
    /// <param name="bordersType">The type of the borders of the table.</param>
112
    /// <exception cref="ArgumentException">Is thrown when the number of columns in the table is not consistent with itself or with the headers.</exception>
113
    /// <exception cref="NullReferenceException">Is thrown when no body lines were provided.</exception>
114
    /// <remarks>
115
    /// For more information, refer to the following resources:
116
    /// <list type="bullet">
117
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
118
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
119
    /// </list>
120
    /// </remarks>
121
    public TableSelector(
71✔
122
        string? title = null,
71✔
123
        List<string>? headers = null,
71✔
124
        List<List<string>>? lines = null,
71✔
125
        bool excludeHeader = true,
71✔
126
        bool excludeFooter = true,
71✔
127
        string? footerText = null,
71✔
128
        Placement placement = Placement.TopCenter,
71✔
129
        BordersType bordersType = BordersType.SingleStraight
71✔
130
    )
71✔
131
    {
132
        _title = title;
71✔
133
        _rawHeaders = headers;
71✔
134
        _rawLines = lines;
71✔
135
        _excludeHeader = excludeHeader;
71✔
136
        _excludeFooter = excludeFooter;
71✔
137
        _footerText = footerText;
71✔
138
        _placement = placement;
71✔
139
        _borders = new Borders(bordersType);
71✔
140
        if (CompatibilityCheck())
71✔
141
        {
142
            BuildTable();
49✔
143
        }
144
    }
69✔
145
    #endregion
146

147
    #region Check Methods
148
    private bool CompatibilityCheck()
149
    {
150
        if (_rawHeaders is null)
84✔
151
        {
152
            return CheckRawLines();
16✔
153
        }
154
        else if (_rawLines is null)
68✔
155
        {
156
            return true;
3✔
157
        }
158
        else
159
        {
160
            return CheckRawHeadersAndLines();
65✔
161
        }
162
    }
163

164
    private bool CheckRawLines()
165
    {
166
        if (_rawLines is null || _rawLines.Count == 0)
16✔
167
        {
168
            return false;
11✔
169
        }
170

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

181
        return true;
4✔
182
    }
183

184
    private bool CheckRawHeadersAndLines()
185
    {
186
        if (_rawLines is null || _rawLines.Count == 0)
65✔
187
        {
188
            return false;
13✔
189
        }
190

191
        for (int i = 0; i < _rawLines.Count; i++)
410✔
192
        {
193
            if (_rawLines[i].Count != _rawHeaders?.Count)
155!
194
            {
195
                throw new ArgumentException(
2✔
196
                    "The number of columns in the table is not consistent(Headers or Lines)."
2✔
197
                );
2✔
198
            }
199
        }
200

201
        return true;
50✔
202
    }
203
    #endregion
204

205
    #region Build Methods
206
    private void BuildTable()
207
    {
208
        if (_rawHeaders is null)
64✔
209
        {
210
            if (_rawLines is not null)
5✔
211
            {
212
                BuildLines();
5✔
213
            }
214
        }
215
        else
216
        {
217
            if (_rawLines is null)
59✔
218
            {
219
                BuildHeaders();
4✔
220
            }
221
            else
222
            {
223
                BuildHeadersAndLines();
55✔
224
            }
225
        }
226
    }
55✔
227

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

242
            for (int i = 0; i < _rawLines.Count; i++)
430✔
243
            {
244
                for (int j = 0; j < _rawLines[i].Count; j++)
1,920✔
245
                {
246
                    if (_rawLines[i][j]?.ToString()?.Length > localMax[j])
800!
247
                    {
248
                        localMax[j] = _rawLines[i][j]?.ToString()?.Length ?? 0;
36!
249
                    }
250
                }
251
            }
252

253
            StringBuilder headerBuilder = new("│ ");
55✔
254
            for (int i = 0; i < _rawHeaders.Count; i++)
660✔
255
            {
256
                headerBuilder.Append(_rawHeaders[i]?.PadRight(localMax[i]) ?? "");
275!
257
                if (i != _rawHeaders.Count - 1)
275✔
258
                {
259
                    headerBuilder.Append(" │ ");
220✔
260
                }
261
                else
262
                {
263
                    headerBuilder.Append(" │");
55✔
264
                }
265
            }
266
            stringList.Add(headerBuilder.ToString());
55✔
267

268
            StringBuilder upperBorderBuilder = new(Borders.TopLeft.ToString());
55✔
269
            for (int i = 0; i < _rawHeaders.Count; i++)
660✔
270
            {
271
                upperBorderBuilder.Append(new string('─', localMax[i] + 2));
275✔
272
                upperBorderBuilder.Append(
275✔
273
                    (i != _rawHeaders.Count - 1) ? "┬" : Borders.TopRight.ToString()
275✔
274
                );
275✔
275
            }
276
            stringList.Insert(0, upperBorderBuilder.ToString());
55✔
277

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

286
            for (int i = 0; i < _rawLines.Count; i++)
430✔
287
            {
288
                StringBuilder lineBuilder = new("│ ");
160✔
289
                for (int j = 0; j < _rawLines[i].Count; j++)
1,920✔
290
                {
291
                    lineBuilder.Append(_rawLines[i][j]?.ToString()?.PadRight(localMax[j]) ?? "");
800!
292
                    if (j != _rawLines[i].Count - 1)
800✔
293
                    {
294
                        lineBuilder.Append(" │ ");
640✔
295
                    }
296
                    else
297
                    {
298
                        lineBuilder.Append(" │");
160✔
299
                    }
300
                }
301
                stringList.Add(lineBuilder.ToString());
160✔
302
            }
303

304
            StringBuilder lowerBorderBuilder = new(Borders.BottomLeft.ToString());
55✔
305
            for (int i = 0; i < _rawHeaders.Count; i++)
660✔
306
            {
307
                lowerBorderBuilder.Append(new string('─', localMax[i] + 2));
275✔
308
                lowerBorderBuilder.Append(
275✔
309
                    (i != _rawHeaders.Count - 1) ? "â”´" : Borders.BottomRight.ToString()
275✔
310
                );
275✔
311
            }
312
            stringList.Add(lowerBorderBuilder.ToString());
55✔
313

314
            _displayArray = stringList.ToArray();
55✔
315
            BuildTitle();
55✔
316
        }
317
    }
55✔
318

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

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

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

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

469
    /// <summary>
470
    /// This method updates the type of the borders of the table.
471
    /// </summary>
472
    /// <param name="bordersType">The type of the borders of the table.</param>
473
    /// <remarks>
474
    /// For more information, refer to the following resources:
475
    /// <list type="bullet">
476
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
477
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
478
    /// </list>
479
    /// </remarks>
480
    public void UpdateBordersType(BordersType bordersType)
481
    {
482
        _borders.UpdateBordersType(bordersType);
2✔
483
        if (CompatibilityCheck())
2✔
484
        {
485
            BuildTable();
1✔
486
        }
487
    }
2✔
488

489
    /// <summary>
490
    /// This method updates the text of the footer.
491
    /// </summary>
492
    /// <param name="footerText"></param>
493
    public void UpdateFooterText(string footerText)
494
    {
495
        _footerText = footerText;
2✔
496
        if (CompatibilityCheck())
2✔
497
        {
498
            BuildTable();
1✔
499
        }
500
    }
2✔
501

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

522
    /// <summary>
523
    /// This method sets the table to exclude the footer.
524
    /// </summary>
525
    /// <param name="excludeFooter">Whether to exclude the footer or not.</param>
526
    /// <remarks>
527
    /// For more information, refer to the following resources:
528
    /// <list type="bullet">
529
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
530
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
531
    /// </list>
532
    /// </remarks>
533
    public void SetExcludeFooter(bool excludeFooter = true)
534
    {
535
        _excludeFooter = excludeFooter;
2✔
536
        if (CompatibilityCheck())
2✔
537
        {
538
            BuildTable();
1✔
539
        }
540
    }
2✔
541

542
    /// <summary>
543
    /// This method adds headers to the table.
544
    /// </summary>
545
    /// <param name="headers">The headers to add.</param>
546
    /// <remarks>
547
    /// For more information, refer to the following resources:
548
    /// <list type="bullet">
549
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
550
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
551
    /// </list>
552
    /// </remarks>
553
    public void AddHeaders(List<string> headers)
554
    {
555
        _rawHeaders = headers;
3✔
556
        if (CompatibilityCheck())
3✔
557
        {
558
            BuildTable();
2✔
559
        }
560
    }
2✔
561

562
    /// <summary>
563
    /// This method updates the headers of the table.
564
    /// </summary>
565
    /// <param name="headers">The headers to update.</param>
566
    /// <remarks>
567
    /// For more information, refer to the following resources:
568
    /// <list type="bullet">
569
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
570
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
571
    /// </list>
572
    /// </remarks>
573
    public void UpdateHeaders(List<string> headers)
574
    {
575
        AddHeaders(headers);
1✔
576
    }
1✔
577

578
    /// <summary>
579
    /// This method adds a title to the table.
580
    /// </summary>
581
    /// <param name="title">The title to add.</param>
582
    /// <remarks>
583
    /// For more information, refer to the following resources:
584
    /// <list type="bullet">
585
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
586
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
587
    /// </list>
588
    /// </remarks>
589
    public void AddTitle(string title)
590
    {
591
        _title = title;
2✔
592
        BuildTable();
2✔
593
    }
2✔
594

595
    /// <summary>
596
    /// This method updates the title of the table.
597
    /// </summary>
598
    /// <param name="title">The title to update.</param>
599
    /// <remarks>
600
    /// For more information, refer to the following resources:
601
    /// <list type="bullet">
602
    /// <item><description><a href="https://morgankryze.github.io/ConsoleAppVisuals/">Documentation</a></description></item>
603
    /// <item><description><a href="https://github.com/MorganKryze/ConsoleAppVisuals/blob/main/example/">Example Project</a></description></item>
604
    /// </list>
605
    /// </remarks>
606
    public void UpdateTitle(string title)
607
    {
608
        AddTitle(title);
1✔
609
    }
1✔
610

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

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

653
        if (index < 0 || index >= _rawLines[0].Count)
5✔
654
        {
655
            throw new ArgumentOutOfRangeException(nameof(index), "Invalid column index.");
3✔
656
        }
657

658
        List<string>? list = new();
2✔
659
        for (int i = 0; i < _rawLines.Count; i++)
16✔
660
        {
661
            list.Add(_rawLines[i][index]);
6✔
662
        }
663
        return list;
2✔
664
    }
665

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

694
        return GetColumnData(_rawHeaders.IndexOf(header));
1✔
695
    }
696

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

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

747
        _rawLines?.RemoveAt(index);
2!
748
        BuildTable();
1✔
749
    }
1✔
750

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

774
            if (line.Count != _rawHeaders?.Count)
2!
775
            {
776
                throw new ArgumentException(
1✔
777
                    "The number of columns in the table is not consistent."
1✔
778
                );
1✔
779
            }
780
        }
781
        _rawLines![index] = line;
2✔
782
        BuildTable();
1✔
783
    }
1✔
784

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

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

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

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

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

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

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

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

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

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