diff --git a/modules/juce_audio_plugin_client/RTAS/juce_RTAS_MacUtilities.mm b/modules/juce_audio_plugin_client/RTAS/juce_RTAS_MacUtilities.mm index 53b840be9c..d3dbc7bfb4 100644 --- a/modules/juce_audio_plugin_client/RTAS/juce_RTAS_MacUtilities.mm +++ b/modules/juce_audio_plugin_client/RTAS/juce_RTAS_MacUtilities.mm @@ -69,7 +69,7 @@ void* attachSubWindow (void* hostWindowRef, Component* comp) f.size.height = comp->getHeight(); [content setFrame: f]; - const int mainScreenHeight = [[[NSScreen screens] objectAtIndex: 0] frame].size.height; + const int mainScreenHeight = getMainScreenHeight(); #if WINDOWPOSITION_BODGE { diff --git a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 6792da31db..ebda571bd2 100644 --- a/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -41,6 +41,23 @@ extern CheckEventBlockedByModalComps isEventBlockedByModalComps; namespace juce { #endif +//============================================================================== +static CGFloat getMainScreenHeight() noexcept +{ + return [[[NSScreen screens] objectAtIndex: 0] frame].size.height; +} + +static void flipScreenRect (NSRect& r) noexcept +{ + r.origin.y = getMainScreenHeight() - (r.origin.y + r.size.height); +} + +static NSRect flippedScreenRect (NSRect r) noexcept +{ + flipScreenRect (r); + return r; +} + //============================================================================== class NSViewComponentPeer : public ComponentPeer { @@ -107,7 +124,7 @@ public: { r.origin.x = (CGFloat) component.getX(); r.origin.y = (CGFloat) component.getY(); - r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - (r.origin.y + r.size.height); + flipScreenRect (r); window = [createWindowInstance() initWithContentRect: r styleMask: getNSWindowStyleMask (windowStyleFlags) @@ -241,9 +258,7 @@ public: } else { - r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - (r.origin.y + r.size.height); - - [window setFrame: [window frameRectForContentRect: r] + [window setFrame: [window frameRectForContentRect: flippedScreenRect (r)] display: true]; } } @@ -263,7 +278,7 @@ public: r.origin = [viewWindow convertBaseToScreen: r.origin]; #endif - r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height; + flipScreenRect (r); } else { @@ -875,13 +890,9 @@ public: #endif ) { - NSRect current = [window frame]; - current.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - current.origin.y - current.size.height; + Rectangle pos (convertToRectInt (flippedScreenRect (r))); + Rectangle original (convertToRectInt (flippedScreenRect ([window frame]))); - r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height; - - Rectangle pos (convertToRectInt (r)); - Rectangle original (convertToRectInt (current)); const Rectangle screenBounds (Desktop::getInstance().getDisplays().getTotalBounds (true)); #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 @@ -898,15 +909,12 @@ public: { constrainer->checkBounds (pos, original, screenBounds, pos.getY() != original.getY() && pos.getBottom() == original.getBottom(), - pos.getX() != original.getX() && pos.getRight() == original.getRight(), + pos.getX() != original.getX() && pos.getRight() == original.getRight(), pos.getY() == original.getY() && pos.getBottom() != original.getBottom(), - pos.getX() == original.getX() && pos.getRight() != original.getRight()); + pos.getX() == original.getX() && pos.getRight() != original.getRight()); } - r.origin.x = pos.getX(); - r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.size.height - pos.getY(); - r.size.width = pos.getWidth(); - r.size.height = pos.getHeight(); + r = flippedScreenRect (makeNSRect (pos)); } return r; @@ -1578,17 +1586,8 @@ private: static NSRect firstRectForCharacterRange (id self, SEL, NSRange) { if (NSViewComponentPeer* const owner = getOwner (self)) - { if (Component* const comp = dynamic_cast (owner->findCurrentTextInputTarget())) - { - const Rectangle bounds (comp->getScreenBounds()); - - return NSMakeRect (bounds.getX(), - [[[NSScreen screens] objectAtIndex: 0] frame].size.height - bounds.getY(), - bounds.getWidth(), - bounds.getHeight()); - } - } + return flippedScreenRect (makeNSRect (comp->getScreenBounds())); return NSZeroRect; } diff --git a/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/modules/juce_gui_basics/native/juce_mac_Windowing.mm index b629729d5a..aa4d3c1f80 100644 --- a/modules/juce_gui_basics/native/juce_mac_Windowing.mm +++ b/modules/juce_gui_basics/native/juce_mac_Windowing.mm @@ -214,7 +214,7 @@ Point MouseInputSource::getCurrentRawMousePosition() JUCE_AUTORELEASEPOOL { const NSPoint p ([NSEvent mouseLocation]); - return Point (roundToInt (p.x), roundToInt ([[[NSScreen screens] objectAtIndex: 0] frame].size.height - p.y)); + return Point (roundToInt (p.x), roundToInt (getMainScreenHeight() - p.y)); } } diff --git a/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h b/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h index d1de9436c3..c93bfe671c 100644 --- a/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h +++ b/modules/juce_gui_extra/misc/juce_SystemTrayIconComponent.h @@ -55,13 +55,27 @@ public: ~SystemTrayIconComponent(); //============================================================================== - /** Changes the image shown in the taskbar. - */ + /** Changes the image shown in the taskbar. */ void setIconImage (const Image& newImage); - /** Changes the tooltip that Windows shows above the icon. */ + /** Changes the icon's tooltip (if the current OS supports this). */ void setIconTooltip (const String& tooltip); + /** Highlights the icon (if the current OS supports this). */ + void setHighlighted (bool); + + /** Shows a floating text bubble pointing to the icon (if the current OS supports this). */ + void showInfoBubble (const String& title, const String& content); + + /** Hides the icon's floating text bubble (if the current OS supports this). */ + void hideInfoBubble(); + + /** Returns the raw handle to whatever kind of internal OS structure is + involved in showing this icon. + @see ComponentPeer::getNativeHandle() + */ + void* getNativeHandle() const; + #if JUCE_LINUX /** @internal */ void paint (Graphics&) override; diff --git a/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp b/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp index d0c3a87723..711daacf02 100644 --- a/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp +++ b/modules/juce_gui_extra/native/juce_linux_SystemTrayIcon.cpp @@ -28,8 +28,7 @@ extern Display* display; class SystemTrayIconComponent::Pimpl { public: - Pimpl (const Image& image_, Window windowH) - : image (image_) + Pimpl (const Image& im, Window windowH) : image (im) { ScopedXLock xlock; @@ -117,7 +116,27 @@ void SystemTrayIconComponent::paint (Graphics& g) RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize, false); } -void SystemTrayIconComponent::setIconTooltip (const String& /* tooltip */) +void SystemTrayIconComponent::setIconTooltip (const String& /*tooltip*/) { - // xxx not yet implemented! + // xxx Not implemented! +} + +void SystemTrayIconComponent::setHighlighted (bool) +{ + // xxx Not implemented! +} + +void SystemTrayIconComponent::showInfoBubble (const String& /*title*/, const String& /*content*/) +{ + // xxx Not implemented! +} + +void SystemTrayIconComponent::hideInfoBubble() +{ + // xxx Not implemented! +} + +void* SystemTrayIconComponent::getNativeHandle() const +{ + return getWindowHandle(); } diff --git a/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp b/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp index b6ea3c1d39..99c91777ef 100644 --- a/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp +++ b/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp @@ -44,6 +44,11 @@ public: statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]; [statusItem setView: view]; + + [[NSNotificationCenter defaultCenter] addObserver: view + selector: @selector (frameChanged:) + name: NSWindowDidMoveNotification + object: nil]; } ~Pimpl() @@ -88,10 +93,6 @@ public: if (([e modifierFlags] & NSCommandKeyMask) != 0) eventMods = eventMods.withFlags (ModifierKeys::commandModifier); - NSRect r = [[e window] frame]; - r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height; - owner.setBounds (convertToRectInt (r)); - const Time now (Time::getCurrentTime()); MouseInputSource mouseSource = Desktop::getInstance().getMainMouseSource(); @@ -117,9 +118,10 @@ public: } } -private: SystemTrayIconComponent& owner; NSStatusItem* statusItem; + +private: NSImage* statusIcon; NSControl* view; bool isHighlighted; @@ -139,6 +141,7 @@ private: addMethod (@selector (mouseDown:), handleEventDown, "v@:@"); addMethod (@selector (rightMouseDown:), handleEventDown, "v@:@"); addMethod (@selector (drawRect:), drawRect, "v@:@"); + addMethod (@selector (frameChanged:), frameChanged, "v@:@"); registerClass(); } @@ -178,6 +181,17 @@ private: fraction: 1.0f]; } } + + static void frameChanged (id self, SEL, NSNotification*) + { + if (Pimpl* const owner = getOwner (self)) + { + NSRect r = [[[owner->statusItem view] window] frame]; + NSRect sr = [[[NSScreen screens] objectAtIndex: 0] frame]; + r.origin.y = sr.size.height - r.origin.y - r.size.height; + owner->owner.setBounds (convertToRectInt (r)); + } + } }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) @@ -204,3 +218,24 @@ void SystemTrayIconComponent::setIconTooltip (const String&) { // xxx not yet implemented! } + +void SystemTrayIconComponent::setHighlighted (bool highlight) +{ + if (pimpl != nullptr) + pimpl->setHighlighted (highlight); +} + +void SystemTrayIconComponent::showInfoBubble (const String& /*title*/, const String& /*content*/) +{ + // xxx Not implemented! +} + +void SystemTrayIconComponent::hideInfoBubble() +{ + // xxx Not implemented! +} + +void* SystemTrayIconComponent::getNativeHandle() const +{ + return pimpl != nullptr ? pimpl->statusItem : nullptr; +} diff --git a/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp b/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp index 077581b246..823bb0476f 100644 --- a/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp +++ b/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp @@ -48,7 +48,7 @@ public: iconData.uCallbackMessage = WM_TRAYNOTIFY; iconData.hIcon = hicon; - Shell_NotifyIcon (NIM_ADD, &iconData); + notify (NIM_ADD); // In order to receive the "TaskbarCreated" message, we need to request that it's not filtered out. // (Need to load dynamically, as ChangeWindowMessageFilter is only available in Vista and later) @@ -64,7 +64,7 @@ public: SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc); iconData.uFlags = 0; - Shell_NotifyIcon (NIM_DELETE, &iconData); + notify (NIM_DELETE); DestroyIcon (iconData.hIcon); } @@ -74,7 +74,7 @@ public: iconData.hIcon = hicon; iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; - Shell_NotifyIcon (NIM_MODIFY, &iconData); + notify (NIM_MODIFY); DestroyIcon (oldIcon); } @@ -83,7 +83,7 @@ public: { iconData.uFlags = NIF_TIP; toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1); - Shell_NotifyIcon (NIM_MODIFY, &iconData); + notify (NIM_MODIFY); } void handleTaskBarEvent (const LPARAM lParam) @@ -162,19 +162,30 @@ public: else if (message == taskbarCreatedMessage) { iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; - Shell_NotifyIcon (NIM_ADD, &iconData); + notify (NIM_ADD); } return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam); } -private: + void showBubble (const String& title, const String& content) + { + iconData.uFlags = 0x10 /*NIF_INFO*/; + title.copyToUTF16 (iconData.szInfoTitle, sizeof (iconData.szInfoTitle) - 1); + content.copyToUTF16 (iconData.szInfo, sizeof (iconData.szInfo) - 1); + notify (NIM_MODIFY); + } + SystemTrayIconComponent& owner; NOTIFYICONDATA iconData; + +private: WNDPROC originalWndProc; const DWORD taskbarCreatedMessage; enum { WM_TRAYNOTIFY = WM_USER + 100 }; + void notify (DWORD message) noexcept { Shell_NotifyIcon (message, &iconData); } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; @@ -201,3 +212,24 @@ void SystemTrayIconComponent::setIconTooltip (const String& tooltip) if (pimpl != nullptr) pimpl->setToolTip (tooltip); } + +void SystemTrayIconComponent::setHighlighted (bool) +{ + // N/A on Windows. +} + +void SystemTrayIconComponent::showInfoBubble (const String& title, const String& content) +{ + if (pimpl != nullptr) + pimpl->showBubble (title, content); +} + +void SystemTrayIconComponent::hideInfoBubble() +{ + showInfoBubble (String::empty, String::empty); +} + +void* SystemTrayIconComponent::getNativeHandle() const +{ + return pimpl != nullptr ? &(pimpl->iconData) : nullptr; +}