mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
remove VerticalArrangerGrid/Cursor
This commit is contained in:
parent
a4269337bc
commit
0b71a57ead
4 changed files with 196 additions and 205 deletions
|
|
@ -62,7 +62,7 @@ pub struct Scene {
|
||||||
#[derive(PartialEq, Clone, Copy)]
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
/// Represents the current user selection in the arranger
|
/// Represents the current user selection in the arranger
|
||||||
pub enum ArrangementFocus {
|
pub enum ArrangementFocus {
|
||||||
/** The whole mix is selected */
|
/// The whole mix is selected
|
||||||
Mix,
|
Mix,
|
||||||
/// A track is selected.
|
/// A track is selected.
|
||||||
Track(usize),
|
Track(usize),
|
||||||
|
|
@ -77,19 +77,27 @@ pub enum ArrangementViewMode {
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical(usize),
|
Vertical(usize),
|
||||||
}
|
}
|
||||||
/// A collection of phrases to play on each track.
|
/// Arrangement, rendered vertically (session/grid mode).
|
||||||
pub struct VerticalArranger<'a, E: Engine>(
|
pub struct VerticalArranger<'a, E: Engine>(
|
||||||
pub &'a Arrangement<E>, pub usize
|
pub &'a Arrangement<E>, pub usize
|
||||||
);
|
);
|
||||||
pub struct VerticalArrangerGrid<'a>(
|
/// Arrangement, rendered horizontally (arrangement/track mode).
|
||||||
pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)]
|
|
||||||
);
|
|
||||||
pub struct VerticalArrangerCursor<'a>(
|
|
||||||
pub bool, pub ArrangementFocus, pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)],
|
|
||||||
);
|
|
||||||
pub struct HorizontalArranger<'a, E: Engine>(
|
pub struct HorizontalArranger<'a, E: Engine>(
|
||||||
pub &'a Arrangement<E>
|
pub &'a Arrangement<E>
|
||||||
);
|
);
|
||||||
|
impl<E: Engine> Arranger<E> {
|
||||||
|
pub fn edit_phrase (&mut self) {
|
||||||
|
self.editor.phrase = self.arrangement.phrase().clone();
|
||||||
|
self.focus(ArrangerFocus::PhraseEditor);
|
||||||
|
}
|
||||||
|
pub fn toggle_play (&mut self) -> Perhaps<bool> {
|
||||||
|
match self.transport {
|
||||||
|
Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; },
|
||||||
|
None => { return Ok(None) }
|
||||||
|
}
|
||||||
|
Ok(Some(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Focus layout of arranger app
|
/// Focus layout of arranger app
|
||||||
impl<E: Engine> FocusGrid<ArrangerFocus> for Arranger<E> {
|
impl<E: Engine> FocusGrid<ArrangerFocus> for Arranger<E> {
|
||||||
fn cursor (&self) -> (usize, usize) { self.focus_cursor }
|
fn cursor (&self) -> (usize, usize) { self.focus_cursor }
|
||||||
|
|
@ -175,13 +183,53 @@ impl<E: Engine> Arrangement<E> {
|
||||||
pub fn is_last_row (&self) -> bool {
|
pub fn is_last_row (&self) -> bool {
|
||||||
let selected = self.selected;
|
let selected = self.selected;
|
||||||
(self.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected {
|
(self.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected {
|
||||||
ArrangementFocus::Scene(s) =>
|
ArrangementFocus::Scene(s) => s == self.scenes.len() - 1,
|
||||||
s == self.scenes.len() - 1,
|
ArrangementFocus::Clip(_, s) => s == self.scenes.len() - 1,
|
||||||
ArrangementFocus::Clip(_, s) =>
|
|
||||||
s == self.scenes.len() - 1,
|
|
||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn toggle_loop (&mut self) {
|
||||||
|
if let Some(phrase) = self.phrase() {
|
||||||
|
phrase.write().unwrap().toggle_loop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn go_up (&mut self) {
|
||||||
|
match self.mode {
|
||||||
|
ArrangementViewMode::Horizontal => self.track_prev(),
|
||||||
|
_ => self.scene_prev(),
|
||||||
|
};
|
||||||
|
self.show_phrase();
|
||||||
|
}
|
||||||
|
pub fn go_down (&mut self) {
|
||||||
|
match self.mode {
|
||||||
|
ArrangementViewMode::Horizontal => self.track_next(),
|
||||||
|
_ => self.scene_next(),
|
||||||
|
};
|
||||||
|
self.show_phrase();
|
||||||
|
}
|
||||||
|
pub fn go_left (&mut self) {
|
||||||
|
match self.mode {
|
||||||
|
ArrangementViewMode::Horizontal => self.scene_prev(),
|
||||||
|
_ => self.track_prev(),
|
||||||
|
};
|
||||||
|
self.show_phrase();
|
||||||
|
}
|
||||||
|
pub fn go_right (&mut self) {
|
||||||
|
match self.mode {
|
||||||
|
ArrangementViewMode::Horizontal => self.scene_next(),
|
||||||
|
_ => self.track_next(),
|
||||||
|
};
|
||||||
|
self.show_phrase();
|
||||||
|
}
|
||||||
|
pub fn move_back (&mut self) {
|
||||||
|
todo!("arrangement: move back")
|
||||||
|
}
|
||||||
|
pub fn move_forward (&mut self) {
|
||||||
|
todo!("arrangement: move forward")
|
||||||
|
}
|
||||||
|
pub fn rename_selected (&mut self) {
|
||||||
|
todo!("arrangement: rename selected")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Methods for tracks in arrangement
|
/// Methods for tracks in arrangement
|
||||||
impl<E: Engine> Arrangement<E> {
|
impl<E: Engine> Arrangement<E> {
|
||||||
|
|
|
||||||
|
|
@ -2,51 +2,51 @@ use crate::*;
|
||||||
/// Handle top-level events in standalone arranger.
|
/// Handle top-level events in standalone arranger.
|
||||||
impl Handle<Tui> for Arranger<Tui> {
|
impl Handle<Tui> for Arranger<Tui> {
|
||||||
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
fn handle (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
if !match self.focused() {
|
if !self.handle_focused(from)?.unwrap_or(false) {
|
||||||
ArrangerFocus::Transport => self.transport.handle(from)?,
|
|
||||||
ArrangerFocus::PhrasePool => self.phrases.handle(from)?,
|
|
||||||
ArrangerFocus::PhraseEditor => self.editor.handle(from)?,
|
|
||||||
ArrangerFocus::Arrangement => {
|
|
||||||
let mut handle_phrase = ||{
|
|
||||||
let result = self.phrases.handle(from);
|
|
||||||
self.arrangement.phrase_put();
|
|
||||||
result
|
|
||||||
};
|
|
||||||
match from.event() {
|
|
||||||
key!(KeyCode::Char('a')) => handle_phrase()?,
|
|
||||||
key!(KeyCode::Char('c')) => handle_phrase()?,
|
|
||||||
key!(KeyCode::Char('i')) => handle_phrase()?,
|
|
||||||
key!(KeyCode::Char('d')) => handle_phrase()?,
|
|
||||||
_ => self.arrangement.handle(from)?
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}.unwrap_or(false) {
|
|
||||||
match from.event() {
|
match from.event() {
|
||||||
// Tab navigation
|
|
||||||
key!(KeyCode::Tab) => { self.focus_next(); },
|
key!(KeyCode::Tab) => { self.focus_next(); },
|
||||||
key!(Shift-KeyCode::Tab) => { self.focus_prev(); },
|
key!(Shift-KeyCode::Tab) => { self.focus_prev(); },
|
||||||
key!(KeyCode::BackTab) => { self.focus_prev(); },
|
key!(KeyCode::BackTab) => { self.focus_prev(); },
|
||||||
key!(Shift-KeyCode::BackTab) => { self.focus_prev(); },
|
key!(Shift-KeyCode::BackTab) => { self.focus_prev(); },
|
||||||
// Directional navigation
|
|
||||||
key!(KeyCode::Up) => { self.focus_up(); },
|
key!(KeyCode::Up) => { self.focus_up(); },
|
||||||
key!(KeyCode::Down) => { self.focus_down(); },
|
key!(KeyCode::Down) => { self.focus_down(); },
|
||||||
key!(KeyCode::Left) => { self.focus_left(); },
|
key!(KeyCode::Left) => { self.focus_left(); },
|
||||||
key!(KeyCode::Right) => { self.focus_right(); },
|
key!(KeyCode::Right) => { self.focus_right(); },
|
||||||
key!(KeyCode::Char('e')) => {
|
key!(KeyCode::Char('e')) => { self.edit_phrase(); },
|
||||||
self.editor.phrase = self.arrangement.phrase().clone();
|
key!(KeyCode::Char(' ')) => { self.toggle_play(); },
|
||||||
self.focus(ArrangerFocus::PhraseEditor);
|
_ => return Ok(None)
|
||||||
}
|
|
||||||
// Global play/pause binding
|
|
||||||
key!(KeyCode::Char(' ')) => match self.transport {
|
|
||||||
Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; },
|
|
||||||
None => { return Ok(None) }
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(Some(true))
|
Ok(Some(true))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl Arranger<Tui> {
|
||||||
|
/// Helper for event passthru to focused component
|
||||||
|
fn handle_focused (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
match self.focused() {
|
||||||
|
ArrangerFocus::Transport => self.transport.handle(from),
|
||||||
|
ArrangerFocus::PhrasePool => self.phrases.handle(from),
|
||||||
|
ArrangerFocus::PhraseEditor => self.editor.handle(from),
|
||||||
|
ArrangerFocus::Arrangement => self.handle_arrangement(from),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Helper for phrase event passthru when arrangement is focused
|
||||||
|
fn handle_arrangement (&mut self, from: &TuiInput) -> Perhaps<bool> {
|
||||||
|
let mut handle_phrase = ||{
|
||||||
|
let result = self.phrases.handle(from);
|
||||||
|
self.arrangement.phrase_put();
|
||||||
|
result
|
||||||
|
};
|
||||||
|
match from.event() {
|
||||||
|
key!(KeyCode::Char('a')) => handle_phrase(),
|
||||||
|
key!(KeyCode::Char('c')) => handle_phrase(),
|
||||||
|
key!(KeyCode::Char('i')) => handle_phrase(),
|
||||||
|
key!(KeyCode::Char('d')) => handle_phrase(),
|
||||||
|
_ => self.arrangement.handle(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// 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> {
|
||||||
match from.event() {
|
match from.event() {
|
||||||
|
|
@ -55,46 +55,18 @@ impl Handle<Tui> for Arrangement<Tui> {
|
||||||
key!(KeyCode::Enter) => { self.activate(); },
|
key!(KeyCode::Enter) => { self.activate(); },
|
||||||
key!(KeyCode::Char('.')) => { self.increment(); },
|
key!(KeyCode::Char('.')) => { self.increment(); },
|
||||||
key!(KeyCode::Char(',')) => { self.decrement(); },
|
key!(KeyCode::Char(',')) => { self.decrement(); },
|
||||||
//key!(KeyCode::Char('<')) => { self.move_back(); },
|
key!(KeyCode::Char('<')) => { self.move_back(); },
|
||||||
//key!(KeyCode::Char('>')) => { self.move_forward(); },
|
key!(KeyCode::Char('>')) => { self.move_forward(); },
|
||||||
// Put selected phrase at position
|
|
||||||
key!(KeyCode::Char('s')) => { self.phrase_put(); },
|
key!(KeyCode::Char('s')) => { self.phrase_put(); },
|
||||||
// Select phrase at current position
|
|
||||||
key!(KeyCode::Char('g')) => { self.phrase_get(); },
|
key!(KeyCode::Char('g')) => { self.phrase_get(); },
|
||||||
key!(Ctrl-KeyCode::Char('a')) => { self.scene_add(None, None)?; },
|
key!(Ctrl-KeyCode::Char('a')) => { self.scene_add(None, None)?; },
|
||||||
key!(Ctrl-KeyCode::Char('t')) => { self.track_add(None, None)?; },
|
key!(Ctrl-KeyCode::Char('t')) => { self.track_add(None, None)?; },
|
||||||
key!(KeyCode::Char('n')) => { todo!("rename selected"); },
|
key!(KeyCode::Char('n')) => { self.rename_selected(); },
|
||||||
key!(KeyCode::Char('l')) => if let Some(phrase) = self.phrase() {
|
key!(KeyCode::Char('l')) => { self.toggle_loop() },
|
||||||
phrase.write().unwrap().toggle_loop()
|
key!(KeyCode::Up) => { self.go_up() },
|
||||||
},
|
key!(KeyCode::Down) => { self.go_down() },
|
||||||
key!(KeyCode::Up) => {
|
key!(KeyCode::Left) => { self.go_left() },
|
||||||
match self.mode {
|
key!(KeyCode::Right) => { self.go_right() },
|
||||||
ArrangementViewMode::Horizontal => self.track_prev(),
|
|
||||||
_ => self.scene_prev(),
|
|
||||||
};
|
|
||||||
self.show_phrase();
|
|
||||||
},
|
|
||||||
key!(KeyCode::Down) => {
|
|
||||||
match self.mode {
|
|
||||||
ArrangementViewMode::Horizontal => self.track_next(),
|
|
||||||
_ => self.scene_next(),
|
|
||||||
};
|
|
||||||
self.show_phrase();
|
|
||||||
},
|
|
||||||
key!(KeyCode::Left) => {
|
|
||||||
match self.mode {
|
|
||||||
ArrangementViewMode::Horizontal => self.scene_prev(),
|
|
||||||
_ => self.track_prev(),
|
|
||||||
};
|
|
||||||
self.show_phrase();
|
|
||||||
},
|
|
||||||
key!(KeyCode::Right) => {
|
|
||||||
match self.mode {
|
|
||||||
ArrangementViewMode::Horizontal => self.scene_next(),
|
|
||||||
_ => self.track_next(),
|
|
||||||
};
|
|
||||||
self.show_phrase();
|
|
||||||
},
|
|
||||||
_ => return Ok(None)
|
_ => return Ok(None)
|
||||||
}
|
}
|
||||||
Ok(Some(true))
|
Ok(Some(true))
|
||||||
|
|
|
||||||
|
|
@ -79,13 +79,98 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
||||||
//let height = rows.last().map(|(w,y)|(y+w)/PPQ).unwrap_or(16);
|
//let height = rows.last().map(|(w,y)|(y+w)/PPQ).unwrap_or(16);
|
||||||
let tracks: &[ArrangementTrack<Tui>] = state.tracks.as_ref();
|
let tracks: &[ArrangementTrack<Tui>] = state.tracks.as_ref();
|
||||||
let scenes: &[Scene] = state.scenes.as_ref();
|
let scenes: &[Scene] = state.scenes.as_ref();
|
||||||
let offset = 3 + Scene::longest_name(scenes) as u16;
|
let offset = 3 + Scene::longest_name(scenes) as u16;
|
||||||
let content = Layers::new(move |add|{
|
let title_fg = if self.0.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)};
|
||||||
|
let border_fg = if self.0.focused{Color::Rgb(100, 110, 40)}else{Color::Rgb( 70, 80, 50)};
|
||||||
|
let border_bg = Color::Rgb(40, 50, 30);
|
||||||
|
let border = Lozenge(Style::default().bg(border_bg).fg(border_fg));
|
||||||
|
let content = Layers::new(move |add|{
|
||||||
let rows: &[(usize, usize)] = rows.as_ref();
|
let rows: &[(usize, usize)] = rows.as_ref();
|
||||||
let cols: &[(usize, usize)] = cols.as_ref();
|
let cols: &[(usize, usize)] = cols.as_ref();
|
||||||
|
|
||||||
|
add(&CustomWidget::new(|_|Ok(Some([0,0])), move|to: &mut TuiOutput|{
|
||||||
|
let area = to.area();
|
||||||
|
let style = Some(Style::default().fg(COLOR_SEPARATOR));
|
||||||
|
for (_, x) in cols.iter() {
|
||||||
|
let x = offset + area.x() + *x as u16 - 1;
|
||||||
|
for y in area.y()..area.y2() {
|
||||||
|
to.blit(&"▎", x, y, style);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (_, y) in rows.iter() {
|
||||||
|
let y = area.y() + (*y / PPQ) as u16 + 1;
|
||||||
|
if y >= to.buffer.area.height {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for x in area.x()..area.x2().saturating_sub(2) {
|
||||||
|
if x < to.buffer.area.x && y < to.buffer.area.y {
|
||||||
|
let cell = to.buffer.get_mut(x, y);
|
||||||
|
cell.modifier = Modifier::UNDERLINED;
|
||||||
|
cell.underline_color = COLOR_SEPARATOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
add(&CustomWidget::new(|_|Ok(Some([0,0])), move|to: &mut TuiOutput|{
|
||||||
|
let area = to.area();
|
||||||
|
let focused = state.focused;
|
||||||
|
let selected = state.selected;
|
||||||
|
let get_track_area = |t: usize| [
|
||||||
|
offset + area.x() + cols[t].1 as u16 - 1, area.y(),
|
||||||
|
cols[t].0 as u16, area.h(),
|
||||||
|
];
|
||||||
|
let get_scene_area = |s: usize| [
|
||||||
|
area.x(), 2 + area.y() + (rows[s].1 / PPQ) as u16,
|
||||||
|
area.w(), (rows[s].0 / PPQ) as u16
|
||||||
|
];
|
||||||
|
let get_clip_area = |t: usize, s: usize| [
|
||||||
|
offset+area.x()+cols[t].1 as u16 - 1, 2+area.y()+(rows[s].1/PPQ) as u16,
|
||||||
|
cols[t].0 as u16, (rows[s].0 / PPQ) as u16
|
||||||
|
];
|
||||||
|
let mut track_area: Option<[u16;4]> = None;
|
||||||
|
let mut scene_area: Option<[u16;4]> = None;
|
||||||
|
let mut clip_area: Option<[u16;4]> = None;
|
||||||
|
let area = match selected {
|
||||||
|
ArrangementFocus::Mix => {
|
||||||
|
if focused { to.fill_bg(area, Color::Rgb(40, 50, 30)); }
|
||||||
|
area
|
||||||
|
},
|
||||||
|
ArrangementFocus::Track(t) => { track_area = Some(get_track_area(t)); area },
|
||||||
|
ArrangementFocus::Scene(s) => { scene_area = Some(get_scene_area(s)); area },
|
||||||
|
ArrangementFocus::Clip(t, s) => {
|
||||||
|
track_area = Some(get_track_area(t));
|
||||||
|
scene_area = Some(get_scene_area(s));
|
||||||
|
clip_area = Some(get_clip_area(t, s));
|
||||||
|
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 + width, y, 1, height], Color::Rgb(70, 80, 50));
|
||||||
|
}
|
||||||
|
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 + height - 1, area.w(), 1], Color::Rgb(70, 80, 50));
|
||||||
|
}
|
||||||
|
if focused {
|
||||||
|
if let Some(clip_area) = clip_area {
|
||||||
|
to.render_in(clip_area, &CORNERS)?;
|
||||||
|
to.fill_bg(clip_area, Color::Rgb(40, 50, 30));
|
||||||
|
} else if let Some(track_area) = track_area {
|
||||||
|
to.render_in(track_area.clip_h(2), &CORNERS)?;
|
||||||
|
to.fill_bg(track_area, Color::Rgb(40, 50, 30));
|
||||||
|
} else if let Some(scene_area) = scene_area {
|
||||||
|
to.render_in(scene_area.clip_w(offset-1), &CORNERS)?;
|
||||||
|
to.fill_bg(scene_area, Color::Rgb(40, 50, 30));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}))?;
|
||||||
|
|
||||||
let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) =>
|
let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) =>
|
||||||
(&format!(" {}", &track.name.read().unwrap()).as_str() as &dyn Widget<Engine = Tui>)
|
(&format!("▎{}", &track.name.read().unwrap()).as_str() as &dyn Widget<Engine = Tui>)
|
||||||
.min_xy(*w as u16, 2)
|
.min_xy(*w as u16, 2)
|
||||||
.bg(track.color)
|
.bg(track.color)
|
||||||
.push_x(offset - 1));
|
.push_x(offset - 1));
|
||||||
|
|
@ -126,123 +211,9 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
||||||
}).fixed_y(height)
|
}).fixed_y(height)
|
||||||
});
|
});
|
||||||
|
|
||||||
add(&VerticalArrangerGrid(offset, &rows, &cols))?;
|
add(&col!(track_titles, tracks_clips))
|
||||||
add(&VerticalArrangerCursor(state.focused, state.selected, offset, &cols, &rows))?;
|
}).bg(Color::Rgb(28, 35, 25)).border(border);
|
||||||
add(&col!(track_titles, tracks_clips))?;
|
lay!(content, TuiStyle::fg("Session", title_fg).push_x(1))
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.bg(Color::Rgb(28, 35, 25))
|
|
||||||
.border(Lozenge(Style::default()
|
|
||||||
.bg(Color::Rgb(40, 50, 30))
|
|
||||||
.fg(if self.0.focused {
|
|
||||||
Color::Rgb(100, 110, 40)
|
|
||||||
} else {
|
|
||||||
Color::Rgb(70, 80, 50)
|
|
||||||
})));
|
|
||||||
lay!(content, TuiStyle::fg("Session", if self.0.focused {
|
|
||||||
Color::Rgb(150, 160, 90)
|
|
||||||
} else {
|
|
||||||
Color::Rgb(120, 130, 100)
|
|
||||||
}).push_x(1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> Widget for VerticalArrangerGrid<'a> {
|
|
||||||
type Engine = Tui;
|
|
||||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
|
||||||
let area = to.area();
|
|
||||||
let Self(offset, rows, cols) = self;
|
|
||||||
let style = Some(Style::default().fg(COLOR_SEPARATOR));
|
|
||||||
for (_, x) in cols.iter() {
|
|
||||||
let x = offset + area.x() + *x as u16 - 1;
|
|
||||||
for y in area.y()..area.y2() {
|
|
||||||
to.blit(&"▎", x, y, style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (_, y) in rows.iter() {
|
|
||||||
let y = area.y() + (*y / PPQ) as u16 + 1;
|
|
||||||
if y >= to.buffer.area.height {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
for x in area.x()..area.x2().saturating_sub(2) {
|
|
||||||
if x < to.buffer.area.x && y < to.buffer.area.y {
|
|
||||||
let cell = to.buffer.get_mut(x, y);
|
|
||||||
cell.modifier = Modifier::UNDERLINED;
|
|
||||||
cell.underline_color = COLOR_SEPARATOR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> Widget for VerticalArrangerCursor<'a> {
|
|
||||||
type Engine = Tui;
|
|
||||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
|
||||||
let area = to.area();
|
|
||||||
let Self(focused, selected, offset, cols, rows) = *self;
|
|
||||||
let get_track_area = |t: usize| [
|
|
||||||
offset + area.x() + cols[t].1 as u16 - 1,
|
|
||||||
area.y(),
|
|
||||||
cols[t].0 as u16,
|
|
||||||
area.h()
|
|
||||||
];
|
|
||||||
let get_scene_area = |s: usize| [
|
|
||||||
area.x(),
|
|
||||||
2 + area.y() + (rows[s].1 / PPQ) as u16,
|
|
||||||
area.w(),
|
|
||||||
(rows[s].0 / PPQ) as u16
|
|
||||||
];
|
|
||||||
let get_clip_area = |t: usize, s: usize| [
|
|
||||||
offset + area.x() + cols[t].1 as u16 - 1,
|
|
||||||
2 + area.y() + (rows[s].1 / PPQ) as u16,
|
|
||||||
cols[t].0 as u16,
|
|
||||||
(rows[s].0 / PPQ) as u16
|
|
||||||
];
|
|
||||||
let mut track_area: Option<[u16;4]> = None;
|
|
||||||
let mut scene_area: Option<[u16;4]> = None;
|
|
||||||
let mut clip_area: Option<[u16;4]> = None;
|
|
||||||
let area = match selected {
|
|
||||||
ArrangementFocus::Mix => {
|
|
||||||
if focused {
|
|
||||||
to.fill_bg(area, Color::Rgb(40, 50, 30));
|
|
||||||
}
|
|
||||||
area
|
|
||||||
},
|
|
||||||
ArrangementFocus::Track(t) => {
|
|
||||||
track_area = Some(get_track_area(t));
|
|
||||||
area
|
|
||||||
},
|
|
||||||
ArrangementFocus::Scene(s) => {
|
|
||||||
scene_area = Some(get_scene_area(s));
|
|
||||||
area
|
|
||||||
},
|
|
||||||
ArrangementFocus::Clip(t, s) => {
|
|
||||||
track_area = Some(get_track_area(t));
|
|
||||||
scene_area = Some(get_scene_area(s));
|
|
||||||
clip_area = Some(get_clip_area(t, s));
|
|
||||||
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 + width, y, 1, height], Color::Rgb(70, 80, 50));
|
|
||||||
}
|
|
||||||
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 + height - 1, area.w(), 1], Color::Rgb(70, 80, 50));
|
|
||||||
}
|
|
||||||
if focused {
|
|
||||||
if let Some(clip_area) = clip_area {
|
|
||||||
to.render_in(clip_area, &CORNERS)?;
|
|
||||||
to.fill_bg(clip_area, Color::Rgb(40, 50, 30));
|
|
||||||
} else if let Some(track_area) = track_area {
|
|
||||||
to.render_in(track_area.clip_h(2), &CORNERS)?;
|
|
||||||
to.fill_bg(track_area, Color::Rgb(40, 50, 30));
|
|
||||||
} else if let Some(scene_area) = scene_area {
|
|
||||||
to.render_in(scene_area.clip_w(offset-1), &CORNERS)?;
|
|
||||||
to.fill_bg(scene_area, Color::Rgb(40, 50, 30));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> Content for HorizontalArranger<'a, Tui> {
|
impl<'a> Content for HorizontalArranger<'a, Tui> {
|
||||||
|
|
|
||||||
12
shell.nix
12
shell.nix
|
|
@ -1,23 +1,23 @@
|
||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
{pkgs?import<nixpkgs>{}}:let
|
{pkgs?import<nixpkgs>{}}:let
|
||||||
|
|
||||||
suil = pkgs.enableDebugging (pkgs.suil.overrideAttrs (a: b: {
|
#suil = pkgs.enableDebugging (pkgs.suil.overrideAttrs (a: b: {
|
||||||
dontStrip = true; separateDebugInfo = true;
|
#dontStrip = true; separateDebugInfo = true;
|
||||||
}));
|
#}));
|
||||||
|
|
||||||
in pkgs.mkShell {
|
in pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
pkg-config
|
pkg-config
|
||||||
freetype
|
freetype
|
||||||
libclang
|
libclang
|
||||||
bear
|
#bear
|
||||||
];
|
];
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
jack2
|
jack2
|
||||||
lilv
|
lilv
|
||||||
serd
|
serd
|
||||||
libclang
|
libclang
|
||||||
suil
|
#suil
|
||||||
glib
|
glib
|
||||||
gtk3
|
gtk3
|
||||||
];
|
];
|
||||||
|
|
@ -33,7 +33,7 @@ in pkgs.mkShell {
|
||||||
xorg.libXcursor
|
xorg.libXcursor
|
||||||
xorg.libXi
|
xorg.libXi
|
||||||
libxkbcommon
|
libxkbcommon
|
||||||
suil
|
#suil
|
||||||
# for Helm:
|
# for Helm:
|
||||||
alsa-lib
|
alsa-lib
|
||||||
curl
|
curl
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue