| @@ -93,6 +93,11 @@ bool ComponentDocument::loadFromXml (const XmlElement& xml) | |||||
| return false; | return false; | ||||
| } | } | ||||
| void ComponentDocument::applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| backgroundGraphics->applyCustomPaintSnippets (snippets); | |||||
| } | |||||
| //============================================================================== | //============================================================================== | ||||
| class NormalTestComponent : public Component | class NormalTestComponent : public Component | ||||
| { | { | ||||
| @@ -53,6 +53,7 @@ public: | |||||
| bool loadFromXml (const XmlElement& xml); | bool loadFromXml (const XmlElement& xml); | ||||
| void fillInGeneratedCode (GeneratedCode& code) const; | void fillInGeneratedCode (GeneratedCode& code) const; | ||||
| void applyCustomPaintSnippets (StringArray&); | |||||
| private: | private: | ||||
| ScopedPointer<ComponentLayout> components; | ScopedPointer<ComponentLayout> components; | ||||
| @@ -294,7 +294,7 @@ static void copyAcrossUserSections (String& dest, const String& src) | |||||
| { | { | ||||
| StringArray sourceLines; | StringArray sourceLines; | ||||
| if (getUserSection (srcLines, tag, sourceLines)) | |||||
| if (tag != "UserPaintCustomArguments" && getUserSection (srcLines, tag, sourceLines)) | |||||
| { | { | ||||
| for (int j = endLine - i; --j > 0;) | for (int j = endLine - i; --j > 0;) | ||||
| dstLines.remove (i + 1); | dstLines.remove (i + 1); | ||||
| @@ -42,14 +42,7 @@ const char* const defaultParentClasses = "public Component"; | |||||
| JucerDocument::JucerDocument (SourceCodeDocument* c) | JucerDocument::JucerDocument (SourceCodeDocument* c) | ||||
| : cpp (c), | : cpp (c), | ||||
| className (defaultClassName), | className (defaultClassName), | ||||
| parentClasses (defaultParentClasses), | |||||
| fixedSize (false), | |||||
| initialWidth (600), | |||||
| initialHeight (400), | |||||
| snapGridPixels (8), | |||||
| snapActive (true), | |||||
| snapShown (true), | |||||
| componentOverlayOpacity (0.33f) | |||||
| parentClasses (defaultParentClasses) | |||||
| { | { | ||||
| jassert (cpp != nullptr); | jassert (cpp != nullptr); | ||||
| resources.setDocument (this); | resources.setDocument (this); | ||||
| @@ -599,13 +592,49 @@ bool JucerDocument::reloadFromDocument() | |||||
| resources.loadFromCpp (getCppFile(), cppContent); | resources.loadFromCpp (getCppFile(), cppContent); | ||||
| return loadFromXml (*currentXML); | |||||
| bool result = loadFromXml (*currentXML); | |||||
| extractCustomPaintSnippetsFromCppFile (cppContent); | |||||
| return result; | |||||
| } | |||||
| void JucerDocument::refreshCustomCodeFromDocument() | |||||
| { | |||||
| const String cppContent (cpp->getCodeDocument().getAllContent()); | |||||
| extractCustomPaintSnippetsFromCppFile (cppContent); | |||||
| } | |||||
| void JucerDocument::extractCustomPaintSnippetsFromCppFile (const String& cppContent) | |||||
| { | |||||
| StringArray customPaintSnippets; | |||||
| auto lines = StringArray::fromLines (cppContent); | |||||
| int last = 0; | |||||
| while (last >= 0) | |||||
| { | |||||
| const int start = indexOfLineStartingWith (lines, "//[UserPaintCustomArguments]", last); | |||||
| if (start < 0) | |||||
| break; | |||||
| const int end = indexOfLineStartingWith (lines, "//[/UserPaintCustomArguments]", start); | |||||
| if (end < 0) | |||||
| break; | |||||
| last = end + 1; | |||||
| String result; | |||||
| for (int i = start + 1; i < end; ++i) | |||||
| result << lines [i] << newLine; | |||||
| customPaintSnippets.add (CodeHelpers::unindent (result, 4)); | |||||
| } | |||||
| applyCustomPaintSnippets (customPaintSnippets); | |||||
| } | } | ||||
| XmlElement* JucerDocument::pullMetaDataFromCppFile (const String& cpp) | XmlElement* JucerDocument::pullMetaDataFromCppFile (const String& cpp) | ||||
| { | { | ||||
| StringArray lines; | |||||
| lines.addLines (cpp); | |||||
| auto lines = StringArray::fromLines (cpp); | |||||
| const int startLine = indexOfLineStartingWith (lines, "BEGIN_JUCER_METADATA", 0); | const int startLine = indexOfLineStartingWith (lines, "BEGIN_JUCER_METADATA", 0); | ||||
| @@ -624,17 +653,15 @@ XmlElement* JucerDocument::pullMetaDataFromCppFile (const String& cpp) | |||||
| bool JucerDocument::isValidJucerCppFile (const File& f) | bool JucerDocument::isValidJucerCppFile (const File& f) | ||||
| { | { | ||||
| if (f.hasFileExtension (".cpp")) | if (f.hasFileExtension (".cpp")) | ||||
| { | |||||
| const ScopedPointer<XmlElement> xml (pullMetaDataFromCppFile (f.loadFileAsString())); | |||||
| return xml != nullptr && xml->hasTagName (jucerCompXmlTag); | |||||
| } | |||||
| if (ScopedPointer<XmlElement> xml = pullMetaDataFromCppFile (f.loadFileAsString())) | |||||
| return xml->hasTagName (jucerCompXmlTag); | |||||
| return false; | return false; | ||||
| } | } | ||||
| static JucerDocument* createDocument (SourceCodeDocument* cpp) | static JucerDocument* createDocument (SourceCodeDocument* cpp) | ||||
| { | { | ||||
| CodeDocument& codeDoc = cpp->getCodeDocument(); | |||||
| auto& codeDoc = cpp->getCodeDocument(); | |||||
| ScopedPointer<XmlElement> xml (JucerDocument::pullMetaDataFromCppFile (codeDoc.getAllContent())); | ScopedPointer<XmlElement> xml (JucerDocument::pullMetaDataFromCppFile (codeDoc.getAllContent())); | ||||
| @@ -684,9 +711,9 @@ public: | |||||
| bool saveHeader() | bool saveHeader() | ||||
| { | { | ||||
| OpenDocumentManager& odm = ProjucerApplication::getApp().openDocumentManager; | |||||
| auto& odm = ProjucerApplication::getApp().openDocumentManager; | |||||
| if (OpenDocumentManager::Document* header = odm.openFile (nullptr, getFile().withFileExtension (".h"))) | |||||
| if (auto* header = odm.openFile (nullptr, getFile().withFileExtension (".h"))) | |||||
| return header->save(); | return header->save(); | ||||
| return false; | return false; | ||||
| @@ -694,9 +721,7 @@ public: | |||||
| Component* createEditor() override | Component* createEditor() override | ||||
| { | { | ||||
| ScopedPointer<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile())); | |||||
| if (jucerDoc != nullptr) | |||||
| if (ScopedPointer<JucerDocument> jucerDoc = JucerDocument::createForCppFile (getProject(), getFile())) | |||||
| return new JucerDocumentEditor (jucerDoc.release()); | return new JucerDocumentEditor (jucerDoc.release()); | ||||
| return SourceCodeDocument::createEditor(); | return SourceCodeDocument::createEditor(); | ||||
| @@ -736,15 +761,13 @@ public: | |||||
| headerFile.replaceWithText (String()); | headerFile.replaceWithText (String()); | ||||
| cppFile.replaceWithText (String()); | cppFile.replaceWithText (String()); | ||||
| OpenDocumentManager& odm = ProjucerApplication::getApp().openDocumentManager; | |||||
| auto& odm = ProjucerApplication::getApp().openDocumentManager; | |||||
| if (SourceCodeDocument* cpp = dynamic_cast<SourceCodeDocument*> (odm.openFile (nullptr, cppFile))) | |||||
| if (auto* cpp = dynamic_cast<SourceCodeDocument*> (odm.openFile (nullptr, cppFile))) | |||||
| { | { | ||||
| if (SourceCodeDocument* header = dynamic_cast<SourceCodeDocument*> (odm.openFile (nullptr, headerFile))) | |||||
| if (auto* header = dynamic_cast<SourceCodeDocument*> (odm.openFile (nullptr, headerFile))) | |||||
| { | { | ||||
| ScopedPointer<JucerDocument> jucerDoc (new ComponentDocument (cpp)); | |||||
| if (jucerDoc != nullptr) | |||||
| if (ScopedPointer<JucerDocument> jucerDoc = new ComponentDocument (cpp)) | |||||
| { | { | ||||
| jucerDoc->setClassName (newFile.getFileNameWithoutExtension()); | jucerDoc->setClassName (newFile.getFileNameWithoutExtension()); | ||||
| @@ -136,14 +136,17 @@ public: | |||||
| static bool shouldUseTransMacro() noexcept { return true; } | static bool shouldUseTransMacro() noexcept { return true; } | ||||
| //============================================================================== | |||||
| void refreshCustomCodeFromDocument(); | |||||
| protected: | protected: | ||||
| SourceCodeDocument* cpp; | SourceCodeDocument* cpp; | ||||
| String className, componentName, templateFile; | String className, componentName, templateFile; | ||||
| String parentClasses, constructorParams, variableInitialisers; | String parentClasses, constructorParams, variableInitialisers; | ||||
| bool fixedSize; | |||||
| int initialWidth, initialHeight; | |||||
| bool fixedSize = false; | |||||
| int initialWidth = 600, initialHeight = 400; | |||||
| BinaryResources resources; | BinaryResources resources; | ||||
| @@ -153,6 +156,8 @@ protected: | |||||
| virtual void fillInGeneratedCode (GeneratedCode&) const; | virtual void fillInGeneratedCode (GeneratedCode&) const; | ||||
| virtual void fillInPaintCode (GeneratedCode&) const; | virtual void fillInPaintCode (GeneratedCode&) const; | ||||
| virtual void applyCustomPaintSnippets (StringArray&) {} | |||||
| static void addMethod (const String& base, const String& returnVal, | static void addMethod (const String& base, const String& returnVal, | ||||
| const String& method, const String& initialContent, | const String& method, const String& initialContent, | ||||
| StringArray& baseClasses, StringArray& returnValues, | StringArray& baseClasses, StringArray& returnValues, | ||||
| @@ -160,9 +165,9 @@ protected: | |||||
| private: | private: | ||||
| UndoManager undoManager; | UndoManager undoManager; | ||||
| int snapGridPixels; | |||||
| bool snapActive, snapShown; | |||||
| float componentOverlayOpacity; | |||||
| int snapGridPixels = 8; | |||||
| bool snapActive = true, snapShown = true; | |||||
| float componentOverlayOpacity = 0.33f; | |||||
| StringArray activeExtraMethods; | StringArray activeExtraMethods; | ||||
| ScopedPointer<XmlElement> currentXML; | ScopedPointer<XmlElement> currentXML; | ||||
| ScopedPointer<Timer> userDocChangeTimer; | ScopedPointer<Timer> userDocChangeTimer; | ||||
| @@ -172,6 +177,7 @@ private: | |||||
| void codeDocumentTextDeleted (int startIndex, int endIndex) override; | void codeDocumentTextDeleted (int startIndex, int endIndex) override; | ||||
| void userEditedCpp(); | void userEditedCpp(); | ||||
| bool documentAboutToClose (OpenDocumentManager::Document*) override; | bool documentAboutToClose (OpenDocumentManager::Document*) override; | ||||
| void extractCustomPaintSnippetsFromCppFile (const String& cpp); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JucerDocument) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JucerDocument) | ||||
| }; | }; | ||||
| @@ -293,16 +293,9 @@ void PaintRoutine::copySelectedToClipboard() | |||||
| XmlElement clip (clipboardXmlTag); | XmlElement clip (clipboardXmlTag); | ||||
| for (int i = 0; i < elements.size(); ++i) | |||||
| { | |||||
| PaintElement* const pe = elements.getUnchecked(i); | |||||
| for (auto* pe : elements) | |||||
| if (selectedElements.isSelected (pe)) | if (selectedElements.isSelected (pe)) | ||||
| { | |||||
| XmlElement* const e = pe->createXml(); | |||||
| clip.addChildElement (e); | |||||
| } | |||||
| } | |||||
| clip.addChildElement (pe->createXml()); | |||||
| SystemClipboard::copyTextToClipboard (clip.createDocument ("", false, false)); | SystemClipboard::copyTextToClipboard (clip.createDocument ("", false, false)); | ||||
| } | } | ||||
| @@ -325,8 +318,8 @@ void PaintRoutine::paste() | |||||
| void PaintRoutine::deleteSelected() | void PaintRoutine::deleteSelected() | ||||
| { | { | ||||
| const SelectedItemSet <PaintElement*> temp1 (selectedElements); | |||||
| const SelectedItemSet <PathPoint*> temp2 (selectedPoints); | |||||
| const SelectedItemSet<PaintElement*> temp1 (selectedElements); | |||||
| const SelectedItemSet<PathPoint*> temp2 (selectedPoints); | |||||
| if (temp2.getNumSelected() > 0) | if (temp2.getNumSelected() > 0) | ||||
| { | { | ||||
| @@ -369,7 +362,7 @@ void PaintRoutine::selectAll() | |||||
| void PaintRoutine::selectedToFront() | void PaintRoutine::selectedToFront() | ||||
| { | { | ||||
| const SelectedItemSet <PaintElement*> temp (selectedElements); | |||||
| const SelectedItemSet<PaintElement*> temp (selectedElements); | |||||
| for (int i = temp.getNumSelected(); --i >= 0;) | for (int i = temp.getNumSelected(); --i >= 0;) | ||||
| elementToFront (temp.getSelectedItem(i), true); | elementToFront (temp.getSelectedItem(i), true); | ||||
| @@ -377,7 +370,7 @@ void PaintRoutine::selectedToFront() | |||||
| void PaintRoutine::selectedToBack() | void PaintRoutine::selectedToBack() | ||||
| { | { | ||||
| const SelectedItemSet <PaintElement*> temp (selectedElements); | |||||
| const SelectedItemSet<PaintElement*> temp (selectedElements); | |||||
| for (int i = 0; i < temp.getNumSelected(); ++i) | for (int i = 0; i < temp.getNumSelected(); ++i) | ||||
| elementToBack (temp.getSelectedItem(i), true); | elementToBack (temp.getSelectedItem(i), true); | ||||
| @@ -399,11 +392,9 @@ void PaintRoutine::ungroupSelected() | |||||
| void PaintRoutine::bringLostItemsBackOnScreen (const Rectangle<int>& parentArea) | void PaintRoutine::bringLostItemsBackOnScreen (const Rectangle<int>& parentArea) | ||||
| { | { | ||||
| for (int i = 0; i < elements.size(); ++i) | |||||
| for (auto* c : elements) | |||||
| { | { | ||||
| PaintElement* const c = elements[i]; | |||||
| Rectangle<int> r (c->getCurrentBounds (parentArea)); | |||||
| auto r = c->getCurrentBounds (parentArea); | |||||
| if (! r.intersects (parentArea)) | if (! r.intersects (parentArea)) | ||||
| { | { | ||||
| @@ -415,11 +406,9 @@ void PaintRoutine::bringLostItemsBackOnScreen (const Rectangle<int>& parentArea) | |||||
| void PaintRoutine::startDragging (const Rectangle<int>& parentArea) | void PaintRoutine::startDragging (const Rectangle<int>& parentArea) | ||||
| { | { | ||||
| for (int i = 0; i < elements.size(); ++i) | |||||
| for (auto* c : elements) | |||||
| { | { | ||||
| PaintElement* const c = elements[i]; | |||||
| Rectangle<int> r (c->getCurrentBounds (parentArea)); | |||||
| auto r = c->getCurrentBounds (parentArea); | |||||
| c->getProperties().set ("xDragStart", r.getX()); | c->getProperties().set ("xDragStart", r.getX()); | ||||
| c->getProperties().set ("yDragStart", r.getY()); | c->getProperties().set ("yDragStart", r.getY()); | ||||
| @@ -490,8 +479,8 @@ void PaintRoutine::drawElements (Graphics& g, const Rectangle<int>& relativeTo) | |||||
| Component temp; | Component temp; | ||||
| temp.setBounds (relativeTo); | temp.setBounds (relativeTo); | ||||
| for (int i = 0; i < elements.size(); ++i) | |||||
| elements.getUnchecked (i)->draw (g, getDocument()->getComponentLayout(), relativeTo); | |||||
| for (auto* e : elements) | |||||
| e->draw (g, getDocument()->getComponentLayout(), relativeTo); | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -501,17 +490,16 @@ void PaintRoutine::dropImageAt (const File& f, int x, int y) | |||||
| if (d != nullptr) | if (d != nullptr) | ||||
| { | { | ||||
| Rectangle<float> bounds (d->getDrawableBounds()); | |||||
| auto bounds = d->getDrawableBounds(); | |||||
| d = nullptr; | d = nullptr; | ||||
| PaintElement* newElement | |||||
| = addNewElement (ObjectTypes::createNewImageElement (this), -1, true); | |||||
| auto* newElement = addNewElement (ObjectTypes::createNewImageElement (this), -1, true); | |||||
| if (PaintElementImage* pei = dynamic_cast<PaintElementImage*> (newElement)) | |||||
| if (auto* pei = dynamic_cast<PaintElementImage*> (newElement)) | |||||
| { | { | ||||
| String resourceName (getDocument()->getResources().findUniqueName (f.getFileName())); | String resourceName (getDocument()->getResources().findUniqueName (f.getFileName())); | ||||
| if (const BinaryResources::BinaryResource* existingResource = getDocument()->getResources().getResourceForFile (f)) | |||||
| if (auto* existingResource = getDocument()->getResources().getResourceForFile (f)) | |||||
| { | { | ||||
| resourceName = existingResource->name; | resourceName = existingResource->name; | ||||
| } | } | ||||
| @@ -546,12 +534,11 @@ const char* PaintRoutine::xmlTagName = "BACKGROUND"; | |||||
| XmlElement* PaintRoutine::createXml() const | XmlElement* PaintRoutine::createXml() const | ||||
| { | { | ||||
| XmlElement* const xml = new XmlElement (xmlTagName); | |||||
| auto* xml = new XmlElement (xmlTagName); | |||||
| xml->setAttribute ("backgroundColour", backgroundColour.toString()); | xml->setAttribute ("backgroundColour", backgroundColour.toString()); | ||||
| for (int i = 0; i < elements.size(); ++i) | |||||
| xml->addChildElement (elements.getUnchecked (i)->createXml()); | |||||
| for (auto* e : elements) | |||||
| xml->addChildElement (e->createXml()); | |||||
| return xml; | return xml; | ||||
| } | } | ||||
| @@ -565,7 +552,7 @@ bool PaintRoutine::loadFromXml (const XmlElement& xml) | |||||
| clear(); | clear(); | ||||
| forEachXmlChildElement (xml, e) | forEachXmlChildElement (xml, e) | ||||
| if (PaintElement* const newElement = ObjectTypes::createElementForXml (e, this)) | |||||
| if (auto* newElement = ObjectTypes::createElementForXml (e, this)) | |||||
| elements.add (newElement); | elements.add (newElement); | ||||
| return true; | return true; | ||||
| @@ -579,6 +566,12 @@ void PaintRoutine::fillInGeneratedCode (GeneratedCode& code, String& paintMethod | |||||
| if (! backgroundColour.isTransparent()) | if (! backgroundColour.isTransparent()) | ||||
| paintMethodCode << "g.fillAll (" << CodeHelpers::colourToCode (backgroundColour) << ");\n\n"; | paintMethodCode << "g.fillAll (" << CodeHelpers::colourToCode (backgroundColour) << ");\n\n"; | ||||
| for (int i = 0; i < elements.size(); ++i) | |||||
| elements[i]->fillInGeneratedCode (code, paintMethodCode); | |||||
| for (auto* e : elements) | |||||
| e->fillInGeneratedCode (code, paintMethodCode); | |||||
| } | |||||
| void PaintRoutine::applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| for (auto* e : elements) | |||||
| e->applyCustomPaintSnippets (snippets); | |||||
| } | } | ||||
| @@ -103,6 +103,8 @@ public: | |||||
| void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) const; | void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) const; | ||||
| void applyCustomPaintSnippets (StringArray&); | |||||
| //============================================================================== | //============================================================================== | ||||
| private: | private: | ||||
| OwnedArray <PaintElement> elements; | OwnedArray <PaintElement> elements; | ||||
| @@ -117,58 +117,89 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) const | |||||
| String generateVariablesCode (String type) const | |||||
| { | { | ||||
| String s; | String s; | ||||
| switch (mode) | switch (mode) | ||||
| { | { | ||||
| case solidColour: | |||||
| s << "g.setColour (" << CodeHelpers::colourToCode (colour) << ");\n"; | |||||
| break; | |||||
| case solidColour: | |||||
| s << "Colour " << type << "Colour = " << CodeHelpers::colourToCode (colour) << ";\n"; | |||||
| break; | |||||
| case linearGradient: | |||||
| case radialGradient: | |||||
| { | |||||
| String x1, y1, w, h, x2, y2; | |||||
| positionToCode (gradPos1, code.document->getComponentLayout(), x1, y1, w, h); | |||||
| positionToCode (gradPos2, code.document->getComponentLayout(), x2, y2, w, h); | |||||
| case linearGradient: | |||||
| case radialGradient: | |||||
| s << "Colour " << type << "Colour1 = " << CodeHelpers::colourToCode (gradCol1) << ", " << type << "Colour2 = " << CodeHelpers::colourToCode (gradCol2) << ";\n"; | |||||
| break; | |||||
| s << "g.setGradientFill (ColourGradient ("; | |||||
| case imageBrush: | |||||
| break; | |||||
| const String indent (String::repeatedString (" ", s.length())); | |||||
| default: | |||||
| jassertfalse; | |||||
| break; | |||||
| } | |||||
| s << CodeHelpers::colourToCode (gradCol1) << ",\n" | |||||
| << indent << castToFloat (x1) << ", " << castToFloat (y1) << ",\n" | |||||
| << indent << CodeHelpers::colourToCode (gradCol2) << ",\n" | |||||
| << indent << castToFloat (x2) << ", " << castToFloat (y2) << ",\n" | |||||
| << indent << CodeHelpers::boolLiteral (mode == radialGradient) << "));\n"; | |||||
| return s; | |||||
| } | |||||
| void fillInGeneratedCode (String type, RelativePositionedRectangle relativeTo, GeneratedCode& code, String& paintMethodCode) const | |||||
| { | |||||
| String s; | |||||
| switch (mode) | |||||
| { | |||||
| case solidColour: | |||||
| s << "g.setColour (" << type << "Colour);\n"; | |||||
| break; | break; | ||||
| } | |||||
| case imageBrush: | |||||
| { | |||||
| const String imageVariable ("cachedImage_" + imageResourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix())); | |||||
| case linearGradient: | |||||
| case radialGradient: | |||||
| { | |||||
| String x0, y0, x1, y1, w, h, x2, y2; | |||||
| positionToCode (relativeTo, code.document->getComponentLayout(), x0, y0, w, h); | |||||
| positionToCode (gradPos1, code.document->getComponentLayout(), x1, y1, w, h); | |||||
| positionToCode (gradPos2, code.document->getComponentLayout(), x2, y2, w, h); | |||||
| s << "g.setGradientFill (ColourGradient ("; | |||||
| auto indent = String::repeatedString (" ", s.length()); | |||||
| s << type << "Colour1,\n" | |||||
| << indent << castToFloat (x1) << " - " << castToFloat (x0) << " + x,\n" | |||||
| << indent << castToFloat (y1) << " - " << castToFloat (y0) << " + y,\n" | |||||
| << indent << type << "Colour2,\n" | |||||
| << indent << castToFloat (x2) << " - " << castToFloat (x0) << " + x,\n" | |||||
| << indent << castToFloat (y2) << " - " << castToFloat (y0) << " + y,\n" | |||||
| << indent << CodeHelpers::boolLiteral (mode == radialGradient) << "));\n"; | |||||
| break; | |||||
| } | |||||
| code.addImageResourceLoader (imageVariable, imageResourceName); | |||||
| case imageBrush: | |||||
| { | |||||
| auto imageVariable = "cachedImage_" + imageResourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix()); | |||||
| String x, y, w, h; | |||||
| positionToCode (imageAnchor, code.document->getComponentLayout(), x, y, w, h); | |||||
| code.addImageResourceLoader (imageVariable, imageResourceName); | |||||
| s << "g.setTiledImageFill ("; | |||||
| String x0, y0, x1, y1, w, h; | |||||
| positionToCode (relativeTo, code.document->getComponentLayout(), x0, y0, w, h); | |||||
| positionToCode (imageAnchor, code.document->getComponentLayout(), x1, y1, w, h); | |||||
| const String indent (String::repeatedString (" ", s.length())); | |||||
| s << "g.setTiledImageFill ("; | |||||
| s << imageVariable << ",\n" | |||||
| << indent << x << ", " << y << ",\n" | |||||
| << indent << CodeHelpers::floatLiteral (imageOpacity, 4) << ");\n"; | |||||
| const String indent (String::repeatedString (" ", s.length())); | |||||
| break; | |||||
| } | |||||
| s << imageVariable << ",\n" | |||||
| << indent << x1 << " - " << x0 << " + x,\n" | |||||
| << indent << y1 << " - " << y0 << " + y,\n" | |||||
| << indent << CodeHelpers::floatLiteral (imageOpacity, 4) << ");\n"; | |||||
| default: | |||||
| jassertfalse; | |||||
| break; | |||||
| break; | |||||
| } | |||||
| default: | |||||
| jassertfalse; | |||||
| break; | |||||
| } | } | ||||
| paintMethodCode += s; | paintMethodCode += s; | ||||
| @@ -178,29 +209,29 @@ public: | |||||
| { | { | ||||
| switch (mode) | switch (mode) | ||||
| { | { | ||||
| case solidColour: | |||||
| return "solid: " + colour.toString(); | |||||
| case linearGradient: | |||||
| case radialGradient: | |||||
| return (mode == linearGradient ? "linear: " | |||||
| : " radial: ") | |||||
| + gradPos1.toString() | |||||
| + ", " | |||||
| + gradPos2.toString() | |||||
| + ", 0=" + gradCol1.toString() | |||||
| + ", 1=" + gradCol2.toString(); | |||||
| case imageBrush: | |||||
| return "image: " + imageResourceName.replaceCharacter (':', '#') | |||||
| + ", " | |||||
| + String (imageOpacity) | |||||
| + ", " | |||||
| + imageAnchor.toString(); | |||||
| default: | |||||
| jassertfalse; | |||||
| break; | |||||
| case solidColour: | |||||
| return "solid: " + colour.toString(); | |||||
| case linearGradient: | |||||
| case radialGradient: | |||||
| return (mode == linearGradient ? "linear: " | |||||
| : " radial: ") | |||||
| + gradPos1.toString() | |||||
| + ", " | |||||
| + gradPos2.toString() | |||||
| + ", 0=" + gradCol1.toString() | |||||
| + ", 1=" + gradCol2.toString(); | |||||
| case imageBrush: | |||||
| return "image: " + imageResourceName.replaceCharacter (':', '#') | |||||
| + ", " | |||||
| + String (imageOpacity) | |||||
| + ", " | |||||
| + imageAnchor.toString(); | |||||
| default: | |||||
| jassertfalse; | |||||
| break; | |||||
| } | } | ||||
| return {}; | return {}; | ||||
| @@ -94,6 +94,8 @@ public: | |||||
| void changeListenerCallback (ChangeBroadcaster*); | void changeListenerCallback (ChangeBroadcaster*); | ||||
| void parentHierarchyChanged(); | void parentHierarchyChanged(); | ||||
| virtual void applyCustomPaintSnippets (StringArray&) {} | |||||
| int borderThickness; | int borderThickness; | ||||
| protected: | protected: | ||||
| @@ -159,7 +161,7 @@ public: | |||||
| void changeListenerCallback (ChangeBroadcaster*) | void changeListenerCallback (ChangeBroadcaster*) | ||||
| { | { | ||||
| jassert (propToRefresh != nullptr); | jassert (propToRefresh != nullptr); | ||||
| if (propToRefresh != nullptr) | |||||
| if (propToRefresh != nullptr && owner != nullptr) | |||||
| propToRefresh->refresh(); | propToRefresh->refresh(); | ||||
| } | } | ||||
| @@ -62,35 +62,53 @@ public: | |||||
| void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | ||||
| { | { | ||||
| if (fillType.isInvisible() && (strokeType.isInvisible() || ! isStrokePresent)) | |||||
| return; | |||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| s << "{\n" | |||||
| << " float x = " << castToFloat (x) << ", y = " << castToFloat (y) << ", " | |||||
| << "width = " << castToFloat (w) << ", height = " << castToFloat (h) << ";\n"; | |||||
| if (! fillType.isInvisible()) | if (! fillType.isInvisible()) | ||||
| { | |||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| s << " " << fillType.generateVariablesCode ("fill"); | |||||
| fillType.fillInGeneratedCode (code, paintMethodCode); | |||||
| s << "g.fillEllipse (" | |||||
| << castToFloat (x) << ", " | |||||
| << castToFloat (y) << ", " | |||||
| << castToFloat (w) << ", " | |||||
| << castToFloat (h) << ");\n\n"; | |||||
| if (isStrokePresent && ! strokeType.isInvisible()) | |||||
| s << " " << strokeType.fill.generateVariablesCode ("stroke"); | |||||
| paintMethodCode += s; | |||||
| s << " //[UserPaintCustomArguments] Customize the painting arguments here..\n" | |||||
| << customPaintCode | |||||
| << " //[/UserPaintCustomArguments]\n"; | |||||
| if (! fillType.isInvisible()) | |||||
| { | |||||
| s << " "; | |||||
| fillType.fillInGeneratedCode ("fill", position, code, s); | |||||
| s << " g.fillEllipse (x, y, width, height);\n"; | |||||
| } | } | ||||
| if (isStrokePresent && ! strokeType.isInvisible()) | if (isStrokePresent && ! strokeType.isInvisible()) | ||||
| { | { | ||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| strokeType.fill.fillInGeneratedCode (code, paintMethodCode); | |||||
| s << "g.drawEllipse (" | |||||
| << castToFloat (x) << ", " | |||||
| << castToFloat (y) << ", " | |||||
| << castToFloat (w) << ", " | |||||
| << castToFloat (h) << ", " | |||||
| << CodeHelpers::floatLiteral (strokeType.stroke.getStrokeThickness(), 3) << ");\n\n"; | |||||
| paintMethodCode += s; | |||||
| s << " "; | |||||
| strokeType.fill.fillInGeneratedCode ("stroke", position, code, s); | |||||
| s << " g.drawEllipse (x, y, width, height, " << CodeHelpers::floatLiteral (strokeType.stroke.getStrokeThickness(), 3) << ");\n"; | |||||
| } | |||||
| s << "}\n\n"; | |||||
| paintMethodCode += s; | |||||
| } | |||||
| void applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| customPaintCode.clear(); | |||||
| if (! snippets.isEmpty() && (! fillType.isInvisible() || (isStrokePresent && ! strokeType.isInvisible()))) | |||||
| { | |||||
| customPaintCode = snippets[0]; | |||||
| snippets.remove (0); | |||||
| } | } | ||||
| } | } | ||||
| @@ -131,10 +149,11 @@ public: | |||||
| } | } | ||||
| private: | private: | ||||
| String customPaintCode; | |||||
| //============================================================================== | //============================================================================== | ||||
| class ShapeToPathProperty : public ButtonPropertyComponent | |||||
| struct ShapeToPathProperty : public ButtonPropertyComponent | |||||
| { | { | ||||
| public: | |||||
| ShapeToPathProperty (PaintElementEllipse* const e) | ShapeToPathProperty (PaintElementEllipse* const e) | ||||
| : ButtonPropertyComponent ("path", false), | : ButtonPropertyComponent ("path", false), | ||||
| element (e) | element (e) | ||||
| @@ -151,7 +170,6 @@ private: | |||||
| return "convert to a path"; | return "convert to a path"; | ||||
| } | } | ||||
| private: | |||||
| PaintElementEllipse* const element; | |||||
| PaintElementEllipse* element; | |||||
| }; | }; | ||||
| }; | }; | ||||
| @@ -209,12 +209,17 @@ public: | |||||
| return false; | return false; | ||||
| } | } | ||||
| void applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| for (auto* e : subElements) | |||||
| e->applyCustomPaintSnippets (snippets); | |||||
| } | |||||
| private: | private: | ||||
| OwnedArray <PaintElement> subElements; | |||||
| OwnedArray<PaintElement> subElements; | |||||
| class UngroupProperty : public ButtonPropertyComponent | |||||
| struct UngroupProperty : public ButtonPropertyComponent | |||||
| { | { | ||||
| public: | |||||
| UngroupProperty (PaintElementGroup* const e) | UngroupProperty (PaintElementGroup* const e) | ||||
| : ButtonPropertyComponent ("ungroup", false), | : ButtonPropertyComponent ("ungroup", false), | ||||
| element (e) | element (e) | ||||
| @@ -231,7 +236,6 @@ private: | |||||
| return "Ungroup"; | return "Ungroup"; | ||||
| } | } | ||||
| private: | |||||
| PaintElementGroup* const element; | |||||
| PaintElementGroup* element; | |||||
| }; | }; | ||||
| }; | }; | ||||
| @@ -95,10 +95,16 @@ public: | |||||
| void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | ||||
| { | { | ||||
| String r; | |||||
| if (opacity > 0) | if (opacity > 0) | ||||
| { | { | ||||
| String x, y, w, h, r; | |||||
| positionToCode (position, getDocument()->getComponentLayout(), x, y, w, h); | |||||
| r << "{\n" | |||||
| << " int x = " << x << ", y = " << y << ", width = " << w << ", height = " << h << ";\n" | |||||
| << " //[UserPaintCustomArguments] Customize the painting arguments here..\n" | |||||
| << customPaintCode | |||||
| << " //[/UserPaintCustomArguments]\n"; | |||||
| if (dynamic_cast<const DrawableImage*> (getDrawable()) != 0) | if (dynamic_cast<const DrawableImage*> (getDrawable()) != 0) | ||||
| { | { | ||||
| const String imageVariable ("cachedImage_" + resourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix())); | const String imageVariable ("cachedImage_" + resourceName.replace ("::", "_") + "_" + String (code.getUniqueSuffix())); | ||||
| @@ -106,36 +112,32 @@ public: | |||||
| code.addImageResourceLoader (imageVariable, resourceName); | code.addImageResourceLoader (imageVariable, resourceName); | ||||
| if (opacity >= 254.0 / 255.0) | if (opacity >= 254.0 / 255.0) | ||||
| r << "g.setColour (Colours::black);\n"; | |||||
| r << " g.setColour (Colours::black);\n"; | |||||
| else | else | ||||
| r << "g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n"; | |||||
| r << " g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n"; | |||||
| String x, y, w, h; | |||||
| positionToCode (position, getDocument()->getComponentLayout(), x, y, w, h); | |||||
| if (mode == stretched) | if (mode == stretched) | ||||
| { | { | ||||
| r << "g.drawImage (" << imageVariable << ",\n " | |||||
| << x << ", " << y << ", " << w << ", " << h | |||||
| << ",\n 0, 0, " | |||||
| << imageVariable << ".getWidth(), " | |||||
| << imageVariable << ".getHeight());\n\n"; | |||||
| r << " g.drawImage (" << imageVariable << ",\n" | |||||
| << " x, y, width, height,\n" | |||||
| << " 0, 0, " << imageVariable << ".getWidth(), " << imageVariable << ".getHeight());\n"; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| r << "g.drawImageWithin (" << imageVariable << ",\n " | |||||
| << x << ", " << y << ", " << w << ", " << h | |||||
| << ",\n "; | |||||
| r << " g.drawImageWithin (" << imageVariable << ",\n" | |||||
| << " x, y, width, height,\n" | |||||
| << " "; | |||||
| if (mode == proportionalReducingOnly) | if (mode == proportionalReducingOnly) | ||||
| r << "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize"; | r << "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize"; | ||||
| else | else | ||||
| r << "RectanglePlacement::centred"; | r << "RectanglePlacement::centred"; | ||||
| r << ",\n false);\n\n"; | |||||
| r << ",\n" | |||||
| << " false);\n"; | |||||
| } | } | ||||
| paintMethodCode += r; | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| @@ -154,28 +156,35 @@ public: | |||||
| << imageVariable << " = nullptr;\n"; | << imageVariable << " = nullptr;\n"; | ||||
| if (opacity >= 254.0 / 255.0) | if (opacity >= 254.0 / 255.0) | ||||
| r << "g.setColour (Colours::black);\n"; | |||||
| r << " g.setColour (Colours::black);\n"; | |||||
| else | else | ||||
| r << "g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n"; | |||||
| String x, y, w, h; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| r << " g.setColour (Colours::black.withAlpha (" << CodeHelpers::floatLiteral (opacity, 3) << "));\n"; | |||||
| r << "jassert (" << imageVariable << " != 0);\n" | |||||
| << "if (" << imageVariable << " != 0)\n " | |||||
| << imageVariable << "->drawWithin (g, Rectangle<float> (" | |||||
| << x << ", " << y << ", " << w << ", " << h | |||||
| << "),\n" | |||||
| << String::repeatedString (" ", imageVariable.length() + 18) | |||||
| r << " jassert (" << imageVariable << " != 0);\n" | |||||
| << " if (" << imageVariable << " != 0)\n" | |||||
| << " " << imageVariable << "->drawWithin (g, Rectangle<float> (x, y, width, height),\n" | |||||
| << " " << String::repeatedString (" ", imageVariable.length() + 18) | |||||
| << (mode == stretched ? "RectanglePlacement::stretchToFit" | << (mode == stretched ? "RectanglePlacement::stretchToFit" | ||||
| : (mode == proportionalReducingOnly ? "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize" | : (mode == proportionalReducingOnly ? "RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize" | ||||
| : "RectanglePlacement::centred")) | : "RectanglePlacement::centred")) | ||||
| << ", " << CodeHelpers::floatLiteral (opacity, 3) | |||||
| << ");\n\n"; | |||||
| paintMethodCode += r; | |||||
| << ", " << CodeHelpers::floatLiteral (opacity, 3) << ");\n"; | |||||
| } | } | ||||
| } | } | ||||
| r << "}\n\n"; | |||||
| paintMethodCode += r; | |||||
| } | |||||
| } | |||||
| void applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| customPaintCode.clear(); | |||||
| if (! snippets.isEmpty() && opacity > 0) | |||||
| { | |||||
| customPaintCode = snippets[0]; | |||||
| snippets.remove (0); | |||||
| } | } | ||||
| } | } | ||||
| @@ -385,6 +394,7 @@ private: | |||||
| String resourceName; | String resourceName; | ||||
| double opacity; | double opacity; | ||||
| StretchMode mode; | StretchMode mode; | ||||
| String customPaintCode; | |||||
| //============================================================================== | //============================================================================== | ||||
| class ImageElementResourceProperty : public ImageResourceProperty <PaintElementImage> | class ImageElementResourceProperty : public ImageResourceProperty <PaintElementImage> | ||||
| @@ -382,10 +382,8 @@ void PaintElementPath::fillInGeneratedCode (GeneratedCode& code, String& paintMe | |||||
| if (! nonZeroWinding) | if (! nonZeroWinding) | ||||
| r << pathVariable << ".setUsingNonZeroWinding (false);\n"; | r << pathVariable << ".setUsingNonZeroWinding (false);\n"; | ||||
| for (int i = 0; i < points.size(); ++i) | |||||
| for (auto* p : points) | |||||
| { | { | ||||
| const PathPoint* const p = points.getUnchecked(i); | |||||
| switch (p->type) | switch (p->type) | ||||
| { | { | ||||
| case Path::Iterator::startNewSubPath: | case Path::Iterator::startNewSubPath: | ||||
| @@ -400,15 +398,17 @@ void PaintElementPath::fillInGeneratedCode (GeneratedCode& code, String& paintMe | |||||
| case Path::Iterator::quadraticTo: | case Path::Iterator::quadraticTo: | ||||
| r << pathVariable << ".quadraticTo (" << positionToPairOfValues (p->pos[0], layout) | r << pathVariable << ".quadraticTo (" << positionToPairOfValues (p->pos[0], layout) | ||||
| << ", " << positionToPairOfValues (p->pos[1], layout) << ");\n"; | |||||
| << ", " << positionToPairOfValues (p->pos[1], layout) << ");\n"; | |||||
| somePointsAreRelative = somePointsAreRelative || ! p->pos[0].rect.isPositionAbsolute(); | somePointsAreRelative = somePointsAreRelative || ! p->pos[0].rect.isPositionAbsolute(); | ||||
| somePointsAreRelative = somePointsAreRelative || ! p->pos[1].rect.isPositionAbsolute(); | somePointsAreRelative = somePointsAreRelative || ! p->pos[1].rect.isPositionAbsolute(); | ||||
| break; | break; | ||||
| case Path::Iterator::cubicTo: | case Path::Iterator::cubicTo: | ||||
| r << pathVariable << ".cubicTo (" << positionToPairOfValues (p->pos[0], layout) | r << pathVariable << ".cubicTo (" << positionToPairOfValues (p->pos[0], layout) | ||||
| << ", " << positionToPairOfValues (p->pos[1], layout) | |||||
| << ", " << positionToPairOfValues (p->pos[2], layout) << ");\n"; | |||||
| << ", " << positionToPairOfValues (p->pos[1], layout) | |||||
| << ", " << positionToPairOfValues (p->pos[2], layout) << ");\n"; | |||||
| somePointsAreRelative = somePointsAreRelative || ! p->pos[0].rect.isPositionAbsolute(); | somePointsAreRelative = somePointsAreRelative || ! p->pos[0].rect.isPositionAbsolute(); | ||||
| somePointsAreRelative = somePointsAreRelative || ! p->pos[1].rect.isPositionAbsolute(); | somePointsAreRelative = somePointsAreRelative || ! p->pos[1].rect.isPositionAbsolute(); | ||||
| somePointsAreRelative = somePointsAreRelative || ! p->pos[2].rect.isPositionAbsolute(); | somePointsAreRelative = somePointsAreRelative || ! p->pos[2].rect.isPositionAbsolute(); | ||||
| @@ -432,24 +432,50 @@ void PaintElementPath::fillInGeneratedCode (GeneratedCode& code, String& paintMe | |||||
| else | else | ||||
| code.constructorCode << r; | code.constructorCode << r; | ||||
| String s; | |||||
| s << "{\n" | |||||
| << " float x = 0, y = 0;\n"; | |||||
| if (! fillType.isInvisible()) | if (! fillType.isInvisible()) | ||||
| { | |||||
| fillType.fillInGeneratedCode (code, paintMethodCode); | |||||
| s << " " << fillType.generateVariablesCode ("fill"); | |||||
| if (isStrokePresent && ! strokeType.isInvisible()) | |||||
| s << " " << strokeType.fill.generateVariablesCode ("stroke"); | |||||
| s << " //[UserPaintCustomArguments] Customize the painting arguments here..\n" | |||||
| << customPaintCode | |||||
| << " //[/UserPaintCustomArguments]\n"; | |||||
| paintMethodCode << "g.fillPath (" << pathVariable << ");\n"; | |||||
| RelativePositionedRectangle zero; | |||||
| if (! fillType.isInvisible()) | |||||
| { | |||||
| s << " "; | |||||
| fillType.fillInGeneratedCode ("fill", zero, code, s); | |||||
| s << " g.fillPath (" << pathVariable << ", AffineTransform::translation(x, y));\n"; | |||||
| } | } | ||||
| if (isStrokePresent && ! strokeType.isInvisible()) | if (isStrokePresent && ! strokeType.isInvisible()) | ||||
| { | { | ||||
| String s; | |||||
| s << " "; | |||||
| strokeType.fill.fillInGeneratedCode ("stroke", zero, code, s); | |||||
| s << " g.strokePath (" << pathVariable << ", " << strokeType.getPathStrokeCode() << ", AffineTransform::translation(x, y));\n"; | |||||
| } | |||||
| strokeType.fill.fillInGeneratedCode (code, s); | |||||
| s << "g.strokePath (" << pathVariable << ", " << strokeType.getPathStrokeCode() << ");\n"; | |||||
| s << "}\n\n"; | |||||
| paintMethodCode += s; | |||||
| } | |||||
| paintMethodCode += s; | |||||
| } | |||||
| void PaintElementPath::applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| customPaintCode.clear(); | |||||
| paintMethodCode += "\n"; | |||||
| if (! snippets.isEmpty() && (! fillType.isInvisible() || (isStrokePresent && ! strokeType.isInvisible()))) | |||||
| { | |||||
| customPaintCode = snippets[0]; | |||||
| snippets.remove(0); | |||||
| } | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -101,6 +101,7 @@ public: | |||||
| void getEditableProperties (Array<PropertyComponent*>& props); | void getEditableProperties (Array<PropertyComponent*>& props); | ||||
| void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode); | void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode); | ||||
| void applyCustomPaintSnippets (StringArray& snippets); | |||||
| //============================================================================== | //============================================================================== | ||||
| static const char* getTagName() noexcept { return "PATH"; } | static const char* getTagName() noexcept { return "PATH"; } | ||||
| @@ -132,6 +133,7 @@ private: | |||||
| mutable Rectangle<int> lastPathBounds; | mutable Rectangle<int> lastPathBounds; | ||||
| int mouseDownOnSegment; | int mouseDownOnSegment; | ||||
| bool mouseDownSelectSegmentStatus; | bool mouseDownSelectSegmentStatus; | ||||
| String customPaintCode; | |||||
| String pathToString() const; | String pathToString() const; | ||||
| void restorePathFromString (const String& s); | void restorePathFromString (const String& s); | ||||
| @@ -76,27 +76,52 @@ public: | |||||
| void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | ||||
| { | { | ||||
| if (fillType.isInvisible() && (strokeType.isInvisible() || ! isStrokePresent)) | |||||
| return; | |||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| s << "{\n" | |||||
| << " int x = " << x << ", y = " << y << ", width = " << w << ", height = " << h << ";\n"; | |||||
| if (! fillType.isInvisible()) | if (! fillType.isInvisible()) | ||||
| { | |||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| s << " " << fillType.generateVariablesCode ("fill"); | |||||
| fillType.fillInGeneratedCode (code, paintMethodCode); | |||||
| s << "g.fillRect (" << x << ", " << y << ", " << w << ", " << h << ");\n\n"; | |||||
| if (isStrokePresent && ! strokeType.isInvisible()) | |||||
| s << " " << strokeType.fill.generateVariablesCode ("stroke"); | |||||
| s << " //[UserPaintCustomArguments] Customize the painting arguments here..\n" | |||||
| << customPaintCode | |||||
| << " //[/UserPaintCustomArguments]\n"; | |||||
| paintMethodCode += s; | |||||
| if (! fillType.isInvisible()) | |||||
| { | |||||
| s << " "; | |||||
| fillType.fillInGeneratedCode ("fill", position, code, s); | |||||
| s << " g.fillRect (x, y, width, height);\n"; | |||||
| } | } | ||||
| if (isStrokePresent && ! strokeType.isInvisible()) | if (isStrokePresent && ! strokeType.isInvisible()) | ||||
| { | { | ||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| s << " "; | |||||
| strokeType.fill.fillInGeneratedCode ("stroke", position, code, s); | |||||
| s << " g.drawRect (x, y, width, height, " << roundToInt (strokeType.stroke.getStrokeThickness()) << ");\n\n"; | |||||
| } | |||||
| s << "}\n\n"; | |||||
| strokeType.fill.fillInGeneratedCode (code, paintMethodCode); | |||||
| s << "g.drawRect (" << x << ", " << y << ", " << w << ", " << h << ", " | |||||
| << roundToInt (strokeType.stroke.getStrokeThickness()) << ");\n\n"; | |||||
| paintMethodCode += s; | |||||
| } | |||||
| paintMethodCode += s; | |||||
| void applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| customPaintCode.clear(); | |||||
| if (! snippets.isEmpty() && (! fillType.isInvisible() || (isStrokePresent && ! strokeType.isInvisible()))) | |||||
| { | |||||
| customPaintCode = snippets[0]; | |||||
| snippets.remove (0); | |||||
| } | } | ||||
| } | } | ||||
| @@ -134,6 +159,8 @@ public: | |||||
| } | } | ||||
| private: | private: | ||||
| String customPaintCode; | |||||
| class ShapeToPathProperty : public ButtonPropertyComponent | class ShapeToPathProperty : public ButtonPropertyComponent | ||||
| { | { | ||||
| public: | public: | ||||
| @@ -116,37 +116,54 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | void fillInGeneratedCode (GeneratedCode& code, String& paintMethodCode) | ||||
| { | { | ||||
| if (fillType.isInvisible() && (strokeType.isInvisible() || ! isStrokePresent)) | |||||
| return; | |||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| s << "{\n" | |||||
| << " float x = " << castToFloat (x) << ", y = " << castToFloat (y) << ", " | |||||
| << "width = " << castToFloat (w) << ", height = " << castToFloat (h) << ";\n"; | |||||
| if (! fillType.isInvisible()) | |||||
| s << " " << fillType.generateVariablesCode ("fill"); | |||||
| if (isStrokePresent && ! strokeType.isInvisible()) | |||||
| s << " " << strokeType.fill.generateVariablesCode ("stroke"); | |||||
| s << " //[UserPaintCustomArguments] Customize the painting arguments here..\n" | |||||
| << customPaintCode | |||||
| << " //[/UserPaintCustomArguments]\n"; | |||||
| if (! fillType.isInvisible()) | if (! fillType.isInvisible()) | ||||
| { | { | ||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| fillType.fillInGeneratedCode (code, paintMethodCode); | |||||
| s << "g.fillRoundedRectangle (" | |||||
| << castToFloat (x) << ", " | |||||
| << castToFloat (y) << ", " | |||||
| << castToFloat (w) << ", " | |||||
| << castToFloat (h) << ", " | |||||
| << CodeHelpers::floatLiteral (cornerSize, 3) << ");\n\n"; | |||||
| paintMethodCode += s; | |||||
| s << " "; | |||||
| fillType.fillInGeneratedCode ("fill", position, code, s); | |||||
| s << " g.fillRoundedRectangle (x, y, width, height, " << CodeHelpers::floatLiteral (cornerSize, 3) << ");\n"; | |||||
| } | } | ||||
| if (isStrokePresent && ! strokeType.isInvisible()) | if (isStrokePresent && ! strokeType.isInvisible()) | ||||
| { | { | ||||
| String x, y, w, h, s; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | |||||
| strokeType.fill.fillInGeneratedCode (code, paintMethodCode); | |||||
| s << "g.drawRoundedRectangle (" | |||||
| << castToFloat (x) << ", " | |||||
| << castToFloat (y) << ", " | |||||
| << castToFloat (w) << ", " | |||||
| << castToFloat (h) << ", " | |||||
| << CodeHelpers::floatLiteral (cornerSize, 3) << ", " | |||||
| << CodeHelpers::floatLiteral (strokeType.stroke.getStrokeThickness(), 3) << ");\n\n"; | |||||
| paintMethodCode += s; | |||||
| s << " "; | |||||
| strokeType.fill.fillInGeneratedCode ("stroke", position, code, s); | |||||
| s << " g.drawRoundedRectangle (x, y, width, height, " << CodeHelpers::floatLiteral (cornerSize, 3) | |||||
| << ", " << CodeHelpers::floatLiteral (strokeType.stroke.getStrokeThickness(), 3) << ");\n"; | |||||
| } | |||||
| s << "}\n\n"; | |||||
| paintMethodCode += s; | |||||
| } | |||||
| void applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| customPaintCode.clear(); | |||||
| if (! snippets.isEmpty() && (! fillType.isInvisible() || (isStrokePresent && ! strokeType.isInvisible()))) | |||||
| { | |||||
| customPaintCode = snippets[0]; | |||||
| snippets.remove (0); | |||||
| } | } | ||||
| } | } | ||||
| @@ -191,6 +208,7 @@ public: | |||||
| private: | private: | ||||
| double cornerSize; | double cornerSize; | ||||
| String customPaintCode; | |||||
| //============================================================================== | //============================================================================== | ||||
| class CornerSizeProperty : public SliderPropertyComponent, | class CornerSizeProperty : public SliderPropertyComponent, | ||||
| @@ -83,27 +83,37 @@ public: | |||||
| { | { | ||||
| if (! fillType.isInvisible()) | if (! fillType.isInvisible()) | ||||
| { | { | ||||
| String r; | |||||
| fillType.fillInGeneratedCode (code, paintMethodCode); | |||||
| String x, y, w, h; | |||||
| String x, y, w, h, r; | |||||
| positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | positionToCode (position, code.document->getComponentLayout(), x, y, w, h); | ||||
| r << "g.setFont (" | |||||
| << FontPropertyComponent::getCompleteFontCode (font, typefaceName) | |||||
| << ");\ng.drawText (" | |||||
| << quotedString (text, code.shouldUseTransMacro()) | |||||
| << ",\n " | |||||
| << x << ", " << y << ", " << w << ", " << h | |||||
| << ",\n " | |||||
| << CodeHelpers::justificationToCode (justification) | |||||
| << ", true);\n\n"; | |||||
| r << "{\n" | |||||
| << " int x = " << x << ", y = " << y << ", width = " << w << ", height = " << h << ";\n" | |||||
| << " String text (" << quotedString (text, code.shouldUseTransMacro()) << ");\n" | |||||
| << " " << fillType.generateVariablesCode ("fill") | |||||
| << " //[UserPaintCustomArguments] Customize the painting arguments here..\n" | |||||
| << customPaintCode | |||||
| << " //[/UserPaintCustomArguments]\n" | |||||
| << " "; | |||||
| fillType.fillInGeneratedCode ("fill", position, code, r); | |||||
| r << " g.setFont (" << FontPropertyComponent::getCompleteFontCode (font, typefaceName) << ");\n" | |||||
| << " g.drawText (text, x, y, width, height,\n" | |||||
| << " " << CodeHelpers::justificationToCode (justification) << ", true);\n" | |||||
| << "}\n\n"; | |||||
| paintMethodCode += r; | paintMethodCode += r; | ||||
| } | } | ||||
| } | } | ||||
| void applyCustomPaintSnippets (StringArray& snippets) | |||||
| { | |||||
| customPaintCode.clear(); | |||||
| if (! snippets.isEmpty() && ! fillType.isInvisible()) | |||||
| { | |||||
| customPaintCode = snippets[0]; | |||||
| snippets.remove (0); | |||||
| } | |||||
| } | |||||
| static const char* getTagName() noexcept { return "TEXT"; } | static const char* getTagName() noexcept { return "TEXT"; } | ||||
| XmlElement* createXml() const | XmlElement* createXml() const | ||||
| @@ -376,7 +386,8 @@ private: | |||||
| Font font; | Font font; | ||||
| String typefaceName; | String typefaceName; | ||||
| Justification justification; | Justification justification; | ||||
| String customPaintCode; | |||||
| Array <Justification> justificationTypes; | Array <Justification> justificationTypes; | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -318,11 +318,7 @@ static SourceCodeEditor* createCodeEditor (const File& file, SourceCodeDocument& | |||||
| //============================================================================== | //============================================================================== | ||||
| JucerDocumentEditor::JucerDocumentEditor (JucerDocument* const doc) | JucerDocumentEditor::JucerDocumentEditor (JucerDocument* const doc) | ||||
| : document (doc), | : document (doc), | ||||
| tabbedComponent (TabbedButtonBar::TabsAtTop), | |||||
| compLayoutPanel (0), | |||||
| lastViewportX (0), | |||||
| lastViewportY (0), | |||||
| currentZoomLevel (1.0) | |||||
| tabbedComponent (doc) | |||||
| { | { | ||||
| setOpaque (true); | setOpaque (true); | ||||
| @@ -1028,9 +1024,7 @@ bool JucerDocumentEditor::perform (const InvocationInfo& info) | |||||
| case StandardApplicationCommandIDs::paste: | case StandardApplicationCommandIDs::paste: | ||||
| { | { | ||||
| ScopedPointer<XmlElement> doc (XmlDocument::parse (SystemClipboard::getTextFromClipboard())); | |||||
| if (doc != nullptr) | |||||
| if (ScopedPointer<XmlElement> doc = XmlDocument::parse (SystemClipboard::getTextFromClipboard())) | |||||
| { | { | ||||
| if (doc->hasTagName (ComponentLayout::clipboardXmlTag)) | if (doc->hasTagName (ComponentLayout::clipboardXmlTag)) | ||||
| { | { | ||||
| @@ -1095,9 +1089,8 @@ bool JucerDocumentEditor::keyPressed (const KeyPress& key) | |||||
| JucerDocumentEditor* JucerDocumentEditor::getActiveDocumentHolder() | JucerDocumentEditor* JucerDocumentEditor::getActiveDocumentHolder() | ||||
| { | { | ||||
| ApplicationCommandInfo info (0); | ApplicationCommandInfo info (0); | ||||
| ApplicationCommandTarget* target = ProjucerApplication::getCommandManager().getTargetForCommand (JucerCommandIDs::editCompLayout, info); | |||||
| return dynamic_cast<JucerDocumentEditor*> (target); | |||||
| return dynamic_cast<JucerDocumentEditor*> (ProjucerApplication::getCommandManager() | |||||
| .getTargetForCommand (JucerCommandIDs::editCompLayout, info)); | |||||
| } | } | ||||
| Image JucerDocumentEditor::createComponentLayerSnapshot() const | Image JucerDocumentEditor::createComponentLayerSnapshot() const | ||||
| @@ -1105,7 +1098,7 @@ Image JucerDocumentEditor::createComponentLayerSnapshot() const | |||||
| if (compLayoutPanel != nullptr) | if (compLayoutPanel != nullptr) | ||||
| return compLayoutPanel->createComponentSnapshot(); | return compLayoutPanel->createComponentSnapshot(); | ||||
| return Image(); | |||||
| return {}; | |||||
| } | } | ||||
| const int gridSnapMenuItemBase = 0x8723620; | const int gridSnapMenuItemBase = 0x8723620; | ||||
| @@ -1187,13 +1180,13 @@ void createGUIEditorMenu (PopupMenu& menu) | |||||
| void handleGUIEditorMenuCommand (int menuItemID) | void handleGUIEditorMenuCommand (int menuItemID) | ||||
| { | { | ||||
| if (JucerDocumentEditor* ed = JucerDocumentEditor::getActiveDocumentHolder()) | |||||
| if (auto* ed = JucerDocumentEditor::getActiveDocumentHolder()) | |||||
| { | { | ||||
| int gridIndex = menuItemID - gridSnapMenuItemBase; | int gridIndex = menuItemID - gridSnapMenuItemBase; | ||||
| if (isPositiveAndBelow (gridIndex, numElementsInArray (snapSizes))) | if (isPositiveAndBelow (gridIndex, numElementsInArray (snapSizes))) | ||||
| { | { | ||||
| JucerDocument& doc = *ed->getDocument(); | |||||
| auto& doc = *ed->getDocument(); | |||||
| doc.setSnappingGrid (snapSizes [gridIndex], | doc.setSnappingGrid (snapSizes [gridIndex], | ||||
| doc.isSnapActive (false), | doc.isSnapActive (false), | ||||
| @@ -58,28 +58,37 @@ public: | |||||
| Image createComponentLayerSnapshot() const; | Image createComponentLayerSnapshot() const; | ||||
| //============================================================================== | //============================================================================== | ||||
| void paint (Graphics& g); | |||||
| void resized(); | |||||
| void changeListenerCallback (ChangeBroadcaster*); | |||||
| bool keyPressed (const KeyPress&); | |||||
| void paint (Graphics& g) override; | |||||
| void resized() override; | |||||
| void changeListenerCallback (ChangeBroadcaster*) override; | |||||
| bool keyPressed (const KeyPress&) override; | |||||
| //============================================================================== | //============================================================================== | ||||
| ApplicationCommandTarget* getNextCommandTarget(); | |||||
| void getAllCommands (Array <CommandID>&); | |||||
| void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result); | |||||
| bool perform (const InvocationInfo&); | |||||
| ApplicationCommandTarget* getNextCommandTarget() override; | |||||
| void getAllCommands (Array<CommandID>&) override; | |||||
| void getCommandInfo (CommandID, ApplicationCommandInfo&) override; | |||||
| bool perform (const InvocationInfo&) override; | |||||
| static JucerDocumentEditor* getActiveDocumentHolder(); | static JucerDocumentEditor* getActiveDocumentHolder(); | ||||
| private: | private: | ||||
| ScopedPointer<JucerDocument> document; | ScopedPointer<JucerDocument> document; | ||||
| TabbedComponent tabbedComponent; | |||||
| ComponentLayoutPanel* compLayoutPanel; | |||||
| ComponentLayoutPanel* compLayoutPanel = nullptr; | |||||
| bool isSomethingSelected() const; | |||||
| int lastViewportX, lastViewportY; | |||||
| struct JucerDocumentTabs : public TabbedComponent | |||||
| { | |||||
| JucerDocumentTabs (JucerDocument* d) : TabbedComponent (TabbedButtonBar::TabsAtTop), document (d) {} | |||||
| void currentTabChanged (int, const String&) override { document->refreshCustomCodeFromDocument(); } | |||||
| JucerDocument* document; | |||||
| }; | |||||
| JucerDocumentTabs tabbedComponent; | |||||
| double currentZoomLevel; | |||||
| int lastViewportX = 0, lastViewportY = 0; | |||||
| double currentZoomLevel = 1.0; | |||||
| bool isSomethingSelected() const; | |||||
| // only non-zero if a layout tab is selected | // only non-zero if a layout tab is selected | ||||
| ComponentLayout* getCurrentLayout() const; | ComponentLayout* getCurrentLayout() const; | ||||
| @@ -89,6 +98,6 @@ private: | |||||
| void setZoom (double scale); | void setZoom (double scale); | ||||
| double getZoom() const; | double getZoom() const; | ||||
| void addElement (const int index); | |||||
| void addComponent (const int index); | |||||
| void addElement (int index); | |||||
| void addComponent (int index); | |||||
| }; | }; | ||||
| @@ -36,23 +36,39 @@ namespace CodeHelpers | |||||
| if (numSpaces == 0) | if (numSpaces == 0) | ||||
| return code; | return code; | ||||
| const String space (String::repeatedString (" ", numSpaces)); | |||||
| auto space = String::repeatedString (" ", numSpaces); | |||||
| auto lines = StringArray::fromLines (code); | |||||
| StringArray lines; | |||||
| lines.addLines (code); | |||||
| for (int i = (indentFirstLine ? 0 : 1); i < lines.size(); ++i) | |||||
| for (auto& line : lines) | |||||
| { | { | ||||
| String s (lines[i].trimEnd()); | |||||
| if (s.isNotEmpty()) | |||||
| s = space + s; | |||||
| if (! indentFirstLine) | |||||
| { | |||||
| indentFirstLine = true; | |||||
| continue; | |||||
| } | |||||
| lines.set (i, s); | |||||
| if (line.trimEnd().isNotEmpty()) | |||||
| line = space + line; | |||||
| } | } | ||||
| return lines.joinIntoString (newLine); | return lines.joinIntoString (newLine); | ||||
| } | } | ||||
| String unindent (const String& code, const int numSpaces) | |||||
| { | |||||
| if (numSpaces == 0) | |||||
| return code; | |||||
| auto space = String::repeatedString (" ", numSpaces); | |||||
| auto lines = StringArray::fromLines (code); | |||||
| for (auto& line : lines) | |||||
| if (line.startsWith (space)) | |||||
| line = line.substring (numSpaces); | |||||
| return lines.joinIntoString (newLine); | |||||
| } | |||||
| String makeValidIdentifier (String s, bool capitalise, bool removeColons, bool allowTemplates) | String makeValidIdentifier (String s, bool capitalise, bool removeColons, bool allowTemplates) | ||||
| { | { | ||||
| if (s.isEmpty()) | if (s.isEmpty()) | ||||
| @@ -30,7 +30,8 @@ | |||||
| //============================================================================== | //============================================================================== | ||||
| namespace CodeHelpers | namespace CodeHelpers | ||||
| { | { | ||||
| String indent (const String& code, const int numSpaces, bool indentFirstLine); | |||||
| String indent (const String& code, int numSpaces, bool indentFirstLine); | |||||
| String unindent (const String& code, int numSpaces); | |||||
| String makeValidIdentifier (String s, bool capitalise, bool removeColons, bool allowTemplates); | String makeValidIdentifier (String s, bool capitalise, bool removeColons, bool allowTemplates); | ||||
| String createIncludeStatement (const File& includedFile, const File& targetFile); | String createIncludeStatement (const File& includedFile, const File& targetFile); | ||||
| String createIncludeStatement (const String& includePath); | String createIncludeStatement (const String& includePath); | ||||
| @@ -41,7 +42,7 @@ namespace CodeHelpers | |||||
| String floatLiteral (double value, int numDecPlaces); | String floatLiteral (double value, int numDecPlaces); | ||||
| String boolLiteral (bool value); | String boolLiteral (bool value); | ||||
| String colourToCode (Colour col); | |||||
| String colourToCode (Colour); | |||||
| String justificationToCode (Justification); | String justificationToCode (Justification); | ||||
| String alignFunctionCallParams (const String& call, const StringArray& parameters, int maxLineLength); | String alignFunctionCallParams (const String& call, const StringArray& parameters, int maxLineLength); | ||||
| @@ -50,7 +51,7 @@ namespace CodeHelpers | |||||
| bool breakAtNewLines, bool allowStringBreaks); | bool breakAtNewLines, bool allowStringBreaks); | ||||
| void createStringMatcher (OutputStream& out, const String& utf8PointerVariable, | void createStringMatcher (OutputStream& out, const String& utf8PointerVariable, | ||||
| const StringArray& strings, const StringArray& codeToExecute, const int indentLevel); | |||||
| const StringArray& strings, const StringArray& codeToExecute, int indentLevel); | |||||
| String getLeadingWhitespace (String line); | String getLeadingWhitespace (String line); | ||||
| int getBraceCount (String::CharPointerType line); | int getBraceCount (String::CharPointerType line); | ||||