| @@ -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 | |||
| { | |||
| @@ -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<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)); | |||
| #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 <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; | |||
| } | |||
| @@ -214,7 +214,7 @@ Point<int> MouseInputSource::getCurrentRawMousePosition() | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| 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(); | |||
| //============================================================================== | |||
| /** 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; | |||
| @@ -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(); | |||
| } | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||