2
0
Fork 0

6.1.6 framework update and tooltip enhancements

This commit is contained in:
George Reales 2022-03-10 18:45:34 +01:00
parent 29bce70503
commit 5adcad4a7a
7 changed files with 432 additions and 151 deletions

View File

@ -113,7 +113,7 @@ public:
startTimer (500);
}
virtual ~StandalonePluginHolder() override
~StandalonePluginHolder() override
{
stopTimer();
@ -128,13 +128,7 @@ public:
processor->disableNonMainBuses();
processor->setRateAndBufferSizeDetails (44100, 512);
int inChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
: processor->getMainBusNumInputChannels());
int outChannels = (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
: processor->getMainBusNumOutputChannels());
processorHasPotentialFeedbackLoop = (inChannels > 0 && outChannels > 0);
processorHasPotentialFeedbackLoop = (getNumInputChannels() > 0 && getNumOutputChannels() > 0);
}
virtual void deletePlugin()
@ -143,6 +137,24 @@ public:
processor = nullptr;
}
int getNumInputChannels() const
{
if (processor == nullptr)
return 0;
return (channelConfiguration.size() > 0 ? channelConfiguration[0].numIns
: processor->getMainBusNumInputChannels());
}
int getNumOutputChannels() const
{
if (processor == nullptr)
return 0;
return (channelConfiguration.size() > 0 ? channelConfiguration[0].numOuts
: processor->getMainBusNumOutputChannels());
}
static String getFilePatterns (const String& fileSuffix)
{
if (fileSuffix.isEmpty())
@ -179,11 +191,18 @@ public:
/** Pops up a dialog letting the user save the processor's state to a file. */
void askUserToSaveState (const String& fileSuffix = String())
{
#if JUCE_MODAL_LOOPS_PERMITTED
FileChooser fc (TRANS("Save current state"), getLastFile(), getFilePatterns (fileSuffix));
stateFileChooser = std::make_unique<FileChooser> (TRANS("Save current state"),
getLastFile(),
getFilePatterns (fileSuffix));
auto flags = FileBrowserComponent::saveMode
| FileBrowserComponent::canSelectFiles
| FileBrowserComponent::warnAboutOverwriting;
if (fc.browseForFileToSave (true))
stateFileChooser->launchAsync (flags, [this] (const FileChooser& fc)
{
if (fc.getResult() == File{})
return;
setLastFile (fc);
MemoryBlock data;
@ -193,20 +212,23 @@ public:
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
TRANS("Error whilst saving"),
TRANS("Couldn't write to the specified file!"));
}
#else
ignoreUnused (fileSuffix);
#endif
});
}
/** Pops up a dialog letting the user re-load the processor's state from a file. */
void askUserToLoadState (const String& fileSuffix = String())
{
#if JUCE_MODAL_LOOPS_PERMITTED
FileChooser fc (TRANS("Load a saved state"), getLastFile(), getFilePatterns (fileSuffix));
stateFileChooser = std::make_unique<FileChooser> (TRANS("Load a saved state"),
getLastFile(),
getFilePatterns (fileSuffix));
auto flags = FileBrowserComponent::openMode
| FileBrowserComponent::canSelectFiles;
if (fc.browseForFileToOpen())
stateFileChooser->launchAsync (flags, [this] (const FileChooser& fc)
{
if (fc.getResult() == File{})
return;
setLastFile (fc);
MemoryBlock data;
@ -217,10 +239,7 @@ public:
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
TRANS("Error whilst loading"),
TRANS("Couldn't read from the specified file!"));
}
#else
ignoreUnused (fileSuffix);
#endif
});
}
//==============================================================================
@ -264,15 +283,11 @@ public:
if (auto* bus = processor->getBus (false, 0))
maxNumOutputs = jmax (0, bus->getDefaultLayout().size());
o.content.setOwned (new SettingsComponent (*this, deviceManager, maxNumInputs, maxNumOutputs));
LookAndFeel_V4 *lfv4 = dynamic_cast<LookAndFeel_V4*>(&o.content->getLookAndFeel());
if (lfv4) {
lfv4->setColour(ResizableWindow::backgroundColourId, Colour::fromHSV (0.0f, 0.0f, 0.1f, 1.0f));
}
o.content->setSize (500, 400);
auto content = std::make_unique<SettingsComponent> (*this, deviceManager, maxNumInputs, maxNumOutputs);
content->setSize (500, 550);
content->setToRecommendedSize();
o.content.setOwned (content.release());
o.dialogTitle = TRANS("Audio/MIDI Settings");
o.dialogBackgroundColour = o.content->getLookAndFeel().findColour (ResizableWindow::backgroundColourId);
@ -312,18 +327,17 @@ public:
#endif
}
auto totalInChannels = processor->getMainBusNumInputChannels();
auto totalOutChannels = processor->getMainBusNumOutputChannels();
auto inputChannels = getNumInputChannels();
auto outputChannels = getNumOutputChannels();
if (channelConfiguration.size() > 0)
if (inputChannels == 0 && outputChannels == 0 && processor->isMidiEffect())
{
auto defaultConfig = channelConfiguration.getReference (0);
totalInChannels = defaultConfig.numIns;
totalOutChannels = defaultConfig.numOuts;
// add a dummy output channel for MIDI effect plug-ins so they can receive audio callbacks
outputChannels = 1;
}
deviceManager.initialise (enableAudioInput ? totalInChannels : 0,
totalOutChannels,
deviceManager.initialise (enableAudioInput ? inputChannels : 0,
outputChannels,
savedState.get(),
true,
preferredDefaultDeviceName,
@ -403,7 +417,93 @@ public:
std::unique_ptr<AudioDeviceManager::AudioDeviceSetup> options;
Array<MidiDeviceInfo> lastMidiDevices;
std::unique_ptr<FileChooser> stateFileChooser;
private:
/* This class can be used to ensure that audio callbacks use buffers with a
predictable maximum size.
On some platforms (such as iOS 10), the expected buffer size reported in
audioDeviceAboutToStart may be smaller than the blocks passed to
audioDeviceIOCallback. This can lead to out-of-bounds reads if the render
callback depends on additional buffers which were initialised using the
smaller size.
As a workaround, this class will ensure that the render callback will
only ever be called with a block with a length less than or equal to the
expected block size.
*/
class CallbackMaxSizeEnforcer : public AudioIODeviceCallback
{
public:
explicit CallbackMaxSizeEnforcer (AudioIODeviceCallback& callbackIn)
: inner (callbackIn) {}
void audioDeviceAboutToStart (AudioIODevice* device) override
{
maximumSize = device->getCurrentBufferSizeSamples();
storedInputChannels .resize ((size_t) device->getActiveInputChannels() .countNumberOfSetBits());
storedOutputChannels.resize ((size_t) device->getActiveOutputChannels().countNumberOfSetBits());
inner.audioDeviceAboutToStart (device);
}
void audioDeviceIOCallback (const float** inputChannelData,
int numInputChannels,
float** outputChannelData,
int numOutputChannels,
int numSamples) override
{
jassertquiet ((int) storedInputChannels.size() == numInputChannels);
jassertquiet ((int) storedOutputChannels.size() == numOutputChannels);
int position = 0;
while (position < numSamples)
{
const auto blockLength = jmin (maximumSize, numSamples - position);
initChannelPointers (inputChannelData, storedInputChannels, position);
initChannelPointers (outputChannelData, storedOutputChannels, position);
inner.audioDeviceIOCallback (storedInputChannels.data(),
(int) storedInputChannels.size(),
storedOutputChannels.data(),
(int) storedOutputChannels.size(),
blockLength);
position += blockLength;
}
}
void audioDeviceStopped() override
{
inner.audioDeviceStopped();
}
private:
struct GetChannelWithOffset
{
int offset;
template <typename Ptr>
auto operator() (Ptr ptr) const noexcept -> Ptr { return ptr + offset; }
};
template <typename Ptr, typename Vector>
void initChannelPointers (Ptr&& source, Vector&& target, int offset)
{
std::transform (source, source + target.size(), target.begin(), GetChannelWithOffset { offset });
}
AudioIODeviceCallback& inner;
int maximumSize = 0;
std::vector<const float*> storedInputChannels;
std::vector<float*> storedOutputChannels;
};
CallbackMaxSizeEnforcer maxSizeEnforcer { *this };
//==============================================================================
class SettingsComponent : public Component
{
@ -414,7 +514,7 @@ private:
int maxAudioOutputChannels)
: owner (pluginHolder),
deviceSelector (deviceManagerToUse,
0, 0, // maxAudioInputChannels, // force zero to fully disable input selection
0, maxAudioInputChannels,
0, maxAudioOutputChannels,
true,
(pluginHolder.processor.get() != nullptr && pluginHolder.processor->producesMidi()),
@ -436,17 +536,6 @@ private:
shouldMuteLabel.attachToComponent (&shouldMuteButton, true);
}
for (int i =0; i < deviceSelector.getNumChildComponents(); i ++){
LookAndFeel_V4 *lfv4 = dynamic_cast<LookAndFeel_V4*>(&deviceSelector.getChildComponent(i) ->getLookAndFeel());
if (lfv4) {
lfv4->setColour(ComboBox::ColourIds::backgroundColourId, Colour::fromHSV (0.0f, 0.0f, 0.2f, 1.0f));
lfv4->setColour(ListBox::ColourIds::backgroundColourId, Colour::fromHSV (0.0f, 0.0f, 0.2f, 1.0f));
lfv4->setColour(TextButton::ColourIds::buttonColourId, Colour::fromHSV (0.0f, 0.0f, 0.1f, 1.0f));
}
}
}
void paint (Graphics& g) override
@ -456,6 +545,8 @@ private:
void resized() override
{
const ScopedValueSetter<bool> scope (isResizing, true);
auto r = getLocalBounds();
if (owner.getProcessorHasPotentialFeedbackLoop())
@ -473,12 +564,34 @@ private:
deviceSelector.setBounds (r);
}
void childBoundsChanged (Component* childComp) override
{
if (! isResizing && childComp == &deviceSelector)
setToRecommendedSize();
}
void setToRecommendedSize()
{
const auto extraHeight = [&]
{
if (! owner.getProcessorHasPotentialFeedbackLoop())
return 0;
const auto itemHeight = deviceSelector.getItemHeight();
const auto separatorHeight = (itemHeight >> 1);
return itemHeight + separatorHeight;
}();
setSize (getWidth(), deviceSelector.getHeight() + extraHeight);
}
private:
//==============================================================================
StandalonePluginHolder& owner;
AudioDeviceSelectorComponent deviceSelector;
Label shouldMuteLabel;
ToggleButton shouldMuteButton;
bool isResizing = false;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SettingsComponent)
@ -522,7 +635,7 @@ private:
const String& preferredDefaultDeviceName,
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions)
{
deviceManager.addAudioCallback (this);
deviceManager.addAudioCallback (&maxSizeEnforcer);
deviceManager.addMidiInputDeviceCallback ({}, &player);
reloadAudioDeviceState (enableAudioInput, preferredDefaultDeviceName, preferredSetupOptions);
@ -533,7 +646,7 @@ private:
saveAudioDeviceState();
deviceManager.removeMidiInputDeviceCallback ({}, &player);
deviceManager.removeAudioCallback (this);
deviceManager.removeAudioCallback (&maxSizeEnforcer);
}
void timerCallback() override
@ -568,8 +681,9 @@ private:
@tags{Audio}
*/
class StandaloneFilterWindow : public DocumentWindow,
public Button::Listener,
private Button::Listener,
public MenuBarModel
{
public:
//==============================================================================
@ -588,46 +702,46 @@ public:
const String& preferredDefaultDeviceName = String(),
const AudioDeviceManager::AudioDeviceSetup* preferredSetupOptions = nullptr,
const Array<PluginInOuts>& constrainToConfiguration = {},
#if JUCE_ANDROID || JUCE_IOS
bool autoOpenMidiDevices = true
#else
bool autoOpenMidiDevices = false
#endif
)
#if JUCE_ANDROID || JUCE_IOS
bool autoOpenMidiDevices = true
#else
bool autoOpenMidiDevices = true
#endif
)
#if JUCE_MAC
: DocumentWindow ("", backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
: DocumentWindow("", backgroundColour, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
#endif
#if ! JUCE_MAC
: DocumentWindow (title, Colour::fromHSV (0.0f, 0.0f, 0.1f, 0.5f), DocumentWindow::minimiseButton | DocumentWindow::closeButton),
: DocumentWindow(title, juce::Colours::black, DocumentWindow::minimiseButton | DocumentWindow::closeButton),
#endif
menuBar(this)
#if ! JUCE_MAC
, optionsButton ("Settings")
#endif
#if ! JUCE_MAC
, optionsButton("Settings")
#endif
{
#if JUCE_IOS || JUCE_ANDROID
setTitleBarHeight (0);
#else
LookAndFeel_V4 *lfv4 = dynamic_cast<LookAndFeel_V4*>(&getLookAndFeel());
if (lfv4) {
lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widgetBackground, Colour::fromHSV (0.0f, 0.0f, 0.1f, 1.0f));
}
setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
#if JUCE_MAC
#if JUCE_IOS || JUCE_ANDROID
setTitleBarHeight(0);
#else
LookAndFeel_V4* lfv4 = dynamic_cast<LookAndFeel_V4*>(&getLookAndFeel());
if (lfv4) {
lfv4->getCurrentColourScheme().setUIColour(LookAndFeel_V4::ColourScheme::widgetBackground, Colour::fromHSV(0.0f, 0.0f, 0.1f, 1.0f));
}
setTitleBarButtonsRequired(DocumentWindow::minimiseButton | DocumentWindow::closeButton, false);
#if JUCE_MAC
setUsingNativeTitleBar(true);
menu.addItem (1, TRANS("Audio/MIDI Settings..."));
MenuBarModel::setMacMainMenu (this, &menu);
#else
Component::addAndMakeVisible (optionsButton);
optionsButton.addListener (this);
optionsButton.setTriggeredOnMouseDown (true);
#endif
#endif
menu.addItem(1, TRANS("Audio/MIDI Settings..."));
MenuBarModel::setMacMainMenu(this, &menu);
#else
Component::addAndMakeVisible(optionsButton);
optionsButton.addListener(this);
optionsButton.setTriggeredOnMouseDown(true);
#endif
#endif
pluginHolder.reset (new StandalonePluginHolder (settingsToUse, takeOwnershipOfSettings,
preferredDefaultDeviceName, preferredSetupOptions,
@ -635,32 +749,55 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
#if JUCE_IOS || JUCE_ANDROID
setFullScreen (true);
setContentOwned (new MainContentComponent (*this), false);
updateContent();
#else
setContentOwned (new MainContentComponent (*this), true);
updateContent();
if (auto* props = pluginHolder->settings.get())
const auto windowScreenBounds = [this]() -> Rectangle<int>
{
const int x = props->getIntValue ("windowX", -100);
const int y = props->getIntValue ("windowY", -100);
const auto width = getWidth();
const auto height = getHeight();
if (x != -100 && y != -100)
setBoundsConstrained ({ x, y, getWidth(), getHeight() });
else
centreWithSize (getWidth(), getHeight());
}
else
{
centreWithSize (getWidth(), getHeight());
}
const auto& displays = Desktop::getInstance().getDisplays();
if (auto* props = pluginHolder->settings.get())
{
constexpr int defaultValue = -100;
const auto x = props->getIntValue ("windowX", defaultValue);
const auto y = props->getIntValue ("windowY", defaultValue);
if (x != defaultValue && y != defaultValue)
{
const auto screenLimits = displays.getDisplayForRect ({ x, y, width, height })->userArea;
return { jlimit (screenLimits.getX(), jmax (screenLimits.getX(), screenLimits.getRight() - width), x),
jlimit (screenLimits.getY(), jmax (screenLimits.getY(), screenLimits.getBottom() - height), y),
width, height };
}
}
const auto displayArea = displays.getPrimaryDisplay()->userArea;
return { displayArea.getCentreX() - width / 2,
displayArea.getCentreY() - height / 2,
width, height };
}();
setBoundsConstrained (windowScreenBounds);
if (auto* processor = getAudioProcessor())
if (auto* editor = processor->getActiveEditor())
setResizable (editor->isResizable(), false);
#endif
}
~StandaloneFilterWindow() override
{
#if JUCE_MAC
setMacMainMenu(nullptr);
#endif
#if JUCE_MAC
MenuBarModel::setMacMainMenu(nullptr);
#endif
#if (! JUCE_IOS) && (! JUCE_ANDROID)
if (auto* props = pluginHolder->settings.get())
{
@ -689,7 +826,7 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
props->removeValue ("filterState");
pluginHolder->createPlugin();
setContentOwned (new MainContentComponent (*this), true);
updateContent();
pluginHolder->startPlaying();
}
@ -703,31 +840,31 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
StringArray getMenuBarNames() override
{
// StringArray menuBarNames;
// menuBarNames.add("Options");
// return menuBarNames;
const char *menuNames[] = { 0 };
return StringArray (menuNames);
// StringArray menuBarNames;
// menuBarNames.add("Options");
// return menuBarNames;
const char* menuNames[] = { 0 };
return StringArray(menuNames);
}
PopupMenu getMenuForIndex (int topLevelMenuIndex, const String& menuName) override
PopupMenu getMenuForIndex(int topLevelMenuIndex, const String& menuName) override
{
PopupMenu m;
// m.addItem (1, TRANS("Audio Settings..."));
// m.addSeparator();
// m.addItem (1, TRANS("Audio Settings..."));
// m.addSeparator();
return m;
}
void menuItemSelected (int menuItemID, int topLevelMenuIndex) override
void menuItemSelected(int menuItemID, int topLevelMenuIndex) override
{
handleMenuResult(menuItemID);
}
void menuBarActivated (bool isActive) override {};
void menuBarActivated(bool isActive) override {};
void handleMenuResult (int result)
{
@ -750,9 +887,7 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
void resized() override
{
DocumentWindow::resized();
#if ! JUCE_MAC
optionsButton.setBounds (8, 6, 60, getTitleBarHeight() - 8);
#endif
}
virtual StandalonePluginHolder* getPluginHolder() { return pluginHolder.get(); }
@ -762,20 +897,23 @@ lfv4->getCurrentColourScheme().setUIColour (LookAndFeel_V4::ColourScheme::widget
PopupMenu menu;
private:
void updateContent()
{
auto* content = new MainContentComponent (*this);
decoratorConstrainer.setMainContentComponent (content);
#if JUCE_IOS || JUCE_ANDROID
constexpr auto resizeAutomatically = false;
#else
constexpr auto resizeAutomatically = true;
#endif
setContentOwned (content, resizeAutomatically);
}
void buttonClicked (Button*) override
{
pluginHolder->showAudioSettingsDialog();
//PopupMenu m;
//m.addItem (1, TRANS("Audio/MIDI Settings..."));
//m.addSeparator();
//m.addItem (2, TRANS("Save current state..."));
//m.addItem (3, TRANS("Load a saved state..."));
//m.addSeparator();
//m.addItem (4, TRANS("Reset to default state"));
//m.showMenuAsync (PopupMenu::Options(),
// ModalCallbackFunction::forComponent (menuCallback, this));
}
//==============================================================================
@ -790,7 +928,7 @@ private:
editor (owner.getAudioProcessor()->hasEditor() ? owner.getAudioProcessor()->createEditorIfNeeded()
: new GenericAudioProcessorEditor (*owner.getAudioProcessor()))
{
Value& inputMutedValue = owner.pluginHolder->getMuteInputValue();
inputMutedValue.referTo (owner.pluginHolder->getMuteInputValue());
if (editor != nullptr)
{
@ -829,9 +967,31 @@ private:
notification.setBounds (r.removeFromTop (NotificationArea::height));
if (editor != nullptr)
editor->setBounds (editor->getLocalArea (this, r.toFloat())
.withPosition (r.getTopLeft().toFloat().transformedBy (editor->getTransform().inverted()))
.toNearestInt());
{
const auto newPos = r.getTopLeft().toFloat().transformedBy (editor->getTransform().inverted());
if (preventResizingEditor)
editor->setTopLeftPosition (newPos.roundToInt());
else
editor->setBoundsConstrained (editor->getLocalArea (this, r.toFloat()).withPosition (newPos).toNearestInt());
}
}
ComponentBoundsConstrainer* getEditorConstrainer() const
{
if (auto* e = editor.get())
return e->getConstrainer();
return nullptr;
}
BorderSize<int> computeBorder() const
{
const auto outer = owner.getContentComponentBorder();
return { outer.getTop() + (shouldShowNotification ? NotificationArea::height : 0),
outer.getLeft(),
outer.getBottom(),
outer.getRight() };
}
private:
@ -846,7 +1006,7 @@ private:
#if JUCE_IOS || JUCE_ANDROID
settingsButton ("Unmute Input")
#else
settingsButton ("Settings")
settingsButton ("Settings...")
#endif
{
setOpaque (true);
@ -893,10 +1053,9 @@ private:
#else
if (editor != nullptr)
{
auto rect = getSizeToContainEditor();
setSize (rect.getWidth(),
rect.getHeight() + (shouldShowNotification ? NotificationArea::height : 0));
const int extraHeight = shouldShowNotification ? NotificationArea::height : 0;
const auto rect = getSizeToContainEditor();
setSize (rect.getWidth(), rect.getHeight() + extraHeight);
}
#endif
}
@ -914,6 +1073,8 @@ private:
//==============================================================================
void componentMovedOrResized (Component&, bool, bool) override
{
const ScopedValueSetter<bool> scope (preventResizingEditor, true);
if (editor != nullptr)
{
auto rect = getSizeToContainEditor();
@ -935,15 +1096,87 @@ private:
StandaloneFilterWindow& owner;
NotificationArea notification;
std::unique_ptr<AudioProcessorEditor> editor;
Value inputMutedValue;
bool shouldShowNotification = false;
bool preventResizingEditor = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
/* This custom constrainer checks with the AudioProcessorEditor (which might itself be
constrained) to ensure that any size we choose for the standalone window will be suitable
for the editor too.
Without this constrainer, attempting to resize the standalone window may set bounds on the
peer that are unsupported by the inner editor. In this scenario, the peer will be set to a
'bad' size, then the inner editor will be resized. The editor will check the new bounds with
its own constrainer, and may set itself to a more suitable size. After that, the resizable
window will see that its content component has changed size, and set the bounds of the peer
accordingly. The end result is that the peer is resized twice in a row to different sizes,
which can appear glitchy/flickery to the user.
*/
struct DecoratorConstrainer : public ComponentBoundsConstrainer
{
void checkBounds (Rectangle<int>& bounds,
const Rectangle<int>& previousBounds,
const Rectangle<int>& limits,
bool isStretchingTop,
bool isStretchingLeft,
bool isStretchingBottom,
bool isStretchingRight) override
{
auto* decorated = contentComponent != nullptr ? contentComponent->getEditorConstrainer()
: nullptr;
if (decorated != nullptr)
{
const auto border = contentComponent->computeBorder();
const auto requestedBounds = bounds;
border.subtractFrom (bounds);
decorated->checkBounds (bounds,
border.subtractedFrom (previousBounds),
limits,
isStretchingTop,
isStretchingLeft,
isStretchingBottom,
isStretchingRight);
border.addTo (bounds);
bounds = bounds.withPosition (requestedBounds.getPosition());
if (isStretchingTop && ! isStretchingBottom)
bounds = bounds.withBottomY (previousBounds.getBottom());
if (! isStretchingTop && isStretchingBottom)
bounds = bounds.withY (previousBounds.getY());
if (isStretchingLeft && ! isStretchingRight)
bounds = bounds.withRightX (previousBounds.getRight());
if (! isStretchingLeft && isStretchingRight)
bounds = bounds.withX (previousBounds.getX());
}
else
{
ComponentBoundsConstrainer::checkBounds (bounds,
previousBounds,
limits,
isStretchingTop,
isStretchingLeft,
isStretchingBottom,
isStretchingRight);
}
}
void setMainContentComponent (MainContentComponent* in) { contentComponent = in; }
private:
MainContentComponent* contentComponent = nullptr;
};
//==============================================================================
#if ! JUCE_MAC
TextButton optionsButton;
#endif
DecoratorConstrainer decoratorConstrainer;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandaloneFilterWindow)
};

View File

@ -9,11 +9,12 @@
pluginAUExportPrefix="" pluginRTASCategory="2048" aaxIdentifier="com.discodsp.obxd"
companyName="discoDSP" companyWebsite="https://www.discodsp.com/"
pluginIsMidiEffectPlugin="0" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn"
pluginFormats="buildAU,buildStandalone,buildVST,buildVST3" buildVST="1"
buildVST3="1" buildAU="1" buildAUv3="0" buildRTAS="0" buildAAX="0"
buildStandalone="1" enableIAA="0" jucerFormatVersion="1" pluginChannelConfigs="{0,2}"
pluginFormats="buildStandalone,buildVST3" buildVST="0" buildVST3="1"
buildAU="0" buildAUv3="0" buildRTAS="0" buildAAX="0" buildStandalone="1"
enableIAA="0" jucerFormatVersion="1" pluginChannelConfigs="{0,2}"
companyCopyright="discoDSP" companyEmail="contactus@discodsp.com"
pluginAAXCategory="2048" pluginVSTCategory="kPlugCategSynth">
pluginAAXCategory="2048" pluginVSTCategory="kPlugCategSynth"
displaySplashScreen="1">
<MAINGROUP id="NZ3n4V" name="OB-Xd">
<GROUP id="{90740217-84AB-FD0D-FBC4-CA9EA2C68D5E}" name="Source">
<GROUP id="{E11C29DD-69D5-DA26-5CFF-B65751876DEE}" name="MTS">
@ -91,7 +92,7 @@
<EXPORTFORMATS>
<XCODE_MAC targetFolder="Builds/MacOSX" bigIcon="nnY63W" smallIcon="nnY63W"
vstLegacyFolder="Modules/vstsdk2.4" vst3Folder="Modules/vstsdk3"
aaxFolder="Modules/aax">
aaxFolder="Modules/aax" extraDefs="JUCE_MODAL_LOOPS_PERMITTED=1">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="OB-Xd"
stripLocalSymbols="0" linkTimeOptimisation="0"/>
@ -116,7 +117,7 @@
</MODULEPATHS>
</XCODE_MAC>
<LINUX_MAKE targetFolder="Builds/LinuxMakefile" extraLinkerFlags="-no-pie"
vstLegacyFolder="Modules/vstsdk2.4">
vstLegacyFolder="Modules/vstsdk2.4" extraDefs="JUCE_MODAL_LOOPS_PERMITTED=1">
<CONFIGURATIONS>
<CONFIGURATION name="Release64" libraryPath="/usr/X11R6/lib/" isDebug="0" optimisation="3"
targetName="OB-Xd" linuxArchitecture="-m64" headerPath="../../JuceLibraryCode&#10;../../Source&#10;../Modules/vstsdk2.4&#10;/usr/include/freetype2&#10;/usr/include"
@ -140,7 +141,7 @@
</LINUX_MAKE>
<VS2019 targetFolder="Builds/VisualStudio2019" smallIcon="nnY63W" bigIcon="nnY63W"
vstLegacyFolder="Modules/vstsdk2.4" vst3Folder="Modules/vstsdk3"
aaxFolder="Modules/aax">
aaxFolder="Modules/aax" extraDefs="JUCE_MODAL_LOOPS_PERMITTED=1">
<CONFIGURATIONS>
<CONFIGURATION isDebug="1" name="Debug" targetName="OB-Xd" headerPath="../../Modules/asiosdk2.3.2/common"
useRuntimeLibDLL="0" enablePluginBinaryCopyStep="1"/>

View File

@ -15,4 +15,4 @@ Latest binaries can be downloaded at https://www.discodsp.com/obxd/
# Building
Source code can be compiled with [JUCE 6.0.5](https://github.com/juce-framework/JUCE/releases/tag/6.0.5) and VST3 SDK.
Source code can be compiled with [JUCE 6.1.6](https://github.com/juce-framework/JUCE/releases/tag/6.1.6) and VST3 SDK.

View File

@ -61,7 +61,6 @@ public:
h2 = fh;
w2 = kni.getWidth();
numFr = kni.getHeight() / h2;
setPopupDisplayEnabled(true, true, getParentComponent());
setLookAndFeel(&lookAndFeel);
}

View File

@ -122,8 +122,15 @@ void ObxdAudioProcessorEditor::resized() {
if (mappingComps[name] != nullptr){
if (auto* knob = dynamic_cast<Knob*>(mappingComps[name])){
knob->setBounds(transformBounds(x, y, d,d));
if (!tooltipEnabled)
const auto tooltipBehavior = processor.getTooltipBehavior();
if (tooltipBehavior == Tooltip::Disable ||
(tooltipBehavior == Tooltip::StandardDisplay && !tooltipEnabled))
{
knob->setPopupDisplayEnabled(false, false, nullptr);
} else
{
knob->setPopupDisplayEnabled(true, true, knob->getParentComponent());
}
}
else if (dynamic_cast<ButtonList*>(mappingComps[name])){
mappingComps[name]->setBounds(transformBounds(x, y, w, h));
@ -954,6 +961,24 @@ void ObxdAudioProcessorEditor::createMenu ()
scaleMenu.addItem(menuScaleNum+2, "2x", true, getScaleFactor() == 2.0f);
menu->addSubMenu("GUI Size", scaleMenu, true);
PopupMenu tooltipMenu;
tooltipMenu.addItem("Disabled", true, processor.getTooltipBehavior() == Tooltip::Disable, [&]
{
processor.setTooltipBehavior(Tooltip::Disable);
resized();
});
tooltipMenu.addItem("Standard Display", true, processor.getTooltipBehavior() == Tooltip::StandardDisplay, [&]
{
processor.setTooltipBehavior(Tooltip::StandardDisplay);
resized();
});
tooltipMenu.addItem("Full Display", true, processor.getTooltipBehavior() == Tooltip::FullDisplay, [&]
{
processor.setTooltipBehavior(Tooltip::FullDisplay);
resized();
});
menu->addSubMenu("Tooltips", tooltipMenu, true);
#ifdef LINUX
menu->addItem(1, String("Release ") + String(JucePlugin_VersionString).dropLastCharacters(2), false);
#endif

View File

@ -74,6 +74,7 @@ ObxdAudioProcessor::ObxdAudioProcessor()
config = std::unique_ptr<PropertiesFile> (new PropertiesFile (getDocumentFolder().getChildFile ("Skin.xml"), options));
showPresetBar = config->getBoolValue("presetnavigation");
gui_size = config->getIntValue("gui_size", 1);
tooltipBehavior = static_cast<Tooltip>(config->getIntValue("tooltip", 1));
currentSkin = config->containsKey("skin") ? config->getValue("skin") : "Ilkka Rosma Dark";
currentBank = "000 - FMR OB-Xa Patch Book";
@ -824,6 +825,19 @@ void ObxdAudioProcessor::setGuiSize(const int gui_size) {
config->setValue("gui_size", gui_size);
config->setNeedsToBeSaved(true);
}
Tooltip ObxdAudioProcessor::getTooltipBehavior() const
{
return tooltipBehavior;
}
void ObxdAudioProcessor::setTooltipBehavior(const Tooltip tooltip)
{
this->tooltipBehavior = tooltip;
config->setValue("tooltip", static_cast<int>(tooltip));
config->setNeedsToBeSaved(true);
}
//==============================================================================
String ObxdAudioProcessor::getEngineParameterId (size_t index)
{
@ -1017,7 +1031,7 @@ String ObxdAudioProcessor::getTrueParameterValueFromNormalizedRange(size_t index
break;
}
return String{ value, 2 };
return String{ static_cast<int>(jmap(value, 0.f, 127.f)) };
}
int ObxdAudioProcessor::getParameterIndexFromId (String paramId)

View File

@ -110,6 +110,12 @@ static inline float fxbSwapFloat (const float x) noexcept
#endif
}
enum class Tooltip
{
Disable = 0,
StandardDisplay,
FullDisplay
};
//==============================================================================
/**
*/
@ -196,6 +202,8 @@ public:
File getCurrentSkinFolder() const;
void setCurrentSkinFolder(const String& folderName);
void setGuiSize(const int gui_size);
Tooltip getTooltipBehavior() const;
void setTooltipBehavior(const Tooltip tooltip);
//==============================================================================
static String getEngineParameterId (size_t);
static String getTrueParameterValueFromNormalizedRange(size_t, float normalizedValue);
@ -251,6 +259,7 @@ public:
private:
Array<File> bankFiles;
Array<File> skinFiles;
Tooltip tooltipBehavior;
std::unique_ptr<PropertiesFile> config;
InterProcessLock configLock;