Cut shell/panel comment bloat ~47% (comments only, zero code change)

This commit is contained in:
2026-07-29 20:48:56 -04:00
parent 1f24c4b095
commit cc36dd59c7
15 changed files with 627 additions and 1320 deletions
+42 -81
View File
@@ -1,14 +1,12 @@
// panel_bank_ops.cpp — the bank-CRUD-UX + menus seam of the docked bank panel
// (Q-W2 split of the former bank_panel god-module; Phase B4/B5). Since Q-W6 the
// promptless bank verbs live in shell/bank_ops (model op + persistBankOp, taking
// ReaSamplerSession&); this TU is the panel's THIN UX SKIN over them — the menu
// handlers (prompts / confirms / message boxes / panel-state nudges / repaint),
// the book/bank accessors, the popup menus that drive them, and the selection-id /
// OS-drag path resolvers. The bindable bank_actions family is the sibling skin.
// panel_bank_ops.cpp — the bank-CRUD-UX + menus seam of the docked bank panel. The
// promptless bank verbs live in shell/bank_ops (bankOp* + persistBankOp); this TU is
// the panel's THIN UX SKIN over them — menu handlers, book/bank accessors, popup
// menus, and the selection-id / OS-drag path resolvers. `bank_actions` is the
// sibling bindable-action skin.
//
// Compiled into the reaper_reasampler MODULE. Includes reaper_plugin_functions.h
// WITHOUT REAPERAPI_IMPLEMENT — main.cpp owns the API pointers; here they are
// extern (CLAUDE.md §contract). DAW-verified, not unit tested.
// WITHOUT REAPERAPI_IMPLEMENT — main.cpp owns the API pointers. DAW-verified, not
// unit-tested.
#include <cstdio>
#include <filesystem>
@@ -18,7 +16,7 @@
#include "shell/panel/panel_state.h"
#include "shell/panel/panel_bank_ops.h"
#include "shell/bank_ops/bank_ops.h" // bankOp* promptless verbs (the Q-W6 non-UI seam)
#include "shell/bank_ops/bank_ops.h" // bankOp* promptless verbs
#include "shell/persist/session.h" // ReaSamplerSession — the live session the ops mutate
#define REAPERAPI_MINIMAL
@@ -32,7 +30,7 @@ namespace reasampler::panel {
namespace fs = std::filesystem;
// --- Current-project directory (mirrors the persist shell's derivation, ext_state_io.cpp)
// Mirrors the persist shell's derivation (ext_state_io.cpp).
std::string currentProjectDir() {
std::vector<char> buf(4096, '\0');
EnumProjects(-1, buf.data(), static_cast<int>(buf.size()));
@@ -41,8 +39,6 @@ std::string currentProjectDir() {
return normalizeSlashes(fs::path(rpp).parent_path().string());
}
// --- Book / bank accessors ----------------------------------------------------
BankBook* book() { return g_panel.session ? &g_panel.session->book() : nullptr; }
// The BankModel a region currently displays. Pool region -> the pool; banks region ->
@@ -72,17 +68,9 @@ std::vector<const Bank*> namedBanks() {
return out;
}
// --- Bank management ops (id-keyed; THIN UX SKINS over the bankOp* verbs) ------
//
// Q-W4/Q-W6: each handler here owns only the panel's UX (prompts / confirms /
// message boxes / panel-state nudges / repaint); the model op + persist is the
// shared bankOp* inner verb (shell/bank_ops), which takes the live session by
// reference — the book() check answers the one session-liveness question per
// handler. After a STRUCTURAL mutation (create/delete/evacuate) any
// Bank*/BankModel& is invalid — we resolve fresh, pass ids, and let the next
// refreshFingerprint repaint. On an unsaved project the empty-close discard in
// persistBankOp ensures no stale state survives (matches the capture/B3
// quiet-persist idiom).
// Bank management ops (id-keyed; THIN UX SKINS over the bankOp* verbs). After a
// STRUCTURAL mutation (create/delete/evacuate) any Bank*/BankModel& is invalid — we
// resolve fresh, pass ids, and let the next refreshFingerprint repaint.
void doCreateBank() {
if (!book()) return;
@@ -116,9 +104,9 @@ void doRenameBank(const std::string& bankId) {
invalidatePanel();
}
// Delete with the RICHER confirm-on-non-empty affordance (B4): the confirm names the
// member count AND offers evacuate as the one-click alternative (Yes=delete anyway,
// No=evacuate-then-keep, Cancel=abort) — richer than B3's basic YESNO.
// Delete with a confirm-on-non-empty affordance: the confirm names the member count
// and offers evacuate as the one-click alternative (Yes=delete anyway,
// No=evacuate-then-keep, Cancel=abort).
void doDeleteBank(const std::string& bankId) {
if (!book()) return;
const Bank* bk = book()->bank(bankId);
@@ -144,13 +132,10 @@ void doDeleteBank(const std::string& bankId) {
}
// r == 6 (Yes) falls through to a plain delete (drops members).
}
// S9: bump when the bank held samples (either the Yes-drop path or the No-evacuate-then-
// delete path moved/dropped members) — both change what a live instance could play. An
// empty-bank delete is purely organizational, no bump. The ORIGINAL member count decides
// (the No-path evacuated them moments ago, but the membership still changed).
// Bump generation when the bank held samples — an empty-bank delete is purely
// organizational. The ORIGINAL member count decides (the No-path already evacuated them).
if (!bankOpDelete(*g_panel.session, bankId, /*bumpGeneration=*/members > 0)) return;
// shownBankId is reconciled by the next fingerprint pass. If no named banks remain,
// nudge focus to the pool so the selection has a valid home.
// If no named banks remain, nudge focus to the pool so the selection has a valid home.
if (namedBanks().empty()) g_panel.focusedRegion = Region::Pool;
invalidatePanel();
}
@@ -172,40 +157,33 @@ void doActivateBank(const std::string& bankId) {
} // namespace
// Move or copy `sampleIds` from `srcBankId` to `destBankId` (index-only). Thin panel
// skin over bankOpTransfer (the one-home verb owns the loop, the verb-aware no-op
// guardrail, and the undo-batched persist); this layer clears the stale selection
// and repaints on an actual mutation.
// skin over bankOpTransfer; clears the stale selection and repaints on an actual mutation.
void transferSamples(const std::vector<std::string>& sampleIds,
const std::string& srcBankId, const std::string& destBankId,
bool copy) {
if (!book()) return; // no live session — nothing to transfer within
if (!bankOpTransfer(*g_panel.session, sampleIds, srcBankId, destBankId, copy))
return; // nothing changed — no persist, no undo point
// The selection indexed into the source; after a move those indices are stale, so
// clear it (the fingerprint pass will also clear, but do it now for immediacy).
// Selection indexed into the source; after a move those indices are stale.
g_panel.selection = Selection{};
invalidatePanel();
}
// Remove `sampleIds` from `srcBankId` (index-only, this-bank scope). Thin panel skin
// over bankOpRemove — see the verb for the never-deletes-bytes / silent-remove /
// one-Ctrl-Z contract. Clears the stale selection and repaints on an actual removal.
// over bankOpRemove. Clears the stale selection and repaints on an actual removal.
void removeSamples(const std::vector<std::string>& sampleIds,
const std::string& srcBankId) {
if (!book()) return; // no live session — nothing to remove from
if (!bankOpRemove(*g_panel.session, sampleIds, srcBankId))
return; // nothing changed — no persist, no undo point
// The selection indexed into the source; after a remove those indices are stale, so
// clear it (the fingerprint pass will also clear, but do it now for immediacy).
g_panel.selection = Selection{};
invalidatePanel();
}
// The selection's sample ids resolved against the FOCUSED region's bank (source of a
// move/copy). Returns ids in bank order; empty when nothing selected.
// move/copy). Selection ordinals index the DISPLAY (slot) order, not BankModel
// insertion order. Returns ids in bank order; empty when nothing selected.
std::vector<std::string> focusedSelectionIds() {
// L7: selection ordinals index the DISPLAY (slot) order, not BankModel insertion order.
// orderedIds[i] is the id at selection ordinal i.
std::vector<std::string> ids;
const RegionDisplay disp = focusedDisplay();
const int count = disp.occupiedCount();
@@ -214,14 +192,11 @@ std::vector<std::string> focusedSelectionIds() {
return ids;
}
// Resolves the ARMED drag payload (g_panel.dragSampleIds, from g_panel.dragSourceBankId) to
// the absolute, existing-file path list for a native OS drag-out (M11). Reuses the SAME M4
// path machinery the panel uses for audition/insert (resolveBankFile over the current
// project dir) — no temp copies; the drag points straight at the on-disk bank files. Each
// id is looked up in its SOURCE bank's index (the payload's origin, not the focused region,
// which can differ once the pointer roams), resolved, stat'd, then handed to the pure
// drag_out::assemblePathList for dedupe + skip-missing/unresolved policy. Read-only: no
// mutation of sample / index / selection (invariant #2).
// Resolves the ARMED drag payload to the absolute, existing-file path list for a native
// OS drag-out. Reuses resolveBankFile (audition/insert's path machinery) — no temp
// copies. Each id is looked up in its SOURCE bank's index (not the focused region, which
// can differ once the pointer roams), then handed to drag_out::assemblePathList for
// dedupe + skip-missing/unresolved policy. Read-only.
std::vector<std::string> resolveDragPathsForOs() {
std::vector<ResolvedSample> resolved;
BankBook* b = book();
@@ -242,19 +217,13 @@ std::vector<std::string> resolveDragPathsForOs() {
return assemblePathList(resolved).paths;
}
// --- Popup menus --------------------------------------------------------------
//
// SWELL/Win32 both expose CreatePopupMenu / InsertMenu (SWELL aliases SWELL_InsertMenu
// -> InsertMenu) / TrackPopupMenu(TPM_RETURNCMD) / DestroyMenu. We build a menu of
// (label -> small int command), track it at screen coords, and switch on the return.
// Menu command ids are LOCAL to the popup (not REAPER action ids) — TPM_RETURNCMD
// hands the chosen id straight back, so no hookcommand routing is involved.
// SWELL/Win32 both expose CreatePopupMenu / InsertMenu / TrackPopupMenu(TPM_RETURNCMD) /
// DestroyMenu. Menu command ids below are LOCAL to the popup (not REAPER action ids) —
// TPM_RETURNCMD hands the chosen id straight back, so no hookcommand routing is involved.
namespace {
// Appends a string item (id) to `menu` at its end. Portable over Win32/SWELL: both
// accept InsertMenu(menu, pos, MF_BYPOSITION|MF_STRING, id, text) with a negative
// position appending. Win32 and SWELL both treat pos < 0 as an append.
// Win32 and SWELL both treat pos < 0 as append.
void menuAppend(HMENU menu, unsigned int id, const char* text, bool grayed = false) {
UINT flags = MF_BYPOSITION | MF_STRING;
if (grayed) flags |= MF_GRAYED;
@@ -272,7 +241,7 @@ enum : unsigned int {
kMenuDelete,
kMenuEvacuate,
kMenuCreate,
kMenuRemove, // remove selected sample(s) from the source bank (B5)
kMenuRemove, // remove selected sample(s) from the source bank
kMenuMoveBase = 1000, // move-to-bank: kMenuMoveBase + destination index
kMenuCopyBase = 2000, // copy-to-bank: kMenuCopyBase + destination index
};
@@ -313,11 +282,10 @@ void showTabMenu(int screenX, int screenY, const std::string& bankId) {
}
}
// Opens the top-toolbar overflow ("⋯" More) popup at the button's screen position and fires the
// chosen rare-capture variant's command (L5 refinement 1). Menu ids are LOCAL to the popup
// (1-based ordinal into overflowMenuRows); TPM_RETURNCMD hands the chosen id back, then we
// resolve + fire the corresponding registered command id via the SAME contract the visible
// buttons use. Defined here (after menuAppend/menuSeparator); forward-declared above.
// Opens the top-toolbar overflow ("⋯" More) popup and fires the chosen rare-capture
// variant's command. Menu ids are LOCAL to the popup (1-based ordinal into
// overflowMenuRows); we resolve + fire the corresponding registered command id via
// the same contract the visible buttons use.
void showMoreMenu() {
if (!g_panel.hwnd) return;
const std::vector<ActionBarRow> rows = overflowMenuRows();
@@ -349,9 +317,8 @@ void showMoreMenu() {
}
// Shows the move/copy menu for the current selection (the SOURCE is the focused
// region's bank). Lists every OTHER bank (pool + named) as a move destination, then a
// copy submenu-free flat list (copy entries follow the move block). Move is the
// default (listed first); copy is the deliberate secondary act.
// region's bank). Lists every OTHER bank as a move destination, then the same list
// as a copy destination. Move is the default (listed first); copy is secondary.
void showSelectionMenu(int screenX, int screenY) {
const std::vector<std::string> sel = focusedSelectionIds();
if (sel.empty()) return;
@@ -401,18 +368,14 @@ void showSelectionMenu(int screenX, int screenY) {
} // namespace reasampler::panel
// --- Public API (panel_bank_ops.h) ---------------------------------------------
namespace reasampler {
// One home (Q-W4) for the former actions/panel byte-identical twins.
// COMMA GUARD: GetUserInputs splits returned values on a separator defaulting to ',',
// so the return separator is overridden to \x1f (un-typeable) via the documented
// `separator=X` trailing pseudo-caption (SDK ~3806) — any printable name round-trips.
// GetUserInputs splits returned values on a separator defaulting to ',', so the
// separator is overridden to \x1f (un-typeable) via the documented `separator=X`
// trailing pseudo-caption — any printable name round-trips.
bool promptBankName(const char* title, const char* caption, const std::string& initial,
std::string& out) {
std::vector<char> buf(512, '\0');
// Pre-fill: GetUserInputs seeds the field from the retvals buffer's initial value.
std::snprintf(buf.data(), buf.size(), "%s", initial.c_str());
const std::string captions = std::string(caption) + ",separator=\x1f";
if (!GetUserInputs(title, 1, captions.c_str(), buf.data(),
@@ -424,8 +387,6 @@ bool promptBankName(const char* title, const char* caption, const std::string& i
return true;
}
// --- Selection read seam --------------------------------------------------------
std::vector<std::string> bankPanelSelectedSampleIds() {
return panel::focusedSelectionIds();
}