/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2020 - Raw Material Software Limited 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 6 End-User License Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). End User License Agreement: www.juce.com/juce-6-licence Privacy Policy: www.juce.com/juce-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. ============================================================================== */ #include "../Application/jucer_Headers.h" #include "jucer_JucerDocument.h" #include "jucer_ObjectTypes.h" #include "UI/jucer_JucerDocumentEditor.h" #include "Components/jucer_ComponentUndoableAction.h" //============================================================================== ComponentLayout::ComponentLayout() : document (nullptr), nextCompUID (1) { } ComponentLayout::~ComponentLayout() { components.clear(); } //============================================================================== void ComponentLayout::changed() { if (document != nullptr) document->changed(); } void ComponentLayout::perform (UndoableAction* action, const String& actionName) { jassert (document != nullptr); if (document != nullptr) { document->getUndoManager().perform (action, actionName); } else { std::unique_ptr deleter (action); action->perform(); } } //============================================================================== void ComponentLayout::clearComponents() { selected.deselectAll(); selected.dispatchPendingMessages(); components.clear(); changed(); } //============================================================================== class AddCompAction : public UndoableAction { public: AddCompAction (XmlElement* const xml_, ComponentLayout& layout_) : indexAdded (-1), xml (xml_), layout (layout_) { } bool perform() { showCorrectTab(); Component* const newComp = layout.addComponentFromXml (*xml, false); jassert (newComp != nullptr); indexAdded = layout.indexOfComponent (newComp); jassert (indexAdded >= 0); return indexAdded >= 0; } bool undo() { showCorrectTab(); layout.removeComponent (layout.getComponent (indexAdded), false); return true; } int getSizeInUnits() { return 10; } int indexAdded; private: std::unique_ptr xml; ComponentLayout& layout; static void showCorrectTab() { if (JucerDocumentEditor* const ed = JucerDocumentEditor::getActiveDocumentHolder()) ed->showLayout(); } AddCompAction (const AddCompAction&); AddCompAction& operator= (const AddCompAction&); }; //============================================================================== class DeleteCompAction : public ComponentUndoableAction { public: DeleteCompAction (Component* const comp, ComponentLayout& l) : ComponentUndoableAction (comp, l), oldIndex (-1) { if (ComponentTypeHandler* const h = ComponentTypeHandler::getHandlerFor (*comp)) xml.reset (h->createXmlFor (comp, &layout)); else jassertfalse; oldIndex = l.indexOfComponent (comp); } bool perform() { showCorrectTab(); layout.removeComponent (getComponent(), false); return true; } bool undo() { Component* c = layout.addComponentFromXml (*xml, false); jassert (c != nullptr); layout.moveComponentZOrder (layout.indexOfComponent (c), oldIndex); showCorrectTab(); return c != nullptr; } private: std::unique_ptr xml; int oldIndex; }; void ComponentLayout::removeComponent (Component* comp, const bool undoable) { if (comp != nullptr && components.contains (comp)) { if (undoable) { perform (new DeleteCompAction (comp, *this), "Delete components"); } else { selected.deselect (comp); selected.changed (true); // synchronous message to get rid of any property components components.removeObject (comp); changed(); } } } //============================================================================== class FrontBackCompAction : public ComponentUndoableAction { public: FrontBackCompAction (Component* const comp, ComponentLayout& l, int newIndex_) : ComponentUndoableAction (comp, l), newIndex (newIndex_) { oldIndex = l.indexOfComponent (comp); } bool perform() { showCorrectTab(); Component* comp = layout.getComponent (oldIndex); layout.moveComponentZOrder (oldIndex, newIndex); newIndex = layout.indexOfComponent (comp); return true; } bool undo() { showCorrectTab(); layout.moveComponentZOrder (newIndex, oldIndex); return true; } private: int newIndex, oldIndex; }; void ComponentLayout::moveComponentZOrder (int oldIndex, int newIndex) { jassert (components [oldIndex] != nullptr); if (oldIndex != newIndex && components [oldIndex] != nullptr) { components.move (oldIndex, newIndex); changed(); } } void ComponentLayout::componentToFront (Component* comp, const bool undoable) { if (comp != nullptr && components.contains (comp)) { if (undoable) perform (new FrontBackCompAction (comp, *this, -1), "Move components to front"); else moveComponentZOrder (components.indexOf (comp), -1); } } void ComponentLayout::componentToBack (Component* comp, const bool undoable) { if (comp != nullptr && components.contains (comp)) { if (undoable) perform (new FrontBackCompAction (comp, *this, 0), "Move components to back"); else moveComponentZOrder (components.indexOf (comp), 0); } } //============================================================================== const char* const ComponentLayout::clipboardXmlTag = "COMPONENTS"; void ComponentLayout::copySelectedToClipboard() { if (selected.getNumSelected() == 0) return; XmlElement clip (clipboardXmlTag); for (int i = 0; i < components.size(); ++i) { auto c = components.getUnchecked(i); if (selected.isSelected (c)) { if (auto type = ComponentTypeHandler::getHandlerFor (*c)) { auto e = type->createXmlFor (c, this); clip.addChildElement (e); } } } SystemClipboard::copyTextToClipboard (clip.toString()); } void ComponentLayout::paste() { if (auto doc = parseXMLIfTagMatches (SystemClipboard::getTextFromClipboard(), clipboardXmlTag)) { selected.deselectAll(); for (auto* e : doc->getChildIterator()) if (Component* newComp = addComponentFromXml (*e, true)) selected.addToSelection (newComp); startDragging(); dragSelectedComps (Random::getSystemRandom().nextInt (40), Random::getSystemRandom().nextInt (40)); endDragging(); } } void ComponentLayout::deleteSelected() { const SelectedItemSet temp (selected); selected.deselectAll(); selected.changed (true); // synchronous message to get rid of any property components if (temp.getNumSelected() > 0) { for (int i = temp.getNumSelected(); --i >= 0;) removeComponent (temp.getSelectedItem (i), true); changed(); if (document != nullptr) document->dispatchPendingMessages(); // forces the change to propagate before a paint() callback can happen, // in case there are components floating around that are now dangling pointers } } void ComponentLayout::selectAll() { for (int i = 0; i < components.size(); ++i) selected.addToSelection (components.getUnchecked (i)); } void ComponentLayout::selectedToFront() { const SelectedItemSet temp (selected); for (int i = temp.getNumSelected(); --i >= 0;) componentToFront (temp.getSelectedItem(i), true); } void ComponentLayout::selectedToBack() { const SelectedItemSet temp (selected); for (int i = 0; i < temp.getNumSelected(); ++i) componentToBack (temp.getSelectedItem(i), true); } void ComponentLayout::alignTop() { if (selected.getNumSelected() > 1) { auto* main = selected.getSelectedItem (0); auto yPos = main->getY(); for (auto* other : selected) { if (other != main) setComponentBoundsAndProperties (other, other->getBounds().withPosition (other->getX(), yPos), main, true); } } } void ComponentLayout::alignRight() { if (selected.getNumSelected() > 1) { auto* main = selected.getSelectedItem (0); auto rightPos = main->getRight(); for (auto* other : selected) { if (other != main) setComponentBoundsAndProperties (other, other->getBounds().withPosition (rightPos - other->getWidth(), other->getY()), main, true); } } } void ComponentLayout::alignBottom() { if (selected.getNumSelected() > 1) { auto* main = selected.getSelectedItem (0); auto bottomPos = main->getBottom(); for (auto* other : selected) { if (other != main) setComponentBoundsAndProperties (other, other->getBounds().withPosition (other->getX(), bottomPos - other->getHeight()), main, true); } } } void ComponentLayout::alignLeft() { if (selected.getNumSelected() > 1) { auto* main = selected.getSelectedItem (0); auto xPos = main->getX(); for (auto* other : selected) { if (other != main) setComponentBoundsAndProperties (other, other->getBounds().withPosition (xPos, other->getY()), main, true); } } } void ComponentLayout::bringLostItemsBackOnScreen (int width, int height) { for (int i = components.size(); --i >= 0;) { Component* const c = components[i]; if (! c->getBounds().intersects (Rectangle (0, 0, width, height))) { c->setTopLeftPosition (width / 2, height / 2); updateStoredComponentPosition (c, false); } } } Component* ComponentLayout::addNewComponent (ComponentTypeHandler* const type, int x, int y) { std::unique_ptr c (type->createNewComponent (getDocument())); jassert (c != nullptr); if (c != nullptr) { c->setSize (type->getDefaultWidth(), type->getDefaultHeight()); c->setCentrePosition (x, y); updateStoredComponentPosition (c.get(), false); c->getProperties().set ("id", nextCompUID++); std::unique_ptr xml (type->createXmlFor (c.get(), this)); c.reset (addComponentFromXml (*xml, true)); String memberName (build_tools::makeValidIdentifier (type->getClassName (c.get()), true, true, false)); setComponentMemberVariableName (c.get(), memberName); selected.selectOnly (c.get()); } return c.release(); } Component* ComponentLayout::addComponentFromXml (const XmlElement& xml, const bool undoable) { if (undoable) { AddCompAction* const action = new AddCompAction (new XmlElement (xml), *this); perform (action, "Add new components"); return components [action->indexAdded]; } if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerForXmlTag (xml.getTagName())) { std::unique_ptr newComp (type->createNewComponent (getDocument())); if (type->restoreFromXml (xml, newComp.get(), this)) { // ensure that the new comp's name is unique setComponentMemberVariableName (newComp.get(), getComponentMemberVariableName (newComp.get())); // check for duped IDs.. while (findComponentWithId (ComponentTypeHandler::getComponentId (newComp.get())) != nullptr) ComponentTypeHandler::setComponentId (newComp.get(), Random::getSystemRandom().nextInt64()); components.add (newComp.get()); changed(); return newComp.release(); } } return nullptr; } Component* ComponentLayout::findComponentWithId (const int64 componentId) const { for (int i = 0; i < components.size(); ++i) if (ComponentTypeHandler::getComponentId (components.getUnchecked(i)) == componentId) return components.getUnchecked(i); return nullptr; } //============================================================================== static const char* const dimensionSuffixes[] = { "X", "Y", "W", "H" }; Component* ComponentLayout::getComponentRelativePosTarget (Component* comp, int whichDimension) const { jassert (comp != nullptr); if (PaintElement* const pe = dynamic_cast (comp)) { int64 compId; if (whichDimension == 0) compId = pe->getPosition().relativeToX; else if (whichDimension == 1) compId = pe->getPosition().relativeToY; else if (whichDimension == 2) compId = pe->getPosition().relativeToW; else compId = pe->getPosition().relativeToH; return findComponentWithId (compId); } return findComponentWithId (comp->getProperties() [String ("relativeTo") + dimensionSuffixes [whichDimension]] .toString().getHexValue64()); } void ComponentLayout::setComponentRelativeTarget (Component* comp, int whichDimension, Component* compToBeRelativeTo) { PaintElement* const pe = dynamic_cast (comp); jassert (comp != nullptr); jassert (pe != nullptr || components.contains (comp)); jassert (compToBeRelativeTo == nullptr || components.contains (compToBeRelativeTo)); jassert (compToBeRelativeTo == nullptr || ! dependsOnComponentForRelativePos (compToBeRelativeTo, comp)); if (compToBeRelativeTo != getComponentRelativePosTarget (comp, whichDimension) && (compToBeRelativeTo == nullptr || ! dependsOnComponentForRelativePos (compToBeRelativeTo, comp))) { const int64 compId = ComponentTypeHandler::getComponentId (compToBeRelativeTo); Rectangle oldBounds (comp->getBounds()); RelativePositionedRectangle pos; if (pe != nullptr) { oldBounds = pe->getCurrentBounds (dynamic_cast (pe->getParentComponent())->getComponentArea()); pos = pe->getPosition(); } else { pos = ComponentTypeHandler::getComponentPosition (comp); } if (whichDimension == 0) pos.relativeToX = compId; else if (whichDimension == 1) pos.relativeToY = compId; else if (whichDimension == 2) pos.relativeToW = compId; else if (whichDimension == 3) pos.relativeToH = compId; if (pe != nullptr) { pe->setPosition (pos, true); pe->setCurrentBounds (oldBounds, dynamic_cast (pe->getParentComponent())->getComponentArea(), true); } else { setComponentPosition (comp, pos, true); comp->setBounds (oldBounds); updateStoredComponentPosition (comp, false); } changed(); } } bool ComponentLayout::dependsOnComponentForRelativePos (Component* comp, Component* possibleDependee) const { for (int i = 0; i < 4; ++i) if (Component* const c = getComponentRelativePosTarget (comp, i)) if (c == possibleDependee || dependsOnComponentForRelativePos (c, possibleDependee)) return true; return false; } bool ComponentLayout::isComponentPositionRelative (Component* comp) const { for (int i = 0; i < getNumComponents(); ++i) if (dependsOnComponentForRelativePos (comp, getComponent (i))) return true; return false; } const int menuIdBase = 0x63240000; PopupMenu ComponentLayout::getRelativeTargetMenu (Component* comp, int whichDimension) const { PopupMenu m; auto current = getComponentRelativePosTarget (comp, whichDimension); m.addItem (menuIdBase, "Relative to parent component", true, current == nullptr); m.addSeparator(); for (int i = 0; i < components.size(); ++i) { auto* const c = components.getUnchecked (i); if (c != nullptr && c != comp) m.addItem (menuIdBase + i + 1, "Relative to " + getComponentMemberVariableName (c) + " (class: " + ComponentTypeHandler::getHandlerFor (*c)->getClassName (c) + ")", ! dependsOnComponentForRelativePos (c, comp), current == c); } return m; } void ComponentLayout::processRelativeTargetMenuResult (Component* comp, int whichDimension, int menuResultID) { if (menuResultID != 0) { Component* const newTarget = components [menuResultID - menuIdBase - 1]; setComponentRelativeTarget (comp, whichDimension, newTarget); } } //============================================================================== class ChangeCompPositionAction : public ComponentUndoableAction { public: ChangeCompPositionAction (Component* const comp, ComponentLayout& l, const RelativePositionedRectangle& newPos_) : ComponentUndoableAction (comp, l), newPos (newPos_), oldPos (ComponentTypeHandler::getComponentPosition (comp)) { } bool perform() { showCorrectTab(); layout.setComponentPosition (getComponent(), newPos, false); return true; } bool undo() { showCorrectTab(); layout.setComponentPosition (getComponent(), oldPos, false); return true; } private: RelativePositionedRectangle newPos, oldPos; }; class ChangeCompBoundsAndPropertiesAction : public ComponentUndoableAction { public: ChangeCompBoundsAndPropertiesAction (Component* const comp, ComponentLayout& l, const Rectangle& bounds, const NamedValueSet& props) : ComponentUndoableAction (comp, l), newBounds (bounds), oldBounds (comp->getBounds()), newProps (props), oldProps(comp->getProperties()) { } bool perform() { showCorrectTab(); getComponent()->setBounds (newBounds); getComponent()->getProperties() = newProps; layout.updateStoredComponentPosition (getComponent(), false); return true; } bool undo() { showCorrectTab(); getComponent()->setBounds (oldBounds); getComponent()->getProperties() = oldProps; layout.updateStoredComponentPosition (getComponent(), false); return true; } private: Rectangle newBounds, oldBounds; NamedValueSet newProps, oldProps; }; void ComponentLayout::setComponentPosition (Component* comp, const RelativePositionedRectangle& newPos, const bool undoable) { if (ComponentTypeHandler::getComponentPosition (comp) != newPos) { if (undoable) { perform (new ChangeCompPositionAction (comp, *this, newPos), "Move components"); } else { ComponentTypeHandler::setComponentPosition (comp, newPos, this); changed(); } } } void ComponentLayout::setComponentBoundsAndProperties (Component* componentToPosition, const Rectangle& newBounds, Component* referenceComponent, const bool undoable) { auto props = NamedValueSet (componentToPosition->getProperties()); auto rect = ComponentTypeHandler::getComponentPosition (componentToPosition).rect; auto referenceComponentPosition = ComponentTypeHandler::getComponentPosition (referenceComponent); auto referenceComponentRect = referenceComponentPosition.rect; rect.setModes (referenceComponentRect.getAnchorPointX(), referenceComponentRect.getPositionModeX(), referenceComponentRect.getAnchorPointY(), referenceComponentRect.getPositionModeY(), referenceComponentRect.getWidthMode(), referenceComponentRect.getHeightMode(), componentToPosition->getBounds()); props.set ("pos", rect.toString()); props.set ("relativeToX", String::toHexString (referenceComponentPosition.relativeToX)); props.set ("relativeToY", String::toHexString (referenceComponentPosition.relativeToY)); props.set ("relativeToW", String::toHexString (referenceComponentPosition.relativeToW)); props.set ("relativeToH", String::toHexString (referenceComponentPosition.relativeToH)); if (componentToPosition->getBounds() != newBounds || componentToPosition->getProperties() != props) { if (undoable) { perform (new ChangeCompBoundsAndPropertiesAction (componentToPosition, *this, newBounds, props), "Change component bounds"); } else { componentToPosition->setBounds (newBounds); componentToPosition->getProperties() = props; updateStoredComponentPosition (componentToPosition, false); changed(); } } } void ComponentLayout::updateStoredComponentPosition (Component* comp, const bool undoable) { RelativePositionedRectangle newPos (ComponentTypeHandler::getComponentPosition (comp)); newPos.updateFromComponent (*comp, this); setComponentPosition (comp, newPos, undoable); } //============================================================================== void ComponentLayout::startDragging() { for (int i = 0; i < components.size(); ++i) { Component* const c = components[i]; c->getProperties().set ("xDragStart", c->getX()); c->getProperties().set ("yDragStart", c->getY()); } jassert (document != nullptr); document->beginTransaction(); } void ComponentLayout::dragSelectedComps (int dx, int dy, const bool allowSnap) { if (allowSnap && document != nullptr && selected.getNumSelected() > 1) { dx = document->snapPosition (dx); dy = document->snapPosition (dy); } for (int i = 0; i < selected.getNumSelected(); ++i) { Component* const c = selected.getSelectedItem (i); const int startX = c->getProperties() ["xDragStart"]; const int startY = c->getProperties() ["yDragStart"]; if (allowSnap && document != nullptr && selected.getNumSelected() == 1) { c->setTopLeftPosition (document->snapPosition (startX + dx), document->snapPosition (startY + dy)); } else { c->setTopLeftPosition (startX + dx, startY + dy); } updateStoredComponentPosition (c, false); } } void ComponentLayout::endDragging() { // after the drag, roll back all the comps to their start position, then // back to their finish positions using an undoable command. document->beginTransaction(); for (int i = 0; i < selected.getNumSelected(); ++i) { Component* const c = selected.getSelectedItem (i); const int newX = c->getX(); const int newY = c->getY(); const int startX = c->getProperties() ["xDragStart"]; const int startY = c->getProperties() ["yDragStart"]; c->setTopLeftPosition (startX, startY); updateStoredComponentPosition (c, false); c->setTopLeftPosition (newX, newY); updateStoredComponentPosition (c, true); } document->beginTransaction(); } void ComponentLayout::moveSelectedComps (int dx, int dy, bool snap) { startDragging(); dragSelectedComps (dx, dy, snap); endDragging(); } void ComponentLayout::stretchSelectedComps (int dw, int dh, bool allowSnap) { int neww, newh; if (document != nullptr && selected.getNumSelected() == 1) { Component* const c = selected.getSelectedItem (0); if (allowSnap) { int bot = c->getBottom() + dh; int right = c->getRight() + dw; bot = (dh != 0) ? document->snapPosition (bot) : bot; right = (dw != 0) ? document->snapPosition (right) : right; newh = bot - c->getY(); neww = right - c->getX(); } else { newh = c->getHeight() + dh; neww = c->getWidth() + dw; } c->setSize (neww, newh); updateStoredComponentPosition (c, true); } else { for (int i = 0; i < selected.getNumSelected(); ++i) { Component* const c = selected.getSelectedItem (i); neww = c->getWidth() + dw; newh = c->getHeight() + dh; c->setSize (neww, newh); updateStoredComponentPosition (c, true); } } } //============================================================================== void ComponentLayout::fillInGeneratedCode (GeneratedCode& code) const { for (int i = 0; i < components.size(); ++i) if (Component* const comp = components.getUnchecked(i)) if (ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*comp)) type->fillInGeneratedCode (comp, code); } //============================================================================== String ComponentLayout::getComponentMemberVariableName (Component* comp) const { if (comp == nullptr) return {}; String name (comp->getProperties() ["memberName"].toString()); if (name.isEmpty()) name = getUnusedMemberName (build_tools::makeValidIdentifier (comp->getName(), true, true, false), comp); return name; } void ComponentLayout::setComponentMemberVariableName (Component* comp, const String& newName) { jassert (comp != nullptr); const String oldName (getComponentMemberVariableName (comp)); comp->getProperties().set ("memberName", String()); const String n (getUnusedMemberName (build_tools::makeValidIdentifier (newName, false, true, false), comp)); comp->getProperties().set ("memberName", n); if (n != oldName) changed(); } String ComponentLayout::getUnusedMemberName (String nameRoot, Component* comp) const { String n (nameRoot); while (CharacterFunctions::isDigit (nameRoot.getLastCharacter())) nameRoot = nameRoot.dropLastCharacters (1); int suffix = 2; for (;;) { bool alreadyUsed = false; for (int i = 0; i < components.size(); ++i) { if (components[i] != comp && components[i]->getProperties() ["memberName"] == n) { alreadyUsed = true; break; } } if (! alreadyUsed) break; n = nameRoot + String (suffix++); } return n; } //============================================================================== String ComponentLayout::getComponentVirtualClassName (Component* comp) const { if (comp == nullptr) return {}; return comp->getProperties() ["virtualName"]; } void ComponentLayout::setComponentVirtualClassName (Component* comp, const String& newName) { jassert (comp != nullptr); const String name (build_tools::makeValidIdentifier (newName, false, false, true)); if (name != getComponentVirtualClassName (comp)) { comp->getProperties().set ("virtualName", name); changed(); } } //============================================================================== void ComponentLayout::addToXml (XmlElement& xml) const { for (int i = 0; i < components.size(); ++i) if (ComponentTypeHandler* h = ComponentTypeHandler::getHandlerFor (*components [i])) xml.addChildElement (h->createXmlFor (components [i], this)); } static String bracketIfNeeded (const String& s) { return s.containsAnyOf ("+-*/%") ? "(" + s + ")" : s; } //============================================================================== void positionToCode (const RelativePositionedRectangle& position, const ComponentLayout* layout, String& x, String& y, String& w, String& h) { // these are the code sections for the positions of the relative comps String xrx, xry, xrw, xrh; if (Component* const relCompX = layout != nullptr ? layout->findComponentWithId (position.relativeToX) : nullptr) positionToCode (ComponentTypeHandler::getComponentPosition (relCompX), layout, xrx, xry, xrw, xrh); String yrx, yry, yrw, yrh; if (Component* const relCompY = layout != nullptr ? layout->findComponentWithId (position.relativeToY) : nullptr) positionToCode (ComponentTypeHandler::getComponentPosition (relCompY), layout, yrx, yry, yrw, yrh); String wrx, wry, wrw, wrh; if (Component* const relCompW = (layout != nullptr && position.rect.getWidthMode() != PositionedRectangle::absoluteSize) ? layout->findComponentWithId (position.relativeToW) : nullptr) positionToCode (ComponentTypeHandler::getComponentPosition (relCompW), layout, wrx, wry, wrw, wrh); String hrx, hry, hrw, hrh; if (Component* const relCompH = (layout != nullptr && position.rect.getHeightMode() != PositionedRectangle::absoluteSize) ? layout->findComponentWithId (position.relativeToH) : nullptr) positionToCode (ComponentTypeHandler::getComponentPosition (relCompH), layout, hrx, hry, hrw, hrh); // width if (position.rect.getWidthMode() == PositionedRectangle::proportionalSize) { if (wrw.isNotEmpty()) w << "juce::roundToInt (" << bracketIfNeeded (wrw) << " * " << CodeHelpers::floatLiteral (position.rect.getWidth(), 4) << ")"; else w << "proportionOfWidth (" << CodeHelpers::floatLiteral (position.rect.getWidth(), 4) << ")"; } else if (position.rect.getWidthMode() == PositionedRectangle::parentSizeMinusAbsolute) { if (wrw.isNotEmpty()) w << bracketIfNeeded (wrw) << " - " << roundToInt (position.rect.getWidth()); else w << "getWidth() - " << roundToInt (position.rect.getWidth()); } else { if (wrw.isNotEmpty()) w << bracketIfNeeded (wrw) << " + "; w << roundToInt (position.rect.getWidth()); } // height if (position.rect.getHeightMode() == PositionedRectangle::proportionalSize) { if (hrh.isNotEmpty()) h << "juce::roundToInt (" << bracketIfNeeded (hrh) << " * " << CodeHelpers::floatLiteral (position.rect.getHeight(), 4) << ")"; else h << "proportionOfHeight (" << CodeHelpers::floatLiteral (position.rect.getHeight(), 4) << ")"; } else if (position.rect.getHeightMode() == PositionedRectangle::parentSizeMinusAbsolute) { if (hrh.isNotEmpty()) h << bracketIfNeeded (hrh) << " - " << roundToInt (position.rect.getHeight()); else h << "getHeight() - " << roundToInt (position.rect.getHeight()); } else { if (hrh.isNotEmpty()) h << bracketIfNeeded (hrh) << " + "; h << roundToInt (position.rect.getHeight()); } // x-pos if (position.rect.getPositionModeX() == PositionedRectangle::proportionOfParentSize) { if (xrx.isNotEmpty() && xrw.isNotEmpty()) x << bracketIfNeeded (xrx) << " + juce::roundToInt (" << bracketIfNeeded (xrw) << " * " << CodeHelpers::floatLiteral (position.rect.getX(), 4) << ")"; else x << "proportionOfWidth (" << CodeHelpers::floatLiteral (position.rect.getX(), 4) << ")"; } else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentTopLeft) { if (xrx.isNotEmpty()) x << bracketIfNeeded (xrx) << " + "; x << roundToInt (position.rect.getX()); } else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentBottomRight) { if (xrx.isNotEmpty()) x << bracketIfNeeded (xrx) << " + " << bracketIfNeeded (xrw); else x << "getWidth()"; const int d = roundToInt (position.rect.getX()); if (d != 0) x << " - " << d; } else if (position.rect.getPositionModeX() == PositionedRectangle::absoluteFromParentCentre) { if (xrx.isNotEmpty()) x << bracketIfNeeded (xrx) << " + " << bracketIfNeeded (xrw) << " / 2"; else x << "(getWidth() / 2)"; const int d = roundToInt (position.rect.getX()); if (d != 0) x << " + " << d; } if (w != "0") { if (position.rect.getAnchorPointX() == PositionedRectangle::anchorAtRightOrBottom) x << " - " << bracketIfNeeded (w); else if (position.rect.getAnchorPointX() == PositionedRectangle::anchorAtCentre) x << " - (" << bracketIfNeeded (w) << " / 2)"; } // y-pos if (position.rect.getPositionModeY() == PositionedRectangle::proportionOfParentSize) { if (yry.isNotEmpty() && yrh.isNotEmpty()) y << bracketIfNeeded (yry) << " + juce::roundToInt (" << bracketIfNeeded (yrh) << " * " << CodeHelpers::floatLiteral (position.rect.getY(), 4) << ")"; else y << "proportionOfHeight (" << CodeHelpers::floatLiteral (position.rect.getY(), 4) << ")"; } else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentTopLeft) { if (yry.isNotEmpty()) y << bracketIfNeeded (yry) << " + "; y << roundToInt (position.rect.getY()); } else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentBottomRight) { if (yry.isNotEmpty()) y << bracketIfNeeded (yry) << " + " << bracketIfNeeded (yrh); else y << "getHeight()"; const int d = roundToInt (position.rect.getY()); if (d != 0) y << " - " << d; } else if (position.rect.getPositionModeY() == PositionedRectangle::absoluteFromParentCentre) { if (yry.isNotEmpty()) y << bracketIfNeeded (yry) << " + " << bracketIfNeeded (yrh) << " / 2"; else y << "(getHeight() / 2)"; const int d = roundToInt (position.rect.getY()); if (d != 0) y << " + " << d; } if (h != "0") { if (position.rect.getAnchorPointY() == PositionedRectangle::anchorAtRightOrBottom) y << " - " << bracketIfNeeded (h); else if (position.rect.getAnchorPointY() == PositionedRectangle::anchorAtCentre) y << " - (" << bracketIfNeeded (h) << " / 2)"; } }