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.

271 lines
9.4KB

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