mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: some trickery with piano roll size
This commit is contained in:
parent
51351a16dc
commit
66c29525be
5 changed files with 165 additions and 119 deletions
|
|
@ -2,38 +2,46 @@ use crate::*;
|
|||
|
||||
/// A widget that tracks its render width and height
|
||||
#[derive(Default)]
|
||||
pub struct Measure<E: Engine>(PhantomData<E>, AtomicUsize, AtomicUsize, bool);
|
||||
pub struct Measure<E: Engine> {
|
||||
_engine: PhantomData<E>,
|
||||
pub x: Arc<AtomicUsize>,
|
||||
pub y: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
impl<E: Engine> Clone for Measure<E> {
|
||||
fn clone (&self) -> Self {
|
||||
Self(
|
||||
Default::default(),
|
||||
AtomicUsize::from(self.1.load(Ordering::Relaxed)),
|
||||
AtomicUsize::from(self.2.load(Ordering::Relaxed)),
|
||||
self.3
|
||||
)
|
||||
Self {
|
||||
_engine: Default::default(),
|
||||
x: self.x.clone(),
|
||||
y: self.y.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> std::fmt::Debug for Measure<E> {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("Measure")
|
||||
.field("width", &self.1)
|
||||
.field("height", &self.2)
|
||||
.field("width", &self.x)
|
||||
.field("height", &self.y)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> Measure<E> {
|
||||
pub fn w (&self) -> usize { self.1.load(Ordering::Relaxed) }
|
||||
pub fn h (&self) -> usize { self.2.load(Ordering::Relaxed) }
|
||||
pub fn w (&self) -> usize { self.x.load(Ordering::Relaxed) }
|
||||
pub fn h (&self) -> usize { self.y.load(Ordering::Relaxed) }
|
||||
pub fn wh (&self) -> [usize;2] { [self.w(), self.h()] }
|
||||
pub fn set_w (&self, w: impl Into<usize>) { self.1.store(w.into(), Ordering::Relaxed) }
|
||||
pub fn set_h (&self, h: impl Into<usize>) { self.2.store(h.into(), Ordering::Relaxed) }
|
||||
pub fn set_w (&self, w: impl Into<usize>) { self.x.store(w.into(), Ordering::Relaxed) }
|
||||
pub fn set_h (&self, h: impl Into<usize>) { self.y.store(h.into(), Ordering::Relaxed) }
|
||||
pub fn set_wh (&self, w: impl Into<usize>, h: impl Into<usize>) { self.set_w(w); self.set_h(h); }
|
||||
pub fn new () -> Self { Self(PhantomData::default(), 0.into(), 0.into(), false) }
|
||||
pub fn debug () -> Self { Self(PhantomData::default(), 0.into(), 0.into(), true) }
|
||||
pub fn format (&self) -> String { format!("{}x{}", self.w(), self.h()) }
|
||||
pub fn new () -> Self {
|
||||
Self {
|
||||
_engine: PhantomData::default(),
|
||||
x: Arc::new(0.into()),
|
||||
y: Arc::new(0.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Render<Tui> for Measure<Tui> {
|
||||
|
|
@ -41,14 +49,24 @@ impl Render<Tui> for Measure<Tui> {
|
|||
Ok(Some([0u16.into(), 0u16.into()].into()))
|
||||
}
|
||||
fn render (&self, to: &mut TuiOutput) -> Usually<()> {
|
||||
let w = to.area().w();
|
||||
self.set_w(w);
|
||||
let h = to.area().h();
|
||||
self.set_h(h);
|
||||
Ok(if self.3 {
|
||||
to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some(
|
||||
Style::default().bold().italic().bg(Color::Rgb(255, 0, 255)).fg(Color::Rgb(0,0,0))
|
||||
))
|
||||
})
|
||||
self.set_w(to.area().w());
|
||||
self.set_h(to.area().h());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Measure<Tui> {
|
||||
pub fn debug (&self) -> ShowMeasure {
|
||||
let measure: Measure<Tui> = (*self).clone();
|
||||
ShowMeasure(measure)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShowMeasure(Measure<Tui>);
|
||||
render!(|self: ShowMeasure|render(|to|Ok({
|
||||
let w = self.0.w();
|
||||
let h = self.0.h();
|
||||
to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some(
|
||||
Style::default().bold().italic().bg(Color::Rgb(255, 0, 255)).fg(Color::Rgb(0,0,0))
|
||||
))
|
||||
})));
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ impl SamplerTui {
|
|||
}
|
||||
impl Audio for SamplerTui {
|
||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||
todo!()
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::*;
|
||||
use Ordering::Relaxed;
|
||||
|
||||
pub trait HasEditor {
|
||||
fn editor (&self) -> &PhraseEditorModel;
|
||||
|
|
@ -26,17 +27,19 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
|||
fn input_to_command (state: &PhraseEditorModel, from: &TuiInput) -> Option<Self> {
|
||||
use PhraseCommand::*;
|
||||
use KeyCode::{Char, Up, Down, PageUp, PageDown, Left, Right};
|
||||
let note_lo = state.range.note_lo.load(Ordering::Relaxed);
|
||||
let note_point = state.point.note_point.load(Ordering::Relaxed);
|
||||
let time_start = state.range.time_start.load(Ordering::Relaxed);
|
||||
let time_point = state.point.time_point.load(Ordering::Relaxed);
|
||||
let time_zoom = state.mode.time_zoom();
|
||||
let point = state.point();
|
||||
let note_point = point.note_point.load(Relaxed);
|
||||
let time_point = point.time_point.load(Relaxed);
|
||||
let note_len = point.note_len.load(Relaxed);
|
||||
let range = state.range();
|
||||
let note_lo = range.note_lo.load(Relaxed);
|
||||
let time_start = range.time_start.load(Relaxed);
|
||||
let time_zoom = range.time_zoom();
|
||||
let length = state.phrase().read().unwrap().as_ref()
|
||||
.map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||
let note_len = state.point.note_len.load(Ordering::Relaxed);
|
||||
Some(match from.event() {
|
||||
key!(Char('`')) => ToggleDirection,
|
||||
key!(Char('z')) => SetTimeZoomLock(!state.mode.time_zoom_lock()),
|
||||
key!(Char('z')) => SetTimeZoomLock(!state.range().time_lock()),
|
||||
key!(Char('-')) => SetTimeZoom(next_note_length(time_zoom)),
|
||||
key!(Char('_')) => SetTimeZoom(next_note_length(time_zoom)),
|
||||
key!(Char('=')) => SetTimeZoom(prev_note_length(time_zoom)),
|
||||
|
|
@ -75,22 +78,24 @@ impl InputToCommand<Tui, PhraseEditorModel> for PhraseCommand {
|
|||
impl Command<PhraseEditorModel> for PhraseCommand {
|
||||
fn execute (self, state: &mut PhraseEditorModel) -> Perhaps<Self> {
|
||||
use PhraseCommand::*;
|
||||
let range = state.range();
|
||||
let point = state.point();
|
||||
match self {
|
||||
Show(phrase) => { state.set_phrase(phrase); },
|
||||
PutNote => { state.put_note(false); },
|
||||
AppendNote => { state.put_note(true); },
|
||||
SetTimeZoom(zoom) => { state.mode.set_time_zoom(zoom); },
|
||||
SetTimeZoomLock(lock) => { state.mode.set_time_zoom_lock(lock); },
|
||||
SetTimeScroll(time) => { state.range.time_start.store(time, Ordering::Relaxed); },
|
||||
SetTimeCursor(time) => { state.point.time_point.store(time, Ordering::Relaxed); },
|
||||
SetNoteLength(time) => { state.point.note_len.store(time, Ordering::Relaxed); },
|
||||
SetNoteScroll(note) => { state.range.note_lo.store(note, Ordering::Relaxed); },
|
||||
SetNoteCursor(note) => {
|
||||
Show(phrase) => { state.set_phrase(phrase); },
|
||||
PutNote => { state.put_note(false); },
|
||||
AppendNote => { state.put_note(true); },
|
||||
SetTimeZoom(x) => { range.set_time_zoom(x); },
|
||||
SetTimeZoomLock(x) => { range.set_time_lock(x); },
|
||||
SetTimeScroll(x) => { range.set_time_start(x); },
|
||||
SetNoteScroll(x) => { range.set_note_lo(x); },
|
||||
SetNoteLength(x) => { point.set_note_len(x); },
|
||||
SetTimeCursor(x) => { point.set_time_point(x); },
|
||||
SetNoteCursor(note) => {
|
||||
let note = 127.min(note);
|
||||
let start = state.range.note_lo.load(Ordering::Relaxed);
|
||||
state.point.note_point.store(note, Ordering::Relaxed);
|
||||
let start = range.note_lo.load(Relaxed);
|
||||
point.note_point.store(note, Relaxed);
|
||||
if note < start {
|
||||
state.range.note_lo.store(note, Ordering::Relaxed);
|
||||
range.note_lo.store(note, Relaxed);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -106,29 +111,21 @@ pub struct PhraseEditorModel {
|
|||
pub phrase: Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>,
|
||||
/// Renders the phrase
|
||||
pub mode: Box<dyn PhraseViewMode>,
|
||||
/// The display window
|
||||
pub range: PhraseEditorRange,
|
||||
/// The note cursor
|
||||
pub point: PhraseEditorPoint,
|
||||
}
|
||||
|
||||
impl Default for PhraseEditorModel {
|
||||
fn default () -> Self {
|
||||
let phrase = Arc::new(RwLock::new(None));
|
||||
let range = PhraseEditorRange::default();
|
||||
let point = PhraseEditorPoint::default();
|
||||
let mode = PianoHorizontal::new(&phrase, &range, &point);
|
||||
Self { phrase, mode: Box::new(mode), range, point }
|
||||
let mode = PianoHorizontal::new(&phrase);
|
||||
Self { phrase, mode: Box::new(mode) }
|
||||
}
|
||||
}
|
||||
|
||||
render!(|self: PhraseEditorModel|self.mode);
|
||||
|
||||
pub trait PhraseViewMode: Render<Tui> + Debug + Send + Sync {
|
||||
fn time_zoom (&self) -> usize;
|
||||
fn set_time_zoom (&mut self, time_zoom: usize);
|
||||
fn time_zoom_lock (&self) -> bool;
|
||||
fn set_time_zoom_lock (&mut self, time_zoom: bool);
|
||||
fn range (&self) -> &PhraseEditorRange;
|
||||
fn point (&self) -> &PhraseEditorPoint;
|
||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize);
|
||||
fn redraw (&mut self);
|
||||
fn phrase (&self) -> &Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>;
|
||||
|
|
@ -139,17 +136,11 @@ pub trait PhraseViewMode: Render<Tui> + Debug + Send + Sync {
|
|||
}
|
||||
|
||||
impl PhraseViewMode for PhraseEditorModel {
|
||||
fn time_zoom (&self) -> usize {
|
||||
self.mode.time_zoom()
|
||||
fn range (&self) -> &PhraseEditorRange {
|
||||
self.mode.range()
|
||||
}
|
||||
fn set_time_zoom (&mut self, time_zoom: usize) {
|
||||
self.mode.set_time_zoom(time_zoom)
|
||||
}
|
||||
fn time_zoom_lock (&self) -> bool {
|
||||
self.mode.time_zoom_lock()
|
||||
}
|
||||
fn set_time_zoom_lock (&mut self, time_lock: bool) {
|
||||
self.mode.set_time_zoom_lock(time_lock);
|
||||
fn point (&self) -> &PhraseEditorPoint {
|
||||
self.mode.point()
|
||||
}
|
||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
||||
self.mode.buffer_size(phrase)
|
||||
|
|
@ -164,10 +155,16 @@ impl PhraseViewMode for PhraseEditorModel {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PhraseEditorRange {
|
||||
/// Length of visible time axis
|
||||
pub time_axis: Arc<AtomicUsize>,
|
||||
/// Earliest time displayed
|
||||
pub time_start: Arc<AtomicUsize>,
|
||||
/// Time step
|
||||
pub time_zoom: Arc<AtomicUsize>,
|
||||
/// Auto rezoom to fit in time axis
|
||||
pub time_lock: Arc<AtomicBool>,
|
||||
/// Length of visible note axis
|
||||
pub note_axis: Arc<AtomicUsize>,
|
||||
// Lowest note displayed
|
||||
pub note_lo: Arc<AtomicUsize>,
|
||||
}
|
||||
|
|
@ -175,12 +172,35 @@ pub struct PhraseEditorRange {
|
|||
impl Default for PhraseEditorRange {
|
||||
fn default () -> Self {
|
||||
Self {
|
||||
time_axis: Arc::new(0.into()),
|
||||
time_start: Arc::new(0.into()),
|
||||
time_zoom: Arc::new(24.into()),
|
||||
time_lock: Arc::new(true.into()),
|
||||
note_axis: Arc::new(0.into()),
|
||||
note_lo: Arc::new(0.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl PhraseEditorRange {
|
||||
pub fn time_zoom (&self) -> usize {
|
||||
self.time_zoom.load(Relaxed)
|
||||
}
|
||||
pub fn set_time_zoom (&self, x: usize) {
|
||||
self.time_zoom.store(x, Relaxed);
|
||||
}
|
||||
pub fn time_lock (&self) -> bool {
|
||||
self.time_lock.load(Relaxed)
|
||||
}
|
||||
pub fn set_time_lock (&self, x: bool) {
|
||||
self.time_lock.store(x, Relaxed);
|
||||
}
|
||||
pub fn set_time_start (&self, x: usize) {
|
||||
self.time_start.store(x, Relaxed);
|
||||
}
|
||||
pub fn set_note_lo (&self, x: usize) {
|
||||
self.note_lo.store(x, Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PhraseEditorPoint {
|
||||
|
|
@ -201,27 +221,42 @@ impl Default for PhraseEditorPoint {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl PhraseEditorPoint {
|
||||
pub fn note_len (&self) -> usize {
|
||||
self.note_len.load(Relaxed)
|
||||
}
|
||||
pub fn set_note_len (&self, x: usize) {
|
||||
self.note_len.store(x, Relaxed)
|
||||
}
|
||||
pub fn time_point (&self) -> usize {
|
||||
self.time_point.load(Relaxed)
|
||||
}
|
||||
pub fn set_time_point (&self, x: usize) {
|
||||
self.time_point.store(x, Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl PhraseEditorModel {
|
||||
/// Put note at current position
|
||||
pub fn put_note (&mut self, advance: bool) {
|
||||
if let Some(phrase) = &*self.phrase.read().unwrap() {
|
||||
let note_len = self.point.note_len.load(Ordering::Relaxed);
|
||||
let time = self.point.time_point.load(Ordering::Relaxed);
|
||||
let note = self.point.note_point.load(Ordering::Relaxed);
|
||||
let point = self.point().clone();
|
||||
let note_len = point.note_len.load(Relaxed);
|
||||
let time = point.time_point.load(Relaxed);
|
||||
let note = point.note_point.load(Relaxed);
|
||||
let mut phrase = phrase.write().unwrap();
|
||||
let key: u7 = u7::from(note as u8);
|
||||
let vel: u7 = 100.into();
|
||||
let start = time;
|
||||
let end = (start + note_len) % phrase.length;
|
||||
let key: u7 = u7::from(note as u8);
|
||||
let vel: u7 = 100.into();
|
||||
let start = time;
|
||||
let end = (start + note_len) % phrase.length;
|
||||
phrase.notes[time].push(MidiMessage::NoteOn { key, vel });
|
||||
phrase.notes[end].push(MidiMessage::NoteOff { key, vel });
|
||||
self.mode.redraw();
|
||||
if advance {
|
||||
let point = self.point.time_point.load(Ordering::Relaxed);
|
||||
let time = point.time_point.load(Relaxed);
|
||||
let length = phrase.length;
|
||||
let forward = |time|(time + note_len) % length;
|
||||
self.point.time_point.store(forward(point), Ordering::Relaxed);
|
||||
point.set_time_point(forward(time));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -244,8 +279,8 @@ impl From<Option<Arc<RwLock<Phrase>>>> for PhraseEditorModel {
|
|||
impl std::fmt::Debug for PhraseEditorModel {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("PhraseEditorModel")
|
||||
.field("range", &self.range)
|
||||
.field("point", &self.point)
|
||||
.field("range", &self.range())
|
||||
.field("point", &self.point())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
@ -253,13 +288,13 @@ impl std::fmt::Debug for PhraseEditorModel {
|
|||
fn autoscroll_notes (
|
||||
range: &PhraseEditorRange, point: &PhraseEditorPoint, height: usize
|
||||
) -> (usize, (usize, usize)) {
|
||||
let note_point = point.note_point.load(Ordering::Relaxed);
|
||||
let mut note_lo = range.note_lo.load(Ordering::Relaxed);
|
||||
let note_point = point.note_point.load(Relaxed);
|
||||
let mut note_lo = range.note_lo.load(Relaxed);
|
||||
let mut note_hi = 127.min((note_lo + height).saturating_sub(2));
|
||||
if note_point > note_hi {
|
||||
note_lo += note_point - note_hi;
|
||||
note_hi = note_point;
|
||||
range.note_lo.store(note_lo, Ordering::Relaxed);
|
||||
range.note_lo.store(note_lo, Relaxed);
|
||||
}
|
||||
(note_point, (note_lo, note_hi))
|
||||
}
|
||||
|
|
@ -275,9 +310,9 @@ fn autoscroll_notes (
|
|||
//Self {
|
||||
//note_point,
|
||||
//note_range,
|
||||
//time_start: editor.range.time_start.load(Ordering::Relaxed),
|
||||
//time_point: editor.point.time_point.load(Ordering::Relaxed),
|
||||
//note_len: editor.point.note_len.load(Ordering::Relaxed),
|
||||
//time_start: editor.range.time_start.load(Relaxed),
|
||||
//time_point: editor.point.time_point.load(Relaxed),
|
||||
//note_len: editor.point.note_len.load(Relaxed),
|
||||
//phrase: editor.phrase.clone(),
|
||||
//mode: &editor.mode,
|
||||
//size: &editor.size,
|
||||
|
|
|
|||
|
|
@ -3,28 +3,28 @@ use super::*;
|
|||
|
||||
/// A phrase, rendered as a horizontal piano roll.
|
||||
pub struct PianoHorizontal {
|
||||
phrase: Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>,
|
||||
time_lock: bool,
|
||||
time_zoom: Arc<AtomicUsize>,
|
||||
note_len: Arc<AtomicUsize>,
|
||||
buffer: BigBuffer,
|
||||
phrase: Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>,
|
||||
buffer: BigBuffer,
|
||||
/// Width and height of notes area at last render
|
||||
size: Measure<Tui>,
|
||||
size: Measure<Tui>,
|
||||
/// The display window
|
||||
range: PhraseEditorRange,
|
||||
/// The note cursor
|
||||
point: PhraseEditorPoint,
|
||||
}
|
||||
|
||||
impl PianoHorizontal {
|
||||
pub fn new (
|
||||
phrase: &Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>,
|
||||
range: &PhraseEditorRange,
|
||||
point: &PhraseEditorPoint,
|
||||
) -> Self {
|
||||
pub fn new (phrase: &Arc<RwLock<Option<Arc<RwLock<Phrase>>>>>) -> Self {
|
||||
let size = Measure::new();
|
||||
let mut range = PhraseEditorRange::default();
|
||||
range.time_axis = size.x.clone();
|
||||
range.note_axis = size.y.clone();
|
||||
Self {
|
||||
phrase: phrase.clone(),
|
||||
buffer: Default::default(),
|
||||
time_lock: true,
|
||||
time_zoom: range.time_zoom.clone(),
|
||||
note_len: point.note_len.clone(),
|
||||
size: Measure::new()
|
||||
buffer: Default::default(),
|
||||
phrase: phrase.clone(),
|
||||
point: PhraseEditorPoint::default(),
|
||||
range,
|
||||
size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -229,31 +229,24 @@ impl PhraseViewMode for PianoHorizontal {
|
|||
fn phrase (&self) -> &Arc<RwLock<Option<Arc<RwLock<Phrase>>>>> {
|
||||
&self.phrase
|
||||
}
|
||||
fn time_zoom (&self) -> usize {
|
||||
self.time_zoom.load(Ordering::Relaxed)
|
||||
fn range (&self) -> &PhraseEditorRange {
|
||||
&self.range
|
||||
}
|
||||
fn set_time_zoom (&mut self, time_zoom: usize) {
|
||||
self.time_zoom.store(time_zoom, Ordering::Relaxed);
|
||||
self.redraw()
|
||||
}
|
||||
fn time_zoom_lock (&self) -> bool {
|
||||
self.time_lock
|
||||
}
|
||||
fn set_time_zoom_lock (&mut self, time_lock: bool) {
|
||||
self.time_lock = time_lock;
|
||||
self.redraw()
|
||||
fn point (&self) -> &PhraseEditorPoint {
|
||||
&self.point
|
||||
}
|
||||
/// Determine the required space to render the phrase.
|
||||
fn buffer_size (&self, phrase: &Phrase) -> (usize, usize) {
|
||||
(phrase.length / self.time_zoom(), 128)
|
||||
(phrase.length / self.range.time_zoom(), 128)
|
||||
}
|
||||
fn redraw (&mut self) {
|
||||
let buffer = if let Some(phrase) = &*self.phrase().read().unwrap() {
|
||||
let phrase = phrase.read().unwrap();
|
||||
let phrase = phrase.read().unwrap();
|
||||
let mut buffer = BigBuffer::from(self.buffer_size(&phrase));
|
||||
let note_len = self.note_len.load(Ordering::Relaxed);
|
||||
PianoHorizontal::draw_bg(&mut buffer, &phrase, self.time_zoom(), note_len);
|
||||
PianoHorizontal::draw_fg(&mut buffer, &phrase, self.time_zoom());
|
||||
let note_len = self.point.note_len();
|
||||
let time_zoom = self.range.time_zoom();
|
||||
PianoHorizontal::draw_bg(&mut buffer, &phrase, time_zoom, note_len);
|
||||
PianoHorizontal::draw_fg(&mut buffer, &phrase, time_zoom);
|
||||
buffer
|
||||
} else {
|
||||
Default::default()
|
||||
|
|
@ -265,7 +258,7 @@ impl PhraseViewMode for PianoHorizontal {
|
|||
impl std::fmt::Debug for PianoHorizontal {
|
||||
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("PianoHorizontal")
|
||||
.field("time_zoom", &self.time_zoom)
|
||||
.field("time_zoom", &self.range.time_zoom)
|
||||
.field("buffer", &format!("{}x{}", self.buffer.width, self.buffer.height))
|
||||
.finish()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue