migrating to the latest JUCE version
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,274 +1,271 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
CallOutBox::CallOutBox (Component& c, Rectangle<int> area, Component* const parent)
|
||||
: content (c)
|
||||
{
|
||||
addAndMakeVisible (content);
|
||||
|
||||
if (parent != nullptr)
|
||||
{
|
||||
parent->addChildComponent (this);
|
||||
updatePosition (area, parent->getLocalBounds());
|
||||
setVisible (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
updatePosition (area, Desktop::getInstance().getDisplays().getDisplayForRect (area)->userArea);
|
||||
addToDesktop (ComponentPeer::windowIsTemporary);
|
||||
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
creationTime = Time::getCurrentTime();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class CallOutBoxCallback : public ModalComponentManager::Callback,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
CallOutBoxCallback (std::unique_ptr<Component> c, const Rectangle<int>& area, Component* parent, bool dismissIfBg)
|
||||
: content (std::move (c)),
|
||||
callout (*content, area, parent), dismissIfBackgrounded(dismissIfBg)
|
||||
{
|
||||
callout.setVisible (true);
|
||||
callout.enterModalState (true, this);
|
||||
if (dismissIfBackgrounded) {
|
||||
startTimer (200);
|
||||
}
|
||||
}
|
||||
|
||||
void modalStateFinished (int) override {}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (! isForegroundOrEmbeddedProcess (&callout))
|
||||
callout.dismiss();
|
||||
}
|
||||
|
||||
std::unique_ptr<Component> content;
|
||||
CallOutBox callout;
|
||||
bool dismissIfBackgrounded = true;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CallOutBoxCallback)
|
||||
};
|
||||
|
||||
CallOutBox& CallOutBox::launchAsynchronously (std::unique_ptr<Component> content, Rectangle<int> area, Component* parent, bool dismissIfBackgrounded)
|
||||
{
|
||||
jassert (content != nullptr); // must be a valid content component!
|
||||
|
||||
return (new CallOutBoxCallback (std::move (content), area, parent, dismissIfBackgrounded))->callout;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CallOutBox::setArrowSize (const float newSize)
|
||||
{
|
||||
arrowSize = newSize;
|
||||
refreshPath();
|
||||
}
|
||||
|
||||
int CallOutBox::getBorderSize() const noexcept
|
||||
{
|
||||
return jmax (getLookAndFeel().getCallOutBoxBorderSize (*this), (int) arrowSize);
|
||||
}
|
||||
|
||||
void CallOutBox::lookAndFeelChanged()
|
||||
{
|
||||
resized();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void CallOutBox::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawCallOutBoxBackground (*this, g, outline, background);
|
||||
}
|
||||
|
||||
void CallOutBox::resized()
|
||||
{
|
||||
auto borderSpace = getBorderSize();
|
||||
content.setTopLeftPosition (borderSpace, borderSpace);
|
||||
refreshPath();
|
||||
}
|
||||
|
||||
void CallOutBox::moved()
|
||||
{
|
||||
refreshPath();
|
||||
}
|
||||
|
||||
void CallOutBox::childBoundsChanged (Component*)
|
||||
{
|
||||
updatePosition (targetArea, availableArea);
|
||||
}
|
||||
|
||||
bool CallOutBox::hitTest (int x, int y)
|
||||
{
|
||||
return outline.contains ((float) x, (float) y);
|
||||
}
|
||||
|
||||
void CallOutBox::inputAttemptWhenModal()
|
||||
{
|
||||
if (dismissalMouseClicksAreAlwaysConsumed
|
||||
|| targetArea.contains (getMouseXYRelative() + getBounds().getPosition()))
|
||||
{
|
||||
// if you click on the area that originally popped-up the callout, you expect it
|
||||
// to get rid of the box, but deleting the box here allows the click to pass through and
|
||||
// probably re-trigger it, so we need to dismiss the box asynchronously to consume the click..
|
||||
|
||||
// For touchscreens, we make sure not to dismiss the CallOutBox immediately,
|
||||
// as Windows still sends touch events before the CallOutBox had a chance
|
||||
// to really open.
|
||||
|
||||
auto elapsed = Time::getCurrentTime() - creationTime;
|
||||
|
||||
if (elapsed.inMilliseconds() > 200)
|
||||
dismiss();
|
||||
}
|
||||
else
|
||||
{
|
||||
exitModalState (0);
|
||||
setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void CallOutBox::setDismissalMouseClicksAreAlwaysConsumed (bool b) noexcept
|
||||
{
|
||||
dismissalMouseClicksAreAlwaysConsumed = b;
|
||||
}
|
||||
|
||||
static constexpr int callOutBoxDismissCommandId = 0x4f83a04b;
|
||||
|
||||
void CallOutBox::handleCommandMessage (int commandId)
|
||||
{
|
||||
Component::handleCommandMessage (commandId);
|
||||
|
||||
if (commandId == callOutBoxDismissCommandId)
|
||||
{
|
||||
exitModalState (0);
|
||||
setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void CallOutBox::dismiss()
|
||||
{
|
||||
postCommandMessage (callOutBoxDismissCommandId);
|
||||
}
|
||||
|
||||
bool CallOutBox::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (key.isKeyCode (KeyPress::escapeKey))
|
||||
{
|
||||
inputAttemptWhenModal();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CallOutBox::updatePosition (const Rectangle<int>& newAreaToPointTo, const Rectangle<int>& newAreaToFitIn)
|
||||
{
|
||||
targetArea = newAreaToPointTo;
|
||||
availableArea = newAreaToFitIn;
|
||||
|
||||
auto borderSpace = getBorderSize();
|
||||
auto newBounds = getLocalArea (&content, Rectangle<int> (content.getWidth() + borderSpace * 2,
|
||||
content.getHeight() + borderSpace * 2));
|
||||
|
||||
auto hw = newBounds.getWidth() / 2;
|
||||
auto hh = newBounds.getHeight() / 2;
|
||||
auto hwReduced = (float) (hw - borderSpace * 2);
|
||||
auto hhReduced = (float) (hh - borderSpace * 2);
|
||||
auto arrowIndent = (float) borderSpace - arrowSize;
|
||||
|
||||
Point<float> targets[4] = { { (float) targetArea.getCentreX(), (float) targetArea.getBottom() },
|
||||
{ (float) targetArea.getRight(), (float) targetArea.getCentreY() },
|
||||
{ (float) targetArea.getX(), (float) targetArea.getCentreY() },
|
||||
{ (float) targetArea.getCentreX(), (float) targetArea.getY() } };
|
||||
|
||||
Line<float> lines[4] = { { targets[0].translated (-hwReduced, hh - arrowIndent), targets[0].translated (hwReduced, hh - arrowIndent) },
|
||||
{ targets[1].translated (hw - arrowIndent, -hhReduced), targets[1].translated (hw - arrowIndent, hhReduced) },
|
||||
{ targets[2].translated (-(hw - arrowIndent), -hhReduced), targets[2].translated (-(hw - arrowIndent), hhReduced) },
|
||||
{ targets[3].translated (-hwReduced, -(hh - arrowIndent)), targets[3].translated (hwReduced, -(hh - arrowIndent)) } };
|
||||
|
||||
auto centrePointArea = newAreaToFitIn.reduced (hw, hh).toFloat();
|
||||
auto targetCentre = targetArea.getCentre().toFloat();
|
||||
|
||||
float nearest = 1.0e9f;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
Line<float> constrainedLine (centrePointArea.getConstrainedPoint (lines[i].getStart()),
|
||||
centrePointArea.getConstrainedPoint (lines[i].getEnd()));
|
||||
|
||||
auto centre = constrainedLine.findNearestPointTo (targetCentre);
|
||||
auto distanceFromCentre = centre.getDistanceFrom (targets[i]);
|
||||
|
||||
if (! centrePointArea.intersects (lines[i]))
|
||||
distanceFromCentre += 1000.0f;
|
||||
|
||||
if (distanceFromCentre < nearest)
|
||||
{
|
||||
nearest = distanceFromCentre;
|
||||
targetPoint = targets[i];
|
||||
|
||||
newBounds.setPosition ((int) (centre.x - (float) hw),
|
||||
(int) (centre.y - (float) hh));
|
||||
}
|
||||
}
|
||||
|
||||
setBounds (newBounds);
|
||||
}
|
||||
|
||||
void CallOutBox::refreshPath()
|
||||
{
|
||||
repaint();
|
||||
background = {};
|
||||
outline.clear();
|
||||
|
||||
const float gap = 4.5f;
|
||||
|
||||
outline.addBubble (getLocalArea (&content, content.getLocalBounds().toFloat()).expanded (gap, gap),
|
||||
getLocalBounds().toFloat(),
|
||||
targetPoint - getPosition().toFloat(),
|
||||
getLookAndFeel().getCallOutBoxCornerSize (*this), arrowSize * 0.7f);
|
||||
}
|
||||
|
||||
void CallOutBox::timerCallback()
|
||||
{
|
||||
toFront (true);
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> CallOutBox::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::window);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
CallOutBox::CallOutBox (Component& c, Rectangle<int> area, Component* const parent)
|
||||
: content (c)
|
||||
{
|
||||
addAndMakeVisible (content);
|
||||
|
||||
if (parent != nullptr)
|
||||
{
|
||||
parent->addChildComponent (this);
|
||||
updatePosition (area, parent->getLocalBounds());
|
||||
setVisible (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
updatePosition (area, Desktop::getInstance().getDisplays().getDisplayForRect (area)->userArea);
|
||||
addToDesktop (ComponentPeer::windowIsTemporary);
|
||||
|
||||
startTimer (100);
|
||||
}
|
||||
|
||||
creationTime = Time::getCurrentTime();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class CallOutBoxCallback : public ModalComponentManager::Callback,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
CallOutBoxCallback (std::unique_ptr<Component> c, const Rectangle<int>& area, Component* parent)
|
||||
: content (std::move (c)),
|
||||
callout (*content, area, parent)
|
||||
{
|
||||
callout.setVisible (true);
|
||||
callout.enterModalState (true, this);
|
||||
startTimer (200);
|
||||
}
|
||||
|
||||
void modalStateFinished (int) override {}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (! isForegroundOrEmbeddedProcess (&callout))
|
||||
callout.dismiss();
|
||||
}
|
||||
|
||||
std::unique_ptr<Component> content;
|
||||
CallOutBox callout;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CallOutBoxCallback)
|
||||
};
|
||||
|
||||
CallOutBox& CallOutBox::launchAsynchronously (std::unique_ptr<Component> content, Rectangle<int> area, Component* parent)
|
||||
{
|
||||
jassert (content != nullptr); // must be a valid content component!
|
||||
|
||||
return (new CallOutBoxCallback (std::move (content), area, parent))->callout;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CallOutBox::setArrowSize (const float newSize)
|
||||
{
|
||||
arrowSize = newSize;
|
||||
refreshPath();
|
||||
}
|
||||
|
||||
int CallOutBox::getBorderSize() const noexcept
|
||||
{
|
||||
return jmax (getLookAndFeel().getCallOutBoxBorderSize (*this), (int) arrowSize);
|
||||
}
|
||||
|
||||
void CallOutBox::lookAndFeelChanged()
|
||||
{
|
||||
resized();
|
||||
repaint();
|
||||
}
|
||||
|
||||
void CallOutBox::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawCallOutBoxBackground (*this, g, outline, background);
|
||||
}
|
||||
|
||||
void CallOutBox::resized()
|
||||
{
|
||||
auto borderSpace = getBorderSize();
|
||||
content.setTopLeftPosition (borderSpace, borderSpace);
|
||||
refreshPath();
|
||||
}
|
||||
|
||||
void CallOutBox::moved()
|
||||
{
|
||||
refreshPath();
|
||||
}
|
||||
|
||||
void CallOutBox::childBoundsChanged (Component*)
|
||||
{
|
||||
updatePosition (targetArea, availableArea);
|
||||
}
|
||||
|
||||
bool CallOutBox::hitTest (int x, int y)
|
||||
{
|
||||
return outline.contains ((float) x, (float) y);
|
||||
}
|
||||
|
||||
void CallOutBox::inputAttemptWhenModal()
|
||||
{
|
||||
if (dismissalMouseClicksAreAlwaysConsumed
|
||||
|| targetArea.contains (getMouseXYRelative() + getBounds().getPosition()))
|
||||
{
|
||||
// if you click on the area that originally popped-up the callout, you expect it
|
||||
// to get rid of the box, but deleting the box here allows the click to pass through and
|
||||
// probably re-trigger it, so we need to dismiss the box asynchronously to consume the click..
|
||||
|
||||
// For touchscreens, we make sure not to dismiss the CallOutBox immediately,
|
||||
// as Windows still sends touch events before the CallOutBox had a chance
|
||||
// to really open.
|
||||
|
||||
auto elapsed = Time::getCurrentTime() - creationTime;
|
||||
|
||||
if (elapsed.inMilliseconds() > 200)
|
||||
dismiss();
|
||||
}
|
||||
else
|
||||
{
|
||||
exitModalState (0);
|
||||
setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void CallOutBox::setDismissalMouseClicksAreAlwaysConsumed (bool b) noexcept
|
||||
{
|
||||
dismissalMouseClicksAreAlwaysConsumed = b;
|
||||
}
|
||||
|
||||
static constexpr int callOutBoxDismissCommandId = 0x4f83a04b;
|
||||
|
||||
void CallOutBox::handleCommandMessage (int commandId)
|
||||
{
|
||||
Component::handleCommandMessage (commandId);
|
||||
|
||||
if (commandId == callOutBoxDismissCommandId)
|
||||
{
|
||||
exitModalState (0);
|
||||
setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void CallOutBox::dismiss()
|
||||
{
|
||||
postCommandMessage (callOutBoxDismissCommandId);
|
||||
}
|
||||
|
||||
bool CallOutBox::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (key.isKeyCode (KeyPress::escapeKey))
|
||||
{
|
||||
inputAttemptWhenModal();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CallOutBox::updatePosition (const Rectangle<int>& newAreaToPointTo, const Rectangle<int>& newAreaToFitIn)
|
||||
{
|
||||
targetArea = newAreaToPointTo;
|
||||
availableArea = newAreaToFitIn;
|
||||
|
||||
auto borderSpace = getBorderSize();
|
||||
auto newBounds = getLocalArea (&content, Rectangle<int> (content.getWidth() + borderSpace * 2,
|
||||
content.getHeight() + borderSpace * 2));
|
||||
|
||||
auto hw = newBounds.getWidth() / 2;
|
||||
auto hh = newBounds.getHeight() / 2;
|
||||
auto hwReduced = (float) (hw - borderSpace * 2);
|
||||
auto hhReduced = (float) (hh - borderSpace * 2);
|
||||
auto arrowIndent = (float) borderSpace - arrowSize;
|
||||
|
||||
Point<float> targets[4] = { { (float) targetArea.getCentreX(), (float) targetArea.getBottom() },
|
||||
{ (float) targetArea.getRight(), (float) targetArea.getCentreY() },
|
||||
{ (float) targetArea.getX(), (float) targetArea.getCentreY() },
|
||||
{ (float) targetArea.getCentreX(), (float) targetArea.getY() } };
|
||||
|
||||
Line<float> lines[4] = { { targets[0].translated (-hwReduced, hh - arrowIndent), targets[0].translated (hwReduced, hh - arrowIndent) },
|
||||
{ targets[1].translated (hw - arrowIndent, -hhReduced), targets[1].translated (hw - arrowIndent, hhReduced) },
|
||||
{ targets[2].translated (-(hw - arrowIndent), -hhReduced), targets[2].translated (-(hw - arrowIndent), hhReduced) },
|
||||
{ targets[3].translated (-hwReduced, -(hh - arrowIndent)), targets[3].translated (hwReduced, -(hh - arrowIndent)) } };
|
||||
|
||||
auto centrePointArea = newAreaToFitIn.reduced (hw, hh).toFloat();
|
||||
auto targetCentre = targetArea.getCentre().toFloat();
|
||||
|
||||
float nearest = 1.0e9f;
|
||||
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
Line<float> constrainedLine (centrePointArea.getConstrainedPoint (lines[i].getStart()),
|
||||
centrePointArea.getConstrainedPoint (lines[i].getEnd()));
|
||||
|
||||
auto centre = constrainedLine.findNearestPointTo (targetCentre);
|
||||
auto distanceFromCentre = centre.getDistanceFrom (targets[i]);
|
||||
|
||||
if (! centrePointArea.intersects (lines[i]))
|
||||
distanceFromCentre += 1000.0f;
|
||||
|
||||
if (distanceFromCentre < nearest)
|
||||
{
|
||||
nearest = distanceFromCentre;
|
||||
targetPoint = targets[i];
|
||||
|
||||
newBounds.setPosition ((int) (centre.x - (float) hw),
|
||||
(int) (centre.y - (float) hh));
|
||||
}
|
||||
}
|
||||
|
||||
setBounds (newBounds);
|
||||
}
|
||||
|
||||
void CallOutBox::refreshPath()
|
||||
{
|
||||
repaint();
|
||||
background = {};
|
||||
outline.clear();
|
||||
|
||||
const float gap = 4.5f;
|
||||
|
||||
outline.addBubble (getLocalArea (&content, content.getLocalBounds().toFloat()).expanded (gap, gap),
|
||||
getLocalBounds().toFloat(),
|
||||
targetPoint - getPosition().toFloat(),
|
||||
getLookAndFeel().getCallOutBoxCornerSize (*this), arrowSize * 0.7f);
|
||||
}
|
||||
|
||||
void CallOutBox::timerCallback()
|
||||
{
|
||||
toFront (true);
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> CallOutBox::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::dialogWindow);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,190 +1,187 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A box with a small arrow that can be used as a temporary pop-up window to show
|
||||
extra controls when a button or other component is clicked.
|
||||
|
||||
Using one of these is similar to having a popup menu attached to a button or
|
||||
other component - but it looks fancier, and has an arrow that can indicate the
|
||||
object that it applies to.
|
||||
|
||||
The class works best when shown modally, but obviously running modal loops is
|
||||
evil and must never be done, so the launchAsynchronously method is provided as
|
||||
a handy way of launching an instance of a CallOutBox and automatically managing
|
||||
its lifetime, e.g.
|
||||
|
||||
@code
|
||||
void mouseUp (const MouseEvent&)
|
||||
{
|
||||
auto content = std::make_unique<FoobarContentComp>();
|
||||
content->setSize (300, 300);
|
||||
|
||||
auto& myBox = CallOutBox::launchAsynchronously (std::move (content),
|
||||
getScreenBounds(),
|
||||
nullptr);
|
||||
}
|
||||
@endcode
|
||||
|
||||
The call-out will resize and position itself when the content changes size.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CallOutBox : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a CallOutBox.
|
||||
|
||||
@param contentComponent the component to display inside the call-out. This should
|
||||
already have a size set (although the call-out will also
|
||||
update itself when the component's size is changed later).
|
||||
Obviously this component must not be deleted until the
|
||||
call-out box has been deleted.
|
||||
@param areaToPointTo the area that the call-out's arrow should point towards. If
|
||||
a parentComponent is supplied, then this is relative to that
|
||||
parent; otherwise, it's a global screen coord.
|
||||
@param parentComponent if not a nullptr, this is the component to add the call-out to.
|
||||
If this is a nullptr, the call-out will be added to the desktop.
|
||||
*/
|
||||
CallOutBox (Component& contentComponent,
|
||||
Rectangle<int> areaToPointTo,
|
||||
Component* parentComponent);
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the base width of the arrow. */
|
||||
void setArrowSize (float newSize);
|
||||
|
||||
/** Updates the position and size of the box.
|
||||
|
||||
You shouldn't normally need to call this, unless you need more precise control over the
|
||||
layout.
|
||||
|
||||
@param newAreaToPointTo the rectangle to make the box's arrow point to
|
||||
@param newAreaToFitIn the area within which the box's position should be constrained
|
||||
*/
|
||||
void updatePosition (const Rectangle<int>& newAreaToPointTo,
|
||||
const Rectangle<int>& newAreaToFitIn);
|
||||
|
||||
|
||||
/** This will launch a callout box containing the given content, pointing to the
|
||||
specified target component.
|
||||
|
||||
This method will create and display a callout, returning immediately, after which
|
||||
the box will continue to run modally until the user clicks on some other component, at
|
||||
which point it will be dismissed and deleted automatically.
|
||||
|
||||
It returns a reference to the newly-created box so that you can customise it, but don't
|
||||
keep a pointer to it, as it'll be deleted at some point when it gets closed.
|
||||
|
||||
@param contentComponent the component to display inside the call-out. This should
|
||||
already have a size set (although the call-out will also
|
||||
update itself when the component's size is changed later).
|
||||
@param areaToPointTo the area that the call-out's arrow should point towards. If
|
||||
a parentComponent is supplied, then this is relative to that
|
||||
parent; otherwise, it's a global screen coord.
|
||||
@param parentComponent if not a nullptr, this is the component to add the call-out to.
|
||||
If this is a nullptr, the call-out will be added to the desktop.
|
||||
@param dismissIfBackgrounded If this is true, the call-out will be dismissed if we are no
|
||||
longer the foreground app.
|
||||
*/
|
||||
static CallOutBox& launchAsynchronously (std::unique_ptr<Component> contentComponent,
|
||||
Rectangle<int> areaToPointTo,
|
||||
Component* parentComponent,
|
||||
bool dismissIfBackgrounded=true);
|
||||
|
||||
/** Posts a message which will dismiss the callout box asynchronously.
|
||||
NB: it's safe to call this method from any thread.
|
||||
*/
|
||||
void dismiss();
|
||||
|
||||
/** Determines whether the mouse events for clicks outside the calloutbox are
|
||||
consumed, or allowed to arrive at the other component that they were aimed at.
|
||||
|
||||
By default this is false, so that when you click on something outside the calloutbox,
|
||||
that event will also be sent to the component that was clicked on. If you set it to
|
||||
true, then the first click will always just dismiss the box and not be sent to
|
||||
anything else.
|
||||
*/
|
||||
void setDismissalMouseClicksAreAlwaysConsumed (bool shouldAlwaysBeConsumed) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawCallOutBoxBackground (CallOutBox&, Graphics&, const Path&, Image&) = 0;
|
||||
virtual int getCallOutBoxBorderSize (const CallOutBox&) = 0;
|
||||
virtual float getCallOutBoxCornerSize (const CallOutBox&) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void moved() override;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
void inputAttemptWhenModal() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void handleCommandMessage (int) override;
|
||||
/** @internal */
|
||||
int getBorderSize() const noexcept;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component& content;
|
||||
Path outline;
|
||||
Point<float> targetPoint;
|
||||
Rectangle<int> availableArea, targetArea;
|
||||
Image background;
|
||||
float arrowSize = 16.0f;
|
||||
bool dismissalMouseClicksAreAlwaysConsumed = false;
|
||||
|
||||
Time creationTime;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void refreshPath();
|
||||
void timerCallback() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallOutBox)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A box with a small arrow that can be used as a temporary pop-up window to show
|
||||
extra controls when a button or other component is clicked.
|
||||
|
||||
Using one of these is similar to having a popup menu attached to a button or
|
||||
other component - but it looks fancier, and has an arrow that can indicate the
|
||||
object that it applies to.
|
||||
|
||||
The class works best when shown modally, but obviously running modal loops is
|
||||
evil and must never be done, so the launchAsynchronously method is provided as
|
||||
a handy way of launching an instance of a CallOutBox and automatically managing
|
||||
its lifetime, e.g.
|
||||
|
||||
@code
|
||||
void mouseUp (const MouseEvent&)
|
||||
{
|
||||
auto content = std::make_unique<FoobarContentComp>();
|
||||
content->setSize (300, 300);
|
||||
|
||||
auto& myBox = CallOutBox::launchAsynchronously (std::move (content),
|
||||
getScreenBounds(),
|
||||
nullptr);
|
||||
}
|
||||
@endcode
|
||||
|
||||
The call-out will resize and position itself when the content changes size.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API CallOutBox : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a CallOutBox.
|
||||
|
||||
@param contentComponent the component to display inside the call-out. This should
|
||||
already have a size set (although the call-out will also
|
||||
update itself when the component's size is changed later).
|
||||
Obviously this component must not be deleted until the
|
||||
call-out box has been deleted.
|
||||
@param areaToPointTo the area that the call-out's arrow should point towards. If
|
||||
a parentComponent is supplied, then this is relative to that
|
||||
parent; otherwise, it's a global screen coord.
|
||||
@param parentComponent if not a nullptr, this is the component to add the call-out to.
|
||||
If this is a nullptr, the call-out will be added to the desktop.
|
||||
*/
|
||||
CallOutBox (Component& contentComponent,
|
||||
Rectangle<int> areaToPointTo,
|
||||
Component* parentComponent);
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the base width of the arrow. */
|
||||
void setArrowSize (float newSize);
|
||||
|
||||
/** Updates the position and size of the box.
|
||||
|
||||
You shouldn't normally need to call this, unless you need more precise control over the
|
||||
layout.
|
||||
|
||||
@param newAreaToPointTo the rectangle to make the box's arrow point to
|
||||
@param newAreaToFitIn the area within which the box's position should be constrained
|
||||
*/
|
||||
void updatePosition (const Rectangle<int>& newAreaToPointTo,
|
||||
const Rectangle<int>& newAreaToFitIn);
|
||||
|
||||
|
||||
/** This will launch a callout box containing the given content, pointing to the
|
||||
specified target component.
|
||||
|
||||
This method will create and display a callout, returning immediately, after which
|
||||
the box will continue to run modally until the user clicks on some other component, at
|
||||
which point it will be dismissed and deleted automatically.
|
||||
|
||||
It returns a reference to the newly-created box so that you can customise it, but don't
|
||||
keep a pointer to it, as it'll be deleted at some point when it gets closed.
|
||||
|
||||
@param contentComponent the component to display inside the call-out. This should
|
||||
already have a size set (although the call-out will also
|
||||
update itself when the component's size is changed later).
|
||||
@param areaToPointTo the area that the call-out's arrow should point towards. If
|
||||
a parentComponent is supplied, then this is relative to that
|
||||
parent; otherwise, it's a global screen coord.
|
||||
@param parentComponent if not a nullptr, this is the component to add the call-out to.
|
||||
If this is a nullptr, the call-out will be added to the desktop.
|
||||
*/
|
||||
static CallOutBox& launchAsynchronously (std::unique_ptr<Component> contentComponent,
|
||||
Rectangle<int> areaToPointTo,
|
||||
Component* parentComponent);
|
||||
|
||||
/** Posts a message which will dismiss the callout box asynchronously.
|
||||
NB: it's safe to call this method from any thread.
|
||||
*/
|
||||
void dismiss();
|
||||
|
||||
/** Determines whether the mouse events for clicks outside the calloutbox are
|
||||
consumed, or allowed to arrive at the other component that they were aimed at.
|
||||
|
||||
By default this is false, so that when you click on something outside the calloutbox,
|
||||
that event will also be sent to the component that was clicked on. If you set it to
|
||||
true, then the first click will always just dismiss the box and not be sent to
|
||||
anything else.
|
||||
*/
|
||||
void setDismissalMouseClicksAreAlwaysConsumed (bool shouldAlwaysBeConsumed) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawCallOutBoxBackground (CallOutBox&, Graphics&, const Path&, Image&) = 0;
|
||||
virtual int getCallOutBoxBorderSize (const CallOutBox&) = 0;
|
||||
virtual float getCallOutBoxCornerSize (const CallOutBox&) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void moved() override;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
/** @internal */
|
||||
bool hitTest (int x, int y) override;
|
||||
/** @internal */
|
||||
void inputAttemptWhenModal() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void handleCommandMessage (int) override;
|
||||
/** @internal */
|
||||
int getBorderSize() const noexcept;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component& content;
|
||||
Path outline;
|
||||
Point<float> targetPoint;
|
||||
Rectangle<int> availableArea, targetArea;
|
||||
Image background;
|
||||
float arrowSize = 16.0f;
|
||||
bool dismissalMouseClicksAreAlwaysConsumed = false;
|
||||
|
||||
Time creationTime;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void refreshPath();
|
||||
void timerCallback() override;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallOutBox)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,181 +1,181 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
DialogWindow::DialogWindow (const String& name, Colour colour,
|
||||
const bool escapeCloses, const bool onDesktop,
|
||||
const float scale)
|
||||
: DocumentWindow (name, colour, DocumentWindow::closeButton, onDesktop),
|
||||
desktopScale (scale),
|
||||
escapeKeyTriggersCloseButton (escapeCloses)
|
||||
{
|
||||
}
|
||||
|
||||
DialogWindow::~DialogWindow() = default;
|
||||
|
||||
bool DialogWindow::escapeKeyPressed()
|
||||
{
|
||||
if (escapeKeyTriggersCloseButton)
|
||||
{
|
||||
setVisible (false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DialogWindow::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (key == KeyPress::escapeKey && escapeKeyPressed())
|
||||
return true;
|
||||
|
||||
return DocumentWindow::keyPressed (key);
|
||||
}
|
||||
|
||||
void DialogWindow::resized()
|
||||
{
|
||||
DocumentWindow::resized();
|
||||
|
||||
if (escapeKeyTriggersCloseButton)
|
||||
{
|
||||
if (auto* close = getCloseButton())
|
||||
{
|
||||
const KeyPress esc (KeyPress::escapeKey, 0, 0);
|
||||
|
||||
if (! close->isRegisteredForShortcut (esc))
|
||||
close->addShortcut (esc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DefaultDialogWindow : public DialogWindow
|
||||
{
|
||||
public:
|
||||
DefaultDialogWindow (LaunchOptions& options)
|
||||
: DialogWindow (options.dialogTitle, options.dialogBackgroundColour,
|
||||
options.escapeKeyTriggersCloseButton, true,
|
||||
options.componentToCentreAround != nullptr
|
||||
? Component::getApproximateScaleFactorForComponent (options.componentToCentreAround)
|
||||
: 1.0f)
|
||||
{
|
||||
setUsingNativeTitleBar (options.useNativeTitleBar);
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
|
||||
if (options.content.willDeleteObject())
|
||||
setContentOwned (options.content.release(), true);
|
||||
else
|
||||
setContentNonOwned (options.content.release(), true);
|
||||
|
||||
centreAroundComponent (options.componentToCentreAround, getWidth(), getHeight());
|
||||
setResizable (options.resizable, options.useBottomRightCornerResizer);
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (DefaultDialogWindow)
|
||||
};
|
||||
|
||||
DialogWindow::LaunchOptions::LaunchOptions() noexcept {}
|
||||
|
||||
DialogWindow* DialogWindow::LaunchOptions::create()
|
||||
{
|
||||
jassert (content != nullptr); // You need to provide some kind of content for the dialog!
|
||||
|
||||
return new DefaultDialogWindow (*this);
|
||||
}
|
||||
|
||||
DialogWindow* DialogWindow::LaunchOptions::launchAsync()
|
||||
{
|
||||
auto* d = create();
|
||||
d->enterModalState (true, nullptr, true);
|
||||
return d;
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
int DialogWindow::LaunchOptions::runModal()
|
||||
{
|
||||
return launchAsync()->runModalLoop();
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void DialogWindow::showDialog (const String& dialogTitle,
|
||||
Component* const contentComponent,
|
||||
Component* const componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
const bool escapeKeyTriggersCloseButton,
|
||||
const bool resizable,
|
||||
const bool useBottomRightCornerResizer)
|
||||
{
|
||||
LaunchOptions o;
|
||||
o.dialogTitle = dialogTitle;
|
||||
o.content.setNonOwned (contentComponent);
|
||||
o.componentToCentreAround = componentToCentreAround;
|
||||
o.dialogBackgroundColour = backgroundColour;
|
||||
o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton;
|
||||
o.useNativeTitleBar = false;
|
||||
o.resizable = resizable;
|
||||
o.useBottomRightCornerResizer = useBottomRightCornerResizer;
|
||||
|
||||
o.launchAsync();
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
int DialogWindow::showModalDialog (const String& dialogTitle,
|
||||
Component* const contentComponent,
|
||||
Component* const componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
const bool escapeKeyTriggersCloseButton,
|
||||
const bool resizable,
|
||||
const bool useBottomRightCornerResizer)
|
||||
{
|
||||
LaunchOptions o;
|
||||
o.dialogTitle = dialogTitle;
|
||||
o.content.setNonOwned (contentComponent);
|
||||
o.componentToCentreAround = componentToCentreAround;
|
||||
o.dialogBackgroundColour = backgroundColour;
|
||||
o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton;
|
||||
o.useNativeTitleBar = false;
|
||||
o.resizable = resizable;
|
||||
o.useBottomRightCornerResizer = useBottomRightCornerResizer;
|
||||
|
||||
return o.runModal();
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> DialogWindow::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::dialogWindow);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
DialogWindow::DialogWindow (const String& name, Colour colour,
|
||||
const bool escapeCloses, const bool onDesktop,
|
||||
const float scale)
|
||||
: DocumentWindow (name, colour, DocumentWindow::closeButton, onDesktop),
|
||||
desktopScale (scale),
|
||||
escapeKeyTriggersCloseButton (escapeCloses)
|
||||
{
|
||||
}
|
||||
|
||||
DialogWindow::~DialogWindow() = default;
|
||||
|
||||
bool DialogWindow::escapeKeyPressed()
|
||||
{
|
||||
if (escapeKeyTriggersCloseButton)
|
||||
{
|
||||
setVisible (false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DialogWindow::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (key == KeyPress::escapeKey && escapeKeyPressed())
|
||||
return true;
|
||||
|
||||
return DocumentWindow::keyPressed (key);
|
||||
}
|
||||
|
||||
void DialogWindow::resized()
|
||||
{
|
||||
DocumentWindow::resized();
|
||||
|
||||
if (escapeKeyTriggersCloseButton)
|
||||
{
|
||||
if (auto* close = getCloseButton())
|
||||
{
|
||||
const KeyPress esc (KeyPress::escapeKey, 0, 0);
|
||||
|
||||
if (! close->isRegisteredForShortcut (esc))
|
||||
close->addShortcut (esc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DefaultDialogWindow : public DialogWindow
|
||||
{
|
||||
public:
|
||||
DefaultDialogWindow (LaunchOptions& options)
|
||||
: DialogWindow (options.dialogTitle, options.dialogBackgroundColour,
|
||||
options.escapeKeyTriggersCloseButton, true,
|
||||
options.componentToCentreAround != nullptr
|
||||
? Component::getApproximateScaleFactorForComponent (options.componentToCentreAround)
|
||||
: 1.0f)
|
||||
{
|
||||
if (options.content.willDeleteObject())
|
||||
setContentOwned (options.content.release(), true);
|
||||
else
|
||||
setContentNonOwned (options.content.release(), true);
|
||||
|
||||
centreAroundComponent (options.componentToCentreAround, getWidth(), getHeight());
|
||||
setResizable (options.resizable, options.useBottomRightCornerResizer);
|
||||
|
||||
setUsingNativeTitleBar (options.useNativeTitleBar);
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
}
|
||||
|
||||
void closeButtonPressed() override
|
||||
{
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE (DefaultDialogWindow)
|
||||
};
|
||||
|
||||
DialogWindow::LaunchOptions::LaunchOptions() noexcept {}
|
||||
|
||||
DialogWindow* DialogWindow::LaunchOptions::create()
|
||||
{
|
||||
jassert (content != nullptr); // You need to provide some kind of content for the dialog!
|
||||
|
||||
return new DefaultDialogWindow (*this);
|
||||
}
|
||||
|
||||
DialogWindow* DialogWindow::LaunchOptions::launchAsync()
|
||||
{
|
||||
auto* d = create();
|
||||
d->enterModalState (true, nullptr, true);
|
||||
return d;
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
int DialogWindow::LaunchOptions::runModal()
|
||||
{
|
||||
return launchAsync()->runModalLoop();
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void DialogWindow::showDialog (const String& dialogTitle,
|
||||
Component* const contentComponent,
|
||||
Component* const componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
const bool escapeKeyTriggersCloseButton,
|
||||
const bool resizable,
|
||||
const bool useBottomRightCornerResizer)
|
||||
{
|
||||
LaunchOptions o;
|
||||
o.dialogTitle = dialogTitle;
|
||||
o.content.setNonOwned (contentComponent);
|
||||
o.componentToCentreAround = componentToCentreAround;
|
||||
o.dialogBackgroundColour = backgroundColour;
|
||||
o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton;
|
||||
o.useNativeTitleBar = false;
|
||||
o.resizable = resizable;
|
||||
o.useBottomRightCornerResizer = useBottomRightCornerResizer;
|
||||
|
||||
o.launchAsync();
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
int DialogWindow::showModalDialog (const String& dialogTitle,
|
||||
Component* const contentComponent,
|
||||
Component* const componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
const bool escapeKeyTriggersCloseButton,
|
||||
const bool resizable,
|
||||
const bool useBottomRightCornerResizer)
|
||||
{
|
||||
LaunchOptions o;
|
||||
o.dialogTitle = dialogTitle;
|
||||
o.content.setNonOwned (contentComponent);
|
||||
o.componentToCentreAround = componentToCentreAround;
|
||||
o.dialogBackgroundColour = backgroundColour;
|
||||
o.escapeKeyTriggersCloseButton = escapeKeyTriggersCloseButton;
|
||||
o.useNativeTitleBar = false;
|
||||
o.resizable = resizable;
|
||||
o.useBottomRightCornerResizer = useBottomRightCornerResizer;
|
||||
|
||||
return o.runModal();
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> DialogWindow::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::dialogWindow);
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,276 +1,276 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A dialog-box style window.
|
||||
|
||||
This class is a convenient way of creating a DocumentWindow with a close button
|
||||
that can be triggered by pressing the escape key.
|
||||
|
||||
Any of the methods available to a DocumentWindow or ResizableWindow are also
|
||||
available to this, so it can be made resizable, have a menu bar, etc.
|
||||
|
||||
You can either override or use an instance of the DialogWindow class directly,
|
||||
or you can use a DialogWindow::LaunchOptions structure to quickly set up and
|
||||
launch a box containing a content component.
|
||||
|
||||
If you use the class directly, you'll need to override the
|
||||
DocumentWindow::closeButtonPressed() method to handle the user clicking the close
|
||||
button - for more info, see the DocumentWindow help.
|
||||
|
||||
@see DocumentWindow, ResizableWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DialogWindow : public DocumentWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DialogWindow.
|
||||
|
||||
@param name the name to give the component - this is also
|
||||
the title shown at the top of the window. To change
|
||||
this later, use setName()
|
||||
@param backgroundColour the colour to use for filling the window's background.
|
||||
@param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the
|
||||
close button to be triggered
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
@param desktopScale specifies the scale to use when drawing the window. In a plugin,
|
||||
the host controls the scale used to render the plugin editor.
|
||||
You should query the editor scale with
|
||||
Component::getApproximateScaleFactorForComponent() and pass the
|
||||
result here. You can ignore this parameter in a standalone app
|
||||
*/
|
||||
DialogWindow (const String& name,
|
||||
Colour backgroundColour,
|
||||
bool escapeKeyTriggersCloseButton,
|
||||
bool addToDesktop = true,
|
||||
float desktopScale = 1.0f);
|
||||
|
||||
/** Destructor.
|
||||
If a content component has been set with setContentOwned(), it will be deleted.
|
||||
*/
|
||||
~DialogWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** This class defines a collection of settings to be used to open a DialogWindow.
|
||||
|
||||
The easiest way to open a DialogWindow is to create yourself a LaunchOptions structure,
|
||||
initialise its fields with the appropriate details, and then call its launchAsync()
|
||||
method to launch the dialog.
|
||||
*/
|
||||
struct JUCE_API LaunchOptions
|
||||
{
|
||||
LaunchOptions() noexcept;
|
||||
|
||||
/** The title to give the window. */
|
||||
String dialogTitle;
|
||||
|
||||
/** The background colour for the window. */
|
||||
Colour dialogBackgroundColour = Colours::lightgrey;
|
||||
|
||||
/** The content component to show in the window. This must not be null!
|
||||
Using an OptionalScopedPointer to hold this pointer lets you indicate whether
|
||||
you'd like the dialog to automatically delete the component when the dialog
|
||||
has terminated.
|
||||
*/
|
||||
OptionalScopedPointer<Component> content;
|
||||
|
||||
/** If this is not a nullptr, it indicates a component that you'd like to position this
|
||||
dialog box in front of. See the DocumentWindow::centreAroundComponent() method for
|
||||
more info about this parameter.
|
||||
*/
|
||||
Component* componentToCentreAround = nullptr;
|
||||
|
||||
/** If true, then the escape key will trigger the dialog's close button. */
|
||||
bool escapeKeyTriggersCloseButton = true;
|
||||
/** If true, the dialog will use a native title bar. See TopLevelWindow::setUsingNativeTitleBar() */
|
||||
bool useNativeTitleBar = true;
|
||||
/** If true, the window will be resizable. See ResizableWindow::setResizable() */
|
||||
bool resizable = true;
|
||||
/** Indicates whether to use a border or corner resizer component. See ResizableWindow::setResizable() */
|
||||
bool useBottomRightCornerResizer = false;
|
||||
|
||||
/** Launches a new modal dialog window.
|
||||
This will create a dialog based on the settings in this structure,
|
||||
launch it modally, and return immediately. The window that is returned
|
||||
will be automatically deleted when the modal state is terminated.
|
||||
|
||||
When the dialog's close button is clicked, it'll automatically terminate its
|
||||
modal state, but you can also do this programmatically by calling
|
||||
exitModalState (returnValue) on the DialogWindow.
|
||||
|
||||
If your content component needs to find the dialog window that it is
|
||||
contained in, a quick trick is to do this:
|
||||
@code
|
||||
if (DialogWindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>())
|
||||
dw->exitModalState (1234);
|
||||
@endcode
|
||||
*/
|
||||
DialogWindow* launchAsync();
|
||||
|
||||
/** Creates a new DialogWindow instance with these settings.
|
||||
This method simply creates the window, it doesn't run it modally. In most cases
|
||||
you'll want to use launchAsync() or runModal() instead.
|
||||
*/
|
||||
DialogWindow* create();
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Launches and runs the dialog modally, returning the status code that was
|
||||
used to terminate the modal loop.
|
||||
|
||||
Note that running modal loops inline is a BAD technique. If possible, always
|
||||
use launchAsync() instead of this method.
|
||||
*/
|
||||
int runModal();
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LaunchOptions)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Easy way of quickly showing a dialog box containing a given component.
|
||||
|
||||
Note: This method has been superseded by the DialogWindow::LaunchOptions structure,
|
||||
which does the same job with some extra flexibility. The showDialog method is here
|
||||
for backwards compatibility, but please use DialogWindow::LaunchOptions in new code.
|
||||
|
||||
This will open and display a DialogWindow containing a given component, making it
|
||||
modal, but returning immediately to allow the dialog to finish in its own time. If
|
||||
you want to block and run a modal loop until the dialog is dismissed, use showModalDialog()
|
||||
instead.
|
||||
|
||||
To close the dialog programmatically, you should call exitModalState (returnValue) on
|
||||
the DialogWindow that is created. To find a pointer to this window from your
|
||||
contentComponent, you can do something like this:
|
||||
@code
|
||||
if (DialogWindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>())
|
||||
dw->exitModalState (1234);
|
||||
@endcode
|
||||
|
||||
@param dialogTitle the dialog box's title
|
||||
@param contentComponent the content component for the dialog box. Make sure
|
||||
that this has been set to the size you want it to
|
||||
be before calling this method. The component won't
|
||||
be deleted by this call, so you can re-use it or delete
|
||||
it afterwards
|
||||
@param componentToCentreAround if this is not a nullptr, it indicates a component that
|
||||
you'd like to show this dialog box in front of. See the
|
||||
DocumentWindow::centreAroundComponent() method for more
|
||||
info on this parameter
|
||||
@param backgroundColour a colour to use for the dialog box's background colour
|
||||
@param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the
|
||||
close button to be triggered
|
||||
@param shouldBeResizable if true, the dialog window has either a resizable border, or
|
||||
a corner resizer
|
||||
@param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether
|
||||
to use a border or corner resizer component. See ResizableWindow::setResizable()
|
||||
*/
|
||||
static void showDialog (const String& dialogTitle,
|
||||
Component* contentComponent,
|
||||
Component* componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
bool escapeKeyTriggersCloseButton,
|
||||
bool shouldBeResizable = false,
|
||||
bool useBottomRightCornerResizer = false);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Easy way of quickly showing a dialog box containing a given component.
|
||||
|
||||
Note: This method has been superseded by the DialogWindow::LaunchOptions structure,
|
||||
which does the same job with some extra flexibility. The showDialog method is here
|
||||
for backwards compatibility, but please use DialogWindow::LaunchOptions in new code.
|
||||
|
||||
This will open and display a DialogWindow containing a given component, returning
|
||||
when the user clicks its close button.
|
||||
|
||||
It returns the value that was returned by the dialog box's runModalLoop() call.
|
||||
|
||||
To close the dialog programmatically, you should call exitModalState (returnValue) on
|
||||
the DialogWindow that is created. To find a pointer to this window from your
|
||||
contentComponent, you can do something like this:
|
||||
@code
|
||||
if (DialogWindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>())
|
||||
dw->exitModalState (1234);
|
||||
@endcode
|
||||
|
||||
@param dialogTitle the dialog box's title
|
||||
@param contentComponent the content component for the dialog box. Make sure
|
||||
that this has been set to the size you want it to
|
||||
be before calling this method. The component won't
|
||||
be deleted by this call, so you can re-use it or delete
|
||||
it afterwards
|
||||
@param componentToCentreAround if this is not a nullptr, it indicates a component that
|
||||
you'd like to show this dialog box in front of. See the
|
||||
DocumentWindow::centreAroundComponent() method for more
|
||||
info on this parameter
|
||||
@param backgroundColour a colour to use for the dialog box's background colour
|
||||
@param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the
|
||||
close button to be triggered
|
||||
@param shouldBeResizable if true, the dialog window has either a resizable border, or
|
||||
a corner resizer
|
||||
@param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether
|
||||
to use a border or corner resizer component. See ResizableWindow::setResizable()
|
||||
*/
|
||||
static int showModalDialog (const String& dialogTitle,
|
||||
Component* contentComponent,
|
||||
Component* componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
bool escapeKeyTriggersCloseButton,
|
||||
bool shouldBeResizable = false,
|
||||
bool useBottomRightCornerResizer = false);
|
||||
#endif
|
||||
|
||||
|
||||
/** Called when the escape key is pressed.
|
||||
This can be overridden to do things other than the default behaviour, which is to hide
|
||||
the window. Return true if the key has been used, or false if it was ignored.
|
||||
*/
|
||||
virtual bool escapeKeyPressed();
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
float getDesktopScaleFactor() const override { return desktopScale * Desktop::getInstance().getGlobalScaleFactor(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
|
||||
float desktopScale = 1.0f;
|
||||
bool escapeKeyTriggersCloseButton;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DialogWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A dialog-box style window.
|
||||
|
||||
This class is a convenient way of creating a DocumentWindow with a close button
|
||||
that can be triggered by pressing the escape key.
|
||||
|
||||
Any of the methods available to a DocumentWindow or ResizableWindow are also
|
||||
available to this, so it can be made resizable, have a menu bar, etc.
|
||||
|
||||
You can either override or use an instance of the DialogWindow class directly,
|
||||
or you can use a DialogWindow::LaunchOptions structure to quickly set up and
|
||||
launch a box containing a content component.
|
||||
|
||||
If you use the class directly, you'll need to override the
|
||||
DocumentWindow::closeButtonPressed() method to handle the user clicking the close
|
||||
button - for more info, see the DocumentWindow help.
|
||||
|
||||
@see DocumentWindow, ResizableWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DialogWindow : public DocumentWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a DialogWindow.
|
||||
|
||||
@param name the name to give the component - this is also
|
||||
the title shown at the top of the window. To change
|
||||
this later, use setName()
|
||||
@param backgroundColour the colour to use for filling the window's background.
|
||||
@param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the
|
||||
close button to be triggered
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
@param desktopScale specifies the scale to use when drawing the window. In a plugin,
|
||||
the host controls the scale used to render the plugin editor.
|
||||
You should query the editor scale with
|
||||
Component::getApproximateScaleFactorForComponent() and pass the
|
||||
result here. You can ignore this parameter in a standalone app
|
||||
*/
|
||||
DialogWindow (const String& name,
|
||||
Colour backgroundColour,
|
||||
bool escapeKeyTriggersCloseButton,
|
||||
bool addToDesktop = true,
|
||||
float desktopScale = 1.0f);
|
||||
|
||||
/** Destructor.
|
||||
If a content component has been set with setContentOwned(), it will be deleted.
|
||||
*/
|
||||
~DialogWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** This class defines a collection of settings to be used to open a DialogWindow.
|
||||
|
||||
The easiest way to open a DialogWindow is to create yourself a LaunchOptions structure,
|
||||
initialise its fields with the appropriate details, and then call its launchAsync()
|
||||
method to launch the dialog.
|
||||
*/
|
||||
struct JUCE_API LaunchOptions
|
||||
{
|
||||
LaunchOptions() noexcept;
|
||||
|
||||
/** The title to give the window. */
|
||||
String dialogTitle;
|
||||
|
||||
/** The background colour for the window. */
|
||||
Colour dialogBackgroundColour = Colours::lightgrey;
|
||||
|
||||
/** The content component to show in the window. This must not be null!
|
||||
Using an OptionalScopedPointer to hold this pointer lets you indicate whether
|
||||
you'd like the dialog to automatically delete the component when the dialog
|
||||
has terminated.
|
||||
*/
|
||||
OptionalScopedPointer<Component> content;
|
||||
|
||||
/** If this is not a nullptr, it indicates a component that you'd like to position this
|
||||
dialog box in front of. See the DocumentWindow::centreAroundComponent() method for
|
||||
more info about this parameter.
|
||||
*/
|
||||
Component* componentToCentreAround = nullptr;
|
||||
|
||||
/** If true, then the escape key will trigger the dialog's close button. */
|
||||
bool escapeKeyTriggersCloseButton = true;
|
||||
/** If true, the dialog will use a native title bar. See TopLevelWindow::setUsingNativeTitleBar() */
|
||||
bool useNativeTitleBar = true;
|
||||
/** If true, the window will be resizable. See ResizableWindow::setResizable() */
|
||||
bool resizable = true;
|
||||
/** Indicates whether to use a border or corner resizer component. See ResizableWindow::setResizable() */
|
||||
bool useBottomRightCornerResizer = false;
|
||||
|
||||
/** Launches a new modal dialog window.
|
||||
This will create a dialog based on the settings in this structure,
|
||||
launch it modally, and return immediately. The window that is returned
|
||||
will be automatically deleted when the modal state is terminated.
|
||||
|
||||
When the dialog's close button is clicked, it'll automatically terminate its
|
||||
modal state, but you can also do this programmatically by calling
|
||||
exitModalState (returnValue) on the DialogWindow.
|
||||
|
||||
If your content component needs to find the dialog window that it is
|
||||
contained in, a quick trick is to do this:
|
||||
@code
|
||||
if (DialogWindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>())
|
||||
dw->exitModalState (1234);
|
||||
@endcode
|
||||
*/
|
||||
DialogWindow* launchAsync();
|
||||
|
||||
/** Creates a new DialogWindow instance with these settings.
|
||||
This method simply creates the window, it doesn't run it modally. In most cases
|
||||
you'll want to use launchAsync() or runModal() instead.
|
||||
*/
|
||||
DialogWindow* create();
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Launches and runs the dialog modally, returning the status code that was
|
||||
used to terminate the modal loop.
|
||||
|
||||
Note that running modal loops inline is a BAD technique. If possible, always
|
||||
use launchAsync() instead of this method.
|
||||
*/
|
||||
int runModal();
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (LaunchOptions)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Easy way of quickly showing a dialog box containing a given component.
|
||||
|
||||
Note: This method has been superseded by the DialogWindow::LaunchOptions structure,
|
||||
which does the same job with some extra flexibility. The showDialog method is here
|
||||
for backwards compatibility, but please use DialogWindow::LaunchOptions in new code.
|
||||
|
||||
This will open and display a DialogWindow containing a given component, making it
|
||||
modal, but returning immediately to allow the dialog to finish in its own time. If
|
||||
you want to block and run a modal loop until the dialog is dismissed, use showModalDialog()
|
||||
instead.
|
||||
|
||||
To close the dialog programmatically, you should call exitModalState (returnValue) on
|
||||
the DialogWindow that is created. To find a pointer to this window from your
|
||||
contentComponent, you can do something like this:
|
||||
@code
|
||||
if (DialogWindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>())
|
||||
dw->exitModalState (1234);
|
||||
@endcode
|
||||
|
||||
@param dialogTitle the dialog box's title
|
||||
@param contentComponent the content component for the dialog box. Make sure
|
||||
that this has been set to the size you want it to
|
||||
be before calling this method. The component won't
|
||||
be deleted by this call, so you can re-use it or delete
|
||||
it afterwards
|
||||
@param componentToCentreAround if this is not a nullptr, it indicates a component that
|
||||
you'd like to show this dialog box in front of. See the
|
||||
DocumentWindow::centreAroundComponent() method for more
|
||||
info on this parameter
|
||||
@param backgroundColour a colour to use for the dialog box's background colour
|
||||
@param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the
|
||||
close button to be triggered
|
||||
@param shouldBeResizable if true, the dialog window has either a resizable border, or
|
||||
a corner resizer
|
||||
@param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether
|
||||
to use a border or corner resizer component. See ResizableWindow::setResizable()
|
||||
*/
|
||||
static void showDialog (const String& dialogTitle,
|
||||
Component* contentComponent,
|
||||
Component* componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
bool escapeKeyTriggersCloseButton,
|
||||
bool shouldBeResizable = false,
|
||||
bool useBottomRightCornerResizer = false);
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Easy way of quickly showing a dialog box containing a given component.
|
||||
|
||||
Note: This method has been superseded by the DialogWindow::LaunchOptions structure,
|
||||
which does the same job with some extra flexibility. The showDialog method is here
|
||||
for backwards compatibility, but please use DialogWindow::LaunchOptions in new code.
|
||||
|
||||
This will open and display a DialogWindow containing a given component, returning
|
||||
when the user clicks its close button.
|
||||
|
||||
It returns the value that was returned by the dialog box's runModalLoop() call.
|
||||
|
||||
To close the dialog programmatically, you should call exitModalState (returnValue) on
|
||||
the DialogWindow that is created. To find a pointer to this window from your
|
||||
contentComponent, you can do something like this:
|
||||
@code
|
||||
if (DialogWindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>())
|
||||
dw->exitModalState (1234);
|
||||
@endcode
|
||||
|
||||
@param dialogTitle the dialog box's title
|
||||
@param contentComponent the content component for the dialog box. Make sure
|
||||
that this has been set to the size you want it to
|
||||
be before calling this method. The component won't
|
||||
be deleted by this call, so you can re-use it or delete
|
||||
it afterwards
|
||||
@param componentToCentreAround if this is not a nullptr, it indicates a component that
|
||||
you'd like to show this dialog box in front of. See the
|
||||
DocumentWindow::centreAroundComponent() method for more
|
||||
info on this parameter
|
||||
@param backgroundColour a colour to use for the dialog box's background colour
|
||||
@param escapeKeyTriggersCloseButton if true, then pressing the escape key will cause the
|
||||
close button to be triggered
|
||||
@param shouldBeResizable if true, the dialog window has either a resizable border, or
|
||||
a corner resizer
|
||||
@param useBottomRightCornerResizer if shouldBeResizable is true, this indicates whether
|
||||
to use a border or corner resizer component. See ResizableWindow::setResizable()
|
||||
*/
|
||||
static int showModalDialog (const String& dialogTitle,
|
||||
Component* contentComponent,
|
||||
Component* componentToCentreAround,
|
||||
Colour backgroundColour,
|
||||
bool escapeKeyTriggersCloseButton,
|
||||
bool shouldBeResizable = false,
|
||||
bool useBottomRightCornerResizer = false);
|
||||
#endif
|
||||
|
||||
|
||||
/** Called when the escape key is pressed.
|
||||
This can be overridden to do things other than the default behaviour, which is to hide
|
||||
the window. Return true if the key has been used, or false if it was ignored.
|
||||
*/
|
||||
virtual bool escapeKeyPressed();
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
float getDesktopScaleFactor() const override { return desktopScale * Desktop::getInstance().getGlobalScaleFactor(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
|
||||
float desktopScale = 1.0f;
|
||||
bool escapeKeyTriggersCloseButton;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DialogWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,360 +1,360 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class DocumentWindow::ButtonListenerProxy : public Button::Listener
|
||||
{
|
||||
public:
|
||||
ButtonListenerProxy (DocumentWindow& w) : owner (w) {}
|
||||
|
||||
void buttonClicked (Button* button) override
|
||||
{
|
||||
if (button == owner.getMinimiseButton()) owner.minimiseButtonPressed();
|
||||
else if (button == owner.getMaximiseButton()) owner.maximiseButtonPressed();
|
||||
else if (button == owner.getCloseButton()) owner.closeButtonPressed();
|
||||
}
|
||||
|
||||
private:
|
||||
DocumentWindow& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonListenerProxy)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
DocumentWindow::DocumentWindow (const String& title,
|
||||
Colour backgroundColour,
|
||||
int requiredButtons_,
|
||||
bool addToDesktop_)
|
||||
: ResizableWindow (title, backgroundColour, addToDesktop_),
|
||||
requiredButtons (requiredButtons_),
|
||||
#if JUCE_MAC
|
||||
positionTitleBarButtonsOnLeft (true)
|
||||
#else
|
||||
positionTitleBarButtonsOnLeft (false)
|
||||
#endif
|
||||
{
|
||||
setResizeLimits (128, 128, 32768, 32768);
|
||||
|
||||
DocumentWindow::lookAndFeelChanged();
|
||||
}
|
||||
|
||||
DocumentWindow::~DocumentWindow()
|
||||
{
|
||||
// Don't delete or remove the resizer components yourself! They're managed by the
|
||||
// DocumentWindow, and you should leave them alone! You may have deleted them
|
||||
// accidentally by careless use of deleteAllChildren()..?
|
||||
jassert (menuBar == nullptr || getIndexOfChildComponent (menuBar.get()) >= 0);
|
||||
jassert (titleBarButtons[0] == nullptr || getIndexOfChildComponent (titleBarButtons[0].get()) >= 0);
|
||||
jassert (titleBarButtons[1] == nullptr || getIndexOfChildComponent (titleBarButtons[1].get()) >= 0);
|
||||
jassert (titleBarButtons[2] == nullptr || getIndexOfChildComponent (titleBarButtons[2].get()) >= 0);
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
b.reset();
|
||||
|
||||
menuBar.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::repaintTitleBar()
|
||||
{
|
||||
repaint (getTitleBarArea());
|
||||
}
|
||||
|
||||
void DocumentWindow::setName (const String& newName)
|
||||
{
|
||||
if (newName != getName())
|
||||
{
|
||||
Component::setName (newName);
|
||||
repaintTitleBar();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentWindow::setIcon (const Image& imageToUse)
|
||||
{
|
||||
titleBarIcon = imageToUse;
|
||||
repaintTitleBar();
|
||||
}
|
||||
|
||||
void DocumentWindow::setTitleBarHeight (const int newHeight)
|
||||
{
|
||||
titleBarHeight = newHeight;
|
||||
resized();
|
||||
repaintTitleBar();
|
||||
}
|
||||
|
||||
void DocumentWindow::setTitleBarButtonsRequired (const int buttons, const bool onLeft)
|
||||
{
|
||||
requiredButtons = buttons;
|
||||
positionTitleBarButtonsOnLeft = onLeft;
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void DocumentWindow::setTitleBarTextCentred (const bool textShouldBeCentred)
|
||||
{
|
||||
drawTitleTextCentred = textShouldBeCentred;
|
||||
repaintTitleBar();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::setMenuBar (MenuBarModel* newMenuBarModel, const int newMenuBarHeight)
|
||||
{
|
||||
if (menuBarModel != newMenuBarModel)
|
||||
{
|
||||
menuBar.reset();
|
||||
|
||||
menuBarModel = newMenuBarModel;
|
||||
menuBarHeight = newMenuBarHeight > 0 ? newMenuBarHeight
|
||||
: getLookAndFeel().getDefaultMenuBarHeight();
|
||||
|
||||
if (menuBarModel != nullptr)
|
||||
setMenuBarComponent (new MenuBarComponent (menuBarModel));
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
Component* DocumentWindow::getMenuBarComponent() const noexcept
|
||||
{
|
||||
return menuBar.get();
|
||||
}
|
||||
|
||||
void DocumentWindow::setMenuBarComponent (Component* newMenuBarComponent)
|
||||
{
|
||||
menuBar.reset (newMenuBarComponent);
|
||||
Component::addAndMakeVisible (menuBar.get()); // (call the superclass method directly to avoid the assertion in ResizableWindow)
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setEnabled (isActiveWindow());
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::closeButtonPressed()
|
||||
{
|
||||
/* If you've got a close button, you have to override this method to get
|
||||
rid of your window!
|
||||
|
||||
If the window is just a pop-up, you should override this method and make
|
||||
it delete the window in whatever way is appropriate for your app. E.g. you
|
||||
might just want to call "delete this".
|
||||
|
||||
If your app is centred around this window such that the whole app should quit when
|
||||
the window is closed, then you will probably want to use this method as an opportunity
|
||||
to call JUCEApplicationBase::quit(), and leave the window to be deleted later by your
|
||||
JUCEApplicationBase::shutdown() method. (Doing it this way means that your window will
|
||||
still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac
|
||||
or closing it via the taskbar icon on Windows).
|
||||
*/
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
void DocumentWindow::minimiseButtonPressed()
|
||||
{
|
||||
setMinimised (true);
|
||||
}
|
||||
|
||||
void DocumentWindow::maximiseButtonPressed()
|
||||
{
|
||||
setFullScreen (! isFullScreen());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::paint (Graphics& g)
|
||||
{
|
||||
ResizableWindow::paint (g);
|
||||
|
||||
auto titleBarArea = getTitleBarArea();
|
||||
g.reduceClipRegion (titleBarArea);
|
||||
g.setOrigin (titleBarArea.getPosition());
|
||||
|
||||
int titleSpaceX1 = 6;
|
||||
int titleSpaceX2 = titleBarArea.getWidth() - 6;
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
{
|
||||
if (b != nullptr)
|
||||
{
|
||||
if (positionTitleBarButtonsOnLeft)
|
||||
titleSpaceX1 = jmax (titleSpaceX1, b->getRight() + (getWidth() - b->getRight()) / 8);
|
||||
else
|
||||
titleSpaceX2 = jmin (titleSpaceX2, b->getX() - (b->getX() / 8));
|
||||
}
|
||||
}
|
||||
|
||||
getLookAndFeel().drawDocumentWindowTitleBar (*this, g,
|
||||
titleBarArea.getWidth(),
|
||||
titleBarArea.getHeight(),
|
||||
titleSpaceX1,
|
||||
jmax (1, titleSpaceX2 - titleSpaceX1),
|
||||
titleBarIcon.isValid() ? &titleBarIcon : nullptr,
|
||||
! drawTitleTextCentred);
|
||||
}
|
||||
|
||||
void DocumentWindow::resized()
|
||||
{
|
||||
ResizableWindow::resized();
|
||||
|
||||
if (auto* b = getMaximiseButton())
|
||||
b->setToggleState (isFullScreen(), dontSendNotification);
|
||||
|
||||
auto titleBarArea = getTitleBarArea();
|
||||
|
||||
getLookAndFeel()
|
||||
.positionDocumentWindowButtons (*this,
|
||||
titleBarArea.getX(), titleBarArea.getY(),
|
||||
titleBarArea.getWidth(), titleBarArea.getHeight(),
|
||||
titleBarButtons[0].get(),
|
||||
titleBarButtons[1].get(),
|
||||
titleBarButtons[2].get(),
|
||||
positionTitleBarButtonsOnLeft);
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setBounds (titleBarArea.getX(), titleBarArea.getBottom(),
|
||||
titleBarArea.getWidth(), menuBarHeight);
|
||||
}
|
||||
|
||||
BorderSize<int> DocumentWindow::getBorderThickness()
|
||||
{
|
||||
return ResizableWindow::getBorderThickness();
|
||||
}
|
||||
|
||||
BorderSize<int> DocumentWindow::getContentComponentBorder()
|
||||
{
|
||||
auto border = getBorderThickness();
|
||||
|
||||
if (! isKioskMode())
|
||||
border.setTop (border.getTop()
|
||||
+ (isUsingNativeTitleBar() ? 0 : titleBarHeight)
|
||||
+ (menuBar != nullptr ? menuBarHeight : 0));
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
int DocumentWindow::getTitleBarHeight() const
|
||||
{
|
||||
return isUsingNativeTitleBar() ? 0 : jmin (titleBarHeight, getHeight() - 4);
|
||||
}
|
||||
|
||||
Rectangle<int> DocumentWindow::getTitleBarArea()
|
||||
{
|
||||
if (isKioskMode())
|
||||
return {};
|
||||
|
||||
auto border = getBorderThickness();
|
||||
return { border.getLeft(), border.getTop(), getWidth() - border.getLeftAndRight(), getTitleBarHeight() };
|
||||
}
|
||||
|
||||
Button* DocumentWindow::getCloseButton() const noexcept { return titleBarButtons[2].get(); }
|
||||
Button* DocumentWindow::getMinimiseButton() const noexcept { return titleBarButtons[0].get(); }
|
||||
Button* DocumentWindow::getMaximiseButton() const noexcept { return titleBarButtons[1].get(); }
|
||||
|
||||
int DocumentWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
auto styleFlags = ResizableWindow::getDesktopWindowStyleFlags();
|
||||
|
||||
if ((requiredButtons & minimiseButton) != 0) styleFlags |= ComponentPeer::windowHasMinimiseButton;
|
||||
if ((requiredButtons & maximiseButton) != 0) styleFlags |= ComponentPeer::windowHasMaximiseButton;
|
||||
if ((requiredButtons & closeButton) != 0) styleFlags |= ComponentPeer::windowHasCloseButton;
|
||||
|
||||
return styleFlags;
|
||||
}
|
||||
|
||||
void DocumentWindow::lookAndFeelChanged()
|
||||
{
|
||||
for (auto& b : titleBarButtons)
|
||||
b.reset();
|
||||
|
||||
if (! isUsingNativeTitleBar())
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
if ((requiredButtons & minimiseButton) != 0) titleBarButtons[0].reset (lf.createDocumentWindowButton (minimiseButton));
|
||||
if ((requiredButtons & maximiseButton) != 0) titleBarButtons[1].reset (lf.createDocumentWindowButton (maximiseButton));
|
||||
if ((requiredButtons & closeButton) != 0) titleBarButtons[2].reset (lf.createDocumentWindowButton (closeButton));
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
{
|
||||
if (b != nullptr)
|
||||
{
|
||||
if (buttonListener == nullptr)
|
||||
buttonListener.reset (new ButtonListenerProxy (*this));
|
||||
|
||||
b->addListener (buttonListener.get());
|
||||
b->setWantsKeyboardFocus (false);
|
||||
|
||||
// (call the Component method directly to avoid the assertion in ResizableWindow)
|
||||
Component::addAndMakeVisible (b.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* b = getCloseButton())
|
||||
{
|
||||
#if JUCE_MAC
|
||||
b->addShortcut (KeyPress ('w', ModifierKeys::commandModifier, 0));
|
||||
#else
|
||||
b->addShortcut (KeyPress (KeyPress::F4Key, ModifierKeys::altModifier, 0));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
activeWindowStatusChanged();
|
||||
|
||||
ResizableWindow::lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void DocumentWindow::parentHierarchyChanged()
|
||||
{
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void DocumentWindow::activeWindowStatusChanged()
|
||||
{
|
||||
ResizableWindow::activeWindowStatusChanged();
|
||||
bool isActive = isActiveWindow();
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
if (b != nullptr)
|
||||
b->setEnabled (isActive);
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setEnabled (isActive);
|
||||
}
|
||||
|
||||
void DocumentWindow::mouseDoubleClick (const MouseEvent& e)
|
||||
{
|
||||
if (getTitleBarArea().contains (e.x, e.y))
|
||||
if (auto* maximise = getMaximiseButton())
|
||||
maximise->triggerClick();
|
||||
}
|
||||
|
||||
void DocumentWindow::userTriedToCloseWindow()
|
||||
{
|
||||
closeButtonPressed();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class DocumentWindow::ButtonListenerProxy : public Button::Listener
|
||||
{
|
||||
public:
|
||||
ButtonListenerProxy (DocumentWindow& w) : owner (w) {}
|
||||
|
||||
void buttonClicked (Button* button) override
|
||||
{
|
||||
if (button == owner.getMinimiseButton()) owner.minimiseButtonPressed();
|
||||
else if (button == owner.getMaximiseButton()) owner.maximiseButtonPressed();
|
||||
else if (button == owner.getCloseButton()) owner.closeButtonPressed();
|
||||
}
|
||||
|
||||
private:
|
||||
DocumentWindow& owner;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonListenerProxy)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
DocumentWindow::DocumentWindow (const String& title,
|
||||
Colour backgroundColour,
|
||||
int requiredButtons_,
|
||||
bool addToDesktop_)
|
||||
: ResizableWindow (title, backgroundColour, addToDesktop_),
|
||||
requiredButtons (requiredButtons_),
|
||||
#if JUCE_MAC
|
||||
positionTitleBarButtonsOnLeft (true)
|
||||
#else
|
||||
positionTitleBarButtonsOnLeft (false)
|
||||
#endif
|
||||
{
|
||||
setResizeLimits (128, 128, 32768, 32768);
|
||||
|
||||
DocumentWindow::lookAndFeelChanged();
|
||||
}
|
||||
|
||||
DocumentWindow::~DocumentWindow()
|
||||
{
|
||||
// Don't delete or remove the resizer components yourself! They're managed by the
|
||||
// DocumentWindow, and you should leave them alone! You may have deleted them
|
||||
// accidentally by careless use of deleteAllChildren()..?
|
||||
jassert (menuBar == nullptr || getIndexOfChildComponent (menuBar.get()) >= 0);
|
||||
jassert (titleBarButtons[0] == nullptr || getIndexOfChildComponent (titleBarButtons[0].get()) >= 0);
|
||||
jassert (titleBarButtons[1] == nullptr || getIndexOfChildComponent (titleBarButtons[1].get()) >= 0);
|
||||
jassert (titleBarButtons[2] == nullptr || getIndexOfChildComponent (titleBarButtons[2].get()) >= 0);
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
b.reset();
|
||||
|
||||
menuBar.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::repaintTitleBar()
|
||||
{
|
||||
repaint (getTitleBarArea());
|
||||
}
|
||||
|
||||
void DocumentWindow::setName (const String& newName)
|
||||
{
|
||||
if (newName != getName())
|
||||
{
|
||||
Component::setName (newName);
|
||||
repaintTitleBar();
|
||||
}
|
||||
}
|
||||
|
||||
void DocumentWindow::setIcon (const Image& imageToUse)
|
||||
{
|
||||
titleBarIcon = imageToUse;
|
||||
repaintTitleBar();
|
||||
}
|
||||
|
||||
void DocumentWindow::setTitleBarHeight (const int newHeight)
|
||||
{
|
||||
titleBarHeight = newHeight;
|
||||
resized();
|
||||
repaintTitleBar();
|
||||
}
|
||||
|
||||
void DocumentWindow::setTitleBarButtonsRequired (const int buttons, const bool onLeft)
|
||||
{
|
||||
requiredButtons = buttons;
|
||||
positionTitleBarButtonsOnLeft = onLeft;
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void DocumentWindow::setTitleBarTextCentred (const bool textShouldBeCentred)
|
||||
{
|
||||
drawTitleTextCentred = textShouldBeCentred;
|
||||
repaintTitleBar();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::setMenuBar (MenuBarModel* newMenuBarModel, const int newMenuBarHeight)
|
||||
{
|
||||
if (menuBarModel != newMenuBarModel)
|
||||
{
|
||||
menuBar.reset();
|
||||
|
||||
menuBarModel = newMenuBarModel;
|
||||
menuBarHeight = newMenuBarHeight > 0 ? newMenuBarHeight
|
||||
: getLookAndFeel().getDefaultMenuBarHeight();
|
||||
|
||||
if (menuBarModel != nullptr)
|
||||
setMenuBarComponent (new MenuBarComponent (menuBarModel));
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
Component* DocumentWindow::getMenuBarComponent() const noexcept
|
||||
{
|
||||
return menuBar.get();
|
||||
}
|
||||
|
||||
void DocumentWindow::setMenuBarComponent (Component* newMenuBarComponent)
|
||||
{
|
||||
menuBar.reset (newMenuBarComponent);
|
||||
Component::addAndMakeVisible (menuBar.get()); // (call the superclass method directly to avoid the assertion in ResizableWindow)
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setEnabled (isActiveWindow());
|
||||
|
||||
resized();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::closeButtonPressed()
|
||||
{
|
||||
/* If you've got a close button, you have to override this method to get
|
||||
rid of your window!
|
||||
|
||||
If the window is just a pop-up, you should override this method and make
|
||||
it delete the window in whatever way is appropriate for your app. E.g. you
|
||||
might just want to call "delete this".
|
||||
|
||||
If your app is centred around this window such that the whole app should quit when
|
||||
the window is closed, then you will probably want to use this method as an opportunity
|
||||
to call JUCEApplicationBase::quit(), and leave the window to be deleted later by your
|
||||
JUCEApplicationBase::shutdown() method. (Doing it this way means that your window will
|
||||
still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac
|
||||
or closing it via the taskbar icon on Windows).
|
||||
*/
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
void DocumentWindow::minimiseButtonPressed()
|
||||
{
|
||||
setMinimised (true);
|
||||
}
|
||||
|
||||
void DocumentWindow::maximiseButtonPressed()
|
||||
{
|
||||
setFullScreen (! isFullScreen());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void DocumentWindow::paint (Graphics& g)
|
||||
{
|
||||
ResizableWindow::paint (g);
|
||||
|
||||
auto titleBarArea = getTitleBarArea();
|
||||
g.reduceClipRegion (titleBarArea);
|
||||
g.setOrigin (titleBarArea.getPosition());
|
||||
|
||||
int titleSpaceX1 = 6;
|
||||
int titleSpaceX2 = titleBarArea.getWidth() - 6;
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
{
|
||||
if (b != nullptr)
|
||||
{
|
||||
if (positionTitleBarButtonsOnLeft)
|
||||
titleSpaceX1 = jmax (titleSpaceX1, b->getRight() + (getWidth() - b->getRight()) / 8);
|
||||
else
|
||||
titleSpaceX2 = jmin (titleSpaceX2, b->getX() - (b->getX() / 8));
|
||||
}
|
||||
}
|
||||
|
||||
getLookAndFeel().drawDocumentWindowTitleBar (*this, g,
|
||||
titleBarArea.getWidth(),
|
||||
titleBarArea.getHeight(),
|
||||
titleSpaceX1,
|
||||
jmax (1, titleSpaceX2 - titleSpaceX1),
|
||||
titleBarIcon.isValid() ? &titleBarIcon : nullptr,
|
||||
! drawTitleTextCentred);
|
||||
}
|
||||
|
||||
void DocumentWindow::resized()
|
||||
{
|
||||
ResizableWindow::resized();
|
||||
|
||||
if (auto* b = getMaximiseButton())
|
||||
b->setToggleState (isFullScreen(), dontSendNotification);
|
||||
|
||||
auto titleBarArea = getTitleBarArea();
|
||||
|
||||
getLookAndFeel()
|
||||
.positionDocumentWindowButtons (*this,
|
||||
titleBarArea.getX(), titleBarArea.getY(),
|
||||
titleBarArea.getWidth(), titleBarArea.getHeight(),
|
||||
titleBarButtons[0].get(),
|
||||
titleBarButtons[1].get(),
|
||||
titleBarButtons[2].get(),
|
||||
positionTitleBarButtonsOnLeft);
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setBounds (titleBarArea.getX(), titleBarArea.getBottom(),
|
||||
titleBarArea.getWidth(), menuBarHeight);
|
||||
}
|
||||
|
||||
BorderSize<int> DocumentWindow::getBorderThickness()
|
||||
{
|
||||
return ResizableWindow::getBorderThickness();
|
||||
}
|
||||
|
||||
BorderSize<int> DocumentWindow::getContentComponentBorder()
|
||||
{
|
||||
auto border = getBorderThickness();
|
||||
|
||||
if (! isKioskMode())
|
||||
border.setTop (border.getTop()
|
||||
+ (isUsingNativeTitleBar() ? 0 : titleBarHeight)
|
||||
+ (menuBar != nullptr ? menuBarHeight : 0));
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
int DocumentWindow::getTitleBarHeight() const
|
||||
{
|
||||
return isUsingNativeTitleBar() ? 0 : jmin (titleBarHeight, getHeight() - 4);
|
||||
}
|
||||
|
||||
Rectangle<int> DocumentWindow::getTitleBarArea()
|
||||
{
|
||||
if (isKioskMode())
|
||||
return {};
|
||||
|
||||
auto border = getBorderThickness();
|
||||
return { border.getLeft(), border.getTop(), getWidth() - border.getLeftAndRight(), getTitleBarHeight() };
|
||||
}
|
||||
|
||||
Button* DocumentWindow::getCloseButton() const noexcept { return titleBarButtons[2].get(); }
|
||||
Button* DocumentWindow::getMinimiseButton() const noexcept { return titleBarButtons[0].get(); }
|
||||
Button* DocumentWindow::getMaximiseButton() const noexcept { return titleBarButtons[1].get(); }
|
||||
|
||||
int DocumentWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
auto styleFlags = ResizableWindow::getDesktopWindowStyleFlags();
|
||||
|
||||
if ((requiredButtons & minimiseButton) != 0) styleFlags |= ComponentPeer::windowHasMinimiseButton;
|
||||
if ((requiredButtons & maximiseButton) != 0) styleFlags |= ComponentPeer::windowHasMaximiseButton;
|
||||
if ((requiredButtons & closeButton) != 0) styleFlags |= ComponentPeer::windowHasCloseButton;
|
||||
|
||||
return styleFlags;
|
||||
}
|
||||
|
||||
void DocumentWindow::lookAndFeelChanged()
|
||||
{
|
||||
for (auto& b : titleBarButtons)
|
||||
b.reset();
|
||||
|
||||
if (! isUsingNativeTitleBar())
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
if ((requiredButtons & minimiseButton) != 0) titleBarButtons[0].reset (lf.createDocumentWindowButton (minimiseButton));
|
||||
if ((requiredButtons & maximiseButton) != 0) titleBarButtons[1].reset (lf.createDocumentWindowButton (maximiseButton));
|
||||
if ((requiredButtons & closeButton) != 0) titleBarButtons[2].reset (lf.createDocumentWindowButton (closeButton));
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
{
|
||||
if (b != nullptr)
|
||||
{
|
||||
if (buttonListener == nullptr)
|
||||
buttonListener.reset (new ButtonListenerProxy (*this));
|
||||
|
||||
b->addListener (buttonListener.get());
|
||||
b->setWantsKeyboardFocus (false);
|
||||
|
||||
// (call the Component method directly to avoid the assertion in ResizableWindow)
|
||||
Component::addAndMakeVisible (b.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* b = getCloseButton())
|
||||
{
|
||||
#if JUCE_MAC
|
||||
b->addShortcut (KeyPress ('w', ModifierKeys::commandModifier, 0));
|
||||
#else
|
||||
b->addShortcut (KeyPress (KeyPress::F4Key, ModifierKeys::altModifier, 0));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
activeWindowStatusChanged();
|
||||
|
||||
ResizableWindow::lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void DocumentWindow::parentHierarchyChanged()
|
||||
{
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
void DocumentWindow::activeWindowStatusChanged()
|
||||
{
|
||||
ResizableWindow::activeWindowStatusChanged();
|
||||
bool isActive = isActiveWindow();
|
||||
|
||||
for (auto& b : titleBarButtons)
|
||||
if (b != nullptr)
|
||||
b->setEnabled (isActive);
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setEnabled (isActive);
|
||||
}
|
||||
|
||||
void DocumentWindow::mouseDoubleClick (const MouseEvent& e)
|
||||
{
|
||||
if (getTitleBarArea().contains (e.x, e.y))
|
||||
if (auto* maximise = getMaximiseButton())
|
||||
maximise->triggerClick();
|
||||
}
|
||||
|
||||
void DocumentWindow::userTriedToCloseWindow()
|
||||
{
|
||||
closeButtonPressed();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,295 +1,307 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A resizable window with a title bar and maximise, minimise and close buttons.
|
||||
|
||||
This subclass of ResizableWindow creates a fairly standard type of window with
|
||||
a title bar and various buttons. The name of the component is shown in the
|
||||
title bar, and an icon can optionally be specified with setIcon().
|
||||
|
||||
All the methods available to a ResizableWindow are also available to this,
|
||||
so it can easily be made resizable, minimised, maximised, etc.
|
||||
|
||||
It's not advisable to add child components directly to a DocumentWindow: put them
|
||||
inside your content component instead. And overriding methods like resized(), moved(), etc
|
||||
is also not recommended - instead override these methods for your content component.
|
||||
(If for some obscure reason you do need to override these methods, always remember to
|
||||
call the super-class's resized() method too, otherwise it'll fail to lay out the window
|
||||
decorations correctly).
|
||||
|
||||
You can also automatically add a menu bar to the window, using the setMenuBar()
|
||||
method.
|
||||
|
||||
@see ResizableWindow, DialogWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DocumentWindow : public ResizableWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** The set of available button-types that can be put on the title bar.
|
||||
|
||||
@see setTitleBarButtonsRequired
|
||||
*/
|
||||
enum TitleBarButtons
|
||||
{
|
||||
minimiseButton = 1,
|
||||
maximiseButton = 2,
|
||||
closeButton = 4,
|
||||
|
||||
/** A combination of all the buttons above. */
|
||||
allButtons = 7
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a DocumentWindow.
|
||||
|
||||
@param name the name to give the component - this is also
|
||||
the title shown at the top of the window. To change
|
||||
this later, use setName()
|
||||
@param backgroundColour the colour to use for filling the window's background.
|
||||
@param requiredButtons specifies which of the buttons (close, minimise, maximise)
|
||||
should be shown on the title bar. This value is a bitwise
|
||||
combination of values from the TitleBarButtons enum. Note
|
||||
that it can be "allButtons" to get them all. You
|
||||
can change this later with the setTitleBarButtonsRequired()
|
||||
method, which can also specify where they are positioned.
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
@see TitleBarButtons
|
||||
*/
|
||||
DocumentWindow (const String& name,
|
||||
Colour backgroundColour,
|
||||
int requiredButtons,
|
||||
bool addToDesktop = true);
|
||||
|
||||
/** Destructor.
|
||||
If a content component has been set with setContentOwned(), it will be deleted.
|
||||
*/
|
||||
~DocumentWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the component's name.
|
||||
|
||||
(This is overridden from Component::setName() to cause a repaint, as
|
||||
the name is what gets drawn across the window's title bar).
|
||||
*/
|
||||
void setName (const String& newName) override;
|
||||
|
||||
/** Sets an icon to show in the title bar, next to the title.
|
||||
|
||||
A copy is made internally of the image, so the caller can delete the
|
||||
image after calling this. If an empty Image is passed-in, any existing icon
|
||||
will be removed.
|
||||
*/
|
||||
void setIcon (const Image& imageToUse);
|
||||
|
||||
/** Changes the height of the title-bar. */
|
||||
void setTitleBarHeight (int newHeight);
|
||||
|
||||
/** Returns the current title bar height. */
|
||||
int getTitleBarHeight() const;
|
||||
|
||||
/** Changes the set of title-bar buttons being shown.
|
||||
|
||||
@param requiredButtons specifies which of the buttons (close, minimise, maximise)
|
||||
should be shown on the title bar. This value is a bitwise
|
||||
combination of values from the TitleBarButtons enum. Note
|
||||
that it can be "allButtons" to get them all.
|
||||
@param positionTitleBarButtonsOnLeft if true, the buttons should go at the
|
||||
left side of the bar; if false, they'll be placed at the right
|
||||
*/
|
||||
void setTitleBarButtonsRequired (int requiredButtons,
|
||||
bool positionTitleBarButtonsOnLeft);
|
||||
|
||||
/** Sets whether the title should be centred within the window.
|
||||
|
||||
If true, the title text is shown in the middle of the title-bar; if false,
|
||||
it'll be shown at the left of the bar.
|
||||
*/
|
||||
void setTitleBarTextCentred (bool textShouldBeCentred);
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a menu inside this window.
|
||||
|
||||
@param menuBarModel this specifies a MenuBarModel that should be used to
|
||||
generate the contents of a menu bar that will be placed
|
||||
just below the title bar, and just above any content
|
||||
component. If this value is a nullptr, any existing menu bar
|
||||
will be removed from the component; if it is not a nullptr,
|
||||
one will be added if it's required.
|
||||
@param menuBarHeight the height of the menu bar component, if one is needed. Pass a value of zero
|
||||
or less to use the look-and-feel's default size.
|
||||
*/
|
||||
void setMenuBar (MenuBarModel* menuBarModel,
|
||||
int menuBarHeight = 0);
|
||||
|
||||
/** Returns the current menu bar component, or null if there isn't one.
|
||||
This is probably a MenuBarComponent, unless a custom one has been set using
|
||||
setMenuBarComponent().
|
||||
*/
|
||||
Component* getMenuBarComponent() const noexcept;
|
||||
|
||||
/** Replaces the current menu bar with a custom component.
|
||||
The component will be owned and deleted by the document window.
|
||||
*/
|
||||
void setMenuBarComponent (Component* newMenuBarComponent);
|
||||
|
||||
//==============================================================================
|
||||
/** This method is called when the user tries to close the window.
|
||||
|
||||
This is triggered by the user clicking the close button, or using some other
|
||||
OS-specific key shortcut or OS menu for getting rid of a window.
|
||||
|
||||
If the window is just a pop-up, you should override this closeButtonPressed()
|
||||
method and make it delete the window in whatever way is appropriate for your
|
||||
app. E.g. you might just want to call "delete this".
|
||||
|
||||
If your app is centred around this window such that the whole app should quit when
|
||||
the window is closed, then you will probably want to use this method as an opportunity
|
||||
to call JUCEApplicationBase::quit(), and leave the window to be deleted later by your
|
||||
JUCEApplicationBase::shutdown() method. (Doing it this way means that your window will
|
||||
still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac
|
||||
or closing it via the taskbar icon on Windows).
|
||||
|
||||
(Note that the DocumentWindow class overrides Component::userTriedToCloseWindow() and
|
||||
redirects it to call this method, so any methods of closing the window that are
|
||||
caught by userTriedToCloseWindow() will also end up here).
|
||||
*/
|
||||
virtual void closeButtonPressed();
|
||||
|
||||
/** Callback that is triggered when the minimise button is pressed.
|
||||
|
||||
The default implementation of this calls ResizableWindow::setMinimised(), but
|
||||
you can override it to do more customised behaviour.
|
||||
*/
|
||||
virtual void minimiseButtonPressed();
|
||||
|
||||
/** Callback that is triggered when the maximise button is pressed, or when the
|
||||
title-bar is double-clicked.
|
||||
|
||||
The default implementation of this calls ResizableWindow::setFullScreen(), but
|
||||
you can override it to do more customised behaviour.
|
||||
*/
|
||||
virtual void maximiseButtonPressed();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the close button, (or nullptr if there isn't one). */
|
||||
Button* getCloseButton() const noexcept;
|
||||
|
||||
/** Returns the minimise button, (or nullptr if there isn't one). */
|
||||
Button* getMinimiseButton() const noexcept;
|
||||
|
||||
/** Returns the maximise button, (or nullptr if there isn't one). */
|
||||
Button* getMaximiseButton() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the window.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1005701, /**< The colour to draw any text with. It's up to the look
|
||||
and feel class how this is used. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawDocumentWindowTitleBar (DocumentWindow&,
|
||||
Graphics&, int w, int h,
|
||||
int titleSpaceX, int titleSpaceW,
|
||||
const Image* icon,
|
||||
bool drawTitleTextOnLeft) = 0;
|
||||
|
||||
virtual Button* createDocumentWindowButton (int buttonType) = 0;
|
||||
|
||||
virtual void positionDocumentWindowButtons (DocumentWindow&,
|
||||
int titleBarX, int titleBarY, int titleBarW, int titleBarH,
|
||||
Button* minimiseButton,
|
||||
Button* maximiseButton,
|
||||
Button* closeButton,
|
||||
bool positionTitleBarButtonsOnLeft) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
BorderSize<int> getBorderThickness() override;
|
||||
/** @internal */
|
||||
BorderSize<int> getContentComponentBorder() override;
|
||||
/** @internal */
|
||||
void mouseDoubleClick (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void userTriedToCloseWindow() override;
|
||||
/** @internal */
|
||||
void activeWindowStatusChanged() override;
|
||||
/** @internal */
|
||||
int getDesktopWindowStyleFlags() const override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
Rectangle<int> getTitleBarArea();
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int titleBarHeight = 26, menuBarHeight = 24, requiredButtons;
|
||||
bool positionTitleBarButtonsOnLeft, drawTitleTextCentred = true;
|
||||
std::unique_ptr<Button> titleBarButtons [3];
|
||||
Image titleBarIcon;
|
||||
std::unique_ptr<Component> menuBar;
|
||||
MenuBarModel* menuBarModel = nullptr;
|
||||
|
||||
class ButtonListenerProxy;
|
||||
std::unique_ptr<ButtonListenerProxy> buttonListener;
|
||||
|
||||
void repaintTitleBar();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A resizable window with a title bar and maximise, minimise and close buttons.
|
||||
|
||||
This subclass of ResizableWindow creates a fairly standard type of window with
|
||||
a title bar and various buttons. The name of the component is shown in the
|
||||
title bar, and an icon can optionally be specified with setIcon().
|
||||
|
||||
All the methods available to a ResizableWindow are also available to this,
|
||||
so it can easily be made resizable, minimised, maximised, etc.
|
||||
|
||||
It's not advisable to add child components directly to a DocumentWindow: put them
|
||||
inside your content component instead. And overriding methods like resized(), moved(), etc
|
||||
is also not recommended - instead override these methods for your content component.
|
||||
(If for some obscure reason you do need to override these methods, always remember to
|
||||
call the super-class's resized() method too, otherwise it'll fail to lay out the window
|
||||
decorations correctly).
|
||||
|
||||
You can also automatically add a menu bar to the window, using the setMenuBar()
|
||||
method.
|
||||
|
||||
@see ResizableWindow, DialogWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API DocumentWindow : public ResizableWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** The set of available button-types that can be put on the title bar.
|
||||
|
||||
@see setTitleBarButtonsRequired
|
||||
*/
|
||||
enum TitleBarButtons
|
||||
{
|
||||
minimiseButton = 1,
|
||||
maximiseButton = 2,
|
||||
closeButton = 4,
|
||||
|
||||
/** A combination of all the buttons above. */
|
||||
allButtons = 7
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a DocumentWindow.
|
||||
|
||||
@param name the name to give the component - this is also
|
||||
the title shown at the top of the window. To change
|
||||
this later, use setName()
|
||||
@param backgroundColour the colour to use for filling the window's background.
|
||||
@param requiredButtons specifies which of the buttons (close, minimise, maximise)
|
||||
should be shown on the title bar. This value is a bitwise
|
||||
combination of values from the TitleBarButtons enum. Note
|
||||
that it can be "allButtons" to get them all. You
|
||||
can change this later with the setTitleBarButtonsRequired()
|
||||
method, which can also specify where they are positioned.
|
||||
The behaviour of native titlebars on macOS is slightly different:
|
||||
the maximiseButton flag controls whether or not the window can enter
|
||||
native fullscreen mode, and the zoom button can be disabled by
|
||||
making the window non-resizable.
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
@see TitleBarButtons
|
||||
*/
|
||||
DocumentWindow (const String& name,
|
||||
Colour backgroundColour,
|
||||
int requiredButtons,
|
||||
bool addToDesktop = true);
|
||||
|
||||
/** Destructor.
|
||||
If a content component has been set with setContentOwned(), it will be deleted.
|
||||
*/
|
||||
~DocumentWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the component's name.
|
||||
|
||||
(This is overridden from Component::setName() to cause a repaint, as
|
||||
the name is what gets drawn across the window's title bar).
|
||||
*/
|
||||
void setName (const String& newName) override;
|
||||
|
||||
/** Sets an icon to show in the title bar, next to the title.
|
||||
|
||||
A copy is made internally of the image, so the caller can delete the
|
||||
image after calling this. If an empty Image is passed-in, any existing icon
|
||||
will be removed.
|
||||
*/
|
||||
void setIcon (const Image& imageToUse);
|
||||
|
||||
/** Changes the height of the title-bar. */
|
||||
void setTitleBarHeight (int newHeight);
|
||||
|
||||
/** Returns the current title bar height. */
|
||||
int getTitleBarHeight() const;
|
||||
|
||||
/** Changes the set of title-bar buttons being shown.
|
||||
|
||||
@param requiredButtons specifies which of the buttons (close, minimise, maximise)
|
||||
should be shown on the title bar. This value is a bitwise
|
||||
combination of values from the TitleBarButtons enum. Note
|
||||
that it can be "allButtons" to get them all.
|
||||
The behaviour of native titlebars on macOS is slightly different:
|
||||
the maximiseButton flag controls whether or not the window can enter
|
||||
native fullscreen mode, and the zoom button can be disabled by
|
||||
making the window non-resizable.
|
||||
@param positionTitleBarButtonsOnLeft if true, the buttons should go at the
|
||||
left side of the bar; if false, they'll be placed at the right
|
||||
*/
|
||||
void setTitleBarButtonsRequired (int requiredButtons,
|
||||
bool positionTitleBarButtonsOnLeft);
|
||||
|
||||
/** Sets whether the title should be centred within the window.
|
||||
|
||||
If true, the title text is shown in the middle of the title-bar; if false,
|
||||
it'll be shown at the left of the bar.
|
||||
*/
|
||||
void setTitleBarTextCentred (bool textShouldBeCentred);
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a menu inside this window.
|
||||
|
||||
@param menuBarModel this specifies a MenuBarModel that should be used to
|
||||
generate the contents of a menu bar that will be placed
|
||||
just below the title bar, and just above any content
|
||||
component. If this value is a nullptr, any existing menu bar
|
||||
will be removed from the component; if it is not a nullptr,
|
||||
one will be added if it's required.
|
||||
@param menuBarHeight the height of the menu bar component, if one is needed. Pass a value of zero
|
||||
or less to use the look-and-feel's default size.
|
||||
*/
|
||||
void setMenuBar (MenuBarModel* menuBarModel,
|
||||
int menuBarHeight = 0);
|
||||
|
||||
/** Returns the current menu bar component, or null if there isn't one.
|
||||
This is probably a MenuBarComponent, unless a custom one has been set using
|
||||
setMenuBarComponent().
|
||||
*/
|
||||
Component* getMenuBarComponent() const noexcept;
|
||||
|
||||
/** Replaces the current menu bar with a custom component.
|
||||
The component will be owned and deleted by the document window.
|
||||
*/
|
||||
void setMenuBarComponent (Component* newMenuBarComponent);
|
||||
|
||||
//==============================================================================
|
||||
/** This method is called when the user tries to close the window.
|
||||
|
||||
This is triggered by the user clicking the close button, or using some other
|
||||
OS-specific key shortcut or OS menu for getting rid of a window.
|
||||
|
||||
If the window is just a pop-up, you should override this closeButtonPressed()
|
||||
method and make it delete the window in whatever way is appropriate for your
|
||||
app. E.g. you might just want to call "delete this".
|
||||
|
||||
If your app is centred around this window such that the whole app should quit when
|
||||
the window is closed, then you will probably want to use this method as an opportunity
|
||||
to call JUCEApplicationBase::quit(), and leave the window to be deleted later by your
|
||||
JUCEApplicationBase::shutdown() method. (Doing it this way means that your window will
|
||||
still get cleaned-up if the app is quit by some other means (e.g. a cmd-Q on the mac
|
||||
or closing it via the taskbar icon on Windows).
|
||||
|
||||
(Note that the DocumentWindow class overrides Component::userTriedToCloseWindow() and
|
||||
redirects it to call this method, so any methods of closing the window that are
|
||||
caught by userTriedToCloseWindow() will also end up here).
|
||||
*/
|
||||
virtual void closeButtonPressed();
|
||||
|
||||
/** Callback that is triggered when the minimise button is pressed.
|
||||
|
||||
This function is only called when using a non-native titlebar.
|
||||
|
||||
The default implementation of this calls ResizableWindow::setMinimised(), but
|
||||
you can override it to do more customised behaviour.
|
||||
*/
|
||||
virtual void minimiseButtonPressed();
|
||||
|
||||
/** Callback that is triggered when the maximise button is pressed, or when the
|
||||
title-bar is double-clicked.
|
||||
|
||||
This function is only called when using a non-native titlebar.
|
||||
|
||||
The default implementation of this calls ResizableWindow::setFullScreen(), but
|
||||
you can override it to do more customised behaviour.
|
||||
*/
|
||||
virtual void maximiseButtonPressed();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the close button, (or nullptr if there isn't one). */
|
||||
Button* getCloseButton() const noexcept;
|
||||
|
||||
/** Returns the minimise button, (or nullptr if there isn't one). */
|
||||
Button* getMinimiseButton() const noexcept;
|
||||
|
||||
/** Returns the maximise button, (or nullptr if there isn't one). */
|
||||
Button* getMaximiseButton() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the window.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
textColourId = 0x1005701, /**< The colour to draw any text with. It's up to the look
|
||||
and feel class how this is used. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual void drawDocumentWindowTitleBar (DocumentWindow&,
|
||||
Graphics&, int w, int h,
|
||||
int titleSpaceX, int titleSpaceW,
|
||||
const Image* icon,
|
||||
bool drawTitleTextOnLeft) = 0;
|
||||
|
||||
virtual Button* createDocumentWindowButton (int buttonType) = 0;
|
||||
|
||||
virtual void positionDocumentWindowButtons (DocumentWindow&,
|
||||
int titleBarX, int titleBarY, int titleBarW, int titleBarH,
|
||||
Button* minimiseButton,
|
||||
Button* maximiseButton,
|
||||
Button* closeButton,
|
||||
bool positionTitleBarButtonsOnLeft) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
BorderSize<int> getBorderThickness() override;
|
||||
/** @internal */
|
||||
BorderSize<int> getContentComponentBorder() override;
|
||||
/** @internal */
|
||||
void mouseDoubleClick (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void userTriedToCloseWindow() override;
|
||||
/** @internal */
|
||||
void activeWindowStatusChanged() override;
|
||||
/** @internal */
|
||||
int getDesktopWindowStyleFlags() const override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
Rectangle<int> getTitleBarArea();
|
||||
#endif
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int titleBarHeight = 26, menuBarHeight = 24, requiredButtons;
|
||||
bool positionTitleBarButtonsOnLeft, drawTitleTextCentred = true;
|
||||
std::unique_ptr<Button> titleBarButtons [3];
|
||||
Image titleBarIcon;
|
||||
std::unique_ptr<Component> menuBar;
|
||||
MenuBarModel* menuBarModel = nullptr;
|
||||
|
||||
class ButtonListenerProxy;
|
||||
std::unique_ptr<ButtonListenerProxy> buttonListener;
|
||||
|
||||
void repaintTitleBar();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,143 +1,143 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** The type of icon to show in the dialog box. */
|
||||
enum class MessageBoxIconType
|
||||
{
|
||||
NoIcon, /**< No icon will be shown on the dialog box. */
|
||||
QuestionIcon, /**< A question-mark icon, for dialog boxes that need the
|
||||
user to answer a question. */
|
||||
WarningIcon, /**< An exclamation mark to indicate that the dialog is a
|
||||
warning about something and shouldn't be ignored. */
|
||||
InfoIcon /**< An icon that indicates that the dialog box is just
|
||||
giving the user some information, which doesn't require
|
||||
a response from them. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Class used to create a set of options to pass to the AlertWindow and NativeMessageBox
|
||||
methods for showing dialog boxes.
|
||||
|
||||
You can chain together a series of calls to this class's methods to create
|
||||
a set of whatever options you want to specify.
|
||||
|
||||
E.g. @code
|
||||
AlertWindow::showAsync (MessageBoxOptions()
|
||||
.withIconType (MessageBoxIconType::InfoIcon)
|
||||
.withTitle ("A Title")
|
||||
.withMessage ("A message.")
|
||||
.withButton ("OK")
|
||||
.withButton ("Cancel")
|
||||
.withAssociatedComponent (myComp),
|
||||
myCallback);
|
||||
@endcode
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API MessageBoxOptions
|
||||
{
|
||||
public:
|
||||
MessageBoxOptions() = default;
|
||||
MessageBoxOptions (const MessageBoxOptions&) = default;
|
||||
MessageBoxOptions& operator= (const MessageBoxOptions&) = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the type of icon that should be used for the dialog box. */
|
||||
MessageBoxOptions withIconType (MessageBoxIconType type) const { return with (*this, &MessageBoxOptions::iconType, type); }
|
||||
|
||||
/** Sets the title of the dialog box. */
|
||||
MessageBoxOptions withTitle (const String& boxTitle) const { return with (*this, &MessageBoxOptions::title, boxTitle); }
|
||||
|
||||
/** Sets the message that should be displayed in the dialog box. */
|
||||
MessageBoxOptions withMessage (const String& boxMessage) const { return with (*this, &MessageBoxOptions::message, boxMessage); }
|
||||
|
||||
/** If the string passed in is not empty, this will add a button to the
|
||||
dialog box with the specified text.
|
||||
|
||||
Generally up to 3 buttons are supported for dialog boxes, so adding any more
|
||||
than this may have no effect.
|
||||
*/
|
||||
MessageBoxOptions withButton (const String& text) const { auto copy = *this; copy.buttons.add (text); return copy; }
|
||||
|
||||
/** The component that the dialog box should be associated with. */
|
||||
MessageBoxOptions withAssociatedComponent (Component* component) const { return with (*this, &MessageBoxOptions::associatedComponent, component); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the icon type of the dialog box.
|
||||
|
||||
@see withIconType
|
||||
*/
|
||||
MessageBoxIconType getIconType() const noexcept { return iconType; }
|
||||
|
||||
/** Returns the title of the dialog box.
|
||||
|
||||
@see withTitle
|
||||
*/
|
||||
String getTitle() const { return title; }
|
||||
|
||||
/** Returns the message of the dialog box.
|
||||
|
||||
@see withMessage
|
||||
*/
|
||||
String getMessage() const { return message; }
|
||||
|
||||
/** Returns the number of buttons that have been added to the dialog box.
|
||||
|
||||
@see withButtonText
|
||||
*/
|
||||
int getNumButtons() const noexcept { return buttons.size(); }
|
||||
|
||||
/** Returns the text that has been set for one of the buttons of the dialog box.
|
||||
|
||||
@see withButtonText, getNumButtons
|
||||
*/
|
||||
String getButtonText (int buttonIndex) const { return buttons[buttonIndex]; }
|
||||
|
||||
/** Returns the component that the dialog box is associated with.
|
||||
|
||||
@see withAssociatedComponent
|
||||
*/
|
||||
Component* getAssociatedComponent() const noexcept { return associatedComponent; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename Member, typename Item>
|
||||
static MessageBoxOptions with (MessageBoxOptions options, Member&& member, Item&& item)
|
||||
{
|
||||
options.*member = std::forward<Item> (item);
|
||||
return options;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MessageBoxIconType iconType = MessageBoxIconType::InfoIcon;
|
||||
String title, message;
|
||||
StringArray buttons;
|
||||
WeakReference<Component> associatedComponent;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** The type of icon to show in the dialog box. */
|
||||
enum class MessageBoxIconType
|
||||
{
|
||||
NoIcon, /**< No icon will be shown on the dialog box. */
|
||||
QuestionIcon, /**< A question-mark icon, for dialog boxes that need the
|
||||
user to answer a question. */
|
||||
WarningIcon, /**< An exclamation mark to indicate that the dialog is a
|
||||
warning about something and shouldn't be ignored. */
|
||||
InfoIcon /**< An icon that indicates that the dialog box is just
|
||||
giving the user some information, which doesn't require
|
||||
a response from them. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Class used to create a set of options to pass to the AlertWindow and NativeMessageBox
|
||||
methods for showing dialog boxes.
|
||||
|
||||
You can chain together a series of calls to this class's methods to create
|
||||
a set of whatever options you want to specify.
|
||||
|
||||
E.g. @code
|
||||
AlertWindow::showAsync (MessageBoxOptions()
|
||||
.withIconType (MessageBoxIconType::InfoIcon)
|
||||
.withTitle ("A Title")
|
||||
.withMessage ("A message.")
|
||||
.withButton ("OK")
|
||||
.withButton ("Cancel")
|
||||
.withAssociatedComponent (myComp),
|
||||
myCallback);
|
||||
@endcode
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API MessageBoxOptions
|
||||
{
|
||||
public:
|
||||
MessageBoxOptions() = default;
|
||||
MessageBoxOptions (const MessageBoxOptions&) = default;
|
||||
MessageBoxOptions& operator= (const MessageBoxOptions&) = default;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the type of icon that should be used for the dialog box. */
|
||||
JUCE_NODISCARD MessageBoxOptions withIconType (MessageBoxIconType type) const { return with (*this, &MessageBoxOptions::iconType, type); }
|
||||
|
||||
/** Sets the title of the dialog box. */
|
||||
JUCE_NODISCARD MessageBoxOptions withTitle (const String& boxTitle) const { return with (*this, &MessageBoxOptions::title, boxTitle); }
|
||||
|
||||
/** Sets the message that should be displayed in the dialog box. */
|
||||
JUCE_NODISCARD MessageBoxOptions withMessage (const String& boxMessage) const { return with (*this, &MessageBoxOptions::message, boxMessage); }
|
||||
|
||||
/** If the string passed in is not empty, this will add a button to the
|
||||
dialog box with the specified text.
|
||||
|
||||
Generally up to 3 buttons are supported for dialog boxes, so adding any more
|
||||
than this may have no effect.
|
||||
*/
|
||||
JUCE_NODISCARD MessageBoxOptions withButton (const String& text) const { auto copy = *this; copy.buttons.add (text); return copy; }
|
||||
|
||||
/** The component that the dialog box should be associated with. */
|
||||
JUCE_NODISCARD MessageBoxOptions withAssociatedComponent (Component* component) const { return with (*this, &MessageBoxOptions::associatedComponent, component); }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the icon type of the dialog box.
|
||||
|
||||
@see withIconType
|
||||
*/
|
||||
MessageBoxIconType getIconType() const noexcept { return iconType; }
|
||||
|
||||
/** Returns the title of the dialog box.
|
||||
|
||||
@see withTitle
|
||||
*/
|
||||
String getTitle() const { return title; }
|
||||
|
||||
/** Returns the message of the dialog box.
|
||||
|
||||
@see withMessage
|
||||
*/
|
||||
String getMessage() const { return message; }
|
||||
|
||||
/** Returns the number of buttons that have been added to the dialog box.
|
||||
|
||||
@see withButtonText
|
||||
*/
|
||||
int getNumButtons() const noexcept { return buttons.size(); }
|
||||
|
||||
/** Returns the text that has been set for one of the buttons of the dialog box.
|
||||
|
||||
@see withButtonText, getNumButtons
|
||||
*/
|
||||
String getButtonText (int buttonIndex) const { return buttons[buttonIndex]; }
|
||||
|
||||
/** Returns the component that the dialog box is associated with.
|
||||
|
||||
@see withAssociatedComponent
|
||||
*/
|
||||
Component* getAssociatedComponent() const noexcept { return associatedComponent; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename Member, typename Item>
|
||||
static MessageBoxOptions with (MessageBoxOptions options, Member&& member, Item&& item)
|
||||
{
|
||||
options.*member = std::forward<Item> (item);
|
||||
return options;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MessageBoxIconType iconType = MessageBoxIconType::InfoIcon;
|
||||
String title, message;
|
||||
StringArray buttons;
|
||||
WeakReference<Component> associatedComponent;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,265 +1,265 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class contains some static methods for showing native alert windows.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class NativeMessageBox
|
||||
{
|
||||
public:
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Shows a dialog box that just has a message and a single 'ok' button to close it.
|
||||
|
||||
The box is shown modally, and the method will block until the user has clicked its
|
||||
button (or pressed the escape or return keys).
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
*/
|
||||
static void JUCE_CALLTYPE showMessageBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
Component* associatedComponent = nullptr);
|
||||
|
||||
/** Shows a dialog box using the specified options.
|
||||
|
||||
The box is shown modally, and the method will block until the user dismisses it.
|
||||
|
||||
@param options the options to use when creating the dialog.
|
||||
|
||||
@returns the index of the button that was clicked.
|
||||
|
||||
@see MessageBoxOptions
|
||||
*/
|
||||
static int JUCE_CALLTYPE show (const MessageBoxOptions& options);
|
||||
#endif
|
||||
|
||||
/** Shows a dialog box using the specified options.
|
||||
|
||||
The box will be displayed and placed into a modal state, but this method will return
|
||||
immediately, and the callback will be invoked later when the user dismisses the box.
|
||||
|
||||
@param options the options to use when creating the dialog.
|
||||
@param callback if this is non-null, the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed with the index of the
|
||||
button that was clicked as its argument.
|
||||
The callback object will be owned and deleted by the system, so make sure
|
||||
that it works safely and doesn't keep any references to objects that might
|
||||
be deleted before it gets called.
|
||||
|
||||
@see MessageBoxOptions
|
||||
*/
|
||||
static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options,
|
||||
ModalComponentManager::Callback* callback);
|
||||
|
||||
/** Shows a dialog box using the specified options.
|
||||
|
||||
The box will be displayed and placed into a modal state, but this method will return
|
||||
immediately, and the callback will be invoked later when the user dismisses the box.
|
||||
|
||||
@param options the options to use when creating the dialog.
|
||||
@param callback if this is non-null, the callback will be called when the box is
|
||||
dismissed with the index of the button that was clicked as its argument.
|
||||
|
||||
@see MessageBoxOptions
|
||||
*/
|
||||
static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options,
|
||||
std::function<void (int)> callback);
|
||||
|
||||
/** Shows a dialog box that just has a message and a single 'ok' button to close it.
|
||||
|
||||
The box will be displayed and placed into a modal state, but this method will return
|
||||
immediately, and the callback will be invoked later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed. The callback object
|
||||
will be owned and deleted by the system, so make sure that it works
|
||||
safely and doesn't keep any references to objects that might be deleted
|
||||
before it gets called. You can use the ModalCallbackFunction to easily
|
||||
pass in a lambda for this parameter.
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static void JUCE_CALLTYPE showMessageBoxAsync (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
|
||||
/** Shows a dialog box with two buttons.
|
||||
|
||||
Ideal for ok/cancel or yes/no choices. The return key can also be used
|
||||
to trigger the first button, and the escape key for the second button.
|
||||
|
||||
If the callback parameter is null and modal loops are enabled, the box is shown modally,
|
||||
and the method will block until the user has clicked the button (or pressed the escape or
|
||||
return keys). If the callback parameter is non-null, the box will be displayed and placed
|
||||
into a modal state, but this method will return immediately, and the callback will be invoked
|
||||
later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the box will be launched asynchronously,
|
||||
returning immediately, and the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed, with its parameter
|
||||
being 1 if the ok button was pressed, or 0 for cancel, The callback object
|
||||
will be owned and deleted by the system, so make sure that it works
|
||||
safely and doesn't keep any references to objects that might be deleted
|
||||
before it gets called. You can use the ModalCallbackFunction to easily
|
||||
pass in a lambda for this parameter.
|
||||
@returns true if button 1 was clicked, false if it was button 2. If the callback parameter
|
||||
is not null, the method always returns false, and the user's choice is delivered
|
||||
later by the callback.
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static bool JUCE_CALLTYPE showOkCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
/** Shows a dialog box with three buttons.
|
||||
|
||||
Ideal for yes/no/cancel boxes.
|
||||
|
||||
The escape key can be used to trigger the third button.
|
||||
|
||||
If the callback parameter is null and modal loops are enabled, the box is shown modally,
|
||||
and the method will block until the user has clicked the button (or pressed the escape or
|
||||
return keys). If the callback parameter is non-null, the box will be displayed and placed
|
||||
into a modal state, but this method will return immediately, and the callback will be invoked
|
||||
later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the box will be launched asynchronously,
|
||||
returning immediately, and the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed, with its parameter
|
||||
being 1 if the "yes" button was pressed, 2 for the "no" button, or 0
|
||||
if it was cancelled, The callback object will be owned and deleted by the
|
||||
system, so make sure that it works safely and doesn't keep any references
|
||||
to objects that might be deleted before it gets called. You can use the
|
||||
ModalCallbackFunction to easily pass in a lambda for this parameter.
|
||||
|
||||
@returns If the callback parameter has been set, this returns 0. Otherwise, it returns one
|
||||
of the following values:
|
||||
- 0 if 'cancel' was pressed
|
||||
- 1 if 'yes' was pressed
|
||||
- 2 if 'no' was pressed
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static int JUCE_CALLTYPE showYesNoCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
/** Shows a dialog box with two buttons.
|
||||
|
||||
Ideal for yes/no boxes.
|
||||
|
||||
The escape key can be used to trigger the no button.
|
||||
|
||||
If the callback parameter is null and modal loops are enabled, the box is shown modally,
|
||||
and the method will block until the user has clicked the button (or pressed the escape or
|
||||
return keys). If the callback parameter is non-null, the box will be displayed and placed
|
||||
into a modal state, but this method will return immediately, and the callback will be invoked
|
||||
later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the box will be launched asynchronously,
|
||||
returning immediately, and the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed, with its parameter
|
||||
being 1 if the "yes" button was pressed or 0 for the "no" button was
|
||||
pressed. The callback object will be owned and deleted by the
|
||||
system, so make sure that it works safely and doesn't keep any references
|
||||
to objects that might be deleted before it gets called. You can use the
|
||||
ModalCallbackFunction to easily pass in a lambda for this parameter.
|
||||
|
||||
@returns If the callback parameter has been set, this returns 0. Otherwise, it returns one
|
||||
of the following values:
|
||||
- 0 if 'no' was pressed
|
||||
- 1 if 'yes' was pressed
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static int JUCE_CALLTYPE showYesNoBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
private:
|
||||
NativeMessageBox() = delete;
|
||||
JUCE_DECLARE_NON_COPYABLE (NativeMessageBox)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class contains some static methods for showing native alert windows.
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class NativeMessageBox
|
||||
{
|
||||
public:
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Shows a dialog box that just has a message and a single 'ok' button to close it.
|
||||
|
||||
The box is shown modally, and the method will block until the user has clicked its
|
||||
button (or pressed the escape or return keys).
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
*/
|
||||
static void JUCE_CALLTYPE showMessageBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
Component* associatedComponent = nullptr);
|
||||
|
||||
/** Shows a dialog box using the specified options.
|
||||
|
||||
The box is shown modally, and the method will block until the user dismisses it.
|
||||
|
||||
@param options the options to use when creating the dialog.
|
||||
|
||||
@returns the index of the button that was clicked.
|
||||
|
||||
@see MessageBoxOptions
|
||||
*/
|
||||
static int JUCE_CALLTYPE show (const MessageBoxOptions& options);
|
||||
#endif
|
||||
|
||||
/** Shows a dialog box using the specified options.
|
||||
|
||||
The box will be displayed and placed into a modal state, but this method will return
|
||||
immediately, and the callback will be invoked later when the user dismisses the box.
|
||||
|
||||
@param options the options to use when creating the dialog.
|
||||
@param callback if this is non-null, the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed with the index of the
|
||||
button that was clicked as its argument.
|
||||
The callback object will be owned and deleted by the system, so make sure
|
||||
that it works safely and doesn't keep any references to objects that might
|
||||
be deleted before it gets called.
|
||||
|
||||
@see MessageBoxOptions
|
||||
*/
|
||||
static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options,
|
||||
ModalComponentManager::Callback* callback);
|
||||
|
||||
/** Shows a dialog box using the specified options.
|
||||
|
||||
The box will be displayed and placed into a modal state, but this method will return
|
||||
immediately, and the callback will be invoked later when the user dismisses the box.
|
||||
|
||||
@param options the options to use when creating the dialog.
|
||||
@param callback if this is non-null, the callback will be called when the box is
|
||||
dismissed with the index of the button that was clicked as its argument.
|
||||
|
||||
@see MessageBoxOptions
|
||||
*/
|
||||
static void JUCE_CALLTYPE showAsync (const MessageBoxOptions& options,
|
||||
std::function<void (int)> callback);
|
||||
|
||||
/** Shows a dialog box that just has a message and a single 'ok' button to close it.
|
||||
|
||||
The box will be displayed and placed into a modal state, but this method will return
|
||||
immediately, and the callback will be invoked later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed. The callback object
|
||||
will be owned and deleted by the system, so make sure that it works
|
||||
safely and doesn't keep any references to objects that might be deleted
|
||||
before it gets called. You can use the ModalCallbackFunction to easily
|
||||
pass in a lambda for this parameter.
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static void JUCE_CALLTYPE showMessageBoxAsync (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
|
||||
/** Shows a dialog box with two buttons.
|
||||
|
||||
Ideal for ok/cancel or yes/no choices. The return key can also be used
|
||||
to trigger the first button, and the escape key for the second button.
|
||||
|
||||
If the callback parameter is null and modal loops are enabled, the box is shown modally,
|
||||
and the method will block until the user has clicked the button (or pressed the escape or
|
||||
return keys). If the callback parameter is non-null, the box will be displayed and placed
|
||||
into a modal state, but this method will return immediately, and the callback will be invoked
|
||||
later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the box will be launched asynchronously,
|
||||
returning immediately, and the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed, with its parameter
|
||||
being 1 if the ok button was pressed, or 0 for cancel, The callback object
|
||||
will be owned and deleted by the system, so make sure that it works
|
||||
safely and doesn't keep any references to objects that might be deleted
|
||||
before it gets called. You can use the ModalCallbackFunction to easily
|
||||
pass in a lambda for this parameter.
|
||||
@returns true if button 1 was clicked, false if it was button 2. If the callback parameter
|
||||
is not null, the method always returns false, and the user's choice is delivered
|
||||
later by the callback.
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static bool JUCE_CALLTYPE showOkCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
/** Shows a dialog box with three buttons.
|
||||
|
||||
Ideal for yes/no/cancel boxes.
|
||||
|
||||
The escape key can be used to trigger the third button.
|
||||
|
||||
If the callback parameter is null and modal loops are enabled, the box is shown modally,
|
||||
and the method will block until the user has clicked the button (or pressed the escape or
|
||||
return keys). If the callback parameter is non-null, the box will be displayed and placed
|
||||
into a modal state, but this method will return immediately, and the callback will be invoked
|
||||
later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the box will be launched asynchronously,
|
||||
returning immediately, and the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed, with its parameter
|
||||
being 1 if the "yes" button was pressed, 2 for the "no" button, or 0
|
||||
if it was cancelled, The callback object will be owned and deleted by the
|
||||
system, so make sure that it works safely and doesn't keep any references
|
||||
to objects that might be deleted before it gets called. You can use the
|
||||
ModalCallbackFunction to easily pass in a lambda for this parameter.
|
||||
|
||||
@returns If the callback parameter has been set, this returns 0. Otherwise, it returns one
|
||||
of the following values:
|
||||
- 0 if 'cancel' was pressed
|
||||
- 1 if 'yes' was pressed
|
||||
- 2 if 'no' was pressed
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static int JUCE_CALLTYPE showYesNoCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
/** Shows a dialog box with two buttons.
|
||||
|
||||
Ideal for yes/no boxes.
|
||||
|
||||
The escape key can be used to trigger the no button.
|
||||
|
||||
If the callback parameter is null and modal loops are enabled, the box is shown modally,
|
||||
and the method will block until the user has clicked the button (or pressed the escape or
|
||||
return keys). If the callback parameter is non-null, the box will be displayed and placed
|
||||
into a modal state, but this method will return immediately, and the callback will be invoked
|
||||
later when the user dismisses the box.
|
||||
|
||||
@param iconType the type of icon to show.
|
||||
@param title the headline to show at the top of the box.
|
||||
@param message a longer, more descriptive message to show underneath the title.
|
||||
@param associatedComponent if this is non-null, it specifies the component that the
|
||||
alert window should be associated with. Depending on the look
|
||||
and feel, this might be used for positioning of the alert window.
|
||||
@param callback if this is non-null, the box will be launched asynchronously,
|
||||
returning immediately, and the callback will receive a call to its
|
||||
modalStateFinished() when the box is dismissed, with its parameter
|
||||
being 1 if the "yes" button was pressed or 0 for the "no" button was
|
||||
pressed. The callback object will be owned and deleted by the
|
||||
system, so make sure that it works safely and doesn't keep any references
|
||||
to objects that might be deleted before it gets called. You can use the
|
||||
ModalCallbackFunction to easily pass in a lambda for this parameter.
|
||||
|
||||
@returns If the callback parameter has been set, this returns 0. Otherwise, it returns one
|
||||
of the following values:
|
||||
- 0 if 'no' was pressed
|
||||
- 1 if 'yes' was pressed
|
||||
|
||||
@see ModalCallbackFunction
|
||||
*/
|
||||
static int JUCE_CALLTYPE showYesNoBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
private:
|
||||
NativeMessageBox() = delete;
|
||||
JUCE_DECLARE_NON_COPYABLE (NativeMessageBox)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,413 +1,413 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for top-level windows that can be dragged around and resized.
|
||||
|
||||
To add content to the window, use its setContentOwned() or setContentNonOwned() methods
|
||||
to give it a component that will remain positioned inside it (leaving a gap around
|
||||
the edges for a border).
|
||||
|
||||
It's not advisable to add child components directly to a ResizableWindow: put them
|
||||
inside your content component instead. And overriding methods like resized(), moved(), etc
|
||||
is also not recommended - instead override these methods for your content component.
|
||||
(If for some obscure reason you do need to override these methods, always remember to
|
||||
call the super-class's resized() method too, otherwise it'll fail to lay out the window
|
||||
decorations correctly).
|
||||
|
||||
By default resizing isn't enabled - use the setResizable() method to enable it and
|
||||
to choose the style of resizing to use.
|
||||
|
||||
@see TopLevelWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ResizableWindow : public TopLevelWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ResizableWindow.
|
||||
|
||||
This constructor doesn't specify a background colour, so the LookAndFeel's default
|
||||
background colour will be used.
|
||||
|
||||
@param name the name to give the component
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
*/
|
||||
ResizableWindow (const String& name,
|
||||
bool addToDesktop);
|
||||
|
||||
/** Creates a ResizableWindow.
|
||||
|
||||
@param name the name to give the component
|
||||
@param backgroundColour the colour to use for filling the window's background.
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
*/
|
||||
ResizableWindow (const String& name,
|
||||
Colour backgroundColour,
|
||||
bool addToDesktop);
|
||||
|
||||
/** Destructor.
|
||||
If a content component has been set with setContentOwned(), it will be deleted.
|
||||
*/
|
||||
~ResizableWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the colour currently being used for the window's background.
|
||||
|
||||
As a convenience the window will fill itself with this colour, but you
|
||||
can override the paint() method if you need more customised behaviour.
|
||||
|
||||
This method is the same as retrieving the colour for ResizableWindow::backgroundColourId.
|
||||
|
||||
@see setBackgroundColour
|
||||
*/
|
||||
Colour getBackgroundColour() const noexcept;
|
||||
|
||||
/** Changes the colour currently being used for the window's background.
|
||||
|
||||
As a convenience the window will fill itself with this colour, but you
|
||||
can override the paint() method if you need more customised behaviour.
|
||||
|
||||
Note that the opaque state of this window is altered by this call to reflect
|
||||
the opacity of the colour passed-in. On window systems which can't support
|
||||
semi-transparent windows this might cause problems, (though it's unlikely you'll
|
||||
be using this class as a base for a semi-transparent component anyway).
|
||||
|
||||
You can also use the ResizableWindow::backgroundColourId colour id to set
|
||||
this colour.
|
||||
|
||||
@see getBackgroundColour
|
||||
*/
|
||||
void setBackgroundColour (Colour newColour);
|
||||
|
||||
//==============================================================================
|
||||
/** Make the window resizable or fixed.
|
||||
|
||||
@param shouldBeResizable whether it's resizable at all
|
||||
@param useBottomRightCornerResizer if true, it'll add a ResizableCornerComponent at the
|
||||
bottom-right; if false, it'll use a ResizableBorderComponent
|
||||
around the edge
|
||||
@see setResizeLimits, isResizable
|
||||
*/
|
||||
void setResizable (bool shouldBeResizable,
|
||||
bool useBottomRightCornerResizer);
|
||||
|
||||
/** Returns true if resizing is enabled.
|
||||
@see setResizable
|
||||
*/
|
||||
bool isResizable() const noexcept;
|
||||
|
||||
/** This sets the maximum and minimum sizes for the window.
|
||||
|
||||
If the window's current size is outside these limits, it will be resized to
|
||||
make sure it's within them.
|
||||
|
||||
A direct call to setBounds() will bypass any constraint checks, but when the
|
||||
window is dragged by the user or resized by other indirect means, the constrainer
|
||||
will limit the numbers involved.
|
||||
|
||||
@see setResizable, setFixedAspectRatio
|
||||
*/
|
||||
void setResizeLimits (int newMinimumWidth,
|
||||
int newMinimumHeight,
|
||||
int newMaximumWidth,
|
||||
int newMaximumHeight) noexcept;
|
||||
|
||||
/** Can be used to enable or disable user-dragging of the window. */
|
||||
void setDraggable (bool shouldBeDraggable) noexcept;
|
||||
|
||||
/** Returns true if the window can be dragged around by the user. */
|
||||
bool isDraggable() const noexcept { return canDrag; }
|
||||
|
||||
/** Returns the bounds constrainer object that this window is using.
|
||||
You can access this to change its properties.
|
||||
*/
|
||||
ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; }
|
||||
|
||||
/** Sets the bounds-constrainer object to use for resizing and dragging this window.
|
||||
|
||||
A pointer to the object you pass in will be kept, but it won't be deleted
|
||||
by this object, so it's the caller's responsibility to manage it.
|
||||
|
||||
If you pass a nullptr, then no constraints will be placed on the positioning of the window.
|
||||
*/
|
||||
void setConstrainer (ComponentBoundsConstrainer* newConstrainer);
|
||||
|
||||
/** Calls the window's setBounds method, after first checking these bounds
|
||||
with the current constrainer.
|
||||
@see setConstrainer
|
||||
*/
|
||||
void setBoundsConstrained (const Rectangle<int>& newBounds);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the window is currently in full-screen mode.
|
||||
@see setFullScreen
|
||||
*/
|
||||
bool isFullScreen() const;
|
||||
|
||||
/** Puts the window into full-screen mode, or restores it to its normal size.
|
||||
|
||||
If true, the window will become full-screen; if false, it will return to the
|
||||
last size it was before being made full-screen.
|
||||
|
||||
@see isFullScreen
|
||||
*/
|
||||
void setFullScreen (bool shouldBeFullScreen);
|
||||
|
||||
/** Returns true if the window is currently minimised.
|
||||
@see setMinimised
|
||||
*/
|
||||
bool isMinimised() const;
|
||||
|
||||
/** Minimises the window, or restores it to its previous position and size.
|
||||
|
||||
When being un-minimised, it'll return to the last position and size it
|
||||
was in before being minimised.
|
||||
|
||||
@see isMinimised
|
||||
*/
|
||||
void setMinimised (bool shouldMinimise);
|
||||
|
||||
/** Returns true if the window has been placed in kiosk-mode.
|
||||
@see Desktop::setKioskComponent
|
||||
*/
|
||||
bool isKioskMode() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a string which encodes the window's current size and position.
|
||||
|
||||
This string will encapsulate the window's size, position, and whether it's
|
||||
in full-screen mode. It's intended for letting your application save and
|
||||
restore a window's position.
|
||||
|
||||
Use the restoreWindowStateFromString() to restore from a saved state.
|
||||
|
||||
@see restoreWindowStateFromString
|
||||
*/
|
||||
String getWindowStateAsString();
|
||||
|
||||
/** Restores the window to a previously-saved size and position.
|
||||
|
||||
This restores the window's size, position and full-screen status from an
|
||||
string that was previously created with the getWindowStateAsString()
|
||||
method.
|
||||
|
||||
@returns false if the string wasn't a valid window state
|
||||
@see getWindowStateAsString
|
||||
*/
|
||||
bool restoreWindowStateFromString (const String& previousState);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current content component.
|
||||
|
||||
This will be the component set by setContentOwned() or setContentNonOwned, or
|
||||
nullptr if none has yet been specified.
|
||||
|
||||
@see setContentOwned, setContentNonOwned
|
||||
*/
|
||||
Component* getContentComponent() const noexcept { return contentComponent; }
|
||||
|
||||
/** Changes the current content component.
|
||||
|
||||
This sets a component that will be placed in the centre of the ResizableWindow,
|
||||
(leaving a space around the edge for the border).
|
||||
|
||||
You should never add components directly to a ResizableWindow (or any of its subclasses)
|
||||
with addChildComponent(). Instead, add them to the content component.
|
||||
|
||||
@param newContentComponent the new component to use - this component will be deleted when it's
|
||||
no longer needed (i.e. when the window is deleted or a new content
|
||||
component is set for it). To set a component that this window will not
|
||||
delete, call setContentNonOwned() instead.
|
||||
@param resizeToFitWhenContentChangesSize if true, then the ResizableWindow will maintain its size
|
||||
such that it always fits around the size of the content component. If false,
|
||||
the new content will be resized to fit the current space available.
|
||||
*/
|
||||
void setContentOwned (Component* newContentComponent,
|
||||
bool resizeToFitWhenContentChangesSize);
|
||||
|
||||
/** Changes the current content component.
|
||||
|
||||
This sets a component that will be placed in the centre of the ResizableWindow,
|
||||
(leaving a space around the edge for the border).
|
||||
|
||||
You should never add components directly to a ResizableWindow (or any of its subclasses)
|
||||
with addChildComponent(). Instead, add them to the content component.
|
||||
|
||||
@param newContentComponent the new component to use - this component will NOT be deleted by this
|
||||
component, so it's the caller's responsibility to manage its lifetime (it's
|
||||
ok to delete it while this window is still using it). To set a content
|
||||
component that the window will delete, call setContentOwned() instead.
|
||||
@param resizeToFitWhenContentChangesSize if true, then the ResizableWindow will maintain its size
|
||||
such that it always fits around the size of the content component. If false,
|
||||
the new content will be resized to fit the current space available.
|
||||
*/
|
||||
void setContentNonOwned (Component* newContentComponent,
|
||||
bool resizeToFitWhenContentChangesSize);
|
||||
|
||||
/** Removes the current content component.
|
||||
If the previous content component was added with setContentOwned(), it will also be deleted. If
|
||||
it was added with setContentNonOwned(), it will simply be removed from this component.
|
||||
*/
|
||||
void clearContentComponent();
|
||||
|
||||
/** Changes the window so that the content component ends up with the specified size.
|
||||
|
||||
This is basically a setSize call on the window, but which adds on the borders,
|
||||
so you can specify the content component's target size.
|
||||
*/
|
||||
void setContentComponentSize (int width, int height);
|
||||
|
||||
/** Returns the width of the frame to use around the window.
|
||||
@see getContentComponentBorder
|
||||
*/
|
||||
virtual BorderSize<int> getBorderThickness();
|
||||
|
||||
/** Returns the insets to use when positioning the content component.
|
||||
@see getBorderThickness
|
||||
*/
|
||||
virtual BorderSize<int> getContentComponentBorder();
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the window.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1005700, /**< A colour to use to fill the window's background. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("use setContentOwned and setContentNonOwned instead.")]]
|
||||
void setContentComponent (Component* newContentComponent,
|
||||
bool deleteOldOne = true,
|
||||
bool resizeToFit = false);
|
||||
#endif
|
||||
|
||||
using TopLevelWindow::addToDesktop;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawCornerResizer (Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) = 0;
|
||||
virtual void drawResizableFrame (Graphics&, int w, int h, const BorderSize<int>&) = 0;
|
||||
|
||||
virtual void fillResizableWindowBackground (Graphics&, int w, int h, const BorderSize<int>&, ResizableWindow&) = 0;
|
||||
virtual void drawResizableWindowBorder (Graphics&, int w, int h, const BorderSize<int>& border, ResizableWindow&) = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** (if overriding this, make sure you call ResizableWindow::moved() in your subclass) */
|
||||
void moved() override;
|
||||
/** (if overriding this, make sure you call ResizableWindow::resized() in your subclass) */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
/** @internal */
|
||||
void parentSizeChanged() override;
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
/** @internal */
|
||||
void activeWindowStatusChanged() override;
|
||||
/** @internal */
|
||||
int getDesktopWindowStyleFlags() const override;
|
||||
|
||||
#if JUCE_DEBUG
|
||||
/** Overridden to warn people about adding components directly to this component
|
||||
instead of using setContentOwned().
|
||||
|
||||
If you know what you're doing and are sure you really want to add a component, specify
|
||||
a base-class method call to Component::addAndMakeVisible(), to side-step this warning.
|
||||
*/
|
||||
void addChildComponent (Component*, int zOrder = -1);
|
||||
/** Overridden to warn people about adding components directly to this component
|
||||
instead of using setContentOwned().
|
||||
|
||||
If you know what you're doing and are sure you really want to add a component, specify
|
||||
a base-class method call to Component::addAndMakeVisible(), to side-step this warning.
|
||||
*/
|
||||
void addAndMakeVisible (Component*, int zOrder = -1);
|
||||
#endif
|
||||
|
||||
std::unique_ptr<ResizableCornerComponent> resizableCorner;
|
||||
std::unique_ptr<ResizableBorderComponent> resizableBorder;
|
||||
|
||||
//==============================================================================
|
||||
// The parameters for these methods have changed - please update your code!
|
||||
void getBorderThickness (int& left, int& top, int& right, int& bottom);
|
||||
void getContentComponentBorder (int& left, int& top, int& right, int& bottom);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component::SafePointer<Component> contentComponent, splashScreen;
|
||||
bool ownsContentComponent = false, resizeToFitContent = false, fullscreen = false, canDrag = true, dragStarted = false;
|
||||
ComponentDragger dragger;
|
||||
Rectangle<int> lastNonFullScreenPos;
|
||||
ComponentBoundsConstrainer defaultConstrainer;
|
||||
ComponentBoundsConstrainer* constrainer = nullptr;
|
||||
#if JUCE_DEBUG
|
||||
bool hasBeenResized = false;
|
||||
#endif
|
||||
|
||||
void initialise (bool addToDesktop);
|
||||
void updateLastPosIfNotFullScreen();
|
||||
void updateLastPosIfShowing();
|
||||
void setContent (Component*, bool takeOwnership, bool resizeToFit);
|
||||
void updatePeerConstrainer();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for top-level windows that can be dragged around and resized.
|
||||
|
||||
To add content to the window, use its setContentOwned() or setContentNonOwned() methods
|
||||
to give it a component that will remain positioned inside it (leaving a gap around
|
||||
the edges for a border).
|
||||
|
||||
It's not advisable to add child components directly to a ResizableWindow: put them
|
||||
inside your content component instead. And overriding methods like resized(), moved(), etc
|
||||
is also not recommended - instead override these methods for your content component.
|
||||
(If for some obscure reason you do need to override these methods, always remember to
|
||||
call the super-class's resized() method too, otherwise it'll fail to lay out the window
|
||||
decorations correctly).
|
||||
|
||||
By default resizing isn't enabled - use the setResizable() method to enable it and
|
||||
to choose the style of resizing to use.
|
||||
|
||||
@see TopLevelWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ResizableWindow : public TopLevelWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a ResizableWindow.
|
||||
|
||||
This constructor doesn't specify a background colour, so the LookAndFeel's default
|
||||
background colour will be used.
|
||||
|
||||
@param name the name to give the component
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
*/
|
||||
ResizableWindow (const String& name,
|
||||
bool addToDesktop);
|
||||
|
||||
/** Creates a ResizableWindow.
|
||||
|
||||
@param name the name to give the component
|
||||
@param backgroundColour the colour to use for filling the window's background.
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
*/
|
||||
ResizableWindow (const String& name,
|
||||
Colour backgroundColour,
|
||||
bool addToDesktop);
|
||||
|
||||
/** Destructor.
|
||||
If a content component has been set with setContentOwned(), it will be deleted.
|
||||
*/
|
||||
~ResizableWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the colour currently being used for the window's background.
|
||||
|
||||
As a convenience the window will fill itself with this colour, but you
|
||||
can override the paint() method if you need more customised behaviour.
|
||||
|
||||
This method is the same as retrieving the colour for ResizableWindow::backgroundColourId.
|
||||
|
||||
@see setBackgroundColour
|
||||
*/
|
||||
Colour getBackgroundColour() const noexcept;
|
||||
|
||||
/** Changes the colour currently being used for the window's background.
|
||||
|
||||
As a convenience the window will fill itself with this colour, but you
|
||||
can override the paint() method if you need more customised behaviour.
|
||||
|
||||
Note that the opaque state of this window is altered by this call to reflect
|
||||
the opacity of the colour passed-in. On window systems which can't support
|
||||
semi-transparent windows this might cause problems, (though it's unlikely you'll
|
||||
be using this class as a base for a semi-transparent component anyway).
|
||||
|
||||
You can also use the ResizableWindow::backgroundColourId colour id to set
|
||||
this colour.
|
||||
|
||||
@see getBackgroundColour
|
||||
*/
|
||||
void setBackgroundColour (Colour newColour);
|
||||
|
||||
//==============================================================================
|
||||
/** Make the window resizable or fixed.
|
||||
|
||||
@param shouldBeResizable whether it's resizable at all
|
||||
@param useBottomRightCornerResizer if true, it'll add a ResizableCornerComponent at the
|
||||
bottom-right; if false, it'll use a ResizableBorderComponent
|
||||
around the edge
|
||||
@see setResizeLimits, isResizable
|
||||
*/
|
||||
void setResizable (bool shouldBeResizable,
|
||||
bool useBottomRightCornerResizer);
|
||||
|
||||
/** Returns true if resizing is enabled.
|
||||
@see setResizable
|
||||
*/
|
||||
bool isResizable() const noexcept;
|
||||
|
||||
/** This sets the maximum and minimum sizes for the window.
|
||||
|
||||
If the window's current size is outside these limits, it will be resized to
|
||||
make sure it's within them.
|
||||
|
||||
A direct call to setBounds() will bypass any constraint checks, but when the
|
||||
window is dragged by the user or resized by other indirect means, the constrainer
|
||||
will limit the numbers involved.
|
||||
|
||||
@see setResizable, setFixedAspectRatio
|
||||
*/
|
||||
void setResizeLimits (int newMinimumWidth,
|
||||
int newMinimumHeight,
|
||||
int newMaximumWidth,
|
||||
int newMaximumHeight) noexcept;
|
||||
|
||||
/** Can be used to enable or disable user-dragging of the window. */
|
||||
void setDraggable (bool shouldBeDraggable) noexcept;
|
||||
|
||||
/** Returns true if the window can be dragged around by the user. */
|
||||
bool isDraggable() const noexcept { return canDrag; }
|
||||
|
||||
/** Returns the bounds constrainer object that this window is using.
|
||||
You can access this to change its properties.
|
||||
*/
|
||||
ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; }
|
||||
|
||||
/** Sets the bounds-constrainer object to use for resizing and dragging this window.
|
||||
|
||||
A pointer to the object you pass in will be kept, but it won't be deleted
|
||||
by this object, so it's the caller's responsibility to manage it.
|
||||
|
||||
If you pass a nullptr, then no constraints will be placed on the positioning of the window.
|
||||
*/
|
||||
void setConstrainer (ComponentBoundsConstrainer* newConstrainer);
|
||||
|
||||
/** Calls the window's setBounds method, after first checking these bounds
|
||||
with the current constrainer.
|
||||
@see setConstrainer
|
||||
*/
|
||||
void setBoundsConstrained (const Rectangle<int>& newBounds);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the window is currently in full-screen mode.
|
||||
@see setFullScreen
|
||||
*/
|
||||
bool isFullScreen() const;
|
||||
|
||||
/** Puts the window into full-screen mode, or restores it to its normal size.
|
||||
|
||||
If true, the window will become full-screen; if false, it will return to the
|
||||
last size it was before being made full-screen.
|
||||
|
||||
@see isFullScreen
|
||||
*/
|
||||
void setFullScreen (bool shouldBeFullScreen);
|
||||
|
||||
/** Returns true if the window is currently minimised.
|
||||
@see setMinimised
|
||||
*/
|
||||
bool isMinimised() const;
|
||||
|
||||
/** Minimises the window, or restores it to its previous position and size.
|
||||
|
||||
When being un-minimised, it'll return to the last position and size it
|
||||
was in before being minimised.
|
||||
|
||||
@see isMinimised
|
||||
*/
|
||||
void setMinimised (bool shouldMinimise);
|
||||
|
||||
/** Returns true if the window has been placed in kiosk-mode.
|
||||
@see Desktop::setKioskComponent
|
||||
*/
|
||||
bool isKioskMode() const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a string which encodes the window's current size and position.
|
||||
|
||||
This string will encapsulate the window's size, position, and whether it's
|
||||
in full-screen mode. It's intended for letting your application save and
|
||||
restore a window's position.
|
||||
|
||||
Use the restoreWindowStateFromString() to restore from a saved state.
|
||||
|
||||
@see restoreWindowStateFromString
|
||||
*/
|
||||
String getWindowStateAsString();
|
||||
|
||||
/** Restores the window to a previously-saved size and position.
|
||||
|
||||
This restores the window's size, position and full-screen status from an
|
||||
string that was previously created with the getWindowStateAsString()
|
||||
method.
|
||||
|
||||
@returns false if the string wasn't a valid window state
|
||||
@see getWindowStateAsString
|
||||
*/
|
||||
bool restoreWindowStateFromString (const String& previousState);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the current content component.
|
||||
|
||||
This will be the component set by setContentOwned() or setContentNonOwned, or
|
||||
nullptr if none has yet been specified.
|
||||
|
||||
@see setContentOwned, setContentNonOwned
|
||||
*/
|
||||
Component* getContentComponent() const noexcept { return contentComponent; }
|
||||
|
||||
/** Changes the current content component.
|
||||
|
||||
This sets a component that will be placed in the centre of the ResizableWindow,
|
||||
(leaving a space around the edge for the border).
|
||||
|
||||
You should never add components directly to a ResizableWindow (or any of its subclasses)
|
||||
with addChildComponent(). Instead, add them to the content component.
|
||||
|
||||
@param newContentComponent the new component to use - this component will be deleted when it's
|
||||
no longer needed (i.e. when the window is deleted or a new content
|
||||
component is set for it). To set a component that this window will not
|
||||
delete, call setContentNonOwned() instead.
|
||||
@param resizeToFitWhenContentChangesSize if true, then the ResizableWindow will maintain its size
|
||||
such that it always fits around the size of the content component. If false,
|
||||
the new content will be resized to fit the current space available.
|
||||
*/
|
||||
void setContentOwned (Component* newContentComponent,
|
||||
bool resizeToFitWhenContentChangesSize);
|
||||
|
||||
/** Changes the current content component.
|
||||
|
||||
This sets a component that will be placed in the centre of the ResizableWindow,
|
||||
(leaving a space around the edge for the border).
|
||||
|
||||
You should never add components directly to a ResizableWindow (or any of its subclasses)
|
||||
with addChildComponent(). Instead, add them to the content component.
|
||||
|
||||
@param newContentComponent the new component to use - this component will NOT be deleted by this
|
||||
component, so it's the caller's responsibility to manage its lifetime (it's
|
||||
ok to delete it while this window is still using it). To set a content
|
||||
component that the window will delete, call setContentOwned() instead.
|
||||
@param resizeToFitWhenContentChangesSize if true, then the ResizableWindow will maintain its size
|
||||
such that it always fits around the size of the content component. If false,
|
||||
the new content will be resized to fit the current space available.
|
||||
*/
|
||||
void setContentNonOwned (Component* newContentComponent,
|
||||
bool resizeToFitWhenContentChangesSize);
|
||||
|
||||
/** Removes the current content component.
|
||||
If the previous content component was added with setContentOwned(), it will also be deleted. If
|
||||
it was added with setContentNonOwned(), it will simply be removed from this component.
|
||||
*/
|
||||
void clearContentComponent();
|
||||
|
||||
/** Changes the window so that the content component ends up with the specified size.
|
||||
|
||||
This is basically a setSize call on the window, but which adds on the borders,
|
||||
so you can specify the content component's target size.
|
||||
*/
|
||||
void setContentComponentSize (int width, int height);
|
||||
|
||||
/** Returns the width of the frame to use around the window.
|
||||
@see getContentComponentBorder
|
||||
*/
|
||||
virtual BorderSize<int> getBorderThickness();
|
||||
|
||||
/** Returns the insets to use when positioning the content component.
|
||||
@see getBorderThickness
|
||||
*/
|
||||
virtual BorderSize<int> getContentComponentBorder();
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the window.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1005700, /**< A colour to use to fill the window's background. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
[[deprecated ("use setContentOwned and setContentNonOwned instead.")]]
|
||||
void setContentComponent (Component* newContentComponent,
|
||||
bool deleteOldOne = true,
|
||||
bool resizeToFit = false);
|
||||
#endif
|
||||
|
||||
using TopLevelWindow::addToDesktop;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
//==============================================================================
|
||||
virtual void drawCornerResizer (Graphics&, int w, int h, bool isMouseOver, bool isMouseDragging) = 0;
|
||||
virtual void drawResizableFrame (Graphics&, int w, int h, const BorderSize<int>&) = 0;
|
||||
|
||||
virtual void fillResizableWindowBackground (Graphics&, int w, int h, const BorderSize<int>&, ResizableWindow&) = 0;
|
||||
virtual void drawResizableWindowBorder (Graphics&, int w, int h, const BorderSize<int>& border, ResizableWindow&) = 0;
|
||||
};
|
||||
|
||||
protected:
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** (if overriding this, make sure you call ResizableWindow::moved() in your subclass) */
|
||||
void moved() override;
|
||||
/** (if overriding this, make sure you call ResizableWindow::resized() in your subclass) */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseUp (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
void childBoundsChanged (Component*) override;
|
||||
/** @internal */
|
||||
void parentSizeChanged() override;
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
/** @internal */
|
||||
void activeWindowStatusChanged() override;
|
||||
/** @internal */
|
||||
int getDesktopWindowStyleFlags() const override;
|
||||
|
||||
#if JUCE_DEBUG
|
||||
/** Overridden to warn people about adding components directly to this component
|
||||
instead of using setContentOwned().
|
||||
|
||||
If you know what you're doing and are sure you really want to add a component, specify
|
||||
a base-class method call to Component::addAndMakeVisible(), to side-step this warning.
|
||||
*/
|
||||
void addChildComponent (Component*, int zOrder = -1);
|
||||
/** Overridden to warn people about adding components directly to this component
|
||||
instead of using setContentOwned().
|
||||
|
||||
If you know what you're doing and are sure you really want to add a component, specify
|
||||
a base-class method call to Component::addAndMakeVisible(), to side-step this warning.
|
||||
*/
|
||||
void addAndMakeVisible (Component*, int zOrder = -1);
|
||||
#endif
|
||||
|
||||
std::unique_ptr<ResizableCornerComponent> resizableCorner;
|
||||
std::unique_ptr<ResizableBorderComponent> resizableBorder;
|
||||
|
||||
//==============================================================================
|
||||
// The parameters for these methods have changed - please update your code!
|
||||
void getBorderThickness (int& left, int& top, int& right, int& bottom);
|
||||
void getContentComponentBorder (int& left, int& top, int& right, int& bottom);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component::SafePointer<Component> contentComponent, splashScreen;
|
||||
bool ownsContentComponent = false, resizeToFitContent = false, fullscreen = false, canDrag = true, dragStarted = false;
|
||||
ComponentDragger dragger;
|
||||
Rectangle<int> lastNonFullScreenPos;
|
||||
ComponentBoundsConstrainer defaultConstrainer;
|
||||
ComponentBoundsConstrainer* constrainer = nullptr;
|
||||
#if JUCE_DEBUG
|
||||
bool hasBeenResized = false;
|
||||
#endif
|
||||
|
||||
void initialise (bool addToDesktop);
|
||||
void updateLastPosIfNotFullScreen();
|
||||
void updateLastPosIfShowing();
|
||||
void setContent (Component*, bool takeOwnership, bool resizeToFit);
|
||||
void updatePeerConstrainer();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,119 +1,119 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title,
|
||||
const bool hasProgressBar,
|
||||
const bool hasCancelButton,
|
||||
const int cancellingTimeOutMs,
|
||||
const String& cancelButtonText,
|
||||
Component* componentToCentreAround)
|
||||
: Thread ("ThreadWithProgressWindow"),
|
||||
progress (0.0),
|
||||
timeOutMsWhenCancelling (cancellingTimeOutMs),
|
||||
wasCancelledByUser (false)
|
||||
{
|
||||
alertWindow.reset (LookAndFeel::getDefaultLookAndFeel()
|
||||
.createAlertWindow (title, {},
|
||||
cancelButtonText.isEmpty() ? TRANS("Cancel")
|
||||
: cancelButtonText,
|
||||
{}, {}, MessageBoxIconType::NoIcon, hasCancelButton ? 1 : 0,
|
||||
componentToCentreAround));
|
||||
|
||||
// if there are no buttons, we won't allow the user to interrupt the thread.
|
||||
alertWindow->setEscapeKeyCancels (false);
|
||||
|
||||
if (hasProgressBar)
|
||||
alertWindow->addProgressBarComponent (progress);
|
||||
}
|
||||
|
||||
ThreadWithProgressWindow::~ThreadWithProgressWindow()
|
||||
{
|
||||
stopThread (timeOutMsWhenCancelling);
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::launchThread (int priority)
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
|
||||
startThread (priority);
|
||||
startTimer (100);
|
||||
|
||||
{
|
||||
const ScopedLock sl (messageLock);
|
||||
alertWindow->setMessage (message);
|
||||
}
|
||||
|
||||
alertWindow->enterModalState();
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::setProgress (const double newProgress)
|
||||
{
|
||||
progress = newProgress;
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::setStatusMessage (const String& newStatusMessage)
|
||||
{
|
||||
const ScopedLock sl (messageLock);
|
||||
message = newStatusMessage;
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::timerCallback()
|
||||
{
|
||||
bool threadStillRunning = isThreadRunning();
|
||||
|
||||
if (! (threadStillRunning && alertWindow->isCurrentlyModal (false)))
|
||||
{
|
||||
stopTimer();
|
||||
stopThread (timeOutMsWhenCancelling);
|
||||
alertWindow->exitModalState (1);
|
||||
alertWindow->setVisible (false);
|
||||
|
||||
wasCancelledByUser = threadStillRunning;
|
||||
threadComplete (threadStillRunning);
|
||||
return; // (this may be deleted now)
|
||||
}
|
||||
|
||||
const ScopedLock sl (messageLock);
|
||||
alertWindow->setMessage (message);
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::threadComplete (bool) {}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool ThreadWithProgressWindow::runThread (const int priority)
|
||||
{
|
||||
launchThread (priority);
|
||||
|
||||
while (isTimerRunning())
|
||||
MessageManager::getInstance()->runDispatchLoopUntil (5);
|
||||
|
||||
return ! wasCancelledByUser;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title,
|
||||
const bool hasProgressBar,
|
||||
const bool hasCancelButton,
|
||||
const int cancellingTimeOutMs,
|
||||
const String& cancelButtonText,
|
||||
Component* componentToCentreAround)
|
||||
: Thread ("ThreadWithProgressWindow"),
|
||||
progress (0.0),
|
||||
timeOutMsWhenCancelling (cancellingTimeOutMs),
|
||||
wasCancelledByUser (false)
|
||||
{
|
||||
alertWindow.reset (LookAndFeel::getDefaultLookAndFeel()
|
||||
.createAlertWindow (title, {},
|
||||
cancelButtonText.isEmpty() ? TRANS("Cancel")
|
||||
: cancelButtonText,
|
||||
{}, {}, MessageBoxIconType::NoIcon, hasCancelButton ? 1 : 0,
|
||||
componentToCentreAround));
|
||||
|
||||
// if there are no buttons, we won't allow the user to interrupt the thread.
|
||||
alertWindow->setEscapeKeyCancels (false);
|
||||
|
||||
if (hasProgressBar)
|
||||
alertWindow->addProgressBarComponent (progress);
|
||||
}
|
||||
|
||||
ThreadWithProgressWindow::~ThreadWithProgressWindow()
|
||||
{
|
||||
stopThread (timeOutMsWhenCancelling);
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::launchThread (int priority)
|
||||
{
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
|
||||
startThread (priority);
|
||||
startTimer (100);
|
||||
|
||||
{
|
||||
const ScopedLock sl (messageLock);
|
||||
alertWindow->setMessage (message);
|
||||
}
|
||||
|
||||
alertWindow->enterModalState();
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::setProgress (const double newProgress)
|
||||
{
|
||||
progress = newProgress;
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::setStatusMessage (const String& newStatusMessage)
|
||||
{
|
||||
const ScopedLock sl (messageLock);
|
||||
message = newStatusMessage;
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::timerCallback()
|
||||
{
|
||||
bool threadStillRunning = isThreadRunning();
|
||||
|
||||
if (! (threadStillRunning && alertWindow->isCurrentlyModal (false)))
|
||||
{
|
||||
stopTimer();
|
||||
stopThread (timeOutMsWhenCancelling);
|
||||
alertWindow->exitModalState (1);
|
||||
alertWindow->setVisible (false);
|
||||
|
||||
wasCancelledByUser = threadStillRunning;
|
||||
threadComplete (threadStillRunning);
|
||||
return; // (this may be deleted now)
|
||||
}
|
||||
|
||||
const ScopedLock sl (messageLock);
|
||||
alertWindow->setMessage (message);
|
||||
}
|
||||
|
||||
void ThreadWithProgressWindow::threadComplete (bool) {}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
bool ThreadWithProgressWindow::runThread (const int priority)
|
||||
{
|
||||
launchThread (priority);
|
||||
|
||||
while (isTimerRunning())
|
||||
MessageManager::getInstance()->runDispatchLoopUntil (5);
|
||||
|
||||
return ! wasCancelledByUser;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,175 +1,175 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A thread that automatically pops up a modal dialog box with a progress bar
|
||||
and cancel button while it's busy running.
|
||||
|
||||
These are handy for performing some sort of task while giving the user feedback
|
||||
about how long there is to go, etc.
|
||||
|
||||
E.g. @code
|
||||
class MyTask : public ThreadWithProgressWindow
|
||||
{
|
||||
public:
|
||||
MyTask() : ThreadWithProgressWindow ("busy...", true, true)
|
||||
{
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
for (int i = 0; i < thingsToDo; ++i)
|
||||
{
|
||||
// must check this as often as possible, because this is
|
||||
// how we know if the user's pressed 'cancel'
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
|
||||
// this will update the progress bar on the dialog box
|
||||
setProgress (i / (double) thingsToDo);
|
||||
|
||||
|
||||
// ... do the business here...
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void doTheTask()
|
||||
{
|
||||
MyTask m;
|
||||
|
||||
if (m.runThread())
|
||||
{
|
||||
// thread finished normally..
|
||||
}
|
||||
else
|
||||
{
|
||||
// user pressed the cancel button..
|
||||
}
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@see Thread, AlertWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ThreadWithProgressWindow : public Thread,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates the thread.
|
||||
|
||||
Initially, the dialog box won't be visible, it'll only appear when the
|
||||
runThread() method is called.
|
||||
|
||||
@param windowTitle the title to go at the top of the dialog box
|
||||
@param hasProgressBar whether the dialog box should have a progress bar (see
|
||||
setProgress() )
|
||||
@param hasCancelButton whether the dialog box should have a cancel button
|
||||
@param timeOutMsWhenCancelling when 'cancel' is pressed, this is how long to wait for
|
||||
the thread to stop before killing it forcibly (see
|
||||
Thread::stopThread() )
|
||||
@param cancelButtonText the text that should be shown in the cancel button
|
||||
(if it has one). Leave this empty for the default "Cancel"
|
||||
@param componentToCentreAround if this is non-null, the window will be positioned
|
||||
so that it's centred around this component.
|
||||
*/
|
||||
ThreadWithProgressWindow (const String& windowTitle,
|
||||
bool hasProgressBar,
|
||||
bool hasCancelButton,
|
||||
int timeOutMsWhenCancelling = 10000,
|
||||
const String& cancelButtonText = String(),
|
||||
Component* componentToCentreAround = nullptr);
|
||||
|
||||
/** Destructor. */
|
||||
~ThreadWithProgressWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Starts the thread and waits for it to finish.
|
||||
|
||||
This will start the thread, make the dialog box appear, and wait until either
|
||||
the thread finishes normally, or until the cancel button is pressed.
|
||||
|
||||
Before returning, the dialog box will be hidden.
|
||||
|
||||
@param priority the priority to use when starting the thread - see
|
||||
Thread::startThread() for values
|
||||
@returns true if the thread finished normally; false if the user pressed cancel
|
||||
*/
|
||||
bool runThread (int priority = 5);
|
||||
#endif
|
||||
|
||||
/** Starts the thread and returns.
|
||||
|
||||
This will start the thread and make the dialog box appear in a modal state. When
|
||||
the thread finishes normally, or the cancel button is pressed, the window will be
|
||||
hidden and the threadComplete() method will be called.
|
||||
|
||||
@param priority the priority to use when starting the thread - see
|
||||
Thread::startThread() for values
|
||||
*/
|
||||
void launchThread (int priority = 5);
|
||||
|
||||
/** The thread should call this periodically to update the position of the progress bar.
|
||||
|
||||
@param newProgress the progress, from 0.0 to 1.0
|
||||
@see setStatusMessage
|
||||
*/
|
||||
void setProgress (double newProgress);
|
||||
|
||||
/** The thread can call this to change the message that's displayed in the dialog box. */
|
||||
void setStatusMessage (const String& newStatusMessage);
|
||||
|
||||
/** Returns the AlertWindow that is being used. */
|
||||
AlertWindow* getAlertWindow() const noexcept { return alertWindow.get(); }
|
||||
|
||||
//==============================================================================
|
||||
/** This method is called (on the message thread) when the operation has finished.
|
||||
You may choose to use this callback to delete the ThreadWithProgressWindow object.
|
||||
*/
|
||||
virtual void threadComplete (bool userPressedCancel);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void timerCallback() override;
|
||||
|
||||
double progress;
|
||||
std::unique_ptr<AlertWindow> alertWindow;
|
||||
String message;
|
||||
CriticalSection messageLock;
|
||||
const int timeOutMsWhenCancelling;
|
||||
bool wasCancelledByUser;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadWithProgressWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A thread that automatically pops up a modal dialog box with a progress bar
|
||||
and cancel button while it's busy running.
|
||||
|
||||
These are handy for performing some sort of task while giving the user feedback
|
||||
about how long there is to go, etc.
|
||||
|
||||
E.g. @code
|
||||
class MyTask : public ThreadWithProgressWindow
|
||||
{
|
||||
public:
|
||||
MyTask() : ThreadWithProgressWindow ("busy...", true, true)
|
||||
{
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
for (int i = 0; i < thingsToDo; ++i)
|
||||
{
|
||||
// must check this as often as possible, because this is
|
||||
// how we know if the user's pressed 'cancel'
|
||||
if (threadShouldExit())
|
||||
break;
|
||||
|
||||
// this will update the progress bar on the dialog box
|
||||
setProgress (i / (double) thingsToDo);
|
||||
|
||||
|
||||
// ... do the business here...
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void doTheTask()
|
||||
{
|
||||
MyTask m;
|
||||
|
||||
if (m.runThread())
|
||||
{
|
||||
// thread finished normally..
|
||||
}
|
||||
else
|
||||
{
|
||||
// user pressed the cancel button..
|
||||
}
|
||||
}
|
||||
|
||||
@endcode
|
||||
|
||||
@see Thread, AlertWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ThreadWithProgressWindow : public Thread,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates the thread.
|
||||
|
||||
Initially, the dialog box won't be visible, it'll only appear when the
|
||||
runThread() method is called.
|
||||
|
||||
@param windowTitle the title to go at the top of the dialog box
|
||||
@param hasProgressBar whether the dialog box should have a progress bar (see
|
||||
setProgress() )
|
||||
@param hasCancelButton whether the dialog box should have a cancel button
|
||||
@param timeOutMsWhenCancelling when 'cancel' is pressed, this is how long to wait for
|
||||
the thread to stop before killing it forcibly (see
|
||||
Thread::stopThread() )
|
||||
@param cancelButtonText the text that should be shown in the cancel button
|
||||
(if it has one). Leave this empty for the default "Cancel"
|
||||
@param componentToCentreAround if this is non-null, the window will be positioned
|
||||
so that it's centred around this component.
|
||||
*/
|
||||
ThreadWithProgressWindow (const String& windowTitle,
|
||||
bool hasProgressBar,
|
||||
bool hasCancelButton,
|
||||
int timeOutMsWhenCancelling = 10000,
|
||||
const String& cancelButtonText = String(),
|
||||
Component* componentToCentreAround = nullptr);
|
||||
|
||||
/** Destructor. */
|
||||
~ThreadWithProgressWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Starts the thread and waits for it to finish.
|
||||
|
||||
This will start the thread, make the dialog box appear, and wait until either
|
||||
the thread finishes normally, or until the cancel button is pressed.
|
||||
|
||||
Before returning, the dialog box will be hidden.
|
||||
|
||||
@param priority the priority to use when starting the thread - see
|
||||
Thread::startThread() for values
|
||||
@returns true if the thread finished normally; false if the user pressed cancel
|
||||
*/
|
||||
bool runThread (int priority = 5);
|
||||
#endif
|
||||
|
||||
/** Starts the thread and returns.
|
||||
|
||||
This will start the thread and make the dialog box appear in a modal state. When
|
||||
the thread finishes normally, or the cancel button is pressed, the window will be
|
||||
hidden and the threadComplete() method will be called.
|
||||
|
||||
@param priority the priority to use when starting the thread - see
|
||||
Thread::startThread() for values
|
||||
*/
|
||||
void launchThread (int priority = 5);
|
||||
|
||||
/** The thread should call this periodically to update the position of the progress bar.
|
||||
|
||||
@param newProgress the progress, from 0.0 to 1.0
|
||||
@see setStatusMessage
|
||||
*/
|
||||
void setProgress (double newProgress);
|
||||
|
||||
/** The thread can call this to change the message that's displayed in the dialog box. */
|
||||
void setStatusMessage (const String& newStatusMessage);
|
||||
|
||||
/** Returns the AlertWindow that is being used. */
|
||||
AlertWindow* getAlertWindow() const noexcept { return alertWindow.get(); }
|
||||
|
||||
//==============================================================================
|
||||
/** This method is called (on the message thread) when the operation has finished.
|
||||
You may choose to use this callback to delete the ThreadWithProgressWindow object.
|
||||
*/
|
||||
virtual void threadComplete (bool userPressedCancel);
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void timerCallback() override;
|
||||
|
||||
double progress;
|
||||
std::unique_ptr<AlertWindow> alertWindow;
|
||||
String message;
|
||||
CriticalSection messageLock;
|
||||
const int timeOutMsWhenCancelling;
|
||||
bool wasCancelledByUser;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadWithProgressWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,232 +1,255 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
TooltipWindow::TooltipWindow (Component* parentComp, int delayMs)
|
||||
: Component ("tooltip"),
|
||||
millisecondsBeforeTipAppears (delayMs)
|
||||
{
|
||||
setAlwaysOnTop (true);
|
||||
setOpaque (true);
|
||||
setAccessible (false);
|
||||
|
||||
if (parentComp != nullptr)
|
||||
parentComp->addChildComponent (this);
|
||||
|
||||
if (Desktop::getInstance().getMainMouseSource().canHover())
|
||||
startTimer (123);
|
||||
}
|
||||
|
||||
TooltipWindow::~TooltipWindow()
|
||||
{
|
||||
hideTip();
|
||||
}
|
||||
|
||||
void TooltipWindow::setMillisecondsBeforeTipAppears (const int newTimeMs) noexcept
|
||||
{
|
||||
millisecondsBeforeTipAppears = newTimeMs;
|
||||
}
|
||||
|
||||
void TooltipWindow::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawTooltip (g, tipShowing, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
void TooltipWindow::mouseEnter (const MouseEvent&)
|
||||
{
|
||||
hideTip();
|
||||
}
|
||||
|
||||
void TooltipWindow::updatePosition (const String& tip, Point<int> pos, Rectangle<int> parentArea)
|
||||
{
|
||||
setBounds (getLookAndFeel().getTooltipBounds (tip, pos, parentArea));
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
#if JUCE_DEBUG
|
||||
static Array<TooltipWindow*> activeTooltipWindows;
|
||||
#endif
|
||||
|
||||
void TooltipWindow::displayTip (Point<int> screenPos, const String& tip)
|
||||
{
|
||||
jassert (tip.isNotEmpty());
|
||||
|
||||
if (! reentrant)
|
||||
{
|
||||
ScopedValueSetter<bool> setter (reentrant, true, false);
|
||||
|
||||
if (tipShowing != tip)
|
||||
{
|
||||
tipShowing = tip;
|
||||
repaint();
|
||||
}
|
||||
|
||||
if (auto* parent = getParentComponent())
|
||||
{
|
||||
updatePosition (tip, parent->getLocalPoint (nullptr, screenPos),
|
||||
parent->getLocalBounds());
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto physicalPos = ScalingHelpers::scaledScreenPosToUnscaled (screenPos);
|
||||
const auto scaledPos = ScalingHelpers::unscaledScreenPosToScaled (*this, physicalPos);
|
||||
updatePosition (tip, scaledPos, Desktop::getInstance().getDisplays().getDisplayForPoint (screenPos)->userArea);
|
||||
|
||||
addToDesktop (ComponentPeer::windowHasDropShadow
|
||||
| ComponentPeer::windowIsTemporary
|
||||
| ComponentPeer::windowIgnoresKeyPresses
|
||||
| ComponentPeer::windowIgnoresMouseClicks);
|
||||
}
|
||||
|
||||
#if JUCE_DEBUG
|
||||
activeTooltipWindows.addIfNotAlreadyThere (this);
|
||||
|
||||
auto* parent = getParentComponent();
|
||||
|
||||
for (auto* w : activeTooltipWindows)
|
||||
{
|
||||
if (w != nullptr && w != this && w->tipShowing == tipShowing && w->getParentComponent() == parent)
|
||||
{
|
||||
// Looks like you have more than one TooltipWindow showing the same tip..
|
||||
// Be careful not to create more than one instance of this class with the
|
||||
// same parent component!
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
toFront (false);
|
||||
}
|
||||
}
|
||||
|
||||
String TooltipWindow::getTipFor (Component& c)
|
||||
{
|
||||
if (isForegroundOrEmbeddedProcess (&c)
|
||||
&& ! ModifierKeys::currentModifiers.isAnyMouseButtonDown())
|
||||
{
|
||||
if (auto* ttc = dynamic_cast<TooltipClient*> (&c))
|
||||
if (! c.isCurrentlyBlockedByAnotherModalComponent())
|
||||
return ttc->getTooltip();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void TooltipWindow::hideTip()
|
||||
{
|
||||
if (! reentrant)
|
||||
{
|
||||
tipShowing.clear();
|
||||
removeFromDesktop();
|
||||
setVisible (false);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
activeTooltipWindows.removeAllInstancesOf (this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
float TooltipWindow::getDesktopScaleFactor() const
|
||||
{
|
||||
if (lastComponentUnderMouse != nullptr)
|
||||
return Component::getApproximateScaleFactorForComponent (lastComponentUnderMouse);
|
||||
|
||||
return Component::getDesktopScaleFactor();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> TooltipWindow::createAccessibilityHandler()
|
||||
{
|
||||
return createIgnoredAccessibilityHandler (*this);
|
||||
}
|
||||
|
||||
void TooltipWindow::timerCallback()
|
||||
{
|
||||
auto& desktop = Desktop::getInstance();
|
||||
auto mouseSource = desktop.getMainMouseSource();
|
||||
auto now = Time::getApproximateMillisecondCounter();
|
||||
|
||||
auto* newComp = mouseSource.isTouch() ? nullptr : mouseSource.getComponentUnderMouse();
|
||||
|
||||
if (newComp == nullptr || getParentComponent() == nullptr || newComp->getPeer() == getPeer())
|
||||
{
|
||||
auto newTip = newComp != nullptr ? getTipFor (*newComp) : String();
|
||||
bool tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse);
|
||||
lastComponentUnderMouse = newComp;
|
||||
lastTipUnderMouse = newTip;
|
||||
|
||||
auto clickCount = desktop.getMouseButtonClickCounter();
|
||||
auto wheelCount = desktop.getMouseWheelMoveCounter();
|
||||
bool mouseWasClicked = (clickCount > mouseClicks || wheelCount > mouseWheelMoves);
|
||||
mouseClicks = clickCount;
|
||||
mouseWheelMoves = wheelCount;
|
||||
|
||||
auto mousePos = mouseSource.getScreenPosition();
|
||||
bool mouseMovedQuickly = mousePos.getDistanceFrom (lastMousePos) > 12;
|
||||
lastMousePos = mousePos;
|
||||
|
||||
if (tipChanged || mouseWasClicked || mouseMovedQuickly)
|
||||
lastCompChangeTime = now;
|
||||
|
||||
auto showTip = [this, &mouseSource, &mousePos, &newTip]
|
||||
{
|
||||
bool mouseHasMovedSinceClick = mouseSource.getLastMouseDownPosition() != lastMousePos;
|
||||
|
||||
if (mouseHasMovedSinceClick)
|
||||
displayTip (mousePos.roundToInt(), newTip);
|
||||
};
|
||||
|
||||
if (isVisible() || now < lastHideTime + 500)
|
||||
{
|
||||
// if a tip is currently visible (or has just disappeared), update to a new one
|
||||
// immediately if needed..
|
||||
if (newComp == nullptr || mouseWasClicked || newTip.isEmpty())
|
||||
{
|
||||
if (isVisible())
|
||||
{
|
||||
lastHideTime = now;
|
||||
hideTip();
|
||||
}
|
||||
}
|
||||
else if (tipChanged)
|
||||
{
|
||||
showTip();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there isn't currently a tip, but one is needed, only let it appear after a timeout
|
||||
if (newTip.isNotEmpty()
|
||||
&& newTip != tipShowing
|
||||
&& now > lastCompChangeTime + (uint32) millisecondsBeforeTipAppears)
|
||||
{
|
||||
showTip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
TooltipWindow::TooltipWindow (Component* parentComp, int delayMs)
|
||||
: Component ("tooltip"),
|
||||
millisecondsBeforeTipAppears (delayMs)
|
||||
{
|
||||
setAlwaysOnTop (true);
|
||||
setOpaque (true);
|
||||
setAccessible (false);
|
||||
|
||||
if (parentComp != nullptr)
|
||||
parentComp->addChildComponent (this);
|
||||
|
||||
auto& desktop = Desktop::getInstance();
|
||||
|
||||
if (desktop.getMainMouseSource().canHover())
|
||||
{
|
||||
desktop.addGlobalMouseListener (this);
|
||||
startTimer (123);
|
||||
}
|
||||
}
|
||||
|
||||
TooltipWindow::~TooltipWindow()
|
||||
{
|
||||
hideTip();
|
||||
Desktop::getInstance().removeGlobalMouseListener (this);
|
||||
}
|
||||
|
||||
void TooltipWindow::setMillisecondsBeforeTipAppears (const int newTimeMs) noexcept
|
||||
{
|
||||
millisecondsBeforeTipAppears = newTimeMs;
|
||||
}
|
||||
|
||||
void TooltipWindow::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawTooltip (g, tipShowing, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
void TooltipWindow::mouseEnter (const MouseEvent& e)
|
||||
{
|
||||
if (e.eventComponent == this)
|
||||
hideTip();
|
||||
}
|
||||
|
||||
void TooltipWindow::mouseDown (const MouseEvent&)
|
||||
{
|
||||
if (isVisible())
|
||||
dismissalMouseEventOccurred = true;
|
||||
}
|
||||
|
||||
void TooltipWindow::mouseWheelMove (const MouseEvent&, const MouseWheelDetails&)
|
||||
{
|
||||
if (isVisible())
|
||||
dismissalMouseEventOccurred = true;
|
||||
}
|
||||
|
||||
void TooltipWindow::updatePosition (const String& tip, Point<int> pos, Rectangle<int> parentArea)
|
||||
{
|
||||
setBounds (getLookAndFeel().getTooltipBounds (tip, pos, parentArea));
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
#if JUCE_DEBUG
|
||||
static Array<TooltipWindow*> activeTooltipWindows;
|
||||
#endif
|
||||
|
||||
void TooltipWindow::displayTip (Point<int> screenPos, const String& tip)
|
||||
{
|
||||
jassert (tip.isNotEmpty());
|
||||
|
||||
displayTipInternal (screenPos, tip, ShownManually::yes);
|
||||
}
|
||||
|
||||
void TooltipWindow::displayTipInternal (Point<int> screenPos, const String& tip, ShownManually shownManually)
|
||||
{
|
||||
if (! reentrant)
|
||||
{
|
||||
ScopedValueSetter<bool> setter (reentrant, true, false);
|
||||
|
||||
if (tipShowing != tip)
|
||||
{
|
||||
tipShowing = tip;
|
||||
repaint();
|
||||
}
|
||||
|
||||
if (auto* parent = getParentComponent())
|
||||
{
|
||||
updatePosition (tip, parent->getLocalPoint (nullptr, screenPos),
|
||||
parent->getLocalBounds());
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto physicalPos = ScalingHelpers::scaledScreenPosToUnscaled (screenPos);
|
||||
const auto scaledPos = ScalingHelpers::unscaledScreenPosToScaled (*this, physicalPos);
|
||||
updatePosition (tip, scaledPos, Desktop::getInstance().getDisplays().getDisplayForPoint (screenPos)->userArea);
|
||||
|
||||
addToDesktop (ComponentPeer::windowHasDropShadow
|
||||
| ComponentPeer::windowIsTemporary
|
||||
| ComponentPeer::windowIgnoresKeyPresses
|
||||
| ComponentPeer::windowIgnoresMouseClicks);
|
||||
}
|
||||
|
||||
#if JUCE_DEBUG
|
||||
activeTooltipWindows.addIfNotAlreadyThere (this);
|
||||
|
||||
auto* parent = getParentComponent();
|
||||
|
||||
for (auto* w : activeTooltipWindows)
|
||||
{
|
||||
if (w != nullptr && w != this && w->tipShowing == tipShowing && w->getParentComponent() == parent)
|
||||
{
|
||||
// Looks like you have more than one TooltipWindow showing the same tip..
|
||||
// Be careful not to create more than one instance of this class with the
|
||||
// same parent component!
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
toFront (false);
|
||||
manuallyShownTip = shownManually == ShownManually::yes ? tip : String();
|
||||
dismissalMouseEventOccurred = false;
|
||||
}
|
||||
}
|
||||
|
||||
String TooltipWindow::getTipFor (Component& c)
|
||||
{
|
||||
if (isForegroundOrEmbeddedProcess (&c)
|
||||
&& ! ModifierKeys::currentModifiers.isAnyMouseButtonDown())
|
||||
{
|
||||
if (auto* ttc = dynamic_cast<TooltipClient*> (&c))
|
||||
if (! c.isCurrentlyBlockedByAnotherModalComponent())
|
||||
return ttc->getTooltip();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void TooltipWindow::hideTip()
|
||||
{
|
||||
if (isVisible() && ! reentrant)
|
||||
{
|
||||
tipShowing = {};
|
||||
manuallyShownTip = {};
|
||||
dismissalMouseEventOccurred = false;
|
||||
|
||||
removeFromDesktop();
|
||||
setVisible (false);
|
||||
|
||||
lastHideTime = Time::getApproximateMillisecondCounter();
|
||||
|
||||
#if JUCE_DEBUG
|
||||
activeTooltipWindows.removeAllInstancesOf (this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
float TooltipWindow::getDesktopScaleFactor() const
|
||||
{
|
||||
if (lastComponentUnderMouse != nullptr)
|
||||
return Component::getApproximateScaleFactorForComponent (lastComponentUnderMouse);
|
||||
|
||||
return Component::getDesktopScaleFactor();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> TooltipWindow::createAccessibilityHandler()
|
||||
{
|
||||
return createIgnoredAccessibilityHandler (*this);
|
||||
}
|
||||
|
||||
void TooltipWindow::timerCallback()
|
||||
{
|
||||
const auto mouseSource = Desktop::getInstance().getMainMouseSource();
|
||||
auto* newComp = mouseSource.isTouch() ? nullptr : mouseSource.getComponentUnderMouse();
|
||||
|
||||
if (manuallyShownTip.isNotEmpty())
|
||||
{
|
||||
if (dismissalMouseEventOccurred || newComp == nullptr)
|
||||
hideTip();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (newComp == nullptr || getParentComponent() == nullptr || newComp->getPeer() == getPeer())
|
||||
{
|
||||
const auto newTip = newComp != nullptr ? getTipFor (*newComp) : String();
|
||||
|
||||
const auto mousePos = mouseSource.getScreenPosition();
|
||||
const auto mouseMovedQuickly = (mousePos.getDistanceFrom (lastMousePos) > 12);
|
||||
lastMousePos = mousePos;
|
||||
|
||||
const auto tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse);
|
||||
const auto now = Time::getApproximateMillisecondCounter();
|
||||
|
||||
lastComponentUnderMouse = newComp;
|
||||
lastTipUnderMouse = newTip;
|
||||
|
||||
if (tipChanged || dismissalMouseEventOccurred || mouseMovedQuickly)
|
||||
lastCompChangeTime = now;
|
||||
|
||||
const auto showTip = [this, &mouseSource, &mousePos, &newTip]
|
||||
{
|
||||
if (mouseSource.getLastMouseDownPosition() != lastMousePos)
|
||||
displayTipInternal (mousePos.roundToInt(), newTip, ShownManually::no);
|
||||
};
|
||||
|
||||
if (isVisible() || now < lastHideTime + 500)
|
||||
{
|
||||
// if a tip is currently visible (or has just disappeared), update to a new one
|
||||
// immediately if needed..
|
||||
if (newComp == nullptr || dismissalMouseEventOccurred || newTip.isEmpty())
|
||||
hideTip();
|
||||
else if (tipChanged)
|
||||
showTip();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if there isn't currently a tip, but one is needed, only let it appear after a timeout
|
||||
if (newTip.isNotEmpty()
|
||||
&& newTip != tipShowing
|
||||
&& now > lastCompChangeTime + (uint32) millisecondsBeforeTipAppears)
|
||||
{
|
||||
showTip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,149 +1,159 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A window that displays a pop-up tooltip when the mouse hovers over another component.
|
||||
|
||||
To enable tooltips in your app, just create a single instance of a TooltipWindow
|
||||
object. Note that if you instantiate more than one instance of this class with the
|
||||
same parentComponent (even if both TooltipWindow's parentComponent is nil), you'll
|
||||
end up with multiple tooltips being shown! To avoid this use a SharedResourcePointer
|
||||
to instantiate the TooltipWindow only once.
|
||||
|
||||
For audio plug-ins (which should not be opening native windows) it is better
|
||||
to add a TooltipWindow as a member variable to the editor and ensure that the
|
||||
editor is the parentComponent of your TooltipWindow. This will ensure that your
|
||||
TooltipWindow is scaled according to your editor and the DAWs scaling setting.
|
||||
|
||||
The TooltipWindow object will then stay invisible, waiting until the mouse
|
||||
hovers for the specified length of time - it will then see if it's currently
|
||||
over a component which implements the TooltipClient interface, and if so,
|
||||
it will make itself visible to show the tooltip in the appropriate place.
|
||||
|
||||
@see TooltipClient, SettableTooltipClient, SharedResourcePointer
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TooltipWindow : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a tooltip window.
|
||||
|
||||
Make sure your app only creates one instance of this class, otherwise you'll
|
||||
get multiple overlaid tooltips appearing. The window will initially be invisible
|
||||
and will make itself visible when it needs to display a tip.
|
||||
|
||||
To change the style of tooltips, see the LookAndFeel class for its tooltip
|
||||
methods.
|
||||
|
||||
@param parentComponent if set to nullptr, the TooltipWindow will appear on the desktop,
|
||||
otherwise the tooltip will be added to the given parent
|
||||
component.
|
||||
@param millisecondsBeforeTipAppears the time for which the mouse has to stay still
|
||||
before a tooltip will be shown
|
||||
|
||||
@see TooltipClient, LookAndFeel::drawTooltip, LookAndFeel::getTooltipBounds
|
||||
*/
|
||||
explicit TooltipWindow (Component* parentComponent = nullptr,
|
||||
int millisecondsBeforeTipAppears = 700);
|
||||
|
||||
/** Destructor. */
|
||||
~TooltipWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the time before the tip appears.
|
||||
This lets you change the value that was set in the constructor.
|
||||
*/
|
||||
void setMillisecondsBeforeTipAppears (int newTimeMs = 700) noexcept;
|
||||
|
||||
/** Can be called to manually force a tip to be shown at a particular location. */
|
||||
void displayTip (Point<int> screenPosition, const String& text);
|
||||
|
||||
/** Can be called to manually hide the tip if it's showing. */
|
||||
void hideTip();
|
||||
|
||||
/** Asks a component for its tooltip.
|
||||
This can be overridden if you need custom lookup behaviour or to modify the strings.
|
||||
*/
|
||||
virtual String getTipFor (Component&);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the tooltip.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1001b00, /**< The colour to fill the background with. */
|
||||
textColourId = 0x1001c00, /**< The colour to use for the text. */
|
||||
outlineColourId = 0x1001c10 /**< The colour to use to draw an outline around the tooltip. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
/** returns the bounds for a tooltip at the given screen coordinate, constrained within the given desktop area. */
|
||||
virtual Rectangle<int> getTooltipBounds (const String& tipText, Point<int> screenPos, Rectangle<int> parentArea) = 0;
|
||||
virtual void drawTooltip (Graphics&, const String& text, int width, int height) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
float getDesktopScaleFactor() const override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Point<float> lastMousePos;
|
||||
Component* lastComponentUnderMouse = nullptr;
|
||||
String tipShowing, lastTipUnderMouse;
|
||||
int millisecondsBeforeTipAppears;
|
||||
int mouseClicks = 0, mouseWheelMoves = 0;
|
||||
unsigned int lastCompChangeTime = 0, lastHideTime = 0;
|
||||
bool reentrant = false;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void paint (Graphics&) override;
|
||||
void mouseEnter (const MouseEvent&) override;
|
||||
void timerCallback() override;
|
||||
void updatePosition (const String&, Point<int>, Rectangle<int>);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TooltipWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A window that displays a pop-up tooltip when the mouse hovers over another component.
|
||||
|
||||
To enable tooltips in your app, just create a single instance of a TooltipWindow
|
||||
object. Note that if you instantiate more than one instance of this class with the
|
||||
same parentComponent (even if both TooltipWindow's parentComponent is nil), you'll
|
||||
end up with multiple tooltips being shown! To avoid this use a SharedResourcePointer
|
||||
to instantiate the TooltipWindow only once.
|
||||
|
||||
For audio plug-ins (which should not be opening native windows) it is better
|
||||
to add a TooltipWindow as a member variable to the editor and ensure that the
|
||||
editor is the parentComponent of your TooltipWindow. This will ensure that your
|
||||
TooltipWindow is scaled according to your editor and the DAWs scaling setting.
|
||||
|
||||
The TooltipWindow object will then stay invisible, waiting until the mouse
|
||||
hovers for the specified length of time - it will then see if it's currently
|
||||
over a component which implements the TooltipClient interface, and if so,
|
||||
it will make itself visible to show the tooltip in the appropriate place.
|
||||
|
||||
@see TooltipClient, SettableTooltipClient, SharedResourcePointer
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TooltipWindow : public Component,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a tooltip window.
|
||||
|
||||
Make sure your app only creates one instance of this class, otherwise you'll
|
||||
get multiple overlaid tooltips appearing. The window will initially be invisible
|
||||
and will make itself visible when it needs to display a tip.
|
||||
|
||||
To change the style of tooltips, see the LookAndFeel class for its tooltip
|
||||
methods.
|
||||
|
||||
@param parentComponent if set to nullptr, the TooltipWindow will appear on the desktop,
|
||||
otherwise the tooltip will be added to the given parent
|
||||
component.
|
||||
@param millisecondsBeforeTipAppears the time for which the mouse has to stay still
|
||||
before a tooltip will be shown
|
||||
|
||||
@see TooltipClient, LookAndFeel::drawTooltip, LookAndFeel::getTooltipBounds
|
||||
*/
|
||||
explicit TooltipWindow (Component* parentComponent = nullptr,
|
||||
int millisecondsBeforeTipAppears = 700);
|
||||
|
||||
/** Destructor. */
|
||||
~TooltipWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the time before the tip appears.
|
||||
This lets you change the value that was set in the constructor.
|
||||
*/
|
||||
void setMillisecondsBeforeTipAppears (int newTimeMs = 700) noexcept;
|
||||
|
||||
/** Can be called to manually force a tip to be shown at a particular location.
|
||||
|
||||
The tip will be shown until hideTip() is called, or a dismissal mouse event
|
||||
occurs.
|
||||
|
||||
@see hideTip
|
||||
*/
|
||||
void displayTip (Point<int> screenPosition, const String& text);
|
||||
|
||||
/** Can be called to manually hide the tip if it's showing. */
|
||||
void hideTip();
|
||||
|
||||
/** Asks a component for its tooltip.
|
||||
This can be overridden if you need custom lookup behaviour or to modify the strings.
|
||||
*/
|
||||
virtual String getTipFor (Component&);
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the tooltip.
|
||||
|
||||
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
|
||||
methods.
|
||||
|
||||
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
|
||||
*/
|
||||
enum ColourIds
|
||||
{
|
||||
backgroundColourId = 0x1001b00, /**< The colour to fill the background with. */
|
||||
textColourId = 0x1001c00, /**< The colour to use for the text. */
|
||||
outlineColourId = 0x1001c10 /**< The colour to use to draw an outline around the tooltip. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
/** returns the bounds for a tooltip at the given screen coordinate, constrained within the given desktop area. */
|
||||
virtual Rectangle<int> getTooltipBounds (const String& tipText, Point<int> screenPos, Rectangle<int> parentArea) = 0;
|
||||
virtual void drawTooltip (Graphics&, const String& text, int width, int height) = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
float getDesktopScaleFactor() const override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Point<float> lastMousePos;
|
||||
SafePointer<Component> lastComponentUnderMouse;
|
||||
String tipShowing, lastTipUnderMouse, manuallyShownTip;
|
||||
int millisecondsBeforeTipAppears;
|
||||
unsigned int lastCompChangeTime = 0, lastHideTime = 0;
|
||||
bool reentrant = false, dismissalMouseEventOccurred = false;
|
||||
|
||||
enum ShownManually { yes, no };
|
||||
void displayTipInternal (Point<int>, const String&, ShownManually);
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void paint (Graphics&) override;
|
||||
void mouseEnter (const MouseEvent&) override;
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
|
||||
void timerCallback() override;
|
||||
void updatePosition (const String&, Point<int>, Rectangle<int>);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TooltipWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,358 +1,358 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** Keeps track of the active top level window. */
|
||||
class TopLevelWindowManager : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
TopLevelWindowManager() {}
|
||||
~TopLevelWindowManager() override { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (TopLevelWindowManager)
|
||||
|
||||
void checkFocusAsync()
|
||||
{
|
||||
startTimer (10);
|
||||
}
|
||||
|
||||
void checkFocus()
|
||||
{
|
||||
startTimer (jmin (1731, getTimerInterval() * 2));
|
||||
|
||||
auto* newActive = findCurrentlyActiveWindow();
|
||||
|
||||
if (newActive != currentActive)
|
||||
{
|
||||
currentActive = newActive;
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
if (auto* tlw = windows[i])
|
||||
tlw->setWindowActive (isWindowActive (tlw));
|
||||
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
}
|
||||
}
|
||||
|
||||
bool addWindow (TopLevelWindow* const w)
|
||||
{
|
||||
windows.add (w);
|
||||
checkFocusAsync();
|
||||
|
||||
return isWindowActive (w);
|
||||
}
|
||||
|
||||
void removeWindow (TopLevelWindow* const w)
|
||||
{
|
||||
checkFocusAsync();
|
||||
|
||||
if (currentActive == w)
|
||||
currentActive = nullptr;
|
||||
|
||||
windows.removeFirstMatchingValue (w);
|
||||
|
||||
if (windows.isEmpty())
|
||||
deleteInstance();
|
||||
}
|
||||
|
||||
Array<TopLevelWindow*> windows;
|
||||
|
||||
private:
|
||||
TopLevelWindow* currentActive = nullptr;
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
checkFocus();
|
||||
}
|
||||
|
||||
bool isWindowActive (TopLevelWindow* const tlw) const
|
||||
{
|
||||
return (tlw == currentActive
|
||||
|| tlw->isParentOf (currentActive)
|
||||
|| tlw->hasKeyboardFocus (true))
|
||||
&& tlw->isShowing();
|
||||
}
|
||||
|
||||
TopLevelWindow* findCurrentlyActiveWindow() const
|
||||
{
|
||||
if (Process::isForegroundProcess())
|
||||
{
|
||||
auto* focusedComp = Component::getCurrentlyFocusedComponent();
|
||||
auto* w = dynamic_cast<TopLevelWindow*> (focusedComp);
|
||||
|
||||
if (w == nullptr && focusedComp != nullptr)
|
||||
w = focusedComp->findParentComponentOfClass<TopLevelWindow>();
|
||||
|
||||
if (w == nullptr)
|
||||
w = currentActive;
|
||||
|
||||
if (w != nullptr && w->isShowing())
|
||||
return w;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (TopLevelWindowManager)
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (TopLevelWindowManager)
|
||||
|
||||
void juce_checkCurrentlyFocusedTopLevelWindow();
|
||||
void juce_checkCurrentlyFocusedTopLevelWindow()
|
||||
{
|
||||
if (auto* wm = TopLevelWindowManager::getInstanceWithoutCreating())
|
||||
wm->checkFocusAsync();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
TopLevelWindow::TopLevelWindow (const String& name, const bool shouldAddToDesktop)
|
||||
: Component (name)
|
||||
{
|
||||
setTitle (name);
|
||||
|
||||
setOpaque (true);
|
||||
|
||||
if (shouldAddToDesktop)
|
||||
Component::addToDesktop (TopLevelWindow::getDesktopWindowStyleFlags());
|
||||
else
|
||||
setDropShadowEnabled (true);
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
setBroughtToFrontOnMouseClick (true);
|
||||
isCurrentlyActive = TopLevelWindowManager::getInstance()->addWindow (this);
|
||||
}
|
||||
|
||||
TopLevelWindow::~TopLevelWindow()
|
||||
{
|
||||
shadower.reset();
|
||||
TopLevelWindowManager::getInstance()->removeWindow (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TopLevelWindow::focusOfChildComponentChanged (FocusChangeType)
|
||||
{
|
||||
auto* wm = TopLevelWindowManager::getInstance();
|
||||
|
||||
if (hasKeyboardFocus (true))
|
||||
wm->checkFocus();
|
||||
else
|
||||
wm->checkFocusAsync();
|
||||
}
|
||||
|
||||
void TopLevelWindow::setWindowActive (const bool isNowActive)
|
||||
{
|
||||
if (isCurrentlyActive != isNowActive)
|
||||
{
|
||||
isCurrentlyActive = isNowActive;
|
||||
activeWindowStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::activeWindowStatusChanged()
|
||||
{
|
||||
}
|
||||
|
||||
bool TopLevelWindow::isUsingNativeTitleBar() const noexcept
|
||||
{
|
||||
return useNativeTitleBar && (isOnDesktop() || ! isShowing());
|
||||
}
|
||||
|
||||
void TopLevelWindow::visibilityChanged()
|
||||
{
|
||||
if (isShowing())
|
||||
if (auto* p = getPeer())
|
||||
if ((p->getStyleFlags() & (ComponentPeer::windowIsTemporary
|
||||
| ComponentPeer::windowIgnoresKeyPresses)) == 0)
|
||||
toFront (true);
|
||||
}
|
||||
|
||||
void TopLevelWindow::parentHierarchyChanged()
|
||||
{
|
||||
setDropShadowEnabled (useDropShadow);
|
||||
}
|
||||
|
||||
int TopLevelWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
int styleFlags = ComponentPeer::windowAppearsOnTaskbar;
|
||||
|
||||
if (useDropShadow) styleFlags |= ComponentPeer::windowHasDropShadow;
|
||||
if (useNativeTitleBar) styleFlags |= ComponentPeer::windowHasTitleBar;
|
||||
|
||||
return styleFlags;
|
||||
}
|
||||
|
||||
void TopLevelWindow::setDropShadowEnabled (const bool useShadow)
|
||||
{
|
||||
useDropShadow = useShadow;
|
||||
|
||||
if (isOnDesktop())
|
||||
{
|
||||
shadower.reset();
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (useShadow && isOpaque())
|
||||
{
|
||||
if (shadower == nullptr)
|
||||
{
|
||||
shadower.reset (getLookAndFeel().createDropShadowerForComponent (this));
|
||||
|
||||
if (shadower != nullptr)
|
||||
shadower->setOwner (this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shadower.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::setUsingNativeTitleBar (const bool shouldUseNativeTitleBar)
|
||||
{
|
||||
if (useNativeTitleBar != shouldUseNativeTitleBar)
|
||||
{
|
||||
FocusRestorer focusRestorer;
|
||||
useNativeTitleBar = shouldUseNativeTitleBar;
|
||||
recreateDesktopWindow();
|
||||
sendLookAndFeelChange();
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::recreateDesktopWindow()
|
||||
{
|
||||
if (isOnDesktop())
|
||||
{
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
toFront (true);
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::addToDesktop()
|
||||
{
|
||||
shadower.reset();
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
setDropShadowEnabled (isDropShadowEnabled()); // force an update to clear away any fake shadows if necessary.
|
||||
}
|
||||
|
||||
void TopLevelWindow::addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo)
|
||||
{
|
||||
/* It's not recommended to change the desktop window flags directly for a TopLevelWindow,
|
||||
because this class needs to make sure its layout corresponds with settings like whether
|
||||
it's got a native title bar or not.
|
||||
|
||||
If you need custom flags for your window, you can override the getDesktopWindowStyleFlags()
|
||||
method. If you do this, it's best to call the base class's getDesktopWindowStyleFlags()
|
||||
method, then add or remove whatever flags are necessary from this value before returning it.
|
||||
*/
|
||||
jassert ((windowStyleFlags & ~ComponentPeer::windowIsSemiTransparent)
|
||||
== (getDesktopWindowStyleFlags() & ~ComponentPeer::windowIsSemiTransparent));
|
||||
|
||||
Component::addToDesktop (windowStyleFlags, nativeWindowToAttachTo);
|
||||
|
||||
if (windowStyleFlags != getDesktopWindowStyleFlags())
|
||||
sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> TopLevelWindow::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::window);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TopLevelWindow::centreAroundComponent (Component* c, const int width, const int height)
|
||||
{
|
||||
if (c == nullptr)
|
||||
c = TopLevelWindow::getActiveTopLevelWindow();
|
||||
|
||||
if (c == nullptr || c->getBounds().isEmpty())
|
||||
{
|
||||
centreWithSize (width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto scale = getDesktopScaleFactor() / Desktop::getInstance().getGlobalScaleFactor();
|
||||
|
||||
auto targetCentre = c->localPointToGlobal (c->getLocalBounds().getCentre()) / scale;
|
||||
auto parentArea = c->getParentMonitorArea();
|
||||
|
||||
if (auto* parent = getParentComponent())
|
||||
{
|
||||
targetCentre = parent->getLocalPoint (nullptr, targetCentre);
|
||||
parentArea = parent->getLocalBounds();
|
||||
}
|
||||
|
||||
setBounds (Rectangle<int> (targetCentre.x - width / 2,
|
||||
targetCentre.y - height / 2,
|
||||
width, height)
|
||||
.constrainedWithin (parentArea.reduced (12, 12)));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int TopLevelWindow::getNumTopLevelWindows() noexcept
|
||||
{
|
||||
return TopLevelWindowManager::getInstance()->windows.size();
|
||||
}
|
||||
|
||||
TopLevelWindow* TopLevelWindow::getTopLevelWindow (const int index) noexcept
|
||||
{
|
||||
return TopLevelWindowManager::getInstance()->windows [index];
|
||||
}
|
||||
|
||||
TopLevelWindow* TopLevelWindow::getActiveTopLevelWindow() noexcept
|
||||
{
|
||||
TopLevelWindow* best = nullptr;
|
||||
int bestNumTWLParents = -1;
|
||||
|
||||
for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
|
||||
{
|
||||
auto* tlw = TopLevelWindow::getTopLevelWindow (i);
|
||||
|
||||
if (tlw->isActiveWindow())
|
||||
{
|
||||
int numTWLParents = 0;
|
||||
|
||||
for (auto* c = tlw->getParentComponent(); c != nullptr; c = c->getParentComponent())
|
||||
if (dynamic_cast<const TopLevelWindow*> (c) != nullptr)
|
||||
++numTWLParents;
|
||||
|
||||
if (bestNumTWLParents < numTWLParents)
|
||||
{
|
||||
best = tlw;
|
||||
bestNumTWLParents = numTWLParents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/** Keeps track of the active top level window. */
|
||||
class TopLevelWindowManager : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
TopLevelWindowManager() {}
|
||||
~TopLevelWindowManager() override { clearSingletonInstance(); }
|
||||
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (TopLevelWindowManager)
|
||||
|
||||
void checkFocusAsync()
|
||||
{
|
||||
startTimer (10);
|
||||
}
|
||||
|
||||
void checkFocus()
|
||||
{
|
||||
startTimer (jmin (1731, getTimerInterval() * 2));
|
||||
|
||||
auto* newActive = findCurrentlyActiveWindow();
|
||||
|
||||
if (newActive != currentActive)
|
||||
{
|
||||
currentActive = newActive;
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
if (auto* tlw = windows[i])
|
||||
tlw->setWindowActive (isWindowActive (tlw));
|
||||
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
}
|
||||
}
|
||||
|
||||
bool addWindow (TopLevelWindow* const w)
|
||||
{
|
||||
windows.add (w);
|
||||
checkFocusAsync();
|
||||
|
||||
return isWindowActive (w);
|
||||
}
|
||||
|
||||
void removeWindow (TopLevelWindow* const w)
|
||||
{
|
||||
checkFocusAsync();
|
||||
|
||||
if (currentActive == w)
|
||||
currentActive = nullptr;
|
||||
|
||||
windows.removeFirstMatchingValue (w);
|
||||
|
||||
if (windows.isEmpty())
|
||||
deleteInstance();
|
||||
}
|
||||
|
||||
Array<TopLevelWindow*> windows;
|
||||
|
||||
private:
|
||||
TopLevelWindow* currentActive = nullptr;
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
checkFocus();
|
||||
}
|
||||
|
||||
bool isWindowActive (TopLevelWindow* const tlw) const
|
||||
{
|
||||
return (tlw == currentActive
|
||||
|| tlw->isParentOf (currentActive)
|
||||
|| tlw->hasKeyboardFocus (true))
|
||||
&& tlw->isShowing();
|
||||
}
|
||||
|
||||
TopLevelWindow* findCurrentlyActiveWindow() const
|
||||
{
|
||||
if (Process::isForegroundProcess())
|
||||
{
|
||||
auto* focusedComp = Component::getCurrentlyFocusedComponent();
|
||||
auto* w = dynamic_cast<TopLevelWindow*> (focusedComp);
|
||||
|
||||
if (w == nullptr && focusedComp != nullptr)
|
||||
w = focusedComp->findParentComponentOfClass<TopLevelWindow>();
|
||||
|
||||
if (w == nullptr)
|
||||
w = currentActive;
|
||||
|
||||
if (w != nullptr && w->isShowing())
|
||||
return w;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (TopLevelWindowManager)
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (TopLevelWindowManager)
|
||||
|
||||
void juce_checkCurrentlyFocusedTopLevelWindow();
|
||||
void juce_checkCurrentlyFocusedTopLevelWindow()
|
||||
{
|
||||
if (auto* wm = TopLevelWindowManager::getInstanceWithoutCreating())
|
||||
wm->checkFocusAsync();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
TopLevelWindow::TopLevelWindow (const String& name, const bool shouldAddToDesktop)
|
||||
: Component (name)
|
||||
{
|
||||
setTitle (name);
|
||||
|
||||
setOpaque (true);
|
||||
|
||||
if (shouldAddToDesktop)
|
||||
Component::addToDesktop (TopLevelWindow::getDesktopWindowStyleFlags());
|
||||
else
|
||||
setDropShadowEnabled (true);
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
setBroughtToFrontOnMouseClick (true);
|
||||
isCurrentlyActive = TopLevelWindowManager::getInstance()->addWindow (this);
|
||||
}
|
||||
|
||||
TopLevelWindow::~TopLevelWindow()
|
||||
{
|
||||
shadower = nullptr;
|
||||
TopLevelWindowManager::getInstance()->removeWindow (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TopLevelWindow::focusOfChildComponentChanged (FocusChangeType)
|
||||
{
|
||||
auto* wm = TopLevelWindowManager::getInstance();
|
||||
|
||||
if (hasKeyboardFocus (true))
|
||||
wm->checkFocus();
|
||||
else
|
||||
wm->checkFocusAsync();
|
||||
}
|
||||
|
||||
void TopLevelWindow::setWindowActive (const bool isNowActive)
|
||||
{
|
||||
if (isCurrentlyActive != isNowActive)
|
||||
{
|
||||
isCurrentlyActive = isNowActive;
|
||||
activeWindowStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::activeWindowStatusChanged()
|
||||
{
|
||||
}
|
||||
|
||||
bool TopLevelWindow::isUsingNativeTitleBar() const noexcept
|
||||
{
|
||||
return useNativeTitleBar && (isOnDesktop() || ! isShowing());
|
||||
}
|
||||
|
||||
void TopLevelWindow::visibilityChanged()
|
||||
{
|
||||
if (isShowing())
|
||||
if (auto* p = getPeer())
|
||||
if ((p->getStyleFlags() & (ComponentPeer::windowIsTemporary
|
||||
| ComponentPeer::windowIgnoresKeyPresses)) == 0)
|
||||
toFront (true);
|
||||
}
|
||||
|
||||
void TopLevelWindow::parentHierarchyChanged()
|
||||
{
|
||||
setDropShadowEnabled (useDropShadow);
|
||||
}
|
||||
|
||||
int TopLevelWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
int styleFlags = ComponentPeer::windowAppearsOnTaskbar;
|
||||
|
||||
if (useDropShadow) styleFlags |= ComponentPeer::windowHasDropShadow;
|
||||
if (useNativeTitleBar) styleFlags |= ComponentPeer::windowHasTitleBar;
|
||||
|
||||
return styleFlags;
|
||||
}
|
||||
|
||||
void TopLevelWindow::setDropShadowEnabled (const bool useShadow)
|
||||
{
|
||||
useDropShadow = useShadow;
|
||||
|
||||
if (isOnDesktop())
|
||||
{
|
||||
shadower = nullptr;
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (useShadow && isOpaque())
|
||||
{
|
||||
if (shadower == nullptr)
|
||||
{
|
||||
shadower = getLookAndFeel().createDropShadowerForComponent (*this);
|
||||
|
||||
if (shadower != nullptr)
|
||||
shadower->setOwner (this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shadower = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::setUsingNativeTitleBar (const bool shouldUseNativeTitleBar)
|
||||
{
|
||||
if (useNativeTitleBar != shouldUseNativeTitleBar)
|
||||
{
|
||||
FocusRestorer focusRestorer;
|
||||
useNativeTitleBar = shouldUseNativeTitleBar;
|
||||
recreateDesktopWindow();
|
||||
sendLookAndFeelChange();
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::recreateDesktopWindow()
|
||||
{
|
||||
if (isOnDesktop())
|
||||
{
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
toFront (true);
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::addToDesktop()
|
||||
{
|
||||
shadower = nullptr;
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
setDropShadowEnabled (isDropShadowEnabled()); // force an update to clear away any fake shadows if necessary.
|
||||
}
|
||||
|
||||
void TopLevelWindow::addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo)
|
||||
{
|
||||
/* It's not recommended to change the desktop window flags directly for a TopLevelWindow,
|
||||
because this class needs to make sure its layout corresponds with settings like whether
|
||||
it's got a native title bar or not.
|
||||
|
||||
If you need custom flags for your window, you can override the getDesktopWindowStyleFlags()
|
||||
method. If you do this, it's best to call the base class's getDesktopWindowStyleFlags()
|
||||
method, then add or remove whatever flags are necessary from this value before returning it.
|
||||
*/
|
||||
jassert ((windowStyleFlags & ~ComponentPeer::windowIsSemiTransparent)
|
||||
== (getDesktopWindowStyleFlags() & ~ComponentPeer::windowIsSemiTransparent));
|
||||
|
||||
Component::addToDesktop (windowStyleFlags, nativeWindowToAttachTo);
|
||||
|
||||
if (windowStyleFlags != getDesktopWindowStyleFlags())
|
||||
sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> TopLevelWindow::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::window);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TopLevelWindow::centreAroundComponent (Component* c, const int width, const int height)
|
||||
{
|
||||
if (c == nullptr)
|
||||
c = TopLevelWindow::getActiveTopLevelWindow();
|
||||
|
||||
if (c == nullptr || c->getBounds().isEmpty())
|
||||
{
|
||||
centreWithSize (width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto scale = getDesktopScaleFactor() / Desktop::getInstance().getGlobalScaleFactor();
|
||||
|
||||
auto targetCentre = c->localPointToGlobal (c->getLocalBounds().getCentre()) / scale;
|
||||
auto parentArea = getLocalArea (nullptr, c->getParentMonitorArea());
|
||||
|
||||
if (auto* parent = getParentComponent())
|
||||
{
|
||||
targetCentre = parent->getLocalPoint (nullptr, targetCentre);
|
||||
parentArea = parent->getLocalBounds();
|
||||
}
|
||||
|
||||
setBounds (Rectangle<int> (targetCentre.x - width / 2,
|
||||
targetCentre.y - height / 2,
|
||||
width, height)
|
||||
.constrainedWithin (parentArea.reduced (12, 12)));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int TopLevelWindow::getNumTopLevelWindows() noexcept
|
||||
{
|
||||
return TopLevelWindowManager::getInstance()->windows.size();
|
||||
}
|
||||
|
||||
TopLevelWindow* TopLevelWindow::getTopLevelWindow (const int index) noexcept
|
||||
{
|
||||
return TopLevelWindowManager::getInstance()->windows [index];
|
||||
}
|
||||
|
||||
TopLevelWindow* TopLevelWindow::getActiveTopLevelWindow() noexcept
|
||||
{
|
||||
TopLevelWindow* best = nullptr;
|
||||
int bestNumTWLParents = -1;
|
||||
|
||||
for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;)
|
||||
{
|
||||
auto* tlw = TopLevelWindow::getTopLevelWindow (i);
|
||||
|
||||
if (tlw->isActiveWindow())
|
||||
{
|
||||
int numTWLParents = 0;
|
||||
|
||||
for (auto* c = tlw->getParentComponent(); c != nullptr; c = c->getParentComponent())
|
||||
if (dynamic_cast<const TopLevelWindow*> (c) != nullptr)
|
||||
++numTWLParents;
|
||||
|
||||
if (bestNumTWLParents < numTWLParents)
|
||||
{
|
||||
best = tlw;
|
||||
bestNumTWLParents = numTWLParents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -1,166 +1,166 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for top-level windows.
|
||||
|
||||
This class is used for components that are considered a major part of your
|
||||
application - e.g. ResizableWindow, DocumentWindow, DialogWindow, AlertWindow,
|
||||
etc. Things like menus that pop up briefly aren't derived from it.
|
||||
|
||||
A TopLevelWindow is probably on the desktop, but this isn't mandatory - it
|
||||
could itself be the child of another component.
|
||||
|
||||
The class manages a list of all instances of top-level windows that are in use,
|
||||
and each one is also given the concept of being "active". The active window is
|
||||
one that is actively being used by the user. This isn't quite the same as the
|
||||
component with the keyboard focus, because there may be a popup menu or other
|
||||
temporary window which gets keyboard focus while the active top level window is
|
||||
unchanged.
|
||||
|
||||
A top-level window also has an optional drop-shadow.
|
||||
|
||||
@see ResizableWindow, DocumentWindow, DialogWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TopLevelWindow : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a TopLevelWindow.
|
||||
|
||||
@param name the name to give the component
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
*/
|
||||
TopLevelWindow (const String& name, bool addToDesktop);
|
||||
|
||||
/** Destructor. */
|
||||
~TopLevelWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** True if this is currently the TopLevelWindow that is actively being used.
|
||||
|
||||
This isn't quite the same as having keyboard focus, because the focus may be
|
||||
on a child component or a temporary pop-up menu, etc, while this window is
|
||||
still considered to be active.
|
||||
|
||||
@see activeWindowStatusChanged
|
||||
*/
|
||||
bool isActiveWindow() const noexcept { return isCurrentlyActive; }
|
||||
|
||||
//==============================================================================
|
||||
/** This will set the bounds of the window so that it's centred in front of another
|
||||
window.
|
||||
|
||||
If your app has a few windows open and want to pop up a dialog box for one of
|
||||
them, you can use this to show it in front of the relevant parent window, which
|
||||
is a bit neater than just having it appear in the middle of the screen.
|
||||
|
||||
If componentToCentreAround is nullptr, then the currently active TopLevelWindow will
|
||||
be used instead. If no window is focused, it'll just default to the middle of the
|
||||
screen.
|
||||
*/
|
||||
void centreAroundComponent (Component* componentToCentreAround,
|
||||
int width, int height);
|
||||
|
||||
//==============================================================================
|
||||
/** Turns the drop-shadow on and off. */
|
||||
void setDropShadowEnabled (bool useShadow);
|
||||
|
||||
/** True if drop-shadowing is enabled. */
|
||||
bool isDropShadowEnabled() const noexcept { return useDropShadow; }
|
||||
|
||||
/** Sets whether an OS-native title bar will be used, or a JUCE one.
|
||||
@see isUsingNativeTitleBar
|
||||
*/
|
||||
void setUsingNativeTitleBar (bool useNativeTitleBar);
|
||||
|
||||
/** Returns true if the window is currently using an OS-native title bar.
|
||||
@see setUsingNativeTitleBar
|
||||
*/
|
||||
bool isUsingNativeTitleBar() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of TopLevelWindow objects currently in use.
|
||||
@see getTopLevelWindow
|
||||
*/
|
||||
static int getNumTopLevelWindows() noexcept;
|
||||
|
||||
/** Returns one of the TopLevelWindow objects currently in use.
|
||||
The index is 0 to (getNumTopLevelWindows() - 1).
|
||||
*/
|
||||
static TopLevelWindow* getTopLevelWindow (int index) noexcept;
|
||||
|
||||
/** Returns the currently-active top level window.
|
||||
There might not be one, of course, so this can return nullptr.
|
||||
*/
|
||||
static TopLevelWindow* getActiveTopLevelWindow() noexcept;
|
||||
|
||||
/** Adds the window to the desktop using the default flags. */
|
||||
void addToDesktop();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo = nullptr) override;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This callback happens when this window becomes active or inactive.
|
||||
@see isActiveWindow
|
||||
*/
|
||||
virtual void activeWindowStatusChanged();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void focusOfChildComponentChanged (FocusChangeType) override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
virtual int getDesktopWindowStyleFlags() const;
|
||||
/** @internal */
|
||||
void recreateDesktopWindow();
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
|
||||
private:
|
||||
friend class TopLevelWindowManager;
|
||||
friend class ResizableWindow;
|
||||
bool useDropShadow = true, useNativeTitleBar = false, isCurrentlyActive = false;
|
||||
std::unique_ptr<DropShadower> shadower;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void setWindowActive (bool);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TopLevelWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A base class for top-level windows.
|
||||
|
||||
This class is used for components that are considered a major part of your
|
||||
application - e.g. ResizableWindow, DocumentWindow, DialogWindow, AlertWindow,
|
||||
etc. Things like menus that pop up briefly aren't derived from it.
|
||||
|
||||
A TopLevelWindow is probably on the desktop, but this isn't mandatory - it
|
||||
could itself be the child of another component.
|
||||
|
||||
The class manages a list of all instances of top-level windows that are in use,
|
||||
and each one is also given the concept of being "active". The active window is
|
||||
one that is actively being used by the user. This isn't quite the same as the
|
||||
component with the keyboard focus, because there may be a popup menu or other
|
||||
temporary window which gets keyboard focus while the active top level window is
|
||||
unchanged.
|
||||
|
||||
A top-level window also has an optional drop-shadow.
|
||||
|
||||
@see ResizableWindow, DocumentWindow, DialogWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API TopLevelWindow : public Component
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a TopLevelWindow.
|
||||
|
||||
@param name the name to give the component
|
||||
@param addToDesktop if true, the window will be automatically added to the
|
||||
desktop; if false, you can use it as a child component
|
||||
*/
|
||||
TopLevelWindow (const String& name, bool addToDesktop);
|
||||
|
||||
/** Destructor. */
|
||||
~TopLevelWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** True if this is currently the TopLevelWindow that is actively being used.
|
||||
|
||||
This isn't quite the same as having keyboard focus, because the focus may be
|
||||
on a child component or a temporary pop-up menu, etc, while this window is
|
||||
still considered to be active.
|
||||
|
||||
@see activeWindowStatusChanged
|
||||
*/
|
||||
bool isActiveWindow() const noexcept { return isCurrentlyActive; }
|
||||
|
||||
//==============================================================================
|
||||
/** This will set the bounds of the window so that it's centred in front of another
|
||||
window.
|
||||
|
||||
If your app has a few windows open and want to pop up a dialog box for one of
|
||||
them, you can use this to show it in front of the relevant parent window, which
|
||||
is a bit neater than just having it appear in the middle of the screen.
|
||||
|
||||
If componentToCentreAround is nullptr, then the currently active TopLevelWindow will
|
||||
be used instead. If no window is focused, it'll just default to the middle of the
|
||||
screen.
|
||||
*/
|
||||
void centreAroundComponent (Component* componentToCentreAround,
|
||||
int width, int height);
|
||||
|
||||
//==============================================================================
|
||||
/** Turns the drop-shadow on and off. */
|
||||
void setDropShadowEnabled (bool useShadow);
|
||||
|
||||
/** True if drop-shadowing is enabled. */
|
||||
bool isDropShadowEnabled() const noexcept { return useDropShadow; }
|
||||
|
||||
/** Sets whether an OS-native title bar will be used, or a JUCE one.
|
||||
@see isUsingNativeTitleBar
|
||||
*/
|
||||
void setUsingNativeTitleBar (bool useNativeTitleBar);
|
||||
|
||||
/** Returns true if the window is currently using an OS-native title bar.
|
||||
@see setUsingNativeTitleBar
|
||||
*/
|
||||
bool isUsingNativeTitleBar() const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of TopLevelWindow objects currently in use.
|
||||
@see getTopLevelWindow
|
||||
*/
|
||||
static int getNumTopLevelWindows() noexcept;
|
||||
|
||||
/** Returns one of the TopLevelWindow objects currently in use.
|
||||
The index is 0 to (getNumTopLevelWindows() - 1).
|
||||
*/
|
||||
static TopLevelWindow* getTopLevelWindow (int index) noexcept;
|
||||
|
||||
/** Returns the currently-active top level window.
|
||||
There might not be one, of course, so this can return nullptr.
|
||||
*/
|
||||
static TopLevelWindow* getActiveTopLevelWindow() noexcept;
|
||||
|
||||
/** Adds the window to the desktop using the default flags. */
|
||||
void addToDesktop();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo = nullptr) override;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** This callback happens when this window becomes active or inactive.
|
||||
@see isActiveWindow
|
||||
*/
|
||||
virtual void activeWindowStatusChanged();
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void focusOfChildComponentChanged (FocusChangeType) override;
|
||||
/** @internal */
|
||||
void parentHierarchyChanged() override;
|
||||
/** @internal */
|
||||
virtual int getDesktopWindowStyleFlags() const;
|
||||
/** @internal */
|
||||
void recreateDesktopWindow();
|
||||
/** @internal */
|
||||
void visibilityChanged() override;
|
||||
|
||||
private:
|
||||
friend class TopLevelWindowManager;
|
||||
friend class ResizableWindow;
|
||||
bool useDropShadow = true, useNativeTitleBar = false, isCurrentlyActive = false;
|
||||
std::unique_ptr<DropShadower> shadower;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void setWindowActive (bool);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TopLevelWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
Reference in New Issue
Block a user