Merge Insert as FX: load ReaSampler 9000 onto the selected track from the panel

# Conflicts:
#	src/shell/actions/CLAUDE.md
This commit is contained in:
2026-08-03 16:46:40 -04:00
14 changed files with 442 additions and 21 deletions
+1
View File
@@ -110,6 +110,7 @@ L7 sub-pass, 2026-07-27):
- `footer_bar` — pure footer layout/hit-test: `[Arrange|Design]` mode-toggle geometry, Tail button, and Prune placement, plus `modeSegmentEnabled` — the mode segment's live/dead predicate under the playback gate AND under whether the shell resolved a routable command id for it (both bools passed IN, so this stays REAPER-free).
- `overflow_menu` — pure overflow-menu-button geometry/reserve/hit-test for the top-toolbar More (⋯) button.
- `mode_enable` — pure opposite-mode enablement predicate: given the active mode, computes per-button live/disabled state for the four Item/Track × Arrange/Design tag buttons.
- `insert_fx_enable` — pure refusal fold behind the Insert-as-FX verb (one instance holds one capture, so it needs exactly one focused capture AND a destination track), plus the button's live/dead bit derived from that same fold and one sentence per refusal. The payload/destination axes are ordered deliberately because they are knowable at different times — the header states which half the button may speak for.
- `tooltip` — pure tooltip placement + prefix-strip: strips the `ReaSampler:` display prefix from the registered action phrase; width clamped to the client rect.
- `card_drag` — pure drag-gesture precedence + slot hit-test: leave-client → OS drag-out; other-bank → move/copy; same-bank → reorder / Alt-over-occupied → replace.
- `card_meta` — pure card-metadata formatters: bars.beats.subdivisions and seconds.milliseconds; blank when the sample is unstamped. Also `cardNameStrip` + the two strip constants — the card's name line sits across the TOP of the cell, drawn over the waveform exactly as the length read-out is over it at the bottom, and is suppressed entirely on a cell with no room for both strips plus a waveform band.
+6 -1
View File
@@ -17,7 +17,12 @@ reasampler_pure_library(component_geometry SOURCES component_geometry.cpp)
reasampler_test(component_geometry LINK component_geometry)
reasampler_pure_library(action_bar SOURCES action_bar.cpp)
reasampler_test(action_bar LINK action_bar)
# overflow_menu supplies the More-button reserve the panel subtracts before tiling; the
# bar's narrow-width behaviour is only meaningful against that composition.
reasampler_test(action_bar LINK action_bar overflow_menu)
reasampler_pure_library(insert_fx_enable SOURCES insert_fx_enable.cpp)
reasampler_test(insert_fx_enable LINK insert_fx_enable)
# prune_button owns the FooterRect input type the footer layout reuses.
reasampler_pure_library(footer_bar SOURCES footer_bar.cpp LINK PUBLIC prune_button)
+41
View File
@@ -0,0 +1,41 @@
// insert_fx_enable — pure implementation. See insert_fx_enable.h.
#include "core/ui/insert_fx_enable.h"
namespace reasampler::ui {
InsertFxRefusal insertFxRefusal(int focusedCaptureCount, bool trackSelected,
bool masterOnlySelected) {
if (focusedCaptureCount <= 0) return InsertFxRefusal::NoCapture;
if (focusedCaptureCount > 1) return InsertFxRefusal::MultiCapture;
if (!trackSelected)
return masterOnlySelected ? InsertFxRefusal::MasterOnlySelected : InsertFxRefusal::NoTrack;
return InsertFxRefusal::None;
}
bool insertFxButtonEnabled(int focusedCaptureCount) {
return insertFxRefusal(focusedCaptureCount, /*trackSelected=*/true) ==
InsertFxRefusal::None;
}
std::string insertFxRefusalMessage(InsertFxRefusal refusal) {
switch (refusal) {
case InsertFxRefusal::None:
return {};
case InsertFxRefusal::NoCapture:
return "select a capture in the bank panel first, then insert it as an "
"instrument on the selected track";
case InsertFxRefusal::MultiCapture:
return "select exactly ONE capture -- a ReaSampler 9000 instance holds one "
"capture, so a multi-capture selection has no single sound to load";
case InsertFxRefusal::NoTrack:
return "select a track first -- the instrument is added to the selected "
"track's FX chain";
case InsertFxRefusal::MasterOnlySelected:
return "select a track other than the master -- the master track cannot "
"host the instrument";
}
return {};
}
} // namespace reasampler::ui
+35
View File
@@ -0,0 +1,35 @@
#pragma once
// insert_fx_enable — refusal fold behind the Insert-as-FX verb (bank_panel Placement
// button + its bindable action). One ReaSampler 9000 instance holds exactly one capture,
// so the verb needs exactly one focused capture AND a destination track.
#include <string>
namespace reasampler::ui {
// Why the verb cannot run, or None. The two axes are deliberately ordered payload-first,
// because they are knowable at DIFFERENT times: the panel selection is authoritative at
// draw time, so the button paints dead for it; track selection lives in the project with
// no change callback, so it is only ever spoken as a message at press time. A live button
// therefore does NOT imply NoTrack was ruled out — every caller must run the full fold.
enum class InsertFxRefusal {
None,
NoCapture,
MultiCapture,
NoTrack,
MasterOnlySelected, // only the master track is selected — GetSelectedTrack ignores it
};
InsertFxRefusal insertFxRefusal(int focusedCaptureCount, bool trackSelected,
bool masterOnlySelected = false);
// The button's live/dead bit: the payload axis alone, derived from the same fold so the
// drawn state and the pressed outcome cannot disagree about the capture rules.
bool insertFxButtonEnabled(int focusedCaptureCount);
// One sentence per refusal, prefix- and punctuation-free so the shell can frame it. The
// wording lives here rather than in the shell so "no silent refusal" is assertable
// without a DAW. None yields an empty string — nothing to say about a verb that ran.
std::string insertFxRefusalMessage(InsertFxRefusal refusal);
} // namespace reasampler::ui