#include "PluginBridgeEditor.h" static const juce::Colour kBgDark (0xff0d0d1a); static const juce::Colour kBgPanel (0xff1a1a2e); static const juce::Colour kAccent (0xff6c63ff); static const juce::Colour kTextPrimary (0xffe8e8f0); static const juce::Colour kTextSecondary(0xff8888aa); static const juce::Colour kGreen (0xff4cef50); static const juce::Colour kRed (0xffef5350); static const juce::Colour kYellow (0xffffeb3b); static const juce::Colour kBtnBg (0xff2a2a44); PluginBridgeEditor::PluginBridgeEditor(PluginBridgeProcessor& p) : AudioProcessorEditor(&p), processorRef(p) { setSize(340, 200); titleLabel.setText("PluginBridge", juce::dontSendNotification); titleLabel.setFont(juce::FontOptions(18.0f)); titleLabel.setJustificationType(juce::Justification::centredLeft); titleLabel.setColour(juce::Label::textColourId, kTextPrimary); addAndMakeVisible(titleLabel); mcpStatusLabel.setFont(juce::FontOptions(11.0f)); mcpStatusLabel.setJustificationType(juce::Justification::centredRight); addAndMakeVisible(mcpStatusLabel); pluginStatusLabel.setFont(juce::FontOptions(13.0f)); pluginStatusLabel.setJustificationType(juce::Justification::centredLeft); pluginStatusLabel.setColour(juce::Label::textColourId, kTextSecondary); addAndMakeVisible(pluginStatusLabel); connectionLabel.setFont(juce::FontOptions(10.0f)); connectionLabel.setJustificationType(juce::Justification::centredLeft); connectionLabel.setColour(juce::Label::textColourId, kTextSecondary); addAndMakeVisible(connectionLabel); selectButton.onClick = [this]() { showPluginMenu(); }; selectButton.setColour(juce::TextButton::buttonColourId, kAccent); selectButton.setColour(juce::TextButton::textColourOffId, kTextPrimary); addAndMakeVisible(selectButton); guiButton.onClick = [this]() { toggleGui(); }; guiButton.setColour(juce::TextButton::buttonColourId, kBtnBg); guiButton.setColour(juce::TextButton::textColourOffId, kTextPrimary); addAndMakeVisible(guiButton); reloadButton.onClick = [this]() { auto path = processorRef.getLastPluginPath(); if (path.isNotEmpty()) processorRef.loadPlugin(path); }; reloadButton.setColour(juce::TextButton::buttonColourId, kRed); reloadButton.setColour(juce::TextButton::textColourOffId, kTextPrimary); reloadButton.setVisible(false); addAndMakeVisible(reloadButton); startTimerHz(8); } PluginBridgeEditor::~PluginBridgeEditor() { stopTimer(); } void PluginBridgeEditor::paint(juce::Graphics& g) { g.fillAll(kBgDark); auto panelArea = getLocalBounds().reduced(8); g.setColour(kBgPanel); g.fillRoundedRectangle(panelArea.toFloat(), 8.0f); g.setColour(kAccent.withAlpha(0.3f)); g.drawRoundedRectangle(panelArea.toFloat(), 8.0f, 1.0f); } void PluginBridgeEditor::resized() { auto area = getLocalBounds().reduced(16); auto topRow = area.removeFromTop(24); titleLabel.setBounds(topRow.removeFromLeft(160)); mcpStatusLabel.setBounds(topRow); area.removeFromTop(8); pluginStatusLabel.setBounds(area.removeFromTop(20)); connectionLabel.setBounds(area.removeFromTop(16)); area.removeFromTop(12); auto buttonRow = area.removeFromTop(32); selectButton.setBounds(buttonRow.removeFromLeft(130)); buttonRow.removeFromLeft(6); guiButton.setBounds(buttonRow.removeFromLeft(90)); buttonRow.removeFromLeft(6); reloadButton.setBounds(buttonRow); } void PluginBridgeEditor::timerCallback() { if (processorRef.isMcpServerRunning()) { mcpStatusLabel.setText("MCP:" + juce::String(processorRef.getMcpServerPort()), juce::dontSendNotification); mcpStatusLabel.setColour(juce::Label::textColourId, kGreen); } else { mcpStatusLabel.setText("MCP: off", juce::dontSendNotification); mcpStatusLabel.setColour(juce::Label::textColourId, kRed); } auto connState = processorRef.getConnectionState(); if (processorRef.isPluginLoaded()) { pluginStatusLabel.setText(processorRef.getLoadedPluginName() + " • " + juce::String(processorRef.getLoadedPluginParamCount()) + " params", juce::dontSendNotification); pluginStatusLabel.setColour(juce::Label::textColourId, kTextPrimary); selectButton.setButtonText(processorRef.getLoadedPluginName()); guiButton.setVisible(true); reloadButton.setVisible(false); } else { pluginStatusLabel.setText("No plugin loaded", juce::dontSendNotification); pluginStatusLabel.setColour(juce::Label::textColourId, kTextSecondary); selectButton.setButtonText("Select Plugin"); guiButton.setVisible(false); } switch (connState) { case HelperConnection::State::Disconnected: connectionLabel.setText("", juce::dontSendNotification); break; case HelperConnection::State::Starting: connectionLabel.setText("Starting helper...", juce::dontSendNotification); connectionLabel.setColour(juce::Label::textColourId, kYellow); break; case HelperConnection::State::Connected: connectionLabel.setText("Helper connected", juce::dontSendNotification); connectionLabel.setColour(juce::Label::textColourId, kGreen); break; case HelperConnection::State::Crashed: connectionLabel.setText("Plugin crashed - click Reload", juce::dontSendNotification); connectionLabel.setColour(juce::Label::textColourId, kRed); reloadButton.setVisible(true); guiButton.setVisible(false); guiOpen = false; break; } } void PluginBridgeEditor::toggleGui() { if (guiOpen) { processorRef.hidePluginGui(); guiButton.setButtonText("Open GUI"); guiButton.setColour(juce::TextButton::buttonColourId, kBtnBg); guiOpen = false; } else { processorRef.showPluginGui(); guiButton.setButtonText("Close GUI"); guiButton.setColour(juce::TextButton::buttonColourId, kRed.withAlpha(0.7f)); guiOpen = true; } } void PluginBridgeEditor::showPluginMenu() { auto files = processorRef.getInstalledPluginFiles(); juce::PopupMenu menu; if (files.isEmpty()) { menu.addItem(-1, "(No plugins found)", false); menu.showMenuAsync(juce::PopupMenu::Options().withTargetComponent(&selectButton), [](int) {}); return; } juce::PopupMenu vst3Menu; juce::PopupMenu auMenu; int itemId = 1; for (int i = 0; i < files.size(); ++i) { const auto& file = files[i]; auto name = file.getFileNameWithoutExtension(); if (file.getFileExtension() == ".vst3") vst3Menu.addItem(itemId, name); else auMenu.addItem(itemId, name); itemId++; } if (vst3Menu.getNumItems() > 0) menu.addSubMenu("VST3", vst3Menu); if (auMenu.getNumItems() > 0) menu.addSubMenu("Audio Units", auMenu); menu.showMenuAsync(juce::PopupMenu::Options().withTargetComponent(&selectButton), [this, files](int result) { if (result > 0 && result <= files.size()) { guiOpen = false; guiButton.setButtonText("Open GUI"); guiButton.setColour(juce::TextButton::buttonColourId, juce::Colour(0xff2a2a44)); processorRef.loadPlugin(files[result - 1].getFullPathName()); } }); }