-
Notifications
You must be signed in to change notification settings - Fork 87
/
Copy pathui.rs
225 lines (198 loc) · 7.76 KB
/
ui.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// ANCHOR: all
use ratatui::{
layout::{Constraint, Direction, Layout, Rect},
style::{Color, Style},
text::{Line, Span, Text},
widgets::{Block, Borders, Clear, List, ListItem, Paragraph, Wrap},
Frame,
};
use crate::app::{App, CurrentScreen, CurrentlyEditing};
// ANCHOR: method_sig
pub fn ui(f: &mut Frame, app: &App) {
// ANCHOR_END: method_sig
// Create the layout sections.
// ANCHOR: ui_layout
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3),
Constraint::Min(1),
Constraint::Length(3),
])
.split(f.size());
// ANCHOR_END: ui_layout
// ANCHOR: title_paragraph
let title_block = Block::default()
.borders(Borders::ALL)
.style(Style::default());
let title = Paragraph::new(Text::styled(
"Create New Json",
Style::default().fg(Color::Green),
))
.block(title_block);
f.render_widget(title, chunks[0]);
// ANCHOR_END: title_paragraph
// ANCHOR: key_value_list
let mut list_items = Vec::<ListItem>::new();
for key in app.pairs.keys() {
list_items.push(ListItem::new(Line::from(Span::styled(
format!("{: <25} : {}", key, app.pairs.get(key).unwrap()),
Style::default().fg(Color::Yellow),
))));
}
let list = List::new(list_items);
f.render_widget(list, chunks[1]);
// ANCHOR_END: key_value_list
// ANCHOR: lower_navigation_current_screen
let current_navigation_text = vec![
// The first half of the text
match app.current_screen {
CurrentScreen::Main => {
Span::styled("Normal Mode", Style::default().fg(Color::Green))
}
CurrentScreen::Editing => {
Span::styled("Editing Mode", Style::default().fg(Color::Yellow))
}
CurrentScreen::Exiting => {
Span::styled("Exiting", Style::default().fg(Color::LightRed))
}
}
.to_owned(),
// A white divider bar to separate the two sections
Span::styled(" | ", Style::default().fg(Color::White)),
// The final section of the text, with hints on what the user is editing
{
if let Some(editing) = &app.currently_editing {
match editing {
CurrentlyEditing::Key => Span::styled(
"Editing Json Key",
Style::default().fg(Color::Green),
),
CurrentlyEditing::Value => Span::styled(
"Editing Json Value",
Style::default().fg(Color::LightGreen),
),
}
} else {
Span::styled(
"Not Editing Anything",
Style::default().fg(Color::DarkGray),
)
}
},
];
let mode_footer = Paragraph::new(Line::from(current_navigation_text))
.block(Block::default().borders(Borders::ALL));
// ANCHOR_END: lower_navigation_current_screen
// ANCHOR: lower_navigation_key_hint
let current_keys_hint = {
match app.current_screen {
CurrentScreen::Main => Span::styled(
"(q) to quit / (e) to make new pair",
Style::default().fg(Color::Red),
),
CurrentScreen::Editing => Span::styled(
"(ESC) to cancel/(Tab) to switch boxes/enter to complete",
Style::default().fg(Color::Red),
),
CurrentScreen::Exiting => Span::styled(
"(q) to quit / (e) to make new pair",
Style::default().fg(Color::Red),
),
}
};
let key_notes_footer = Paragraph::new(Line::from(current_keys_hint))
.block(Block::default().borders(Borders::ALL));
// ANCHOR_END: lower_navigation_key_hint
// ANCHOR: lower_navigation_layout
let footer_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(chunks[2]);
// ANCHOR_END: lower_navigation_layout
// ANCHOR: lower_navigation_rendering
f.render_widget(mode_footer, footer_chunks[0]);
f.render_widget(key_notes_footer, footer_chunks[1]);
// ANCHOR_END: lower_navigation_rendering
// ANCHOR: editing_popup
if let Some(editing) = &app.currently_editing {
let popup_block = Block::default()
.title("Enter a new key-value pair")
.borders(Borders::NONE)
.style(Style::default().bg(Color::DarkGray));
let area = centered_rect(60, 25, f.size());
f.render_widget(popup_block, area);
// ANCHOR_END: editing_popup
// ANCHOR: popup_layout
let popup_chunks = Layout::default()
.direction(Direction::Horizontal)
.margin(1)
.constraints([
Constraint::Percentage(50),
Constraint::Percentage(50),
])
.split(area);
// ANCHOR_END: popup_layout
// ANCHOR: key_value_blocks
let mut key_block = Block::default().title("Key").borders(Borders::ALL);
let mut value_block =
Block::default().title("Value").borders(Borders::ALL);
let active_style =
Style::default().bg(Color::LightYellow).fg(Color::Black);
match editing {
CurrentlyEditing::Key => key_block = key_block.style(active_style),
CurrentlyEditing::Value => {
value_block = value_block.style(active_style)
}
};
let key_text = Paragraph::new(app.key_input.clone()).block(key_block);
f.render_widget(key_text, popup_chunks[0]);
let value_text =
Paragraph::new(app.value_input.clone()).block(value_block);
f.render_widget(value_text, popup_chunks[1]);
}
// ANCHOR_END: key_value_blocks
// ANCHOR: exit_screen
if let CurrentScreen::Exiting = app.current_screen {
f.render_widget(Clear, f.size()); //this clears the entire screen and anything already drawn
let popup_block = Block::default()
.title("Y/N")
.borders(Borders::NONE)
.style(Style::default().bg(Color::DarkGray));
let exit_text = Text::styled(
"Would you like to output the buffer as json? (y/n)",
Style::default().fg(Color::Red),
);
// the `trim: false` will stop the text from being cut off when over the edge of the block
let exit_paragraph = Paragraph::new(exit_text)
.block(popup_block)
.wrap(Wrap { trim: false });
let area = centered_rect(60, 25, f.size());
f.render_widget(exit_paragraph, area);
}
// ANCHOR_END: exit_screen
}
// ANCHOR: centered_rect
/// helper function to create a centered rect using up certain percentage of the available rect `r`
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
// Cut the given rectangle into three vertical pieces
let popup_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
])
.split(r);
// Then cut the middle vertical piece into three width-wise pieces
Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
])
.split(popup_layout[1])[1] // Return the middle chunk
}
// ANCHOR_END: centered_rect
// ANCHOR_END: all