270 lines
9.1 KiB
C++
270 lines
9.1 KiB
C++
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE library.
|
||
|
Copyright (c) 2017 - ROLI Ltd.
|
||
|
|
||
|
JUCE is an open source library subject to commercial or open-source
|
||
|
licensing.
|
||
|
|
||
|
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
|
||
|
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||
|
27th April 2017).
|
||
|
|
||
|
End User License Agreement: www.juce.com/juce-5-licence
|
||
|
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||
|
|
||
|
Or: You may also use this code under the terms of the GPL v3 (see
|
||
|
www.gnu.org/licenses).
|
||
|
|
||
|
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||
|
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||
|
DISCLAIMED.
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
namespace juce
|
||
|
{
|
||
|
|
||
|
namespace RelativeRectangleHelpers
|
||
|
{
|
||
|
inline void skipComma (String::CharPointerType& s)
|
||
|
{
|
||
|
s = s.findEndOfWhitespace();
|
||
|
|
||
|
if (*s == ',')
|
||
|
++s;
|
||
|
}
|
||
|
|
||
|
static bool dependsOnSymbolsOtherThanThis (const Expression& e)
|
||
|
{
|
||
|
if (e.getType() == Expression::operatorType && e.getSymbolOrFunction() == ".")
|
||
|
return true;
|
||
|
|
||
|
if (e.getType() == Expression::symbolType)
|
||
|
{
|
||
|
switch (RelativeCoordinate::StandardStrings::getTypeOf (e.getSymbolOrFunction()))
|
||
|
{
|
||
|
case RelativeCoordinate::StandardStrings::x:
|
||
|
case RelativeCoordinate::StandardStrings::y:
|
||
|
case RelativeCoordinate::StandardStrings::left:
|
||
|
case RelativeCoordinate::StandardStrings::right:
|
||
|
case RelativeCoordinate::StandardStrings::top:
|
||
|
case RelativeCoordinate::StandardStrings::bottom: return false;
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (int i = e.getNumInputs(); --i >= 0;)
|
||
|
if (dependsOnSymbolsOtherThanThis (e.getInput(i)))
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
RelativeRectangle::RelativeRectangle()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
RelativeRectangle::RelativeRectangle (const RelativeCoordinate& left_, const RelativeCoordinate& right_,
|
||
|
const RelativeCoordinate& top_, const RelativeCoordinate& bottom_)
|
||
|
: left (left_), right (right_), top (top_), bottom (bottom_)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
RelativeRectangle::RelativeRectangle (const Rectangle<float>& rect)
|
||
|
: left (rect.getX()),
|
||
|
right (Expression::symbol (RelativeCoordinate::Strings::left) + Expression ((double) rect.getWidth())),
|
||
|
top (rect.getY()),
|
||
|
bottom (Expression::symbol (RelativeCoordinate::Strings::top) + Expression ((double) rect.getHeight()))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
RelativeRectangle::RelativeRectangle (const String& s)
|
||
|
{
|
||
|
String error;
|
||
|
String::CharPointerType text (s.getCharPointer());
|
||
|
left = RelativeCoordinate (Expression::parse (text, error));
|
||
|
RelativeRectangleHelpers::skipComma (text);
|
||
|
top = RelativeCoordinate (Expression::parse (text, error));
|
||
|
RelativeRectangleHelpers::skipComma (text);
|
||
|
right = RelativeCoordinate (Expression::parse (text, error));
|
||
|
RelativeRectangleHelpers::skipComma (text);
|
||
|
bottom = RelativeCoordinate (Expression::parse (text, error));
|
||
|
}
|
||
|
|
||
|
bool RelativeRectangle::operator== (const RelativeRectangle& other) const noexcept
|
||
|
{
|
||
|
return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
|
||
|
}
|
||
|
|
||
|
bool RelativeRectangle::operator!= (const RelativeRectangle& other) const noexcept
|
||
|
{
|
||
|
return ! operator== (other);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
// An expression context that can evaluate expressions using "this"
|
||
|
class RelativeRectangleLocalScope : public Expression::Scope
|
||
|
{
|
||
|
public:
|
||
|
RelativeRectangleLocalScope (const RelativeRectangle& rect_) : rect (rect_) {}
|
||
|
|
||
|
Expression getSymbolValue (const String& symbol) const
|
||
|
{
|
||
|
switch (RelativeCoordinate::StandardStrings::getTypeOf (symbol))
|
||
|
{
|
||
|
case RelativeCoordinate::StandardStrings::x:
|
||
|
case RelativeCoordinate::StandardStrings::left: return rect.left.getExpression();
|
||
|
case RelativeCoordinate::StandardStrings::y:
|
||
|
case RelativeCoordinate::StandardStrings::top: return rect.top.getExpression();
|
||
|
case RelativeCoordinate::StandardStrings::right: return rect.right.getExpression();
|
||
|
case RelativeCoordinate::StandardStrings::bottom: return rect.bottom.getExpression();
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
return Expression::Scope::getSymbolValue (symbol);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
const RelativeRectangle& rect;
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE (RelativeRectangleLocalScope)
|
||
|
};
|
||
|
|
||
|
const Rectangle<float> RelativeRectangle::resolve (const Expression::Scope* scope) const
|
||
|
{
|
||
|
if (scope == nullptr)
|
||
|
{
|
||
|
RelativeRectangleLocalScope defaultScope (*this);
|
||
|
return resolve (&defaultScope);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const double l = left.resolve (scope);
|
||
|
const double r = right.resolve (scope);
|
||
|
const double t = top.resolve (scope);
|
||
|
const double b = bottom.resolve (scope);
|
||
|
|
||
|
return Rectangle<float> ((float) l, (float) t, (float) jmax (0.0, r - l), (float) jmax (0.0, b - t));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RelativeRectangle::moveToAbsolute (const Rectangle<float>& newPos, const Expression::Scope* scope)
|
||
|
{
|
||
|
left.moveToAbsolute (newPos.getX(), scope);
|
||
|
right.moveToAbsolute (newPos.getRight(), scope);
|
||
|
top.moveToAbsolute (newPos.getY(), scope);
|
||
|
bottom.moveToAbsolute (newPos.getBottom(), scope);
|
||
|
}
|
||
|
|
||
|
bool RelativeRectangle::isDynamic() const
|
||
|
{
|
||
|
using namespace RelativeRectangleHelpers;
|
||
|
|
||
|
return dependsOnSymbolsOtherThanThis (left.getExpression())
|
||
|
|| dependsOnSymbolsOtherThanThis (right.getExpression())
|
||
|
|| dependsOnSymbolsOtherThanThis (top.getExpression())
|
||
|
|| dependsOnSymbolsOtherThanThis (bottom.getExpression());
|
||
|
}
|
||
|
|
||
|
String RelativeRectangle::toString() const
|
||
|
{
|
||
|
return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
|
||
|
}
|
||
|
|
||
|
void RelativeRectangle::renameSymbol (const Expression::Symbol& oldSymbol, const String& newName, const Expression::Scope& scope)
|
||
|
{
|
||
|
left = left.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
|
||
|
right = right.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
|
||
|
top = top.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
|
||
|
bottom = bottom.getExpression().withRenamedSymbol (oldSymbol, newName, scope);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
class RelativeRectangleComponentPositioner : public RelativeCoordinatePositionerBase
|
||
|
{
|
||
|
public:
|
||
|
RelativeRectangleComponentPositioner (Component& comp, const RelativeRectangle& r)
|
||
|
: RelativeCoordinatePositionerBase (comp),
|
||
|
rectangle (r)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool registerCoordinates() override
|
||
|
{
|
||
|
bool ok = addCoordinate (rectangle.left);
|
||
|
ok = addCoordinate (rectangle.right) && ok;
|
||
|
ok = addCoordinate (rectangle.top) && ok;
|
||
|
ok = addCoordinate (rectangle.bottom) && ok;
|
||
|
return ok;
|
||
|
}
|
||
|
|
||
|
bool isUsingRectangle (const RelativeRectangle& other) const noexcept
|
||
|
{
|
||
|
return rectangle == other;
|
||
|
}
|
||
|
|
||
|
void applyToComponentBounds() override
|
||
|
{
|
||
|
for (int i = 32; --i >= 0;)
|
||
|
{
|
||
|
ComponentScope scope (getComponent());
|
||
|
const Rectangle<int> newBounds (rectangle.resolve (&scope).getSmallestIntegerContainer());
|
||
|
|
||
|
if (newBounds == getComponent().getBounds())
|
||
|
return;
|
||
|
|
||
|
getComponent().setBounds (newBounds);
|
||
|
}
|
||
|
|
||
|
jassertfalse; // Seems to be a recursive reference!
|
||
|
}
|
||
|
|
||
|
void applyNewBounds (const Rectangle<int>& newBounds) override
|
||
|
{
|
||
|
if (newBounds != getComponent().getBounds())
|
||
|
{
|
||
|
ComponentScope scope (getComponent());
|
||
|
rectangle.moveToAbsolute (newBounds.toFloat(), &scope);
|
||
|
|
||
|
applyToComponentBounds();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
RelativeRectangle rectangle;
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativeRectangleComponentPositioner)
|
||
|
};
|
||
|
|
||
|
void RelativeRectangle::applyToComponent (Component& component) const
|
||
|
{
|
||
|
if (isDynamic())
|
||
|
{
|
||
|
RelativeRectangleComponentPositioner* current = dynamic_cast<RelativeRectangleComponentPositioner*> (component.getPositioner());
|
||
|
|
||
|
if (current == nullptr || ! current->isUsingRectangle (*this))
|
||
|
{
|
||
|
RelativeRectangleComponentPositioner* p = new RelativeRectangleComponentPositioner (component, *this);
|
||
|
|
||
|
component.setPositioner (p);
|
||
|
p->apply();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
component.setPositioner (nullptr);
|
||
|
component.setBounds (resolve (nullptr).getSmallestIntegerContainer());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace juce
|