diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 43426a8a..ebac8ea9 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: '' +labels: 'bug' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d..36014cde 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: '' -labels: '' +labels: 'enhancement' assignees: '' --- diff --git a/.github/workflows/pre-release.yaml b/.github/workflows/pre-release.yaml index 82257d98..5fdba7c7 100644 --- a/.github/workflows/pre-release.yaml +++ b/.github/workflows/pre-release.yaml @@ -27,7 +27,7 @@ jobs: - name: Generate Release Notes id: generate_notes - uses: release-drafter/release-drafter@v5 + uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/Cargo.lock b/Cargo.lock index baea2394..9b7e7efa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,7 +161,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -230,6 +230,22 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.6.0", + "crossterm_winapi", + "libc", + "mio 0.8.11", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + [[package]] name = "crossterm" version = "0.28.1" @@ -238,7 +254,7 @@ checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.6.0", "crossterm_winapi", - "mio", + "mio 1.0.2", "parking_lot", "rustix", "signal-hook", @@ -334,7 +350,7 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -506,7 +522,20 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "log", + "wasi", + "windows-sys 0.52.0", ] [[package]] @@ -564,7 +593,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -655,7 +684,7 @@ dependencies = [ "bitflags 2.6.0", "cassowary", "compact_str", - "crossterm", + "crossterm 0.28.1", "instability", "itertools", "lru", @@ -835,7 +864,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio", + "mio 0.8.11", + "mio 1.0.2", "signal-hook", ] @@ -978,7 +1008,7 @@ version = "0.1.0" dependencies = [ "chrono", "clap", - "crossterm", + "crossterm 0.27.0", "ego-tree", "include_dir", "oneshot", @@ -1176,6 +1206,15 @@ name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets", ] @@ -1186,7 +1225,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1195,28 +1234,61 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -1229,24 +1301,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index bce71991..1d10db9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,4 +34,4 @@ lto = true codegen-units = 1 panic = "abort" strip = true -incremental = false \ No newline at end of file +incremental = false diff --git a/build/linutil b/build/linutil index 394b056d..33af69c7 100755 Binary files a/build/linutil and b/build/linutil differ diff --git a/src/float.rs b/src/float.rs index 3b063cae..e60fe464 100644 --- a/src/float.rs +++ b/src/float.rs @@ -4,10 +4,13 @@ use ratatui::{ Frame, }; +use crate::hint::ShortcutList; + pub trait FloatContent { fn draw(&mut self, frame: &mut Frame, area: Rect); fn handle_key_event(&mut self, key: &KeyEvent) -> bool; fn is_finished(&self) -> bool; + fn get_shortcut_list(&self) -> ShortcutList; } pub struct Float { @@ -69,4 +72,8 @@ impl Float { _ => self.content.handle_key_event(key), } } + + pub fn get_shortcut_list(&self) -> ShortcutList { + self.content.get_shortcut_list() + } } diff --git a/src/floating_text.rs b/src/floating_text.rs index 482a8822..e1d8deb3 100644 --- a/src/floating_text.rs +++ b/src/floating_text.rs @@ -1,4 +1,8 @@ -use crate::{float::FloatContent, running_command::Command}; +use crate::{ + float::FloatContent, + hint::{Shortcut, ShortcutList}, + running_command::Command, +}; use crossterm::event::{KeyCode, KeyEvent}; use ratatui::{ layout::Rect, @@ -103,4 +107,15 @@ impl FloatContent for FloatingText { fn is_finished(&self) -> bool { true } + + fn get_shortcut_list(&self) -> ShortcutList { + ShortcutList { + scope_name: "Floating text", + hints: vec![ + Shortcut::new(vec!["j", "Down"], "Scroll down"), + Shortcut::new(vec!["k", "Up"], "Scroll up"), + Shortcut::new(vec!["Enter", "q"], "Close window"), + ], + } + } } diff --git a/src/hint.rs b/src/hint.rs new file mode 100644 index 00000000..27474ea9 --- /dev/null +++ b/src/hint.rs @@ -0,0 +1,161 @@ +use ratatui::{ + layout::{Margin, Rect}, + style::{Style, Stylize}, + text::{Line, Span}, + widgets::{Block, Borders, Paragraph}, + Frame, +}; + +use crate::state::{AppState, Focus}; + +pub const SHORTCUT_LINES: usize = 2; + +pub struct ShortcutList { + pub scope_name: &'static str, + pub hints: Vec, +} + +pub struct Shortcut { + pub key_sequenses: Vec>, + pub desc: &'static str, +} + +pub fn span_vec_len(span_vec: &[Span]) -> usize { + span_vec.iter().rfold(0, |init, s| init + s.width()) +} +impl ShortcutList { + pub fn draw(&self, frame: &mut Frame, area: Rect) { + let block = Block::default() + .title(self.scope_name) + .borders(Borders::all()); + let inner_area = area.inner(Margin::new(1, 1)); + let mut shortcut_list: Vec> = self.hints.iter().map(|h| h.to_spans()).collect(); + + let mut lines = vec![Line::default(); SHORTCUT_LINES]; + let mut idx = 0; + + while idx < SHORTCUT_LINES - 1 { + let split_idx = shortcut_list + .iter() + .scan(0usize, |total_len, s| { + *total_len += span_vec_len(s); + if *total_len > inner_area.width as usize { + None + } else { + *total_len += 4; + Some(1) + } + }) + .count(); + let new_shortcut_list = shortcut_list.split_off(split_idx); + let line: Vec<_> = shortcut_list + .into_iter() + .flat_map(|mut s| { + s.push(Span::default().content(" ")); + s + }) + .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); + frame.render_widget(p, area); + } +} + +impl Shortcut { + pub fn new(key_sequences: Vec<&'static str>, desc: &'static str) -> Self { + Self { + key_sequenses: key_sequences + .iter() + .map(|s| Span::styled(*s, Style::default().bold())) + .collect(), + desc, + } + } + + fn to_spans(&self) -> Vec { + let mut ret: Vec<_> = self + .key_sequenses + .iter() + .flat_map(|seq| { + [ + Span::default().content("["), + seq.clone(), + Span::default().content("] "), + ] + }) + .collect(); + ret.push(Span::styled(self.desc, Style::default().italic())); + ret + } +} + +fn get_list_item_shortcut(state: &AppState) -> Shortcut { + if state.selected_item_is_dir() { + Shortcut::new(vec!["l", "Right", "Enter"], "Go to selected dir") + } else { + Shortcut::new(vec!["l", "Right", "Enter"], "Run selected command") + } +} + +pub fn draw_shortcuts(state: &AppState, frame: &mut Frame, area: Rect) { + match state.focus { + Focus::Search => ShortcutList { + scope_name: "Search bar", + hints: vec![Shortcut::new(vec!["Enter"], "Finish search")], + }, + Focus::List => { + let mut hints = Vec::new(); + hints.push(Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil")); + if state.at_root() { + hints.push(Shortcut::new(vec!["h", "Left", "Tab"], "Focus tab list")); + hints.push(get_list_item_shortcut(state)); + } else { + if state.selected_item_is_up_dir() { + hints.push(Shortcut::new( + vec!["l", "Right", "Enter", "h", "Left"], + "Go to parrent directory", + )); + } else { + hints.push(Shortcut::new(vec!["h", "Left"], "Go to parrent directory")); + hints.push(get_list_item_shortcut(state)); + if state.selected_item_is_cmd() { + hints.push(Shortcut::new(vec!["p"], "Enable preview")); + } + } + hints.push(Shortcut::new(vec!["Tab"], "Focus tab list")); + }; + hints.push(Shortcut::new(vec!["k", "Up"], "Select item above")); + hints.push(Shortcut::new(vec!["j", "Down"], "Select item below")); + hints.push(Shortcut::new(vec!["t"], "Next theme")); + hints.push(Shortcut::new(vec!["T"], "Previous theme")); + ShortcutList { + scope_name: "Item list", + hints, + } + } + Focus::TabList => ShortcutList { + scope_name: "Tab list", + hints: vec![ + Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil"), + Shortcut::new(vec!["l", "Right", "Tab", "Enter"], "Focus action list"), + Shortcut::new(vec!["k", "Up"], "Select item above"), + Shortcut::new(vec!["j", "Down"], "Select item below"), + Shortcut::new(vec!["t"], "Next theme"), + Shortcut::new(vec!["T"], "Previous theme"), + ], + }, + Focus::FloatingWindow(ref float) => float.get_shortcut_list(), + } + .draw(frame, area); +} diff --git a/src/main.rs b/src/main.rs index 85c0278f..1759435c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod filter; mod float; mod floating_text; +mod hint; mod running_command; pub mod state; mod tabs; @@ -71,7 +72,6 @@ fn main() -> std::io::Result<()> { fn run(terminal: &mut Terminal, state: &mut AppState) -> io::Result<()> { loop { terminal.draw(|frame| state.draw(frame)).unwrap(); - // Wait for an event if !event::poll(Duration::from_millis(10))? { continue; diff --git a/src/running_command.rs b/src/running_command.rs index f5041ee4..0840f189 100644 --- a/src/running_command.rs +++ b/src/running_command.rs @@ -1,4 +1,7 @@ -use crate::float::FloatContent; +use crate::{ + float::FloatContent, + hint::{Shortcut, ShortcutList}, +}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use oneshot::{channel, Receiver}; use portable_pty::{ @@ -123,6 +126,20 @@ impl FloatContent for RunningCommand { true } } + + fn get_shortcut_list(&self) -> ShortcutList { + if self.is_finished() { + ShortcutList { + scope_name: "Finished command", + hints: vec![Shortcut::new(vec!["Enter", "q"], "Close window")], + } + } else { + ShortcutList { + scope_name: "Running command", + hints: vec![Shortcut::new(vec!["CTRL-c"], "Kill the command")], + } + } + } } impl RunningCommand { diff --git a/src/state.rs b/src/state.rs index 93dd6a4c..36b5ce34 100644 --- a/src/state.rs +++ b/src/state.rs @@ -2,6 +2,7 @@ use crate::{ filter::{Filter, SearchAction}, float::{Float, FloatContent}, floating_text::FloatingText, + hint::{draw_shortcuts, SHORTCUT_LINES}, running_command::{Command, RunningCommand}, tabs::{ListNode, Tab}, theme::Theme, @@ -9,10 +10,10 @@ use crate::{ use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use ego_tree::NodeId; use ratatui::{ - layout::{Constraint, Direction, Layout}, + layout::{Alignment, Constraint, Direction, Layout}, style::{Style, Stylize}, - text::Line, - widgets::{Block, Borders, List, ListState}, + text::{Line, Span}, + widgets::{Block, Borders, List, ListState, Paragraph}, Frame, }; use std::path::Path; @@ -21,7 +22,7 @@ pub struct AppState { /// Selected theme theme: Theme, /// Currently focused area - focus: Focus, + pub focus: Focus, /// List of tabs tabs: Vec, /// Current tab @@ -65,12 +66,44 @@ impl AppState { state } pub fn draw(&mut self, frame: &mut Frame) { + let label_block = + Block::default() + .borders(Borders::all()) + .border_set(ratatui::symbols::border::Set { + top_left: " ", + top_right: " ", + bottom_left: " ", + bottom_right: " ", + vertical_left: " ", + vertical_right: " ", + horizontal_top: "*", + horizontal_bottom: "*", + }); + let str1 = "Linutil "; + let str2 = "by Chris Titus"; + let label = Paragraph::new(Line::from(vec![ + Span::styled(str1, Style::default().bold()), + Span::styled(str2, Style::default().italic()), + ])) + .block(label_block) + .alignment(Alignment::Center); + let longest_tab_display_len = self .tabs .iter() .map(|tab| tab.name.len() + self.theme.tab_icon().len()) .max() - .unwrap_or(0); + .unwrap_or(0) + .max(str1.len() + str2.len()); + + let vertical = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage(100), + Constraint::Min(2 + SHORTCUT_LINES as u16), + ]) + .margin(0) + .split(frame.size()); let horizontal = Layout::default() .direction(Direction::Horizontal) @@ -78,11 +111,13 @@ impl AppState { Constraint::Min(longest_tab_display_len as u16 + 5), Constraint::Percentage(100), ]) - .split(frame.area()); + .split(vertical[0]); + let left_chunks = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Length(3), Constraint::Min(1)]) .split(horizontal[0]); + frame.render_widget(label, left_chunks[0]); let tabs = self .tabs @@ -148,6 +183,8 @@ impl AppState { if let Focus::FloatingWindow(float) = &mut self.focus { float.draw(frame, chunks[1]); } + + draw_shortcuts(self, frame, vertical[1]); } pub fn handle_key(&mut self, key: &KeyEvent) -> bool { match &mut self.focus { @@ -213,7 +250,7 @@ impl AppState { /// Checks ehther the current tree node is the root node (can we go up the tree or no) /// Returns `true` if we can't go up the tree (we are at the tree root) /// else returns `false` - fn at_root(&self) -> bool { + pub fn at_root(&self) -> bool { self.visit_stack.len() == 1 } fn enter_parent_directory(&mut self) { @@ -221,13 +258,10 @@ impl AppState { self.selection.select(Some(0)); self.update_items(); } - fn get_selected_command(&mut self, change_directory: bool) -> Option { + pub fn get_selected_command(&self) -> Option { let mut selected_index = self.selection.selected().unwrap_or(0); if !self.at_root() && selected_index == 0 { - if change_directory { - self.enter_parent_directory(); - } return None; } if !self.at_root() { @@ -237,25 +271,83 @@ impl AppState { if let Some(item) = self.filter.item_list().get(selected_index) { if !item.has_children { return Some(item.node.command.clone()); - } else if change_directory { + } + } + None + } + pub fn go_to_selected_dir(&mut self) { + let mut selected_index = self.selection.selected().unwrap_or(0); + + if !self.at_root() && selected_index == 0 { + self.enter_parent_directory(); + return; + } + + 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 { self.visit_stack.push(item.id); self.selection.select(Some(0)); self.update_items(); } } - None + } + pub fn selected_item_is_dir(&self) -> bool { + let mut selected_index = self.selection.selected().unwrap_or(0); + + 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_cmd(&self) -> bool { + let mut selected_index = self.selection.selected().unwrap_or(0); + + 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 { + let selected_index = self.selection.selected().unwrap_or(0); + + !self.at_root() && selected_index == 0 } fn enable_preview(&mut self) { - if let Some(command) = self.get_selected_command(false) { + if let Some(command) = self.get_selected_command() { if let Some(preview) = FloatingText::from_command(&command) { self.spawn_float(preview, 80, 80); } } } fn handle_enter(&mut self) { - if let Some(cmd) = self.get_selected_command(true) { + if let Some(cmd) = self.get_selected_command() { let command = RunningCommand::new(cmd); self.spawn_float(command, 80, 80); + } else { + self.go_to_selected_dir(); } } fn spawn_float(&mut self, float: T, width: u16, height: u16) {