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.

330 lines
12KB

  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. DrawableComposite::DrawableComposite()
  20. : bounds (Point<float>(), Point<float> (100.0f, 0.0f), Point<float> (0.0f, 100.0f))
  21. {
  22. setContentArea (RelativeRectangle (Rectangle<float> (0.0f, 0.0f, 100.0f, 100.0f)));
  23. }
  24. DrawableComposite::DrawableComposite (const DrawableComposite& other)
  25. : Drawable (other),
  26. bounds (other.bounds),
  27. markersX (other.markersX),
  28. markersY (other.markersY)
  29. {
  30. for (auto* c : other.getChildren())
  31. if (auto* d = dynamic_cast<const Drawable*> (c))
  32. addAndMakeVisible (d->createCopy());
  33. }
  34. DrawableComposite::~DrawableComposite()
  35. {
  36. deleteAllChildren();
  37. }
  38. Drawable* DrawableComposite::createCopy() const
  39. {
  40. return new DrawableComposite (*this);
  41. }
  42. //==============================================================================
  43. Rectangle<float> DrawableComposite::getDrawableBounds() const
  44. {
  45. Rectangle<float> r;
  46. for (auto* c : getChildren())
  47. if (auto* d = dynamic_cast<const Drawable*> (c))
  48. r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform())
  49. : d->getDrawableBounds());
  50. return r;
  51. }
  52. MarkerList* DrawableComposite::getMarkers (bool xAxis)
  53. {
  54. return xAxis ? &markersX : &markersY;
  55. }
  56. RelativeRectangle DrawableComposite::getContentArea() const
  57. {
  58. jassert (markersX.getNumMarkers() >= 2 && markersX.getMarker (0)->name == contentLeftMarkerName && markersX.getMarker (1)->name == contentRightMarkerName);
  59. jassert (markersY.getNumMarkers() >= 2 && markersY.getMarker (0)->name == contentTopMarkerName && markersY.getMarker (1)->name == contentBottomMarkerName);
  60. return RelativeRectangle (markersX.getMarker(0)->position, markersX.getMarker(1)->position,
  61. markersY.getMarker(0)->position, markersY.getMarker(1)->position);
  62. }
  63. void DrawableComposite::setContentArea (const RelativeRectangle& newArea)
  64. {
  65. markersX.setMarker (contentLeftMarkerName, newArea.left);
  66. markersX.setMarker (contentRightMarkerName, newArea.right);
  67. markersY.setMarker (contentTopMarkerName, newArea.top);
  68. markersY.setMarker (contentBottomMarkerName, newArea.bottom);
  69. }
  70. void DrawableComposite::setBoundingBox (const RelativeParallelogram& newBounds)
  71. {
  72. if (bounds != newBounds)
  73. {
  74. bounds = newBounds;
  75. if (bounds.isDynamic())
  76. {
  77. auto p = new Drawable::Positioner<DrawableComposite> (*this);
  78. setPositioner (p);
  79. p->apply();
  80. }
  81. else
  82. {
  83. setPositioner (nullptr);
  84. recalculateCoordinates (nullptr);
  85. }
  86. }
  87. }
  88. void DrawableComposite::resetBoundingBoxToContentArea()
  89. {
  90. const RelativeRectangle content (getContentArea());
  91. setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
  92. RelativePoint (content.right, content.top),
  93. RelativePoint (content.left, content.bottom)));
  94. }
  95. void DrawableComposite::resetContentAreaAndBoundingBoxToFitChildren()
  96. {
  97. setContentArea (RelativeRectangle (getDrawableBounds()));
  98. resetBoundingBoxToContentArea();
  99. }
  100. bool DrawableComposite::registerCoordinates (RelativeCoordinatePositionerBase& pos)
  101. {
  102. bool ok = pos.addPoint (bounds.topLeft);
  103. ok = pos.addPoint (bounds.topRight) && ok;
  104. return pos.addPoint (bounds.bottomLeft) && ok;
  105. }
  106. void DrawableComposite::recalculateCoordinates (Expression::Scope* scope)
  107. {
  108. Point<float> resolved[3];
  109. bounds.resolveThreePoints (resolved, scope);
  110. auto content = getContentArea().resolve (scope);
  111. auto t = AffineTransform::fromTargetPoints (content.getX(), content.getY(), resolved[0].x, resolved[0].y,
  112. content.getRight(), content.getY(), resolved[1].x, resolved[1].y,
  113. content.getX(), content.getBottom(), resolved[2].x, resolved[2].y);
  114. if (t.isSingularity())
  115. t = AffineTransform();
  116. setTransform (t);
  117. }
  118. void DrawableComposite::parentHierarchyChanged()
  119. {
  120. if (auto* parent = getParent())
  121. originRelativeToComponent = parent->originRelativeToComponent - getPosition();
  122. }
  123. void DrawableComposite::childBoundsChanged (Component*)
  124. {
  125. updateBoundsToFitChildren();
  126. }
  127. void DrawableComposite::childrenChanged()
  128. {
  129. updateBoundsToFitChildren();
  130. }
  131. void DrawableComposite::updateBoundsToFitChildren()
  132. {
  133. if (! updateBoundsReentrant)
  134. {
  135. const ScopedValueSetter<bool> setter (updateBoundsReentrant, true, false);
  136. Rectangle<int> childArea;
  137. for (auto* c : getChildren())
  138. childArea = childArea.getUnion (c->getBoundsInParent());
  139. auto delta = childArea.getPosition();
  140. childArea += getPosition();
  141. if (childArea != getBounds())
  142. {
  143. if (! delta.isOrigin())
  144. {
  145. originRelativeToComponent -= delta;
  146. for (auto* c : getChildren())
  147. c->setBounds (c->getBounds() - delta);
  148. }
  149. setBounds (childArea);
  150. }
  151. }
  152. }
  153. //==============================================================================
  154. const char* const DrawableComposite::contentLeftMarkerName = "left";
  155. const char* const DrawableComposite::contentRightMarkerName = "right";
  156. const char* const DrawableComposite::contentTopMarkerName = "top";
  157. const char* const DrawableComposite::contentBottomMarkerName = "bottom";
  158. //==============================================================================
  159. const Identifier DrawableComposite::valueTreeType ("Group");
  160. const Identifier DrawableComposite::ValueTreeWrapper::topLeft ("topLeft");
  161. const Identifier DrawableComposite::ValueTreeWrapper::topRight ("topRight");
  162. const Identifier DrawableComposite::ValueTreeWrapper::bottomLeft ("bottomLeft");
  163. const Identifier DrawableComposite::ValueTreeWrapper::childGroupTag ("Drawables");
  164. const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagX ("MarkersX");
  165. const Identifier DrawableComposite::ValueTreeWrapper::markerGroupTagY ("MarkersY");
  166. //==============================================================================
  167. DrawableComposite::ValueTreeWrapper::ValueTreeWrapper (const ValueTree& state_)
  168. : ValueTreeWrapperBase (state_)
  169. {
  170. jassert (state.hasType (valueTreeType));
  171. }
  172. ValueTree DrawableComposite::ValueTreeWrapper::getChildList() const
  173. {
  174. return state.getChildWithName (childGroupTag);
  175. }
  176. ValueTree DrawableComposite::ValueTreeWrapper::getChildListCreating (UndoManager* undoManager)
  177. {
  178. return state.getOrCreateChildWithName (childGroupTag, undoManager);
  179. }
  180. RelativeParallelogram DrawableComposite::ValueTreeWrapper::getBoundingBox() const
  181. {
  182. return RelativeParallelogram (state.getProperty (topLeft, "0, 0"),
  183. state.getProperty (topRight, "100, 0"),
  184. state.getProperty (bottomLeft, "0, 100"));
  185. }
  186. void DrawableComposite::ValueTreeWrapper::setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager)
  187. {
  188. state.setProperty (topLeft, newBounds.topLeft.toString(), undoManager);
  189. state.setProperty (topRight, newBounds.topRight.toString(), undoManager);
  190. state.setProperty (bottomLeft, newBounds.bottomLeft.toString(), undoManager);
  191. }
  192. void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoManager* undoManager)
  193. {
  194. const RelativeRectangle content (getContentArea());
  195. setBoundingBox (RelativeParallelogram (RelativePoint (content.left, content.top),
  196. RelativePoint (content.right, content.top),
  197. RelativePoint (content.left, content.bottom)), undoManager);
  198. }
  199. RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const
  200. {
  201. MarkerList::ValueTreeWrapper marksX (getMarkerList (true));
  202. MarkerList::ValueTreeWrapper marksY (getMarkerList (false));
  203. return RelativeRectangle (marksX.getMarker (marksX.getMarkerState (0)).position,
  204. marksX.getMarker (marksX.getMarkerState (1)).position,
  205. marksY.getMarker (marksY.getMarkerState (0)).position,
  206. marksY.getMarker (marksY.getMarkerState (1)).position);
  207. }
  208. void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager)
  209. {
  210. MarkerList::ValueTreeWrapper marksX (getMarkerListCreating (true, nullptr));
  211. MarkerList::ValueTreeWrapper marksY (getMarkerListCreating (false, nullptr));
  212. marksX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager);
  213. marksX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager);
  214. marksY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager);
  215. marksY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager);
  216. }
  217. MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const
  218. {
  219. return state.getChildWithName (xAxis ? markerGroupTagX : markerGroupTagY);
  220. }
  221. MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerListCreating (bool xAxis, UndoManager* undoManager)
  222. {
  223. return state.getOrCreateChildWithName (xAxis ? markerGroupTagX : markerGroupTagY, undoManager);
  224. }
  225. //==============================================================================
  226. void DrawableComposite::refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder)
  227. {
  228. const ValueTreeWrapper wrapper (tree);
  229. setComponentID (wrapper.getID());
  230. wrapper.getMarkerList (true).applyTo (markersX);
  231. wrapper.getMarkerList (false).applyTo (markersY);
  232. setBoundingBox (wrapper.getBoundingBox());
  233. builder.updateChildComponents (*this, wrapper.getChildList());
  234. }
  235. ValueTree DrawableComposite::createValueTree (ComponentBuilder::ImageProvider* imageProvider) const
  236. {
  237. ValueTree tree (valueTreeType);
  238. ValueTreeWrapper v (tree);
  239. v.setID (getComponentID());
  240. v.setBoundingBox (bounds, nullptr);
  241. ValueTree childList (v.getChildListCreating (nullptr));
  242. for (auto* c : getChildren())
  243. {
  244. auto* d = dynamic_cast<const Drawable*> (c);
  245. jassert (d != nullptr); // You can't save a mix of Drawables and normal components!
  246. childList.addChild (d->createValueTree (imageProvider), -1, nullptr);
  247. }
  248. v.getMarkerListCreating (true, nullptr).readFrom (markersX, nullptr);
  249. v.getMarkerListCreating (false, nullptr).readFrom (markersY, nullptr);
  250. return tree;
  251. }
  252. Path DrawableComposite::getOutlineAsPath() const
  253. {
  254. Path p;
  255. for (auto* c : getChildren())
  256. if (auto* d = dynamic_cast<Drawable*> (c))
  257. p.addPath (d->getOutlineAsPath());
  258. p.applyTransform (getTransform());
  259. return p;
  260. }