From fffa698104744f550079ed22f6967b17714f9adc Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Fri, 4 Jun 2010 12:37:47 +0100 Subject: [PATCH] Jucer development. --- .../Component/jucer_ComponentDocument.cpp | 9 +- .../model/Drawable/jucer_DrawableDocument.cpp | 170 +++++-- .../model/Drawable/jucer_DrawableDocument.h | 13 +- .../Drawable/jucer_DrawableTypeHandler.cpp | 464 ++++++++++++++++-- .../Drawable/jucer_DrawableTypeHandler.h | 22 +- .../Source/model/Project/jucer_Project.cpp | 22 +- .../Source/model/Project/jucer_Project.h | 3 + .../jucer_ComponentEditorCanvas.h | 16 +- .../Drawable Editor/jucer_DrawableEditor.cpp | 57 ++- .../ui/Drawable Editor/jucer_DrawableEditor.h | 1 + .../jucer_DrawableEditorCanvas.h | 226 +++++++-- .../jucer_DrawableEditorTreeView.h | 106 +++- .../ui/Editor Base/jucer_EditorCanvas.cpp | 111 ++++- .../ui/Editor Base/jucer_EditorCanvas.h | 16 +- .../Editor Base/jucer_EditorDragOperation.h | 73 ++- .../jucer_ItemPreviewComponent.cpp | 31 +- .../jucer_ItemPreviewComponent.h | 3 +- .../utility/jucer_FillTypePropertyComponent.h | 164 ++++++- 18 files changed, 1257 insertions(+), 250 deletions(-) diff --git a/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp b/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp index 5af7a16211..f8f6a6ea7e 100644 --- a/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/Component/jucer_ComponentDocument.cpp @@ -761,10 +761,9 @@ void ComponentDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerS } menu.addSeparator(); - const MarkerList& markerList = document.getMarkerList (isX); - for (int i = 0; i < markerList.size(); ++i) - document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), + for (int i = 0; i < size(); ++i) + document.addMarkerMenuItem (100 + i, coord, getName (getMarker (i)), String::empty, menu, isAnchor1, fullCoordName); } @@ -773,10 +772,8 @@ const String ComponentDocument::MarkerList::getChosenMarkerMenuItem (const Relat if (i == 1) return isX ? "parent.left" : "parent.top"; if (i == 2) return isX ? "parent.right" : "parent.bottom"; - const MarkerList& markerList = document.getMarkerList (isX); - if (i >= 100 && i < 10000) - return markerList.getName (markerList.getMarker (i - 100)); + return getName (getMarker (i - 100)); jassertfalse; return String::empty; diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp index 3f70c6e926..83103559ce 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.cpp @@ -56,10 +56,10 @@ DrawableDocument::~DrawableDocument() root.removeListener (this); } -void DrawableDocument::recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d) +void DrawableDocument::recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d, StringArray& recentlyUsedIdCache) { if (d.getID().isEmpty()) - d.setID (createUniqueID (d.getState().getType().toString().toLowerCase() + "1"), 0); + d.setID (createUniqueID (d.getState().getType().toString().toLowerCase() + "1", recentlyUsedIdCache), 0); if (d.getState().getType() == DrawableComposite::valueTreeType) { @@ -68,7 +68,7 @@ void DrawableDocument::recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d) for (int i = 0; i < composite.getNumDrawables(); ++i) { Drawable::ValueTreeWrapperBase child (composite.getDrawableState (i)); - recursivelyUpdateIDs (child); + recursivelyUpdateIDs (child, recentlyUsedIdCache); } } } @@ -85,7 +85,13 @@ void DrawableDocument::checkRootObject() markersY = new MarkerList (*this, false); DrawableComposite::ValueTreeWrapper rootObject (getRootDrawableNode()); - recursivelyUpdateIDs (rootObject); + StringArray idCache; + recursivelyUpdateIDs (rootObject, idCache); +} + +const String DrawableDocument::getUniqueId() const +{ + return root [Ids::id_]; } //============================================================================== @@ -201,26 +207,59 @@ ValueTree DrawableDocument::findDrawableState (const String& objectId, bool recu return getRootDrawableNode().getDrawableWithId (objectId, recursive); } -const String DrawableDocument::createUniqueID (const String& name) const +const String DrawableDocument::createUniqueID (const String& name, StringArray& recentlyUsedIdCache) const { String n (CodeHelpers::makeValidIdentifier (name, false, true, false)); int suffix = 2; + int cacheIndex = -1; + + const String withoutNumbers (n.trimCharactersAtEnd ("0123456789")); + + for (int i = 0; i < recentlyUsedIdCache.size(); ++i) + { + if (recentlyUsedIdCache[i].startsWith (withoutNumbers)) + { + cacheIndex = i; + suffix = jmax (suffix, recentlyUsedIdCache[i].substring (withoutNumbers.length()).getIntValue() + 1); + n = withoutNumbers + String (suffix++); + break; + } + } while (markersX->getMarkerNamed (n).isValid() || markersY->getMarkerNamed (n).isValid() || findDrawableState (n, true).isValid()) - n = n.trimCharactersAtEnd ("0123456789") + String (suffix++); + n = withoutNumbers + String (suffix++); + + if (cacheIndex >= 0) + recentlyUsedIdCache.set (cacheIndex, n); + else + recentlyUsedIdCache.add (n); return n; } bool DrawableDocument::createItemProperties (Array & props, const String& itemId) { - ValueTree drawable (findDrawableState (itemId, false)); + ValueTree drawable (findDrawableState (itemId.upToFirstOccurrenceOf ("/", false, false), false)); if (drawable.isValid()) { DrawableTypeInstance item (*this, drawable); - item.createProperties (props); + + if (itemId.containsChar ('/')) + { + OwnedArray points; + item.getAllControlPoints (points); + + for (int i = 0; i < points.size(); ++i) + if (points.getUnchecked(i)->getID() == itemId) + points.getUnchecked(i)->createProperties (*this, props); + } + else + { + item.createProperties (props); + } + return true; } @@ -264,8 +303,8 @@ const ValueTree DrawableDocument::performNewItemMenuItem (int menuResultCode) Random::getSystemRandom().nextFloat() * 100.0f + 100.0f))); Drawable::ValueTreeWrapperBase wrapper (state); - recursivelyUpdateIDs (wrapper); - + StringArray idCache; + recursivelyUpdateIDs (wrapper, idCache); getRootDrawableNode().addDrawable (state, -1, getUndoManager()); return state; @@ -274,15 +313,80 @@ const ValueTree DrawableDocument::performNewItemMenuItem (int menuResultCode) return ValueTree::invalid; } +const ValueTree DrawableDocument::insertSVG (const File& file, const Point& position) +{ + ScopedPointer d (Drawable::createFromImageFile (file)); + DrawableComposite* dc = dynamic_cast (static_cast (d)); + + if (dc != 0) + { + ValueTree state (dc->createValueTree (this)); + + if (state.isValid()) + { + Drawable::ValueTreeWrapperBase wrapper (state); + getRootDrawableNode().addDrawable (state, -1, getUndoManager()); + StringArray idCache; + recursivelyUpdateIDs (wrapper, idCache); + + return state; + } + } + + return ValueTree::invalid; +} + //============================================================================== const Image DrawableDocument::getImageForIdentifier (const var& imageIdentifier) { - return ImageCache::getFromMemory (BinaryData::juce_icon_png, BinaryData::juce_icon_pngSize); + const String s (imageIdentifier.toString()); + + if (s.startsWithIgnoreCase ("id:")) + { + jassert (project != 0); + + if (project != 0) + { + Project::Item item (project->getMainGroup().findItemWithID (s.substring (3).trim())); + + if (item.isValid()) + { + Image im (ImageCache::getFromFile (item.getFile())); + + if (im.isValid()) + { + im.setTag (imageIdentifier); + return im; + } + } + } + } + + static Image dummy; + + if (dummy.isNull()) + { + dummy = Image (Image::ARGB, 128, 128, true); + Graphics g (dummy); + g.fillAll (Colours::khaki.withAlpha (0.51f)); + g.setColour (Colours::grey); + g.drawRect (0, 0, 128, 128); + + for (int i = -128; i < 128; i += 16) + g.drawLine (i, 0, i + 128, 128); + + g.setColour (Colours::darkgrey); + g.drawRect (0, 0, 128, 128); + g.setFont (16.0f, Font::bold); + g.drawText ("(Image Missing)", 0, 0, 128, 128, Justification::centred, false); + } + + return dummy; } const var DrawableDocument::getIdentifierForImage (const Image& image) { - return var::null; //xxx todo + return image.getTag(); } //============================================================================== @@ -403,50 +507,40 @@ bool DrawableDocument::MarkerList::createProperties (Array & return false; } -void DrawableDocument::addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& objectName, const String& edge, PopupMenu& menu, - bool isAnchor1, const String& fullCoordName) +void DrawableDocument::MarkerList::addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& name, const String& edge, PopupMenu& menu, + bool isAnchor1, const String& fullCoordName) { -// RelativeCoordinate requestedCoord (findNamedCoordinate (objectName, edge, coord.isHorizontal())); + RelativeCoordinate requestedCoord (findNamedCoordinate (name, edge)); -// menu.addItem (i, name, - // ! (name == fullCoordName || requestedCoord.referencesIndirectly (fullCoordName, *this)), - // name == (isAnchor1 ? coord.getAnchor1() : coord.getAnchor2())); + menu.addItem (i, edge.isEmpty() ? name : (name + "." + edge), + ! (name == fullCoordName || (fullCoordName.isNotEmpty() && requestedCoord.references (fullCoordName, this))), + name == (isAnchor1 ? coord.getAnchorName1() : coord.getAnchorName2())); } void DrawableDocument::MarkerList::addMarkerMenuItems (const ValueTree& markerState, const RelativeCoordinate& coord, PopupMenu& menu, bool isAnchor1) { -/* const String fullCoordName (getName (markerState)); + const String fullCoordName (getName (markerState)); - if (coord.isHorizontal()) - { - document.addMarkerMenuItem (1, coord, "parent", "left", menu, isAnchor1, fullCoordName); - document.addMarkerMenuItem (2, coord, "parent", "right", menu, isAnchor1, fullCoordName); - } + if (isHorizontal()) + addMarkerMenuItem (1, coord, "parent", "left", menu, isAnchor1, fullCoordName); else - { - document.addMarkerMenuItem (1, coord, "parent", "top", menu, isAnchor1, fullCoordName); - document.addMarkerMenuItem (2, coord, "parent", "bottom", menu, isAnchor1, fullCoordName); - } + addMarkerMenuItem (1, coord, "parent", "top", menu, isAnchor1, fullCoordName); menu.addSeparator(); - const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); - for (int i = 0; i < markerList.size(); ++i) - document.addMarkerMenuItem (100 + i, coord, markerList.getName (markerList.getMarker (i)), - String::empty, menu, isAnchor1, fullCoordName);*/ + for (int i = 0; i < size(); ++i) + addMarkerMenuItem (100 + i, coord, getName (getMarker (i)), + String::empty, menu, isAnchor1, fullCoordName); } const String DrawableDocument::MarkerList::getChosenMarkerMenuItem (const RelativeCoordinate& coord, int i) const { -/* if (i == 1) return coord.isHorizontal() ? "parent.left" : "parent.top"; - if (i == 2) return coord.isHorizontal() ? "parent.right" : "parent.bottom"; - - const MarkerList& markerList = document.getMarkerList (coord.isHorizontal()); + if (i == 1) return isHorizontal() ? "parent.left" : "parent.top"; if (i >= 100 && i < 10000) - return markerList.getName (markerList.getMarker (i - 100)); + return getName (getMarker (i - 100)); - jassertfalse;*/ + jassertfalse; return String::empty; } diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h index 2aaa84b41f..e0665efcfb 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableDocument.h @@ -49,16 +49,20 @@ public: bool hasChangedSinceLastSave() const; void changed(); + Project* getProject() const throw() { return project; } + const String getUniqueId() const; + ValueTree& getRoot() { return root; } DrawableComposite::ValueTreeWrapper getRootDrawableNode() const; ValueTree findDrawableState (const String& objectId, bool recursive) const; - const String createUniqueID (const String& suggestion) const; + const String createUniqueID (const String& suggestion, StringArray& recentlyUsedIdCache) const; void createItemProperties (Array & props, const StringArray& selectedItemIds); void addNewItemMenuItems (PopupMenu& menu) const; const ValueTree performNewItemMenuItem (int menuResultCode); + const ValueTree insertSVG (const File& file, const Point& position); //============================================================================== class MarkerList : public MarkerListBase @@ -86,6 +90,9 @@ public: DrawableDocument& document; DrawableComposite::ValueTreeWrapper object; + void addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& objectName, const String& edge, + PopupMenu& menu, bool isAnchor1, const String& fullCoordName); + MarkerList (const MarkerList&); MarkerList& operator= (const MarkerList&); }; @@ -116,7 +123,7 @@ private: bool saveAsXml, needsSaving; void checkRootObject(); - void recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d); + void recursivelyUpdateIDs (Drawable::ValueTreeWrapperBase& d, StringArray& recentlyUsedIdCache); Value getRootValueUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, getUndoManager()); } Value getRootValueNonUndoable (const Identifier& name) const { return root.getPropertyAsValue (name, 0); } @@ -127,8 +134,6 @@ private: bool createItemProperties (Array & props, const String& itemId); const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const; - void addMarkerMenuItem (int i, const RelativeCoordinate& coord, const String& objectName, const String& edge, - PopupMenu& menu, bool isAnchor1, const String& fullCoordName); }; diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp index f4dc27cfff..66d6d53c29 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.cpp @@ -24,7 +24,92 @@ */ #include "jucer_DrawableTypeHandler.h" +#include "../../utility/jucer_ColourPropertyComponent.h" +//============================================================================== +class ControlPointPropertyComp : public CoordinatePropertyComponent +{ +public: + ControlPointPropertyComp (DrawableTypeInstance& item_, ControlPoint* cp, const String& name, bool isHorizontal_, UndoManager* undoManager) + : CoordinatePropertyComponent (0, name, Value (new CoordExtractor (cp->getPositionValue (undoManager), isHorizontal_)), isHorizontal_), + item (item_) + { + nameSource = &item; + } + + ~ControlPointPropertyComp() + { + } + + const String pickMarker (TextButton* button, const String& currentMarker, bool isAnchor1) + { + RelativeCoordinate coord (getCoordinate()); + + PopupMenu m; + item.getDocument().getMarkerList (isHorizontal).addMarkerMenuItems (ValueTree::invalid, coord, m, isAnchor1); + + const int r = m.showAt (button); + + if (r > 0) + return item.getDocument().getMarkerList (isHorizontal).getChosenMarkerMenuItem (coord, r); + + return String::empty; + } + + DrawableTypeInstance item; + + + //============================================================================== + class CoordExtractor : public Value::ValueSource, + public Value::Listener + { + public: + CoordExtractor (const Value& sourceValue_, const bool isX_) + : sourceValue (sourceValue_), isX (isX_) + { + sourceValue.addListener (this); + } + + ~CoordExtractor() {} + + const var getValue() const + { + RelativePoint p (sourceValue.toString()); + return getCoord (p).toString(); + } + + void setValue (const var& newValue) + { + RelativePoint p (sourceValue.toString()); + RelativeCoordinate& coord = getCoord (p); + coord = RelativeCoordinate (newValue.toString(), isX); + + const String newVal (p.toString()); + if (sourceValue != newVal) + sourceValue = newVal; + } + + void valueChanged (Value&) + { + sendChangeMessage (true); + } + + //============================================================================== + juce_UseDebuggingNewOperator + + protected: + Value sourceValue; + bool isX; + + RelativeCoordinate& getCoord (RelativePoint& p) const + { + return isX ? p.x : p.y; + } + + CoordExtractor (const CoordExtractor&); + const CoordExtractor& operator= (const CoordExtractor&); + }; +}; //============================================================================== class DrawablePathHandler : public DrawableTypeHandler @@ -73,7 +158,8 @@ public: { public: DrawablePathFillPropComp (DrawableTypeInstance& item_, const String& name, const ValueTree& fill) - : FillTypePropertyComponent (item_.getDocument().getUndoManager(), name, fill), + : FillTypePropertyComponent (item_.getDocument().getUndoManager(), name, fill, + &item_.getDocument(), item_.getProject()), item (item_) {} @@ -110,9 +196,9 @@ public: class GradientControlPoint : public ControlPoint { public: - GradientControlPoint (const ValueTree& item_, + GradientControlPoint (const String& id_, const ValueTree& item_, const bool isStart_, const bool isStroke_) - : item (item_), isStart (isStart_), isStroke (isStroke_) + : ControlPoint (id_), item (item_), isStart (isStart_), isStroke (isStroke_) {} ~GradientControlPoint() {} @@ -124,7 +210,8 @@ public: RelativePoint p; const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState(), isStart ? &p : 0, - isStart ? 0 : &p, 0)); + isStart ? 0 : &p, 0, + 0)); jassert (fill.isGradient()); return p; } @@ -135,7 +222,7 @@ public: RelativePoint p1, p2; ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); - const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (fillState, &p1, &p2, 0)); + const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (fillState, &p1, &p2, 0, 0)); jassert (fill.isGradient()); if (isStart) @@ -143,11 +230,32 @@ public: else p2 = newPoint; - Drawable::ValueTreeWrapperBase::writeFillType (fillState, fill, &p1, &p2, undoManager); + Drawable::ValueTreeWrapperBase::writeFillType (fillState, fill, &p1, &p2, 0, undoManager); } - bool hasLine() { return false; } - RelativePoint getEndOfLine() { return RelativePoint(); } + bool hasLine() { return isStart; } + RelativePoint getEndOfLine() + { + RelativePoint p; + DrawablePath::ValueTreeWrapper wrapper (item); + ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); + Drawable::ValueTreeWrapperBase::readFillType (fillState, 0, &p, 0, 0); + return p; + } + + const Value getPositionValue (UndoManager* undoManager) + { + DrawablePath::ValueTreeWrapper wrapper (item); + ValueTree fillState (isStroke ? wrapper.getStrokeFillState() : wrapper.getMainFillState()); + return fillState.getPropertyAsValue (isStart ? Drawable::ValueTreeWrapperBase::gradientPoint1 : Drawable::ValueTreeWrapperBase::gradientPoint2, undoManager); + } + + void createProperties (DrawableDocument& document, Array & props) + { + DrawableTypeInstance instance (document, item); + props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager())); + props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager())); + } private: ValueTree item; @@ -158,8 +266,10 @@ public: class PathControlPoint : public ControlPoint { public: - PathControlPoint (const DrawablePath::ValueTreeWrapper::Element& element_, const int cpNum_) - : element (element_), cpNum (cpNum_) + PathControlPoint (const String& id_, + const DrawablePath::ValueTreeWrapper::Element& element_, + const DrawablePath::ValueTreeWrapper::Element& previousElement_, const int cpNum_, const int numCps_) + : ControlPoint (id_), element (element_), previousElement (previousElement_), cpNum (cpNum_), numCps (numCps_) {} ~PathControlPoint() {} @@ -174,45 +284,133 @@ public: element.setControlPoint (cpNum, newPoint, undoManager); } - bool hasLine() { return false; } - RelativePoint getEndOfLine() { return RelativePoint(); } + const Value getPositionValue (UndoManager* undoManager) + { + return element.getControlPointValue (cpNum, undoManager); + } + + bool hasLine() { return numCps > 1 && cpNum == 0 || cpNum == 1; } + + RelativePoint getEndOfLine() + { + if (cpNum == 0) + return previousElement.getEndPoint(); + else + return element.getControlPoint (2); + } + + void createProperties (DrawableDocument& document, Array & props) + { + DrawableTypeInstance instance (document, element.getParent().getState()); + props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager())); + props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager())); + } private: - DrawablePath::ValueTreeWrapper::Element element; - int cpNum; + DrawablePath::ValueTreeWrapper::Element element, previousElement; + int cpNum, numCps; }; + void getGradientControlPoints (DrawablePath::ValueTreeWrapper& wrapper, DrawableTypeInstance& item, + OwnedArray & points, const String& itemId) + { + const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getMainFillState(), 0, 0, 0, 0)); + + if (fill.isGradient()) + { + points.add (new GradientControlPoint (itemId + "/gf1", item.getState(), true, false)); + points.add (new GradientControlPoint (itemId + "/gf2", item.getState(), false, false)); + } + + const FillType stroke (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getStrokeFillState(), 0, 0, 0, 0)); + + if (stroke.isGradient()) + { + points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), true, true)); + points.add (new GradientControlPoint (itemId + "/gs1", item.getState(), false, true)); + } + } + void getAllControlPoints (DrawableTypeInstance& item, OwnedArray & points) { DrawablePath::ValueTreeWrapper wrapper (item.getState()); const ValueTree pathTree (wrapper.getPathState()); const int numElements = pathTree.getNumChildren(); + const String itemId (item.getID()); - for (int i = 0; i < numElements; ++i) + if (numElements > 0) { - const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); - const int numCps = e.getNumControlPoints(); + DrawablePath::ValueTreeWrapper::Element last (pathTree.getChild(0)); - for (int j = 0; j < numCps; ++j) - points.add (new PathControlPoint (e, j)); - } + for (int i = 0; i < numElements; ++i) + { + const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); + const int numCps = e.getNumControlPoints(); - const FillType fill (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getMainFillState(), 0, 0, 0)); + for (int j = 0; j < numCps; ++j) + points.add (new PathControlPoint (itemId + "/" + String(i) + "/" + String(j), e, last, j, numCps)); - if (fill.isGradient()) - { - points.add (new GradientControlPoint (item.getState(), true, false)); - points.add (new GradientControlPoint (item.getState(), false, false)); + last = e; + } } - const FillType stroke (Drawable::ValueTreeWrapperBase::readFillType (wrapper.getStrokeFillState(), 0, 0, 0)); + getGradientControlPoints (wrapper, item, points, itemId); + } - if (stroke.isGradient()) + void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray & points, const EditorCanvasBase::SelectedItems& selection) + { + DrawablePath::ValueTreeWrapper wrapper (item.getState()); + + const ValueTree pathTree (wrapper.getPathState()); + const int numElements = pathTree.getNumChildren(); + const String itemId (item.getID()); + + if (numElements > 0) { - points.add (new GradientControlPoint (item.getState(), true, true)); - points.add (new GradientControlPoint (item.getState(), false, true)); + DrawablePath::ValueTreeWrapper::Element last (pathTree.getChild(0)); + bool lastWasSelected = false; + + for (int i = 0; i < numElements; ++i) + { + const String elementIdRoot (itemId + "/" + String(i) + "/"); + const DrawablePath::ValueTreeWrapper::Element e (pathTree.getChild(i)); + int numCps = e.getNumControlPoints(); + bool pointIsSelected = false; + + for (int k = numCps; --k >= 0;) + { + if (selection.isSelected (elementIdRoot + String (k))) + { + pointIsSelected = true; + break; + } + } + + if (numCps > 1) + { + if (pointIsSelected || lastWasSelected) + { + for (int j = 0; j < numCps; ++j) + points.add (new PathControlPoint (elementIdRoot + String(j), e, last, j, numCps)); + } + else + { + points.add (new PathControlPoint (elementIdRoot + String (numCps - 1), e, last, numCps - 1, numCps)); + } + } + else + { + for (int j = 0; j < numCps; ++j) + points.add (new PathControlPoint (elementIdRoot + String(j), e, last, j, numCps)); + } + + last = e; + lastWasSelected = pointIsSelected; + } } + + getGradientControlPoints (wrapper, item, points, itemId); } }; @@ -244,6 +442,34 @@ public: void createPropertyEditors (DrawableTypeInstance& item, Array & props) { + DrawableImage::ValueTreeWrapper wrapper (item.getState()); + + if (item.getDocument().getProject() != 0) + { + OwnedArray images; + item.getDocument().getProject()->findAllImageItems (images); + + StringArray choices; + Array ids; + + for (int i = 0; i < images.size(); ++i) + { + choices.add (images.getUnchecked(i)->getName().toString()); + ids.add (images.getUnchecked(i)->getImageFileID()); + } + + props.add (new ChoicePropertyComponent (wrapper.getImageIdentifierValue (item.getDocument().getUndoManager()), + "Image", choices, ids)); + } + + props.add (new SliderPropertyComponent (wrapper.getOpacityValue (item.getDocument().getUndoManager()), + "Opacity", 0, 1.0, 0.001)); + + props.add (new ColourPropertyComponent (item.getDocument().getUndoManager(), "Overlay Colour", + wrapper.getOverlayColourValue (item.getDocument().getUndoManager()), + Colours::transparentBlack, true)); + + props.add (new ResetButtonPropertyComponent (item, wrapper)); } void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) @@ -254,8 +480,8 @@ public: class ImageControlPoint : public ControlPoint { public: - ImageControlPoint (const DrawableTypeInstance& item_, const int cpNum_) - : item (item_), cpNum (cpNum_) + ImageControlPoint (const String& id_, const DrawableTypeInstance& item_, const int cpNum_) + : ControlPoint (id_), item (item_), cpNum (cpNum_) {} ~ImageControlPoint() {} @@ -288,9 +514,29 @@ public: } } + const Value getPositionValue (UndoManager* undoManager) + { + DrawableImage::ValueTreeWrapper wrapper (item.getState()); + + switch (cpNum) + { + case 0: return item.getState().getPropertyAsValue (DrawableImage::ValueTreeWrapper::topLeft, undoManager); + case 1: return item.getState().getPropertyAsValue (DrawableImage::ValueTreeWrapper::topRight, undoManager); + case 2: return item.getState().getPropertyAsValue (DrawableImage::ValueTreeWrapper::bottomLeft, undoManager); + default: jassertfalse; break; + } + return Value(); + } + bool hasLine() { return false; } RelativePoint getEndOfLine() { return RelativePoint(); } + void createProperties (DrawableDocument& document, Array & props) + { + props.add (new ControlPointPropertyComp (item, this, "X", true, document.getUndoManager())); + props.add (new ControlPointPropertyComp (item, this, "Y", false, document.getUndoManager())); + } + private: DrawableTypeInstance item; int cpNum; @@ -298,9 +544,52 @@ public: void getAllControlPoints (DrawableTypeInstance& item, OwnedArray & points) { + const String itemIDRoot (item.getID() + "/"); + for (int i = 0; i < 3; ++i) - points.add (new ImageControlPoint (item, i)); + points.add (new ImageControlPoint (itemIDRoot + String (i), item, i)); + } + + void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray & points, const EditorCanvasBase::SelectedItems&) + { + return getAllControlPoints (item, points); } + + //============================================================================== + class ResetButtonPropertyComponent : public ButtonPropertyComponent + { + public: + ResetButtonPropertyComponent (DrawableTypeInstance& item_, + const DrawableImage::ValueTreeWrapper& wrapper_) + : ButtonPropertyComponent ("Reset", false), + item (item_), wrapper (wrapper_) + { + } + + const String getButtonText() const { return "Reset to Original Size"; } + + void buttonClicked() + { + Image im (item.getDocument().getImageForIdentifier (wrapper.getImageIdentifier())); + + if (im.isValid()) + { + RelativePoint topLeft (wrapper.getTargetPositionForTopLeft()); + RelativePoint topRight (wrapper.getTargetPositionForTopRight()); + RelativePoint bottomLeft (wrapper.getTargetPositionForBottomLeft()); + + topRight.moveToAbsolute (topLeft.resolve (&item) + Point (im.getWidth(), 0.0f), &item); + bottomLeft.moveToAbsolute (topLeft.resolve (&item) + Point (0.0f, im.getHeight()), &item); + + wrapper.setTargetPositionForTopRight (topRight, item.getDocument().getUndoManager()); + wrapper.setTargetPositionForBottomLeft (bottomLeft, item.getDocument().getUndoManager()); + } + } + + private: + DrawableTypeInstance item; + DrawableImage::ValueTreeWrapper wrapper; + }; }; //============================================================================== @@ -312,33 +601,20 @@ public: void createPropertyEditors (DrawableTypeInstance& item, Array & props) { + DrawableComposite::ValueTreeWrapper wrapper (item.getState()); + props.add (new ResetButtonPropertyComponent (item, wrapper)); } void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) { } - const RelativeCoordinate findNamedCoordinate (const DrawableTypeInstance& item, const String& objectName, const String& edge) const - { - DrawableComposite::ValueTreeWrapper wrapper (const_cast (item).getState()); - - ValueTree markerState (wrapper.getMarkerState (true, objectName)); - if (markerState.isValid()) - return wrapper.getMarker (true, markerState).position; - - markerState = wrapper.getMarkerState (false, objectName); - if (markerState.isValid()) - return wrapper.getMarker (false, markerState).position; - - return RelativeCoordinate(); - } - //============================================================================== class CompositeControlPoint : public ControlPoint { public: - CompositeControlPoint (const ValueTree& item_, const int cpNum_) - : item (item_), cpNum (cpNum_) + CompositeControlPoint (const String& id_, const ValueTree& item_, const int cpNum_) + : ControlPoint (id_), item (item_), cpNum (cpNum_) {} ~CompositeControlPoint() {} @@ -371,9 +647,22 @@ public: } } + const Value getPositionValue (UndoManager* undoManager) + { + jassertfalse + return Value(); + } + bool hasLine() { return false; } RelativePoint getEndOfLine() { return RelativePoint(); } + void createProperties (DrawableDocument& document, Array & props) + { + DrawableTypeInstance instance (document, item); + props.add (new ControlPointPropertyComp (instance, this, "X", true, document.getUndoManager())); + props.add (new ControlPointPropertyComp (instance, this, "Y", false, document.getUndoManager())); + } + private: ValueTree item; int cpNum; @@ -381,9 +670,47 @@ public: void getAllControlPoints (DrawableTypeInstance& item, OwnedArray & points) { + const String itemIDRoot (item.getID() + "/"); + for (int i = 0; i < 3; ++i) - points.add (new CompositeControlPoint (item.getState(), i)); + points.add (new CompositeControlPoint (itemIDRoot + String(i), item.getState(), i)); + } + + void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray & points, const EditorCanvasBase::SelectedItems&) + { + return getAllControlPoints (item, points); } + + //============================================================================== + class ResetButtonPropertyComponent : public ButtonPropertyComponent + { + public: + ResetButtonPropertyComponent (DrawableTypeInstance& item_, + const DrawableComposite::ValueTreeWrapper& wrapper_) + : ButtonPropertyComponent ("Reset", false), + item (item_), wrapper (wrapper_) + { + } + + const String getButtonText() const { return "Reset to Original Size"; } + + void buttonClicked() + { + RelativePoint topLeft (wrapper.getTargetPositionForOrigin()); + RelativePoint topRight (wrapper.getTargetPositionForX1Y0()); + RelativePoint bottomLeft (wrapper.getTargetPositionForX0Y1()); + + topRight.moveToAbsolute (topLeft.resolve (&item) + Point (1.0f, 0.0f), &item); + bottomLeft.moveToAbsolute (topLeft.resolve (&item) + Point (0.0f, 1.0f), &item); + + wrapper.setTargetPositionForX1Y0 (topRight, item.getDocument().getUndoManager()); + wrapper.setTargetPositionForX0Y1 (bottomLeft, item.getDocument().getUndoManager()); + } + + private: + DrawableTypeInstance item; + DrawableComposite::ValueTreeWrapper wrapper; + }; }; @@ -459,7 +786,21 @@ DrawableTypeHandler* DrawableTypeInstance::getHandler() const const RelativeCoordinate DrawableTypeInstance::findNamedCoordinate (const String& objectName, const String& edge) const { - return getHandler()->findNamedCoordinate (*this, objectName, edge); + ValueTree v (state); + while (v.getParent().isValid() && ! v.hasType (DrawableComposite::valueTreeType)) + v = v.getParent(); + + DrawableComposite::ValueTreeWrapper wrapper (v); + + ValueTree markerState (wrapper.getMarkerState (true, objectName)); + if (markerState.isValid()) + return wrapper.getMarker (true, markerState).position; + + markerState = wrapper.getMarkerState (false, objectName); + if (markerState.isValid()) + return wrapper.getMarker (false, markerState).position; + + return RelativeCoordinate(); } const Rectangle DrawableTypeInstance::getBounds() @@ -485,7 +826,28 @@ void DrawableTypeInstance::setBounds (Drawable* drawable, const Rectangle return getHandler()->setBounds (*this, drawable, newBounds); } +void DrawableTypeInstance::applyTransform (Drawable* drawable, const AffineTransform& transform) +{ + OwnedArray points; + getAllControlPoints (points); + + for (int i = points.size(); --i >= 0;) + { + RelativePoint rp (points.getUnchecked(i)->getPosition()); + Point p (rp.resolve (drawable->getParent())); + p.applyTransform (transform); + rp.moveToAbsolute (p, drawable->getParent()); + + points.getUnchecked(i)->setPosition (rp, document.getUndoManager()); + } +} + void DrawableTypeInstance::getAllControlPoints (OwnedArray & points) { return getHandler()->getAllControlPoints (*this, points); } + +void DrawableTypeInstance::getVisibleControlPoints (OwnedArray & points, const EditorCanvasBase::SelectedItems& selection) +{ + return getHandler()->getVisibleControlPoints (*this, points, selection); +} diff --git a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h index e4b9b828fd..92401f39ff 100644 --- a/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h +++ b/extras/Jucer (experimental)/Source/model/Drawable/jucer_DrawableTypeHandler.h @@ -28,22 +28,36 @@ #include "jucer_DrawableDocument.h" #include "../../utility/jucer_FillTypePropertyComponent.h" +#include "../../ui/Editor Base/jucer_EditorCanvas.h" class DrawableTypeHandler; + //============================================================================== class ControlPoint { public: - ControlPoint() {} + ControlPoint (const String& pointID_) : pointID (pointID_) {} virtual ~ControlPoint() {} + const String& getID() const throw() { return pointID; } + virtual const RelativePoint getPosition() = 0; virtual void setPosition (const RelativePoint& newPoint, UndoManager* undoManager) = 0; virtual bool hasLine() = 0; virtual RelativePoint getEndOfLine() = 0; + + virtual const Value getPositionValue (UndoManager* undoManager) = 0; + virtual void createProperties (DrawableDocument& document, Array & props) = 0; + +private: + const String pointID; + + ControlPoint (const ControlPoint&); + ControlPoint& operator= (const ControlPoint&); }; + //============================================================================== class DrawableTypeInstance : public RelativeCoordinate::NamedCoordinateFinder { @@ -52,13 +66,17 @@ public: //============================================================================== DrawableDocument& getDocument() throw() { return document; } + Project* getProject() { return document.getProject(); } ValueTree& getState() throw() { return state; } + const String getID() const { return Drawable::ValueTreeWrapperBase (state).getID(); } Value getValue (const Identifier& name) const; void createProperties (Array & props); const Rectangle getBounds(); void setBounds (Drawable* drawable, const Rectangle& newBounds); + void applyTransform (Drawable* drawable, const AffineTransform& transform); void getAllControlPoints (OwnedArray & points); + void getVisibleControlPoints (OwnedArray & points, const EditorCanvasBase::SelectedItems& selection); const RelativeCoordinate findNamedCoordinate (const String& objectName, const String& edge) const; @@ -87,7 +105,7 @@ public: virtual void createPropertyEditors (DrawableTypeInstance& item, Array & props) = 0; virtual void itemDoubleClicked (const MouseEvent& e, DrawableTypeInstance& item) = 0; virtual void getAllControlPoints (DrawableTypeInstance& item, OwnedArray & points) = 0; - virtual const RelativeCoordinate findNamedCoordinate (const DrawableTypeInstance& item, const String& objectName, const String& edge) const { return RelativeCoordinate(); } + virtual void getVisibleControlPoints (DrawableTypeInstance& item, OwnedArray & points, const EditorCanvasBase::SelectedItems& selection) = 0; const String& getDisplayName() const { return displayName; } const Identifier& getValueTreeType() const { return valueTreeType; } diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp index 756879c7a3..959d70b9ba 100644 --- a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp +++ b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.cpp @@ -411,6 +411,25 @@ Project::Item Project::createNewItem (const File& file) return item; } +static void findImages (const Project::Item& item, OwnedArray& found) +{ + if (item.isFile()) + { + if (item.getFile().hasFileExtension ("png;jpg;jpeg;gif")) + found.add (new Project::Item (item)); + } + else if (item.isGroup()) + { + for (int i = 0; i < item.getNumChildren(); ++i) + findImages (item.getChild (i), found); + } +} + +void Project::findAllImageItems (OwnedArray& items) +{ + findImages (getMainGroup(), items); +} + //============================================================================== Project::Item::Item (Project& project_, const ValueTree& node_) : project (project_), node (node_) @@ -426,7 +445,8 @@ Project::Item::~Item() { } -const String Project::Item::getID() const { return node [Ids::id_]; } +const String Project::Item::getID() const { return node [Ids::id_]; } +const String Project::Item::getImageFileID() const { return "id:" + getID(); } bool Project::Item::isFile() const { return node.hasType (Tags::file); } bool Project::Item::isGroup() const { return node.hasType (Tags::group) || isMainGroup(); } diff --git a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h index ca30c391ad..2c402f1c63 100644 --- a/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h +++ b/extras/Jucer (experimental)/Source/model/Project/jucer_Project.h @@ -166,6 +166,7 @@ public: const String getID() const; Item findItemWithID (const String& targetId) const; // (recursive search) + const String getImageFileID() const; //============================================================================== Value getName() const; @@ -207,6 +208,8 @@ public: Item createNewGroup(); Item createNewItem (const File& file); + void findAllImageItems (OwnedArray& items); + //============================================================================== class BuildConfiguration { diff --git a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h index 2b4a58ef56..52f02cd76c 100644 --- a/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Component Editor/jucer_ComponentEditorCanvas.h @@ -175,10 +175,10 @@ public: { public: DragOperation (ComponentEditorCanvas* canvas_, - const MouseEvent& e, const Point& mousePos, + const Point& mousePos, Component* snapGuideParentComp_, const ResizableBorderComponent::Zone& zone_) - : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_) + : EditorDragOperation (canvas_, mousePos, snapGuideParentComp_, zone_, false) { } @@ -262,12 +262,18 @@ public: ComponentDocument& doc = getDocument(); return (float) doc.getMarkerList (isX).getCoordinate (marker).resolve (&doc); } + + void transformObject (ValueTree& state, const AffineTransform& transform) + { + } }; - DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent, - const ResizableBorderComponent::Zone& zone) + bool canRotate() const { return false; } + + DragOperation* createDragOperation (const Point& mouseDownPos, Component* snapGuideParentComponent, + const ResizableBorderComponent::Zone& zone, bool isRotating) { - DragOperation* d = new DragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone); + DragOperation* d = new DragOperation (this, mouseDownPos, snapGuideParentComponent, zone); Array selected, unselected; diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp index be85a1f61a..15c24c8019 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.cpp @@ -115,14 +115,57 @@ const StringArray DrawableEditor::getSelectedIds() const void DrawableEditor::deleteSelection() { + getUndoManager()->beginNewTransaction(); + DrawableComposite::ValueTreeWrapper root (getDocument().getRootDrawableNode()); + + const StringArray ids (getSelectedIds()); + for (int i = ids.size(); --i >= 0;) + { + const ValueTree v (root.getDrawableWithId (ids[i], false)); + root.removeDrawable (v, getUndoManager()); + } + + getUndoManager()->beginNewTransaction(); } void DrawableEditor::selectionToFront() { + getUndoManager()->beginNewTransaction(); + + DrawableComposite::ValueTreeWrapper root (getDocument().getRootDrawableNode()); + int index = 0; + + for (int i = root.getNumDrawables(); --i >= 0;) + { + const Drawable::ValueTreeWrapperBase d (root.getDrawableState (index)); + + if (getSelection().isSelected (d.getID())) + root.moveDrawableOrder (index, -1, getUndoManager()); + else + ++index; + } + + getUndoManager()->beginNewTransaction(); } void DrawableEditor::selectionToBack() { + getUndoManager()->beginNewTransaction(); + + DrawableComposite::ValueTreeWrapper root (getDocument().getRootDrawableNode()); + int index = root.getNumDrawables() - 1; + + for (int i = root.getNumDrawables(); --i >= 0;) + { + const Drawable::ValueTreeWrapperBase d (root.getDrawableState (index)); + + if (getSelection().isSelected (d.getID())) + root.moveDrawableOrder (index, 0, getUndoManager()); + else + --index; + } + + getUndoManager()->beginNewTransaction(); } void DrawableEditor::showNewShapeMenu (Component* componentToAttachTo) @@ -130,7 +173,11 @@ void DrawableEditor::showNewShapeMenu (Component* componentToAttachTo) PopupMenu m; getDocument().addNewItemMenuItems (m); const int r = m.showAt (componentToAttachTo); - getDocument().performNewItemMenuItem (r); + + ValueTree newItem (getDocument().performNewItemMenuItem (r)); + + if (newItem.isValid()) + getSelection().selectOnly (Drawable::ValueTreeWrapperBase (newItem).getID()); } //============================================================================== @@ -213,13 +260,13 @@ bool DrawableEditor::perform (const InvocationInfo& info) switch (info.commandID) { case CommandIDs::undo: - getDocument().getUndoManager()->beginNewTransaction(); - getDocument().getUndoManager()->undo(); + getUndoManager()->beginNewTransaction(); + getUndoManager()->undo(); return true; case CommandIDs::redo: - getDocument().getUndoManager()->beginNewTransaction(); - getDocument().getUndoManager()->redo(); + getUndoManager()->beginNewTransaction(); + getUndoManager()->redo(); return true; case CommandIDs::toFront: diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h index 7482fde09c..fb19030810 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditor.h @@ -59,6 +59,7 @@ public: //============================================================================== DrawableDocument& getDocument() const { return *drawableDocument; } + UndoManager* getUndoManager() const { return getDocument().getUndoManager(); } EditorCanvasBase::SelectedItems& getSelection() { return selection; } diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h index c9786bac85..19139efdfe 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorCanvas.h @@ -32,6 +32,7 @@ //============================================================================== class DrawableEditorCanvas : public EditorCanvasBase, + public FileDragAndDropTarget, public Timer { public: @@ -124,9 +125,7 @@ public: } else { - getDocument().addNewItemMenuItems (m); - const int r = m.show(); - getDocument().performNewItemMenuItem (r); + editor.showNewShapeMenu (0); } } @@ -210,6 +209,21 @@ public: return getObjectPositionFloat (state).getSmallestIntegerContainer(); } + void transformObject (ValueTree& state, const AffineTransform& transform) + { + if (drawable != 0) + { + Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (state).getID()); + + if (d != 0) + { + d->refreshFromValueTree (state, &getDocument()); + DrawableTypeInstance di (getDocument(), state); + di.applyTransform (d, transform); + } + } + } + RelativeRectangle getObjectCoords (const ValueTree& state) { return RelativeRectangle(); @@ -221,9 +235,10 @@ public: public: ControlPointComponent (DrawableEditorCanvas* canvas, const ValueTree& drawableState_, int controlPointNum_) : OverlayItemComponent (canvas), drawableState (drawableState_), - controlPointNum (controlPointNum_), isDragging (false), mouseDownResult (false), selected (false) + controlPointNum (controlPointNum_), isDragging (false), mouseDownResult (false), selected (false), + sizeNormal (7), sizeOver (11) { - selectionId = getControlPointId (drawableState, controlPointNum); + setRepaintsOnMouseActivity (true); } ~ControlPointComponent() @@ -232,11 +247,24 @@ public: void paint (Graphics& g) { + Rectangle r (getLocalBounds()); + + if (! isMouseOverOrDragging()) + r = r.reduced ((sizeOver - sizeNormal) / 2, (sizeOver - sizeNormal) / 2); + g.setColour (Colour (selected ? 0xaaaaaaaa : 0xaa333333)); - g.drawRect (0, 0, getWidth(), getHeight()); + g.drawRect (r); g.setColour (Colour (selected ? 0xaa000000 : 0x99ffffff)); - g.fillRect (1, 1, getWidth() - 2, getHeight() - 2); + g.fillRect (r.reduced (1, 1)); + } + + bool hitTest (int x, int y) + { + if (isMouseOverOrDragging()) + return true; + + return getLocalBounds().reduced ((sizeOver - sizeNormal) / 2, (sizeOver - sizeNormal) / 2).contains (x, y); } void mouseDown (const MouseEvent& e) @@ -261,7 +289,7 @@ public: isDragging = true; canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), - ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); + ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre), false, Point()); } if (isDragging) @@ -273,9 +301,12 @@ public: void mouseUp (const MouseEvent& e) { - if (isDragging) + if (! e.mods.isPopupMenu()) { - canvas->endDrag (e.getEventRelativeTo (getParentComponent())); + if (isDragging) + canvas->endDrag (e.getEventRelativeTo (getParentComponent())); + else + canvas->getSelection().addToSelectionOnMouseUp (selectionId, e.mods, false, mouseDownResult); } } @@ -283,10 +314,50 @@ public: { } + class LineComponent : public OverlayItemComponent + { + public: + LineComponent (EditorCanvasBase* canvas) + : OverlayItemComponent (canvas) + {} + + ~LineComponent() {} + + void setLine (const Line& newLine) + { + if (line != newLine) + { + line = newLine; + setBoundsInTargetSpace (Rectangle (line.getStart(), line.getEnd()) + .getSmallestIntegerContainer().expanded (2, 2)); + repaint(); + } + } + + void paint (Graphics& g) + { + g.setColour (Colours::black.withAlpha (0.6f)); + g.drawLine (Line (pointToLocalSpace (line.getStart()), + pointToLocalSpace (line.getEnd())), 1.0f); + } + + bool hitTest (int, int) + { + return false; + } + + private: + Line line; + }; + void updatePosition (ControlPoint& point, RelativeCoordinate::NamedCoordinateFinder* nameFinder) { + selectionId = point.getID(); + const Point p (point.getPosition().resolve (nameFinder)); - setBoundsInTargetSpace (Rectangle (roundToInt (p.getX()) - 2, roundToInt (p.getY()) - 2, 7, 7)); + setBoundsInTargetSpace (Rectangle (roundToInt (p.getX()) - sizeOver / 2, + roundToInt (p.getY()) - sizeOver / 2, + sizeOver, sizeOver)); const bool nowSelected = canvas->getSelection().isSelected (selectionId); @@ -295,6 +366,21 @@ public: selected = nowSelected; repaint(); } + + if (point.hasLine()) + { + if (line == 0) + { + line = new LineComponent (canvas); + getParentComponent()->addAndMakeVisible (line, 0); + } + + line->setLine (Line (p, point.getEndOfLine().resolve (nameFinder))); + } + else + { + line = 0; + } } private: @@ -302,6 +388,8 @@ public: int controlPointNum; bool isDragging, mouseDownResult, selected; String selectionId; + ScopedPointer line; + const int sizeNormal, sizeOver; }; void updateControlPointComponents (Component* parent, OwnedArray& comps) @@ -314,7 +402,7 @@ public: DrawableTypeInstance item (getDocument(), controlPointEditingTarget); OwnedArray points; - item.getAllControlPoints (points); + item.getVisibleControlPoints (points, getSelection()); Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); DrawableComposite* parentDrawable = d->getParent(); @@ -366,12 +454,31 @@ public: if (drawable != 0) { - for (int i = drawable->getNumDrawables(); --i >= 0;) + if (isControlPointMode()) { - Drawable* d = drawable->getDrawable (i); + DrawableTypeInstance item (getDocument(), controlPointEditingTarget); + OwnedArray points; + item.getVisibleControlPoints (points, getSelection()); + + const Rectangle floatArea (area.toFloat()); + + for (int i = 0; i < points.size(); ++i) + { + const Point p (points.getUnchecked(i)->getPosition().resolve (drawable)); + + if (floatArea.contains (p)) + itemsFound.add (points.getUnchecked(i)->getID()); + } + } + else + { + for (int i = drawable->getNumDrawables(); --i >= 0;) + { + Drawable* d = drawable->getDrawable (i); - if (d->getBounds().intersects (floatArea)) - itemsFound.add (d->getName()); + if (d->getBounds().intersects (floatArea)) + itemsFound.add (d->getName()); + } } } } @@ -381,20 +488,14 @@ public: return itemId.containsChar ('/'); } - static const String getControlPointId (const ValueTree& drawableState, int index) - { - return Drawable::ValueTreeWrapperBase (drawableState).getID() + "/" + String (index); - } - //============================================================================== class ObjectDragOperation : public EditorDragOperation { public: - ObjectDragOperation (DrawableEditorCanvas* canvas_, - const MouseEvent& e, const Point& mousePos, - Component* snapGuideParentComp_, - const ResizableBorderComponent::Zone& zone_) - : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), drawableCanvas (canvas_) + ObjectDragOperation (DrawableEditorCanvas* canvas_, const Point& mousePos, + Component* snapGuideParentComp_, const ResizableBorderComponent::Zone& zone_, bool isRotating) + : EditorDragOperation (canvas_, mousePos, snapGuideParentComp_, zone_, isRotating), + drawableCanvas (canvas_) { } @@ -423,6 +524,11 @@ public: drawableCanvas->setObjectPositionFloat (state, newBounds); } + void transformObject (ValueTree& state, const AffineTransform& transform) + { + drawableCanvas->transformObject (state, transform); + } + float getMarkerPosition (const ValueTree& marker, bool isX) { return 0; @@ -438,14 +544,14 @@ public: public: ControlPointDragOperation (DrawableEditorCanvas* canvas_, const DrawableTypeInstance& drawableItem_, - Drawable* drawable_, - const MouseEvent& e, const Point& mousePos, + DrawableComposite* drawable_, + const Point& mousePos, Component* snapGuideParentComp_, const ResizableBorderComponent::Zone& zone_) - : EditorDragOperation (canvas_, e, mousePos, snapGuideParentComp_, zone_), + : EditorDragOperation (canvas_, mousePos, snapGuideParentComp_, zone_, false), drawableCanvas (canvas_), drawableItem (drawableItem_), drawable (drawable_) { - drawableItem.getAllControlPoints (points); + drawableItem.getVisibleControlPoints (points, canvas_->getSelection()); } ~ControlPointDragOperation() {} @@ -467,27 +573,31 @@ public: const Rectangle getObjectPosition (const ValueTree& state) { - int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue(); + int index = state [Ids::id_]; ControlPoint* cp = points[index]; if (cp == 0) return Rectangle(); - Point p (cp->getPosition().resolve (drawable->getParent())); + Point p (cp->getPosition().resolve (drawable)); return Rectangle (p, p); } void setObjectPosition (ValueTree& state, const Rectangle& newBounds) { - int index = state [Ids::id_].toString().fromFirstOccurrenceOf ("/", false, false).getIntValue(); + int index = state [Ids::id_]; ControlPoint* cp = points[index]; if (cp != 0) { RelativePoint p (cp->getPosition()); - p.moveToAbsolute (newBounds.getPosition(), drawable->getParent()); + p.moveToAbsolute (newBounds.getPosition(), drawable); cp->setPosition (p, getDocument().getUndoManager()); } } + void transformObject (ValueTree& state, const AffineTransform& transform) + { + } + float getMarkerPosition (const ValueTree& marker, bool isX) { return 0; @@ -496,30 +606,30 @@ public: private: DrawableEditorCanvas* drawableCanvas; DrawableTypeInstance drawableItem; - Drawable* drawable; + DrawableComposite* drawable; }; //============================================================================== - DragOperation* createDragOperation (const MouseEvent& e, Component* snapGuideParentComponent, - const ResizableBorderComponent::Zone& zone) + bool canRotate() const { return true; } + + DragOperation* createDragOperation (const Point& mouseDownPos, Component* snapGuideParentComponent, + const ResizableBorderComponent::Zone& zone, bool isRotating) { Array selected, unselected; EditorDragOperation* drag = 0; if (isControlPointMode()) { - Drawable* d = drawable->getDrawableWithName (Drawable::ValueTreeWrapperBase (controlPointEditingTarget).getID()); DrawableTypeInstance item (getDocument(), controlPointEditingTarget); - - ControlPointDragOperation* cpd = new ControlPointDragOperation (this, item, d, e, e.getPosition() - origin, snapGuideParentComponent, zone); + ControlPointDragOperation* cpd = new ControlPointDragOperation (this, item, drawable, mouseDownPos, snapGuideParentComponent, zone); drag = cpd; for (int i = 0; i < cpd->points.size(); ++i) { - const String pointId (getControlPointId (item.getState(), i)); + const String pointId (cpd->points.getUnchecked(i)->getID()); ValueTree v (Ids::controlPoint); - v.setProperty (Ids::id_, pointId, 0); + v.setProperty (Ids::id_, i, 0); if (editor.getSelection().isSelected (pointId)) selected.add (v); @@ -529,9 +639,8 @@ public: } else { - drag = new ObjectDragOperation (this, e, e.getPosition() - origin, snapGuideParentComponent, zone); - DrawableComposite::ValueTreeWrapper mainGroup (getDocument().getRootDrawableNode()); + drag = new ObjectDragOperation (this, mouseDownPos, snapGuideParentComponent, zone, isRotating); for (int i = mainGroup.getNumDrawables(); --i >= 0;) { @@ -556,6 +665,35 @@ public: getUndoManager().beginNewTransaction(); } + //============================================================================== + bool isInterestedInFileDrag (const StringArray& files) + { + for (int i = files.size(); --i >= 0;) + if (File (files[i]).hasFileExtension ("svg;jpg;jpeg;gif;png")) + return true; + + return false; + } + + void filesDropped (const StringArray& files, int x, int y) + { + for (int i = files.size(); --i >= 0;) + { + const File f (files[i]); + + if (f.hasFileExtension ("svg")) + { + ValueTree newItem (getDocument().insertSVG (f, screenSpaceToObjectSpace (Point (x, y).toFloat()))); + + if (newItem.isValid()) + getSelection().selectOnly (Drawable::ValueTreeWrapperBase (newItem).getID()); + } + else if (f.hasFileExtension ("jpg;jpeg;gif;png")) + { + } + } + } + //============================================================================== class DrawableComponent : public Component { diff --git a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h index 8d17a29f16..72e911f9ef 100644 --- a/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h +++ b/extras/Jucer (experimental)/Source/ui/Drawable Editor/jucer_DrawableEditorTreeView.h @@ -32,7 +32,8 @@ */ class DrawableTreeViewItem : public JucerTreeViewBase, public ValueTree::Listener, - public ChangeListener + public ChangeListener, + public AsyncUpdater { public: DrawableTreeViewItem (DrawableEditor& editor_, const ValueTree& drawableRoot) @@ -51,18 +52,25 @@ public: //============================================================================== void valueTreePropertyChanged (ValueTree& tree, const Identifier& property) { + if (property == Drawable::ValueTreeWrapperBase::idProperty) + repaintItem(); } void valueTreeChildrenChanged (ValueTree& tree) { - if (tree == node.getState()) - refreshSubItems(); + if (tree == node.getState() || tree.isAChildOf (node.getState())) + triggerAsyncUpdate(); } void valueTreeParentChanged (ValueTree& tree) { } + void handleAsyncUpdate() + { + refreshSubItems(); + } + //============================================================================== // TreeViewItem stuff.. bool mightContainSubItems() @@ -157,9 +165,14 @@ public: return String::empty; } + static const String getDragIdFor (DrawableEditor& editor) + { + return drawableItemDragType + editor.getDocument().getUniqueId(); + } + const String getDragSourceDescription() { - return drawableItemDragType; + return getDragIdFor (editor); } //============================================================================== @@ -175,11 +188,94 @@ public: bool isInterestedInDragSource (const String& sourceDescription, Component* sourceComponent) { - return false; + return node.getState().getType() == DrawableComposite::valueTreeType + && sourceDescription == getDragIdFor (editor) + && editor.getSelection().getNumSelected() > 0; } void itemDropped (const String& sourceDescription, Component* sourceComponent, int insertIndex) { + if (editor.getSelection().getNumSelected() > 0) + { + TreeView* tree = getOwnerView(); + const ScopedPointer oldOpenness (tree->getOpennessState (false)); + + Array selectedComps; + // scan the source tree rather than look at the selection manager, because it might + // be from a different editor, and the order needs to be correct. + getAllSelectedNodesInTree (sourceComponent, selectedComps); + insertItems (selectedComps, insertIndex); + + if (oldOpenness != 0) + tree->restoreOpennessState (*oldOpenness); + } + } + + static void getAllSelectedNodesInTree (Component* componentInTree, Array& selectedItems) + { + TreeView* tree = dynamic_cast (componentInTree); + + if (tree == 0) + tree = componentInTree->findParentComponentOfClass ((TreeView*) 0); + + if (tree != 0) + { + const int numSelected = tree->getNumSelectedItems(); + + for (int i = 0; i < numSelected; ++i) + { + DrawableTreeViewItem* const item = dynamic_cast (tree->getSelectedItem (i)); + + if (item != 0) + selectedItems.add (item->node.getState()); + } + } + } + + void insertItems (Array & items, int insertIndex) + { + DrawableComposite::ValueTreeWrapper composite (node.getState()); + + int i; + for (i = items.size(); --i >= 0;) + if (node.getState() == items.getReference(i) || composite.getState().isAChildOf (items.getReference(i))) // Check for recursion. + return; + + // Don't include any nodes that are children of other selected nodes.. + for (i = items.size(); --i >= 0;) + { + const ValueTree& n = items.getReference(i); + + for (int j = items.size(); --j >= 0;) + { + if (j != i && n.isAChildOf (items.getReference(j))) + { + items.remove (i); + break; + } + } + } + + // Remove and re-insert them one at a time.. + for (i = 0; i < items.size(); ++i) + { + ValueTree& n = items.getReference(i); + + int index = composite.indexOfDrawable (n); + + if (index >= 0 && index < insertIndex) + --insertIndex; + + if (index >= 0) + { + composite.moveDrawableOrder (index, insertIndex++, editor.getDocument().getUndoManager()); + } + else + { + n.getParent().removeChild (n, editor.getDocument().getUndoManager()); + composite.addDrawable (n, insertIndex++, editor.getDocument().getUndoManager()); + } + } } //============================================================================== diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp index 7b6e5c40c9..f04c9aa8e3 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.cpp @@ -38,7 +38,9 @@ public: objectState (objectState_), objectId (objectId_), borderThickness (4), - isDragging (false) + isDragging (false), + isRotating (false), + canRotate (canvas_->canRotate()) { jassert (objectState.isValid()); } @@ -49,8 +51,13 @@ public: void paint (Graphics& g) { - g.setColour (resizableBorderColour); - g.drawRect (0, 0, getWidth(), getHeight(), borderThickness); + if (! canvas->isRotating()) + { + g.setColour (resizableBorderColour); + g.drawRect (0, 0, getWidth(), getHeight(), borderThickness); + + g.fillRect (rotateArea); + } } void mouseEnter (const MouseEvent& e) { updateDragZone (e.getPosition()); } @@ -60,36 +67,47 @@ public: void mouseDown (const MouseEvent& e) { updateDragZone (e.getPosition()); + isDragging = false; if (e.mods.isPopupMenu()) { - isDragging = false; canvas->showPopupMenu (true); } - else - { - isDragging = true; - canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()).getEventRelativeTo (getParentComponent()), dragZone); - canvas->showSizeGuides(); - } } void mouseDrag (const MouseEvent& e) { + if (! (isDragging || e.mods.isPopupMenu() || e.mouseWasClicked())) + { + isDragging = true; + bool isRotating = rotateArea.contains (e.getMouseDownPosition()); + + canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()), + dragZone, isRotating, canvas->getObjectPosition (objectState).getCentre().toFloat()); + + if (! isRotating) + canvas->showSizeGuides(); + + repaint(); + } + if (isDragging) { - canvas->continueDrag (e.getEventRelativeTo (getParentComponent())); + canvas->continueDrag (e); autoScrollForMouseEvent (e); } } void mouseUp (const MouseEvent& e) { - if (isDragging) + if (isDragging || isRotating) { + isRotating = false; canvas->hideSizeGuides(); - canvas->endDrag (e.getEventRelativeTo (getParentComponent())); + canvas->endDrag (e); updateDragZone (e.getPosition()); + + repaint(); } } @@ -101,7 +119,7 @@ public: bool hitTest (int x, int y) { if (ModifierKeys::getCurrentModifiers().isAnyModifierKeyDown()) - return ! getCentreArea().contains (x, y); + return rotateArea.contains (x, y) || ! getCentreArea().contains (x, y); return true; } @@ -114,6 +132,9 @@ public: const Rectangle bounds (canvas->getObjectPosition (objectState)); setBoundsInTargetSpace (bounds.expanded (borderThickness, borderThickness)); + if (canRotate) + rotateArea = Rectangle (2, 2, 10, 10); + int i; for (i = sizeGuides.size(); --i >= 0;) { @@ -126,7 +147,6 @@ public: const String& getTargetObjectID() const { return objectId; } - //============================================================================== class SizeGuideComponent : public OverlayItemComponent, public ComponentListener @@ -200,7 +220,8 @@ private: ResizableBorderComponent::Zone dragZone; const int borderThickness; OwnedArray sizeGuides; - bool isDragging; + Rectangle rotateArea; + bool isDragging, canRotate, isRotating; const Rectangle getCentreArea() const { @@ -467,7 +488,9 @@ public: isDraggingClickedComp = true; canvas->enableResizingMode(); getSelection().addToSelectionOnMouseUp (mouseDownCompUID, e.mods, true, mouseDownResult); - canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()), ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre)); + canvas->beginDrag (e.withNewPosition (e.getMouseDownPosition()), + ResizableBorderComponent::Zone (ResizableBorderComponent::Zone::centre), + false, Point()); } if (isDraggingClickedComp) @@ -843,6 +866,16 @@ const Point EditorCanvasBase::objectSpaceToScreenSpace (const Point& p return p + origin; } +const Point EditorCanvasBase::screenSpaceToObjectSpace (const Point& p) const +{ + return p - origin.toFloat(); +} + +const Point EditorCanvasBase::objectSpaceToScreenSpace (const Point& p) const +{ + return p + origin.toFloat(); +} + const Rectangle EditorCanvasBase::screenSpaceToObjectSpace (const Rectangle& r) const { return r - origin; @@ -868,6 +901,11 @@ void EditorCanvasBase::enableControlPointMode (const ValueTree& objectToEdit) } } +bool EditorCanvasBase::isRotating() const +{ + return dragger != 0 && dragger->isRotating(); +} + //============================================================================== void EditorCanvasBase::paint (Graphics& g) { @@ -927,6 +965,9 @@ void EditorCanvasBase::handleAsyncUpdate() const Rectangle canvasBounds (getCanvasBounds()); const Point newOrigin (jmax (0, -canvasBounds.getX()), jmax (0, -canvasBounds.getY())); + const int newWidth = jmax (canvasBounds.getWidth(), canvasBounds.getRight()) + border.getLeftAndRight(); + const int newHeight = jmax (canvasBounds.getHeight(), canvasBounds.getBottom()) + border.getTopAndBottom(); + if (origin != newOrigin) { repaint(); @@ -936,16 +977,16 @@ void EditorCanvasBase::handleAsyncUpdate() setBounds (jmin (0, getX() + oldOrigin.getX() - origin.getX()), jmin (0, getY() + oldOrigin.getY() - origin.getY()), - jmax (canvasBounds.getWidth(), canvasBounds.getRight()) + border.getLeftAndRight(), - jmax (canvasBounds.getHeight(), canvasBounds.getBottom()) + border.getTopAndBottom()); + newWidth, newHeight); + } + else if (getWidth() != newWidth || getHeight() != newHeight) + { + setSize (newWidth, newHeight); } else { - setSize (jmax (canvasBounds.getWidth(), canvasBounds.getRight()) + border.getLeftAndRight(), - jmax (canvasBounds.getHeight(), canvasBounds.getBottom()) + border.getTopAndBottom()); + overlay->update(); } - - overlay->update(); } void EditorCanvasBase::resized() @@ -954,6 +995,7 @@ void EditorCanvasBase::resized() overlay->setBounds (getLocalBounds()); resizeFrame->setBounds (getLocalBounds()); overlay->update(); + handleUpdateNowIfNeeded(); } //============================================================================== @@ -962,25 +1004,33 @@ void EditorCanvasBase::hideSizeGuides() { overlay->hideSizeGuides(); } //============================================================================== -void EditorCanvasBase::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone) +void EditorCanvasBase::beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone, + bool isRotating, const Point& rotationCentre) { - dragger = createDragOperation (e, overlay, zone); + dragger = createDragOperation (e.getEventRelativeTo (overlay).getPosition() - origin, overlay, zone, isRotating); + dragger->setRotationCentre (rotationCentre); + repaint(); } void EditorCanvasBase::continueDrag (const MouseEvent& e) { + MouseEvent e2 (e.getEventRelativeTo (overlay)); + if (dragger != 0) - dragger->drag (e, e.getPosition() - origin); + dragger->drag (e2, e2.getPosition() - origin); } void EditorCanvasBase::endDrag (const MouseEvent& e) { if (dragger != 0) { - dragger->drag (e, e.getPosition() - origin); + MouseEvent e2 (e.getEventRelativeTo (overlay)); + dragger->drag (e2, e2.getPosition() - origin); dragger = 0; getUndoManager().beginNewTransaction(); + + repaint(); } } @@ -999,3 +1049,10 @@ void EditorCanvasBase::OverlayItemComponent::setBoundsInTargetSpace (const Recta setBounds (canvas->objectSpaceToScreenSpace (r) + canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point())); } + +const Point EditorCanvasBase::OverlayItemComponent::pointToLocalSpace (const Point& p) const +{ + return canvas->objectSpaceToScreenSpace (p) + + (canvas->getComponentHolder()->relativePositionToOtherComponent (getParentComponent(), Point()) + - getPosition()).toFloat(); +} diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h index 5d7a306303..a84fdb1b78 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorCanvas.h @@ -96,13 +96,19 @@ public: virtual ~DragOperation() {} virtual void drag (const MouseEvent& e, const Point& newPos) = 0; + virtual void setRotationCentre (const Point& rotationCentre) = 0; + virtual bool isRotating() const = 0; }; - virtual DragOperation* createDragOperation (const MouseEvent& e, + virtual DragOperation* createDragOperation (const Point& mouseDownPos, Component* snapGuideParentComponent, - const ResizableBorderComponent::Zone& zone) = 0; + const ResizableBorderComponent::Zone& zone, + bool isRotating) = 0; - void beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone); + virtual bool canRotate() const = 0; + + void beginDrag (const MouseEvent& e, const ResizableBorderComponent::Zone& zone, + bool isRotating, const Point& rotationCentre); void continueDrag (const MouseEvent& e); void endDrag (const MouseEvent& e); @@ -111,6 +117,7 @@ public: bool isResizingMode() const { return ! isControlPointMode(); } bool isControlPointMode() const { return controlPointEditingTarget.isValid(); } + bool isRotating() const; //============================================================================== Component* getComponentHolder() const { return componentHolder; } @@ -118,7 +125,9 @@ public: const Point& getOrigin() const throw() { return origin; } const Point screenSpaceToObjectSpace (const Point& p) const; + const Point screenSpaceToObjectSpace (const Point& p) const; const Point objectSpaceToScreenSpace (const Point& p) const; + const Point objectSpaceToScreenSpace (const Point& p) const; const Rectangle screenSpaceToObjectSpace (const Rectangle& r) const; const Rectangle objectSpaceToScreenSpace (const Rectangle& r) const; @@ -130,6 +139,7 @@ public: ~OverlayItemComponent(); void setBoundsInTargetSpace (const Rectangle& r); + const Point pointToLocalSpace (const Point& p) const; protected: EditorCanvasBase* canvas; diff --git a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h index af995a175a..eac4bbd96a 100644 --- a/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h +++ b/extras/Jucer (experimental)/Source/ui/Editor Base/jucer_EditorDragOperation.h @@ -33,13 +33,14 @@ class EditorDragOperation : public EditorCanvasBase::DragOperation { public: - EditorDragOperation (EditorCanvasBase* canvas_, const MouseEvent& e, const Point& mousePos, - Component* snapGuideParentComp_, - const ResizableBorderComponent::Zone& zone_) + EditorDragOperation (EditorCanvasBase* canvas_, const Point& mousePos, + Component* snapGuideParentComp_, const ResizableBorderComponent::Zone& zone_, + bool rotating_) : canvas (canvas_), snapGuideParentComp (snapGuideParentComp_), zone (zone_), - mouseDownPos (mousePos) + mouseDownPos (mousePos), + rotating (rotating_) { } @@ -141,6 +142,16 @@ public: getUndoManager().beginNewTransaction(); } + void setRotationCentre (const Point& rotationCentre) + { + centre = rotationCentre; + } + + bool isRotating() const + { + return rotating; + } + //============================================================================== struct SnapLine { @@ -195,31 +206,35 @@ public: { getUndoManager().undoCurrentTransactionOnly(); - // (can't use getOffsetFromDragStart() because of auto-scrolling) - Point distance (newPos - mouseDownPos); - if (! isDraggingLeftRight()) - distance = distance.withX (0); + if (rotating) + { + const float angle = centre.getAngleToPoint (mouseDownPos.toFloat()) - centre.getAngleToPoint (newPos.toFloat()); + const AffineTransform transform (AffineTransform::rotation (angle, centre.getX(), centre.getY())); - if (! isDraggingUpDown()) - distance = distance.withY (0); + for (int i = 0; i < updateList.size(); ++i) + transformObject (updateList.getReference(i), transform); + } + else + { + // (can't use getOffsetFromDragStart() because of auto-scrolling) + Point distance (newPos - mouseDownPos); + if (! isDraggingLeftRight()) + distance = distance.withX (0); - snapGuides.clear(); + if (! isDraggingUpDown()) + distance = distance.withY (0); - if (canvas->getPanel()->isSnappingEnabled() != (e.mods.isCommandDown() || e.mods.isCtrlDown())) - { - performSnap (verticalSnapTargets, getVerticalSnapPositions (distance), true, distance); - performSnap (horizontalSnapTargets, getHorizontalSnapPositions (distance), false, distance); - } + snapGuides.clear(); - for (int i = 0; i < updateList.size(); ++i) - dragItem (updateList.getReference(i), distance, originalPositions.getReference(i)); - } + if (canvas->getPanel()->isSnappingEnabled() != (e.mods.isCommandDown() || e.mods.isCtrlDown())) + { + performSnap (verticalSnapTargets, getVerticalSnapPositions (distance), true, distance); + performSnap (horizontalSnapTargets, getHorizontalSnapPositions (distance), false, distance); + } - void dragItem (ValueTree& v, const Point& distance, const Rectangle& originalPos) - { - const Rectangle newBounds (zone.resizeRectangleBy (originalPos, Point ((float) distance.getX(), - (float) distance.getY()))); - setObjectPosition (v, newBounds); + for (int i = 0; i < updateList.size(); ++i) + dragItem (updateList.getReference(i), distance, originalPositions.getReference(i)); + } } protected: @@ -231,6 +246,7 @@ protected: virtual void getObjectDependencies (const ValueTree& state, Array& deps) = 0; virtual const Rectangle getObjectPosition (const ValueTree& state) = 0; virtual void setObjectPosition (ValueTree& state, const Rectangle& newBounds) = 0; + virtual void transformObject (ValueTree& state, const AffineTransform& transform) = 0; virtual UndoManager& getUndoManager() = 0; @@ -246,6 +262,15 @@ private: OwnedArray snapGuides; Component* snapGuideParentComp; Point mouseDownPos; + Point centre; + bool rotating; + + void dragItem (ValueTree& v, const Point& distance, const Rectangle& originalPos) + { + const Rectangle newBounds (zone.resizeRectangleBy (originalPos, Point ((float) distance.getX(), + (float) distance.getY()))); + setObjectPosition (v, newBounds); + } void getCompleteDependencyList (const ValueTree& object, Array & deps, const Array& activeObjects) { diff --git a/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.cpp b/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.cpp index cdab7c0388..786a104a62 100644 --- a/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.cpp +++ b/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.cpp @@ -31,44 +31,41 @@ ItemPreviewComponent::ItemPreviewComponent (const File& file_) : file (file_) { - facts.add (file.getFullPathName()); - tryToLoadImage (file.createInputStream()); - facts.removeEmptyStrings (true); -} - -ItemPreviewComponent::ItemPreviewComponent (InputStream* input, const String& name) -{ - facts.add (name); - tryToLoadImage (input); - facts.removeEmptyStrings (true); + tryToLoadImage(); } ItemPreviewComponent::~ItemPreviewComponent() { } -void ItemPreviewComponent::tryToLoadImage (InputStream* in) +void ItemPreviewComponent::tryToLoadImage() { - if (in != 0) - { - ScopedPointer input (in); + facts.clear(); + facts.add (file.getFullPathName()); + image = Image(); + ScopedPointer input (file.createInputStream()); + + if (input != 0) + { + const int64 totalSize = input->getTotalLength(); ImageFileFormat* format = ImageFileFormat::findImageFormatForStream (*input); + input = 0; String formatName; if (format != 0) formatName = " " + format->getFormatName(); - image = ImageFileFormat::loadFrom (*input); + image = ImageCache::getFromFile (file); if (image.isValid()) facts.add (String (image.getWidth()) + " x " + String (image.getHeight()) + formatName); - const int64 totalSize = input->getTotalLength(); - if (totalSize > 0) facts.add (File::descriptionOfSizeInBytes (totalSize)); } + + facts.removeEmptyStrings (true); } void ItemPreviewComponent::paint (Graphics& g) diff --git a/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.h b/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.h index 54e5eaeed1..f84a4b394d 100644 --- a/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.h +++ b/extras/Jucer (experimental)/Source/ui/Project Editor/jucer_ItemPreviewComponent.h @@ -35,7 +35,6 @@ class ItemPreviewComponent : public Component public: //============================================================================== // This will delete the stream - ItemPreviewComponent (InputStream* input, const String& name); ItemPreviewComponent (const File& file); ~ItemPreviewComponent(); @@ -50,7 +49,7 @@ private: File file; Image image; - void tryToLoadImage (InputStream* input); + void tryToLoadImage(); }; diff --git a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h index c47a73c358..a1062b692b 100644 --- a/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h +++ b/extras/Jucer (experimental)/Source/utility/jucer_FillTypePropertyComponent.h @@ -26,19 +26,25 @@ #ifndef __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ #define __JUCER_FILLTYPEPROPERTYCOMPONENT_H_88CF1300__ +#include "../model/Project/jucer_Project.h" class FillTypeEditorComponent; + //============================================================================== class PopupFillSelector : public Component, public ChangeListener, public ValueTree::Listener, - public ButtonListener + public ButtonListener, + public AsyncUpdater { public: - PopupFillSelector (const ValueTree& fillState_, const ColourGradient& defaultGradient_, UndoManager* undoManager_) + PopupFillSelector (const ValueTree& fillState_, const ColourGradient& defaultGradient_, + Drawable::ImageProvider* imageProvider_, Project* project, UndoManager* undoManager_) : gradientPicker (defaultGradient_), defaultGradient (defaultGradient_), + tilePicker (imageProvider_, project), fillState (fillState_), + imageProvider (imageProvider_), undoManager (undoManager_) { colourButton.setButtonText ("Colour"); @@ -60,6 +66,9 @@ public: addChildComponent (&gradientPicker); gradientPicker.addChangeListener (this); + addChildComponent (&tilePicker); + tilePicker.addChangeListener (this); + fillState.addListener (this); colourButton.setRadioGroupId (123); @@ -90,6 +99,7 @@ public: const Rectangle content (2, y + h + 4, getWidth() - 4, getHeight() - (y + h + 6)); colourPicker.setBounds (content); gradientPicker.setBounds (content); + tilePicker.setBounds (content); } void buttonClicked (Button* b) @@ -109,7 +119,7 @@ public: // Use a cunning trick to make the wrapper dig out the earlier gradient settings, if there are any.. FillType newFill (defaultGradient); ValueTree temp ("dummy"); - Drawable::ValueTreeWrapperBase::writeFillType (temp, newFill, 0, 0, 0); + Drawable::ValueTreeWrapperBase::writeFillType (temp, newFill, 0, 0, 0, 0); fillState.setProperty (Drawable::ValueTreeWrapperBase::type, temp [Drawable::ValueTreeWrapperBase::type], undoManager); newFill = readFillType (&gp1, &gp2); @@ -117,11 +127,11 @@ public: if (newFill.gradient->getNumColours() <= 1) { newFill = FillType (defaultGradient); - Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, undoManager); + Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, imageProvider, undoManager); } else { - Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, undoManager); + Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, imageProvider, undoManager); } refresh(); @@ -137,7 +147,7 @@ public: const FillType readFillType (RelativePoint* gp1, RelativePoint* gp2) const { - return Drawable::ValueTreeWrapperBase::readFillType (fillState, gp1, gp2, 0); + return Drawable::ValueTreeWrapperBase::readFillType (fillState, gp1, gp2, 0, imageProvider); } void setFillType (const FillType& newFill) @@ -150,7 +160,7 @@ public: if (undoManager != 0) undoManager->undoCurrentTransactionOnly(); - Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, undoManager); + Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, &gp1, &gp2, imageProvider, undoManager); refresh(); } } @@ -163,6 +173,8 @@ public: setFillType (colourPicker.getCurrentColour()); else if (currentFill.isGradient()) setFillType (gradientPicker.getGradient()); + else if (currentFill.isTiledImage()) + setFillType (tilePicker.getFill()); } void refresh() @@ -171,6 +183,7 @@ public: colourPicker.setVisible (newFill.isColour()); gradientPicker.setVisible (newFill.isGradient()); + tilePicker.setVisible (newFill.isTiledImage()); if (newFill.isColour()) { @@ -182,7 +195,7 @@ public: if (newFill.gradient->getNumColours() <= 1) { newFill = FillType (defaultGradient); - Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, undoManager); + Drawable::ValueTreeWrapperBase::writeFillType (fillState, newFill, 0, 0, imageProvider, undoManager); } gradientButton.setToggleState (true, false); @@ -190,12 +203,14 @@ public: } else { + tilePicker.setFill (newFill); imageButton.setToggleState (true, false); } } - void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { refresh(); } - void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { refresh(); } + void handleAsyncUpdate() { refresh(); } + void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, const Identifier& property) { triggerAsyncUpdate(); } + void valueTreeChildrenChanged (ValueTree& treeWhoseChildHasChanged) { triggerAsyncUpdate(); } void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) {} private: @@ -433,12 +448,124 @@ private: } }; + //============================================================================== + class TiledFillDesigner : public Component, + public ChangeBroadcaster, + public ComboBoxListener, + public SliderListener + { + public: + TiledFillDesigner (Drawable::ImageProvider* imageProvider_, Project* project_) + : imageProvider (imageProvider_), project (project_) + { + addAndMakeVisible (&imageBox); + addAndMakeVisible (&opacitySlider); + opacitySlider.setRange (0.0, 1.0, 0.001); + + sliderLabel.setText ("Opacity:", false); + sliderLabel.attachToComponent (&opacitySlider, false); + + OwnedArray images; + project->findAllImageItems (images); + + for (int i = 0; i < images.size(); ++i) + imageBox.addItem (images.getUnchecked(i)->getName().toString(), i + 1); + + imageBox.setTextWhenNothingSelected ("Select an image..."); + + opacitySlider.addListener (this); + imageBox.addListener (this); + } + + ~TiledFillDesigner() + { + } + + const FillType getFill() const + { + return fill; + } + + void setFill (const FillType& newFill) + { + if (fill != newFill) + { + fill = newFill; + + OwnedArray images; + project->findAllImageItems (images); + + const String currentID (imageProvider->getIdentifierForImage (fill.image).toString()); + int idToSelect = -1; + for (int i = 0; i < images.size(); ++i) + { + if (images.getUnchecked(i)->getImageFileID() == currentID) + { + idToSelect = i + 1; + break; + } + } + + imageBox.setSelectedId (idToSelect, true); + opacitySlider.setValue (fill.getOpacity(), false, false); + } + } + + void resized() + { + imageBox.setBounds (20, 10, getWidth() - 40, 22); + opacitySlider.setBounds (20, 60, getWidth() - 40, 22); + } + + void sliderValueChanged (Slider* slider) + { + if (opacitySlider.getValue() != fill.getOpacity()) + { + FillType f (fill); + f.setOpacity ((float) opacitySlider.getValue()); + setFill (f); + sendChangeMessage (this); + } + } + + void comboBoxChanged (ComboBox* comboBoxThatHasChanged) + { + OwnedArray images; + project->findAllImageItems (images); + + Project::Item* item = images [imageBox.getSelectedId() - 1]; + if (item != 0) + { + Image im (imageProvider->getImageForIdentifier (item->getImageFileID())); + + if (im.isValid() && im != fill.image) + { + FillType f (fill); + f.image = im; + setFill (f); + sendChangeMessage (this); + } + } + } + + private: + FillType fill; + Drawable::ImageProvider* imageProvider; + Project* project; + + ComboBox imageBox; + Slider opacitySlider; + Label sliderLabel; + }; + //============================================================================== FillTypeEditorComponent* owner; StoredSettings::ColourSelectorWithSwatches colourPicker; GradientDesigner gradientPicker; + TiledFillDesigner tilePicker; ColourGradient defaultGradient; ValueTree fillState; + Drawable::ImageProvider* imageProvider; UndoManager* undoManager; TextButton colourButton, gradientButton, imageButton; @@ -454,8 +581,10 @@ class FillTypeEditorComponent : public Component, public ValueTree::Listener { public: - FillTypeEditorComponent (const ValueTree& fillState_, UndoManager* undoManager_) - : fillState (fillState_), undoManager (undoManager_) + FillTypeEditorComponent (const ValueTree& fillState_, Drawable::ImageProvider* imageProvider_, + Project* project_, UndoManager* undoManager_) + : fillState (fillState_), undoManager (undoManager_), + imageProvider (imageProvider_), project (project_) { fillState.addListener (this); refresh(); @@ -498,7 +627,7 @@ public: void refresh() { - const FillType newFill (Drawable::ValueTreeWrapperBase::readFillType (fillState, 0, 0, 0)); + const FillType newFill (Drawable::ValueTreeWrapperBase::readFillType (fillState, 0, 0, 0, imageProvider)); if (newFill != fillType) { @@ -511,7 +640,7 @@ public: { undoManager->beginNewTransaction(); - PopupFillSelector popup (fillState, getDefaultGradient(), undoManager); + PopupFillSelector popup (fillState, getDefaultGradient(), imageProvider, project, undoManager); PopupMenu m; m.addCustomItem (1234, &popup, 300, 450, false); @@ -526,7 +655,9 @@ public: private: ValueTree fillState; + Drawable::ImageProvider* imageProvider; UndoManager* undoManager; + Project* project; FillType fillType; }; @@ -536,9 +667,10 @@ class FillTypePropertyComponent : public PropertyComponent { public: //============================================================================== - FillTypePropertyComponent (UndoManager* undoManager, const String& name, const ValueTree& fill) + FillTypePropertyComponent (UndoManager* undoManager, const String& name, const ValueTree& fill, + Drawable::ImageProvider* imageProvider, Project* project) : PropertyComponent (name), - editor (fill, undoManager) + editor (fill, imageProvider, project, undoManager) { jassert (fill.isValid()); addAndMakeVisible (&editor);