Download Source/Plugin/PluginBridgeEditor.cpp from RAM2118/pluginbridge: direct link, hf CLI and curl.
- Browser
- Download file 7.07 kB
-
https://huggingface.co/RAM2118/pluginbridge/resolve/ddb115bf829f00a019effbe2c307028edd9a0d27/Source/Plugin/PluginBridgeEditor.cpp
- Command line
-
hf download hf://RAM2118/pluginbridge@ddb115bf829f00a019effbe2c307028edd9a0d27/Source/Plugin/PluginBridgeEditor.cpp
-
curl -L -o PluginBridgeEditor.cpp https://huggingface.co/RAM2118/pluginbridge/resolve/ddb115bf829f00a019effbe2c307028edd9a0d27/Source/Plugin/PluginBridgeEditor.cpp
7.07 kB
| 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); | |
| PluginBridgeEditor::PluginBridgeEditor(PluginBridgeProcessor& p) | |
| : AudioProcessorEditor(&p), processorRef(p) | |
| { | |
| setSize(kDefaultWidth, kDefaultHeight); | |
| setResizable(true, true); | |
| // Title | |
| titleLabel.setText("PluginBridge", juce::dontSendNotification); | |
| titleLabel.setFont(juce::FontOptions(14.0f)); | |
| titleLabel.setJustificationType(juce::Justification::centredLeft); | |
| titleLabel.setColour(juce::Label::textColourId, kTextPrimary); | |
| addAndMakeVisible(titleLabel); | |
| // MCP status | |
| mcpStatusLabel.setFont(juce::FontOptions(11.0f)); | |
| mcpStatusLabel.setJustificationType(juce::Justification::centredRight); | |
| addAndMakeVisible(mcpStatusLabel); | |
| // Plugin name | |
| pluginStatusLabel.setFont(juce::FontOptions(11.0f)); | |
| pluginStatusLabel.setJustificationType(juce::Justification::centredLeft); | |
| pluginStatusLabel.setColour(juce::Label::textColourId, kTextSecondary); | |
| addAndMakeVisible(pluginStatusLabel); | |
| // Select button | |
| selectButton.onClick = [this]() { showPluginMenu(); }; | |
| selectButton.setColour(juce::TextButton::buttonColourId, kAccent); | |
| selectButton.setColour(juce::TextButton::textColourOffId, kTextPrimary); | |
| addAndMakeVisible(selectButton); | |
| // Callbacks from processor | |
| processorRef.onPluginLoaded = [this]() | |
| { | |
| juce::MessageManager::callAsync([this]() { embedPluginEditor(); }); | |
| }; | |
| processorRef.onPluginUnloaded = [this]() | |
| { | |
| juce::MessageManager::callAsync([this]() { removePluginEditor(); }); | |
| }; | |
| processorRef.onLoadFailed = [this](const juce::String& error) | |
| { | |
| juce::MessageManager::callAsync([this, error]() | |
| { | |
| pluginStatusLabel.setText(error, juce::dontSendNotification); | |
| pluginStatusLabel.setColour(juce::Label::textColourId, kRed); | |
| }); | |
| }; | |
| // If plugin already loaded (e.g. restoring state), embed it | |
| if (processorRef.isPluginLoaded()) | |
| embedPluginEditor(); | |
| startTimerHz(4); | |
| } | |
| PluginBridgeEditor::~PluginBridgeEditor() | |
| { | |
| stopTimer(); | |
| processorRef.onPluginLoaded = nullptr; | |
| processorRef.onPluginUnloaded = nullptr; | |
| processorRef.onLoadFailed = nullptr; | |
| removePluginEditor(); | |
| } | |
| void PluginBridgeEditor::paint(juce::Graphics& g) | |
| { | |
| g.fillAll(kBgDark); | |
| // Top bar background | |
| auto topBar = getLocalBounds().removeFromTop(kTopBarHeight); | |
| g.setColour(kBgPanel); | |
| g.fillRect(topBar); | |
| // Separator line | |
| g.setColour(kAccent.withAlpha(0.3f)); | |
| g.drawHorizontalLine(kTopBarHeight - 1, 0.0f, (float)getWidth()); | |
| } | |
| void PluginBridgeEditor::resized() | |
| { | |
| auto area = getLocalBounds(); | |
| auto topBar = area.removeFromTop(kTopBarHeight).reduced(6, 4); | |
| // Top bar layout | |
| titleLabel.setBounds(topBar.removeFromLeft(90)); | |
| selectButton.setBounds(topBar.removeFromLeft(110).reduced(0, 2)); | |
| topBar.removeFromLeft(6); | |
| pluginStatusLabel.setBounds(topBar.removeFromLeft(topBar.getWidth() - 70)); | |
| mcpStatusLabel.setBounds(topBar); | |
| // Plugin editor fills the rest | |
| if (hostedEditor) | |
| hostedEditor->setBounds(area); | |
| } | |
| 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); | |
| } | |
| if (processorRef.isPluginLoaded()) | |
| { | |
| selectButton.setButtonText(processorRef.getLoadedPluginName()); | |
| pluginStatusLabel.setText(juce::String(processorRef.getLoadedPluginParamCount()) + " params", juce::dontSendNotification); | |
| pluginStatusLabel.setColour(juce::Label::textColourId, kGreen); | |
| } | |
| else if (pluginStatusLabel.getColour(juce::Label::textColourId) != kRed) | |
| { | |
| selectButton.setButtonText("Select Plugin"); | |
| pluginStatusLabel.setText("No plugin loaded", juce::dontSendNotification); | |
| pluginStatusLabel.setColour(juce::Label::textColourId, kTextSecondary); | |
| } | |
| } | |
| void PluginBridgeEditor::embedPluginEditor() | |
| { | |
| removePluginEditor(); | |
| auto* editor = processorRef.createHostedPluginEditor(); | |
| if (!editor) return; | |
| hostedEditor.reset(editor); | |
| addAndMakeVisible(hostedEditor.get()); | |
| // Resize PluginBridge editor to fit the hosted plugin's GUI + top bar | |
| auto editorWidth = std::max(hostedEditor->getWidth(), kDefaultWidth); | |
| auto editorHeight = hostedEditor->getHeight() + kTopBarHeight; | |
| setSize(editorWidth, editorHeight); | |
| } | |
| void PluginBridgeEditor::removePluginEditor() | |
| { | |
| if (hostedEditor) | |
| { | |
| removeChildComponent(hostedEditor.get()); | |
| hostedEditor.reset(); | |
| setSize(kDefaultWidth, kDefaultHeight); | |
| } | |
| } | |
| 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(); | |
| auto path = file.getFullPathName(); | |
| bool blocked = processorRef.isBlocked(path); | |
| if (file.getFileExtension() == ".vst3") | |
| vst3Menu.addItem(itemId, blocked ? name + " (not compatible)" : name, !blocked); | |
| else | |
| auMenu.addItem(itemId, blocked ? name + " (not compatible)" : name, !blocked); | |
| itemId++; | |
| } | |
| if (vst3Menu.getNumItems() > 0) | |
| menu.addSubMenu("VST3", vst3Menu); | |
| if (auMenu.getNumItems() > 0) | |
| menu.addSubMenu("Audio Units", auMenu); | |
| menu.addSeparator(); | |
| menu.addItem(9999, "Clear blocklist"); | |
| menu.showMenuAsync(juce::PopupMenu::Options().withTargetComponent(&selectButton), | |
| [this, files](int result) | |
| { | |
| if (result == 9999) | |
| { | |
| processorRef.getBlocklist().clear(); | |
| return; | |
| } | |
| if (result > 0 && result <= files.size()) | |
| { | |
| pluginStatusLabel.setText("Loading...", juce::dontSendNotification); | |
| pluginStatusLabel.setColour(juce::Label::textColourId, kTextSecondary); | |
| processorRef.loadPlugin(files[result - 1].getFullPathName()); | |
| } | |
| }); | |
| } | |