mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
refactoring arranger into components
This commit is contained in:
parent
0bc43ed36f
commit
5670fc179b
4 changed files with 262 additions and 288 deletions
|
|
@ -316,6 +316,20 @@ impl<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//pub fn collect <'a, E: Engine, const N: usize> (
|
||||||
|
//items: &'a [&'a dyn Widget<Engine = E>;N]
|
||||||
|
//) -> impl Send + Sync + Fn(&'a mut dyn FnMut(&'a dyn Widget<Engine = E>)->Usually<()>)->Usually<()> + '_ {
|
||||||
|
//|add: &'a mut dyn FnMut(&'a dyn Widget<Engine = E>)->Usually<()>| {
|
||||||
|
//for item in items.iter() {
|
||||||
|
//add(item)?;
|
||||||
|
//}
|
||||||
|
//Ok(())
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//`Layers<_, impl (Fn(&mut dyn FnMut(&dyn Widget<Engine = _>) -> Result<(), Box<...>>) -> ... ) + Send + Sync>`
|
||||||
|
//`Layers<Tui, impl (Fn(&mut dyn FnMut(&dyn Widget<Engine = Tui>) -> Result<(), Box<(dyn std::error::Error + 'static)>>) -> Result<(), Box<(dyn std::error::Error + 'static)>>) + Send + Sync + '_>`
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Up,
|
Up,
|
||||||
|
|
|
||||||
|
|
@ -582,10 +582,11 @@ impl<T: Widget<Engine = Tui>> Widget for Align<T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||||
self.layout(to.area())?
|
if let Some(area) = self.layout(to.area())? {
|
||||||
.map(|area|to.render_in(area, self.inner()))
|
to.render_in(area, self.inner())
|
||||||
.transpose()
|
} else {
|
||||||
.map(|x|x.flatten())
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,62 +79,44 @@ impl<E: Engine> Arranger<E> {
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
pub fn track (&self) -> Option<&Sequencer<E>> {
|
||||||
/// Display mode of arranger
|
self.selected.track().map(|t|self.tracks.get(t)).flatten()
|
||||||
pub enum ArrangerViewMode {
|
}
|
||||||
VerticalExpanded,
|
pub fn track_mut (&mut self) -> Option<&mut Sequencer<E>> {
|
||||||
VerticalCompact1,
|
self.selected.track().map(|t|self.tracks.get_mut(t)).flatten()
|
||||||
VerticalCompact2,
|
}
|
||||||
Horizontal,
|
pub fn track_next (&mut self) {
|
||||||
}
|
self.selected.track_next(self.tracks.len() - 1)
|
||||||
/// Arranger display mode can be cycled
|
}
|
||||||
impl ArrangerViewMode {
|
pub fn track_prev (&mut self) {
|
||||||
/// Cycle arranger display mode
|
self.selected.track_prev()
|
||||||
pub fn to_next (&mut self) {
|
}
|
||||||
*self = match self {
|
pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut Sequencer<E>> {
|
||||||
Self::VerticalExpanded => Self::VerticalCompact1,
|
self.tracks.push(name.map_or_else(
|
||||||
Self::VerticalCompact1 => Self::VerticalCompact2,
|
|| Sequencer::new(&self.track_default_name()),
|
||||||
Self::VerticalCompact2 => Self::Horizontal,
|
|name| Sequencer::new(name),
|
||||||
Self::Horizontal => Self::VerticalExpanded,
|
));
|
||||||
}
|
let index = self.tracks.len() - 1;
|
||||||
|
Ok(&mut self.tracks[index])
|
||||||
|
}
|
||||||
|
pub fn track_del (&mut self) {
|
||||||
|
unimplemented!("Arranger::track_del");
|
||||||
|
}
|
||||||
|
pub fn track_default_name (&self) -> String {
|
||||||
|
format!("Track {}", self.tracks.len() + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Widget for Arranger<Tui> {
|
impl Arranger<Tui> {
|
||||||
type Engine = Tui;
|
pub fn rename_selected (&mut self) {
|
||||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
self.modal = Some(Box::new(ArrangerRenameModal::new(
|
||||||
todo!()
|
self.selected,
|
||||||
}
|
&match self.selected {
|
||||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
ArrangerFocus::Mix => self.name.clone(),
|
||||||
let area = (|to|match self.mode {
|
ArrangerFocus::Track(t) => self.tracks[t].name.clone(),
|
||||||
ArrangerViewMode::Horizontal => draw_h(self, to),
|
ArrangerFocus::Scene(s) => self.scenes[s].name.clone(),
|
||||||
ArrangerViewMode::VerticalCompact1 => {
|
ArrangerFocus::Clip(t, s) => self.tracks[t].phrases[s].read().unwrap().name.clone(),
|
||||||
let track_cols = track_clip_name_lengths(self.tracks.as_slice());
|
}
|
||||||
let scene_rows = (0..=self.scenes.len()).map(|i|(96, 96*i)).collect::<Vec<_>>();
|
)));
|
||||||
draw_v(self, to, track_cols.as_slice(), scene_rows.as_slice())
|
|
||||||
},
|
|
||||||
ArrangerViewMode::VerticalCompact2 => {
|
|
||||||
let track_cols = track_clip_name_lengths(self.tracks.as_slice());
|
|
||||||
let scene_rows = (0..=self.scenes.len()).map(|i|(192, 192*i)).collect::<Vec<_>>();
|
|
||||||
draw_v(self, to, track_cols.as_slice(), scene_rows.as_slice())
|
|
||||||
},
|
|
||||||
ArrangerViewMode::VerticalExpanded => {
|
|
||||||
let track_cols = track_clip_name_lengths(self.tracks.as_slice());
|
|
||||||
let scene_rows = scene_ppqs(self.tracks.as_slice(), self.scenes.as_slice());
|
|
||||||
draw_v(self, to, track_cols.as_slice(), scene_rows.as_slice())
|
|
||||||
},
|
|
||||||
})(&mut to.alter_area(|[x, y, w, h]|[
|
|
||||||
x + 1,
|
|
||||||
y + 1,
|
|
||||||
w.saturating_sub(2),
|
|
||||||
h.saturating_sub(2),
|
|
||||||
]))?.unwrap();
|
|
||||||
Lozenge(Style::default().fg(Nord::BG2))
|
|
||||||
.draw(&mut to.alter_area(|[x, y, w, h]|[
|
|
||||||
x.saturating_sub(1),
|
|
||||||
y.saturating_sub(1),
|
|
||||||
w + 2,
|
|
||||||
h + 2,
|
|
||||||
]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Focusable<Tui> for Arranger<Tui> {
|
impl Focusable<Tui> for Arranger<Tui> {
|
||||||
|
|
@ -173,10 +155,6 @@ impl ArrangerFocus {
|
||||||
pub fn is_clip (&self) -> bool {
|
pub fn is_clip (&self) -> bool {
|
||||||
match self { Self::Clip(_, _) => true, _ => false }
|
match self { Self::Clip(_, _) => true, _ => false }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Track focus methods
|
|
||||||
impl ArrangerFocus {
|
|
||||||
pub fn track (&self) -> Option<usize> {
|
pub fn track (&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
Self::Clip(t, _) => Some(*t),
|
Self::Clip(t, _) => Some(*t),
|
||||||
|
|
@ -208,10 +186,6 @@ impl ArrangerFocus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Scene focus methods
|
|
||||||
impl ArrangerFocus {
|
|
||||||
pub fn scene (&self) -> Option<usize> {
|
pub fn scene (&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
Self::Clip(_, s) => Some(*s),
|
Self::Clip(_, s) => Some(*s),
|
||||||
|
|
@ -244,8 +218,6 @@ impl ArrangerFocus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Handle<Tui> for Arranger<Tui> {
|
impl Handle<Tui> for Arranger<Tui> {
|
||||||
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
|
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
|
||||||
if let Some(modal) = self.modal.as_mut() {
|
if let Some(modal) = self.modal.as_mut() {
|
||||||
|
|
@ -341,212 +313,78 @@ impl Handle<Tui> for Arranger<Tui> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Arranger<Tui> {
|
/// Display mode of arranger
|
||||||
pub fn rename_selected (&mut self) {
|
#[derive(PartialEq)]
|
||||||
self.modal = Some(Box::new(ArrangerRenameModal::new(
|
pub enum ArrangerViewMode { VerticalExpanded, VerticalCompact1, VerticalCompact2, Horizontal }
|
||||||
self.selected,
|
/// Arranger display mode can be cycled
|
||||||
&match self.selected {
|
impl ArrangerViewMode {
|
||||||
ArrangerFocus::Mix => self.name.clone(),
|
/// Cycle arranger display mode
|
||||||
ArrangerFocus::Track(t) => self.tracks[t].name.clone(),
|
pub fn to_next (&mut self) {
|
||||||
ArrangerFocus::Scene(s) => self.scenes[s].name.clone(),
|
*self = match self {
|
||||||
ArrangerFocus::Clip(t, s) => self.tracks[t].phrases[s].read().unwrap().name.clone(),
|
Self::VerticalExpanded => Self::VerticalCompact1,
|
||||||
}
|
Self::VerticalCompact1 => Self::VerticalCompact2,
|
||||||
)));
|
Self::VerticalCompact2 => Self::Horizontal,
|
||||||
}
|
Self::Horizontal => Self::VerticalExpanded,
|
||||||
}
|
|
||||||
/// Appears on first run (i.e. if state dir is missing).
|
|
||||||
pub struct ArrangerRenameModal<E: Engine> {
|
|
||||||
_engine: std::marker::PhantomData<E>,
|
|
||||||
done: bool,
|
|
||||||
target: ArrangerFocus,
|
|
||||||
value: String,
|
|
||||||
result: Arc<RwLock<String>>,
|
|
||||||
cursor: usize
|
|
||||||
}
|
|
||||||
impl<E: Engine> ArrangerRenameModal<E> {
|
|
||||||
pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self {
|
|
||||||
Self {
|
|
||||||
_engine: Default::default(),
|
|
||||||
done: false,
|
|
||||||
target,
|
|
||||||
value: value.read().unwrap().clone(),
|
|
||||||
cursor: value.read().unwrap().len(),
|
|
||||||
result: value.clone(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Widget for ArrangerRenameModal<Tui> {
|
impl Content for Arranger<Tui> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
todo!()
|
Layers::new(move |add|{
|
||||||
}
|
//Lozenge(Style::default().fg(Nord::BG2))
|
||||||
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
//.draw(&mut to.alter_area(|[x, y, w, h]|[
|
||||||
let area = to.area();
|
//x.saturating_sub(1),
|
||||||
let y = area.y() + area.h() / 2;
|
//y.saturating_sub(1),
|
||||||
let bg_area = [1, y - 1, area.w() - 2, 3];
|
//w + 2,
|
||||||
to.fill_bg(bg_area, Nord::BG0);
|
//h + 2,
|
||||||
Lozenge(Style::default().bold().white().dim()).draw(to.with_rect(bg_area));
|
//]))
|
||||||
let label = match self.target {
|
match self.mode {
|
||||||
ArrangerFocus::Mix => "Rename project:",
|
ArrangerViewMode::Horizontal => add(&ArrangerViewHorizontal(&self)),
|
||||||
ArrangerFocus::Track(_) => "Rename track:",
|
ArrangerViewMode::VerticalCompact1 => add(&ArrangerViewVertical(
|
||||||
ArrangerFocus::Scene(_) => "Rename scene:",
|
&self,
|
||||||
ArrangerFocus::Clip(_, _) => "Rename clip:",
|
track_clip_name_lengths(self.tracks.as_slice()).as_slice(),
|
||||||
};
|
(0..=self.scenes.len()).map(|i|(96, 96*i)).collect::<Vec<_>>().as_slice(),
|
||||||
let style = Some(Style::default().not_bold().white().not_dim());
|
)),
|
||||||
to.blit(&label, area.x() + 3, y, style)?;
|
ArrangerViewMode::VerticalCompact2 => add(&ArrangerViewVertical(
|
||||||
let style = Some(Style::default().bold().white().not_dim());
|
&self,
|
||||||
to.blit(&self.value, area.x() + 3 + label.len() as u16 + 1, y, style)?;
|
track_clip_name_lengths(self.tracks.as_slice()).as_slice(),
|
||||||
let style = Some(Style::default().bold().white().not_dim().reversed());
|
(0..=self.scenes.len()).map(|i|(192, 192*i)).collect::<Vec<_>>().as_slice()
|
||||||
to.blit(&"▂", area.x() + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?;
|
)),
|
||||||
Ok(Some(area))
|
ArrangerViewMode::VerticalExpanded => add(&ArrangerViewVertical(
|
||||||
}
|
&self,
|
||||||
}
|
track_clip_name_lengths(self.tracks.as_slice()).as_slice(),
|
||||||
impl Handle<Tui> for ArrangerRenameModal<Tui> {
|
scene_ppqs(self.tracks.as_slice(), self.scenes.as_slice()).as_slice(),
|
||||||
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
|
)),
|
||||||
match from.event() {
|
}
|
||||||
TuiEvent::Input(Event::Key(k)) => {
|
})
|
||||||
match k.code {
|
|
||||||
KeyCode::Esc => {
|
|
||||||
self.exit();
|
|
||||||
},
|
|
||||||
KeyCode::Enter => {
|
|
||||||
*self.result.write().unwrap() = self.value.clone();
|
|
||||||
self.exit();
|
|
||||||
},
|
|
||||||
KeyCode::Left => {
|
|
||||||
self.cursor = self.cursor.saturating_sub(1);
|
|
||||||
},
|
|
||||||
KeyCode::Right => {
|
|
||||||
self.cursor = self.value.len().min(self.cursor + 1)
|
|
||||||
},
|
|
||||||
KeyCode::Backspace => {
|
|
||||||
let last = self.value.len().saturating_sub(1);
|
|
||||||
self.value = format!("{}{}",
|
|
||||||
&self.value[0..self.cursor.min(last)],
|
|
||||||
&self.value[self.cursor.min(last)..last]
|
|
||||||
);
|
|
||||||
self.cursor = self.cursor.saturating_sub(1)
|
|
||||||
}
|
|
||||||
KeyCode::Char(c) => {
|
|
||||||
self.value.insert(self.cursor, c);
|
|
||||||
self.cursor = self.value.len().min(self.cursor + 1)
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Ok(Some(true))
|
|
||||||
},
|
|
||||||
_ => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<E: Engine + Send> Exit for ArrangerRenameModal<E> {
|
|
||||||
fn exited (&self) -> bool {
|
|
||||||
self.done
|
|
||||||
}
|
|
||||||
fn exit (&mut self) {
|
|
||||||
self.done = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Track management methods
|
struct ArrangerViewVertical<'a, 'b, E: Engine>(
|
||||||
impl<E: Engine> Arranger<E> {
|
&'a Arranger<E>, &'b [(usize, usize)], &'b [(usize, usize)]
|
||||||
pub fn track (&self) -> Option<&Sequencer<E>> {
|
);
|
||||||
self.selected.track().map(|t|self.tracks.get(t)).flatten()
|
|
||||||
}
|
|
||||||
pub fn track_mut (&mut self) -> Option<&mut Sequencer<E>> {
|
|
||||||
self.selected.track().map(|t|self.tracks.get_mut(t)).flatten()
|
|
||||||
}
|
|
||||||
pub fn track_next (&mut self) {
|
|
||||||
self.selected.track_next(self.tracks.len() - 1)
|
|
||||||
}
|
|
||||||
pub fn track_prev (&mut self) {
|
|
||||||
self.selected.track_prev()
|
|
||||||
}
|
|
||||||
pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut Sequencer<E>> {
|
|
||||||
self.tracks.push(name.map_or_else(
|
|
||||||
|| Sequencer::new(&self.track_default_name()),
|
|
||||||
|name| Sequencer::new(name),
|
|
||||||
));
|
|
||||||
let index = self.tracks.len() - 1;
|
|
||||||
Ok(&mut self.tracks[index])
|
|
||||||
}
|
|
||||||
pub fn track_del (&mut self) {
|
|
||||||
unimplemented!("Arranger::track_del");
|
|
||||||
}
|
|
||||||
pub fn track_default_name (&self) -> String {
|
|
||||||
format!("Track {}", self.tracks.len() + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn track_name_max_len <E: Engine> (tracks: &[Sequencer<E>]) -> usize {
|
impl<'a, 'b> Content for ArrangerViewVertical<'a, 'b, Tui> {
|
||||||
tracks.iter()
|
type Engine = Tui;
|
||||||
.map(|s|s.name.read().unwrap().len())
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
.fold(0, usize::max)
|
let Self(state, cols, rows) = self;
|
||||||
}
|
//let area = to.area();
|
||||||
|
//let area = [area.x(), area.y(), area.w(), 2 + (rows[rows.len() - 1].1 / 96) as u16];
|
||||||
pub fn track_clip_name_lengths <E: Engine> (tracks: &[Sequencer<E>]) -> Vec<(usize, usize)> {
|
let tracks = state.tracks.as_ref();
|
||||||
let mut total = 0;
|
let scenes = state.scenes.as_ref();
|
||||||
let mut lengths: Vec<(usize, usize)> = tracks.iter().map(|track|{
|
let offset = 3 + scene_name_max_len(scenes) as u16;
|
||||||
let len = 4 + track.phrases
|
Layers::new(move |add|{
|
||||||
.iter()
|
//.add_ref(&FillBg(Color::Rgb(30, 33, 36)))//COLOR_BG1))//bg_lo(state.focused, state.entered)))
|
||||||
.fold(track.name.read().unwrap().len(), |len, phrase|{
|
add(&ColumnSeparators(offset, cols))?;
|
||||||
len.max(phrase.read().unwrap().name.read().unwrap().len())
|
add(&RowSeparators(rows))?;
|
||||||
});
|
add(&CursorFocus(state.selected, offset, cols, rows))?;
|
||||||
total = total + len;
|
add(&Split::right(|add|{
|
||||||
(len, total - len)
|
add(&TracksHeader(offset, cols, tracks))?;
|
||||||
}).collect();
|
add(&SceneRows(offset, cols, rows, tracks, scenes))
|
||||||
lengths.push((0, total));
|
}))
|
||||||
lengths
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw arranger with 1 row per scene.
|
|
||||||
pub fn draw_v_compact_1 <'a> (
|
|
||||||
state: &Arranger<Tui>, to: &mut Tui
|
|
||||||
) -> Perhaps<[u16;4]> {
|
|
||||||
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
|
||||||
let scene_rows = (0..=state.scenes.len()).map(|i|(96, 96*i)).collect::<Vec<_>>();
|
|
||||||
draw_v(state, to, track_cols.as_slice(), scene_rows.as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw arranger with 2 rows per scene.
|
|
||||||
pub fn draw_v_compact_2 <'a> (
|
|
||||||
state: &Arranger<Tui>, to: &mut Tui
|
|
||||||
) -> Perhaps<[u16;4]> {
|
|
||||||
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
|
||||||
let scene_rows = (0..=state.scenes.len()).map(|i|(192, 192*i)).collect::<Vec<_>>();
|
|
||||||
draw_v(state, to, track_cols.as_slice(), scene_rows.as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw arranger with number of rows per scene proportional to duration of scene.
|
|
||||||
pub fn draw_v_expanded <'a> (
|
|
||||||
state: &Arranger<Tui>, to: &mut Tui
|
|
||||||
) -> Perhaps<[u16;4]> {
|
|
||||||
let track_cols = track_clip_name_lengths(state.tracks.as_slice());
|
|
||||||
let scene_rows = scene_ppqs(state.tracks.as_slice(), state.scenes.as_slice());
|
|
||||||
draw_v(state, to, track_cols.as_slice(), scene_rows.as_slice())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_v <'a, 'b> (
|
|
||||||
state: &Arranger<Tui>,
|
|
||||||
to: &mut Tui,
|
|
||||||
cols: &'b [(usize, usize)],
|
|
||||||
rows: &'b [(usize, usize)],
|
|
||||||
) -> Perhaps<[u16;4]> {
|
|
||||||
let area = to.area();
|
|
||||||
let area = [area.x(), area.y(), area.w(), 2 + (rows[rows.len() - 1].1 / 96) as u16];
|
|
||||||
let offset = 3 + scene_name_max_len(state.scenes.as_ref()) as u16;
|
|
||||||
let tracks = state.tracks.as_ref();
|
|
||||||
let scenes = state.scenes.as_ref();
|
|
||||||
Layers::new(|add|{
|
|
||||||
//.add_ref(&FillBg(Color::Rgb(30, 33, 36)))//COLOR_BG1))//bg_lo(state.focused, state.entered)))
|
|
||||||
add(&ColumnSeparators(offset, cols))?;
|
|
||||||
add(&RowSeparators(rows))?;
|
|
||||||
add(&CursorFocus(state.selected, offset, cols, rows))?;
|
|
||||||
add(&Split::right(|add|{
|
|
||||||
add(&TracksHeader(offset, cols, tracks))?;
|
|
||||||
add(&SceneRows(offset, cols, rows, tracks, scenes))
|
|
||||||
}))
|
|
||||||
}).render(to.with_rect(area))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
|
struct ColumnSeparators<'a>(u16, &'a [(usize, usize)]);
|
||||||
|
|
@ -760,23 +598,31 @@ impl<'a> Widget for SceneClip<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_h (state: &Arranger<Tui>, to: &mut Tui) -> Perhaps<[u16;4]> {
|
struct ArrangerViewHorizontal<'a, E: Engine>(
|
||||||
let area = to.area();
|
&'a Arranger<E>
|
||||||
let area = [area.x(), area.y(), area.w(), area.h().min((2 + state.tracks.len() * 2) as u16)];
|
);
|
||||||
let tracks = state.tracks.as_slice();
|
|
||||||
Layers::new(|add|{
|
impl<'a> Content for ArrangerViewHorizontal<'a, Tui> {
|
||||||
add(&state.focused.then_some(FillBg(COLOR_BG0)))?;
|
type Engine = Tui;
|
||||||
add(&Split::right(|add|{
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
add(&TrackNameColumn(tracks, state.selected))?;
|
//let area = to.area();
|
||||||
add(&TrackMonitorColumn(tracks))?;
|
//let area = [area.x(), area.y(), area.w(), area.h().min((2 + state.tracks.len() * 2) as u16)];
|
||||||
add(&TrackRecordColumn(tracks))?;
|
let Arranger { tracks, focused, selected, scenes, .. } = self.0;
|
||||||
add(&TrackOverdubColumn(tracks))?;
|
let tracks = tracks.as_slice();
|
||||||
add(&TrackEraseColumn(tracks))?;
|
Layers::new(|add|{
|
||||||
add(&TrackGainColumn(tracks))?;
|
add(&focused.then_some(FillBg(COLOR_BG0)))?;
|
||||||
add(&TrackScenesColumn(tracks, state.scenes.as_slice(), state.selected))?;
|
add(&Split::right(|add|{
|
||||||
Ok(())
|
add(&TrackNameColumn(tracks, *selected))?;
|
||||||
}))
|
add(&TrackMonitorColumn(tracks))?;
|
||||||
}).render(to.with_rect(area))
|
add(&TrackRecordColumn(tracks))?;
|
||||||
|
add(&TrackOverdubColumn(tracks))?;
|
||||||
|
add(&TrackEraseColumn(tracks))?;
|
||||||
|
add(&TrackGainColumn(tracks))?;
|
||||||
|
add(&TrackScenesColumn(tracks, scenes.as_slice(), *selected))?;
|
||||||
|
Ok(())
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TrackNameColumn<'a>(&'a [Sequencer<Tui>], ArrangerFocus);
|
struct TrackNameColumn<'a>(&'a [Sequencer<Tui>], ArrangerFocus);
|
||||||
|
|
@ -1021,3 +867,117 @@ impl<'a> Widget for TrackScenesColumn<'a> {
|
||||||
Ok(Some([x, y, x2, height]))
|
Ok(Some([x, y, x2, height]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Appears on first run (i.e. if state dir is missing).
|
||||||
|
pub struct ArrangerRenameModal<E: Engine> {
|
||||||
|
_engine: std::marker::PhantomData<E>,
|
||||||
|
done: bool,
|
||||||
|
target: ArrangerFocus,
|
||||||
|
value: String,
|
||||||
|
result: Arc<RwLock<String>>,
|
||||||
|
cursor: usize
|
||||||
|
}
|
||||||
|
impl<E: Engine> ArrangerRenameModal<E> {
|
||||||
|
pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self {
|
||||||
|
Self {
|
||||||
|
_engine: Default::default(),
|
||||||
|
done: false,
|
||||||
|
target,
|
||||||
|
value: value.read().unwrap().clone(),
|
||||||
|
cursor: value.read().unwrap().len(),
|
||||||
|
result: value.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Widget for ArrangerRenameModal<Tui> {
|
||||||
|
type Engine = Tui;
|
||||||
|
fn layout (&self, area: [u16;4]) -> Perhaps<[u16;4]> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
fn render (&self, to: &mut Tui) -> Perhaps<[u16;4]> {
|
||||||
|
let area = to.area();
|
||||||
|
let y = area.y() + area.h() / 2;
|
||||||
|
let bg_area = [1, y - 1, area.w() - 2, 3];
|
||||||
|
to.fill_bg(bg_area, Nord::BG0);
|
||||||
|
Lozenge(Style::default().bold().white().dim()).draw(to.with_rect(bg_area));
|
||||||
|
let label = match self.target {
|
||||||
|
ArrangerFocus::Mix => "Rename project:",
|
||||||
|
ArrangerFocus::Track(_) => "Rename track:",
|
||||||
|
ArrangerFocus::Scene(_) => "Rename scene:",
|
||||||
|
ArrangerFocus::Clip(_, _) => "Rename clip:",
|
||||||
|
};
|
||||||
|
let style = Some(Style::default().not_bold().white().not_dim());
|
||||||
|
to.blit(&label, area.x() + 3, y, style)?;
|
||||||
|
let style = Some(Style::default().bold().white().not_dim());
|
||||||
|
to.blit(&self.value, area.x() + 3 + label.len() as u16 + 1, y, style)?;
|
||||||
|
let style = Some(Style::default().bold().white().not_dim().reversed());
|
||||||
|
to.blit(&"▂", area.x() + 3 + label.len() as u16 + 1 + self.cursor as u16, y, style)?;
|
||||||
|
Ok(Some(area))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Handle<Tui> for ArrangerRenameModal<Tui> {
|
||||||
|
fn handle (&mut self, from: &Tui) -> Perhaps<bool> {
|
||||||
|
match from.event() {
|
||||||
|
TuiEvent::Input(Event::Key(k)) => {
|
||||||
|
match k.code {
|
||||||
|
KeyCode::Esc => {
|
||||||
|
self.exit();
|
||||||
|
},
|
||||||
|
KeyCode::Enter => {
|
||||||
|
*self.result.write().unwrap() = self.value.clone();
|
||||||
|
self.exit();
|
||||||
|
},
|
||||||
|
KeyCode::Left => {
|
||||||
|
self.cursor = self.cursor.saturating_sub(1);
|
||||||
|
},
|
||||||
|
KeyCode::Right => {
|
||||||
|
self.cursor = self.value.len().min(self.cursor + 1)
|
||||||
|
},
|
||||||
|
KeyCode::Backspace => {
|
||||||
|
let last = self.value.len().saturating_sub(1);
|
||||||
|
self.value = format!("{}{}",
|
||||||
|
&self.value[0..self.cursor.min(last)],
|
||||||
|
&self.value[self.cursor.min(last)..last]
|
||||||
|
);
|
||||||
|
self.cursor = self.cursor.saturating_sub(1)
|
||||||
|
}
|
||||||
|
KeyCode::Char(c) => {
|
||||||
|
self.value.insert(self.cursor, c);
|
||||||
|
self.cursor = self.value.len().min(self.cursor + 1)
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Ok(Some(true))
|
||||||
|
},
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<E: Engine + Send> Exit for ArrangerRenameModal<E> {
|
||||||
|
fn exited (&self) -> bool {
|
||||||
|
self.done
|
||||||
|
}
|
||||||
|
fn exit (&mut self) {
|
||||||
|
self.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn track_name_max_len <E: Engine> (tracks: &[Sequencer<E>]) -> usize {
|
||||||
|
tracks.iter()
|
||||||
|
.map(|s|s.name.read().unwrap().len())
|
||||||
|
.fold(0, usize::max)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn track_clip_name_lengths <E: Engine> (tracks: &[Sequencer<E>]) -> Vec<(usize, usize)> {
|
||||||
|
let mut total = 0;
|
||||||
|
let mut lengths: Vec<(usize, usize)> = tracks.iter().map(|track|{
|
||||||
|
let len = 4 + track.phrases
|
||||||
|
.iter()
|
||||||
|
.fold(track.name.read().unwrap().len(), |len, phrase|{
|
||||||
|
len.max(phrase.read().unwrap().name.read().unwrap().len())
|
||||||
|
});
|
||||||
|
total = total + len;
|
||||||
|
(len, total - len)
|
||||||
|
}).collect();
|
||||||
|
lengths.push((0, total));
|
||||||
|
lengths
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,12 @@ impl Content for Demo<Tui> {
|
||||||
add(&Split::down(|add|{
|
add(&Split::down(|add|{
|
||||||
add(&Layers::new(|add|{
|
add(&Layers::new(|add|{
|
||||||
add(&FillBg(Color::Rgb(0,128,0)))?;
|
add(&FillBg(Color::Rgb(0,128,0)))?;
|
||||||
add(&Align::Center("55555"))?;
|
add(&Align::Center("12345"))?;
|
||||||
add(&Align::Center("FOO"))
|
add(&Align::Center("FOO"))
|
||||||
}))?;
|
}))?;
|
||||||
add(&Layers::new(|add|{
|
add(&Layers::new(|add|{
|
||||||
add(&FillBg(Color::Rgb(0,0,128)))?;
|
add(&FillBg(Color::Rgb(0,0,128)))?;
|
||||||
add(&Align::Center("7777777"))?;
|
add(&Align::Center("1234567"))?;
|
||||||
add(&Align::Center("BAR"))
|
add(&Align::Center("BAR"))
|
||||||
}))
|
}))
|
||||||
}))
|
}))
|
||||||
|
|
@ -59,7 +59,6 @@ impl Handle<Tui> for Demo<Tui> {
|
||||||
match from.event() {
|
match from.event() {
|
||||||
key!(KeyCode::PageUp) => {
|
key!(KeyCode::PageUp) => {
|
||||||
self.index = (self.index + 1) % self.items.len();
|
self.index = (self.index + 1) % self.items.len();
|
||||||
Ok(Some(true))
|
|
||||||
},
|
},
|
||||||
key!(KeyCode::PageDown) => {
|
key!(KeyCode::PageDown) => {
|
||||||
self.index = if self.index > 1 {
|
self.index = if self.index > 1 {
|
||||||
|
|
@ -67,10 +66,10 @@ impl Handle<Tui> for Demo<Tui> {
|
||||||
} else {
|
} else {
|
||||||
self.items.len() - 1
|
self.items.len() - 1
|
||||||
};
|
};
|
||||||
Ok(Some(true))
|
|
||||||
},
|
},
|
||||||
_ => Ok(None)
|
_ => return Ok(None)
|
||||||
}
|
}
|
||||||
|
Ok(Some(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue