mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-15 00:24:19 +00:00
Added AudioAppExample file in examples
This commit is contained in:
parent
06b9bdefb6
commit
c81ee3b5be
1140 changed files with 442849 additions and 10 deletions
|
|
@ -0,0 +1,713 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
static juce_wchar getDefaultPasswordChar() noexcept
|
||||
{
|
||||
#if JUCE_LINUX
|
||||
return 0x2022;
|
||||
#else
|
||||
return 0x25cf;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AlertWindow::AlertWindow (const String& title,
|
||||
const String& message,
|
||||
AlertIconType iconType,
|
||||
Component* comp)
|
||||
: TopLevelWindow (title, true),
|
||||
alertIconType (iconType),
|
||||
associatedComponent (comp),
|
||||
escapeKeyCancels (true)
|
||||
{
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
|
||||
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()
|
||||
{
|
||||
removeAllChildren();
|
||||
}
|
||||
|
||||
void AlertWindow::userTriedToCloseWindow()
|
||||
{
|
||||
if (escapeKeyCancels || buttons.size() > 0)
|
||||
exitModalState (0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::setMessage (const String& message)
|
||||
{
|
||||
const String newMessage (message.substring (0, 2048));
|
||||
|
||||
if (text != newMessage)
|
||||
{
|
||||
text = newMessage;
|
||||
updateLayout (true);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::buttonClicked (Button* button)
|
||||
{
|
||||
if (Component* parent = button->getParentComponent())
|
||||
parent->exitModalState (button->getCommandID());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addButton (const String& name,
|
||||
const int returnValue,
|
||||
const KeyPress& shortcutKey1,
|
||||
const KeyPress& shortcutKey2)
|
||||
{
|
||||
TextButton* const b = new TextButton (name, String::empty);
|
||||
buttons.add (b);
|
||||
|
||||
b->setWantsKeyboardFocus (true);
|
||||
b->setMouseClickGrabsKeyboardFocus (false);
|
||||
b->setCommandToTrigger (0, returnValue, false);
|
||||
b->addShortcut (shortcutKey1);
|
||||
b->addShortcut (shortcutKey2);
|
||||
b->addListener (this);
|
||||
b->changeWidthToFitText (getLookAndFeel().getAlertWindowButtonHeight());
|
||||
|
||||
addAndMakeVisible (b, 0);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
int AlertWindow::getNumButtons() const
|
||||
{
|
||||
return buttons.size();
|
||||
}
|
||||
|
||||
void AlertWindow::triggerButtonClick (const String& buttonName)
|
||||
{
|
||||
for (int i = buttons.size(); --i >= 0;)
|
||||
{
|
||||
TextButton* const b = buttons.getUnchecked(i);
|
||||
|
||||
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)
|
||||
{
|
||||
TextEditor* 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());
|
||||
ed->setText (initialContents);
|
||||
ed->setCaretPosition (initialContents.length());
|
||||
addAndMakeVisible (ed);
|
||||
textboxNames.add (onScreenLabel);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
TextEditor* AlertWindow::getTextEditor (const String& nameOfTextEditor) const
|
||||
{
|
||||
for (int i = textBoxes.size(); --i >= 0;)
|
||||
if (textBoxes.getUnchecked(i)->getName() == nameOfTextEditor)
|
||||
return textBoxes.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
String AlertWindow::getTextEditorContents (const String& nameOfTextEditor) const
|
||||
{
|
||||
if (TextEditor* const t = getTextEditor (nameOfTextEditor))
|
||||
return t->getText();
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addComboBox (const String& name,
|
||||
const StringArray& items,
|
||||
const String& onScreenLabel)
|
||||
{
|
||||
ComboBox* const 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 (int i = comboBoxes.size(); --i >= 0;)
|
||||
if (comboBoxes.getUnchecked(i)->getName() == nameOfList)
|
||||
return comboBoxes.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AlertTextComp : public TextEditor
|
||||
{
|
||||
public:
|
||||
AlertTextComp (const String& message,
|
||||
const Font& font)
|
||||
{
|
||||
setReadOnly (true);
|
||||
setMultiLine (true, true);
|
||||
setCaretVisible (false);
|
||||
setScrollbarsShown (true);
|
||||
lookAndFeelChanged();
|
||||
setWantsKeyboardFocus (false);
|
||||
|
||||
setFont (font);
|
||||
setText (message, false);
|
||||
|
||||
bestWidth = 2 * (int) std::sqrt (font.getHeight() * font.getStringWidth (message));
|
||||
|
||||
setColour (TextEditor::backgroundColourId, Colours::transparentBlack);
|
||||
setColour (TextEditor::outlineColourId, Colours::transparentBlack);
|
||||
setColour (TextEditor::shadowColourId, Colours::transparentBlack);
|
||||
}
|
||||
|
||||
int getPreferredWidth() const noexcept { return bestWidth; }
|
||||
|
||||
void updateLayout (const int width)
|
||||
{
|
||||
AttributedString s;
|
||||
s.setJustification (Justification::topLeft);
|
||||
s.append (getText(), getFont());
|
||||
|
||||
TextLayout text;
|
||||
text.createLayoutWithBalancedLineLengths (s, width - 8.0f);
|
||||
setSize (width, jmin (width, (int) (text.getHeight() + getFont().getHeight())));
|
||||
}
|
||||
|
||||
private:
|
||||
int bestWidth;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AlertTextComp)
|
||||
};
|
||||
|
||||
void AlertWindow::addTextBlock (const String& textBlock)
|
||||
{
|
||||
AlertTextComp* const c = new AlertTextComp (textBlock, getLookAndFeel().getAlertWindowMessageFont());
|
||||
textBlocks.add (c);
|
||||
allComps.add (c);
|
||||
|
||||
addAndMakeVisible (c);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::addProgressBarComponent (double& progressValue)
|
||||
{
|
||||
ProgressBar* const 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 (const int index) const
|
||||
{
|
||||
return customComps [index];
|
||||
}
|
||||
|
||||
Component* AlertWindow::removeCustomComponent (const int index)
|
||||
{
|
||||
Component* const c = getCustomComponent (index);
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
customComps.removeFirstMatchingValue (c);
|
||||
allComps.removeFirstMatchingValue (c);
|
||||
removeChildComponent (c);
|
||||
|
||||
updateLayout (false);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void AlertWindow::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawAlertBox (g, *this, textArea, textLayout);
|
||||
|
||||
g.setColour (findColour (textColourId));
|
||||
g.setFont (getLookAndFeel().getAlertWindowFont());
|
||||
|
||||
for (int i = textBoxes.size(); --i >= 0;)
|
||||
{
|
||||
const TextEditor* const 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;)
|
||||
{
|
||||
const ComboBox* const cb = comboBoxes.getUnchecked(i);
|
||||
|
||||
g.drawFittedText (comboBoxNames[i],
|
||||
cb->getX(), cb->getY() - 14,
|
||||
cb->getWidth(), 14,
|
||||
Justification::centredLeft, 1);
|
||||
}
|
||||
|
||||
for (int i = customComps.size(); --i >= 0;)
|
||||
{
|
||||
const Component* const c = customComps.getUnchecked(i);
|
||||
|
||||
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;
|
||||
|
||||
const Font font (getLookAndFeel().getAlertWindowMessageFont());
|
||||
|
||||
const int wid = jmax (font.getStringWidth (text),
|
||||
font.getStringWidth (getName()));
|
||||
|
||||
const int sw = (int) std::sqrt (font.getHeight() * wid);
|
||||
int w = jmin (300 + sw * 2, (int) (getParentWidth() * 0.7f));
|
||||
const int edgeGap = 10;
|
||||
const int labelHeight = 18;
|
||||
int iconSpace = 0;
|
||||
|
||||
AttributedString attributedText;
|
||||
attributedText.append (getName(), font.withHeight (font.getHeight() * 1.1f).boldened());
|
||||
|
||||
if (text.isNotEmpty())
|
||||
attributedText.append ("\n\n" + text, font);
|
||||
|
||||
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) (getParentWidth() * 0.7f));
|
||||
|
||||
const int textLayoutH = (int) textLayout.getHeight();
|
||||
const int textBottom = 16 + titleH + textLayoutH;
|
||||
int h = textBottom;
|
||||
|
||||
int buttonW = 40;
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
buttonW += 16 + buttons.getUnchecked(i)->getWidth();
|
||||
|
||||
w = jmax (buttonW, w);
|
||||
|
||||
h += (textBoxes.size() + comboBoxes.size() + progressBars.size()) * 50;
|
||||
|
||||
if (buttons.size() > 0)
|
||||
h += 20 + buttons.getUnchecked(0)->getHeight();
|
||||
|
||||
for (int i = customComps.size(); --i >= 0;)
|
||||
{
|
||||
Component* c = customComps.getUnchecked(i);
|
||||
w = jmax (w, (c->getWidth() * 100) / 80);
|
||||
h += 10 + c->getHeight();
|
||||
|
||||
if (c->getName().isNotEmpty())
|
||||
h += labelHeight;
|
||||
}
|
||||
|
||||
for (int i = textBlocks.size(); --i >= 0;)
|
||||
{
|
||||
const AlertTextComp* const ac = static_cast<const AlertTextComp*> (textBlocks.getUnchecked(i));
|
||||
w = jmax (w, ac->getPreferredWidth());
|
||||
}
|
||||
|
||||
w = jmin (w, (int) (getParentWidth() * 0.7f));
|
||||
|
||||
for (int i = textBlocks.size(); --i >= 0;)
|
||||
{
|
||||
AlertTextComp* const ac = static_cast<AlertTextComp*> (textBlocks.getUnchecked(i));
|
||||
ac->updateLayout ((int) (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
|
||||
{
|
||||
const int cx = getX() + getWidth() / 2;
|
||||
const int cy = getY() + getHeight() / 2;
|
||||
|
||||
setBounds (cx - w / 2,
|
||||
cy - h / 2,
|
||||
w, h);
|
||||
}
|
||||
|
||||
textArea.setBounds (edgeGap, edgeGap, w - (edgeGap * 2), h - edgeGap);
|
||||
|
||||
const int spacer = 16;
|
||||
int totalWidth = -spacer;
|
||||
|
||||
for (int i = buttons.size(); --i >= 0;)
|
||||
totalWidth += buttons.getUnchecked(i)->getWidth() + spacer;
|
||||
|
||||
int x = (w - totalWidth) / 2;
|
||||
int y = (int) (getHeight() * 0.95f);
|
||||
|
||||
for (int i = 0; i < buttons.size(); ++i)
|
||||
{
|
||||
TextButton* const c = buttons.getUnchecked(i);
|
||||
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 (int i = 0; i < allComps.size(); ++i)
|
||||
{
|
||||
Component* const c = allComps.getUnchecked(i);
|
||||
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 (int i = buttons.size(); --i >= 0;)
|
||||
{
|
||||
TextButton* const b = buttons.getUnchecked(i);
|
||||
|
||||
if (b->isRegisteredForShortcut (key))
|
||||
{
|
||||
b->triggerClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (key.isKeyCode (KeyPress::escapeKey) && escapeKeyCancels && buttons.size() == 0)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AlertWindowInfo
|
||||
{
|
||||
public:
|
||||
AlertWindowInfo (const String& t, const String& m, Component* component,
|
||||
AlertWindow::AlertIconType icon, int numButts,
|
||||
ModalComponentManager::Callback* cb, bool runModally)
|
||||
: title (t), message (m), iconType (icon), numButtons (numButts),
|
||||
returnValue (0), associatedComponent (component),
|
||||
callback (cb), modal (runModally)
|
||||
{
|
||||
}
|
||||
|
||||
String title, message, button1, button2, button3;
|
||||
|
||||
int invoke() const
|
||||
{
|
||||
MessageManager::getInstance()->callFunctionOnMessageThread (showCallback, (void*) this);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private:
|
||||
AlertWindow::AlertIconType iconType;
|
||||
int numButtons, returnValue;
|
||||
WeakReference<Component> associatedComponent;
|
||||
ModalComponentManager::Callback* callback;
|
||||
bool modal;
|
||||
|
||||
void show()
|
||||
{
|
||||
LookAndFeel& lf = associatedComponent != nullptr ? associatedComponent->getLookAndFeel()
|
||||
: LookAndFeel::getDefaultLookAndFeel();
|
||||
|
||||
ScopedPointer<Component> alertBox (lf.createAlertWindow (title, message, button1, button2, button3,
|
||||
iconType, numButtons, associatedComponent));
|
||||
|
||||
jassert (alertBox != nullptr); // you have to return one of these!
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
if (modal)
|
||||
{
|
||||
returnValue = alertBox->runModalLoop();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
(void) modal; // (to avoid an unused variable warning)
|
||||
|
||||
alertBox->enterModalState (true, callback, true);
|
||||
alertBox.release();
|
||||
}
|
||||
}
|
||||
|
||||
static void* showCallback (void* userData)
|
||||
{
|
||||
static_cast<AlertWindowInfo*> (userData)->show();
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
void AlertWindow::showMessageBox (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& buttonText,
|
||||
Component* associatedComponent)
|
||||
{
|
||||
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
|
||||
{
|
||||
NativeMessageBox::showMessageBox (iconType, title, message, associatedComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlertWindowInfo info (title, message, associatedComponent, iconType, 1, nullptr, true);
|
||||
info.button1 = buttonText.isEmpty() ? TRANS("OK") : buttonText;
|
||||
|
||||
info.invoke();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void AlertWindow::showMessageBoxAsync (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& buttonText,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
|
||||
{
|
||||
NativeMessageBox::showMessageBoxAsync (iconType, title, message, associatedComponent, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
AlertWindowInfo info (title, message, associatedComponent, iconType, 1, callback, false);
|
||||
info.button1 = buttonText.isEmpty() ? TRANS("OK") : buttonText;
|
||||
|
||||
info.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
bool AlertWindow::showOkCancelBox (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& button1Text,
|
||||
const String& button2Text,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
|
||||
return NativeMessageBox::showOkCancelBox (iconType, title, message, associatedComponent, callback);
|
||||
|
||||
AlertWindowInfo info (title, message, associatedComponent, iconType, 2, callback, callback == nullptr);
|
||||
info.button1 = button1Text.isEmpty() ? TRANS("OK") : button1Text;
|
||||
info.button2 = button2Text.isEmpty() ? TRANS("Cancel") : button2Text;
|
||||
|
||||
return info.invoke() != 0;
|
||||
}
|
||||
|
||||
int AlertWindow::showYesNoCancelBox (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& button1Text,
|
||||
const String& button2Text,
|
||||
const String& button3Text,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback)
|
||||
{
|
||||
if (LookAndFeel::getDefaultLookAndFeel().isUsingNativeAlertWindows())
|
||||
return NativeMessageBox::showYesNoCancelBox (iconType, title, message, associatedComponent, callback);
|
||||
|
||||
AlertWindowInfo info (title, message, associatedComponent, iconType, 3, callback, callback == nullptr);
|
||||
info.button1 = button1Text.isEmpty() ? TRANS("Yes") : button1Text;
|
||||
info.button2 = button2Text.isEmpty() ? TRANS("No") : button2Text;
|
||||
info.button3 = button3Text.isEmpty() ? TRANS("Cancel") : button3Text;
|
||||
|
||||
return info.invoke();
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
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
|
||||
|
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_ALERTWINDOW_H_INCLUDED
|
||||
#define JUCE_ALERTWINDOW_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** 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
|
||||
*/
|
||||
class JUCE_API AlertWindow : public TopLevelWindow,
|
||||
private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** The type of icon to show in the dialog box. */
|
||||
enum AlertIconType
|
||||
{
|
||||
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. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** 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,
|
||||
AlertIconType iconType,
|
||||
Component* associatedComponent = nullptr);
|
||||
|
||||
/** Destroys the AlertWindow */
|
||||
~AlertWindow();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the type of alert icon that was specified when the window
|
||||
was created. */
|
||||
AlertIconType 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::empty,
|
||||
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::empty);
|
||||
|
||||
/** 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 0
|
||||
@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 0
|
||||
@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;
|
||||
|
||||
//==============================================================================
|
||||
// easy-to-use message box functions:
|
||||
|
||||
#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 (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& buttonText = String::empty,
|
||||
Component* associatedComponent = nullptr);
|
||||
#endif
|
||||
|
||||
/** 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 (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
const String& buttonText = String::empty,
|
||||
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 (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
const String& button1Text = String::empty,
|
||||
const String& button2Text = String::empty,
|
||||
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 (AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
const String& button1Text = String::empty,
|
||||
const String& button2Text = String::empty,
|
||||
const String& button3Text = String::empty,
|
||||
Component* associatedComponent = nullptr,
|
||||
ModalComponentManager::Callback* callback = nullptr);
|
||||
#else
|
||||
const String& button1Text,
|
||||
const String& button2Text,
|
||||
const String& button3Text,
|
||||
Component* associatedComponent,
|
||||
ModalComponentManager::Callback* callback);
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** 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.
|
||||
*/
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
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() {}
|
||||
|
||||
virtual AlertWindow* createAlertWindow (const String& title, const String& message,
|
||||
const String& button1,
|
||||
const String& button2,
|
||||
const String& button3,
|
||||
AlertWindow::AlertIconType iconType,
|
||||
int numButtons,
|
||||
Component* associatedComponent) = 0;
|
||||
|
||||
virtual void drawAlertBox (Graphics&, AlertWindow&, const Rectangle<int>& textArea, TextLayout&) = 0;
|
||||
|
||||
virtual int getAlertBoxWindowFlags() = 0;
|
||||
|
||||
virtual int getAlertWindowButtonHeight() = 0;
|
||||
|
||||
virtual Font getAlertWindowMessageFont() = 0;
|
||||
virtual Font getAlertWindowFont() = 0;
|
||||
};
|
||||
|
||||
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 buttonClicked (Button*) override;
|
||||
/** @internal */
|
||||
void lookAndFeelChanged() override;
|
||||
/** @internal */
|
||||
void userTriedToCloseWindow() override;
|
||||
/** @internal */
|
||||
int getDesktopWindowStyleFlags() const override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String text;
|
||||
TextLayout textLayout;
|
||||
AlertIconType 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* associatedComponent;
|
||||
bool escapeKeyCancels;
|
||||
|
||||
void updateLayout (bool onlyIncreaseSize);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindow)
|
||||
};
|
||||
|
||||
#endif // JUCE_ALERTWINDOW_H_INCLUDED
|
||||
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
CallOutBox::CallOutBox (Component& c, const Rectangle<int>& area, Component* const parent)
|
||||
: arrowSize (16.0f), content (c), dismissalMouseClicksAreAlwaysConsumed (false)
|
||||
{
|
||||
addAndMakeVisible (content);
|
||||
|
||||
if (parent != nullptr)
|
||||
{
|
||||
parent->addChildComponent (this);
|
||||
updatePosition (area, parent->getLocalBounds());
|
||||
setVisible (true);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAlwaysOnTop (juce_areThereAnyAlwaysOnTopWindows());
|
||||
|
||||
updatePosition (area, Desktop::getInstance().getDisplays()
|
||||
.getDisplayContaining (area.getCentre()).userArea);
|
||||
|
||||
addToDesktop (ComponentPeer::windowIsTemporary);
|
||||
}
|
||||
}
|
||||
|
||||
CallOutBox::~CallOutBox()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class CallOutBoxCallback : public ModalComponentManager::Callback,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
CallOutBoxCallback (Component* c, const Rectangle<int>& area, Component* parent)
|
||||
: content (c), callout (*c, area, parent)
|
||||
{
|
||||
callout.setVisible (true);
|
||||
callout.enterModalState (true, this);
|
||||
startTimer (200);
|
||||
}
|
||||
|
||||
void modalStateFinished (int) override {}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (! Process::isForegroundProcess())
|
||||
callout.dismiss();
|
||||
}
|
||||
|
||||
ScopedPointer<Component> content;
|
||||
CallOutBox callout;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CallOutBoxCallback)
|
||||
};
|
||||
|
||||
CallOutBox& CallOutBox::launchAsynchronously (Component* content, const Rectangle<int>& area, Component* parent)
|
||||
{
|
||||
jassert (content != nullptr); // must be a valid content component!
|
||||
|
||||
return (new CallOutBoxCallback (content, area, parent))->callout;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CallOutBox::setArrowSize (const float newSize)
|
||||
{
|
||||
arrowSize = newSize;
|
||||
refreshPath();
|
||||
}
|
||||
|
||||
int CallOutBox::getBorderSize() const noexcept
|
||||
{
|
||||
return jmax (getLookAndFeel().getCallOutBoxBorderSize (*this), (int) arrowSize);
|
||||
}
|
||||
|
||||
void CallOutBox::paint (Graphics& g)
|
||||
{
|
||||
getLookAndFeel().drawCallOutBoxBackground (*this, g, outline, background);
|
||||
}
|
||||
|
||||
void CallOutBox::resized()
|
||||
{
|
||||
const int 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..
|
||||
dismiss();
|
||||
}
|
||||
else
|
||||
{
|
||||
exitModalState (0);
|
||||
setVisible (false);
|
||||
}
|
||||
}
|
||||
|
||||
void CallOutBox::setDismissalMouseClicksAreAlwaysConsumed (bool b) noexcept
|
||||
{
|
||||
dismissalMouseClicksAreAlwaysConsumed = b;
|
||||
}
|
||||
|
||||
enum { 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;
|
||||
|
||||
const int borderSpace = getBorderSize();
|
||||
|
||||
Rectangle<int> newBounds (content.getWidth() + borderSpace * 2,
|
||||
content.getHeight() + borderSpace * 2);
|
||||
|
||||
const int hw = newBounds.getWidth() / 2;
|
||||
const int hh = newBounds.getHeight() / 2;
|
||||
const float hwReduced = (float) (hw - borderSpace * 2);
|
||||
const float hhReduced = (float) (hh - borderSpace * 2);
|
||||
const float arrowIndent = borderSpace - arrowSize;
|
||||
|
||||
Point<float> targets[4] = { Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getBottom()),
|
||||
Point<float> ((float) targetArea.getRight(), (float) targetArea.getCentreY()),
|
||||
Point<float> ((float) targetArea.getX(), (float) targetArea.getCentreY()),
|
||||
Point<float> ((float) targetArea.getCentreX(), (float) targetArea.getY()) };
|
||||
|
||||
Line<float> lines[4] = { Line<float> (targets[0].translated (-hwReduced, hh - arrowIndent), targets[0].translated (hwReduced, hh - arrowIndent)),
|
||||
Line<float> (targets[1].translated (hw - arrowIndent, -hhReduced), targets[1].translated (hw - arrowIndent, hhReduced)),
|
||||
Line<float> (targets[2].translated (-(hw - arrowIndent), -hhReduced), targets[2].translated (-(hw - arrowIndent), hhReduced)),
|
||||
Line<float> (targets[3].translated (-hwReduced, -(hh - arrowIndent)), targets[3].translated (hwReduced, -(hh - arrowIndent))) };
|
||||
|
||||
const Rectangle<float> centrePointArea (newAreaToFitIn.reduced (hw, hh).toFloat());
|
||||
const Point<float> 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()));
|
||||
|
||||
const Point<float> centre (constrainedLine.findNearestPointTo (targetCentre));
|
||||
float 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 - hw),
|
||||
(int) (centre.y - hh));
|
||||
}
|
||||
}
|
||||
|
||||
setBounds (newBounds);
|
||||
}
|
||||
|
||||
void CallOutBox::refreshPath()
|
||||
{
|
||||
repaint();
|
||||
background = Image::null;
|
||||
outline.clear();
|
||||
|
||||
const float gap = 4.5f;
|
||||
|
||||
outline.addBubble (content.getBounds().toFloat().expanded (gap, gap),
|
||||
getLocalBounds().toFloat(),
|
||||
targetPoint - getPosition().toFloat(),
|
||||
9.0f, arrowSize * 0.7f);
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_CALLOUTBOX_H_INCLUDED
|
||||
#define JUCE_CALLOUTBOX_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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&)
|
||||
{
|
||||
FoobarContentComp* content = new FoobarContentComp();
|
||||
content->setSize (300, 300);
|
||||
|
||||
CallOutBox& myBox
|
||||
= CallOutBox::launchAsynchronously (content, getScreenBounds(), nullptr);
|
||||
}
|
||||
@endcode
|
||||
|
||||
The call-out will resize and position itself when the content changes size.
|
||||
*/
|
||||
class JUCE_API CallOutBox : public Component
|
||||
{
|
||||
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 non-zero, 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,
|
||||
const Rectangle<int>& areaToPointTo,
|
||||
Component* parentComponent);
|
||||
|
||||
/** Destructor. */
|
||||
~CallOutBox();
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the length 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).
|
||||
This component will be owned by the callout box and deleted
|
||||
later when the box is dismissed.
|
||||
@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 non-zero, this is the component to add the call-out to. If
|
||||
this is a nullptr, the call-out will be added to the desktop.
|
||||
*/
|
||||
static CallOutBox& launchAsynchronously (Component* contentComponent,
|
||||
const Rectangle<int>& areaToPointTo,
|
||||
Component* parentComponent);
|
||||
|
||||
/** Posts a message which will dismiss the callout box asynchronously.
|
||||
NB: it's safe to call this method from any thread.
|
||||
*/
|
||||
void dismiss();
|
||||
|
||||
/** Determines whether the mouse events for clicks outside the calloutbox are
|
||||
consumed, or allowed to arrive at the other component that they were aimed at.
|
||||
|
||||
By default this is false, so that when you click on something outside the calloutbox,
|
||||
that event will also be sent to the component that was clicked on. If you set it to
|
||||
true, then the first click will always just dismiss the box and not be sent to
|
||||
anything else.
|
||||
*/
|
||||
void setDismissalMouseClicksAreAlwaysConsumed (bool shouldAlwaysBeConsumed) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes. */
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() {}
|
||||
|
||||
virtual void drawCallOutBoxBackground (CallOutBox&, Graphics&, const Path&, Image& cachedImage) = 0;
|
||||
virtual int getCallOutBoxBorderSize (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;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
float arrowSize;
|
||||
Component& content;
|
||||
Path outline;
|
||||
Point<float> targetPoint;
|
||||
Rectangle<int> availableArea, targetArea;
|
||||
Image background;
|
||||
bool dismissalMouseClicksAreAlwaysConsumed;
|
||||
|
||||
void refreshPath();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallOutBox)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_CALLOUTBOX_H_INCLUDED
|
||||
|
|
@ -0,0 +1,591 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
static uint32 lastUniquePeerID = 1;
|
||||
|
||||
//==============================================================================
|
||||
ComponentPeer::ComponentPeer (Component& comp, const int flags)
|
||||
: component (comp),
|
||||
styleFlags (flags),
|
||||
constrainer (nullptr),
|
||||
lastDragAndDropCompUnderMouse (nullptr),
|
||||
uniqueID (lastUniquePeerID += 2), // increment by 2 so that this can never hit 0
|
||||
isWindowMinimised (false)
|
||||
{
|
||||
Desktop::getInstance().peers.add (this);
|
||||
}
|
||||
|
||||
ComponentPeer::~ComponentPeer()
|
||||
{
|
||||
Desktop& 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
|
||||
{
|
||||
const Array<ComponentPeer*>& peers = Desktop::getInstance().peers;
|
||||
|
||||
for (int i = peers.size(); --i >= 0;)
|
||||
{
|
||||
ComponentPeer* const peer = peers.getUnchecked(i);
|
||||
|
||||
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 (int touchIndex, Point<float> pos, ModifierKeys newMods, int64 time)
|
||||
{
|
||||
if (MouseInputSource* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (touchIndex))
|
||||
MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods);
|
||||
}
|
||||
|
||||
void ComponentPeer::handleMouseWheel (int touchIndex, Point<float> pos, int64 time, const MouseWheelDetails& wheel)
|
||||
{
|
||||
if (MouseInputSource* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (touchIndex))
|
||||
MouseInputSource (*mouse).handleWheel (*this, pos, time, wheel);
|
||||
}
|
||||
|
||||
void ComponentPeer::handleMagnifyGesture (int touchIndex, Point<float> pos, int64 time, float scaleFactor)
|
||||
{
|
||||
if (MouseInputSource* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (touchIndex))
|
||||
MouseInputSource (*mouse).handleMagnifyGesture (*this, pos, time, scaleFactor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::handlePaint (LowLevelGraphicsContext& contextToPaintTo)
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
Graphics g (contextToPaintTo);
|
||||
|
||||
if (component.isTransformed())
|
||||
g.addTransform (component.getTransform());
|
||||
|
||||
const Rectangle<int> peerBounds (getBounds());
|
||||
|
||||
if (peerBounds.getWidth() != component.getWidth() || peerBounds.getHeight() != component.getHeight())
|
||||
// Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size
|
||||
g.addTransform (AffineTransform::scale (peerBounds.getWidth() / (float) component.getWidth(),
|
||||
peerBounds.getHeight() / (float) component.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()
|
||||
{
|
||||
Component* c = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (c == nullptr)
|
||||
c = &component;
|
||||
|
||||
if (c->isCurrentlyBlockedByAnotherModalComponent())
|
||||
if (Component* const currentModalComp = Component::getCurrentlyModalComponent())
|
||||
c = currentModalComp;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleKeyPress (const int keyCode, const juce_wchar textCharacter)
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
bool keyWasUsed = false;
|
||||
|
||||
const KeyPress keyInfo (keyCode,
|
||||
ModifierKeys::getCurrentModifiers().withoutMouseButtons(),
|
||||
textCharacter);
|
||||
|
||||
for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
|
||||
{
|
||||
const WeakReference<Component> deletionChecker (target);
|
||||
|
||||
if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
|
||||
{
|
||||
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 (Component* const currentlyFocused = Component::getCurrentlyFocusedComponent())
|
||||
{
|
||||
const bool isTab = (keyInfo == KeyPress::tabKey);
|
||||
const bool isShiftTab = (keyInfo == KeyPress (KeyPress::tabKey, ModifierKeys::shiftModifier, 0));
|
||||
|
||||
if (isTab || isShiftTab)
|
||||
{
|
||||
currentlyFocused->moveKeyboardFocusToSibling (isTab);
|
||||
keyWasUsed = (currentlyFocused != Component::getCurrentlyFocusedComponent());
|
||||
|
||||
if (keyWasUsed || deletionChecker == nullptr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keyWasUsed;
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleKeyUpOrDown (const bool isKeyDown)
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
bool keyWasUsed = false;
|
||||
|
||||
for (Component* target = getTargetForKeyPress(); target != nullptr; target = target->getParentComponent())
|
||||
{
|
||||
const WeakReference<Component> deletionChecker (target);
|
||||
|
||||
keyWasUsed = target->keyStateChanged (isKeyDown);
|
||||
|
||||
if (keyWasUsed || deletionChecker == nullptr)
|
||||
break;
|
||||
|
||||
if (const Array <KeyListener*>* const keyListeners = target->keyListeners)
|
||||
{
|
||||
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()
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
Component* target = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
|
||||
|
||||
if (target == nullptr)
|
||||
target = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (target == nullptr)
|
||||
target = &component;
|
||||
|
||||
if (target != nullptr)
|
||||
target->internalModifierKeysChanged();
|
||||
}
|
||||
|
||||
TextInputTarget* ComponentPeer::findCurrentTextInputTarget()
|
||||
{
|
||||
Component* const c = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (component.isParentOf (c))
|
||||
if (TextInputTarget* const ti = dynamic_cast <TextInputTarget*> (c))
|
||||
if (ti->isTextInputActive())
|
||||
return ti;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ComponentPeer::dismissPendingTextInput() {}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::handleBroughtToFront()
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
component.internalBroughtToFront();
|
||||
}
|
||||
|
||||
void ComponentPeer::setConstrainer (ComponentBoundsConstrainer* const newConstrainer) noexcept
|
||||
{
|
||||
constrainer = newConstrainer;
|
||||
}
|
||||
|
||||
void ComponentPeer::handleMovedOrResized()
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
const bool nowMinimised = isMinimised();
|
||||
|
||||
if (component.flags.hasHeavyweightPeerFlag && ! nowMinimised)
|
||||
{
|
||||
const WeakReference<Component> deletionChecker (&component);
|
||||
|
||||
Rectangle<int> newBounds (Component::ComponentHelpers::rawPeerPositionToLocal (component, getBounds()));
|
||||
Rectangle<int> 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.bounds = newBounds;
|
||||
|
||||
if (wasResized)
|
||||
component.repaint();
|
||||
|
||||
component.sendMovedResizedMessages (wasMoved, wasResized);
|
||||
|
||||
if (deletionChecker == nullptr)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isWindowMinimised != nowMinimised)
|
||||
{
|
||||
isWindowMinimised = nowMinimised;
|
||||
component.minimisationStateChanged (nowMinimised);
|
||||
component.sendVisibilityChangeMessage();
|
||||
}
|
||||
|
||||
if (! isFullScreen())
|
||||
lastNonFullscreenBounds = component.getBounds();
|
||||
}
|
||||
|
||||
void ComponentPeer::handleFocusGain()
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
if (component.isParentOf (lastFocusedComponent))
|
||||
{
|
||||
Component::currentlyFocusedComponent = lastFocusedComponent;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
lastFocusedComponent->internalFocusGain (Component::focusChangedDirectly);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! component.isCurrentlyBlockedByAnotherModalComponent())
|
||||
component.grabKeyboardFocus();
|
||||
else
|
||||
ModalComponentManager::getInstance()->bringModalComponentsToFront();
|
||||
}
|
||||
}
|
||||
|
||||
void ComponentPeer::handleFocusLoss()
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
if (component.hasKeyboardFocus (true))
|
||||
{
|
||||
lastFocusedComponent = Component::currentlyFocusedComponent;
|
||||
|
||||
if (lastFocusedComponent != nullptr)
|
||||
{
|
||||
Component::currentlyFocusedComponent = nullptr;
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
lastFocusedComponent->internalFocusLoss (Component::focusChangedByMouseClick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component* ComponentPeer::getLastFocusedSubcomponent() const noexcept
|
||||
{
|
||||
return (component.isParentOf (lastFocusedComponent) && lastFocusedComponent->isShowing())
|
||||
? static_cast <Component*> (lastFocusedComponent)
|
||||
: &component;
|
||||
}
|
||||
|
||||
void ComponentPeer::handleScreenSizeChange()
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
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<int> ComponentPeer::getAreaCoveredBy (Component& subComponent) const
|
||||
{
|
||||
return ScalingHelpers::scaledScreenPosToUnscaled
|
||||
(component, component.getLocalArea (&subComponent, subComponent.getLocalBounds()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace DragHelpers
|
||||
{
|
||||
static bool isFileDrag (const ComponentPeer::DragInfo& info)
|
||||
{
|
||||
return info.files.size() > 0;
|
||||
}
|
||||
|
||||
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* const lastOne)
|
||||
{
|
||||
for (; c != nullptr; c = c->getParentComponent())
|
||||
if (isSuitableTarget (info, c) && (c == lastOne || isInterested (info, c)))
|
||||
return c;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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..
|
||||
class AsyncDropMessage : public CallbackMessage
|
||||
{
|
||||
public:
|
||||
AsyncDropMessage (Component* c, const ComponentPeer::DragInfo& d) : target (c), info (d) {}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (Component* const c = target.get())
|
||||
{
|
||||
if (isFileDrag (info))
|
||||
dynamic_cast <FileDragAndDropTarget*> (c)->filesDropped (info.files, info.position.x, info.position.y);
|
||||
else
|
||||
dynamic_cast <TextDragAndDropTarget*> (c)->textDropped (info.text, info.position.x, info.position.y);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WeakReference<Component> target;
|
||||
const ComponentPeer::DragInfo info;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (AsyncDropMessage)
|
||||
};
|
||||
}
|
||||
|
||||
bool ComponentPeer::handleDragMove (const ComponentPeer::DragInfo& info)
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
|
||||
Component* const compUnderMouse = component.getComponentAt (info.position);
|
||||
|
||||
Component* const lastTarget = dragAndDropTargetComponent;
|
||||
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;
|
||||
const Point<int> 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;
|
||||
|
||||
const Point<int> 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 (Component* const targetComp = dragAndDropTargetComponent)
|
||||
{
|
||||
dragAndDropTargetComponent = nullptr;
|
||||
lastDragAndDropCompUnderMouse = nullptr;
|
||||
|
||||
if (DragHelpers::isSuitableTarget (info, targetComp))
|
||||
{
|
||||
if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
targetComp->internalModalInputAttempt();
|
||||
|
||||
if (targetComp->isCurrentlyBlockedByAnotherModalComponent())
|
||||
return true;
|
||||
}
|
||||
|
||||
ComponentPeer::DragInfo info2 (info);
|
||||
info2.position = targetComp->getLocalPoint (&component, info.position);
|
||||
|
||||
(new DragHelpers::AsyncDropMessage (targetComp, info2))->post();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ComponentPeer::handleUserClosingWindow()
|
||||
{
|
||||
ModifierKeys::updateCurrentModifiers();
|
||||
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); (void) index; }
|
||||
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_COMPONENTPEER_H_INCLUDED
|
||||
#define JUCE_COMPONENTPEER_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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 */
|
||||
windowIsSemiTransparent = (1 << 31) /**< 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);
|
||||
|
||||
/** Returns the area in peer coordinates that is covered by the given sub-comp (which
|
||||
may be at any depth)
|
||||
*/
|
||||
Rectangle<int> getAreaCoveredBy (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 focus. */
|
||||
virtual void toFront (bool makeActive) = 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 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 (int touchIndex, Point<float> positionWithinPeer, ModifierKeys newMods, int64 time);
|
||||
void handleMouseWheel (int touchIndex, Point<float> positionWithinPeer, int64 time, const MouseWheelDetails&);
|
||||
void handleMagnifyGesture (int touchIndex, Point<float> positionWithinPeer, int64 time, float scaleFactor);
|
||||
|
||||
void handleUserClosingWindow();
|
||||
|
||||
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);
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
Component& component;
|
||||
const int styleFlags;
|
||||
Rectangle<int> lastNonFullscreenBounds;
|
||||
ComponentBoundsConstrainer* constrainer;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
WeakReference<Component> lastFocusedComponent, dragAndDropTargetComponent;
|
||||
Component* lastDragAndDropCompUnderMouse;
|
||||
const uint32 uniqueID;
|
||||
bool isWindowMinimised;
|
||||
Component* getTargetForKeyPress();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentPeer)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_COMPONENTPEER_H_INCLUDED
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
DialogWindow::DialogWindow (const String& name, Colour colour,
|
||||
const bool escapeCloses, const bool onDesktop)
|
||||
: DocumentWindow (name, colour, DocumentWindow::closeButton, onDesktop),
|
||||
escapeKeyTriggersCloseButton (escapeCloses)
|
||||
{
|
||||
}
|
||||
|
||||
DialogWindow::~DialogWindow()
|
||||
{
|
||||
}
|
||||
|
||||
bool DialogWindow::keyPressed (const KeyPress& key)
|
||||
{
|
||||
if (escapeKeyTriggersCloseButton && key == KeyPress::escapeKey)
|
||||
{
|
||||
setVisible (false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return DocumentWindow::keyPressed (key);
|
||||
}
|
||||
|
||||
void DialogWindow::resized()
|
||||
{
|
||||
DocumentWindow::resized();
|
||||
|
||||
if (escapeKeyTriggersCloseButton)
|
||||
{
|
||||
if (Button* const 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)
|
||||
{
|
||||
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
|
||||
: dialogBackgroundColour (Colours::lightgrey),
|
||||
componentToCentreAround (nullptr),
|
||||
escapeKeyTriggersCloseButton (true),
|
||||
useNativeTitleBar (true),
|
||||
resizable (true),
|
||||
useBottomRightCornerResizer (false)
|
||||
{
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
DialogWindow* const d = create();
|
||||
d->enterModalState (true, nullptr, true);
|
||||
return d;
|
||||
}
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN
|
||||
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
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DIALOGWINDOW_H_INCLUDED
|
||||
#define JUCE_DIALOGWINDOW_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
DialogWindow (const String& name,
|
||||
Colour backgroundColour,
|
||||
bool escapeKeyTriggersCloseButton,
|
||||
bool addToDesktop = true);
|
||||
|
||||
/** Destructor.
|
||||
If a content component has been set with setContentOwned(), it will be deleted.
|
||||
*/
|
||||
~DialogWindow();
|
||||
|
||||
//==============================================================================
|
||||
/** 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;
|
||||
|
||||
/** 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;
|
||||
|
||||
/** If true, then the escape key will trigger the dialog's close button. */
|
||||
bool escapeKeyTriggersCloseButton;
|
||||
/** If true, the dialog will use a native title bar. See TopLevelWindow::setUsingNativeTitleBar() */
|
||||
bool useNativeTitleBar;
|
||||
/** If true, the window will be resizable. See ResizableWindow::setResizable() */
|
||||
bool resizable;
|
||||
/** Indicates whether to use a border or corner resizer component. See ResizableWindow::setResizable() */
|
||||
bool useBottomRightCornerResizer;
|
||||
|
||||
/** 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
|
||||
Dialogwindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>();
|
||||
|
||||
if (dw != nullptr)
|
||||
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 || DOXYGEN
|
||||
/** 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
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** Easy way of quickly showing a dialog box containing a given component.
|
||||
|
||||
Note: this method has been superceded 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
|
||||
Dialogwindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>();
|
||||
|
||||
if (dw != nullptr)
|
||||
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 non-zero, 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 || DOXYGEN
|
||||
/** Easy way of quickly showing a dialog box containing a given component.
|
||||
|
||||
Note: this method has been superceded 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
|
||||
Dialogwindow* dw = contentComponent->findParentComponentOfClass<DialogWindow>();
|
||||
|
||||
if (dw != nullptr)
|
||||
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 non-zero, 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
|
||||
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void resized() override;
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&) override;
|
||||
|
||||
private:
|
||||
bool escapeKeyTriggersCloseButton;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DialogWindow)
|
||||
};
|
||||
|
||||
#endif // JUCE_DIALOGWINDOW_H_INCLUDED
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
class DocumentWindow::ButtonListenerProxy : public ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug)
|
||||
{
|
||||
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_),
|
||||
titleBarHeight (26),
|
||||
menuBarHeight (24),
|
||||
requiredButtons (requiredButtons_),
|
||||
#if JUCE_MAC
|
||||
positionTitleBarButtonsOnLeft (true),
|
||||
#else
|
||||
positionTitleBarButtonsOnLeft (false),
|
||||
#endif
|
||||
drawTitleTextCentred (true),
|
||||
menuBarModel (nullptr)
|
||||
{
|
||||
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) >= 0);
|
||||
jassert (titleBarButtons[0] == nullptr || getIndexOfChildComponent (titleBarButtons[0]) >= 0);
|
||||
jassert (titleBarButtons[1] == nullptr || getIndexOfChildComponent (titleBarButtons[1]) >= 0);
|
||||
jassert (titleBarButtons[2] == nullptr || getIndexOfChildComponent (titleBarButtons[2]) >= 0);
|
||||
|
||||
for (int i = numElementsInArray (titleBarButtons); --i >= 0;)
|
||||
titleBarButtons[i] = nullptr;
|
||||
|
||||
menuBar = nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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 = nullptr;
|
||||
|
||||
menuBarModel = newMenuBarModel;
|
||||
menuBarHeight = newMenuBarHeight > 0 ? newMenuBarHeight
|
||||
: getLookAndFeel().getDefaultMenuBarHeight();
|
||||
|
||||
if (menuBarModel != nullptr)
|
||||
setMenuBarComponent (new MenuBarComponent (menuBarModel));
|
||||
|
||||
resized();
|
||||
}
|
||||
}
|
||||
|
||||
Component* DocumentWindow::getMenuBarComponent() const noexcept
|
||||
{
|
||||
return menuBar;
|
||||
}
|
||||
|
||||
void DocumentWindow::setMenuBarComponent (Component* newMenuBarComponent)
|
||||
{
|
||||
// (call the Component method directly to avoid the assertion in ResizableWindow)
|
||||
Component::addAndMakeVisible (menuBar = newMenuBarComponent);
|
||||
|
||||
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);
|
||||
|
||||
if (resizableBorder == nullptr)
|
||||
{
|
||||
RectangleList<int> border (getLocalBounds());
|
||||
border.subtract (getBorderThickness().subtractedFrom (getLocalBounds()));
|
||||
|
||||
g.setColour (getBackgroundColour().overlaidWith (Colour (0x80000000)));
|
||||
g.fillRectList (border);
|
||||
}
|
||||
|
||||
const Rectangle<int> titleBarArea (getTitleBarArea());
|
||||
g.reduceClipRegion (titleBarArea);
|
||||
g.setOrigin (titleBarArea.getPosition());
|
||||
|
||||
int titleSpaceX1 = 6;
|
||||
int titleSpaceX2 = titleBarArea.getWidth() - 6;
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (Button* const b = titleBarButtons[i])
|
||||
{
|
||||
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 : 0,
|
||||
! drawTitleTextCentred);
|
||||
}
|
||||
|
||||
void DocumentWindow::resized()
|
||||
{
|
||||
ResizableWindow::resized();
|
||||
|
||||
if (Button* const b = getMaximiseButton())
|
||||
b->setToggleState (isFullScreen(), dontSendNotification);
|
||||
|
||||
const Rectangle<int> titleBarArea (getTitleBarArea());
|
||||
|
||||
getLookAndFeel()
|
||||
.positionDocumentWindowButtons (*this,
|
||||
titleBarArea.getX(), titleBarArea.getY(),
|
||||
titleBarArea.getWidth(), titleBarArea.getHeight(),
|
||||
titleBarButtons[0],
|
||||
titleBarButtons[1],
|
||||
titleBarButtons[2],
|
||||
positionTitleBarButtonsOnLeft);
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setBounds (titleBarArea.getX(), titleBarArea.getBottom(),
|
||||
titleBarArea.getWidth(), menuBarHeight);
|
||||
}
|
||||
|
||||
BorderSize<int> DocumentWindow::getBorderThickness()
|
||||
{
|
||||
return ResizableWindow::getBorderThickness();
|
||||
}
|
||||
|
||||
BorderSize<int> DocumentWindow::getContentComponentBorder()
|
||||
{
|
||||
BorderSize<int> 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()
|
||||
{
|
||||
const BorderSize<int> border (getBorderThickness());
|
||||
|
||||
if (isKioskMode())
|
||||
return Rectangle<int>();
|
||||
|
||||
return Rectangle<int> (border.getLeft(), border.getTop(),
|
||||
getWidth() - border.getLeftAndRight(), getTitleBarHeight());
|
||||
}
|
||||
|
||||
Button* DocumentWindow::getCloseButton() const noexcept { return titleBarButtons[2]; }
|
||||
Button* DocumentWindow::getMinimiseButton() const noexcept { return titleBarButtons[0]; }
|
||||
Button* DocumentWindow::getMaximiseButton() const noexcept { return titleBarButtons[1]; }
|
||||
|
||||
int DocumentWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
int 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 (int i = numElementsInArray (titleBarButtons); --i >= 0;)
|
||||
titleBarButtons[i] = nullptr;
|
||||
|
||||
if (! isUsingNativeTitleBar())
|
||||
{
|
||||
LookAndFeel& lf = getLookAndFeel();
|
||||
|
||||
if ((requiredButtons & minimiseButton) != 0) titleBarButtons[0] = lf.createDocumentWindowButton (minimiseButton);
|
||||
if ((requiredButtons & maximiseButton) != 0) titleBarButtons[1] = lf.createDocumentWindowButton (maximiseButton);
|
||||
if ((requiredButtons & closeButton) != 0) titleBarButtons[2] = lf.createDocumentWindowButton (closeButton);
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (Button* const b = titleBarButtons[i])
|
||||
{
|
||||
if (buttonListener == nullptr)
|
||||
buttonListener = new ButtonListenerProxy (*this);
|
||||
|
||||
b->addListener (buttonListener);
|
||||
b->setWantsKeyboardFocus (false);
|
||||
|
||||
// (call the Component method directly to avoid the assertion in ResizableWindow)
|
||||
Component::addAndMakeVisible (b);
|
||||
}
|
||||
}
|
||||
|
||||
if (Button* const 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();
|
||||
|
||||
for (int i = numElementsInArray (titleBarButtons); --i >= 0;)
|
||||
if (Button* const b = titleBarButtons[i])
|
||||
b->setEnabled (isActiveWindow());
|
||||
|
||||
if (menuBar != nullptr)
|
||||
menuBar->setEnabled (isActiveWindow());
|
||||
}
|
||||
|
||||
void DocumentWindow::mouseDoubleClick (const MouseEvent& e)
|
||||
{
|
||||
Button* const maximise = getMaximiseButton();
|
||||
|
||||
if (maximise != nullptr && getTitleBarArea().contains (e.x, e.y))
|
||||
maximise->triggerClick();
|
||||
}
|
||||
|
||||
void DocumentWindow::userTriedToCloseWindow()
|
||||
{
|
||||
closeButtonPressed();
|
||||
}
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_DOCUMENTWINDOW_H_INCLUDED
|
||||
#define JUCE_DOCUMENTWINDOW_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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);
|
||||
|
||||
/** 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 0 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 zero, any existing menu bar
|
||||
will be removed from the component; if non-zero, 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() {}
|
||||
|
||||
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, menuBarHeight, requiredButtons;
|
||||
bool positionTitleBarButtonsOnLeft, drawTitleTextCentred;
|
||||
ScopedPointer <Button> titleBarButtons [3];
|
||||
Image titleBarIcon;
|
||||
ScopedPointer <Component> menuBar;
|
||||
MenuBarModel* menuBarModel;
|
||||
|
||||
class ButtonListenerProxy;
|
||||
friend struct ContainerDeletePolicy<ButtonListenerProxy>;
|
||||
ScopedPointer<ButtonListenerProxy> buttonListener;
|
||||
|
||||
void repaintTitleBar();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DocumentWindow)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_DOCUMENTWINDOW_H_INCLUDED
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_NATIVEMESSAGEBOX_H_INCLUDED
|
||||
#define JUCE_NATIVEMESSAGEBOX_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
This class contains some static methods for showing native alert windows.
|
||||
*/
|
||||
class NativeMessageBox
|
||||
{
|
||||
public:
|
||||
/** 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.
|
||||
*/
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
static void JUCE_CALLTYPE showMessageBox (AlertWindow::AlertIconType iconType,
|
||||
const String& title,
|
||||
const String& message,
|
||||
Component* associatedComponent = nullptr);
|
||||
#endif
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
static void JUCE_CALLTYPE showMessageBoxAsync (AlertWindow::AlertIconType 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, 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.
|
||||
@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 (AlertWindow::AlertIconType 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, 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.
|
||||
|
||||
@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
|
||||
*/
|
||||
static int JUCE_CALLTYPE showYesNoCancelBox (AlertWindow::AlertIconType 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() JUCE_DELETED_FUNCTION;
|
||||
JUCE_DECLARE_NON_COPYABLE (NativeMessageBox)
|
||||
};
|
||||
|
||||
#endif // JUCE_NATIVEMESSAGEBOX_H_INCLUDED
|
||||
|
|
@ -0,0 +1,617 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
ResizableWindow::ResizableWindow (const String& name,
|
||||
const bool addToDesktop_)
|
||||
: TopLevelWindow (name, addToDesktop_),
|
||||
ownsContentComponent (false),
|
||||
resizeToFitContent (false),
|
||||
fullscreen (false),
|
||||
constrainer (nullptr)
|
||||
#if JUCE_DEBUG
|
||||
, hasBeenResized (false)
|
||||
#endif
|
||||
{
|
||||
initialise (addToDesktop_);
|
||||
}
|
||||
|
||||
ResizableWindow::ResizableWindow (const String& name,
|
||||
Colour backgroundColour_,
|
||||
const bool addToDesktop_)
|
||||
: TopLevelWindow (name, addToDesktop_),
|
||||
ownsContentComponent (false),
|
||||
resizeToFitContent (false),
|
||||
fullscreen (false),
|
||||
constrainer (nullptr)
|
||||
#if JUCE_DEBUG
|
||||
, hasBeenResized (false)
|
||||
#endif
|
||||
{
|
||||
setBackgroundColour (backgroundColour_);
|
||||
|
||||
initialise (addToDesktop_);
|
||||
}
|
||||
|
||||
ResizableWindow::~ResizableWindow()
|
||||
{
|
||||
// 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) >= 0);
|
||||
jassert (resizableBorder == nullptr || getIndexOfChildComponent (resizableBorder) >= 0);
|
||||
|
||||
resizableCorner = nullptr;
|
||||
resizableBorder = nullptr;
|
||||
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)
|
||||
{
|
||||
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,
|
||||
const bool takeOwnership,
|
||||
const 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..
|
||||
|
||||
const BorderSize<int> border (getContentComponentBorder());
|
||||
|
||||
setSize (width + border.getLeftAndRight(),
|
||||
height + border.getTopAndBottom());
|
||||
}
|
||||
|
||||
BorderSize<int> ResizableWindow::getBorderThickness()
|
||||
{
|
||||
if (isUsingNativeTitleBar() || isKioskMode())
|
||||
return BorderSize<int>();
|
||||
|
||||
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);
|
||||
|
||||
const BorderSize<int> borders (getContentComponentBorder());
|
||||
|
||||
setSize (child->getWidth() + borders.getLeftAndRight(),
|
||||
child->getHeight() + borders.getTopAndBottom());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void ResizableWindow::activeWindowStatusChanged()
|
||||
{
|
||||
const BorderSize<int> border (getContentComponentBorder());
|
||||
|
||||
Rectangle<int> 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 = nullptr;
|
||||
|
||||
if (resizableCorner == nullptr)
|
||||
{
|
||||
Component::addChildComponent (resizableCorner = new ResizableCornerComponent (this, constrainer));
|
||||
resizableCorner->setAlwaysOnTop (true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resizableCorner = nullptr;
|
||||
|
||||
if (resizableBorder == nullptr)
|
||||
Component::addChildComponent (resizableBorder = new ResizableBorderComponent (this, constrainer));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resizableCorner = nullptr;
|
||||
resizableBorder = nullptr;
|
||||
}
|
||||
|
||||
if (isUsingNativeTitleBar())
|
||||
recreateDesktopWindow();
|
||||
|
||||
childBoundsChanged (contentComponent);
|
||||
resized();
|
||||
}
|
||||
|
||||
bool ResizableWindow::isResizable() const noexcept
|
||||
{
|
||||
return resizableCorner != nullptr
|
||||
|| resizableBorder != nullptr;
|
||||
}
|
||||
|
||||
void ResizableWindow::setResizeLimits (const int newMinimumWidth,
|
||||
const int newMinimumHeight,
|
||||
const int newMaximumWidth,
|
||||
const 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::setConstrainer (ComponentBoundsConstrainer* newConstrainer)
|
||||
{
|
||||
if (constrainer != newConstrainer)
|
||||
{
|
||||
constrainer = newConstrainer;
|
||||
|
||||
const bool useBottomRightCornerResizer = resizableCorner != nullptr;
|
||||
const bool shouldBeResizable = useBottomRightCornerResizer || resizableBorder != nullptr;
|
||||
|
||||
resizableCorner = nullptr;
|
||||
resizableBorder = nullptr;
|
||||
|
||||
setResizable (shouldBeResizable, useBottomRightCornerResizer);
|
||||
|
||||
if (ComponentPeer* const peer = getPeer())
|
||||
peer->setConstrainer (newConstrainer);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
LookAndFeel& 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());
|
||||
|
||||
if (ComponentPeer* const peer = getPeer())
|
||||
peer->setConstrainer (constrainer);
|
||||
}
|
||||
}
|
||||
|
||||
Colour ResizableWindow::getBackgroundColour() const noexcept
|
||||
{
|
||||
return findColour (backgroundColourId, false);
|
||||
}
|
||||
|
||||
void ResizableWindow::setBackgroundColour (Colour newColour)
|
||||
{
|
||||
Colour backgroundColour (newColour);
|
||||
|
||||
if (! Desktop::canUseSemiTransparentWindows())
|
||||
backgroundColour = newColour.withAlpha (1.0f);
|
||||
|
||||
setColour (backgroundColourId, backgroundColour);
|
||||
|
||||
setOpaque (backgroundColour.isOpaque());
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ResizableWindow::isFullScreen() const
|
||||
{
|
||||
if (isOnDesktop())
|
||||
{
|
||||
ComponentPeer* const peer = getPeer();
|
||||
return peer != nullptr && peer->isFullScreen();
|
||||
}
|
||||
|
||||
return fullscreen;
|
||||
}
|
||||
|
||||
void ResizableWindow::setFullScreen (const bool shouldBeFullScreen)
|
||||
{
|
||||
if (shouldBeFullScreen != isFullScreen())
|
||||
{
|
||||
updateLastPosIfShowing();
|
||||
fullscreen = shouldBeFullScreen;
|
||||
|
||||
if (isOnDesktop())
|
||||
{
|
||||
if (ComponentPeer* const peer = getPeer())
|
||||
{
|
||||
// keep a copy of this intact in case the real one gets messed-up while we're un-maximising
|
||||
const Rectangle<int> 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 (ComponentPeer* const peer = getPeer())
|
||||
return peer->isMinimised();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResizableWindow::setMinimised (const bool shouldMinimise)
|
||||
{
|
||||
if (shouldMinimise != isMinimised())
|
||||
{
|
||||
if (ComponentPeer* const peer = getPeer())
|
||||
{
|
||||
updateLastPosIfShowing();
|
||||
peer->setMinimised (shouldMinimise);
|
||||
}
|
||||
else
|
||||
{
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ResizableWindow::isKioskMode() const
|
||||
{
|
||||
if (isOnDesktop())
|
||||
if (ComponentPeer* peer = getPeer())
|
||||
return peer->isKioskMode();
|
||||
|
||||
return Desktop::getInstance().getKioskModeComponent() == this;
|
||||
}
|
||||
|
||||
void ResizableWindow::updateLastPosIfShowing()
|
||||
{
|
||||
if (isShowing())
|
||||
updateLastPosIfNotFullScreen();
|
||||
}
|
||||
|
||||
void ResizableWindow::updateLastPosIfNotFullScreen()
|
||||
{
|
||||
if (! (isFullScreen() || isMinimised() || isKioskMode()))
|
||||
lastNonFullScreenPos = getBounds();
|
||||
}
|
||||
|
||||
void ResizableWindow::parentSizeChanged()
|
||||
{
|
||||
if (isFullScreen() && getParentComponent() != nullptr)
|
||||
setBounds (getParentComponent()->getLocalBounds());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String ResizableWindow::getWindowStateAsString()
|
||||
{
|
||||
updateLastPosIfShowing();
|
||||
return (isFullScreen() && ! isKioskMode() ? "fs " : "") + lastNonFullScreenPos.toString();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ComponentPeer* const peer = isOnDesktop() ? getPeer() : nullptr;
|
||||
if (peer != nullptr)
|
||||
peer->getFrameSize().addTo (newPos);
|
||||
|
||||
{
|
||||
Desktop& desktop = Desktop::getInstance();
|
||||
RectangleList<int> allMonitors (desktop.getDisplays().getRectangleList (true));
|
||||
allMonitors.clipTo (newPos);
|
||||
const Rectangle<int> onScreenArea (allMonitors.getBounds());
|
||||
|
||||
if (onScreenArea.getWidth() * onScreenArea.getHeight() < 32 * 32)
|
||||
{
|
||||
const Rectangle<int> screen (desktop.getDisplays().getDisplayContaining (newPos.getCentre()).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();
|
||||
setFullScreen (fs);
|
||||
|
||||
if (! fs)
|
||||
setBoundsConstrained (newPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ResizableWindow::mouseDown (const MouseEvent& e)
|
||||
{
|
||||
if (! isFullScreen())
|
||||
dragger.startDraggingComponent (this, e);
|
||||
}
|
||||
|
||||
void ResizableWindow::mouseDrag (const MouseEvent& e)
|
||||
{
|
||||
if (! isFullScreen())
|
||||
dragger.dragComponent (this, e, constrainer);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#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
|
||||
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_RESIZABLEWINDOW_H_INCLUDED
|
||||
#define JUCE_RESIZABLEWINDOW_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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;
|
||||
|
||||
/** 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 responsiblity to manage it.
|
||||
|
||||
If you pass a nullptr, then no contraints 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>& bounds);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** 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, positon 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. */
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Deprecated: use setContentOwned() and setContentNonOwned() instead.
|
||||
JUCE_DEPRECATED (void setContentComponent (Component* newContentComponent,
|
||||
bool deleteOldOne = true,
|
||||
bool resizeToFit = false));
|
||||
using TopLevelWindow::addToDesktop;
|
||||
|
||||
//==============================================================================
|
||||
/** This abstract base class is implemented by LookAndFeel classes to provide
|
||||
window drawing functionality.
|
||||
*/
|
||||
struct JUCE_API LookAndFeelMethods
|
||||
{
|
||||
virtual ~LookAndFeelMethods() {}
|
||||
|
||||
//==============================================================================
|
||||
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 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
|
||||
|
||||
ScopedPointer <ResizableCornerComponent> resizableCorner;
|
||||
ScopedPointer <ResizableBorderComponent> resizableBorder;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
Component::SafePointer<Component> contentComponent;
|
||||
bool ownsContentComponent, resizeToFitContent, fullscreen;
|
||||
ComponentDragger dragger;
|
||||
Rectangle<int> lastNonFullScreenPos;
|
||||
ComponentBoundsConstrainer defaultConstrainer;
|
||||
ComponentBoundsConstrainer* constrainer;
|
||||
#if JUCE_DEBUG
|
||||
bool hasBeenResized;
|
||||
#endif
|
||||
|
||||
void initialise (bool addToDesktop);
|
||||
void updateLastPosIfNotFullScreen();
|
||||
void updateLastPosIfShowing();
|
||||
void setContent (Component*, bool takeOwnership, bool resizeToFit);
|
||||
|
||||
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
|
||||
// The parameters for these methods have changed - please update your code!
|
||||
JUCE_DEPRECATED (void getBorderThickness (int& left, int& top, int& right, int& bottom));
|
||||
JUCE_DEPRECATED (void getContentComponentBorder (int& left, int& top, int& right, int& bottom));
|
||||
#endif
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableWindow)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_RESIZABLEWINDOW_H_INCLUDED
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
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 = LookAndFeel::getDefaultLookAndFeel()
|
||||
.createAlertWindow (title, String(),
|
||||
cancelButtonText.isEmpty() ? TRANS("Cancel")
|
||||
: cancelButtonText,
|
||||
String(), String(),
|
||||
AlertWindow::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)
|
||||
{
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
|
||||
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()))
|
||||
{
|
||||
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
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_THREADWITHPROGRESSWINDOW_H_INCLUDED
|
||||
#define JUCE_THREADWITHPROGRESSWINDOW_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
#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 threadPriority 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 threadPriority = 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 threadPriority the priority to use when starting the thread - see
|
||||
Thread::startThread() for values
|
||||
*/
|
||||
void launchThread (int threadPriority = 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; }
|
||||
|
||||
//==============================================================================
|
||||
/** 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;
|
||||
ScopedPointer<AlertWindow> alertWindow;
|
||||
String message;
|
||||
CriticalSection messageLock;
|
||||
const int timeOutMsWhenCancelling;
|
||||
bool wasCancelledByUser;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadWithProgressWindow)
|
||||
};
|
||||
|
||||
#endif // JUCE_THREADWITHPROGRESSWINDOW_H_INCLUDED
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
TooltipWindow::TooltipWindow (Component* const parentComp, const int delayMs)
|
||||
: Component ("tooltip"),
|
||||
millisecondsBeforeTipAppears (delayMs),
|
||||
mouseClicks (0),
|
||||
mouseWheelMoves (0),
|
||||
lastHideTime (0),
|
||||
lastComponentUnderMouse (nullptr)
|
||||
{
|
||||
if (Desktop::getInstance().getMainMouseSource().canHover())
|
||||
startTimer (123);
|
||||
|
||||
setAlwaysOnTop (true);
|
||||
setOpaque (true);
|
||||
|
||||
if (parentComp != nullptr)
|
||||
parentComp->addChildComponent (this);
|
||||
}
|
||||
|
||||
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, const Rectangle<int>& parentArea)
|
||||
{
|
||||
int w, h;
|
||||
getLookAndFeel().getTooltipSize (tip, w, h);
|
||||
|
||||
setBounds (Rectangle<int> (pos.x > parentArea.getCentreX() ? pos.x - (w + 12) : pos.x + 24,
|
||||
pos.y > parentArea.getCentreY() ? pos.y - (h + 6) : pos.y + 6,
|
||||
w, h)
|
||||
.constrainedWithin (parentArea));
|
||||
|
||||
setVisible (true);
|
||||
}
|
||||
|
||||
void TooltipWindow::displayTip (Point<int> screenPos, const String& tip)
|
||||
{
|
||||
jassert (tip.isNotEmpty());
|
||||
if (tipShowing != tip)
|
||||
repaint();
|
||||
|
||||
tipShowing = tip;
|
||||
|
||||
if (Component* const parent = getParentComponent())
|
||||
{
|
||||
updatePosition (tip, parent->getLocalPoint (nullptr, screenPos),
|
||||
parent->getLocalBounds());
|
||||
}
|
||||
else
|
||||
{
|
||||
updatePosition (tip, screenPos, Desktop::getInstance().getDisplays()
|
||||
.getDisplayContaining (screenPos).userArea);
|
||||
|
||||
addToDesktop (ComponentPeer::windowHasDropShadow
|
||||
| ComponentPeer::windowIsTemporary
|
||||
| ComponentPeer::windowIgnoresKeyPresses);
|
||||
}
|
||||
|
||||
toFront (false);
|
||||
}
|
||||
|
||||
String TooltipWindow::getTipFor (Component* const c)
|
||||
{
|
||||
if (c != nullptr
|
||||
&& Process::isForegroundProcess()
|
||||
&& ! ModifierKeys::getCurrentModifiers().isAnyMouseButtonDown())
|
||||
{
|
||||
if (TooltipClient* const ttc = dynamic_cast <TooltipClient*> (c))
|
||||
if (! c->isCurrentlyBlockedByAnotherModalComponent())
|
||||
return ttc->getTooltip();
|
||||
}
|
||||
|
||||
return String::empty;
|
||||
}
|
||||
|
||||
void TooltipWindow::hideTip()
|
||||
{
|
||||
tipShowing.clear();
|
||||
removeFromDesktop();
|
||||
setVisible (false);
|
||||
}
|
||||
|
||||
void TooltipWindow::timerCallback()
|
||||
{
|
||||
Desktop& desktop = Desktop::getInstance();
|
||||
const MouseInputSource mouseSource (desktop.getMainMouseSource());
|
||||
const unsigned int now = Time::getApproximateMillisecondCounter();
|
||||
|
||||
Component* const newComp = mouseSource.isMouse() ? mouseSource.getComponentUnderMouse() : nullptr;
|
||||
const String newTip (getTipFor (newComp));
|
||||
const bool tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse);
|
||||
lastComponentUnderMouse = newComp;
|
||||
lastTipUnderMouse = newTip;
|
||||
|
||||
const int clickCount = desktop.getMouseButtonClickCounter();
|
||||
const int wheelCount = desktop.getMouseWheelMoveCounter();
|
||||
const bool mouseWasClicked = (clickCount > mouseClicks || wheelCount > mouseWheelMoves);
|
||||
mouseClicks = clickCount;
|
||||
mouseWheelMoves = wheelCount;
|
||||
|
||||
const Point<float> mousePos (mouseSource.getScreenPosition());
|
||||
const bool mouseMovedQuickly = mousePos.getDistanceFrom (lastMousePos) > 12;
|
||||
lastMousePos = mousePos;
|
||||
|
||||
if (tipChanged || mouseWasClicked || mouseMovedQuickly)
|
||||
lastCompChangeTime = now;
|
||||
|
||||
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)
|
||||
{
|
||||
displayTip (mousePos.roundToInt(), newTip);
|
||||
}
|
||||
}
|
||||
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 + (unsigned int) millisecondsBeforeTipAppears)
|
||||
{
|
||||
displayTip (mousePos.roundToInt(), newTip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_TOOLTIPWINDOW_H_INCLUDED
|
||||
#define JUCE_TOOLTIPWINDOW_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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, you'll
|
||||
end up with multiple tooltips being shown!
|
||||
|
||||
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
|
||||
*/
|
||||
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 0, 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::getTooltipSize
|
||||
*/
|
||||
explicit TooltipWindow (Component* parentComponent = nullptr,
|
||||
int millisecondsBeforeTipAppears = 700);
|
||||
|
||||
/** Destructor. */
|
||||
~TooltipWindow();
|
||||
|
||||
//==============================================================================
|
||||
/** 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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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() {}
|
||||
|
||||
virtual void getTooltipSize (const String& tipText, int& width, int& height) = 0;
|
||||
virtual void drawTooltip (Graphics&, const String& text, int width, int height) = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
int millisecondsBeforeTipAppears;
|
||||
Point<float> lastMousePos;
|
||||
int mouseClicks, mouseWheelMoves;
|
||||
unsigned int lastCompChangeTime, lastHideTime;
|
||||
Component* lastComponentUnderMouse;
|
||||
String tipShowing, lastTipUnderMouse;
|
||||
|
||||
void paint (Graphics&) override;
|
||||
void mouseEnter (const MouseEvent&) override;
|
||||
void timerCallback() override;
|
||||
void updatePosition (const String&, Point<int>, const Rectangle<int>&);
|
||||
|
||||
static String getTipFor (Component*);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TooltipWindow)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_TOOLTIPWINDOW_H_INCLUDED
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/** Keeps track of the active top level window. */
|
||||
class TopLevelWindowManager : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
TopLevelWindowManager() : currentActive (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~TopLevelWindowManager()
|
||||
{
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (TopLevelWindowManager);
|
||||
|
||||
void checkFocusAsync()
|
||||
{
|
||||
startTimer (10);
|
||||
}
|
||||
|
||||
void checkFocus()
|
||||
{
|
||||
startTimer (jmin (1731, getTimerInterval() * 2));
|
||||
|
||||
TopLevelWindow* newActive = findCurrentlyActiveWindow();
|
||||
|
||||
if (newActive != currentActive)
|
||||
{
|
||||
currentActive = newActive;
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
if (TopLevelWindow* 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.size() == 0)
|
||||
deleteInstance();
|
||||
}
|
||||
|
||||
Array<TopLevelWindow*> windows;
|
||||
|
||||
private:
|
||||
TopLevelWindow* currentActive;
|
||||
|
||||
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())
|
||||
{
|
||||
Component* const focusedComp = Component::getCurrentlyFocusedComponent();
|
||||
TopLevelWindow* 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_ImplementSingleton_SingleThreaded (TopLevelWindowManager)
|
||||
|
||||
void juce_checkCurrentlyFocusedTopLevelWindow();
|
||||
void juce_checkCurrentlyFocusedTopLevelWindow()
|
||||
{
|
||||
if (TopLevelWindowManager* const wm = TopLevelWindowManager::getInstanceWithoutCreating())
|
||||
wm->checkFocusAsync();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
TopLevelWindow::TopLevelWindow (const String& name, const bool shouldAddToDesktop)
|
||||
: Component (name),
|
||||
useDropShadow (true),
|
||||
useNativeTitleBar (false),
|
||||
isCurrentlyActive (false)
|
||||
{
|
||||
setOpaque (true);
|
||||
|
||||
if (shouldAddToDesktop)
|
||||
Component::addToDesktop (TopLevelWindow::getDesktopWindowStyleFlags());
|
||||
else
|
||||
setDropShadowEnabled (true);
|
||||
|
||||
setWantsKeyboardFocus (true);
|
||||
setBroughtToFrontOnMouseClick (true);
|
||||
isCurrentlyActive = TopLevelWindowManager::getInstance()->addWindow (this);
|
||||
}
|
||||
|
||||
TopLevelWindow::~TopLevelWindow()
|
||||
{
|
||||
shadower = nullptr;
|
||||
TopLevelWindowManager::getInstance()->removeWindow (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void TopLevelWindow::focusOfChildComponentChanged (FocusChangeType)
|
||||
{
|
||||
TopLevelWindowManager* const 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 (ComponentPeer* p = getPeer())
|
||||
if ((p->getStyleFlags() & (ComponentPeer::windowIsTemporary
|
||||
| ComponentPeer::windowIgnoresKeyPresses)) == 0)
|
||||
toFront (true);
|
||||
}
|
||||
|
||||
void TopLevelWindow::parentHierarchyChanged()
|
||||
{
|
||||
setDropShadowEnabled (useDropShadow);
|
||||
}
|
||||
|
||||
int TopLevelWindow::getDesktopWindowStyleFlags() const
|
||||
{
|
||||
int styleFlags = ComponentPeer::windowAppearsOnTaskbar;
|
||||
|
||||
if (useDropShadow) styleFlags |= ComponentPeer::windowHasDropShadow;
|
||||
if (useNativeTitleBar) styleFlags |= ComponentPeer::windowHasTitleBar;
|
||||
|
||||
return styleFlags;
|
||||
}
|
||||
|
||||
void TopLevelWindow::setDropShadowEnabled (const bool useShadow)
|
||||
{
|
||||
useDropShadow = useShadow;
|
||||
|
||||
if (isOnDesktop())
|
||||
{
|
||||
shadower = nullptr;
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (useShadow && isOpaque())
|
||||
{
|
||||
if (shadower == nullptr)
|
||||
{
|
||||
shadower = getLookAndFeel().createDropShadowerForComponent (this);
|
||||
|
||||
if (shadower != nullptr)
|
||||
shadower->setOwner (this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shadower = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::setUsingNativeTitleBar (const bool shouldUseNativeTitleBar)
|
||||
{
|
||||
if (useNativeTitleBar != shouldUseNativeTitleBar)
|
||||
{
|
||||
FocusRestorer focusRestorer;
|
||||
useNativeTitleBar = shouldUseNativeTitleBar;
|
||||
recreateDesktopWindow();
|
||||
sendLookAndFeelChange();
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::recreateDesktopWindow()
|
||||
{
|
||||
if (isOnDesktop())
|
||||
{
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
toFront (true);
|
||||
}
|
||||
}
|
||||
|
||||
void TopLevelWindow::addToDesktop()
|
||||
{
|
||||
shadower = nullptr;
|
||||
Component::addToDesktop (getDesktopWindowStyleFlags());
|
||||
setDropShadowEnabled (isDropShadowEnabled()); // force an update to clear away any fake shadows if necessary.
|
||||
}
|
||||
|
||||
void TopLevelWindow::addToDesktop (int windowStyleFlags, void* nativeWindowToAttachTo)
|
||||
{
|
||||
/* It's not recommended to change the desktop window flags directly for a TopLevelWindow,
|
||||
because this class needs to make sure its layout corresponds with settings like whether
|
||||
it's got a native title bar or not.
|
||||
|
||||
If you need custom flags for your window, you can override the getDesktopWindowStyleFlags()
|
||||
method. If you do this, it's best to call the base class's getDesktopWindowStyleFlags()
|
||||
method, then add or remove whatever flags are necessary from this value before returning it.
|
||||
*/
|
||||
|
||||
jassert ((windowStyleFlags & ~ComponentPeer::windowIsSemiTransparent)
|
||||
== (getDesktopWindowStyleFlags() & ~ComponentPeer::windowIsSemiTransparent));
|
||||
|
||||
Component::addToDesktop (windowStyleFlags, nativeWindowToAttachTo);
|
||||
|
||||
if (windowStyleFlags != getDesktopWindowStyleFlags())
|
||||
sendLookAndFeelChange();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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
|
||||
{
|
||||
Point<int> targetCentre (c->localPointToGlobal (c->getLocalBounds().getCentre()));
|
||||
Rectangle<int> parentArea (c->getParentMonitorArea());
|
||||
|
||||
if (Component* const 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;)
|
||||
{
|
||||
TopLevelWindow* const tlw = TopLevelWindow::getTopLevelWindow (i);
|
||||
|
||||
if (tlw->isActiveWindow())
|
||||
{
|
||||
int numTWLParents = 0;
|
||||
|
||||
for (const Component* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2013 - Raw Material Software Ltd.
|
||||
|
||||
Permission is granted to use this software under the terms of either:
|
||||
a) the GPL v2 (or any later version)
|
||||
b) the Affero GPL v3
|
||||
|
||||
Details of these licenses can be found at: www.gnu.org/licenses
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.juce.com for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#ifndef JUCE_TOPLEVELWINDOW_H_INCLUDED
|
||||
#define JUCE_TOPLEVELWINDOW_H_INCLUDED
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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();
|
||||
|
||||
//==============================================================================
|
||||
/** 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 relevent parent window, which
|
||||
is a bit neater than just having it appear in the middle of the screen.
|
||||
|
||||
If componentToCentreAround is 0, 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, useNativeTitleBar, isCurrentlyActive;
|
||||
ScopedPointer<DropShadower> shadower;
|
||||
|
||||
void setWindowActive (bool);
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TopLevelWindow)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_TOPLEVELWINDOW_H_INCLUDED
|
||||
Loading…
Add table
Add a link
Reference in a new issue