mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
arranger: theme trait
This commit is contained in:
parent
89cb8d7bbe
commit
0b1193722d
6 changed files with 149 additions and 87 deletions
|
|
@ -12,8 +12,8 @@ pub fn delegate <B, C: Command<S>, S> (
|
||||||
Ok(cmd.execute(state)?.map(|x|wrap(x)))
|
Ok(cmd.execute(state)?.map(|x|wrap(x)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MatchInput<E: Engine, S>: Sized {
|
pub trait InputToCommand<E: Engine, S>: Sized {
|
||||||
fn match_input (state: &S, input: &E::Input) -> Option<Self>;
|
fn input_to_command (state: &S, input: &E::Input) -> Option<Self>;
|
||||||
}
|
}
|
||||||
pub struct MenuBar<E: Engine, S, C: Command<S>> {
|
pub struct MenuBar<E: Engine, S, C: Command<S>> {
|
||||||
pub menus: Vec<Menu<E, S, C>>,
|
pub menus: Vec<Menu<E, S, C>>,
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ pub struct Arranger<E: Engine> {
|
||||||
pub size: Measure<E>,
|
pub size: Measure<E>,
|
||||||
/// Menu bar
|
/// Menu bar
|
||||||
pub menu: MenuBar<E, Self, ArrangerCommand>,
|
pub menu: MenuBar<E, Self, ArrangerCommand>,
|
||||||
|
/// Command history
|
||||||
|
pub history: Vec<ArrangerCommand>,
|
||||||
}
|
}
|
||||||
/// Sections in the arranger app that may be focused
|
/// Sections in the arranger app that may be focused
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
|
@ -142,6 +144,7 @@ impl<E: Engine> Arranger<E> {
|
||||||
transport: transport.clone(),
|
transport: transport.clone(),
|
||||||
arrangement,
|
arrangement,
|
||||||
phrases,
|
phrases,
|
||||||
|
history: vec![],
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
clock: if let Some(ref transport) = transport {
|
clock: if let Some(ref transport) = transport {
|
||||||
transport.read().unwrap().clock.clone()
|
transport.read().unwrap().clock.clone()
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ pub enum ArrangerCommand {
|
||||||
Phrases(PhrasePoolCommand),
|
Phrases(PhrasePoolCommand),
|
||||||
Editor(PhraseEditorCommand),
|
Editor(PhraseEditorCommand),
|
||||||
Arrangement(ArrangementCommand),
|
Arrangement(ArrangementCommand),
|
||||||
|
EditPhrase,
|
||||||
}
|
}
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub enum ArrangementCommand {
|
pub enum ArrangementCommand {
|
||||||
|
|
@ -32,6 +33,7 @@ pub enum ArrangementCommand {
|
||||||
GoDown,
|
GoDown,
|
||||||
GoLeft,
|
GoLeft,
|
||||||
GoRight,
|
GoRight,
|
||||||
|
Edit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: Engine> Command<Arranger<E>> for ArrangerCommand {
|
impl<E: Engine> Command<Arranger<E>> for ArrangerCommand {
|
||||||
|
|
@ -54,6 +56,14 @@ impl<E: Engine> Command<Arranger<E>> for ArrangerCommand {
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
},
|
},
|
||||||
|
Self::EditPhrase => if let Some(phrase) = state.arrangement.phrase() {
|
||||||
|
state.editor.phrase = Some(phrase.clone());
|
||||||
|
state.focus(ArrangerFocus::PhraseEditor);
|
||||||
|
state.focus_enter();
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}?;
|
}?;
|
||||||
state.show_phrase();
|
state.show_phrase();
|
||||||
state.update_status();
|
state.update_status();
|
||||||
|
|
@ -67,6 +77,7 @@ impl<E: Engine> Command<Arrangement<E>> for ArrangementCommand {
|
||||||
New => todo!(),
|
New => todo!(),
|
||||||
Load => todo!(),
|
Load => todo!(),
|
||||||
Save => todo!(),
|
Save => todo!(),
|
||||||
|
Edit => todo!(),
|
||||||
ToggleViewMode => { state.mode.to_next(); },
|
ToggleViewMode => { state.mode.to_next(); },
|
||||||
Delete => { state.delete(); },
|
Delete => { state.delete(); },
|
||||||
Activate => { state.activate(); },
|
Activate => { state.activate(); },
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
/// Layout for standalone arranger app.
|
/// Layout for standalone arranger app.
|
||||||
impl Content for Arranger<Tui> {
|
impl Content for Arranger<Tui> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
let focused = self.arrangement.focused;
|
let focused = self.arrangement.focused;
|
||||||
let border_color = if focused{Color::Rgb(100, 110, 40)}else{Color::Rgb(70, 80, 50)};
|
let border_bg = Arranger::<Tui>::border_bg();
|
||||||
let title_color = if focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
let border_fg = Arranger::<Tui>::border_fg(focused);
|
||||||
let border = Lozenge(Style::default().bg(Color::Rgb(40, 50, 30)).fg(border_color));
|
let title_fg = Arranger::<Tui>::title_fg(focused);
|
||||||
|
let border = Lozenge(Style::default().bg(border_bg).fg(border_fg));
|
||||||
|
let entered = if self.arrangement.entered { "■" } else { " " };
|
||||||
Split::down(
|
Split::down(
|
||||||
1,
|
1,
|
||||||
row!(menu in self.menu.menus.iter() => {
|
row!(menu in self.menu.menus.iter() => {
|
||||||
|
|
@ -23,11 +26,7 @@ impl Content for Arranger<Tui> {
|
||||||
lay!(
|
lay!(
|
||||||
widget(&self.arrangement).grow_y(1).border(border),
|
widget(&self.arrangement).grow_y(1).border(border),
|
||||||
widget(&self.arrangement.size),
|
widget(&self.arrangement.size),
|
||||||
widget(&format!("[{}] Arrangement", if self.arrangement.entered {
|
widget(&format!("[{}] Arrangement", entered)).fg(title_fg).push_x(1),
|
||||||
"■"
|
|
||||||
} else {
|
|
||||||
" "
|
|
||||||
})).fg(title_color).push_x(1),
|
|
||||||
),
|
),
|
||||||
Split::right(
|
Split::right(
|
||||||
self.phrases_split,
|
self.phrases_split,
|
||||||
|
|
@ -53,9 +52,10 @@ impl Content for ArrangerStatusBar {
|
||||||
Self::PhraseView => "VIEW SEQ",
|
Self::PhraseView => "VIEW SEQ",
|
||||||
Self::PhraseEdit => "EDIT SEQ",
|
Self::PhraseEdit => "EDIT SEQ",
|
||||||
};
|
};
|
||||||
let mode = TuiStyle::bg(format!(" {label} "), Color::Rgb(150, 160, 90))
|
let status_bar_bg = Arranger::<Tui>::status_bar_bg();
|
||||||
.fg(Color::Rgb(0, 0, 0))
|
let mode_bg = Arranger::<Tui>::mode_bg();
|
||||||
.bold(true);
|
let mode_fg = Arranger::<Tui>::mode_fg();
|
||||||
|
let mode = TuiStyle::bold(format!(" {label} "), true).bg(mode_bg).fg(mode_fg);
|
||||||
let commands = match self {
|
let commands = match self {
|
||||||
Self::ArrangementMix => command(&[
|
Self::ArrangementMix => command(&[
|
||||||
["", "c", "olor"],
|
["", "c", "olor"],
|
||||||
|
|
@ -118,13 +118,18 @@ impl Content for ArrangerStatusBar {
|
||||||
_ => command(&[])
|
_ => command(&[])
|
||||||
};
|
};
|
||||||
//let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}"));
|
//let commands = commands.iter().reduce(String::new(), |s, (a, b, c)| format!("{s} {a}{b}{c}"));
|
||||||
row!(mode, commands).fill_x().bg(Color::Rgb(28, 35, 25))
|
row!(mode, commands).fill_x().bg(status_bar_bg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn command (commands: &[[impl Widget<Engine = Tui>;3]]) -> impl Widget<Engine = Tui> + '_ {
|
fn command (commands: &[[impl Widget<Engine = Tui>;3]]) -> impl Widget<Engine = Tui> + '_ {
|
||||||
Stack::right(|add|{
|
Stack::right(|add|{
|
||||||
Ok(for [a, b, c] in commands.iter() {
|
Ok(for [a, b, c] in commands.iter() {
|
||||||
add(&row!(" ", widget(a), widget(b).bold(true).fg(Color::Rgb(255,255,0)), widget(c)))?;
|
add(&row!(
|
||||||
|
" ",
|
||||||
|
widget(a),
|
||||||
|
widget(b).bold(true).fg(Arranger::<Tui>::hotkey_fg()),
|
||||||
|
widget(c),
|
||||||
|
))?;
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -133,15 +138,9 @@ impl Content for Arrangement<Tui> {
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
Layers::new(move |add|{
|
Layers::new(move |add|{
|
||||||
match self.mode {
|
match self.mode {
|
||||||
ArrangementViewMode::Horizontal => {
|
ArrangementViewMode::Horizontal => { add(&HorizontalArranger(&self)) },
|
||||||
add(&HorizontalArranger(&self))
|
ArrangementViewMode::Vertical(factor) => { add(&VerticalArranger(&self, factor)) },
|
||||||
},
|
|
||||||
ArrangementViewMode::Vertical(factor) => {
|
|
||||||
add(&VerticalArranger(&self, factor))
|
|
||||||
},
|
|
||||||
}?;
|
}?;
|
||||||
let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
|
||||||
//add(&TuiStyle::fg("Project", color).push_x(2))?;
|
|
||||||
add(&self.size)
|
add(&self.size)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +154,8 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
||||||
let cols = state.track_widths();
|
let cols = state.track_widths();
|
||||||
let rows = Scene::ppqs(scenes, *factor);
|
let rows = Scene::ppqs(scenes, *factor);
|
||||||
let bg = state.color;
|
let bg = state.color;
|
||||||
let clip_bg = Color::Rgb(40, 50, 30);
|
let clip_bg = Arranger::<Tui>::border_bg();
|
||||||
|
let sep_fg = Arranger::<Tui>::separator_fg(false);
|
||||||
let header_h = 3u16;//5u16;
|
let header_h = 3u16;//5u16;
|
||||||
let scenes_w = 3 + Scene::longest_name(scenes) as u16; // x of 1st track
|
let scenes_w = 3 + Scene::longest_name(scenes) as u16; // x of 1st track
|
||||||
let clock = &self.0.clock;
|
let clock = &self.0.clock;
|
||||||
|
|
@ -165,7 +165,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
||||||
let any_size = |_|Ok(Some([0,0]));
|
let any_size = |_|Ok(Some([0,0]));
|
||||||
// column separators
|
// column separators
|
||||||
add(&CustomWidget::new(any_size, move|to: &mut TuiOutput|{
|
add(&CustomWidget::new(any_size, move|to: &mut TuiOutput|{
|
||||||
let style = Some(Style::default().fg(Color::Rgb(0, 0, 0)));
|
let style = Some(Style::default().fg(sep_fg));
|
||||||
Ok(for x in cols.iter().map(|col|col.1) {
|
Ok(for x in cols.iter().map(|col|col.1) {
|
||||||
let x = scenes_w + to.area().x() + x as u16;
|
let x = scenes_w + to.area().x() + x as u16;
|
||||||
for y in to.area().y()..to.area().y2() { to.blit(&"▎", x, y, style); }
|
for y in to.area().y()..to.area().y2() { to.blit(&"▎", x, y, style); }
|
||||||
|
|
@ -180,7 +180,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
||||||
if x < to.buffer.area.x && y < to.buffer.area.y {
|
if x < to.buffer.area.x && y < to.buffer.area.y {
|
||||||
let cell = to.buffer.get_mut(x, y);
|
let cell = to.buffer.get_mut(x, y);
|
||||||
cell.modifier = Modifier::UNDERLINED;
|
cell.modifier = Modifier::UNDERLINED;
|
||||||
cell.underline_color = Color::Rgb(0, 0, 0);
|
cell.underline_color = sep_fg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -307,13 +307,14 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
||||||
area
|
area
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let bg = Arranger::<Tui>::border_bg();
|
||||||
if let Some([x, y, width, height]) = track_area {
|
if let Some([x, y, width, height]) = track_area {
|
||||||
to.fill_fg([x, y, 1, height], Color::Rgb(70, 80, 50));
|
to.fill_fg([x, y, 1, height], bg);
|
||||||
to.fill_fg([x + width, y, 1, height], Color::Rgb(70, 80, 50));
|
to.fill_fg([x + width, y, 1, height], bg);
|
||||||
}
|
}
|
||||||
if let Some([_, y, _, height]) = scene_area {
|
if let Some([_, y, _, height]) = scene_area {
|
||||||
to.fill_ul([area.x(), y - 1, area.w(), 1], Color::Rgb(70, 80, 50));
|
to.fill_ul([area.x(), y - 1, area.w(), 1], bg);
|
||||||
to.fill_ul([area.x(), y + height - 1, area.w(), 1], Color::Rgb(70, 80, 50));
|
to.fill_ul([area.x(), y + height - 1, area.w(), 1], bg);
|
||||||
}
|
}
|
||||||
Ok(if focused {
|
Ok(if focused {
|
||||||
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
to.render_in(if let Some(clip_area) = clip_area { clip_area }
|
||||||
|
|
@ -323,7 +324,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}).bg(bg.rgb);
|
}).bg(bg.rgb);
|
||||||
let color = if self.0.focused {Color::Rgb(150, 160, 90)} else {Color::Rgb(120, 130, 100)};
|
let color = Arranger::<Tui>::title_fg(self.0.focused);
|
||||||
let size = format!("{}x{}", self.0.size.w(), self.0.size.h());
|
let size = format!("{}x{}", self.0.size.w(), self.0.size.h());
|
||||||
let lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy();
|
let lower_right = TuiStyle::fg(size, color).pull_x(1).align_se().fill_xy();
|
||||||
lay!(arrangement, lower_right)
|
lay!(arrangement, lower_right)
|
||||||
|
|
@ -335,7 +336,7 @@ impl<'a> Content for HorizontalArranger<'a, Tui> {
|
||||||
let Arrangement { tracks, focused, .. } = self.0;
|
let Arrangement { tracks, focused, .. } = self.0;
|
||||||
let _tracks = tracks.as_slice();
|
let _tracks = tracks.as_slice();
|
||||||
lay!(
|
lay!(
|
||||||
focused.then_some(Background(Color::Rgb(40, 50, 30))),
|
focused.then_some(Background(Arranger::<Tui>::border_bg())),
|
||||||
row!(
|
row!(
|
||||||
// name
|
// name
|
||||||
CustomWidget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
CustomWidget::new(|_|{todo!()}, |_: &mut TuiOutput|{
|
||||||
|
|
@ -538,7 +539,7 @@ impl Handle<Tui> for Arranger<Tui> {
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(if let Some(command) = ArrangerCommand::match_input(self, i) {
|
Ok(if let Some(command) = ArrangerCommand::input_to_command(self, i) {
|
||||||
let _undo = command.execute(self)?;
|
let _undo = command.execute(self)?;
|
||||||
Some(true)
|
Some(true)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -549,7 +550,7 @@ impl Handle<Tui> for Arranger<Tui> {
|
||||||
/// Handle events for arrangement.
|
/// Handle events for arrangement.
|
||||||
impl Handle<Tui> for Arrangement<Tui> {
|
impl Handle<Tui> for Arrangement<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
Ok(if let Some(command) = ArrangementCommand::match_input(self, from) {
|
Ok(if let Some(command) = ArrangementCommand::input_to_command(self, from) {
|
||||||
let _undo = command.execute(self)?;
|
let _undo = command.execute(self)?;
|
||||||
Some(true)
|
Some(true)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -557,8 +558,8 @@ impl Handle<Tui> for Arrangement<Tui> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, Arranger<Tui>> for ArrangerCommand {
|
impl InputToCommand<Tui, Arranger<Tui>> for ArrangerCommand {
|
||||||
fn match_input (state: &Arranger<Tui>, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &Arranger<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
use FocusCommand::*;
|
use FocusCommand::*;
|
||||||
use ArrangerCommand::*;
|
use ArrangerCommand::*;
|
||||||
match input.event() {
|
match input.event() {
|
||||||
|
|
@ -575,46 +576,50 @@ impl MatchInput<Tui, Arranger<Tui>> for ArrangerCommand {
|
||||||
key!(KeyCode::Char(' ')) => Some(Transport(TransportCommand::PlayToggle)),
|
key!(KeyCode::Char(' ')) => Some(Transport(TransportCommand::PlayToggle)),
|
||||||
_ => match state.focused() {
|
_ => match state.focused() {
|
||||||
ArrangerFocus::Transport => state.transport.as_ref()
|
ArrangerFocus::Transport => state.transport.as_ref()
|
||||||
.map(|t|TransportCommand::match_input(&*t.read().unwrap(), input)
|
.map(|t|TransportCommand::input_to_command(&*t.read().unwrap(), input)
|
||||||
.map(Transport))
|
.map(Transport))
|
||||||
.flatten(),
|
.flatten(),
|
||||||
ArrangerFocus::PhrasePool =>
|
ArrangerFocus::PhrasePool =>
|
||||||
PhrasePoolCommand::match_input(&*state.phrases.read().unwrap(), input)
|
PhrasePoolCommand::input_to_command(&*state.phrases.read().unwrap(), input)
|
||||||
.map(Phrases),
|
.map(Phrases),
|
||||||
ArrangerFocus::PhraseEditor =>
|
ArrangerFocus::PhraseEditor =>
|
||||||
PhraseEditorCommand::match_input(&state.editor, input)
|
PhraseEditorCommand::input_to_command(&state.editor, input)
|
||||||
.map(Editor),
|
.map(Editor),
|
||||||
ArrangerFocus::Arrangement =>
|
ArrangerFocus::Arrangement => match input.event() {
|
||||||
ArrangementCommand::match_input(&state.arrangement, &input)
|
key!(KeyCode::Char('e')) => Some(EditPhrase),
|
||||||
|
_ => ArrangementCommand::input_to_command(&state.arrangement, &input)
|
||||||
.map(Arrangement)
|
.map(Arrangement)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, Arrangement<Tui>> for ArrangementCommand {
|
impl InputToCommand<Tui, Arrangement<Tui>> for ArrangementCommand {
|
||||||
fn match_input (_: &Arrangement<Tui>, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (_: &Arrangement<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
|
use ArrangementCommand::*;
|
||||||
match input.event() {
|
match input.event() {
|
||||||
key!(KeyCode::Char('`')) => Some(Self::ToggleViewMode),
|
key!(KeyCode::Char('`')) => Some(ToggleViewMode),
|
||||||
key!(KeyCode::Delete) => Some(Self::Delete),
|
key!(KeyCode::Delete) => Some(Delete),
|
||||||
key!(KeyCode::Enter) => Some(Self::Activate),
|
key!(KeyCode::Enter) => Some(Activate),
|
||||||
key!(KeyCode::Char('.')) => Some(Self::Increment),
|
key!(KeyCode::Char('.')) => Some(Increment),
|
||||||
key!(KeyCode::Char(',')) => Some(Self::Decrement),
|
key!(KeyCode::Char(',')) => Some(Decrement),
|
||||||
key!(KeyCode::Char('+')) => Some(Self::ZoomIn),
|
key!(KeyCode::Char('+')) => Some(ZoomIn),
|
||||||
key!(KeyCode::Char('=')) => Some(Self::ZoomOut),
|
key!(KeyCode::Char('=')) => Some(ZoomOut),
|
||||||
key!(KeyCode::Char('_')) => Some(Self::ZoomOut),
|
key!(KeyCode::Char('_')) => Some(ZoomOut),
|
||||||
key!(KeyCode::Char('-')) => Some(Self::ZoomOut),
|
key!(KeyCode::Char('-')) => Some(ZoomOut),
|
||||||
key!(KeyCode::Char('<')) => Some(Self::MoveBack),
|
key!(KeyCode::Char('<')) => Some(MoveBack),
|
||||||
key!(KeyCode::Char('>')) => Some(Self::MoveForward),
|
key!(KeyCode::Char('>')) => Some(MoveForward),
|
||||||
key!(KeyCode::Char('c')) => Some(Self::RandomColor),
|
key!(KeyCode::Char('c')) => Some(RandomColor),
|
||||||
key!(KeyCode::Char('s')) => Some(Self::Put),
|
key!(KeyCode::Char('s')) => Some(Put),
|
||||||
key!(KeyCode::Char('g')) => Some(Self::Get),
|
key!(KeyCode::Char('g')) => Some(Get),
|
||||||
key!(Ctrl-KeyCode::Char('a')) => Some(Self::AddScene),
|
key!(KeyCode::Char('e')) => Some(Edit),
|
||||||
key!(Ctrl-KeyCode::Char('t')) => Some(Self::AddTrack),
|
key!(Ctrl-KeyCode::Char('a')) => Some(AddScene),
|
||||||
key!(KeyCode::Char('l')) => Some(Self::ToggleLoop),
|
key!(Ctrl-KeyCode::Char('t')) => Some(AddTrack),
|
||||||
key!(KeyCode::Up) => Some(Self::GoUp),
|
key!(KeyCode::Char('l')) => Some(ToggleLoop),
|
||||||
key!(KeyCode::Down) => Some(Self::GoDown),
|
key!(KeyCode::Up) => Some(GoUp),
|
||||||
key!(KeyCode::Left) => Some(Self::GoLeft),
|
key!(KeyCode::Down) => Some(GoDown),
|
||||||
key!(KeyCode::Right) => Some(Self::GoRight),
|
key!(KeyCode::Left) => Some(GoLeft),
|
||||||
|
key!(KeyCode::Right) => Some(GoRight),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -670,3 +675,41 @@ impl MatchInput<Tui, Arrangement<Tui>> for ArrangementCommand {
|
||||||
//Ok(Some(true))
|
//Ok(Some(true))
|
||||||
//}
|
//}
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
trait ArrangerTheme<E: Engine> {
|
||||||
|
fn border_bg () -> Color;
|
||||||
|
fn border_fg (focused: bool) -> Color;
|
||||||
|
fn title_fg (focused: bool) -> Color;
|
||||||
|
fn separator_fg (focused: bool) -> Color;
|
||||||
|
fn hotkey_fg () -> Color;
|
||||||
|
fn mode_bg () -> Color;
|
||||||
|
fn mode_fg () -> Color;
|
||||||
|
fn status_bar_bg () -> Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrangerTheme<Tui> for Arranger<Tui> {
|
||||||
|
fn border_bg () -> Color {
|
||||||
|
Color::Rgb(40, 50, 30)
|
||||||
|
}
|
||||||
|
fn border_fg (focused: bool) -> Color {
|
||||||
|
if focused { Color::Rgb(100, 110, 40) } else { Color::Rgb(70, 80, 50) }
|
||||||
|
}
|
||||||
|
fn title_fg (focused: bool) -> Color {
|
||||||
|
if focused { Color::Rgb(150, 160, 90) } else { Color::Rgb(120, 130, 100) }
|
||||||
|
}
|
||||||
|
fn separator_fg (_: bool) -> Color {
|
||||||
|
Color::Rgb(0, 0, 0)
|
||||||
|
}
|
||||||
|
fn hotkey_fg () -> Color {
|
||||||
|
Color::Rgb(255, 255, 0)
|
||||||
|
}
|
||||||
|
fn mode_bg () -> Color {
|
||||||
|
Color::Rgb(150, 160, 90)
|
||||||
|
}
|
||||||
|
fn mode_fg () -> Color {
|
||||||
|
Color::Rgb(255, 255, 255)
|
||||||
|
}
|
||||||
|
fn status_bar_bg () -> Color {
|
||||||
|
Color::Rgb(28, 35, 25)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,15 @@ impl Handle<Tui> for Sequencer<Tui> {
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(command) = SequencerCommand::match_input(self, i) {
|
if let Some(command) = SequencerCommand::input_to_command(self, i) {
|
||||||
let _undo = command.execute(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, Sequencer<Tui>> for SequencerCommand {
|
impl InputToCommand<Tui, Sequencer<Tui>> for SequencerCommand {
|
||||||
fn match_input (state: &Sequencer<Tui>, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &Sequencer<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
use SequencerCommand::*;
|
use SequencerCommand::*;
|
||||||
use FocusCommand::*;
|
use FocusCommand::*;
|
||||||
match input.event() {
|
match input.event() {
|
||||||
|
|
@ -45,15 +45,15 @@ impl MatchInput<Tui, Sequencer<Tui>> for SequencerCommand {
|
||||||
key!(KeyCode::Char(' ')) => Some(Transport(TransportCommand::PlayToggle)),
|
key!(KeyCode::Char(' ')) => Some(Transport(TransportCommand::PlayToggle)),
|
||||||
_ => match state.focused() {
|
_ => match state.focused() {
|
||||||
SequencerFocus::Transport => if let Some(t) = state.transport.as_ref() {
|
SequencerFocus::Transport => if let Some(t) = state.transport.as_ref() {
|
||||||
TransportCommand::match_input(&*t.read().unwrap(), input).map(Transport)
|
TransportCommand::input_to_command(&*t.read().unwrap(), input).map(Transport)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
SequencerFocus::PhrasePool =>
|
SequencerFocus::PhrasePool =>
|
||||||
PhrasePoolCommand::match_input(&*state.phrases.read().unwrap(), input)
|
PhrasePoolCommand::input_to_command(&*state.phrases.read().unwrap(), input)
|
||||||
.map(Phrases),
|
.map(Phrases),
|
||||||
SequencerFocus::PhraseEditor =>
|
SequencerFocus::PhraseEditor =>
|
||||||
PhraseEditorCommand::match_input(&state.editor, input)
|
PhraseEditorCommand::input_to_command(&state.editor, input)
|
||||||
.map(Editor),
|
.map(Editor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,15 +100,15 @@ impl Content for PhrasePool<Tui> {
|
||||||
}
|
}
|
||||||
impl Handle<Tui> for PhrasePool<Tui> {
|
impl Handle<Tui> for PhrasePool<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = PhrasePoolCommand::match_input(self, from) {
|
if let Some(command) = PhrasePoolCommand::input_to_command(self, from) {
|
||||||
let _undo = command.execute(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, PhrasePool<Tui>> for PhrasePoolCommand {
|
impl InputToCommand<Tui, PhrasePool<Tui>> for PhrasePoolCommand {
|
||||||
fn match_input (state: &PhrasePool<Tui>, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (state: &PhrasePool<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
match input.event() {
|
match input.event() {
|
||||||
key!(KeyCode::Up) => Some(Self::Prev),
|
key!(KeyCode::Up) => Some(Self::Prev),
|
||||||
key!(KeyCode::Down) => Some(Self::Next),
|
key!(KeyCode::Down) => Some(Self::Next),
|
||||||
|
|
@ -122,17 +122,17 @@ impl MatchInput<Tui, PhrasePool<Tui>> for PhrasePoolCommand {
|
||||||
key!(KeyCode::Char('n')) => Some(Self::Rename(PhraseRenameCommand::Begin)),
|
key!(KeyCode::Char('n')) => Some(Self::Rename(PhraseRenameCommand::Begin)),
|
||||||
key!(KeyCode::Char('t')) => Some(Self::Length(PhraseLengthCommand::Begin)),
|
key!(KeyCode::Char('t')) => Some(Self::Length(PhraseLengthCommand::Begin)),
|
||||||
_ => match state.mode {
|
_ => match state.mode {
|
||||||
Some(PhrasePoolMode::Rename(..)) => PhraseRenameCommand::match_input(state, input)
|
Some(PhrasePoolMode::Rename(..)) => PhraseRenameCommand::input_to_command(state, input)
|
||||||
.map(Self::Rename),
|
.map(Self::Rename),
|
||||||
Some(PhrasePoolMode::Length(..)) => PhraseLengthCommand::match_input(state, input)
|
Some(PhrasePoolMode::Length(..)) => PhraseLengthCommand::input_to_command(state, input)
|
||||||
.map(Self::Length),
|
.map(Self::Length),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, PhrasePool<Tui>> for PhraseRenameCommand {
|
impl InputToCommand<Tui, PhrasePool<Tui>> for PhraseRenameCommand {
|
||||||
fn match_input (_: &PhrasePool<Tui>, from: &TuiInput) -> Option<Self> {
|
fn input_to_command (_: &PhrasePool<Tui>, from: &TuiInput) -> Option<Self> {
|
||||||
match from.event() {
|
match from.event() {
|
||||||
key!(KeyCode::Backspace) => Some(Self::Backspace),
|
key!(KeyCode::Backspace) => Some(Self::Backspace),
|
||||||
key!(KeyCode::Enter) => Some(Self::Confirm),
|
key!(KeyCode::Enter) => Some(Self::Confirm),
|
||||||
|
|
@ -142,8 +142,8 @@ impl MatchInput<Tui, PhrasePool<Tui>> for PhraseRenameCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, PhrasePool<Tui>> for PhraseLengthCommand {
|
impl InputToCommand<Tui, PhrasePool<Tui>> for PhraseLengthCommand {
|
||||||
fn match_input (_: &PhrasePool<Tui>, from: &TuiInput) -> Option<Self> {
|
fn input_to_command (_: &PhrasePool<Tui>, from: &TuiInput) -> Option<Self> {
|
||||||
match from.event() {
|
match from.event() {
|
||||||
key!(KeyCode::Up) => Some(Self::Inc),
|
key!(KeyCode::Up) => Some(Self::Inc),
|
||||||
key!(KeyCode::Down) => Some(Self::Dec),
|
key!(KeyCode::Down) => Some(Self::Dec),
|
||||||
|
|
@ -307,10 +307,15 @@ impl Content for PhraseEditor<Tui> {
|
||||||
//note_clamp.unwrap_or(0),
|
//note_clamp.unwrap_or(0),
|
||||||
//);
|
//);
|
||||||
}
|
}
|
||||||
|
let upper_right = if let Some(phrase) = phrase {
|
||||||
|
format!("┤Length: {}├", phrase.read().unwrap().length)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
lay!(
|
lay!(
|
||||||
content,
|
content,
|
||||||
TuiStyle::fg(upper_left.to_string(), title_color).push_x(1).align_nw().fill_xy(),
|
TuiStyle::fg(upper_left.to_string(), title_color).push_x(1).align_nw().fill_xy(),
|
||||||
//TuiStyle::fg(upper_right.to_string(), title_color).pull_x(1).align_ne().fill_xy(),
|
TuiStyle::fg(upper_right.to_string(), title_color).pull_x(1).align_ne().fill_xy(),
|
||||||
TuiStyle::fg(lower_right.to_string(), title_color).pull_x(1).align_se().fill_xy(),
|
TuiStyle::fg(lower_right.to_string(), title_color).pull_x(1).align_se().fill_xy(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -456,15 +461,15 @@ pub(crate) fn keys_vert () -> Buffer {
|
||||||
}
|
}
|
||||||
impl Handle<Tui> for PhraseEditor<Tui> {
|
impl Handle<Tui> for PhraseEditor<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = PhraseEditorCommand::match_input(self, from) {
|
if let Some(command) = PhraseEditorCommand::input_to_command(self, from) {
|
||||||
let _undo = command.execute(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, PhraseEditor<Tui>> for PhraseEditorCommand {
|
impl InputToCommand<Tui, PhraseEditor<Tui>> for PhraseEditorCommand {
|
||||||
fn match_input (_: &PhraseEditor<Tui>, from: &TuiInput) -> Option<Self> {
|
fn input_to_command (_: &PhraseEditor<Tui>, from: &TuiInput) -> Option<Self> {
|
||||||
match from.event() {
|
match from.event() {
|
||||||
key!(KeyCode::Char('`')) => Some(Self::ToggleDirection),
|
key!(KeyCode::Char('`')) => Some(Self::ToggleDirection),
|
||||||
key!(KeyCode::Enter) => Some(Self::EnterEditMode),
|
key!(KeyCode::Enter) => Some(Self::EnterEditMode),
|
||||||
|
|
|
||||||
|
|
@ -47,15 +47,15 @@ impl TransportToolbarFocus {
|
||||||
}
|
}
|
||||||
impl Handle<Tui> for TransportToolbar<Tui> {
|
impl Handle<Tui> for TransportToolbar<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if let Some(command) = TransportCommand::match_input(self, from) {
|
if let Some(command) = TransportCommand::input_to_command(self, from) {
|
||||||
let _undo = command.execute(self)?;
|
let _undo = command.execute(self)?;
|
||||||
return Ok(Some(true))
|
return Ok(Some(true))
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl MatchInput<Tui, TransportToolbar<Tui>> for TransportCommand {
|
impl InputToCommand<Tui, TransportToolbar<Tui>> for TransportCommand {
|
||||||
fn match_input (_: &TransportToolbar<Tui>, input: &TuiInput) -> Option<Self> {
|
fn input_to_command (_: &TransportToolbar<Tui>, input: &TuiInput) -> Option<Self> {
|
||||||
match input.event() {
|
match input.event() {
|
||||||
key!(KeyCode::Char(' ')) => Some(Self::FocusPrev),
|
key!(KeyCode::Char(' ')) => Some(Self::FocusPrev),
|
||||||
key!(Shift-KeyCode::Char(' ')) => Some(Self::FocusPrev),
|
key!(Shift-KeyCode::Char(' ')) => Some(Self::FocusPrev),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue