diff --git a/examples/GUI/WidgetsDemo.h b/examples/GUI/WidgetsDemo.h index 4d524c0359..b1751a08b7 100644 --- a/examples/GUI/WidgetsDemo.h +++ b/examples/GUI/WidgetsDemo.h @@ -461,7 +461,8 @@ private: //============================================================================== -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 1c703f92b4..4aff0d49be 100644 --- a/examples/GUI/WindowsDemo.h +++ b/examples/GUI/WindowsDemo.h @@ -228,6 +228,9 @@ public: addAndMakeVisible (closeWindowsButton); closeWindowsButton.onClick = [this] { closeAllWindows(); }; + addAndMakeVisible (alertWindowResult); + alertWindowResult.setJustificationType (Justification::centred); + setSize (250, 250); } @@ -253,14 +256,27 @@ public: 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 @@ private: TextButton showWindowsButton { "Show Windows" }, closeWindowsButton { "Close Windows" }; + Label alertWindowResult { "Alert Window result" }; void showAllWindows() { @@ -280,6 +297,7 @@ private: showDocumentWindow (false); showDocumentWindow (true); showTransparentWindow(); + showAlertWindow(); showDialogWindow(); } @@ -289,6 +307,12 @@ private: window.deleteAndZero(); windows.clear(); + alertWindowResult.setText ("", dontSendNotification); + } + + static auto getDisplayArea() + { + return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea.reduced (20); } void showDialogWindow() @@ -333,8 +357,7 @@ private: | 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 @@ private: | 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 0ee0835763..ceb641a416 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 c3a75da556..1338440206 100644 --- a/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h +++ b/extras/Projucer/Source/Utility/UI/jucer_ProjucerLookAndFeel.h @@ -74,7 +74,7 @@ public: 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 5236cfcf0b..e5ca03d917 100644 --- a/modules/juce_gui_basics/components/juce_Component.h +++ b/modules/juce_gui_basics/components/juce_Component.h @@ -2128,13 +2128,14 @@ public: 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 47ac2c87fa..9cf2b16b42 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 8be0fd052c..68174244ea 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 @@ public: 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 306acaf6fe..f6accad940 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 f2072f1982..cbf1c61d35 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 @@ public: 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 @@ public: 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 7fe373cf65..5ca85b798e 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