mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-08-28 21:06:56 +02:00
parent
6a675ec964
commit
4aefaf452c
8 changed files with 260 additions and 246 deletions
|
|
@ -8,7 +8,19 @@ mod ticker; pub use self::ticker::*;
|
|||
mod timebase; pub use self::timebase::*;
|
||||
mod clock_view; pub use self::clock_view::*;
|
||||
|
||||
impl <T: AsRef<Clock>+AsMut<Clock>> HasClock for T {}
|
||||
impl App {
|
||||
/// Update memoized render of clock values.
|
||||
/// ```
|
||||
/// tek::App::default().update_clock();
|
||||
/// ```
|
||||
pub fn update_clock (&self) {
|
||||
ClockView::update_clock(
|
||||
&self.project.clock.view_cache, self.clock(), self.size.w() > 80
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: AsRef<Clock>+AsMut<Clock>> HasClock for T {}
|
||||
pub trait HasClock: AsRef<Clock> + AsMut<Clock> {
|
||||
fn clock (&self) -> &Clock { self.as_ref() }
|
||||
fn clock_mut (&mut self) -> &mut Clock { self.as_mut() }
|
||||
|
|
|
|||
|
|
@ -1,22 +1,39 @@
|
|||
use crate::{*, device::*};
|
||||
|
||||
/// Various possible dialog modes.
|
||||
///
|
||||
/// ```
|
||||
/// let dialog: tek::Dialog = Default::default();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default, PartialEq)] pub enum Dialog {
|
||||
#[default] None,
|
||||
Help(usize),
|
||||
Menu(usize, MenuItems),
|
||||
Device(usize),
|
||||
Message(Arc<str>),
|
||||
Browse(BrowseTarget, Arc<Browse>),
|
||||
Options,
|
||||
pub fn draw_dialog (
|
||||
to: &mut Tui, mut frags: std::str::Split<&str>, state: &App, dsl: &impl Expression
|
||||
) -> Drawn<u16> {
|
||||
match frags.next() {
|
||||
Some("menu") => if let Dialog::Menu(selected, items) = &state.dialog {
|
||||
//Some(iter_south(move||items.0.iter().enumerate().map(move|(index, MenuItem(item, _))|{
|
||||
//let f = if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) };
|
||||
//let b = if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) };
|
||||
//fg_bg(f, b, item.full_w().align_x().exact_h(2)).push_y(index as u16 * 4)
|
||||
//})).full_wh())
|
||||
//let items = items.clone();
|
||||
//let selected = selected;
|
||||
//Some(draw(move|to: &mut Tui|{
|
||||
//for (index, MenuItem(item, _)) in items.0.iter().enumerate() {
|
||||
//let f = if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) };
|
||||
//let b = if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) };
|
||||
//fg_bg(f, b, item.exact_h(2).full_w().align_x()).draw(to)?;
|
||||
//}
|
||||
//Ok(Some(to.area().into()))
|
||||
//}))
|
||||
Some(iter_south(move||items.0.iter().enumerate().map(move|(index, MenuItem(item, _))|{
|
||||
let f = if *selected == index { Rgb(240,200,180) } else { Rgb(200, 200, 200) };
|
||||
let b = if *selected == index { Rgb(80, 80, 50) } else { Rgb(30, 30, 30) };
|
||||
fg_bg(f, b, item).align_x()//.exact_wh(20, 3)//.exact_wh(20, 3).align_x()//.push_y(index as u16 * 4).
|
||||
})).exact_w(20).align_x())
|
||||
} else {
|
||||
None
|
||||
}.draw(to),
|
||||
_ => unimplemented!("App::interpret_word: {dsl:?} ({frags:?})"),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'n> Namespace<'n, Dialog> for App {
|
||||
fn namespace (&self, src: impl Language) -> Perhaps<Dialog> {
|
||||
impl App {
|
||||
pub fn get_dialog (&self, src: impl Language) -> Perhaps<Dialog> {
|
||||
src.word()?.map(|word|Ok(match word {
|
||||
":dialog/none" => Dialog::None,
|
||||
":dialog/options" => Dialog::Options,
|
||||
|
|
@ -47,8 +64,87 @@ impl<'n> Namespace<'n, Dialog> for App {
|
|||
)
|
||||
})).transpose()
|
||||
}
|
||||
/// Set modal dialog.
|
||||
///
|
||||
/// ```
|
||||
/// let previous: tek::Dialog = tek::App::default().set_dialog(tek::Dialog::welcome());
|
||||
/// ```
|
||||
pub fn set_dialog (&mut self, mut dialog: Dialog) -> Dialog {
|
||||
std::mem::swap(&mut self.dialog, &mut dialog);
|
||||
dialog
|
||||
}
|
||||
pub fn inc (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
||||
Ok(match (&self.dialog, axis) {
|
||||
(Dialog::None, _) => todo!(),
|
||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||
AppCommand::SetDialog { dialog: self.dialog.menu_next() }.act(self)?,
|
||||
_ => todo!()
|
||||
})
|
||||
}
|
||||
pub fn dec (&mut self, axis: &ControlAxis) -> Perhaps<AppCommand> {
|
||||
Ok(match (&self.dialog, axis) {
|
||||
(Dialog::None, _) => None,
|
||||
(Dialog::Menu(_, _), ControlAxis::Y) =>
|
||||
AppCommand::SetDialog { dialog: self.dialog.menu_prev() }.act(self)?,
|
||||
_ => todo!()
|
||||
})
|
||||
}
|
||||
pub fn confirm (&mut self) -> Perhaps<AppCommand> {
|
||||
Ok(match &self.dialog {
|
||||
Dialog::Menu(index, items) => {
|
||||
let callback = items.0[*index].1.clone();
|
||||
callback(self)?;
|
||||
None
|
||||
},
|
||||
_ => todo!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Various possible dialog modes.
|
||||
///
|
||||
/// ```
|
||||
/// let dialog: tek::Dialog = Default::default();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default, PartialEq)] pub enum Dialog {
|
||||
#[default] None,
|
||||
Help(usize),
|
||||
Menu(usize, MenuItems),
|
||||
Device(usize),
|
||||
Message(Arc<str>),
|
||||
Browse(BrowseTarget, Arc<Browse>),
|
||||
Options,
|
||||
}
|
||||
|
||||
/// List of menu items.
|
||||
///
|
||||
/// ```
|
||||
/// let items: tek::MenuItems = Default::default();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default, PartialEq)] pub struct MenuItems(
|
||||
pub Arc<[MenuItem]>
|
||||
);
|
||||
|
||||
/// An item of a menu.
|
||||
///
|
||||
/// ```
|
||||
/// let item: tek::MenuItem = Default::default();
|
||||
/// ```
|
||||
#[derive(Clone)] pub struct MenuItem(
|
||||
/// Label
|
||||
pub Arc<str>,
|
||||
/// Callback
|
||||
pub Arc<Box<dyn Fn(&mut App)->Usually<()> + Send + Sync>>
|
||||
);
|
||||
|
||||
impl_debug!(MenuItem |self, w| { write!(w, "{}", &self.0) });
|
||||
|
||||
impl_default!(MenuItem: Self("".into(), Arc::new(Box::new(|_|Ok(())))));
|
||||
|
||||
impl PartialEq for MenuItem { fn eq (&self, other: &Self) -> bool { self.0 == other.0 } }
|
||||
|
||||
impl AsRef<Arc<[MenuItem]>> for MenuItems { fn as_ref (&self) -> &Arc<[MenuItem]> { &self.0 } }
|
||||
|
||||
impl Dialog {
|
||||
/// ```
|
||||
/// let _ = tek::Dialog::welcome();
|
||||
|
|
@ -56,16 +152,20 @@ impl Dialog {
|
|||
pub fn welcome () -> Self {
|
||||
Self::Menu(1, MenuItems([
|
||||
|
||||
MenuItem("Resume session".into(), Arc::new(Box::new(|_|Ok(())))),
|
||||
MenuItem(" Resume session \n".into(), Arc::new(Box::new(|_app|{
|
||||
Ok(())
|
||||
}))),
|
||||
|
||||
MenuItem("New session".into(), Arc::new(Box::new(|app|Ok({
|
||||
MenuItem(" Create session \n".into(), Arc::new(Box::new(|app|Ok({
|
||||
app.dialog = Dialog::None;
|
||||
app.mode = Some(":arranger".into());
|
||||
})))),
|
||||
|
||||
MenuItem("Load session".into(), Arc::new(Box::new(|_|Ok(())))),
|
||||
MenuItem(" Load session \n".into(), Arc::new(Box::new(|_|{
|
||||
Ok(())
|
||||
}))),
|
||||
|
||||
MenuItem("Exit".into(), Arc::new(Box::new(|app|Ok({
|
||||
MenuItem(" Exit \n".into(), Arc::new(Box::new(|app|Ok({
|
||||
app.exit.exit();
|
||||
println!("Exited cleanly.")
|
||||
})))),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,61 @@
|
|||
#![allow(unused)]
|
||||
use crate::*;
|
||||
|
||||
impl App {
|
||||
/// Is a MIDI editor currently focused?
|
||||
///
|
||||
/// ```
|
||||
/// tek::App::default().editor_focused();
|
||||
/// ```
|
||||
pub fn editor_focused (&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Toggle MIDI editor.
|
||||
///
|
||||
/// ```
|
||||
/// tek::App::default().toggle_editor(None);
|
||||
/// ```
|
||||
pub fn toggle_editor (&mut self, value: Option<bool>) {
|
||||
//FIXME: self.editing.store(value.unwrap_or_else(||!self.is_editing()), Relaxed);
|
||||
let value = value.unwrap_or_else(||!self.editor().is_some());
|
||||
if value {
|
||||
// Create new clip in pool when entering empty cell
|
||||
if let Selection::TrackClip { track, scene } = *self.selection()
|
||||
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
&& let Some(slot) = scene.clips.get_mut(track)
|
||||
&& slot.is_none()
|
||||
&& let Some(track) = self.project.tracks.get_mut(track)
|
||||
{
|
||||
let (_index, clip) = self.pool.add_new_clip();
|
||||
// autocolor: new clip colors from scene and track color
|
||||
let color = track.color.base.mix(scene.color.base, 0.5);
|
||||
clip.write().unwrap().color = ItemColor::random_near(color, 0.2).into();
|
||||
if let Some(editor) = &mut self.project.editor {
|
||||
editor.set_clip(Some(&clip));
|
||||
}
|
||||
*slot = Some(clip.clone());
|
||||
//Some(clip)
|
||||
} else {
|
||||
//None
|
||||
}
|
||||
} else if let Selection::TrackClip { track, scene } = *self.selection()
|
||||
&& let Some(scene) = self.project.scenes.get_mut(scene)
|
||||
&& let Some(slot) = scene.clips.get_mut(track)
|
||||
&& let Some(clip) = slot.as_mut()
|
||||
{
|
||||
// Remove clip from arrangement when exiting empty clip editor
|
||||
let mut swapped = None;
|
||||
if clip.read().unwrap().count_midi_messages() == 0 {
|
||||
std::mem::swap(&mut swapped, slot);
|
||||
}
|
||||
if let Some(clip) = swapped {
|
||||
self.pool.delete_clip(&clip.read().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains state for viewing and editing a clip.
|
||||
///
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
impl_debug!(MenuItem |self, w| { write!(w, "{}", &self.0) });
|
||||
impl_default!(MenuItem: Self("".into(), Arc::new(Box::new(|_|Ok(())))));
|
||||
impl PartialEq for MenuItem { fn eq (&self, other: &Self) -> bool { self.0 == other.0 } }
|
||||
impl AsRef<Arc<[MenuItem]>> for MenuItems { fn as_ref (&self) -> &Arc<[MenuItem]> { &self.0 } }
|
||||
|
||||
/// List of menu items.
|
||||
///
|
||||
/// ```
|
||||
/// let items: tek::MenuItems = Default::default();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Default, PartialEq)] pub struct MenuItems(
|
||||
pub Arc<[MenuItem]>
|
||||
);
|
||||
|
||||
/// An item of a menu.
|
||||
///
|
||||
/// ```
|
||||
/// let item: tek::MenuItem = Default::default();
|
||||
/// ```
|
||||
#[derive(Clone)] pub struct MenuItem(
|
||||
/// Label
|
||||
pub Arc<str>,
|
||||
/// Callback
|
||||
pub Arc<Box<dyn Fn(&mut App)->Usually<()> + Send + Sync>>
|
||||
);
|
||||
Loading…
Add table
Add a link
Reference in a new issue