Merge dev (tail-toggle panel refactor) into D2 Wave 2
# Conflicts: # src/bank_panel.cpp
This commit is contained in:
+98
-5
@@ -46,6 +46,7 @@
|
||||
#include "mode_switch.h"
|
||||
#include "peaks.h"
|
||||
#include "persist.h"
|
||||
#include "tail_control.h" // TailSetting, cycleTailMode, tailToggleLabel (pure)
|
||||
#include "track_guid.h" // guidString — canonical track GUID key (D2 Wave 2)
|
||||
#include "view.h" // applyMode — the D2/D4 mode-activation entrypoint the switch fires
|
||||
#include "view_mode_model.h" // autoTagNewContent / NewItem (D2 Wave 2)
|
||||
@@ -150,6 +151,20 @@ const LICE_pixel kColSegBorder = LICE_RGBA(70, 70, 76, 255); // segment di
|
||||
const COLORREF kRgbSegText = RGB(170, 170, 176); // inactive label
|
||||
const COLORREF kRgbSegActiveText = RGB(220, 235, 228); // active label
|
||||
|
||||
// --- Tail-mode footer (T1 exposure) -------------------------------------------
|
||||
// A fixed-height strip at the BOTTOM of the client area holding the tail-mode
|
||||
// toggle ("Tail: Off / Auto / Manual"). Clicking anywhere in it cycles the mode
|
||||
// (None -> Auto -> Manual -> None). Display/settings only: it mutates the panel's
|
||||
// in-memory tail setting the plain capture actions read — NEVER the project/bank/
|
||||
// arrange. The cycle/label logic is the pure tail_control module; only the draw +
|
||||
// click routing is here. The grid viewport is shortened by this strip's height so
|
||||
// cells never draw under it.
|
||||
constexpr int kFooterHeight = 26; // px; fixed strip at the bottom
|
||||
|
||||
const LICE_pixel kColFooterBg = LICE_RGBA(20, 20, 22, 255); // footer strip fill
|
||||
const LICE_pixel kColFooterBorder = LICE_RGBA(70, 70, 76, 255); // top divider
|
||||
const COLORREF kRgbFooterText = RGB(190, 205, 198); // toggle label
|
||||
|
||||
// --- Panel state --------------------------------------------------------------
|
||||
|
||||
// A computed thumbnail: the per-channel envelope at a known width. Held in the
|
||||
@@ -189,6 +204,12 @@ struct PanelState {
|
||||
// clear the selection rather than risk indices pointing past the new count.
|
||||
int selItemCount = 0;
|
||||
|
||||
// --- Tail-mode toggle (T1 exposure) ---------------------------------------
|
||||
// The current tail setting the plain capture actions read (bankPanelTailSetting).
|
||||
// Default None (exact bounds). In-memory only — resets on panel teardown; project
|
||||
// persistence is a noted follow-on. Mutated ONLY by a click in the footer strip.
|
||||
TailSetting tail;
|
||||
|
||||
// --- Audition preview (Wave B) --------------------------------------------
|
||||
//
|
||||
// The stock preview register we hand to PlayPreview/StopPreview. Its cs/mutex
|
||||
@@ -454,6 +475,45 @@ void drawModeSwitch(LICE_IBitmap* bmp, int w) {
|
||||
}
|
||||
}
|
||||
|
||||
// The tail-toggle footer rect for a client of width `w` and height `h`: the
|
||||
// full-width strip of fixed height pinned to the BOTTOM. A RECT (not HeaderRect)
|
||||
// since the whole strip is one hit target — a click anywhere in it cycles the mode.
|
||||
// Shared by paint and click routing so both agree on the band. Degenerate (empty)
|
||||
// when the client is too short to host it above the header.
|
||||
RECT panelFooter(int w, int h) {
|
||||
RECT rc{};
|
||||
rc.left = 0;
|
||||
rc.right = w;
|
||||
rc.top = h - kFooterHeight;
|
||||
rc.bottom = h;
|
||||
// Clamp so the footer never rides up into (or above) the header band on a very
|
||||
// short panel — it collapses to empty rather than overlapping the mode switch.
|
||||
if (rc.top < kHeaderHeight) rc.top = rc.bottom; // empty: top == bottom
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Draws the tail-mode toggle into the footer strip: a filled band, a top divider,
|
||||
// and the current mode's label ("Tail: Off / Auto / Manual") from the pure
|
||||
// tail_control module. READ-ONLY: reads g_panel.tail; the click handler mutates it.
|
||||
void drawTailFooter(LICE_IBitmap* bmp, int w, int h) {
|
||||
const RECT f = panelFooter(w, h);
|
||||
if (f.top >= f.bottom) return; // no room — skip (short panel)
|
||||
|
||||
LICE_FillRect(bmp, f.left, f.top, w, kFooterHeight, kColFooterBg, 1.0f, 0);
|
||||
// Top divider so the strip reads as distinct from the grid above it.
|
||||
LICE_Line(bmp, f.left, f.top, f.right, f.top, kColFooterBorder, 1.0f, 0, false);
|
||||
|
||||
HDC dc = bmp->getDC();
|
||||
if (!dc) return;
|
||||
const std::string label = tailToggleLabel(g_panel.tail);
|
||||
RECT rc = f;
|
||||
rc.left += 8; // small left pad so the label is not flush against the edge
|
||||
SetTextColor(dc, kRgbFooterText);
|
||||
SetBkMode(dc, TRANSPARENT);
|
||||
DrawText(dc, label.c_str(), -1, &rc,
|
||||
DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
|
||||
}
|
||||
|
||||
// The cell rects for the panel's CURRENT client width and bank size, translated
|
||||
// DOWN by the header height so the grid sits below the mode switch. Both paint and
|
||||
// mouse hit-testing call this so they share identical geometry (no drift between
|
||||
@@ -499,11 +559,15 @@ void paintPanel(HWND hwnd, HDC hdc) {
|
||||
// Draw each cell's thumbnail. Inner drawable width == cell width - inset;
|
||||
// compute the envelope at the cell's inner column count so bins map 1:1.
|
||||
const int binWidth = kGrid.cellWidth - 4;
|
||||
// Cells must not draw under the footer strip: the visible grid stops at the
|
||||
// footer top (or the client bottom when the panel is too short for a footer).
|
||||
const RECT footer = panelFooter(w, h);
|
||||
const int gridBottom = footer.top < footer.bottom ? footer.top : h;
|
||||
for (std::size_t i = 0; i < rects.size(); ++i) {
|
||||
const CellRect& rect = rects[i];
|
||||
// Skip cells entirely below the viewport (Wave A has no scroll; this
|
||||
// just avoids computing thumbnails that cannot be seen).
|
||||
if (rect.y >= h) continue;
|
||||
// Skip cells entirely below the visible grid area (Wave A has no scroll;
|
||||
// this just avoids computing thumbnails that cannot be seen).
|
||||
if (rect.y >= gridBottom) continue;
|
||||
const int idx = static_cast<int>(i);
|
||||
const bool selected = g_panel.selection.contains(idx);
|
||||
const bool focused = g_panel.selection.focus == idx;
|
||||
@@ -512,9 +576,10 @@ void paintPanel(HWND hwnd, HDC hdc) {
|
||||
}
|
||||
}
|
||||
|
||||
// The mode switch draws LAST so its header band overlays the top of the grid /
|
||||
// empty-state area regardless of which branch ran above.
|
||||
// The mode switch and tail footer draw LAST so their bands overlay the top/bottom
|
||||
// of the grid / empty-state area regardless of which branch ran above.
|
||||
drawModeSwitch(&bmp, w);
|
||||
drawTailFooter(&bmp, w, h);
|
||||
|
||||
BitBlt(hdc, 0, 0, w, h, bmp.getDC(), 0, 0, SRCCOPY);
|
||||
}
|
||||
@@ -836,6 +901,23 @@ void handleClick(int x, int y) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tail-mode footer: a click anywhere in the bottom strip cycles the tail mode
|
||||
// (None -> Auto -> Manual -> None) and repaints. Settings-only — it mutates the
|
||||
// panel's in-memory tail setting the capture actions read, and NOTHING in the
|
||||
// project/bank/arrange. Checked before the grid so a footer click never selects.
|
||||
{
|
||||
RECT cr{};
|
||||
GetClientRect(g_panel.hwnd, &cr);
|
||||
const int w = cr.right - cr.left;
|
||||
const int h = cr.bottom - cr.top;
|
||||
const RECT f = panelFooter(w, h);
|
||||
if (f.top < f.bottom && x >= f.left && x < f.right && y >= f.top && y < f.bottom) {
|
||||
g_panel.tail.mode = cycleTailMode(g_panel.tail.mode);
|
||||
invalidatePanel();
|
||||
return; // footer click consumed; do NOT fall through to grid selection
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<CellRect> rects = panelRects();
|
||||
const int hit = hitTestCell(x, y, rects);
|
||||
const int count = bankItemCount();
|
||||
@@ -1073,6 +1155,17 @@ void bankPanelRefresh() {
|
||||
InvalidateRect(g_panel.hwnd, nullptr, FALSE);
|
||||
}
|
||||
|
||||
TailSetting bankPanelTailSetting() {
|
||||
// In-memory for the extension's lifetime (g_panel is static): the toggle's mode
|
||||
// survives panel open/close and bank changes, and resets to the default None only
|
||||
// on extension unload. Persistence across project reload is a noted follow-on.
|
||||
// manualMs is clamped here so a caller always receives a within-cap length, even
|
||||
// if a future fine-adjust UI stored an over-cap value.
|
||||
TailSetting s = g_panel.tail;
|
||||
s.manualMs = clampManualMs(s.manualMs);
|
||||
return s;
|
||||
}
|
||||
|
||||
void bankPanelShutdown() {
|
||||
closePanel(); // stops audition + destroys the window
|
||||
deinitPreview(); // destroy the preview lock (after the last stop)
|
||||
|
||||
Reference in New Issue
Block a user