juce_RelativeRectangle.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software 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::CharPointerType text (s.getCharPointer());
  71. left = RelativeCoordinate (Expression::parse (text));
  72. RelativeRectangleHelpers::skipComma (text);
  73. top = RelativeCoordinate (Expression::parse (text));
  74. RelativeRectangleHelpers::skipComma (text);
  75. right = RelativeCoordinate (Expression::parse (text));
  76. RelativeRectangleHelpers::skipComma (text);
  77. bottom = RelativeCoordinate (Expression::parse (text));
  78. }
  79. bool RelativeRectangle::operator== (const RelativeRectangle& other) const noexcept
  80. {
  81. return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
  82. }
  83. bool RelativeRectangle::operator!= (const RelativeRectangle& other) const noexcept
  84. {
  85. return ! operator== (other);
  86. }
  87. //==============================================================================
  88. // An expression context that can evaluate expressions using "this"
  89. class RelativeRectangleLocalScope : public Expression::Scope
  90. {
  91. public:
  92. RelativeRectangleLocalScope (const RelativeRectangle& rect_) : rect (rect_) {}
  93. Expression getSymbolValue (const String& symbol) const
  94. {
  95. switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol))
  96. {
  97. case RelativeCoordinate::StandardStrings::x:
  98. case RelativeCoordinate::StandardStrings::left: return rect.left.getExpression();
  99. case RelativeCoordinate::StandardStrings::y:
  100. case RelativeCoordinate::StandardStrings::top: return rect.top.getExpression();
  101. case RelativeCoordinate::StandardStrings::right: return rect.right.getExpression();
  102. case RelativeCoordinate::StandardStrings::bottom: return rect.bottom.getExpression();
  103. default: break;
  104. }
  105. return Expression::Scope::getSymbolValue (symbol);
  106. }
  107. private:
  108. const RelativeRectangle& rect;
  109. JUCE_DECLARE_NON_COPYABLE (RelativeRectangleLocalScope)
  110. };
  111. const Rectangle<float> RelativeRectangle::resolve (const Expression::Scope* scope) const
  112. {
  113. if (scope == nullptr)
  114. {
  115. RelativeRectangleLocalScope defaultScope (*this);
  116. return resolve (&defaultScope);
  117. }
  118. else
  119. {
  120. const double l = left.resolve (scope);
  121. const double r = right.resolve (scope);
  122. const double t = top.resolve (scope);
  123. const double b = bottom.resolve (scope);
  124. return Rectangle<float> ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t));
  125. }
  126. }
  127. void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const Expression::Scope* scope)
  128. {
  129. left.moveToAbsolute (newPos.getX(), scope);
  130. right.moveToAbsolute (newPos.getRight(), scope);
  131. top.moveToAbsolute (newPos.getY(), scope);
  132. bottom.moveToAbsolute (newPos.getBottom(), scope);
  133. }
  134. bool RelativeRectangle::isDynamic() const
  135. {
  136. using namespace RelativeRectangleHelpers;
  137. return dependsOnSymbolsOtherThanThis (left.getExpression())
  138. || dependsOnSymbolsOtherThanThis (right.getExpression())
  139. || dependsOnSymbolsOtherThanThis (top.getExpression())
  140. || dependsOnSymbolsOtherThanThis (bottom.getExpression());
  141. }
  142. String RelativeRectangle::toString() const
  143. {
  144. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  145. }
  146. void RelativeRectangle::renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope)
  147. {
  148. left = left.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  149. right = right.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  150. top = top.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  151. bottom = bottom.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
  152. }
  153. //==============================================================================
  154. class RelativeRectangleComponentPositioner : public RelativeCoordinatePositionerBase
  155. {
  156. public:
  157. RelativeRectangleComponentPositioner (Component& comp, const RelativeRectangle& r)
  158. : RelativeCoordinatePositionerBase (comp),
  159. rectangle (r)
  160. {
  161. }
  162. bool registerCoordinates()
  163. {
  164. bool ok = addCoordinate (rectangle.left);
  165. ok = addCoordinate (rectangle.right) && ok;
  166. ok = addCoordinate (rectangle.top) && ok;
  167. ok = addCoordinate (rectangle.bottom) && ok;
  168. return ok;
  169. }
  170. bool isUsingRectangle (const RelativeRectangle& other) const noexcept
  171. {
  172. return rectangle == other;
  173. }
  174. void applyToComponentBounds()
  175. {
  176. for (int i = 32; --i >= 0;)
  177. {
  178. ComponentScope scope (getComponent());
  179. const Rectangle<int> newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer());
  180. if (newBounds == getComponent().getBounds())
  181. return;
  182. getComponent().setBounds (newBounds);
  183. }
  184. jassertfalse; // Seems to be a recursive reference!
  185. }
  186. void applyNewBounds (const Rectangle<int>& newBounds)
  187. {
  188. if (newBounds != getComponent().getBounds())
  189. {
  190. ComponentScope scope (getComponent());
  191. rectangle.moveToAbsolute (newBounds.toFloat(), &scope);
  192. applyToComponentBounds();
  193. }
  194. }
  195. private:
  196. RelativeRectangle rectangle;
  197. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner)
  198. };
  199. void RelativeRectangle::applyToComponent (Component& component) const
  200. {
  201. if (isDynamic())
  202. {
  203. RelativeRectangleComponentPositioner* current = dynamic_cast <RelativeRectangleComponentPositioner*> (component.getPositioner());
  204. if (current == nullptr || ! current->isUsingRectangle (*this))
  205. {
  206. RelativeRectangleComponentPositioner* p = new RelativeRectangleComponentPositioner (component, *this);
  207. component.setPositioner (p);
  208. p->apply();
  209. }
  210. }
  211. else
  212. {
  213. component.setPositioner (nullptr);
  214. component.setBounds (resolve (nullptr).getSmallestIntegerContainer());
  215. }
  216. }