mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 03:36:41 +01:00
last small wave of 15 errors?
This commit is contained in:
parent
4fe51b5267
commit
872c2d94d6
11 changed files with 181 additions and 189 deletions
|
|
@ -30,9 +30,9 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
app.toggle_editor(Some(value));
|
app.toggle_editor(Some(value));
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
fn color (app: &mut App, theme: ItemTheme) -> Perhaps<Self> {
|
//fn color (app: &mut App, theme: ItemTheme) -> Perhaps<Self> {
|
||||||
Ok(app.set_color(Some(theme)).map(|theme|Self::Color{theme}))
|
//Ok(app.set_color(Some(theme)).map(|theme|Self::Color{theme}))
|
||||||
}
|
//}
|
||||||
fn enqueue (app: &mut App, clip: Option<Arc<RwLock<MidiClip>>>) -> Perhaps<Self> {
|
fn enqueue (app: &mut App, clip: Option<Arc<RwLock<MidiClip>>>) -> Perhaps<Self> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
@ -42,19 +42,18 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
fn zoom (app: &mut App, zoom: usize) -> Perhaps<Self> {
|
fn zoom (app: &mut App, zoom: usize) -> Perhaps<Self> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn launch (app: &mut App) -> Perhaps<Self> {
|
//fn launch (app: &mut App) -> Perhaps<Self> {
|
||||||
app.project.launch();
|
//app.project.launch();
|
||||||
Ok(None)
|
//Ok(None)
|
||||||
}
|
//}
|
||||||
fn select (app: &mut App, selection: Selection) -> Perhaps<Self> {
|
fn select (app: &mut App, selection: Selection) -> Perhaps<Self> {
|
||||||
app.project.select(selection);
|
*app.project.selection_mut() = selection;
|
||||||
if let Some(ref mut editor) = app.editor {
|
if let Some(ref mut editor) = app.editor {
|
||||||
editor.set_clip(match app.project.selected {
|
editor.set_clip(match app.project.selection() {
|
||||||
Some(Selection::TrackClip { track, scene })
|
Selection::TrackClip { track, scene } if let Some(Some(Some(clip))) = app
|
||||||
if let Some(Some(Some(clip))) = app
|
|
||||||
.project
|
.project
|
||||||
.scenes.get(scene)
|
.scenes.get(*scene)
|
||||||
.map(|s|s.clips.get(track))
|
.map(|s|s.clips.get(*track))
|
||||||
=>
|
=>
|
||||||
Some(clip),
|
Some(clip),
|
||||||
_ =>
|
_ =>
|
||||||
|
|
@ -79,14 +78,11 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
.transpose()?
|
.transpose()?
|
||||||
.flatten())
|
.flatten())
|
||||||
}
|
}
|
||||||
fn arrange (app: &mut App, command: ArrangementCommand) -> Perhaps<Self> {
|
fn project (app: &mut App, command: ArrangementCommand) -> Perhaps<Self> {
|
||||||
Ok(command.delegate(app, |command|Self::Arrange{command})?)
|
Ok(command.delegate(&mut app.project, |command|Self::Project{command})?)
|
||||||
}
|
}
|
||||||
fn clock (app: &mut App, command: ClockCommand) -> Perhaps<Self> {
|
fn clock (app: &mut App, command: ClockCommand) -> Perhaps<Self> {
|
||||||
Ok(command.execute(&mut app.clock())?.map(|command|Self::Clock{command}))
|
Ok(command.execute(app.clock_mut())?.map(|command|Self::Clock{command}))
|
||||||
}
|
|
||||||
fn device (app: &mut App, command: DeviceCommand) -> Perhaps<Self> {
|
|
||||||
Ok(command.delegate(app, |command|Self::Device{command})?)
|
|
||||||
}
|
}
|
||||||
fn message (app: &mut App, command: MessageCommand) -> Perhaps<Self> {
|
fn message (app: &mut App, command: MessageCommand) -> Perhaps<Self> {
|
||||||
Ok(command.delegate(app, |command|Self::Message{command})?)
|
Ok(command.delegate(app, |command|Self::Message{command})?)
|
||||||
|
|
@ -106,21 +102,20 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn pool (app: &mut App, command: PoolCommand) -> Perhaps<Self> {
|
fn pool (app: &mut App, command: PoolCommand) -> Perhaps<Self> {
|
||||||
Ok(if let Some(pool) = app.project.pool.as_mut() {
|
let undo = command.clone().delegate(
|
||||||
let undo = command.clone().delegate(pool, |command|AppCommand::Pool{command})?;
|
&mut app.project.pool,
|
||||||
// update linked editor after pool action
|
|command|AppCommand::Pool{command}
|
||||||
app.editor.as_mut().map(|editor|match command {
|
)?;
|
||||||
// autoselect: automatically load selected clip in editor
|
// update linked editor after pool action
|
||||||
PoolCommand::Select { .. } |
|
app.editor.as_mut().map(|editor|match command {
|
||||||
// autocolor: update color in all places simultaneously
|
// autoselect: automatically load selected clip in editor
|
||||||
PoolCommand::Clip { command: PoolClipCommand::SetColor { .. } } =>
|
PoolCommand::Select { .. } |
|
||||||
editor.set_clip(pool.clip().as_ref()),
|
// autocolor: update color in all places simultaneously
|
||||||
_ => {}
|
PoolCommand::Clip { command: PoolClipCommand::SetColor { .. } } =>
|
||||||
});
|
editor.set_clip(app.project.pool.clip().as_ref()),
|
||||||
undo
|
_ => {}
|
||||||
} else {
|
});
|
||||||
None
|
Ok(undo)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,7 +133,7 @@ impl<'state> Context<'state, MidiEditCommand> for App {
|
||||||
|
|
||||||
impl<'state> Context<'state, PoolCommand> for App {
|
impl<'state> Context<'state, PoolCommand> for App {
|
||||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<PoolCommand> {
|
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<PoolCommand> {
|
||||||
self.project.pool.map(|p|Context::get(p, iter)).flatten()
|
Context::get(&self.project.pool, iter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,9 +143,15 @@ impl<'state> Context<'state, SamplerCommand> for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'state> Context<'state, ArrangementCommand> for App {
|
||||||
|
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ArrangementCommand> {
|
||||||
|
Context::get(&self.project, iter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tengri_proc::command(App)] impl MessageCommand {
|
#[tengri_proc::command(App)] impl MessageCommand {
|
||||||
fn dismiss (app: &mut App) -> Perhaps<Self> {
|
fn dismiss (app: &mut App) -> Perhaps<Self> {
|
||||||
app.message_dismiss();
|
app.dialog = None;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl HasJack for App {
|
|
||||||
fn jack (&self) -> &Jack {
|
|
||||||
&self.jack
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
audio!(
|
audio!(
|
||||||
|self: App, client, scope|{
|
|self: App, client, scope|{
|
||||||
let t0 = self.perf.get_t0();
|
let t0 = self.perf.get_t0();
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,14 @@ pub struct App {
|
||||||
pub color: ItemTheme,
|
pub color: ItemTheme,
|
||||||
}
|
}
|
||||||
|
|
||||||
has!(Option<Selection>: |self: App|self.project.selection);
|
has!(Jack: |self: App|self.jack);
|
||||||
has!(Vec<JackMidiIn>: |self: App|self.project.midi_ins);
|
has!(Clock: |self: App|self.project.clock);
|
||||||
has!(Vec<JackMidiOut>: |self: App|self.project.midi_outs);
|
has!(Selection: |self: App|self.project.selection);
|
||||||
has!(Vec<Scene>: |self: App|self.project.scenes);
|
has!(Vec<JackMidiIn>: |self: App|self.project.midi_ins);
|
||||||
has!(Vec<Track>: |self: App|self.project.tracks);
|
has!(Vec<JackMidiOut>: |self: App|self.project.midi_outs);
|
||||||
has!(Clock: |self: App|self.project.clock);
|
has!(Vec<Scene>: |self: App|self.project.scenes);
|
||||||
|
has!(Vec<Track>: |self: App|self.project.tracks);
|
||||||
|
|
||||||
has_size!(<TuiOut>|self: App|&self.size);
|
has_size!(<TuiOut>|self: App|&self.size);
|
||||||
has_clips!(|self: App|self.project.pool.clips);
|
has_clips!(|self: App|self.project.pool.clips);
|
||||||
has_editor!(|self: App|{
|
has_editor!(|self: App|{
|
||||||
|
|
@ -46,24 +48,28 @@ has_editor!(|self: App|{
|
||||||
(5 + (time_len / time_zoom)).min(size.saturating_sub(20)).max(16)
|
(5 + (time_len / time_zoom)).min(size.saturating_sub(20)).max(16)
|
||||||
};
|
};
|
||||||
editor_h = 15;
|
editor_h = 15;
|
||||||
is_editing = self.editing.load(Relaxed);
|
is_editing = self.editor.is_some();
|
||||||
});
|
});
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn toggle_dialog (&mut self, dialog: Option<Dialog>) {
|
pub fn toggle_dialog (&mut self, mut dialog: Option<Dialog>) -> Option<Dialog> {
|
||||||
self.dialog = if self.dialog == dialog {
|
std::mem::swap(&mut self.dialog, &mut dialog);
|
||||||
None
|
dialog
|
||||||
} else {
|
|
||||||
dialog
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn toggle_editor (&mut self, value: Option<bool>) {
|
pub fn toggle_editor (&mut self, value: Option<bool>) {
|
||||||
self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
|
//FIXME: self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
|
||||||
self.arranger.map(|arranger|if value {
|
let value = value.unwrap_or_else(||!self.editor.is_some());
|
||||||
arranger.clip_auto_create();
|
if value {
|
||||||
|
self.clip_auto_create();
|
||||||
} else {
|
} else {
|
||||||
arranger.clip_auto_remove();
|
self.clip_auto_remove();
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
pub fn browser (&self) -> Option<&Browser> {
|
||||||
|
self.dialog.as_ref().and_then(|dialog|match dialog {
|
||||||
|
Dialog::Save(b) | Dialog::Load(b) => Some(b),
|
||||||
|
_ => None
|
||||||
|
})
|
||||||
}
|
}
|
||||||
pub(crate) fn device_pick (&mut self, index: usize) {
|
pub(crate) fn device_pick (&mut self, index: usize) {
|
||||||
self.dialog = Some(Dialog::Device(index));
|
self.dialog = Some(Dialog::Device(index));
|
||||||
|
|
@ -85,15 +91,14 @@ impl App {
|
||||||
todo!();
|
todo!();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn device_add_sampler (&mut self, jack: &Jack) -> Usually<usize> {
|
fn device_add_sampler (&mut self) -> Usually<()> {
|
||||||
let name = jack.with_client(|c|c.name().to_string());
|
let name = self.jack.with_client(|c|c.name().to_string());
|
||||||
let midi = self.track().expect("no active track").sequencer.midi_outs[0].name();
|
let midi = self.project.track().expect("no active track").sequencer.midi_outs[0].name();
|
||||||
|
let track = self.track().expect("no active track");
|
||||||
|
let port = format!("{}/Sampler", &track.name);
|
||||||
|
let connect = PortConnect::exact(format!("{name}:{midi}"));
|
||||||
let sampler = if let Ok(sampler) = Sampler::new(
|
let sampler = if let Ok(sampler) = Sampler::new(
|
||||||
jack,
|
&self.jack, &port, &[connect], &[&[], &[]], &[&[], &[]]
|
||||||
&format!("{}/Sampler", Has::<Track>::get(self).name),
|
|
||||||
&[PortConnect::exact(format!("{name}:{midi}"))],
|
|
||||||
&[&[], &[]],
|
|
||||||
&[&[], &[]]
|
|
||||||
) {
|
) {
|
||||||
self.dialog = None;
|
self.dialog = None;
|
||||||
Device::Sampler(sampler)
|
Device::Sampler(sampler)
|
||||||
|
|
@ -101,22 +106,23 @@ impl App {
|
||||||
self.dialog = Some(Dialog::Message(Message::FailedToAddDevice));
|
self.dialog = Some(Dialog::Message(Message::FailedToAddDevice));
|
||||||
return Err("failed to add device".into())
|
return Err("failed to add device".into())
|
||||||
};
|
};
|
||||||
Has::<Track>::get_mut(self).expect("no active track").devices.push(sampler);
|
let track = self.track_mut().expect("no active track");
|
||||||
|
track.devices.push(sampler);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// Create new clip in pool when entering empty cell
|
// Create new clip in pool when entering empty cell
|
||||||
fn clip_auto_create (&mut self) -> Option<Arc<RwLock<MidiClip>>> {
|
fn clip_auto_create (&mut self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
if let Some(Selection::TrackClip { track, scene }) = self.get()
|
if let Selection::TrackClip { track, scene } = *self.selection()
|
||||||
&& let Some(scene) = Has::<Vec<Scene>>::get_mut(self).get_mut(scene)
|
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||||
&& let Some(slot) = scene.clips.get_mut(track)
|
&& let Some(slot) = scene.clips.get_mut(track)
|
||||||
&& slot.is_none()
|
&& slot.is_none()
|
||||||
&& let Some(track) = Has::<Vec<Track>>::get_mut(self).get_mut(track)
|
&& let Some(track) = self.project.tracks.get_mut(track)
|
||||||
{
|
{
|
||||||
let (index, mut clip) = self.arranger.pool.add_new_clip();
|
let (index, mut clip) = self.project.pool.add_new_clip();
|
||||||
// autocolor: new clip colors from scene and track color
|
// autocolor: new clip colors from scene and track color
|
||||||
let color = track.color.base.mix(scene.color.base, 0.5);
|
let color = track.color.base.mix(scene.color.base, 0.5);
|
||||||
clip.write().unwrap().color = ItemColor::random_near(color, 0.2).into();
|
clip.write().unwrap().color = ItemColor::random_near(color, 0.2).into();
|
||||||
if let Some(ref mut editor) = Has::<Option<MidiEditor>>::get_mut(self) {
|
if let Some(ref mut editor) = self.editor {
|
||||||
editor.set_clip(Some(&clip));
|
editor.set_clip(Some(&clip));
|
||||||
}
|
}
|
||||||
*slot = Some(clip.clone());
|
*slot = Some(clip.clone());
|
||||||
|
|
@ -125,37 +131,24 @@ impl App {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "pool", feature = "editor"))]
|
|
||||||
pub trait AutoRemove:
|
|
||||||
Has<Vec<Scene>> +
|
|
||||||
Has<Vec<Track>> +
|
|
||||||
Has<Option<MidiEditor>> +
|
|
||||||
Has<Option<Selection>> +
|
|
||||||
Has<Option<Pool>> +
|
|
||||||
Send + Sync
|
|
||||||
{
|
|
||||||
// Remove clip from arrangement when exiting empty clip editor
|
// Remove clip from arrangement when exiting empty clip editor
|
||||||
fn clip_auto_remove (&mut self) {
|
fn clip_auto_remove (&mut self) {
|
||||||
if let Some(ref mut pool) = Has::<Option<Pool>>::get(self)
|
if let Selection::TrackClip { track, scene } = *self.selection()
|
||||||
&& let Some(Selection::TrackClip { track, scene }) = self.get()
|
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||||
&& let Some(scene) = Has::<Vec<Scene>>::get_mut(self).get_mut(*scene)
|
&& let Some(slot) = scene.clips.get_mut(track)
|
||||||
&& let Some(slot) = scene.clips.get_mut(*track)
|
&& let Some(clip) = slot.as_mut()
|
||||||
&& let Some(clip) = slot.as_mut()
|
|
||||||
{
|
{
|
||||||
let mut swapped = None;
|
let mut swapped = None;
|
||||||
if clip.read().unwrap().count_midi_messages() == 0 {
|
if clip.read().unwrap().count_midi_messages() == 0 {
|
||||||
std::mem::swap(&mut swapped, slot);
|
std::mem::swap(&mut swapped, slot);
|
||||||
}
|
}
|
||||||
if let Some(clip) = swapped {
|
if let Some(clip) = swapped {
|
||||||
pool.delete_clip(&clip.read().unwrap());
|
self.project.pool.delete_clip(&clip.read().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Various possible dialog overlays
|
/// Various possible dialog overlays
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Dialog {
|
pub enum Dialog {
|
||||||
|
|
@ -197,7 +190,7 @@ impl App {
|
||||||
matches!(self.dialog, Some(Dialog::Device(..)))
|
matches!(self.dialog, Some(Dialog::Device(..)))
|
||||||
}
|
}
|
||||||
fn focus_browser (&self) -> bool {
|
fn focus_browser (&self) -> bool {
|
||||||
self.browser.is_visible
|
self.browser().is_some()
|
||||||
}
|
}
|
||||||
fn focus_clip (&self) -> bool {
|
fn focus_clip (&self) -> bool {
|
||||||
!self.is_editing() && self.selection().is_clip()
|
!self.is_editing() && self.selection().is_clip()
|
||||||
|
|
@ -212,16 +205,16 @@ impl App {
|
||||||
!self.is_editing() && self.selection().is_mix()
|
!self.is_editing() && self.selection().is_mix()
|
||||||
}
|
}
|
||||||
fn focus_pool_import (&self) -> bool {
|
fn focus_pool_import (&self) -> bool {
|
||||||
matches!(self.project.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), Some(PoolMode::Import(..)))
|
matches!(self.project.pool.mode, Some(PoolMode::Import(..)))
|
||||||
}
|
}
|
||||||
fn focus_pool_export (&self) -> bool {
|
fn focus_pool_export (&self) -> bool {
|
||||||
matches!(self.project.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), Some(PoolMode::Export(..)))
|
matches!(self.project.pool.mode, Some(PoolMode::Export(..)))
|
||||||
}
|
}
|
||||||
fn focus_pool_rename (&self) -> bool {
|
fn focus_pool_rename (&self) -> bool {
|
||||||
matches!(self.project.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), Some(PoolMode::Rename(..)))
|
matches!(self.project.pool.mode, Some(PoolMode::Rename(..)))
|
||||||
}
|
}
|
||||||
fn focus_pool_length (&self) -> bool {
|
fn focus_pool_length (&self) -> bool {
|
||||||
matches!(self.project.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), Some(PoolMode::Length(..)))
|
matches!(self.project.pool.mode, Some(PoolMode::Length(..)))
|
||||||
}
|
}
|
||||||
fn dialog_device (&self) -> Dialog {
|
fn dialog_device (&self) -> Dialog {
|
||||||
Dialog::Device(0) // TODO
|
Dialog::Device(0) // TODO
|
||||||
|
|
@ -239,10 +232,10 @@ impl App {
|
||||||
Dialog::Menu
|
Dialog::Menu
|
||||||
}
|
}
|
||||||
fn dialog_save (&self) -> Dialog {
|
fn dialog_save (&self) -> Dialog {
|
||||||
Dialog::Save
|
Dialog::Save(Default::default())
|
||||||
}
|
}
|
||||||
fn dialog_load (&self) -> Dialog {
|
fn dialog_load (&self) -> Dialog {
|
||||||
Dialog::Load
|
Dialog::Load(Default::default())
|
||||||
}
|
}
|
||||||
fn dialog_options (&self) -> Dialog {
|
fn dialog_options (&self) -> Dialog {
|
||||||
Dialog::Options
|
Dialog::Options
|
||||||
|
|
@ -251,13 +244,13 @@ impl App {
|
||||||
Some((self.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into())
|
Some((self.editor().as_ref().map(|e|e.get_note_pos()).unwrap() as u8).into())
|
||||||
}
|
}
|
||||||
fn scene_count (&self) -> usize {
|
fn scene_count (&self) -> usize {
|
||||||
self.scenes.len()
|
self.scenes().len()
|
||||||
}
|
}
|
||||||
fn scene_selection (&self) -> Option<usize> {
|
fn scene_selection (&self) -> Option<usize> {
|
||||||
self.selection().scene()
|
self.selection().scene()
|
||||||
}
|
}
|
||||||
fn track_count (&self) -> usize {
|
fn track_count (&self) -> usize {
|
||||||
self.tracks.len()
|
self.tracks().len()
|
||||||
}
|
}
|
||||||
fn track_selection (&self) -> Option<usize> {
|
fn track_selection (&self) -> Option<usize> {
|
||||||
self.selection().track()
|
self.selection().track()
|
||||||
|
|
@ -279,7 +272,7 @@ impl App {
|
||||||
}
|
}
|
||||||
fn clip_selection (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
fn clip_selection (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
match self.selection() {
|
match self.selection() {
|
||||||
Selection::TrackClip { track, scene } => self.scenes()[scene].clips[track].clone(),
|
Selection::TrackClip { track, scene } => self.scenes()[*scene].clips[*track].clone(),
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ impl App {
|
||||||
pub fn view_status (&self) -> impl Content<TuiOut> + use<'_> {
|
pub fn view_status (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
self.update_clock();
|
self.update_clock();
|
||||||
let cache = self.view_cache.read().unwrap();
|
let cache = self.view_cache.read().unwrap();
|
||||||
view_status(self.project.selected.map(|x|x.describe(self.tracks(), self.scenes())),
|
view_status(Some(self.project.selection.describe(self.tracks(), self.scenes())),
|
||||||
cache.sr.view.clone(), cache.buf.view.clone(), cache.lat.view.clone())
|
cache.sr.view.clone(), cache.buf.view.clone(), cache.lat.view.clone())
|
||||||
}
|
}
|
||||||
pub fn view_transport (&self) -> impl Content<TuiOut> + use<'_> {
|
pub fn view_transport (&self) -> impl Content<TuiOut> + use<'_> {
|
||||||
|
|
|
||||||
|
|
@ -6,27 +6,28 @@ impl Arrangement {
|
||||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||||
fn _todo_opt_item_theme_stub (&self) -> Option<ItemTheme> { todo!() }
|
fn _todo_opt_item_theme_stub (&self) -> Option<ItemTheme> { todo!() }
|
||||||
fn _todo_opt_selection_ (&self) -> Option<Selection> { todo!() }
|
fn select_nothing (&self) -> Selection {
|
||||||
|
Selection::Nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tengri_proc::command(Arrangement)]
|
#[tengri_proc::command(Arrangement)]
|
||||||
impl ArrangementCommand {
|
impl ArrangementCommand {
|
||||||
/// Set the selection
|
/// Set the selection
|
||||||
fn select (arranger: &mut Arrangement, s: Option<Selection>) -> Perhaps<Self> {
|
fn select (arranger: &mut Arrangement, s: Selection) -> Perhaps<Self> {
|
||||||
arranger.selected = s;
|
*arranger.selection_mut() = s;
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
/// Launch a clip or scene
|
/// Launch a clip or scene
|
||||||
fn launch (arranger: &mut Arrangement) -> Perhaps<Self> {
|
fn launch (arranger: &mut Arrangement) -> Perhaps<Self> {
|
||||||
use Selection::*;
|
match *arranger.selection() {
|
||||||
match arranger.selected {
|
Selection::Track(t) => {
|
||||||
Some(Track(t)) => {
|
|
||||||
arranger.tracks[t].sequencer.enqueue_next(None)
|
arranger.tracks[t].sequencer.enqueue_next(None)
|
||||||
},
|
},
|
||||||
Some(TrackClip { track, scene }) => {
|
Selection::TrackClip { track, scene } => {
|
||||||
arranger.tracks[track].sequencer.enqueue_next(arranger.scenes[scene].clips[track].as_ref())
|
arranger.tracks[track].sequencer.enqueue_next(arranger.scenes[scene].clips[track].as_ref())
|
||||||
},
|
},
|
||||||
Some(Scene(s)) => {
|
Selection::Scene(s) => {
|
||||||
for t in 0..arranger.tracks.len() {
|
for t in 0..arranger.tracks.len() {
|
||||||
arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref())
|
arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref())
|
||||||
}
|
}
|
||||||
|
|
@ -37,49 +38,44 @@ impl ArrangementCommand {
|
||||||
}
|
}
|
||||||
/// Set the color of the selected entity
|
/// Set the color of the selected entity
|
||||||
fn set_color (arranger: &mut Arrangement, palette: Option<ItemTheme>) -> Perhaps<Self> {
|
fn set_color (arranger: &mut Arrangement, palette: Option<ItemTheme>) -> Perhaps<Self> {
|
||||||
use Selection::*;
|
let mut palette = palette.unwrap_or_else(||ItemTheme::random());
|
||||||
let palette = palette.unwrap_or_else(||ItemTheme::random());
|
let selection = *arranger.selection();
|
||||||
Ok(Some(Self::SetColor { palette: match arranger.selected {
|
Ok(Some(Self::SetColor { palette: Some(match selection {
|
||||||
Some(Mix) => {
|
Selection::Mix => {
|
||||||
let old = arranger.color;
|
std::mem::swap(&mut palette, &mut arranger.color);
|
||||||
arranger.color = palette;
|
palette
|
||||||
Some(old)
|
|
||||||
},
|
},
|
||||||
Some(Scene(s)) => {
|
Selection::Scene(s) => {
|
||||||
let old = arranger.scenes[s].color;
|
std::mem::swap(&mut palette, &mut arranger.scenes[s].color);
|
||||||
arranger.scenes[s].color = palette;
|
palette
|
||||||
Some(old)
|
|
||||||
}
|
}
|
||||||
Some(Track(t)) => {
|
Selection::Track(t) => {
|
||||||
let old = arranger.tracks[t].color;
|
std::mem::swap(&mut palette, &mut arranger.tracks[t].color);
|
||||||
arranger.tracks[t].color = palette;
|
palette
|
||||||
Some(old)
|
|
||||||
}
|
}
|
||||||
Some(TrackClip { track, scene }) => {
|
Selection::TrackClip { track, scene } => {
|
||||||
if let Some(ref clip) = arranger.scenes[scene].clips[track] {
|
if let Some(ref clip) = arranger.scenes[scene].clips[track] {
|
||||||
let mut clip = clip.write().unwrap();
|
let mut clip = clip.write().unwrap();
|
||||||
let old = clip.color;
|
std::mem::swap(&mut palette, &mut clip.color);
|
||||||
clip.color = palette;
|
palette
|
||||||
Some(old)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
return Ok(None)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
} }))
|
}) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn track (arranger: &mut Arrangement, track: TrackCommand) -> Perhaps<Self> {
|
fn track (arranger: &mut Arrangement, track: TrackCommand) -> Perhaps<Self> {
|
||||||
todo!("delegate")
|
todo!("delegate")
|
||||||
}
|
}
|
||||||
fn track_add (arranger: &mut Arrangement) -> Perhaps<Self> {
|
fn track_add (arranger: &mut Arrangement) -> Perhaps<Self> {
|
||||||
let index = arranger.track_add(None, None, &[], &[])?.0;
|
let index = arranger.track_add(None, None, &[], &[])?.0;
|
||||||
arranger.selected = match arranger.selected {
|
*arranger.selection_mut() = match arranger.selection() {
|
||||||
Some(Selection::Track(_)) =>
|
Selection::Track(_) => Selection::Track(index),
|
||||||
Some(Selection::Track(index)),
|
Selection::TrackClip { track, scene } => Selection::TrackClip {
|
||||||
Some(Selection::TrackClip { track, scene }) =>
|
track: index, scene: *scene
|
||||||
Some(Selection::TrackClip { track: index, scene }),
|
},
|
||||||
_ => arranger.selected
|
_ => *arranger.selection()
|
||||||
};
|
};
|
||||||
Ok(Some(Self::TrackDelete { index }))
|
Ok(Some(Self::TrackDelete { index }))
|
||||||
}
|
}
|
||||||
|
|
@ -135,12 +131,13 @@ impl ArrangementCommand {
|
||||||
}
|
}
|
||||||
fn scene_add (arranger: &mut Arrangement) -> Perhaps<Self> {
|
fn scene_add (arranger: &mut Arrangement) -> Perhaps<Self> {
|
||||||
let index = arranger.scene_add(None, None)?.0;
|
let index = arranger.scene_add(None, None)?.0;
|
||||||
arranger.selected = match arranger.selected {
|
*arranger.selection_mut() = match arranger.selection() {
|
||||||
Some(Selection::Scene(_)) =>
|
Selection::Scene(_) => Selection::Scene(index),
|
||||||
Some(Selection::Scene(index)),
|
Selection::TrackClip { track, scene } => Selection::TrackClip {
|
||||||
Some(Selection::TrackClip { track, scene }) =>
|
track: *track,
|
||||||
Some(Selection::TrackClip { track, scene: index }),
|
scene: index
|
||||||
_ => arranger.selected
|
},
|
||||||
|
_ => *arranger.selection()
|
||||||
};
|
};
|
||||||
Ok(None) // TODO
|
Ok(None) // TODO
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ pub struct Arrangement {
|
||||||
/// Scroll offset of scenes
|
/// Scroll offset of scenes
|
||||||
pub scene_scroll: usize,
|
pub scene_scroll: usize,
|
||||||
/// Selected UI element
|
/// Selected UI element
|
||||||
pub selected: Option<Selection>,
|
pub selection: Selection,
|
||||||
/// Contains a render of the project arrangement, redrawn on update.
|
/// Contains a render of the project arrangement, redrawn on update.
|
||||||
/// TODO rename to "render_cache" or smth
|
/// TODO rename to "render_cache" or smth
|
||||||
pub arranger: Arc<RwLock<Buffer>>,
|
pub arranger: Arc<RwLock<Buffer>>,
|
||||||
|
|
@ -43,13 +43,13 @@ pub struct Arrangement {
|
||||||
pub pool: Pool,
|
pub pool: Pool,
|
||||||
}
|
}
|
||||||
|
|
||||||
has!(Option<Selection>: |self: Arrangement|self.selected);
|
has!(Jack: |self: Arrangement|self.jack);
|
||||||
has!(Vec<JackMidiIn>: |self: Arrangement|self.midi_ins);
|
has!(Clock: |self: Arrangement|self.clock);
|
||||||
has!(Vec<JackMidiOut>: |self: Arrangement|self.midi_outs);
|
has!(Selection: |self: Arrangement|self.selection);
|
||||||
has!(Vec<Scene>: |self: Arrangement|self.scenes);
|
has!(Vec<JackMidiIn>: |self: Arrangement|self.midi_ins);
|
||||||
has!(Vec<Track>: |self: Arrangement|self.tracks);
|
has!(Vec<JackMidiOut>: |self: Arrangement|self.midi_outs);
|
||||||
has!(Jack: |self: Arrangement|self.jack);
|
has!(Vec<Scene>: |self: Arrangement|self.scenes);
|
||||||
has!(Clock: |self: Arrangement|self.clock);
|
has!(Vec<Track>: |self: Arrangement|self.tracks);
|
||||||
|
|
||||||
impl Arrangement {
|
impl Arrangement {
|
||||||
/// Width of display
|
/// Width of display
|
||||||
|
|
@ -62,7 +62,7 @@ impl Arrangement {
|
||||||
}
|
}
|
||||||
/// Width taken by all tracks.
|
/// Width taken by all tracks.
|
||||||
pub fn w_tracks (&self) -> u16 {
|
pub fn w_tracks (&self) -> u16 {
|
||||||
self.tracks_with_sizes(&self.selected, None).last()
|
self.tracks_with_sizes(&self.selection(), None).last()
|
||||||
.map(|(_, _, _, x)|x as u16).unwrap_or(0)
|
.map(|(_, _, _, x)|x as u16).unwrap_or(0)
|
||||||
}
|
}
|
||||||
/// Width available to display tracks.
|
/// Width available to display tracks.
|
||||||
|
|
@ -90,18 +90,12 @@ impl Arrangement {
|
||||||
}
|
}
|
||||||
/// Height taken by all scenes.
|
/// Height taken by all scenes.
|
||||||
pub fn h_scenes (&self, is_editing: bool) -> u16 {
|
pub fn h_scenes (&self, is_editing: bool) -> u16 {
|
||||||
let (selected_track, selected_scene) = match Has::<Option<Selection>>::get(self) {
|
|
||||||
Some(Selection::Track(t)) => (Some(*t), None),
|
|
||||||
Some(Selection::Scene(s)) => (None, Some(*s)),
|
|
||||||
Some(Selection::TrackClip { track, scene }) => (Some(*track), Some(*scene)),
|
|
||||||
_ => (None, None)
|
|
||||||
};
|
|
||||||
self.scenes_with_sizes(
|
self.scenes_with_sizes(
|
||||||
is_editing,
|
is_editing,
|
||||||
ArrangerView::H_SCENE,
|
ArrangerView::H_SCENE,
|
||||||
ArrangerView::H_EDITOR,
|
ArrangerView::H_EDITOR,
|
||||||
selected_track,
|
self.selection().track(),
|
||||||
selected_scene
|
self.selection().scene(),
|
||||||
)
|
)
|
||||||
.last()
|
.last()
|
||||||
.map(|(_, _, _, y)|y as u16).unwrap_or(0)
|
.map(|(_, _, _, y)|y as u16).unwrap_or(0)
|
||||||
|
|
@ -121,27 +115,27 @@ impl Arrangement {
|
||||||
}
|
}
|
||||||
/// Get the active track
|
/// Get the active track
|
||||||
fn get_track (&self) -> Option<&Track> {
|
fn get_track (&self) -> Option<&Track> {
|
||||||
let index = self.selection()?.track()?;
|
let index = self.selection().track()?;
|
||||||
Has::<Vec<Track>>::get(self).get(index)
|
Has::<Vec<Track>>::get(self).get(index)
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to the active track
|
/// Get a mutable reference to the active track
|
||||||
fn get_track_mut (&mut self) -> Option<&mut Track> {
|
fn get_track_mut (&mut self) -> Option<&mut Track> {
|
||||||
let index = self.selection()?.track()?;
|
let index = self.selection().track()?;
|
||||||
Has::<Vec<Track>>::get_mut(self).get_mut(index)
|
Has::<Vec<Track>>::get_mut(self).get_mut(index)
|
||||||
}
|
}
|
||||||
/// Get the active scene
|
/// Get the active scene
|
||||||
fn get_scene (&self) -> Option<&Scene> {
|
fn get_scene (&self) -> Option<&Scene> {
|
||||||
let index = self.selection()?.scene()?;
|
let index = self.selection().scene()?;
|
||||||
Has::<Vec<Scene>>::get(self).get(index)
|
Has::<Vec<Scene>>::get(self).get(index)
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to the active scene
|
/// Get a mutable reference to the active scene
|
||||||
fn get_scene_mut (&mut self) -> Option<&mut Scene> {
|
fn get_scene_mut (&mut self) -> Option<&mut Scene> {
|
||||||
let index = self.selection()?.scene()?;
|
let index = self.selection().scene()?;
|
||||||
Has::<Vec<Scene>>::get_mut(self).get_mut(index)
|
Has::<Vec<Scene>>::get_mut(self).get_mut(index)
|
||||||
}
|
}
|
||||||
/// Get the active clip
|
/// Get the active clip
|
||||||
fn get_clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
fn get_clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
self.get_scene()?.clips.get(self.selection()?.track()?)?.clone()
|
self.get_scene()?.clips.get(self.selection().track()?)?.clone()
|
||||||
}
|
}
|
||||||
/// Put a clip in a slot
|
/// Put a clip in a slot
|
||||||
pub fn clip_put (
|
pub fn clip_put (
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
impl<T: Has<Option<Selection>>> HasSelection for T {}
|
impl<T: Has<Selection>> HasSelection for T {}
|
||||||
|
|
||||||
pub trait HasSelection: Has<Option<Selection>> {
|
pub trait HasSelection: Has<Selection> {
|
||||||
fn selection (&self) -> Option<&Selection> {
|
fn selection (&self) -> &Selection {
|
||||||
self.get().as_ref()
|
self.get()
|
||||||
}
|
}
|
||||||
fn selection_mut (&mut self) -> &mut Option<Selection> {
|
fn selection_mut (&mut self) -> &mut Selection {
|
||||||
self.get_mut()
|
self.get_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arrangement {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the current user selection in the arranger
|
/// Represents the current user selection in the arranger
|
||||||
#[derive(PartialEq, Clone, Copy, Debug, Default)]
|
#[derive(PartialEq, Clone, Copy, Debug, Default)]
|
||||||
pub enum Selection {
|
pub enum Selection {
|
||||||
|
|
|
||||||
|
|
@ -149,13 +149,27 @@ impl Track {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Has<Option<Track>> + Send + Sync> HasTrack for T {}
|
pub trait HasTrack {
|
||||||
|
fn track (&self) -> Option<&Track>;
|
||||||
|
fn track_mut (&mut self) -> Option<&mut Track>;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait HasTrack: Has<Option<Track>> + Send + Sync {
|
//impl<T: Has<Option<Track>>> HasTrack for T {
|
||||||
|
//fn track (&self) -> Option<&Track> {
|
||||||
|
//self.get().as_ref()
|
||||||
|
//}
|
||||||
|
//fn track_mut (&mut self) -> Option<&mut Track> {
|
||||||
|
//self.get_mut().as_mut()
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
impl<T: Has<Vec<Track>> + Has<Selection>> HasTrack for T {
|
||||||
fn track (&self) -> Option<&Track> {
|
fn track (&self) -> Option<&Track> {
|
||||||
Has::<Option<Track>>::get(self).as_ref()
|
let index = Has::<Selection>::get(self).track()?;
|
||||||
|
Has::<Vec<Track>>::get(self).get(index)
|
||||||
}
|
}
|
||||||
fn track_mut (&mut self) -> Option<&mut Track> {
|
fn track_mut (&mut self) -> Option<&mut Track> {
|
||||||
Has::<Option<Track>>::get_mut(self).as_mut()
|
let index = Has::<Selection>::get(self).track()?;
|
||||||
|
Has::<Vec<Track>>::get_mut(self).get_mut(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,11 @@ pub trait HasTracks: Has<Vec<Track>> + Send + Sync {
|
||||||
/// Iterate over tracks with their corresponding sizes.
|
/// Iterate over tracks with their corresponding sizes.
|
||||||
fn tracks_with_sizes (
|
fn tracks_with_sizes (
|
||||||
&self,
|
&self,
|
||||||
selection: &Option<Selection>,
|
selection: &Selection,
|
||||||
editor_width: Option<usize>
|
editor_width: Option<usize>
|
||||||
) -> impl TracksSizes<'_> {
|
) -> impl TracksSizes<'_> {
|
||||||
let mut x = 0;
|
let mut x = 0;
|
||||||
let active_track = if let Some(width) = editor_width && let Some(selection) = selection {
|
let active_track = if let Some(width) = editor_width {
|
||||||
selection.track()
|
selection.track()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
@ -74,7 +74,10 @@ impl<'a> ArrangerView<'a> {
|
||||||
-> impl TracksSizes<'a>
|
-> impl TracksSizes<'a>
|
||||||
{
|
{
|
||||||
self.arrangement
|
self.arrangement
|
||||||
.tracks_with_sizes(&self.arrangement.selected, self.is_editing.then_some(20/*FIXME*/))
|
.tracks_with_sizes(
|
||||||
|
&self.arrangement.selection(),
|
||||||
|
self.is_editing.then_some(20/*FIXME*/)
|
||||||
|
)
|
||||||
.map_while(move|(t, track, x1, x2)|{
|
.map_while(move|(t, track, x1, x2)|{
|
||||||
(self.width_mid > x2 as u16).then_some((t, track, x1, x2))
|
(self.width_mid > x2 as u16).then_some((t, track, x1, x2))
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ impl<'a> ArrangerView<'a> {
|
||||||
editor: Option<&'a MidiEditor>
|
editor: Option<&'a MidiEditor>
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let is_editing = editor.is_some();
|
let is_editing = editor.is_some();
|
||||||
let selected = arrangement.selected;
|
|
||||||
let h_tracks_area = arrangement.h_tracks_area();
|
let h_tracks_area = arrangement.h_tracks_area();
|
||||||
let h_scenes_area = arrangement.h_scenes_area();
|
let h_scenes_area = arrangement.h_scenes_area();
|
||||||
let h_scenes = arrangement.h_scenes(is_editing);
|
let h_scenes = arrangement.h_scenes(is_editing);
|
||||||
|
|
@ -54,7 +53,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
outputs_count: arrangement.midi_outs.len(),
|
outputs_count: arrangement.midi_outs.len(),
|
||||||
|
|
||||||
scenes_height: h_scenes_area,
|
scenes_height: h_scenes_area,
|
||||||
scene_selected: selected.map(|s|s.scene()).flatten(),
|
scene_selected: arrangement.selection().scene(),
|
||||||
scene_count: arrangement.scenes.len(),
|
scene_count: arrangement.scenes.len(),
|
||||||
scene_last: arrangement.scenes.len().saturating_sub(1),
|
scene_last: arrangement.scenes.len().saturating_sub(1),
|
||||||
scene_scroll: Fill::y(Fixed::x(1, ScrollbarV {
|
scene_scroll: Fill::y(Fixed::x(1, ScrollbarV {
|
||||||
|
|
@ -65,7 +64,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
|
|
||||||
tracks_height: h_tracks_area,
|
tracks_height: h_tracks_area,
|
||||||
track_count: arrangement.tracks.len(),
|
track_count: arrangement.tracks.len(),
|
||||||
track_selected: selected.map(|s|s.track()).flatten(),
|
track_selected: arrangement.selection().track(),
|
||||||
track_scroll: Fill::x(Fixed::y(1, ScrollbarH {
|
track_scroll: Fill::x(Fixed::y(1, ScrollbarH {
|
||||||
offset: arrangement.track_scroll,
|
offset: arrangement.track_scroll,
|
||||||
length: h_tracks_area as usize,
|
length: h_tracks_area as usize,
|
||||||
|
|
@ -160,9 +159,9 @@ impl<'a> ArrangerView<'a> {
|
||||||
track_selected, is_editing, ..
|
track_selected, is_editing, ..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let selection = Has::<Option<Selection>>::get(self.arrangement);
|
let selection = Has::<Selection>::get(self.arrangement);
|
||||||
let selected_track = selection.map(|s|s.track()).flatten();
|
let selected_track = selection.track();
|
||||||
let selected_scene = selection.map(|s|s.scene()).flatten();
|
let selected_scene = selection.scene();
|
||||||
Tryptich::center(*scenes_height)
|
Tryptich::center(*scenes_height)
|
||||||
|
|
||||||
.left(*width_side, Map::new(
|
.left(*width_side, Map::new(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
|
||||||
/// Browses for phrase to import/export
|
/// Browses for phrase to import/export
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Browser {
|
pub struct Browser {
|
||||||
pub cwd: PathBuf,
|
pub cwd: PathBuf,
|
||||||
pub dirs: Vec<(OsString, String)>,
|
pub dirs: Vec<(OsString, String)>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue