Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

275 lines
9.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace MouseCursorHelpers
  18. {
  19. extern NSImage* createNSImage (const Image&, float scaleFactor = 1.f);
  20. }
  21. extern NSMenu* createNSMenu (const PopupMenu&, const String& name, int topLevelMenuId,
  22. int topLevelIndex, bool addDelegate);
  23. class SystemTrayIconComponent::Pimpl : private Timer
  24. {
  25. public:
  26. Pimpl (SystemTrayIconComponent& iconComp, const Image& im)
  27. : owner (iconComp), statusItem (nil),
  28. statusIcon (MouseCursorHelpers::createNSImage (im)),
  29. view (nil), isHighlighted (false)
  30. {
  31. static SystemTrayViewClass cls;
  32. view = [cls.createInstance() init];
  33. SystemTrayViewClass::setOwner (view, this);
  34. SystemTrayViewClass::setImage (view, statusIcon);
  35. setIconSize();
  36. statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength: NSSquareStatusItemLength] retain];
  37. [statusItem setView: view];
  38. SystemTrayViewClass::frameChanged (view, SEL(), nullptr);
  39. [[NSNotificationCenter defaultCenter] addObserver: view
  40. selector: @selector (frameChanged:)
  41. name: NSWindowDidMoveNotification
  42. object: nil];
  43. }
  44. ~Pimpl()
  45. {
  46. [[NSNotificationCenter defaultCenter] removeObserver: view];
  47. [[NSStatusBar systemStatusBar] removeStatusItem: statusItem];
  48. SystemTrayViewClass::setOwner (view, nullptr);
  49. SystemTrayViewClass::setImage (view, nil);
  50. [statusItem release];
  51. [view release];
  52. [statusIcon release];
  53. }
  54. void updateIcon (const Image& newImage)
  55. {
  56. [statusIcon release];
  57. statusIcon = MouseCursorHelpers::createNSImage (newImage);
  58. setIconSize();
  59. SystemTrayViewClass::setImage (view, statusIcon);
  60. [statusItem setView: view];
  61. }
  62. void setHighlighted (bool shouldHighlight)
  63. {
  64. isHighlighted = shouldHighlight;
  65. [view setNeedsDisplay: true];
  66. }
  67. void handleStatusItemAction (NSEvent* e)
  68. {
  69. NSEventType type = [e type];
  70. const bool isLeft = (type == NSEventTypeLeftMouseDown || type == NSEventTypeLeftMouseUp);
  71. const bool isRight = (type == NSEventTypeRightMouseDown || type == NSEventTypeRightMouseUp);
  72. if (owner.isCurrentlyBlockedByAnotherModalComponent())
  73. {
  74. if (isLeft || isRight)
  75. if (Component* const current = Component::getCurrentlyModalComponent())
  76. current->inputAttemptWhenModal();
  77. }
  78. else
  79. {
  80. ModifierKeys eventMods (ModifierKeys::getCurrentModifiersRealtime());
  81. if (([e modifierFlags] & NSEventModifierFlagCommand) != 0)
  82. eventMods = eventMods.withFlags (ModifierKeys::commandModifier);
  83. const Time now (Time::getCurrentTime());
  84. MouseInputSource mouseSource = Desktop::getInstance().getMainMouseSource();
  85. const float pressure = (float) e.pressure;
  86. if (isLeft || isRight) // Only mouse up is sent by the OS, so simulate a down/up
  87. {
  88. setHighlighted (true);
  89. startTimer (150);
  90. owner.mouseDown (MouseEvent (mouseSource, Point<float>(),
  91. eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier
  92. : ModifierKeys::rightButtonModifier),
  93. pressure, &owner, &owner, now,
  94. Point<float>(), now, 1, false));
  95. owner.mouseUp (MouseEvent (mouseSource, Point<float>(), eventMods.withoutMouseButtons(),
  96. pressure, &owner, &owner, now,
  97. Point<float>(), now, 1, false));
  98. }
  99. else if (type == NSEventTypeMouseMoved)
  100. {
  101. owner.mouseMove (MouseEvent (mouseSource, Point<float>(), eventMods,
  102. pressure, &owner, &owner, now,
  103. Point<float>(), now, 1, false));
  104. }
  105. }
  106. }
  107. void showMenu (const PopupMenu& menu)
  108. {
  109. if (NSMenu* m = createNSMenu (menu, "MenuBarItem", -2, -3, true))
  110. {
  111. setHighlighted (true);
  112. stopTimer();
  113. [statusItem popUpStatusItemMenu: m];
  114. startTimer (1);
  115. }
  116. }
  117. SystemTrayIconComponent& owner;
  118. NSStatusItem* statusItem;
  119. private:
  120. NSImage* statusIcon;
  121. NSControl* view;
  122. bool isHighlighted;
  123. void setIconSize()
  124. {
  125. [statusIcon setSize: NSMakeSize (20.0f, 20.0f)];
  126. }
  127. void timerCallback() override
  128. {
  129. stopTimer();
  130. setHighlighted (false);
  131. }
  132. struct SystemTrayViewClass : public ObjCClass<NSControl>
  133. {
  134. SystemTrayViewClass() : ObjCClass<NSControl> ("JUCESystemTrayView_")
  135. {
  136. addIvar<Pimpl*> ("owner");
  137. addIvar<NSImage*> ("image");
  138. addMethod (@selector (mouseDown:), handleEventDown, "v@:@");
  139. addMethod (@selector (rightMouseDown:), handleEventDown, "v@:@");
  140. addMethod (@selector (drawRect:), drawRect, "v@:@");
  141. addMethod (@selector (frameChanged:), frameChanged, "v@:@");
  142. registerClass();
  143. }
  144. static Pimpl* getOwner (id self) { return getIvar<Pimpl*> (self, "owner"); }
  145. static NSImage* getImage (id self) { return getIvar<NSImage*> (self, "image"); }
  146. static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); }
  147. static void setImage (id self, NSImage* image) { object_setInstanceVariable (self, "image", image); }
  148. static void frameChanged (id self, SEL, NSNotification*)
  149. {
  150. if (Pimpl* const owner = getOwner (self))
  151. {
  152. NSRect r = [[[owner->statusItem view] window] frame];
  153. NSRect sr = [[[NSScreen screens] objectAtIndex: 0] frame];
  154. r.origin.y = sr.size.height - r.origin.y - r.size.height;
  155. owner->owner.setBounds (convertToRectInt (r));
  156. }
  157. }
  158. private:
  159. static void handleEventDown (id self, SEL, NSEvent* e)
  160. {
  161. if (Pimpl* const owner = getOwner (self))
  162. owner->handleStatusItemAction (e);
  163. }
  164. static void drawRect (id self, SEL, NSRect)
  165. {
  166. NSRect bounds = [self bounds];
  167. if (Pimpl* const owner = getOwner (self))
  168. [owner->statusItem drawStatusBarBackgroundInRect: bounds
  169. withHighlight: owner->isHighlighted];
  170. if (NSImage* const im = getImage (self))
  171. {
  172. NSSize imageSize = [im size];
  173. [im drawInRect: NSMakeRect (bounds.origin.x + ((bounds.size.width - imageSize.width) / 2.0f),
  174. bounds.origin.y + ((bounds.size.height - imageSize.height) / 2.0f),
  175. imageSize.width, imageSize.height)
  176. fromRect: NSZeroRect
  177. operation: NSCompositingOperationSourceOver
  178. fraction: 1.0f];
  179. }
  180. }
  181. };
  182. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
  183. };
  184. //==============================================================================
  185. void SystemTrayIconComponent::setIconImage (const Image& newImage)
  186. {
  187. if (newImage.isValid())
  188. {
  189. if (pimpl == nullptr)
  190. pimpl = new Pimpl (*this, newImage);
  191. else
  192. pimpl->updateIcon (newImage);
  193. }
  194. else
  195. {
  196. pimpl = nullptr;
  197. }
  198. }
  199. void SystemTrayIconComponent::setIconTooltip (const String&)
  200. {
  201. // xxx not yet implemented!
  202. }
  203. void SystemTrayIconComponent::setHighlighted (bool highlight)
  204. {
  205. if (pimpl != nullptr)
  206. pimpl->setHighlighted (highlight);
  207. }
  208. void SystemTrayIconComponent::showInfoBubble (const String& /*title*/, const String& /*content*/)
  209. {
  210. // xxx Not implemented!
  211. }
  212. void SystemTrayIconComponent::hideInfoBubble()
  213. {
  214. // xxx Not implemented!
  215. }
  216. void* SystemTrayIconComponent::getNativeHandle() const
  217. {
  218. return pimpl != nullptr ? pimpl->statusItem : nullptr;
  219. }
  220. void SystemTrayIconComponent::showDropdownMenu (const PopupMenu& menu)
  221. {
  222. if (pimpl != nullptr)
  223. pimpl->showMenu (menu);
  224. }