/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2020 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. 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(); forEachXmlChildElement (*doc, e) 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) { Component* const c = components.getUnchecked(i); if (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)"; } }