juce_SidePanel.cpp 8.7 KB

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