Browse Source

NSViewComponentPeer: Allow mouse events to reach unfocused windows

This change allows mouse events (including enter and exit events) to
reach unfocused views on macOS. This matches the behaviour of unfocused
windows on Linux and Windows, where components paint in their "hovered"
states even when the application window is in the background.

As a byproduct of using tracking areas on macOS, we can remove the fake
mouse move generator.
v6.1.6
reuk 4 years ago
parent
commit
4ca923a34b
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
9 changed files with 58 additions and 190 deletions
  1. +0
    -4
      modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp
  2. +0
    -4
      modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
  3. +0
    -1
      modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterApp.cpp
  4. +0
    -3
      modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  5. +0
    -1
      modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.mm
  6. +0
    -4
      modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
  7. +0
    -119
      modules/juce_audio_plugin_client/utility/juce_FakeMouseMoveGenerator.h
  8. +7
    -7
      modules/juce_gui_basics/components/juce_Component.cpp
  9. +51
    -47
      modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm

+ 0
- 4
modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp View File

@@ -31,7 +31,6 @@
#include "../utility/juce_IncludeSystemHeaders.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_WindowsHooks.h"
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
@@ -592,8 +591,6 @@ namespace AAXClasses
setBounds (lastValidSize);
pluginEditor->addMouseListener (this, true);
}
ignoreUnused (fakeMouseGenerator);
}
~ContentWrapperComponent() override
@@ -673,7 +670,6 @@ namespace AAXClasses
#if JUCE_WINDOWS
WindowsHooks hooks;
#endif
FakeMouseMoveGenerator fakeMouseGenerator;
juce::Rectangle<int> lastValidSize;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentWrapperComponent)


+ 0
- 4
modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm View File

@@ -81,7 +81,6 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include "../utility/juce_CarbonVisibility.h"
#include <juce_audio_basics/native/juce_mac_CoreAudioLayouts.h>
@@ -1482,7 +1481,6 @@ public:
setWantsKeyboardFocus (true);
#endif
ignoreUnused (fakeMouseGenerator);
setBounds (getSizeToContainChild());
lastBounds = getBounds();
@@ -1594,7 +1592,6 @@ public:
}
private:
FakeMouseMoveGenerator fakeMouseGenerator;
Rectangle<int> lastBounds;
JUCE_DECLARE_NON_COPYABLE (EditorCompHolder)
@@ -2432,7 +2429,6 @@ private:
//==============================================================================
AudioProcessor* juceFilter;
std::unique_ptr<Component> windowComp;
FakeMouseMoveGenerator fakeMouseGenerator;
void deleteUI()
{


+ 0
- 1
modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterApp.cpp View File

@@ -28,7 +28,6 @@
#include "../utility/juce_IncludeSystemHeaders.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include "../utility/juce_WindowsHooks.h"
#include <juce_audio_devices/juce_audio_devices.h>


+ 0
- 3
modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp View File

@@ -103,7 +103,6 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE
using namespace juce;
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include "../utility/juce_WindowsHooks.h"
#include "../utility/juce_LinuxMessageThread.h"
@@ -984,7 +983,6 @@ public:
#endif
setOpaque (true);
ignoreUnused (fakeMouseGenerator);
}
~EditorCompWrapper() override
@@ -1291,7 +1289,6 @@ public:
//==============================================================================
JuceVSTWrapper& wrapper;
FakeMouseMoveGenerator fakeMouseGenerator;
bool resizingChild = false, resizingParent = false;
float editorScaleFactor = 1.0f;


+ 0
- 1
modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.mm View File

@@ -35,7 +35,6 @@
#include "../utility/juce_IncludeSystemHeaders.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include "../utility/juce_CarbonVisibility.h"
//==============================================================================


+ 0
- 4
modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp View File

@@ -48,7 +48,6 @@ JUCE_BEGIN_NO_SANITIZE ("vptr")
#include "../utility/juce_IncludeSystemHeaders.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_WindowsHooks.h"
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include "../utility/juce_LinuxMessageThread.h"
#include <juce_audio_processors/format_types/juce_LegacyAudioParameter.cpp>
#include <juce_audio_processors/format_types/juce_VST3Common.h>
@@ -1929,8 +1928,6 @@ private:
{
setOpaque (true);
setBroughtToFrontOnMouseClick (true);
ignoreUnused (fakeMouseGenerator);
}
~ContentWrapperComponent() override
@@ -2100,7 +2097,6 @@ private:
private:
JuceVST3Editor& owner;
std::unique_ptr<EditorHostContext> editorHostContext;
FakeMouseMoveGenerator fakeMouseGenerator;
Rectangle<int> lastBounds;
bool resizingChild = false, resizingParent = false;


+ 0
- 119
modules/juce_audio_plugin_client/utility/juce_FakeMouseMoveGenerator.h View File

@@ -1,119 +0,0 @@
/*
==============================================================================
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
{
#ifndef DOXYGEN
#if JUCE_MAC
//==============================================================================
// Helper class to workaround windows not getting mouse-moves...
class FakeMouseMoveGenerator : private Timer
{
public:
FakeMouseMoveGenerator()
{
startTimer (1000 / 30);
}
static bool componentContainsAudioProcessorEditor (Component* comp) noexcept
{
if (dynamic_cast<AudioProcessorEditor*> (comp) != nullptr)
return true;
for (auto* child : comp->getChildren())
if (componentContainsAudioProcessorEditor (child))
return true;
return false;
}
void timerCallback() override
{
// Workaround for windows not getting mouse-moves...
auto screenPos = Desktop::getInstance().getMainMouseSource().getScreenPosition();
if (screenPos != lastScreenPos)
{
lastScreenPos = screenPos;
auto mods = ModifierKeys::currentModifiers;
if (! mods.isAnyMouseButtonDown())
{
if (auto* comp = Desktop::getInstance().findComponentAt (screenPos.roundToInt()))
{
if (componentContainsAudioProcessorEditor (comp->getTopLevelComponent()))
{
safeOldComponent = comp;
if (auto* peer = comp->getPeer())
{
if (! peer->isFocused())
{
peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse,
peer->globalToLocal (Desktop::getInstance().getMainMouseSource().getRawScreenPosition()),
mods,
MouseInputSource::invalidPressure,
MouseInputSource::invalidOrientation,
Time::currentTimeMillis());
}
}
return;
}
}
if (safeOldComponent != nullptr)
{
if (auto* peer = safeOldComponent->getPeer())
{
peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse,
MouseInputSource::offscreenMousePos,
mods,
MouseInputSource::invalidPressure,
MouseInputSource::invalidOrientation,
Time::currentTimeMillis());
}
}
safeOldComponent = nullptr;
}
}
}
private:
Point<float> lastScreenPos;
WeakReference<Component> safeOldComponent;
};
#else
struct FakeMouseMoveGenerator {};
#endif
#endif
} // namespace juce

+ 7
- 7
modules/juce_gui_basics/components/juce_Component.cpp View File

@@ -301,11 +301,11 @@ struct Component::ComponentHelpers
}
//==============================================================================
static bool hitTest (Component& comp, Point<int> localPoint)
static bool hitTest (Component& comp, Point<float> localPoint)
{
return isPositiveAndBelow (localPoint.x, comp.getWidth())
&& isPositiveAndBelow (localPoint.y, comp.getHeight())
&& comp.hitTest (localPoint.x, localPoint.y);
const auto intPoint = localPoint.roundToInt();
return Rectangle<int> { comp.getWidth(), comp.getHeight() }.toFloat().contains (localPoint)
&& comp.hitTest (intPoint.x, intPoint.y);
}
// converts an unscaled position within a peer to the local position within that peer's component
@@ -1376,7 +1376,7 @@ bool Component::hitTest (int x, int y)
auto& child = *childComponentList.getUnchecked (i);
if (child.isVisible()
&& ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point<int> (x, y))))
&& ComponentHelpers::hitTest (child, ComponentHelpers::convertFromParentSpace (child, Point<int> (x, y).toFloat())))
return true;
}
}
@@ -1405,7 +1405,7 @@ bool Component::contains (Point<int> point)
bool Component::containsInternal (Point<float> point)
{
if (ComponentHelpers::hitTest (*this, point.roundToInt()))
if (ComponentHelpers::hitTest (*this, point))
{
if (parentComponent != nullptr)
return parentComponent->containsInternal (ComponentHelpers::convertToParentSpace (*this, point));
@@ -1441,7 +1441,7 @@ Component* Component::getComponentAt (Point<int> position)
Component* Component::getComponentAtInternal (Point<float> position)
{
if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position.roundToInt()))
if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position))
{
for (int i = childComponentList.size(); --i >= 0;)
{


+ 51
- 47
modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm View File

@@ -61,6 +61,16 @@ public:
[view registerForDraggedTypes: getSupportedDragTypes()];
const auto options = NSTrackingMouseEnteredAndExited
| NSTrackingMouseMoved
| NSTrackingEnabledDuringMouseDrag
| NSTrackingActiveAlways
| NSTrackingInVisibleRect;
[view addTrackingArea: [[NSTrackingArea alloc] initWithRect: r
options: options
owner: view
userInfo: nil]];
notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver: view
@@ -118,7 +128,6 @@ public:
setAlwaysOnTop (true);
[window setContentView: view];
[window setAcceptsMouseMovedEvents: YES];
// We'll both retain and also release this on closing because plugin hosts can unexpectedly
// close the window for us, and also tend to get cause trouble if setReleasedWhenClosed is NO.
@@ -392,7 +401,7 @@ public:
NSRect viewFrame = [view frame];
if (! (isPositiveAndBelow (localPos.getX(), viewFrame.size.width)
&& isPositiveAndBelow (localPos.getY(), viewFrame.size.height)))
&& isPositiveAndBelow (localPos.getY(), viewFrame.size.height)))
return false;
if (! SystemStats::isRunningInAppExtensionSandbox())
@@ -630,21 +639,12 @@ public:
void redirectMouseEnter (NSEvent* ev)
{
if (shouldIgnoreMouseEnterExit (ev))
return;
Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate();
ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
sendMouseEvent (ev);
sendMouseEnterExit (ev);
}
void redirectMouseExit (NSEvent* ev)
{
if (shouldIgnoreMouseEnterExit (ev))
return;
ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons();
sendMouseEvent (ev);
sendMouseEnterExit (ev);
}
static float checkDeviceDeltaReturnValue (float v) noexcept
@@ -906,11 +906,11 @@ public:
}
#endif
drawRect (cg, r, displayScale);
drawRectWithContext (cg, r, displayScale);
invalidateTransparentWindowShadow();
}
void drawRect (CGContextRef cg, NSRect r, float displayScale)
void drawRectWithContext (CGContextRef cg, NSRect r, float displayScale)
{
#if USE_COREGRAPHICS_RENDERING
if (usingCoreGraphics)
@@ -1531,10 +1531,13 @@ private:
static NSView* createViewInstance();
static NSWindow* createWindowInstance();
bool shouldIgnoreMouseEnterExit (NSEvent* ev) const
void sendMouseEnterExit (NSEvent* ev)
{
auto* eventTrackingArea = [ev trackingArea];
return eventTrackingArea != nil && ! [[view trackingAreas] containsObject: eventTrackingArea];
if (auto* area = [ev trackingArea])
if (! [[view trackingAreas] containsObject: area])
return;
sendMouseEvent (ev);
}
static void setOwner (id viewOrWindow, NSViewComponentPeer* newOwner)
@@ -1851,30 +1854,30 @@ private:
}
}
static void asyncMouseDown (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseDown (ev); }
static void asyncMouseUp (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseUp (ev); }
static void mouseDragged (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseDrag (ev); }
static void mouseMoved (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseMove (ev); }
static void mouseEntered (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseEnter (ev); }
static void mouseExited (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseExit (ev); }
static void scrollWheel (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMouseWheel (ev); }
static void magnify (id self, SEL, NSEvent* ev) { if (auto* p = getOwner (self)) p->redirectMagnify (ev); }
static void copy (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectCopy (s); }
static void paste (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectPaste (s); }
static void cut (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectCut (s); }
static void selectAll (id self, SEL, NSObject* s) { if (auto* p = getOwner (self)) p->redirectSelectAll (s); }
static void willMoveToWindow (id self, SEL, NSWindow* w) { if (auto* p = getOwner (self)) p->redirectWillMoveToWindow (w); }
static void asyncMouseDown (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseDown, ev); }
static void asyncMouseUp (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseUp, ev); }
static void mouseDragged (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseDrag, ev); }
static void mouseMoved (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseMove, ev); }
static void mouseEntered (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseEnter, ev); }
static void mouseExited (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseExit, ev); }
static void scrollWheel (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMouseWheel, ev); }
static void magnify (id self, SEL, NSEvent* ev) { callOnOwner (self, &NSViewComponentPeer::redirectMagnify, ev); }
static void copy (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectCopy, s); }
static void paste (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectPaste, s); }
static void cut (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectCut, s); }
static void selectAll (id self, SEL, NSObject* s) { callOnOwner (self, &NSViewComponentPeer::redirectSelectAll, s); }
static void willMoveToWindow (id self, SEL, NSWindow* w) { callOnOwner (self, &NSViewComponentPeer::redirectWillMoveToWindow, w); }
static BOOL acceptsFirstMouse (id, SEL, NSEvent*) { return YES; }
static BOOL wantsDefaultClipping (id, SEL) { return YES; } // (this is the default, but may want to customise it in future)
static BOOL worksWhenModal (id self, SEL) { if (auto* p = getOwner (self)) return p->worksWhenModal(); return NO; }
static void drawRect (id self, SEL, NSRect r) { if (auto* p = getOwner (self)) p->drawRect (r); }
static void frameChanged (id self, SEL, NSNotification*) { if (auto* p = getOwner (self)) p->redirectMovedOrResized(); }
static void viewDidMoveToWindow (id self, SEL) { if (auto* p = getOwner (self)) p->viewMovedToWindow(); }
static void dismissModals (id self, SEL) { if (auto* p = getOwner (self)) p->dismissModals(); }
static void becomeKey (id self, SEL) { if (auto* p = getOwner (self)) p->becomeKey(); }
static void resignKey (id self, SEL) { if (auto* p = getOwner (self)) p->resignKey(); }
static void drawRect (id self, SEL, NSRect r) { callOnOwner (self, &NSViewComponentPeer::drawRect, r); }
static void frameChanged (id self, SEL, NSNotification*) { callOnOwner (self, &NSViewComponentPeer::redirectMovedOrResized); }
static void viewDidMoveToWindow (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::viewMovedToWindow); }
static void dismissModals (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::dismissModals); }
static void becomeKey (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::becomeKey); }
static void resignKey (id self, SEL) { callOnOwner (self, &NSViewComponentPeer::resignKey); }
static BOOL isFlipped (id, SEL) { return true; }
@@ -2075,23 +2078,18 @@ private:
//==============================================================================
static void flagsChanged (id self, SEL, NSEvent* ev)
{
if (auto* owner = getOwner (self))
owner->redirectModKeyChange (ev);
callOnOwner (self, &NSViewComponentPeer::redirectModKeyChange, ev);
}
static BOOL becomeFirstResponder (id self, SEL)
{
if (auto* owner = getOwner (self))
owner->viewFocusGain();
callOnOwner (self, &NSViewComponentPeer::viewFocusGain);
return YES;
}
static BOOL resignFirstResponder (id self, SEL)
{
if (auto* owner = getOwner (self))
owner->viewFocusLoss();
callOnOwner (self, &NSViewComponentPeer::viewFocusLoss);
return YES;
}
@@ -2123,8 +2121,7 @@ private:
static void draggingExited (id self, SEL, id<NSDraggingInfo> sender)
{
if (auto* owner = getOwner (self))
owner->sendDragCallback (1, sender);
callOnOwner (self, &NSViewComponentPeer::sendDragCallback, 1, sender);
}
static BOOL prepareForDragOperation (id, SEL, id<NSDraggingInfo>)
@@ -2201,6 +2198,13 @@ private:
return sendSuperclassMessage<BOOL> (self, s, event);
}
template <typename Func, typename... Args>
static void callOnOwner (id self, Func&& func, Args&&... args)
{
if (auto* owner = getOwner (self))
(owner->*func) (std::forward<Args> (args)...);
}
};
//==============================================================================


Loading…
Cancel
Save