@@ -461,7 +461,8 @@ private: | |||||
//============================================================================== | //============================================================================== | ||||
struct MiscPage : public Component | |||||
struct MiscPage : public Component, | |||||
private Timer | |||||
{ | { | ||||
MiscPage() | MiscPage() | ||||
{ | { | ||||
@@ -482,6 +483,21 @@ struct MiscPage : public Component | |||||
comboBox.addItem ("combo box item " + String (i), i); | comboBox.addItem ("combo box item " + String (i), i); | ||||
comboBox.setSelectedId (1); | 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 | void lookAndFeelChanged() override | ||||
@@ -490,10 +506,37 @@ struct MiscPage : public Component | |||||
textEditor2.applyFontToAllText (textEditor2.getFont()); | 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, | TextEditor textEditor1, | ||||
textEditor2 { "Password", (juce_wchar) 0x2022 }; | textEditor2 { "Password", (juce_wchar) 0x2022 }; | ||||
ComboBox comboBox { "Combo" }; | ComboBox comboBox { "Combo" }; | ||||
double progress { 0.0 }; | |||||
ProgressBar linearProgressBar { progress }; | |||||
ProgressBar circularProgressBar { progress }; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -228,6 +228,9 @@ public: | |||||
addAndMakeVisible (closeWindowsButton); | addAndMakeVisible (closeWindowsButton); | ||||
closeWindowsButton.onClick = [this] { closeAllWindows(); }; | closeWindowsButton.onClick = [this] { closeAllWindows(); }; | ||||
addAndMakeVisible (alertWindowResult); | |||||
alertWindowResult.setJustificationType (Justification::centred); | |||||
setSize (250, 250); | setSize (250, 250); | ||||
} | } | ||||
@@ -253,14 +256,27 @@ public: | |||||
void resized() override | void resized() override | ||||
{ | { | ||||
Rectangle<int> 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<int> 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: | private: | ||||
@@ -272,6 +288,7 @@ private: | |||||
TextButton showWindowsButton { "Show Windows" }, | TextButton showWindowsButton { "Show Windows" }, | ||||
closeWindowsButton { "Close Windows" }; | closeWindowsButton { "Close Windows" }; | ||||
Label alertWindowResult { "Alert Window result" }; | |||||
void showAllWindows() | void showAllWindows() | ||||
{ | { | ||||
@@ -280,6 +297,7 @@ private: | |||||
showDocumentWindow (false); | showDocumentWindow (false); | ||||
showDocumentWindow (true); | showDocumentWindow (true); | ||||
showTransparentWindow(); | showTransparentWindow(); | ||||
showAlertWindow(); | |||||
showDialogWindow(); | showDialogWindow(); | ||||
} | } | ||||
@@ -289,6 +307,12 @@ private: | |||||
window.deleteAndZero(); | window.deleteAndZero(); | ||||
windows.clear(); | windows.clear(); | ||||
alertWindowResult.setText ("", dontSendNotification); | |||||
} | |||||
static auto getDisplayArea() | |||||
{ | |||||
return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea.reduced (20); | |||||
} | } | ||||
void showDialogWindow() | void showDialogWindow() | ||||
@@ -333,8 +357,7 @@ private: | |||||
| RectanglePlacement::yTop | | RectanglePlacement::yTop | ||||
| RectanglePlacement::doNotResize); | | RectanglePlacement::doNotResize); | ||||
auto result = placement.appliedTo (area, Desktop::getInstance().getDisplays() | |||||
.getPrimaryDisplay()->userArea.reduced (20)); | |||||
auto result = placement.appliedTo (area, getDisplayArea()); | |||||
dw->setBounds (result); | dw->setBounds (result); | ||||
dw->setResizable (true, ! native); | dw->setResizable (true, ! native); | ||||
@@ -354,12 +377,113 @@ private: | |||||
| RectanglePlacement::yBottom | | RectanglePlacement::yBottom | ||||
| RectanglePlacement::doNotResize); | | RectanglePlacement::doNotResize); | ||||
auto result = placement.appliedTo (area, Desktop::getInstance().getDisplays() | |||||
.getPrimaryDisplay()->userArea.reduced (20)); | |||||
auto result = placement.appliedTo (area, getDisplayArea()); | |||||
balls->setBounds (result); | balls->setBounds (result); | ||||
balls->setVisible (true); | 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> alertWindowCustomComponent; | |||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDemo) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDemo) | ||||
}; | }; |
@@ -367,71 +367,9 @@ void ProjucerLookAndFeel::drawTreeviewPlusMinusBox (Graphics& g, const Rectangle | |||||
g.strokePath (getArrowPath (area, isOpen ? 2 : 1, false, Justification::centredRight), PathStrokeType (2.0f)); | 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<float> ((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<float>::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<float>::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 (TreeView::selectedItemBackgroundColourId, findColour (defaultHighlightColourId)); | ||||
setColour (PopupMenu::highlightedBackgroundColourId, findColour (defaultHighlightColourId).withAlpha (0.75f)); | setColour (PopupMenu::highlightedBackgroundColourId, findColour (defaultHighlightColourId).withAlpha (0.75f)); | ||||
setColour (PopupMenu::highlightedTextColourId, findColour (defaultHighlightedTextColourId)); | setColour (PopupMenu::highlightedTextColourId, findColour (defaultHighlightedTextColourId)); | ||||
setColour (ProgressBar::foregroundColourId, findColour (defaultButtonBackgroundColourId)); | |||||
setColour (0x1000440, /*LassoComponent::lassoFillColourId*/ findColour (defaultHighlightColourId).withAlpha (0.3f)); | setColour (0x1000440, /*LassoComponent::lassoFillColourId*/ findColour (defaultHighlightColourId).withAlpha (0.3f)); | ||||
} | } |
@@ -74,7 +74,7 @@ public: | |||||
void drawTreeviewPlusMinusBox (Graphics&, const Rectangle<float>& area, | void drawTreeviewPlusMinusBox (Graphics&, const Rectangle<float>& area, | ||||
Colour backgroundColour, bool isItemOpen, bool isMouseOver) override; | 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<float> arrowZone, const int direction, | static Path getArrowPath (Rectangle<float> arrowZone, const int direction, | ||||
@@ -2128,13 +2128,14 @@ public: | |||||
The callback is an optional object which will receive a callback when the modal | 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() | 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 | 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 | 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 | 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). | 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, | void enterModalState (bool takeKeyboardFocus = true, | ||||
ModalComponentManager::Callback* callback = nullptr, | ModalComponentManager::Callback* callback = nullptr, | ||||
@@ -615,6 +615,11 @@ bool LookAndFeel_V2::isProgressBarOpaque (ProgressBar& progressBar) | |||||
return progressBar.findColour (ProgressBar::backgroundColourId).isOpaque(); | return progressBar.findColour (ProgressBar::backgroundColourId).isOpaque(); | ||||
} | } | ||||
ProgressBar::Style LookAndFeel_V2::getDefaultProgressBarStyle (const ProgressBar&) | |||||
{ | |||||
return ProgressBar::Style::linear; | |||||
} | |||||
bool LookAndFeel_V2::areScrollbarButtonsVisible() | bool LookAndFeel_V2::areScrollbarButtonsVisible() | ||||
{ | { | ||||
return true; | return true; | ||||
@@ -97,6 +97,7 @@ public: | |||||
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& textToShow) override; | ||||
void drawSpinningWaitAnimation (Graphics&, const Colour& colour, int x, int y, int w, int h) override; | void drawSpinningWaitAnimation (Graphics&, const Colour& colour, int x, int y, int w, int h) override; | ||||
bool isProgressBarOpaque (ProgressBar&) override; | bool isProgressBarOpaque (ProgressBar&) override; | ||||
ProgressBar::Style getDefaultProgressBarStyle (const ProgressBar&) override; | |||||
//============================================================================== | //============================================================================== | ||||
bool areScrollbarButtonsVisible() override; | bool areScrollbarButtonsVisible() override; | ||||
@@ -482,17 +482,30 @@ Font LookAndFeel_V4::getAlertWindowFont() { return { 14.0f }; } | |||||
//============================================================================== | //============================================================================== | ||||
void LookAndFeel_V4::drawProgressBar (Graphics& g, ProgressBar& progressBar, | 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 background = progressBar.findColour (ProgressBar::backgroundColourId); | ||||
auto foreground = progressBar.findColour (ProgressBar::foregroundColourId); | 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<float> ((Time::getMillisecondCounter() / 10) % 360); | |||||
auto normalisedRotation = rotationInDegrees / 360.0f; | |||||
const auto rotationInDegrees = static_cast<float> ((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 startInDegrees = rotationInDegrees; | ||||
auto endInDegrees = startInDegrees + rotationOffset; | auto endInDegrees = startInDegrees + rotationOffset; | ||||
@@ -581,8 +596,8 @@ void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progress | |||||
Path arcPath2; | Path arcPath2; | ||||
arcPath2.addCentredArc (barBounds.getCentreX(), | arcPath2.addCentredArc (barBounds.getCentreX(), | ||||
barBounds.getCentreY(), | barBounds.getCentreY(), | ||||
barBounds.getWidth() * 0.5f, | |||||
barBounds.getHeight() * 0.5f, 0.0f, | |||||
size * 0.5f, | |||||
size * 0.5f, 0.0f, | |||||
0.0f, | 0.0f, | ||||
MathConstants<float>::twoPi, | MathConstants<float>::twoPi, | ||||
true); | true); | ||||
@@ -592,8 +607,8 @@ void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progress | |||||
Path arcPath; | Path arcPath; | ||||
arcPath.addCentredArc (barBounds.getCentreX(), | arcPath.addCentredArc (barBounds.getCentreX(), | ||||
barBounds.getCentreY(), | barBounds.getCentreY(), | ||||
barBounds.getWidth() * 0.5f, | |||||
barBounds.getHeight() * 0.5f, | |||||
size * 0.5f, | |||||
size * 0.5f, | |||||
0.0f, | 0.0f, | ||||
degreesToRadians (startInDegrees), | degreesToRadians (startInDegrees), | ||||
degreesToRadians (endInDegrees), | degreesToRadians (endInDegrees), | ||||
@@ -602,11 +617,11 @@ void LookAndFeel_V4::drawCircularProgressBar (Graphics& g, ProgressBar& progress | |||||
arcPath.applyTransform (AffineTransform::rotation (normalisedRotation * MathConstants<float>::pi * 2.25f, barBounds.getCentreX(), barBounds.getCentreY())); | arcPath.applyTransform (AffineTransform::rotation (normalisedRotation * MathConstants<float>::pi * 2.25f, barBounds.getCentreX(), barBounds.getCentreY())); | ||||
g.strokePath (arcPath, PathStrokeType (4.0f)); | g.strokePath (arcPath, PathStrokeType (4.0f)); | ||||
if (progressText.isNotEmpty()) | |||||
if (textToShow.isNotEmpty()) | |||||
{ | { | ||||
g.setColour (progressBar.findColour (TextButton::textColourOffId)); | g.setColour (progressBar.findColour (TextButton::textColourOffId)); | ||||
g.setFont ({ 12.0f, Font::italic }); | g.setFont ({ 12.0f, Font::italic }); | ||||
g.drawText (progressText, barBounds, Justification::centred, false); | |||||
g.drawText (textToShow, barBounds, Justification::centred, false); | |||||
} | } | ||||
} | } | ||||
@@ -140,8 +140,9 @@ public: | |||||
Font getAlertWindowFont() override; | 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; } | bool isProgressBarOpaque (ProgressBar&) override { return false; } | ||||
ProgressBar::Style getDefaultProgressBarStyle (const ProgressBar&) override; | |||||
//============================================================================== | //============================================================================== | ||||
int getDefaultScrollbarWidth() override; | int getDefaultScrollbarWidth() override; | ||||
@@ -242,9 +243,10 @@ public: | |||||
private: | 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&); | int getPropertyComponentIndent (PropertyComponent&); | ||||
//============================================================================== | //============================================================================== | ||||
@@ -26,15 +26,14 @@ | |||||
namespace juce | namespace juce | ||||
{ | { | ||||
ProgressBar::ProgressBar (double& progress_) | |||||
: progress (progress_), | |||||
displayPercentage (true), | |||||
lastCallbackTime (0) | |||||
ProgressBar::ProgressBar (double& progress_, std::optional<Style> style_) | |||||
: progress { progress_ }, | |||||
style { style_ } | |||||
{ | { | ||||
currentValue = jlimit (0.0, 1.0, progress); | |||||
} | } | ||||
ProgressBar::~ProgressBar() | |||||
ProgressBar::ProgressBar (double& progress_) | |||||
: progress { progress_ } | |||||
{ | { | ||||
} | } | ||||
@@ -51,6 +50,17 @@ void ProgressBar::setTextToDisplay (const String& text) | |||||
displayedMessage = text; | displayedMessage = text; | ||||
} | } | ||||
void ProgressBar::setStyle (std::optional<Style> newStyle) | |||||
{ | |||||
style = newStyle; | |||||
repaint(); | |||||
} | |||||
ProgressBar::Style ProgressBar::getResolvedStyle() const | |||||
{ | |||||
return style.value_or (getLookAndFeel().getDefaultProgressBarStyle (*this)); | |||||
} | |||||
void ProgressBar::lookAndFeelChanged() | void ProgressBar::lookAndFeelChanged() | ||||
{ | { | ||||
setOpaque (getLookAndFeel().isProgressBarOpaque (*this)); | setOpaque (getLookAndFeel().isProgressBarOpaque (*this)); | ||||
@@ -76,9 +86,11 @@ void ProgressBar::paint (Graphics& g) | |||||
text = displayedMessage; | text = displayedMessage; | ||||
} | } | ||||
getLookAndFeel().drawProgressBar (g, *this, | |||||
getWidth(), getHeight(), | |||||
currentValue, text); | |||||
const auto w = getWidth(); | |||||
const auto h = getHeight(); | |||||
const auto v = currentValue; | |||||
getLookAndFeel().drawProgressBar (g, *this, w, h, v, text); | |||||
} | } | ||||
void ProgressBar::visibilityChanged() | void ProgressBar::visibilityChanged() | ||||
@@ -30,16 +30,14 @@ namespace juce | |||||
/** | /** | ||||
A progress bar component. | A progress bar component. | ||||
To use this, just create one and make it visible. It'll run its own timer | |||||
to keep an eye on a variable that you give it, and will automatically | |||||
redraw itself when the variable changes. | |||||
To use this, just create one and make it visible. It'll run its own timer to keep an eye on a | |||||
variable that you give it, and will automatically redraw itself when the variable changes. | |||||
If using LookAndFeel_V4 a circular spinning progress bar will be drawn if | |||||
the width and height of the ProgressBar are equal, otherwise the standard, | |||||
linear ProgressBar will be drawn. | |||||
Two styles of progress bars are supported: circular, and linear bar. If a style isn't given the | |||||
look-and-feel will determine the style based on getDefaultProgressBarStyle(). | |||||
For an easy way of running a background task with a dialog box showing its | |||||
progress, see the ThreadWithProgressWindow class. | |||||
For an easy way of running a background task with a dialog box showing its progress, see | |||||
the ThreadWithProgressWindow class. | |||||
@see ThreadWithProgressWindow | @see ThreadWithProgressWindow | ||||
@@ -50,9 +48,21 @@ class JUCE_API ProgressBar : public Component, | |||||
private Timer | private Timer | ||||
{ | { | ||||
public: | public: | ||||
/** The types of ProgressBar styles available. | |||||
@see setStyle, getStyle, getResolvedStyle | |||||
*/ | |||||
enum class Style | |||||
{ | |||||
linear, /**< A linear progress bar. */ | |||||
circular, /**< A circular progress indicator. */ | |||||
}; | |||||
//============================================================================== | //============================================================================== | ||||
/** Creates a ProgressBar. | /** Creates a ProgressBar. | ||||
The ProgressBar's style will initially be determined by the look-and-feel. | |||||
@param progress pass in a reference to a double that you're going to | @param progress pass in a reference to a double that you're going to | ||||
update with your task's progress. The ProgressBar will | update with your task's progress. The ProgressBar will | ||||
monitor the value of this variable and will redraw itself | monitor the value of this variable and will redraw itself | ||||
@@ -63,8 +73,21 @@ public: | |||||
*/ | */ | ||||
explicit ProgressBar (double& progress); | explicit ProgressBar (double& progress); | ||||
/** Creates a ProgressBar with a specific style. | |||||
@param progress pass in a reference to a double that you're going to | |||||
update with your task's progress. The ProgressBar will | |||||
monitor the value of this variable and will redraw itself | |||||
when the value changes. The range is from 0 to 1.0 and JUCE | |||||
LookAndFeel classes will draw a spinning animation for values | |||||
outside this range. Obviously you'd better be careful not to | |||||
delete this variable while the ProgressBar still exists! | |||||
@param style the style of the progress bar. | |||||
*/ | |||||
ProgressBar (double& progress, std::optional<Style> style); | |||||
/** Destructor. */ | /** Destructor. */ | ||||
~ProgressBar() override; | |||||
~ProgressBar() override = default; | |||||
//============================================================================== | //============================================================================== | ||||
/** Turns the percentage display on or off. | /** Turns the percentage display on or off. | ||||
@@ -81,6 +104,32 @@ public: | |||||
*/ | */ | ||||
void setTextToDisplay (const String& text); | void setTextToDisplay (const String& text); | ||||
/** Sets the progress bar's current style. | |||||
You can use this to force getResolvedStyle() to return a particular value. | |||||
If a non-nullopt style is passed, that style will always be returned by | |||||
getResolvedStyle(). Otherwise, if nullopt is passed, getResolvedStyle() will | |||||
return its LookAndFeel's getDefaultProgressBarStyle(). | |||||
@see getStyle, getResolvedStyle | |||||
*/ | |||||
void setStyle (std::optional<Style> newStyle); | |||||
/** Returns the progress bar's current style, as set in the constructor or in setStyle(). | |||||
@see setStyle, getResolvedStyle | |||||
*/ | |||||
std::optional<Style> getStyle() const { return style; } | |||||
/** Returns the progress bar's current style if it has one, or a default style determined by | |||||
the look-and-feel if it doesn't. | |||||
Use this function in overrides of LookAndFeelMethods::drawProgressBar() in order to | |||||
determine which style to draw. | |||||
@see getStyle, setStyle, LookAndFeelMethods::getDefaultProgressBarStyle | |||||
*/ | |||||
Style getResolvedStyle() const; | |||||
//============================================================================== | //============================================================================== | ||||
/** A set of colour IDs to use to change the colour of various aspects of the bar. | /** A set of colour IDs to use to change the colour of various aspects of the bar. | ||||
@@ -109,12 +158,22 @@ public: | |||||
bar that fills the whole space (i.e. to say that the app is still busy but the progress | bar that fills the whole space (i.e. to say that the app is still busy but the progress | ||||
isn't known). It can use the current time as a basis for playing an animation. | isn't known). It can use the current time as a basis for playing an animation. | ||||
To determine which style of progress-bar to draw call getResolvedStyle(). | |||||
(Used by progress bars in AlertWindow). | (Used by progress bars in AlertWindow). | ||||
@see getResolvedStyle | |||||
*/ | */ | ||||
virtual void drawProgressBar (Graphics&, ProgressBar&, int width, int height, | virtual void drawProgressBar (Graphics&, ProgressBar&, int width, int height, | ||||
double progress, const String& textToShow) = 0; | double progress, const String& textToShow) = 0; | ||||
virtual bool isProgressBarOpaque (ProgressBar&) = 0; | virtual bool isProgressBarOpaque (ProgressBar&) = 0; | ||||
/** Returns the default style a progress bar should use if one hasn't been set. | |||||
@see setStyle, getResolvedStyle | |||||
*/ | |||||
virtual Style getDefaultProgressBarStyle (const ProgressBar&) = 0; | |||||
}; | }; | ||||
/** @internal */ | /** @internal */ | ||||
@@ -133,10 +192,11 @@ protected: | |||||
private: | private: | ||||
double& progress; | double& progress; | ||||
double currentValue; | |||||
bool displayPercentage; | |||||
std::optional<Style> style; | |||||
double currentValue { jlimit (0.0, 1.0, progress) }; | |||||
bool displayPercentage { true }; | |||||
String displayedMessage, currentMessage; | String displayedMessage, currentMessage; | ||||
uint32 lastCallbackTime; | |||||
uint32 lastCallbackTime { 0 }; | |||||
void timerCallback() override; | void timerCallback() override; | ||||
@@ -294,9 +294,9 @@ void AlertWindow::addTextBlock (const String& textBlock) | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
void AlertWindow::addProgressBarComponent (double& progressValue) | |||||
void AlertWindow::addProgressBarComponent (double& progressValue, std::optional<ProgressBar::Style> style) | |||||
{ | { | ||||
auto* pb = new ProgressBar (progressValue); | |||||
auto* pb = new ProgressBar (progressValue, style); | |||||
progressBars.add (pb); | progressBars.add (pb); | ||||
allComps.add (pb); | allComps.add (pb); | ||||
addAndMakeVisible (pb); | addAndMakeVisible (pb); | ||||
@@ -33,11 +33,11 @@ namespace juce | |||||
some static methods for running these. | some static methods for running these. | ||||
For more complex dialogs, an AlertWindow can be created, then it can have some | For more complex dialogs, an AlertWindow can be created, then it can have some | ||||
buttons and components added to it, and its runModalLoop() method is then used to | |||||
show it. The value returned by runModalLoop() shows which button the | |||||
user pressed to dismiss the box. | |||||
buttons and components added to it, and its enterModalState() method is used to | |||||
show it. The value returned to the ModalComponentManager::Callback shows | |||||
which button the user pressed to dismiss the box. | |||||
@see ThreadWithProgressWindow | |||||
@see ThreadWithProgressWindow, Component::enterModalState | |||||
@tags{GUI} | @tags{GUI} | ||||
*/ | */ | ||||
@@ -188,8 +188,13 @@ public: | |||||
@param progressValue a variable that will be repeatedly checked while the | @param progressValue a variable that will be repeatedly checked while the | ||||
dialog box is visible, to see how far the process has | dialog box is visible, to see how far the process has | ||||
got. The value should be in the range 0 to 1.0 | got. The value should be in the range 0 to 1.0 | ||||
@param style determines the style the ProgressBar should adopt. | |||||
By default this use a style automatically chosen by | |||||
the LookAndFeel, but you can force a particular style | |||||
by passing a non-optional value. | |||||
@see ProgressBar::setStyle | |||||
*/ | */ | ||||
void addProgressBarComponent (double& progressValue); | |||||
void addProgressBarComponent (double& progressValue, std::optional<ProgressBar::Style> style = std::nullopt); | |||||
//============================================================================== | //============================================================================== | ||||
/** Adds a user-defined component to the dialog box. | /** Adds a user-defined component to the dialog box. | ||||