Skip to content

Commit

Permalink
implement side scrolling
Browse files Browse the repository at this point in the history
  • Loading branch information
cartercanedy committed Sep 21, 2024
1 parent 6726338 commit 2298001
Showing 1 changed file with 91 additions and 24 deletions.
115 changes: 91 additions & 24 deletions tui/src/floating_text.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(soft_unstable)]

use std::io::{Cursor, Read as _, Seek, SeekFrom, Write as _};
use std::{borrow::Cow, collections::VecDeque, io::{Cursor, Read as _, Seek, SeekFrom, Write as _}};

use crate::{
float::FloatContent,
Expand All @@ -12,10 +12,7 @@ use linutil_core::Command;
use crossterm::event::{KeyCode, KeyEvent};

use ratatui::{
layout::Rect,
style::{Style, Stylize},
widgets::{Block, Borders, Clear, List},
Frame,
layout::Rect, style::{Style, Stylize}, text::Line, widgets::{Block, Borders, Clear, List}, Frame
};

use ansi_to_tui::IntoText;
Expand All @@ -31,7 +28,9 @@ pub enum FloatingTextMode {

pub struct FloatingText {
pub src: Vec<String>,
scroll: usize,
max_line_width: usize,
v_scroll: usize,
h_scroll: usize,
mode: FloatingTextMode,
}

Expand Down Expand Up @@ -110,47 +109,83 @@ fn get_highlighted_string(s: &str) -> Option<String> {
Some(output)
}

macro_rules! max_width {
($($lines:tt)+) => {{
$($lines)+.iter().fold(0, |accum, val| accum.max(val.len()))
}}
}

fn get_lines(s: String) -> Vec<String> {
s.split("\n").map(|s| s.to_string()).collect::<Vec<_>>()
}

impl FloatingText {
pub fn new(text: String, mode: FloatingTextMode) -> Self {
let src = get_lines(text);

let max_line_width = max_width!(src);

Self {
src: text.split("\n").map(|s| s.to_string()).collect(),
scroll: 0,
src,
mode,
max_line_width,
v_scroll: 0,
h_scroll: 0,
}
}

pub fn from_command(command: &Command, mode: FloatingTextMode) -> Option<Self> {
let src = match command {
let (max_line_width, src) = match command {
Command::Raw(cmd) => {
// just apply highlights directly
get_highlighted_string(cmd)
(max_width!(get_lines(cmd.clone())), Some(cmd.clone()))
}

Command::LocalFile(file_path) => {
// have to read from tmp dir to get cmd src
let file_contents = std::fs::read_to_string(file_path)
let raw = std::fs::read_to_string(file_path)
.map_err(|_| format!("File not found: {:?}", file_path))
.unwrap();

get_highlighted_string(&file_contents)
(max_width!(get_lines(raw.clone())), Some(raw))
}

// If command is a folder, we don't display a preview
Command::None => None,
Command::None => (0usize, None)
};

Some(Self::new(src?, mode))
let src = get_lines(get_highlighted_string(&src?)?);

Some(Self {
src,
mode,
max_line_width,
h_scroll: 0,
v_scroll: 0
})
}

fn scroll_down(&mut self) {
if self.scroll + 1 < self.src.len() {
self.scroll += 1;
if self.v_scroll + 1 < self.src.len() {
self.v_scroll += 1;
}
}

fn scroll_up(&mut self) {
if self.scroll > 0 {
self.scroll -= 1;
if self.v_scroll > 0 {
self.v_scroll -= 1;
}
}

fn scroll_left(&mut self) {
if self.h_scroll > 0 {
self.h_scroll -= 1;
}
}

fn scroll_right(&mut self) {
if self.h_scroll + 1 < self.max_line_width {
self.h_scroll += 1;
}
}
}
Expand All @@ -175,12 +210,39 @@ impl FloatContent for FloatingText {

// Calculate the inner area to ensure text is not drawn over the border
let inner_area = block.inner(area);
let Rect { height, .. } = inner_area;
let lines = self
.src
.iter()
.skip(self.scroll)
.take(inner_area.height as usize)
.map(|l| l.into_text().unwrap())
.skip(self.v_scroll)
.take(height as usize)
.flat_map(|l| l.into_text().unwrap())
.map(|line| {
let mut skipped = 0;
let mut spans = line.into_iter()
.skip_while(|span| {
let skip = (skipped + span.content.len()) <= self.h_scroll;
if skip {
skipped += span.content.len();
true
} else {
false
}
})
.collect::<VecDeque<_>>();

if spans.is_empty() {
Line::raw(Cow::Owned(String::new()))
} else {
if skipped < self.h_scroll {
let to_split = spans.pop_front().unwrap();
let new_content = to_split.content.clone().into_owned()[self.h_scroll - skipped..].to_owned();
spans.push_front(to_split.content(Cow::Owned(new_content)));
}

Line::from(Vec::from(spans))
}
})
.collect::<Vec<_>>();

// Create list widget
Expand All @@ -196,9 +258,12 @@ impl FloatContent for FloatingText {
}

fn handle_key_event(&mut self, key: &KeyEvent) -> bool {
use KeyCode::*;
match key.code {
KeyCode::Down | KeyCode::Char('j') => self.scroll_down(),
KeyCode::Up | KeyCode::Char('k') => self.scroll_up(),
Down | Char('j') => self.scroll_down(),
Up | Char('k') => self.scroll_up(),
Left | Char('h') => self.scroll_left(),
Right | Char('l') => self.scroll_right(),
_ => {}
}
false
Expand All @@ -214,7 +279,9 @@ impl FloatContent for FloatingText {
hints: vec![
Shortcut::new(vec!["j", "Down"], "Scroll down"),
Shortcut::new(vec!["k", "Up"], "Scroll up"),
Shortcut::new(vec!["Enter", "q"], "Close window"),
Shortcut::new(vec!["h", "Left"], "Scroll left"),
Shortcut::new(vec!["l", "Right"], "Scroll right"),
Shortcut::new(vec!["Enter", "p", "d"], "Close window"),
],
}
}
Expand Down

0 comments on commit 2298001

Please sign in to comment.