#pragma once // draw_kit — the LICE-facing SHELL half of the shared drawing kit: the ONE source of // drawing for the whole system (bank_panel, the VST editor, and the embed strip all fill, // button, row, slider, waveform, and draw TEXT through this same kit, so a control looks // identical everywhere). // // SHELL: touches LICE and SWELL (HFONT). Palette decisions come from the pure `theme` // module (role -> KitColor); layout/hit-test from the pure `component_geometry` / // mode_switch / etc. modules. This file only turns those pure answers into LICE calls. // DAW-verified, not unit-tested. // // Fonts: kitFontsInit() hands each LICE_CachedFont an HFONT with // LICE_FONT_FLAG_OWNS_HFONT, so the cached font frees the HFONT itself on shutdown/ // reassignment. text() no-ops safely before init, so a draw that races construction // never crashes. // // Every function draws into the caller's offscreen LICE_IBitmap; the caller BitBlt's // once. Nothing here draws direct-to-DC. #include "core/ui/component_geometry.h" // KitBox / SliderGeometry #include "core/audio/peaks.h" // Envelope #include "core/ui/theme.h" // Role / InteractionState / KitColor / TextClass // LICE_pixel is a typedef (unsigned int), not forward-declarable, so the full lice.h is // included for the toLice() declaration; lice.h pulls in on Windows, which is // fine since this header is shell-only and never included by a pure module. #ifdef _WIN32 #include #endif #include "lice/lice.h" class LICE_IBitmap; namespace reasampler { // Re-exports: every draw_kit consumer speaks these types at the call boundary. using audio::Envelope; using ui::InteractionState; using ui::KitBox; using ui::KitButtonBox; using ui::KitColor; using ui::ListRowBox; using ui::Role; using ui::SliderGeometry; // The kit's cached fonts. Consumers pass a Font to text() to pick the size/weight; the kit // maps it to the matching LICE_CachedFont. enum class Font { RegionTitle, // 19px BOLD — text that must qualify as WCAG large-scale (3:1 floor) Title, // ~15px semibold — headings (BODY class: 4.5:1 floor) Label, // ~12px regular — labels, body ValueMono, // ~12px tabular/mono — numbers (dB/ms/notes) that must not jitter Micro, // ~10px dim — units, counts, keybinding sub-labels }; // Horizontal text alignment for text(). Vertical is always centered in the rect (the kit's // single-line convention); a caller wanting multi-line composes rows itself. enum class Align { Left, Center, Right }; // The one place a pure KitColor becomes a LICE_pixel. Defined in draw_kit.cpp. LICE_pixel toLice(const KitColor& c); // Creates the cached fonts once; idempotent. Segoe UI for region-title/title/label/micro, // Consolas (tabular) for value-mono. No-op-safe: if font creation fails, text() // draws nothing rather than crashing. void kitFontsInit(); // Frees the owned HFONTs. Idempotent. Call on panel close / extension shutdown. void kitFontsShutdown(); // Draws a single line of AA cached-font text in `color` inside `box`, horizontally // aligned per `align` and vertically centered, clipped with an end-ellipsis. No-op // before kitFontsInit() or on a null bitmap. void text(LICE_IBitmap* bmp, const KitBox& box, const char* str, Font font, const KitColor& color, Align align); // Convenience overload: text in a palette ROLE's color. void text(LICE_IBitmap* bmp, const KitBox& box, const char* str, Font font, Role role, Align align); // The kit's foundational fill: a micro-gradient plus a 1px inner top-highlight/ // bottom-shadow, so elevation reads without a border. `state` transforms the role // color (hover lightens, pressed darkens, disabled desaturates). void fillSurface(LICE_IBitmap* bmp, const KitBox& box, Role role, InteractionState state); // A rounded, gradient-filled button with a centered label. `warn == true` swaps the // surface to the warn role — the only place warn is drawn. Degenerate box is a no-op. void drawButton(LICE_IBitmap* bmp, const KitButtonBox& button, const char* label, InteractionState state, bool warn); // THE ink role drawButton's own label draws in, for `state` (see draw_kit.cpp). A non-text // button mark (e.g. a drawn glyph, not text()) that needs to sit legibly on a drawButton // surface should call this rather than re-deriving the rule. Role buttonLabelRole(InteractionState state); // A horizontal slider: track groove, accent-filled portion up to the handle, and the // handle itself. `geom` is the pure SliderGeometry the caller computed. void drawSlider(LICE_IBitmap* bmp, const SliderGeometry& geom, InteractionState state); // A selectable list row: row surface, an optional leading thumbnail inset // (`thumbWidth`, 0 for none), and a left-aligned label. Focus draws a 1px ring // distinct from the accent selection fill. void drawListRow(LICE_IBitmap* bmp, const ListRowBox& row, const char* label, int thumbWidth, InteractionState state); // Callers pass waveformColumnCount(box) as computeEnvelope's `binCount` — overbinning // costs memory/CPU without changing a rendered pixel, since columnMinMax's exact // partition already makes the draw gap-free at any bins-to-pixels ratio. Kept at 1 (no // oversampling); present so existing call sites `kWaveformOversample * // waveformColumnCount(box)` compile unchanged. inline constexpr int kWaveformOversample = 1; // A waveform drawn as a min/max plot: a midline per channel and one accent vertical // span per pixel column (peaks::columnMinMax — gap-free at any bins-to-pixels ratio). // The ONE waveform shape in the system: dock-panel thumbnail, browser cards, and // editor hero all render through this. `env` is sized to waveformColumnCount(box) // bins (clamped to frame count); an empty env draws just the midline. void drawWaveform(LICE_IBitmap* bmp, const KitBox& box, const Envelope& env); } // namespace reasampler