Retire the zone system: one capture = one parameter set, and re-seam the engine and Sample face into bands
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
// editor_paint_browse.cpp — the Browse modal's painter: the full-window
|
||||
// select-then-confirm picker (wash, search box, filter tabs, card grid, scrollbar, footer).
|
||||
// Windows-only. Shares the Sample face's title-band + empty-state painters via the class +
|
||||
// editor_internal.h.
|
||||
|
||||
#include "shell/instrument/reasampler_editor.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "core/instrument/ui/browser_scroll.h" // BrowseModal + scroll/search geometry
|
||||
#include "shell/instrument/editor_internal.h" // kit adapters + labels
|
||||
#include "shell/instrument/reasampler_processor.h"
|
||||
|
||||
namespace reasampler::vst {
|
||||
|
||||
using namespace reasampler::ui; // kit vocabulary
|
||||
using namespace reasampler::instrument::ui; // browser geometry
|
||||
using namespace reasampler::instrument::map; // SampleChoice / BankChoice
|
||||
|
||||
void ReaSamplerEditor::paintBrowse(LICE_IBitmap* bmp, int w, int h) {
|
||||
// A full-window modal sheet over the Sample face. Dim the underlying Sample face with a
|
||||
// bg/base wash, then draw the picker opaque on top.
|
||||
LICE_FillRect(bmp, 0, 0, w, h, toLice(roleColor(Role::BgBase)), 0.82f, 0);
|
||||
const BrowseModal bm = computeBrowseModal(w, h);
|
||||
|
||||
// Title band + Back button (returns to Sample, discarding any pending pick).
|
||||
drawTitleBand(bmp, bm.title, "Browse - pick a capture");
|
||||
{
|
||||
const KitButtonBox box{toKitBox(bm.back)};
|
||||
const InteractionState st =
|
||||
isHovered(HoverKind::kBack, -1) ? InteractionState::Hover : InteractionState::Rest;
|
||||
drawButton(bmp, box, "Back", st, /*warn=*/false);
|
||||
}
|
||||
|
||||
// Search box (type-to-filter). A focused box lifts to Focus + a ring; else Rest/Hover.
|
||||
const Rect searchAbs = bm.search;
|
||||
const InteractionState searchState =
|
||||
searchFocused_ ? InteractionState::Focus
|
||||
: (isHovered(HoverKind::kSearchBox, -1) ? InteractionState::Hover
|
||||
: InteractionState::Rest);
|
||||
fillSurface(bmp, toKitBox(searchAbs), Role::BgCell, searchState);
|
||||
if (searchFocused_) {
|
||||
LICE_DrawRect(bmp, searchAbs.x, searchAbs.y, searchAbs.width - 1,
|
||||
searchAbs.height - 1, toLice(roleColor(Role::TextPrimary)), 1.0f, 0);
|
||||
}
|
||||
{
|
||||
std::string sb = searchQuery_.empty()
|
||||
? std::string("Search captures...")
|
||||
: ("Search: " + searchQuery_ + (searchFocused_ ? "_" : ""));
|
||||
Rect sbText = Rect::ltrb(searchAbs.x + 6, searchAbs.y, searchAbs.right() - 6, searchAbs.bottom());
|
||||
kitText(bmp, sbText, sb.c_str(), Font::Label,
|
||||
searchQuery_.empty() ? Role::TextDim : Role::TextPrimary);
|
||||
}
|
||||
|
||||
// Tabs + card grid, laid out over the content sub-area by the pure module (origin-offset).
|
||||
const Rect browserArea = bm.content;
|
||||
const BrowserLayout bl = layoutBrowser(browserArea.width, browserArea.height);
|
||||
const int ox = browserArea.x;
|
||||
const int oy = browserArea.y;
|
||||
scrollOffset_ = clampScrollOffset(bl, static_cast<int>(visible_.size()), scrollOffset_);
|
||||
|
||||
const int tabCount = static_cast<int>(banks_.size()) + 1;
|
||||
for (int i = 0; i < tabCount; ++i) {
|
||||
Rect t = filterTabRect(bl, tabCount, i);
|
||||
t = Rect::ltrb(t.x + ox, t.y + oy, t.right() + ox, t.bottom() + oy);
|
||||
const std::string label = (i == 0) ? "All" : banks_[static_cast<std::size_t>(i - 1)].displayName;
|
||||
const bool active = (i == 0) ? activeFilterBankId_.empty()
|
||||
: (banks_[static_cast<std::size_t>(i - 1)].id == activeFilterBankId_);
|
||||
const InteractionState state =
|
||||
active ? InteractionState::Active
|
||||
: (isHovered(HoverKind::kFilterTab, i) ? InteractionState::Hover
|
||||
: InteractionState::Rest);
|
||||
fillSurface(bmp, toKitBox(t), Role::BgCell, state);
|
||||
kitTextCentered(bmp, t, label.c_str(), Font::Label,
|
||||
active ? Role::BgBase : Role::TextPrimary);
|
||||
}
|
||||
|
||||
// Cards (the visible window at the current scroll offset). The pending pick
|
||||
// (browsePendingId_) is marked with the accent-primary border; the currently-loaded id
|
||||
// gets a faint tertiary border.
|
||||
const int bins = thumbBins(bl);
|
||||
const int cardCount = static_cast<int>(visible_.size());
|
||||
const VisibleRange vr = visibleCardRange(bl, cardCount, scrollOffset_);
|
||||
for (int i = vr.first; i < vr.last; ++i) {
|
||||
Rect content = cardContentRect(bl, i);
|
||||
Rect thumb = cardThumbnailRect(bl, i);
|
||||
Rect labelR = cardLabelRect(bl, i);
|
||||
content = Rect::ltrb(content.x + ox, content.y + oy - scrollOffset_,
|
||||
content.right() + ox, content.bottom() + oy - scrollOffset_);
|
||||
thumb = Rect::ltrb(thumb.x + ox, thumb.y + oy - scrollOffset_,
|
||||
thumb.right() + ox, thumb.bottom() + oy - scrollOffset_);
|
||||
labelR = Rect::ltrb(labelR.x + ox, labelR.y + oy - scrollOffset_,
|
||||
labelR.right() + ox, labelR.bottom() + oy - scrollOffset_);
|
||||
|
||||
const SampleChoice& s = visible_[static_cast<std::size_t>(i)];
|
||||
const bool pending = (s.id == browsePendingId_);
|
||||
const bool loaded = (s.id == selectedId_);
|
||||
const InteractionState cardState =
|
||||
isHovered(HoverKind::kCard, i) ? InteractionState::Hover : InteractionState::Rest;
|
||||
fillSurface(bmp, toKitBox(content), Role::BgCell, cardState);
|
||||
const KitColor cardBorder = pending ? roleColor(Role::AccentPrimary)
|
||||
: (loaded ? roleColor(Role::AccentTertiary)
|
||||
: roleColor(Role::LineHairline));
|
||||
LICE_DrawRect(bmp, content.x, content.y, content.width - 1, content.height - 1,
|
||||
toLice(cardBorder), 1.0f, 0);
|
||||
drawEnvelope(bmp, thumb, thumbnailFor(s.id, bins));
|
||||
|
||||
std::string caption = s.displayName.empty() ? s.id : s.displayName;
|
||||
Rect nameR = Rect::ltrb(labelR.x + 3, labelR.y, labelR.right() - 3, labelR.y + labelR.height / 2);
|
||||
Rect badgeR = Rect::ltrb(labelR.x + 3, nameR.bottom(), labelR.right() - 3, labelR.bottom());
|
||||
kitText(bmp, nameR, caption.c_str(), Font::Label, Role::TextPrimary);
|
||||
std::string badge;
|
||||
if (s.rootNote) badge = "root " + noteLabel(*s.rootNote);
|
||||
else if (s.key) badge = *s.key;
|
||||
else badge = "root -";
|
||||
kitText(bmp, badgeR, badge.c_str(), Font::Micro, Role::TextDim);
|
||||
}
|
||||
|
||||
// Scrollbar thumb.
|
||||
{
|
||||
const Rect thumb = scrollThumbRect(bl, cardCount, scrollOffset_);
|
||||
if (thumb.height > 0) {
|
||||
const bool dragging = (drag_ == DragKind::kScrollThumb);
|
||||
const KitColor tc = roleColor(dragging ? Role::AccentHot : Role::AccentPrimary);
|
||||
LICE_FillRect(bmp, thumb.x + ox, thumb.y + oy, thumb.width, thumb.height,
|
||||
toLice(tc), 0.8f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (visible_.empty()) paintEmptyState(bmp, browserArea);
|
||||
|
||||
// Footer: Cancel (discard, return to Sample) + Load (commit the pending pick). Load is inert
|
||||
// (no accent) until a card is picked. Draw a footer strip so the buttons read as a modal bar.
|
||||
Rect footer = Rect::ltrb(0, bm.content.bottom(), w, h);
|
||||
fillSurface(bmp, toKitBox(footer), Role::BgPanel, InteractionState::Rest);
|
||||
{
|
||||
const KitButtonBox box{toKitBox(bm.cancel)};
|
||||
const InteractionState st =
|
||||
isHovered(HoverKind::kBrowseCancel, -1) ? InteractionState::Hover : InteractionState::Rest;
|
||||
drawButton(bmp, box, "Cancel", st, /*warn=*/false);
|
||||
}
|
||||
{
|
||||
const KitButtonBox box{toKitBox(bm.confirm)};
|
||||
const bool armed = !browsePendingId_.empty();
|
||||
const InteractionState st = armed
|
||||
? (isHovered(HoverKind::kBrowseConfirm, -1) ? InteractionState::Hover : InteractionState::Active)
|
||||
: InteractionState::Rest;
|
||||
drawButton(bmp, box, "Load", st, /*warn=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace reasampler::vst
|
||||
|
||||
#endif // _WIN32
|
||||
Reference in New Issue
Block a user