From ebb0a156a4c06b1e1bc386519a40977bcb7366ac Mon Sep 17 00:00:00 2001 From: hogliux Date: Thu, 19 May 2016 14:28:34 +0100 Subject: [PATCH] Add options to make AuidoProcessorEditors resizable with constraints --- .../audio plugin demo/Source/PluginEditor.cpp | 7 +- .../audio plugin demo/Source/PluginEditor.h | 2 - .../VST3/juce_VST3_Wrapper.cpp | 29 ++++- .../processors/juce_AudioProcessorEditor.cpp | 114 +++++++++++++++++- .../processors/juce_AudioProcessorEditor.h | 73 ++++++++++- 5 files changed, 209 insertions(+), 16 deletions(-) diff --git a/examples/audio plugin demo/Source/PluginEditor.cpp b/examples/audio plugin demo/Source/PluginEditor.cpp index fa134c5de6..463a603b78 100644 --- a/examples/audio plugin demo/Source/PluginEditor.cpp +++ b/examples/audio plugin demo/Source/PluginEditor.cpp @@ -82,9 +82,8 @@ JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemo timecodeDisplayLabel.setColour (Label::textColourId, Colours::blue); timecodeDisplayLabel.setFont (Font (Font::getDefaultMonospacedFontName(), 15.0f, Font::plain)); - // add the triangular resizer component for the bottom-right of the UI - addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits)); - resizeLimits.setSizeLimits (150, 150, 800, 300); + // set resize limits for this plug-in + setResizeLimits (400, 200, 800, 300); // set our component's initial size to be the last one that was stored in the filter's settings setSize (owner.lastUIWidth, @@ -120,8 +119,6 @@ void JuceDemoPluginAudioProcessorEditor::resized() gainSlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth() / 2))); delaySlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth()))); - resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16); - getProcessor().lastUIWidth = getWidth(); getProcessor().lastUIHeight = getHeight(); } diff --git a/examples/audio plugin demo/Source/PluginEditor.h b/examples/audio plugin demo/Source/PluginEditor.h index 6240127574..5ba46d5bf0 100644 --- a/examples/audio plugin demo/Source/PluginEditor.h +++ b/examples/audio plugin demo/Source/PluginEditor.h @@ -36,8 +36,6 @@ private: MidiKeyboardComponent midiKeyboard; Label timecodeDisplayLabel, gainLabel, delayLabel; ScopedPointer gainSlider, delaySlider; - ScopedPointer resizer; - ComponentBoundsConstrainer resizeLimits; //============================================================================== JuceDemoPluginAudioProcessor& getProcessor() const diff --git a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp index e09aa35cd7..9e3a7d5737 100644 --- a/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp +++ b/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp @@ -709,14 +709,30 @@ private: return kResultFalse; } - tresult PLUGIN_API canResize() override { return kResultTrue; } + tresult PLUGIN_API canResize() override + { + if (component != nullptr) + if (AudioProcessorEditor* editor = component->pluginEditor) + return editor->isResizable(); + + return true; + } tresult PLUGIN_API checkSizeConstraint (ViewRect* rectToCheck) override { if (rectToCheck != nullptr && component != nullptr) { - rectToCheck->right = rectToCheck->left + component->getWidth(); - rectToCheck->bottom = rectToCheck->top + component->getHeight(); + // checkSizeConstraint + Rectangle juceRect = Rectangle::leftTopRightBottom (rectToCheck->left, rectToCheck->top, + rectToCheck->right, rectToCheck->bottom); + if (AudioProcessorEditor* editor = component->pluginEditor) + if (ComponentBoundsConstrainer* constrainer = editor->getConstrainer()) + juceRect.setSize (jlimit (constrainer->getMinimumWidth(), constrainer->getMaximumWidth(), juceRect.getWidth()), + jlimit (constrainer->getMinimumHeight(), constrainer->getMaximumHeight(), juceRect.getHeight())); + + rectToCheck->right = rectToCheck->left + juceRect.getWidth(); + rectToCheck->bottom = rectToCheck->top + juceRect.getHeight(); + return kResultTrue; } @@ -730,8 +746,8 @@ private: { public: ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) - : owner (editor), - pluginEditor (plugin.createEditorIfNeeded()) + : pluginEditor (plugin.createEditorIfNeeded()), + owner (editor) { setOpaque (true); setBroughtToFrontOnMouseClick (true); @@ -802,9 +818,10 @@ private: } } + ScopedPointer pluginEditor; + private: JuceVST3Editor& owner; - ScopedPointer pluginEditor; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) }; diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp index 07f865e82a..4a2c6d354b 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp @@ -22,14 +22,16 @@ ============================================================================== */ -AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p) +AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p), constrainer (nullptr) { + initialise(); } -AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p) +AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p), constrainer (nullptr) { // the filter must be valid.. jassert (p != nullptr); + initialise(); } AudioProcessorEditor::~AudioProcessorEditor() @@ -37,7 +39,115 @@ AudioProcessorEditor::~AudioProcessorEditor() // if this fails, then the wrapper hasn't called editorBeingDeleted() on the // filter for some reason.. jassert (processor.getActiveEditor() != this); + removeComponentListener (resizeListener); } void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {} int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; } + +void AudioProcessorEditor::initialise() +{ + setConstrainer (&defaultConstrainer); + addComponentListener (resizeListener = new AudioProcessorEditorListener (*this)); +} + +//============================================================================== +void AudioProcessorEditor::setResizable (const bool shouldBeResizable) +{ + if (shouldBeResizable) + { + if (resizableCorner == nullptr) + { + Component::addChildComponent (resizableCorner = new ResizableCornerComponent (this, constrainer)); + resizableCorner->setAlwaysOnTop (true); + } + } + else + { + setConstrainer (&defaultConstrainer); + + if (getWidth() > 0 && getHeight() > 0) + defaultConstrainer.setSizeLimits (getWidth(), getHeight(), getWidth(), getHeight()); + + resizableCorner = nullptr; + } + + resized(); +} + +bool AudioProcessorEditor::isResizable() const noexcept +{ + return resizableCorner != nullptr; +} + +void AudioProcessorEditor::setResizeLimits (const int newMinimumWidth, + const int newMinimumHeight, + const int newMaximumWidth, + const int newMaximumHeight) noexcept +{ + // if you've set up a custom constrainer then these settings won't have any effect.. + jassert (constrainer == &defaultConstrainer || constrainer == nullptr); + + setResizable (newMinimumWidth != newMaximumWidth || newMinimumHeight != newMaximumHeight); + + if (constrainer == nullptr) + setConstrainer (&defaultConstrainer); + + defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight, + newMaximumWidth, newMaximumHeight); + + setBoundsConstrained (getBounds()); +} + +void AudioProcessorEditor::setConstrainer (ComponentBoundsConstrainer* newConstrainer) +{ + if (constrainer != newConstrainer) + { + constrainer = newConstrainer; + + const bool shouldBeResizable = resizableCorner != nullptr; + + resizableCorner = nullptr; + + setResizable (shouldBeResizable); + + if (isOnDesktop()) + if (ComponentPeer* const peer = getPeer()) + peer->setConstrainer (constrainer); + } +} + +void AudioProcessorEditor::setBoundsConstrained (Rectangle newBounds) +{ + if (constrainer != nullptr) + constrainer->setBoundsForComponent (this, newBounds, false, false, false, false); + else + setBounds (newBounds); +} + +void AudioProcessorEditor::editorResized (bool wasResized) +{ + if (wasResized) + { + bool resizerHidden = false; + + if (ComponentPeer* peer = getPeer()) + resizerHidden = peer->isFullScreen() || peer->isKioskMode(); + + if (resizableCorner != nullptr) + { + resizableCorner->setVisible (! resizerHidden); + + const int resizerSize = 18; + resizableCorner->setBounds (getWidth() - resizerSize, + getHeight() - resizerSize, + resizerSize, resizerSize); + } + else + { + if (getWidth() > 0 && getHeight() > 0) + defaultConstrainer.setSizeLimits (getWidth(), getHeight(), + getWidth(), getHeight()); + } + } +} diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h b/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h index e043c37beb..801fa60460 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h @@ -25,7 +25,7 @@ #ifndef JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED #define JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED - +class AudioProcessorEditorListener; //============================================================================== /** Base class for the component that acts as the GUI for an AudioProcessor. @@ -84,7 +84,78 @@ public: */ virtual int getControlParameterIndex (Component&); + //============================================================================== + /** Make the editor resizable or fixed. + + @param shouldBeResizable whether it's resizable at all + @see setResizeLimits, isResizable + */ + void setResizable (bool shouldBeResizable); + + /** Returns true if resizing is enabled. + + @see setResizable + */ + bool isResizable() const noexcept; + + /** This sets the maximum and minimum sizes for the window. + + If the window's current size is outside these limits, it will be resized to + make sure it's within them. + + A direct call to setBounds() will bypass any constraint checks, but when the + window is dragged by the user or resized by other indirect means, the constrainer + will limit the numbers involved. + + @see setResizable + */ + void setResizeLimits (int newMinimumWidth, + int newMinimumHeight, + int newMaximumWidth, + int newMaximumHeight) noexcept; + + + /** Returns the bounds constrainer object that this window is using. + You can access this to change its properties. + */ + ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; } + + /** Sets the bounds-constrainer object to use for resizing and dragging this window. + + A pointer to the object you pass in will be kept, but it won't be deleted + by this object, so it's the caller's responsibility to manage it. + + If you pass a nullptr, then no contraints will be placed on the positioning of the window. + */ + void setConstrainer (ComponentBoundsConstrainer* newConstrainer); + + /** Calls the window's setBounds method, after first checking these bounds + with the current constrainer. + + @see setConstrainer + */ + void setBoundsConstrained (Rectangle newBounds); + + ScopedPointer resizableCorner; + private: + //============================================================================== + struct AudioProcessorEditorListener : ComponentListener + { + AudioProcessorEditorListener (AudioProcessorEditor& audioEditor) : e (audioEditor) {} + void componentMovedOrResized (Component&, bool, bool wasResized) override { e.editorResized (wasResized); } + AudioProcessorEditor& e; + }; + + //============================================================================== + void initialise(); + void editorResized (bool wasResized); + + //============================================================================== + ScopedPointer resizeListener; + ComponentBoundsConstrainer defaultConstrainer; + ComponentBoundsConstrainer* constrainer; + JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) };