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
9.7KB

  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. namespace RelativeRectangleHelpers
  22. {
  23. inline void skipComma (String::CharPointerType& s)
  24. {
  25. s = s.findEndOfWhitespace();
  26. if (*s == ',')
  27. ++s;
  28. }
  29. static bool dependsOnSymbolsOtherThanThis (const Expression& e)
  30. {
  31. if (e.getType() == Expression::operatorType && e.getSymbolOrFunction() == ".")
  32. return true;
  33. if (e.getType() == Expression::symbolType)
  34. {
  35. switch (RelativeCoordinate::StandardStrings::getTypeOf (e.getSymbolOrFunction()))
  36. {
  37. case RelativeCoordinate::StandardStrings::x:
  38. case RelativeCoordinate::StandardStrings::y:
  39. case RelativeCoordinate::StandardStrings::left:
  40. case RelativeCoordinate::StandardStrings::right:
  41. case RelativeCoordinate::StandardStrings::top:
  42. case RelativeCoordinate::StandardStrings::bottom: return false;
  43. case RelativeCoordinate::StandardStrings::width:
  44. case RelativeCoordinate::StandardStrings::height:
  45. case RelativeCoordinate::StandardStrings::parent:
  46. case RelativeCoordinate::StandardStrings::unknown:
  47. default: break;
  48. }
  49. return true;
  50. }
  51. else
  52. {
  53. for (int i = e.getNumInputs(); --i >= 0;)
  54. if (dependsOnSymbolsOtherThanThis (e.getInput(i)))
  55. return true;
  56. }
  57. return false;
  58. }
  59. }
  60. //==============================================================================
  61. RelativeRectangle::RelativeRectangle()
  62. {
  63. }
  64. RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const RelativeCoordinate& right_,
  65. const RelativeCoordinate& top_, const RelativeCoordinate& bottom_)
  66. : left (left_), right (right_), top (top_), bottom (bottom_)
  67. {
  68. }
  69. RelativeRectangle::RelativeRectangle (const Rectangle<float>& rect)
  70. : left (rect.getX()),
  71. right (Expression::symbol (RelativeCoordinate::Strings::left) + Expression ((double) rect.getWidth())),
  72. top (rect.getY()),
  73. bottom (Expression::symbol (RelativeCoordinate::Strings::top) + Expression ((double) rect.getHeight()))
  74. {
  75. }
  76. RelativeRectangle::RelativeRectangle (const String& s)
  77. {
  78. String error;
  79. String::CharPointerType text (s.getCharPointer());
  80. left = RelativeCoordinate (Expression::parse (text, error));
  81. RelativeRectangleHelpers::skipComma (text);
  82. top = RelativeCoordinate (Expression::parse (text, error));
  83. RelativeRectangleHelpers::skipComma (text);
  84. right = RelativeCoordinate (Expression::parse (text, error));
  85. RelativeRectangleHelpers::skipComma (text);
  86. bottom = RelativeCoordinate (Expression::parse (text, error));
  87. }
  88. bool RelativeRectangle::operator== (const RelativeRectangle& other) const noexcept
  89. {
  90. return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
  91. }
  92. bool RelativeRectangle::operator!= (const RelativeRectangle& other) const noexcept
  93. {
  94. return ! operator== (other);
  95. }
  96. //==============================================================================
  97. // An expression context that can evaluate expressions using "this"
  98. class RelativeRectangleLocalScope : public Expression::Scope
  99. {
  100. public:
  101. RelativeRectangleLocalScope (const RelativeRectangle& rect_) : rect (rect_) {}
  102. Expression getSymbolValue (const String& symbol) const
  103. {
  104. switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol))
  105. {
  106. case RelativeCoordinate::StandardStrings::x:
  107. case RelativeCoordinate::StandardStrings::left: return rect.left.getExpression();
  108. case RelativeCoordinate::StandardStrings::y:
  109. case RelativeCoordinate::StandardStrings::top: return rect.top.getExpression();
  110. case RelativeCoordinate::StandardStrings::right: return rect.right.getExpression();
  111. case RelativeCoordinate::StandardStrings::bottom: return rect.bottom.getExpression();
  112. case RelativeCoordinate::StandardStrings::width:
  113. case RelativeCoordinate::StandardStrings::height:
  114. case RelativeCoordinate::StandardStrings::parent:
  115. case RelativeCoordinate::StandardStrings::unknown:
  116. default: break;
  117. }
  118. return Expression::Scope::getSymbolValue (symbol);
  119. }
  120. private:
  121. const RelativeRectangle& rect;
  122. JUCE_DECLARE_NON_COPYABLE (RelativeRectangleLocalScope)
  123. };
  124. const Rectangle<float> RelativeRectangle::resolve (const Expression::Scope* scope) const
  125. {
  126. if (scope == nullptr)
  127. {
  128. RelativeRectangleLocalScope defaultScope (*this);
  129. return resolve (&defaultScope);
  130. }
  131. else
  132. {
  133. const double l = left.resolve (scope);
  134. const double r = right.resolve (scope);
  135. const double t = top.resolve (scope);
  136. const double b = bottom.resolve (scope);
  137. return Rectangle<float> ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t));
  138. }
  139. }
  140. void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const Expression::Scope* scope)
  141. {
  142. left.moveToAbsolute (newPos.getX(), scope);
  143. right.moveToAbsolute (newPos.getRight(), scope);
  144. top.moveToAbsolute (newPos.getY(), scope);
  145. bottom.moveToAbsolute (newPos.getBottom(), scope);
  146. }
  147. bool RelativeRectangle::isDynamic() const
  148. {
  149. using namespace RelativeRectangleHelpers;
  150. return dependsOnSymbolsOtherThanThis (left.getExpression())
  151. || dependsOnSymbolsOtherThanThis (right.getExpression())
  152. || dependsOnSymbolsOtherThanThis (top.getExpression())
  153. || dependsOnSymbolsOtherThanThis (bottom.getExpression());
  154. }
  155. String RelativeRectangle::toString() const
  156. {
  157. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  158. }
  159. void RelativeRectangle::renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope)
  160. {
  161. left = left.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  162. right = right.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  163. top = top.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  164. bottom = bottom.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  165. }
  166. //==============================================================================
  167. class RelativeRectangleComponentPositioner : public RelativeCoordinatePositionerBase
  168. {
  169. public:
  170. RelativeRectangleComponentPositioner (Component& comp, const RelativeRectangle& r)
  171. : RelativeCoordinatePositionerBase (comp),
  172. rectangle (r)
  173. {
  174. }
  175. bool registerCoordinates() override
  176. {
  177. bool ok = addCoordinate (rectangle.left);
  178. ok = addCoordinate (rectangle.right) && ok;
  179. ok = addCoordinate (rectangle.top) && ok;
  180. ok = addCoordinate (rectangle.bottom) && ok;
  181. return ok;
  182. }
  183. bool isUsingRectangle (const RelativeRectangle& other) const noexcept
  184. {
  185. return rectangle == other;
  186. }
  187. void applyToComponentBounds() override
  188. {
  189. for (int i = 32; --i >= 0;)
  190. {
  191. ComponentScope scope (getComponent());
  192. const Rectangle<int> newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer());
  193. if (newBounds == getComponent().getBounds())
  194. return;
  195. getComponent().setBounds (newBounds);
  196. }
  197. jassertfalse; // Seems to be a recursive reference!
  198. }
  199. void applyNewBounds (const Rectangle<int>& newBounds) override
  200. {
  201. if (newBounds != getComponent().getBounds())
  202. {
  203. ComponentScope scope (getComponent());
  204. rectangle.moveToAbsolute (newBounds.toFloat(), &scope);
  205. applyToComponentBounds();
  206. }
  207. }
  208. private:
  209. RelativeRectangle rectangle;
  210. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner)
  211. };
  212. void RelativeRectangle::applyToComponent (Component& component) const
  213. {
  214. if (isDynamic())
  215. {
  216. RelativeRectangleComponentPositioner* current = dynamic_cast<RelativeRectangleComponentPositioner*> (component.getPositioner());
  217. if (current == nullptr || ! current->isUsingRectangle (*this))
  218. {
  219. RelativeRectangleComponentPositioner* p = new RelativeRectangleComponentPositioner (component, *this);
  220. component.setPositioner (p);
  221. p->apply();
  222. }
  223. }
  224. else
  225. {
  226. component.setPositioner (nullptr);
  227. component.setBounds (resolve (nullptr).getSmallestIntegerContainer());
  228. }
  229. }
  230. } // namespace juce