mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: modularize dialog
This commit is contained in:
parent
baad8254a2
commit
3e9545fe26
17 changed files with 171 additions and 173 deletions
|
|
@ -3,7 +3,7 @@
|
||||||
(info "A session grid.")
|
(info "A session grid.")
|
||||||
|
|
||||||
(keys
|
(keys
|
||||||
(layer-if :is-editing "./keys_editor.edn")
|
(layer-if :focus-editor "./keys_editor.edn")
|
||||||
(layer-if :focus-message "./keys_message.edn")
|
(layer-if :focus-message "./keys_message.edn")
|
||||||
(layer-if :focus-device-add "./keys_device_add.edn")
|
(layer-if :focus-device-add "./keys_device_add.edn")
|
||||||
(layer-if :focus-browser "./keys_browser.edn")
|
(layer-if :focus-browser "./keys_browser.edn")
|
||||||
|
|
@ -26,6 +26,6 @@
|
||||||
(bsp/s :view-tracks-devices
|
(bsp/s :view-tracks-devices
|
||||||
(bsp/s :view-tracks-outputs
|
(bsp/s :view-tracks-outputs
|
||||||
(bsp/s :view-tracks-names
|
(bsp/s :view-tracks-names
|
||||||
(fill/xy (either :is-editing
|
(fill/xy (either :focus-editor
|
||||||
(bsp/e :view-scenes-names :view-editor)
|
(bsp/e :view-scenes-names :view-editor)
|
||||||
:view-scenes)))))))))))
|
:view-scenes)))))))))))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
(@c color)
|
(@c color)
|
||||||
(@q launch)
|
(@q launch)
|
||||||
(@t select :select-track-header)
|
|
||||||
(@s select :select-scene-header)
|
|
||||||
(@tab project edit)
|
(@tab project edit)
|
||||||
(@enter project edit)
|
(@enter project edit)
|
||||||
(@escape project home)
|
(@escape project home)
|
||||||
|
|
@ -15,3 +13,5 @@
|
||||||
(@down select :select-scene-next)
|
(@down select :select-scene-next)
|
||||||
(@left select :select-track-prev)
|
(@left select :select-track-prev)
|
||||||
(@right select :select-track-next)
|
(@right select :select-track-next)
|
||||||
|
(@t select :select-track)
|
||||||
|
(@s select :select-scene)
|
||||||
|
|
|
||||||
1
config/keys_arranger_clip.edn
Normal file
1
config/keys_arranger_clip.edn
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
0
config/keys_arranger_device.edn
Normal file
0
config/keys_arranger_device.edn
Normal file
0
config/keys_arranger_input.rs
Normal file
0
config/keys_arranger_input.rs
Normal file
0
config/keys_arranger_output.rs
Normal file
0
config/keys_arranger_output.rs
Normal file
0
config/keys_arranger_scene.edn
Normal file
0
config/keys_arranger_scene.edn
Normal file
0
config/keys_arranger_track.rs
Normal file
0
config/keys_arranger_track.rs
Normal file
|
|
@ -1,27 +1,19 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
type MaybeClip = Option<Arc<RwLock<MidiClip>>>;
|
type MaybeClip = Option<Arc<RwLock<MidiClip>>>;
|
||||||
|
|
||||||
macro_rules! ns { ($C:ty, $s:expr, $a:expr, $W:expr) => { <$C>::try_from_expr($s, $a).map($W) } }
|
macro_rules! ns { ($C:ty, $s:expr, $a:expr, $W:expr) => { <$C>::try_from_expr($s, $a).map($W) } }
|
||||||
macro_rules! cmd { ($cmd:expr) => {{ $cmd; None }}; }
|
macro_rules! cmd { ($cmd:expr) => {{ $cmd; None }}; }
|
||||||
macro_rules! cmd_todo { ($msg:literal) => {{ println!($msg); None }}; }
|
macro_rules! cmd_todo { ($msg:literal) => {{ println!($msg); None }}; }
|
||||||
|
|
||||||
handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.command(self, input) {
|
handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.command(self, input) {
|
||||||
let undo = command.execute(self)?;
|
// FIXME failed commands not persisted in undo history
|
||||||
if let Some(undo) = undo {
|
let undo = command.clone().execute(self)?;
|
||||||
self.history.push(undo);
|
self.history.push((command, undo));
|
||||||
}
|
|
||||||
Some(true)
|
Some(true)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}));
|
}));
|
||||||
|
#[tengri_proc::command(App)]
|
||||||
#[tengri_proc::command(App)] impl AppCommand {
|
impl AppCommand {
|
||||||
fn dialog (app: &mut App, dialog: Option<Dialog>) -> Perhaps<Self> {
|
|
||||||
app.toggle_dialog(dialog);
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
fn cancel_dialog (app: &mut App) -> Perhaps<Self> {
|
fn cancel_dialog (app: &mut App) -> Perhaps<Self> {
|
||||||
app.toggle_dialog(None);
|
app.toggle_dialog(None);
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
|
@ -73,11 +65,8 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
app.tracks_stop_all();
|
app.tracks_stop_all();
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
fn sampler (app: &mut App, command: SamplerCommand) -> Perhaps<Self> {
|
fn dialog (app: &mut App, command: DialogCommand) -> Perhaps<Self> {
|
||||||
Ok(app.project.sampler_mut()
|
Ok(command.delegate(&mut app.dialog, |command|Self::Dialog{command})?)
|
||||||
.map(|s|command.delegate(s, |command|Self::Sampler{command}))
|
|
||||||
.transpose()?
|
|
||||||
.flatten())
|
|
||||||
}
|
}
|
||||||
fn project (app: &mut App, command: ArrangementCommand) -> Perhaps<Self> {
|
fn project (app: &mut App, command: ArrangementCommand) -> Perhaps<Self> {
|
||||||
Ok(command.delegate(&mut app.project, |command|Self::Project{command})?)
|
Ok(command.delegate(&mut app.project, |command|Self::Project{command})?)
|
||||||
|
|
@ -85,9 +74,6 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
fn clock (app: &mut App, command: ClockCommand) -> Perhaps<Self> {
|
fn clock (app: &mut App, command: ClockCommand) -> Perhaps<Self> {
|
||||||
Ok(command.execute(app.clock_mut())?.map(|command|Self::Clock{command}))
|
Ok(command.execute(app.clock_mut())?.map(|command|Self::Clock{command}))
|
||||||
}
|
}
|
||||||
fn message (app: &mut App, command: MessageCommand) -> Perhaps<Self> {
|
|
||||||
Ok(command.delegate(app, |command|Self::Message{command})?)
|
|
||||||
}
|
|
||||||
fn editor (app: &mut App, command: MidiEditCommand) -> Perhaps<Self> {
|
fn editor (app: &mut App, command: MidiEditCommand) -> Perhaps<Self> {
|
||||||
Ok(if let Some(editor) = app.editor_mut() {
|
Ok(if let Some(editor) = app.editor_mut() {
|
||||||
let undo = command.clone().delegate(editor, |command|AppCommand::Editor{command})?;
|
let undo = command.clone().delegate(editor, |command|AppCommand::Editor{command})?;
|
||||||
|
|
@ -102,6 +88,12 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
fn sampler (app: &mut App, command: SamplerCommand) -> Perhaps<Self> {
|
||||||
|
Ok(app.project.sampler_mut()
|
||||||
|
.map(|s|command.delegate(s, |command|Self::Sampler{command}))
|
||||||
|
.transpose()?
|
||||||
|
.flatten())
|
||||||
|
}
|
||||||
fn pool (app: &mut App, command: PoolCommand) -> Perhaps<Self> {
|
fn pool (app: &mut App, command: PoolCommand) -> Perhaps<Self> {
|
||||||
let undo = command.clone().delegate(
|
let undo = command.clone().delegate(
|
||||||
&mut app.pool,
|
&mut app.pool,
|
||||||
|
|
@ -121,40 +113,33 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm
|
||||||
Ok(undo)
|
Ok(undo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'state> Context<'state, ClockCommand> for App {
|
impl<'state> Context<'state, ClockCommand> for App {
|
||||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ClockCommand> {
|
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ClockCommand> {
|
||||||
Context::get(&self.clock(), iter)
|
Context::get(&self.clock(), iter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'state> Context<'state, MidiEditCommand> for App {
|
impl<'state> Context<'state, MidiEditCommand> for App {
|
||||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<MidiEditCommand> {
|
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<MidiEditCommand> {
|
||||||
self.editor().map(|e|Context::get(e, iter)).flatten()
|
self.editor().map(|e|Context::get(e, iter)).flatten()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
||||||
Context::get(&self.pool, iter)
|
Context::get(&self.pool, iter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'state> Context<'state, SamplerCommand> for App {
|
impl<'state> Context<'state, SamplerCommand> for App {
|
||||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<SamplerCommand> {
|
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<SamplerCommand> {
|
||||||
self.project.sampler().map(|p|Context::get(p, iter)).flatten()
|
self.project.sampler().map(|p|Context::get(p, iter)).flatten()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'state> Context<'state, ArrangementCommand> for App {
|
impl<'state> Context<'state, ArrangementCommand> for App {
|
||||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ArrangementCommand> {
|
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ArrangementCommand> {
|
||||||
Context::get(&self.project, iter)
|
Context::get(&self.project, iter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<'state> Context<'state, DialogCommand> for App {
|
||||||
#[tengri_proc::command(App)] impl MessageCommand {
|
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<DialogCommand> {
|
||||||
fn dismiss (app: &mut App) -> Perhaps<Self> {
|
Context::get(&self, iter)
|
||||||
app.dialog = None;
|
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub struct App {
|
||||||
/// Contains the currently edited musical arrangement
|
/// Contains the currently edited musical arrangement
|
||||||
pub project: Arrangement,
|
pub project: Arrangement,
|
||||||
/// Undo history
|
/// Undo history
|
||||||
pub history: Vec<AppCommand>,
|
pub history: Vec<(AppCommand, Option<AppCommand>)>,
|
||||||
// Dialog overlay
|
// Dialog overlay
|
||||||
pub dialog: Option<Dialog>,
|
pub dialog: Option<Dialog>,
|
||||||
// Cache of formatted strings
|
// Cache of formatted strings
|
||||||
|
|
@ -179,9 +179,6 @@ impl App {
|
||||||
fn focus_editor (&self) -> bool {
|
fn focus_editor (&self) -> bool {
|
||||||
self.project.editor.is_some()
|
self.project.editor.is_some()
|
||||||
}
|
}
|
||||||
fn is_editing (&self) -> bool {
|
|
||||||
self.project.editor.is_some()
|
|
||||||
}
|
|
||||||
fn focus_message (&self) -> bool {
|
fn focus_message (&self) -> bool {
|
||||||
matches!(self.dialog, Some(Dialog::Message(..)))
|
matches!(self.dialog, Some(Dialog::Message(..)))
|
||||||
}
|
}
|
||||||
|
|
@ -192,16 +189,20 @@ impl App {
|
||||||
self.browser().is_some()
|
self.browser().is_some()
|
||||||
}
|
}
|
||||||
fn focus_clip (&self) -> bool {
|
fn focus_clip (&self) -> bool {
|
||||||
!self.is_editing() && self.selection().is_clip()
|
!self.focus_editor() && matches!(self.selection(),
|
||||||
|
Selection::TrackClip{..})
|
||||||
}
|
}
|
||||||
fn focus_track (&self) -> bool {
|
fn focus_track (&self) -> bool {
|
||||||
!self.is_editing() && self.selection().is_track()
|
!self.focus_editor() && matches!(self.selection(),
|
||||||
|
Selection::Track(..))
|
||||||
}
|
}
|
||||||
fn focus_scene (&self) -> bool {
|
fn focus_scene (&self) -> bool {
|
||||||
!self.is_editing() && self.selection().is_scene()
|
!self.focus_editor() && matches!(self.selection(),
|
||||||
|
Selection::Scene(..))
|
||||||
}
|
}
|
||||||
fn focus_mix (&self) -> bool {
|
fn focus_mix (&self) -> bool {
|
||||||
!self.is_editing() && self.selection().is_mix()
|
!self.focus_editor() && matches!(self.selection(),
|
||||||
|
Selection::Mix)
|
||||||
}
|
}
|
||||||
fn focus_pool_import (&self) -> bool {
|
fn focus_pool_import (&self) -> bool {
|
||||||
matches!(self.pool.mode, Some(PoolMode::Import(..)))
|
matches!(self.pool.mode, Some(PoolMode::Import(..)))
|
||||||
|
|
@ -260,29 +261,32 @@ impl App {
|
||||||
fn scene_count (&self) -> usize {
|
fn scene_count (&self) -> usize {
|
||||||
self.scenes().len()
|
self.scenes().len()
|
||||||
}
|
}
|
||||||
fn scene_selection (&self) -> Option<usize> {
|
fn scene_selected (&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_selected (&self) -> Option<usize> {
|
||||||
self.selection().track()
|
self.selection().track()
|
||||||
}
|
}
|
||||||
|
fn select_scene (&self) -> Selection {
|
||||||
|
self.selection().select_scene(self.tracks().len())
|
||||||
|
}
|
||||||
fn select_scene_next (&self) -> Selection {
|
fn select_scene_next (&self) -> Selection {
|
||||||
self.selection().scene_next(self.scenes().len())
|
self.selection().select_scene_next(self.scenes().len())
|
||||||
}
|
}
|
||||||
fn select_scene_prev (&self) -> Selection {
|
fn select_scene_prev (&self) -> Selection {
|
||||||
self.selection().scene_prev()
|
self.selection().select_scene_prev()
|
||||||
}
|
}
|
||||||
fn select_track_header (&self) -> Selection {
|
fn select_track (&self) -> Selection {
|
||||||
self.selection().track_header(self.tracks().len())
|
self.selection().select_track(self.tracks().len())
|
||||||
}
|
}
|
||||||
fn select_track_next (&self) -> Selection {
|
fn select_track_next (&self) -> Selection {
|
||||||
self.selection().track_next(self.tracks().len())
|
self.selection().select_track_next(self.tracks().len())
|
||||||
}
|
}
|
||||||
fn select_track_prev (&self) -> Selection {
|
fn select_track_prev (&self) -> Selection {
|
||||||
self.selection().track_prev()
|
self.selection().select_track_prev()
|
||||||
}
|
}
|
||||||
fn clip_selected (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
fn clip_selected (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||||
match self.selection() {
|
match self.selection() {
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ impl App {
|
||||||
self.tracks(),
|
self.tracks(),
|
||||||
self.scenes()
|
self.scenes()
|
||||||
)))));
|
)))));
|
||||||
|
add(&FieldV(theme, "History", Fill::x(Align::w(format!("{}", self.history.len())))));
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,18 +39,6 @@ pub enum Selection {
|
||||||
|
|
||||||
/// Focus identification methods
|
/// Focus identification methods
|
||||||
impl Selection {
|
impl Selection {
|
||||||
pub fn is_mix (&self) -> bool {
|
|
||||||
matches!(self, Self::Mix)
|
|
||||||
}
|
|
||||||
pub fn is_track (&self) -> bool {
|
|
||||||
matches!(self, Self::Track(_))
|
|
||||||
}
|
|
||||||
pub fn is_scene (&self) -> bool {
|
|
||||||
matches!(self, Self::Scene(_))
|
|
||||||
}
|
|
||||||
pub fn is_clip (&self) -> bool {
|
|
||||||
matches!(self, Self::TrackClip {..})
|
|
||||||
}
|
|
||||||
pub fn track (&self) -> Option<usize> {
|
pub fn track (&self) -> Option<usize> {
|
||||||
use Selection::*;
|
use Selection::*;
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -62,7 +50,7 @@ impl Selection {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn track_header (&self, track_count: usize) -> Self {
|
pub fn select_track (&self, track_count: usize) -> Self {
|
||||||
use Selection::*;
|
use Selection::*;
|
||||||
match self {
|
match self {
|
||||||
Mix => Track(0),
|
Mix => Track(0),
|
||||||
|
|
@ -72,7 +60,7 @@ impl Selection {
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn track_next (&self, len: usize) -> Self {
|
pub fn select_track_next (&self, len: usize) -> Self {
|
||||||
use Selection::*;
|
use Selection::*;
|
||||||
match self {
|
match self {
|
||||||
Mix => Track(0),
|
Mix => Track(0),
|
||||||
|
|
@ -90,7 +78,7 @@ impl Selection {
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn track_prev (&self) -> Self {
|
pub fn select_track_prev (&self) -> Self {
|
||||||
use Selection::*;
|
use Selection::*;
|
||||||
match self {
|
match self {
|
||||||
Mix => Mix,
|
Mix => Mix,
|
||||||
|
|
@ -109,7 +97,16 @@ impl Selection {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn scene_next (&self, len: usize) -> Self {
|
pub fn select_scene (&self, scene_count: usize) -> Self {
|
||||||
|
use Selection::*;
|
||||||
|
match self {
|
||||||
|
Mix | Track(_) => Scene(0),
|
||||||
|
Scene(s) => Scene((s + 1) % scene_count),
|
||||||
|
TrackClip { scene, .. } => Track(*scene),
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn select_scene_next (&self, len: usize) -> Self {
|
||||||
use Selection::*;
|
use Selection::*;
|
||||||
match self {
|
match self {
|
||||||
Mix => Scene(0),
|
Mix => Scene(0),
|
||||||
|
|
@ -127,7 +124,7 @@ impl Selection {
|
||||||
_ => todo!()
|
_ => todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn scene_prev (&self) -> Self {
|
pub fn select_scene_prev (&self) -> Self {
|
||||||
use Selection::*;
|
use Selection::*;
|
||||||
match self {
|
match self {
|
||||||
Mix | Scene(0) => Mix,
|
Mix | Scene(0) => Mix,
|
||||||
|
|
@ -142,11 +139,9 @@ impl Selection {
|
||||||
use Selection::*;
|
use Selection::*;
|
||||||
format!("{}", match self {
|
format!("{}", match self {
|
||||||
Mix => "Everything".to_string(),
|
Mix => "Everything".to_string(),
|
||||||
Scene(s) => scenes.get(*s)
|
Scene(s) => scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name))
|
||||||
.map(|scene|format!("S{s}: {}", &scene.name))
|
|
||||||
.unwrap_or_else(||"S??".into()),
|
.unwrap_or_else(||"S??".into()),
|
||||||
Track(t) => tracks.get(*t)
|
Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name))
|
||||||
.map(|track|format!("T{t}: {}", &track.name))
|
|
||||||
.unwrap_or_else(||"T??".into()),
|
.unwrap_or_else(||"T??".into()),
|
||||||
TrackClip { track, scene } => match (tracks.get(*track), scenes.get(*scene)) {
|
TrackClip { track, scene } => match (tracks.get(*track), scenes.get(*scene)) {
|
||||||
(Some(_), Some(s)) => match s.clip(*track) {
|
(Some(_), Some(s)) => match s.clip(*track) {
|
||||||
|
|
@ -159,6 +154,3 @@ impl Selection {
|
||||||
}).into()
|
}).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arrangement {
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -90,8 +90,11 @@ impl Arrangement {
|
||||||
for (index, track, x1, x2) in self.tracks_with_sizes() {
|
for (index, track, x1, x2) in self.tracks_with_sizes() {
|
||||||
add(&Fixed::xy(self.track_width(index, track), h + 1,
|
add(&Fixed::xy(self.track_width(index, track), h + 1,
|
||||||
Tui::bg(track.color.dark.rgb, Align::nw(Map::south(2, move||0..h,
|
Tui::bg(track.color.dark.rgb, Align::nw(Map::south(2, move||0..h,
|
||||||
|_, index|Fixed::xy(track.width as u16, 2, Tui::bg(ItemTheme::G[32].base.rgb,
|
|_, index|Fixed::xy(track.width as u16, 2,
|
||||||
Align::nw(format!(" · {}", "--")))))))));
|
Tui::fg_bg(
|
||||||
|
ItemTheme::G[32].lightest.rgb,
|
||||||
|
ItemTheme::G[32].dark.rgb,
|
||||||
|
Align::nw(format!(" · {}", "--")))))))));
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
mod dialog_api; pub use self::dialog_api::*;
|
||||||
|
mod dialog_view; pub use self::dialog_view::*;
|
||||||
|
|
||||||
/// Various possible dialog overlays
|
/// Various possible dialog overlays
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -16,98 +18,3 @@ pub enum Dialog {
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
FailedToAddDevice,
|
FailedToAddDevice,
|
||||||
}
|
}
|
||||||
|
|
||||||
content!(TuiOut: |self: Message| match self {
|
|
||||||
Self::FailedToAddDevice => "Failed to add device."
|
|
||||||
});
|
|
||||||
|
|
||||||
content!(TuiOut: |self: Dialog| match self {
|
|
||||||
Self::Menu(_) =>
|
|
||||||
self.view_dialog_menu().boxed(),
|
|
||||||
Self::Help(offset) =>
|
|
||||||
self.view_dialog_help(*offset).boxed(),
|
|
||||||
Self::Browser(target, browser) =>
|
|
||||||
self.view_dialog_browser(target, browser).boxed(),
|
|
||||||
Self::Options =>
|
|
||||||
self.view_dialog_options().boxed(),
|
|
||||||
Self::Device(index) =>
|
|
||||||
self.view_dialog_device(*index).boxed(),
|
|
||||||
Self::Message(message) =>
|
|
||||||
self.view_dialog_message(message).boxed(),
|
|
||||||
});
|
|
||||||
|
|
||||||
impl Dialog {
|
|
||||||
pub fn view_dialog_menu (&self) -> impl Content<TuiOut> {
|
|
||||||
let options = ||["Projects", "Settings", "Help", "Quit"].iter();
|
|
||||||
let option = |a,i|Tui::fg(Rgb(255,255,255), format!("{}", a));
|
|
||||||
Bsp::s(Tui::bold(true, "tek!"), Bsp::s("", Map::south(1, options, option)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_help <'a> (&'a self, offset: usize) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(Tui::bold(true, "Help"), "FIXME")
|
|
||||||
//Bsp::s(Tui::bold(true, "Help"), Bsp::s("", Map::south(1,
|
|
||||||
//move||self.config.keys.layers.iter()
|
|
||||||
//.filter_map(|a|(a.0)(self).then_some(a.1))
|
|
||||||
//.flat_map(|a|a)
|
|
||||||
//.filter_map(|x|if let Value::Exp(_, iter)=x.value{ Some(iter) } else { None })
|
|
||||||
//.skip(offset)
|
|
||||||
//.take(20),
|
|
||||||
//|mut b,i|Fixed::x(60, Align::w(Bsp::e("(", Bsp::e(
|
|
||||||
//b.next().map(|t|Fixed::x(16, Align::w(Tui::fg(Rgb(64,224,0), format!("{}", t.value))))),
|
|
||||||
//Bsp::e(" ", Align::w(format!("{}", b.0.0.trim()))))))))))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_device (&self, index: usize) -> impl Content<TuiOut> + use<'_> {
|
|
||||||
let choices = ||device_kinds().iter();
|
|
||||||
let choice = move|label, i|
|
|
||||||
Fill::x(Tui::bg(if i == index { Rgb(64,128,32) } else { Rgb(0,0,0) },
|
|
||||||
Bsp::e(if i == index { "[ " } else { " " },
|
|
||||||
Bsp::w(if i == index { " ]" } else { " " },
|
|
||||||
label))));
|
|
||||||
Bsp::s(Tui::bold(true, "Add device"), Map::south(1, choices, choice))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_message <'a> (&'a self, message: &'a Message) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(message, Bsp::s("", "[ OK ]"))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_browser <'a> (&'a self, target: &BrowserTarget, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Padding::xy(3, 1, Fill::x(Align::w(FieldV(
|
|
||||||
Default::default(),
|
|
||||||
match target {
|
|
||||||
BrowserTarget::SaveProject => "Save project:",
|
|
||||||
BrowserTarget::LoadProject => "Load project:",
|
|
||||||
BrowserTarget::ImportSample(_) => "Import sample:",
|
|
||||||
BrowserTarget::ExportSample(_) => "Export sample:",
|
|
||||||
BrowserTarget::ImportClip(_) => "Import clip:",
|
|
||||||
BrowserTarget::ExportClip(_) => "Export clip:",
|
|
||||||
},
|
|
||||||
Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_load <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
|
||||||
Tui::bold(true, " Load project: "),
|
|
||||||
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_export <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
|
||||||
Tui::bold(true, " Export: "),
|
|
||||||
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_import <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
Bsp::s(
|
|
||||||
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
|
||||||
Tui::bold(true, " Import: "),
|
|
||||||
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
|
||||||
Outer(true, Style::default().fg(Tui::g(96)))
|
|
||||||
.enclose(Fill::xy(browser)))
|
|
||||||
}
|
|
||||||
pub fn view_dialog_options <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
|
||||||
"TODO"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
9
crates/device/src/dialog/dialog_api.rs
Normal file
9
crates/device/src/dialog/dialog_api.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
#[tengri_proc::command(Option<Dialog>)]
|
||||||
|
impl DialogCommand {
|
||||||
|
fn dismiss (dialog: &mut Option<Dialog>) -> Perhaps<Self> {
|
||||||
|
*dialog = None;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
96
crates/device/src/dialog/dialog_view.rs
Normal file
96
crates/device/src/dialog/dialog_view.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
content!(TuiOut: |self: Dialog| match self {
|
||||||
|
Self::Menu(_) =>
|
||||||
|
self.view_dialog_menu().boxed(),
|
||||||
|
Self::Help(offset) =>
|
||||||
|
self.view_dialog_help(*offset).boxed(),
|
||||||
|
Self::Browser(target, browser) =>
|
||||||
|
self.view_dialog_browser(target, browser).boxed(),
|
||||||
|
Self::Options =>
|
||||||
|
self.view_dialog_options().boxed(),
|
||||||
|
Self::Device(index) =>
|
||||||
|
self.view_dialog_device(*index).boxed(),
|
||||||
|
Self::Message(message) =>
|
||||||
|
self.view_dialog_message(message).boxed(),
|
||||||
|
});
|
||||||
|
|
||||||
|
content!(TuiOut: |self: Message| match self {
|
||||||
|
Self::FailedToAddDevice => "Failed to add device."
|
||||||
|
});
|
||||||
|
|
||||||
|
impl Dialog {
|
||||||
|
pub fn view_dialog_menu (&self) -> impl Content<TuiOut> {
|
||||||
|
let options = ||["Projects", "Settings", "Help", "Quit"].iter();
|
||||||
|
let option = |a,i|Tui::fg(Rgb(255,255,255), format!("{}", a));
|
||||||
|
Bsp::s(Tui::bold(true, "tek!"), Bsp::s("", Map::south(1, options, option)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_help <'a> (&'a self, offset: usize) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(Tui::bold(true, "Help"), "FIXME")
|
||||||
|
//Bsp::s(Tui::bold(true, "Help"), Bsp::s("", Map::south(1,
|
||||||
|
//move||self.config.keys.layers.iter()
|
||||||
|
//.filter_map(|a|(a.0)(self).then_some(a.1))
|
||||||
|
//.flat_map(|a|a)
|
||||||
|
//.filter_map(|x|if let Value::Exp(_, iter)=x.value{ Some(iter) } else { None })
|
||||||
|
//.skip(offset)
|
||||||
|
//.take(20),
|
||||||
|
//|mut b,i|Fixed::x(60, Align::w(Bsp::e("(", Bsp::e(
|
||||||
|
//b.next().map(|t|Fixed::x(16, Align::w(Tui::fg(Rgb(64,224,0), format!("{}", t.value))))),
|
||||||
|
//Bsp::e(" ", Align::w(format!("{}", b.0.0.trim()))))))))))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_device (&self, index: usize) -> impl Content<TuiOut> + use<'_> {
|
||||||
|
let choices = ||device_kinds().iter();
|
||||||
|
let choice = move|label, i|
|
||||||
|
Fill::x(Tui::bg(if i == index { Rgb(64,128,32) } else { Rgb(0,0,0) },
|
||||||
|
Bsp::e(if i == index { "[ " } else { " " },
|
||||||
|
Bsp::w(if i == index { " ]" } else { " " },
|
||||||
|
label))));
|
||||||
|
Bsp::s(Tui::bold(true, "Add device"), Map::south(1, choices, choice))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_message <'a> (&'a self, message: &'a Message) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(message, Bsp::s("", "[ OK ]"))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_browser <'a> (&'a self, target: &BrowserTarget, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Padding::xy(3, 1, Fill::x(Align::w(FieldV(
|
||||||
|
Default::default(),
|
||||||
|
match target {
|
||||||
|
BrowserTarget::SaveProject => "Save project:",
|
||||||
|
BrowserTarget::LoadProject => "Load project:",
|
||||||
|
BrowserTarget::ImportSample(_) => "Import sample:",
|
||||||
|
BrowserTarget::ExportSample(_) => "Export sample:",
|
||||||
|
BrowserTarget::ImportClip(_) => "Import clip:",
|
||||||
|
BrowserTarget::ExportClip(_) => "Export clip:",
|
||||||
|
},
|
||||||
|
Shrink::x(3, Fixed::y(1, Tui::fg(Tui::g(96), RepeatH("🭻")))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_load <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
||||||
|
Tui::bold(true, " Load project: "),
|
||||||
|
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_export <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
||||||
|
Tui::bold(true, " Export: "),
|
||||||
|
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_import <'a> (&'a self, browser: &'a Browser) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
Bsp::s(
|
||||||
|
Fill::x(Align::w(Margin::xy(1, 1, Bsp::e(
|
||||||
|
Tui::bold(true, " Import: "),
|
||||||
|
Shrink::x(3, Fixed::y(1, RepeatH("🭻"))))))),
|
||||||
|
Outer(true, Style::default().fg(Tui::g(96)))
|
||||||
|
.enclose(Fill::xy(browser)))
|
||||||
|
}
|
||||||
|
pub fn view_dialog_options <'a> (&'a self) -> impl Content<TuiOut> + use<'a> {
|
||||||
|
"TODO"
|
||||||
|
}
|
||||||
|
}
|
||||||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 921378b6dbb38d4f301f688abd1cfef9bdc0f941
|
Subproject commit 3bc739328eed0c8fa67b432c7354c7929ddb505f
|
||||||
Loading…
Add table
Add a link
Reference in a new issue