The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

192 lines
5.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. TooltipWindow::TooltipWindow (Component* const parent_,
  19. const int millisecondsBeforeTipAppears_)
  20. : Component ("tooltip"),
  21. millisecondsBeforeTipAppears (millisecondsBeforeTipAppears_),
  22. mouseClicks (0),
  23. mouseWheelMoves (0),
  24. lastHideTime (0),
  25. lastComponentUnderMouse (nullptr),
  26. changedCompsSinceShown (true)
  27. {
  28. if (Desktop::getInstance().getMainMouseSource().canHover())
  29. startTimer (123);
  30. setAlwaysOnTop (true);
  31. setOpaque (true);
  32. if (parent_ != nullptr)
  33. parent_->addChildComponent (this);
  34. }
  35. TooltipWindow::~TooltipWindow()
  36. {
  37. hide();
  38. }
  39. void TooltipWindow::setMillisecondsBeforeTipAppears (const int newTimeMs) noexcept
  40. {
  41. millisecondsBeforeTipAppears = newTimeMs;
  42. }
  43. void TooltipWindow::paint (Graphics& g)
  44. {
  45. getLookAndFeel().drawTooltip (g, tipShowing, getWidth(), getHeight());
  46. }
  47. void TooltipWindow::mouseEnter (const MouseEvent&)
  48. {
  49. hide();
  50. }
  51. void TooltipWindow::showFor (const String& tip)
  52. {
  53. jassert (tip.isNotEmpty());
  54. if (tipShowing != tip)
  55. repaint();
  56. tipShowing = tip;
  57. Point<int> mousePos (Desktop::getMousePosition());
  58. Rectangle<int> parentArea;
  59. Component* const parent = getParentComponent();
  60. if (parent != nullptr)
  61. {
  62. mousePos = parent->getLocalPoint (nullptr, mousePos);
  63. parentArea = parent->getLocalBounds();
  64. }
  65. else
  66. {
  67. parentArea = Desktop::getInstance().getDisplays().getDisplayContaining (mousePos).userArea;
  68. }
  69. int w, h;
  70. getLookAndFeel().getTooltipSize (tip, w, h);
  71. int x = mousePos.x;
  72. if (x > parentArea.getCentreX())
  73. x -= (w + 12);
  74. else
  75. x += 24;
  76. int y = mousePos.y;
  77. if (y > parentArea.getCentreY())
  78. y -= (h + 6);
  79. else
  80. y += 6;
  81. x = jlimit (parentArea.getX(), parentArea.getRight() - w, x);
  82. y = jlimit (parentArea.getY(), parentArea.getBottom() - h, y);
  83. setBounds (x, y, w, h);
  84. setVisible (true);
  85. if (parent == nullptr)
  86. addToDesktop (ComponentPeer::windowHasDropShadow
  87. | ComponentPeer::windowIsTemporary
  88. | ComponentPeer::windowIgnoresKeyPresses);
  89. toFront (false);
  90. }
  91. String TooltipWindow::getTipFor (Component* const c)
  92. {
  93. if (c != nullptr
  94. && Process::isForegroundProcess()
  95. && ! Component::isMouseButtonDownAnywhere())
  96. {
  97. TooltipClient* const ttc = dynamic_cast <TooltipClient*> (c);
  98. if (ttc != nullptr && ! c->isCurrentlyBlockedByAnotherModalComponent())
  99. return ttc->getTooltip();
  100. }
  101. return String::empty;
  102. }
  103. void TooltipWindow::hide()
  104. {
  105. tipShowing = String::empty;
  106. removeFromDesktop();
  107. setVisible (false);
  108. }
  109. void TooltipWindow::timerCallback()
  110. {
  111. const unsigned int now = Time::getApproximateMillisecondCounter();
  112. Component* const newComp = Desktop::getInstance().getMainMouseSource().getComponentUnderMouse();
  113. const String newTip (getTipFor (newComp));
  114. const bool tipChanged = (newTip != lastTipUnderMouse || newComp != lastComponentUnderMouse);
  115. lastComponentUnderMouse = newComp;
  116. lastTipUnderMouse = newTip;
  117. Desktop& desktop = Desktop::getInstance();
  118. const int clickCount = desktop.getMouseButtonClickCounter();
  119. const int wheelCount = desktop.getMouseButtonClickCounter();
  120. const bool mouseWasClicked = (clickCount > mouseClicks || wheelCount > mouseWheelMoves);
  121. mouseClicks = clickCount;
  122. mouseWheelMoves = wheelCount;
  123. const Point<int> mousePos (Desktop::getMousePosition());
  124. const bool mouseMovedQuickly = mousePos.getDistanceFrom (lastMousePos) > 12;
  125. lastMousePos = mousePos;
  126. if (tipChanged || mouseWasClicked || mouseMovedQuickly)
  127. lastCompChangeTime = now;
  128. if (isVisible() || now < lastHideTime + 500)
  129. {
  130. // if a tip is currently visible (or has just disappeared), update to a new one
  131. // immediately if needed..
  132. if (newComp == nullptr || mouseWasClicked || newTip.isEmpty())
  133. {
  134. if (isVisible())
  135. {
  136. lastHideTime = now;
  137. hide();
  138. }
  139. }
  140. else if (tipChanged)
  141. {
  142. showFor (newTip);
  143. }
  144. }
  145. else
  146. {
  147. // if there isn't currently a tip, but one is needed, only let it
  148. // appear after a timeout..
  149. if (newTip.isNotEmpty()
  150. && newTip != tipShowing
  151. && now > lastCompChangeTime + millisecondsBeforeTipAppears)
  152. {
  153. showFor (newTip);
  154. }
  155. }
  156. }