git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
799
deps/juce/modules/juce_gui_basics/windows/juce_AlertWindow.cpp
vendored
Normal file
799
deps/juce/modules/juce_gui_basics/windows/juce_AlertWindow.cpp
vendored
Normal file
@ -0,0 +1,799 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
static juce_wchar getDefaultPasswordChar() noexcept
|
||||
{
|
||||
#if JUCE_LINUX || JUCE_BSD
|
||||
return 0x2022;
|
||||
#else
|
||||
return 0x25cf;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AlertWindow::AlertWindow (const String& title,
|
||||
const String& message,
|
||||
MessageBoxIconType iconType,
|
||||
Component* comp)
|
||||
: TopLevelWindow (title, true),
|
||||
alertIconType (iconType),
|
||||
associatedComponent (comp),
|
||||
desktopScale (comp != nullptr ? Component::getApproximateScaleFactorForComponent (comp) : 1.0f)
|
||||
{
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
|
||||
accessibleMessageLabel.setColour (Label::textColourId, Colours::transparentBlack);
|
||||
addAndMakeVisible (accessibleMessageLabel);
|
||||
|
||||
if (message.isEmpty())
|
||||
text = " "; // to force an update if the message is empty
|
||||
|
||||
setMessage (message);
|
||||
|
||||
AlertWindow::lookAndFeelChanged();
|
||||
constrainer.setMinimumOnscreenAmounts (0x10000, 0x10000, 0x10000, 0x10000);
|
||||
}
|
||||
|
||||
AlertWindow::~AlertWindow()
|
||||
{
|
||||
// Ensure that the focus does not jump to another TextEditor while we
|
||||
// remove children.
|
||||
for (auto* t : textBoxes)
|
||||
t->setWantsKeyboardFocus (false);
|
||||
|
||||
// Give away focus before removing the editors, so that any TextEditor
|
||||
// with focus has a chance to dismiss native keyboard if shown.
|
||||
giveAwayKeyboardFocus();
|
||||
|
||||
removeAllChildren();
|
||||
}
|
||||
|
||||
void AlertWindow::userTriedToCloseWindow()
|
||||
{
|
||||
if (escapeKeyCancels || buttons.size() > 0)
|
||||
exitModalState (0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::setMessage (const String& message)
|
||||
{
|
||||
auto newMessage = message.substring (0, 2048);
|
||||
|
||||
if (text != newMessage)
|
||||
{
|
||||
text = newMessage;
|
||||
|
||||
auto accessibleText = getName() + ". " + text;
|
||||
accessibleMessageLabel.setText (accessibleText, NotificationType::dontSendNotification);
|
||||
setDescription (accessibleText);
|
||||
|
||||
updateLayout (true);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::exitAlert (Button* button)
|
||||
{
|
||||
if (auto* parent = button->getParentComponent())
|
||||
parent->exitModalState (button->getCommandID());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addButton (const String& name,
|
||||
const int returnValue,
|
||||
const KeyPress& shortcutKey1,
|
||||
const KeyPress& shortcutKey2)
|
||||
{
|
||||
auto* b = new TextButton (name, {});
|
||||
buttons.add (b);
|
||||
|
||||
b->setWantsKeyboardFocus (true);
|
||||
b->setExplicitFocusOrder (1);
|
||||
b->setMouseClickGrabsKeyboardFocus (false);
|
||||
b->setCommandToTrigger (nullptr, returnValue, false);
|
||||
b->addShortcut (shortcutKey1);
|
||||
b->addShortcut (shortcutKey2);
|
||||
b->onClick = [this, b] { exitAlert (b); };
|
||||
|
||||
Array<TextButton*> buttonsArray (buttons.begin(), buttons.size());
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
auto buttonHeight = lf.getAlertWindowButtonHeight();
|
||||
auto buttonWidths = lf.getWidthsForTextButtons (*this, buttonsArray);
|
||||
|
||||
jassert (buttonWidths.size() == buttons.size());
|
||||
int i = 0;
|
||||
|
||||
for (auto* button : buttons)
|
||||
button->setSize (buttonWidths[i++], buttonHeight);
|
||||
|
||||
addAndMakeVisible (b, 0);
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
int AlertWindow::getNumButtons() const
|
||||
{
|
||||
return buttons.size();
|
||||
}
|
||||
|
||||
void AlertWindow::triggerButtonClick (const String& buttonName)
|
||||
{
|
||||
for (auto* b : buttons)
|
||||
{
|
||||
if (buttonName == b->getName())
|
||||
{
|
||||
b->triggerClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AlertWindow::setEscapeKeyCancels (bool shouldEscapeKeyCancel)
|
||||
{
|
||||
escapeKeyCancels = shouldEscapeKeyCancel;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addTextEditor (const String& name,
|
||||
const String& initialContents,
|
||||
const String& onScreenLabel,
|
||||
const bool isPasswordBox)
|
||||
{
|
||||
auto* ed = new TextEditor (name, isPasswordBox ? getDefaultPasswordChar() : 0);
|
||||
ed->setSelectAllWhenFocused (true);
|
||||
ed->setEscapeAndReturnKeysConsumed (false);
|
||||
textBoxes.add (ed);
|
||||
allComps.add (ed);
|
||||
|
||||
ed->setColour (TextEditor::outlineColourId, findColour (ComboBox::outlineColourId));
|
||||
ed->setFont (getLookAndFeel().getAlertWindowMessageFont());
|
||||
addAndMakeVisible (ed);
|
||||
ed->setText (initialContents);
|
||||
ed->setCaretPosition (initialContents.length());
|
||||
textboxNames.add (onScreenLabel);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
TextEditor* AlertWindow::getTextEditor (const String& nameOfTextEditor) const
|
||||
{
|
||||
for (auto* tb : textBoxes)
|
||||
if (tb->getName() == nameOfTextEditor)
|
||||
return tb;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
String AlertWindow::getTextEditorContents (const String& nameOfTextEditor) const
|
||||
{
|
||||
if (auto* t = getTextEditor (nameOfTextEditor))
|
||||
return t->getText();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addComboBox (const String& name,
|
||||
const StringArray& items,
|
||||
const String& onScreenLabel)
|
||||
{
|
||||
auto* cb = new ComboBox (name);
|
||||
comboBoxes.add (cb);
|
||||
allComps.add (cb);
|
||||
|
||||
cb->addItemList (items, 1);
|
||||
|
||||
addAndMakeVisible (cb);
|
||||
cb->setSelectedItemIndex (0);
|
||||
|
||||
comboBoxNames.add (onScreenLabel);
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
ComboBox* AlertWindow::getComboBoxComponent (const String& nameOfList) const
|
||||
{
|
||||
for (auto* cb : comboBoxes)
|
||||
if (cb->getName() == nameOfList)
|
||||
return cb;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AlertTextComp : public TextEditor
|
||||
{
|
||||
public:
|
||||
AlertTextComp (AlertWindow& owner, const String& message, const Font& font)
|
||||
{
|
||||
if (owner.isColourSpecified (AlertWindow::textColourId))
|
||||
setColour (TextEditor::textColourId, owner.findColour (AlertWindow::textColourId));
|
||||
|
||||
setColour (TextEditor::backgroundColourId, Colours::transparentBlack);
|
||||
setColour (TextEditor::outlineColourId, Colours::transparentBlack);
|
||||
setColour (TextEditor::shadowColourId, Colours::transparentBlack);
|
||||
|
||||
setReadOnly (true);
|
||||
setMultiLine (true, true);
|
||||
setCaretVisible (false);
|
||||
setScrollbarsShown (true);
|
||||
lookAndFeelChanged();
|
||||
setWantsKeyboardFocus (false);
|
||||
setFont (font);
|
||||
setText (message, false);
|
||||
|
||||
bestWidth = 2 * (int) std::sqrt (font.getHeight() * (float) font.getStringWidth (message));
|
||||
}
|
||||
|
||||
void updateLayout (const int width)
|
||||
{
|
||||
AttributedString s;
|
||||
s.setJustification (Justification::topLeft);
|
||||
s.append (getText(), getFont());
|
||||
|
||||
TextLayout text;
|
||||
text.createLayoutWithBalancedLineLengths (s, (float) width - 8.0f);
|
||||
setSize (width, jmin (width, (int) (text.getHeight() + getFont().getHeight())));
|
||||
}
|
||||
|
||||
int bestWidth;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AlertTextComp)
|
||||
};
|
||||
|
||||
void AlertWindow::addTextBlock (const String& textBlock)
|
||||
{
|
||||
auto* c = new AlertTextComp (*this, textBlock, getLookAndFeel().getAlertWindowMessageFont());
|
||||
textBlocks.add (c);
|
||||
allComps.add (c);
|
||||
addAndMakeVisible (c);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addProgressBarComponent (double& progressValue)
|
||||
{
|
||||
auto* pb = new ProgressBar (progressValue);
|
||||
progressBars.add (pb);
|
||||
allComps.add (pb);
|
||||
addAndMakeVisible (pb);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addCustomComponent (Component* const component)
|
||||
{
|
||||
customComps.add (component);
|
||||
allComps.add (component);
|
||||
addAndMakeVisible (component);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
int AlertWindow::getNumCustomComponents() const { return customComps.size(); }
|
||||
Component* AlertWindow::getCustomComponent (int index) const { return customComps [index]; }
|
||||
|
||||
Component* AlertWindow::removeCustomComponent (const int index)
|
||||
{
|
||||
auto* c = getCustomComponent (index);
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
customComps.removeFirstMatchingValue (c);
|
||||
allComps.removeFirstMatchingValue (c);
|
||||
removeChildComponent (c);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::paint (Graphics& g)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
lf.drawAlertBox (g, *this, textArea, textLayout);
|
||||
|
||||
g.setColour (findColour (textColourId));
|
||||
g.setFont (lf.getAlertWindowFont());
|
||||
|
||||
for (int i = textBoxes.size(); --i >= 0;)
|
||||
{
|
||||
auto* te = textBoxes.getUnchecked(i);
|
||||
|
||||
g.drawFittedText (textboxNames[i],
|
||||
te->getX(), te->getY() - 14,
|
||||
te->getWidth(), 14,
|
||||
Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
for (int i = comboBoxNames.size(); --i >= 0;)
|
||||
{
|
||||
auto* cb = comboBoxes.getUnchecked(i);
|
||||
|
||||
g.drawFittedText (comboBoxNames[i],
|
||||
cb->getX(), cb->getY() - 14,
|
||||
cb->getWidth(), 14,
|
||||
Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
for (auto* c : customComps)
|
||||
g.drawFittedText (c->getName(),
|
||||
c->getX(), c->getY() - 14,
|
||||
c->getWidth(), 14,
|
||||
Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
void AlertWindow::updateLayout (const bool onlyIncreaseSize)
|
||||
{
|
||||
const int titleH = 24;
|
||||
const int iconWidth = 80;
|
||||
|
||||
auto& lf = getLookAndFeel();
|
||||
auto messageFont (lf.getAlertWindowMessageFont());
|
||||
|
||||
auto wid = jmax (messageFont.getStringWidth (text),
|
||||
messageFont.getStringWidth (getName()));
|
||||
|
||||
auto sw = (int) std::sqrt (messageFont.getHeight() * (float) wid);
|
||||
auto w = jmin (300 + sw * 2, (int) ((float) getParentWidth() * 0.7f));
|
||||
const int edgeGap = 10;
|
||||
const int labelHeight = 18;
|
||||
int iconSpace = 0;
|
||||
|
||||
AttributedString attributedText;
|
||||
attributedText.append (getName(), lf.getAlertWindowTitleFont());
|
||||
|
||||
if (text.isNotEmpty())
|
||||
attributedText.append ("\n\n" + text, messageFont);
|
||||
|
||||
attributedText.setColour (findColour (textColourId));
|
||||
|
||||
if (alertIconType == NoIcon)
|
||||
{
|
||||
attributedText.setJustification (Justification::centredTop);
|
||||
textLayout.createLayoutWithBalancedLineLengths (attributedText, (float) w);
|
||||
}
|
||||
else
|
||||
{
|
||||
attributedText.setJustification (Justification::topLeft);
|
||||
textLayout.createLayoutWithBalancedLineLengths (attributedText, (float) w);
|
||||
iconSpace = iconWidth;
|
||||
}
|
||||
|
||||
w = jmax (350, (int) textLayout.getWidth() + iconSpace + edgeGap * 4);
|
||||
w = jmin (w, (int) ((float) getParentWidth() * 0.7f));
|
||||
|
||||
auto textLayoutH = (int) textLayout.getHeight();
|
||||
auto textBottom = 16 + titleH + textLayoutH;
|
||||
int h = textBottom;
|
||||
|
||||
int buttonW = 40;
|
||||
|
||||
for (auto* b : buttons)
|
||||
buttonW += 16 + b->getWidth();
|
||||
|
||||
w = jmax (buttonW, w);
|
||||
|
||||
h += (textBoxes.size() + comboBoxes.size() + progressBars.size()) * 50;
|
||||
|
||||
if (auto* b = buttons[0])
|
||||
h += 20 + b->getHeight();
|
||||
|
||||
for (auto* c : customComps)
|
||||
{
|
||||
w = jmax (w, (c->getWidth() * 100) / 80);
|
||||
h += 10 + c->getHeight();
|
||||
|
||||
if (c->getName().isNotEmpty())
|
||||
h += labelHeight;
|
||||
}
|
||||
|
||||
for (auto* tb : textBlocks)
|
||||
w = jmax (w, static_cast<const AlertTextComp*> (tb)->bestWidth);
|
||||
|
||||
w = jmin (w, (int) ((float) getParentWidth() * 0.7f));
|
||||
|
||||
for (auto* tb : textBlocks)
|
||||
{
|
||||
auto* ac = static_cast<AlertTextComp*> (tb);
|
||||
ac->updateLayout ((int) ((float) w * 0.8f));
|
||||
h += ac->getHeight() + 10;
|
||||
}
|
||||
|
||||
h = jmin (getParentHeight() - 50, h);
|
||||
|
||||
if (onlyIncreaseSize)
|
||||
{
|
||||
w = jmax (w, getWidth());
|
||||
h = jmax (h, getHeight());
|
||||
}
|
||||
|
||||
if (! isVisible())
|
||||
centreAroundComponent (associatedComponent, w, h);
|
||||
else
|
||||
setBounds (getBounds().withSizeKeepingCentre (w, h));
|
||||
|
||||
textArea.setBounds (edgeGap, edgeGap, w - (edgeGap * 2), h - edgeGap);
|
||||
accessibleMessageLabel.setBounds (textArea);
|
||||
|
||||
const int spacer = 16;
|
||||
int totalWidth = -spacer;
|
||||
|
||||
for (auto* b : buttons)
|
||||
totalWidth += b->getWidth() + spacer;
|
||||
|
||||
auto x = (w - totalWidth) / 2;
|
||||
auto y = (int) ((float) getHeight() * 0.95f);
|
||||
|
||||
for (auto* c : buttons)
|
||||
{
|
||||
int ny = proportionOfHeight (0.95f) - c->getHeight();
|
||||
c->setTopLeftPosition (x, ny);
|
||||
if (ny < y)
|
||||
y = ny;
|
||||
|
||||
x += c->getWidth() + spacer;
|
||||
|
||||
c->toFront (false);
|
||||
}
|
||||
|
||||
y = textBottom;
|
||||
|
||||
for (auto* c : allComps)
|
||||
{
|
||||
h = 22;
|
||||
|
||||
const int comboIndex = comboBoxes.indexOf (dynamic_cast<ComboBox*> (c));
|
||||
if (comboIndex >= 0 && comboBoxNames [comboIndex].isNotEmpty())
|
||||
y += labelHeight;
|
||||
|
||||
const int tbIndex = textBoxes.indexOf (dynamic_cast<TextEditor*> (c));
|
||||
if (tbIndex >= 0 && textboxNames[tbIndex].isNotEmpty())
|
||||
y += labelHeight;
|
||||
|
||||
if (customComps.contains (c))
|
||||
{
|
||||
if (c->getName().isNotEmpty())
|
||||
y += labelHeight;
|
||||
|
||||
c->setTopLeftPosition (proportionOfWidth (0.1f), y);
|
||||
h = c->getHeight();
|
||||
}
|
||||
else if (textBlocks.contains (c))
|
||||
{
|
||||
c->setTopLeftPosition ((getWidth() - c->getWidth()) / 2, y);
|
||||
h = c->getHeight();
|
||||
}
|
||||
else
|
||||
{
|
||||
c->setBounds (proportionOfWidth (0.1f), y, proportionOfWidth (0.8f), h);
|
||||
}
|
||||
|
||||
y += h + 10;
|
||||
}
|
||||
|
||||
setWantsKeyboardFocus (getNumChildComponents() == 0);
|
||||
}
|
||||
|
||||
bool AlertWindow::containsAnyExtraComponents() const
|
||||
{
|
||||
return allComps.size() > 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::mouseDown (const MouseEvent& e)
|
||||
{
|
||||
dragger.startDraggingComponent (this, e);
|
||||
}
|
||||
|
||||
void AlertWindow::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
dragger.dragComponent (this, e, &constrainer);
|
||||
}
|
||||
|
||||
bool AlertWindow::keyPressed (const KeyPress& key)
|
||||
{
|
||||
for (auto* b : buttons)
|
||||
{
|
||||
if (b->isRegisteredForShortcut (key))
|
||||
{
|
||||
b->triggerClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (key.isKeyCode (KeyPress::escapeKey) && escapeKeyCancels)
|
||||
{
|
||||
exitModalState (0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key.isKeyCode (KeyPress::returnKey) && buttons.size() == 1)
|
||||
{
|
||||
buttons.getUnchecked(0)->triggerClick();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AlertWindow::lookAndFeelChanged()
|
||||
{
|
||||
const int newFlags = getLookAndFeel().getAlertBoxWindowFlags();
|
||||
|
||||
setUsingNativeTitleBar ((newFlags & ComponentPeer::windowHasTitleBar) != 0);
|
||||
setDropShadowEnabled (isOpaque() && (newFlags & ComponentPeer::windowHasDropShadow) != 0);
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
int AlertWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
return getLookAndFeel().getAlertBoxWindowFlags();
|
||||
}
|
||||
|
||||
enum class Async { no, yes };
|
||||
|
||||
//==============================================================================
|
||||
class AlertWindowInfo
|
||||
{
|
||||
public:
|
||||
AlertWindowInfo (const MessageBoxOptions& opts,
|
||||
std::unique_ptr<ModalComponentManager::Callback>&& cb,
|
||||
Async showAsync)
|
||||
: options (opts),
|
||||
callback (std::move (cb)),
|
||||
async (showAsync)
|
||||
{
|
||||
}
|
||||
|
||||
int invoke() const
|
||||
{
|
||||
MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private:
|
||||
static void* showCallback (void* userData)
|
||||
{
|
||||
static_cast<AlertWindowInfo*> (userData)->show();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void show()
|
||||
{
|
||||
auto* component = options.getAssociatedComponent();
|
||||
|
||||
auto& lf = (component != nullptr ? component->getLookAndFeel()
|
||||
: LookAndFeel::getDefaultLookAndFeel());
|
||||
|
||||
std::unique_ptr<AlertWindow> alertBox (lf.createAlertWindow (options.getTitle(), options.getMessage(),
|
||||
options.getButtonText (0), options.getButtonText (1), options.getButtonText (2),
|
||||
options.getIconType(), options.getNumButtons(), component));
|
||||
|
||||
jassert (alertBox != nullptr); // you have to return one of these!
|
||||
|
||||
alertBox->setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
if (async == Async::no)
|
||||
returnValue = alertBox->runModalLoop();
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ignoreUnused (async);
|
||||
|
||||
alertBox->enterModalState (true, callback.release(), true);
|
||||
alertBox.release();
|
||||
}
|
||||
}
|
||||
|
||||
MessageBoxOptions options;
|
||||
std::unique_ptr<ModalComponentManager::Callback> callback;
|
||||
const Async async;
|
||||
int returnValue = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindowInfo)
|
||||
};
|
||||
|
||||
namespace AlertWindowMappings
|
||||
{
|
||||
using MapFn = int (*) (int);
|
||||
|
||||
static inline int noMapping (int buttonIndex) { return buttonIndex; }
|
||||
static inline int messageBox (int) { return 0; }
|
||||
static inline int okCancel (int buttonIndex) { return buttonIndex == 0 ? 1 : 0; }
|
||||
static inline int yesNoCancel (int buttonIndex) { return buttonIndex == 2 ? 0 : buttonIndex + 1; }
|
||||
|
||||
static std::unique_ptr<ModalComponentManager::Callback> getWrappedCallback (ModalComponentManager::Callback* callbackIn,
|
||||
MapFn mapFn)
|
||||
{
|
||||
jassert (mapFn != nullptr);
|
||||
|
||||
if (callbackIn == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto wrappedCallback = [innerCallback = rawToUniquePtr (callbackIn), mapFn] (int buttonIndex)
|
||||
{
|
||||
innerCallback->modalStateFinished (mapFn (buttonIndex));
|
||||
};
|
||||
|
||||
return rawToUniquePtr (ModalCallbackFunction::create (std::move (wrappedCallback)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
void AlertWindow::showMessageBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& buttonText,
|
||||
Component* associatedComponent)
|
||||
{
|
||||
show (MessageBoxOptions()
|
||||
.withIconType (iconType)
|
||||
.withTitle (title)
|
||||
.withMessage (message)
|
||||
.withButton (buttonText.isEmpty() ? TRANS("OK") : buttonText)
|
||||
.withAssociatedComponent (associatedComponent));
|
||||
}
|
||||
|
||||
int AlertWindow::show (const MessageBoxOptions& options)
|
||||
{
|
||||
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
|
||||
return NativeMessageBox::show (options);
|
||||
|
||||
AlertWindowInfo info (options, nullptr, Async::no);
|
||||
return info.invoke();
|
||||
}
|
||||
|
||||
bool AlertWindow::showNativeDialogBox (const String& title,
|
||||
const String& bodyText,
|
||||
bool isOkCancel)
|
||||
{
|
||||
if (isOkCancel)
|
||||
return NativeMessageBox::showOkCancelBox (AlertWindow::NoIcon, title, bodyText);
|
||||
|
||||
NativeMessageBox::showMessageBox (AlertWindow::NoIcon, title, bodyText);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void AlertWindow::showAsync (const MessageBoxOptions& options, ModalComponentManager::Callback* callback)
|
||||
{
|
||||
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
|
||||
{
|
||||
NativeMessageBox::showAsync (options, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlertWindowInfo info (options, rawToUniquePtr (callback), Async::yes);
|
||||
info.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
void AlertWindow::showAsync (const MessageBoxOptions& options, std::function<void (int)> callback)
|
||||
{
|
||||
showAsync (options, ModalCallbackFunction::create (callback));
|
||||
}
|
||||
|
||||
void AlertWindow::showMessageBoxAsync (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& buttonText,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
showAsync (MessageBoxOptions()
|
||||
.withIconType (iconType)
|
||||
.withTitle (title)
|
||||
.withMessage (message)
|
||||
.withButton (buttonText.isEmpty() ? TRANS("OK") : buttonText)
|
||||
.withAssociatedComponent (associatedComponent),
|
||||
callback);
|
||||
}
|
||||
|
||||
static int showMaybeAsync (const MessageBoxOptions& options,
|
||||
ModalComponentManager::Callback* callbackIn,
|
||||
AlertWindowMappings::MapFn mapFn)
|
||||
{
|
||||
const auto showAsync = (callbackIn != nullptr ? Async::yes
|
||||
: Async::no);
|
||||
|
||||
auto callback = AlertWindowMappings::getWrappedCallback (callbackIn, mapFn);
|
||||
|
||||
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
if (showAsync == Async::no)
|
||||
return mapFn (NativeMessageBox::show (options));
|
||||
#endif
|
||||
|
||||
NativeMessageBox::showAsync (options, callback.release());
|
||||
return false;
|
||||
}
|
||||
|
||||
AlertWindowInfo info (options, std::move (callback), showAsync);
|
||||
return info.invoke();
|
||||
}
|
||||
|
||||
bool AlertWindow::showOkCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& button1Text,
|
||||
const String& button2Text,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
return showMaybeAsync (MessageBoxOptions()
|
||||
.withIconType (iconType)
|
||||
.withTitle (title)
|
||||
.withMessage (message)
|
||||
.withButton (button1Text.isEmpty() ? TRANS("OK") : button1Text)
|
||||
.withButton (button2Text.isEmpty() ? TRANS("Cancel") : button2Text)
|
||||
.withAssociatedComponent (associatedComponent),
|
||||
callback,
|
||||
LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()
|
||||
? AlertWindowMappings::okCancel
|
||||
: AlertWindowMappings::noMapping) == 1;
|
||||
}
|
||||
|
||||
int AlertWindow::showYesNoCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& button1Text,
|
||||
const String& button2Text,
|
||||
const String& button3Text,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
return showMaybeAsync (MessageBoxOptions()
|
||||
.withIconType (iconType)
|
||||
.withTitle (title)
|
||||
.withMessage (message)
|
||||
.withButton (button1Text.isEmpty() ? TRANS("Yes") : button1Text)
|
||||
.withButton (button2Text.isEmpty() ? TRANS("No") : button2Text)
|
||||
.withButton (button3Text.isEmpty() ? TRANS("Cancel") : button3Text)
|
||||
.withAssociatedComponent (associatedComponent),
|
||||
callback,
|
||||
LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows()
|
||||
? AlertWindowMappings::yesNoCancel
|
||||
: AlertWindowMappings::noMapping);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
std::unique_ptr<AccessibilityHandler> AlertWindow::createAccessibilityHandler()
|
||||
{
|
||||
return std::make_unique<AccessibilityHandler> (*this, AccessibilityRole::dialogWindow);
|
||||
}
|
||||
|
||||
} // namespace juce
|
531
deps/juce/modules/juce_gui_basics/windows/juce_AlertWindow.h
vendored
Normal file
531
deps/juce/modules/juce_gui_basics/windows/juce_AlertWindow.h
vendored
Normal file
@ -0,0 +1,531 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 message and has buttons for the user to react to it.
|
||||
|
||||
For simple dialog boxes with just a couple of buttons on them, there are
|
||||
some static methods for running these.
|
||||
|
||||
For more complex dialogs, an AlertWindow can be created, then it can have some
|
||||
buttons and components added to it, and its runModalLoop() method is then used to
|
||||
show it. The value returned by runModalLoop() shows which button the
|
||||
user pressed to dismiss the box.
|
||||
|
||||
@see ThreadWithProgressWindow
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API AlertWindow : public TopLevelWindow
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an AlertWindow.
|
||||
|
||||
@param title the headline to show at the top of the dialog box
|
||||
@param message a longer, more descriptive message to show underneath the
|
||||
headline
|
||||
@param iconType the type of icon to display
|
||||
@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.
|
||||
*/
|
||||
AlertWindow (const String& title,
|
||||
const String& message,
|
||||
MessageBoxIconType iconType,
|
||||
Component* associatedComponent = nullptr);
|
||||
|
||||
/** Destroys the AlertWindow */
|
||||
~AlertWindow() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the type of alert icon that was specified when the window
|
||||
was created. */
|
||||
MessageBoxIconType getAlertType() const noexcept { return alertIconType; }
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the dialog box's message.
|
||||
|
||||
This will also resize the window to fit the new message if required.
|
||||
*/
|
||||
void setMessage (const String& message);
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a button to the window.
|
||||
|
||||
@param name the text to show on the button
|
||||
@param returnValue the value that should be returned from runModalLoop()
|
||||
if this is the button that the user presses.
|
||||
@param shortcutKey1 an optional key that can be pressed to trigger this button
|
||||
@param shortcutKey2 a second optional key that can be pressed to trigger this button
|
||||
*/
|
||||
void addButton (const String& name,
|
||||
int returnValue,
|
||||
const KeyPress& shortcutKey1 = KeyPress(),
|
||||
const KeyPress& shortcutKey2 = KeyPress());
|
||||
|
||||
/** Returns the number of buttons that the window currently has. */
|
||||
int getNumButtons() const;
|
||||
|
||||
/** Invokes a click of one of the buttons. */
|
||||
void triggerButtonClick (const String& buttonName);
|
||||
|
||||
/** If set to true and the window contains no buttons, then pressing the escape key will make
|
||||
the alert cancel its modal state.
|
||||
By default this setting is true - turn it off if you don't want the box to respond to
|
||||
the escape key. Note that it is ignored if you have any buttons, and in that case you
|
||||
should give the buttons appropriate keypresses to trigger cancelling if you want to.
|
||||
*/
|
||||
void setEscapeKeyCancels (bool shouldEscapeKeyCancel);
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a textbox to the window for entering strings.
|
||||
|
||||
@param name an internal name for the text-box. This is the name to pass to
|
||||
the getTextEditorContents() method to find out what the
|
||||
user typed-in.
|
||||
@param initialContents a string to show in the text box when it's first shown
|
||||
@param onScreenLabel if this is non-empty, it will be displayed next to the
|
||||
text-box to label it.
|
||||
@param isPasswordBox if true, the text editor will display asterisks instead of
|
||||
the actual text
|
||||
@see getTextEditorContents
|
||||
*/
|
||||
void addTextEditor (const String& name,
|
||||
const String& initialContents,
|
||||
const String& onScreenLabel = String(),
|
||||
bool isPasswordBox = false);
|
||||
|
||||
/** Returns the contents of a named textbox.
|
||||
|
||||
After showing an AlertWindow that contains a text editor, this can be
|
||||
used to find out what the user has typed into it.
|
||||
|
||||
@param nameOfTextEditor the name of the text box that you're interested in
|
||||
@see addTextEditor
|
||||
*/
|
||||
String getTextEditorContents (const String& nameOfTextEditor) const;
|
||||
|
||||
/** Returns a pointer to a textbox that was added with addTextEditor(). */
|
||||
TextEditor* getTextEditor (const String& nameOfTextEditor) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a drop-down list of choices to the box.
|
||||
|
||||
After the box has been shown, the getComboBoxComponent() method can
|
||||
be used to find out which item the user picked.
|
||||
|
||||
@param name the label to use for the drop-down list
|
||||
@param items the list of items to show in it
|
||||
@param onScreenLabel if this is non-empty, it will be displayed next to the
|
||||
combo-box to label it.
|
||||
@see getComboBoxComponent
|
||||
*/
|
||||
void addComboBox (const String& name,
|
||||
const StringArray& items,
|
||||
const String& onScreenLabel = String());
|
||||
|
||||
/** Returns a drop-down list that was added to the AlertWindow.
|
||||
|
||||
@param nameOfList the name that was passed into the addComboBox() method
|
||||
when creating the drop-down
|
||||
@returns the ComboBox component, or nullptr if none was found for the given name.
|
||||
*/
|
||||
ComboBox* getComboBoxComponent (const String& nameOfList) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a block of text.
|
||||
|
||||
This is handy for adding a multi-line note next to a textbox or combo-box,
|
||||
to provide more details about what's going on.
|
||||
*/
|
||||
void addTextBlock (const String& text);
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a progress-bar to the window.
|
||||
|
||||
@param progressValue a variable that will be repeatedly checked while the
|
||||
dialog box is visible, to see how far the process has
|
||||
got. The value should be in the range 0 to 1.0
|
||||
*/
|
||||
void addProgressBarComponent (double& progressValue);
|
||||
|
||||
//==============================================================================
|
||||
/** Adds a user-defined component to the dialog box.
|
||||
|
||||
@param component the component to add - its size should be set up correctly
|
||||
before it is passed in. The caller is responsible for deleting
|
||||
the component later on - the AlertWindow won't delete it.
|
||||
*/
|
||||
void addCustomComponent (Component* component);
|
||||
|
||||
/** Returns the number of custom components in the dialog box.
|
||||
@see getCustomComponent, addCustomComponent
|
||||
*/
|
||||
int getNumCustomComponents() const;
|
||||
|
||||
/** Returns one of the custom components in the dialog box.
|
||||
|
||||
@param index a value 0 to (getNumCustomComponents() - 1).
|
||||
Out-of-range indexes will return nullptr
|
||||
@see getNumCustomComponents, addCustomComponent
|
||||
*/
|
||||
Component* getCustomComponent (int index) const;
|
||||
|
||||
/** Removes one of the custom components in the dialog box.
|
||||
Note that this won't delete it, it just removes the component from the window
|
||||
|
||||
@param index a value 0 to (getNumCustomComponents() - 1).
|
||||
Out-of-range indexes will return nullptr
|
||||
@returns the component that was removed (or null)
|
||||
@see getNumCustomComponents, addCustomComponent
|
||||
*/
|
||||
Component* removeCustomComponent (int index);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns true if the window contains any components other than just buttons.*/
|
||||
bool containsAnyExtraComponents() const;
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
/** Shows a dialog box that just has a message and a single button to get rid of it.
|
||||
|
||||
The box is shown modally, and the method will block until the user has clicked the
|
||||
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
|
||||
headline
|
||||
@param buttonText the text to show in the button - if this string is empty, the
|
||||
default string "OK" (or a localised version) will be used.
|
||||
@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,
|
||||
const String& buttonText = String(),
|
||||
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 button to get rid of it.
|
||||
|
||||
The box will be displayed and placed into a modal state, but this method will
|
||||
return immediately, and if a callback was supplied, it 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
|
||||
headline
|
||||
@param buttonText the text to show in the button - if this string is empty, the
|
||||
default string "OK" (or a localised version) will be used.
|
||||
@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.
|
||||
*/
|
||||
static void JUCE_CALLTYPE showMessageBoxAsync (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& buttonText = String(),
|
||||
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, 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
|
||||
headline
|
||||
@param button1Text the text to show in the first button - if this string is
|
||||
empty, the default string "OK" (or a localised version of it)
|
||||
will be used.
|
||||
@param button2Text the text to show in the second button - if this string is
|
||||
empty, the default string "cancel" (or a localised version of it)
|
||||
will be used.
|
||||
@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 menu 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.
|
||||
@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.
|
||||
*/
|
||||
static bool JUCE_CALLTYPE showOkCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
const String& button1Text = String(),
|
||||
const String& button2Text = String(),
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
const String& button1Text,
|
||||
const String& button2Text,
|
||||
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, 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
|
||||
headline
|
||||
@param button1Text the text to show in the first button - if an empty string, then
|
||||
"yes" will be used (or a localised version of it)
|
||||
@param button2Text the text to show in the first button - if an empty string, then
|
||||
"no" will be used (or a localised version of it)
|
||||
@param button3Text the text to show in the first button - if an empty string, then
|
||||
"cancel" will be used (or a localised version of it)
|
||||
@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 menu 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.
|
||||
|
||||
@returns If the callback parameter has been set, this returns 0. Otherwise, it
|
||||
returns one of the following values:
|
||||
- 0 if the third button was pressed (normally used for 'cancel')
|
||||
- 1 if the first button was pressed (normally used for 'yes')
|
||||
- 2 if the middle button was pressed (normally used for 'no')
|
||||
*/
|
||||
static int JUCE_CALLTYPE showYesNoCancelBox (MessageBoxIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
const String& button1Text = String(),
|
||||
const String& button2Text = String(),
|
||||
const String& button3Text = String(),
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
const String& button1Text,
|
||||
const String& button2Text,
|
||||
const String& button3Text,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED && ! defined (DOXYGEN)
|
||||
/** Shows an operating-system native dialog box.
|
||||
|
||||
@param title the title to use at the top
|
||||
@param bodyText the longer message to show
|
||||
@param isOkCancel if true, this will show an ok/cancel box, if false,
|
||||
it'll show a box with just an ok button
|
||||
@returns true if the ok button was pressed, false if they pressed cancel.
|
||||
*/
|
||||
[[deprecated ("Use the NativeMessageBox methods instead for more options")]]
|
||||
static bool JUCE_CALLTYPE showNativeDialogBox (const String& title,
|
||||
const String& bodyText,
|
||||
bool isOkCancel);
|
||||
#endif
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of colour IDs to use to change the colour of various aspects of the alert box.
|
||||
|
||||
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 = 0x1001800, /**< The background colour for the window. */
|
||||
textColourId = 0x1001810, /**< The colour for the text. */
|
||||
outlineColourId = 0x1001820 /**< An optional colour to use to draw a border around the window. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
alert-window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() = default;
|
||||
|
||||
virtual AlertWindow* createAlertWindow (const String& title, const String& message,
|
||||
const String& button1,
|
||||
const String& button2,
|
||||
const String& button3,
|
||||
MessageBoxIconType iconType,
|
||||
int numButtons,
|
||||
Component* associatedComponent) = 0;
|
||||
|
||||
virtual void drawAlertBox (Graphics&, AlertWindow&, const Rectangle<int>& textArea, TextLayout&) = 0;
|
||||
|
||||
virtual int getAlertBoxWindowFlags() = 0;
|
||||
|
||||
virtual Array<int> getWidthsForTextButtons (AlertWindow&, const Array<TextButton*>&) = 0;
|
||||
virtual int getAlertWindowButtonHeight() = 0;
|
||||
|
||||
virtual Font getAlertWindowTitleFont() = 0;
|
||||
virtual Font getAlertWindowMessageFont() = 0;
|
||||
virtual Font getAlertWindowFont() = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
using AlertIconType = MessageBoxIconType;
|
||||
|
||||
static constexpr auto NoIcon = MessageBoxIconType::NoIcon;
|
||||
static constexpr auto QuestionIcon = MessageBoxIconType::QuestionIcon;
|
||||
static constexpr auto WarningIcon = MessageBoxIconType::WarningIcon;
|
||||
static constexpr auto InfoIcon = MessageBoxIconType::InfoIcon;
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void paint (Graphics&) override;
|
||||
/** @internal */
|
||||
void mouseDown (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
void mouseDrag (const MouseEvent&) override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
void userTriedToCloseWindow() override;
|
||||
/** @internal */
|
||||
int getDesktopWindowStyleFlags() const override;
|
||||
/** @internal */
|
||||
float getDesktopScaleFactor() const override { return desktopScale * Desktop::getInstance().getGlobalScaleFactor(); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String text;
|
||||
TextLayout textLayout;
|
||||
Label accessibleMessageLabel;
|
||||
MessageBoxIconType alertIconType;
|
||||
ComponentBoundsConstrainer constrainer;
|
||||
ComponentDragger dragger;
|
||||
Rectangle<int> textArea;
|
||||
OwnedArray<TextButton> buttons;
|
||||
OwnedArray<TextEditor> textBoxes;
|
||||
OwnedArray<ComboBox> comboBoxes;
|
||||
OwnedArray<ProgressBar> progressBars;
|
||||
Array<Component*> customComps;
|
||||
OwnedArray<Component> textBlocks;
|
||||
Array<Component*> allComps;
|
||||
StringArray textboxNames, comboBoxNames;
|
||||
Component* const associatedComponent;
|
||||
bool escapeKeyCancels = true;
|
||||
float desktopScale = 1.0f;
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override;
|
||||
void exitAlert (Button* button);
|
||||
void updateLayout (bool onlyIncreaseSize);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindow)
|
||||
};
|
||||
|
||||
} // namespace juce
|
274
deps/juce/modules/juce_gui_basics/windows/juce_CallOutBox.cpp
vendored
Normal file
274
deps/juce/modules/juce_gui_basics/windows/juce_CallOutBox.cpp
vendored
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
190
deps/juce/modules/juce_gui_basics/windows/juce_CallOutBox.h
vendored
Normal file
190
deps/juce/modules/juce_gui_basics/windows/juce_CallOutBox.h
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
589
deps/juce/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp
vendored
Normal file
589
deps/juce/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp
vendored
Normal file
@ -0,0 +1,589 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
static uint32 lastUniquePeerID = 1;
|
||||
|
||||
//==============================================================================
|
||||
ComponentPeer::ComponentPeer (Component& comp, int flags)
|
||||
: component (comp),
|
||||
styleFlags (flags),
|
||||
uniqueID (lastUniquePeerID += 2) // increment by 2 so that this can never hit 0
|
||||
{
|
||||
Desktop::getInstance().peers.add (this);
|
||||
}
|
||||
|
||||
ComponentPeer::~ComponentPeer()
|
||||
{
|
||||
auto& desktop = Desktop::getInstance();
|
||||
desktop.peers.removeFirstMatchingValue (this);
|
||||
desktop.triggerFocusCallback();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int ComponentPeer::getNumPeers() noexcept
|
||||
{
|
||||
return Desktop::getInstance().peers.size();
|
||||
}
|
||||
|
||||
ComponentPeer* ComponentPeer::getPeer (const int index) noexcept
|
||||
{
|
||||
return Desktop::getInstance().peers [index];
|
||||
}
|
||||
|
||||
ComponentPeer* ComponentPeer::getPeerFor (const Component* const component) noexcept
|
||||
{
|
||||
for (auto* peer : Desktop::getInstance().peers)
|
||||
if (&(peer->getComponent()) == component)
|
||||
return peer;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ComponentPeer::isValidPeer (const ComponentPeer* const peer) noexcept
|
||||
{
|
||||
return Desktop::getInstance().peers.contains (const_cast<ComponentPeer*> (peer));
|
||||
}
|
||||
|
||||
void ComponentPeer::updateBounds()
|
||||
{
|
||||
setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, component.getBoundsInParent()), false);
|
||||
}
|
||||
|
||||
bool ComponentPeer::isKioskMode() const
|
||||
{
|
||||
return Desktop::getInstance().getKioskModeComponent() == &component;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::handleMouseEvent (MouseInputSource::InputSourceType type, Point<float> pos, ModifierKeys newMods,
|
||||
float newPressure, float newOrientation, int64 time, PenDetails pen, int touchIndex)
|
||||
{
|
||||
if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
|
||||
MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods, newPressure, newOrientation, pen);
|
||||
}
|
||||
|
||||
void ComponentPeer::handleMouseWheel (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, const MouseWheelDetails& wheel, int touchIndex)
|
||||
{
|
||||
if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
|
||||
MouseInputSource (*mouse).handleWheel (*this, pos, time, wheel);
|
||||
}
|
||||
|
||||
void ComponentPeer::handleMagnifyGesture (MouseInputSource::InputSourceType type, Point<float> pos, int64 time, float scaleFactor, int touchIndex)
|
||||
{
|
||||
if (auto* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (type, touchIndex))
|
||||
MouseInputSource (*mouse).handleMagnifyGesture (*this, pos, time, scaleFactor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo)
|
||||
{
|
||||
Graphics g (contextToPaintTo);
|
||||
|
||||
if (component.isTransformed())
|
||||
g.addTransform (component.getTransform());
|
||||
|
||||
auto peerBounds = getBounds();
|
||||
auto componentBounds = component.getLocalBounds();
|
||||
|
||||
if (component.isTransformed())
|
||||
componentBounds = componentBounds.transformedBy (component.getTransform());
|
||||
|
||||
if (peerBounds.getWidth() != componentBounds.getWidth() || peerBounds.getHeight() != componentBounds.getHeight())
|
||||
// Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
|
||||
g.addTransform (AffineTransform::scale ((float) peerBounds.getWidth() / (float) componentBounds.getWidth(),
|
||||
(float) peerBounds.getHeight() / (float) componentBounds.getHeight()));
|
||||
|
||||
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
#ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
|
||||
if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
|
||||
#endif
|
||||
{
|
||||
g.saveState();
|
||||
}
|
||||
#endif
|
||||
|
||||
JUCE_TRY
|
||||
{
|
||||
component.paintEntireComponent (g, true);
|
||||
}
|
||||
JUCE_CATCH_EXCEPTION
|
||||
|
||||
#if JUCE_ENABLE_REPAINT_DEBUGGING
|
||||
#ifdef JUCE_IS_REPAINT_DEBUGGING_ACTIVE
|
||||
if (JUCE_IS_REPAINT_DEBUGGING_ACTIVE)
|
||||
#endif
|
||||
{
|
||||
// enabling this code will fill all areas that get repainted with a colour overlay, to show
|
||||
// clearly when things are being repainted.
|
||||
g.restoreState();
|
||||
|
||||
static Random rng;
|
||||
|
||||
g.fillAll (Colour ((uint8) rng.nextInt (255),
|
||||
(uint8) rng.nextInt (255),
|
||||
(uint8) rng.nextInt (255),
|
||||
(uint8) 0x50));
|
||||
}
|
||||
#endif
|
||||
|
||||
/** If this fails, it's probably be because your CPU floating-point precision mode has
|
||||
been set to low.. This setting is sometimes changed by things like Direct3D, and can
|
||||
mess up a lot of the calculations that the library needs to do.
|
||||
*/
|
||||
jassert (roundToInt (10.1f) == 10);
|
||||
}
|
||||
|
||||
Component* ComponentPeer::getTargetForKeyPress()
|
||||
{
|
||||
auto* c = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (c == nullptr)
|
||||
c = &component;
|
||||
|
||||
if (c->isCurrentlyBlockedByAnotherModalComponent())
|
||||
if (auto* currentModalComp = Component::getCurrentlyModalComponent())
|
||||
c = currentModalComp;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
|
||||
{
|
||||
return handleKeyPress (KeyPress (keyCode,
|
||||
ModifierKeys::currentModifiers.withoutMouseButtons(),
|
||||
textCharacter));
|
||||
}
|
||||
|
||||
|
||||
bool ComponentPeer::handleKeyPress (const KeyPress& keyInfo)
|
||||
{
|
||||
bool keyWasUsed = false;
|
||||
|
||||
for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
|
||||
{
|
||||
const WeakReference<Component> deletionChecker (target);
|
||||
|
||||
if (auto* keyListeners = target->keyListeners.get())
|
||||
{
|
||||
for (int i = keyListeners->size(); --i >= 0;)
|
||||
{
|
||||
keyWasUsed = keyListeners->getUnchecked (i)->keyPressed (keyInfo, target);
|
||||
|
||||
if (keyWasUsed || deletionChecker == nullptr)
|
||||
return keyWasUsed;
|
||||
|
||||
i = jmin (i, keyListeners->size());
|
||||
}
|
||||
}
|
||||
|
||||
keyWasUsed = target->keyPressed (keyInfo);
|
||||
|
||||
if (keyWasUsed || deletionChecker == nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (! keyWasUsed && keyInfo.isKeyCode (KeyPress::tabKey))
|
||||
{
|
||||
if (auto* currentlyFocused = Component::getCurrentlyFocusedComponent())
|
||||
{
|
||||
currentlyFocused->moveKeyboardFocusToSibling (! keyInfo.getModifiers().isShiftDown());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return keyWasUsed;
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
|
||||
{
|
||||
bool keyWasUsed = false;
|
||||
|
||||
for (auto* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
|
||||
{
|
||||
const WeakReference<Component> deletionChecker (target);
|
||||
|
||||
keyWasUsed = target->keyStateChanged (isKeyDown);
|
||||
|
||||
if (keyWasUsed || deletionChecker == nullptr)
|
||||
break;
|
||||
|
||||
if (auto* keyListeners = target->keyListeners.get())
|
||||
{
|
||||
for (int i = keyListeners->size(); --i >= 0;)
|
||||
{
|
||||
keyWasUsed = keyListeners->getUnchecked (i)->keyStateChanged (isKeyDown, target);
|
||||
|
||||
if (keyWasUsed || deletionChecker == nullptr)
|
||||
return keyWasUsed;
|
||||
|
||||
i = jmin (i, keyListeners->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keyWasUsed;
|
||||
}
|
||||
|
||||
void ComponentPeer::handleModifierKeysChange()
|
||||
{
|
||||
auto* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
|
||||
|
||||
if (target == nullptr)
|
||||
target = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (target == nullptr)
|
||||
target = &component;
|
||||
|
||||
target->internalModifierKeysChanged();
|
||||
}
|
||||
|
||||
TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
|
||||
{
|
||||
auto* c = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (c == &component || component.isParentOf (c))
|
||||
if (auto* ti = dynamic_cast<TextInputTarget*> (c))
|
||||
if (ti->isTextInputActive())
|
||||
return ti;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ComponentPeer::dismissPendingTextInput() {}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::handleBroughtToFront()
|
||||
{
|
||||
component.internalBroughtToFront();
|
||||
}
|
||||
|
||||
void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
|
||||
{
|
||||
constrainer = newConstrainer;
|
||||
}
|
||||
|
||||
void ComponentPeer::handleMovedOrResized()
|
||||
{
|
||||
const bool nowMinimised = isMinimised();
|
||||
|
||||
if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
|
||||
{
|
||||
const WeakReference<Component> deletionChecker (&component);
|
||||
|
||||
auto newBounds = Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds());
|
||||
auto oldBounds = component.getBounds();
|
||||
|
||||
const bool wasMoved = (oldBounds.getPosition() != newBounds.getPosition());
|
||||
const bool wasResized = (oldBounds.getWidth() != newBounds.getWidth() || oldBounds.getHeight() != newBounds.getHeight());
|
||||
|
||||
if (wasMoved || wasResized)
|
||||
{
|
||||
component.boundsRelativeToParent = newBounds;
|
||||
|
||||
if (wasResized)
|
||||
component.repaint();
|
||||
|
||||
component.sendMovedResizedMessages (wasMoved, wasResized);
|
||||
|
||||
if (deletionChecker == nullptr)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWindowMinimised != nowMinimised)
|
||||
{
|
||||
isWindowMinimised = nowMinimised;
|
||||
component.minimisationStateChanged (nowMinimised);
|
||||
component.sendVisibilityChangeMessage();
|
||||
}
|
||||
|
||||
const auto windowInSpecialState = isFullScreen() || isKioskMode() || nowMinimised;
|
||||
|
||||
if (! windowInSpecialState)
|
||||
lastNonFullscreenBounds = component.getBounds();
|
||||
}
|
||||
|
||||
void ComponentPeer::handleFocusGain()
|
||||
{
|
||||
if (component.isParentOf (lastFocusedComponent)
|
||||
&& lastFocusedComponent->isShowing()
|
||||
&& lastFocusedComponent->getWantsKeyboardFocus())
|
||||
{
|
||||
Component::currentlyFocusedComponent = lastFocusedComponent;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
lastFocusedComponent->internalKeyboardFocusGain (Component::focusChangedDirectly);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! component.isCurrentlyBlockedByAnotherModalComponent())
|
||||
component.grabKeyboardFocus();
|
||||
else
|
||||
ModalComponentManager::getInstance()->bringModalComponentsToFront();
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentPeer::handleFocusLoss()
|
||||
{
|
||||
if (component.hasKeyboardFocus (true))
|
||||
{
|
||||
lastFocusedComponent = Component::currentlyFocusedComponent;
|
||||
|
||||
if (lastFocusedComponent != nullptr)
|
||||
{
|
||||
Component::currentlyFocusedComponent = nullptr;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
lastFocusedComponent->internalKeyboardFocusLoss (Component::focusChangedByMouseClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component* ComponentPeer::getLastFocusedSubcomponent() const noexcept
|
||||
{
|
||||
return (component.isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing())
|
||||
? static_cast<Component*> (lastFocusedComponent)
|
||||
: &component;
|
||||
}
|
||||
|
||||
void ComponentPeer::handleScreenSizeChange()
|
||||
{
|
||||
component.parentSizeChanged();
|
||||
handleMovedOrResized();
|
||||
}
|
||||
|
||||
void ComponentPeer::setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept
|
||||
{
|
||||
lastNonFullscreenBounds = newBounds;
|
||||
}
|
||||
|
||||
const Rectangle<int>& ComponentPeer::getNonFullScreenBounds() const noexcept
|
||||
{
|
||||
return lastNonFullscreenBounds;
|
||||
}
|
||||
|
||||
Point<int> ComponentPeer::localToGlobal (Point<int> p) { return localToGlobal (p.toFloat()).roundToInt(); }
|
||||
Point<int> ComponentPeer::globalToLocal (Point<int> p) { return globalToLocal (p.toFloat()).roundToInt(); }
|
||||
|
||||
Rectangle<int> ComponentPeer::localToGlobal (const Rectangle<int>& relativePosition)
|
||||
{
|
||||
return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
|
||||
}
|
||||
|
||||
Rectangle<int> ComponentPeer::globalToLocal (const Rectangle<int>& screenPosition)
|
||||
{
|
||||
return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
|
||||
}
|
||||
|
||||
Rectangle<float> ComponentPeer::localToGlobal (const Rectangle<float>& relativePosition)
|
||||
{
|
||||
return relativePosition.withPosition (localToGlobal (relativePosition.getPosition()));
|
||||
}
|
||||
|
||||
Rectangle<float> ComponentPeer::globalToLocal (const Rectangle<float>& screenPosition)
|
||||
{
|
||||
return screenPosition.withPosition (globalToLocal (screenPosition.getPosition()));
|
||||
}
|
||||
|
||||
Rectangle<int> ComponentPeer::getAreaCoveredBy (const Component& subComponent) const
|
||||
{
|
||||
return ScalingHelpers::scaledScreenPosToUnscaled
|
||||
(component, component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace DragHelpers
|
||||
{
|
||||
static bool isFileDrag (const ComponentPeer::DragInfo& info)
|
||||
{
|
||||
return ! info.files.isEmpty();
|
||||
}
|
||||
|
||||
static bool isSuitableTarget (const ComponentPeer::DragInfo& info, Component* target)
|
||||
{
|
||||
return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target) != nullptr
|
||||
: dynamic_cast<TextDragAndDropTarget*> (target) != nullptr;
|
||||
}
|
||||
|
||||
static bool isInterested (const ComponentPeer::DragInfo& info, Component* target)
|
||||
{
|
||||
return isFileDrag (info) ? dynamic_cast<FileDragAndDropTarget*> (target)->isInterestedInFileDrag (info.files)
|
||||
: dynamic_cast<TextDragAndDropTarget*> (target)->isInterestedInTextDrag (info.text);
|
||||
}
|
||||
|
||||
static Component* findDragAndDropTarget (Component* c, const ComponentPeer::DragInfo& info, Component* lastOne)
|
||||
{
|
||||
for (; c != nullptr; c = c->getParentComponent())
|
||||
if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
|
||||
return c;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
|
||||
{
|
||||
auto* compUnderMouse = component.getComponentAt (info.position);
|
||||
auto* lastTarget = dragAndDropTargetComponent.get();
|
||||
Component* newTarget = nullptr;
|
||||
|
||||
if (compUnderMouse != lastDragAndDropCompUnderMouse)
|
||||
{
|
||||
lastDragAndDropCompUnderMouse = compUnderMouse;
|
||||
newTarget = DragHelpers::findDragAndDropTarget (compUnderMouse, info, lastTarget);
|
||||
|
||||
if (newTarget != lastTarget)
|
||||
{
|
||||
if (lastTarget != nullptr)
|
||||
{
|
||||
if (DragHelpers::isFileDrag (info))
|
||||
dynamic_cast<FileDragAndDropTarget*> (lastTarget)->fileDragExit (info.files);
|
||||
else
|
||||
dynamic_cast<TextDragAndDropTarget*> (lastTarget)->textDragExit (info.text);
|
||||
}
|
||||
|
||||
dragAndDropTargetComponent = nullptr;
|
||||
|
||||
if (DragHelpers::isSuitableTarget (info, newTarget))
|
||||
{
|
||||
dragAndDropTargetComponent = newTarget;
|
||||
auto pos = newTarget->getLocalPoint (&component, info.position);
|
||||
|
||||
if (DragHelpers::isFileDrag (info))
|
||||
dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragEnter (info.files, pos.x, pos.y);
|
||||
else
|
||||
dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragEnter (info.text, pos.x, pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newTarget = lastTarget;
|
||||
}
|
||||
|
||||
if (! DragHelpers::isSuitableTarget (info, newTarget))
|
||||
return false;
|
||||
|
||||
auto pos = newTarget->getLocalPoint (&component, info.position);
|
||||
|
||||
if (DragHelpers::isFileDrag (info))
|
||||
dynamic_cast<FileDragAndDropTarget*> (newTarget)->fileDragMove (info.files, pos.x, pos.y);
|
||||
else
|
||||
dynamic_cast<TextDragAndDropTarget*> (newTarget)->textDragMove (info.text, pos.x, pos.y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleDragExit (const ComponentPeer::DragInfo& info)
|
||||
{
|
||||
DragInfo info2 (info);
|
||||
info2.position.setXY (-1, -1);
|
||||
const bool used = handleDragMove (info2);
|
||||
|
||||
jassert (dragAndDropTargetComponent == nullptr);
|
||||
lastDragAndDropCompUnderMouse = nullptr;
|
||||
return used;
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleDragDrop (const ComponentPeer::DragInfo& info)
|
||||
{
|
||||
handleDragMove (info);
|
||||
|
||||
if (WeakReference<Component> targetComp = dragAndDropTargetComponent)
|
||||
{
|
||||
dragAndDropTargetComponent = nullptr;
|
||||
lastDragAndDropCompUnderMouse = nullptr;
|
||||
|
||||
if (DragHelpers::isSuitableTarget (info, targetComp))
|
||||
{
|
||||
if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
targetComp->internalModalInputAttempt();
|
||||
|
||||
if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
|
||||
return true;
|
||||
}
|
||||
|
||||
ComponentPeer::DragInfo infoCopy (info);
|
||||
infoCopy.position = targetComp->getLocalPoint (&component, info.position);
|
||||
|
||||
// We'll use an async message to deliver the drop, because if the target decides
|
||||
// to run a modal loop, it can gum-up the operating system..
|
||||
MessageManager::callAsync ([=]
|
||||
{
|
||||
if (auto* c = targetComp.get())
|
||||
{
|
||||
if (DragHelpers::isFileDrag (info))
|
||||
dynamic_cast<FileDragAndDropTarget*> (c)->filesDropped (infoCopy.files, infoCopy.position.x, infoCopy.position.y);
|
||||
else
|
||||
dynamic_cast<TextDragAndDropTarget*> (c)->textDropped (infoCopy.text, infoCopy.position.x, infoCopy.position.y);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::handleUserClosingWindow()
|
||||
{
|
||||
component.userTriedToCloseWindow();
|
||||
}
|
||||
|
||||
bool ComponentPeer::setDocumentEditedStatus (bool)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ComponentPeer::setRepresentedFile (const File&)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int ComponentPeer::getCurrentRenderingEngine() const { return 0; }
|
||||
void ComponentPeer::setCurrentRenderingEngine (int index) { jassert (index == 0); ignoreUnused (index); }
|
||||
|
||||
//==============================================================================
|
||||
std::function<ModifierKeys()> ComponentPeer::getNativeRealtimeModifiers = nullptr;
|
||||
|
||||
ModifierKeys ComponentPeer::getCurrentModifiersRealtime() noexcept
|
||||
{
|
||||
if (getNativeRealtimeModifiers != nullptr)
|
||||
return getNativeRealtimeModifiers();
|
||||
|
||||
return ModifierKeys::currentModifiers;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::forceDisplayUpdate()
|
||||
{
|
||||
Desktop::getInstance().displays->refresh();
|
||||
}
|
||||
|
||||
} // namespace juce
|
440
deps/juce/modules/juce_gui_basics/windows/juce_ComponentPeer.h
vendored
Normal file
440
deps/juce/modules/juce_gui_basics/windows/juce_ComponentPeer.h
vendored
Normal file
@ -0,0 +1,440 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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 Component class uses a ComponentPeer internally to create and manage a real
|
||||
operating-system window.
|
||||
|
||||
This is an abstract base class - the platform specific code contains implementations of
|
||||
it for the various platforms.
|
||||
|
||||
User-code should very rarely need to have any involvement with this class.
|
||||
|
||||
@see Component::createNewPeer
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ComponentPeer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** A combination of these flags is passed to the ComponentPeer constructor. */
|
||||
enum StyleFlags
|
||||
{
|
||||
windowAppearsOnTaskbar = (1 << 0), /**< Indicates that the window should have a corresponding
|
||||
entry on the taskbar (ignored on MacOSX) */
|
||||
windowIsTemporary = (1 << 1), /**< Indicates that the window is a temporary popup, like a menu,
|
||||
tooltip, etc. */
|
||||
windowIgnoresMouseClicks = (1 << 2), /**< Indicates that the window should let mouse clicks pass
|
||||
through it (may not be possible on some platforms). */
|
||||
windowHasTitleBar = (1 << 3), /**< Indicates that the window should have a normal OS-specific
|
||||
title bar and frame. if not specified, the window will be
|
||||
borderless. */
|
||||
windowIsResizable = (1 << 4), /**< Indicates that the window should have a resizable border. */
|
||||
windowHasMinimiseButton = (1 << 5), /**< Indicates that if the window has a title bar, it should have a
|
||||
minimise button on it. */
|
||||
windowHasMaximiseButton = (1 << 6), /**< Indicates that if the window has a title bar, it should have a
|
||||
maximise button on it. */
|
||||
windowHasCloseButton = (1 << 7), /**< Indicates that if the window has a title bar, it should have a
|
||||
close button on it. */
|
||||
windowHasDropShadow = (1 << 8), /**< Indicates that the window should have a drop-shadow (this may
|
||||
not be possible on all platforms). */
|
||||
windowRepaintedExplictly = (1 << 9), /**< Not intended for public use - this tells a window not to
|
||||
do its own repainting, but only to repaint when the
|
||||
performAnyPendingRepaintsNow() method is called. */
|
||||
windowIgnoresKeyPresses = (1 << 10), /**< Tells the window not to catch any keypresses. This can
|
||||
be used for things like plugin windows, to stop them interfering
|
||||
with the host's shortcut keys. This will prevent the window from
|
||||
gaining keyboard focus. */
|
||||
windowIsSemiTransparent = (1 << 30) /**< Not intended for public use - makes a window transparent. */
|
||||
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Creates a peer.
|
||||
|
||||
The component is the one that we intend to represent, and the style flags are
|
||||
a combination of the values in the StyleFlags enum
|
||||
*/
|
||||
ComponentPeer (Component& component, int styleFlags);
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ComponentPeer();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the component being represented by this peer. */
|
||||
Component& getComponent() noexcept { return component; }
|
||||
|
||||
/** Returns the set of style flags that were set when the window was created.
|
||||
@see Component::addToDesktop
|
||||
*/
|
||||
int getStyleFlags() const noexcept { return styleFlags; }
|
||||
|
||||
/** Returns a unique ID for this peer.
|
||||
Each peer that is created is given a different ID.
|
||||
*/
|
||||
uint32 getUniqueID() const noexcept { return uniqueID; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the raw handle to whatever kind of window is being used.
|
||||
|
||||
On windows, this is probably a HWND, on the mac, it's likely to be a WindowRef,
|
||||
but remember there's no guarantees what you'll get back.
|
||||
*/
|
||||
virtual void* getNativeHandle() const = 0;
|
||||
|
||||
/** Shows or hides the window. */
|
||||
virtual void setVisible (bool shouldBeVisible) = 0;
|
||||
|
||||
/** Changes the title of the window. */
|
||||
virtual void setTitle (const String& title) = 0;
|
||||
|
||||
/** If this type of window is capable of indicating that the document in it has been
|
||||
edited, then this changes its status.
|
||||
|
||||
For example in OSX, this changes the appearance of the close button.
|
||||
@returns true if the window has a mechanism for showing this, or false if not.
|
||||
*/
|
||||
virtual bool setDocumentEditedStatus (bool edited);
|
||||
|
||||
/** If this type of window is capable of indicating that it represents a file, then
|
||||
this lets you set the file.
|
||||
|
||||
E.g. in OSX it'll show an icon for the file in the title bar.
|
||||
*/
|
||||
virtual void setRepresentedFile (const File&);
|
||||
|
||||
//==============================================================================
|
||||
/** Moves and resizes the window.
|
||||
|
||||
If the native window is contained in another window, then the coordinates are
|
||||
relative to the parent window's origin, not the screen origin.
|
||||
|
||||
This should result in a callback to handleMovedOrResized().
|
||||
*/
|
||||
virtual void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) = 0;
|
||||
|
||||
/** Updates the peer's bounds to match its component. */
|
||||
void updateBounds();
|
||||
|
||||
/** Returns the current position and size of the window.
|
||||
|
||||
If the native window is contained in another window, then the coordinates are
|
||||
relative to the parent window's origin, not the screen origin.
|
||||
*/
|
||||
virtual Rectangle<int> getBounds() const = 0;
|
||||
|
||||
/** Converts a position relative to the top-left of this component to screen coordinates. */
|
||||
virtual Point<float> localToGlobal (Point<float> relativePosition) = 0;
|
||||
|
||||
/** Converts a screen coordinate to a position relative to the top-left of this component. */
|
||||
virtual Point<float> globalToLocal (Point<float> screenPosition) = 0;
|
||||
|
||||
/** Converts a position relative to the top-left of this component to screen coordinates. */
|
||||
Point<int> localToGlobal (Point<int> relativePosition);
|
||||
|
||||
/** Converts a screen coordinate to a position relative to the top-left of this component. */
|
||||
Point<int> globalToLocal (Point<int> screenPosition);
|
||||
|
||||
/** Converts a rectangle relative to the top-left of this component to screen coordinates. */
|
||||
virtual Rectangle<int> localToGlobal (const Rectangle<int>& relativePosition);
|
||||
|
||||
/** Converts a screen area to a position relative to the top-left of this component. */
|
||||
virtual Rectangle<int> globalToLocal (const Rectangle<int>& screenPosition);
|
||||
|
||||
/** Converts a rectangle relative to the top-left of this component to screen coordinates. */
|
||||
Rectangle<float> localToGlobal (const Rectangle<float>& relativePosition);
|
||||
|
||||
/** Converts a screen area to a position relative to the top-left of this component. */
|
||||
Rectangle<float> globalToLocal (const Rectangle<float>& screenPosition);
|
||||
|
||||
/** Returns the area in peer coordinates that is covered by the given sub-comp (which
|
||||
may be at any depth)
|
||||
*/
|
||||
Rectangle<int> getAreaCoveredBy (const Component& subComponent) const;
|
||||
|
||||
/** Minimises the window. */
|
||||
virtual void setMinimised (bool shouldBeMinimised) = 0;
|
||||
|
||||
/** True if the window is currently minimised. */
|
||||
virtual bool isMinimised() const = 0;
|
||||
|
||||
/** Enable/disable fullscreen mode for the window. */
|
||||
virtual void setFullScreen (bool shouldBeFullScreen) = 0;
|
||||
|
||||
/** True if the window is currently full-screen. */
|
||||
virtual bool isFullScreen() const = 0;
|
||||
|
||||
/** True if the window is in kiosk-mode. */
|
||||
virtual bool isKioskMode() const;
|
||||
|
||||
/** Sets the size to restore to if fullscreen mode is turned off. */
|
||||
void setNonFullScreenBounds (const Rectangle<int>& newBounds) noexcept;
|
||||
|
||||
/** Returns the size to restore to if fullscreen mode is turned off. */
|
||||
const Rectangle<int>& getNonFullScreenBounds() const noexcept;
|
||||
|
||||
/** Attempts to change the icon associated with this window. */
|
||||
virtual void setIcon (const Image& newIcon) = 0;
|
||||
|
||||
/** Sets a constrainer to use if the peer can resize itself.
|
||||
The constrainer won't be deleted by this object, so the caller must manage its lifetime.
|
||||
*/
|
||||
void setConstrainer (ComponentBoundsConstrainer* newConstrainer) noexcept;
|
||||
|
||||
/** Returns the current constrainer, if one has been set. */
|
||||
ComponentBoundsConstrainer* getConstrainer() const noexcept { return constrainer; }
|
||||
|
||||
/** Checks if a point is in the window.
|
||||
|
||||
The position is relative to the top-left of this window, in unscaled peer coordinates.
|
||||
If trueIfInAChildWindow is false, then this returns false if the point is actually
|
||||
inside a child of this window.
|
||||
*/
|
||||
virtual bool contains (Point<int> localPos, bool trueIfInAChildWindow) const = 0;
|
||||
|
||||
/** Returns the size of the window frame that's around this window.
|
||||
Whether or not the window has a normal window frame depends on the flags
|
||||
that were set when the window was created by Component::addToDesktop()
|
||||
*/
|
||||
virtual BorderSize<int> getFrameSize() const = 0;
|
||||
|
||||
/** This is called when the window's bounds change.
|
||||
A peer implementation must call this when the window is moved and resized, so that
|
||||
this method can pass the message on to the component.
|
||||
*/
|
||||
void handleMovedOrResized();
|
||||
|
||||
/** This is called if the screen resolution changes.
|
||||
A peer implementation must call this if the monitor arrangement changes or the available
|
||||
screen size changes.
|
||||
*/
|
||||
virtual void handleScreenSizeChange();
|
||||
|
||||
//==============================================================================
|
||||
/** This is called to repaint the component into the given context. */
|
||||
void handlePaint (LowLevelGraphicsContext& contextToPaintTo);
|
||||
|
||||
//==============================================================================
|
||||
/** Sets this window to either be always-on-top or normal.
|
||||
Some kinds of window might not be able to do this, so should return false.
|
||||
*/
|
||||
virtual bool setAlwaysOnTop (bool alwaysOnTop) = 0;
|
||||
|
||||
/** Brings the window to the top, optionally also giving it keyboard focus. */
|
||||
virtual void toFront (bool takeKeyboardFocus) = 0;
|
||||
|
||||
/** Moves the window to be just behind another one. */
|
||||
virtual void toBehind (ComponentPeer* other) = 0;
|
||||
|
||||
/** Called when the window is brought to the front, either by the OS or by a call
|
||||
to toFront().
|
||||
*/
|
||||
void handleBroughtToFront();
|
||||
|
||||
//==============================================================================
|
||||
/** True if the window has the keyboard focus. */
|
||||
virtual bool isFocused() const = 0;
|
||||
|
||||
/** Tries to give the window keyboard focus. */
|
||||
virtual void grabFocus() = 0;
|
||||
|
||||
/** Called when the window gains keyboard focus. */
|
||||
void handleFocusGain();
|
||||
/** Called when the window loses keyboard focus. */
|
||||
void handleFocusLoss();
|
||||
|
||||
Component* getLastFocusedSubcomponent() const noexcept;
|
||||
|
||||
/** Called when a key is pressed.
|
||||
For keycode info, see the KeyPress class.
|
||||
Returns true if the keystroke was used.
|
||||
*/
|
||||
bool handleKeyPress (int keyCode, juce_wchar textCharacter);
|
||||
|
||||
/** Called when a key is pressed.
|
||||
Returns true if the keystroke was used.
|
||||
*/
|
||||
bool handleKeyPress (const KeyPress& key);
|
||||
|
||||
/** Called whenever a key is pressed or released.
|
||||
Returns true if the keystroke was used.
|
||||
*/
|
||||
bool handleKeyUpOrDown (bool isKeyDown);
|
||||
|
||||
/** Called whenever a modifier key is pressed or released. */
|
||||
void handleModifierKeysChange();
|
||||
|
||||
//==============================================================================
|
||||
/** Tells the window that text input may be required at the given position.
|
||||
This may cause things like a virtual on-screen keyboard to appear, depending
|
||||
on the OS.
|
||||
*/
|
||||
virtual void textInputRequired (Point<int> position, TextInputTarget&) = 0;
|
||||
|
||||
/** If there's some kind of OS input-method in progress, this should dismiss it. */
|
||||
virtual void dismissPendingTextInput();
|
||||
|
||||
/** Returns the currently focused TextInputTarget, or null if none is found. */
|
||||
TextInputTarget* findCurrentTextInputTarget();
|
||||
|
||||
//==============================================================================
|
||||
/** Invalidates a region of the window to be repainted asynchronously. */
|
||||
virtual void repaint (const Rectangle<int>& area) = 0;
|
||||
|
||||
/** This can be called (from the message thread) to cause the immediate redrawing
|
||||
of any areas of this window that need repainting.
|
||||
|
||||
You shouldn't ever really need to use this, it's mainly for special purposes
|
||||
like supporting audio plugins where the host's event loop is out of our control.
|
||||
*/
|
||||
virtual void performAnyPendingRepaintsNow() = 0;
|
||||
|
||||
/** Changes the window's transparency. */
|
||||
virtual void setAlpha (float newAlpha) = 0;
|
||||
|
||||
//==============================================================================
|
||||
void handleMouseEvent (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer, ModifierKeys newMods, float pressure,
|
||||
float orientation, int64 time, PenDetails pen = {}, int touchIndex = 0);
|
||||
|
||||
void handleMouseWheel (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer,
|
||||
int64 time, const MouseWheelDetails&, int touchIndex = 0);
|
||||
|
||||
void handleMagnifyGesture (MouseInputSource::InputSourceType type, Point<float> positionWithinPeer,
|
||||
int64 time, float scaleFactor, int touchIndex = 0);
|
||||
|
||||
void handleUserClosingWindow();
|
||||
|
||||
/** Structure to describe drag and drop information */
|
||||
struct DragInfo
|
||||
{
|
||||
StringArray files;
|
||||
String text;
|
||||
Point<int> position;
|
||||
|
||||
bool isEmpty() const noexcept { return files.size() == 0 && text.isEmpty(); }
|
||||
void clear() noexcept { files.clear(); text.clear(); }
|
||||
};
|
||||
|
||||
bool handleDragMove (const DragInfo&);
|
||||
bool handleDragExit (const DragInfo&);
|
||||
bool handleDragDrop (const DragInfo&);
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of currently-active peers.
|
||||
@see getPeer
|
||||
*/
|
||||
static int getNumPeers() noexcept;
|
||||
|
||||
/** Returns one of the currently-active peers.
|
||||
@see getNumPeers
|
||||
*/
|
||||
static ComponentPeer* getPeer (int index) noexcept;
|
||||
|
||||
/** Returns the peer that's attached to the given component, or nullptr if there isn't one. */
|
||||
static ComponentPeer* getPeerFor (const Component*) noexcept;
|
||||
|
||||
/** Checks if this peer object is valid.
|
||||
@see getNumPeers
|
||||
*/
|
||||
static bool isValidPeer (const ComponentPeer* peer) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
virtual StringArray getAvailableRenderingEngines() = 0;
|
||||
virtual int getCurrentRenderingEngine() const;
|
||||
virtual void setCurrentRenderingEngine (int index);
|
||||
|
||||
//==============================================================================
|
||||
/** On desktop platforms this method will check all the mouse and key states and return
|
||||
a ModifierKeys object representing them.
|
||||
|
||||
This isn't recommended and is only needed in special circumstances for up-to-date
|
||||
modifier information at times when the app's event loop isn't running normally.
|
||||
|
||||
Another reason to avoid this method is that it's not stateless and calling it may
|
||||
update the ModifierKeys::currentModifiers object, which could cause subtle changes
|
||||
in the behaviour of some components.
|
||||
*/
|
||||
static ModifierKeys getCurrentModifiersRealtime() noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Used to receive callbacks when the OS scale factor of this ComponentPeer changes.
|
||||
|
||||
This is used internally by some native JUCE windows on Windows and Linux and you
|
||||
shouldn't need to worry about it in your own code unless you are dealing directly
|
||||
with native windows.
|
||||
*/
|
||||
struct JUCE_API ScaleFactorListener
|
||||
{
|
||||
/** Destructor. */
|
||||
virtual ~ScaleFactorListener() = default;
|
||||
|
||||
/** Called when the scale factor changes. */
|
||||
virtual void nativeScaleFactorChanged (double newScaleFactor) = 0;
|
||||
};
|
||||
|
||||
/** Adds a scale factor listener. */
|
||||
void addScaleFactorListener (ScaleFactorListener* listenerToAdd) { scaleFactorListeners.add (listenerToAdd); }
|
||||
|
||||
/** Removes a scale factor listener. */
|
||||
void removeScaleFactorListener (ScaleFactorListener* listenerToRemove) { scaleFactorListeners.remove (listenerToRemove); }
|
||||
|
||||
//==============================================================================
|
||||
/** On Windows and Linux this will return the OS scaling factor currently being applied
|
||||
to the native window. This is used to convert between physical and logical pixels
|
||||
at the OS API level and you shouldn't need to use it in your own code unless you
|
||||
are dealing directly with the native window.
|
||||
*/
|
||||
virtual double getPlatformScaleFactor() const noexcept { return 1.0; }
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
static void forceDisplayUpdate();
|
||||
|
||||
Component& component;
|
||||
const int styleFlags;
|
||||
Rectangle<int> lastNonFullscreenBounds;
|
||||
ComponentBoundsConstrainer* constrainer = nullptr;
|
||||
static std::function<ModifierKeys()> getNativeRealtimeModifiers;
|
||||
ListenerList<ScaleFactorListener> scaleFactorListeners;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component* getTargetForKeyPress();
|
||||
|
||||
WeakReference<Component> lastFocusedComponent, dragAndDropTargetComponent;
|
||||
Component* lastDragAndDropCompUnderMouse = nullptr;
|
||||
const uint32 uniqueID;
|
||||
bool isWindowMinimised = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentPeer)
|
||||
};
|
||||
|
||||
} // namespace juce
|
181
deps/juce/modules/juce_gui_basics/windows/juce_DialogWindow.cpp
vendored
Normal file
181
deps/juce/modules/juce_gui_basics/windows/juce_DialogWindow.cpp
vendored
Normal file
@ -0,0 +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
|
276
deps/juce/modules/juce_gui_basics/windows/juce_DialogWindow.h
vendored
Normal file
276
deps/juce/modules/juce_gui_basics/windows/juce_DialogWindow.h
vendored
Normal file
@ -0,0 +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
|
360
deps/juce/modules/juce_gui_basics/windows/juce_DocumentWindow.cpp
vendored
Normal file
360
deps/juce/modules/juce_gui_basics/windows/juce_DocumentWindow.cpp
vendored
Normal file
@ -0,0 +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
|
295
deps/juce/modules/juce_gui_basics/windows/juce_DocumentWindow.h
vendored
Normal file
295
deps/juce/modules/juce_gui_basics/windows/juce_DocumentWindow.h
vendored
Normal file
@ -0,0 +1,295 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
143
deps/juce/modules/juce_gui_basics/windows/juce_MessageBoxOptions.h
vendored
Normal file
143
deps/juce/modules/juce_gui_basics/windows/juce_MessageBoxOptions.h
vendored
Normal file
@ -0,0 +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
|
265
deps/juce/modules/juce_gui_basics/windows/juce_NativeMessageBox.h
vendored
Normal file
265
deps/juce/modules/juce_gui_basics/windows/juce_NativeMessageBox.h
vendored
Normal file
@ -0,0 +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
|
684
deps/juce/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp
vendored
Normal file
684
deps/juce/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp
vendored
Normal file
@ -0,0 +1,684 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
ResizableWindow::ResizableWindow (const String& name, bool shouldAddToDesktop)
|
||||
: TopLevelWindow (name, shouldAddToDesktop)
|
||||
{
|
||||
initialise (shouldAddToDesktop);
|
||||
}
|
||||
|
||||
ResizableWindow::ResizableWindow (const String& name, Colour bkgnd, bool shouldAddToDesktop)
|
||||
: TopLevelWindow (name, shouldAddToDesktop)
|
||||
{
|
||||
setBackgroundColour (bkgnd);
|
||||
initialise (shouldAddToDesktop);
|
||||
}
|
||||
|
||||
ResizableWindow::~ResizableWindow()
|
||||
{
|
||||
splashScreen.deleteAndZero();
|
||||
|
||||
// Don't delete or remove the resizer components yourself! They're managed by the
|
||||
// ResizableWindow, and you should leave them alone! You may have deleted them
|
||||
// accidentally by careless use of deleteAllChildren()..?
|
||||
jassert (resizableCorner == nullptr || getIndexOfChildComponent (resizableCorner.get()) >= 0);
|
||||
jassert (resizableBorder == nullptr || getIndexOfChildComponent (resizableBorder.get()) >= 0);
|
||||
|
||||
resizableCorner.reset();
|
||||
resizableBorder.reset();
|
||||
clearContentComponent();
|
||||
|
||||
// have you been adding your own components directly to this window..? tut tut tut.
|
||||
// Read the instructions for using a ResizableWindow!
|
||||
jassert (getNumChildComponents() == 0);
|
||||
}
|
||||
|
||||
void ResizableWindow::initialise (const bool shouldAddToDesktop)
|
||||
{
|
||||
/*
|
||||
==========================================================================
|
||||
|
||||
In accordance with the terms of the JUCE 6 End-Use License Agreement, the
|
||||
JUCE Code in SECTION A cannot be removed, changed or otherwise rendered
|
||||
ineffective unless you have a JUCE Indie or Pro license, or are using
|
||||
JUCE under the GPL v3 license.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
|
||||
==========================================================================
|
||||
*/
|
||||
|
||||
// BEGIN SECTION A
|
||||
|
||||
#if ! JucePlugin_Build_Standalone
|
||||
splashScreen = new JUCESplashScreen (*this);
|
||||
#endif
|
||||
|
||||
// END SECTION A
|
||||
|
||||
defaultConstrainer.setMinimumOnscreenAmounts (0x10000, 16, 24, 16);
|
||||
|
||||
lastNonFullScreenPos.setBounds (50, 50, 256, 256);
|
||||
|
||||
if (shouldAddToDesktop)
|
||||
addToDesktop();
|
||||
}
|
||||
|
||||
int ResizableWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
int styleFlags = TopLevelWindow::getDesktopWindowStyleFlags();
|
||||
|
||||
if (isResizable() && (styleFlags & ComponentPeer::windowHasTitleBar) != 0)
|
||||
styleFlags |= ComponentPeer::windowIsResizable;
|
||||
|
||||
return styleFlags;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ResizableWindow::clearContentComponent()
|
||||
{
|
||||
if (ownsContentComponent)
|
||||
{
|
||||
contentComponent.deleteAndZero();
|
||||
}
|
||||
else
|
||||
{
|
||||
removeChildComponent (contentComponent);
|
||||
contentComponent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ResizableWindow::setContent (Component* newContentComponent,
|
||||
bool takeOwnership,
|
||||
bool resizeToFitWhenContentChangesSize)
|
||||
{
|
||||
if (newContentComponent != contentComponent)
|
||||
{
|
||||
clearContentComponent();
|
||||
|
||||
contentComponent = newContentComponent;
|
||||
Component::addAndMakeVisible (contentComponent);
|
||||
}
|
||||
|
||||
ownsContentComponent = takeOwnership;
|
||||
resizeToFitContent = resizeToFitWhenContentChangesSize;
|
||||
|
||||
if (resizeToFitWhenContentChangesSize)
|
||||
childBoundsChanged (contentComponent);
|
||||
|
||||
resized(); // must always be called to position the new content comp
|
||||
}
|
||||
|
||||
void ResizableWindow::setContentOwned (Component* newContentComponent, const bool resizeToFitWhenContentChangesSize)
|
||||
{
|
||||
setContent (newContentComponent, true, resizeToFitWhenContentChangesSize);
|
||||
}
|
||||
|
||||
void ResizableWindow::setContentNonOwned (Component* newContentComponent, const bool resizeToFitWhenContentChangesSize)
|
||||
{
|
||||
setContent (newContentComponent, false, resizeToFitWhenContentChangesSize);
|
||||
}
|
||||
|
||||
void ResizableWindow::setContentComponent (Component* const newContentComponent,
|
||||
const bool deleteOldOne,
|
||||
const bool resizeToFitWhenContentChangesSize)
|
||||
{
|
||||
if (newContentComponent != contentComponent)
|
||||
{
|
||||
if (deleteOldOne)
|
||||
{
|
||||
contentComponent.deleteAndZero();
|
||||
}
|
||||
else
|
||||
{
|
||||
removeChildComponent (contentComponent);
|
||||
contentComponent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
setContent (newContentComponent, true, resizeToFitWhenContentChangesSize);
|
||||
}
|
||||
|
||||
void ResizableWindow::setContentComponentSize (int width, int height)
|
||||
{
|
||||
jassert (width > 0 && height > 0); // not a great idea to give it a zero size..
|
||||
|
||||
auto border = getContentComponentBorder();
|
||||
|
||||
setSize (width + border.getLeftAndRight(),
|
||||
height + border.getTopAndBottom());
|
||||
}
|
||||
|
||||
BorderSize<int> ResizableWindow::getBorderThickness()
|
||||
{
|
||||
if (isUsingNativeTitleBar() || isKioskMode())
|
||||
return {};
|
||||
|
||||
return BorderSize<int> ((resizableBorder != nullptr && ! isFullScreen()) ? 4 : 1);
|
||||
}
|
||||
|
||||
BorderSize<int> ResizableWindow::getContentComponentBorder()
|
||||
{
|
||||
return getBorderThickness();
|
||||
}
|
||||
|
||||
void ResizableWindow::moved()
|
||||
{
|
||||
updateLastPosIfShowing();
|
||||
}
|
||||
|
||||
void ResizableWindow::visibilityChanged()
|
||||
{
|
||||
TopLevelWindow::visibilityChanged();
|
||||
updateLastPosIfShowing();
|
||||
}
|
||||
|
||||
void ResizableWindow::resized()
|
||||
{
|
||||
const bool resizerHidden = isFullScreen() || isKioskMode() || isUsingNativeTitleBar();
|
||||
|
||||
if (resizableBorder != nullptr)
|
||||
{
|
||||
resizableBorder->setVisible (! resizerHidden);
|
||||
resizableBorder->setBorderThickness (getBorderThickness());
|
||||
resizableBorder->setSize (getWidth(), getHeight());
|
||||
resizableBorder->toBack();
|
||||
}
|
||||
|
||||
if (resizableCorner != nullptr)
|
||||
{
|
||||
resizableCorner->setVisible (! resizerHidden);
|
||||
|
||||
const int resizerSize = 18;
|
||||
resizableCorner->setBounds (getWidth() - resizerSize,
|
||||
getHeight() - resizerSize,
|
||||
resizerSize, resizerSize);
|
||||
}
|
||||
|
||||
if (contentComponent != nullptr)
|
||||
{
|
||||
// The window expects to be able to be able to manage the size and position
|
||||
// of its content component, so you can't arbitrarily add a transform to it!
|
||||
jassert (! contentComponent->isTransformed());
|
||||
|
||||
contentComponent->setBoundsInset (getContentComponentBorder());
|
||||
}
|
||||
|
||||
updateLastPosIfShowing();
|
||||
|
||||
#if JUCE_DEBUG
|
||||
hasBeenResized = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResizableWindow::childBoundsChanged (Component* child)
|
||||
{
|
||||
if ((child == contentComponent) && (child != nullptr) && resizeToFitContent)
|
||||
{
|
||||
// not going to look very good if this component has a zero size..
|
||||
jassert (child->getWidth() > 0);
|
||||
jassert (child->getHeight() > 0);
|
||||
|
||||
auto borders = getContentComponentBorder();
|
||||
|
||||
setSize (child->getWidth() + borders.getLeftAndRight(),
|
||||
child->getHeight() + borders.getTopAndBottom());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void ResizableWindow::activeWindowStatusChanged()
|
||||
{
|
||||
auto border = getContentComponentBorder();
|
||||
auto area = getLocalBounds();
|
||||
|
||||
repaint (area.removeFromTop (border.getTop()));
|
||||
repaint (area.removeFromLeft (border.getLeft()));
|
||||
repaint (area.removeFromRight (border.getRight()));
|
||||
repaint (area.removeFromBottom (border.getBottom()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ResizableWindow::setResizable (const bool shouldBeResizable,
|
||||
const bool useBottomRightCornerResizer)
|
||||
{
|
||||
if (shouldBeResizable)
|
||||
{
|
||||
if (useBottomRightCornerResizer)
|
||||
{
|
||||
resizableBorder.reset();
|
||||
|
||||
if (resizableCorner == nullptr)
|
||||
{
|
||||
resizableCorner.reset (new ResizableCornerComponent (this, constrainer));
|
||||
Component::addChildComponent (resizableCorner.get());
|
||||
resizableCorner->setAlwaysOnTop (true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resizableCorner.reset();
|
||||
|
||||
if (resizableBorder == nullptr)
|
||||
{
|
||||
resizableBorder.reset (new ResizableBorderComponent (this, constrainer));
|
||||
Component::addChildComponent (resizableBorder.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resizableCorner.reset();
|
||||
resizableBorder.reset();
|
||||
}
|
||||
|
||||
if (isUsingNativeTitleBar())
|
||||
recreateDesktopWindow();
|
||||
|
||||
childBoundsChanged (contentComponent);
|
||||
resized();
|
||||
}
|
||||
|
||||
bool ResizableWindow::isResizable() const noexcept
|
||||
{
|
||||
return resizableCorner != nullptr
|
||||
|| resizableBorder != nullptr;
|
||||
}
|
||||
|
||||
void ResizableWindow::setResizeLimits (int newMinimumWidth,
|
||||
int newMinimumHeight,
|
||||
int newMaximumWidth,
|
||||
int newMaximumHeight) noexcept
|
||||
{
|
||||
// if you've set up a custom constrainer then these settings won't have any effect..
|
||||
jassert (constrainer == &defaultConstrainer || constrainer == nullptr);
|
||||
|
||||
if (constrainer == nullptr)
|
||||
setConstrainer (&defaultConstrainer);
|
||||
|
||||
defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight,
|
||||
newMaximumWidth, newMaximumHeight);
|
||||
|
||||
setBoundsConstrained (getBounds());
|
||||
}
|
||||
|
||||
void ResizableWindow::setDraggable (bool shouldBeDraggable) noexcept
|
||||
{
|
||||
canDrag = shouldBeDraggable;
|
||||
}
|
||||
|
||||
void ResizableWindow::setConstrainer (ComponentBoundsConstrainer* newConstrainer)
|
||||
{
|
||||
if (constrainer != newConstrainer)
|
||||
{
|
||||
constrainer = newConstrainer;
|
||||
|
||||
bool useBottomRightCornerResizer = resizableCorner != nullptr;
|
||||
bool shouldBeResizable = useBottomRightCornerResizer || resizableBorder != nullptr;
|
||||
|
||||
resizableCorner.reset();
|
||||
resizableBorder.reset();
|
||||
|
||||
setResizable (shouldBeResizable, useBottomRightCornerResizer);
|
||||
updatePeerConstrainer();
|
||||
}
|
||||
}
|
||||
|
||||
void ResizableWindow::setBoundsConstrained (const Rectangle<int>& newBounds)
|
||||
{
|
||||
if (constrainer != nullptr)
|
||||
constrainer->setBoundsForComponent (this, newBounds, false, false, false, false);
|
||||
else
|
||||
setBounds (newBounds);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ResizableWindow::paint (Graphics& g)
|
||||
{
|
||||
auto& lf = getLookAndFeel();
|
||||
|
||||
lf.fillResizableWindowBackground (g, getWidth(), getHeight(),
|
||||
getBorderThickness(), *this);
|
||||
|
||||
if (! isFullScreen())
|
||||
lf.drawResizableWindowBorder (g, getWidth(), getHeight(),
|
||||
getBorderThickness(), *this);
|
||||
|
||||
#if JUCE_DEBUG
|
||||
/* If this fails, then you've probably written a subclass with a resized()
|
||||
callback but forgotten to make it call its parent class's resized() method.
|
||||
|
||||
It's important when you override methods like resized(), moved(),
|
||||
etc., that you make sure the base class methods also get called.
|
||||
|
||||
Of course you shouldn't really be overriding ResizableWindow::resized() anyway,
|
||||
because your content should all be inside the content component - and it's the
|
||||
content component's resized() method that you should be using to do your
|
||||
layout.
|
||||
*/
|
||||
jassert (hasBeenResized || (getWidth() == 0 && getHeight() == 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResizableWindow::lookAndFeelChanged()
|
||||
{
|
||||
resized();
|
||||
|
||||
if (isOnDesktop())
|
||||
{
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
updatePeerConstrainer();
|
||||
}
|
||||
}
|
||||
|
||||
Colour ResizableWindow::getBackgroundColour() const noexcept
|
||||
{
|
||||
return findColour (backgroundColourId, false);
|
||||
}
|
||||
|
||||
void ResizableWindow::setBackgroundColour (Colour newColour)
|
||||
{
|
||||
auto backgroundColour = newColour;
|
||||
|
||||
if (! Desktop::canUseSemiTransparentWindows())
|
||||
backgroundColour = newColour.withAlpha (1.0f);
|
||||
|
||||
setColour (backgroundColourId, backgroundColour);
|
||||
setOpaque (backgroundColour.isOpaque());
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ResizableWindow::isFullScreen() const
|
||||
{
|
||||
if (isOnDesktop())
|
||||
{
|
||||
auto* peer = getPeer();
|
||||
return peer != nullptr && peer->isFullScreen();
|
||||
}
|
||||
|
||||
return fullscreen;
|
||||
}
|
||||
|
||||
void ResizableWindow::setFullScreen (const bool shouldBeFullScreen)
|
||||
{
|
||||
if (shouldBeFullScreen != isFullScreen())
|
||||
{
|
||||
updateLastPosIfShowing();
|
||||
fullscreen = shouldBeFullScreen;
|
||||
|
||||
if (isOnDesktop())
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
// keep a copy of this intact in case the real one gets messed-up while we're un-maximising
|
||||
auto lastPos = lastNonFullScreenPos;
|
||||
|
||||
peer->setFullScreen (shouldBeFullScreen);
|
||||
|
||||
if ((! shouldBeFullScreen) && ! lastPos.isEmpty())
|
||||
setBounds (lastPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shouldBeFullScreen)
|
||||
setBounds (0, 0, getParentWidth(), getParentHeight());
|
||||
else
|
||||
setBounds (lastNonFullScreenPos);
|
||||
}
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
bool ResizableWindow::isMinimised() const
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
return peer->isMinimised();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResizableWindow::setMinimised (const bool shouldMinimise)
|
||||
{
|
||||
if (shouldMinimise != isMinimised())
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
updateLastPosIfShowing();
|
||||
peer->setMinimised (shouldMinimise);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ResizableWindow::isKioskMode() const
|
||||
{
|
||||
if (isOnDesktop())
|
||||
if (auto* peer = getPeer())
|
||||
return peer->isKioskMode();
|
||||
|
||||
return Desktop::getInstance().getKioskModeComponent() == this;
|
||||
}
|
||||
|
||||
void ResizableWindow::updateLastPosIfShowing()
|
||||
{
|
||||
if (isShowing())
|
||||
{
|
||||
updateLastPosIfNotFullScreen();
|
||||
updatePeerConstrainer();
|
||||
}
|
||||
}
|
||||
|
||||
void ResizableWindow::updateLastPosIfNotFullScreen()
|
||||
{
|
||||
if (! (isFullScreen() || isMinimised() || isKioskMode()))
|
||||
lastNonFullScreenPos = getBounds();
|
||||
}
|
||||
|
||||
void ResizableWindow::updatePeerConstrainer()
|
||||
{
|
||||
if (isOnDesktop())
|
||||
if (auto* peer = getPeer())
|
||||
peer->setConstrainer (constrainer);
|
||||
}
|
||||
|
||||
void ResizableWindow::parentSizeChanged()
|
||||
{
|
||||
if (isFullScreen() && getParentComponent() != nullptr)
|
||||
setBounds (getParentComponent()->getLocalBounds());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String ResizableWindow::getWindowStateAsString()
|
||||
{
|
||||
updateLastPosIfShowing();
|
||||
auto stateString = (isFullScreen() && ! isKioskMode() ? "fs " : "") + lastNonFullScreenPos.toString();
|
||||
|
||||
#if JUCE_LINUX
|
||||
if (auto* peer = isOnDesktop() ? getPeer() : nullptr)
|
||||
{
|
||||
const auto frameSize = peer->getFrameSize();
|
||||
stateString << " frame " << frameSize.getTop() << ' ' << frameSize.getLeft()
|
||||
<< ' ' << frameSize.getBottom() << ' ' << frameSize.getRight();
|
||||
}
|
||||
#endif
|
||||
|
||||
return stateString;
|
||||
}
|
||||
|
||||
bool ResizableWindow::restoreWindowStateFromString (const String& s)
|
||||
{
|
||||
StringArray tokens;
|
||||
tokens.addTokens (s, false);
|
||||
tokens.removeEmptyStrings();
|
||||
tokens.trim();
|
||||
|
||||
const bool fs = tokens[0].startsWithIgnoreCase ("fs");
|
||||
const int firstCoord = fs ? 1 : 0;
|
||||
|
||||
if (tokens.size() < firstCoord + 4)
|
||||
return false;
|
||||
|
||||
Rectangle<int> newPos (tokens[firstCoord].getIntValue(),
|
||||
tokens[firstCoord + 1].getIntValue(),
|
||||
tokens[firstCoord + 2].getIntValue(),
|
||||
tokens[firstCoord + 3].getIntValue());
|
||||
|
||||
if (newPos.isEmpty())
|
||||
return false;
|
||||
|
||||
auto* peer = isOnDesktop() ? getPeer() : nullptr;
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->getFrameSize().addTo (newPos);
|
||||
}
|
||||
#if JUCE_LINUX
|
||||
else
|
||||
{
|
||||
// We need to adjust for the frame size before we create a peer, as X11
|
||||
// doesn't provide this information at construction time.
|
||||
if (tokens[firstCoord + 4] == "frame" && tokens.size() == firstCoord + 9)
|
||||
{
|
||||
BorderSize<int> frame { tokens[firstCoord + 5].getIntValue(),
|
||||
tokens[firstCoord + 6].getIntValue(),
|
||||
tokens[firstCoord + 7].getIntValue(),
|
||||
tokens[firstCoord + 8].getIntValue() };
|
||||
|
||||
frame.addTo (newPos);
|
||||
setBounds (newPos);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
auto& desktop = Desktop::getInstance();
|
||||
auto allMonitors = desktop.getDisplays().getRectangleList (true);
|
||||
allMonitors.clipTo (newPos);
|
||||
auto onScreenArea = allMonitors.getBounds();
|
||||
|
||||
if (onScreenArea.getWidth() * onScreenArea.getHeight() < 32 * 32)
|
||||
{
|
||||
auto screen = desktop.getDisplays().getDisplayForRect (newPos)->userArea;
|
||||
|
||||
newPos.setSize (jmin (newPos.getWidth(), screen.getWidth()),
|
||||
jmin (newPos.getHeight(), screen.getHeight()));
|
||||
|
||||
newPos.setPosition (jlimit (screen.getX(), screen.getRight() - newPos.getWidth(), newPos.getX()),
|
||||
jlimit (screen.getY(), screen.getBottom() - newPos.getHeight(), newPos.getY()));
|
||||
}
|
||||
}
|
||||
|
||||
if (peer != nullptr)
|
||||
{
|
||||
peer->getFrameSize().subtractFrom (newPos);
|
||||
peer->setNonFullScreenBounds (newPos);
|
||||
}
|
||||
|
||||
updateLastPosIfNotFullScreen();
|
||||
|
||||
if (fs)
|
||||
setBoundsConstrained (newPos);
|
||||
|
||||
setFullScreen (fs);
|
||||
|
||||
if (! fs)
|
||||
setBoundsConstrained (newPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ResizableWindow::mouseDown (const MouseEvent& e)
|
||||
{
|
||||
if (canDrag && ! isFullScreen())
|
||||
{
|
||||
dragStarted = true;
|
||||
dragger.startDraggingComponent (this, e);
|
||||
}
|
||||
}
|
||||
|
||||
void ResizableWindow::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
if (dragStarted)
|
||||
dragger.dragComponent (this, e, constrainer);
|
||||
}
|
||||
|
||||
void ResizableWindow::mouseUp (const MouseEvent&)
|
||||
{
|
||||
dragStarted = false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_DEBUG
|
||||
void ResizableWindow::addChildComponent (Component* const child, int zOrder)
|
||||
{
|
||||
/* Agh! You shouldn't add components directly to a ResizableWindow - this class
|
||||
manages its child components automatically, and if you add your own it'll cause
|
||||
trouble. Instead, use setContentComponent() to give it a component which
|
||||
will be automatically resized and kept in the right place - then you can add
|
||||
subcomponents to the content comp. See the notes for the ResizableWindow class
|
||||
for more info.
|
||||
|
||||
If you really know what you're doing and want to avoid this assertion, just call
|
||||
Component::addChildComponent directly.
|
||||
*/
|
||||
jassertfalse;
|
||||
|
||||
Component::addChildComponent (child, zOrder);
|
||||
}
|
||||
|
||||
void ResizableWindow::addAndMakeVisible (Component* const child, int zOrder)
|
||||
{
|
||||
/* Agh! You shouldn't add components directly to a ResizableWindow - this class
|
||||
manages its child components automatically, and if you add your own it'll cause
|
||||
trouble. Instead, use setContentComponent() to give it a component which
|
||||
will be automatically resized and kept in the right place - then you can add
|
||||
subcomponents to the content comp. See the notes for the ResizableWindow class
|
||||
for more info.
|
||||
|
||||
If you really know what you're doing and want to avoid this assertion, just call
|
||||
Component::addAndMakeVisible directly.
|
||||
*/
|
||||
jassertfalse;
|
||||
|
||||
Component::addAndMakeVisible (child, zOrder);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
413
deps/juce/modules/juce_gui_basics/windows/juce_ResizableWindow.h
vendored
Normal file
413
deps/juce/modules/juce_gui_basics/windows/juce_ResizableWindow.h
vendored
Normal file
@ -0,0 +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
|
119
deps/juce/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp
vendored
Normal file
119
deps/juce/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp
vendored
Normal file
@ -0,0 +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
|
175
deps/juce/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h
vendored
Normal file
175
deps/juce/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h
vendored
Normal file
@ -0,0 +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
|
232
deps/juce/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp
vendored
Normal file
232
deps/juce/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp
vendored
Normal file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
149
deps/juce/modules/juce_gui_basics/windows/juce_TooltipWindow.h
vendored
Normal file
149
deps/juce/modules/juce_gui_basics/windows/juce_TooltipWindow.h
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
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
|
358
deps/juce/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp
vendored
Normal file
358
deps/juce/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp
vendored
Normal file
@ -0,0 +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
|
166
deps/juce/modules/juce_gui_basics/windows/juce_TopLevelWindow.h
vendored
Normal file
166
deps/juce/modules/juce_gui_basics/windows/juce_TopLevelWindow.h
vendored
Normal file
@ -0,0 +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
|
Reference in New Issue
Block a user