|
- /*
- ==============================================================================
-
- This file is part of the JUCE examples.
- Copyright (c) 2022 - Raw Material Software Limited
-
- The code included in this file is provided under the terms of the ISC license
- http://www.isc.org/downloads/software-support-policy/isc-license. Permission
- To use, copy, modify, and/or distribute this software for any purpose with or
- without fee is hereby granted provided that the above copyright notice and
- this permission notice appear in all copies.
-
- THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
- WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
- PURPOSE, ARE DISCLAIMED.
-
- ==============================================================================
- */
-
- /*******************************************************************************
- The block below describes the properties of this PIP. A PIP is a short snippet
- of code that can be read by the Projucer and used to generate a JUCE project.
-
- BEGIN_JUCE_PIP_METADATA
-
- name: WindowsDemo
- version: 1.0.0
- vendor: JUCE
- website: http://juce.com
- description: Displays various types of windows.
-
- dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
- juce_gui_basics, juce_gui_extra
- exporters: xcode_mac, vs2022, linux_make, androidstudio, xcode_iphone
-
- moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
-
- type: Component
- mainClass: WindowsDemo
-
- useLocalCopy: 1
-
- END_JUCE_PIP_METADATA
-
- *******************************************************************************/
-
- #pragma once
-
- #include "../Assets/DemoUtilities.h"
-
- //==============================================================================
- /** Just a simple window that deletes itself when closed. */
- class BasicWindow final : public DocumentWindow
- {
- public:
- BasicWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
- : DocumentWindow (name, backgroundColour, buttonsNeeded)
- {}
-
- void closeButtonPressed()
- {
- delete this;
- }
-
- private:
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BasicWindow)
- };
-
- //==============================================================================
- /** This window contains a ColourSelector which can be used to change the window's colour. */
- class ColourSelectorWindow final : public DocumentWindow,
- private ChangeListener
- {
- public:
- ColourSelectorWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
- : DocumentWindow (name, backgroundColour, buttonsNeeded)
- {
- selector.setCurrentColour (backgroundColour);
- selector.setColour (ColourSelector::backgroundColourId, Colours::transparentWhite);
- selector.addChangeListener (this);
- setContentOwned (&selector, false);
- }
-
- ~ColourSelectorWindow()
- {
- selector.removeChangeListener (this);
- }
-
- void closeButtonPressed()
- {
- delete this;
- }
-
- private:
- ColourSelector selector { ColourSelector::showColourAtTop
- | ColourSelector::showSliders
- | ColourSelector::showColourspace };
-
- void changeListenerCallback (ChangeBroadcaster* source)
- {
- if (source == &selector)
- setBackgroundColour (selector.getCurrentColour());
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ColourSelectorWindow)
- };
-
- //==============================================================================
- class BouncingBallComponent final : public Component,
- public Timer
- {
- public:
- BouncingBallComponent()
- {
- setInterceptsMouseClicks (false, false);
-
- Random random;
-
- auto size = 10.0f + (float) random.nextInt (30);
-
- ballBounds.setBounds (random.nextFloat() * 100.0f,
- random.nextFloat() * 100.0f,
- size, size);
-
- direction.x = random.nextFloat() * 8.0f - 4.0f;
- direction.y = random.nextFloat() * 8.0f - 4.0f;
-
- colour = Colour ((juce::uint32) random.nextInt())
- .withAlpha (0.5f)
- .withBrightness (0.7f);
-
- startTimer (60);
- }
-
- void paint (Graphics& g) override
- {
- g.setColour (colour);
- g.fillEllipse (ballBounds - getPosition().toFloat());
- }
-
- void timerCallback() override
- {
- ballBounds += direction;
-
- if (ballBounds.getX() < 0) direction.x = std::abs (direction.x);
- if (ballBounds.getY() < 0) direction.y = std::abs (direction.y);
- if (ballBounds.getRight() > (float) getParentWidth()) direction.x = -std::abs (direction.x);
- if (ballBounds.getBottom() > (float) getParentHeight()) direction.y = -std::abs (direction.y);
-
- setBounds (ballBounds.getSmallestIntegerContainer());
- }
-
- private:
- Colour colour;
- Rectangle<float> ballBounds;
- Point<float> direction;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallComponent)
- };
-
- //==============================================================================
- class BouncingBallsContainer final : public Component
- {
- public:
- BouncingBallsContainer (int numBalls)
- {
- for (int i = 0; i < numBalls; ++i)
- {
- auto* newBall = new BouncingBallComponent();
- balls.add (newBall);
- addAndMakeVisible (newBall);
- }
- }
-
- void mouseDown (const MouseEvent& e) override
- {
- dragger.startDraggingComponent (this, e);
- }
-
- void mouseDrag (const MouseEvent& e) override
- {
- // as there's no titlebar we have to manage the dragging ourselves
- dragger.dragComponent (this, e, nullptr);
- }
-
- void paint (Graphics& g) override
- {
- if (isOpaque())
- g.fillAll (Colours::white);
- else
- g.fillAll (Colours::blue.withAlpha (0.2f));
-
- g.setFont (16.0f);
- g.setColour (Colours::black);
- g.drawFittedText ("This window has no titlebar and a transparent background.",
- getLocalBounds().reduced (8, 0),
- Justification::centred, 5);
-
- g.drawRect (getLocalBounds());
- }
-
- private:
- ComponentDragger dragger;
- OwnedArray<BouncingBallComponent> balls;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BouncingBallsContainer)
- };
-
- //==============================================================================
- class WindowsDemo final : public Component
- {
- public:
- enum Windows
- {
- dialog,
- document,
- alert,
- numWindows
- };
-
- WindowsDemo()
- {
- setOpaque (true);
-
- addAndMakeVisible (showWindowsButton);
- showWindowsButton.onClick = [this] { showAllWindows(); };
-
- addAndMakeVisible (closeWindowsButton);
- closeWindowsButton.onClick = [this] { closeAllWindows(); };
-
- addAndMakeVisible (alertWindowResult);
- alertWindowResult.setJustificationType (Justification::centred);
-
- setSize (250, 250);
- }
-
- ~WindowsDemo() override
- {
- if (dialogWindow != nullptr)
- {
- dialogWindow->exitModalState (0);
-
- // we are shutting down: can't wait for the message manager
- // to eventually delete this
- delete dialogWindow;
- }
-
- closeAllWindows();
- }
-
- void paint (Graphics& g) override
- {
- g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground,
- Colours::grey));
- }
-
- void resized() override
- {
- 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));
-
- flexBox.items.add (FlexItem{}.withHeight (gap));
- flexBox.items.add (FlexItem { closeWindowsButton }.withHeight (componentHeight)
- .withMinWidth (buttonWidth)
- .withAlignSelf (FlexItem::AlignSelf::center));
-
- flexBox.items.add (FlexItem{}.withHeight (gap));
- flexBox.items.add (FlexItem { alertWindowResult }.withHeight (componentHeight));
-
- flexBox.performLayout (getLocalBounds());
- }
-
- private:
- // Because in this demo the windows delete themselves, we'll use the
- // Component::SafePointer class to point to them, which automatically becomes
- // null when the component that it points to is deleted.
- Array<Component::SafePointer<Component>> windows;
- SafePointer<DialogWindow> dialogWindow;
-
- TextButton showWindowsButton { "Show Windows" },
- closeWindowsButton { "Close Windows" };
- Label alertWindowResult { "Alert Window result" };
-
- void showAllWindows()
- {
- closeAllWindows();
-
- showDocumentWindow (false);
- showDocumentWindow (true);
- showTransparentWindow();
- showAlertWindow();
- showDialogWindow();
- }
-
- void closeAllWindows()
- {
- for (auto& window : windows)
- window.deleteAndZero();
-
- windows.clear();
- alertWindowResult.setText ("", dontSendNotification);
- }
-
- static auto getDisplayArea()
- {
- return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea.reduced (20);
- }
-
- void showDialogWindow()
- {
- String m;
-
- m << "Dialog Windows can be used to quickly show a component, usually blocking mouse input to other windows." << newLine
- << newLine
- << "They can also be quickly closed with the escape key, try it now.";
-
- DialogWindow::LaunchOptions options;
- auto* label = new Label();
- label->setText (m, dontSendNotification);
- label->setColour (Label::textColourId, Colours::whitesmoke);
- options.content.setOwned (label);
-
- Rectangle<int> area (0, 0, 300, 200);
-
- options.content->setSize (area.getWidth(), area.getHeight());
-
- options.dialogTitle = "Dialog Window";
- options.dialogBackgroundColour = Colour (0xff0e345a);
- options.escapeKeyTriggersCloseButton = true;
- options.useNativeTitleBar = false;
- options.resizable = true;
-
- dialogWindow = options.launchAsync();
-
- if (dialogWindow != nullptr)
- dialogWindow->centreWithSize (300, 200);
- }
-
- void showDocumentWindow (bool native)
- {
- auto* dw = new ColourSelectorWindow ("Document Window", getRandomBrightColour(), DocumentWindow::allButtons);
- windows.add (dw);
-
- Rectangle<int> area (0, 0, 300, 400);
-
- RectanglePlacement placement ((native ? RectanglePlacement::xLeft
- : RectanglePlacement::xRight)
- | RectanglePlacement::yTop
- | RectanglePlacement::doNotResize);
-
- auto result = placement.appliedTo (area, getDisplayArea());
- dw->setBounds (result);
-
- dw->setResizable (true, ! native);
- dw->setUsingNativeTitleBar (native);
- dw->setVisible (true);
- }
-
- void showTransparentWindow()
- {
- auto* balls = new BouncingBallsContainer (3);
- balls->addToDesktop (ComponentPeer::windowIsTemporary);
- windows.add (balls);
-
- Rectangle<int> area (0, 0, 200, 200);
-
- RectanglePlacement placement (RectanglePlacement::xLeft
- | RectanglePlacement::yBottom
- | RectanglePlacement::doNotResize);
-
- 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 final : 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)
- };
|