mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
GUI Basics: Refactor juce_gui_basics file structure
- Created a new detail namespace - Moved shared module implementation details into the detail namespace - Split dependencies so source files only rely on details in the detail namespace - Removed all code from the juce_gui_basics.cpp file
This commit is contained in:
parent
8942f22a9b
commit
cff722a4af
129 changed files with 4458 additions and 2318 deletions
33
modules/juce_gui_basics/detail/juce_AccessibilityHelpers.cpp
Normal file
33
modules/juce_gui_basics/detail/juce_AccessibilityHelpers.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
#if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED
|
||||
void AccessibilityHelpers::notifyAccessibilityEvent (const AccessibilityHandler&, Event) {}
|
||||
#endif
|
||||
|
||||
} // namespace juce::detail
|
||||
70
modules/juce_gui_basics/detail/juce_AccessibilityHelpers.h
Normal file
70
modules/juce_gui_basics/detail/juce_AccessibilityHelpers.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct AccessibilityHelpers
|
||||
{
|
||||
AccessibilityHelpers() = delete;
|
||||
|
||||
enum class Event
|
||||
{
|
||||
elementCreated,
|
||||
elementDestroyed,
|
||||
elementMovedOrResized,
|
||||
focusChanged,
|
||||
windowOpened,
|
||||
windowClosed
|
||||
};
|
||||
|
||||
static void notifyAccessibilityEvent (const AccessibilityHandler&, Event);
|
||||
|
||||
static String getApplicationOrPluginName()
|
||||
{
|
||||
#if defined (JucePlugin_Name)
|
||||
return JucePlugin_Name;
|
||||
#else
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
return app->getApplicationName();
|
||||
|
||||
return "JUCE Application";
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename MemberFn>
|
||||
static const AccessibilityHandler* getEnclosingHandlerWithInterface (const AccessibilityHandler* handler, MemberFn fn)
|
||||
{
|
||||
if (handler == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if ((handler->*fn)() != nullptr)
|
||||
return handler;
|
||||
|
||||
return getEnclosingHandlerWithInterface (handler->getParent(), fn);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
115
modules/juce_gui_basics/detail/juce_AlertWindowHelpers.h
Normal file
115
modules/juce_gui_basics/detail/juce_AlertWindowHelpers.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct AlertWindowHelpers
|
||||
{
|
||||
AlertWindowHelpers() = delete;
|
||||
|
||||
static std::unique_ptr<ScopedMessageBoxInterface> create (const MessageBoxOptions& opts)
|
||||
{
|
||||
class AlertWindowImpl : public detail::ScopedMessageBoxInterface
|
||||
{
|
||||
public:
|
||||
explicit AlertWindowImpl (const MessageBoxOptions& opts) : options (opts) {}
|
||||
|
||||
void runAsync (std::function<void (int)> recipient) override
|
||||
{
|
||||
if (auto* comp = setUpAlert())
|
||||
comp->enterModalState (true, ModalCallbackFunction::create (std::move (recipient)), true);
|
||||
else
|
||||
NullCheckedInvocation::invoke (recipient, 0);
|
||||
}
|
||||
|
||||
int runSync() override
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
if (auto comp = rawToUniquePtr (setUpAlert()))
|
||||
return comp->runModalLoop();
|
||||
#endif
|
||||
|
||||
jassertfalse;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void close() override
|
||||
{
|
||||
if (alert != nullptr)
|
||||
if (alert->isCurrentlyModal())
|
||||
alert->exitModalState();
|
||||
|
||||
alert = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Component* setUpAlert()
|
||||
{
|
||||
auto* component = options.getAssociatedComponent();
|
||||
|
||||
auto& lf = component != nullptr ? component->getLookAndFeel()
|
||||
: LookAndFeel::getDefaultLookAndFeel();
|
||||
|
||||
alert = lf.createAlertWindow (options.getTitle(),
|
||||
options.getMessage(),
|
||||
options.getButtonText (0),
|
||||
options.getButtonText (1),
|
||||
options.getButtonText (2),
|
||||
options.getIconType(),
|
||||
options.getNumButtons(),
|
||||
component);
|
||||
|
||||
if (alert == nullptr)
|
||||
{
|
||||
// You have to return an alert box!
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto* parent = options.getParentComponent())
|
||||
{
|
||||
parent->addAndMakeVisible (alert);
|
||||
|
||||
if (options.getAssociatedComponent() == nullptr)
|
||||
alert->setCentrePosition (parent->getLocalBounds().getCentre());
|
||||
}
|
||||
|
||||
alert->setAlwaysOnTop (detail::WindowingHelpers::areThereAnyAlwaysOnTopWindows());
|
||||
|
||||
return alert;
|
||||
}
|
||||
|
||||
const MessageBoxOptions options;
|
||||
Component::SafePointer<AlertWindow> alert;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlertWindowImpl)
|
||||
};
|
||||
|
||||
return std::make_unique<AlertWindowImpl> (opts);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
125
modules/juce_gui_basics/detail/juce_ButtonAccessibilityHandler.h
Normal file
125
modules/juce_gui_basics/detail/juce_ButtonAccessibilityHandler.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class ButtonAccessibilityHandler : public AccessibilityHandler
|
||||
{
|
||||
public:
|
||||
ButtonAccessibilityHandler (Button& buttonToWrap, AccessibilityRole roleIn)
|
||||
: AccessibilityHandler (buttonToWrap,
|
||||
isRadioButton (buttonToWrap) ? AccessibilityRole::radioButton : roleIn,
|
||||
getAccessibilityActions (buttonToWrap),
|
||||
getAccessibilityInterfaces (buttonToWrap)),
|
||||
button (buttonToWrap)
|
||||
{}
|
||||
|
||||
|
||||
AccessibleState getCurrentState() const override
|
||||
{
|
||||
auto state = AccessibilityHandler::getCurrentState();
|
||||
|
||||
if (button.isToggleable())
|
||||
{
|
||||
state = state.withCheckable();
|
||||
|
||||
if (button.getToggleState())
|
||||
state = state.withChecked();
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
String getTitle() const override
|
||||
{
|
||||
auto title = AccessibilityHandler::getTitle();
|
||||
|
||||
if (title.isEmpty())
|
||||
return button.getButtonText();
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
String getHelp() const override
|
||||
{
|
||||
return button.getTooltip();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
class ButtonValueInterface : public AccessibilityTextValueInterface
|
||||
{
|
||||
public:
|
||||
explicit ButtonValueInterface (Button& buttonToWrap)
|
||||
: button (buttonToWrap)
|
||||
{
|
||||
}
|
||||
|
||||
bool isReadOnly() const override { return true; }
|
||||
String getCurrentValueAsString() const override { return button.getToggleState() ? "On" : "Off"; }
|
||||
void setValueAsString (const String&) override {}
|
||||
|
||||
private:
|
||||
Button& button;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonValueInterface)
|
||||
};
|
||||
|
||||
static bool isRadioButton (const Button& button) noexcept
|
||||
{
|
||||
return button.getRadioGroupId() != 0;
|
||||
}
|
||||
|
||||
static AccessibilityActions getAccessibilityActions (Button& button)
|
||||
{
|
||||
auto actions = AccessibilityActions().addAction (AccessibilityActionType::press,
|
||||
[&button] { button.triggerClick(); });
|
||||
|
||||
if (button.isToggleable())
|
||||
actions = actions.addAction (AccessibilityActionType::toggle,
|
||||
[&button] { button.setToggleState (! button.getToggleState(), sendNotification); });
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
static Interfaces getAccessibilityInterfaces (Button& button)
|
||||
{
|
||||
if (button.isToggleable())
|
||||
return { std::make_unique<ButtonValueInterface> (button) };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Button& button;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonAccessibilityHandler)
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
255
modules/juce_gui_basics/detail/juce_ComponentHelpers.h
Normal file
255
modules/juce_gui_basics/detail/juce_ComponentHelpers.h
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
constexpr char colourPropertyPrefix[] = "jcclr_";
|
||||
|
||||
//==============================================================================
|
||||
struct ComponentHelpers
|
||||
{
|
||||
using SH = ScalingHelpers;
|
||||
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
static void* runModalLoopCallback (void* userData)
|
||||
{
|
||||
return (void*) (pointer_sized_int) static_cast<Component*> (userData)->runModalLoop();
|
||||
}
|
||||
#endif
|
||||
|
||||
static Identifier getColourPropertyID (int colourID)
|
||||
{
|
||||
char buffer[32];
|
||||
auto* end = buffer + numElementsInArray (buffer) - 1;
|
||||
auto* t = end;
|
||||
*t = 0;
|
||||
|
||||
for (auto v = (uint32) colourID;;)
|
||||
{
|
||||
*--t = "0123456789abcdef" [v & 15];
|
||||
v >>= 4;
|
||||
|
||||
if (v == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = (int) sizeof (colourPropertyPrefix) - 1; --i >= 0;)
|
||||
*--t = colourPropertyPrefix[i];
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool hitTest (Component& comp, Point<float> localPoint)
|
||||
{
|
||||
const auto intPoint = localPoint.roundToInt();
|
||||
return Rectangle<int> { comp.getWidth(), comp.getHeight() }.contains (intPoint)
|
||||
&& comp.hitTest (intPoint.x, intPoint.y);
|
||||
}
|
||||
|
||||
// converts an unscaled position within a peer to the local position within that peer's component
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept
|
||||
{
|
||||
if (comp.isTransformed())
|
||||
pos = pos.transformedBy (comp.getTransform().inverted());
|
||||
|
||||
return SH::unscaledScreenPosToScaled (comp, pos);
|
||||
}
|
||||
|
||||
// converts a position within a peer's component to the unscaled position within the peer
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept
|
||||
{
|
||||
if (comp.isTransformed())
|
||||
pos = pos.transformedBy (comp.getTransform());
|
||||
|
||||
return SH::scaledScreenPosToUnscaled (comp, pos);
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect convertFromParentSpace (const Component& comp, const PointOrRect pointInParentSpace)
|
||||
{
|
||||
const auto transformed = comp.affineTransform != nullptr ? pointInParentSpace.transformedBy (comp.affineTransform->inverted())
|
||||
: pointInParentSpace;
|
||||
|
||||
if (comp.isOnDesktop())
|
||||
{
|
||||
if (auto* peer = comp.getPeer())
|
||||
return SH::unscaledScreenPosToScaled (comp, peer->globalToLocal (SH::scaledScreenPosToUnscaled (transformed)));
|
||||
|
||||
jassertfalse;
|
||||
return transformed;
|
||||
}
|
||||
|
||||
if (comp.getParentComponent() == nullptr)
|
||||
return SH::subtractPosition (SH::unscaledScreenPosToScaled (comp, SH::scaledScreenPosToUnscaled (transformed)), comp);
|
||||
|
||||
return SH::subtractPosition (transformed, comp);
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect convertToParentSpace (const Component& comp, const PointOrRect pointInLocalSpace)
|
||||
{
|
||||
const auto preTransform = [&]
|
||||
{
|
||||
if (comp.isOnDesktop())
|
||||
{
|
||||
if (auto* peer = comp.getPeer())
|
||||
return SH::unscaledScreenPosToScaled (peer->localToGlobal (SH::scaledScreenPosToUnscaled (comp, pointInLocalSpace)));
|
||||
|
||||
jassertfalse;
|
||||
return pointInLocalSpace;
|
||||
}
|
||||
|
||||
if (comp.getParentComponent() == nullptr)
|
||||
return SH::unscaledScreenPosToScaled (SH::scaledScreenPosToUnscaled (comp, SH::addPosition (pointInLocalSpace, comp)));
|
||||
|
||||
return SH::addPosition (pointInLocalSpace, comp);
|
||||
}();
|
||||
|
||||
return comp.affineTransform != nullptr ? preTransform.transformedBy (*comp.affineTransform)
|
||||
: preTransform;
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, PointOrRect coordInParent)
|
||||
{
|
||||
auto* directParent = target.getParentComponent();
|
||||
jassert (directParent != nullptr);
|
||||
|
||||
if (directParent == parent)
|
||||
return convertFromParentSpace (target, coordInParent);
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
|
||||
return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent));
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p)
|
||||
{
|
||||
while (source != nullptr)
|
||||
{
|
||||
if (source == target)
|
||||
return p;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
|
||||
|
||||
if (source->isParentOf (target))
|
||||
return convertFromDistantParentSpace (source, *target, p);
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
|
||||
p = convertToParentSpace (*source, p);
|
||||
source = source->getParentComponent();
|
||||
}
|
||||
|
||||
jassert (source == nullptr);
|
||||
if (target == nullptr)
|
||||
return p;
|
||||
|
||||
auto* topLevelComp = target->getTopLevelComponent();
|
||||
|
||||
p = convertFromParentSpace (*topLevelComp, p);
|
||||
|
||||
if (topLevelComp == target)
|
||||
return p;
|
||||
|
||||
return convertFromDistantParentSpace (topLevelComp, *target, p);
|
||||
}
|
||||
|
||||
static bool clipObscuredRegions (const Component& comp, Graphics& g,
|
||||
const Rectangle<int> clipRect, Point<int> delta)
|
||||
{
|
||||
bool wasClipped = false;
|
||||
|
||||
for (int i = comp.childComponentList.size(); --i >= 0;)
|
||||
{
|
||||
auto& child = *comp.childComponentList.getUnchecked(i);
|
||||
|
||||
if (child.isVisible() && ! child.isTransformed())
|
||||
{
|
||||
auto newClip = clipRect.getIntersection (child.boundsRelativeToParent);
|
||||
|
||||
if (! newClip.isEmpty())
|
||||
{
|
||||
if (child.isOpaque() && child.componentTransparency == 0)
|
||||
{
|
||||
g.excludeClipRegion (newClip + delta);
|
||||
wasClipped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto childPos = child.getPosition();
|
||||
|
||||
if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta))
|
||||
wasClipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wasClipped;
|
||||
}
|
||||
|
||||
static Rectangle<int> getParentOrMainMonitorBounds (const Component& comp)
|
||||
{
|
||||
if (auto* p = comp.getParentComponent())
|
||||
return p->getLocalBounds();
|
||||
|
||||
return Desktop::getInstance().getDisplays().getPrimaryDisplay()->userArea;
|
||||
}
|
||||
|
||||
static void releaseAllCachedImageResources (Component& c)
|
||||
{
|
||||
if (auto* cached = c.getCachedComponentImage())
|
||||
cached->releaseResources();
|
||||
|
||||
for (auto* child : c.childComponentList)
|
||||
releaseAllCachedImageResources (*child);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool modalWouldBlockComponent (const Component& maybeBlocked, Component* modal)
|
||||
{
|
||||
return modal != nullptr
|
||||
&& modal != &maybeBlocked
|
||||
&& ! modal->isParentOf (&maybeBlocked)
|
||||
&& ! modal->canModalEventBeSentToComponent (&maybeBlocked);
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
static void sendMouseEventToComponentsThatAreBlockedByModal (Component& modal, Function&& function)
|
||||
{
|
||||
for (auto& ms : Desktop::getInstance().getMouseSources())
|
||||
if (auto* c = ms.getComponentUnderMouse())
|
||||
if (modalWouldBlockComponent (*c, &modal))
|
||||
(c->*function) (ms, SH::screenPosToLocalPos (*c, ms.getScreenPosition()), Time::getCurrentTime());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
35
modules/juce_gui_basics/detail/juce_CustomMouseCursorInfo.h
Normal file
35
modules/juce_gui_basics/detail/juce_CustomMouseCursorInfo.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct CustomMouseCursorInfo
|
||||
{
|
||||
ScaledImage image;
|
||||
Point<int> hotspot;
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
117
modules/juce_gui_basics/detail/juce_FocusHelpers.h
Normal file
117
modules/juce_gui_basics/detail/juce_FocusHelpers.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct FocusHelpers
|
||||
{
|
||||
FocusHelpers() = delete;
|
||||
|
||||
static int getOrder (const Component* c)
|
||||
{
|
||||
auto order = c->getExplicitFocusOrder();
|
||||
return order > 0 ? order : std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
template <typename FocusContainerFn>
|
||||
static void findAllComponents (Component* parent,
|
||||
std::vector<Component*>& components,
|
||||
FocusContainerFn isFocusContainer)
|
||||
{
|
||||
if (parent == nullptr || parent->getNumChildComponents() == 0)
|
||||
return;
|
||||
|
||||
std::vector<Component*> localComponents;
|
||||
|
||||
for (auto* c : parent->getChildren())
|
||||
if (c->isVisible() && c->isEnabled())
|
||||
localComponents.push_back (c);
|
||||
|
||||
const auto compareComponents = [&] (const Component* a, const Component* b)
|
||||
{
|
||||
const auto getComponentOrderAttributes = [] (const Component* c)
|
||||
{
|
||||
return std::make_tuple (getOrder (c),
|
||||
c->isAlwaysOnTop() ? 0 : 1,
|
||||
c->getY(),
|
||||
c->getX());
|
||||
};
|
||||
|
||||
return getComponentOrderAttributes (a) < getComponentOrderAttributes (b);
|
||||
};
|
||||
|
||||
// This will sort so that they are ordered in terms of explicit focus,
|
||||
// always on top, left-to-right, and then top-to-bottom.
|
||||
std::stable_sort (localComponents.begin(), localComponents.end(), compareComponents);
|
||||
|
||||
for (auto* c : localComponents)
|
||||
{
|
||||
components.push_back (c);
|
||||
|
||||
if (! (c->*isFocusContainer)())
|
||||
findAllComponents (c, components, isFocusContainer);
|
||||
}
|
||||
}
|
||||
|
||||
enum class NavigationDirection { forwards, backwards };
|
||||
|
||||
template <typename FocusContainerFn>
|
||||
static Component* navigateFocus (Component* current,
|
||||
Component* focusContainer,
|
||||
NavigationDirection direction,
|
||||
FocusContainerFn isFocusContainer)
|
||||
{
|
||||
if (focusContainer != nullptr)
|
||||
{
|
||||
std::vector<Component*> components;
|
||||
findAllComponents (focusContainer, components, isFocusContainer);
|
||||
|
||||
const auto iter = std::find (components.cbegin(), components.cend(), current);
|
||||
|
||||
if (iter == components.cend())
|
||||
return nullptr;
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case NavigationDirection::forwards:
|
||||
if (iter != std::prev (components.cend()))
|
||||
return *std::next (iter);
|
||||
|
||||
break;
|
||||
|
||||
case NavigationDirection::backwards:
|
||||
if (iter != components.cbegin())
|
||||
return *std::prev (iter);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
46
modules/juce_gui_basics/detail/juce_FocusRestorer.h
Normal file
46
modules/juce_gui_basics/detail/juce_FocusRestorer.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct FocusRestorer
|
||||
{
|
||||
FocusRestorer() : lastFocus (Component::getCurrentlyFocusedComponent()) {}
|
||||
|
||||
~FocusRestorer()
|
||||
{
|
||||
if (lastFocus != nullptr
|
||||
&& lastFocus->isShowing()
|
||||
&& ! lastFocus->isCurrentlyBlockedByAnotherModalComponent())
|
||||
lastFocus->grabKeyboardFocus();
|
||||
}
|
||||
|
||||
WeakReference<Component> lastFocus;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (FocusRestorer)
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
62
modules/juce_gui_basics/detail/juce_LookAndFeelHelpers.h
Normal file
62
modules/juce_gui_basics/detail/juce_LookAndFeelHelpers.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct LookAndFeelHelpers
|
||||
{
|
||||
LookAndFeelHelpers() = delete;
|
||||
|
||||
static Colour createBaseColour (Colour buttonColour,
|
||||
bool hasKeyboardFocus,
|
||||
bool shouldDrawButtonAsHighlighted,
|
||||
bool shouldDrawButtonAsDown) noexcept
|
||||
{
|
||||
const float sat = hasKeyboardFocus ? 1.3f : 0.9f;
|
||||
const Colour baseColour (buttonColour.withMultipliedSaturation (sat));
|
||||
|
||||
if (shouldDrawButtonAsDown) return baseColour.contrasting (0.2f);
|
||||
if (shouldDrawButtonAsHighlighted) return baseColour.contrasting (0.1f);
|
||||
|
||||
return baseColour;
|
||||
}
|
||||
|
||||
static TextLayout layoutTooltipText (const String& text, Colour colour) noexcept
|
||||
{
|
||||
const float tooltipFontSize = 13.0f;
|
||||
const int maxToolTipWidth = 400;
|
||||
|
||||
AttributedString s;
|
||||
s.setJustification (Justification::centred);
|
||||
s.append (text, Font (tooltipFontSize, Font::bold), colour);
|
||||
|
||||
TextLayout tl;
|
||||
tl.createLayoutWithBalancedLineLengths (s, (float) maxToolTipWidth);
|
||||
return tl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
589
modules/juce_gui_basics/detail/juce_MouseInputSourceImpl.h
Normal file
589
modules/juce_gui_basics/detail/juce_MouseInputSourceImpl.h
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
class MouseInputSourceImpl : private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
using SH = ScalingHelpers;
|
||||
|
||||
MouseInputSourceImpl (int i, MouseInputSource::InputSourceType type)
|
||||
: index (i),
|
||||
inputType (type)
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
bool isDragging() const noexcept
|
||||
{
|
||||
return buttonState.isAnyMouseButtonDown();
|
||||
}
|
||||
|
||||
Component* getComponentUnderMouse() const noexcept
|
||||
{
|
||||
return componentUnderMouse.get();
|
||||
}
|
||||
|
||||
ModifierKeys getCurrentModifiers() const noexcept
|
||||
{
|
||||
return ModifierKeys::currentModifiers
|
||||
.withoutMouseButtons()
|
||||
.withFlags (buttonState.getRawFlags());
|
||||
}
|
||||
|
||||
ComponentPeer* getPeer() noexcept
|
||||
{
|
||||
if (! ComponentPeer::isValidPeer (lastPeer))
|
||||
lastPeer = nullptr;
|
||||
|
||||
return lastPeer;
|
||||
}
|
||||
|
||||
static Component* findComponentAt (Point<float> screenPos, ComponentPeer* peer)
|
||||
{
|
||||
if (! ComponentPeer::isValidPeer (peer))
|
||||
return nullptr;
|
||||
|
||||
auto relativePos = SH::unscaledScreenPosToScaled (peer->getComponent(),
|
||||
peer->globalToLocal (screenPos));
|
||||
auto& comp = peer->getComponent();
|
||||
|
||||
// (the contains() call is needed to test for overlapping desktop windows)
|
||||
if (comp.contains (relativePos))
|
||||
return comp.getComponentAt (relativePos);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Point<float> getScreenPosition() const noexcept
|
||||
{
|
||||
// This needs to return the live position if possible, but it mustn't update the lastScreenPos
|
||||
// value, because that can cause continuity problems.
|
||||
return SH::unscaledScreenPosToScaled (getRawScreenPosition());
|
||||
}
|
||||
|
||||
Point<float> getRawScreenPosition() const noexcept
|
||||
{
|
||||
return unboundedMouseOffset + (inputType != MouseInputSource::InputSourceType::touch ? MouseInputSource::getCurrentRawMousePosition()
|
||||
: lastPointerState.position);
|
||||
}
|
||||
|
||||
void setScreenPosition (Point<float> p)
|
||||
{
|
||||
MouseInputSource::setRawMousePosition (SH::scaledScreenPosToUnscaled (p));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_DUMP_MOUSE_EVENTS
|
||||
#define JUCE_MOUSE_EVENT_DBG(desc, screenPos) DBG ("Mouse " << desc << " #" << index \
|
||||
<< ": " << SH::screenPosToLocalPos (comp, screenPos).toString() \
|
||||
<< " - Comp: " << String::toHexString ((pointer_sized_int) &comp));
|
||||
#else
|
||||
#define JUCE_MOUSE_EVENT_DBG(desc, screenPos)
|
||||
#endif
|
||||
|
||||
void sendMouseEnter (Component& comp, const detail::PointerState& pointerState, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("enter", pointerState.position)
|
||||
comp.internalMouseEnter (MouseInputSource (this),
|
||||
SH::screenPosToLocalPos (comp, pointerState.position),
|
||||
time);
|
||||
}
|
||||
|
||||
void sendMouseExit (Component& comp, const detail::PointerState& pointerState, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("exit", pointerState.position)
|
||||
comp.internalMouseExit (MouseInputSource (this),
|
||||
SH::screenPosToLocalPos (comp, pointerState.position),
|
||||
time);
|
||||
}
|
||||
|
||||
void sendMouseMove (Component& comp, const detail::PointerState& pointerState, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("move", pointerState.position)
|
||||
comp.internalMouseMove (MouseInputSource (this),
|
||||
SH::screenPosToLocalPos (comp, pointerState.position),
|
||||
time);
|
||||
}
|
||||
|
||||
void sendMouseDown (Component& comp, const detail::PointerState& pointerState, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("down", pointerState.position)
|
||||
comp.internalMouseDown (MouseInputSource (this),
|
||||
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
|
||||
time);
|
||||
}
|
||||
|
||||
void sendMouseDrag (Component& comp, const detail::PointerState& pointerState, Time time)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("drag", pointerState.position)
|
||||
comp.internalMouseDrag (MouseInputSource (this),
|
||||
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
|
||||
time);
|
||||
}
|
||||
|
||||
void sendMouseUp (Component& comp, const detail::PointerState& pointerState, Time time, ModifierKeys oldMods)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("up", pointerState.position)
|
||||
comp.internalMouseUp (MouseInputSource (this),
|
||||
pointerState.withPosition (SH::screenPosToLocalPos (comp, pointerState.position)),
|
||||
time,
|
||||
oldMods);
|
||||
}
|
||||
|
||||
void sendMouseWheel (Component& comp, Point<float> screenPos, Time time, const MouseWheelDetails& wheel)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("wheel", screenPos)
|
||||
comp.internalMouseWheel (MouseInputSource (this),
|
||||
SH::screenPosToLocalPos (comp, screenPos),
|
||||
time,
|
||||
wheel);
|
||||
}
|
||||
|
||||
void sendMagnifyGesture (Component& comp, Point<float> screenPos, Time time, float amount)
|
||||
{
|
||||
JUCE_MOUSE_EVENT_DBG ("magnify", screenPos)
|
||||
comp.internalMagnifyGesture (MouseInputSource (this),
|
||||
SH::screenPosToLocalPos (comp, screenPos),
|
||||
time,
|
||||
amount);
|
||||
}
|
||||
|
||||
#undef JUCE_MOUSE_EVENT_DBG
|
||||
|
||||
//==============================================================================
|
||||
// (returns true if the button change caused a modal event loop)
|
||||
bool setButtons (const detail::PointerState& pointerState, Time time, ModifierKeys newButtonState)
|
||||
{
|
||||
if (buttonState == newButtonState)
|
||||
return false;
|
||||
|
||||
// (avoid sending a spurious mouse-drag when we receive a mouse-up)
|
||||
if (! (isDragging() && ! newButtonState.isAnyMouseButtonDown()))
|
||||
setPointerState (pointerState, time, false);
|
||||
|
||||
// (ignore secondary clicks when there's already a button down)
|
||||
if (buttonState.isAnyMouseButtonDown() == newButtonState.isAnyMouseButtonDown())
|
||||
{
|
||||
buttonState = newButtonState;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lastCounter = mouseEventCounter;
|
||||
|
||||
if (buttonState.isAnyMouseButtonDown())
|
||||
{
|
||||
if (auto* current = getComponentUnderMouse())
|
||||
{
|
||||
auto oldMods = getCurrentModifiers();
|
||||
buttonState = newButtonState; // must change this before calling sendMouseUp, in case it runs a modal loop
|
||||
|
||||
sendMouseUp (*current, pointerState.withPositionOffset (unboundedMouseOffset), time, oldMods);
|
||||
|
||||
if (lastCounter != mouseEventCounter)
|
||||
return true; // if a modal loop happened, then newButtonState is no longer valid.
|
||||
}
|
||||
|
||||
enableUnboundedMouseMovement (false, false);
|
||||
}
|
||||
|
||||
buttonState = newButtonState;
|
||||
|
||||
if (buttonState.isAnyMouseButtonDown())
|
||||
{
|
||||
Desktop::getInstance().incrementMouseClickCounter();
|
||||
|
||||
if (auto* current = getComponentUnderMouse())
|
||||
{
|
||||
registerMouseDown (pointerState.position, time, *current, buttonState,
|
||||
inputType == MouseInputSource::InputSourceType::touch);
|
||||
sendMouseDown (*current, pointerState, time);
|
||||
}
|
||||
}
|
||||
|
||||
return lastCounter != mouseEventCounter;
|
||||
}
|
||||
|
||||
void setComponentUnderMouse (Component* newComponent, const detail::PointerState& pointerState, Time time)
|
||||
{
|
||||
auto* current = getComponentUnderMouse();
|
||||
|
||||
if (newComponent != current)
|
||||
{
|
||||
WeakReference<Component> safeNewComp (newComponent);
|
||||
auto originalButtonState = buttonState;
|
||||
|
||||
if (current != nullptr)
|
||||
{
|
||||
WeakReference<Component> safeOldComp (current);
|
||||
setButtons (pointerState, time, ModifierKeys());
|
||||
|
||||
if (auto oldComp = safeOldComp.get())
|
||||
{
|
||||
componentUnderMouse = safeNewComp;
|
||||
sendMouseExit (*oldComp, pointerState, time);
|
||||
}
|
||||
|
||||
buttonState = originalButtonState;
|
||||
}
|
||||
|
||||
componentUnderMouse = safeNewComp.get();
|
||||
current = safeNewComp.get();
|
||||
|
||||
if (current != nullptr)
|
||||
sendMouseEnter (*current, pointerState, time);
|
||||
|
||||
revealCursor (false);
|
||||
setButtons (pointerState, time, originalButtonState);
|
||||
}
|
||||
}
|
||||
|
||||
void setPeer (ComponentPeer& newPeer, const detail::PointerState& pointerState, Time time)
|
||||
{
|
||||
if (&newPeer != lastPeer && ( findComponentAt (pointerState.position, &newPeer) != nullptr
|
||||
|| findComponentAt (pointerState.position, lastPeer) == nullptr))
|
||||
{
|
||||
setComponentUnderMouse (nullptr, pointerState, time);
|
||||
lastPeer = &newPeer;
|
||||
setComponentUnderMouse (findComponentAt (pointerState.position, getPeer()), pointerState, time);
|
||||
}
|
||||
}
|
||||
|
||||
void setPointerState (const detail::PointerState& newPointerState, Time time, bool forceUpdate)
|
||||
{
|
||||
const auto& newScreenPos = newPointerState.position;
|
||||
|
||||
if (! isDragging())
|
||||
setComponentUnderMouse (findComponentAt (newScreenPos, getPeer()), newPointerState, time);
|
||||
|
||||
if ((newPointerState != lastPointerState) || forceUpdate)
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
|
||||
if (newPointerState.position != MouseInputSource::offscreenMousePos)
|
||||
lastPointerState = newPointerState;
|
||||
|
||||
if (auto* current = getComponentUnderMouse())
|
||||
{
|
||||
if (isDragging())
|
||||
{
|
||||
registerMouseDrag (newScreenPos);
|
||||
sendMouseDrag (*current, newPointerState.withPositionOffset (unboundedMouseOffset), time);
|
||||
|
||||
if (isUnboundedMouseModeOn)
|
||||
handleUnboundedDrag (*current);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendMouseMove (*current, newPointerState, time);
|
||||
}
|
||||
}
|
||||
|
||||
revealCursor (false);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time,
|
||||
const ModifierKeys newMods, float newPressure, float newOrientation, PenDetails pen)
|
||||
{
|
||||
lastTime = time;
|
||||
++mouseEventCounter;
|
||||
const auto pointerState = detail::PointerState().withPosition (newPeer.localToGlobal (positionWithinPeer))
|
||||
.withPressure (newPressure)
|
||||
.withOrientation (newOrientation)
|
||||
.withRotation (MouseInputSource::defaultRotation)
|
||||
.withTiltX (pen.tiltX)
|
||||
.withTiltY (pen.tiltY);
|
||||
|
||||
if (isDragging() && newMods.isAnyMouseButtonDown())
|
||||
{
|
||||
setPointerState (pointerState, time, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
setPeer (newPeer, pointerState, time);
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
if (setButtons (pointerState, time, newMods))
|
||||
return; // some modal events have been dispatched, so the current event is now out-of-date
|
||||
|
||||
peer = getPeer();
|
||||
|
||||
if (peer != nullptr)
|
||||
setPointerState (pointerState, time, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component* getTargetForGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
|
||||
Time time, Point<float>& screenPos)
|
||||
{
|
||||
lastTime = time;
|
||||
++mouseEventCounter;
|
||||
|
||||
screenPos = peer.localToGlobal (positionWithinPeer);
|
||||
const auto pointerState = lastPointerState.withPosition (screenPos);
|
||||
setPeer (peer, pointerState, time);
|
||||
setPointerState (pointerState, time, false);
|
||||
triggerFakeMove();
|
||||
|
||||
return getComponentUnderMouse();
|
||||
}
|
||||
|
||||
void handleWheel (ComponentPeer& peer, Point<float> positionWithinPeer,
|
||||
Time time, const MouseWheelDetails& wheel)
|
||||
{
|
||||
Desktop::getInstance().incrementMouseWheelCounter();
|
||||
Point<float> screenPos;
|
||||
|
||||
// This will make sure that when the wheel spins in its inertial phase, any events
|
||||
// continue to be sent to the last component that the mouse was over when it was being
|
||||
// actively controlled by the user. This avoids confusion when scrolling through nested
|
||||
// scrollable components.
|
||||
if (lastNonInertialWheelTarget == nullptr || ! wheel.isInertial)
|
||||
lastNonInertialWheelTarget = getTargetForGesture (peer, positionWithinPeer, time, screenPos);
|
||||
else
|
||||
screenPos = peer.localToGlobal (positionWithinPeer);
|
||||
|
||||
if (auto target = lastNonInertialWheelTarget.get())
|
||||
sendMouseWheel (*target, screenPos, time, wheel);
|
||||
}
|
||||
|
||||
void handleMagnifyGesture (ComponentPeer& peer, Point<float> positionWithinPeer,
|
||||
Time time, const float scaleFactor)
|
||||
{
|
||||
Point<float> screenPos;
|
||||
|
||||
if (auto* current = getTargetForGesture (peer, positionWithinPeer, time, screenPos))
|
||||
sendMagnifyGesture (*current, screenPos, time, scaleFactor);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Time getLastMouseDownTime() const noexcept
|
||||
{
|
||||
return mouseDowns[0].time;
|
||||
}
|
||||
|
||||
Point<float> getLastMouseDownPosition() const noexcept
|
||||
{
|
||||
return SH::unscaledScreenPosToScaled (mouseDowns[0].position);
|
||||
}
|
||||
|
||||
int getNumberOfMultipleClicks() const noexcept
|
||||
{
|
||||
int numClicks = 1;
|
||||
|
||||
if (! isLongPressOrDrag())
|
||||
{
|
||||
for (int i = 1; i < numElementsInArray (mouseDowns); ++i)
|
||||
{
|
||||
if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[i], MouseEvent::getDoubleClickTimeout() * jmin (i, 2)))
|
||||
++numClicks;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return numClicks;
|
||||
}
|
||||
|
||||
bool isLongPressOrDrag() const noexcept
|
||||
{
|
||||
return movedSignificantly ||
|
||||
lastTime > (mouseDowns[0].time + RelativeTime::milliseconds (300));
|
||||
}
|
||||
|
||||
bool hasMovedSignificantlySincePressed() const noexcept
|
||||
{
|
||||
return movedSignificantly;
|
||||
}
|
||||
|
||||
// Deprecated method
|
||||
bool hasMouseMovedSignificantlySincePressed() const noexcept
|
||||
{
|
||||
return isLongPressOrDrag();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void triggerFakeMove()
|
||||
{
|
||||
triggerAsyncUpdate();
|
||||
}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
setPointerState (lastPointerState,
|
||||
jmax (lastTime, Time::getCurrentTime()), true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void enableUnboundedMouseMovement (bool enable, bool keepCursorVisibleUntilOffscreen)
|
||||
{
|
||||
enable = enable && isDragging();
|
||||
isCursorVisibleUntilOffscreen = keepCursorVisibleUntilOffscreen;
|
||||
|
||||
if (enable != isUnboundedMouseModeOn)
|
||||
{
|
||||
if ((! enable) && ((! isCursorVisibleUntilOffscreen) || ! unboundedMouseOffset.isOrigin()))
|
||||
{
|
||||
// when released, return the mouse to within the component's bounds
|
||||
if (auto* current = getComponentUnderMouse())
|
||||
setScreenPosition (current->getScreenBounds().toFloat()
|
||||
.getConstrainedPoint (SH::unscaledScreenPosToScaled (lastPointerState.position)));
|
||||
}
|
||||
|
||||
isUnboundedMouseModeOn = enable;
|
||||
unboundedMouseOffset = {};
|
||||
|
||||
revealCursor (true);
|
||||
}
|
||||
}
|
||||
|
||||
void handleUnboundedDrag (Component& current)
|
||||
{
|
||||
auto componentScreenBounds = SH::scaledScreenPosToUnscaled (current.getParentMonitorArea()
|
||||
.reduced (2, 2)
|
||||
.toFloat());
|
||||
|
||||
if (! componentScreenBounds.contains (lastPointerState.position))
|
||||
{
|
||||
auto componentCentre = current.getScreenBounds().toFloat().getCentre();
|
||||
unboundedMouseOffset += (lastPointerState.position - SH::scaledScreenPosToUnscaled (componentCentre));
|
||||
setScreenPosition (componentCentre);
|
||||
}
|
||||
else if (isCursorVisibleUntilOffscreen
|
||||
&& (! unboundedMouseOffset.isOrigin())
|
||||
&& componentScreenBounds.contains (lastPointerState.position + unboundedMouseOffset))
|
||||
{
|
||||
MouseInputSource::setRawMousePosition (lastPointerState.position + unboundedMouseOffset);
|
||||
unboundedMouseOffset = {};
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void showMouseCursor (MouseCursor cursor, bool forcedUpdate)
|
||||
{
|
||||
if (isUnboundedMouseModeOn && ((! unboundedMouseOffset.isOrigin()) || ! isCursorVisibleUntilOffscreen))
|
||||
{
|
||||
cursor = MouseCursor::NoCursor;
|
||||
forcedUpdate = true;
|
||||
}
|
||||
|
||||
if (forcedUpdate || cursor.getHandle() != currentCursorHandle)
|
||||
{
|
||||
currentCursorHandle = cursor.getHandle();
|
||||
cursor.showInWindow (getPeer());
|
||||
}
|
||||
}
|
||||
|
||||
void hideCursor()
|
||||
{
|
||||
showMouseCursor (MouseCursor::NoCursor, true);
|
||||
}
|
||||
|
||||
void revealCursor (bool forcedUpdate)
|
||||
{
|
||||
MouseCursor mc (MouseCursor::NormalCursor);
|
||||
|
||||
if (auto* current = getComponentUnderMouse())
|
||||
mc = current->getLookAndFeel().getMouseCursorFor (*current);
|
||||
|
||||
showMouseCursor (mc, forcedUpdate);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const int index;
|
||||
const MouseInputSource::InputSourceType inputType;
|
||||
Point<float> unboundedMouseOffset; // NB: these are unscaled coords
|
||||
detail::PointerState lastPointerState;
|
||||
ModifierKeys buttonState;
|
||||
|
||||
bool isUnboundedMouseModeOn = false, isCursorVisibleUntilOffscreen = false;
|
||||
|
||||
private:
|
||||
WeakReference<Component> componentUnderMouse, lastNonInertialWheelTarget;
|
||||
ComponentPeer* lastPeer = nullptr;
|
||||
|
||||
void* currentCursorHandle = nullptr;
|
||||
int mouseEventCounter = 0;
|
||||
|
||||
struct RecentMouseDown
|
||||
{
|
||||
RecentMouseDown() = default;
|
||||
|
||||
Point<float> position;
|
||||
Time time;
|
||||
ModifierKeys buttons;
|
||||
uint32 peerID = 0;
|
||||
bool isTouch = false;
|
||||
|
||||
bool canBePartOfMultipleClickWith (const RecentMouseDown& other, int maxTimeBetweenMs) const noexcept
|
||||
{
|
||||
return time - other.time < RelativeTime::milliseconds (maxTimeBetweenMs)
|
||||
&& std::abs (position.x - other.position.x) < (float) getPositionToleranceForInputType()
|
||||
&& std::abs (position.y - other.position.y) < (float) getPositionToleranceForInputType()
|
||||
&& buttons == other.buttons
|
||||
&& peerID == other.peerID;
|
||||
}
|
||||
|
||||
int getPositionToleranceForInputType() const noexcept { return isTouch ? 25 : 8; }
|
||||
};
|
||||
|
||||
RecentMouseDown mouseDowns[4];
|
||||
Time lastTime;
|
||||
bool movedSignificantly = false;
|
||||
|
||||
void registerMouseDown (Point<float> screenPos, Time time, Component& component,
|
||||
const ModifierKeys modifiers, bool isTouchSource) noexcept
|
||||
{
|
||||
for (int i = numElementsInArray (mouseDowns); --i > 0;)
|
||||
mouseDowns[i] = mouseDowns[i - 1];
|
||||
|
||||
mouseDowns[0].position = screenPos;
|
||||
mouseDowns[0].time = time;
|
||||
mouseDowns[0].buttons = modifiers.withOnlyMouseButtons();
|
||||
mouseDowns[0].isTouch = isTouchSource;
|
||||
|
||||
if (auto* peer = component.getPeer())
|
||||
mouseDowns[0].peerID = peer->getUniqueID();
|
||||
else
|
||||
mouseDowns[0].peerID = 0;
|
||||
|
||||
movedSignificantly = false;
|
||||
lastNonInertialWheelTarget = nullptr;
|
||||
}
|
||||
|
||||
void registerMouseDrag (Point<float> screenPos) noexcept
|
||||
{
|
||||
movedSignificantly = movedSignificantly || mouseDowns[0].position.getDistanceFrom (screenPos) >= 4;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MouseInputSourceImpl)
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
154
modules/juce_gui_basics/detail/juce_MouseInputSourceList.h
Normal file
154
modules/juce_gui_basics/detail/juce_MouseInputSourceList.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
class MouseInputSourceList : public Timer
|
||||
{
|
||||
public:
|
||||
MouseInputSourceList()
|
||||
{
|
||||
#if JUCE_ANDROID || JUCE_IOS
|
||||
auto mainMouseInputType = MouseInputSource::InputSourceType::touch;
|
||||
#else
|
||||
auto mainMouseInputType = MouseInputSource::InputSourceType::mouse;
|
||||
#endif
|
||||
|
||||
addSource (0, mainMouseInputType);
|
||||
}
|
||||
|
||||
MouseInputSource* addSource (int index, MouseInputSource::InputSourceType type)
|
||||
{
|
||||
auto* s = new MouseInputSourceImpl (index, type);
|
||||
sources.add (s);
|
||||
sourceArray.add (MouseInputSource (s));
|
||||
|
||||
return &sourceArray.getReference (sourceArray.size() - 1);
|
||||
}
|
||||
|
||||
MouseInputSource* getMouseSource (int index) noexcept
|
||||
{
|
||||
return isPositiveAndBelow (index, sourceArray.size()) ? &sourceArray.getReference (index)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
MouseInputSource* getOrCreateMouseInputSource (MouseInputSource::InputSourceType type, int touchIndex = 0)
|
||||
{
|
||||
if (type == MouseInputSource::InputSourceType::mouse
|
||||
|| type == MouseInputSource::InputSourceType::pen)
|
||||
{
|
||||
for (auto& m : sourceArray)
|
||||
if (type == m.getType())
|
||||
return &m;
|
||||
|
||||
addSource (0, type);
|
||||
}
|
||||
else if (type == MouseInputSource::InputSourceType::touch)
|
||||
{
|
||||
jassert (0 <= touchIndex && touchIndex < 100); // sanity-check on number of fingers
|
||||
|
||||
for (auto& m : sourceArray)
|
||||
if (type == m.getType() && touchIndex == m.getIndex())
|
||||
return &m;
|
||||
|
||||
if (canUseTouch())
|
||||
return addSource (touchIndex, type);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int getNumDraggingMouseSources() const noexcept
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
for (auto* s : sources)
|
||||
if (s->isDragging())
|
||||
++num;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
MouseInputSource* getDraggingMouseSource (int index) noexcept
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
for (auto& s : sourceArray)
|
||||
{
|
||||
if (s.isDragging())
|
||||
{
|
||||
if (index == num)
|
||||
return &s;
|
||||
|
||||
++num;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void beginDragAutoRepeat (int interval)
|
||||
{
|
||||
if (interval > 0)
|
||||
{
|
||||
if (getTimerInterval() != interval)
|
||||
startTimer (interval);
|
||||
}
|
||||
else
|
||||
{
|
||||
stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
bool anyDragging = false;
|
||||
|
||||
for (auto* s : sources)
|
||||
{
|
||||
// NB: when doing auto-repeat, we need to force an update of the current position and button state,
|
||||
// because on some OSes the queue can get overloaded with messages so that mouse-events don't get through..
|
||||
if (s->isDragging() && ComponentPeer::getCurrentModifiersRealtime().isAnyMouseButtonDown())
|
||||
{
|
||||
s->lastPointerState.position = s->getRawScreenPosition();
|
||||
s->triggerFakeMove();
|
||||
anyDragging = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! anyDragging)
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
OwnedArray<MouseInputSourceImpl> sources;
|
||||
Array<MouseInputSource> sourceArray;
|
||||
|
||||
private:
|
||||
bool addSource();
|
||||
bool canUseTouch() const;
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
104
modules/juce_gui_basics/detail/juce_PointerState.h
Normal file
104
modules/juce_gui_basics/detail/juce_PointerState.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
class PointerState
|
||||
{
|
||||
auto tie() const noexcept
|
||||
{
|
||||
return std::tie (position, pressure, orientation, rotation, tiltX, tiltY);
|
||||
}
|
||||
|
||||
public:
|
||||
PointerState() = default;
|
||||
|
||||
bool operator== (const PointerState& other) const noexcept { return tie() == other.tie(); }
|
||||
bool operator!= (const PointerState& other) const noexcept { return tie() != other.tie(); }
|
||||
|
||||
[[nodiscard]] PointerState withPositionOffset (Point<float> x) const noexcept { return with (&PointerState::position, position + x); }
|
||||
[[nodiscard]] PointerState withPosition (Point<float> x) const noexcept { return with (&PointerState::position, x); }
|
||||
[[nodiscard]] PointerState withPressure (float x) const noexcept { return with (&PointerState::pressure, x); }
|
||||
[[nodiscard]] PointerState withOrientation (float x) const noexcept { return with (&PointerState::orientation, x); }
|
||||
[[nodiscard]] PointerState withRotation (float x) const noexcept { return with (&PointerState::rotation, x); }
|
||||
[[nodiscard]] PointerState withTiltX (float x) const noexcept { return with (&PointerState::tiltX, x); }
|
||||
[[nodiscard]] PointerState withTiltY (float x) const noexcept { return with (&PointerState::tiltY, x); }
|
||||
|
||||
Point<float> position;
|
||||
float pressure = MouseInputSource::defaultPressure;
|
||||
float orientation = MouseInputSource::defaultOrientation;
|
||||
float rotation = MouseInputSource::defaultRotation;
|
||||
float tiltX = MouseInputSource::defaultTiltX;
|
||||
float tiltY = MouseInputSource::defaultTiltY;
|
||||
|
||||
bool isPressureValid() const noexcept { return 0.0f <= pressure && pressure <= 1.0f; }
|
||||
bool isOrientationValid() const noexcept { return 0.0f <= orientation && orientation <= MathConstants<float>::twoPi; }
|
||||
bool isRotationValid() const noexcept { return 0.0f <= rotation && rotation <= MathConstants<float>::twoPi; }
|
||||
bool isTiltValid (bool isX) const noexcept
|
||||
{
|
||||
return isX ? (-1.0f <= tiltX && tiltX <= 1.0f)
|
||||
: (-1.0f <= tiltY && tiltY <= 1.0f);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Value>
|
||||
PointerState with (Value PointerState::* member, Value item) const
|
||||
{
|
||||
auto copy = *this;
|
||||
copy.*member = std::move (item);
|
||||
return copy;
|
||||
}
|
||||
};
|
||||
|
||||
inline auto makeMouseEvent (MouseInputSource source,
|
||||
const PointerState& ps,
|
||||
ModifierKeys modifiers,
|
||||
Component* eventComponent,
|
||||
Component* originator,
|
||||
Time eventTime,
|
||||
Point<float> mouseDownPos,
|
||||
Time mouseDownTime,
|
||||
int numberOfClicks,
|
||||
bool mouseWasDragged)
|
||||
{
|
||||
return MouseEvent (source,
|
||||
ps.position,
|
||||
modifiers,
|
||||
ps.pressure,
|
||||
ps.orientation,
|
||||
ps.rotation,
|
||||
ps.tiltX,
|
||||
ps.tiltY,
|
||||
eventComponent,
|
||||
originator,
|
||||
eventTime,
|
||||
mouseDownPos,
|
||||
mouseDownTime,
|
||||
numberOfClicks,
|
||||
mouseWasDragged);
|
||||
}
|
||||
|
||||
} // namespace juce::detail
|
||||
123
modules/juce_gui_basics/detail/juce_ScalingHelpers.h
Normal file
123
modules/juce_gui_basics/detail/juce_ScalingHelpers.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct ScalingHelpers
|
||||
{
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect unscaledScreenPosToScaled (float scale, PointOrRect pos) noexcept
|
||||
{
|
||||
return scale != 1.0f ? pos / scale : pos;
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect scaledScreenPosToUnscaled (float scale, PointOrRect pos) noexcept
|
||||
{
|
||||
return scale != 1.0f ? pos * scale : pos;
|
||||
}
|
||||
|
||||
// For these, we need to avoid getSmallestIntegerContainer being used, which causes
|
||||
// judder when moving windows
|
||||
static Rectangle<int> unscaledScreenPosToScaled (float scale, Rectangle<int> pos) noexcept
|
||||
{
|
||||
return scale != 1.0f ? Rectangle<int> (roundToInt ((float) pos.getX() / scale),
|
||||
roundToInt ((float) pos.getY() / scale),
|
||||
roundToInt ((float) pos.getWidth() / scale),
|
||||
roundToInt ((float) pos.getHeight() / scale)) : pos;
|
||||
}
|
||||
|
||||
static Rectangle<int> scaledScreenPosToUnscaled (float scale, Rectangle<int> pos) noexcept
|
||||
{
|
||||
return scale != 1.0f ? Rectangle<int> (roundToInt ((float) pos.getX() * scale),
|
||||
roundToInt ((float) pos.getY() * scale),
|
||||
roundToInt ((float) pos.getWidth() * scale),
|
||||
roundToInt ((float) pos.getHeight() * scale)) : pos;
|
||||
}
|
||||
|
||||
static Rectangle<float> unscaledScreenPosToScaled (float scale, Rectangle<float> pos) noexcept
|
||||
{
|
||||
return scale != 1.0f ? Rectangle<float> (pos.getX() / scale,
|
||||
pos.getY() / scale,
|
||||
pos.getWidth() / scale,
|
||||
pos.getHeight() / scale) : pos;
|
||||
}
|
||||
|
||||
static Rectangle<float> scaledScreenPosToUnscaled (float scale, Rectangle<float> pos) noexcept
|
||||
{
|
||||
return scale != 1.0f ? Rectangle<float> (pos.getX() * scale,
|
||||
pos.getY() * scale,
|
||||
pos.getWidth() * scale,
|
||||
pos.getHeight() * scale) : pos;
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept
|
||||
{
|
||||
return unscaledScreenPosToScaled (Desktop::getInstance().getGlobalScaleFactor(), pos);
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept
|
||||
{
|
||||
return scaledScreenPosToUnscaled (Desktop::getInstance().getGlobalScaleFactor(), pos);
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect unscaledScreenPosToScaled (const Component& comp, PointOrRect pos) noexcept
|
||||
{
|
||||
return unscaledScreenPosToScaled (comp.getDesktopScaleFactor(), pos);
|
||||
}
|
||||
|
||||
template <typename PointOrRect>
|
||||
static PointOrRect scaledScreenPosToUnscaled (const Component& comp, PointOrRect pos) noexcept
|
||||
{
|
||||
return scaledScreenPosToUnscaled (comp.getDesktopScaleFactor(), pos);
|
||||
}
|
||||
|
||||
static Point<int> addPosition (Point<int> p, const Component& c) noexcept { return p + c.getPosition(); }
|
||||
static Rectangle<int> addPosition (Rectangle<int> p, const Component& c) noexcept { return p + c.getPosition(); }
|
||||
static Point<float> addPosition (Point<float> p, const Component& c) noexcept { return p + c.getPosition().toFloat(); }
|
||||
static Rectangle<float> addPosition (Rectangle<float> p, const Component& c) noexcept { return p + c.getPosition().toFloat(); }
|
||||
static Point<int> subtractPosition (Point<int> p, const Component& c) noexcept { return p - c.getPosition(); }
|
||||
static Rectangle<int> subtractPosition (Rectangle<int> p, const Component& c) noexcept { return p - c.getPosition(); }
|
||||
static Point<float> subtractPosition (Point<float> p, const Component& c) noexcept { return p - c.getPosition().toFloat(); }
|
||||
static Rectangle<float> subtractPosition (Rectangle<float> p, const Component& c) noexcept { return p - c.getPosition().toFloat(); }
|
||||
|
||||
static Point<float> screenPosToLocalPos (Component& comp, Point<float> pos)
|
||||
{
|
||||
if (auto* peer = comp.getPeer())
|
||||
{
|
||||
pos = peer->globalToLocal (pos);
|
||||
auto& peerComp = peer->getComponent();
|
||||
return comp.getLocalPoint (&peerComp, unscaledScreenPosToScaled (peerComp, pos));
|
||||
}
|
||||
|
||||
return comp.getLocalPoint (nullptr, unscaledScreenPosToScaled (comp, pos));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
123
modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h
Normal file
123
modules/juce_gui_basics/detail/juce_ScopedMessageBoxImpl.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class ScopedMessageBoxImpl : private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
static ScopedMessageBox show (std::unique_ptr<ScopedMessageBoxInterface>&& native,
|
||||
std::function<void (int)> callback)
|
||||
{
|
||||
return ScopedMessageBox (runAsync (std::move (native),
|
||||
rawToUniquePtr (ModalCallbackFunction::create (std::move (callback)))));
|
||||
}
|
||||
|
||||
static int showUnmanaged (std::unique_ptr<ScopedMessageBoxInterface>&& native,
|
||||
ModalComponentManager::Callback* cb)
|
||||
{
|
||||
#if JUCE_MODAL_LOOPS_PERMITTED
|
||||
if (cb == nullptr)
|
||||
return runSync (std::move (native));
|
||||
#endif
|
||||
|
||||
runAsync (std::move (native), rawToUniquePtr (cb));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
~ScopedMessageBoxImpl() override
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
nativeImplementation->close();
|
||||
self.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
static std::shared_ptr<ScopedMessageBoxImpl> runAsync (std::unique_ptr<ScopedMessageBoxInterface>&& p,
|
||||
std::unique_ptr<ModalComponentManager::Callback>&& c)
|
||||
{
|
||||
std::shared_ptr<ScopedMessageBoxImpl> result (new ScopedMessageBoxImpl (std::move (p), std::move (c)));
|
||||
result->self = result;
|
||||
result->triggerAsyncUpdate();
|
||||
return result;
|
||||
}
|
||||
|
||||
static int runSync (std::unique_ptr<ScopedMessageBoxInterface>&& p)
|
||||
{
|
||||
auto local = std::move (p);
|
||||
return local != nullptr ? local->runSync() : 0;
|
||||
}
|
||||
|
||||
explicit ScopedMessageBoxImpl (std::unique_ptr<ScopedMessageBoxInterface>&& p)
|
||||
: ScopedMessageBoxImpl (std::move (p), nullptr) {}
|
||||
|
||||
ScopedMessageBoxImpl (std::unique_ptr<ScopedMessageBoxInterface>&& p,
|
||||
std::unique_ptr<ModalComponentManager::Callback>&& c)
|
||||
: callback (std::move (c)), nativeImplementation (std::move (p)) {}
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
nativeImplementation->runAsync ([weakRecipient = std::weak_ptr<ScopedMessageBoxImpl> (self)] (int result)
|
||||
{
|
||||
const auto notifyRecipient = [result, weakRecipient]
|
||||
{
|
||||
if (const auto locked = weakRecipient.lock())
|
||||
{
|
||||
if (auto* cb = locked->callback.get())
|
||||
cb->modalStateFinished (result);
|
||||
|
||||
locked->self.reset();
|
||||
}
|
||||
};
|
||||
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
notifyRecipient();
|
||||
else
|
||||
MessageManager::callAsync (notifyRecipient);
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<ModalComponentManager::Callback> callback;
|
||||
std::unique_ptr<ScopedMessageBoxInterface> nativeImplementation;
|
||||
|
||||
/* The 'old' native message box API doesn't have a concept of message box owners.
|
||||
Instead, message boxes have to clean up after themselves, once they're done displaying.
|
||||
To allow this mode of usage, the implementation keeps an owning reference to itself,
|
||||
which is cleared once the message box is closed or asked to quit. To display a native
|
||||
message box without a scoped lifetime, just create a Pimpl instance without using
|
||||
the ScopedMessageBox wrapper, and the Pimpl will destroy itself after it is dismissed.
|
||||
*/
|
||||
std::shared_ptr<ScopedMessageBoxImpl> self;
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
/*
|
||||
Instances of this type can show and dismiss a message box.
|
||||
|
||||
This is an interface rather than a concrete type so that platforms can pick an implementation at
|
||||
runtime if necessary.
|
||||
*/
|
||||
struct ScopedMessageBoxInterface
|
||||
{
|
||||
virtual ~ScopedMessageBoxInterface() = default;
|
||||
|
||||
/* Shows the message box.
|
||||
|
||||
When the message box exits normally, it should send the result to the passed-in function.
|
||||
The passed-in function is safe to call from any thread at any time.
|
||||
*/
|
||||
virtual void runAsync (std::function<void (int)>) = 0;
|
||||
|
||||
/* Shows the message box and blocks. */
|
||||
virtual int runSync() = 0;
|
||||
|
||||
/* Forcefully closes the message box.
|
||||
|
||||
This will be called when the message box handle has fallen out of scope.
|
||||
If the message box has already been closed by the user, this shouldn't do anything.
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
/* Implemented differently for each platform. */
|
||||
static std::unique_ptr<ScopedMessageBoxInterface> create (const MessageBoxOptions& options);
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
class ToolbarItemDragAndDropOverlayComponent : public Component
|
||||
{
|
||||
public:
|
||||
ToolbarItemDragAndDropOverlayComponent()
|
||||
: isDragging (false)
|
||||
{
|
||||
setAlwaysOnTop (true);
|
||||
setRepaintsOnMouseActivity (true);
|
||||
setMouseCursor (MouseCursor::DraggingHandCursor);
|
||||
}
|
||||
|
||||
void paint (Graphics& g) override
|
||||
{
|
||||
if (ToolbarItemComponent* const tc = getToolbarItemComponent())
|
||||
{
|
||||
if (isMouseOverOrDragging()
|
||||
&& tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar)
|
||||
{
|
||||
g.setColour (findColour (Toolbar::editingModeOutlineColourId, true));
|
||||
g.drawRect (getLocalBounds(), jmin (2, (getWidth() - 1) / 2,
|
||||
(getHeight() - 1) / 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDown (const MouseEvent& e) override
|
||||
{
|
||||
isDragging = false;
|
||||
|
||||
if (ToolbarItemComponent* const tc = getToolbarItemComponent())
|
||||
{
|
||||
tc->dragOffsetX = e.x;
|
||||
tc->dragOffsetY = e.y;
|
||||
}
|
||||
}
|
||||
|
||||
void mouseDrag (const MouseEvent& e) override
|
||||
{
|
||||
if (e.mouseWasDraggedSinceMouseDown() && ! isDragging)
|
||||
{
|
||||
isDragging = true;
|
||||
|
||||
if (DragAndDropContainer* const dnd = DragAndDropContainer::findParentDragContainerFor (this))
|
||||
{
|
||||
dnd->startDragging (Toolbar::toolbarDragDescriptor, getParentComponent(), ScaledImage(), true, nullptr, &e.source);
|
||||
|
||||
if (ToolbarItemComponent* const tc = getToolbarItemComponent())
|
||||
{
|
||||
tc->isBeingDragged = true;
|
||||
|
||||
if (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar)
|
||||
tc->setVisible (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent&) override
|
||||
{
|
||||
isDragging = false;
|
||||
|
||||
if (ToolbarItemComponent* const tc = getToolbarItemComponent())
|
||||
{
|
||||
tc->isBeingDragged = false;
|
||||
|
||||
if (Toolbar* const tb = tc->getToolbar())
|
||||
tb->updateAllItemPositions (true);
|
||||
else if (tc->getEditingMode() == ToolbarItemComponent::editableOnToolbar)
|
||||
delete tc;
|
||||
}
|
||||
}
|
||||
|
||||
void parentSizeChanged() override
|
||||
{
|
||||
setBounds (0, 0, getParentWidth(), getParentHeight());
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
bool isDragging;
|
||||
|
||||
ToolbarItemComponent* getToolbarItemComponent() const noexcept
|
||||
{
|
||||
return dynamic_cast<ToolbarItemComponent*> (getParentComponent());
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToolbarItemDragAndDropOverlayComponent)
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
136
modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h
Normal file
136
modules/juce_gui_basics/detail/juce_TopLevelWindowManager.h
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
/** Keeps track of the active top level window. */
|
||||
class TopLevelWindowManager : private Timer,
|
||||
private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
TopLevelWindowManager() = default;
|
||||
|
||||
~TopLevelWindowManager() override
|
||||
{
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (TopLevelWindowManager)
|
||||
|
||||
static void checkCurrentlyFocusedTopLevelWindow()
|
||||
{
|
||||
if (auto* wm = TopLevelWindowManager::getInstanceWithoutCreating())
|
||||
wm->checkFocusAsync();
|
||||
}
|
||||
|
||||
void checkFocusAsync()
|
||||
{
|
||||
startTimer (10);
|
||||
}
|
||||
|
||||
void checkFocus()
|
||||
{
|
||||
startTimer (jmin (1731, getTimerInterval() * 2));
|
||||
|
||||
auto* newActive = findCurrentlyActiveWindow();
|
||||
|
||||
if (newActive != currentActive)
|
||||
{
|
||||
currentActive = newActive;
|
||||
|
||||
for (int i = windows.size(); --i >= 0;)
|
||||
if (auto* tlw = windows[i])
|
||||
tlw->setWindowActive (isWindowActive (tlw));
|
||||
|
||||
Desktop::getInstance().triggerFocusCallback();
|
||||
}
|
||||
}
|
||||
|
||||
bool addWindow (TopLevelWindow* const w)
|
||||
{
|
||||
windows.add (w);
|
||||
checkFocusAsync();
|
||||
|
||||
return isWindowActive (w);
|
||||
}
|
||||
|
||||
void removeWindow (TopLevelWindow* const w)
|
||||
{
|
||||
checkFocusAsync();
|
||||
|
||||
if (currentActive == w)
|
||||
currentActive = nullptr;
|
||||
|
||||
windows.removeFirstMatchingValue (w);
|
||||
|
||||
if (windows.isEmpty())
|
||||
deleteInstance();
|
||||
}
|
||||
|
||||
Array<TopLevelWindow*> windows;
|
||||
|
||||
private:
|
||||
TopLevelWindow* currentActive = nullptr;
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
checkFocus();
|
||||
}
|
||||
|
||||
bool isWindowActive (TopLevelWindow* const tlw) const
|
||||
{
|
||||
return (tlw == currentActive
|
||||
|| tlw->isParentOf (currentActive)
|
||||
|| tlw->hasKeyboardFocus (true))
|
||||
&& tlw->isShowing();
|
||||
}
|
||||
|
||||
TopLevelWindow* findCurrentlyActiveWindow() const
|
||||
{
|
||||
if (Process::isForegroundProcess())
|
||||
{
|
||||
auto* focusedComp = Component::getCurrentlyFocusedComponent();
|
||||
auto* w = dynamic_cast<TopLevelWindow*> (focusedComp);
|
||||
|
||||
if (w == nullptr && focusedComp != nullptr)
|
||||
w = focusedComp->findParentComponentOfClass<TopLevelWindow>();
|
||||
|
||||
if (w == nullptr)
|
||||
w = currentActive;
|
||||
|
||||
if (w != nullptr && w->isShowing())
|
||||
return w;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (TopLevelWindowManager)
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (TopLevelWindowManager)
|
||||
|
||||
} // namespace juce::detail
|
||||
49
modules/juce_gui_basics/detail/juce_ViewportHelpers.h
Normal file
49
modules/juce_gui_basics/detail/juce_ViewportHelpers.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct ViewportHelpers
|
||||
{
|
||||
ViewportHelpers() = delete;
|
||||
|
||||
static bool wouldScrollOnEvent (const Viewport* vp, const MouseInputSource& src)
|
||||
{
|
||||
if (vp != nullptr)
|
||||
{
|
||||
switch (vp->getScrollOnDragMode())
|
||||
{
|
||||
case Viewport::ScrollOnDragMode::all: return true;
|
||||
case Viewport::ScrollOnDragMode::nonHover: return ! src.canHover();
|
||||
case Viewport::ScrollOnDragMode::never: return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
54
modules/juce_gui_basics/detail/juce_WindowingHelpers.h
Normal file
54
modules/juce_gui_basics/detail/juce_WindowingHelpers.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce::detail
|
||||
{
|
||||
|
||||
struct WindowingHelpers
|
||||
{
|
||||
WindowingHelpers() = delete;
|
||||
|
||||
static Image createIconForFile (const File& file);
|
||||
|
||||
static bool areThereAnyAlwaysOnTopWindows();
|
||||
|
||||
#if JUCE_WINDOWS
|
||||
static bool isEmbeddedInForegroundProcess (Component* c);
|
||||
static bool isWindowOnCurrentVirtualDesktop (void*);
|
||||
#else
|
||||
static bool isEmbeddedInForegroundProcess (Component*) { return false; }
|
||||
static bool isWindowOnCurrentVirtualDesktop (void*) { return true; }
|
||||
#endif
|
||||
|
||||
/* Returns true if this process is in the foreground, or if the viewComponent
|
||||
is embedded into a window owned by the foreground process.
|
||||
*/
|
||||
static bool isForegroundOrEmbeddedProcess (Component* viewComponent)
|
||||
{
|
||||
return Process::isForegroundProcess() || isEmbeddedInForegroundProcess (viewComponent);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce::detail
|
||||
Loading…
Add table
Add a link
Reference in a new issue