ui thrashing

This commit is contained in:
🪞👃🪞 2024-07-07 17:55:05 +03:00
parent acb952736e
commit 20b7267225
18 changed files with 695 additions and 233 deletions

View file

@ -28,7 +28,7 @@ fn cursor_down (state: &mut Sampler) -> Usually<bool> {
fn trigger (state: &mut Sampler) -> Usually<bool> { fn trigger (state: &mut Sampler) -> Usually<bool> {
for (i, sample) in state.samples.values().enumerate() { for (i, sample) in state.samples.values().enumerate() {
if i == state.cursor.0 { if i == state.cursor.0 {
state.voices.push(sample.play(0)) state.voices.push(sample.play(0, &100.into()))
} }
} }
Ok(true) Ok(true)

View file

@ -3,6 +3,21 @@ pub(crate) use ratatui::prelude::*;
pub(crate) use ratatui::buffer::Cell; pub(crate) use ratatui::buffer::Cell;
use ratatui::widgets::WidgetRef; 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 { pub trait Blit {
// Render something to X, Y coordinates in a buffer, ignoring width/height. // 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>); fn blit (&self, buf: &mut Buffer, x: u16, y: u16, style: Option<Style>);

View file

@ -81,8 +81,10 @@ pub fn main () -> Usually<()> {
sample!(34, "808", "/home/user/Lab/Music/pak/808.wav"), sample!(34, "808", "/home/user/Lab/Music/pak/808.wav"),
sample!(35, "Kick1", "/home/user/Lab/Music/pak/kik.wav"), sample!(35, "Kick1", "/home/user/Lab/Music/pak/kik.wav"),
sample!(36, "Kick2", "/home/user/Lab/Music/pak/kik2.wav"), sample!(36, "Kick2", "/home/user/Lab/Music/pak/kik2.wav"),
sample!(40, "Snare", "/home/user/Lab/Music/pak/sna.wav"), sample!(38, "Snare1", "/home/user/Lab/Music/pak/sna.wav"),
sample!(44, "Hihat", "/home/user/Lab/Music/pak/chh.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|{ ])))?, |track, device|{
device.connect_midi_in(0, &track.midi_out.clone_unowned())?; device.connect_midi_in(0, &track.midi_out.clone_unowned())?;
Ok(()) Ok(())

View file

@ -146,12 +146,6 @@ impl App {
}; };
Ok(()) 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 ( pub fn add_track (
&mut self, &mut self,
name: Option<&str>, name: Option<&str>,
@ -185,6 +179,12 @@ impl App {
self.tracks.get_mut(id).map(|t|(id, t)) 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)> { pub fn scene (&self) -> Option<(usize, &Scene)> {
match self.scene_cursor { 0 => None, _ => { match self.scene_cursor { 0 => None, _ => {
let id = self.scene_cursor as usize - 1; let id = self.scene_cursor as usize - 1;

View file

@ -6,7 +6,7 @@ pub struct Mixer {
pub selected_track: usize, pub selected_track: usize,
pub selected_column: usize, pub selected_column: usize,
} }
render!(Mixer = crate::view::mixer::render); //render!(Mixer = crate::view::mixer::render);
handle!(Mixer = crate::control::mixer::handle); handle!(Mixer = crate::control::mixer::handle);
process!(Mixer = process); process!(Mixer = process);

View file

@ -67,9 +67,9 @@ impl Sampler {
fn process_midi_in (&mut self, scope: &ProcessScope) { fn process_midi_in (&mut self, scope: &ProcessScope) {
for RawMidi { time, bytes } in self.ports.midi_ins.get("midi").unwrap().iter(scope) { 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 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) { 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> { 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 }) 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 { Voice {
sample: self.clone(), sample: self.clone(),
after, 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 sample: Arc<Sample>,
pub after: usize, pub after: usize,
pub position: usize, pub position: usize,
pub velocity: f32,
} }
const BUFFER: [f32;64] = [0.0f32;64]; const BUFFER: [f32;64] = [0.0f32;64];
@ -171,8 +173,8 @@ impl Iterator for Voice {
let position = self.position; let position = self.position;
self.position = self.position + 1; self.position = self.position + 1;
return Some([ return Some([
self.sample.channels[0][position], self.sample.channels[0][position] * self.velocity,
self.sample.channels[0][position], self.sample.channels[0][position] * self.velocity,
]) ])
} }
None None

View file

@ -1,24 +1,97 @@
pub mod chain; pub mod chain;
pub mod grid; pub mod grid;
pub mod layout; pub mod layout;
pub mod mixer;
pub mod sampler; pub mod sampler;
pub mod sequencer; pub mod sequencer;
pub mod transport; pub mod transport;
pub mod plugin; pub mod plugin;
pub mod focus;
pub mod border;
pub use self::border::*;
pub use self::layout::*; pub use self::layout::*;
pub use self::transport::TransportView; pub use self::transport::TransportView;
pub use self::grid::*; pub use self::grid::*;
pub use self::focus::*;
pub use self::chain::ChainView; pub use self::chain::ChainView;
pub use self::sequencer::SequencerView; pub use self::sequencer::SequencerView;
use crate::{render, App, core::*}; use crate::{render, App, core::*};
render!(App |self, buf, area| { 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, timebase: &self.timebase,
playing: *self.playing.as_ref().unwrap_or(&TransportState::Stopped), playing: *self.playing.as_ref().unwrap_or(&TransportState::Stopped),
monitor: self.track().map(|t|t.1.monitoring).unwrap_or(false), 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), overdub: self.track().map(|t|t.1.overdub).unwrap_or(false),
frame: self.playhead, frame: self.playhead,
quant: self.quant, quant: self.quant,
}.render(buf, area)?.height; }.render(buf, area)
}
y = y + if self.grid_mode { fn draw_grid_horizontal (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
SceneGridViewHorizontal { SceneGridViewHorizontal {
buf, buf,
area: Rect { x, y, width, height: height / 3 }, area,
focused: self.section == 0, focused: self.section == 0,
entered: self.entered, entered: self.entered,
scenes: &self.scenes, scenes: &self.scenes,
tracks: &self.tracks, tracks: &self.tracks,
cursor: &(self.track_cursor, self.scene_cursor), cursor: &(self.track_cursor, self.scene_cursor),
}.draw()? }.draw()
} else { }
fn draw_grid_vertical (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
SceneGridViewVertical { SceneGridViewVertical {
buf, buf,
area: Rect { x, y, width, height: height / 3 }, area,
focused: self.section == 0, focused: self.section == 0,
entered: self.entered, entered: self.entered,
scenes: &self.scenes, scenes: &self.scenes,
tracks: &self.tracks, tracks: &self.tracks,
cursor: &(self.track_cursor, self.scene_cursor), cursor: &(self.track_cursor, self.scene_cursor),
}.draw()? }.draw()
}.height; }
fn draw_chain (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
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 };
ChainView { ChainView {
focused: self.section == 1, focused: self.section == 1,
track: Some(track), track: self.tracks.get(self.track_cursor - 1),
vertical: false, 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 phrase = self.phrase();
let seq_area = SequencerView { let seq_area = SequencerView {
phrase, phrase,
focused: self.section == 2, focused: self.section == 2,
@ -76,51 +142,14 @@ render!(App |self, buf, area| {
time_zoom: self.time_zoom, time_zoom: self.time_zoom,
note_cursor: self.note_cursor, note_cursor: self.note_cursor,
note_start: self.note_start, 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 { if phrase.is_none() && self.section == 2 {
let label = format!("[ENTER] Create new clip: {}", track.name); let label = format!("[ENTER] Create new clip: {}", track.name);
let x = x + seq_area.width / 2 - (label.len() / 2) as u16; let x = area.x + seq_area.width / 2 - (label.len() / 2) as u16;
let y = y + seq_area.height / 2; let y = area.y + seq_area.height / 2;
label.blit(buf, x, y, Some(Style::default().white())); label.blit(buf, x, y, Some(Style::default().white()));
} }
Ok(seq_area)
} 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);
}
} }
} }

130
src/view/border.rs Normal file
View 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)
}
}

View file

@ -30,7 +30,7 @@ impl<'a> Render for ChainView<'a> {
} else { } else {
Color::Reset Color::Reset
}); });
lozenge_left(buf, x, y, height, style); //lozenge_left(buf, x, y, height, style);
let (area, _plugins) = if self.track.is_some() { let (area, _plugins) = if self.track.is_some() {
if self.vertical { if self.vertical {
self.draw_as_column(buf, area, style)? self.draw_as_column(buf, area, style)?
@ -40,7 +40,7 @@ impl<'a> Render for ChainView<'a> {
} else { } else {
(area, vec![]) (area, vec![])
}; };
lozenge_right(buf, x + width - 1, y, height, style); //lozenge_right(buf, x + width - 1, y, height, style);
Ok(area) Ok(area)
} }
} }
@ -145,7 +145,9 @@ impl<'a> ChainView<'a> {
//y = y + 1; //y = y + 1;
//} //}
} }
if frames.len() > 0 {
draw_box_styled(buf, frames[track.device], selected); draw_box_styled(buf, frames[track.device], selected);
}
Ok((Rect { x, y: area.y, width: w, height: y - area.y }, frames)) Ok((Rect { x, y: area.y, width: w, height: y - area.y }, frames))
//let area = Rect { x, y, width: 40, height: 30 }; //let area = Rect { x, y, width: 40, height: 30 };

67
src/view/focus.rs Normal file
View 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
*/

View file

@ -26,8 +26,9 @@ impl<'a> SceneGridViewVertical<'a> {
Color::Reset Color::Reset
}); });
if self.focused && self.entered { if self.focused && self.entered {
lozenge_left(self.buf, x, y, height, style); QuarterV(Style::default().green().dim()).draw(self.buf, Rect { x, y, width, height });
lozenge_right(self.buf, x + width - 1, y, height, style); //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 { let bg_color = if self.focused && self.entered {
Color::Rgb(30, 90, 25) Color::Rgb(30, 90, 25)
@ -209,8 +210,10 @@ impl<'a> SceneGridViewHorizontal<'a> {
Color::Reset Color::Reset
}); });
if self.focused && self.entered { if self.focused && self.entered {
lozenge_left(self.buf, x, y, height, style); //RailV::draw(self.buf, Rect { x, y, width, height });
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 mut x2 = 0; let mut x2 = 0;
self.draw_tracks(&mut x2); self.draw_tracks(&mut x2);
@ -298,3 +301,71 @@ fn longest_scene_name (scenes: &[Scene]) -> u16 {
} }
w 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
View 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)
//}
//}

View file

@ -1,5 +1,68 @@
use crate::core::*; 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] = [ const LOZENGE: [[&'static str;3];3] = [
["", "", ""], ["", "", ""],
["", " ", ""], ["", " ", ""],

View file

@ -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
}

View file

@ -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)
}

View file

@ -25,17 +25,8 @@ pub struct SequencerView<'a> {
impl<'a> Render for SequencerView<'a> { impl<'a> Render for SequencerView<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> { fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, height } = area; let bg = if self.focused { Color::Rgb(20, 45, 5) } else { Color::Reset };
let style = Some(Style::default().green().dim()); fill_bg(buf, area, bg);
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);
}
self.draw_horizontal(buf, area)?; self.draw_horizontal(buf, area)?;
Ok(area) Ok(area)
} }

View file

@ -12,6 +12,7 @@ pub struct TransportView<'a> {
impl<'a> Render for TransportView<'a> { impl<'a> Render for TransportView<'a> {
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> { 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; let Rect { x, y, width, .. } = area;
draw_play_stop(buf, x + 1, y, &self.playing); draw_play_stop(buf, x + 1, y, &self.playing);
if width > 100 { if width > 100 {