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.

juce_DrawableComposite.cpp 12KB

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