mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
draw keys correctly without buffer
This commit is contained in:
parent
eafc06edc6
commit
130a53220a
4 changed files with 132 additions and 126 deletions
|
|
@ -8,7 +8,7 @@ pub use std::collections::BTreeMap;
|
||||||
pub use once_cell::sync::Lazy;
|
pub use once_cell::sync::Lazy;
|
||||||
pub use std::sync::atomic::{Ordering, AtomicBool, AtomicUsize, AtomicU16};
|
pub use std::sync::atomic::{Ordering, AtomicBool, AtomicUsize, AtomicU16};
|
||||||
pub use std::rc::Rc;
|
pub use std::rc::Rc;
|
||||||
pub use std::cell::RefCell;
|
pub use std::cell::{Cell, RefCell};
|
||||||
pub use std::marker::PhantomData;
|
pub use std::marker::PhantomData;
|
||||||
pub(crate) use std::error::Error;
|
pub(crate) use std::error::Error;
|
||||||
pub(crate) use std::io::{stdout};
|
pub(crate) use std::io::{stdout};
|
||||||
|
|
|
||||||
|
|
@ -61,22 +61,22 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
||||||
PhraseEditMode::Note => match from.event() {
|
PhraseEditMode::Note => match from.event() {
|
||||||
key!(Char('e')) => SetEditMode(PhraseEditMode::Scroll),
|
key!(Char('e')) => SetEditMode(PhraseEditMode::Scroll),
|
||||||
key!(Up) => SetNoteScroll(
|
key!(Up) => SetNoteScroll(
|
||||||
state.note_start.load(Ordering::Relaxed) + 1
|
state.note_lo.load(Ordering::Relaxed) + 1
|
||||||
),
|
),
|
||||||
key!(Down) => SetNoteScroll(
|
key!(Down) => SetNoteScroll(
|
||||||
state.note_start.load(Ordering::Relaxed).saturating_sub(1)
|
state.note_lo.load(Ordering::Relaxed).saturating_sub(1)
|
||||||
),
|
),
|
||||||
key!(PageUp) => SetNoteScroll(
|
key!(PageUp) => SetNoteScroll(
|
||||||
state.note_start.load(Ordering::Relaxed) + 3
|
state.note_lo.load(Ordering::Relaxed) + 3
|
||||||
),
|
),
|
||||||
key!(PageDown) => SetNoteScroll(
|
key!(PageDown) => SetNoteScroll(
|
||||||
state.note_start.load(Ordering::Relaxed).saturating_sub(3)
|
state.note_lo.load(Ordering::Relaxed).saturating_sub(3)
|
||||||
),
|
),
|
||||||
key!(Left) => SetTimeScroll(
|
key!(Left) => SetTimeScroll(
|
||||||
state.note_start.load(Ordering::Relaxed).saturating_sub(1)
|
state.note_lo.load(Ordering::Relaxed).saturating_sub(1)
|
||||||
),
|
),
|
||||||
key!(Right) => SetTimeScroll(
|
key!(Right) => SetTimeScroll(
|
||||||
state.note_start.load(Ordering::Relaxed) + 1
|
state.note_lo.load(Ordering::Relaxed) + 1
|
||||||
),
|
),
|
||||||
key!(Char('a')) => AppendNote,
|
key!(Char('a')) => AppendNote,
|
||||||
key!(Char('s')) => PutNote,
|
key!(Char('s')) => PutNote,
|
||||||
|
|
@ -127,7 +127,7 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
SetNoteScroll(note) => {
|
SetNoteScroll(note) => {
|
||||||
state.note_start.store(note, Ordering::Relaxed);
|
state.note_lo.store(note, Ordering::Relaxed);
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
SetNoteLength(time) => {
|
SetNoteLength(time) => {
|
||||||
|
|
@ -136,10 +136,10 @@ impl Command<PhraseEditorModel> for PhraseCommand {
|
||||||
},
|
},
|
||||||
SetNoteCursor(note) => {
|
SetNoteCursor(note) => {
|
||||||
let note = 127.min(note);
|
let note = 127.min(note);
|
||||||
let start = state.note_start.load(Ordering::Relaxed);
|
let start = state.note_lo.load(Ordering::Relaxed);
|
||||||
state.note_point.store(note, Ordering::Relaxed);
|
state.note_point.store(note, Ordering::Relaxed);
|
||||||
if note < start {
|
if note < start {
|
||||||
state.note_start.store((note / 2) * 2, Ordering::Relaxed);
|
state.note_lo.store((note / 2) * 2, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ pub struct PhraseEditorModel {
|
||||||
pub(crate) phrase: Option<Arc<RwLock<Phrase>>>,
|
pub(crate) phrase: Option<Arc<RwLock<Phrase>>>,
|
||||||
/// Length of note that will be inserted, in pulses
|
/// Length of note that will be inserted, in pulses
|
||||||
pub(crate) note_len: usize,
|
pub(crate) note_len: usize,
|
||||||
/// The full piano keys are rendered to this buffer
|
|
||||||
pub(crate) keys: Buffer,
|
|
||||||
/// The full piano roll is rendered to this buffer
|
/// The full piano roll is rendered to this buffer
|
||||||
pub(crate) buffer: BigBuffer,
|
pub(crate) buffer: BigBuffer,
|
||||||
/// Notes currently held at input
|
/// Notes currently held at input
|
||||||
|
|
@ -19,9 +17,8 @@ pub struct PhraseEditorModel {
|
||||||
/// Width and height of notes area at last render
|
/// Width and height of notes area at last render
|
||||||
pub(crate) size: Measure<Tui>,
|
pub(crate) size: Measure<Tui>,
|
||||||
|
|
||||||
pub(crate) note_start: AtomicUsize,
|
pub(crate) note_lo: AtomicUsize,
|
||||||
pub(crate) note_point: AtomicUsize,
|
pub(crate) note_point: AtomicUsize,
|
||||||
pub(crate) note_clamp: AtomicUsize,
|
|
||||||
|
|
||||||
pub(crate) time_start: AtomicUsize,
|
pub(crate) time_start: AtomicUsize,
|
||||||
pub(crate) time_point: AtomicUsize,
|
pub(crate) time_point: AtomicUsize,
|
||||||
|
|
@ -34,10 +31,9 @@ pub struct PhraseEditorModel {
|
||||||
impl std::fmt::Debug for PhraseEditorModel {
|
impl std::fmt::Debug for PhraseEditorModel {
|
||||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
f.debug_struct("PhraseEditorModel")
|
f.debug_struct("PhraseEditorModel")
|
||||||
.field("note_axis", &format!("{} {} {}",
|
.field("note_axis", &format!("{} {}",
|
||||||
self.note_start.load(Ordering::Relaxed),
|
self.note_lo.load(Ordering::Relaxed),
|
||||||
self.note_point.load(Ordering::Relaxed),
|
self.note_point.load(Ordering::Relaxed),
|
||||||
self.note_clamp.load(Ordering::Relaxed),
|
|
||||||
))
|
))
|
||||||
.field("time_axis", &format!("{} {} {} {}",
|
.field("time_axis", &format!("{} {} {} {}",
|
||||||
self.time_start.load(Ordering::Relaxed),
|
self.time_start.load(Ordering::Relaxed),
|
||||||
|
|
@ -54,16 +50,14 @@ impl Default for PhraseEditorModel {
|
||||||
Self {
|
Self {
|
||||||
phrase: None,
|
phrase: None,
|
||||||
note_len: 24,
|
note_len: 24,
|
||||||
keys: keys_vert(),
|
|
||||||
buffer: Default::default(),
|
buffer: Default::default(),
|
||||||
notes_in: RwLock::new([false;128]).into(),
|
notes_in: RwLock::new([false;128]).into(),
|
||||||
notes_out: RwLock::new([false;128]).into(),
|
notes_out: RwLock::new([false;128]).into(),
|
||||||
now: Pulse::default().into(),
|
now: Pulse::default().into(),
|
||||||
size: Measure::new(),
|
size: Measure::new(),
|
||||||
edit_mode: PhraseEditMode::Scroll,
|
edit_mode: PhraseEditMode::Scroll,
|
||||||
note_start: 12.into(),
|
note_lo: 0.into(),
|
||||||
note_point: 36.into(),
|
note_point: 24.into(),
|
||||||
note_clamp: 127.into(),
|
|
||||||
time_start: 0.into(),
|
time_start: 0.into(),
|
||||||
time_point: 0.into(),
|
time_point: 0.into(),
|
||||||
time_clamp: 0.into(),
|
time_clamp: 0.into(),
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,17 @@ pub struct PhraseView<'a> {
|
||||||
pub(crate) focused: bool,
|
pub(crate) focused: bool,
|
||||||
pub(crate) entered: bool,
|
pub(crate) entered: bool,
|
||||||
pub(crate) phrase: &'a Option<Arc<RwLock<Phrase>>>,
|
pub(crate) phrase: &'a Option<Arc<RwLock<Phrase>>>,
|
||||||
pub(crate) size: &'a Measure<Tui>,
|
|
||||||
pub(crate) keys: &'a Buffer,
|
|
||||||
pub(crate) buffer: &'a BigBuffer,
|
pub(crate) buffer: &'a BigBuffer,
|
||||||
pub(crate) note_len: usize,
|
pub(crate) note_len: usize,
|
||||||
pub(crate) now: &'a Arc<Pulse>,
|
pub(crate) now: &'a Arc<Pulse>,
|
||||||
|
|
||||||
pub(crate) note_start: &'a AtomicUsize,
|
size: &'a Measure<Tui>,
|
||||||
pub(crate) note_point: usize,
|
width: usize,
|
||||||
pub(crate) note_clamp: usize,
|
height: usize,
|
||||||
|
|
||||||
note_range: (&'a str, &'a str),
|
note_point: usize,
|
||||||
|
note_range: (usize, usize),
|
||||||
|
note_names: (&'a str, &'a str),
|
||||||
|
|
||||||
pub(crate) time_start: usize,
|
pub(crate) time_start: usize,
|
||||||
pub(crate) time_point: usize,
|
pub(crate) time_point: usize,
|
||||||
|
|
@ -24,30 +24,45 @@ pub struct PhraseView<'a> {
|
||||||
|
|
||||||
impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> {
|
impl<'a, T: HasEditor> From<&'a T> for PhraseView<'a> {
|
||||||
fn from (state: &'a T) -> Self {
|
fn from (state: &'a T) -> Self {
|
||||||
let [w, h] = state.editor().size.wh();
|
let editor = state.editor();
|
||||||
let note_start = state.editor().note_start.load(Ordering::Relaxed);
|
|
||||||
let note_clamp = state.editor().note_clamp.load(Ordering::Relaxed);
|
let [width, height] = editor.size.wh();
|
||||||
let note_end = (note_start + h.saturating_sub(2) / 2).min(127);
|
|
||||||
let note_range = (to_note_name(note_start), to_note_name(note_end));
|
let note_point = editor.note_point.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
let mut note_lo = editor.note_lo.load(Ordering::Relaxed);
|
||||||
|
if note_point < note_lo {
|
||||||
|
note_lo = note_point;
|
||||||
|
editor.note_lo.store(note_lo, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut note_hi = 127.min(note_lo + height * 2 + 1);
|
||||||
|
if note_point > note_hi {
|
||||||
|
note_lo += note_point - note_hi;
|
||||||
|
note_hi = note_point;
|
||||||
|
editor.note_lo.store(note_lo, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
focused: state.editor_focused(),
|
focused: state.editor_focused(),
|
||||||
entered: state.editor_entered(),
|
entered: state.editor_entered(),
|
||||||
note_len: state.editor().note_len,
|
note_len: editor.note_len,
|
||||||
phrase: &state.editor().phrase,
|
phrase: &editor.phrase,
|
||||||
size: &state.editor().size,
|
buffer: &editor.buffer,
|
||||||
keys: &state.editor().keys,
|
now: &editor.now,
|
||||||
buffer: &state.editor().buffer,
|
|
||||||
now: &state.editor().now,
|
|
||||||
|
|
||||||
note_start: &state.editor().note_start,
|
size: &editor.size,
|
||||||
note_point: state.editor().note_point.load(Ordering::Relaxed),
|
width,
|
||||||
note_clamp,
|
height,
|
||||||
note_range,
|
|
||||||
|
|
||||||
time_start: state.editor().time_start.load(Ordering::Relaxed),
|
note_point,
|
||||||
time_point: state.editor().time_point.load(Ordering::Relaxed),
|
note_range: (note_lo, note_hi),
|
||||||
time_clamp: state.editor().time_clamp.load(Ordering::Relaxed),
|
note_names: (to_note_name(note_lo), to_note_name(note_hi)),
|
||||||
time_scale: state.editor().time_scale.load(Ordering::Relaxed),
|
|
||||||
|
time_start: editor.time_start.load(Ordering::Relaxed),
|
||||||
|
time_point: editor.time_point.load(Ordering::Relaxed),
|
||||||
|
time_clamp: editor.time_clamp.load(Ordering::Relaxed),
|
||||||
|
time_scale: editor.time_scale.load(Ordering::Relaxed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,33 +71,73 @@ impl<'a> Content for PhraseView<'a> {
|
||||||
type Engine = Tui;
|
type Engine = Tui;
|
||||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||||
let Self {
|
let Self {
|
||||||
focused, entered, phrase, size, keys, buffer, note_len, now,
|
focused,
|
||||||
note_point, note_range,
|
entered,
|
||||||
time_start, time_point, time_clamp, time_scale, ..
|
phrase,
|
||||||
|
size,
|
||||||
|
buffer,
|
||||||
|
note_len,
|
||||||
|
note_range: (note_lo, note_hi),
|
||||||
|
note_names: (note_lo_name, note_hi_name),
|
||||||
|
note_point,
|
||||||
|
time_start,
|
||||||
|
time_point,
|
||||||
|
time_clamp,
|
||||||
|
time_scale,
|
||||||
|
now,
|
||||||
|
..
|
||||||
} = self;
|
} = self;
|
||||||
let note_start = self.note_start.load(Ordering::Relaxed);
|
|
||||||
let keys = move|to: &mut TuiOutput|Ok(if to.area().h() >= 2 {
|
let keys = move|to: &mut TuiOutput|Ok(if to.area().h() >= 2 {
|
||||||
to.buffer_update(to.area().set_w(5), &|cell, x, y|{
|
for y in to.area.y()..to.area.y2() {
|
||||||
let y = y + (note_start / 2) as u16;
|
let n = y - to.area.y();
|
||||||
if x < keys.area.width && y < keys.area.height {
|
let m = note_hi.saturating_sub(n as usize*2);
|
||||||
*cell = keys.get(x, y).clone()
|
//let c = format!("{m:>03} {n}");
|
||||||
|
//to.blit(&c, to.area.x(), y, None);
|
||||||
|
let x = to.area.x();
|
||||||
|
let s = Some(Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(0,0,0)));
|
||||||
|
match m % 2 {
|
||||||
|
1 => match (m / 2) % 6 {
|
||||||
|
5 => to.blit(&"▀", x, y, s),
|
||||||
|
4 => to.blit(&"▀", x, y, s),
|
||||||
|
3 => to.blit(&"▀", x, y, s),
|
||||||
|
2 => to.blit(&"█", x, y, s),
|
||||||
|
1 => to.blit(&"▄", x, y, s),
|
||||||
|
0 => to.blit(&"▄", x, y, s),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
0 => match (m / 2) % 6 {
|
||||||
|
5 => to.blit(&"▄", x, y, s),
|
||||||
|
4 => to.blit(&"▄", x, y, s),
|
||||||
|
3 => to.blit(&"▄", x, y, s),
|
||||||
|
2 => to.blit(&"▀", x, y, s),
|
||||||
|
1 => to.blit(&"▀", x, y, s),
|
||||||
|
0 => to.blit(&"█", x, y, s),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
}
|
||||||
|
//to.buffer_update(to.area().set_w(5), &|cell, x, y|{
|
||||||
|
////let y = y + (note_hi / 2) as u16;
|
||||||
|
//if x < keys.area.width && y < keys.area.height {
|
||||||
|
//[>cell = keys.get(x, y).clone()
|
||||||
|
////cell.set_symbol(&format!("{y}"));
|
||||||
|
////}
|
||||||
|
//}
|
||||||
|
//});
|
||||||
});
|
});
|
||||||
let notes_bg_null = Color::Rgb(28, 35, 25);
|
let notes_bg_null = Color::Rgb(28, 35, 25);
|
||||||
let notes = move|to: &mut TuiOutput|{
|
let notes = move|to: &mut TuiOutput|{
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
let h = area.h() as usize;
|
let h = area.h() as usize;
|
||||||
size.set_wh(area.w(), h);
|
size.set_wh(area.w(), h - 1);
|
||||||
if note_point.saturating_sub(note_start) > (h * 2).saturating_sub(1) {
|
|
||||||
self.note_start.store(note_start + 2, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
Ok(if to.area().h() >= 2 {
|
Ok(if to.area().h() >= 2 {
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
to.buffer_update(area, &move |cell, x, y|{
|
to.buffer_update(area, &move |cell, x, y|{
|
||||||
cell.set_bg(notes_bg_null);
|
cell.set_bg(notes_bg_null);
|
||||||
let src_x = (x as usize + time_start) * time_scale;
|
let src_x = (x as usize + time_start) * time_scale;
|
||||||
let src_y = y as usize + note_start / 2;
|
let src_y = y as usize + note_lo / 2;
|
||||||
if src_x < buffer.width && src_y < buffer.height - 1 {
|
if src_x < buffer.width && src_y < buffer.height - 1 {
|
||||||
buffer.get(src_x, buffer.height - src_y - 2).map(|src|{
|
buffer.get(src_x, buffer.height - src_y - 2).map(|src|{
|
||||||
cell.set_symbol(src.symbol());
|
cell.set_symbol(src.symbol());
|
||||||
|
|
@ -97,10 +152,15 @@ impl<'a> Content for PhraseView<'a> {
|
||||||
let area = to.area();
|
let area = to.area();
|
||||||
let x1 = area.x() + (time_point / time_scale) as u16;
|
let x1 = area.x() + (time_point / time_scale) as u16;
|
||||||
let x2 = x1 + (note_len / time_scale) as u16;
|
let x2 = x1 + (note_len / time_scale) as u16;
|
||||||
let y = area.y() + note_point.saturating_sub(note_start) as u16 / 2;
|
let y = area.y() + (note_hi - note_point) as u16 / 2;
|
||||||
let c = if note_point % 2 == 0 { "▀" } else { "▄" };
|
let c = if note_lo % 2 == 0 {
|
||||||
|
if note_point % 2 == 0 { "▄" } else { "▀" }
|
||||||
|
} else {
|
||||||
|
if note_point % 2 == 0 { "▀" } else { "▄" }
|
||||||
|
};
|
||||||
|
let style = Some(Style::default().fg(Color::Rgb(0,255,0)));
|
||||||
for x in x1..x2 {
|
for x in x1..x2 {
|
||||||
to.blit(&c, x, y, Some(Style::default().fg(Color::Rgb(0,255,0))));
|
to.blit(&c, x, y, style);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let playhead_inactive = Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(40,50,30));
|
let playhead_inactive = Style::default().fg(Color::Rgb(255,255,255)).bg(Color::Rgb(40,50,30));
|
||||||
|
|
@ -129,7 +189,7 @@ impl<'a> Content for PhraseView<'a> {
|
||||||
CustomWidget::new(|to|Ok(Some(to)), cursor)
|
CustomWidget::new(|to|Ok(Some(to)), cursor)
|
||||||
).fill_x();
|
).fill_x();
|
||||||
let piano_roll = row!(
|
let piano_roll = row!(
|
||||||
CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(5))), keys).fill_y(),
|
CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(2))), keys).fill_y(),
|
||||||
note_area
|
note_area
|
||||||
).fill_x().bg(Color::Rgb(40, 50, 30)).border(border);
|
).fill_x().bg(Color::Rgb(40, 50, 30)).border(border);
|
||||||
let content = lay!(
|
let content = lay!(
|
||||||
|
|
@ -140,30 +200,22 @@ impl<'a> Content for PhraseView<'a> {
|
||||||
if let Some(phrase) = phrase {
|
if let Some(phrase) = phrase {
|
||||||
name = phrase.read().unwrap().name.clone();
|
name = phrase.read().unwrap().name.clone();
|
||||||
}
|
}
|
||||||
let mut upper_left = format!("{} [{}] {name}", note_range.1, if *entered {"■"} else {" "},);
|
let mut upper_left = format!("{note_hi} {note_hi_name} {name}");
|
||||||
let mut lower_left = format!("{}", note_range.0);
|
let mut lower_left = format!("{note_lo} {note_lo_name}");
|
||||||
let mut lower_right = format!("┤{}├", size.format());
|
let mut lower_right = format!("┤{}├", size.format());
|
||||||
lower_right = format!("┤Zoom: {}├─{lower_right}", pulses_to_name(*time_scale));
|
|
||||||
//lower_right = format!("Zoom: {} (+{}:{}*{}|{})",
|
|
||||||
//pulses_to_name(time_scale),
|
|
||||||
//time_start, time_point.unwrap_or(0),
|
|
||||||
//time_scale, time_clamp.unwrap_or(0),
|
|
||||||
//);
|
|
||||||
if *focused && *entered {
|
if *focused && *entered {
|
||||||
lower_right = format!("┤Note: {} {}├─{lower_right}",
|
lower_right = format!("┤Note: {} ({}) {}├─{lower_right}",
|
||||||
note_point,
|
note_point,
|
||||||
pulses_to_name(*note_len));
|
to_note_name(*note_point),
|
||||||
//lower_right = format!("Note: {} (+{}:{}|{}) {upper_right}",
|
pulses_to_name(*note_len)
|
||||||
//pulses_to_name(*note_len),
|
);
|
||||||
//note_start,
|
|
||||||
//note_point.unwrap_or(0),
|
|
||||||
//note_clamp.unwrap_or(0),
|
|
||||||
//);
|
|
||||||
}
|
}
|
||||||
let upper_right = if let Some(phrase) = phrase {
|
let mut upper_right = format!("[{}]", if *entered {"■"} else {" "});
|
||||||
format!("┤Length: {}├", phrase.read().unwrap().length)
|
if let Some(phrase) = phrase {
|
||||||
} else {
|
upper_right = format!("┤Length: {}├─┤Zoom: {}├{upper_right}",
|
||||||
String::new()
|
phrase.read().unwrap().length,
|
||||||
|
pulses_to_name(*time_scale),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
lay!(
|
lay!(
|
||||||
content,
|
content,
|
||||||
|
|
@ -175,49 +227,9 @@ impl<'a> Content for PhraseView<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Colors of piano keys
|
|
||||||
const KEY_COLORS: [(Color, Color);6] = [
|
|
||||||
(Color::Rgb(255, 255, 255), Color::Rgb(255, 255, 255)),
|
|
||||||
(Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)),
|
|
||||||
(Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)),
|
|
||||||
(Color::Rgb(0, 0, 0), Color::Rgb(255, 255, 255)),
|
|
||||||
(Color::Rgb(255, 255, 255), Color::Rgb(0, 0, 0)),
|
|
||||||
(Color::Rgb(255, 255, 255), Color::Rgb(0, 0, 0)),
|
|
||||||
];
|
|
||||||
|
|
||||||
pub(crate) fn keys_vert () -> Buffer {
|
|
||||||
let area = [0, 0, 5, 64];
|
|
||||||
let mut buffer = Buffer::empty(Rect {
|
|
||||||
x: area.x(), y: area.y(), width: area.w(), height: area.h()
|
|
||||||
});
|
|
||||||
buffer_update(&mut buffer, area, &|cell, x, y| {
|
|
||||||
let y = 63 - y;
|
|
||||||
match x {
|
|
||||||
0 => if y % 6 == 0 {
|
|
||||||
cell.set_char('C');
|
|
||||||
},
|
|
||||||
1 => if y % 6 == 0 {
|
|
||||||
cell.set_symbol(NTH_OCTAVE[(y / 6) as usize]);
|
|
||||||
},
|
|
||||||
2 => {
|
|
||||||
cell.set_char('▀');
|
|
||||||
let (fg, bg) = KEY_COLORS[((6 - y % 6) % 6) as usize];
|
|
||||||
cell.set_fg(fg);
|
|
||||||
cell.set_bg(bg);
|
|
||||||
},
|
|
||||||
3 => {
|
|
||||||
cell.set_char('▀');
|
|
||||||
cell.set_fg(Color::White);
|
|
||||||
cell.set_bg(Color::White);
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
const NTH_OCTAVE: [&'static str; 11] = [
|
const NTH_OCTAVE: [&'static str; 11] = [
|
||||||
"-2", "-1", "0", "1", "2", "3", "4", "5", "6", "7", "8",
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "X",
|
||||||
|
//"-2", "-1", "0", "1", "2", "3", "4", "5", "6", "7", "8",
|
||||||
];
|
];
|
||||||
|
|
||||||
impl PhraseEditorModel {
|
impl PhraseEditorModel {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue