kopia lustrzana https://github.com/jameshball/osci-render
Fix UI bugs with spout/syphon
rodzic
67650b682e
commit
93d82fa4da
|
@ -101,6 +101,8 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
|
|||
}
|
||||
|
||||
OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {
|
||||
audioProcessor.syphonInputActive = false;
|
||||
|
||||
// Clear the file removal callback
|
||||
audioProcessor.setFileRemovedCallback(nullptr);
|
||||
|
||||
|
@ -516,7 +518,6 @@ void OscirenderAudioProcessorEditor::openSyphonInputDialog() {
|
|||
sharedTextureManager,
|
||||
[this](const juce::String& server, const juce::String& app) { connectSyphonInput(server, app); },
|
||||
[this]() { disconnectSyphonInput(); },
|
||||
syphonFrameGrabber && syphonFrameGrabber->isActive(),
|
||||
getSyphonSourceName());
|
||||
}
|
||||
juce::DialogWindow::LaunchOptions options;
|
||||
|
@ -535,6 +536,8 @@ void OscirenderAudioProcessorEditor::connectSyphonInput(const juce::String& serv
|
|||
if (!syphonFrameGrabber) {
|
||||
syphonFrameGrabber = std::make_unique<SyphonFrameGrabber>(sharedTextureManager, server, app, audioProcessor.syphonImageParser);
|
||||
audioProcessor.syphonInputActive = true;
|
||||
model.resetMenuItems();
|
||||
model.menuItemsChanged();
|
||||
{
|
||||
juce::MessageManagerLock lock;
|
||||
audioProcessor.fileChangeBroadcaster.sendChangeMessage();
|
||||
|
@ -549,6 +552,8 @@ void OscirenderAudioProcessorEditor::disconnectSyphonInput() {
|
|||
}
|
||||
audioProcessor.syphonInputActive = false;
|
||||
syphonFrameGrabber.reset();
|
||||
model.resetMenuItems();
|
||||
model.menuItemsChanged();
|
||||
{
|
||||
juce::MessageManagerLock lock;
|
||||
audioProcessor.fileChangeBroadcaster.sendChangeMessage();
|
||||
|
|
|
@ -21,7 +21,7 @@ juce::StringArray MainMenuBarModel::getMenuBarNames() {
|
|||
|
||||
juce::PopupMenu MainMenuBarModel::getMenuForIndex(int topLevelMenuIndex, const juce::String& menuName) {
|
||||
juce::PopupMenu menu;
|
||||
|
||||
|
||||
if (customMenuLogic) {
|
||||
customMenuLogic(menu, topLevelMenuIndex);
|
||||
}
|
||||
|
@ -41,3 +41,8 @@ void MainMenuBarModel::menuItemSelected(int menuItemID, int topLevelMenuIndex) {
|
|||
}
|
||||
|
||||
void MainMenuBarModel::menuBarActivated(bool isActive) {}
|
||||
|
||||
void MainMenuBarModel::resetMenuItems() {
|
||||
topLevelMenuNames.clear();
|
||||
menuItems.clear();
|
||||
}
|
|
@ -8,7 +8,8 @@ public:
|
|||
|
||||
void addTopLevelMenu(const juce::String& name);
|
||||
void addMenuItem(int topLevelMenuIndex, const juce::String& name, std::function<void()> action);
|
||||
|
||||
void resetMenuItems();
|
||||
|
||||
std::function<void(juce::PopupMenu&, int)> customMenuLogic;
|
||||
std::function<bool(int, int)> customMenuSelectedLogic;
|
||||
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
#include "OsciMainMenuBarModel.h"
|
||||
|
||||
#include "../PluginEditor.h"
|
||||
#include "../PluginProcessor.h"
|
||||
|
||||
OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& e) : audioProcessor(p), editor(e) {
|
||||
resetMenuItems();
|
||||
}
|
||||
|
||||
void OsciMainMenuBarModel::resetMenuItems() {
|
||||
MainMenuBarModel::resetMenuItems();
|
||||
|
||||
addTopLevelMenu("File");
|
||||
addTopLevelMenu("About");
|
||||
addTopLevelMenu("Recording");
|
||||
addTopLevelMenu("Video");
|
||||
if (editor.processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone) {
|
||||
addTopLevelMenu("Audio");
|
||||
}
|
||||
|
@ -20,19 +27,22 @@ OsciMainMenuBarModel::OsciMainMenuBarModel(OscirenderAudioProcessor& p, Oscirend
|
|||
addMenuItem(1, "About osci-render", [this] {
|
||||
juce::DialogWindow::LaunchOptions options;
|
||||
AboutComponent* about = new AboutComponent(BinaryData::logo_png, BinaryData::logo_pngSize,
|
||||
juce::String(ProjectInfo::projectName) + " by " + ProjectInfo::companyName + "\n"
|
||||
juce::String(ProjectInfo::projectName) + " by " + ProjectInfo::companyName +
|
||||
"\n"
|
||||
#if OSCI_PREMIUM
|
||||
"Thank you for purchasing osci-render premium!\n"
|
||||
"Thank you for purchasing osci-render premium!\n"
|
||||
#else
|
||||
"Free version\n"
|
||||
#endif
|
||||
"Version " + ProjectInfo::versionString + "\n\n"
|
||||
"A huge thank you to:\n"
|
||||
"DJ_Level_3, for contributing several features to osci-render\n"
|
||||
"BUS ERROR Collective, for providing the source code for the Hilligoss encoder\n"
|
||||
"Jean Perbet (@jeanprbt) for the osci-render macOS icon\n"
|
||||
"All the community, for suggesting features and reporting issues!",
|
||||
std::any_cast<int>(audioProcessor.getProperty("objectServerPort")));
|
||||
"Version " +
|
||||
ProjectInfo::versionString +
|
||||
"\n\n"
|
||||
"A huge thank you to:\n"
|
||||
"DJ_Level_3, for contributing several features to osci-render\n"
|
||||
"BUS ERROR Collective, for providing the source code for the Hilligoss encoder\n"
|
||||
"Jean Perbet (@jeanprbt) for the osci-render macOS icon\n"
|
||||
"All the community, for suggesting features and reporting issues!",
|
||||
std::any_cast<int>(audioProcessor.getProperty("objectServerPort")));
|
||||
options.content.setOwned(about);
|
||||
options.content->setSize(500, 270);
|
||||
options.dialogTitle = "About";
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#include "AboutComponent.h"
|
||||
#include "MainMenuBarModel.h"
|
||||
|
||||
|
||||
class OscirenderAudioProcessorEditor;
|
||||
class OscirenderAudioProcessor;
|
||||
class OsciMainMenuBarModel : public MainMenuBarModel {
|
||||
|
@ -11,6 +11,7 @@ public:
|
|||
OsciMainMenuBarModel(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor);
|
||||
void openSyphonInputDialog();
|
||||
void disconnectSyphonInput();
|
||||
void resetMenuItems();
|
||||
|
||||
private:
|
||||
OscirenderAudioProcessor& audioProcessor;
|
||||
|
|
|
@ -1,34 +1,35 @@
|
|||
#include "SosciMainMenuBarModel.h"
|
||||
|
||||
#include "../SosciPluginEditor.h"
|
||||
#include "../SosciPluginProcessor.h"
|
||||
|
||||
SosciMainMenuBarModel::SosciMainMenuBarModel(SosciPluginEditor& e, SosciAudioProcessor& p) : editor(e), processor(p) {
|
||||
addTopLevelMenu("File");
|
||||
addTopLevelMenu("About");
|
||||
addTopLevelMenu("Recording");
|
||||
addTopLevelMenu("Video");
|
||||
addTopLevelMenu("Audio");
|
||||
|
||||
std::vector<std::tuple<juce::String, const void *, int>> examples = {
|
||||
|
||||
std::vector<std::tuple<juce::String, const void*, int>> examples = {
|
||||
{"default.sosci", BinaryData::default_sosci, BinaryData::default_sosciSize},
|
||||
{"clean.sosci", BinaryData::clean_sosci, BinaryData::clean_sosciSize},
|
||||
{"vector_display.sosci", BinaryData::vector_display_sosci, BinaryData::vector_display_sosciSize},
|
||||
{"real_oscilloscope.sosci", BinaryData::real_oscilloscope_sosci, BinaryData::real_oscilloscope_sosciSize},
|
||||
{"rainbow.sosci", BinaryData::rainbow_sosci, BinaryData::rainbow_sosciSize},
|
||||
};
|
||||
|
||||
|
||||
// This is a hack - ideally I would improve the MainMenuBarModel class to allow for submenus
|
||||
customMenuLogic = [this, examples](juce::PopupMenu& menu, int topLevelMenuIndex) {
|
||||
if (topLevelMenuIndex == 0) {
|
||||
juce::PopupMenu submenu;
|
||||
|
||||
|
||||
for (int i = 0; i < examples.size(); i++) {
|
||||
submenu.addItem(SUBMENU_ID + i, std::get<0>(examples[i]));
|
||||
}
|
||||
|
||||
|
||||
menu.addSubMenu("Examples", submenu);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
customMenuSelectedLogic = [this, examples](int menuItemID, int topLevelMenuIndex) {
|
||||
if (topLevelMenuIndex == 0) {
|
||||
if (menuItemID >= SUBMENU_ID) {
|
||||
|
@ -61,19 +62,21 @@ SosciMainMenuBarModel::SosciMainMenuBarModel(SosciPluginEditor& e, SosciAudioPro
|
|||
addMenuItem(1, "About sosci", [&]() {
|
||||
juce::DialogWindow::LaunchOptions options;
|
||||
AboutComponent* about = new AboutComponent(BinaryData::sosci_logo_png, BinaryData::sosci_logo_pngSize,
|
||||
juce::String(ProjectInfo::projectName) + " by " + ProjectInfo::companyName + "\n"
|
||||
juce::String(ProjectInfo::projectName) + " by " + ProjectInfo::companyName +
|
||||
"\n"
|
||||
#if OSCI_PREMIUM
|
||||
"Thank you for purchasing sosci!\n"
|
||||
"Thank you for purchasing sosci!\n"
|
||||
#else
|
||||
"Free version\n"
|
||||
#endif
|
||||
"Version " + ProjectInfo::versionString + "\n\n"
|
||||
"A huge thank you to:\n"
|
||||
"Neil Thapen, for allowing me to adapt the brilliant dood.al/oscilloscope\n"
|
||||
"Kevin Kripper, for guiding much of the features and development of sosci\n"
|
||||
"DJ_Level_3, for testing throughout and helping add features\n"
|
||||
"All the community, for suggesting features and reporting issues!"
|
||||
);
|
||||
"Version " +
|
||||
ProjectInfo::versionString +
|
||||
"\n\n"
|
||||
"A huge thank you to:\n"
|
||||
"Neil Thapen, for allowing me to adapt the brilliant dood.al/oscilloscope\n"
|
||||
"Kevin Kripper, for guiding much of the features and development of sosci\n"
|
||||
"DJ_Level_3, for testing throughout and helping add features\n"
|
||||
"All the community, for suggesting features and reporting issues!");
|
||||
options.content.setOwned(about);
|
||||
options.content->setSize(500, 270);
|
||||
options.dialogTitle = "About";
|
||||
|
@ -89,7 +92,7 @@ SosciMainMenuBarModel::SosciMainMenuBarModel(SosciPluginEditor& e, SosciAudioPro
|
|||
|
||||
juce::DialogWindow* dw = options.launchAsync();
|
||||
});
|
||||
|
||||
|
||||
addMenuItem(2, "Settings...", [this] {
|
||||
editor.openRecordingSettings();
|
||||
});
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#include "../modules/juce_sharedtexture/SharedTexture.h"
|
||||
|
||||
class SyphonInputSelectorComponent : public juce::Component, private juce::Button::Listener {
|
||||
public:
|
||||
SyphonInputSelectorComponent(SharedTextureManager& manager, std::function<void(const juce::String&, const juce::String&)> onConnect, std::function<void()> onDisconnect, bool isConnected, juce::String currentSource)
|
||||
: sharedTextureManager(manager), onConnectCallback(onConnect), onDisconnectCallback(onDisconnect), connected(isConnected), currentSourceName(currentSource) {
|
||||
SyphonInputSelectorComponent(SharedTextureManager& manager, std::function<void(const juce::String&, const juce::String&)> onConnect, std::function<void()> onDisconnect, juce::String currentSource = "")
|
||||
: sharedTextureManager(manager), onConnectCallback(onConnect), onDisconnectCallback(onDisconnect), currentSourceName(currentSource) {
|
||||
addAndMakeVisible(sourceLabel);
|
||||
sourceLabel.setText("Syphon/Spout Source:", juce::dontSendNotification);
|
||||
|
||||
|
@ -16,12 +17,16 @@ public:
|
|||
};
|
||||
|
||||
addAndMakeVisible(connectButton);
|
||||
connectButton.setButtonText(connected ? "Disconnect" : "Connect");
|
||||
connectButton.setButtonText("Connect");
|
||||
connectButton.addListener(this);
|
||||
|
||||
refreshSources();
|
||||
|
||||
// Auto-select first item if no current source specified
|
||||
if (!currentSourceName.isEmpty()) {
|
||||
sourceDropdown.setText(currentSourceName, juce::dontSendNotification);
|
||||
} else if (sourceDropdown.getNumItems() > 0) {
|
||||
sourceDropdown.setSelectedItemIndex(0, juce::sendNotificationSync);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,21 +41,23 @@ public:
|
|||
auto area = getLocalBounds().reduced(10);
|
||||
sourceLabel.setBounds(area.removeFromTop(24));
|
||||
sourceDropdown.setBounds(area.removeFromTop(28));
|
||||
connectButton.setBounds(area.removeFromTop(28).reduced(0, 8));
|
||||
|
||||
// Make the button not take up the full width
|
||||
auto buttonArea = area.removeFromTop(40).reduced(0, 8);
|
||||
connectButton.setBounds(buttonArea.withSizeKeepingCentre(120, buttonArea.getHeight()));
|
||||
}
|
||||
|
||||
void buttonClicked(juce::Button* b) override {
|
||||
if (connected) {
|
||||
onDisconnectCallback();
|
||||
} else {
|
||||
auto selected = sourceDropdown.getText();
|
||||
if (selected.isNotEmpty()) {
|
||||
// Syphon: "ServerName - AppName"
|
||||
auto parts = juce::StringArray::fromTokens(selected, "-", "");
|
||||
juce::String server = parts[0].trim();
|
||||
juce::String app = parts.size() > 1 ? parts[1].trim() : juce::String();
|
||||
onConnectCallback(server, app);
|
||||
}
|
||||
auto selected = sourceDropdown.getText();
|
||||
if (selected.isNotEmpty()) {
|
||||
// Syphon: "ServerName - AppName"
|
||||
auto parts = juce::StringArray::fromTokens(selected, "-", "");
|
||||
juce::String server = parts[0].trim();
|
||||
juce::String app = parts.size() > 1 ? parts[1].trim() : juce::String();
|
||||
onConnectCallback(server, app);
|
||||
|
||||
if (auto* window = findParentComponentOfClass<juce::DialogWindow>())
|
||||
window->exitModalState(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +65,6 @@ private:
|
|||
SharedTextureManager& sharedTextureManager;
|
||||
std::function<void(const juce::String&, const juce::String&)> onConnectCallback;
|
||||
std::function<void()> onDisconnectCallback;
|
||||
bool connected;
|
||||
juce::String currentSourceName;
|
||||
juce::String selectedSource;
|
||||
|
||||
|
|
|
@ -70,7 +70,11 @@ public:
|
|||
|
||||
juce::String getSourceName() const {
|
||||
if (receiver) {
|
||||
return receiver->sharingName + " (" + receiver->sharingAppName + ")";
|
||||
auto name = receiver->sharingName;
|
||||
if (receiver->sharingAppName.isNotEmpty()) {
|
||||
name += " (" + receiver->sharingAppName + ")";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginWantsMidiIn"
|
||||
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
|
||||
cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"
|
||||
version="2.5.0.1" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
version="2.5.0.2" companyName="James H Ball" companyWebsite="https://osci-render.com"
|
||||
companyEmail="james@ball.sh" defines="NOMINMAX=1 INTERNET_FLAG_NO_AUTO_REDIRECT=0 OSCI_PREMIUM=1 JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 JUCE_MODAL_LOOPS_PERMITTED=1"
|
||||
pluginAUMainType="'aumf'">
|
||||
<MAINGROUP id="j5Ge2T" name="osci-render">
|
||||
|
|
Ładowanie…
Reference in New Issue