mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
ui thrashing
This commit is contained in:
parent
acb952736e
commit
20b7267225
18 changed files with 695 additions and 233 deletions
|
|
@ -28,7 +28,7 @@ fn cursor_down (state: &mut Sampler) -> Usually<bool> {
|
|||
fn trigger (state: &mut Sampler) -> Usually<bool> {
|
||||
for (i, sample) in state.samples.values().enumerate() {
|
||||
if i == state.cursor.0 {
|
||||
state.voices.push(sample.play(0))
|
||||
state.voices.push(sample.play(0, &100.into()))
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,21 @@ pub(crate) use ratatui::prelude::*;
|
|||
pub(crate) use ratatui::buffer::Cell;
|
||||
use ratatui::widgets::WidgetRef;
|
||||
|
||||
pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) {
|
||||
let Rect { x, y, width, height } = area;
|
||||
for y in y..y+height {
|
||||
if y >= buf.area.height {
|
||||
break
|
||||
}
|
||||
for x in x..x+width {
|
||||
if x >= buf.area.width {
|
||||
break
|
||||
}
|
||||
buf.get_mut(x, y).set_bg(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Blit {
|
||||
// Render something to X, Y coordinates in a buffer, ignoring width/height.
|
||||
fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>);
|
||||
|
|
|
|||
|
|
@ -81,8 +81,10 @@ pub fn main () -> Usually<()> {
|
|||
sample!(34, "808", "/home/user/Lab/Music/pak/808.wav"),
|
||||
sample!(35, "Kick1", "/home/user/Lab/Music/pak/kik.wav"),
|
||||
sample!(36, "Kick2", "/home/user/Lab/Music/pak/kik2.wav"),
|
||||
sample!(40, "Snare", "/home/user/Lab/Music/pak/sna.wav"),
|
||||
sample!(44, "Hihat", "/home/user/Lab/Music/pak/chh.wav"),
|
||||
sample!(38, "Snare1", "/home/user/Lab/Music/pak/sna.wav"),
|
||||
sample!(40, "Snare2", "/home/user/Lab/Music/pak/sna2.wav"),
|
||||
sample!(42, "Hihat", "/home/user/Lab/Music/pak/chh.wav"),
|
||||
sample!(44, "Hihat", "/home/user/Lab/Music/pak/chh2.wav"),
|
||||
])))?, |track, device|{
|
||||
device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
|
||||
Ok(())
|
||||
|
|
|
|||
12
src/model.rs
12
src/model.rs
|
|
@ -146,12 +146,6 @@ impl App {
|
|||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
||||
let name = name.ok_or_else(||format!("Scene {}", self.scenes.len() + 1))?;
|
||||
self.scenes.push(Scene::new(&name, vec![]));
|
||||
self.scene_cursor = self.scenes.len();
|
||||
Ok(&mut self.scenes[self.scene_cursor - 1])
|
||||
}
|
||||
pub fn add_track (
|
||||
&mut self,
|
||||
name: Option<&str>,
|
||||
|
|
@ -185,6 +179,12 @@ impl App {
|
|||
self.tracks.get_mut(id).map(|t|(id, t))
|
||||
} }
|
||||
}
|
||||
pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> {
|
||||
let name = name.ok_or_else(||format!("Scene {}", self.scenes.len() + 1))?;
|
||||
self.scenes.push(Scene::new(&name, vec![]));
|
||||
self.scene_cursor = self.scenes.len();
|
||||
Ok(&mut self.scenes[self.scene_cursor - 1])
|
||||
}
|
||||
pub fn scene (&self) -> Option<(usize, &Scene)> {
|
||||
match self.scene_cursor { 0 => None, _ => {
|
||||
let id = self.scene_cursor as usize - 1;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub struct Mixer {
|
|||
pub selected_track: usize,
|
||||
pub selected_column: usize,
|
||||
}
|
||||
render!(Mixer = crate::view::mixer::render);
|
||||
//render!(Mixer = crate::view::mixer::render);
|
||||
handle!(Mixer = crate::control::mixer::handle);
|
||||
process!(Mixer = process);
|
||||
|
||||
|
|
|
|||
|
|
@ -67,9 +67,9 @@ impl Sampler {
|
|||
fn process_midi_in (&mut self, scope: &ProcessScope) {
|
||||
for RawMidi { time, bytes } in self.ports.midi_ins.get("midi").unwrap().iter(scope) {
|
||||
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
|
||||
if let MidiMessage::NoteOn { ref key, .. } = message {
|
||||
if let MidiMessage::NoteOn { ref key, ref vel } = message {
|
||||
if let Some(sample) = self.samples.get(key) {
|
||||
self.voices.push(sample.play(time as usize));
|
||||
self.voices.push(sample.play(time as usize, vel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -143,11 +143,12 @@ impl Sample {
|
|||
pub fn new (name: &str, start: usize, end: usize, channels: Vec<Vec<f32>>) -> Arc<Self> {
|
||||
Arc::new(Self { name: name.to_string(), start, end, channels })
|
||||
}
|
||||
pub fn play (self: &Arc<Self>, after: usize) -> Voice {
|
||||
pub fn play (self: &Arc<Self>, after: usize, velocity: &u7) -> Voice {
|
||||
Voice {
|
||||
sample: self.clone(),
|
||||
after,
|
||||
position: self.start
|
||||
position: self.start,
|
||||
velocity: velocity.as_int() as f32 / 127.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +157,7 @@ pub struct Voice {
|
|||
pub sample: Arc<Sample>,
|
||||
pub after: usize,
|
||||
pub position: usize,
|
||||
pub velocity: f32,
|
||||
}
|
||||
|
||||
const BUFFER: [f32;64] = [0.0f32;64];
|
||||
|
|
@ -171,8 +173,8 @@ impl Iterator for Voice {
|
|||
let position = self.position;
|
||||
self.position = self.position + 1;
|
||||
return Some([
|
||||
self.sample.channels[0][position],
|
||||
self.sample.channels[0][position],
|
||||
self.sample.channels[0][position] * self.velocity,
|
||||
self.sample.channels[0][position] * self.velocity,
|
||||
])
|
||||
}
|
||||
None
|
||||
|
|
|
|||
161
src/view.rs
161
src/view.rs
|
|
@ -1,24 +1,97 @@
|
|||
pub mod chain;
|
||||
pub mod grid;
|
||||
pub mod layout;
|
||||
pub mod mixer;
|
||||
pub mod sampler;
|
||||
pub mod sequencer;
|
||||
pub mod transport;
|
||||
pub mod plugin;
|
||||
pub mod focus;
|
||||
pub mod border;
|
||||
|
||||
pub use self::border::*;
|
||||
pub use self::layout::*;
|
||||
pub use self::transport::TransportView;
|
||||
pub use self::grid::*;
|
||||
pub use self::focus::*;
|
||||
pub use self::chain::ChainView;
|
||||
pub use self::sequencer::SequencerView;
|
||||
|
||||
use crate::{render, App, core::*};
|
||||
|
||||
render!(App |self, buf, area| {
|
||||
let Rect { x, mut y, width, height } = area;
|
||||
let Rect { x, y, width, height } = area;
|
||||
let transport = self.draw_transport(buf, area)?;
|
||||
let y = y + transport.height;
|
||||
let grid = if self.grid_mode {
|
||||
self.draw_grid_horizontal(buf, Rect {
|
||||
x, y, width, height: height / 3
|
||||
})?
|
||||
} else {
|
||||
self.draw_grid_vertical(buf, Rect {
|
||||
x, y, width, height: height / 3
|
||||
})?
|
||||
};
|
||||
if self.section == 0 {
|
||||
QuarterV(if self.entered {
|
||||
Style::default().green()
|
||||
} else {
|
||||
Style::default().green().dim()
|
||||
}).draw(buf, grid)
|
||||
}
|
||||
let y = y + grid.height;
|
||||
if self.track_cursor > 0 {
|
||||
let chain = self.draw_chain(buf, Rect {
|
||||
x, y: y + height - height / 3 - 1, width, height: height / 3
|
||||
})?;
|
||||
if self.section == 1 {
|
||||
QuarterV(if self.entered {
|
||||
Style::default().green()
|
||||
} else {
|
||||
Style::default().green().dim()
|
||||
}).draw(buf, Rect { x, y, width, height: chain.height })
|
||||
}
|
||||
let phrase = self.draw_phrase(buf, Rect {
|
||||
x, y, width, height: height - height / 3
|
||||
})?;
|
||||
if self.section == 2 {
|
||||
QuarterV(if self.entered {
|
||||
Style::default().green()
|
||||
} else {
|
||||
Style::default().green().dim()
|
||||
}).draw(buf, phrase)
|
||||
}
|
||||
chain
|
||||
} else {
|
||||
self.draw_mixer(buf, Rect {
|
||||
x, y, width, height
|
||||
})?
|
||||
};
|
||||
if let Some(ref modal) = self.modal {
|
||||
modal.render(buf, area)?;
|
||||
}
|
||||
Ok(area)
|
||||
});
|
||||
|
||||
y = y + TransportView {
|
||||
impl App {
|
||||
fn draw_mixer (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let mut x = area.x;
|
||||
for track in self.tracks.iter() {
|
||||
track.name.blit(buf, x + 1, area.y, Some(Style::default().white().bold()));
|
||||
x = x + ChainView {
|
||||
focused: self.section == 1,
|
||||
track: Some(track),
|
||||
vertical: true,
|
||||
}
|
||||
.render(buf, Rect {
|
||||
x, y: area.y + 1, width: area.width, height: area.height - area.y - 1
|
||||
})?
|
||||
.width
|
||||
.max(track.name.len() as u16);
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
fn draw_transport (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
TransportView {
|
||||
timebase: &self.timebase,
|
||||
playing: *self.playing.as_ref().unwrap_or(&TransportState::Stopped),
|
||||
monitor: self.track().map(|t|t.1.monitoring).unwrap_or(false),
|
||||
|
|
@ -26,46 +99,39 @@ render!(App |self, buf, area| {
|
|||
overdub: self.track().map(|t|t.1.overdub).unwrap_or(false),
|
||||
frame: self.playhead,
|
||||
quant: self.quant,
|
||||
}.render(buf, area)?.height;
|
||||
|
||||
y = y + if self.grid_mode {
|
||||
}.render(buf, area)
|
||||
}
|
||||
fn draw_grid_horizontal (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
SceneGridViewHorizontal {
|
||||
buf,
|
||||
area: Rect { x, y, width, height: height / 3 },
|
||||
area,
|
||||
focused: self.section == 0,
|
||||
entered: self.entered,
|
||||
scenes: &self.scenes,
|
||||
tracks: &self.tracks,
|
||||
cursor: &(self.track_cursor, self.scene_cursor),
|
||||
}.draw()?
|
||||
} else {
|
||||
}.draw()
|
||||
}
|
||||
fn draw_grid_vertical (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
SceneGridViewVertical {
|
||||
buf,
|
||||
area: Rect { x, y, width, height: height / 3 },
|
||||
area,
|
||||
focused: self.section == 0,
|
||||
entered: self.entered,
|
||||
scenes: &self.scenes,
|
||||
tracks: &self.tracks,
|
||||
cursor: &(self.track_cursor, self.scene_cursor),
|
||||
}.draw()?
|
||||
}.height;
|
||||
|
||||
if self.track_cursor > 0 {
|
||||
|
||||
let track = self.tracks
|
||||
.get(self.track_cursor - 1)
|
||||
.unwrap();
|
||||
|
||||
let chain_area = Rect { x, y: y + height - height / 3 - 1, width, height: height / 3 };
|
||||
}.draw()
|
||||
}
|
||||
fn draw_chain (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
ChainView {
|
||||
focused: self.section == 1,
|
||||
track: Some(track),
|
||||
track: self.tracks.get(self.track_cursor - 1),
|
||||
vertical: false,
|
||||
}.render(buf, area)
|
||||
}
|
||||
.render(buf, chain_area)?;
|
||||
|
||||
fn draw_phrase (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let phrase = self.phrase();
|
||||
|
||||
let seq_area = SequencerView {
|
||||
phrase,
|
||||
focused: self.section == 2,
|
||||
|
|
@ -76,51 +142,14 @@ render!(App |self, buf, area| {
|
|||
time_zoom: self.time_zoom,
|
||||
note_cursor: self.note_cursor,
|
||||
note_start: self.note_start,
|
||||
}.render(buf, Rect { x, y, width, height: height - height / 3 })?;
|
||||
|
||||
}.render(buf, area)?;
|
||||
let track = self.tracks.get(self.track_cursor - 1).unwrap();
|
||||
if phrase.is_none() && self.section == 2 {
|
||||
let label = format!("[ENTER] Create new clip: {}", track.name);
|
||||
let x = x + seq_area.width / 2 - (label.len() / 2) as u16;
|
||||
let y = y + seq_area.height / 2;
|
||||
let x = area.x + seq_area.width / 2 - (label.len() / 2) as u16;
|
||||
let y = area.y + seq_area.height / 2;
|
||||
label.blit(buf, x, y, Some(Style::default().white()));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
let mut x = x;
|
||||
|
||||
for track in self.tracks.iter() {
|
||||
track.name.blit(buf, x + 1, y, Some(Style::default().white().bold()));
|
||||
x = x + ChainView {
|
||||
focused: self.section == 1,
|
||||
track: Some(track),
|
||||
vertical: true,
|
||||
}
|
||||
.render(buf, Rect { x, y: y + 1, width, height: height - y - 1 })?
|
||||
.width
|
||||
.max(track.name.len() as u16);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if let Some(ref modal) = self.modal {
|
||||
modal.render(buf, area)?;
|
||||
}
|
||||
|
||||
Ok(area)
|
||||
});
|
||||
|
||||
pub fn fill_bg (buf: &mut Buffer, area: Rect, color: Color) {
|
||||
let Rect { x, y, width, height } = area;
|
||||
for y in y..y+height {
|
||||
if y >= buf.area.height {
|
||||
break
|
||||
}
|
||||
for x in x..x+width {
|
||||
if x >= buf.area.width {
|
||||
break
|
||||
}
|
||||
buf.get_mut(x, y).set_bg(color);
|
||||
}
|
||||
Ok(seq_area)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
130
src/view/border.rs
Normal file
130
src/view/border.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
use crate::core::*;
|
||||
|
||||
pub trait BorderStyle {
|
||||
const NW: &'static str = "";
|
||||
const N: &'static str = "";
|
||||
const NE: &'static str = "";
|
||||
const E: &'static str = "";
|
||||
const SE: &'static str = "";
|
||||
const S: &'static str = "";
|
||||
const SW: &'static str = "";
|
||||
const W: &'static str = "";
|
||||
|
||||
fn draw (&self, buf: &mut Buffer, area: Rect) {
|
||||
self.draw_horizontal(buf, area);
|
||||
self.draw_vertical(buf, area);
|
||||
self.draw_corners(buf, area);
|
||||
}
|
||||
|
||||
fn draw_horizontal (&self, buf: &mut Buffer, area: Rect) {
|
||||
let style = self.style();
|
||||
for x in area.x..(area.x+area.width).saturating_sub(1) {
|
||||
Self::N.blit(buf, x, area.y, style);
|
||||
Self::S.blit(buf, x, area.y + area.height - 1, style);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_vertical (&self, buf: &mut Buffer, area: Rect) {
|
||||
let style = self.style();
|
||||
for y in area.y..(area.y+area.height).saturating_sub(1) {
|
||||
Self::W.blit(buf, area.x, y, style);
|
||||
Self::E.blit(buf, area.x + area.width - 1, y, style);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_corners (&self, buf: &mut Buffer, area: Rect) {
|
||||
let style = self.style();
|
||||
Self::NW.blit(buf, area.x, area.y, style);
|
||||
Self::NE.blit(buf, area.x + area.width - 1, area.y, style);
|
||||
Self::SW.blit(buf, area.x, area.y + area.height - 1, style);
|
||||
Self::SE.blit(buf, area.x + area.width - 1, area.y + area.height - 1, style);
|
||||
}
|
||||
|
||||
fn style (&self) -> Option<Style> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lozenge(pub Style);
|
||||
|
||||
impl BorderStyle for Lozenge {
|
||||
const N: &'static str = "─";
|
||||
const S: &'static str = "─";
|
||||
const NW: &'static str = "╭";
|
||||
const W: &'static str = "│";
|
||||
const SW: &'static str = "╰";
|
||||
const NE: &'static str = "╮";
|
||||
const E: &'static str = "│";
|
||||
const SE: &'static str = "╯";
|
||||
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Quarter(pub Style);
|
||||
|
||||
impl BorderStyle for Quarter {
|
||||
const N: &'static str = "▔";
|
||||
const S: &'static str = "▁";
|
||||
const NW: &'static str = "▎";
|
||||
const W: &'static str = "▎";
|
||||
const SW: &'static str = "▎";
|
||||
const NE: &'static str = "🮇";
|
||||
const E: &'static str = "🮇";
|
||||
const SE: &'static str = "🮇";
|
||||
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct QuarterV(pub Style);
|
||||
|
||||
impl BorderStyle for QuarterV {
|
||||
const NW: &'static str = "▎";
|
||||
const W: &'static str = "▎";
|
||||
const SW: &'static str = "▎";
|
||||
const NE: &'static str = "🮇";
|
||||
const E: &'static str = "🮇";
|
||||
const SE: &'static str = "🮇";
|
||||
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
const LOZENGE: [[&'static str;3];3] = [
|
||||
["╭", "─", "╮"],
|
||||
["│", " ", "│"],
|
||||
["╰", "─", "╯"],
|
||||
];
|
||||
|
||||
pub fn lozenge_left (buf: &mut Buffer, x: u16, y1: u16, h: u16, style: Option<Style>) {
|
||||
let y2 = y1 + h;
|
||||
let y3 = y2.saturating_sub(1);
|
||||
for y in y1..y2 {
|
||||
if y == y1 {
|
||||
LOZENGE[0][0]
|
||||
} else if y == y3 {
|
||||
LOZENGE[2][0]
|
||||
} else {
|
||||
LOZENGE[1][0]
|
||||
}.blit(buf, x, y, style)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lozenge_right (buf: &mut Buffer, x: u16, y1: u16, h: u16, style: Option<Style>) {
|
||||
let y2 = y1 + h;
|
||||
let y3 = y2.saturating_sub(1);
|
||||
for y in y1..y2 {
|
||||
if y == y1 {
|
||||
LOZENGE[0][2]
|
||||
} else if y == y3 {
|
||||
LOZENGE[2][2]
|
||||
} else {
|
||||
LOZENGE[1][2]
|
||||
}.blit(buf, x, y, style)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ impl<'a> Render for ChainView<'a> {
|
|||
} else {
|
||||
Color::Reset
|
||||
});
|
||||
lozenge_left(buf, x, y, height, style);
|
||||
//lozenge_left(buf, x, y, height, style);
|
||||
let (area, _plugins) = if self.track.is_some() {
|
||||
if self.vertical {
|
||||
self.draw_as_column(buf, area, style)?
|
||||
|
|
@ -40,7 +40,7 @@ impl<'a> Render for ChainView<'a> {
|
|||
} else {
|
||||
(area, vec![])
|
||||
};
|
||||
lozenge_right(buf, x + width - 1, y, height, style);
|
||||
//lozenge_right(buf, x + width - 1, y, height, style);
|
||||
Ok(area)
|
||||
}
|
||||
}
|
||||
|
|
@ -145,7 +145,9 @@ impl<'a> ChainView<'a> {
|
|||
//y = y + 1;
|
||||
//}
|
||||
}
|
||||
if frames.len() > 0 {
|
||||
draw_box_styled(buf, frames[track.device], selected);
|
||||
}
|
||||
Ok((Rect { x, y: area.y, width: w, height: y - area.y }, frames))
|
||||
|
||||
//let area = Rect { x, y, width: 40, height: 30 };
|
||||
|
|
|
|||
67
src/view/focus.rs
Normal file
67
src/view/focus.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use crate::*;
|
||||
|
||||
//trait Focus<T> {
|
||||
//fn focused (&self) -> &T;
|
||||
//fn focused_mut (&mut self) -> &mut T;
|
||||
//fn focus (&mut self, value: T) -> &mut T;
|
||||
//fn focus_next (&mut self) -> &mut T;
|
||||
//fn focus_prev (&mut self) -> &mut T;
|
||||
//fn focus_enter (&mut self) -> &mut T;
|
||||
//fn focus_exit (&mut self) -> &mut T;
|
||||
//}
|
||||
|
||||
//enum AppSection {
|
||||
//Grid,
|
||||
//Sequence,
|
||||
//Chain,
|
||||
//}
|
||||
|
||||
//impl Focus<Self> for (AppSection, bool) {
|
||||
//fn focused (&self) -> &Self {
|
||||
//self
|
||||
//}
|
||||
//fn focused_mut (&mut self) -> &mut Self {
|
||||
//self
|
||||
//}
|
||||
//fn focus (&mut self, value: Self) -> &mut Self {
|
||||
//*self = value;
|
||||
//self
|
||||
//}
|
||||
//fn focus_prev (&mut self) -> &mut Self {
|
||||
//self.focus((match self.0 {
|
||||
//AppSection::Grid => AppSection::Chain,
|
||||
//AppSection::Sequence => AppSection::Grid,
|
||||
//AppSection::Chain => AppSection::Sequence,
|
||||
//}, self.1))
|
||||
//}
|
||||
//fn focus_next (&mut self) -> &mut Self {
|
||||
//self.focus((match self.0 {
|
||||
//AppSection::Grid => AppSection::Sequence,
|
||||
//AppSection::Sequence => AppSection::Chain,
|
||||
//AppSection::Chain => AppSection::Grid,
|
||||
//}, self.1))
|
||||
//}
|
||||
//}
|
||||
|
||||
/*
|
||||
|
||||
App(
|
||||
Column(
|
||||
Transport(
|
||||
Row(PLAY, REC, DUB, MON, BPM, SYNC, QUANT, _, TIME, BBT),
|
||||
RowOrColumn(
|
||||
Grid,
|
||||
|
||||
)
|
||||
|
||||
<App>
|
||||
<Transport />
|
||||
<Grid>
|
||||
<Chains>
|
||||
App
|
||||
\ (Transport)
|
||||
\ Grid
|
||||
\ Chains
|
||||
\ Sequencer + Chain
|
||||
|
||||
*/
|
||||
|
|
@ -26,8 +26,9 @@ impl<'a> SceneGridViewVertical<'a> {
|
|||
Color::Reset
|
||||
});
|
||||
if self.focused && self.entered {
|
||||
lozenge_left(self.buf, x, y, height, style);
|
||||
lozenge_right(self.buf, x + width - 1, y, height, style);
|
||||
QuarterV(Style::default().green().dim()).draw(self.buf, Rect { x, y, width, height });
|
||||
//lozenge_left(self.buf, x, y, height, style);
|
||||
//lozenge_right(self.buf, x + width - 1, y, height, style);
|
||||
}
|
||||
let bg_color = if self.focused && self.entered {
|
||||
Color::Rgb(30, 90, 25)
|
||||
|
|
@ -209,8 +210,10 @@ impl<'a> SceneGridViewHorizontal<'a> {
|
|||
Color::Reset
|
||||
});
|
||||
if self.focused && self.entered {
|
||||
lozenge_left(self.buf, x, y, height, style);
|
||||
lozenge_right(self.buf, x + width - 1, y, height, style);
|
||||
//RailV::draw(self.buf, Rect { x, y, width, height });
|
||||
QuarterV(Style::default().green().dim()).draw(self.buf, Rect { x, y, width, height });
|
||||
//lozenge_left(self.buf, x, y, height, style);
|
||||
//lozenge_right(self.buf, x + width - 1, y, height, style);
|
||||
}
|
||||
let mut x2 = 0;
|
||||
self.draw_tracks(&mut x2);
|
||||
|
|
@ -298,3 +301,71 @@ fn longest_scene_name (scenes: &[Scene]) -> u16 {
|
|||
}
|
||||
w
|
||||
}
|
||||
//use crate::core::*;
|
||||
//use crate::view::*;
|
||||
//use crate::model::*;
|
||||
|
||||
//pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
|
||||
//-> Usually<Rect>
|
||||
//{
|
||||
//if area.height < 2 {
|
||||
//return Ok(area)
|
||||
//}
|
||||
//area.x = area.width.saturating_sub(80) / 2;
|
||||
//area.width = area.width.min(80);
|
||||
//area.height = state.tracks.len() as u16 + 2;
|
||||
//draw_box(buf, area);
|
||||
//let x = area.x + 1;
|
||||
//let y = area.y + 1;
|
||||
//let _h = area.height - 2;
|
||||
//for (i, track) in state.tracks.iter().enumerate() {
|
||||
////buf.set_string(
|
||||
////x, y + index as u16,
|
||||
////&track.name, Style::default().bold().not_dim()
|
||||
////);
|
||||
//for (j, (column, field)) in [
|
||||
//(0, format!(" {:10} ", track.name)),
|
||||
//(12, format!(" {:.1}dB ", track.gain)),
|
||||
//(22, format!(" [ ] ")),
|
||||
//(30, format!(" C ")),
|
||||
//(35, format!(" {:.1}dB ", track.level)),
|
||||
//(45, format!(" [ ] ")),
|
||||
//(51, format!(" {:7} ", track.route)),
|
||||
//].into_iter().enumerate() {
|
||||
//buf.set_string(
|
||||
//x + column as u16,
|
||||
//y + i as u16,
|
||||
//field,
|
||||
//if state.selected_track == i && state.selected_column == j {
|
||||
//Style::default().white().bold().not_dim()
|
||||
//} else {
|
||||
//Style::default().not_dim()
|
||||
//}
|
||||
//);
|
||||
////stdout.queue(move_to(column, row))?;
|
||||
////if state.selected_track == i && state.selected_column == j {
|
||||
////stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
||||
////} else {
|
||||
////stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
||||
////}
|
||||
////fn render_meters (
|
||||
////state: &mut Mixer,
|
||||
////stdout: &mut Stdout,
|
||||
////offset: Rect
|
||||
////) -> Result<(), Box<dyn Error>> {
|
||||
////let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
||||
////for (i, track) in state.tracks.iter().enumerate() {
|
||||
////let row = (i + 1) as u16;
|
||||
////stdout
|
||||
////.queue(move_to(10, row))?.queue(PrintStyledContent("▁".green()))?
|
||||
////.queue(move_to(20, row))?.queue(PrintStyledContent("▁".green()))?
|
||||
////.queue(move_to(28, row))?.queue(PrintStyledContent("▁".green()))?
|
||||
////.queue(move_to(43, row))?.queue(PrintStyledContent("▁".green()))?;
|
||||
////}
|
||||
////Ok(())
|
||||
////}
|
||||
//}
|
||||
//}
|
||||
//Ok(area)
|
||||
//}
|
||||
|
||||
|
|
|
|||
211
src/view/layout.rs
Normal file
211
src/view/layout.rs
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
use crate::core::*;
|
||||
mod container; pub use self::container::*;
|
||||
mod scroll; pub use self::scroll::*;
|
||||
|
||||
pub mod table;
|
||||
|
||||
pub trait Modal<T>: Device {
|
||||
fn handle_with_state (&self, state: &mut T, event: &AppEvent) -> Usually<bool>;
|
||||
}
|
||||
|
||||
pub trait MaxHeight: Device {
|
||||
fn max_height (&self) -> u16 {
|
||||
u16::MAX
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Device> MaxHeight for T {}
|
||||
|
||||
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
||||
draw_box_styled(buffer, area, Some(Style::default().gray().dim()))
|
||||
}
|
||||
|
||||
pub fn draw_box_styled (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
format!("╭{}╮", "─".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
"│".blit(buffer, area.x, y, style);
|
||||
"│".blit(buffer, area.x + area.width - 1, y, style);
|
||||
}
|
||||
format!("╰{}╯", "─".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y + area.height - 1, style);
|
||||
area
|
||||
}
|
||||
|
||||
pub fn draw_box_styled_dotted (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
format!("╭{}╮", "┅".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
"┇".blit(buffer, area.x, y, style);
|
||||
"┇".blit(buffer, area.x + area.width - 1, y, style);
|
||||
}
|
||||
format!("╰{}╯", "┅".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y + area.height - 1, style);
|
||||
area
|
||||
}
|
||||
|
||||
//struct AppLayout {
|
||||
//focus: usize,
|
||||
//track_focus: usize,
|
||||
//chain_focus: usize,
|
||||
//device_focus: usize,
|
||||
//enter: bool,
|
||||
//track_enter: bool,
|
||||
//chain_enter: bool,
|
||||
//device_enter: bool,
|
||||
//}
|
||||
|
||||
//impl AppLayout {
|
||||
//fn grid (&self) -> &mut crate::model::sampler::Sampler {
|
||||
//unimplemented!();
|
||||
//}
|
||||
//fn has_track (&self) -> bool {
|
||||
//unimplemented!();
|
||||
//}
|
||||
//fn sequencer (&self) -> &mut crate::model::sampler::Sampler {
|
||||
//unimplemented!();
|
||||
//}
|
||||
//fn chain (&self) -> &mut crate::model::sampler::Sampler {
|
||||
//unimplemented!();
|
||||
//}
|
||||
//fn chains (&self) -> Vec<&mut crate::model::sampler::Sampler> {
|
||||
//unimplemented!();
|
||||
//}
|
||||
//fn devices (&self) -> Vec<&mut crate::model::sampler::Sampler> {
|
||||
//unimplemented!();
|
||||
//}
|
||||
//}
|
||||
|
||||
//handle!(AppLayout |self, event| {
|
||||
//let mut grid = self.grid();
|
||||
//let mut sequencer = self.sequencer();
|
||||
//let mut devices = self.devices();
|
||||
//let mut chains = self.chains();
|
||||
//});
|
||||
|
||||
//struct FocusConst<'a, const N: usize>(
|
||||
//usize,
|
||||
//bool,
|
||||
//[&'a mut dyn Handle; N]
|
||||
//);
|
||||
|
||||
//impl<'a, const N: usize> Handle for FocusConst<'a, N> {
|
||||
//fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||
//Ok(true)
|
||||
//}
|
||||
//}
|
||||
|
||||
//struct FocusDyn<'a, T: Handle>(
|
||||
//usize,
|
||||
//bool,
|
||||
//&'a mut [&mut T]
|
||||
//);
|
||||
|
||||
//impl<'a, T: Handle> Handle for FocusDyn<'a, T> {
|
||||
//fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||
//Ok(true)
|
||||
//}
|
||||
//}
|
||||
|
||||
//render!(AppLayout |self, buf, area| {
|
||||
//Flex::col(&[
|
||||
//self.transport,
|
||||
//Flex::col(&[
|
||||
//self.grid,
|
||||
//if self.track {
|
||||
//Flex::col(&[
|
||||
//self.sequencer,
|
||||
//self.chain
|
||||
//])
|
||||
//} else {
|
||||
//Flex::row(&self.chains)
|
||||
//}
|
||||
//])
|
||||
//]).render(buf, area)
|
||||
//})
|
||||
|
||||
//struct Flex<'a>(
|
||||
//FlexDir,
|
||||
//&'a [&'a dyn Render]
|
||||
//);
|
||||
|
||||
//impl<'a> Flex<'a> {
|
||||
//pub fn row (items: &'a [impl Render]) -> Self {
|
||||
//Self(FlexDir::Row, items)
|
||||
//}
|
||||
//pub fn col (items: &'a [impl Render]) -> Self {
|
||||
//Self(FlexDir::Col, items)
|
||||
//}
|
||||
//pub fn row_rev (items: &'a [impl Render]) -> Self {
|
||||
//Self(FlexDir::RowRev, items)
|
||||
//}
|
||||
//pub fn col_rev (items: &'a [impl Render]) -> Self {
|
||||
//Self(FlexDir::ColRev, items)
|
||||
//}
|
||||
//}
|
||||
|
||||
//enum FlexDir {
|
||||
//Row,
|
||||
//Col,
|
||||
//RowRev,
|
||||
//ColRev
|
||||
//}
|
||||
|
||||
//impl<'a> Render for Flex<'a> {
|
||||
//fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
//let Rect { x, y, width, height } = area;
|
||||
//let (mut x2, mut y2) = (0, 0);
|
||||
//for item in self.1.iter() {
|
||||
//let Rect { width, height, .. } = item.render(buf, Rect {
|
||||
//x: x + x2, y: y + y2, width, height
|
||||
//})?;
|
||||
//let Rect { width, height, .. } = item.render(buf, match self.0 {
|
||||
//FlexDir::Row => Rect {
|
||||
//x: x + x2,
|
||||
//y: y + y2,
|
||||
//width: width - x2,
|
||||
//height: height - y2,
|
||||
//},
|
||||
//FlexDir::Col => Rect {
|
||||
//x: x + x2,
|
||||
//y: y + y2,
|
||||
//width: width - x2,
|
||||
//height: height - y2,
|
||||
//},
|
||||
//FlexDir::RowRev => Rect {
|
||||
//x: x + width - 1 - x2,
|
||||
//y: y + y2,
|
||||
//width: width - x2,
|
||||
//height: height - y2,
|
||||
//},
|
||||
//FlexDir::ColRev => Rect {
|
||||
//x: x + x2,
|
||||
//y: y + height - 1 - y2,
|
||||
//width: width - x2,
|
||||
//height: height - y2,
|
||||
//}
|
||||
//})?;
|
||||
//match self.0 {
|
||||
//FlexDir::Row => {
|
||||
//x2 = x2 + width;
|
||||
//},
|
||||
//FlexDir::Col => {
|
||||
//y2 = y2 + height;
|
||||
//},
|
||||
//FlexDir::RowRev => {
|
||||
//x2 = x2 + width
|
||||
//},
|
||||
//FlexDir::ColRev => {
|
||||
//y2 = y2 + height;
|
||||
//}
|
||||
//}
|
||||
//}
|
||||
//Ok(area)
|
||||
//}
|
||||
//}
|
||||
|
|
@ -1,5 +1,68 @@
|
|||
use crate::core::*;
|
||||
|
||||
pub trait BorderStyle {
|
||||
const NW: &'static str = "";
|
||||
const N: &'static str = "";
|
||||
const NE: &'static str = "";
|
||||
const E: &'static str = "";
|
||||
const SE: &'static str = "";
|
||||
const S: &'static str = "";
|
||||
const SW: &'static str = "";
|
||||
const W: &'static str = "";
|
||||
|
||||
fn draw (&self, buf: &mut Buffer, area: Rect) {
|
||||
let style = self.style();
|
||||
for x in area.x+1..(area.x+area.width).saturating_sub(2) {
|
||||
Self::N.blit(buf, x, area.y, style);
|
||||
Self::S.blit(buf, x, area.y + area.height - 1, style);
|
||||
}
|
||||
for y in area.y+1..(area.y+area.height).saturating_sub(2) {
|
||||
Self::W.blit(buf, area.x, y, style);
|
||||
Self::E.blit(buf, area.x + area.width - 1, y, style);
|
||||
}
|
||||
Self::NW.blit(buf, area.x, area.y, style);
|
||||
Self::NE.blit(buf, area.x + area.width - 1, area.y, style);
|
||||
Self::SW.blit(buf, area.x, area.y + area.height - 1, style);
|
||||
Self::SE.blit(buf, area.x + area.width - 1, area.y + area.height - 1, style);
|
||||
}
|
||||
|
||||
fn style (&self) -> Option<Style> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lozenge(pub Style);
|
||||
|
||||
impl BorderStyle for Lozenge {
|
||||
const N: &'static str = "─";
|
||||
const S: &'static str = "─";
|
||||
const NW: &'static str = "╭";
|
||||
const W: &'static str = "│";
|
||||
const SW: &'static str = "╰";
|
||||
const NE: &'static str = "╮";
|
||||
const E: &'static str = "│";
|
||||
const SE: &'static str = "╯";
|
||||
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RailV(pub Style);
|
||||
|
||||
impl BorderStyle for RailV {
|
||||
const NW: &'static str = "▌";
|
||||
const W: &'static str = "▌";
|
||||
const SW: &'static str = "▌";
|
||||
const NE: &'static str = "▐";
|
||||
const E: &'static str = "▐";
|
||||
const SE: &'static str = "▐";
|
||||
|
||||
fn style (&self) -> Option<Style> {
|
||||
Some(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
const LOZENGE: [[&'static str;3];3] = [
|
||||
["╭", "─", "╮"],
|
||||
["│", " ", "│"],
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
mod focus; pub use self::focus::*;
|
||||
mod container; pub use self::container::*;
|
||||
mod scroll; pub use self::scroll::*;
|
||||
mod lozenge; pub use self::lozenge::*;
|
||||
|
||||
pub mod table;
|
||||
|
||||
use crate::core::*;
|
||||
|
||||
pub trait Modal<T>: Device {
|
||||
fn handle_with_state (&self, state: &mut T, event: &AppEvent) -> Usually<bool>;
|
||||
}
|
||||
|
||||
pub trait MaxHeight: Device {
|
||||
fn max_height (&self) -> u16 {
|
||||
u16::MAX
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Device> MaxHeight for T {}
|
||||
|
||||
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
||||
draw_box_styled(buffer, area, Some(Style::default().gray().dim()))
|
||||
}
|
||||
|
||||
pub fn draw_box_styled (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
format!("╭{}╮", "─".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
"│".blit(buffer, area.x, y, style);
|
||||
"│".blit(buffer, area.x + area.width - 1, y, style);
|
||||
}
|
||||
format!("╰{}╯", "─".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y + area.height - 1, style);
|
||||
area
|
||||
}
|
||||
|
||||
pub fn draw_box_styled_dotted (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
format!("╭{}╮", "┅".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
"┇".blit(buffer, area.x, y, style);
|
||||
"┇".blit(buffer, area.x + area.width - 1, y, style);
|
||||
}
|
||||
format!("╰{}╯", "┅".repeat((area.width - 2).into()))
|
||||
.blit(buffer, area.x, area.y + area.height - 1, style);
|
||||
area
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
use crate::core::*;
|
||||
use crate::view::*;
|
||||
use crate::model::*;
|
||||
|
||||
pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
|
||||
-> Usually<Rect>
|
||||
{
|
||||
if area.height < 2 {
|
||||
return Ok(area)
|
||||
}
|
||||
area.x = area.width.saturating_sub(80) / 2;
|
||||
area.width = area.width.min(80);
|
||||
area.height = state.tracks.len() as u16 + 2;
|
||||
draw_box(buf, area);
|
||||
let x = area.x + 1;
|
||||
let y = area.y + 1;
|
||||
let _h = area.height - 2;
|
||||
for (i, track) in state.tracks.iter().enumerate() {
|
||||
//buf.set_string(
|
||||
//x, y + index as u16,
|
||||
//&track.name, Style::default().bold().not_dim()
|
||||
//);
|
||||
for (j, (column, field)) in [
|
||||
(0, format!(" {:10} ", track.name)),
|
||||
(12, format!(" {:.1}dB ", track.gain)),
|
||||
(22, format!(" [ ] ")),
|
||||
(30, format!(" C ")),
|
||||
(35, format!(" {:.1}dB ", track.level)),
|
||||
(45, format!(" [ ] ")),
|
||||
(51, format!(" {:7} ", track.route)),
|
||||
].into_iter().enumerate() {
|
||||
buf.set_string(
|
||||
x + column as u16,
|
||||
y + i as u16,
|
||||
field,
|
||||
if state.selected_track == i && state.selected_column == j {
|
||||
Style::default().white().bold().not_dim()
|
||||
} else {
|
||||
Style::default().not_dim()
|
||||
}
|
||||
);
|
||||
//stdout.queue(move_to(column, row))?;
|
||||
//if state.selected_track == i && state.selected_column == j {
|
||||
//stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
||||
//} else {
|
||||
//stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
||||
//}
|
||||
//fn render_meters (
|
||||
//state: &mut Mixer,
|
||||
//stdout: &mut Stdout,
|
||||
//offset: Rect
|
||||
//) -> Result<(), Box<dyn Error>> {
|
||||
//let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
||||
//for (i, track) in state.tracks.iter().enumerate() {
|
||||
//let row = (i + 1) as u16;
|
||||
//stdout
|
||||
//.queue(move_to(10, row))?.queue(PrintStyledContent("▁".green()))?
|
||||
//.queue(move_to(20, row))?.queue(PrintStyledContent("▁".green()))?
|
||||
//.queue(move_to(28, row))?.queue(PrintStyledContent("▁".green()))?
|
||||
//.queue(move_to(43, row))?.queue(PrintStyledContent("▁".green()))?;
|
||||
//}
|
||||
//Ok(())
|
||||
//}
|
||||
}
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
|
|
@ -25,17 +25,8 @@ pub struct SequencerView<'a> {
|
|||
|
||||
impl<'a> Render for SequencerView<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, width, height } = area;
|
||||
let style = Some(Style::default().green().dim());
|
||||
fill_bg(buf, area, if self.focused {
|
||||
Color::Rgb(20, 45, 5)
|
||||
} else {
|
||||
Color::Reset
|
||||
});
|
||||
if self.focused {
|
||||
lozenge_left(buf, x, y, height, style);
|
||||
lozenge_right(buf, x + width - 1, y, height, style);
|
||||
}
|
||||
let bg = if self.focused { Color::Rgb(20, 45, 5) } else { Color::Reset };
|
||||
fill_bg(buf, area, bg);
|
||||
self.draw_horizontal(buf, area)?;
|
||||
Ok(area)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub struct TransportView<'a> {
|
|||
|
||||
impl<'a> Render for TransportView<'a> {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
fill_bg(buf, area, Color::Rgb(20, 45, 5));
|
||||
let Rect { x, y, width, .. } = area;
|
||||
draw_play_stop(buf, x + 1, y, &self.playing);
|
||||
if width > 100 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue