Fixes
This commit is contained in:
167
src/input/mod.rs
Normal file
167
src/input/mod.rs
Normal file
@@ -0,0 +1,167 @@
|
||||
mod engine_page;
|
||||
mod help_page;
|
||||
mod main_page;
|
||||
mod modal;
|
||||
mod options_page;
|
||||
mod panel;
|
||||
mod patterns_page;
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::app::App;
|
||||
use crate::commands::AppCommand;
|
||||
use crate::engine::{AudioCommand, LinkState, SeqCommand, SequencerSnapshot};
|
||||
use crate::page::Page;
|
||||
use crate::state::{Modal, PanelFocus};
|
||||
|
||||
pub enum InputResult {
|
||||
Continue,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub struct InputContext<'a> {
|
||||
pub app: &'a mut App,
|
||||
pub link: &'a LinkState,
|
||||
pub snapshot: &'a SequencerSnapshot,
|
||||
pub playing: &'a Arc<AtomicBool>,
|
||||
pub audio_tx: &'a ArcSwap<Sender<AudioCommand>>,
|
||||
pub seq_cmd_tx: &'a Sender<SeqCommand>,
|
||||
pub nudge_us: &'a Arc<AtomicI64>,
|
||||
}
|
||||
|
||||
impl<'a> InputContext<'a> {
|
||||
fn dispatch(&mut self, cmd: AppCommand) {
|
||||
self.app.dispatch(cmd, self.link, self.snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_key(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
|
||||
if handle_live_keys(ctx, &key) {
|
||||
return InputResult::Continue;
|
||||
}
|
||||
|
||||
if key.kind == KeyEventKind::Release {
|
||||
return InputResult::Continue;
|
||||
}
|
||||
|
||||
let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
|
||||
let is_arrow = matches!(
|
||||
key.code,
|
||||
KeyCode::Left | KeyCode::Right | KeyCode::Up | KeyCode::Down
|
||||
);
|
||||
if ctx.app.ui.minimap_until.is_some() && !(ctrl && is_arrow) {
|
||||
ctx.dispatch(AppCommand::ClearMinimap);
|
||||
}
|
||||
|
||||
if ctx.app.ui.show_title {
|
||||
ctx.dispatch(AppCommand::HideTitle);
|
||||
return InputResult::Continue;
|
||||
}
|
||||
|
||||
ctx.dispatch(AppCommand::ClearStatus);
|
||||
|
||||
if matches!(ctx.app.ui.modal, Modal::None) {
|
||||
handle_normal_input(ctx, key)
|
||||
} else {
|
||||
modal::handle_modal_input(ctx, key)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_live_keys(ctx: &mut InputContext, key: &KeyEvent) -> bool {
|
||||
match (key.code, key.kind) {
|
||||
_ if !matches!(ctx.app.ui.modal, Modal::None) => false,
|
||||
(KeyCode::Char('f'), KeyEventKind::Press) => {
|
||||
ctx.dispatch(AppCommand::ToggleLiveKeysFill);
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_normal_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
|
||||
let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
|
||||
|
||||
if ctx.app.panel.visible && ctx.app.panel.focus == PanelFocus::Side {
|
||||
return panel::handle_panel_input(ctx, key);
|
||||
}
|
||||
|
||||
if ctrl {
|
||||
let minimap_timeout = Some(Instant::now() + Duration::from_millis(250));
|
||||
match key.code {
|
||||
KeyCode::Left => {
|
||||
ctx.app.ui.minimap_until = minimap_timeout;
|
||||
ctx.dispatch(AppCommand::PageLeft);
|
||||
return InputResult::Continue;
|
||||
}
|
||||
KeyCode::Right => {
|
||||
ctx.app.ui.minimap_until = minimap_timeout;
|
||||
ctx.dispatch(AppCommand::PageRight);
|
||||
return InputResult::Continue;
|
||||
}
|
||||
KeyCode::Up => {
|
||||
ctx.app.ui.minimap_until = minimap_timeout;
|
||||
ctx.dispatch(AppCommand::PageUp);
|
||||
return InputResult::Continue;
|
||||
}
|
||||
KeyCode::Down => {
|
||||
ctx.app.ui.minimap_until = minimap_timeout;
|
||||
ctx.dispatch(AppCommand::PageDown);
|
||||
return InputResult::Continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
match ctx.app.page {
|
||||
Page::Main => main_page::handle_main_page(ctx, key, ctrl),
|
||||
Page::Patterns => patterns_page::handle_patterns_page(ctx, key),
|
||||
Page::Engine => engine_page::handle_engine_page(ctx, key),
|
||||
Page::Options => options_page::handle_options_page(ctx, key),
|
||||
Page::Help => help_page::handle_help_page(ctx, key),
|
||||
Page::Dict => help_page::handle_dict_page(ctx, key),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_project_samples(ctx: &mut InputContext) {
|
||||
let paths = ctx.app.project_state.project.sample_paths.clone();
|
||||
if paths.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut total_count = 0;
|
||||
let mut all_preload_entries = Vec::new();
|
||||
for path in &paths {
|
||||
if path.is_dir() {
|
||||
let index = doux::sampling::scan_samples_dir(path);
|
||||
let count = index.len();
|
||||
total_count += count;
|
||||
for e in &index {
|
||||
all_preload_entries.push((e.name.clone(), e.path.clone()));
|
||||
}
|
||||
let _ = ctx.audio_tx.load().send(AudioCommand::LoadSamples(index));
|
||||
}
|
||||
}
|
||||
|
||||
ctx.app.audio.config.sample_paths = paths;
|
||||
ctx.app.audio.config.sample_count = total_count;
|
||||
|
||||
if total_count > 0 {
|
||||
if let Some(registry) = ctx.app.audio.sample_registry.clone() {
|
||||
let sr = ctx.app.audio.config.sample_rate;
|
||||
std::thread::Builder::new()
|
||||
.name("sample-preload".into())
|
||||
.spawn(move || {
|
||||
crate::init::preload_sample_heads(all_preload_entries, sr, ®istry);
|
||||
})
|
||||
.expect("failed to spawn preload thread");
|
||||
}
|
||||
ctx.dispatch(AppCommand::SetStatus(format!(
|
||||
"Loaded {total_count} samples from project"
|
||||
)));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user