git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
90
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h
vendored
Normal file
90
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/** A type used to hold the unique ID for an application command.
|
||||
|
||||
This is a numeric type, so it can be stored as an integer.
|
||||
|
||||
@see ApplicationCommandInfo, ApplicationCommandManager,
|
||||
ApplicationCommandTarget, KeyPressMappingSet
|
||||
*/
|
||||
using CommandID = int;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** A set of general-purpose application command IDs.
|
||||
|
||||
Because these commands are likely to be used in most apps, they're defined
|
||||
here to help different apps to use the same numeric values for them.
|
||||
|
||||
Of course you don't have to use these, but some of them are used internally by
|
||||
JUCE - e.g. the quit ID is recognised as a command by the JUCEApplication class.
|
||||
|
||||
@see ApplicationCommandInfo, ApplicationCommandManager,
|
||||
ApplicationCommandTarget, KeyPressMappingSet
|
||||
*/
|
||||
namespace StandardApplicationCommandIDs
|
||||
{
|
||||
enum
|
||||
{
|
||||
/** This command ID should be used to send a "Quit the App" command.
|
||||
|
||||
This command is recognised by the JUCEApplication class, so if it is invoked
|
||||
and no other ApplicationCommandTarget handles the event first, the JUCEApplication
|
||||
object will catch it and call JUCEApplicationBase::systemRequestedQuit().
|
||||
*/
|
||||
quit = 0x1001,
|
||||
|
||||
/** The command ID that should be used to send a "Delete" command. */
|
||||
del = 0x1002,
|
||||
|
||||
/** The command ID that should be used to send a "Cut" command. */
|
||||
cut = 0x1003,
|
||||
|
||||
/** The command ID that should be used to send a "Copy to clipboard" command. */
|
||||
copy = 0x1004,
|
||||
|
||||
/** The command ID that should be used to send a "Paste from clipboard" command. */
|
||||
paste = 0x1005,
|
||||
|
||||
/** The command ID that should be used to send a "Select all" command. */
|
||||
selectAll = 0x1006,
|
||||
|
||||
/** The command ID that should be used to send a "Deselect all" command. */
|
||||
deselectAll = 0x1007,
|
||||
|
||||
/** The command ID that should be used to send a "undo" command. */
|
||||
undo = 0x1008,
|
||||
|
||||
/** The command ID that should be used to send a "redo" command. */
|
||||
redo = 0x1009
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace juce
|
66
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp
vendored
Normal file
66
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ApplicationCommandInfo::ApplicationCommandInfo (const CommandID cid) noexcept
|
||||
: commandID (cid), flags (0)
|
||||
{
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setInfo (const String& shortName_,
|
||||
const String& description_,
|
||||
const String& categoryName_,
|
||||
const int flags_) noexcept
|
||||
{
|
||||
shortName = shortName_;
|
||||
description = description_;
|
||||
categoryName = categoryName_;
|
||||
flags = flags_;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setActive (const bool b) noexcept
|
||||
{
|
||||
if (b)
|
||||
flags &= ~isDisabled;
|
||||
else
|
||||
flags |= isDisabled;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::setTicked (const bool b) noexcept
|
||||
{
|
||||
if (b)
|
||||
flags |= isTicked;
|
||||
else
|
||||
flags &= ~isTicked;
|
||||
}
|
||||
|
||||
void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, ModifierKeys modifiers) noexcept
|
||||
{
|
||||
defaultKeypresses.add (KeyPress (keyCode, modifiers, 0));
|
||||
}
|
||||
|
||||
} // namespace juce
|
190
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h
vendored
Normal file
190
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Holds information describing an application command.
|
||||
|
||||
This object is used to pass information about a particular command, such as its
|
||||
name, description and other usage flags.
|
||||
|
||||
When an ApplicationCommandTarget is asked to provide information about the commands
|
||||
it can perform, this is the structure gets filled-in to describe each one.
|
||||
|
||||
@see ApplicationCommandTarget, ApplicationCommandTarget::getCommandInfo(),
|
||||
ApplicationCommandManager
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
struct JUCE_API ApplicationCommandInfo
|
||||
{
|
||||
//==============================================================================
|
||||
explicit ApplicationCommandInfo (CommandID commandID) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets a number of the structures values at once.
|
||||
|
||||
The meanings of each of the parameters is described below, in the appropriate
|
||||
member variable's description.
|
||||
*/
|
||||
void setInfo (const String& shortName,
|
||||
const String& description,
|
||||
const String& categoryName,
|
||||
int flags) noexcept;
|
||||
|
||||
/** An easy way to set or remove the isDisabled bit in the structure's flags field.
|
||||
|
||||
If isActive is true, the flags member has the isDisabled bit cleared; if isActive
|
||||
is false, the bit is set.
|
||||
*/
|
||||
void setActive (bool isActive) noexcept;
|
||||
|
||||
/** An easy way to set or remove the isTicked bit in the structure's flags field.
|
||||
*/
|
||||
void setTicked (bool isTicked) noexcept;
|
||||
|
||||
/** Handy method for adding a keypress to the defaultKeypresses array.
|
||||
|
||||
This is just so you can write things like:
|
||||
@code
|
||||
myinfo.addDefaultKeypress ('s', ModifierKeys::commandModifier);
|
||||
@endcode
|
||||
instead of
|
||||
@code
|
||||
myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier));
|
||||
@endcode
|
||||
*/
|
||||
void addDefaultKeypress (int keyCode, ModifierKeys modifiers) noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** The command's unique ID number.
|
||||
*/
|
||||
CommandID commandID;
|
||||
|
||||
/** A short name to describe the command.
|
||||
|
||||
This should be suitable for use in menus, on buttons that trigger the command, etc.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String shortName;
|
||||
|
||||
/** A longer description of the command.
|
||||
|
||||
This should be suitable for use in contexts such as a KeyMappingEditorComponent or
|
||||
pop-up tooltip describing what the command does.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String description;
|
||||
|
||||
/** A named category that the command fits into.
|
||||
|
||||
You can give your commands any category you like, and these will be displayed in
|
||||
contexts such as the KeyMappingEditorComponent, where the category is used to group
|
||||
commands together.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
String categoryName;
|
||||
|
||||
/** A list of zero or more keypresses that should be used as the default keys for
|
||||
this command.
|
||||
|
||||
Methods such as KeyPressMappingSet::resetToDefaultMappings() will use the keypresses in
|
||||
this list to initialise the default set of key-to-command mappings.
|
||||
|
||||
@see addDefaultKeypress
|
||||
*/
|
||||
Array<KeyPress> defaultKeypresses;
|
||||
|
||||
//==============================================================================
|
||||
/** Flags describing the ways in which this command should be used.
|
||||
|
||||
A bitwise-OR of these values is stored in the ApplicationCommandInfo::flags
|
||||
variable.
|
||||
*/
|
||||
enum CommandFlags
|
||||
{
|
||||
/** Indicates that the command can't currently be performed.
|
||||
|
||||
The ApplicationCommandTarget::getCommandInfo() method must set this flag if it's
|
||||
not currently permissible to perform the command. If the flag is set, then
|
||||
components that trigger the command, e.g. PopupMenu, may choose to grey-out the
|
||||
command or show themselves as not being enabled.
|
||||
|
||||
@see ApplicationCommandInfo::setActive
|
||||
*/
|
||||
isDisabled = 1 << 0,
|
||||
|
||||
/** Indicates that the command should have a tick next to it on a menu.
|
||||
|
||||
If your command is shown on a menu and this is set, it'll show a tick next to
|
||||
it. Other components such as buttons may also use this flag to indicate that it
|
||||
is a value that can be toggled, and is currently in the 'on' state.
|
||||
|
||||
@see ApplicationCommandInfo::setTicked
|
||||
*/
|
||||
isTicked = 1 << 1,
|
||||
|
||||
/** If this flag is present, then when a KeyPressMappingSet invokes the command,
|
||||
it will call the command twice, once on key-down and again on key-up.
|
||||
|
||||
@see ApplicationCommandTarget::InvocationInfo
|
||||
*/
|
||||
wantsKeyUpDownCallbacks = 1 << 2,
|
||||
|
||||
/** If this flag is present, then a KeyMappingEditorComponent will not display the
|
||||
command in its list.
|
||||
*/
|
||||
hiddenFromKeyEditor = 1 << 3,
|
||||
|
||||
/** If this flag is present, then a KeyMappingEditorComponent will display the
|
||||
command in its list, but won't allow the assigned keypress to be changed.
|
||||
*/
|
||||
readOnlyInKeyEditor = 1 << 4,
|
||||
|
||||
/** If this flag is present and the command is invoked from a keypress, then any
|
||||
buttons or menus that are also connected to the command will not flash to
|
||||
indicate that they've been triggered.
|
||||
*/
|
||||
dontTriggerVisualFeedback = 1 << 5
|
||||
};
|
||||
|
||||
/** A bitwise-OR of the values specified in the CommandFlags enum.
|
||||
|
||||
You can use the setInfo() method to quickly set this and some of the command's
|
||||
other properties.
|
||||
*/
|
||||
int flags;
|
||||
};
|
||||
|
||||
} // namespace juce
|
322
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp
vendored
Normal file
322
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp
vendored
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ApplicationCommandManager::ApplicationCommandManager()
|
||||
{
|
||||
keyMappings.reset (new KeyPressMappingSet (*this));
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
ApplicationCommandManager::~ApplicationCommandManager()
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
keyMappings.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationCommandManager::clearCommands()
|
||||
{
|
||||
commands.clear();
|
||||
keyMappings->clearAllKeyPresses();
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& newCommand)
|
||||
{
|
||||
// zero isn't a valid command ID!
|
||||
jassert (newCommand.commandID != 0);
|
||||
|
||||
// the name isn't optional!
|
||||
jassert (newCommand.shortName.isNotEmpty());
|
||||
|
||||
if (auto* command = getMutableCommandForID (newCommand.commandID))
|
||||
{
|
||||
// Trying to re-register the same command ID with different parameters can often indicate a typo.
|
||||
// This assertion is here because I've found it useful catching some mistakes, but it may also cause
|
||||
// false alarms if you're deliberately updating some flags for a command.
|
||||
jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName
|
||||
&& newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName
|
||||
&& newCommand.defaultKeypresses == getCommandForID (newCommand.commandID)->defaultKeypresses
|
||||
&& (newCommand.flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor))
|
||||
== (getCommandForID (newCommand.commandID)->flags & (ApplicationCommandInfo::wantsKeyUpDownCallbacks | ApplicationCommandInfo::hiddenFromKeyEditor | ApplicationCommandInfo::readOnlyInKeyEditor)));
|
||||
|
||||
*command = newCommand;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* newInfo = new ApplicationCommandInfo (newCommand);
|
||||
newInfo->flags &= ~ApplicationCommandInfo::isTicked;
|
||||
commands.add (newInfo);
|
||||
|
||||
keyMappings->resetToDefaultMapping (newCommand.commandID);
|
||||
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommandTarget* target)
|
||||
{
|
||||
if (target != nullptr)
|
||||
{
|
||||
Array<CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
for (int i = 0; i < commandIDs.size(); ++i)
|
||||
{
|
||||
ApplicationCommandInfo info (commandIDs.getUnchecked(i));
|
||||
target->getCommandInfo (info.commandID, info);
|
||||
|
||||
registerCommand (info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::removeCommand (const CommandID commandID)
|
||||
{
|
||||
for (int i = commands.size(); --i >= 0;)
|
||||
{
|
||||
if (commands.getUnchecked (i)->commandID == commandID)
|
||||
{
|
||||
commands.remove (i);
|
||||
triggerAsyncUpdate();
|
||||
|
||||
const Array<KeyPress> keys (keyMappings->getKeyPressesAssignedToCommand (commandID));
|
||||
|
||||
for (int j = keys.size(); --j >= 0;)
|
||||
keyMappings->removeKeyPress (keys.getReference (j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::commandStatusChanged()
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandInfo* ApplicationCommandManager::getMutableCommandForID (CommandID commandID) const noexcept
|
||||
{
|
||||
for (int i = commands.size(); --i >= 0;)
|
||||
if (commands.getUnchecked(i)->commandID == commandID)
|
||||
return commands.getUnchecked(i);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (CommandID commandID) const noexcept
|
||||
{
|
||||
return getMutableCommandForID (commandID);
|
||||
}
|
||||
|
||||
String ApplicationCommandManager::getNameOfCommand (CommandID commandID) const noexcept
|
||||
{
|
||||
if (auto* ci = getCommandForID (commandID))
|
||||
return ci->shortName;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String ApplicationCommandManager::getDescriptionOfCommand (CommandID commandID) const noexcept
|
||||
{
|
||||
if (auto* ci = getCommandForID (commandID))
|
||||
return ci->description.isNotEmpty() ? ci->description
|
||||
: ci->shortName;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
StringArray ApplicationCommandManager::getCommandCategories() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
s.addIfNotAlreadyThere (commands.getUnchecked(i)->categoryName, false);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Array<CommandID> ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const
|
||||
{
|
||||
Array<CommandID> results;
|
||||
|
||||
for (int i = 0; i < commands.size(); ++i)
|
||||
if (commands.getUnchecked(i)->categoryName == categoryName)
|
||||
results.add (commands.getUnchecked(i)->commandID);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandManager::invokeDirectly (CommandID commandID, bool asynchronously)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
|
||||
|
||||
return invoke (info, asynchronously);
|
||||
}
|
||||
|
||||
bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& inf, bool asynchronously)
|
||||
{
|
||||
// This call isn't thread-safe for use from a non-UI thread without locking the message
|
||||
// manager first..
|
||||
JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
|
||||
|
||||
bool ok = false;
|
||||
ApplicationCommandInfo commandInfo (0);
|
||||
|
||||
if (auto* target = getTargetForCommand (inf.commandID, commandInfo))
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (inf);
|
||||
info.commandFlags = commandInfo.flags;
|
||||
|
||||
sendListenerInvokeCallback (info);
|
||||
ok = target->invoke (info, asynchronously);
|
||||
commandStatusChanged();
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ApplicationCommandManager::getFirstCommandTarget (CommandID)
|
||||
{
|
||||
return firstTarget != nullptr ? firstTarget
|
||||
: findDefaultComponentTarget();
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept
|
||||
{
|
||||
firstTarget = newTarget;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandManager::getTargetForCommand (CommandID commandID,
|
||||
ApplicationCommandInfo& upToDateInfo)
|
||||
{
|
||||
auto* target = getFirstCommandTarget (commandID);
|
||||
|
||||
if (target == nullptr)
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
target = target->getTargetForCommand (commandID);
|
||||
|
||||
if (target != nullptr)
|
||||
{
|
||||
upToDateInfo.commandID = commandID;
|
||||
target->getCommandInfo (commandID, upToDateInfo);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget* ApplicationCommandManager::findTargetForComponent (Component* c)
|
||||
{
|
||||
auto* target = dynamic_cast<ApplicationCommandTarget*> (c);
|
||||
|
||||
if (target == nullptr && c != nullptr)
|
||||
target = c->findParentComponentOfClass<ApplicationCommandTarget>();
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget()
|
||||
{
|
||||
auto* c = Component::getCurrentlyFocusedComponent();
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
if (auto* activeWindow = TopLevelWindow::getActiveTopLevelWindow())
|
||||
{
|
||||
if (auto* peer = activeWindow->getPeer())
|
||||
{
|
||||
c = peer->getLastFocusedSubcomponent();
|
||||
|
||||
if (c == nullptr)
|
||||
c = activeWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == nullptr)
|
||||
{
|
||||
auto& desktop = Desktop::getInstance();
|
||||
|
||||
// getting a bit desperate now: try all desktop comps..
|
||||
for (int i = desktop.getNumComponents(); --i >= 0;)
|
||||
if (auto* component = desktop.getComponent (i))
|
||||
if (isForegroundOrEmbeddedProcess (component))
|
||||
if (auto* peer = component->getPeer())
|
||||
if (auto* target = findTargetForComponent (peer->getLastFocusedSubcomponent()))
|
||||
return target;
|
||||
}
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
// if we're focused on a ResizableWindow, chances are that it's the content
|
||||
// component that really should get the event. And if not, the event will
|
||||
// still be passed up to the top level window anyway, so let's send it to the
|
||||
// content comp.
|
||||
if (auto* resizableWindow = dynamic_cast<ResizableWindow*> (c))
|
||||
if (auto* content = resizableWindow->getContentComponent())
|
||||
c = content;
|
||||
|
||||
if (auto* target = findTargetForComponent (c))
|
||||
return target;
|
||||
}
|
||||
|
||||
return JUCEApplication::getInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ApplicationCommandManager::addListener (ApplicationCommandManagerListener* listener)
|
||||
{
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::removeListener (ApplicationCommandManagerListener* listener)
|
||||
{
|
||||
listeners.remove (listener);
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info)
|
||||
{
|
||||
listeners.call ([&] (ApplicationCommandManagerListener& l) { l.applicationCommandInvoked (info); });
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::handleAsyncUpdate()
|
||||
{
|
||||
listeners.call ([] (ApplicationCommandManagerListener& l) { l.applicationCommandListChanged(); });
|
||||
}
|
||||
|
||||
void ApplicationCommandManager::globalFocusChanged (Component*)
|
||||
{
|
||||
commandStatusChanged();
|
||||
}
|
||||
|
||||
} // namespace juce
|
349
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h
vendored
Normal file
349
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h
vendored
Normal file
@ -0,0 +1,349 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
One of these objects holds a list of all the commands your app can perform,
|
||||
and despatches these commands when needed.
|
||||
|
||||
Application commands are a good way to trigger actions in your app, e.g. "Quit",
|
||||
"Copy", "Paste", etc. Menus, buttons and keypresses can all be given commands
|
||||
to invoke automatically, which means you don't have to handle the result of a menu
|
||||
or button click manually. Commands are despatched to ApplicationCommandTarget objects
|
||||
which can choose which events they want to handle.
|
||||
|
||||
This architecture also allows for nested ApplicationCommandTargets, so that for example
|
||||
you could have two different objects, one inside the other, both of which can respond to
|
||||
a "delete" command. Depending on which one has focus, the command will be sent to the
|
||||
appropriate place, regardless of whether it was triggered by a menu, keypress or some other
|
||||
method.
|
||||
|
||||
To set up your app to use commands, you'll need to do the following:
|
||||
|
||||
- Create a global ApplicationCommandManager to hold the list of all possible
|
||||
commands. (This will also manage a set of key-mappings for them).
|
||||
|
||||
- Make some of your UI components (or other objects) inherit from ApplicationCommandTarget.
|
||||
This allows the object to provide a list of commands that it can perform, and
|
||||
to handle them.
|
||||
|
||||
- Register each type of command using ApplicationCommandManager::registerAllCommandsForTarget(),
|
||||
or ApplicationCommandManager::registerCommand().
|
||||
|
||||
- If you want key-presses to trigger your commands, use the ApplicationCommandManager::getKeyMappings()
|
||||
method to access the key-mapper object, which you will need to register as a key-listener
|
||||
in whatever top-level component you're using. See the KeyPressMappingSet class for more help
|
||||
about setting this up.
|
||||
|
||||
- Use methods such as PopupMenu::addCommandItem() or Button::setCommandToTrigger() to
|
||||
cause these commands to be invoked automatically.
|
||||
|
||||
- Commands can be invoked directly by your code using ApplicationCommandManager::invokeDirectly().
|
||||
|
||||
When a command is invoked, the ApplicationCommandManager will try to choose the best
|
||||
ApplicationCommandTarget to receive the specified command. To do this it will use the
|
||||
current keyboard focus to see which component might be interested, and will search the
|
||||
component hierarchy for those that also implement the ApplicationCommandTarget interface.
|
||||
If an ApplicationCommandTarget isn't interested in the command that is being invoked, then
|
||||
the next one in line will be tried (see the ApplicationCommandTarget::getNextCommandTarget()
|
||||
method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns nullptr.
|
||||
At this point if the command still hasn't been performed, it will be passed to the current
|
||||
JUCEApplication object (which is itself an ApplicationCommandTarget).
|
||||
|
||||
To exert some custom control over which ApplicationCommandTarget is chosen to invoke a command,
|
||||
you can override the ApplicationCommandManager::getFirstCommandTarget() method and choose
|
||||
the object yourself.
|
||||
|
||||
@see ApplicationCommandTarget, ApplicationCommandInfo
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ApplicationCommandManager : private AsyncUpdater,
|
||||
private FocusChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates an ApplicationCommandManager.
|
||||
|
||||
Once created, you'll need to register all your app's commands with it, using
|
||||
ApplicationCommandManager::registerAllCommandsForTarget() or
|
||||
ApplicationCommandManager::registerCommand().
|
||||
*/
|
||||
ApplicationCommandManager();
|
||||
|
||||
/** Destructor.
|
||||
|
||||
Make sure that you don't delete this if pointers to it are still being used by
|
||||
objects such as PopupMenus or Buttons.
|
||||
*/
|
||||
~ApplicationCommandManager() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Clears the current list of all commands.
|
||||
Note that this will also clear the contents of the KeyPressMappingSet.
|
||||
*/
|
||||
void clearCommands();
|
||||
|
||||
/** Adds a command to the list of registered commands.
|
||||
@see registerAllCommandsForTarget
|
||||
*/
|
||||
void registerCommand (const ApplicationCommandInfo& newCommand);
|
||||
|
||||
/** Adds all the commands that this target publishes to the manager's list.
|
||||
|
||||
This will use ApplicationCommandTarget::getAllCommands() and ApplicationCommandTarget::getCommandInfo()
|
||||
to get details about all the commands that this target can do, and will call
|
||||
registerCommand() to add each one to the manger's list.
|
||||
|
||||
@see registerCommand
|
||||
*/
|
||||
void registerAllCommandsForTarget (ApplicationCommandTarget* target);
|
||||
|
||||
/** Removes the command with a specified ID.
|
||||
Note that this will also remove any key mappings that are mapped to the command.
|
||||
*/
|
||||
void removeCommand (CommandID commandID);
|
||||
|
||||
/** This should be called to tell the manager that one of its registered commands may have changed
|
||||
its active status.
|
||||
|
||||
Because the command manager only finds out whether a command is active or inactive by querying
|
||||
the current ApplicationCommandTarget, this is used to tell it that things may have changed. It
|
||||
allows things like buttons to update their enablement, etc.
|
||||
|
||||
This method will cause an asynchronous call to ApplicationCommandManagerListener::applicationCommandListChanged()
|
||||
for any registered listeners.
|
||||
*/
|
||||
void commandStatusChanged();
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the number of commands that have been registered.
|
||||
@see registerCommand
|
||||
*/
|
||||
int getNumCommands() const noexcept { return commands.size(); }
|
||||
|
||||
/** Returns the details about one of the registered commands.
|
||||
The index is between 0 and (getNumCommands() - 1).
|
||||
*/
|
||||
const ApplicationCommandInfo* getCommandForIndex (int index) const noexcept { return commands [index]; }
|
||||
|
||||
/** Returns the details about a given command ID.
|
||||
|
||||
This will search the list of registered commands for one with the given command
|
||||
ID number, and return its associated info. If no matching command is found, this
|
||||
will return nullptr.
|
||||
*/
|
||||
const ApplicationCommandInfo* getCommandForID (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the name field for a command.
|
||||
|
||||
An empty string is returned if no command with this ID has been registered.
|
||||
@see getDescriptionOfCommand
|
||||
*/
|
||||
String getNameOfCommand (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the description field for a command.
|
||||
|
||||
An empty string is returned if no command with this ID has been registered. If the
|
||||
command has no description, this will return its short name field instead.
|
||||
|
||||
@see getNameOfCommand
|
||||
*/
|
||||
String getDescriptionOfCommand (CommandID commandID) const noexcept;
|
||||
|
||||
/** Returns the list of categories.
|
||||
|
||||
This will go through all registered commands, and return a list of all the distinct
|
||||
categoryName values from their ApplicationCommandInfo structure.
|
||||
|
||||
@see getCommandsInCategory()
|
||||
*/
|
||||
StringArray getCommandCategories() const;
|
||||
|
||||
/** Returns a list of all the command UIDs in a particular category.
|
||||
@see getCommandCategories()
|
||||
*/
|
||||
Array<CommandID> getCommandsInCategory (const String& categoryName) const;
|
||||
|
||||
//==============================================================================
|
||||
/** Returns the manager's internal set of key mappings.
|
||||
|
||||
This object can be used to edit the keypresses. To actually link this object up
|
||||
to invoke commands when a key is pressed, see the comments for the KeyPressMappingSet
|
||||
class.
|
||||
|
||||
@see KeyPressMappingSet
|
||||
*/
|
||||
KeyPressMappingSet* getKeyMappings() const noexcept { return keyMappings.get(); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Invokes the given command directly, sending it to the default target.
|
||||
This is just an easy way to call invoke() without having to fill out the InvocationInfo
|
||||
structure.
|
||||
*/
|
||||
bool invokeDirectly (CommandID commandID, bool asynchronously);
|
||||
|
||||
/** Sends a command to the default target.
|
||||
|
||||
This will choose a target using getFirstCommandTarget(), and send the specified command
|
||||
to it using the ApplicationCommandTarget::invoke() method. This means that if the
|
||||
first target can't handle the command, it will be passed on to targets further down the
|
||||
chain (see ApplicationCommandTarget::invoke() for more info).
|
||||
|
||||
@param invocationInfo this must be correctly filled-in, describing the context for
|
||||
the invocation.
|
||||
@param asynchronously if false, the command will be performed before this method returns.
|
||||
If true, a message will be posted so that the command will be performed
|
||||
later on the message thread, and this method will return immediately.
|
||||
|
||||
@see ApplicationCommandTarget::invoke
|
||||
*/
|
||||
bool invoke (const ApplicationCommandTarget::InvocationInfo& invocationInfo,
|
||||
bool asynchronously);
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/** Chooses the ApplicationCommandTarget to which a command should be sent.
|
||||
|
||||
Whenever the manager needs to know which target a command should be sent to, it calls
|
||||
this method to determine the first one to try.
|
||||
|
||||
By default, this method will return the target that was set by calling setFirstCommandTarget().
|
||||
If no target is set, it will return the result of findDefaultComponentTarget().
|
||||
|
||||
If you need to make sure all commands go via your own custom target, then you can
|
||||
either use setFirstCommandTarget() to specify a single target, or override this method
|
||||
if you need more complex logic to choose one.
|
||||
|
||||
It may return nullptr if no targets are available.
|
||||
|
||||
@see getTargetForCommand, invoke, invokeDirectly
|
||||
*/
|
||||
virtual ApplicationCommandTarget* getFirstCommandTarget (CommandID commandID);
|
||||
|
||||
/** Sets a target to be returned by getFirstCommandTarget().
|
||||
|
||||
If this is set to nullptr, then getFirstCommandTarget() will by default return the
|
||||
result of findDefaultComponentTarget().
|
||||
|
||||
If you use this to set a target, make sure you call setFirstCommandTarget(nullptr)
|
||||
before deleting the target object.
|
||||
*/
|
||||
void setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept;
|
||||
|
||||
/** Tries to find the best target to use to perform a given command.
|
||||
|
||||
This will call getFirstCommandTarget() to find the preferred target, and will
|
||||
check whether that target can handle the given command. If it can't, then it'll use
|
||||
ApplicationCommandTarget::getNextCommandTarget() to find the next one to try, and
|
||||
so on until no more are available.
|
||||
|
||||
If no targets are found that can perform the command, this method will return nullptr.
|
||||
|
||||
If a target is found, then it will get the target to fill-in the upToDateInfo
|
||||
structure with the latest info about that command, so that the caller can see
|
||||
whether the command is disabled, ticked, etc.
|
||||
*/
|
||||
ApplicationCommandTarget* getTargetForCommand (CommandID commandID,
|
||||
ApplicationCommandInfo& upToDateInfo);
|
||||
|
||||
//==============================================================================
|
||||
/** Registers a listener that will be called when various events occur. */
|
||||
void addListener (ApplicationCommandManagerListener* listener);
|
||||
|
||||
/** Deregisters a previously-added listener. */
|
||||
void removeListener (ApplicationCommandManagerListener* listener);
|
||||
|
||||
//==============================================================================
|
||||
/** Looks for a suitable command target based on which Components have the keyboard focus.
|
||||
|
||||
This is used by the default implementation of ApplicationCommandTarget::getFirstCommandTarget(),
|
||||
but is exposed here in case it's useful.
|
||||
|
||||
It tries to pick the best ApplicationCommandTarget by looking at focused components, top level
|
||||
windows, etc., and using the findTargetForComponent() method.
|
||||
*/
|
||||
static ApplicationCommandTarget* findDefaultComponentTarget();
|
||||
|
||||
/** Examines this component and all its parents in turn, looking for the first one
|
||||
which is an ApplicationCommandTarget.
|
||||
|
||||
Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them
|
||||
implement that class.
|
||||
*/
|
||||
static ApplicationCommandTarget* findTargetForComponent (Component*);
|
||||
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OwnedArray<ApplicationCommandInfo> commands;
|
||||
ListenerList<ApplicationCommandManagerListener> listeners;
|
||||
std::unique_ptr<KeyPressMappingSet> keyMappings;
|
||||
ApplicationCommandTarget* firstTarget = nullptr;
|
||||
|
||||
void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo&);
|
||||
void handleAsyncUpdate() override;
|
||||
void globalFocusChanged (Component*) override;
|
||||
ApplicationCommandInfo* getMutableCommandForID (CommandID) const noexcept;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A listener that receives callbacks from an ApplicationCommandManager when
|
||||
commands are invoked or the command list is changed.
|
||||
|
||||
@see ApplicationCommandManager::addListener, ApplicationCommandManager::removeListener
|
||||
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ApplicationCommandManagerListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
virtual ~ApplicationCommandManagerListener() = default;
|
||||
|
||||
/** Called when an app command is about to be invoked. */
|
||||
virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) = 0;
|
||||
|
||||
/** Called when commands are registered or deregistered from the
|
||||
command manager, or when commands are made active or inactive.
|
||||
|
||||
Note that if you're using this to watch for changes to whether a command is disabled,
|
||||
you'll need to make sure that ApplicationCommandManager::commandStatusChanged() is called
|
||||
whenever the status of your command might have changed.
|
||||
*/
|
||||
virtual void applicationCommandListChanged() = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
186
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp
vendored
Normal file
186
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp
vendored
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
class ApplicationCommandTarget::CommandMessage : public MessageManager::MessageBase
|
||||
{
|
||||
public:
|
||||
CommandMessage (ApplicationCommandTarget* const target, const InvocationInfo& inf)
|
||||
: owner (target), info (inf)
|
||||
{
|
||||
}
|
||||
|
||||
void messageCallback() override
|
||||
{
|
||||
if (ApplicationCommandTarget* const target = owner)
|
||||
target->tryToInvoke (info, false);
|
||||
}
|
||||
|
||||
private:
|
||||
WeakReference<ApplicationCommandTarget> owner;
|
||||
const InvocationInfo info;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CommandMessage)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget::ApplicationCommandTarget() {}
|
||||
ApplicationCommandTarget::~ApplicationCommandTarget() {}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bool async)
|
||||
{
|
||||
if (isCommandActive (info.commandID))
|
||||
{
|
||||
if (async)
|
||||
{
|
||||
(new CommandMessage (this, info))->post();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (perform (info))
|
||||
return true;
|
||||
|
||||
// Hmm.. your target claimed that it could perform this command, but failed to do so.
|
||||
// If it can't do it at the moment for some reason, it should clear the 'isActive' flag
|
||||
// when it returns the command's info.
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent()
|
||||
{
|
||||
if (Component* const c = dynamic_cast<Component*> (this))
|
||||
return c->findParentComponentOfClass<ApplicationCommandTarget>();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const CommandID commandID)
|
||||
{
|
||||
ApplicationCommandTarget* target = this;
|
||||
int depth = 0;
|
||||
|
||||
while (target != nullptr)
|
||||
{
|
||||
Array<CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
if (commandIDs.contains (commandID))
|
||||
return target;
|
||||
|
||||
target = target->getNextCommandTarget();
|
||||
|
||||
++depth;
|
||||
jassert (depth < 100); // could be a recursive command chain??
|
||||
jassert (target != this); // definitely a recursive command chain!
|
||||
|
||||
if (depth > 100 || target == this)
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == nullptr)
|
||||
{
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
{
|
||||
Array<CommandID> commandIDs;
|
||||
target->getAllCommands (commandIDs);
|
||||
|
||||
if (commandIDs.contains (commandID))
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ApplicationCommandTarget::isCommandActive (const CommandID commandID)
|
||||
{
|
||||
ApplicationCommandInfo info (commandID);
|
||||
info.flags = ApplicationCommandInfo::isDisabled;
|
||||
|
||||
getCommandInfo (commandID, info);
|
||||
|
||||
return (info.flags & ApplicationCommandInfo::isDisabled) == 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool ApplicationCommandTarget::invoke (const InvocationInfo& info, const bool async)
|
||||
{
|
||||
ApplicationCommandTarget* target = this;
|
||||
int depth = 0;
|
||||
|
||||
while (target != nullptr)
|
||||
{
|
||||
if (target->tryToInvoke (info, async))
|
||||
return true;
|
||||
|
||||
target = target->getNextCommandTarget();
|
||||
|
||||
++depth;
|
||||
jassert (depth < 100); // could be a recursive command chain??
|
||||
jassert (target != this); // definitely a recursive command chain!
|
||||
|
||||
if (depth > 100 || target == this)
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == nullptr)
|
||||
{
|
||||
target = JUCEApplication::getInstance();
|
||||
|
||||
if (target != nullptr)
|
||||
return target->tryToInvoke (info, async);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const bool asynchronously)
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::direct;
|
||||
|
||||
return invoke (info, asynchronously);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID command)
|
||||
: commandID (command),
|
||||
commandFlags (0),
|
||||
invocationMethod (direct),
|
||||
originatingComponent (nullptr),
|
||||
isKeyDown (false),
|
||||
millisecsSinceKeyPressed (0)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace juce
|
244
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h
vendored
Normal file
244
deps/juce/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A command target publishes a list of command IDs that it can perform.
|
||||
|
||||
An ApplicationCommandManager despatches commands to targets, which must be
|
||||
able to provide information about what commands they can handle.
|
||||
|
||||
To create a target, you'll need to inherit from this class, implementing all of
|
||||
its pure virtual methods.
|
||||
|
||||
For info about how a target is chosen to receive a command, see
|
||||
ApplicationCommandManager::getFirstCommandTarget().
|
||||
|
||||
@see ApplicationCommandManager, ApplicationCommandInfo
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API ApplicationCommandTarget
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a command target. */
|
||||
ApplicationCommandTarget();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~ApplicationCommandTarget();
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Contains contextual details about the invocation of a command.
|
||||
*/
|
||||
struct JUCE_API InvocationInfo
|
||||
{
|
||||
//==============================================================================
|
||||
InvocationInfo (const CommandID commandID);
|
||||
|
||||
//==============================================================================
|
||||
/** The UID of the command that should be performed. */
|
||||
CommandID commandID;
|
||||
|
||||
/** The command's flags.
|
||||
See ApplicationCommandInfo for a description of these flag values.
|
||||
*/
|
||||
int commandFlags;
|
||||
|
||||
//==============================================================================
|
||||
/** The types of context in which the command might be called. */
|
||||
enum InvocationMethod
|
||||
{
|
||||
direct = 0, /**< The command is being invoked directly by a piece of code. */
|
||||
fromKeyPress, /**< The command is being invoked by a key-press. */
|
||||
fromMenu, /**< The command is being invoked by a menu selection. */
|
||||
fromButton /**< The command is being invoked by a button click. */
|
||||
};
|
||||
|
||||
/** The type of event that triggered this command. */
|
||||
InvocationMethod invocationMethod;
|
||||
|
||||
//==============================================================================
|
||||
/** If triggered by a keypress or menu, this will be the component that had the
|
||||
keyboard focus at the time.
|
||||
|
||||
If triggered by a button, it may be set to that component, or it may be null.
|
||||
*/
|
||||
Component* originatingComponent;
|
||||
|
||||
//==============================================================================
|
||||
/** The keypress that was used to invoke it.
|
||||
|
||||
Note that this will be an invalid keypress if the command was invoked
|
||||
by some other means than a keyboard shortcut.
|
||||
*/
|
||||
KeyPress keyPress;
|
||||
|
||||
/** True if the callback is being invoked when the key is pressed,
|
||||
false if the key is being released.
|
||||
|
||||
@see KeyPressMappingSet::addCommand()
|
||||
*/
|
||||
bool isKeyDown;
|
||||
|
||||
/** If the key is being released, this indicates how long it had been held
|
||||
down for.
|
||||
|
||||
(Only relevant if isKeyDown is false.)
|
||||
*/
|
||||
int millisecsSinceKeyPressed;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/** This must return the next target to try after this one.
|
||||
|
||||
When a command is being sent, and the first target can't handle
|
||||
that command, this method is used to determine the next target that should
|
||||
be tried.
|
||||
|
||||
It may return nullptr if it doesn't know of another target.
|
||||
|
||||
If your target is a Component, you would usually use the findFirstTargetParentComponent()
|
||||
method to return a parent component that might want to handle it.
|
||||
|
||||
@see invoke
|
||||
*/
|
||||
virtual ApplicationCommandTarget* getNextCommandTarget() = 0;
|
||||
|
||||
/** This must return a complete list of commands that this target can handle.
|
||||
|
||||
Your target should add all the command IDs that it handles to the array that is
|
||||
passed-in.
|
||||
*/
|
||||
virtual void getAllCommands (Array<CommandID>& commands) = 0;
|
||||
|
||||
/** This must provide details about one of the commands that this target can perform.
|
||||
|
||||
This will be called with one of the command IDs that the target provided in its
|
||||
getAllCommands() methods.
|
||||
|
||||
It should fill-in all appropriate fields of the ApplicationCommandInfo structure with
|
||||
suitable information about the command. (The commandID field will already have been filled-in
|
||||
by the caller).
|
||||
|
||||
The easiest way to set the info is using the ApplicationCommandInfo::setInfo() method to
|
||||
set all the fields at once.
|
||||
|
||||
If the command is currently inactive for some reason, this method must use
|
||||
ApplicationCommandInfo::setActive() to make that clear, (or it should set the isDisabled
|
||||
bit of the ApplicationCommandInfo::flags field).
|
||||
|
||||
Any default key-presses for the command should be appended to the
|
||||
ApplicationCommandInfo::defaultKeypresses field.
|
||||
|
||||
Note that if you change something that affects the status of the commands
|
||||
that would be returned by this method (e.g. something that makes some commands
|
||||
active or inactive), you should call ApplicationCommandManager::commandStatusChanged()
|
||||
to cause the manager to refresh its status.
|
||||
*/
|
||||
virtual void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) = 0;
|
||||
|
||||
/** This must actually perform the specified command.
|
||||
|
||||
If this target is able to perform the command specified by the commandID field of the
|
||||
InvocationInfo structure, then it should do so, and must return true.
|
||||
|
||||
If it can't handle this command, it should return false, which tells the caller to pass
|
||||
the command on to the next target in line.
|
||||
|
||||
@see invoke, ApplicationCommandManager::invoke
|
||||
*/
|
||||
virtual bool perform (const InvocationInfo& info) = 0;
|
||||
|
||||
//==============================================================================
|
||||
/** Makes this target invoke a command.
|
||||
|
||||
Your code can call this method to invoke a command on this target, but normally
|
||||
you'd call it indirectly via ApplicationCommandManager::invoke() or
|
||||
ApplicationCommandManager::invokeDirectly().
|
||||
|
||||
If this target can perform the given command, it will call its perform() method to
|
||||
do so. If not, then getNextCommandTarget() will be used to determine the next target
|
||||
to try, and the command will be passed along to it.
|
||||
|
||||
@param invocationInfo this must be correctly filled-in, describing the context for
|
||||
the invocation.
|
||||
@param asynchronously if false, the command will be performed before this method returns.
|
||||
If true, a message will be posted so that the command will be performed
|
||||
later on the message thread, and this method will return immediately.
|
||||
@see perform, ApplicationCommandManager::invoke
|
||||
*/
|
||||
bool invoke (const InvocationInfo& invocationInfo,
|
||||
const bool asynchronously);
|
||||
|
||||
/** Invokes a given command directly on this target.
|
||||
|
||||
This is just an easy way to call invoke() without having to fill out the InvocationInfo
|
||||
structure.
|
||||
*/
|
||||
bool invokeDirectly (const CommandID commandID,
|
||||
const bool asynchronously);
|
||||
|
||||
//==============================================================================
|
||||
/** Searches this target and all subsequent ones for the first one that can handle
|
||||
the specified command.
|
||||
|
||||
This will use getNextCommandTarget() to determine the chain of targets to try
|
||||
after this one.
|
||||
*/
|
||||
ApplicationCommandTarget* getTargetForCommand (const CommandID commandID);
|
||||
|
||||
/** Checks whether this command can currently be performed by this target.
|
||||
|
||||
This will return true only if a call to getCommandInfo() doesn't set the
|
||||
isDisabled flag to indicate that the command is inactive.
|
||||
*/
|
||||
bool isCommandActive (const CommandID commandID);
|
||||
|
||||
/** If this object is a Component, this method will search upwards in its current
|
||||
UI hierarchy for the next parent component that implements the
|
||||
ApplicationCommandTarget class.
|
||||
|
||||
If your target is a Component, this is a very handy method to use in your
|
||||
getNextCommandTarget() implementation.
|
||||
*/
|
||||
ApplicationCommandTarget* findFirstTargetParentComponent();
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class CommandMessage;
|
||||
friend class CommandMessage;
|
||||
|
||||
bool tryToInvoke (const InvocationInfo&, bool async);
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ApplicationCommandTarget)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget)
|
||||
};
|
||||
|
||||
} // namespace juce
|
418
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp
vendored
Normal file
418
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp
vendored
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager& cm)
|
||||
: commandManager (cm)
|
||||
{
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other)
|
||||
: KeyListener(), ChangeBroadcaster(), FocusChangeListener(), commandManager (other.commandManager)
|
||||
{
|
||||
Desktop::getInstance().addFocusChangeListener (this);
|
||||
}
|
||||
|
||||
KeyPressMappingSet::~KeyPressMappingSet()
|
||||
{
|
||||
Desktop::getInstance().removeFocusChangeListener (this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Array<KeyPress> KeyPressMappingSet::getKeyPressesAssignedToCommand (const CommandID commandID) const
|
||||
{
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
return mappings.getUnchecked (i)->keypresses;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::addKeyPress (const CommandID commandID, const KeyPress& newKeyPress, int insertIndex)
|
||||
{
|
||||
// If you specify an upper-case letter but no shift key, how is the user supposed to press it!?
|
||||
// Stick to lower-case letters when defining a keypress, to avoid ambiguity.
|
||||
jassert (! (CharacterFunctions::isUpperCase (newKeyPress.getTextCharacter())
|
||||
&& ! newKeyPress.getModifiers().isShiftDown()));
|
||||
|
||||
if (findCommandForKeyPress (newKeyPress) != commandID)
|
||||
{
|
||||
if (newKeyPress.isValid())
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.getUnchecked(i)->keypresses.insert (insertIndex, newKeyPress);
|
||||
|
||||
sendChangeMessage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
|
||||
{
|
||||
CommandMapping* const cm = new CommandMapping();
|
||||
cm->commandID = commandID;
|
||||
cm->keypresses.add (newKeyPress);
|
||||
cm->wantsKeyUpDownCallbacks = (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) != 0;
|
||||
|
||||
mappings.add (cm);
|
||||
sendChangeMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If you hit this, you're trying to attach a keypress to a command ID that
|
||||
// doesn't exist, so the key is not being attached.
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void addKeyPresses (KeyPressMappingSet& set, const ApplicationCommandInfo* const ci)
|
||||
{
|
||||
for (int j = 0; j < ci->defaultKeypresses.size(); ++j)
|
||||
set.addKeyPress (ci->commandID, ci->defaultKeypresses.getReference (j));
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::resetToDefaultMappings()
|
||||
{
|
||||
mappings.clear();
|
||||
|
||||
for (int i = 0; i < commandManager.getNumCommands(); ++i)
|
||||
addKeyPresses (*this, commandManager.getCommandForIndex (i));
|
||||
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID)
|
||||
{
|
||||
clearAllKeyPresses (commandID);
|
||||
|
||||
if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID))
|
||||
addKeyPresses (*this, ci);
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::clearAllKeyPresses()
|
||||
{
|
||||
if (mappings.size() > 0)
|
||||
{
|
||||
sendChangeMessage();
|
||||
mappings.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::clearAllKeyPresses (const CommandID commandID)
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.remove (i);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress)
|
||||
{
|
||||
if (keypress.isValid())
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
CommandMapping& cm = *mappings.getUnchecked(i);
|
||||
|
||||
for (int j = cm.keypresses.size(); --j >= 0;)
|
||||
{
|
||||
if (keypress == cm.keypresses [j])
|
||||
{
|
||||
cm.keypresses.remove (j);
|
||||
sendChangeMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::removeKeyPress (const CommandID commandID, const int keyPressIndex)
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
{
|
||||
mappings.getUnchecked(i)->keypresses.remove (keyPressIndex);
|
||||
sendChangeMessage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CommandID KeyPressMappingSet::findCommandForKeyPress (const KeyPress& keyPress) const noexcept
|
||||
{
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
if (mappings.getUnchecked(i)->keypresses.contains (keyPress))
|
||||
return mappings.getUnchecked(i)->commandID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool KeyPressMappingSet::containsMapping (const CommandID commandID, const KeyPress& keyPress) const noexcept
|
||||
{
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
if (mappings.getUnchecked(i)->commandID == commandID)
|
||||
return mappings.getUnchecked(i)->keypresses.contains (keyPress);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::invokeCommand (const CommandID commandID,
|
||||
const KeyPress& key,
|
||||
const bool isKeyDown,
|
||||
const int millisecsSinceKeyPressed,
|
||||
Component* const originatingComponent) const
|
||||
{
|
||||
ApplicationCommandTarget::InvocationInfo info (commandID);
|
||||
|
||||
info.invocationMethod = ApplicationCommandTarget::InvocationInfo::fromKeyPress;
|
||||
info.isKeyDown = isKeyDown;
|
||||
info.keyPress = key;
|
||||
info.millisecsSinceKeyPressed = millisecsSinceKeyPressed;
|
||||
info.originatingComponent = originatingComponent;
|
||||
|
||||
commandManager.invoke (info, false);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion)
|
||||
{
|
||||
if (xmlVersion.hasTagName ("KEYMAPPINGS"))
|
||||
{
|
||||
if (xmlVersion.getBoolAttribute ("basedOnDefaults", true))
|
||||
{
|
||||
// if the XML was created as a set of differences from the default mappings,
|
||||
// (i.e. by calling createXml (true)), then we need to first restore the defaults.
|
||||
resetToDefaultMappings();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the XML was created calling createXml (false), then we need to clear all
|
||||
// the keys and treat the xml as describing the entire set of mappings.
|
||||
clearAllKeyPresses();
|
||||
}
|
||||
|
||||
for (auto* map : xmlVersion.getChildIterator())
|
||||
{
|
||||
const CommandID commandId = map->getStringAttribute ("commandId").getHexValue32();
|
||||
|
||||
if (commandId != 0)
|
||||
{
|
||||
auto key = KeyPress::createFromDescription (map->getStringAttribute ("key"));
|
||||
|
||||
if (map->hasTagName ("MAPPING"))
|
||||
{
|
||||
addKeyPress (commandId, key);
|
||||
}
|
||||
else if (map->hasTagName ("UNMAPPING"))
|
||||
{
|
||||
for (auto& m : mappings)
|
||||
if (m->commandID == commandId)
|
||||
m->keypresses.removeAllInstancesOf (key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<XmlElement> KeyPressMappingSet::createXml (const bool saveDifferencesFromDefaultSet) const
|
||||
{
|
||||
std::unique_ptr<KeyPressMappingSet> defaultSet;
|
||||
|
||||
if (saveDifferencesFromDefaultSet)
|
||||
{
|
||||
defaultSet = std::make_unique<KeyPressMappingSet> (commandManager);
|
||||
defaultSet->resetToDefaultMappings();
|
||||
}
|
||||
|
||||
auto doc = std::make_unique<XmlElement> ("KEYMAPPINGS");
|
||||
|
||||
doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet);
|
||||
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
{
|
||||
auto& cm = *mappings.getUnchecked(i);
|
||||
|
||||
for (int j = 0; j < cm.keypresses.size(); ++j)
|
||||
{
|
||||
if (defaultSet == nullptr
|
||||
|| ! defaultSet->containsMapping (cm.commandID, cm.keypresses.getReference (j)))
|
||||
{
|
||||
auto map = doc->createNewChildElement ("MAPPING");
|
||||
|
||||
map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
|
||||
map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
|
||||
map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultSet != nullptr)
|
||||
{
|
||||
for (int i = 0; i < defaultSet->mappings.size(); ++i)
|
||||
{
|
||||
auto& cm = *defaultSet->mappings.getUnchecked(i);
|
||||
|
||||
for (int j = 0; j < cm.keypresses.size(); ++j)
|
||||
{
|
||||
if (! containsMapping (cm.commandID, cm.keypresses.getReference (j)))
|
||||
{
|
||||
auto map = doc->createNewChildElement ("UNMAPPING");
|
||||
|
||||
map->setAttribute ("commandId", String::toHexString ((int) cm.commandID));
|
||||
map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID));
|
||||
map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool KeyPressMappingSet::keyPressed (const KeyPress& key, Component* const originatingComponent)
|
||||
{
|
||||
bool commandWasDisabled = false;
|
||||
|
||||
for (int i = 0; i < mappings.size(); ++i)
|
||||
{
|
||||
CommandMapping& cm = *mappings.getUnchecked(i);
|
||||
|
||||
if (cm.keypresses.contains (key))
|
||||
{
|
||||
if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (cm.commandID))
|
||||
{
|
||||
if ((ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0)
|
||||
{
|
||||
ApplicationCommandInfo info (0);
|
||||
|
||||
if (commandManager.getTargetForCommand (cm.commandID, info) != nullptr)
|
||||
{
|
||||
if ((info.flags & ApplicationCommandInfo::isDisabled) == 0)
|
||||
{
|
||||
invokeCommand (cm.commandID, key, true, 0, originatingComponent);
|
||||
return true;
|
||||
}
|
||||
|
||||
commandWasDisabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (originatingComponent != nullptr && commandWasDisabled)
|
||||
originatingComponent->getLookAndFeel().playAlertSound();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* originatingComponent)
|
||||
{
|
||||
bool used = false;
|
||||
const uint32 now = Time::getMillisecondCounter();
|
||||
|
||||
for (int i = mappings.size(); --i >= 0;)
|
||||
{
|
||||
CommandMapping& cm = *mappings.getUnchecked(i);
|
||||
|
||||
if (cm.wantsKeyUpDownCallbacks)
|
||||
{
|
||||
for (int j = cm.keypresses.size(); --j >= 0;)
|
||||
{
|
||||
const KeyPress key (cm.keypresses.getReference (j));
|
||||
const bool isDown = key.isCurrentlyDown();
|
||||
|
||||
int keyPressEntryIndex = 0;
|
||||
bool wasDown = false;
|
||||
|
||||
for (int k = keysDown.size(); --k >= 0;)
|
||||
{
|
||||
if (key == keysDown.getUnchecked(k)->key)
|
||||
{
|
||||
keyPressEntryIndex = k;
|
||||
wasDown = true;
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDown != wasDown)
|
||||
{
|
||||
int millisecs = 0;
|
||||
|
||||
if (isDown)
|
||||
{
|
||||
KeyPressTime* const k = new KeyPressTime();
|
||||
k->key = key;
|
||||
k->timeWhenPressed = now;
|
||||
|
||||
keysDown.add (k);
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32 pressTime = keysDown.getUnchecked (keyPressEntryIndex)->timeWhenPressed;
|
||||
|
||||
if (now > pressTime)
|
||||
millisecs = (int) (now - pressTime);
|
||||
|
||||
keysDown.remove (keyPressEntryIndex);
|
||||
}
|
||||
|
||||
invokeCommand (cm.commandID, key, isDown, millisecs, originatingComponent);
|
||||
used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
void KeyPressMappingSet::globalFocusChanged (Component* focusedComponent)
|
||||
{
|
||||
if (focusedComponent != nullptr)
|
||||
focusedComponent->keyStateChanged (false);
|
||||
}
|
||||
|
||||
} // namespace juce
|
244
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h
vendored
Normal file
244
deps/juce/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
Manages and edits a list of keypresses, which it uses to invoke the appropriate
|
||||
command in an ApplicationCommandManager.
|
||||
|
||||
Normally, you won't actually create a KeyPressMappingSet directly, because
|
||||
each ApplicationCommandManager contains its own KeyPressMappingSet, so typically
|
||||
you'd create yourself an ApplicationCommandManager, and call its
|
||||
ApplicationCommandManager::getKeyMappings() method to get a pointer to its
|
||||
KeyPressMappingSet.
|
||||
|
||||
For one of these to actually use keypresses, you'll need to add it as a KeyListener
|
||||
to the top-level component for which you want to handle keystrokes. So for example:
|
||||
|
||||
@code
|
||||
class MyMainWindow : public Component
|
||||
{
|
||||
ApplicationCommandManager* myCommandManager;
|
||||
|
||||
public:
|
||||
MyMainWindow()
|
||||
{
|
||||
myCommandManager = new ApplicationCommandManager();
|
||||
|
||||
// first, make sure the command manager has registered all the commands that its
|
||||
// targets can perform..
|
||||
myCommandManager->registerAllCommandsForTarget (myCommandTarget1);
|
||||
myCommandManager->registerAllCommandsForTarget (myCommandTarget2);
|
||||
|
||||
// this will use the command manager to initialise the KeyPressMappingSet with
|
||||
// the default keypresses that were specified when the targets added their commands
|
||||
// to the manager.
|
||||
myCommandManager->getKeyMappings()->resetToDefaultMappings();
|
||||
|
||||
// having set up the default key-mappings, you might now want to load the last set
|
||||
// of mappings that the user configured.
|
||||
myCommandManager->getKeyMappings()->restoreFromXml (lastSavedKeyMappingsXML);
|
||||
|
||||
// Now tell our top-level window to send any keypresses that arrive to the
|
||||
// KeyPressMappingSet, which will use them to invoke the appropriate commands.
|
||||
addKeyListener (myCommandManager->getKeyMappings());
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
|
||||
KeyPressMappingSet derives from ChangeBroadcaster so that interested parties can
|
||||
register to be told when a command or mapping is added, removed, etc.
|
||||
|
||||
There's also a UI component called KeyMappingEditorComponent that can be used
|
||||
to easily edit the key mappings.
|
||||
|
||||
@see Component::addKeyListener(), KeyMappingEditorComponent, ApplicationCommandManager
|
||||
|
||||
@tags{GUI}
|
||||
*/
|
||||
class JUCE_API KeyPressMappingSet : public KeyListener,
|
||||
public ChangeBroadcaster,
|
||||
private FocusChangeListener
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
/** Creates a KeyPressMappingSet for a given command manager.
|
||||
|
||||
Normally, you won't actually create a KeyPressMappingSet directly, because
|
||||
each ApplicationCommandManager contains its own KeyPressMappingSet, so the
|
||||
best thing to do is to create your ApplicationCommandManager, and use the
|
||||
ApplicationCommandManager::getKeyMappings() method to access its mappings.
|
||||
|
||||
When a suitable keypress happens, the manager's invoke() method will be
|
||||
used to invoke the appropriate command.
|
||||
|
||||
@see ApplicationCommandManager
|
||||
*/
|
||||
explicit KeyPressMappingSet (ApplicationCommandManager&);
|
||||
|
||||
/** Creates an copy of a KeyPressMappingSet. */
|
||||
KeyPressMappingSet (const KeyPressMappingSet&);
|
||||
|
||||
/** Destructor. */
|
||||
~KeyPressMappingSet() override;
|
||||
|
||||
//==============================================================================
|
||||
ApplicationCommandManager& getCommandManager() const noexcept { return commandManager; }
|
||||
|
||||
//==============================================================================
|
||||
/** Returns a list of keypresses that are assigned to a particular command.
|
||||
|
||||
@param commandID the command's ID
|
||||
*/
|
||||
Array<KeyPress> getKeyPressesAssignedToCommand (CommandID commandID) const;
|
||||
|
||||
/** Assigns a keypress to a command.
|
||||
|
||||
If the keypress is already assigned to a different command, it will first be
|
||||
removed from that command, to avoid it triggering multiple functions.
|
||||
|
||||
@param commandID the ID of the command that you want to add a keypress to. If
|
||||
this is 0, the keypress will be removed from anything that it
|
||||
was previously assigned to, but not re-assigned
|
||||
@param newKeyPress the new key-press
|
||||
@param insertIndex if this is less than zero, the key will be appended to the
|
||||
end of the list of keypresses; otherwise the new keypress will
|
||||
be inserted into the existing list at this index
|
||||
*/
|
||||
void addKeyPress (CommandID commandID,
|
||||
const KeyPress& newKeyPress,
|
||||
int insertIndex = -1);
|
||||
|
||||
/** Reset all mappings to the defaults, as dictated by the ApplicationCommandManager.
|
||||
@see resetToDefaultMapping
|
||||
*/
|
||||
void resetToDefaultMappings();
|
||||
|
||||
/** Resets all key-mappings to the defaults for a particular command.
|
||||
@see resetToDefaultMappings
|
||||
*/
|
||||
void resetToDefaultMapping (CommandID commandID);
|
||||
|
||||
/** Removes all keypresses that are assigned to any commands. */
|
||||
void clearAllKeyPresses();
|
||||
|
||||
/** Removes all keypresses that are assigned to a particular command. */
|
||||
void clearAllKeyPresses (CommandID commandID);
|
||||
|
||||
/** Removes one of the keypresses that are assigned to a command.
|
||||
See the getKeyPressesAssignedToCommand() for the list of keypresses to
|
||||
which the keyPressIndex refers.
|
||||
*/
|
||||
void removeKeyPress (CommandID commandID, int keyPressIndex);
|
||||
|
||||
/** Removes a keypress from any command that it may be assigned to. */
|
||||
void removeKeyPress (const KeyPress& keypress);
|
||||
|
||||
/** Returns true if the given command is linked to this key. */
|
||||
bool containsMapping (CommandID commandID, const KeyPress& keyPress) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Looks for a command that corresponds to a keypress.
|
||||
@returns the UID of the command or 0 if none was found
|
||||
*/
|
||||
CommandID findCommandForKeyPress (const KeyPress& keyPress) const noexcept;
|
||||
|
||||
//==============================================================================
|
||||
/** Tries to recreate the mappings from a previously stored state.
|
||||
|
||||
The XML passed in must have been created by the createXml() method.
|
||||
|
||||
If the stored state makes any reference to commands that aren't
|
||||
currently available, these will be ignored.
|
||||
|
||||
If the set of mappings being loaded was a set of differences (using createXml (true)),
|
||||
then this will call resetToDefaultMappings() and then merge the saved mappings
|
||||
on top. If the saved set was created with createXml (false), then this method
|
||||
will first clear all existing mappings and load the saved ones as a complete set.
|
||||
|
||||
@returns true if it manages to load the XML correctly
|
||||
@see createXml
|
||||
*/
|
||||
bool restoreFromXml (const XmlElement& xmlVersion);
|
||||
|
||||
/** Creates an XML representation of the current mappings.
|
||||
|
||||
This will produce a lump of XML that can be later reloaded using
|
||||
restoreFromXml() to recreate the current mapping state.
|
||||
|
||||
@param saveDifferencesFromDefaultSet if this is false, then all keypresses
|
||||
will be saved into the XML. If it's true, then the XML will
|
||||
only store the differences between the current mappings and
|
||||
the default mappings you'd get from calling resetToDefaultMappings().
|
||||
The advantage of saving a set of differences from the default is that
|
||||
if you change the default mappings (in a new version of your app, for
|
||||
example), then these will be merged into a user's saved preferences.
|
||||
|
||||
@see restoreFromXml
|
||||
*/
|
||||
std::unique_ptr<XmlElement> createXml (bool saveDifferencesFromDefaultSet) const;
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
bool keyPressed (const KeyPress&, Component*) override;
|
||||
/** @internal */
|
||||
bool keyStateChanged (bool isKeyDown, Component*) override;
|
||||
/** @internal */
|
||||
void globalFocusChanged (Component*) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
ApplicationCommandManager& commandManager;
|
||||
|
||||
struct CommandMapping
|
||||
{
|
||||
CommandID commandID;
|
||||
Array<KeyPress> keypresses;
|
||||
bool wantsKeyUpDownCallbacks;
|
||||
};
|
||||
|
||||
OwnedArray<CommandMapping> mappings;
|
||||
|
||||
struct KeyPressTime
|
||||
{
|
||||
KeyPress key;
|
||||
uint32 timeWhenPressed;
|
||||
};
|
||||
|
||||
OwnedArray<KeyPressTime> keysDown;
|
||||
|
||||
void invokeCommand (const CommandID, const KeyPress&, const bool isKeyDown,
|
||||
const int millisecsSinceKeyPressed, Component* originator) const;
|
||||
|
||||
KeyPressMappingSet& operator= (const KeyPressMappingSet&);
|
||||
JUCE_LEAK_DETECTOR (KeyPressMappingSet)
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user