mirror of
https://github.com/ChrisTitusTech/linutil.git
synced 2024-11-05 13:15:21 +00:00
Merge branch 'main' into pr/adamperkowski/271
This commit is contained in:
commit
e5f324caa7
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -2,7 +2,7 @@
|
|||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
|
|
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -2,7 +2,7 @@
|
|||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
|
2
.github/workflows/pre-release.yaml
vendored
2
.github/workflows/pre-release.yaml
vendored
|
@ -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:
|
||||
|
|
130
Cargo.lock
generated
130
Cargo.lock
generated
|
@ -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"
|
||||
|
|
BIN
build/linutil
BIN
build/linutil
Binary file not shown.
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
161
src/hint.rs
Normal file
161
src/hint.rs
Normal file
|
@ -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<Shortcut>,
|
||||
}
|
||||
|
||||
pub struct Shortcut {
|
||||
pub key_sequenses: Vec<Span<'static>>,
|
||||
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<Vec<Span>> = 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<Span> {
|
||||
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);
|
||||
}
|
|
@ -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<B: Backend>(terminal: &mut Terminal<B>, 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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
122
src/state.rs
122
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<Tab>,
|
||||
/// 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<Command> {
|
||||
pub fn get_selected_command(&self) -> Option<Command> {
|
||||
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<T: FloatContent + 'static>(&mut self, float: T, width: u16, height: u16) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user