| @@ -69,7 +69,7 @@ void* attachSubWindow (void* hostWindowRef, Component* comp) | |||||
| f.size.height = comp->getHeight(); | f.size.height = comp->getHeight(); | ||||
| [content setFrame: f]; | [content setFrame: f]; | ||||
| const int mainScreenHeight = [[[NSScreen screens] objectAtIndex: 0] frame].size.height; | |||||
| const int mainScreenHeight = getMainScreenHeight(); | |||||
| #if WINDOWPOSITION_BODGE | #if WINDOWPOSITION_BODGE | ||||
| { | { | ||||
| @@ -41,6 +41,23 @@ extern CheckEventBlockedByModalComps isEventBlockedByModalComps; | |||||
| namespace juce { | namespace juce { | ||||
| #endif | #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 | class NSViewComponentPeer : public ComponentPeer | ||||
| { | { | ||||
| @@ -107,7 +124,7 @@ public: | |||||
| { | { | ||||
| r.origin.x = (CGFloat) component.getX(); | r.origin.x = (CGFloat) component.getX(); | ||||
| r.origin.y = (CGFloat) component.getY(); | 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 | window = [createWindowInstance() initWithContentRect: r | ||||
| styleMask: getNSWindowStyleMask (windowStyleFlags) | styleMask: getNSWindowStyleMask (windowStyleFlags) | ||||
| @@ -241,9 +258,7 @@ public: | |||||
| } | } | ||||
| else | 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]; | display: true]; | ||||
| } | } | ||||
| } | } | ||||
| @@ -263,7 +278,7 @@ public: | |||||
| r.origin = [viewWindow convertBaseToScreen: r.origin]; | r.origin = [viewWindow convertBaseToScreen: r.origin]; | ||||
| #endif | #endif | ||||
| r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height; | |||||
| flipScreenRect (r); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -875,13 +890,9 @@ public: | |||||
| #endif | #endif | ||||
| ) | ) | ||||
| { | { | ||||
| NSRect current = [window frame]; | |||||
| current.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - current.origin.y - current.size.height; | |||||
| Rectangle<int> pos (convertToRectInt (flippedScreenRect (r))); | |||||
| Rectangle<int> original (convertToRectInt (flippedScreenRect ([window frame]))); | |||||
| r.origin.y = [[[NSScreen screens] objectAtIndex: 0] frame].size.height - r.origin.y - r.size.height; | |||||
| Rectangle<int> pos (convertToRectInt (r)); | |||||
| Rectangle<int> original (convertToRectInt (current)); | |||||
| const Rectangle<int> screenBounds (Desktop::getInstance().getDisplays().getTotalBounds (true)); | const Rectangle<int> 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 | #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, | constrainer->checkBounds (pos, original, screenBounds, | ||||
| pos.getY() != original.getY() && pos.getBottom() == original.getBottom(), | 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.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; | return r; | ||||
| @@ -1578,17 +1586,8 @@ private: | |||||
| static NSRect firstRectForCharacterRange (id self, SEL, NSRange) | static NSRect firstRectForCharacterRange (id self, SEL, NSRange) | ||||
| { | { | ||||
| if (NSViewComponentPeer* const owner = getOwner (self)) | if (NSViewComponentPeer* const owner = getOwner (self)) | ||||
| { | |||||
| if (Component* const comp = dynamic_cast <Component*> (owner->findCurrentTextInputTarget())) | if (Component* const comp = dynamic_cast <Component*> (owner->findCurrentTextInputTarget())) | ||||
| { | |||||
| const Rectangle<int> 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; | return NSZeroRect; | ||||
| } | } | ||||
| @@ -214,7 +214,7 @@ Point<int> MouseInputSource::getCurrentRawMousePosition() | |||||
| JUCE_AUTORELEASEPOOL | JUCE_AUTORELEASEPOOL | ||||
| { | { | ||||
| const NSPoint p ([NSEvent mouseLocation]); | const NSPoint p ([NSEvent mouseLocation]); | ||||
| return Point<int> (roundToInt (p.x), roundToInt ([[[NSScreen screens] objectAtIndex: 0] frame].size.height - p.y)); | |||||
| return Point<int> (roundToInt (p.x), roundToInt (getMainScreenHeight() - p.y)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -55,13 +55,27 @@ public: | |||||
| ~SystemTrayIconComponent(); | ~SystemTrayIconComponent(); | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Changes the image shown in the taskbar. | |||||
| */ | |||||
| /** Changes the image shown in the taskbar. */ | |||||
| void setIconImage (const Image& newImage); | 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); | 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 | #if JUCE_LINUX | ||||
| /** @internal */ | /** @internal */ | ||||
| void paint (Graphics&) override; | void paint (Graphics&) override; | ||||
| @@ -28,8 +28,7 @@ extern Display* display; | |||||
| class SystemTrayIconComponent::Pimpl | class SystemTrayIconComponent::Pimpl | ||||
| { | { | ||||
| public: | public: | ||||
| Pimpl (const Image& image_, Window windowH) | |||||
| : image (image_) | |||||
| Pimpl (const Image& im, Window windowH) : image (im) | |||||
| { | { | ||||
| ScopedXLock xlock; | ScopedXLock xlock; | ||||
| @@ -117,7 +116,27 @@ void SystemTrayIconComponent::paint (Graphics& g) | |||||
| RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize, false); | 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(); | |||||
| } | } | ||||
| @@ -44,6 +44,11 @@ public: | |||||
| statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]; | statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain]; | ||||
| [statusItem setView: view]; | [statusItem setView: view]; | ||||
| [[NSNotificationCenter defaultCenter] addObserver: view | |||||
| selector: @selector (frameChanged:) | |||||
| name: NSWindowDidMoveNotification | |||||
| object: nil]; | |||||
| } | } | ||||
| ~Pimpl() | ~Pimpl() | ||||
| @@ -88,10 +93,6 @@ public: | |||||
| if (([e modifierFlags] & NSCommandKeyMask) != 0) | if (([e modifierFlags] & NSCommandKeyMask) != 0) | ||||
| eventMods = eventMods.withFlags (ModifierKeys::commandModifier); | 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()); | const Time now (Time::getCurrentTime()); | ||||
| MouseInputSource mouseSource = Desktop::getInstance().getMainMouseSource(); | MouseInputSource mouseSource = Desktop::getInstance().getMainMouseSource(); | ||||
| @@ -117,9 +118,10 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| private: | |||||
| SystemTrayIconComponent& owner; | SystemTrayIconComponent& owner; | ||||
| NSStatusItem* statusItem; | NSStatusItem* statusItem; | ||||
| private: | |||||
| NSImage* statusIcon; | NSImage* statusIcon; | ||||
| NSControl* view; | NSControl* view; | ||||
| bool isHighlighted; | bool isHighlighted; | ||||
| @@ -139,6 +141,7 @@ private: | |||||
| addMethod (@selector (mouseDown:), handleEventDown, "v@:@"); | addMethod (@selector (mouseDown:), handleEventDown, "v@:@"); | ||||
| addMethod (@selector (rightMouseDown:), handleEventDown, "v@:@"); | addMethod (@selector (rightMouseDown:), handleEventDown, "v@:@"); | ||||
| addMethod (@selector (drawRect:), drawRect, "v@:@"); | addMethod (@selector (drawRect:), drawRect, "v@:@"); | ||||
| addMethod (@selector (frameChanged:), frameChanged, "v@:@"); | |||||
| registerClass(); | registerClass(); | ||||
| } | } | ||||
| @@ -178,6 +181,17 @@ private: | |||||
| fraction: 1.0f]; | 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) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | ||||
| @@ -204,3 +218,24 @@ void SystemTrayIconComponent::setIconTooltip (const String&) | |||||
| { | { | ||||
| // xxx not yet implemented! | // 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; | |||||
| } | |||||
| @@ -48,7 +48,7 @@ public: | |||||
| iconData.uCallbackMessage = WM_TRAYNOTIFY; | iconData.uCallbackMessage = WM_TRAYNOTIFY; | ||||
| iconData.hIcon = hicon; | 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. | // 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) | // (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); | SetWindowLongPtr (iconData.hWnd, GWLP_WNDPROC, (LONG_PTR) originalWndProc); | ||||
| iconData.uFlags = 0; | iconData.uFlags = 0; | ||||
| Shell_NotifyIcon (NIM_DELETE, &iconData); | |||||
| notify (NIM_DELETE); | |||||
| DestroyIcon (iconData.hIcon); | DestroyIcon (iconData.hIcon); | ||||
| } | } | ||||
| @@ -74,7 +74,7 @@ public: | |||||
| iconData.hIcon = hicon; | iconData.hIcon = hicon; | ||||
| iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; | iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; | ||||
| Shell_NotifyIcon (NIM_MODIFY, &iconData); | |||||
| notify (NIM_MODIFY); | |||||
| DestroyIcon (oldIcon); | DestroyIcon (oldIcon); | ||||
| } | } | ||||
| @@ -83,7 +83,7 @@ public: | |||||
| { | { | ||||
| iconData.uFlags = NIF_TIP; | iconData.uFlags = NIF_TIP; | ||||
| toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1); | toolTip.copyToUTF16 (iconData.szTip, sizeof (iconData.szTip) - 1); | ||||
| Shell_NotifyIcon (NIM_MODIFY, &iconData); | |||||
| notify (NIM_MODIFY); | |||||
| } | } | ||||
| void handleTaskBarEvent (const LPARAM lParam) | void handleTaskBarEvent (const LPARAM lParam) | ||||
| @@ -162,19 +162,30 @@ public: | |||||
| else if (message == taskbarCreatedMessage) | else if (message == taskbarCreatedMessage) | ||||
| { | { | ||||
| iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; | iconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; | ||||
| Shell_NotifyIcon (NIM_ADD, &iconData); | |||||
| notify (NIM_ADD); | |||||
| } | } | ||||
| return CallWindowProc (originalWndProc, hwnd, message, wParam, lParam); | 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; | SystemTrayIconComponent& owner; | ||||
| NOTIFYICONDATA iconData; | NOTIFYICONDATA iconData; | ||||
| private: | |||||
| WNDPROC originalWndProc; | WNDPROC originalWndProc; | ||||
| const DWORD taskbarCreatedMessage; | const DWORD taskbarCreatedMessage; | ||||
| enum { WM_TRAYNOTIFY = WM_USER + 100 }; | enum { WM_TRAYNOTIFY = WM_USER + 100 }; | ||||
| void notify (DWORD message) noexcept { Shell_NotifyIcon (message, &iconData); } | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | ||||
| }; | }; | ||||
| @@ -201,3 +212,24 @@ void SystemTrayIconComponent::setIconTooltip (const String& tooltip) | |||||
| if (pimpl != nullptr) | if (pimpl != nullptr) | ||||
| pimpl->setToolTip (tooltip); | 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; | |||||
| } | |||||