#pragma once #include #include "Params.h" #include "PluginProcessor.h" class LcdPanel : public juce::Component, public juce::Timer { public: explicit LcdPanel (RMX19AudioProcessor& proc); ~LcdPanel() override; void paint (juce::Graphics&) override; void navUp(); void navDown(); void navLeft(); void navRight(); void navEnter(); void navExit(); void applyPatchOnBrowserMove(); void resetToHome(); static bool firstRunEver(); static juce::File uiStateFile(); static constexpr int gridW = 160; // dot-matrix cell resolution static constexpr int gridH = 64; // internal supersampling: dots are rasterized as anti-aliased circles at 4x, // then box-downsampled 2:1 onto the 2px/dot presentation image. Downsampling // turned off = chunky squares; 2x direct = boxy edges. 4x gives genuine round, // separated dots that survive the (fractional) uiScale upscale. static constexpr int subsample = 4; static constexpr int presentScale = 2; // presentation pixels per logical dot bool keyTrap (const juce::KeyPress&); void mouseDown (const juce::MouseEvent&) override; private: enum class Screen { Splash, Home, Menu, Patches, Patch, Param, System, Info, Category }; RMX19AudioProcessor& proc; Screen screen = Screen::Splash; juce::uint32 splashUntil = 0; int cursor = 0; // menu / param / system / catCursor (when in Category) int patchCursor = 0; // patch cursor (global index) int paramScreenIdx = 0; // which param screen is open int catCursor = 0; // category browser cursor std::vector filteredPatches; // patch indices for current category filter float powerGlow = 0.0f; int lastVoices = -1; juce::uint32 lastMidi = 0; // last-screen persistence: the panel reopens where the user left off juce::uint32 lastUiWrite = 0; bool uiDirty = false; void rememberUiState(); bool restoreUiState(); juce::Image displayImage; // (gridW*subsample)x(gridH*subsample) render target: glass base + content dots juce::Image presentImage; // (gridW*presentScale)x(gridH*presentScale) box-downsampled for the panel juce::Image backdrop; // (gridW*subsample)x(gridH*subsample) static 3D glass base (backlight + sheen + vignette + ghost dots) bool contentDirty = true; static const juce::uint8* glyphFor (int key); static const juce::uint8* glyphM (int key); void rebuild(); void buildBackdrop(); juce::Rectangle lcdImageRect() const; void drawContent (juce::Graphics&); void px (juce::Graphics&, int x, int y, float intensity); void pxText (juce::Graphics&, int x, int y, const juce::String&, float intensity); void pxTextM (juce::Graphics&, int x, int y, const juce::String&, float intensity); void drawFilled (juce::Graphics&, int x, int y, int w, int h, float intensity); void drawHBar (juce::Graphics&, int x, int y, int w, double level, float intensity); void drawSelBar (juce::Graphics&, int y); void drawSplash (juce::Graphics&); void drawHome (juce::Graphics&); void drawMenu (juce::Graphics&); void drawCategory (juce::Graphics&); void drawPatches (juce::Graphics&); void drawPatchList (juce::Graphics&); void drawParamScreen (juce::Graphics&); void drawSystem (juce::Graphics&); void drawInfo (juce::Graphics&); void drawFooter (juce::Graphics&); void selectPatchCyclic (int dir); void stepParam (prm::Id id, int dir, bool coarse); void stepDelaySync (int dir); void setDirty() { contentDirty = true; repaint(); } void enterScreen (Screen s) { screen = s; cursor = 0; uiDirty = true; setDirty(); } // open the category/patch browser, positioned on the loaded patch's category void openPatchBrowser(); void timerCallback() override; int rowCountForScreen() const; void moveCursor (int d); void adjustCurrent (int dir, bool coarse); void openParamScreen (int idx); const char* paramScreenTitle (int idx) const; int paramCountForScreen (int idx) const; int patchCountForCurrentView() const; int patchIndexAtCursor (int viewIdx) const; void buildFilteredPatches (int catIdx); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LcdPanel) };