mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-24 14:11:55 +00:00
Refactor various components of Rust code (#490)
* refactor: Simplify hint code, replacing while loop with fold iterator * refactor: Add spacing to lines in a separate function * refactor: Replace if let with map_or for better readability * refactor: Remove unnecessary duplicate body from is_cmd function * refactor: Create generic function to replace duplicate get_selected code * refactor: Remove duplicate code, remove unnecessary nesting * refactor: Move style into its own variable * refactor: Use constants for min width and height * refactor: Remove pointless duplicate variable --------- Co-authored-by: Chris Titus <contact@christitus.com>
This commit is contained in:
parent
a747f80c85
commit
a5b3df0776
|
@ -50,15 +50,7 @@ impl Float {
|
||||||
|
|
||||||
pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) {
|
pub fn draw(&mut self, frame: &mut Frame, parent_area: Rect) {
|
||||||
let popup_area = self.floating_window(parent_area);
|
let popup_area = self.floating_window(parent_area);
|
||||||
|
self.content.draw(frame, popup_area);
|
||||||
let content_area = Rect {
|
|
||||||
x: popup_area.x,
|
|
||||||
y: popup_area.y,
|
|
||||||
width: popup_area.width,
|
|
||||||
height: popup_area.height,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.content.draw(frame, content_area);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the floating window is finished.
|
// Returns true if the floating window is finished.
|
||||||
|
|
|
@ -20,6 +20,15 @@ pub struct Shortcut {
|
||||||
pub desc: &'static str,
|
pub desc: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_spacing(list: Vec<Vec<Span>>) -> Line {
|
||||||
|
list.into_iter()
|
||||||
|
.flat_map(|mut s| {
|
||||||
|
s.push(Span::default().content(" "));
|
||||||
|
s
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn span_vec_len(span_vec: &[Span]) -> usize {
|
pub fn span_vec_len(span_vec: &[Span]) -> usize {
|
||||||
span_vec.iter().rfold(0, |init, s| init + s.width())
|
span_vec.iter().rfold(0, |init, s| init + s.width())
|
||||||
}
|
}
|
||||||
|
@ -29,15 +38,14 @@ impl ShortcutList {
|
||||||
.title(self.scope_name)
|
.title(self.scope_name)
|
||||||
.borders(Borders::all());
|
.borders(Borders::all());
|
||||||
let inner_area = area.inner(Margin::new(1, 1));
|
let inner_area = area.inner(Margin::new(1, 1));
|
||||||
let mut shortcut_list: Vec<Vec<Span>> = self.hints.iter().map(|h| h.to_spans()).collect();
|
let shortcut_spans: Vec<Vec<Span>> = self.hints.iter().map(|h| h.to_spans()).collect();
|
||||||
|
|
||||||
let mut lines = vec![Line::default(); SHORTCUT_LINES];
|
let mut lines: Vec<Line> = Vec::with_capacity(SHORTCUT_LINES);
|
||||||
let mut idx = 0;
|
|
||||||
|
|
||||||
while idx < SHORTCUT_LINES - 1 {
|
let shortcut_list = (0..SHORTCUT_LINES - 1).fold(shortcut_spans, |mut acc, _| {
|
||||||
let split_idx = shortcut_list
|
let split_idx = acc
|
||||||
.iter()
|
.iter()
|
||||||
.scan(0usize, |total_len, s| {
|
.scan(0_usize, |total_len, s| {
|
||||||
*total_len += span_vec_len(s);
|
*total_len += span_vec_len(s);
|
||||||
if *total_len > inner_area.width as usize {
|
if *total_len > inner_area.width as usize {
|
||||||
None
|
None
|
||||||
|
@ -47,25 +55,13 @@ impl ShortcutList {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.count();
|
.count();
|
||||||
let new_shortcut_list = shortcut_list.split_off(split_idx);
|
|
||||||
let line: Vec<_> = shortcut_list
|
let new_shortcut_list = acc.split_off(split_idx);
|
||||||
.into_iter()
|
lines.push(add_spacing(acc));
|
||||||
.flat_map(|mut s| {
|
|
||||||
s.push(Span::default().content(" "));
|
new_shortcut_list
|
||||||
s
|
});
|
||||||
})
|
lines.push(add_spacing(shortcut_list));
|
||||||
.collect();
|
|
||||||
shortcut_list = new_shortcut_list;
|
|
||||||
lines[idx] = line.into();
|
|
||||||
idx += 1;
|
|
||||||
}
|
|
||||||
lines[idx] = shortcut_list
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|mut s| {
|
|
||||||
s.push(Span::default().content(" "));
|
|
||||||
s
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let p = Paragraph::new(lines).block(block);
|
let p = Paragraph::new(lines).block(block);
|
||||||
frame.render_widget(p, area);
|
frame.render_widget(p, area);
|
||||||
|
|
113
tui/src/state.rs
113
tui/src/state.rs
|
@ -17,6 +17,9 @@ use ratatui::{
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const MIN_WIDTH: u16 = 77;
|
||||||
|
const MIN_HEIGHT: u16 = 19;
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
/// Selected theme
|
/// Selected theme
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
|
@ -72,16 +75,14 @@ impl AppState {
|
||||||
}
|
}
|
||||||
pub fn draw(&mut self, frame: &mut Frame) {
|
pub fn draw(&mut self, frame: &mut Frame) {
|
||||||
let terminal_size = frame.area();
|
let terminal_size = frame.area();
|
||||||
let min_width = 77; // Minimum width threshold
|
|
||||||
let min_height = 19; // Minimum height threshold
|
|
||||||
|
|
||||||
if terminal_size.width < min_width || terminal_size.height < min_height {
|
if terminal_size.width < MIN_WIDTH || terminal_size.height < MIN_HEIGHT {
|
||||||
let size_warning_message = format!(
|
let size_warning_message = format!(
|
||||||
"Terminal size too small:\nWidth = {} Height = {}\n\nMinimum size:\nWidth = {} Height = {}",
|
"Terminal size too small:\nWidth = {} Height = {}\n\nMinimum size:\nWidth = {} Height = {}",
|
||||||
terminal_size.width,
|
terminal_size.width,
|
||||||
terminal_size.height,
|
terminal_size.height,
|
||||||
min_width,
|
MIN_WIDTH,
|
||||||
min_height,
|
MIN_HEIGHT,
|
||||||
);
|
);
|
||||||
|
|
||||||
let warning_paragraph = Paragraph::new(size_warning_message.clone())
|
let warning_paragraph = Paragraph::new(size_warning_message.clone())
|
||||||
|
@ -230,6 +231,12 @@ impl AppState {
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let style = if let Focus::List = self.focus {
|
||||||
|
Style::default().reversed()
|
||||||
|
} else {
|
||||||
|
Style::new()
|
||||||
|
};
|
||||||
|
|
||||||
// Create the list widget with items
|
// Create the list widget with items
|
||||||
let list = List::new(items)
|
let list = List::new(items)
|
||||||
.highlight_style(if let Focus::List = self.focus {
|
.highlight_style(if let Focus::List = self.focus {
|
||||||
|
@ -259,13 +266,13 @@ impl AppState {
|
||||||
pub fn handle_key(&mut self, key: &KeyEvent) -> bool {
|
pub fn handle_key(&mut self, key: &KeyEvent) -> bool {
|
||||||
// This should be defined first to allow closing
|
// This should be defined first to allow closing
|
||||||
// the application even when not drawable ( If terminal is small )
|
// the application even when not drawable ( If terminal is small )
|
||||||
if matches!(self.focus, Focus::TabList | Focus::List) {
|
// Exit on 'q' or 'Ctrl-c' input
|
||||||
if key.code == KeyCode::Char('q')
|
if matches!(self.focus, Focus::TabList | Focus::List)
|
||||||
|| key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL)
|
&& (key.code == KeyCode::Char('q')
|
||||||
|
|| key.modifiers.contains(KeyModifiers::CONTROL) && key.code == KeyCode::Char('c'))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If UI is not drawable returning true will mark as the key handled
|
// If UI is not drawable returning true will mark as the key handled
|
||||||
if !self.drawable {
|
if !self.drawable {
|
||||||
|
@ -303,20 +310,11 @@ impl AppState {
|
||||||
self.focus = Focus::List;
|
self.focus = Focus::List;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Focus::Search => match self.filter.handle_key(key) {
|
Focus::Search => match self.filter.handle_key(key) {
|
||||||
SearchAction::Exit => self.exit_search(),
|
SearchAction::Exit => self.exit_search(),
|
||||||
SearchAction::Update => self.update_items(),
|
SearchAction::Update => self.update_items(),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ if key.code == KeyCode::Char('q')
|
|
||||||
|| key.code == KeyCode::Char('c')
|
|
||||||
&& key.modifiers.contains(KeyModifiers::CONTROL) =>
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Focus::TabList => match key.code {
|
Focus::TabList => match key.code {
|
||||||
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.focus = Focus::List,
|
KeyCode::Enter | KeyCode::Char('l') | KeyCode::Right => self.focus = Focus::List,
|
||||||
|
|
||||||
|
@ -337,7 +335,6 @@ impl AppState {
|
||||||
KeyCode::Char('T') => self.theme.prev(),
|
KeyCode::Char('T') => self.theme.prev(),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
||||||
Focus::List if key.kind != KeyEventKind::Release => match key.code {
|
Focus::List if key.kind != KeyEventKind::Release => match key.code {
|
||||||
KeyCode::Char('j') | KeyCode::Down => self.selection.select_next(),
|
KeyCode::Char('j') | KeyCode::Down => self.selection.select_next(),
|
||||||
KeyCode::Char('k') | KeyCode::Up => self.selection.select_previous(),
|
KeyCode::Char('k') | KeyCode::Up => self.selection.select_previous(),
|
||||||
|
@ -358,7 +355,6 @@ impl AppState {
|
||||||
KeyCode::Char(' ') if self.multi_select => self.toggle_selection(),
|
KeyCode::Char(' ') if self.multi_select => self.toggle_selection(),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
true
|
true
|
||||||
|
@ -410,39 +406,29 @@ impl AppState {
|
||||||
self.selection.select(Some(0));
|
self.selection.select(Some(0));
|
||||||
self.update_items();
|
self.update_items();
|
||||||
}
|
}
|
||||||
|
fn get_selected_node(&self) -> Option<&ListNode> {
|
||||||
|
let mut selected_index = self.selection.selected().unwrap_or(0);
|
||||||
|
|
||||||
|
if !self.at_root() && selected_index == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if !self.at_root() {
|
||||||
|
selected_index = selected_index.saturating_sub(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(item) = self.filter.item_list().get(selected_index) {
|
||||||
|
if !item.has_children {
|
||||||
|
return Some(&item.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
pub fn get_selected_command(&self) -> Option<Command> {
|
pub fn get_selected_command(&self) -> Option<Command> {
|
||||||
let mut selected_index = self.selection.selected().unwrap_or(0);
|
self.get_selected_node().map(|node| node.command.clone())
|
||||||
|
|
||||||
if !self.at_root() && selected_index == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
if !self.at_root() {
|
fn get_selected_description(&self) -> Option<String> {
|
||||||
selected_index = selected_index.saturating_sub(1);
|
self.get_selected_node()
|
||||||
}
|
.map(|node| node.description.clone())
|
||||||
|
|
||||||
if let Some(item) = self.filter.item_list().get(selected_index) {
|
|
||||||
if !item.has_children {
|
|
||||||
return Some(item.node.command.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn get_selected_description(&mut self) -> Option<String> {
|
|
||||||
let mut selected_index = self.selection.selected().unwrap_or(0);
|
|
||||||
|
|
||||||
if !self.at_root() && selected_index == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if !self.at_root() {
|
|
||||||
selected_index = selected_index.saturating_sub(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(item) = self.filter.item_list().get(selected_index) {
|
|
||||||
if !item.has_children {
|
|
||||||
return Some(item.node.description.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
pub fn go_to_selected_dir(&mut self) {
|
pub fn go_to_selected_dir(&mut self) {
|
||||||
let mut selected_index = self.selection.selected().unwrap_or(0);
|
let mut selected_index = self.selection.selected().unwrap_or(0);
|
||||||
|
@ -475,29 +461,14 @@ impl AppState {
|
||||||
selected_index = selected_index.saturating_sub(1);
|
selected_index = selected_index.saturating_sub(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(item) = self.filter.item_list().get(selected_index) {
|
self.filter
|
||||||
item.has_children
|
.item_list()
|
||||||
} else {
|
.get(selected_index)
|
||||||
false
|
.map_or(false, |item| item.has_children)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected_item_is_cmd(&self) -> bool {
|
pub fn selected_item_is_cmd(&self) -> bool {
|
||||||
let mut selected_index = self.selection.selected().unwrap_or(0);
|
!self.selected_item_is_dir()
|
||||||
|
|
||||||
if !self.at_root() && selected_index == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.at_root() {
|
|
||||||
selected_index = selected_index.saturating_sub(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(item) = self.filter.item_list().get(selected_index) {
|
|
||||||
!item.has_children
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn selected_item_is_up_dir(&self) -> bool {
|
pub fn selected_item_is_up_dir(&self) -> bool {
|
||||||
let selected_index = self.selection.selected().unwrap_or(0);
|
let selected_index = self.selection.selected().unwrap_or(0);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user