| @@ -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(); | |||
| } | |||
| @@ -36,8 +36,6 @@ private: | |||
| MidiKeyboardComponent midiKeyboard; | |||
| Label timecodeDisplayLabel, gainLabel, delayLabel; | |||
| ScopedPointer<ParameterSlider> gainSlider, delaySlider; | |||
| ScopedPointer<ResizableCornerComponent> resizer; | |||
| ComponentBoundsConstrainer resizeLimits; | |||
| //============================================================================== | |||
| JuceDemoPluginAudioProcessor& getProcessor() const | |||
| @@ -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<int> juceRect = Rectangle<int>::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<AudioProcessorEditor> pluginEditor; | |||
| private: | |||
| JuceVST3Editor& owner; | |||
| ScopedPointer<AudioProcessorEditor> pluginEditor; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) | |||
| }; | |||
| @@ -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<int> 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()); | |||
| } | |||
| } | |||
| } | |||
| @@ -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<int> newBounds); | |||
| ScopedPointer<ResizableCornerComponent> 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<AudioProcessorEditorListener> resizeListener; | |||
| ComponentBoundsConstrainer defaultConstrainer; | |||
| ComponentBoundsConstrainer* constrainer; | |||
| JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) | |||
| }; | |||