#pragma once #include #include "PluginProcessor.h" #include "ScopeEngine.h" #include #include /** * Hosts the React webview UI and bridges the reused Prism spectrum DSP to it. * * Drains stereo audio buffered by the processor, feeds the selected scope engine, * and emits frame payloads to the webview after the UI reports readiness. macOS * uses display vblank; Windows/Linux use steady message-thread timers to avoid * fragile host/browser embedding behavior. */ class PrismSpectrumEditor : public juce::AudioProcessorEditor #if JUCE_WINDOWS || JUCE_LINUX , private juce::Timer #endif { public: explicit PrismSpectrumEditor(PrismSpectrumProcessor&); ~PrismSpectrumEditor() override; void resized() override; #if JUCE_WINDOWS || JUCE_LINUX void timerCallback() override; #endif // Called by the webview event listeners (message thread). void onPrismConfig(juce::var payload); void onPrismReady(); // Scope-specific native config (e.g. the spectrogram's canvas-derived rowCount). void onScopeNativeConfig(juce::var payload); // The UI's settings panel opened/closed along the bottom. Grow/shrink the editor // height by exactly the panel height so the scope area is unchanged (the window // accommodates the panel, like the app). 0 = closed. void onSettingsPanel(juce::var payload); // Push the processor's saved settings to the UI (used on ready + on host // state restore, to cover either ordering). void pushRestoreSettings(); // Read the user's Prism app theme + active profile from disk and send them // to the UI as defaults (per-instance overrides still win). void sendAppDefaults(); private: void loadUi(); bool createWebView(); void loadEmbeddedWebView(); void showWebViewFallback(); void startFrameDriver(); void renderFrame(); #if JUCE_LINUX void loadNativeSmoke(); void loadWebSmoke(); void loadFloatingWebView(); void showLinuxDiagnosticPlaceholder(const juce::String& text); #endif PrismSpectrumProcessor& processorRef; std::unique_ptr engine; std::vector drainLeft, drainRight; double lastSampleRate = 0.0; // Height (px) the editor is currently grown by for the open settings panel. int settingsPanelHeight = 0; bool webViewReady = false; bool frameDriverStarted = false; // One-time attempt to lift WKWebView's private 60fps cap (macOS). bool frameRateUncapped = false; int uncapAttempts = 0; std::unique_ptr webView; #if JUCE_LINUX std::unique_ptr floatingWebViewWindow; #endif juce::Label webViewFallback; // Declared last so it is destroyed first; no vblank callback can fire into a // partially-destroyed editor. Windows/Linux use juce::Timer instead. #if ! JUCE_WINDOWS && ! JUCE_LINUX juce::VBlankAttachment vblank; #endif JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrismSpectrumEditor) };