mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-07-17 15:56:57 +02:00
refactor: move some things with their appropriate verticals
This commit is contained in:
parent
3b1c31c78d
commit
51faa82d74
9 changed files with 197 additions and 218 deletions
185
src/app.rs
185
src/app.rs
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
pub mod audio; pub use self::audio::*;
|
pub mod audio; #[allow(unused)] pub use self::audio::*;
|
||||||
pub mod bind; pub use self::bind::*;
|
pub mod bind; pub use self::bind::*;
|
||||||
pub mod cli; pub use self::cli::*;
|
pub mod cli; pub use self::cli::*;
|
||||||
pub mod config; pub use self::config::*;
|
pub mod config; pub use self::config::*;
|
||||||
pub mod view; pub use self::view::*;
|
pub mod view; pub use self::view::*;
|
||||||
pub mod draw; pub use self::draw::*;
|
pub mod draw; pub use self::draw::*;
|
||||||
pub mod mode; pub use self::mode::*;
|
pub mod mode; pub use self::mode::*;
|
||||||
|
pub mod size; pub use self::size::*;
|
||||||
|
|
||||||
/// Total application state.
|
/// Total application state.
|
||||||
///
|
///
|
||||||
|
|
@ -81,26 +82,6 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_shared (
|
|
||||||
jack: &Jack<'static>, project: Arrangement, config: Config, mode: impl AsRef<str>
|
|
||||||
) -> Arc<RwLock<Self>> {
|
|
||||||
Arc::new(RwLock::new(App::new(jack, project, config, ":menu")))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_shared_run <W: std::io::Write + Send + Sync + 'static> (
|
|
||||||
exit: &Exit,
|
|
||||||
output: W,
|
|
||||||
jack: &Jack<'static>,
|
|
||||||
project: Arrangement,
|
|
||||||
config: Config,
|
|
||||||
mode: impl AsRef<str>
|
|
||||||
) -> Usually<(Arc<RwLock<Self>>, Task, Task)> {
|
|
||||||
let state = Self::new_shared(&jack, project, config, mode);
|
|
||||||
let keyboard = tui_input(exit.as_ref(), &state, Duration::from_millis(100))?;
|
|
||||||
let terminal = tui_output(exit.as_ref(), &state, Duration::from_millis(10), output)?;
|
|
||||||
Ok((state, keyboard, terminal))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn confirm (&mut self) -> Perhaps<AppCommand> {
|
pub fn confirm (&mut self) -> Perhaps<AppCommand> {
|
||||||
Ok(match &self.dialog {
|
Ok(match &self.dialog {
|
||||||
Dialog::Menu(index, items) => {
|
Dialog::Menu(index, items) => {
|
||||||
|
|
@ -250,65 +231,24 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [Draw] implementation for [App] handles the loaded view,
|
|
||||||
/// which is defined in terms of [dizzle] DSL.
|
|
||||||
///
|
|
||||||
/// If there is an error, the error is displayed. FIXME: overlay it
|
|
||||||
/// Then, every top-level form of the DSL description is rendered.
|
|
||||||
impl Draw<Tui> for App {
|
|
||||||
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
|
||||||
let xywh = to.area().into();
|
|
||||||
if let Some(e) = self.error.read().unwrap().as_ref() {
|
|
||||||
to.show(xywh, e.as_ref());
|
|
||||||
}
|
|
||||||
for (index, dsl) in self.mode.view.iter().enumerate() {
|
|
||||||
if let Err(e) = self.interpret(to, dsl) {
|
|
||||||
*self.error.write().unwrap() = Some(format!("view #{index}: {e}").into());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(xywh)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasClipsSize for App {
|
|
||||||
fn clips_size (&self) -> &Size { &self.project.size_inner }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HasJack<'static> for App {
|
|
||||||
fn jack (&self) -> &Jack<'static> { &self.jack }
|
|
||||||
}
|
|
||||||
|
|
||||||
def_command!(AppCommand: |app: App| {
|
|
||||||
Nop => Ok(None),
|
|
||||||
Cancel => todo!(), // TODO delegate:
|
|
||||||
Confirm => app.confirm(),
|
|
||||||
Inc { axis: ControlAxis } => app.inc(axis),
|
|
||||||
Dec { axis: ControlAxis } => app.dec(axis),
|
|
||||||
SetDialog { dialog: Dialog } => {
|
|
||||||
swap_value(&mut app.dialog, dialog, |dialog|Self::SetDialog { dialog })
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
impl_default!(AppCommand: Self::Nop);
|
|
||||||
|
|
||||||
impl_has!(Clock: |self: App|self.project.clock);
|
impl_has!(Clock: |self: App|self.project.clock);
|
||||||
impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
|
impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
|
||||||
impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
|
impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
|
||||||
impl_has!(Dialog: |self: App|self.dialog);
|
impl_has!(Dialog: |self: App|self.dialog);
|
||||||
impl_has!(Jack<'static>: |self: App|self.jack);
|
impl_has!(Jack<'static>: |self: App|self.jack);
|
||||||
impl_has!(Size: |self: App|self.size);
|
|
||||||
impl_has!(Pool: |self: App|self.pool);
|
impl_has!(Pool: |self: App|self.pool);
|
||||||
impl_has!(Selection: |self: App|self.project.selection);
|
impl_has!(Selection: |self: App|self.project.selection);
|
||||||
|
|
||||||
impl_as_ref!(Vec<Scene>: |self: App|self.project.as_ref());
|
impl_as_ref!(Vec<Scene>: |self: App|self.project.as_ref());
|
||||||
impl_as_mut!(Vec<Scene>: |self: App|self.project.as_mut());
|
impl_as_mut!(Vec<Scene>: |self: App|self.project.as_mut());
|
||||||
|
|
||||||
impl_as_ref_opt!(MidiEditor: |self: App|self.project.as_ref_opt());
|
impl_as_ref_opt!(MidiEditor: |self: App|self.project.as_ref_opt());
|
||||||
impl_as_mut_opt!(MidiEditor: |self: App|self.project.as_mut_opt());
|
impl_as_mut_opt!(MidiEditor: |self: App|self.project.as_mut_opt());
|
||||||
|
|
||||||
impl_has_clips!( |self: App|self.pool.clips);
|
impl_has_clips!( |self: App|self.pool.clips);
|
||||||
|
|
||||||
|
primitive!(u8: try_to_u8);
|
||||||
|
primitive!(u16: try_to_u16);
|
||||||
|
primitive!(usize: try_to_usize);
|
||||||
|
primitive!(isize: try_to_isize);
|
||||||
|
|
||||||
namespace!(App: Arc<str> { literal = |dsl|Ok(dsl.src()?.map(|x|x.into())); });
|
namespace!(App: Arc<str> { literal = |dsl|Ok(dsl.src()?.map(|x|x.into())); });
|
||||||
|
|
||||||
namespace!(App: u8 { literal = |dsl|try_to_u8(dsl); });
|
namespace!(App: u8 { literal = |dsl|try_to_u8(dsl); });
|
||||||
|
|
@ -374,83 +314,52 @@ namespace!(App: Option<usize> { symbol = |app| {
|
||||||
":selected/track" => app.selection().track(),
|
":selected/track" => app.selection().track(),
|
||||||
}; });
|
}; });
|
||||||
|
|
||||||
namespace!(App: Option<Arc<RwLock<MidiClip>>> {
|
namespace!(App: Option<Arc<RwLock<MidiClip>>> { symbol = |app| {
|
||||||
symbol = |app| {
|
":selected/clip" => if let Selection::TrackClip { track, scene } = app.selection() {
|
||||||
":selected/clip" => if let Selection::TrackClip { track, scene } = app.selection() {
|
app.scenes()[*scene].clips[*track].clone()
|
||||||
app.scenes()[*scene].clips[*track].clone()
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
}
|
||||||
}
|
}; });
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
impl<'a> Namespace<'a, AppCommand> for App {
|
pub fn swap_value <T: Clone + PartialEq, U> (
|
||||||
symbols!('a |app| -> AppCommand {
|
target: &mut T, value: &T, returned: impl Fn(T)->U
|
||||||
"x/inc" => AppCommand::Inc { axis: ControlAxis::X },
|
) -> Perhaps<U> {
|
||||||
"x/dec" => AppCommand::Dec { axis: ControlAxis::X },
|
if *target == *value {
|
||||||
"y/inc" => AppCommand::Inc { axis: ControlAxis::Y },
|
Ok(None)
|
||||||
"y/dec" => AppCommand::Dec { axis: ControlAxis::Y },
|
} else {
|
||||||
"confirm" => AppCommand::Confirm,
|
let mut value = value.clone();
|
||||||
"cancel" => AppCommand::Cancel,
|
std::mem::swap(target, &mut value);
|
||||||
});
|
Ok(Some(returned(value)))
|
||||||
}
|
|
||||||
|
|
||||||
impl HasTrackScroll for App {
|
|
||||||
fn track_scroll (&self) -> usize {
|
|
||||||
self.project.track_scroll()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasSceneScroll for App {
|
pub fn toggle_bool <U> (
|
||||||
fn scene_scroll (&self) -> usize {
|
target: &mut bool, value: &Option<bool>, returned: impl Fn(Option<bool>)->U
|
||||||
self.project.scene_scroll()
|
) -> Perhaps<U> {
|
||||||
|
let mut value = value.unwrap_or(!*target);
|
||||||
|
if value == *target {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
std::mem::swap(target, &mut value);
|
||||||
|
Ok(Some(returned(Some(value))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScenesView for App {
|
pub fn scan (dir: &PathBuf) -> Usually<(Vec<OsString>, Vec<OsString>)> {
|
||||||
fn w_mid (&self) -> u16 {
|
let (mut subdirs, mut files) = std::fs::read_dir(dir)?
|
||||||
(self.size.w() as u16).saturating_sub(self.w_side())
|
.fold((vec!["..".into()], vec![]), |(mut subdirs, mut files), entry|{
|
||||||
}
|
let entry = entry.expect("failed to read drectory entry");
|
||||||
fn w_side (&self) -> u16 {
|
let meta = entry.metadata().expect("failed to read entry metadata");
|
||||||
20
|
if meta.is_file() {
|
||||||
}
|
files.push(entry.file_name());
|
||||||
fn h_scenes (&self) -> u16 {
|
} else if meta.is_dir() {
|
||||||
(self.size.h() as u16).saturating_sub(20)
|
subdirs.push(entry.file_name());
|
||||||
}
|
}
|
||||||
|
(subdirs, files)
|
||||||
|
});
|
||||||
|
subdirs.sort();
|
||||||
|
files.sort();
|
||||||
|
Ok((subdirs, files))
|
||||||
}
|
}
|
||||||
|
|
||||||
//impl_handle!(TuiIn: |self: App, input|{
|
|
||||||
//let commands = tek_collect_commands(self, input)?;
|
|
||||||
//let history = tek_execute_commands(self, commands)?;
|
|
||||||
//self.history.extend(history.into_iter());
|
|
||||||
//Ok(None)
|
|
||||||
//});
|
|
||||||
|
|
||||||
//fn tek_collect_commands (app: &App, input: &TuiIn) -> Usually<Vec<AppCommand>> {
|
|
||||||
//let mut commands = vec![];
|
|
||||||
//for id in app.mode.keys.iter() {
|
|
||||||
//if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref())
|
|
||||||
//&& let Some(bindings) = event_map.query(input.event()) {
|
|
||||||
//for binding in bindings {
|
|
||||||
//for command in binding.commands.iter() {
|
|
||||||
//if let Some(command) = app.namespace(command)? as Option<AppCommand> {
|
|
||||||
//commands.push(command)
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//Ok(commands)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//fn tek_execute_commands (
|
|
||||||
//app: &mut App, commands: Vec<AppCommand>
|
|
||||||
//) -> Usually<Vec<(AppCommand, Option<AppCommand>)>> {
|
|
||||||
//let mut history = vec![];
|
|
||||||
//for command in commands.into_iter() {
|
|
||||||
//let result = command.act(app);
|
|
||||||
//match result { Err(err) => { history.push((command, None)); return Err(err) }
|
|
||||||
//Ok(undo) => { history.push((command, undo)); } };
|
|
||||||
//}
|
|
||||||
//Ok(history)
|
|
||||||
//}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
impl HasJack<'static> for App { fn jack (&self) -> &Jack<'static> { &self.jack } }
|
||||||
|
|
||||||
impl_audio!(App: tek_jack_process, tek_jack_event);
|
impl_audio!(App: tek_jack_process, tek_jack_event);
|
||||||
|
|
||||||
fn tek_jack_process (state: &mut App, client: &Client, scope: &ProcessScope) -> Control {
|
fn tek_jack_process (state: &mut App, client: &Client, scope: &ProcessScope) -> Control {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
tui_keys!(|self: App, input| {
|
||||||
|
todo!()
|
||||||
|
});
|
||||||
|
|
||||||
/// A control axis.
|
/// A control axis.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -128,3 +133,63 @@ impl<E: Clone + Ord, C> Bind<E, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_debug!(Condition |self, w| { write!(w, "*") });
|
impl_debug!(Condition |self, w| { write!(w, "*") });
|
||||||
|
|
||||||
|
impl_default!(AppCommand: Self::Nop);
|
||||||
|
|
||||||
|
def_command!(AppCommand: |app: App| {
|
||||||
|
Nop => Ok(None),
|
||||||
|
Cancel => todo!(), // TODO delegate:
|
||||||
|
Confirm => app.confirm(),
|
||||||
|
Inc { axis: ControlAxis } => app.inc(axis),
|
||||||
|
Dec { axis: ControlAxis } => app.dec(axis),
|
||||||
|
SetDialog { dialog: Dialog } => {
|
||||||
|
swap_value(&mut app.dialog, dialog, |dialog|Self::SetDialog { dialog })
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
impl<'a> Namespace<'a, AppCommand> for App {
|
||||||
|
symbols!('a |app| -> AppCommand {
|
||||||
|
"x/inc" => AppCommand::Inc { axis: ControlAxis::X },
|
||||||
|
"x/dec" => AppCommand::Dec { axis: ControlAxis::X },
|
||||||
|
"y/inc" => AppCommand::Inc { axis: ControlAxis::Y },
|
||||||
|
"y/dec" => AppCommand::Dec { axis: ControlAxis::Y },
|
||||||
|
"confirm" => AppCommand::Confirm,
|
||||||
|
"cancel" => AppCommand::Cancel,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//impl_handle!(TuiIn: |self: App, input|{
|
||||||
|
//let commands = tek_collect_commands(self, input)?;
|
||||||
|
//let history = tek_execute_commands(self, commands)?;
|
||||||
|
//self.history.extend(history.into_iter());
|
||||||
|
//Ok(None)
|
||||||
|
//});
|
||||||
|
|
||||||
|
//fn tek_collect_commands (app: &App, input: &TuiIn) -> Usually<Vec<AppCommand>> {
|
||||||
|
//let mut commands = vec![];
|
||||||
|
//for id in app.mode.keys.iter() {
|
||||||
|
//if let Some(event_map) = app.config.binds.clone().read().unwrap().get(id.as_ref())
|
||||||
|
//&& let Some(bindings) = event_map.query(input.event()) {
|
||||||
|
//for binding in bindings {
|
||||||
|
//for command in binding.commands.iter() {
|
||||||
|
//if let Some(command) = app.namespace(command)? as Option<AppCommand> {
|
||||||
|
//commands.push(command)
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//Ok(commands)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//fn tek_execute_commands (
|
||||||
|
//app: &mut App, commands: Vec<AppCommand>
|
||||||
|
//) -> Usually<Vec<(AppCommand, Option<AppCommand>)>> {
|
||||||
|
//let mut history = vec![];
|
||||||
|
//for command in commands.into_iter() {
|
||||||
|
//let result = command.act(app);
|
||||||
|
//match result { Err(err) => { history.push((command, None)); return Err(err) }
|
||||||
|
//Ok(undo) => { history.push((command, undo)); } };
|
||||||
|
//}
|
||||||
|
//Ok(history)
|
||||||
|
//}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ impl Action {
|
||||||
//return Ok(())
|
//return Ok(())
|
||||||
//}
|
//}
|
||||||
// Initialize the app state
|
// Initialize the app state
|
||||||
let app = App::new_shared(&jack, proj, config, ":menu");
|
let app = Arc::new(RwLock::new(App::new(&jack, proj, config, ":menu")));
|
||||||
//if matches!(self, Action::Headless) {
|
//if matches!(self, Action::Headless) {
|
||||||
//// TODO: Headless mode (daemon + client over IPC, then over network...)
|
//// TODO: Headless mode (daemon + client over IPC, then over network...)
|
||||||
//println!("todo headless");
|
//println!("todo headless");
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,26 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
/// The [Draw] implementation for [App] handles the loaded view,
|
||||||
|
/// which is defined in terms of [dizzle] DSL.
|
||||||
|
///
|
||||||
|
/// If there is an error, the error is displayed. FIXME: overlay it
|
||||||
|
/// Then, every top-level form of the DSL description is rendered.
|
||||||
|
impl Draw<Tui> for App {
|
||||||
|
fn draw (self, to: &mut Tui) -> Usually<XYWH<u16>> {
|
||||||
|
let xywh = to.area().into();
|
||||||
|
if let Some(e) = self.error.read().unwrap().as_ref() {
|
||||||
|
to.show(xywh, e.as_ref())?;
|
||||||
|
}
|
||||||
|
for (index, dsl) in self.mode.view.iter().enumerate() {
|
||||||
|
if let Err(e) = self.interpret(to, dsl) {
|
||||||
|
*self.error.write().unwrap() = Some(format!("view #{index}: {e}").into());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(xywh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Interpret<Tui, XYWH<u16>> for App {
|
impl Interpret<Tui, XYWH<u16>> for App {
|
||||||
fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression)
|
fn interpret_expr <'a> (&'a self, to: &mut Tui, lang: &'a impl Expression)
|
||||||
-> Usually<XYWH<u16>>
|
-> Usually<XYWH<u16>>
|
||||||
|
|
|
||||||
57
src/app/size.rs
Normal file
57
src/app/size.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl_has!(Size: |self: App|self.size);
|
||||||
|
|
||||||
|
/// Define a type alias for iterators of sized items (columns).
|
||||||
|
macro_rules! def_sizes_iter {
|
||||||
|
($Type:ident => $($Item:ty),+) => {
|
||||||
|
pub trait $Type<'a> =
|
||||||
|
Iterator<Item=(usize, $(&'a $Item,)+ usize, usize)> + Send + Sync + 'a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def_sizes_iter!(InputsSizes => MidiInput);
|
||||||
|
def_sizes_iter!(OutputsSizes => MidiOutput);
|
||||||
|
def_sizes_iter!(PortsSizes => Arc<str>, [Connect]);
|
||||||
|
def_sizes_iter!(ScenesSizes => Scene);
|
||||||
|
def_sizes_iter!(TracksSizes => Track);
|
||||||
|
|
||||||
|
pub(crate) fn track_width (_index: usize, track: &Track) -> u16 {
|
||||||
|
track.width as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HasWidth {
|
||||||
|
const MIN_WIDTH: usize;
|
||||||
|
/// Increment track width.
|
||||||
|
fn width_inc (&mut self);
|
||||||
|
/// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH].
|
||||||
|
fn width_dec (&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasClipsSize for App {
|
||||||
|
fn clips_size (&self) -> &Size { &self.project.size_inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasTrackScroll for App {
|
||||||
|
fn track_scroll (&self) -> usize {
|
||||||
|
self.project.track_scroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasSceneScroll for App {
|
||||||
|
fn scene_scroll (&self) -> usize {
|
||||||
|
self.project.scene_scroll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScenesView for App {
|
||||||
|
fn w_mid (&self) -> u16 {
|
||||||
|
(self.size.w() as u16).saturating_sub(self.w_side())
|
||||||
|
}
|
||||||
|
fn w_side (&self) -> u16 {
|
||||||
|
20
|
||||||
|
}
|
||||||
|
fn h_scenes (&self) -> u16 {
|
||||||
|
(self.size.h() as u16).saturating_sub(20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
tui_view!(|self: App| {
|
||||||
|
""
|
||||||
|
});
|
||||||
|
|
||||||
/// Collection of custom view definitions.
|
/// Collection of custom view definitions.
|
||||||
pub type Views = Arc<RwLock<BTreeMap<Arc<str>, Arc<str>>>>;
|
pub type Views = Arc<RwLock<BTreeMap<Arc<str>, Arc<str>>>>;
|
||||||
|
|
||||||
|
|
|
||||||
79
src/tek.rs
79
src/tek.rs
|
|
@ -98,87 +98,8 @@ pub fn main () -> Usually<()> {
|
||||||
}).map(|_|())
|
}).map(|_|())
|
||||||
}
|
}
|
||||||
|
|
||||||
tui_keys!(|self: App, input| {
|
|
||||||
todo!()
|
|
||||||
});
|
|
||||||
|
|
||||||
tui_view!(|self: App| {
|
|
||||||
""
|
|
||||||
});
|
|
||||||
|
|
||||||
pub fn swap_value <T: Clone + PartialEq, U> (
|
|
||||||
target: &mut T, value: &T, returned: impl Fn(T)->U
|
|
||||||
) -> Perhaps<U> {
|
|
||||||
if *target == *value {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
let mut value = value.clone();
|
|
||||||
std::mem::swap(target, &mut value);
|
|
||||||
Ok(Some(returned(value)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle_bool <U> (
|
|
||||||
target: &mut bool, value: &Option<bool>, returned: impl Fn(Option<bool>)->U
|
|
||||||
) -> Perhaps<U> {
|
|
||||||
let mut value = value.unwrap_or(!*target);
|
|
||||||
if value == *target {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
std::mem::swap(target, &mut value);
|
|
||||||
Ok(Some(returned(Some(value))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//take!(DeviceCommand|state: Arrangement, iter|state.selected_device().as_ref()
|
//take!(DeviceCommand|state: Arrangement, iter|state.selected_device().as_ref()
|
||||||
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
|
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
|
||||||
|
|
||||||
fn scan (dir: &PathBuf) -> Usually<(Vec<OsString>, Vec<OsString>)> {
|
|
||||||
let (mut subdirs, mut files) = std::fs::read_dir(dir)?
|
|
||||||
.fold((vec!["..".into()], vec![]), |(mut subdirs, mut files), entry|{
|
|
||||||
let entry = entry.expect("failed to read drectory entry");
|
|
||||||
let meta = entry.metadata().expect("failed to read entry metadata");
|
|
||||||
if meta.is_file() {
|
|
||||||
files.push(entry.file_name());
|
|
||||||
} else if meta.is_dir() {
|
|
||||||
subdirs.push(entry.file_name());
|
|
||||||
}
|
|
||||||
(subdirs, files)
|
|
||||||
});
|
|
||||||
subdirs.sort();
|
|
||||||
files.sort();
|
|
||||||
Ok((subdirs, files))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn track_width (_index: usize, track: &Track) -> u16 {
|
|
||||||
track.width as u16
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define a type alias for iterators of sized items (columns).
|
|
||||||
macro_rules! def_sizes_iter {
|
|
||||||
($Type:ident => $($Item:ty),+) => {
|
|
||||||
pub trait $Type<'a> =
|
|
||||||
Iterator<Item=(usize, $(&'a $Item,)+ usize, usize)> + Send + Sync + 'a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive!(u8: try_to_u8);
|
|
||||||
primitive!(u16: try_to_u16);
|
|
||||||
primitive!(usize: try_to_usize);
|
|
||||||
primitive!(isize: try_to_isize);
|
|
||||||
pub trait HasWidth {
|
|
||||||
const MIN_WIDTH: usize;
|
|
||||||
/// Increment track width.
|
|
||||||
fn width_inc (&mut self);
|
|
||||||
/// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH].
|
|
||||||
fn width_dec (&mut self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod device; pub use self::device::*;
|
pub mod device; pub use self::device::*;
|
||||||
pub mod app; pub use self::app::*;
|
pub mod app; pub use self::app::*;
|
||||||
|
|
||||||
def_sizes_iter!(InputsSizes => MidiInput);
|
|
||||||
def_sizes_iter!(OutputsSizes => MidiOutput);
|
|
||||||
def_sizes_iter!(PortsSizes => Arc<str>, [Connect]);
|
|
||||||
def_sizes_iter!(ScenesSizes => Scene);
|
|
||||||
def_sizes_iter!(TracksSizes => Track);
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue