mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16: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> {
|
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)
|
||||||
|
|
|
||||||
|
|
@ -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>);
|
||||||
|
|
|
||||||
12
src/main.rs
12
src/main.rs
|
|
@ -78,11 +78,13 @@ pub fn main () -> Usually<()> {
|
||||||
state.add_track_with_cb(Some("Drums"), |_, track|{
|
state.add_track_with_cb(Some("Drums"), |_, track|{
|
||||||
|
|
||||||
track.add_device_with_cb(Sampler::new("Sampler", Some(BTreeMap::from([
|
track.add_device_with_cb(Sampler::new("Sampler", Some(BTreeMap::from([
|
||||||
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(())
|
||||||
|
|
|
||||||
12
src/model.rs
12
src/model.rs
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
177
src/view.rs
177
src/view.rs
|
|
@ -1,71 +1,137 @@
|
||||||
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 {
|
||||||
timebase: &self.timebase,
|
fn draw_mixer (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
playing: *self.playing.as_ref().unwrap_or(&TransportState::Stopped),
|
let mut x = area.x;
|
||||||
monitor: self.track().map(|t|t.1.monitoring).unwrap_or(false),
|
for track in self.tracks.iter() {
|
||||||
record: self.track().map(|t|t.1.recording).unwrap_or(false),
|
track.name.blit(buf, x + 1, area.y, Some(Style::default().white().bold()));
|
||||||
overdub: self.track().map(|t|t.1.overdub).unwrap_or(false),
|
x = x + ChainView {
|
||||||
frame: self.playhead,
|
focused: self.section == 1,
|
||||||
quant: self.quant,
|
track: Some(track),
|
||||||
}.render(buf, area)?.height;
|
vertical: true,
|
||||||
|
}
|
||||||
y = y + if self.grid_mode {
|
.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),
|
||||||
|
record: self.track().map(|t|t.1.recording).unwrap_or(false),
|
||||||
|
overdub: self.track().map(|t|t.1.overdub).unwrap_or(false),
|
||||||
|
frame: self.playhead,
|
||||||
|
quant: self.quant,
|
||||||
|
}.render(buf, area)
|
||||||
|
}
|
||||||
|
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
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 {
|
} 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;
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
draw_box_styled(buf, frames[track.device], selected);
|
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))
|
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
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
|
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
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::*;
|
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] = [
|
||||||
["╭", "─", "╮"],
|
["╭", "─", "╮"],
|
||||||
["│", " ", "│"],
|
["│", " ", "│"],
|
||||||
|
|
|
||||||
|
|
@ -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> {
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue