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.

247 lines
7.6KB

  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. SidePanel::SidePanel (StringRef title, int width, bool positionOnLeft,
  22. Component* contentToDisplay, bool deleteComponentWhenNoLongerNeeded)
  23. : titleLabel ("titleLabel", title),
  24. isOnLeft (positionOnLeft),
  25. panelWidth (width)
  26. {
  27. lookAndFeelChanged();
  28. addAndMakeVisible (titleLabel);
  29. dismissButton.onClick = [this] { showOrHide (false); };
  30. addAndMakeVisible (dismissButton);
  31. Desktop::getInstance().addGlobalMouseListener (this);
  32. if (contentToDisplay != nullptr)
  33. setContent (contentToDisplay, deleteComponentWhenNoLongerNeeded);
  34. setOpaque (false);
  35. }
  36. SidePanel::~SidePanel()
  37. {
  38. if (parent != nullptr)
  39. parent->removeComponentListener (this);
  40. }
  41. void SidePanel::setContent (Component* newContent, bool deleteComponentWhenNoLongerNeeded)
  42. {
  43. if (contentComponent.get() != newContent)
  44. {
  45. if (deleteComponentWhenNoLongerNeeded)
  46. contentComponent.setOwned (newContent);
  47. else
  48. contentComponent.setNonOwned (newContent);
  49. addAndMakeVisible (contentComponent);
  50. resized();
  51. }
  52. }
  53. void SidePanel::showOrHide (bool show)
  54. {
  55. if (parent != nullptr)
  56. {
  57. isShowing = show;
  58. Desktop::getInstance().getAnimator().animateComponent (this, calculateBoundsInParent (*parent),
  59. 1.0f, 250, true, 1.0, 0.0);
  60. if (onPanelShowHide != nullptr)
  61. onPanelShowHide (isShowing);
  62. }
  63. }
  64. void SidePanel::moved()
  65. {
  66. if (onPanelMove != nullptr)
  67. onPanelMove();
  68. }
  69. void SidePanel::resized()
  70. {
  71. auto bounds = getLocalBounds();
  72. calculateAndRemoveShadowBounds (bounds);
  73. auto titleBounds = bounds.removeFromTop (titleBarHeight);
  74. dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
  75. : titleBounds.removeFromLeft (30).withTrimmedLeft (10));
  76. titleLabel.setBounds (isOnLeft ? titleBounds.withTrimmedRight (40)
  77. : titleBounds.withTrimmedLeft (40));
  78. if (contentComponent != nullptr)
  79. contentComponent->setBounds (bounds);
  80. }
  81. void SidePanel::paint (Graphics& g)
  82. {
  83. auto& lf = getLookAndFeel();
  84. auto bgColour = lf.findColour (SidePanel::backgroundColour);
  85. auto shadowColour = lf.findColour (SidePanel::shadowBaseColour);
  86. g.setGradientFill (ColourGradient (shadowColour.withAlpha (0.7f), (isOnLeft ? shadowArea.getTopLeft()
  87. : shadowArea.getTopRight()).toFloat(),
  88. shadowColour.withAlpha (0.0f), (isOnLeft ? shadowArea.getTopRight()
  89. : shadowArea.getTopLeft()).toFloat(), false));
  90. g.fillRect (shadowArea);
  91. g.excludeClipRegion (shadowArea);
  92. g.fillAll (bgColour);
  93. }
  94. void SidePanel::parentHierarchyChanged()
  95. {
  96. auto* newParent = getParentComponent();
  97. if ((newParent != nullptr) && (parent != newParent))
  98. {
  99. if (parent != nullptr)
  100. parent->removeComponentListener (this);
  101. parent = newParent;
  102. parent->addComponentListener (this);
  103. }
  104. }
  105. void SidePanel::mouseDrag (const MouseEvent& e)
  106. {
  107. if (shouldResize)
  108. {
  109. Point<int> convertedPoint;
  110. if (getParentComponent() == nullptr)
  111. convertedPoint = e.eventComponent->localPointToGlobal (e.getPosition());
  112. else
  113. convertedPoint = getParentComponent()->getLocalPoint (e.eventComponent, e.getPosition());
  114. auto currentMouseDragX = convertedPoint.x;
  115. if (isOnLeft)
  116. {
  117. amountMoved = startingBounds.getRight() - currentMouseDragX;
  118. setBounds (getBounds().withX (startingBounds.getX() - jmax (amountMoved, 0)));
  119. }
  120. else
  121. {
  122. amountMoved = currentMouseDragX - startingBounds.getX();
  123. setBounds (getBounds().withX (startingBounds.getX() + jmax (amountMoved, 0)));
  124. }
  125. }
  126. else if (isShowing)
  127. {
  128. auto relativeMouseDownPosition = getLocalPoint (e.eventComponent, e.getMouseDownPosition());
  129. auto relativeMouseDragPosition = getLocalPoint (e.eventComponent, e.getPosition());
  130. if (! getLocalBounds().contains (relativeMouseDownPosition)
  131. && getLocalBounds().contains (relativeMouseDragPosition))
  132. {
  133. shouldResize = true;
  134. startingBounds = getBounds();
  135. }
  136. }
  137. }
  138. void SidePanel::mouseUp (const MouseEvent&)
  139. {
  140. if (shouldResize)
  141. {
  142. showOrHide (amountMoved < (panelWidth / 2));
  143. amountMoved = 0;
  144. shouldResize = false;
  145. }
  146. }
  147. //==========================================================================
  148. void SidePanel::lookAndFeelChanged()
  149. {
  150. auto& lf = getLookAndFeel();
  151. dismissButton.setShape (lf.getSidePanelDismissButtonShape (*this), false, true, false);
  152. dismissButton.setColours (lf.findColour (SidePanel::dismissButtonNormalColour),
  153. lf.findColour (SidePanel::dismissButtonOverColour),
  154. lf.findColour (SidePanel::dismissButtonDownColour));
  155. titleLabel.setFont (lf.getSidePanelTitleFont (*this));
  156. titleLabel.setColour (Label::textColourId, findColour (SidePanel::titleTextColour));
  157. titleLabel.setJustificationType (lf.getSidePanelTitleJustification (*this));
  158. }
  159. void SidePanel::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
  160. {
  161. ignoreUnused (wasMoved);
  162. if (wasResized && (&component == parent))
  163. setBounds (calculateBoundsInParent (component));
  164. }
  165. Rectangle<int> SidePanel::calculateBoundsInParent (Component& parentComp) const
  166. {
  167. auto parentBounds = parentComp.getBounds();
  168. if (isOnLeft)
  169. {
  170. return isShowing ? parentBounds.removeFromLeft (panelWidth)
  171. : parentBounds.withX (parentBounds.getX() - panelWidth).withWidth (panelWidth);
  172. }
  173. return isShowing ? parentBounds.removeFromRight (panelWidth)
  174. : parentBounds.withX (parentBounds.getRight()).withWidth (panelWidth);
  175. }
  176. void SidePanel::calculateAndRemoveShadowBounds (Rectangle<int>& bounds)
  177. {
  178. shadowArea = isOnLeft ? bounds.removeFromRight (shadowWidth)
  179. : bounds.removeFromLeft (shadowWidth);
  180. }
  181. bool SidePanel::isMouseEventInThisOrChildren (Component* eventComponent)
  182. {
  183. if (eventComponent == this)
  184. return true;
  185. for (auto& child : getChildren())
  186. if (eventComponent == child)
  187. return true;
  188. return false;
  189. }
  190. } // namespace juce