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
+2 -2
View File
@@ -10,8 +10,8 @@ VST3 editor (`shell/instrument/`).
Pure layout/hit-test/palette modules the panel draws through (`theme`,
`component_geometry`, `bank_grid`, `tab_strip`, `mode_switch`, `action_bar`,
`footer_bar`, `overflow_menu`, `prune_button`, `mode_enable`, `tooltip`, `card_drag`,
`card_meta`) live in `core/ui` / `core/model` and are documented there — this
`footer_bar`, `overflow_menu`, `prune_button`, `mode_enable`, `insert_fx_enable`,
`tooltip`, `card_drag`, `card_meta`) live in `core/ui` / `core/model` and are documented there — this
directory consumes them but does not own them. The promptless bank-mutation verbs
(`bankOpCreate`/`Rename`/`Delete`/… + `persistBankOp`) that `panel_bank_ops` skins
live in `shell/bank_ops`, a sibling directory, not here.
+29 -3
View File
@@ -128,6 +128,17 @@ std::vector<ActionBarRow> topBarRows() {
rows.push_back({"INSERT_SELECTED_CONFORM", "Insert Conform",
"insert selected sample at edit cursor (conform to tempo)",
ActionCluster::Placement, true});
// The one top-bar row with a live/dead gate: it places the PLAYER, and one instance
// holds one capture. Only the payload half of the fold is knowable at draw time —
// core/ui/insert_fx_enable.h states why the track half stays a press-time message.
// Counts through focusedSelectionIds() (not the raw selection) so the drawn state
// can't diverge from doInsertAsFx's own count — the raw selection can go stale
// against live membership (e.g. an undo/redo book restore) without being cleared.
rows.push_back({"LOAD_INSTRUMENT_ON_TRACK", "Insert as FX",
"insert selected sample as ReaSampler 9000 on the selected track",
ActionCluster::Placement,
insertFxButtonEnabled(
static_cast<int>(focusedSelectionIds().size()))});
return rows;
}
@@ -268,12 +279,27 @@ bool currentTooltip(int w, int h, std::string& textOut, int& ax, int& ay, int& a
if (s.index == hv.index) { slot = &s; break; }
if (!slot) return false;
const ActionBarRow& row = rows[static_cast<std::size_t>(hv.index)];
// Insert as FX paints dead only for the payload axis (NoCapture/MultiCapture);
// speak that refusal here rather than the generic phrase, so a dead button says
// WHY. NoTrack stays a press-time message (insert_fx_enable.h: track selection
// has no change callback, so it is only ever knowable at press time).
if (row.suffix == "LOAD_INSTRUMENT_ON_TRACK" && !row.enabled) {
const InsertFxRefusal refusal =
insertFxRefusal(static_cast<int>(focusedSelectionIds().size()), /*trackSelected=*/true);
if (refusal != InsertFxRefusal::None) {
textOut = insertFxRefusalMessage(refusal);
ax = slot->x; ay = slot->y; aw = slot->width; ah = slot->height;
return true;
}
}
// fullName is stored prefix-free; strip defensively in case a source ever
// carries the display prefix. Tooltip carries name + binding when bound
// (e.g. "capture selected item — F5"), name only when unbound.
const std::string phrase = stripActionPrefix(rows[static_cast<std::size_t>(hv.index)].fullName,
actionDisplayPrefix());
const int cmd = resolveBarCommandId(rows[static_cast<std::size_t>(hv.index)]);
const std::string phrase = stripActionPrefix(row.fullName, actionDisplayPrefix());
const int cmd = resolveBarCommandId(row);
const std::string binding = barBindingText(cmd);
textOut = binding.empty() ? phrase : phrase + " \xe2\x80\x94 " + binding; // " — " (em dash, UTF-8)
ax = slot->x; ay = slot->y; aw = slot->width; ah = slot->height;
+8 -2
View File
@@ -41,6 +41,7 @@
#include "core/ui/component_geometry.h"
#include "core/ui/drag_out.h"
#include "core/ui/footer_bar.h"
#include "core/ui/insert_fx_enable.h"
#include "core/ui/mode_enable.h"
#include "core/ui/overflow_menu.h"
#include "core/ui/prune_button.h"
@@ -134,6 +135,10 @@ using ui::hitTestMenuButton;
using ui::hitTestPruneButton;
using ui::hitTestSlot;
using ui::hitTestTabStrip;
using ui::insertFxButtonEnabled;
using ui::insertFxRefusal;
using ui::insertFxRefusalMessage;
using ui::InsertFxRefusal;
using ui::kCardNameScrimAlpha;
using ui::kCardStripHeight;
using ui::kCardStripPad;
@@ -420,8 +425,9 @@ struct ActionBarRow {
std::string shortLabel;
std::string fullName;
ActionCluster cluster = ActionCluster::Capture;
bool enabled = true; // opposite-mode gate for bottom-bar tag buttons; always true
// for the top bar (unconditional triggers)
bool enabled = true; // opposite-mode gate for the bottom-bar tag buttons, and the
// payload gate for the top bar's Insert as FX; every other top-
// bar row is an unconditional trigger and stays true
};
// Modifier state at event time. Alt = the replace modifier.