| @@ -82,9 +82,8 @@ JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemo | |||||
| timecodeDisplayLabel.setColour (Label::textColourId, Colours::blue); | timecodeDisplayLabel.setColour (Label::textColourId, Colours::blue); | ||||
| timecodeDisplayLabel.setFont (Font (Font::getDefaultMonospacedFontName(), 15.0f, Font::plain)); | 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 | // set our component's initial size to be the last one that was stored in the filter's settings | ||||
| setSize (owner.lastUIWidth, | setSize (owner.lastUIWidth, | ||||
| @@ -120,8 +119,6 @@ void JuceDemoPluginAudioProcessorEditor::resized() | |||||
| gainSlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth() / 2))); | gainSlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth() / 2))); | ||||
| delaySlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth()))); | delaySlider->setBounds (sliderArea.removeFromLeft (jmin (180, sliderArea.getWidth()))); | ||||
| resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16); | |||||
| getProcessor().lastUIWidth = getWidth(); | getProcessor().lastUIWidth = getWidth(); | ||||
| getProcessor().lastUIHeight = getHeight(); | getProcessor().lastUIHeight = getHeight(); | ||||
| } | } | ||||
| @@ -36,8 +36,6 @@ private: | |||||
| MidiKeyboardComponent midiKeyboard; | MidiKeyboardComponent midiKeyboard; | ||||
| Label timecodeDisplayLabel, gainLabel, delayLabel; | Label timecodeDisplayLabel, gainLabel, delayLabel; | ||||
| ScopedPointer<ParameterSlider> gainSlider, delaySlider; | ScopedPointer<ParameterSlider> gainSlider, delaySlider; | ||||
| ScopedPointer<ResizableCornerComponent> resizer; | |||||
| ComponentBoundsConstrainer resizeLimits; | |||||
| //============================================================================== | //============================================================================== | ||||
| JuceDemoPluginAudioProcessor& getProcessor() const | JuceDemoPluginAudioProcessor& getProcessor() const | ||||
| @@ -709,14 +709,30 @@ private: | |||||
| return kResultFalse; | 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 | tresult PLUGIN_API checkSizeConstraint (ViewRect* rectToCheck) override | ||||
| { | { | ||||
| if (rectToCheck != nullptr && component != nullptr) | 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; | return kResultTrue; | ||||
| } | } | ||||
| @@ -730,8 +746,8 @@ private: | |||||
| { | { | ||||
| public: | public: | ||||
| ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) | ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin) | ||||
| : owner (editor), | |||||
| pluginEditor (plugin.createEditorIfNeeded()) | |||||
| : pluginEditor (plugin.createEditorIfNeeded()), | |||||
| owner (editor) | |||||
| { | { | ||||
| setOpaque (true); | setOpaque (true); | ||||
| setBroughtToFrontOnMouseClick (true); | setBroughtToFrontOnMouseClick (true); | ||||
| @@ -802,9 +818,10 @@ private: | |||||
| } | } | ||||
| } | } | ||||
| ScopedPointer<AudioProcessorEditor> pluginEditor; | |||||
| private: | private: | ||||
| JuceVST3Editor& owner; | JuceVST3Editor& owner; | ||||
| ScopedPointer<AudioProcessorEditor> pluginEditor; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent) | 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.. | // the filter must be valid.. | ||||
| jassert (p != nullptr); | jassert (p != nullptr); | ||||
| initialise(); | |||||
| } | } | ||||
| AudioProcessorEditor::~AudioProcessorEditor() | AudioProcessorEditor::~AudioProcessorEditor() | ||||
| @@ -37,7 +39,115 @@ AudioProcessorEditor::~AudioProcessorEditor() | |||||
| // if this fails, then the wrapper hasn't called editorBeingDeleted() on the | // if this fails, then the wrapper hasn't called editorBeingDeleted() on the | ||||
| // filter for some reason.. | // filter for some reason.. | ||||
| jassert (processor.getActiveEditor() != this); | jassert (processor.getActiveEditor() != this); | ||||
| removeComponentListener (resizeListener); | |||||
| } | } | ||||
| void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {} | void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {} | ||||
| int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; } | 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 | #ifndef JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED | ||||
| #define JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED | #define JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED | ||||
| class AudioProcessorEditorListener; | |||||
| //============================================================================== | //============================================================================== | ||||
| /** | /** | ||||
| Base class for the component that acts as the GUI for an AudioProcessor. | Base class for the component that acts as the GUI for an AudioProcessor. | ||||
| @@ -84,7 +84,78 @@ public: | |||||
| */ | */ | ||||
| virtual int getControlParameterIndex (Component&); | 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: | 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) | JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) | ||||
| }; | }; | ||||