Merge Ω-W1-T4: deck reflow, focus-by-click overlay selection, single-button toggles
This commit is contained in:
@@ -34,6 +34,16 @@ void ReaSamplerEditor::onMouseDown(int x, int y) {
|
||||
// Band order matters only where bands can overlap on a degenerate window; each branch
|
||||
// reports whether it consumed the click so the next band gets a clean shot.
|
||||
const FaceLayout fl = faceLayout(w, h);
|
||||
// The overlay focus follows the deck (mouseDownDeck sets it), and clicking a control
|
||||
// surface OUTSIDE the envelope decks clears it. The waveform band is deliberately not one
|
||||
// of those surfaces: it IS the overlay, so editing what you selected must not deselect it.
|
||||
// Chrome's own dead space (the title band, the gaps between its controls) is not a control
|
||||
// surface either — hoverChrome answers only for chrome's actual interactive elements.
|
||||
if (drag_ == DragKind::kNone && hoverChrome(fl, x, y).kind != HoverKind::kNone &&
|
||||
overlayEnv_ != OverlayEnv::kNone) {
|
||||
overlayEnv_ = OverlayEnv::kNone;
|
||||
invalidate(); // the chrome branches below repaint only what THEY changed
|
||||
}
|
||||
if (mouseDownChrome(fl, x, y)) return;
|
||||
if (selectedId_.empty()) return; // empty state — chrome nav only
|
||||
if (mouseDownDeck(fl, x, y)) return;
|
||||
|
||||
@@ -75,28 +75,19 @@ bool ReaSamplerEditor::mouseDownChrome(const FaceLayout& fl, int x, int y) {
|
||||
}
|
||||
// The loop enable. Inert (not hidden) outside Gate: that refusal comes from the engine and
|
||||
// no click can talk it out of it — unlike the user's own off, which the marks themselves
|
||||
// still offer to reverse.
|
||||
if (loopControlsLive()) {
|
||||
if (contains(cr.loopOff, x, y)) {
|
||||
setLoopEnabled(false);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
if (contains(cr.loopOn, x, y)) {
|
||||
setLoopEnabled(true);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (contains(cr.chanMono, x, y)) {
|
||||
channelMode_ = ChannelMode::Mono;
|
||||
processor_->setChannelMode(ChannelMode::Mono);
|
||||
// still offer to reverse. One button, so the click's meaning is "the other state": read the
|
||||
// current enable off the markers, which is the same source the button's paint reads.
|
||||
if (loopControlsLive() && contains(cr.loop, x, y)) {
|
||||
// setLoopEnabled owns the frames <= 0 guard (empty capture) — no need to repeat it here.
|
||||
const auto frames = static_cast<std::int64_t>(monoPcmFor(selectedId_).size());
|
||||
setLoopEnabled(!pickedMarkers(frames).hasLoop);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
if (contains(cr.chanStereo, x, y)) {
|
||||
channelMode_ = ChannelMode::Stereo;
|
||||
processor_->setChannelMode(ChannelMode::Stereo);
|
||||
if (contains(cr.channel, x, y)) {
|
||||
channelMode_ =
|
||||
(channelMode_ == ChannelMode::Stereo) ? ChannelMode::Mono : ChannelMode::Stereo;
|
||||
processor_->setChannelMode(channelMode_);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
@@ -161,12 +152,8 @@ HoverTarget ReaSamplerEditor::hoverChrome(const FaceLayout& fl, int x,
|
||||
if (contains(cr.bake, x, y)) return {HoverKind::kBake, -1};
|
||||
if (contains(cr.preview, x, y)) return {HoverKind::kPreview, -1};
|
||||
if (contains(cr.velCell, x, y)) return {HoverKind::kVelKnob, -1};
|
||||
if (loopControlsLive()) {
|
||||
if (contains(cr.loopOff, x, y)) return {HoverKind::kLoopOff, -1};
|
||||
if (contains(cr.loopOn, x, y)) return {HoverKind::kLoopOn, -1};
|
||||
}
|
||||
if (contains(cr.chanMono, x, y)) return {HoverKind::kChanMono, -1};
|
||||
if (contains(cr.chanStereo, x, y)) return {HoverKind::kChanStereo, -1};
|
||||
if (loopControlsLive() && contains(cr.loop, x, y)) return {HoverKind::kLoop, -1};
|
||||
if (contains(cr.channel, x, y)) return {HoverKind::kChannel, -1};
|
||||
if (!cr.rootStrip.empty()) {
|
||||
const StripLayout sl = layoutStrip(cr.rootStrip.width, cr.rootStrip.height);
|
||||
const int note = keyAtPoint(sl, x - cr.rootStrip.x, y - cr.rootStrip.y);
|
||||
|
||||
@@ -29,10 +29,20 @@ bool ReaSamplerEditor::mouseDownDeck(const FaceLayout& fl, int x, int y) {
|
||||
|
||||
const DeckLayout dl = layoutDeck(fl.deckDescs, band.x, band.y, band.width);
|
||||
const DeckHit hit = hitTestDeck(dl, x, y);
|
||||
if (hit.kind == DeckHitKind::CaptionRadio) {
|
||||
overlayEnv_ = nextOverlaySelection(overlayEnv_, hit.id);
|
||||
invalidate(); // view state only: no parameter write, no reload
|
||||
return true;
|
||||
// The deck IS the overlay's click target: anywhere inside an envelope group — panel
|
||||
// background, knob or button — focuses that envelope, and any other group clears it. Set
|
||||
// BEFORE the kind switch and it consumes nothing, so every grab and commit below still
|
||||
// runs. A drag in flight owns the surface, so it refuses the change — always true at this
|
||||
// call site today (mouse capture makes a real WM_LBUTTONDOWN-while-dragging unreachable;
|
||||
// WM_CAPTURECHANGED and WM_LBUTTONUP both reset drag_ before another down can land), but
|
||||
// left explicit rather than assumed so a future capture-handling change fails loud, not
|
||||
// by silently letting a drag's own surface steal its own focus mid-gesture.
|
||||
if (drag_ == DragKind::kNone) {
|
||||
const OverlayEnv focus = overlayEnvForGroup(hit.group);
|
||||
if (focus != overlayEnv_) {
|
||||
overlayEnv_ = focus;
|
||||
invalidate(); // view state only: no parameter write, no reload
|
||||
}
|
||||
}
|
||||
if (hit.kind == DeckHitKind::CaptionToggle || hit.kind == DeckHitKind::RowToggle) {
|
||||
switch (static_cast<ParamControl>(hit.id)) {
|
||||
@@ -65,29 +75,29 @@ bool ReaSamplerEditor::mouseDownDeck(const FaceLayout& fl, int x, int y) {
|
||||
commitAndReload();
|
||||
break;
|
||||
case ParamControl::kLimiterEnable: {
|
||||
const bool on = (hit.segment == 1);
|
||||
if (on != params_.limiterEnabled) {
|
||||
params_.limiterEnabled = on;
|
||||
// Commits the audible state and the persisted state together, here, because
|
||||
// this is a control the user A/Bs. The funnel only ARMS the host's latency
|
||||
// restart — the sync tick delivers it — so nothing on this path calls into
|
||||
// the host from inside a mouse handler.
|
||||
processor_->setLimiterEnabled(on);
|
||||
}
|
||||
// One button, so its next state is the opposite of the current one — which is
|
||||
// also why the old "did it actually change" guard is gone: it always does.
|
||||
const bool on = !params_.limiterEnabled;
|
||||
params_.limiterEnabled = on;
|
||||
// Commits the audible state and the persisted state together, here, because
|
||||
// this is a control the user A/Bs. The funnel only ARMS the host's latency
|
||||
// restart — the sync tick delivers it — so nothing on this path calls into
|
||||
// the host from inside a mouse handler.
|
||||
processor_->setLimiterEnabled(on);
|
||||
invalidate();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// Parameter-set toggles (play mode / pitch engine / pitch-env + filter enable,
|
||||
// and the three env-mode toggles).
|
||||
applyParamControl(hit.id, 0.0, hit.segment);
|
||||
// and the three env-mode selectors). A single button carries no segment, so
|
||||
// its next state is derived from the parameter set rather than read off the
|
||||
// click — deck_values owns that derivation.
|
||||
const int segment =
|
||||
hit.segment >= 0
|
||||
? hit.segment
|
||||
: nextToggleSegment(static_cast<ParamControl>(hit.id), params_.play);
|
||||
applyParamControl(hit.id, 0.0, segment);
|
||||
commitAndReload();
|
||||
// Flipping an EG's Staged|Spline toggle makes THAT envelope's overlay active,
|
||||
// so the contour (or the staged shape you just returned to) is what's drawn.
|
||||
// overlayEnvForModeToggle answers kNone for every other toggle this default
|
||||
// case handles, which is why the assignment is conditional.
|
||||
const OverlayEnv modeEnv = overlayEnvForModeToggle(hit.id);
|
||||
if (modeEnv != OverlayEnv::kNone) overlayEnv_ = modeEnv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -215,7 +225,6 @@ HoverTarget ReaSamplerEditor::hoverDeck(const FaceLayout& fl, int x,
|
||||
// The meter reports its own state continuously; a hover on it would only mean "the clip
|
||||
// cap is clearable", which the cap's presence already says.
|
||||
if (dh.kind == DeckHitKind::Column) return {};
|
||||
if (dh.kind == DeckHitKind::CaptionRadio) return {HoverKind::kEnvRadio, dh.id};
|
||||
if (dh.kind == DeckHitKind::Knob && dh.inner &&
|
||||
curveParamFor(static_cast<ParamControl>(dh.id)) != ParamControl::kCount) {
|
||||
// Indexed by the OUTER cell id so the paint side can find the cell it belongs to.
|
||||
|
||||
@@ -26,16 +26,13 @@ enum class HoverKind {
|
||||
kCard, // a capture card (index = visible_ index)
|
||||
kBrowseConfirm, // the Browse modal "Load" confirm button
|
||||
kBrowseCancel, // the Browse modal "Cancel" button
|
||||
kChanMono, // the mono channel-mode segment
|
||||
kChanStereo, // the stereo channel-mode segment
|
||||
kLoopOff, // the loop enable's Off segment
|
||||
kLoopOn, // the loop enable's On segment
|
||||
kChannel, // the channel-mode selector button
|
||||
kLoop, // the loop enable button
|
||||
kWaveMark, // a waveform overlay mark (index = WaveMark ordinal); promotes its label
|
||||
kPreview, // the preview-trigger button
|
||||
kBake, // the resample-bake trigger
|
||||
kControl, // a knob-deck element (index = control id)
|
||||
kInnerDial, // a knob cell's inner curve dial (index = the OUTER control id)
|
||||
kEnvRadio, // an envelope deck's overlay-select radio (index = radio control id)
|
||||
kCurveNode, // a velocity-curve control point (index = point index)
|
||||
kVelKnob, // the chrome preview-velocity radial knob
|
||||
kHoldKnob, // the chrome bake-Hold radial knob
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// editor_paint_chrome.cpp — the CHROME band's painter: the toolbar row (product title +
|
||||
// live readout, then the control run — preview, preview-velocity knob, Loop Off|On,
|
||||
// Mono|Stereo, Browse) over the strip row, which the piano strip has to itself. Windows-only;
|
||||
// live readout, then the control run — preview, preview-velocity knob, Loop, the channel mode,
|
||||
// Browse) over the strip row, which the piano strip has to itself. Windows-only;
|
||||
// all rects come from the pure sample_chrome interior and the pure keyboard_strip geometry.
|
||||
|
||||
#include "shell/instrument/reasampler_editor.h"
|
||||
@@ -212,44 +212,51 @@ void ReaSamplerEditor::paintChrome(LICE_IBitmap* bmp, const FaceLayout& fl, bool
|
||||
}
|
||||
}
|
||||
|
||||
// Loop Off | On. A two-segment toggle in the same primitive as Mono|Stereo because it is
|
||||
// the same class of control: a playback mode of the loaded capture. Outside Gate both
|
||||
// segments draw Disabled and neither accepts a click — the state is preserved, not cleared,
|
||||
// so the return to Gate restores it.
|
||||
// The two single-button chrome controls, in the deck's own grammar: Loop is an ENABLE
|
||||
// (Primary on, dim gray off), the channel button is a MODE SELECTOR whose label reads the
|
||||
// current mode and so has no off state. Outside Gate the loop button draws Disabled and
|
||||
// accepts no click — a third state, not an off: the enable's own state is preserved, not
|
||||
// cleared, so the return to Gate restores it.
|
||||
{
|
||||
const bool live = loopControlsLive();
|
||||
const bool on = marks.hasLoop;
|
||||
const auto segState = [&](bool active, HoverKind hk) {
|
||||
if (!live) return InteractionState::Disabled;
|
||||
if (active) return InteractionState::Active;
|
||||
return isHovered(hk, -1) ? InteractionState::Hover : InteractionState::Rest;
|
||||
const auto drawChromeToggle = [&](const Rect& r, const char* label, HoverKind hk,
|
||||
bool active, bool disabled) {
|
||||
const bool hov = !disabled && isHovered(hk, -1);
|
||||
const InteractionState st =
|
||||
disabled ? InteractionState::Disabled
|
||||
: (active ? InteractionState::Active
|
||||
: (hov ? InteractionState::Hover : InteractionState::Rest));
|
||||
fillSurface(bmp, toKitBox(r), Role::BgCell, st);
|
||||
// Disabled's wash over bg/cell is barely a shade off Rest's — the third state needs
|
||||
// its own mark, not just a slightly-quieter fill, so it draws the one outline this
|
||||
// control ever gets. See PLAN.md's Off-vs-Disabled ruling: Off is live and
|
||||
// clickable, Disabled is not, and the two must not read as the same thing.
|
||||
// Outlined in text/dim rather than line/hairline: hairline over this fill composites
|
||||
// to ~1.36:1, under the 3:1 state-indicator floor; text/dim clears it at ~5:1.
|
||||
if (disabled) {
|
||||
LICE_DrawRect(bmp, r.x, r.y, r.width - 1, r.height - 1,
|
||||
toLice(roleColor(Role::TextDim)), 1.0f, 0);
|
||||
}
|
||||
kitTextCentered(bmp, r, label, kToolbarFont,
|
||||
active && !disabled ? Role::BgBase
|
||||
: (hov ? Role::TextPrimary : Role::TextDim));
|
||||
};
|
||||
const InteractionState offState = segState(!on, HoverKind::kLoopOff);
|
||||
const InteractionState onState = segState(on, HoverKind::kLoopOn);
|
||||
fillSurface(bmp, toKitBox(cr.loopOff), Role::BgCell, offState);
|
||||
fillSurface(bmp, toKitBox(cr.loopOn), Role::BgCell, onState);
|
||||
const Role dim = live ? Role::TextPrimary : Role::TextDim;
|
||||
kitTextCentered(bmp, cr.loopOff, "Loop Off", kToolbarFont,
|
||||
(live && !on) ? Role::BgBase : dim);
|
||||
kitTextCentered(bmp, cr.loopOn, "Loop On", kToolbarFont,
|
||||
(live && on) ? Role::BgBase : dim);
|
||||
}
|
||||
drawChromeToggle(cr.loop, "Loop", HoverKind::kLoop, marks.hasLoop,
|
||||
!loopControlsLive());
|
||||
|
||||
// Mono | Stereo output-mode toggle.
|
||||
{
|
||||
// The channel button is a MODE selector like the deck's Stage|Spline buttons — always
|
||||
// "on", so InteractionState::Active would otherwise swallow every hover. Filled by
|
||||
// accent/primary rather than bg/cell so Hover's mix-toward-accent/hot actually moves the
|
||||
// surface instead of nudging bg/cell by a few percent (roleColorState's Active case
|
||||
// always answers accent/primary regardless of the role passed in, so Rest is unreachable
|
||||
// here and this is purely which color Hover mixes FROM).
|
||||
const auto drawModeChromeToggle = [&](const Rect& r, const char* label, HoverKind hk) {
|
||||
const bool hov = isHovered(hk, -1);
|
||||
const InteractionState st = hov ? InteractionState::Hover : InteractionState::Active;
|
||||
fillSurface(bmp, toKitBox(r), Role::AccentPrimary, st);
|
||||
kitTextCentered(bmp, r, label, kToolbarFont, Role::BgBase);
|
||||
};
|
||||
const bool isStereo = (channelMode_ == ChannelMode::Stereo);
|
||||
const InteractionState monoState = !isStereo ? InteractionState::Active
|
||||
: (isHovered(HoverKind::kChanMono, -1) ? InteractionState::Hover
|
||||
: InteractionState::Rest);
|
||||
const InteractionState stereoState = isStereo ? InteractionState::Active
|
||||
: (isHovered(HoverKind::kChanStereo, -1) ? InteractionState::Hover
|
||||
: InteractionState::Rest);
|
||||
fillSurface(bmp, toKitBox(cr.chanMono), Role::BgCell, monoState);
|
||||
fillSurface(bmp, toKitBox(cr.chanStereo), Role::BgCell, stereoState);
|
||||
kitTextCentered(bmp, cr.chanMono, "Mono", kToolbarFont,
|
||||
!isStereo ? Role::BgBase : Role::TextPrimary);
|
||||
kitTextCentered(bmp, cr.chanStereo, "Stereo", kToolbarFont,
|
||||
isStereo ? Role::BgBase : Role::TextPrimary);
|
||||
drawModeChromeToggle(cr.channel, isStereo ? "Stereo" : "Mono", HoverKind::kChannel);
|
||||
}
|
||||
|
||||
// The strip row: the full 128-key piano with the root lit. The loaded capture responds
|
||||
|
||||
@@ -162,6 +162,43 @@ void ReaSamplerEditor::paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl) {
|
||||
disabled ? Role::TextDim
|
||||
: (seg1Active ? Role::BgBase : Role::TextPrimary));
|
||||
};
|
||||
// The single-button ENABLE form. Reads Primary on / dim gray off; a control the ENABLE
|
||||
// gates draws Disabled, a third state and not a synonym for off (off is live and
|
||||
// clickable). No deck ENABLE currently drives `disabled` true through this path — the
|
||||
// deck's one Disabled control (FILTER's Band|Notch law when the filter itself is off)
|
||||
// stays segmented and draws through `drawToggle` above — but the parameter stays for
|
||||
// parity with that segmented form and because a future ENABLE could plausibly gate on
|
||||
// something else the way Loop (chrome) already does outside this deck.
|
||||
const auto drawButtonToggle = [&](const DeckToggleLayout& t, const char* label, bool active,
|
||||
bool disabled) {
|
||||
const bool hov = !disabled && isHovered(HoverKind::kControl, t.id);
|
||||
// Hover outranks Active here (the reverse of the old order) so an enabled button still
|
||||
// gets a hover cue — same gap drawModeToggle's comment above explains, just on the
|
||||
// ENABLE form's own base role: mixing toward accent/hot FROM accent/primary (rather
|
||||
// than bg/cell) is what makes the already-lit button visibly brighten on hover.
|
||||
const InteractionState st =
|
||||
disabled ? InteractionState::Disabled
|
||||
: (hov ? InteractionState::Hover
|
||||
: (active ? InteractionState::Active : InteractionState::Rest));
|
||||
fillSurface(bmp, toKitBox(t.seg0), active ? Role::AccentPrimary : Role::BgCell, st);
|
||||
kitTextCentered(bmp, t.seg0, label, Font::Micro,
|
||||
active && !disabled ? Role::BgBase
|
||||
: (hov ? Role::TextPrimary : Role::TextDim));
|
||||
};
|
||||
// The single-button MODE form: the label reads the current mode, so there is no off state
|
||||
// and no dim-gray rest — it is always "on". Filled by accent/primary rather than routed
|
||||
// through drawButtonToggle (which would force InteractionState::Active regardless of hov,
|
||||
// leaving MODE with no hover cue at all): Hover mixes toward accent/hot FROM whatever base
|
||||
// roleColorState is handed, so passing accent/primary here is what lets the already-lit
|
||||
// button visibly brighten on hover instead of a hover mix nobody would notice against
|
||||
// bg/cell's dark base.
|
||||
const auto drawModeToggle = [&](const DeckToggleLayout& t, EnvMode mode) {
|
||||
const char* label = mode == EnvMode::Spline ? "Spline" : "Stage";
|
||||
const bool hov = isHovered(HoverKind::kControl, t.id);
|
||||
const InteractionState st = hov ? InteractionState::Hover : InteractionState::Active;
|
||||
fillSurface(bmp, toKitBox(t.seg0), Role::AccentPrimary, st);
|
||||
kitTextCentered(bmp, t.seg0, label, Font::Micro, Role::BgBase);
|
||||
};
|
||||
const bool anySpline = splineActive(play);
|
||||
|
||||
// The knob's short name label (swapped for the live value during hover/drag — no third
|
||||
@@ -209,10 +246,14 @@ void ReaSamplerEditor::paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl) {
|
||||
};
|
||||
|
||||
for (const DeckGroupLayout& g : dl.groups) {
|
||||
// The fence: a bg/panel box with a hairline border, caption micro-caps left.
|
||||
// The fence: a bg/panel box with a hairline border, caption micro-caps left. An
|
||||
// envelope deck whose overlay is the one on the waveform takes the primary accent
|
||||
// instead — the deck itself is the selection affordance, so the whole box says so.
|
||||
const OverlayEnv groupEnv = overlayEnvForGroup(g.id);
|
||||
const bool focused = groupEnv != OverlayEnv::kNone && groupEnv == overlayEnv_;
|
||||
fillSurface(bmp, toKitBox(g.box), Role::BgPanel, InteractionState::Rest);
|
||||
LICE_DrawRect(bmp, g.box.x, g.box.y, g.box.width - 1, g.box.height - 1,
|
||||
hairline, 1.0f, 0);
|
||||
focused ? toLice(roleColor(Role::AccentPrimary)) : hairline, 1.0f, 0);
|
||||
const char* caption = "";
|
||||
switch (g.id) {
|
||||
case kGroupAmpEnv: caption = "AMP ENVELOPE"; break;
|
||||
@@ -227,8 +268,9 @@ void ReaSamplerEditor::paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl) {
|
||||
}
|
||||
kitText(bmp, g.caption, caption, Font::Micro, Role::TextDim);
|
||||
|
||||
// The gain-reduction lamp. ROUND, where the overlay radios in this same slot are
|
||||
// square, so it reads as a lamp rather than a control.
|
||||
// The gain-reduction lamp — MASTER's own corner slot, the one the env decks' overlay
|
||||
// radios used to share before focus-by-click replaced them. ROUND, not square, so it
|
||||
// reads as a passive readout rather than a control.
|
||||
if (g.captionRadio.id >= 0 && g.captionRadio.passive) {
|
||||
const Rect& rb = g.captionRadio.box;
|
||||
const float r = rb.width / 2.0f - 0.5f;
|
||||
@@ -237,26 +279,6 @@ void ReaSamplerEditor::paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl) {
|
||||
: Role::LineHairline)),
|
||||
1.0f, 0, true);
|
||||
}
|
||||
// The overlay-select radio: filled in the tertiary accent (the colour the overlay
|
||||
// traces in) when this group's envelope is the one on the waveform, hollow otherwise.
|
||||
if (g.captionRadio.id >= 0 && !g.captionRadio.passive) {
|
||||
// overlayEnvForRadio returns kNone for BOTH "not a radio id" and "no selection" —
|
||||
// a non-radio id must never read as lit just because nothing is selected, so the
|
||||
// picked env has to be checked against kNone itself, not just matched by equality.
|
||||
const OverlayEnv picked = overlayEnvForRadio(g.captionRadio.id);
|
||||
const bool on = picked != OverlayEnv::kNone && overlayEnv_ == picked;
|
||||
const bool hov = isHovered(HoverKind::kEnvRadio, g.captionRadio.id);
|
||||
const Rect& rb = g.captionRadio.box;
|
||||
LICE_DrawRect(bmp, rb.x, rb.y, rb.width - 1, rb.height - 1,
|
||||
toLice(roleColor(on || hov ? Role::AccentTertiary
|
||||
: Role::LineHairline)),
|
||||
1.0f, 0);
|
||||
if (on) {
|
||||
LICE_FillRect(bmp, rb.x + 3, rb.y + 3, rb.width - 6, rb.height - 6,
|
||||
toLice(roleColor(Role::AccentTertiary)), 1.0f, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// The compact caption toggles (right-anchored in the caption row, never full-width).
|
||||
for (const DeckToggleLayout* tp : {&g.captionToggle, &g.captionToggle2}) {
|
||||
if (tp->id < 0) continue;
|
||||
@@ -271,13 +293,13 @@ void ReaSamplerEditor::paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl) {
|
||||
play.pitchEngine == PitchEngine::Preserve, false);
|
||||
break;
|
||||
case ParamControl::kPitchEnvEnable:
|
||||
drawToggle(t, "Off", "On", play.pitchEnv.enabled, false);
|
||||
drawButtonToggle(t, "Envelope", play.pitchEnv.enabled, false);
|
||||
break;
|
||||
case ParamControl::kVoiceMode:
|
||||
drawToggle(t, "Poly", "Mono", isMono, false);
|
||||
break;
|
||||
case ParamControl::kFilterEnable:
|
||||
drawToggle(t, "Off", "On", play.filter.enabled, false);
|
||||
drawButtonToggle(t, "Filter", play.filter.enabled, false);
|
||||
break;
|
||||
case ParamControl::kFilterLaw:
|
||||
drawToggle(t, "Band", "Notch",
|
||||
@@ -286,16 +308,18 @@ void ReaSamplerEditor::paintDeck(LICE_IBitmap* bmp, const FaceLayout& fl) {
|
||||
!play.filter.enabled);
|
||||
break;
|
||||
case ParamControl::kLimiterEnable:
|
||||
drawToggle(t, "Off", "On", params_.limiterEnabled, false);
|
||||
drawButtonToggle(t, "Limiter", params_.limiterEnabled, false);
|
||||
break;
|
||||
// A mode SELECTOR: the label is the state, so it is drawn Active either way —
|
||||
// there is nothing here for a dim-gray off to mean.
|
||||
case ParamControl::kAmpEnvMode:
|
||||
drawToggle(t, "Stg", "Spl", play.ampSpline.mode == EnvMode::Spline, false);
|
||||
drawModeToggle(t, play.ampSpline.mode);
|
||||
break;
|
||||
case ParamControl::kPitchEnvMode:
|
||||
drawToggle(t, "Stg", "Spl", play.pitchSpline.mode == EnvMode::Spline, false);
|
||||
drawModeToggle(t, play.pitchSpline.mode);
|
||||
break;
|
||||
case ParamControl::kFilterEnvMode:
|
||||
drawToggle(t, "Stg", "Spl", play.filterSpline.mode == EnvMode::Spline, false);
|
||||
drawModeToggle(t, play.filterSpline.mode);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user