Skip to content

Commit 68a31ee

Browse files
Tokclaude
andcommitted
amen: bigger wheel + PLAY + SYNC BPM + DIR label + dropdown scroll
Round of fixes on the amen panel UX: Slice wheel: - Size up from 56 → 96 px (about 2× as user requested) with a bit of horizontal padding either side so it breathes. - Hub direction arrow glyph swapped to ► / ◄ (black pointing, U+25BA / U+25C4) — the earlier open-pointer chars fell back to a square tofu on systems without glyph coverage. Picker row: - New ▶ PLAY button triggers a one-shot amen hit through the normal DSP trigger pipeline (DrumVoice::Amen, slice 0 = auto-advance). Lets you audition a break or a specific slice without running the sequencer. Uses current slice / gate / stutter / reverse settings. - Combo box width now reserves room for all three buttons (RND + LD + PLAY) so they fit inside the 3-cell module width. - Popup dropdown now wraps its entries in a ScrollArea with a 200 px max — long sample packs were getting their bottom items clipped by the panel bounds. Tempo row: - "=" button next to SRC BPM snaps it to the current sequencer BPM (the "host"). Hover tooltip shows the actual target value. Clicking = then leaving STRETCH on effectively makes it a no-op until sequencer.bpm changes — the "default synced" behavior the user expects. DIR / LOOP row: - Added a "DIR" label before the FWD/REV toggle so the state is labelled inline, not just signalled by the hub arrow. Module height: - Bumped AmenSampler from 3×2 → 3×3. With the bigger wheel the content no longer fits in 2 cells; 3 gives breathing room. (Right-side-wheel 2-cell layout was an option but is a bigger rewrite — saved for later if the 3-cell version feels too tall.) 480 tests still passing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 903fc99 commit 68a31ee

2 files changed

Lines changed: 84 additions & 28 deletions

File tree

src/state/rack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ impl ModuleKind {
194194
Self::DrumKit909 => (4, 3),
195195
Self::HooverLead => (4, 2),
196196
Self::An1xVoice => (6, 6),
197-
Self::AmenSampler => (3, 2),
197+
Self::AmenSampler => (3, 3),
198198
Self::NoiseVoice => (2, 1),
199199
Self::GranularTexture => (3, 1),
200200
Self::LlmAgent => (3, 2),

src/ui/panels/amen.rs

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,16 @@ fn draw_slice_wheel(
302302
if looping {
303303
painter.circle_stroke(center, r_outer + 3.0, egui::Stroke::new(1.0, theme::CHALK));
304304
}
305-
// Reverse arrow indicator in the hub
305+
// Direction arrow in the hub (black pointing glyphs render reliably
306+
// in egui's default font — the earlier open-pointer chars showed as
307+
// a fallback square on some systems).
306308
let arrow_col = theme::ASH;
307-
let hub_label = if reverse { "" } else { "" };
309+
let hub_label = if reverse { "" } else { "" };
308310
painter.text(
309311
center,
310312
egui::Align2::CENTER_CENTER,
311313
hub_label,
312-
egui::FontId::proportional(size * 0.28),
314+
egui::FontId::monospace(size * 0.22),
313315
arrow_col,
314316
);
315317
}
@@ -415,28 +417,36 @@ pub fn draw_amen(app: &mut ImpulseApp, ui: &mut egui::Ui) {
415417
// plus item_spacing). Using a fixed cap keeps the picker from
416418
// pushing outside the 3-cell module width on narrow rack layouts.
417419
egui::ComboBox::from_id_source("amen_sample_picker")
418-
.width((ui.available_width() - 70.0).max(60.0))
420+
// Room for RND + LD + PLAY (3 small buttons).
421+
.width((ui.available_width() - 96.0).max(60.0))
419422
.selected_text(egui::RichText::new(current_name).monospace().size(8.0))
420423
.show_ui(ui, |ui| {
421-
for sp in &samples {
422-
let sp_str = sp.to_string_lossy().to_string();
423-
let name = sp
424-
.file_name()
425-
.and_then(|n| n.to_str())
426-
.unwrap_or(sp_str.as_str())
427-
.to_string();
428-
if ui
429-
.selectable_label(
430-
path == sp_str,
431-
egui::RichText::new(name).monospace().size(8.0),
432-
)
433-
.clicked()
434-
{
435-
path = sp_str;
436-
app.state.write().amen.path = path.clone();
437-
load_and_cache(app, &path);
438-
}
439-
}
424+
// Cap the popup height so long sample-pack dirs don't
425+
// overflow the rack/panel bounds (the user reported
426+
// entries getting cut off). 200px ≈ 14 rows at 8pt.
427+
egui::ScrollArea::vertical()
428+
.max_height(200.0)
429+
.show(ui, |ui| {
430+
for sp in &samples {
431+
let sp_str = sp.to_string_lossy().to_string();
432+
let name = sp
433+
.file_name()
434+
.and_then(|n| n.to_str())
435+
.unwrap_or(sp_str.as_str())
436+
.to_string();
437+
if ui
438+
.selectable_label(
439+
path == sp_str,
440+
egui::RichText::new(name).monospace().size(8.0),
441+
)
442+
.clicked()
443+
{
444+
path = sp_str;
445+
app.state.write().amen.path = path.clone();
446+
load_and_cache(app, &path);
447+
}
448+
}
449+
});
440450
});
441451
if ui
442452
.small_button(egui::RichText::new("RND").monospace().size(7.0))
@@ -456,6 +466,30 @@ pub fn draw_amen(app: &mut ImpulseApp, ui: &mut egui::Ui) {
456466
let p = path.clone();
457467
load_and_cache(app, &p);
458468
}
469+
// PLAY — one-shot trigger of the amen voice through the
470+
// normal DSP trigger pipeline, using current slice/gate/
471+
// stutter settings. Useful for auditioning without the
472+
// sequencer running.
473+
if ui
474+
.small_button(egui::RichText::new("▶").monospace().size(7.5))
475+
.on_hover_text(
476+
"Trigger the sample once (uses current slice, gate,\n\
477+
stutter, reverse, and BPM-stretch settings).\n\
478+
Slice 0 = auto-advance.",
479+
)
480+
.clicked()
481+
{
482+
use crate::audio::AudioCommand;
483+
use crate::sequencer::TriggerEvent;
484+
use crate::state::DrumVoice;
485+
let _ = app
486+
.audio_tx
487+
.push(AudioCommand::Trigger(TriggerEvent::DrumTrigger {
488+
voice: DrumVoice::Amen,
489+
velocity: 1.0,
490+
slice: 0,
491+
}));
492+
}
459493
});
460494
}
461495

@@ -557,8 +591,12 @@ pub fn draw_amen(app: &mut ImpulseApp, ui: &mut egui::Ui) {
557591
// SLICES buttons, REV/LOOP toggles, and the tempo row (SRC BPM +
558592
// STRETCH). Packing all three into that column frees the lower half
559593
// of the panel for a single knob row.
594+
let host_bpm = app.state.read().sequencer.bpm;
560595
ui.horizontal(|ui| {
561-
draw_slice_wheel(ui, slice_count, active, reverse, loop_mode, 56.0);
596+
// Small padding around the wheel so it doesn't hug other widgets.
597+
ui.add_space(4.0);
598+
draw_slice_wheel(ui, slice_count, active, reverse, loop_mode, 96.0);
599+
ui.add_space(6.0);
562600
ui.vertical(|ui| {
563601
// SLICES selector
564602
ui.horizontal(|ui| {
@@ -591,8 +629,15 @@ pub fn draw_amen(app: &mut ImpulseApp, ui: &mut egui::Ui) {
591629
}
592630
}
593631
});
594-
// REV / LOOP
632+
// DIR (REV/FWD) + LOOP — label added to the direction toggle
633+
// so the state reads cleanly without needing the hub arrow.
595634
ui.horizontal(|ui| {
635+
ui.label(
636+
egui::RichText::new("DIR")
637+
.monospace()
638+
.size(7.5)
639+
.color(theme::SMOKE),
640+
);
596641
if widgets::toggle_button(ui, if reverse { "REV" } else { "FWD" }, &mut reverse) {
597642
changed = true;
598643
}
@@ -604,8 +649,11 @@ pub fn draw_amen(app: &mut ImpulseApp, ui: &mut egui::Ui) {
604649
changed = true;
605650
}
606651
});
607-
// SRC BPM + STRETCH — tempo-matching controls. STRETCH on =
608-
// resample-based stretch to sequencer.bpm (pitches the sample).
652+
// SRC BPM + STRETCH — tempo-matching controls. SYNC button
653+
// snaps source_bpm to the sequencer BPM (the "host" tempo)
654+
// so STRETCH effectively becomes a no-op until you change
655+
// sequencer.bpm, matching the "default synced" behavior the
656+
// user expects.
609657
ui.horizontal(|ui| {
610658
ui.label(
611659
egui::RichText::new("BPM")
@@ -621,6 +669,14 @@ pub fn draw_amen(app: &mut ImpulseApp, ui: &mut egui::Ui) {
621669
source_bpm = v;
622670
changed = true;
623671
}
672+
if ui
673+
.small_button(egui::RichText::new("=").monospace().size(7.0))
674+
.on_hover_text(format!("Sync source BPM to host ({:.0})", host_bpm))
675+
.clicked()
676+
{
677+
source_bpm = host_bpm;
678+
changed = true;
679+
}
624680
if widgets::toggle_button(
625681
ui,
626682
if bpm_stretch { "STRETCH" } else { "FREE" },

0 commit comments

Comments
 (0)