1use std::io;
2
3use {
4 crossterm::{
5 event::{self, Event, KeyCode, KeyEvent, KeyEventKind},
6 execute,
7 terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
8 },
9 matetui::widgets::gridselector::{GridItem, GridSelector, GridSelectorState},
10 ratatui::{
11 layout::{Alignment, Constraint, Direction, Layout, Rect},
12 prelude::CrosstermBackend,
13 style::{Color, Modifier, Style, Stylize},
14 widgets::Paragraph,
15 Frame, Terminal,
16 },
17};
18
19struct Item {
20 name: String,
21 emoji: String,
22}
23
24impl Item {
25 fn new(name: &str, emoji: &str) -> Self {
26 Item {
27 name: name.to_string(),
28 emoji: emoji.to_string(),
29 }
30 }
31}
32
33impl From<Item> for GridItem {
34 fn from(val: Item) -> Self {
35 GridItem::new(format!("{} {}", val.name, val.emoji))
36 }
37}
38
39fn main() -> io::Result<()> {
40 let stdout = io::stdout();
41 let mut stdout = stdout.lock();
42 enable_raw_mode()?;
43 execute!(stdout, EnterAlternateScreen)?;
44 let backend = CrosstermBackend::new(stdout);
45 let mut term = Terminal::new(backend)?;
46
47 run(&mut term)?;
49
50 disable_raw_mode()?;
51 execute!(term.backend_mut(), LeaveAlternateScreen)?;
52 term.show_cursor()?;
53 Ok(())
54}
55
56fn run<B>(term: &mut Terminal<B>) -> io::Result<()>
57where
58 B: ratatui::backend::Backend,
59{
60 let items = vec![
61 Item::new("feat", "โจ"),
62 Item::new("fix", "๐"),
63 Item::new("chore", "๐งน"),
64 Item::new("docs", "๐"),
65 Item::new("style", "๐
"),
66 Item::new("refactor", "๐จ"),
67 Item::new("perf", "๐"),
68 Item::new("test", "๐จ"),
69 Item::new("build", "๐ท"),
70 Item::new("ci", "๐ง"),
71 Item::new("revert", "โช"),
72 Item::new("release", "๐"),
73 Item::new("wip", "๐ง"),
74 ];
75
76 let mut grid_state = GridSelectorState::new(items).columns(5);
77
78 loop {
79 term.draw(|f| draw(f, f.area(), &mut grid_state))?;
80
81 match event::read()? {
82 Event::Key(key) => match key {
83 KeyEvent {
84 code: KeyCode::Esc, ..
85 } => break,
86 KeyEvent {
87 code: KeyCode::Right,
88 kind: KeyEventKind::Press,
89 ..
90 } => grid_state.move_right(),
91 KeyEvent {
92 code: KeyCode::Left,
93 kind: KeyEventKind::Press,
94 ..
95 } => grid_state.move_left(),
96 KeyEvent {
97 code: KeyCode::Up,
98 kind: KeyEventKind::Press,
99 ..
100 } => grid_state.move_up(),
101 KeyEvent {
102 code: KeyCode::Down,
103 kind: KeyEventKind::Press,
104 ..
105 } => grid_state.move_down(),
106 KeyEvent {
107 code: KeyCode::Home,
108 kind: KeyEventKind::Press,
109 ..
110 } => grid_state.move_to_row_start(),
111 KeyEvent {
112 code: KeyCode::End,
113 kind: KeyEventKind::Press,
114 ..
115 } => grid_state.move_to_row_end(),
116 KeyEvent {
118 code: KeyCode::Enter,
119 kind: KeyEventKind::Press,
120 ..
121 } => grid_state.select(),
122 _ => false,
123 },
124 _ => false,
125 };
126 }
127
128 Ok(())
129}
130
131fn draw(f: &mut Frame<'_>, area: Rect, state: &mut GridSelectorState) {
132 let layout = Layout::default()
134 .direction(Direction::Vertical)
135 .constraints([
136 Constraint::Min(0),
137 Constraint::Length(2),
138 Constraint::Length(1),
139 Constraint::Length(2),
140 ])
141 .split(area);
142
143 f.render_stateful_widget(
144 GridSelector::default().with_selected_color(Color::Magenta),
145 layout[0],
146 state,
147 );
148
149 let selected = state.selected().unwrap_or(GridItem::new("None"));
150 let hovered = state.hovered().unwrap_or(GridItem::new("None"));
151
152 let ps = Paragraph::new(selected)
153 .alignment(Alignment::Center)
154 .alignment(Alignment::Center)
155 .style(Style::default().fg(Color::Magenta));
156
157 let ph = Paragraph::new(hovered)
158 .alignment(Alignment::Center)
159 .style(Style::default().fg(Color::Blue));
160
161 f.render_widget(
162 Paragraph::new("Hovered/Selected")
163 .alignment(Alignment::Center)
164 .add_modifier(Modifier::BOLD),
165 layout[1],
166 );
167 f.render_widget(ph, layout[2]);
168 f.render_widget(ps, layout[3]);
169}