From 364976b1798c4646d9354682e370978b1ca43262 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 19 Jun 2023 17:42:34 +0100 Subject: [PATCH 01/11] AudioProcessor: Tidy up bus assertions --- .../juce_audio_processors/processors/juce_AudioProcessor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 31422a588a11..7ea8d23bcc07 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -1154,7 +1154,7 @@ void AudioProcessor::Bus::updateChannelCount() noexcept void AudioProcessor::BusesProperties::addBus (bool isInput, const String& name, const AudioChannelSet& dfltLayout, bool isActivatedByDefault) { - jassert (dfltLayout.size() != 0); + jassert (! dfltLayout.isDisabled()); BusProperties props; From 2d0f21fb4a87364b11a5768f8fbb8560129d5fc0 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 19 Jun 2023 17:42:49 +0100 Subject: [PATCH 02/11] VST3 Host: Tidy up input/output loops --- .../format_types/juce_VST3PluginFormat.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 701eae8eb116..2229ff468e53 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -2761,9 +2761,8 @@ class VST3PluginInstance final : public AudioPluginInstance // not much we can do to check the layout while the audio processor is running // Let's at least check if it is a VST3 compatible layout - for (int dir = 0; dir < 2; ++dir) + for (const auto isInput : { true, false }) { - bool isInput = (dir == 0); auto n = getBusCount (isInput); for (int i = 0; i < n; ++i) @@ -2776,9 +2775,8 @@ class VST3PluginInstance final : public AudioPluginInstance bool syncBusLayouts (const BusesLayout& layouts) const { - for (int dir = 0; dir < 2; ++dir) + for (const auto isInput : { true, false }) { - bool isInput = (dir == 0); auto n = getBusCount (isInput); const Vst::BusDirection vstDir = (isInput ? Vst::kInput : Vst::kOutput); @@ -3437,9 +3435,8 @@ class VST3PluginInstance final : public AudioPluginInstance VSTComSmartPtr processor; processor.loadFrom (component.get()); - for (int dirIdx = 0; dirIdx < 2; ++dirIdx) + for (const auto isInput : { true, false }) { - const bool isInput = (dirIdx == 0); const Vst::BusDirection dir = (isInput ? Vst::kInput : Vst::kOutput); const int numBuses = component->getBusCount (Vst::kAudio, dir); From c5b8b7eae0b07a77c50a78962efe3cf6fa2a2fe0 Mon Sep 17 00:00:00 2001 From: reuk Date: Mon, 19 Jun 2023 17:52:43 +0100 Subject: [PATCH 03/11] VST3 Host: Remove assertion when plugin requests that its editor should be opened --- .../format_types/juce_VST3PluginFormat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 2229ff468e53..fb18953a14c4 100644 --- a/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -417,7 +417,7 @@ struct VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0 //============================================================================== tresult PLUGIN_API requestOpenEditor ([[maybe_unused]] FIDString name) override { - jassertfalse; + // This request cannot currently be surfaced in the JUCE public API return kResultFalse; } From 0836cf33b19900a655ddae3b8601b7d407a53663 Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 20 Jun 2023 13:02:02 +0100 Subject: [PATCH 04/11] VST3 Client: Avoid assertion failure in setBusesLayout when using PreferredChannelConfigurations Using a preferred channel config with no input or output channels could cause an assertion here because the AudioProcessor will always have a single input/output bus by default. --- .../juce_audio_plugin_client_VST3.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp index 4ed1cdadf92d..cdb72a1db548 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp @@ -3186,10 +3186,10 @@ class JuceVST3Component : public Vst::IComponent, if (type == Vst::kAudio) { - const auto numInputBuses = getNumAudioBuses (true); - const auto numOutputBuses = getNumAudioBuses (false); + const auto numPublicInputBuses = getNumAudioBuses (true); + const auto numPublicOutputBuses = getNumAudioBuses (false); - if (! isPositiveAndBelow (index, dir == Vst::kInput ? numInputBuses : numOutputBuses)) + if (! isPositiveAndBelow (index, dir == Vst::kInput ? numPublicInputBuses : numPublicOutputBuses)) return kResultFalse; // The host is allowed to enable/disable buses as it sees fit, so the plugin needs to be @@ -3212,12 +3212,18 @@ class JuceVST3Component : public Vst::IComponent, AudioProcessor::BusesLayout desiredLayout; - for (auto i = 0; i < numInputBuses; ++i) + for (auto i = 0; i < numPublicInputBuses; ++i) desiredLayout.inputBuses.add (bufferMapper.getRequestedLayoutForInputBus ((size_t) i)); - for (auto i = 0; i < numOutputBuses; ++i) + while (desiredLayout.inputBuses.size() < pluginInstance->getBusCount (true)) + desiredLayout.inputBuses.add (AudioChannelSet::disabled()); + + for (auto i = 0; i < numPublicOutputBuses; ++i) desiredLayout.outputBuses.add (bufferMapper.getRequestedLayoutForOutputBus ((size_t) i)); + while (desiredLayout.outputBuses.size() < pluginInstance->getBusCount (false)) + desiredLayout.outputBuses.add (AudioChannelSet::disabled()); + const auto prev = pluginInstance->getBusesLayout(); const auto busesLayoutSupported = [&] From cb55176b0ea9d555a118b5832dd5ba185ca8dbaa Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 20 Jun 2023 13:12:34 +0100 Subject: [PATCH 05/11] VST3 Client: DRY activateBus implementation --- .../juce_audio_plugin_client_VST3.cpp | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp index cdb72a1db548..cd38b33a482a 100644 --- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_VST3.cpp @@ -3212,17 +3212,20 @@ class JuceVST3Component : public Vst::IComponent, AudioProcessor::BusesLayout desiredLayout; - for (auto i = 0; i < numPublicInputBuses; ++i) - desiredLayout.inputBuses.add (bufferMapper.getRequestedLayoutForInputBus ((size_t) i)); - - while (desiredLayout.inputBuses.size() < pluginInstance->getBusCount (true)) - desiredLayout.inputBuses.add (AudioChannelSet::disabled()); + for (const auto isInput : { true, false }) + { + const auto numPublicBuses = isInput ? numPublicInputBuses : numPublicOutputBuses; + auto& layoutBuses = isInput ? desiredLayout.inputBuses : desiredLayout.outputBuses; - for (auto i = 0; i < numPublicOutputBuses; ++i) - desiredLayout.outputBuses.add (bufferMapper.getRequestedLayoutForOutputBus ((size_t) i)); + for (auto i = 0; i < numPublicBuses; ++i) + { + layoutBuses.add (isInput ? bufferMapper.getRequestedLayoutForInputBus ((size_t) i) + : bufferMapper.getRequestedLayoutForOutputBus ((size_t) i)); + } - while (desiredLayout.outputBuses.size() < pluginInstance->getBusCount (false)) - desiredLayout.outputBuses.add (AudioChannelSet::disabled()); + while (layoutBuses.size() < pluginInstance->getBusCount (isInput)) + layoutBuses.add (AudioChannelSet::disabled()); + } const auto prev = pluginInstance->getBusesLayout(); From 0bf9f745bf110b412685df0deaa065422260ac0a Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 20 Jun 2023 13:32:01 +0100 Subject: [PATCH 06/11] Projucer: Disable hardened runtime for LV2 and VST3 helpers Enabling the hardened runtime also enables library validation, which means that the manifest generator may not be able to load the built plugin if the generator runs before the signing step. The manifest generator tools should not be distributed/archived, so disabling the hardened runtime for these targets is appropriate. --- .../Source/ProjectSaving/jucer_ProjectExport_Xcode.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h index 2296aa3f19c5..a3f237cd13b1 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h @@ -1319,6 +1319,11 @@ class XcodeProjectExporter : public ProjectExporter owner.addObject (v); } + bool shouldUseHardenedRuntime() const + { + return type != VST3Helper && type != LV2Helper && owner.isHardenedRuntimeEnabled(); + } + //============================================================================== String getTargetAttributes() const { @@ -1341,7 +1346,7 @@ class XcodeProjectExporter : public ProjectExporter || owner.getProject().isAUPluginHost()); capabilities["Push"] = owner.isPushNotificationsEnabled(); capabilities["Sandbox"] = type == Target::AudioUnitv3PlugIn || owner.isAppSandboxEnabled(); - capabilities["HardenedRuntime"] = owner.isHardenedRuntimeEnabled(); + capabilities["HardenedRuntime"] = shouldUseHardenedRuntime(); if (owner.iOS && owner.isiCloudPermissionsEnabled()) capabilities["com.apple.iCloud"] = true; @@ -1397,7 +1402,7 @@ class XcodeProjectExporter : public ProjectExporter if (owner.isPushNotificationsEnabled() || owner.isAppGroupsEnabled() || owner.isAppSandboxEnabled() - || owner.isHardenedRuntimeEnabled() + || shouldUseHardenedRuntime() || owner.isNetworkingMulticastEnabled() || (owner.isiOS() && owner.isiCloudPermissionsEnabled()) || (owner.isiOS() && owner.getProject().isAUPluginHost())) @@ -1687,7 +1692,7 @@ class XcodeProjectExporter : public ProjectExporter s.set ("CONFIGURATION_BUILD_DIR", addQuotesIfRequired (adjustedConfigBuildDir)); - if (owner.isHardenedRuntimeEnabled()) + if (shouldUseHardenedRuntime()) s.set ("ENABLE_HARDENED_RUNTIME", "YES"); String gccVersion ("com.apple.compilers.llvm.clang.1_0"); From 02eb66ee7aaa1f6796188bb78f2e719418af6546 Mon Sep 17 00:00:00 2001 From: Anthony Nicholls Date: Thu, 22 Jun 2023 13:07:03 +0000 Subject: [PATCH 07/11] ProgressBar: Add style parameter --- examples/GUI/WidgetsDemo.h | 45 +++++- examples/GUI/WindowsDemo.h | 144 ++++++++++++++++-- .../Utility/UI/jucer_ProjucerLookAndFeel.cpp | 67 +------- .../Utility/UI/jucer_ProjucerLookAndFeel.h | 2 +- .../components/juce_Component.h | 5 +- .../lookandfeel/juce_LookAndFeel_V2.cpp | 5 + .../lookandfeel/juce_LookAndFeel_V2.h | 1 + .../lookandfeel/juce_LookAndFeel_V4.cpp | 59 ++++--- .../lookandfeel/juce_LookAndFeel_V4.h | 8 +- .../widgets/juce_ProgressBar.cpp | 30 ++-- .../widgets/juce_ProgressBar.h | 84 ++++++++-- .../windows/juce_AlertWindow.cpp | 4 +- .../windows/juce_AlertWindow.h | 15 +- 13 files changed, 338 insertions(+), 131 deletions(-) diff --git a/examples/GUI/WidgetsDemo.h b/examples/GUI/WidgetsDemo.h index 4d524c035904..b1751a08b744 100644 --- a/examples/GUI/WidgetsDemo.h +++ b/examples/GUI/WidgetsDemo.h @@ -461,7 +461,8 @@ struct ButtonsPage : public Component //============================================================================== -struct MiscPage : public Component +struct MiscPage : public Component, + private Timer { MiscPage() { @@ -482,6 +483,21 @@ struct MiscPage : public Component comboBox.addItem ("combo box item " + String (i), i); comboBox.setSelectedId (1); + + addAndMakeVisible (linearProgressBar); + linearProgressBar.setStyle (ProgressBar::Style::linear); + linearProgressBar.setBounds (10, 115, 200, 24); + + addAndMakeVisible (circularProgressBar); + circularProgressBar.setStyle (ProgressBar::Style::circular); + circularProgressBar.setBounds (10, 145, 200, 100); + + startTimerHz (10); + } + + ~MiscPage() override + { + stopTimer(); } void lookAndFeelChanged() override @@ -490,10 +506,37 @@ struct MiscPage : public Component textEditor2.applyFontToAllText (textEditor2.getFont()); } + void timerCallback() override + { + constexpr auto minValue = -0.2; + constexpr auto maxValue = 1.2; + constexpr auto maxIncrement = 0.05; + + if (progress >= maxValue) + progress = minValue; + else + progress += Random::getSystemRandom().nextDouble() * maxIncrement; + + if (isPositiveAndNotGreaterThan (progress, 1.0)) + { + linearProgressBar.setPercentageDisplay (true); + circularProgressBar.setPercentageDisplay (true); + } + else + { + linearProgressBar.setTextToDisplay ("Linear progress bar"); + circularProgressBar.setTextToDisplay ("Circular progress bar"); + } + } + TextEditor textEditor1, textEditor2 { "Password", (juce_wchar) 0x2022 }; ComboBox comboBox { "Combo" }; + + double progress { 0.0 }; + ProgressBar linearProgressBar { progress }; + ProgressBar circularProgressBar { progress }; }; //============================================================================== diff --git a/examples/GUI/WindowsDemo.h b/examples/GUI/WindowsDemo.h index 1c703f92b459..4aff0d49be25 100644 --- a/examples/GUI/WindowsDemo.h +++ b/examples/GUI/WindowsDemo.h @@ -228,6 +228,9 @@ class WindowsDemo : public Component addAndMakeVisible (closeWindowsButton); closeWindowsButton.onClick = [this] { closeAllWindows(); }; + addAndMakeVisible (alertWindowResult); + alertWindowResult.setJustificationType (Justification::centred); + setSize (250, 250); } @@ -253,14 +256,27 @@ class WindowsDemo : public Component void resized() override { - Rectangle buttonSize (0, 0, 108, 28); + FlexBox flexBox; + flexBox.flexDirection = FlexBox::Direction::column; + flexBox.justifyContent = FlexBox::JustifyContent::center; + + constexpr auto buttonWidth = 108.0f; + constexpr auto componentHeight = 24.0f; + constexpr auto gap = 4.0f; + + flexBox.items.add (FlexItem { showWindowsButton }.withHeight (componentHeight) + .withMinWidth (buttonWidth) + .withAlignSelf (FlexItem::AlignSelf::center)); - Rectangle area ((getWidth() / 2) - (buttonSize.getWidth() / 2), - (getHeight() / 2) - buttonSize.getHeight(), - buttonSize.getWidth(), buttonSize.getHeight()); + flexBox.items.add (FlexItem{}.withHeight (gap)); + flexBox.items.add (FlexItem { closeWindowsButton }.withHeight (componentHeight) + .withMinWidth (buttonWidth) + .withAlignSelf (FlexItem::AlignSelf::center)); - showWindowsButton .setBounds (area.reduced (2)); - closeWindowsButton.setBounds (area.translated (0, buttonSize.getHeight()).reduced (2)); + flexBox.items.add (FlexItem{}.withHeight (gap)); + flexBox.items.add (FlexItem { alertWindowResult }.withHeight (componentHeight)); + + flexBox.performLayout (getLocalBounds()); } private: @@ -272,6 +288,7 @@ class WindowsDemo : public Component TextButton showWindowsButton { "Show Windows" }, closeWindowsButton { "Close Windows" }; + Label alertWindowResult { "Alert Window result" }; void showAllWindows() { @@ -280,6 +297,7 @@ class WindowsDemo : public Component showDocumentWindow (false); showDocumentWindow (true); showTransparentWindow(); + showAlertWindow(); showDialogWindow(); } @@ -289,6 +307,12 @@ class WindowsDemo : public Component window.deleteAndZero(); windows.clear(); + alertWindowResult.setText ("", dontSendNotification); + } + + static auto getDisplayArea() + { + return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea.reduced (20); } void showDialogWindow() @@ -333,8 +357,7 @@ class WindowsDemo : public Component | RectanglePlacement::yTop | RectanglePlacement::doNotResize); - auto result = placement.appliedTo (area, Desktop::getInstance().getDisplays() - .getPrimaryDisplay()->userArea.reduced (20)); + auto result = placement.appliedTo (area, getDisplayArea()); dw->setBounds (result); dw->setResizable (true, ! native); @@ -354,12 +377,113 @@ class WindowsDemo : public Component | RectanglePlacement::yBottom | RectanglePlacement::doNotResize); - auto result = placement.appliedTo (area, Desktop::getInstance().getDisplays() - .getPrimaryDisplay()->userArea.reduced (20)); + auto result = placement.appliedTo (area, getDisplayArea()); balls->setBounds (result); balls->setVisible (true); } + void showAlertWindow() + { + auto* alertWindow = new AlertWindow ("Alert Window", + "For more complex dialogs, you can easily add components to an AlertWindow, such as...", + MessageBoxIconType::InfoIcon); + windows.add (alertWindow); + + alertWindow->addTextBlock ("Text block"); + alertWindow->addComboBox ("Combo box", {"Combo box", "Item 2", "Item 3"}); + alertWindow->addTextEditor ("Text editor", "Text editor"); + alertWindow->addTextEditor ("Password", "password", "including for passwords", true); + alertWindowCustomComponent.emplace(); + alertWindow->addCustomComponent (&(*alertWindowCustomComponent)); + alertWindow->addTextBlock ("Progress bar"); + alertWindow->addProgressBarComponent (alertWindowCustomComponent->value, ProgressBar::Style::linear); + alertWindow->addProgressBarComponent (alertWindowCustomComponent->value, ProgressBar::Style::circular); + alertWindow->addTextBlock ("Press any button, or the escape key, to close the window"); + + enum AlertWindowResult + { + noButtonPressed, + button1Pressed, + button2Pressed + }; + + alertWindow->addButton ("Button 1", AlertWindowResult::button1Pressed); + alertWindow->addButton ("Button 2", AlertWindowResult::button2Pressed); + + RectanglePlacement placement { RectanglePlacement::yMid + | RectanglePlacement::xLeft + | RectanglePlacement::doNotResize }; + + alertWindow->setBounds (placement.appliedTo (alertWindow->getBounds(), getDisplayArea())); + + alertWindowResult.setText ("", dontSendNotification); + alertWindow->enterModalState (false, ModalCallbackFunction::create ([ref = SafePointer { this }] (int result) + { + if (ref == nullptr) + return; + + const auto text = [&] + { + switch (result) + { + case noButtonPressed: + return "Dismissed the Alert Window without pressing a button"; + case button1Pressed: + return "Dismissed the Alert Window using Button 1"; + case button2Pressed: + return "Dismissed the Alert Window using Button 2"; + } + + return "Unhandled event when dismissing the Alert Window"; + }(); + + ref->alertWindowResult.setText (text, dontSendNotification); + }), true); + } + + class AlertWindowCustomComponent : public Component, + private Slider::Listener + { + public: + AlertWindowCustomComponent() + { + slider.setRange (0.0, 1.0); + slider.setValue (0.5, NotificationType::dontSendNotification); + slider.addListener (this); + + addAndMakeVisible (label); + addAndMakeVisible (slider); + + setSize (200, 50); + } + + ~AlertWindowCustomComponent() override + { + slider.removeListener (this); + } + + void resized() override + { + auto bounds = getLocalBounds(); + label.setBounds (bounds.removeFromTop (getHeight() / 2)); + slider.setBounds (bounds); + } + + void sliderValueChanged (Slider*) override + { + value = slider.getValue(); + } + + double value { -1.0 }; + + private: + Label label { "Label", "Custom component" }; + Slider slider { Slider::SliderStyle::LinearHorizontal, + Slider::TextEntryBoxPosition::NoTextBox }; + }; + + std::optional alertWindowCustomComponent; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDemo) }; diff --git a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp index 0ee08357630f..ceb641a416ad 100644 --- a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp +++ b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.cpp @@ -367,71 +367,9 @@ void ProjucerLookAndFeel::drawTreeviewPlusMinusBox (Graphics& g, const Rectangle g.strokePath (getArrowPath (area, isOpen ? 2 : 1, false, Justification::centredRight), PathStrokeType (2.0f)); } -void ProjucerLookAndFeel::drawProgressBar (Graphics& g, ProgressBar& progressBar, - int width, int height, double progress, const String& textToShow) +ProgressBar::Style ProjucerLookAndFeel::getDefaultProgressBarStyle (const ProgressBar&) { - ignoreUnused (width, height, progress); - - const auto background = progressBar.findColour (ProgressBar::backgroundColourId); - const auto foreground = progressBar.findColour (defaultButtonBackgroundColourId); - - const auto sideLength = jmin (width, height); - - auto barBounds = progressBar.getLocalBounds().withSizeKeepingCentre (sideLength, sideLength).reduced (1).toFloat(); - - auto rotationInDegrees = static_cast ((Time::getMillisecondCounter() / 10) % 360); - auto normalisedRotation = rotationInDegrees / 360.0f; - - const auto rotationOffset = 22.5f; - const auto maxRotation = 315.0f; - - auto startInDegrees = rotationInDegrees; - auto endInDegrees = startInDegrees + rotationOffset; - - if (normalisedRotation >= 0.25f && normalisedRotation < 0.5f) - { - const auto rescaledRotation = (normalisedRotation * 4.0f) - 1.0f; - endInDegrees = startInDegrees + rotationOffset + (maxRotation * rescaledRotation); - } - else if (normalisedRotation >= 0.5f && normalisedRotation <= 1.0f) - { - endInDegrees = startInDegrees + rotationOffset + maxRotation; - const auto rescaledRotation = 1.0f - ((normalisedRotation * 2.0f) - 1.0f); - startInDegrees = endInDegrees - rotationOffset - (maxRotation * rescaledRotation); - } - - g.setColour (background); - Path arcPath2; - arcPath2.addCentredArc (barBounds.getCentreX(), - barBounds.getCentreY(), - barBounds.getWidth() * 0.5f, - barBounds.getHeight() * 0.5f, 0.0f, - 0.0f, - MathConstants::twoPi, - true); - g.strokePath (arcPath2, PathStrokeType (2.0f)); - - g.setColour (foreground); - Path arcPath; - arcPath.addCentredArc (barBounds.getCentreX(), - barBounds.getCentreY(), - barBounds.getWidth() * 0.5f, - barBounds.getHeight() * 0.5f, - 0.0f, - degreesToRadians (startInDegrees), - degreesToRadians (endInDegrees), - true); - - arcPath.applyTransform (AffineTransform::rotation (normalisedRotation * MathConstants::pi * 2.25f, - barBounds.getCentreX(), barBounds.getCentreY())); - g.strokePath (arcPath, PathStrokeType (2.0f)); - - if (textToShow.isNotEmpty()) - { - g.setColour (progressBar.findColour (TextButton::textColourOffId)); - g.setFont (Font (12.0f, 2)); - g.drawText (textToShow, barBounds, Justification::centred, false); - } + return ProgressBar::Style::circular; } //============================================================================== @@ -583,5 +521,6 @@ void ProjucerLookAndFeel::setupColours() setColour (TreeView::selectedItemBackgroundColourId, findColour (defaultHighlightColourId)); setColour (PopupMenu::highlightedBackgroundColourId, findColour (defaultHighlightColourId).withAlpha (0.75f)); setColour (PopupMenu::highlightedTextColourId, findColour (defaultHighlightedTextColourId)); + setColour (ProgressBar::foregroundColourId, findColour (defaultButtonBackgroundColourId)); setColour (0x1000440, /*LassoComponent::lassoFillColourId*/ findColour (defaultHighlightColourId).withAlpha (0.3f)); } diff --git a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h index c3a75da556a5..1338440206bb 100644 --- a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h +++ b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h @@ -74,7 +74,7 @@ class ProjucerLookAndFeel : public LookAndFeel_V4 void drawTreeviewPlusMinusBox (Graphics&, const Rectangle& area, Colour backgroundColour, bool isItemOpen, bool isMouseOver) override; - void drawProgressBar (Graphics&, ProgressBar&, int width, int height, double progress, const String& textToShow) override; + ProgressBar::Style getDefaultProgressBarStyle (const ProgressBar&) override; //============================================================================== static Path getArrowPath (Rectangle arrowZone, const int direction, diff --git a/modules/juce_gui_basics/components/juce_Component.h b/modules/juce_gui_basics/components/juce_Component.h index 5236cfcf0b6e..e5ca03d91743 100644 --- a/modules/juce_gui_basics/components/juce_Component.h +++ b/modules/juce_gui_basics/components/juce_Component.h @@ -2128,13 +2128,14 @@ class JUCE_API Component : public MouseListener The callback is an optional object which will receive a callback when the modal component loses its modal status, either by being hidden or when exitModalState() is called. If you pass an object in here, the system will take care of deleting it - later, after making the callback + later, after making the callback. If deleteWhenDismissed is true, then when it is dismissed, the component will be deleted and then the callback will be called. (This will safely handle the situation where the component is deleted before its exitModalState() method is called). - @see exitModalState, runModalLoop, ModalComponentManager::attachCallback + @see exitModalState, runModalLoop, ModalComponentManager::attachCallback, + ModalCallbackFunction */ void enterModalState (bool takeKeyboardFocus = true, ModalComponentManager::Callback* callback = nullptr, diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 47ac2c87fa91..9cf2b16b42c2 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -615,6 +615,11 @@ bool LookAndFeel_V2::isProgressBarOpaque (ProgressBar& progressBar) return progressBar.findColour (ProgressBar::backgroundColourId).isOpaque(); } +ProgressBar::Style LookAndFeel_V2::getDefaultProgressBarStyle (const ProgressBar&) +{ + return ProgressBar::Style::linear; +} + bool LookAndFeel_V2::areScrollbarButtonsVisible() { return true; diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h index 8be0fd052cb7..68174244eacb 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -97,6 +97,7 @@ class JUCE_API LookAndFeel_V2 : public LookAndFeel void drawProgressBar (Graphics&, ProgressBar&, int width, int height, double progress, const String& textToShow) override; void drawSpinningWaitAnimation (Graphics&, const Colour& colour, int x, int y, int w, int h) override; bool isProgressBarOpaque (ProgressBar&) override; + ProgressBar::Style getDefaultProgressBarStyle (const ProgressBar&) override; //============================================================================== bool areScrollbarButtonsVisible() override; diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp index 306acaf6fe04..f6accad940cf 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp @@ -482,17 +482,30 @@ Font LookAndFeel_V4::getAlertWindowFont() { return { 14.0f }; } //============================================================================== void LookAndFeel_V4::drawProgressBar (Graphics& g, ProgressBar& progressBar, - int width, int height, double progress, const String& textToShow) + int width, int height, double progress, + const String& textToShow) { - if (width == height) - drawCircularProgressBar (g, progressBar, textToShow); - else - drawLinearProgressBar (g, progressBar, width, height, progress, textToShow); + switch (progressBar.getResolvedStyle()) + { + case ProgressBar::Style::linear: + drawLinearProgressBar (g, progressBar, width, height, progress, textToShow); + break; + + case ProgressBar::Style::circular: + drawCircularProgressBar (g, progressBar, textToShow); + break; + } +} + +ProgressBar::Style LookAndFeel_V4::getDefaultProgressBarStyle (const ProgressBar& progressBar) +{ + return progressBar.getWidth() == progressBar.getHeight() ? ProgressBar::Style::circular + : ProgressBar::Style::linear; } -void LookAndFeel_V4::drawLinearProgressBar (Graphics& g, ProgressBar& progressBar, - int width, int height, - double progress, const String& textToShow) +void LookAndFeel_V4::drawLinearProgressBar (Graphics& g, const ProgressBar& progressBar, + int width, int height, double progress, + const String& textToShow) { auto background = progressBar.findColour (ProgressBar::backgroundColourId); auto foreground = progressBar.findColour (ProgressBar::foregroundColourId); @@ -549,18 +562,20 @@ void LookAndFeel_V4::drawLinearProgressBar (Graphics& g, ProgressBar& progressBa } } -void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progressBar, const String& progressText) +void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, const ProgressBar& progressBar, + const String& textToShow) { - auto background = progressBar.findColour (ProgressBar::backgroundColourId); - auto foreground = progressBar.findColour (ProgressBar::foregroundColourId); + const auto background = progressBar.findColour (ProgressBar::backgroundColourId); + const auto foreground = progressBar.findColour (ProgressBar::foregroundColourId); - auto barBounds = progressBar.getLocalBounds().reduced (2, 2).toFloat(); + const auto barBounds = progressBar.getLocalBounds().reduced (2, 2).toFloat(); + const auto size = jmin (barBounds.getWidth(), barBounds.getHeight()); - auto rotationInDegrees = static_cast ((Time::getMillisecondCounter() / 10) % 360); - auto normalisedRotation = rotationInDegrees / 360.0f; + const auto rotationInDegrees = static_cast ((Time::getMillisecondCounter() / 10) % 360); + const auto normalisedRotation = rotationInDegrees / 360.0f; - auto rotationOffset = 22.5f; - auto maxRotation = 315.0f; + constexpr auto rotationOffset = 22.5f; + constexpr auto maxRotation = 315.0f; auto startInDegrees = rotationInDegrees; auto endInDegrees = startInDegrees + rotationOffset; @@ -581,8 +596,8 @@ void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progress Path arcPath2; arcPath2.addCentredArc (barBounds.getCentreX(), barBounds.getCentreY(), - barBounds.getWidth() * 0.5f, - barBounds.getHeight() * 0.5f, 0.0f, + size * 0.5f, + size * 0.5f, 0.0f, 0.0f, MathConstants::twoPi, true); @@ -592,8 +607,8 @@ void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progress Path arcPath; arcPath.addCentredArc (barBounds.getCentreX(), barBounds.getCentreY(), - barBounds.getWidth() * 0.5f, - barBounds.getHeight() * 0.5f, + size * 0.5f, + size * 0.5f, 0.0f, degreesToRadians (startInDegrees), degreesToRadians (endInDegrees), @@ -602,11 +617,11 @@ void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progress arcPath.applyTransform (AffineTransform::rotation (normalisedRotation * MathConstants::pi * 2.25f, barBounds.getCentreX(), barBounds.getCentreY())); g.strokePath (arcPath, PathStrokeType (4.0f)); - if (progressText.isNotEmpty()) + if (textToShow.isNotEmpty()) { g.setColour (progressBar.findColour (TextButton::textColourOffId)); g.setFont ({ 12.0f, Font::italic }); - g.drawText (progressText, barBounds, Justification::centred, false); + g.drawText (textToShow, barBounds, Justification::centred, false); } } diff --git a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h index f2072f198273..cbf1c61d3575 100644 --- a/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h +++ b/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.h @@ -140,8 +140,9 @@ class JUCE_API LookAndFeel_V4 : public LookAndFeel_V3 Font getAlertWindowFont() override; //============================================================================== - void drawProgressBar (Graphics&, ProgressBar&, int width, int height, double progress, const String& textToShow) override; + void drawProgressBar (Graphics&, ProgressBar&, int width, int height, double progress, const String&) override; bool isProgressBarOpaque (ProgressBar&) override { return false; } + ProgressBar::Style getDefaultProgressBarStyle (const ProgressBar&) override; //============================================================================== int getDefaultScrollbarWidth() override; @@ -242,9 +243,10 @@ class JUCE_API LookAndFeel_V4 : public LookAndFeel_V3 private: //============================================================================== - void drawLinearProgressBar (Graphics&, ProgressBar&, int width, int height, double progress, const String&); - void drawCircularProgressBar (Graphics&, ProgressBar&, const String&); + static void drawLinearProgressBar (Graphics&, const ProgressBar&, int, int, double, const String&); + static void drawCircularProgressBar (Graphics&, const ProgressBar&, const String&); + //============================================================================== int getPropertyComponentIndent (PropertyComponent&); //============================================================================== diff --git a/modules/juce_gui_basics/widgets/juce_ProgressBar.cpp b/modules/juce_gui_basics/widgets/juce_ProgressBar.cpp index 7fe373cf65d5..5ca85b798ef1 100644 --- a/modules/juce_gui_basics/widgets/juce_ProgressBar.cpp +++ b/modules/juce_gui_basics/widgets/juce_ProgressBar.cpp @@ -26,15 +26,14 @@ namespace juce { -ProgressBar::ProgressBar (double& progress_) - : progress (progress_), - displayPercentage (true), - lastCallbackTime (0) +ProgressBar::ProgressBar (double& progress_, std::optional