mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
add Exit trait to modals
This commit is contained in:
parent
0cc8d88e5f
commit
33e5f47526
6 changed files with 67 additions and 33 deletions
|
|
@ -47,7 +47,7 @@ impl AppPaths {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SetupModal(pub Option<Arc<XdgApp>>);
|
pub struct SetupModal(pub Option<Arc<XdgApp>>, pub bool);
|
||||||
|
|
||||||
render!(SetupModal |self, buf, area| {
|
render!(SetupModal |self, buf, area| {
|
||||||
for cell in buf.content.iter_mut() {
|
for cell in buf.content.iter_mut() {
|
||||||
|
|
@ -83,9 +83,17 @@ handle!(SetupModal |self, e| {
|
||||||
..
|
..
|
||||||
})) = e {
|
})) = e {
|
||||||
AppPaths::new(&self.0.as_ref().unwrap())?.create()?;
|
AppPaths::new(&self.0.as_ref().unwrap())?.create()?;
|
||||||
self.0 = None;
|
self.exit();
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
impl Exit for SetupModal {
|
||||||
|
fn exited (&self) -> bool {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
fn exit (&mut self) {
|
||||||
|
self.1 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ handle!{
|
||||||
App |self, e| {
|
App |self, e| {
|
||||||
if let Some(ref mut modal) = self.modal {
|
if let Some(ref mut modal) = self.modal {
|
||||||
if modal.handle(e)? {
|
if modal.handle(e)? {
|
||||||
self.modal = None;
|
if modal.exited() {
|
||||||
|
self.modal = None;
|
||||||
|
}
|
||||||
return Ok(true)
|
return Ok(true)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -145,10 +147,12 @@ pub const KEYMAP: &'static [KeyBinding<App>] = keymap!(App {
|
||||||
|
|
||||||
fn focus_next (app: &mut App) -> Usually<bool> {
|
fn focus_next (app: &mut App) -> Usually<bool> {
|
||||||
app.section.next();
|
app.section.next();
|
||||||
|
app.transport.focused = app.section == AppSection::Transport;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_prev (app: &mut App) -> Usually<bool> {
|
fn focus_prev (app: &mut App) -> Usually<bool> {
|
||||||
app.section.prev();
|
app.section.prev();
|
||||||
|
app.transport.focused = app.section == AppSection::Transport;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,14 @@ pub trait Component: Render + Handle + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Exit: Component {
|
||||||
|
fn exited (&self) -> bool;
|
||||||
|
fn exit (&mut self);
|
||||||
|
fn boxed (self) -> Box<dyn Exit> where Self: Sized + 'static {
|
||||||
|
Box::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Anything that implements `Render` + `Handle` can be used as a UI component.
|
/// Anything that implements `Render` + `Handle` can be used as a UI component.
|
||||||
impl<T: Render + Handle + Sync> Component for T {}
|
impl<T: Render + Handle + Sync> Component for T {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ pub struct App {
|
||||||
/// Display buffer for sequencer
|
/// Display buffer for sequencer
|
||||||
pub seq_buf: BufferedSequencerView,
|
pub seq_buf: BufferedSequencerView,
|
||||||
/// Optional modal dialog
|
/// Optional modal dialog
|
||||||
pub modal: Option<Box<dyn Component>>,
|
pub modal: Option<Box<dyn Exit>>,
|
||||||
/// Currently focused section
|
/// Currently focused section
|
||||||
pub section: AppSection,
|
pub section: AppSection,
|
||||||
/// Whether the current focus section has input priority
|
/// Whether the current focus section has input priority
|
||||||
|
|
@ -57,8 +57,6 @@ pub struct App {
|
||||||
xdg: Option<Arc<XdgApp>>,
|
xdg: Option<Arc<XdgApp>>,
|
||||||
/// Main audio outputs.
|
/// Main audio outputs.
|
||||||
audio_outs: Vec<Arc<Port<Unowned>>>,
|
audio_outs: Vec<Arc<Port<Unowned>>>,
|
||||||
/// Tick enable?
|
|
||||||
metronome: bool,
|
|
||||||
/// Number of frames requested by process callback
|
/// Number of frames requested by process callback
|
||||||
chunk_size: usize,
|
chunk_size: usize,
|
||||||
|
|
||||||
|
|
@ -78,10 +76,8 @@ impl App {
|
||||||
chunk_size: 0,
|
chunk_size: 0,
|
||||||
entered: true,
|
entered: true,
|
||||||
jack: Some(jack),
|
jack: Some(jack),
|
||||||
metronome: false,
|
|
||||||
midi_in: None,
|
midi_in: None,
|
||||||
midi_ins: vec![],
|
midi_ins: vec![],
|
||||||
modal: first_run.then(||crate::config::SetupModal(Some(xdg.clone())).boxed()),
|
|
||||||
note_cursor: 0,
|
note_cursor: 0,
|
||||||
note_start: 2,
|
note_start: 2,
|
||||||
scene_cursor: 1,
|
scene_cursor: 1,
|
||||||
|
|
@ -92,6 +88,9 @@ impl App {
|
||||||
time_cursor: 0,
|
time_cursor: 0,
|
||||||
track_cursor: 1,
|
track_cursor: 1,
|
||||||
tracks: vec![],
|
tracks: vec![],
|
||||||
|
modal: first_run.then(
|
||||||
|
||Exit::boxed(crate::config::SetupModal(Some(xdg.clone()), false))
|
||||||
|
),
|
||||||
xdg: Some(xdg),
|
xdg: Some(xdg),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,29 @@
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
|
|
||||||
pub struct TransportToolbar {
|
pub struct TransportToolbar {
|
||||||
pub mode: bool,
|
pub metronome: bool,
|
||||||
pub focused: bool,
|
pub mode: bool,
|
||||||
pub entered: bool,
|
pub focused: bool,
|
||||||
|
pub entered: bool,
|
||||||
/// Current sample rate, tempo, and PPQ.
|
/// Current sample rate, tempo, and PPQ.
|
||||||
pub timebase: Arc<Timebase>,
|
pub timebase: Arc<Timebase>,
|
||||||
/// JACK transport handle.
|
/// JACK transport handle.
|
||||||
transport: Option<Transport>,
|
transport: Option<Transport>,
|
||||||
/// Quantization factor
|
/// Quantization factor
|
||||||
pub quant: usize,
|
pub quant: usize,
|
||||||
/// Current transport state
|
/// Current transport state
|
||||||
pub playing: Option<TransportState>,
|
pub playing: Option<TransportState>,
|
||||||
/// Current position according to transport
|
/// Current position according to transport
|
||||||
playhead: usize,
|
playhead: usize,
|
||||||
/// Global frame and usec at which playback started
|
/// Global frame and usec at which playback started
|
||||||
pub started: Option<(usize, usize)>,
|
pub started: Option<(usize, usize)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransportToolbar {
|
impl TransportToolbar {
|
||||||
pub fn new (transport: Option<Transport>) -> Self {
|
pub fn new (transport: Option<Transport>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
transport,
|
transport,
|
||||||
|
metronome: false,
|
||||||
mode: false,
|
mode: false,
|
||||||
focused: false,
|
focused: false,
|
||||||
entered: false,
|
entered: false,
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,27 @@ use crate::{core::*, view::*};
|
||||||
pub struct HelpModal {
|
pub struct HelpModal {
|
||||||
cursor: usize,
|
cursor: usize,
|
||||||
search: Option<String>,
|
search: Option<String>,
|
||||||
|
exited: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HelpModal {
|
impl HelpModal {
|
||||||
pub fn new () -> Self {
|
pub fn new () -> Self {
|
||||||
Self { cursor: 0, search: None }
|
Self { cursor: 0, search: None, exited: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Exit for HelpModal {
|
||||||
|
fn exited (&self) -> bool {
|
||||||
|
self.exited
|
||||||
|
}
|
||||||
|
fn exit (&mut self) {
|
||||||
|
self.exited = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render!(HelpModal |self, buf, area|{
|
render!(HelpModal |self, buf, area|{
|
||||||
for cell in buf.content.iter_mut() {
|
for cell in buf.content.iter_mut() {
|
||||||
cell.bg = ratatui::style::Color::Rgb(44,44,44);
|
cell.bg = ratatui::style::Color::Rgb(30,30,30);
|
||||||
cell.fg = ratatui::style::Color::Rgb(88,88,88);
|
cell.fg = ratatui::style::Color::Rgb(100,100,100);
|
||||||
cell.modifier = ratatui::style::Modifier::DIM;
|
cell.modifier = ratatui::style::Modifier::DIM;
|
||||||
}
|
}
|
||||||
let width = 64.min(area.width * 3 / 5);
|
let width = 64.min(area.width * 3 / 5);
|
||||||
|
|
@ -40,25 +49,28 @@ render!(HelpModal |self, buf, area|{
|
||||||
for i in 0..height-3 {
|
for i in 0..height-3 {
|
||||||
let y = y + i;
|
let y = y + i;
|
||||||
if let Some(command) = crate::control::KEYMAP_FOCUS.get(i as usize) {
|
if let Some(command) = crate::control::KEYMAP_FOCUS.get(i as usize) {
|
||||||
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().bold()))?;
|
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().white().bold()))?;
|
||||||
command.2.blit(buf, x + 11, y, Some(Style::default().bold()))?;
|
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
|
||||||
command.3.blit(buf, x + 26, y, None)?;
|
command.3.blit(buf, x + 26, y, Some(Style::default().white().dim()))?;
|
||||||
} else if let Some(command) = crate::control::KEYMAP.get((i as usize) - crate::control::KEYMAP_FOCUS.len()) {
|
} else if let Some(command) = crate::control::KEYMAP.get((i as usize) - crate::control::KEYMAP_FOCUS.len()) {
|
||||||
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().bold()))?;
|
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().white().bold()))?;
|
||||||
command.2.blit(buf, x + 11, y, Some(Style::default().bold()))?;
|
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
|
||||||
command.3.blit(buf, x + 26, y, None)?;
|
command.3.blit(buf, x + 26, y, Some(Style::default().white().dim()))?;
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let hi_area = Rect { x: area.x + 1, width: area.width - 2, y: area.y + 3 + self.cursor as u16, height: 1 };
|
let hi_area = Rect { x: area.x + 1, width: area.width - 2, y: area.y + 3 + self.cursor as u16, height: 1 };
|
||||||
fill_bg(buf, hi_area, Nord::bg_hi(true, true));
|
fill_bg(buf, hi_area, Nord::bg_hi(true, true));
|
||||||
fill_fg(buf, hi_area, Color::Reset);
|
fill_fg(buf, hi_area, Color::White);
|
||||||
Lozenge(Style::default()).draw(buf, area)
|
Lozenge(Style::default()).draw(buf, area)
|
||||||
});
|
});
|
||||||
|
|
||||||
handle!(HelpModal |self, e| {
|
handle!(HelpModal |self, e| {
|
||||||
Ok(handle_keymap(self, e, KEYMAP_HELP)? || match e {
|
if handle_keymap(self, e, KEYMAP_HELP)? {
|
||||||
|
return Ok(true)
|
||||||
|
}
|
||||||
|
Ok(match e {
|
||||||
AppEvent::Input(Event::Key(KeyEvent {
|
AppEvent::Input(Event::Key(KeyEvent {
|
||||||
code: KeyCode::Char(c),
|
code: KeyCode::Char(c),
|
||||||
modifiers: KeyModifiers::NONE, ..
|
modifiers: KeyModifiers::NONE, ..
|
||||||
|
|
@ -67,22 +79,23 @@ handle!(HelpModal |self, e| {
|
||||||
self.search = Some(String::new());
|
self.search = Some(String::new());
|
||||||
}
|
}
|
||||||
self.search.as_mut().unwrap().push(*c);
|
self.search.as_mut().unwrap().push(*c);
|
||||||
false
|
true
|
||||||
},
|
},
|
||||||
_ => false
|
_ => true
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
pub const KEYMAP_HELP: &'static [KeyBinding<HelpModal>] = keymap!(HelpModal {
|
pub const KEYMAP_HELP: &'static [KeyBinding<HelpModal>] = keymap!(HelpModal {
|
||||||
[Esc, NONE, "help_close", "close help dialog", |_: &mut HelpModal|{
|
[Esc, NONE, "help_close", "close help dialog", |modal: &mut HelpModal|{
|
||||||
|
modal.exit();
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
[Up, NONE, "help_prev", "select previous command", |modal: &mut HelpModal|{
|
[Up, NONE, "help_prev", "select previous command", |modal: &mut HelpModal|{
|
||||||
modal.cursor = modal.cursor.saturating_sub(1);
|
modal.cursor = modal.cursor.saturating_sub(1);
|
||||||
Ok(false)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
[Down, NONE, "help_next", "select next command", |modal: &mut HelpModal|{
|
[Down, NONE, "help_next", "select next command", |modal: &mut HelpModal|{
|
||||||
modal.cursor = modal.cursor + 1;
|
modal.cursor = modal.cursor + 1;
|
||||||
Ok(false)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue