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.

263 lines
9.1KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace RelativeRectangleHelpers
  18. {
  19. inline void skipComma (String::CharPointerType& s)
  20. {
  21. s = s.findEndOfWhitespace();
  22. if (*s == ',')
  23. ++s;
  24. }
  25. static bool dependsOnSymbolsOtherThanThis (const Expression& e)
  26. {
  27. if (e.getType() == Expression::operatorType && e.getSymbolOrFunction() == ".")
  28. return true;
  29. if (e.getType() == Expression::symbolType)
  30. {
  31. switch (RelativeCoordinate::StandardStrings::getTypeOf (e.getSymbolOrFunction()))
  32. {
  33. case RelativeCoordinate::StandardStrings::x:
  34. case RelativeCoordinate::StandardStrings::y:
  35. case RelativeCoordinate::StandardStrings::left:
  36. case RelativeCoordinate::StandardStrings::right:
  37. case RelativeCoordinate::StandardStrings::top:
  38. case RelativeCoordinate::StandardStrings::bottom: return false;
  39. default: break;
  40. }
  41. return true;
  42. }
  43. else
  44. {
  45. for (int i = e.getNumInputs(); --i >= 0;)
  46. if (dependsOnSymbolsOtherThanThis (e.getInput(i)))
  47. return true;
  48. }
  49. return false;
  50. }
  51. }
  52. //==============================================================================
  53. RelativeRectangle::RelativeRectangle()
  54. {
  55. }
  56. RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const RelativeCoordinate& right_,
  57. const RelativeCoordinate& top_, const RelativeCoordinate& bottom_)
  58. : left (left_), right (right_), top (top_), bottom (bottom_)
  59. {
  60. }
  61. RelativeRectangle::RelativeRectangle (const Rectangle<float>& rect)
  62. : left (rect.getX()),
  63. right (Expression::symbol (RelativeCoordinate::Strings::left) + Expression ((double) rect.getWidth())),
  64. top (rect.getY()),
  65. bottom (Expression::symbol (RelativeCoordinate::Strings::top) + Expression ((double) rect.getHeight()))
  66. {
  67. }
  68. RelativeRectangle::RelativeRectangle (const String& s)
  69. {
  70. String error;
  71. String::CharPointerType text (s.getCharPointer());
  72. left = RelativeCoordinate (Expression::parse (text, error));
  73. RelativeRectangleHelpers::skipComma (text);
  74. top = RelativeCoordinate (Expression::parse (text, error));
  75. RelativeRectangleHelpers::skipComma (text);
  76. right = RelativeCoordinate (Expression::parse (text, error));
  77. RelativeRectangleHelpers::skipComma (text);
  78. bottom = RelativeCoordinate (Expression::parse (text, error));
  79. }
  80. bool RelativeRectangle::operator== (const RelativeRectangle& other) const noexcept
  81. {
  82. return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
  83. }
  84. bool RelativeRectangle::operator!= (const RelativeRectangle& other) const noexcept
  85. {
  86. return ! operator== (other);
  87. }
  88. //==============================================================================
  89. // An expression context that can evaluate expressions using "this"
  90. class RelativeRectangleLocalScope : public Expression::Scope
  91. {
  92. public:
  93. RelativeRectangleLocalScope (const RelativeRectangle& rect_) : rect (rect_) {}
  94. Expression getSymbolValue (const String& symbol) const
  95. {
  96. switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol))
  97. {
  98. case RelativeCoordinate::StandardStrings::x:
  99. case RelativeCoordinate::StandardStrings::left: return rect.left.getExpression();
  100. case RelativeCoordinate::StandardStrings::y:
  101. case RelativeCoordinate::StandardStrings::top: return rect.top.getExpression();
  102. case RelativeCoordinate::StandardStrings::right: return rect.right.getExpression();
  103. case RelativeCoordinate::StandardStrings::bottom: return rect.bottom.getExpression();
  104. default: break;
  105. }
  106. return Expression::Scope::getSymbolValue (symbol);
  107. }
  108. private:
  109. const RelativeRectangle& rect;
  110. JUCE_DECLARE_NON_COPYABLE (RelativeRectangleLocalScope)
  111. };
  112. const Rectangle<float> RelativeRectangle::resolve (const Expression::Scope* scope) const
  113. {
  114. if (scope == nullptr)
  115. {
  116. RelativeRectangleLocalScope defaultScope (*this);
  117. return resolve (&defaultScope);
  118. }
  119. else
  120. {
  121. const double l = left.resolve (scope);
  122. const double r = right.resolve (scope);
  123. const double t = top.resolve (scope);
  124. const double b = bottom.resolve (scope);
  125. return Rectangle<float> ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t));
  126. }
  127. }
  128. void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const Expression::Scope* scope)
  129. {
  130. left.moveToAbsolute (newPos.getX(), scope);
  131. right.moveToAbsolute (newPos.getRight(), scope);
  132. top.moveToAbsolute (newPos.getY(), scope);
  133. bottom.moveToAbsolute (newPos.getBottom(), scope);
  134. }
  135. bool RelativeRectangle::isDynamic() const
  136. {
  137. using namespace RelativeRectangleHelpers;
  138. return dependsOnSymbolsOtherThanThis (left.getExpression())
  139. || dependsOnSymbolsOtherThanThis (right.getExpression())
  140. || dependsOnSymbolsOtherThanThis (top.getExpression())
  141. || dependsOnSymbolsOtherThanThis (bottom.getExpression());
  142. }
  143. String RelativeRectangle::toString() const
  144. {
  145. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  146. }
  147. void RelativeRectangle::renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope)
  148. {
  149. left = left.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  150. right = right.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  151. top = top.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  152. bottom = bottom.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  153. }
  154. //==============================================================================
  155. class RelativeRectangleComponentPositioner : public RelativeCoordinatePositionerBase
  156. {
  157. public:
  158. RelativeRectangleComponentPositioner (Component& comp, const RelativeRectangle& r)
  159. : RelativeCoordinatePositionerBase (comp),
  160. rectangle (r)
  161. {
  162. }
  163. bool registerCoordinates() override
  164. {
  165. bool ok = addCoordinate (rectangle.left);
  166. ok = addCoordinate (rectangle.right) && ok;
  167. ok = addCoordinate (rectangle.top) && ok;
  168. ok = addCoordinate (rectangle.bottom) && ok;
  169. return ok;
  170. }
  171. bool isUsingRectangle (const RelativeRectangle& other) const noexcept
  172. {
  173. return rectangle == other;
  174. }
  175. void applyToComponentBounds() override
  176. {
  177. for (int i = 32; --i >= 0;)
  178. {
  179. ComponentScope scope (getComponent());
  180. const Rectangle<int> newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer());
  181. if (newBounds == getComponent().getBounds())
  182. return;
  183. getComponent().setBounds (newBounds);
  184. }
  185. jassertfalse; // Seems to be a recursive reference!
  186. }
  187. void applyNewBounds (const Rectangle<int>& newBounds) override
  188. {
  189. if (newBounds != getComponent().getBounds())
  190. {
  191. ComponentScope scope (getComponent());
  192. rectangle.moveToAbsolute (newBounds.toFloat(), &scope);
  193. applyToComponentBounds();
  194. }
  195. }
  196. private:
  197. RelativeRectangle rectangle;
  198. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner)
  199. };
  200. void RelativeRectangle::applyToComponent (Component& component) const
  201. {
  202. if (isDynamic())
  203. {
  204. RelativeRectangleComponentPositioner* current = dynamic_cast<RelativeRectangleComponentPositioner*> (component.getPositioner());
  205. if (current == nullptr || ! current->isUsingRectangle (*this))
  206. {
  207. RelativeRectangleComponentPositioner* p = new RelativeRectangleComponentPositioner (component, *this);
  208. component.setPositioner (p);
  209. p->apply();
  210. }
  211. }
  212. else
  213. {
  214. component.setPositioner (nullptr);
  215. component.setBounds (resolve (nullptr).getSmallestIntegerContainer());
  216. }
  217. }