various grid fixes

This commit is contained in:
2025-10-04 13:24:53 -05:00
parent 87d842c2ad
commit b8bcba2585
4 changed files with 97 additions and 22 deletions
+29 -9
View File
@@ -1,7 +1,6 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use eframe::egui; use eframe::egui;
use eframe::egui::{Color32, Context, PointerButton, Pos2, Rangef, Sense, Ui, Vec2}; use eframe::egui::{Color32, Context, Id, PointerButton, Pos2, Rangef, Sense, Ui, Vec2};
use serde::{Deserialize, Serialize};
use crate::gui::Showable; use crate::gui::Showable;
use crate::noise::NoteType; use crate::noise::NoteType;
use crate::scale::Scale; use crate::scale::Scale;
@@ -17,27 +16,41 @@ impl NoteType {
NoteType::Noise => Color32::from_rgb(240, 240, 240), NoteType::Noise => Color32::from_rgb(240, 240, 240),
} }
} }
fn name(&self) -> &'static str {
match self {
NoteType::Sine => "Sine",
NoteType::Triangle => "Triangle",
NoteType::Sawtooth => "Sawtooth",
NoteType::Square => "Square",
NoteType::Noise => "Noise"
}
}
} }
#[derive(Serialize, Deserialize, Clone)] #[derive(Clone)]
pub struct Grid { pub struct Grid {
pub note_type: NoteType, pub note_type: NoteType,
pub volume: f32, pub volume: f32,
pub length: u64, pub length: u64,
scale: Scale, pub scale: Scale,
notes: Vec<bool>, pub notes: Vec<bool>,
id: String pub id: Id,
pub name: String,
pub open: bool
} }
impl Grid { impl Grid {
pub fn for_note_type(note_type: NoteType, counter: usize) -> Self { pub fn for_note_type(note_type: NoteType, id: Id) -> Self {
Self { Self {
note_type, note_type,
volume: 1.0, volume: 1.0,
length: 250, length: 250,
open: true,
scale: Scale::CMajor, scale: Scale::CMajor,
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize], notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
id: format!("Track {}", counter) name: format!("{} Track", note_type.name()),
id
} }
} }
@@ -91,7 +104,12 @@ impl Grid {
impl Showable<f32> for Grid { impl Showable<f32> for Grid {
fn show(&mut self, ctx: &Context, cursor: &f32) { fn show(&mut self, ctx: &Context, cursor: &f32) {
let win = egui::Window::new(&self.id).resizable(false).scroll([false, false]); let mut open = true;
let win = egui::Window::new(&self.name)
.id(self.id)
.resizable(false)
.scroll([false, false])
.open(&mut open);
win.show(ctx, |ui| { win.show(ctx, |ui| {
egui::MenuBar::new().ui(ui, |ui| { egui::MenuBar::new().ui(ui, |ui| {
if ui.button("Clear").clicked() { if ui.button("Clear").clicked() {
@@ -128,5 +146,7 @@ impl Showable<f32> for Grid {
self.draw_grid(ui, *cursor) self.draw_grid(ui, *cursor)
}); });
}); });
self.open = open;
} }
} }
+9 -6
View File
@@ -2,7 +2,7 @@ use std::fs;
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use std::path::Path; use std::path::Path;
use eframe::egui; use eframe::egui;
use eframe::egui::{Context, TopBottomPanel}; use eframe::egui::{Context, Id, TopBottomPanel};
use crate::noise::NoteType; use crate::noise::NoteType;
use crate::saveload::PersistedTenori; use crate::saveload::PersistedTenori;
use crate::Tenori; use crate::Tenori;
@@ -90,11 +90,7 @@ impl Tenori {
.pick_file() { .pick_file() {
let serialized = fs::read_to_string(path).map_err(|e| e.to_string())?; let serialized = fs::read_to_string(path).map_err(|e| e.to_string())?;
let persisted = toml::from_str::<PersistedTenori>(serialized.as_str()).map_err(|e| e.to_string())?; let persisted = toml::from_str::<PersistedTenori>(serialized.as_str()).map_err(|e| e.to_string())?;
persisted.apply_to(self);
self.grids = persisted.grids;
self.tempo = persisted.tempo;
self.playing = false;
self.timer = 0.0;
} }
Ok(()) Ok(())
} }
@@ -103,6 +99,13 @@ impl Tenori {
for g in self.grids.iter_mut() { for g in self.grids.iter_mut() {
g.show(ctx, cursor) g.show(ctx, cursor)
} }
self.grids.retain(|g| g.open);
}
/// Return a unique (among all the windows created since a file load) id string for a window.
pub fn window_id(&mut self) -> Id {
self.window_counter += 1;
format!("window {}", self.window_counter).into()
} }
} }
+55 -3
View File
@@ -1,18 +1,70 @@
use eframe::egui::Id;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::grid::Grid; use crate::grid::Grid;
use crate::noise::NoteType;
use crate::scale::Scale;
use crate::tenori::Tenori; use crate::tenori::Tenori;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct PersistedTenori { pub struct PersistedTenori {
pub tempo: u32, tempo: u32,
pub grids: Vec<Grid> grids: Vec<PersistedGrid>
} }
impl From<&Tenori> for PersistedTenori { impl From<&Tenori> for PersistedTenori {
fn from(value: &Tenori) -> Self { fn from(value: &Tenori) -> Self {
Self { Self {
tempo: value.tempo, tempo: value.tempo,
grids: value.grids.iter().map(Grid::clone).collect() grids: value.grids.iter().map(PersistedGrid::from).collect()
}
}
}
impl PersistedTenori {
pub fn apply_to(self, tenori: &mut Tenori) {
tenori.grids = self.grids.into_iter().map(|g| g.into_grid(tenori.window_id())).collect();
tenori.tempo = self.tempo;
tenori.playing = false; // Start paused
tenori.timer = 0.0; // Start at the beginning of the loop
}
}
#[derive(Serialize, Deserialize)]
struct PersistedGrid {
note_type: NoteType,
volume: f32,
length: u64,
scale: Scale,
notes: String,
name: String
}
impl From<&Grid> for PersistedGrid {
fn from(value: &Grid) -> Self {
let notes: String = value.notes.iter().map(|n| if *n { '1' } else { '0' }).collect();
Self {
note_type: value.note_type,
volume: value.volume,
length: value.length,
scale: value.scale,
name: value.name.clone(),
notes
}
}
}
impl PersistedGrid {
pub fn into_grid(self, id: Id) -> Grid {
let notes: Vec<_> = self.notes.chars().map(|c| c == '1').collect();
Grid {
note_type: self.note_type,
volume: self.volume,
length: self.length,
scale: self.scale,
name: self.name,
open: true,
notes,
id
} }
} }
} }
+4 -4
View File
@@ -22,8 +22,8 @@ pub struct Tenori {
/// The grids that we currently have going /// The grids that we currently have going
pub grids: Vec<Grid>, pub grids: Vec<Grid>,
// Running count of windows created (for ids) /// Running count of windows created (for ids)
window_counter: usize, pub window_counter: usize,
// The audio output stream to which we will play notes // The audio output stream to which we will play notes
output_stream: OutputStream, output_stream: OutputStream,
@@ -91,8 +91,8 @@ impl Tenori {
} }
pub fn add_window(&mut self, note_type: NoteType) { pub fn add_window(&mut self, note_type: NoteType) {
self.window_counter += 1; let id = self.window_id();
self.grids.push(Grid::for_note_type(note_type, self.window_counter)); self.grids.push(Grid::for_note_type(note_type, id));
} }
pub fn notes_for_beat(&self) -> Vec<Note> { pub fn notes_for_beat(&self) -> Vec<Note> {