2024-09-06 22:36:12 +01:00
|
|
|
use crate::{
|
|
|
|
float::FloatContent,
|
|
|
|
hint::{Shortcut, ShortcutList},
|
|
|
|
};
|
2024-08-08 22:12:34 +01:00
|
|
|
use crossterm::event::{KeyCode, KeyEvent};
|
2024-09-06 00:39:10 +01:00
|
|
|
use linutil_core::Command;
|
2024-08-08 22:12:34 +01:00
|
|
|
use ratatui::{
|
|
|
|
layout::Rect,
|
|
|
|
style::{Style, Stylize},
|
|
|
|
text::Line,
|
|
|
|
widgets::{Block, Borders, List},
|
|
|
|
Frame,
|
|
|
|
};
|
2024-09-19 01:17:08 +01:00
|
|
|
pub enum FloatingTextMode {
|
|
|
|
Preview,
|
|
|
|
Description,
|
|
|
|
}
|
2024-08-08 22:12:34 +01:00
|
|
|
pub struct FloatingText {
|
|
|
|
text: Vec<String>,
|
2024-09-19 01:17:08 +01:00
|
|
|
mode: FloatingTextMode,
|
2024-08-08 22:12:34 +01:00
|
|
|
scroll: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FloatingText {
|
2024-09-19 01:17:08 +01:00
|
|
|
pub fn new(text: Vec<String>, mode: FloatingTextMode) -> Self {
|
|
|
|
Self {
|
|
|
|
text,
|
|
|
|
scroll: 0,
|
|
|
|
mode,
|
|
|
|
}
|
2024-08-08 22:12:34 +01:00
|
|
|
}
|
|
|
|
|
2024-09-19 01:17:08 +01:00
|
|
|
pub fn from_command(command: &Command, mode: FloatingTextMode) -> Option<Self> {
|
2024-08-09 10:22:19 +01:00
|
|
|
let lines = match command {
|
|
|
|
Command::Raw(cmd) => {
|
|
|
|
// Reconstruct the line breaks and file formatting after the
|
|
|
|
// 'include_str!()' call in the node
|
|
|
|
cmd.lines().map(|line| line.to_string()).collect()
|
|
|
|
}
|
|
|
|
Command::LocalFile(file_path) => {
|
2024-08-13 22:55:59 +01:00
|
|
|
let file_contents = std::fs::read_to_string(file_path)
|
|
|
|
.map_err(|_| format!("File not found: {:?}", file_path))
|
2024-08-09 10:22:19 +01:00
|
|
|
.unwrap();
|
|
|
|
file_contents.lines().map(|line| line.to_string()).collect()
|
|
|
|
}
|
|
|
|
// If command is a folder, we don't display a preview
|
|
|
|
Command::None => return None,
|
|
|
|
};
|
2024-09-19 01:17:08 +01:00
|
|
|
Some(Self::new(lines, mode))
|
2024-08-09 10:22:19 +01:00
|
|
|
}
|
|
|
|
|
2024-08-08 22:12:34 +01:00
|
|
|
fn scroll_down(&mut self) {
|
|
|
|
if self.scroll + 1 < self.text.len() {
|
|
|
|
self.scroll += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn scroll_up(&mut self) {
|
|
|
|
if self.scroll > 0 {
|
|
|
|
self.scroll -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FloatContent for FloatingText {
|
|
|
|
fn draw(&mut self, frame: &mut Frame, area: Rect) {
|
|
|
|
// Define the Block with a border and background color
|
2024-09-19 01:17:08 +01:00
|
|
|
let block_title = match self.mode {
|
|
|
|
FloatingTextMode::Preview => "Command Preview",
|
|
|
|
FloatingTextMode::Description => "Command Description",
|
|
|
|
};
|
|
|
|
|
2024-08-08 22:12:34 +01:00
|
|
|
let block = Block::default()
|
|
|
|
.borders(Borders::ALL)
|
2024-09-19 01:17:08 +01:00
|
|
|
.title(block_title)
|
|
|
|
.title_alignment(ratatui::layout::Alignment::Center)
|
|
|
|
.title_style(Style::default().reversed())
|
2024-08-08 22:12:34 +01:00
|
|
|
.style(Style::default());
|
|
|
|
|
|
|
|
// Draw the Block first
|
|
|
|
frame.render_widget(block.clone(), area);
|
|
|
|
|
|
|
|
// Calculate the inner area to ensure text is not drawn over the border
|
|
|
|
let inner_area = block.inner(area);
|
|
|
|
|
|
|
|
// Create the list of lines to be displayed
|
2024-09-19 01:17:08 +01:00
|
|
|
let mut lines: Vec<Line> = self
|
2024-08-08 22:12:34 +01:00
|
|
|
.text
|
|
|
|
.iter()
|
|
|
|
.skip(self.scroll)
|
2024-08-26 18:48:29 +01:00
|
|
|
.flat_map(|line| {
|
2024-08-26 23:13:48 +01:00
|
|
|
if line.is_empty() {
|
|
|
|
return vec![String::new()];
|
|
|
|
}
|
2024-08-26 18:48:29 +01:00
|
|
|
line.chars()
|
|
|
|
.collect::<Vec<char>>()
|
|
|
|
.chunks(inner_area.width as usize)
|
|
|
|
.map(|chunk| chunk.iter().collect())
|
|
|
|
.collect::<Vec<String>>()
|
|
|
|
})
|
2024-08-08 22:12:34 +01:00
|
|
|
.take(inner_area.height as usize)
|
2024-08-26 18:48:29 +01:00
|
|
|
.map(Line::from)
|
2024-08-08 22:12:34 +01:00
|
|
|
.collect();
|
|
|
|
|
2024-09-19 01:17:08 +01:00
|
|
|
// Prevents background text from appearing after the floating content
|
|
|
|
while lines.len() < inner_area.height as usize {
|
|
|
|
lines.push(Line::from(" ".repeat(inner_area.width as usize)));
|
|
|
|
}
|
2024-08-08 22:12:34 +01:00
|
|
|
// Create list widget
|
|
|
|
let list = List::new(lines)
|
|
|
|
.block(Block::default())
|
|
|
|
.highlight_style(Style::default().reversed());
|
|
|
|
|
|
|
|
// Render the list inside the bordered area
|
|
|
|
frame.render_widget(list, inner_area);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
|
|
|
|
match key.code {
|
2024-08-09 10:22:19 +01:00
|
|
|
KeyCode::Down | KeyCode::Char('j') => self.scroll_down(),
|
|
|
|
KeyCode::Up | KeyCode::Char('k') => self.scroll_up(),
|
|
|
|
_ => {}
|
2024-08-08 22:12:34 +01:00
|
|
|
}
|
2024-08-09 10:22:19 +01:00
|
|
|
false
|
2024-08-08 22:12:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn is_finished(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
2024-09-06 22:36:12 +01:00
|
|
|
|
|
|
|
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"),
|
|
|
|
],
|
|
|
|
}
|
|
|
|
}
|
2024-08-08 22:12:34 +01:00
|
|
|
}
|