diff --git a/modules/juce_events/native/juce_osx_MessageQueue.h b/modules/juce_events/native/juce_osx_MessageQueue.h index 7042f03a14..eee8242eb0 100644 --- a/modules/juce_events/native/juce_osx_MessageQueue.h +++ b/modules/juce_events/native/juce_osx_MessageQueue.h @@ -46,7 +46,7 @@ public: CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes); } - ~MessageQueue() + ~MessageQueue() noexcept { CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes); CFRunLoopSourceInvalidate (runLoopSource); @@ -56,15 +56,20 @@ public: void post (MessageManager::MessageBase* const message) { messages.add (message); - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); + wakeUp(); } private: - ReferenceCountedArray messages; + ReferenceCountedArray messages; CFRunLoopRef runLoop; CFRunLoopSourceRef runLoopSource; + void wakeUp() noexcept + { + CFRunLoopSourceSignal (runLoopSource); + CFRunLoopWakeUp (runLoop); + } + bool deliverNextMessage() { const MessageManager::MessageBase::Ptr nextMessage (messages.removeAndReturn (0)); @@ -84,17 +89,16 @@ private: return true; } - void runLoopCallback() + void runLoopCallback() noexcept { for (int i = 4; --i >= 0;) if (! deliverNextMessage()) return; - CFRunLoopSourceSignal (runLoopSource); - CFRunLoopWakeUp (runLoop); + wakeUp(); } - static void runLoopSourceCallback (void* info) + static void runLoopSourceCallback (void* info) noexcept { static_cast (info)->runLoopCallback(); } diff --git a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h index df34033b39..4210c2acbb 100644 --- a/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h +++ b/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h @@ -29,7 +29,7 @@ class CoreGraphicsContext : public LowLevelGraphicsContext { public: - CoreGraphicsContext (CGContextRef context, const float flipHeight, const float targetScale); + CoreGraphicsContext (CGContextRef context, float flipHeight, float targetScale); ~CoreGraphicsContext(); //============================================================================== diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 69846cf666..b6b551ef48 100644 --- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -59,7 +59,8 @@ static NSRect flippedScreenRect (NSRect r) noexcept } //============================================================================== -class NSViewComponentPeer : public ComponentPeer +class NSViewComponentPeer : public ComponentPeer, + private AsyncUpdater { public: NSViewComponentPeer (Component& comp, const int windowStyleFlags, NSView* viewToAttachTo) @@ -773,6 +774,7 @@ public: handleModifierKeysChange(); } + //============================================================================== void drawRect (NSRect r) { if (r.size.width < 1.0f || r.size.height < 1.0f) @@ -795,10 +797,7 @@ public: if (usingCoreGraphics) { CoreGraphicsContext context (cg, (float) [view frame].size.height, displayScale); - - insideDrawRect = true; - handlePaint (context); - insideDrawRect = false; + invokePaint (context); } else #endif @@ -829,9 +828,7 @@ public: if (intScale != 1) context->addTransform (AffineTransform::scale (displayScale)); - insideDrawRect = true; - handlePaint (*context); - insideDrawRect = false; + invokePaint (*context); } CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); @@ -843,6 +840,62 @@ public: } } + void handleAsyncUpdate() override + { + // When windows are being resized, artificially throttling high-frequency repaints helps + // to stop the event queue getting clogged, and keeps everything working smoothly + if (areAnyWindowsInLiveResize() + && Time::getCurrentTime() < lastRepaintTime + RelativeTime::milliseconds (1000 / 30)) + { + triggerAsyncUpdate(); + return; + } + + for (const Rectangle* i = deferredRepaints.begin(), *e = deferredRepaints.end(); i != e; ++i) + [view setNeedsDisplayInRect: makeNSRect (*i)]; + + deferredRepaints.clear(); + } + + void repaint (const Rectangle& area) override + { + Rectangle r ((float) area.getX(), [view frame].size.height - (float) area.getBottom(), + (float) area.getWidth(), (float) area.getHeight()); + + if (insideDrawRect || areAnyWindowsInLiveResize()) + { + deferredRepaints.add (r); + triggerAsyncUpdate(); + } + else + { + [view setNeedsDisplayInRect: makeNSRect (r)]; + } + } + + void invokePaint (LowLevelGraphicsContext& context) + { + lastRepaintTime = Time::getCurrentTime(); + insideDrawRect = true; + handlePaint (context); + insideDrawRect = false; + } + + void performAnyPendingRepaintsNow() override + { + [view displayIfNeeded]; + } + + static bool areAnyWindowsInLiveResize() noexcept + { + for (NSWindow* w in [NSApp windows]) + if ([w inLiveResize]) + return true; + + return false; + } + + //============================================================================== bool sendModalInputAttemptIfBlocked() { if (Component* modal = Component::getCurrentlyModalComponent()) @@ -1231,43 +1284,6 @@ public: void textInputRequired (Point, TextInputTarget&) override {} - //============================================================================== - void repaint (const Rectangle& area) override - { - if (insideDrawRect) - { - class AsyncRepaintMessage : public CallbackMessage - { - public: - AsyncRepaintMessage (NSViewComponentPeer* const p, const Rectangle& r) - : peer (p), rect (r) - {} - - void messageCallback() override - { - if (ComponentPeer::isValidPeer (peer)) - peer->repaint (rect); - } - - private: - NSViewComponentPeer* const peer; - const Rectangle rect; - }; - - (new AsyncRepaintMessage (this, area))->post(); - } - else - { - [view setNeedsDisplayInRect: NSMakeRect ((CGFloat) area.getX(), [view frame].size.height - (CGFloat) area.getBottom(), - (CGFloat) area.getWidth(), (CGFloat) area.getHeight())]; - } - } - - void performAnyPendingRepaintsNow() override - { - [view displayIfNeeded]; - } - //============================================================================== NSWindow* window; NSView* view; @@ -1276,6 +1292,9 @@ public: String stringBeingComposed; NSNotificationCenter* notificationCenter; + RectangleList deferredRepaints; + Time lastRepaintTime; + static ModifierKeys currentModifiers; static ComponentPeer* currentlyFocusedPeer; static Array keysCurrentlyDown; @@ -1375,24 +1394,22 @@ private: for (int i = ComponentPeer::getNumPeers(); --i >= 0;) { - ComponentPeer* const peer = ComponentPeer::getPeer (i); - NSView* const compView = (NSView*) peer->getNativeHandle(); - - if ([compView window] == w) + if (NSViewComponentPeer* peer = dynamic_cast (ComponentPeer::getPeer (i))) { - if (isKey) + if ([peer->view window] == w) { - if (compView == [w firstResponder]) - return false; - } - else - { - NSViewComponentPeer* nsViewPeer = dynamic_cast (peer); - - if ((nsViewPeer == nullptr || ! nsViewPeer->isSharedWindow) - ? NSPointInRect ([e locationInWindow], NSMakeRect (0, 0, [w frame].size.width, [w frame].size.height)) - : NSPointInRect ([compView convertPoint: [e locationInWindow] fromView: nil], [compView bounds])) - return false; + if (isKey) + { + if (peer->view == [w firstResponder]) + return false; + } + else + { + if (peer->isSharedWindow + ? NSPointInRect ([peer->view convertPoint: [e locationInWindow] fromView: nil], [peer->view bounds]) + : NSPointInRect ([e locationInWindow], NSMakeRect (0, 0, [w frame].size.width, [w frame].size.height))) + return false; + } } } }