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.

360 lines
9.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. MenuBarComponent::MenuBarComponent (MenuBarModel* m)
  22. : model (nullptr),
  23. itemUnderMouse (-1),
  24. currentPopupIndex (-1),
  25. topLevelIndexClicked (0)
  26. {
  27. setRepaintsOnMouseActivity (true);
  28. setWantsKeyboardFocus (false);
  29. setMouseClickGrabsKeyboardFocus (false);
  30. setModel (m);
  31. }
  32. MenuBarComponent::~MenuBarComponent()
  33. {
  34. setModel (nullptr);
  35. Desktop::getInstance().removeGlobalMouseListener (this);
  36. }
  37. MenuBarModel* MenuBarComponent::getModel() const noexcept
  38. {
  39. return model;
  40. }
  41. void MenuBarComponent::setModel (MenuBarModel* const newModel)
  42. {
  43. if (model != newModel)
  44. {
  45. if (model != nullptr)
  46. model->removeListener (this);
  47. model = newModel;
  48. if (model != nullptr)
  49. model->addListener (this);
  50. repaint();
  51. menuBarItemsChanged (nullptr);
  52. }
  53. }
  54. //==============================================================================
  55. void MenuBarComponent::paint (Graphics& g)
  56. {
  57. const bool isMouseOverBar = currentPopupIndex >= 0 || itemUnderMouse >= 0 || isMouseOver();
  58. getLookAndFeel().drawMenuBarBackground (g,
  59. getWidth(),
  60. getHeight(),
  61. isMouseOverBar,
  62. *this);
  63. if (model != nullptr)
  64. {
  65. for (int i = 0; i < menuNames.size(); ++i)
  66. {
  67. Graphics::ScopedSaveState ss (g);
  68. g.setOrigin (xPositions [i], 0);
  69. g.reduceClipRegion (0, 0, xPositions[i + 1] - xPositions[i], getHeight());
  70. getLookAndFeel().drawMenuBarItem (g,
  71. xPositions[i + 1] - xPositions[i],
  72. getHeight(),
  73. i,
  74. menuNames[i],
  75. i == itemUnderMouse,
  76. i == currentPopupIndex,
  77. isMouseOverBar,
  78. *this);
  79. }
  80. }
  81. }
  82. void MenuBarComponent::resized()
  83. {
  84. xPositions.clear();
  85. int x = 0;
  86. xPositions.add (x);
  87. for (int i = 0; i < menuNames.size(); ++i)
  88. {
  89. x += getLookAndFeel().getMenuBarItemWidth (*this, i, menuNames[i]);
  90. xPositions.add (x);
  91. }
  92. }
  93. int MenuBarComponent::getItemAt (Point<int> p)
  94. {
  95. for (int i = 0; i < xPositions.size(); ++i)
  96. if (p.x >= xPositions[i] && p.x < xPositions[i + 1])
  97. return reallyContains (p, true) ? i : -1;
  98. return -1;
  99. }
  100. void MenuBarComponent::repaintMenuItem (int index)
  101. {
  102. if (isPositiveAndBelow (index, xPositions.size()))
  103. {
  104. const int x1 = xPositions [index];
  105. const int x2 = xPositions [index + 1];
  106. repaint (x1 - 2, 0, x2 - x1 + 4, getHeight());
  107. }
  108. }
  109. void MenuBarComponent::setItemUnderMouse (const int index)
  110. {
  111. if (itemUnderMouse != index)
  112. {
  113. repaintMenuItem (itemUnderMouse);
  114. itemUnderMouse = index;
  115. repaintMenuItem (itemUnderMouse);
  116. }
  117. }
  118. void MenuBarComponent::setOpenItem (int index)
  119. {
  120. if (currentPopupIndex != index)
  121. {
  122. if (currentPopupIndex < 0 && index >= 0)
  123. model->handleMenuBarActivate (true);
  124. else if (currentPopupIndex >= 0 && index < 0)
  125. model->handleMenuBarActivate (false);
  126. repaintMenuItem (currentPopupIndex);
  127. currentPopupIndex = index;
  128. repaintMenuItem (currentPopupIndex);
  129. Desktop& desktop = Desktop::getInstance();
  130. if (index >= 0)
  131. desktop.addGlobalMouseListener (this);
  132. else
  133. desktop.removeGlobalMouseListener (this);
  134. }
  135. }
  136. void MenuBarComponent::updateItemUnderMouse (Point<int> p)
  137. {
  138. setItemUnderMouse (getItemAt (p));
  139. }
  140. void MenuBarComponent::showMenu (int index)
  141. {
  142. if (index != currentPopupIndex)
  143. {
  144. PopupMenu::dismissAllActiveMenus();
  145. menuBarItemsChanged (nullptr);
  146. setOpenItem (index);
  147. setItemUnderMouse (index);
  148. if (index >= 0)
  149. {
  150. PopupMenu m (model->getMenuForIndex (itemUnderMouse,
  151. menuNames [itemUnderMouse]));
  152. if (m.lookAndFeel == nullptr)
  153. m.setLookAndFeel (&getLookAndFeel());
  154. const Rectangle<int> itemPos (xPositions [index], 0, xPositions [index + 1] - xPositions [index], getHeight());
  155. m.showMenuAsync (PopupMenu::Options().withTargetComponent (this)
  156. .withTargetScreenArea (localAreaToGlobal (itemPos))
  157. .withMinimumWidth (itemPos.getWidth()),
  158. ModalCallbackFunction::forComponent (menuBarMenuDismissedCallback, this, index));
  159. }
  160. }
  161. }
  162. void MenuBarComponent::menuBarMenuDismissedCallback (int result, MenuBarComponent* bar, int topLevelIndex)
  163. {
  164. if (bar != nullptr)
  165. bar->menuDismissed (topLevelIndex, result);
  166. }
  167. void MenuBarComponent::menuDismissed (int topLevelIndex, int itemId)
  168. {
  169. topLevelIndexClicked = topLevelIndex;
  170. postCommandMessage (itemId);
  171. }
  172. void MenuBarComponent::handleCommandMessage (int commandId)
  173. {
  174. const Point<int> mousePos (getMouseXYRelative());
  175. updateItemUnderMouse (mousePos);
  176. if (currentPopupIndex == topLevelIndexClicked)
  177. setOpenItem (-1);
  178. if (commandId != 0 && model != nullptr)
  179. model->menuItemSelected (commandId, topLevelIndexClicked);
  180. }
  181. //==============================================================================
  182. void MenuBarComponent::mouseEnter (const MouseEvent& e)
  183. {
  184. if (e.eventComponent == this)
  185. updateItemUnderMouse (e.getPosition());
  186. }
  187. void MenuBarComponent::mouseExit (const MouseEvent& e)
  188. {
  189. if (e.eventComponent == this)
  190. updateItemUnderMouse (e.getPosition());
  191. }
  192. void MenuBarComponent::mouseDown (const MouseEvent& e)
  193. {
  194. if (currentPopupIndex < 0)
  195. {
  196. const MouseEvent e2 (e.getEventRelativeTo (this));
  197. updateItemUnderMouse (e2.getPosition());
  198. currentPopupIndex = -2;
  199. showMenu (itemUnderMouse);
  200. }
  201. }
  202. void MenuBarComponent::mouseDrag (const MouseEvent& e)
  203. {
  204. const MouseEvent e2 (e.getEventRelativeTo (this));
  205. const int item = getItemAt (e2.getPosition());
  206. if (item >= 0)
  207. showMenu (item);
  208. }
  209. void MenuBarComponent::mouseUp (const MouseEvent& e)
  210. {
  211. const MouseEvent e2 (e.getEventRelativeTo (this));
  212. updateItemUnderMouse (e2.getPosition());
  213. if (itemUnderMouse < 0 && getLocalBounds().contains (e2.x, e2.y))
  214. {
  215. setOpenItem (-1);
  216. PopupMenu::dismissAllActiveMenus();
  217. }
  218. }
  219. void MenuBarComponent::mouseMove (const MouseEvent& e)
  220. {
  221. const MouseEvent e2 (e.getEventRelativeTo (this));
  222. if (lastMousePos != e2.getPosition())
  223. {
  224. if (currentPopupIndex >= 0)
  225. {
  226. const int item = getItemAt (e2.getPosition());
  227. if (item >= 0)
  228. showMenu (item);
  229. }
  230. else
  231. {
  232. updateItemUnderMouse (e2.getPosition());
  233. }
  234. lastMousePos = e2.getPosition();
  235. }
  236. }
  237. bool MenuBarComponent::keyPressed (const KeyPress& key)
  238. {
  239. const int numMenus = menuNames.size();
  240. if (numMenus > 0)
  241. {
  242. const int currentIndex = jlimit (0, numMenus - 1, currentPopupIndex);
  243. if (key.isKeyCode (KeyPress::leftKey))
  244. {
  245. showMenu ((currentIndex + numMenus - 1) % numMenus);
  246. return true;
  247. }
  248. if (key.isKeyCode (KeyPress::rightKey))
  249. {
  250. showMenu ((currentIndex + 1) % numMenus);
  251. return true;
  252. }
  253. }
  254. return false;
  255. }
  256. void MenuBarComponent::menuBarItemsChanged (MenuBarModel* /*menuBarModel*/)
  257. {
  258. StringArray newNames;
  259. if (model != nullptr)
  260. newNames = model->getMenuBarNames();
  261. if (newNames != menuNames)
  262. {
  263. menuNames = newNames;
  264. repaint();
  265. resized();
  266. }
  267. }
  268. void MenuBarComponent::menuCommandInvoked (MenuBarModel* /*menuBarModel*/,
  269. const ApplicationCommandTarget::InvocationInfo& info)
  270. {
  271. if (model == nullptr || (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) != 0)
  272. return;
  273. for (int i = 0; i < menuNames.size(); ++i)
  274. {
  275. const PopupMenu menu (model->getMenuForIndex (i, menuNames [i]));
  276. if (menu.containsCommandItem (info.commandID))
  277. {
  278. setItemUnderMouse (i);
  279. startTimer (200);
  280. break;
  281. }
  282. }
  283. }
  284. void MenuBarComponent::timerCallback()
  285. {
  286. stopTimer();
  287. updateItemUnderMouse (getMouseXYRelative());
  288. }
  289. } // namespace juce