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

veeso / tui-realm-stdlib / 15137208932

20 May 2025 12:13PM UTC coverage: 67.595% (-0.7%) from 68.289%
15137208932

push

github

web-flow
Fix clippy lints, apply some pedantic fixes and general small improvements (#32)

* style(examples): directly have values as a type instead of casting

* style(examples/utils): ignore unused warnings

* style: run clippy auto fix

and remove redundant tests from "label"

* style: apply some clippy pedantic auto fixes

* test: set specific strings for "should_panic"

So that other panics are catched as failed tests.

* style(bar_chart): remove casting to "u64" when "usize" is directly provided and needed

* refactor(table): move making rows to own function

To appease "clippy::too_many_lines" and slightly reduce nesting.

* style: add "#[must_use]" where applicable

To hint not to forget a value.

104 of 192 new or added lines in 19 files covered. (54.17%)

12 existing lines in 2 files now uncovered.

2985 of 4416 relevant lines covered (67.6%)

1.66 hits per line

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

77.42
/src/components/list.rs
1
//! ## List
2
//!
3
//! `List` represents a read-only textual list component which can be scrollable through arrows or inactive
4

5
use tuirealm::command::{Cmd, CmdResult, Direction, Position};
6
use tuirealm::props::{
7
    Alignment, AttrValue, Attribute, Borders, Color, PropPayload, PropValue, Props, Style, Table,
8
    TextModifiers,
9
};
10
use tuirealm::ratatui::text::Line as Spans;
11
use tuirealm::ratatui::{
12
    layout::Rect,
13
    text::Span,
14
    widgets::{List as TuiList, ListItem, ListState},
15
};
16
use tuirealm::{Frame, MockComponent, State, StateValue};
17

18
// -- States
19

20
#[derive(Default)]
21
pub struct ListStates {
22
    pub list_index: usize, // Index of selected item in list
23
    pub list_len: usize,   // Lines in text area
24
}
25

26
impl ListStates {
27
    /// ### set_list_len
28
    ///
29
    /// Set list length
30
    pub fn set_list_len(&mut self, len: usize) {
6✔
31
        self.list_len = len;
6✔
32
    }
6✔
33

34
    /// ### incr_list_index
35
    ///
36
    /// Incremenet list index
37
    pub fn incr_list_index(&mut self, rewind: bool) {
9✔
38
        // Check if index is at last element
39
        if self.list_index + 1 < self.list_len {
9✔
40
            self.list_index += 1;
7✔
41
        } else if rewind {
7✔
42
            self.list_index = 0;
1✔
43
        }
1✔
44
    }
9✔
45

46
    /// ### decr_list_index
47
    ///
48
    /// Decrement list index
49
    pub fn decr_list_index(&mut self, rewind: bool) {
10✔
50
        // Check if index is bigger than 0
51
        if self.list_index > 0 {
10✔
52
            self.list_index -= 1;
8✔
53
        } else if rewind && self.list_len > 0 {
8✔
54
            self.list_index = self.list_len - 1;
1✔
55
        }
1✔
56
    }
10✔
57

58
    /// ### fix_list_index
59
    ///
60
    /// Keep index if possible, otherwise set to lenght - 1
61
    pub fn fix_list_index(&mut self) {
7✔
62
        if self.list_index >= self.list_len && self.list_len > 0 {
7✔
63
            self.list_index = self.list_len - 1;
2✔
64
        } else if self.list_len == 0 {
5✔
65
            self.list_index = 0;
×
66
        }
5✔
67
    }
7✔
68

69
    /// ### list_index_at_first
70
    ///
71
    /// Set list index to the first item in the list
72
    pub fn list_index_at_first(&mut self) {
2✔
73
        self.list_index = 0;
2✔
74
    }
2✔
75

76
    /// ### list_index_at_last
77
    ///
78
    /// Set list index at the last item of the list
79
    pub fn list_index_at_last(&mut self) {
2✔
80
        if self.list_len > 0 {
2✔
81
            self.list_index = self.list_len - 1;
2✔
82
        } else {
2✔
83
            self.list_index = 0;
×
84
        }
×
85
    }
2✔
86

87
    /// ### calc_max_step_ahead
88
    ///
89
    /// Calculate the max step ahead to scroll list
90
    #[must_use]
91
    pub fn calc_max_step_ahead(&self, max: usize) -> usize {
2✔
92
        let remaining: usize = match self.list_len {
2✔
93
            0 => 0,
×
94
            len => len - 1 - self.list_index,
2✔
95
        };
96
        if remaining > max {
2✔
97
            max
1✔
98
        } else {
99
            remaining
1✔
100
        }
101
    }
2✔
102

103
    /// ### calc_max_step_ahead
104
    ///
105
    /// Calculate the max step ahead to scroll list
106
    #[must_use]
107
    pub fn calc_max_step_behind(&self, max: usize) -> usize {
2✔
108
        if self.list_index > max {
2✔
109
            max
1✔
110
        } else {
111
            self.list_index
1✔
112
        }
113
    }
2✔
114
}
115

116
// -- Component
117

118
/// ## List
119
///
120
/// represents a read-only text component without any container.
121
#[derive(Default)]
122
#[must_use]
123
pub struct List {
124
    props: Props,
125
    pub states: ListStates,
126
    hg_str: Option<String>, // CRAP CRAP CRAP. Thanks to the author of tui-realm for using references every f time
127
}
128

129
impl List {
130
    pub fn foreground(mut self, fg: Color) -> Self {
3✔
131
        self.attr(Attribute::Foreground, AttrValue::Color(fg));
3✔
132
        self
3✔
133
    }
3✔
134

135
    pub fn background(mut self, bg: Color) -> Self {
3✔
136
        self.attr(Attribute::Background, AttrValue::Color(bg));
3✔
137
        self
3✔
138
    }
3✔
139

140
    pub fn modifiers(mut self, m: TextModifiers) -> Self {
3✔
141
        self.attr(Attribute::TextProps, AttrValue::TextModifiers(m));
3✔
142
        self
3✔
143
    }
3✔
144

145
    pub fn borders(mut self, b: Borders) -> Self {
3✔
146
        self.attr(Attribute::Borders, AttrValue::Borders(b));
3✔
147
        self
3✔
148
    }
3✔
149

150
    pub fn title<S: Into<String>>(mut self, t: S, a: Alignment) -> Self {
3✔
151
        self.attr(Attribute::Title, AttrValue::Title((t.into(), a)));
3✔
152
        self
3✔
153
    }
3✔
154

155
    pub fn inactive(mut self, s: Style) -> Self {
×
156
        self.attr(Attribute::FocusStyle, AttrValue::Style(s));
×
157
        self
×
158
    }
×
159

160
    pub fn rewind(mut self, r: bool) -> Self {
1✔
161
        self.attr(Attribute::Rewind, AttrValue::Flag(r));
1✔
162
        self
1✔
163
    }
1✔
164

165
    pub fn step(mut self, step: usize) -> Self {
1✔
166
        self.attr(Attribute::ScrollStep, AttrValue::Length(step));
1✔
167
        self
1✔
168
    }
1✔
169

170
    pub fn scroll(mut self, scrollable: bool) -> Self {
2✔
171
        self.attr(Attribute::Scroll, AttrValue::Flag(scrollable));
2✔
172
        self
2✔
173
    }
2✔
174

175
    pub fn highlighted_str<S: Into<String>>(mut self, s: S) -> Self {
3✔
176
        self.attr(Attribute::HighlightedStr, AttrValue::String(s.into()));
3✔
177
        self
3✔
178
    }
3✔
179

180
    pub fn highlighted_color(mut self, c: Color) -> Self {
3✔
181
        self.attr(Attribute::HighlightedColor, AttrValue::Color(c));
3✔
182
        self
3✔
183
    }
3✔
184

185
    pub fn rows(mut self, rows: Table) -> Self {
3✔
186
        self.attr(Attribute::Content, AttrValue::Table(rows));
3✔
187
        self
3✔
188
    }
3✔
189

190
    /// Set initial selected line
191
    /// This method must be called after `rows` and `scrollable` in order to work
192
    pub fn selected_line(mut self, line: usize) -> Self {
1✔
193
        self.attr(
1✔
194
            Attribute::Value,
1✔
195
            AttrValue::Payload(PropPayload::One(PropValue::Usize(line))),
1✔
196
        );
197
        self
1✔
198
    }
1✔
199

200
    fn scrollable(&self) -> bool {
12✔
201
        self.props
12✔
202
            .get_or(Attribute::Scroll, AttrValue::Flag(false))
12✔
203
            .unwrap_flag()
12✔
204
    }
12✔
205

206
    fn rewindable(&self) -> bool {
2✔
207
        self.props
2✔
208
            .get_or(Attribute::Rewind, AttrValue::Flag(false))
2✔
209
            .unwrap_flag()
2✔
210
    }
2✔
211
}
212

213
impl MockComponent for List {
214
    fn view(&mut self, render: &mut Frame, area: Rect) {
×
215
        if self.props.get_or(Attribute::Display, AttrValue::Flag(true)) == AttrValue::Flag(true) {
×
216
            let foreground = self
×
217
                .props
×
218
                .get_or(Attribute::Foreground, AttrValue::Color(Color::Reset))
×
219
                .unwrap_color();
×
220
            let background = self
×
221
                .props
×
222
                .get_or(Attribute::Background, AttrValue::Color(Color::Reset))
×
223
                .unwrap_color();
×
224
            let modifiers = self
×
225
                .props
×
226
                .get_or(
×
227
                    Attribute::TextProps,
×
228
                    AttrValue::TextModifiers(TextModifiers::empty()),
×
229
                )
230
                .unwrap_text_modifiers();
×
231
            let title = crate::utils::get_title_or_center(&self.props);
×
232
            let borders = self
×
233
                .props
×
234
                .get_or(Attribute::Borders, AttrValue::Borders(Borders::default()))
×
235
                .unwrap_borders();
×
236
            let focus = self
×
237
                .props
×
238
                .get_or(Attribute::Focus, AttrValue::Flag(false))
×
239
                .unwrap_flag();
×
240
            let inactive_style = self
×
241
                .props
×
242
                .get(Attribute::FocusStyle)
×
243
                .map(|x| x.unwrap_style());
×
NEW
244
            let active: bool = if self.scrollable() { focus } else { true };
×
245
            let div = crate::utils::get_block(borders, Some(&title), active, inactive_style);
×
246
            // Make list entries
247
            let list_items: Vec<ListItem> = match self
×
248
                .props
×
249
                .get_ref(Attribute::Content)
×
250
                .and_then(|x| x.as_table())
×
251
            {
252
                Some(table) => table
×
253
                    .iter()
×
254
                    .map(|row| {
×
255
                        let columns: Vec<Span> = row
×
256
                            .iter()
×
257
                            .map(|col| {
×
258
                                let (fg, bg, modifiers) =
×
259
                                    crate::utils::use_or_default_styles(&self.props, col);
×
260
                                Span::styled(
×
261
                                    &col.content,
×
262
                                    Style::default().add_modifier(modifiers).fg(fg).bg(bg),
×
263
                                )
264
                            })
×
265
                            .collect();
×
266
                        ListItem::new(Spans::from(columns))
×
267
                    })
×
268
                    .collect(), // Make List item from TextSpan
×
269
                _ => Vec::new(),
×
270
            };
271
            let highlighted_color = self
×
272
                .props
×
273
                .get(Attribute::HighlightedColor)
×
274
                .map(|x| x.unwrap_color());
×
NEW
275
            let modifiers = if focus {
×
NEW
276
                modifiers | TextModifiers::REVERSED
×
277
            } else {
NEW
278
                modifiers
×
279
            };
280
            // Make list
281

282
            let mut list = TuiList::new(list_items)
×
283
                .block(div)
×
284
                .style(Style::default().fg(foreground).bg(background))
×
285
                .direction(tuirealm::ratatui::widgets::ListDirection::TopToBottom);
×
286
            if let Some(highlighted_color) = highlighted_color {
×
287
                list = list.highlight_style(
×
288
                    Style::default()
×
289
                        .fg(highlighted_color)
×
290
                        .add_modifier(modifiers),
×
291
                );
×
292
            }
×
293
            // Highlighted symbol
294
            self.hg_str = self
×
295
                .props
×
296
                .get(Attribute::HighlightedStr)
×
297
                .map(|x| x.unwrap_string());
×
298
            if let Some(hg_str) = &self.hg_str {
×
299
                list = list.highlight_symbol(hg_str);
×
300
            }
×
301
            if self.scrollable() {
×
302
                let mut state: ListState = ListState::default();
×
303
                state.select(Some(self.states.list_index));
×
304
                render.render_stateful_widget(list, area, &mut state);
×
305
            } else {
×
306
                render.render_widget(list, area);
×
307
            }
×
308
        }
×
309
    }
×
310

311
    fn query(&self, attr: Attribute) -> Option<AttrValue> {
×
312
        self.props.get(attr)
×
313
    }
×
314

315
    fn attr(&mut self, attr: Attribute, value: AttrValue) {
31✔
316
        self.props.set(attr, value);
31✔
317
        if matches!(attr, Attribute::Content) {
31✔
318
            // Update list len and fix index
319
            self.states.set_list_len(
4✔
320
                match self.props.get(Attribute::Content).map(|x| x.unwrap_table()) {
4✔
321
                    Some(spans) => spans.len(),
4✔
322
                    _ => 0,
×
323
                },
324
            );
325
            self.states.fix_list_index();
4✔
326
        } else if matches!(attr, Attribute::Value) && self.scrollable() {
27✔
327
            self.states.list_index = self
2✔
328
                .props
2✔
329
                .get(Attribute::Value)
2✔
330
                .map_or(0, |x| x.unwrap_payload().unwrap_one().unwrap_usize());
2✔
331
            self.states.fix_list_index();
2✔
332
        }
25✔
333
    }
31✔
334

335
    fn state(&self) -> State {
10✔
336
        if self.scrollable() {
10✔
337
            State::One(StateValue::Usize(self.states.list_index))
9✔
338
        } else {
339
            State::None
1✔
340
        }
341
    }
10✔
342

343
    fn perform(&mut self, cmd: Cmd) -> CmdResult {
8✔
344
        match cmd {
2✔
345
            Cmd::Move(Direction::Down) => {
346
                let prev = self.states.list_index;
1✔
347
                self.states.incr_list_index(self.rewindable());
1✔
348
                if prev == self.states.list_index {
1✔
UNCOV
349
                    CmdResult::None
×
350
                } else {
351
                    CmdResult::Changed(self.state())
1✔
352
                }
353
            }
354
            Cmd::Move(Direction::Up) => {
355
                let prev = self.states.list_index;
1✔
356
                self.states.decr_list_index(self.rewindable());
1✔
357
                if prev == self.states.list_index {
1✔
UNCOV
358
                    CmdResult::None
×
359
                } else {
360
                    CmdResult::Changed(self.state())
1✔
361
                }
362
            }
363
            Cmd::Scroll(Direction::Down) => {
364
                let prev = self.states.list_index;
2✔
365
                let step = self
2✔
366
                    .props
2✔
367
                    .get_or(Attribute::ScrollStep, AttrValue::Length(8))
2✔
368
                    .unwrap_length();
2✔
369
                let step: usize = self.states.calc_max_step_ahead(step);
2✔
370
                (0..step).for_each(|_| self.states.incr_list_index(false));
5✔
371
                if prev == self.states.list_index {
2✔
UNCOV
372
                    CmdResult::None
×
373
                } else {
374
                    CmdResult::Changed(self.state())
2✔
375
                }
376
            }
377
            Cmd::Scroll(Direction::Up) => {
378
                let prev = self.states.list_index;
2✔
379
                let step = self
2✔
380
                    .props
2✔
381
                    .get_or(Attribute::ScrollStep, AttrValue::Length(8))
2✔
382
                    .unwrap_length();
2✔
383
                let step: usize = self.states.calc_max_step_behind(step);
2✔
384
                (0..step).for_each(|_| self.states.decr_list_index(false));
6✔
385
                if prev == self.states.list_index {
2✔
UNCOV
386
                    CmdResult::None
×
387
                } else {
388
                    CmdResult::Changed(self.state())
2✔
389
                }
390
            }
391
            Cmd::GoTo(Position::Begin) => {
392
                let prev = self.states.list_index;
1✔
393
                self.states.list_index_at_first();
1✔
394
                if prev == self.states.list_index {
1✔
UNCOV
395
                    CmdResult::None
×
396
                } else {
397
                    CmdResult::Changed(self.state())
1✔
398
                }
399
            }
400
            Cmd::GoTo(Position::End) => {
401
                let prev = self.states.list_index;
1✔
402
                self.states.list_index_at_last();
1✔
403
                if prev == self.states.list_index {
1✔
UNCOV
404
                    CmdResult::None
×
405
                } else {
406
                    CmdResult::Changed(self.state())
1✔
407
                }
408
            }
409
            _ => CmdResult::None,
×
410
        }
411
    }
8✔
412
}
413

414
#[cfg(test)]
415
mod tests {
416

417
    use super::*;
418
    use pretty_assertions::assert_eq;
419
    use tuirealm::props::{TableBuilder, TextSpan};
420

421
    #[test]
422
    fn list_states() {
1✔
423
        let mut states = ListStates::default();
1✔
424
        assert_eq!(states.list_index, 0);
1✔
425
        assert_eq!(states.list_len, 0);
1✔
426
        states.set_list_len(5);
1✔
427
        assert_eq!(states.list_index, 0);
1✔
428
        assert_eq!(states.list_len, 5);
1✔
429
        // Incr
430
        states.incr_list_index(true);
1✔
431
        assert_eq!(states.list_index, 1);
1✔
432
        states.list_index = 4;
1✔
433
        states.incr_list_index(false);
1✔
434
        assert_eq!(states.list_index, 4);
1✔
435
        states.incr_list_index(true);
1✔
436
        assert_eq!(states.list_index, 0);
1✔
437
        // Decr
438
        states.decr_list_index(false);
1✔
439
        assert_eq!(states.list_index, 0);
1✔
440
        states.decr_list_index(true);
1✔
441
        assert_eq!(states.list_index, 4);
1✔
442
        states.decr_list_index(true);
1✔
443
        assert_eq!(states.list_index, 3);
1✔
444
        // Begin
445
        states.list_index_at_first();
1✔
446
        assert_eq!(states.list_index, 0);
1✔
447
        states.list_index_at_last();
1✔
448
        assert_eq!(states.list_index, 4);
1✔
449
        // Fix
450
        states.set_list_len(3);
1✔
451
        states.fix_list_index();
1✔
452
        assert_eq!(states.list_index, 2);
1✔
453
    }
1✔
454

455
    #[test]
456
    fn test_components_list_scrollable() {
1✔
457
        let mut component = List::default()
1✔
458
            .foreground(Color::Red)
1✔
459
            .background(Color::Blue)
1✔
460
            .highlighted_color(Color::Yellow)
1✔
461
            .highlighted_str("🚀")
1✔
462
            .modifiers(TextModifiers::BOLD)
1✔
463
            .scroll(true)
1✔
464
            .step(4)
1✔
465
            .borders(Borders::default())
1✔
466
            .title("events", Alignment::Center)
1✔
467
            .rewind(true)
1✔
468
            .rows(
1✔
469
                TableBuilder::default()
1✔
470
                    .add_col(TextSpan::from("KeyCode::Down"))
1✔
471
                    .add_col(TextSpan::from("OnKey"))
1✔
472
                    .add_col(TextSpan::from("Move cursor down"))
1✔
473
                    .add_row()
1✔
474
                    .add_col(TextSpan::from("KeyCode::Up"))
1✔
475
                    .add_col(TextSpan::from("OnKey"))
1✔
476
                    .add_col(TextSpan::from("Move cursor up"))
1✔
477
                    .add_row()
1✔
478
                    .add_col(TextSpan::from("KeyCode::PageDown"))
1✔
479
                    .add_col(TextSpan::from("OnKey"))
1✔
480
                    .add_col(TextSpan::from("Move cursor down by 8"))
1✔
481
                    .add_row()
1✔
482
                    .add_col(TextSpan::from("KeyCode::PageUp"))
1✔
483
                    .add_col(TextSpan::from("OnKey"))
1✔
484
                    .add_col(TextSpan::from("ove cursor up by 8"))
1✔
485
                    .add_row()
1✔
486
                    .add_col(TextSpan::from("KeyCode::End"))
1✔
487
                    .add_col(TextSpan::from("OnKey"))
1✔
488
                    .add_col(TextSpan::from("Move cursor to last item"))
1✔
489
                    .add_row()
1✔
490
                    .add_col(TextSpan::from("KeyCode::Home"))
1✔
491
                    .add_col(TextSpan::from("OnKey"))
1✔
492
                    .add_col(TextSpan::from("Move cursor to first item"))
1✔
493
                    .add_row()
1✔
494
                    .add_col(TextSpan::from("KeyCode::Char(_)"))
1✔
495
                    .add_col(TextSpan::from("OnKey"))
1✔
496
                    .add_col(TextSpan::from("Return pressed key"))
1✔
497
                    .add_col(TextSpan::from("4th mysterious columns"))
1✔
498
                    .build(),
1✔
499
            );
500
        assert_eq!(component.states.list_len, 7);
1✔
501
        assert_eq!(component.states.list_index, 0);
1✔
502
        // Increment list index
503
        component.states.list_index += 1;
1✔
504
        assert_eq!(component.states.list_index, 1);
1✔
505
        // Check messages
506
        // Handle inputs
507
        assert_eq!(
1✔
508
            component.perform(Cmd::Move(Direction::Down)),
1✔
509
            CmdResult::Changed(State::One(StateValue::Usize(2)))
510
        );
511
        // Index should be incremented
512
        assert_eq!(component.states.list_index, 2);
1✔
513
        // Index should be decremented
514
        assert_eq!(
1✔
515
            component.perform(Cmd::Move(Direction::Up)),
1✔
516
            CmdResult::Changed(State::One(StateValue::Usize(1)))
517
        );
518
        // Index should be incremented
519
        assert_eq!(component.states.list_index, 1);
1✔
520
        // Index should be 2
521
        assert_eq!(
1✔
522
            component.perform(Cmd::Scroll(Direction::Down)),
1✔
523
            CmdResult::Changed(State::One(StateValue::Usize(5)))
524
        );
525
        // Index should be incremented
526
        assert_eq!(component.states.list_index, 5);
1✔
527
        assert_eq!(
1✔
528
            component.perform(Cmd::Scroll(Direction::Down)),
1✔
529
            CmdResult::Changed(State::One(StateValue::Usize(6)))
530
        );
531
        // Index should be incremented
532
        assert_eq!(component.states.list_index, 6);
1✔
533
        // Index should be 0
534
        assert_eq!(
1✔
535
            component.perform(Cmd::Scroll(Direction::Up)),
1✔
536
            CmdResult::Changed(State::One(StateValue::Usize(2)))
537
        );
538
        assert_eq!(component.states.list_index, 2);
1✔
539
        assert_eq!(
1✔
540
            component.perform(Cmd::Scroll(Direction::Up)),
1✔
541
            CmdResult::Changed(State::One(StateValue::Usize(0)))
542
        );
543
        assert_eq!(component.states.list_index, 0);
1✔
544
        // End
545
        assert_eq!(
1✔
546
            component.perform(Cmd::GoTo(Position::End)),
1✔
547
            CmdResult::Changed(State::One(StateValue::Usize(6)))
548
        );
549
        assert_eq!(component.states.list_index, 6);
1✔
550
        // Home
551
        assert_eq!(
1✔
552
            component.perform(Cmd::GoTo(Position::Begin)),
1✔
553
            CmdResult::Changed(State::One(StateValue::Usize(0)))
554
        );
555
        assert_eq!(component.states.list_index, 0);
1✔
556
        // Update
557
        component.attr(
1✔
558
            Attribute::Content,
1✔
559
            AttrValue::Table(
1✔
560
                TableBuilder::default()
1✔
561
                    .add_col(TextSpan::from("name"))
1✔
562
                    .add_col(TextSpan::from("age"))
1✔
563
                    .add_col(TextSpan::from("birthdate"))
1✔
564
                    .build(),
1✔
565
            ),
1✔
566
        );
567
        assert_eq!(component.states.list_len, 1);
1✔
568
        assert_eq!(component.states.list_index, 0);
1✔
569
        // Get value
570
        assert_eq!(component.state(), State::One(StateValue::Usize(0)));
1✔
571
    }
1✔
572

573
    #[test]
574
    fn test_components_list() {
1✔
575
        let component = List::default()
1✔
576
            .foreground(Color::Red)
1✔
577
            .background(Color::Blue)
1✔
578
            .highlighted_color(Color::Yellow)
1✔
579
            .highlighted_str("🚀")
1✔
580
            .modifiers(TextModifiers::BOLD)
1✔
581
            .borders(Borders::default())
1✔
582
            .title("events", Alignment::Center)
1✔
583
            .rows(
1✔
584
                TableBuilder::default()
1✔
585
                    .add_col(TextSpan::from("KeyCode::Down"))
1✔
586
                    .add_col(TextSpan::from("OnKey"))
1✔
587
                    .add_col(TextSpan::from("Move cursor down"))
1✔
588
                    .add_row()
1✔
589
                    .add_col(TextSpan::from("KeyCode::Up"))
1✔
590
                    .add_col(TextSpan::from("OnKey"))
1✔
591
                    .add_col(TextSpan::from("Move cursor up"))
1✔
592
                    .add_row()
1✔
593
                    .add_col(TextSpan::from("KeyCode::PageDown"))
1✔
594
                    .add_col(TextSpan::from("OnKey"))
1✔
595
                    .add_col(TextSpan::from("Move cursor down by 8"))
1✔
596
                    .add_row()
1✔
597
                    .add_col(TextSpan::from("KeyCode::PageUp"))
1✔
598
                    .add_col(TextSpan::from("OnKey"))
1✔
599
                    .add_col(TextSpan::from("ove cursor up by 8"))
1✔
600
                    .add_row()
1✔
601
                    .add_col(TextSpan::from("KeyCode::End"))
1✔
602
                    .add_col(TextSpan::from("OnKey"))
1✔
603
                    .add_col(TextSpan::from("Move cursor to last item"))
1✔
604
                    .add_row()
1✔
605
                    .add_col(TextSpan::from("KeyCode::Home"))
1✔
606
                    .add_col(TextSpan::from("OnKey"))
1✔
607
                    .add_col(TextSpan::from("Move cursor to first item"))
1✔
608
                    .add_row()
1✔
609
                    .add_col(TextSpan::from("KeyCode::Char(_)"))
1✔
610
                    .add_col(TextSpan::from("OnKey"))
1✔
611
                    .add_col(TextSpan::from("Return pressed key"))
1✔
612
                    .build(),
1✔
613
            );
614
        // Get value (not scrollable)
615
        assert_eq!(component.state(), State::None);
1✔
616
    }
1✔
617

618
    #[test]
619
    fn should_init_list_value() {
1✔
620
        let mut component = List::default()
1✔
621
            .foreground(Color::Red)
1✔
622
            .background(Color::Blue)
1✔
623
            .highlighted_color(Color::Yellow)
1✔
624
            .highlighted_str("🚀")
1✔
625
            .modifiers(TextModifiers::BOLD)
1✔
626
            .borders(Borders::default())
1✔
627
            .title("events", Alignment::Center)
1✔
628
            .rows(
1✔
629
                TableBuilder::default()
1✔
630
                    .add_col(TextSpan::from("KeyCode::Down"))
1✔
631
                    .add_col(TextSpan::from("OnKey"))
1✔
632
                    .add_col(TextSpan::from("Move cursor down"))
1✔
633
                    .add_row()
1✔
634
                    .add_col(TextSpan::from("KeyCode::Up"))
1✔
635
                    .add_col(TextSpan::from("OnKey"))
1✔
636
                    .add_col(TextSpan::from("Move cursor up"))
1✔
637
                    .add_row()
1✔
638
                    .add_col(TextSpan::from("KeyCode::PageDown"))
1✔
639
                    .add_col(TextSpan::from("OnKey"))
1✔
640
                    .add_col(TextSpan::from("Move cursor down by 8"))
1✔
641
                    .add_row()
1✔
642
                    .add_col(TextSpan::from("KeyCode::PageUp"))
1✔
643
                    .add_col(TextSpan::from("OnKey"))
1✔
644
                    .add_col(TextSpan::from("ove cursor up by 8"))
1✔
645
                    .add_row()
1✔
646
                    .add_col(TextSpan::from("KeyCode::End"))
1✔
647
                    .add_col(TextSpan::from("OnKey"))
1✔
648
                    .add_col(TextSpan::from("Move cursor to last item"))
1✔
649
                    .add_row()
1✔
650
                    .add_col(TextSpan::from("KeyCode::Home"))
1✔
651
                    .add_col(TextSpan::from("OnKey"))
1✔
652
                    .add_col(TextSpan::from("Move cursor to first item"))
1✔
653
                    .add_row()
1✔
654
                    .add_col(TextSpan::from("KeyCode::Char(_)"))
1✔
655
                    .add_col(TextSpan::from("OnKey"))
1✔
656
                    .add_col(TextSpan::from("Return pressed key"))
1✔
657
                    .build(),
1✔
658
            )
659
            .scroll(true)
1✔
660
            .selected_line(2);
1✔
661
        assert_eq!(component.states.list_index, 2);
1✔
662
        // Index out of bounds
663
        component.attr(
1✔
664
            Attribute::Value,
1✔
665
            AttrValue::Payload(PropPayload::One(PropValue::Usize(50))),
1✔
666
        );
667
        assert_eq!(component.states.list_index, 6);
1✔
668
    }
1✔
669
}
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

© 2026 Coveralls, Inc