Feat: more mouse support

This commit is contained in:
2026-02-28 02:26:33 +01:00
parent 25a5c77344
commit 0a186f774c
4 changed files with 59 additions and 10 deletions

4
Cargo.lock generated
View File

@@ -1809,8 +1809,8 @@ dependencies = [
[[package]]
name = "doux"
version = "0.0.4"
source = "git+https://github.com/sova-org/doux#ba475601a448f36d4755eb1fdcaa629987485892"
version = "0.0.5"
source = "git+https://github.com/sova-org/doux#886702b4fe937d26ed681a2f6d7626d26d6890d0"
dependencies = [
"arc-swap",
"clap",

View File

@@ -394,6 +394,7 @@ impl eframe::App for CagireDesktop {
self.app.flush_queued_changes(&sequencer.cmd_tx);
self.app.flush_dirty_patterns(&sequencer.cmd_tx);
self.app.flush_dirty_script(&sequencer.cmd_tx);
while let Ok(midi_cmd) = self.midi_rx.try_recv() {
match midi_cmd {

View File

@@ -1070,7 +1070,7 @@ impl SequencerState {
}
let script_frontier = if self.script_frontier < 0.0 {
frontier.max(0.0)
frontier
} else {
self.script_frontier
};

View File

@@ -6,8 +6,8 @@ use ratatui::layout::{Constraint, Layout, Rect};
use crate::commands::AppCommand;
use crate::page::Page;
use crate::state::{
DeviceKind, DictFocus, EngineSection, HelpFocus, MinimapMode, Modal, OptionsFocus,
PatternsColumn, SettingKind,
DeviceKind, DictFocus, EditorTarget, EngineSection, HelpFocus, MinimapMode, Modal,
OptionsFocus, PatternsColumn, SettingKind,
};
use crate::views::{dict_view, engine_view, help_view, main_view, patterns_view, script_view};
@@ -94,14 +94,22 @@ fn handle_editor_drag(ctx: &mut InputContext, col: u16, row: u16, term: Rect) {
}
}
fn handle_editor_mouse(ctx: &mut InputContext, col: u16, row: u16, term: Rect, dragging: bool) {
// Reconstruct editor area (mirrors render_modal_editor / ModalFrame::render_centered)
fn editor_modal_rect(term: Rect) -> Rect {
let width = (term.width * 80 / 100).max(40);
let height = (term.height * 60 / 100).max(10);
let modal_w = width.min(term.width.saturating_sub(4));
let modal_h = height.min(term.height.saturating_sub(4));
let mx = term.x + (term.width.saturating_sub(modal_w)) / 2;
let my = term.y + (term.height.saturating_sub(modal_h)) / 2;
Rect::new(mx, my, modal_w, modal_h)
}
fn handle_editor_mouse(ctx: &mut InputContext, col: u16, row: u16, term: Rect, dragging: bool) {
let modal = editor_modal_rect(term);
let mx = modal.x;
let my = modal.y;
let modal_w = modal.width;
let modal_h = modal.height;
// inner = area inside 1-cell border
let inner_x = mx + 1;
let inner_y = my + 1;
@@ -180,6 +188,18 @@ fn handle_scroll(ctx: &mut InputContext, col: u16, row: u16, term: Rect, up: boo
return;
}
if matches!(ctx.app.ui.modal, Modal::Editor) {
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
let code = if up { KeyCode::Up } else { KeyCode::Down };
for _ in 0..3 {
ctx.app
.editor_ctx
.editor
.input(KeyEvent::new(code, KeyModifiers::empty()));
}
return;
}
if !matches!(ctx.app.ui.modal, Modal::None) {
return;
}
@@ -1057,13 +1077,28 @@ fn handle_engine_click(ctx: &mut InputContext, col: u16, row: u16, area: Rect, k
fn handle_modal_click(ctx: &mut InputContext, col: u16, row: u16, term: Rect) {
match &ctx.app.ui.modal {
Modal::Editor => {
let modal_area = editor_modal_rect(term);
if contains(modal_area, col, row) {
handle_editor_mouse(ctx, col, row, term, false);
} else {
match ctx.app.editor_ctx.target {
EditorTarget::Step => {
ctx.dispatch(AppCommand::SaveEditorToStep);
ctx.dispatch(AppCommand::CompileCurrentStep);
}
EditorTarget::Prelude => {
ctx.dispatch(AppCommand::SavePrelude);
ctx.dispatch(AppCommand::EvaluatePrelude);
ctx.dispatch(AppCommand::ClosePreludeEditor);
}
}
ctx.dispatch(AppCommand::CloseModal);
}
}
Modal::Confirm { .. } => {
handle_confirm_click(ctx, col, row, term);
}
Modal::KeybindingsHelp { .. } => {
// Click outside keybindings help to dismiss
let padded = padded(term);
let width = (padded.width * 80 / 100).clamp(60, 100);
let height = (padded.height * 80 / 100).max(15);
@@ -1073,7 +1108,20 @@ fn handle_modal_click(ctx: &mut InputContext, col: u16, row: u16, term: Rect) {
}
}
_ => {
// For other modals, don't dismiss on click (they have their own input)
let (w, h) = match &ctx.app.ui.modal {
Modal::PatternProps { .. } => (50, 18),
Modal::EuclideanDistribution { .. } => (50, 11),
Modal::Onboarding { .. } => (57, 20),
Modal::FileBrowser(_) | Modal::AddSamplePath(_) => (60, 18),
Modal::Rename { .. } => (40, 5),
Modal::SetPattern { .. } | Modal::SetScript { .. } => (45, 5),
Modal::SetTempo(_) | Modal::JumpToStep(_) => (30, 5),
_ => return,
};
let modal_area = centered_rect(term, w, h);
if !contains(modal_area, col, row) {
ctx.dispatch(AppCommand::CloseModal);
}
}
}
}