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.

279 lines
8.8KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #include "../../Application/jucer_Headers.h"
  19. #include "jucer_ComponentLayoutEditor.h"
  20. #include "../jucer_UtilityFunctions.h"
  21. //==============================================================================
  22. ComponentOverlayComponent::ComponentOverlayComponent (Component* const target_,
  23. ComponentLayout& layout_)
  24. : target (target_),
  25. borderThickness (4),
  26. layout (layout_),
  27. selected (false),
  28. dragging (false),
  29. originalAspectRatio (1.0)
  30. {
  31. setMinimumOnscreenAmounts (0, 0, 0, 0);
  32. setSizeLimits (borderThickness * 2 + 2, borderThickness * 2 + 2, 8192, 8192);
  33. border.reset (new ResizableBorderComponent (this, this));
  34. addChildComponent (border.get());
  35. border->setBorderThickness (BorderSize<int> (borderThickness));
  36. target->addComponentListener (this);
  37. changeListenerCallback (nullptr);
  38. layout.getSelectedSet().addChangeListener (this);
  39. setRepaintsOnMouseActivity (true);
  40. border->setRepaintsOnMouseActivity (true);
  41. }
  42. ComponentOverlayComponent::~ComponentOverlayComponent()
  43. {
  44. layout.getSelectedSet().removeChangeListener (this);
  45. if (target != nullptr)
  46. target->removeComponentListener (this);
  47. }
  48. void ComponentOverlayComponent::changeListenerCallback (ChangeBroadcaster*)
  49. {
  50. const bool nowSelected = layout.getSelectedSet().isSelected (target);
  51. if (selected != nowSelected)
  52. {
  53. selected = nowSelected;
  54. border->setVisible (nowSelected);
  55. repaint();
  56. }
  57. }
  58. void ComponentOverlayComponent::paint (Graphics& g)
  59. {
  60. jassert (target != nullptr);
  61. border->setColour (backgroundColourId, Colours::transparentBlack);
  62. if (selected)
  63. {
  64. auto selectedItems = layout.getSelectedSet();
  65. auto baseColour = findColour (defaultHighlightColourId);
  66. const BorderSize<int> borderSize (border->getBorderThickness());
  67. drawResizableBorder (g, getWidth(), getHeight(), borderSize,
  68. (isMouseOverOrDragging() || border->isMouseOverOrDragging()),
  69. baseColour.withAlpha (selectedItems.getSelectedItem (0) == target ? 1.0f : 0.3f));
  70. }
  71. else if (isMouseOverOrDragging())
  72. {
  73. drawMouseOverCorners (g, getWidth(), getHeight());
  74. }
  75. }
  76. void ComponentOverlayComponent::resized()
  77. {
  78. jassert (target != nullptr);
  79. border->setBounds (getLocalBounds());
  80. }
  81. void ComponentOverlayComponent::mouseDown (const MouseEvent& e)
  82. {
  83. dragging = false;
  84. mouseDownSelectStatus = layout.getSelectedSet().addToSelectionOnMouseDown (target, e.mods);
  85. if (e.mods.isPopupMenu())
  86. {
  87. showPopupMenu();
  88. return; // this may be deleted now..
  89. }
  90. }
  91. void ComponentOverlayComponent::mouseDrag (const MouseEvent& e)
  92. {
  93. if (! e.mods.isPopupMenu())
  94. {
  95. if (selected && ! dragging)
  96. {
  97. dragging = e.mouseWasDraggedSinceMouseDown();
  98. if (dragging)
  99. layout.startDragging();
  100. }
  101. if (dragging)
  102. {
  103. layout.dragSelectedComps (e.getDistanceFromDragStartX(),
  104. e.getDistanceFromDragStartY());
  105. }
  106. }
  107. }
  108. void ComponentOverlayComponent::mouseUp (const MouseEvent& e)
  109. {
  110. if (dragging)
  111. layout.endDragging();
  112. layout.getSelectedSet().addToSelectionOnMouseUp (target, e.mods, dragging, mouseDownSelectStatus);
  113. }
  114. void ComponentOverlayComponent::componentMovedOrResized (Component&, bool /*wasMoved*/, bool /*wasResized*/)
  115. {
  116. updateBoundsToMatchTarget();
  117. }
  118. void ComponentOverlayComponent::updateBoundsToMatchTarget()
  119. {
  120. if (Component* const parent = target->getParentComponent())
  121. {
  122. const int dx = parent->getX();
  123. const int dy = parent->getY();
  124. setBounds (dx + target->getX() - borderThickness,
  125. dy + target->getY() - borderThickness,
  126. target->getWidth() + borderThickness * 2,
  127. target->getHeight() + borderThickness * 2);
  128. }
  129. if (border->isMouseButtonDown())
  130. layout.changed();
  131. }
  132. void ComponentOverlayComponent::resizeStart()
  133. {
  134. if (getHeight() > 0)
  135. originalAspectRatio = getWidth() / (double) getHeight();
  136. else
  137. originalAspectRatio = 1.0;
  138. layout.getDocument()->beginTransaction ("Resize components");
  139. }
  140. void ComponentOverlayComponent::resizeEnd()
  141. {
  142. layout.getDocument()->beginTransaction();
  143. }
  144. void ComponentOverlayComponent::checkBounds (Rectangle<int>& b,
  145. const Rectangle<int>& previousBounds,
  146. const Rectangle<int>& limits,
  147. const bool isStretchingTop,
  148. const bool isStretchingLeft,
  149. const bool isStretchingBottom,
  150. const bool isStretchingRight)
  151. {
  152. if (ModifierKeys::currentModifiers.isShiftDown())
  153. setFixedAspectRatio (originalAspectRatio);
  154. else
  155. setFixedAspectRatio (0.0);
  156. ComponentBoundsConstrainer::checkBounds (b, previousBounds, limits, isStretchingTop, isStretchingLeft, isStretchingBottom, isStretchingRight);
  157. if (layout.getDocument()->isSnapActive (true))
  158. {
  159. if (Component* const parent = target->getParentComponent())
  160. {
  161. const int dx = parent->getX();
  162. const int dy = parent->getY();
  163. int x = b.getX();
  164. int y = b.getY();
  165. int w = b.getWidth();
  166. int h = b.getHeight();
  167. x += borderThickness - dx;
  168. y += borderThickness - dy;
  169. w -= borderThickness * 2;
  170. h -= borderThickness * 2;
  171. int right = x + w;
  172. int bottom = y + h;
  173. if (isStretchingRight)
  174. right = layout.getDocument()->snapPosition (right);
  175. if (isStretchingBottom)
  176. bottom = layout.getDocument()->snapPosition (bottom);
  177. if (isStretchingLeft)
  178. x = layout.getDocument()->snapPosition (x);
  179. if (isStretchingTop)
  180. y = layout.getDocument()->snapPosition (y);
  181. w = (right - x) + borderThickness * 2;
  182. h = (bottom - y) + borderThickness * 2;
  183. x -= borderThickness - dx;
  184. y -= borderThickness - dy;
  185. b = Rectangle<int> (x, y, w, h);
  186. }
  187. }
  188. }
  189. void ComponentOverlayComponent::applyBoundsToComponent (Component& component, Rectangle<int> b)
  190. {
  191. if (component.getBounds() != b)
  192. {
  193. layout.getDocument()->getUndoManager().undoCurrentTransactionOnly();
  194. auto dX = b.getX() - component.getX();
  195. auto dY = b.getY() - component.getY();
  196. auto dW = b.getWidth() - component.getWidth();
  197. auto dH = b.getHeight() - component.getHeight();
  198. component.setBounds (b);
  199. if (auto* parent = target->getParentComponent())
  200. target->setBounds (b.getX() + borderThickness - parent->getX(),
  201. b.getY() + borderThickness - parent->getY(),
  202. b.getWidth() - borderThickness * 2,
  203. b.getHeight() - borderThickness * 2);
  204. layout.updateStoredComponentPosition (target, true);
  205. if (layout.getSelectedSet().getNumSelected() > 1)
  206. {
  207. for (auto s : layout.getSelectedSet())
  208. {
  209. if (s != target)
  210. {
  211. s->setBounds (s->getX() + dX, s->getY() + dY, s->getWidth() + dW, s->getHeight() + dH);
  212. layout.updateStoredComponentPosition (s, true);
  213. }
  214. }
  215. }
  216. }
  217. }
  218. void ComponentOverlayComponent::showPopupMenu()
  219. {
  220. ComponentTypeHandler::getHandlerFor (*target)->showPopupMenu (target, layout);
  221. }