mirror of
https://codeberg.org/unspeaker/perch.git
synced 2025-12-06 09:36:42 +01:00
move trimmed strings to tengri
This commit is contained in:
parent
c67d3c8803
commit
6e0bf1e4b6
7 changed files with 135 additions and 200 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
|
@ -1178,9 +1178,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust_decimal"
|
name = "rust_decimal"
|
||||||
version = "1.36.0"
|
version = "1.37.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555"
|
checksum = "5c24af6e7ac43c88a8a458d1139d0246fdce2f6cd2f1ac6cb51eb88b29c978af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"borsh",
|
"borsh",
|
||||||
|
|
@ -1433,7 +1433,6 @@ dependencies = [
|
||||||
"pad",
|
"pad",
|
||||||
"rusty-chromaprint",
|
"rusty-chromaprint",
|
||||||
"tengri",
|
"tengri",
|
||||||
"unicode-width 0.2.0",
|
|
||||||
"walkdir",
|
"walkdir",
|
||||||
"xxhash-rust",
|
"xxhash-rust",
|
||||||
]
|
]
|
||||||
|
|
@ -1446,8 +1445,8 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tengri"
|
name = "tengri"
|
||||||
version = "0.2.0"
|
version = "0.3.2"
|
||||||
source = "git+https://codeberg.org/unspeaker/tengri?rev=10a2d17#10a2d17b487fb6a8690d807d7698f04da4267129"
|
source = "git+https://codeberg.org/unspeaker/tengri?rev=96ff10c#96ff10c4b0fda0d71308f488f5715c2f87f782d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tengri_input",
|
"tengri_input",
|
||||||
"tengri_output",
|
"tengri_output",
|
||||||
|
|
@ -1456,18 +1455,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tengri_input"
|
name = "tengri_input"
|
||||||
version = "0.2.0"
|
version = "0.3.2"
|
||||||
source = "git+https://codeberg.org/unspeaker/tengri?rev=10a2d17#10a2d17b487fb6a8690d807d7698f04da4267129"
|
source = "git+https://codeberg.org/unspeaker/tengri?rev=96ff10c#96ff10c4b0fda0d71308f488f5715c2f87f782d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tengri_output"
|
name = "tengri_output"
|
||||||
version = "0.2.0"
|
version = "0.3.2"
|
||||||
source = "git+https://codeberg.org/unspeaker/tengri?rev=10a2d17#10a2d17b487fb6a8690d807d7698f04da4267129"
|
source = "git+https://codeberg.org/unspeaker/tengri?rev=96ff10c#96ff10c4b0fda0d71308f488f5715c2f87f782d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tengri_tui"
|
name = "tengri_tui"
|
||||||
version = "0.2.0"
|
version = "0.3.2"
|
||||||
source = "git+https://codeberg.org/unspeaker/tengri?rev=10a2d17#10a2d17b487fb6a8690d807d7698f04da4267129"
|
source = "git+https://codeberg.org/unspeaker/tengri?rev=96ff10c#96ff10c4b0fda0d71308f488f5715c2f87f782d0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic_float",
|
"atomic_float",
|
||||||
"better-panic",
|
"better-panic",
|
||||||
|
|
@ -1479,6 +1478,7 @@ dependencies = [
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"tengri_input",
|
"tengri_input",
|
||||||
"tengri_output",
|
"tengri_output",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "taggart"
|
name = "taggart"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies.tengri]
|
[dependencies.tengri]
|
||||||
git = "https://codeberg.org/unspeaker/tengri"
|
git = "https://codeberg.org/unspeaker/tengri"
|
||||||
rev = "10a2d17"
|
rev = "96ff10c"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.4", features = [ "cargo" ] }
|
clap = { version = "4.5.4", features = [ "cargo" ] }
|
||||||
|
|
@ -19,7 +19,6 @@ opener = "0.7"
|
||||||
opus_headers = "0.1.2"
|
opus_headers = "0.1.2"
|
||||||
pad = "0.1"
|
pad = "0.1"
|
||||||
rusty-chromaprint = "0.3.0"
|
rusty-chromaprint = "0.3.0"
|
||||||
unicode-width = "0.2"
|
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
xxhash-rust = { version = "0.8.5", features = ["xxh3"] }
|
xxhash-rust = { version = "0.8.5", features = ["xxh3"] }
|
||||||
|
|
||||||
|
|
|
||||||
70
src/view.rs
70
src/view.rs
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use tengri::tui::ratatui::{style::{Color, Style}, prelude::Stylize};
|
pub(crate) use tengri::tui::ratatui::style::Color;//, Style}, prelude::Stylize};
|
||||||
use std::sync::atomic::{AtomicU16, Ordering::Relaxed};
|
|
||||||
pub(crate) use pad::PadStr;
|
pub(crate) use pad::PadStr;
|
||||||
|
|
||||||
mod column; pub use self::column::*;
|
mod status; pub use self::status::*;
|
||||||
mod string; pub use self::string::*;
|
mod table; pub use self::table::*;
|
||||||
|
|
||||||
impl Content<TuiOut> for Taggart {
|
impl Content<TuiOut> for Taggart {
|
||||||
fn content (&self) -> impl Render<TuiOut> {
|
fn content (&self) -> impl Render<TuiOut> {
|
||||||
|
|
@ -16,51 +15,6 @@ impl Content<TuiOut> for Taggart {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TreeTable<'a>(&'a Taggart);
|
|
||||||
|
|
||||||
impl<'a> Content<TuiOut> for TreeTable<'a> {
|
|
||||||
fn render (&self, to: &mut TuiOut) {
|
|
||||||
let area = to.area();
|
|
||||||
let Taggart { offset, paths, cursor, column, .. } = self.0;
|
|
||||||
let (x, w) = self.0.columns.xw(*column);
|
|
||||||
to.fill_bg([area.x() + x, area.y(), w, area.h()], Color::Rgb(0, 0, 0));
|
|
||||||
for (i, y) in area.iter_y().enumerate() {
|
|
||||||
let i_offset = i + offset;
|
|
||||||
let selected = *cursor == i_offset;
|
|
||||||
let mut x = area.x();
|
|
||||||
if let Some(entry) = paths.get(i_offset) {
|
|
||||||
for (index, _fragment) in entry.path.iter().enumerate() {
|
|
||||||
if index == entry.depth - 1 {
|
|
||||||
let _cursor = if selected { ">" } else { " " };
|
|
||||||
to.area[1] = y;
|
|
||||||
for Column { width, value, .. } in self.0.columns.0.iter() {
|
|
||||||
to.area[0] = x;
|
|
||||||
if let Some(value) = value(entry) {
|
|
||||||
Content::render(&TrimStringRef(*width as u16, &value), to);
|
|
||||||
}
|
|
||||||
x += *width as u16;
|
|
||||||
}
|
|
||||||
if selected {
|
|
||||||
let fill = [area.x(), y, area.w(), 1];
|
|
||||||
to.fill_fg(fill, Color::Rgb(0, 0, 0));
|
|
||||||
to.fill_bg(fill, Color::Rgb(192, 128, 0));
|
|
||||||
let fill = [area.x() + x as u16, y, w, 1];
|
|
||||||
to.fill_bg(fill, Color::Rgb(224, 192, 0));
|
|
||||||
if let Some((_index, value)) = &self.0.editing {
|
|
||||||
let x = area.x() + if x > 0 { x + 1 } else { x } as u16;
|
|
||||||
to.blit(&value, x, y, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
to.area = area;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entry {
|
impl Entry {
|
||||||
pub fn name (&self) -> Option<Arc<str>> {
|
pub fn name (&self) -> Option<Arc<str>> {
|
||||||
let indent = "".pad_to_width((self.depth - 1) * 2);
|
let indent = "".pad_to_width((self.depth - 1) * 2);
|
||||||
|
|
@ -79,15 +33,11 @@ impl Entry {
|
||||||
"⁇"
|
"⁇"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn style (&self) -> Option<Style> {
|
//fn style (&self) -> Option<Style> {
|
||||||
if self.is_dir() {
|
//if self.is_dir() {
|
||||||
None
|
//None
|
||||||
} else {
|
//} else {
|
||||||
Some(Style::default().bold())
|
//Some(Style::default().bold())
|
||||||
}
|
//}
|
||||||
}
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
fn status_bar (content: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
|
||||||
Fixed::y(1, Fill::x(Tui::bold(true, Tui::fg_bg(Color::Rgb(0,0,0), Color::Rgb(255,255,255), content))))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
|
|
||||||
pub struct Column<T> {
|
|
||||||
pub title: Arc<str>,
|
|
||||||
pub width: usize,
|
|
||||||
pub value: Box<dyn Fn(&T)->Option<Arc<str>> + Send + Sync>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Column<T> {
|
|
||||||
pub fn new (
|
|
||||||
title: &impl AsRef<str>,
|
|
||||||
width: usize,
|
|
||||||
value: impl Fn(&T)->Option<Arc<str>> + Send + Sync + 'static
|
|
||||||
) -> Self {
|
|
||||||
Self { width, value: Box::new(value), title: title.as_ref().into() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Columns<T>(pub Vec<Column<T>>);
|
|
||||||
|
|
||||||
impl Default for Columns<Entry> {
|
|
||||||
fn default () -> Self {
|
|
||||||
Self(vec![
|
|
||||||
Column::new(&"HASH", 16, |entry: &Entry|entry.hash()),
|
|
||||||
Column::new(&"FILE", 80, |entry: &Entry|entry.name()),
|
|
||||||
Column::new(&"ARTIST", 30, |entry: &Entry|entry.artist()),
|
|
||||||
Column::new(&"RELEASE", 30, |entry: &Entry|entry.album()),
|
|
||||||
Column::new(&"TRACK", 5, |entry: &Entry|entry.track()),
|
|
||||||
Column::new(&"TITLE", 80, |entry: &Entry|entry.title()),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Columns<T> {
|
|
||||||
pub fn header (&self) -> Arc<str> {
|
|
||||||
let mut output = String::new();
|
|
||||||
for Column { width, title, .. } in self.0.iter() {
|
|
||||||
let cell = title.pad_to_width(*width);
|
|
||||||
output = format!("{output}{cell}│");
|
|
||||||
}
|
|
||||||
output.into()
|
|
||||||
}
|
|
||||||
pub fn row (&self, entry: &T) -> Arc<str> {
|
|
||||||
let mut output = String::new();
|
|
||||||
for Column { width, value, .. } in self.0.iter() {
|
|
||||||
let cell = value(entry).unwrap_or_default().pad_to_width(*width);
|
|
||||||
output = format!("{output}{cell}│");
|
|
||||||
}
|
|
||||||
output.into()
|
|
||||||
}
|
|
||||||
pub fn row_content (&self, entry: &T) -> impl Content<TuiOut> where T: Send + Sync {
|
|
||||||
Map::new(
|
|
||||||
||self.0.iter(),
|
|
||||||
|&Column { width, ref value, .. }, index|map_east(
|
|
||||||
0u16,
|
|
||||||
width as u16,
|
|
||||||
value(entry).map(|x|TrimString(width as u16, x))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
pub fn xw (&self, column: usize) -> (u16, u16) {
|
|
||||||
let mut x: u16 = 0;
|
|
||||||
for (index, Column { width, .. }) in self.0.iter().enumerate() {
|
|
||||||
let w = *width as u16 + 1;
|
|
||||||
if index == column {
|
|
||||||
if x > 0 {
|
|
||||||
return (x - 1, w + 1)
|
|
||||||
} else {
|
|
||||||
return (x, w)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
x += w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(0, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
src/view/status.rs
Normal file
8
src/view/status.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub fn status_bar (content: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
||||||
|
Fixed::y(
|
||||||
|
1,
|
||||||
|
Fill::x(Tui::bold(true, Tui::fg_bg(Color::Rgb(0,0,0), Color::Rgb(255,255,255), content)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use crate::ratatui::prelude::Position;
|
|
||||||
use unicode_width::{UnicodeWidthStr, UnicodeWidthChar};
|
|
||||||
|
|
||||||
/// Displays an owned [str]-like with fixed maximum width.
|
|
||||||
///
|
|
||||||
/// Width is computed using [unicode_width].
|
|
||||||
pub struct TrimString<T: AsRef<str>>(pub u16, pub T);
|
|
||||||
|
|
||||||
impl<'a, T: AsRef<str>> TrimString<T> {
|
|
||||||
fn as_ref (&self) -> TrimStringRef<'_, T> {
|
|
||||||
TrimStringRef(self.0, &self.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a, T: AsRef<str>> Content<TuiOut> for TrimString<T> {
|
|
||||||
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
|
||||||
Content::layout(&self.as_ref(), to)
|
|
||||||
}
|
|
||||||
fn render (&self, to: &mut TuiOut) {
|
|
||||||
Content::render(&self.as_ref(), to)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Displays a borrowed [str]-like with fixed maximum width
|
|
||||||
///
|
|
||||||
/// Width is computed using [unicode_width].
|
|
||||||
pub struct TrimStringRef<'a, T: AsRef<str>>(pub u16, pub &'a T);
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> Content<TuiOut> for TrimStringRef<'_, T> {
|
|
||||||
fn layout (&self, to: [u16; 4]) -> [u16;4] {
|
|
||||||
[to.x(), to.y(), to.w().min(self.0).min(self.1.as_ref().width() as u16), to.h()]
|
|
||||||
}
|
|
||||||
fn render (&self, target: &mut TuiOut) {
|
|
||||||
let area = target.area();
|
|
||||||
let mut width: u16 = 0;
|
|
||||||
let mut chars = self.1.as_ref().chars();
|
|
||||||
while let Some(c) = chars.next() {
|
|
||||||
width += c.width().unwrap_or(0) as u16;
|
|
||||||
if width > self.0 || width > area.w() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if let Some(cell) = target.buffer.cell_mut(Position {
|
|
||||||
x: area.x() + width,
|
|
||||||
y: area.y()
|
|
||||||
}) {
|
|
||||||
cell.set_char(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
104
src/view/table.rs
Normal file
104
src/view/table.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
pub struct TreeTable<'a>(pub(crate) &'a Taggart);
|
||||||
|
|
||||||
|
impl<'a> Content<TuiOut> for TreeTable<'a> {
|
||||||
|
fn render (&self, to: &mut TuiOut) {
|
||||||
|
let area = to.area();
|
||||||
|
let Taggart { offset, paths, cursor, column, .. } = self.0;
|
||||||
|
let (x, w) = self.0.columns.xw(*column);
|
||||||
|
to.fill_bg([area.x() + x, area.y(), w, area.h()], Color::Rgb(0, 0, 0));
|
||||||
|
for (i, y) in area.iter_y().enumerate() {
|
||||||
|
let i_offset = i + offset;
|
||||||
|
let selected = *cursor == i_offset;
|
||||||
|
let mut x = area.x();
|
||||||
|
if let Some(entry) = paths.get(i_offset) {
|
||||||
|
for (index, _fragment) in entry.path.iter().enumerate() {
|
||||||
|
if index == entry.depth - 1 {
|
||||||
|
let _cursor = if selected { ">" } else { " " };
|
||||||
|
to.area[1] = y;
|
||||||
|
for Column { width, value, .. } in self.0.columns.0.iter() {
|
||||||
|
to.area[0] = x;
|
||||||
|
if let Some(value) = value(entry) {
|
||||||
|
Content::render(&TrimStringRef(*width as u16, &value), to);
|
||||||
|
}
|
||||||
|
x += *width as u16 + 1;
|
||||||
|
}
|
||||||
|
if selected {
|
||||||
|
let fill = [area.x(), y, area.w(), 1];
|
||||||
|
to.fill_fg(fill, Color::Rgb(0, 0, 0));
|
||||||
|
to.fill_bg(fill, Color::Rgb(192, 128, 0));
|
||||||
|
let fill = [area.x() + x as u16, y, w, 1];
|
||||||
|
to.fill_bg(fill, Color::Rgb(224, 192, 0));
|
||||||
|
if let Some((_index, value)) = &self.0.editing {
|
||||||
|
let x = area.x() + if x > 0 { x + 1 } else { x } as u16;
|
||||||
|
to.blit(&value, x, y, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to.area = area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Column<T> {
|
||||||
|
pub title: Arc<str>,
|
||||||
|
pub width: usize,
|
||||||
|
pub value: Box<dyn Fn(&T)->Option<Arc<str>> + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Column<T> {
|
||||||
|
pub fn new (
|
||||||
|
title: &impl AsRef<str>,
|
||||||
|
width: usize,
|
||||||
|
value: impl Fn(&T)->Option<Arc<str>> + Send + Sync + 'static
|
||||||
|
) -> Self {
|
||||||
|
Self { width, value: Box::new(value), title: title.as_ref().into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Columns<T>(pub Vec<Column<T>>);
|
||||||
|
|
||||||
|
impl Default for Columns<Entry> {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self(vec![
|
||||||
|
Column::new(&"HASH", 16, |entry: &Entry|entry.hash()),
|
||||||
|
Column::new(&"FILE", 80, |entry: &Entry|entry.name()),
|
||||||
|
Column::new(&"ARTIST", 30, |entry: &Entry|entry.artist()),
|
||||||
|
Column::new(&"RELEASE", 30, |entry: &Entry|entry.album()),
|
||||||
|
Column::new(&"TRACK", 5, |entry: &Entry|entry.track()),
|
||||||
|
Column::new(&"TITLE", 80, |entry: &Entry|entry.title()),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Columns<T> {
|
||||||
|
pub fn header (&self) -> Arc<str> {
|
||||||
|
let mut output = String::new();
|
||||||
|
for Column { width, title, .. } in self.0.iter() {
|
||||||
|
let cell = title.pad_to_width(*width);
|
||||||
|
output = format!("{output}{cell}│");
|
||||||
|
}
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
pub fn xw (&self, column: usize) -> (u16, u16) {
|
||||||
|
let mut x: u16 = 0;
|
||||||
|
for (index, Column { width, .. }) in self.0.iter().enumerate() {
|
||||||
|
let w = *width as u16 + 1;
|
||||||
|
if index == column {
|
||||||
|
if x > 0 {
|
||||||
|
return (x - 1, w + 1)
|
||||||
|
} else {
|
||||||
|
return (x, w)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x += w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue