feat(prune): R3 guarded deletion — confirm-to-delete + panel button
Extend BANK_PRUNE_FOLDER from report-only to dry-run→confirm-with-manifest→ delete exactly the pure-core orphan set (fresh recompute, stale entries skipped). Windows routes to Recycle Bin (SHFileOperation+FOF_ALLOWUNDO), else unlink. New pure prune_button module + footer button dispatching the action id. No ext-state write, no undo point (deletion is not REAPER-undoable).
This commit is contained in:
@@ -57,6 +57,7 @@
|
||||
#include "mode_switch.h"
|
||||
#include "peaks.h"
|
||||
#include "persist.h"
|
||||
#include "prune_button.h" // footer prune-button layout + hit-test (pure, R3)
|
||||
#include "tab_strip.h"
|
||||
#include "tail_control.h" // TailSetting, cycleTailMode, tailToggleLabel (pure)
|
||||
#include "track_guid.h" // guidString — canonical track GUID key (D2 Wave 2)
|
||||
@@ -102,6 +103,7 @@
|
||||
#define REAPERAPI_WANT_StopPreview
|
||||
#define REAPERAPI_WANT_GetUserInputs
|
||||
#define REAPERAPI_WANT_ShowMessageBox
|
||||
#define REAPERAPI_WANT_Main_OnCommand // fire the prune action by command id (R3 button)
|
||||
#define REAPERAPI_WANT_genGuid
|
||||
#define REAPERAPI_WANT_guidToString
|
||||
#include "reaper_plugin_functions.h"
|
||||
@@ -152,6 +154,13 @@ const COLORREF kRgbFooterText = RGB(190, 205, 198);
|
||||
// not an interactive control, so it recedes visually (V3 unobtrusive placement).
|
||||
const COLORREF kRgbFooterVersion = RGB(120, 128, 124);
|
||||
|
||||
// Prune button (R3): a raised control in the footer that fires the "Prune bank folder"
|
||||
// action. A muted warm tone so it reads as a distinct-but-not-alarming affordance (the
|
||||
// destructive confirm lives behind it, not on the button itself).
|
||||
const LICE_pixel kColPruneBtnBg = LICE_RGBA(62, 46, 42, 255);
|
||||
const LICE_pixel kColPruneBtnBorder = LICE_RGBA(96, 72, 66, 255);
|
||||
const COLORREF kRgbPruneBtnText = RGB(210, 188, 180);
|
||||
|
||||
// --- Vertical split + region headers + tab strip (Phase B4) -------------------
|
||||
//
|
||||
// The client area, top to bottom: mode-switch header (kHeaderHeight) | split body |
|
||||
@@ -554,6 +563,37 @@ void drawTailFooter(LICE_IBitmap* bmp, int w, int h) {
|
||||
DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
||||
}
|
||||
|
||||
// The prune button's rect within the footer, derived from the client size. SINGLE source
|
||||
// of truth for both draw and hit-test (they never drift). Empty (button.empty()) when the
|
||||
// footer is degenerate or too narrow to place the button clear of the tail label — the
|
||||
// action stays reachable via its bindable command, so a suppressed button is graceful.
|
||||
// The version readout uses an 8 px right inset (drawTailFooter); the button's rightInset
|
||||
// clears it (~72 px) so the two never overlap at normal panel widths.
|
||||
ButtonRect pruneButtonRectFor(int w, int h) {
|
||||
const RECT f = panelFooter(w, h);
|
||||
if (f.top >= f.bottom) return ButtonRect{}; // degenerate footer -> no button
|
||||
const FooterRect footer{f.left, f.top, f.right - f.left, f.bottom - f.top};
|
||||
return computePruneButton(footer, PruneButtonSpec{});
|
||||
}
|
||||
|
||||
// Draws the prune button into the footer (called after drawTailFooter fills the strip).
|
||||
// No-op when the button is suppressed (footer too narrow). READ-ONLY: draws only.
|
||||
void drawPruneButton(LICE_IBitmap* bmp, int w, int h) {
|
||||
const ButtonRect b = pruneButtonRectFor(w, h);
|
||||
if (b.empty()) return;
|
||||
|
||||
LICE_FillRect(bmp, b.x, b.y, b.width, b.height, kColPruneBtnBg, 1.0f, 0);
|
||||
LICE_DrawRect(bmp, b.x, b.y, b.width, b.height, kColPruneBtnBorder, 1.0f, 0);
|
||||
|
||||
HDC dc = bmp->getDC();
|
||||
if (!dc) return;
|
||||
RECT rc{b.x, b.y, b.x + b.width, b.y + b.height};
|
||||
SetBkMode(dc, TRANSPARENT);
|
||||
SetTextColor(dc, kRgbPruneBtnText);
|
||||
DrawText(dc, "Prune", -1, &rc,
|
||||
DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
||||
}
|
||||
|
||||
// True iff client-relative (x, y) falls inside the (non-degenerate) footer strip.
|
||||
// Shared by the footer click (cycle mode) and the scroll-wheel (Manual fine-adjust)
|
||||
// so both agree on the hit target.
|
||||
@@ -902,6 +942,7 @@ void paintPanel(HWND hwnd, HDC hdc) {
|
||||
|
||||
drawModeSwitch(&bmp, w);
|
||||
drawTailFooter(&bmp, w, h);
|
||||
drawPruneButton(&bmp, w, h); // R3: raised over the footer strip
|
||||
|
||||
BitBlt(hdc, 0, 0, w, h, bmp.getDC(), 0, 0, SRCCOPY);
|
||||
}
|
||||
@@ -1596,6 +1637,20 @@ void handleClick(int x, int y) {
|
||||
}
|
||||
}
|
||||
|
||||
// Prune button (R3): checked BEFORE the footer's tail-cycle so a click on the button
|
||||
// fires prune, not a tail cycle. Fires the "Prune bank folder" action THROUGH its
|
||||
// registered command id (fork R-E: dispatch the command, do not call the session
|
||||
// directly), so the panel affordance and the bindable action share the one guarded
|
||||
// dry-run/confirm/delete path in doBankPruneFolder. A 0 id (pre-registration) no-ops.
|
||||
{
|
||||
const ButtonRect pb = pruneButtonRectFor(w, h);
|
||||
if (hitTestPruneButton(x, y, pb)) {
|
||||
const int cmd = bankPruneCommandId();
|
||||
if (cmd != 0) Main_OnCommand(cmd, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Tail footer: a click anywhere in the bottom strip cycles the tail mode
|
||||
// (None -> Auto -> Manual -> None) and repaints. It mutates the SESSION's tail
|
||||
// setting (which the capture actions read and persist saves with the project) and
|
||||
|
||||
Reference in New Issue
Block a user