mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: sequencer edit mode
This commit is contained in:
parent
eac8986548
commit
0af5f97244
5 changed files with 72 additions and 147 deletions
|
|
@ -9,6 +9,11 @@ pub trait FocusGrid<T: Copy + PartialEq> {
|
||||||
let (x, y) = self.cursor();
|
let (x, y) = self.cursor();
|
||||||
&self.layout()[y][x]
|
&self.layout()[y][x]
|
||||||
}
|
}
|
||||||
|
fn focus (&mut self, target: T) {
|
||||||
|
while self.focused() != &target {
|
||||||
|
self.focus_next()
|
||||||
|
}
|
||||||
|
}
|
||||||
fn focus_up (&mut self) {
|
fn focus_up (&mut self) {
|
||||||
let layout = self.layout();
|
let layout = self.layout();
|
||||||
let (x, y) = self.cursor();
|
let (x, y) = self.cursor();
|
||||||
|
|
|
||||||
|
|
@ -130,124 +130,52 @@ impl<N: Number> Area<N> for [N;4] {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Layout<E: Engine>: Widget<Engine = E> + Sized {
|
pub trait Layout<E: Engine>: Widget<Engine = E> + Sized {
|
||||||
fn align_center (self) -> Align<Self> {
|
fn align_center (self) -> Align<Self> { Align::Center(self) }
|
||||||
Align::Center(self)
|
fn align_nw (self) -> Align<Self> { Align::NW(self) }
|
||||||
}
|
fn align_w (self) -> Align<Self> { Align::W(self) }
|
||||||
fn align_w (self) -> Align<Self> {
|
fn align_sw (self) -> Align<Self> { Align::SW(self) }
|
||||||
Align::W(self)
|
fn align_ne (self) -> Align<Self> { Align::NE(self) }
|
||||||
}
|
fn align_e (self) -> Align<Self> { Align::E(self) }
|
||||||
fn align_e (self) -> Align<Self> {
|
fn align_se (self) -> Align<Self> { Align::SE(self) }
|
||||||
Align::E(self)
|
fn align_x (self) -> Align<Self> { Align::X(self) }
|
||||||
}
|
fn align_y (self) -> Align<Self> { Align::Y(self) }
|
||||||
fn align_x (self) -> Align<Self> {
|
fn fixed_x (self, x: E::Unit) -> Fixed<E::Unit, Self> { Fixed::X(x, self) }
|
||||||
Align::X(self)
|
fn fixed_y (self, y: E::Unit) -> Fixed<E::Unit, Self> { Fixed::Y(y, self) }
|
||||||
}
|
fn fixed_xy (self, x: E::Unit, y: E::Unit) -> Fixed<E::Unit, Self> { Fixed::XY(x, y, self) }
|
||||||
fn align_y (self) -> Align<Self> {
|
fn min_x (self, x: E::Unit) -> Min<E::Unit, Self> { Min::X(x, self) }
|
||||||
Align::Y(self)
|
fn min_y (self, y: E::Unit) -> Min<E::Unit, Self> { Min::Y(y, self) }
|
||||||
}
|
fn min_xy (self, x: E::Unit, y: E::Unit) -> Min<E::Unit, Self> { Min::XY(x, y, self) }
|
||||||
fn fixed_x (self, x: E::Unit) -> Fixed<E::Unit, Self> {
|
fn max_x (self, x: E::Unit) -> Max<E::Unit, Self> { Max::X(x, self) }
|
||||||
Fixed::X(x, self)
|
fn max_y (self, y: E::Unit) -> Max<E::Unit, Self> { Max::Y(y, self) }
|
||||||
}
|
fn max_xy (self, x: E::Unit, y: E::Unit) -> Max<E::Unit, Self> { Max::XY(x, y, self) }
|
||||||
fn fixed_y (self, y: E::Unit) -> Fixed<E::Unit, Self> {
|
fn push_x (self, x: E::Unit) -> Push<E::Unit, Self> { Push::X(x, self) }
|
||||||
Fixed::Y(y, self)
|
fn push_y (self, y: E::Unit) -> Push<E::Unit, Self> { Push::Y(y, self) }
|
||||||
}
|
fn push_xy (self, x: E::Unit, y: E::Unit) -> Push<E::Unit, Self> { Push::XY(x, y, self) }
|
||||||
fn fixed_xy (self, x: E::Unit, y: E::Unit) -> Fixed<E::Unit, Self> {
|
fn pull_x (self, x: E::Unit) -> Pull<E::Unit, Self> { Pull::X(x, self) }
|
||||||
Fixed::XY(x, y, self)
|
fn pull_y (self, y: E::Unit) -> Pull<E::Unit, Self> { Pull::Y(y, self) }
|
||||||
}
|
fn pull_xy (self, x: E::Unit, y: E::Unit) -> Pull<E::Unit, Self> { Pull::XY(x, y, self) }
|
||||||
fn min_x (self, x: E::Unit) -> Min<E::Unit, Self> {
|
fn grow_x (self, x: E::Unit) -> Grow<E::Unit, Self> { Grow::X(x, self) }
|
||||||
Min::X(x, self)
|
fn grow_y (self, y: E::Unit) -> Grow<E::Unit, Self> { Grow::Y(y, self) }
|
||||||
}
|
fn grow_xy (self, x: E::Unit, y: E::Unit) -> Grow<E::Unit, Self> { Grow::XY(x, y, self) }
|
||||||
fn min_y (self, y: E::Unit) -> Min<E::Unit, Self> {
|
fn shrink_x (self, x: E::Unit) -> Shrink<E::Unit, Self> { Shrink::X(x, self) }
|
||||||
Min::Y(y, self)
|
fn shrink_y (self, y: E::Unit) -> Shrink<E::Unit, Self> { Shrink::Y(y, self) }
|
||||||
}
|
fn shrink_xy (self, x: E::Unit, y: E::Unit) -> Shrink<E::Unit, Self> { Shrink::XY(x, y, self) }
|
||||||
fn min_xy (self, x: E::Unit, y: E::Unit) -> Min<E::Unit, Self> {
|
fn inset_x (self, x: E::Unit) -> Inset<E::Unit, Self> { Inset::X(x, self) }
|
||||||
Min::XY(x, y, self)
|
fn inset_y (self, y: E::Unit) -> Inset<E::Unit, Self> { Inset::Y(y, self) }
|
||||||
}
|
fn inset_xy (self, x: E::Unit, y: E::Unit) -> Inset<E::Unit, Self> { Inset::XY(x, y, self) }
|
||||||
fn max_x (self, x: E::Unit) -> Max<E::Unit, Self> {
|
fn outset_x (self, x: E::Unit) -> Outset<E::Unit, Self> { Outset::X(x, self) }
|
||||||
Max::X(x, self)
|
fn outset_y (self, y: E::Unit) -> Outset<E::Unit, Self> { Outset::Y(y, self) }
|
||||||
}
|
fn outset_xy (self, x: E::Unit, y: E::Unit) -> Outset<E::Unit, Self> { Outset::XY(x, y, self) }
|
||||||
fn max_y (self, y: E::Unit) -> Max<E::Unit, Self> {
|
fn fill_x (self) -> Fill<E, Self> { Fill::X(self) }
|
||||||
Max::Y(y, self)
|
fn fill_y (self) -> Fill<E, Self> { Fill::Y(self) }
|
||||||
}
|
fn fill_xy (self) -> Fill<E, Self> { Fill::XY(self) }
|
||||||
fn max_xy (self, x: E::Unit, y: E::Unit) -> Max<E::Unit, Self> {
|
fn debug (self) -> DebugOverlay<E, Self> { DebugOverlay(self) }
|
||||||
Max::XY(x, y, self)
|
|
||||||
}
|
|
||||||
fn push_x (self, x: E::Unit) -> Push<E::Unit, Self> {
|
|
||||||
Push::X(x, self)
|
|
||||||
}
|
|
||||||
fn push_y (self, y: E::Unit) -> Push<E::Unit, Self> {
|
|
||||||
Push::Y(y, self)
|
|
||||||
}
|
|
||||||
fn push_xy (self, x: E::Unit, y: E::Unit) -> Push<E::Unit, Self> {
|
|
||||||
Push::XY(x, y, self)
|
|
||||||
}
|
|
||||||
fn pull_x (self, x: E::Unit) -> Pull<E::Unit, Self> {
|
|
||||||
Pull::X(x, self)
|
|
||||||
}
|
|
||||||
fn pull_y (self, y: E::Unit) -> Pull<E::Unit, Self> {
|
|
||||||
Pull::Y(y, self)
|
|
||||||
}
|
|
||||||
fn pull_xy (self, x: E::Unit, y: E::Unit) -> Pull<E::Unit, Self> {
|
|
||||||
Pull::XY(x, y, self)
|
|
||||||
}
|
|
||||||
fn grow_x (self, x: E::Unit) -> Grow<E::Unit, Self> {
|
|
||||||
Grow::X(x, self)
|
|
||||||
}
|
|
||||||
fn grow_y (self, y: E::Unit) -> Grow<E::Unit, Self> {
|
|
||||||
Grow::Y(y, self)
|
|
||||||
}
|
|
||||||
fn grow_xy (self, x: E::Unit, y: E::Unit) -> Grow<E::Unit, Self> {
|
|
||||||
Grow::XY(x, y, self)
|
|
||||||
}
|
|
||||||
fn shrink_x (self, x: E::Unit) -> Shrink<E::Unit, Self> {
|
|
||||||
Shrink::X(x, self)
|
|
||||||
}
|
|
||||||
fn shrink_y (self, y: E::Unit) -> Shrink<E::Unit, Self> {
|
|
||||||
Shrink::Y(y, self)
|
|
||||||
}
|
|
||||||
fn shrink_xy (self, x: E::Unit, y: E::Unit) -> Shrink<E::Unit, Self> {
|
|
||||||
Shrink::XY(x, y, self)
|
|
||||||
}
|
|
||||||
fn inset_x (self, x: E::Unit) -> Inset<E::Unit, Self> {
|
|
||||||
Inset::X(x, self)
|
|
||||||
}
|
|
||||||
fn inset_y (self, y: E::Unit) -> Inset<E::Unit, Self> {
|
|
||||||
Inset::Y(y, self)
|
|
||||||
}
|
|
||||||
fn inset_xy (self, x: E::Unit, y: E::Unit) -> Inset<E::Unit, Self> {
|
|
||||||
Inset::XY(x, y, self)
|
|
||||||
}
|
|
||||||
fn outset_x (self, x: E::Unit) -> Outset<E::Unit, Self> {
|
|
||||||
Outset::X(x, self)
|
|
||||||
}
|
|
||||||
fn outset_y (self, y: E::Unit) -> Outset<E::Unit, Self> {
|
|
||||||
Outset::Y(y, self)
|
|
||||||
}
|
|
||||||
fn outset_xy (self, x: E::Unit, y: E::Unit) -> Outset<E::Unit, Self> {
|
|
||||||
Outset::XY(x, y, self)
|
|
||||||
}
|
|
||||||
fn fill_x (self) -> Fill<E, Self> {
|
|
||||||
Fill::X(self)
|
|
||||||
}
|
|
||||||
fn fill_y (self) -> Fill<E, Self> {
|
|
||||||
Fill::Y(self)
|
|
||||||
}
|
|
||||||
fn fill_xy (self) -> Fill<E, Self> {
|
|
||||||
Fill::XY(self)
|
|
||||||
}
|
|
||||||
fn debug (self) -> DebugOverlay<E, Self> {
|
|
||||||
DebugOverlay(self)
|
|
||||||
}
|
|
||||||
fn split <W: Widget<Engine = E>> (
|
fn split <W: Widget<Engine = E>> (
|
||||||
self, direction: Direction, amount: E::Unit, other: W
|
self, direction: Direction, amount: E::Unit, other: W
|
||||||
) -> Split<E, Self, W> {
|
) -> Split<E, Self, W> { Split::new(direction, amount, self, other) }
|
||||||
Split::new(direction, amount, self, other)
|
|
||||||
}
|
|
||||||
fn split_flip <W: Widget<Engine = E>> (
|
fn split_flip <W: Widget<Engine = E>> (
|
||||||
self, direction: Direction, amount: E::Unit, other: W
|
self, direction: Direction, amount: E::Unit, other: W
|
||||||
) -> Split<E, W, Self> {
|
) -> Split<E, W, Self> { Split::new(direction, amount, other, self) }
|
||||||
Split::new(direction, amount, other, self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine, W: Widget<Engine = E>> Layout<E> for W {}
|
impl<E: Engine, W: Widget<Engine = E>> Layout<E> for W {}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ impl Handle<Tui> for Arranger<Tui> {
|
||||||
key!(KeyCode::Right) => { self.focus_right(); },
|
key!(KeyCode::Right) => { self.focus_right(); },
|
||||||
key!(KeyCode::Char('e')) => {
|
key!(KeyCode::Char('e')) => {
|
||||||
self.editor.phrase = self.arrangement.phrase().clone();
|
self.editor.phrase = self.arrangement.phrase().clone();
|
||||||
self.focus_cursor = (1, 2);
|
self.focus(ArrangerFocus::PhraseEditor);
|
||||||
}
|
}
|
||||||
// Global play/pause binding
|
// Global play/pause binding
|
||||||
key!(KeyCode::Char(' ')) => match self.transport {
|
key!(KeyCode::Char(' ')) => match self.transport {
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,8 @@ impl Content for Arrangement<Tui> {
|
||||||
ArrangementViewMode::Horizontal => add(&HorizontalArranger(&self)),
|
ArrangementViewMode::Horizontal => add(&HorizontalArranger(&self)),
|
||||||
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor))
|
ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor))
|
||||||
}?;
|
}?;
|
||||||
add(&Align::SE(self.selected.description(
|
let description = self.selected.description(&self.tracks, &self.scenes);
|
||||||
&self.tracks,
|
add(&Align::SE(description.as_str()))
|
||||||
&self.scenes,
|
|
||||||
).as_str()))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ impl Content for PhraseEditor<Tui> {
|
||||||
// keys
|
// keys
|
||||||
CustomWidget::new(|_|Ok(Some([32u16,4u16])), |to: &mut TuiOutput|{
|
CustomWidget::new(|_|Ok(Some([32u16,4u16])), |to: &mut TuiOutput|{
|
||||||
if to.area().h() < 2 { return Ok(()) }
|
if to.area().h() < 2 { return Ok(()) }
|
||||||
Ok(to.buffer_update(to.area().set_w(5).shrink_y(2), &|cell, x, y|{
|
Ok(to.buffer_update(to.area().set_w(5), &|cell, x, y|{
|
||||||
let y = y + self.note_axis.start as u16;
|
let y = y + self.note_axis.start as u16;
|
||||||
if x < self.keys.area.width && y < self.keys.area.height {
|
if x < self.keys.area.width && y < self.keys.area.height {
|
||||||
*cell = self.keys.get(x, y).clone()
|
*cell = self.keys.get(x, y).clone()
|
||||||
|
|
@ -124,7 +124,7 @@ impl Content for PhraseEditor<Tui> {
|
||||||
CustomWidget::new(|_|Ok(Some([32u16,4u16])), |to: &mut TuiOutput|{
|
CustomWidget::new(|_|Ok(Some([32u16,4u16])), |to: &mut TuiOutput|{
|
||||||
let offset = Self::H_KEYS_OFFSET as u16;
|
let offset = Self::H_KEYS_OFFSET as u16;
|
||||||
if to.area().h() < 2 || to.area().w() < offset { return Ok(()) }
|
if to.area().h() < 2 || to.area().w() < offset { return Ok(()) }
|
||||||
let area = to.area().push_x(offset).shrink_x(offset).shrink_y(2);
|
let area = to.area().push_x(offset).shrink_x(offset);
|
||||||
Ok(to.buffer_update(area, &move |cell, x, y|{
|
Ok(to.buffer_update(area, &move |cell, x, y|{
|
||||||
cell.set_bg(Color::Rgb(20, 20, 20));
|
cell.set_bg(Color::Rgb(20, 20, 20));
|
||||||
let src_x = ((x as usize + self.time_axis.start) * self.time_axis.scale) as usize;
|
let src_x = ((x as usize + self.time_axis.start) * self.time_axis.scale) as usize;
|
||||||
|
|
@ -148,33 +148,27 @@ impl Content for PhraseEditor<Tui> {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}),
|
}),
|
||||||
// zoom
|
|
||||||
CustomWidget::new(|_|Ok(Some([10u16,1u16])), |to: &mut TuiOutput|{
|
|
||||||
let [x, y, w, h] = to.area.xywh();
|
|
||||||
let quant = ppq_to_name(self.time_axis.scale);
|
|
||||||
Ok(to.blit(
|
|
||||||
&quant,
|
|
||||||
x + w - 1 - quant.len() as u16,
|
|
||||||
y + h - 2,
|
|
||||||
self.style_focus()
|
|
||||||
))
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
let content = row!(piano_roll)
|
let border_color =
|
||||||
.fill_x()
|
if self.focused { Color::Rgb(100, 110, 40) } else { Color::Rgb(70, 80, 50) };
|
||||||
.bg(Color::Rgb(40, 50, 30))
|
let title_color =
|
||||||
.border(Lozenge(Style::default()
|
if self.focused { Color::Rgb(150, 160, 90) } else { Color::Rgb(120, 130, 100) };
|
||||||
.bg(Color::Rgb(40, 50, 30))
|
let border =
|
||||||
.fg(if self.focused {
|
Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color));
|
||||||
Color::Rgb(100, 110, 40)
|
let content =
|
||||||
} else {
|
piano_roll.fill_x().bg(Color::Rgb(40, 50, 30)).border(border);
|
||||||
Color::Rgb(70, 80, 50)
|
let title = TuiStyle::fg("Sequencer", title_color)
|
||||||
})));
|
.push_x(1);
|
||||||
lay!(content, TuiStyle::fg("Sequencer", if self.focused {
|
let zoom = TuiStyle::fg(format!("{}Zoom: {}",
|
||||||
Color::Rgb(150, 160, 90)
|
if self.focused && !self.entered { "[,.] " } else { "" },
|
||||||
} else {
|
ppq_to_name(self.time_axis.scale),
|
||||||
Color::Rgb(120, 130, 100)
|
), title_color).pull_x(1).align_se().fill_xy();
|
||||||
}).push_x(1))
|
let enter = TuiStyle::fg(if self.focused {
|
||||||
|
if self.entered { "[Esc] Exit edit mode [A]ppend [I]nsert" } else {
|
||||||
|
"[Enter] Edit notes"
|
||||||
|
}
|
||||||
|
} else { "" }, title_color).push_x(1).align_sw().fill_xy();
|
||||||
|
lay!(content, title, enter, zoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl PhraseEditor<Tui> {
|
impl PhraseEditor<Tui> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue