mirror of
https://codeberg.org/unspeaker/perch.git
synced 2025-12-06 09:36:42 +01:00
add stylers; dim column separators; shrink columns; refactor handlers
This commit is contained in:
parent
38f97558a7
commit
004cf96c2e
5 changed files with 70 additions and 43 deletions
32
src/keys.rs
32
src/keys.rs
|
|
@ -39,18 +39,19 @@ impl Handle<TuiIn> for Taggart {
|
||||||
press!(F(1)) => { self.help_begin() },
|
press!(F(1)) => { self.help_begin() },
|
||||||
press!(Char('q')) => { self.quit_begin(&input) },
|
press!(Char('q')) => { self.quit_begin(&input) },
|
||||||
press!(Char('w')) => { self.save_begin() },
|
press!(Char('w')) => { self.save_begin() },
|
||||||
press!(Enter) => { self.edit_begin() },
|
press!(Up) => { self.cursor_select(-1); },
|
||||||
press!(Up) => { self.cursor = self.cursor.saturating_sub(1); },
|
press!(Down) => { self.cursor_select( 1); },
|
||||||
press!(Down) => { self.cursor = self.cursor + 1; },
|
press!(PageUp) => { self.cursor_select(-(PAGE_SIZE as isize)); },
|
||||||
press!(PageUp) => { self.cursor = self.cursor.saturating_sub(PAGE_SIZE); },
|
press!(PageDown) => { self.cursor_select( PAGE_SIZE as isize); },
|
||||||
press!(PageDown) => { self.cursor += PAGE_SIZE; },
|
press!(Left) => { self.column_select(-1); },
|
||||||
press!(Char(' ')) => { self.open_in_player()?; },
|
press!(Right) => { self.column_select( 1); },
|
||||||
press!(Left) => { self.column_prev(); },
|
press!(Char('[')) => { self.column_resize(-1); },
|
||||||
press!(Right) => { self.column_next(); },
|
press!(Char(']')) => { self.column_resize( 1); },
|
||||||
press!(Char('[')) => { self.column_resize(-1); },
|
|
||||||
press!(Char(']')) => { self.column_resize( 1); },
|
|
||||||
press!(Char('{')) => { self.column_collapse(true, 1); },
|
press!(Char('{')) => { self.column_collapse(true, 1); },
|
||||||
press!(Char('}')) => { self.column_collapse(false, -1); },
|
press!(Char('}')) => { self.column_collapse(false, -1); },
|
||||||
|
press!(Delete) => { self.clear() },
|
||||||
|
press!(Enter) => { self.edit_begin() },
|
||||||
|
press!(Char(' ')) => { self.open_in_player()?; },
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -60,15 +61,18 @@ impl Handle<TuiIn> for Taggart {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Taggart {
|
impl Taggart {
|
||||||
|
fn clear (&self) {
|
||||||
|
todo!("clear")
|
||||||
|
}
|
||||||
fn open_in_player (&self) -> Usually<()> {
|
fn open_in_player (&self) -> Usually<()> {
|
||||||
open(self.entries[self.cursor].path.as_ref())?;
|
open(self.entries[self.cursor].path.as_ref())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn column_prev (&mut self) {
|
fn cursor_select (&mut self, amount: isize) {
|
||||||
self.column = self.column.saturating_sub(1);
|
self.cursor = ((self.cursor as isize) + amount).max(0) as usize;
|
||||||
}
|
}
|
||||||
fn column_next (&mut self) {
|
fn column_select (&mut self, amount: isize) {
|
||||||
self.column = self.column + 1;
|
self.column = ((self.column as isize) + amount).max(0) as usize;
|
||||||
}
|
}
|
||||||
fn column_resize (&mut self, amount: isize) {
|
fn column_resize (&mut self, amount: isize) {
|
||||||
let column = &mut self.columns.0[self.column];
|
let column = &mut self.columns.0[self.column];
|
||||||
|
|
|
||||||
10
src/model.rs
10
src/model.rs
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::ratatui::style::Style;
|
||||||
|
|
||||||
mod column; pub use self::column::*;
|
mod column; pub use self::column::*;
|
||||||
mod entry; pub use self::entry::*;
|
mod entry; pub use self::entry::*;
|
||||||
|
|
@ -10,12 +11,17 @@ pub struct Taggart {
|
||||||
pub cursor: usize,
|
pub cursor: usize,
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
pub column: usize,
|
pub column: usize,
|
||||||
pub columns: Columns<fn(&Entry)->Option<Arc<str>>, fn(&mut Self, usize, &str)>,
|
|
||||||
pub display: Measure<TuiOut>,
|
pub display: Measure<TuiOut>,
|
||||||
/// State of modal dialog of editing field
|
/// State of modal dialog of editing field
|
||||||
pub mode: Option<Mode>,
|
pub mode: Option<Mode>,
|
||||||
/// Count of changes to items
|
/// Count of changes to items
|
||||||
pub changes: usize,
|
pub changes: usize,
|
||||||
|
/// Table columns to display
|
||||||
|
pub columns: Columns<
|
||||||
|
fn(&Entry)->Option<Arc<str>>,
|
||||||
|
fn(&mut Self, usize, &str),
|
||||||
|
fn(&Entry)->Option<Style>,
|
||||||
|
>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::ratatui::style::Style;
|
||||||
|
|
||||||
pub struct Column<G, S> {
|
pub struct Column<G, S, U> {
|
||||||
pub title: Arc<str>,
|
pub title: Arc<str>,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
pub collapsed: bool,
|
pub collapsed: bool,
|
||||||
pub getter: G,
|
pub getter: G,
|
||||||
pub setter: Option<S>,
|
pub setter: Option<S>,
|
||||||
//pub styler: Option<U>,
|
pub styler: Option<U>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Getter<T> = fn(&T)->Option<Arc<str>>;
|
type Getter<T> = fn(&T)->Option<Arc<str>>;
|
||||||
type Setter<T> = fn(&mut T, usize, &str);
|
type Setter<T> = fn(&mut T, usize, &str);
|
||||||
|
type Styler<T> = fn(&T)->Option<Style>;
|
||||||
|
|
||||||
impl<G, S> Column<Getter<G>, Setter<S>> {
|
impl<G, S, U> Column<Getter<G>, Setter<S>, Styler<U>> {
|
||||||
pub fn new (
|
pub fn new (
|
||||||
title: &impl AsRef<str>,
|
title: &impl AsRef<str>,
|
||||||
width: usize,
|
width: usize,
|
||||||
|
|
@ -23,6 +25,7 @@ impl<G, S> Column<Getter<G>, Setter<S>> {
|
||||||
title: title.as_ref().into(),
|
title: title.as_ref().into(),
|
||||||
getter,
|
getter,
|
||||||
setter: None,
|
setter: None,
|
||||||
|
styler: None,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,6 +33,10 @@ impl<G, S> Column<Getter<G>, Setter<S>> {
|
||||||
self.setter = Some(setter);
|
self.setter = Some(setter);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
fn styler (mut self, styler: Styler<U>) -> Self {
|
||||||
|
self.styler = Some(styler);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn entries_under (
|
pub(crate) fn entries_under (
|
||||||
|
|
@ -56,7 +63,7 @@ pub(crate) fn entries_under (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Columns<G, S>(pub Vec<Column<G, S>>);
|
pub struct Columns<G, S, U>(pub Vec<Column<G, S, U>>);
|
||||||
|
|
||||||
macro_rules! setter {
|
macro_rules! setter {
|
||||||
($set:ident) => {{
|
($set:ident) => {{
|
||||||
|
|
@ -82,7 +89,11 @@ macro_rules! setter {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Columns<fn(&Entry)->Option<Arc<str>>, fn(&mut Taggart, usize, &str)> {
|
impl Default for Columns<
|
||||||
|
fn(&Entry)->Option<Arc<str>>,
|
||||||
|
fn(&mut Taggart, usize, &str),
|
||||||
|
fn(&Entry)->Option<Style>,
|
||||||
|
> {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
Self(vec![
|
Self(vec![
|
||||||
// Computed file hash
|
// Computed file hash
|
||||||
|
|
@ -92,18 +103,18 @@ impl Default for Columns<fn(&Entry)->Option<Arc<str>>, fn(&mut Taggart, usize, &
|
||||||
// Selected?
|
// Selected?
|
||||||
Column::new(&"∗", 1, |entry: &Entry|Some(" ".into())),
|
Column::new(&"∗", 1, |entry: &Entry|Some(" ".into())),
|
||||||
// File name
|
// File name
|
||||||
Column::new(&"File", 80, |entry: &Entry|entry.name()),
|
Column::new(&"File", 40, |entry: &Entry|entry.name()),
|
||||||
// Modified tags?
|
// Modified tags?
|
||||||
Column::new(&"~", 1, |entry: &Entry|if entry.is_modified() {
|
Column::new(&"~", 1, |entry: &Entry|if entry.is_modified() {
|
||||||
Some("~".into())
|
Some("~".into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}),
|
}),
|
||||||
Column::new(&"Artist", 30, |entry: &Entry|entry.artist()).setter(setter!(set_artist)),
|
Column::new(&"Artist", 15, |entry: &Entry|entry.artist()).setter(setter!(set_artist)),
|
||||||
Column::new(&"Year", 5, |entry: &Entry|entry.year()).setter(setter!(set_year)),
|
Column::new(&"Year", 4, |entry: &Entry|entry.year()).setter(setter!(set_year)),
|
||||||
Column::new(&"Release", 30, |entry: &Entry|entry.album()).setter(setter!(set_album)),
|
Column::new(&"Release", 15, |entry: &Entry|entry.album()).setter(setter!(set_album)),
|
||||||
Column::new(&"Track", 5, |entry: &Entry|entry.track()).setter(setter!(set_track)),
|
Column::new(&"Track", 5, |entry: &Entry|entry.track()).setter(setter!(set_track)),
|
||||||
Column::new(&"Title", 80, |entry: &Entry|entry.title()).setter(setter!(set_title)),
|
Column::new(&"Title", 40, |entry: &Entry|entry.title()).setter(setter!(set_title)),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -224,9 +224,13 @@ macro_rules! music_tag_field {
|
||||||
}
|
}
|
||||||
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
||||||
let value = value.as_ref().trim();
|
let value = value.as_ref().trim();
|
||||||
if let &mut Metadata::Music { ref mut modified_tag, .. } = self {
|
if let &mut Metadata::Music {
|
||||||
match (value.len(), &modified_tag) {
|
ref original_tag,
|
||||||
(0, Some(new_tag)) => {
|
ref mut modified_tag,
|
||||||
|
..
|
||||||
|
} = self {
|
||||||
|
match (value.len(), &original_tag, &modified_tag) {
|
||||||
|
(0, _, Some(new_tag)) => {
|
||||||
if new_tag.read().unwrap().item_count() <= 1 {
|
if new_tag.read().unwrap().item_count() <= 1 {
|
||||||
// removing last entry removes modified_tag
|
// removing last entry removes modified_tag
|
||||||
*modified_tag = None;
|
*modified_tag = None;
|
||||||
|
|
@ -236,16 +240,16 @@ macro_rules! music_tag_field {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
(_, Some(new_tag)) => {
|
(_, _, Some(new_tag)) => {
|
||||||
// add entry to modified_tag
|
// add entry to modified_tag
|
||||||
new_tag.write().unwrap().$set(value.into());
|
new_tag.write().unwrap().$set(value.into());
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
(0, None) => {
|
(0, _, None) => {
|
||||||
// leave modified_tag empty
|
// leave modified_tag empty
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
(_, None) => {
|
(_, _, None) => {
|
||||||
// first entry creates modified_tag
|
// first entry creates modified_tag
|
||||||
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
|
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
|
||||||
new_tag.$set(value.into());
|
new_tag.$set(value.into());
|
||||||
|
|
@ -281,11 +285,13 @@ macro_rules! music_tag_field {
|
||||||
}
|
}
|
||||||
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
pub fn $set (&mut self, value: &impl AsRef<str>) -> bool {
|
||||||
let value = value.as_ref().trim();
|
let value = value.as_ref().trim();
|
||||||
if let &mut Metadata::Music { ref mut modified_tag, .. } = self
|
if let Ok(numeric_value) = value.parse::<u32>() && let &mut Metadata::Music {
|
||||||
&& let Ok(numeric_value) = value.parse::<u32>()
|
ref original_tag,
|
||||||
{
|
ref mut modified_tag,
|
||||||
match (value.len(), &modified_tag) {
|
..
|
||||||
(0, Some(new_tag)) => {
|
} = self {
|
||||||
|
match (value.len(), &original_tag, &modified_tag) {
|
||||||
|
(0, _, Some(new_tag)) => {
|
||||||
if new_tag.read().unwrap().item_count() <= 1 {
|
if new_tag.read().unwrap().item_count() <= 1 {
|
||||||
// removing last entry removes modified_tag
|
// removing last entry removes modified_tag
|
||||||
*modified_tag = None;
|
*modified_tag = None;
|
||||||
|
|
@ -295,16 +301,16 @@ macro_rules! music_tag_field {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
(_, Some(new_tag)) => {
|
(_, _, Some(new_tag)) => {
|
||||||
// add entry to modified_tag
|
// add entry to modified_tag
|
||||||
new_tag.write().unwrap().$set(numeric_value);
|
new_tag.write().unwrap().$set(numeric_value);
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
(0, None) => {
|
(0, _, None) => {
|
||||||
// leave modified_tag empty
|
// leave modified_tag empty
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
(_, None) => {
|
(_, _, None) => {
|
||||||
// first entry creates modified_tag
|
// first entry creates modified_tag
|
||||||
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
|
let mut new_tag = Tag::new(TagType::Id3v2); // FIXME other types
|
||||||
new_tag.$set(numeric_value);
|
new_tag.$set(numeric_value);
|
||||||
|
|
@ -325,7 +331,6 @@ generated_field!(hash = |self|match self {
|
||||||
Metadata::Unknown { hash, .. } => Some(hash.clone()),
|
Metadata::Unknown { hash, .. } => Some(hash.clone()),
|
||||||
_ => None
|
_ => None
|
||||||
});
|
});
|
||||||
|
|
||||||
generated_field!(size = |self|match self {
|
generated_field!(size = |self|match self {
|
||||||
Metadata::Image { size, .. } => Some(size.clone()),
|
Metadata::Image { size, .. } => Some(size.clone()),
|
||||||
Metadata::Music { size, .. } => Some(size.clone()),
|
Metadata::Music { size, .. } => Some(size.clone()),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::ratatui::style::{Style, Stylize};
|
||||||
|
|
||||||
pub struct TreeTable<'a>(pub(crate) &'a Taggart);
|
pub struct TreeTable<'a>(pub(crate) &'a Taggart);
|
||||||
|
|
||||||
|
|
@ -69,12 +70,12 @@ impl<'a> TreeTable<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*x += width as u16 + 1;
|
*x += width as u16 + 1;
|
||||||
to.blit(&"│", *x - 1, y, None);
|
to.blit(&"│", *x - 1, y, Some(Style::default().fg(Color::Rgb(64,64,64))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<G, S> Columns<G, S> {
|
impl<G, S, U> Columns<G, S, U> {
|
||||||
pub fn header (&self) -> Arc<str> {
|
pub fn header (&self) -> Arc<str> {
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
for Column { width, collapsed, title, .. } in self.0.iter() {
|
for Column { width, collapsed, title, .. } in self.0.iter() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue