diff --git a/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.cpp b/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.cpp index 70f0c8038b..0d7cacbecc 100644 --- a/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.cpp +++ b/extras/Introjucer/Source/Project/jucer_ProjectTreeViewBase.cpp @@ -431,7 +431,7 @@ void ProjectTreeViewBase::addSubItems() void ProjectTreeViewBase::refreshSubItems() { - OpennessRestorer openness (*this); + WholeTreeOpennessRestorer openness (*this); clearSubItems(); addSubItems(); } diff --git a/extras/Introjucer/Source/Project/jucer_TreeViewTypes.h b/extras/Introjucer/Source/Project/jucer_TreeViewTypes.h index 79057750c5..183cae4a77 100644 --- a/extras/Introjucer/Source/Project/jucer_TreeViewTypes.h +++ b/extras/Introjucer/Source/Project/jucer_TreeViewTypes.h @@ -39,7 +39,7 @@ public: bool acceptsFileDrop (const StringArray& files) const { return false; } bool acceptsDragItems (const OwnedArray & selectedNodes) { return false; } ProjectTreeViewBase* createSubItem (const Project::Item& child); - void createLeftEdgeComponents (Array& components) {} + void createLeftEdgeComponents (OwnedArray& components) {} void showDocument(); void showPopupMenu(); String getDisplayName() const; @@ -59,7 +59,7 @@ public: void checkFileStatus(); void moveSelectedItemsTo (OwnedArray & selectedNodes, int insertIndex); ProjectTreeViewBase* createSubItem (const Project::Item& child); - void createLeftEdgeComponents (Array& components) {} + void createLeftEdgeComponents (OwnedArray& components) {} void showDocument(); void showPopupMenu(); diff --git a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.cpp b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.cpp index b9ad81932f..c5f0841868 100644 --- a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.cpp +++ b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.cpp @@ -69,9 +69,9 @@ void JucerTreeViewBase::paintOpenCloseButton (Graphics& g, int width, int height Path p; if (isOpen()) - p.addTriangle (width * 0.2f, height * 0.25f, width * 0.8f, height * 0.25f, width * 0.5f, height * 0.75f); + p.addTriangle (width * 0.2f, height * 0.25f, width * 0.8f, height * 0.25f, width * 0.5f, height * 0.75f); else - p.addTriangle (width * 0.25f, height * 0.25f, width * 0.8f, height * 0.5f, width * 0.25f, height * 0.75f); + p.addTriangle (width * 0.25f, height * 0.25f, width * 0.8f, height * 0.5f, width * 0.25f, height * 0.75f); g.setColour (Colours::lightgrey); g.fillPath (p); @@ -81,13 +81,13 @@ void JucerTreeViewBase::paintOpenCloseButton (Graphics& g, int width, int height class TreeLeftHandButtonHolderComponent : public Component { public: - TreeLeftHandButtonHolderComponent (const Array& comps) + TreeLeftHandButtonHolderComponent (OwnedArray& comps) { - components.addArray (comps); + components.swapWithArray (comps); setInterceptsMouseClicks (false, true); - for (int i = 0; i < comps.size(); ++i) - addAndMakeVisible (comps.getUnchecked(i)); + for (int i = 0; i < components.size(); ++i) + addAndMakeVisible (components.getUnchecked(i)); } void resized() @@ -105,7 +105,7 @@ private: Component* JucerTreeViewBase::createItemComponent() { - Array components; + OwnedArray components; createLeftEdgeComponents (components); numLeftHandComps = components.size(); @@ -113,24 +113,44 @@ Component* JucerTreeViewBase::createItemComponent() } //============================================================================== +class RenameTreeItemCallback : public ModalComponentManager::Callback +{ +public: + RenameTreeItemCallback (JucerTreeViewBase& item_, Component& parent, const Rectangle& bounds) + : item (item_) + { + ed.setMultiLine (false, false); + ed.setPopupMenuEnabled (false); + ed.setSelectAllWhenFocused (true); + ed.setFont (item.getFont()); + ed.addListener (&item); + ed.setText (item.getRenamingName()); + ed.setBounds (bounds); + + parent.addAndMakeVisible (&ed); + ed.enterModalState (true, this); + } + + void modalStateFinished (int resultCode) + { + if (resultCode != 0) + item.setName (ed.getText()); + } + +private: + TextEditor ed; + JucerTreeViewBase& item; + + JUCE_DECLARE_NON_COPYABLE (RenameTreeItemCallback); +}; + void JucerTreeViewBase::showRenameBox() { - TextEditor ed (String::empty); - ed.setMultiLine (false, false); - ed.setPopupMenuEnabled (false); - ed.setSelectAllWhenFocused (true); - ed.setFont (getFont()); - ed.addListener (this); - ed.setText (getRenamingName()); - Rectangle r (getItemPosition (true)); r.setLeft (r.getX() + getTextX()); r.setHeight (getItemHeight()); - ed.setBounds (r); - getOwnerView()->addAndMakeVisible (&ed); - if (ed.runModalLoop() != 0) - setName (ed.getText()); + new RenameTreeItemCallback (*this, *getOwnerView(), r); } void JucerTreeViewBase::itemClicked (const MouseEvent& e) diff --git a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h index 80228acb27..dc045314da 100644 --- a/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h +++ b/extras/Introjucer/Source/Utility/jucer_JucerTreeViewBase.h @@ -42,6 +42,7 @@ public: //============================================================================== int getItemWidth() const { return -1; } int getItemHeight() const { return 20; } + Font getFont() const; void paintItem (Graphics& g, int width, int height); void paintOpenCloseButton (Graphics& g, int width, int height, bool isMouseOver); @@ -54,7 +55,7 @@ public: virtual void setName (const String& newName) = 0; virtual bool isMissing() = 0; virtual const Drawable* getIcon() const = 0; - virtual void createLeftEdgeComponents (Array& components) = 0; + virtual void createLeftEdgeComponents (OwnedArray& components) = 0; virtual void showPopupMenu(); virtual void showMultiSelectionPopupMenu(); @@ -67,10 +68,26 @@ public: void textEditorEscapeKeyPressed (TextEditor& editor) { editor.exitModalState (0); } void textEditorFocusLost (TextEditor& editor) { editor.exitModalState (0); } + //============================================================================== + // To handle situations where an item gets deleted before openness is + // restored for it, this OpennessRestorer keeps only a pointer to the + // topmost tree item. + struct WholeTreeOpennessRestorer : public OpennessRestorer + { + WholeTreeOpennessRestorer (TreeViewItem& item) : OpennessRestorer (getTopLevelItem (item)) + {} + + private: + static TreeViewItem& getTopLevelItem (TreeViewItem& item) + { + TreeViewItem* const p = item.getParentItem(); + return p != nullptr ? getTopLevelItem (*p) : item; + } + }; + //============================================================================== private: int numLeftHandComps; - Font getFont() const; int getTextX() const; }; diff --git a/extras/Introjucer/Source/Utility/jucer_MiscUtilities.cpp b/extras/Introjucer/Source/Utility/jucer_MiscUtilities.cpp index 593082839c..4be2d70988 100644 --- a/extras/Introjucer/Source/Utility/jucer_MiscUtilities.cpp +++ b/extras/Introjucer/Source/Utility/jucer_MiscUtilities.cpp @@ -405,3 +405,29 @@ void showUTF8ToolWindow() DialogWindow::showModalDialog ("UTF-8 String Literal Converter", &comp, nullptr, Colours::white, true, true); } + +//============================================================================== +class CallOutBoxCallback : public ModalComponentManager::Callback +{ +public: + CallOutBoxCallback (Component& attachTo, Component* content_) + : content (content_), + callout (*content_, attachTo, attachTo.getTopLevelComponent()) + { + callout.setVisible (true); + callout.enterModalState (true, this); + } + + void modalStateFinished (int) {} + +private: + ScopedPointer content; + CallOutBox callout; + + JUCE_DECLARE_NON_COPYABLE (CallOutBoxCallback); +}; + +void launchAsyncCallOutBox (Component& attachTo, Component* content) +{ + new CallOutBoxCallback (attachTo, content); +} diff --git a/extras/Introjucer/Source/Utility/jucer_MiscUtilities.h b/extras/Introjucer/Source/Utility/jucer_MiscUtilities.h index 9e931a6156..60c5e3f6e8 100644 --- a/extras/Introjucer/Source/Utility/jucer_MiscUtilities.h +++ b/extras/Introjucer/Source/Utility/jucer_MiscUtilities.h @@ -46,6 +46,9 @@ void drawRecessedShadows (Graphics& g, int w, int h, int shadowSize); void showUTF8ToolWindow(); +// Start a callout modally, which will delete the content comp when it's dismissed. +void launchAsyncCallOutBox (Component& attachTo, Component* content); + //============================================================================== class PropertyPanelWithTooltips : public Component, public Timer diff --git a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index 6c26edf236..8e2acd1e32 100644 --- a/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -434,7 +434,7 @@ void Graphics::drawBevel (const int x, const int y, const int width, const int h for (int i = bevelThickness; --i >= 0;) { - const float op = useGradient ? (sharpEdgeOnOutside ? bevelThickness - i : i) / bevelThickness + const float op = useGradient ? (sharpEdgeOnOutside ? bevelThickness - i : i) / (float) bevelThickness : 1.0f; context->setFill (topLeftColour.withMultipliedAlpha (op)); @@ -607,13 +607,13 @@ void Graphics::drawDashedLine (const Line& line, const float* const dashL jassert (dashLengths[n] > 0); // can't have zero-length dashes! const double lastAlpha = alpha; - alpha = jmin (1.0, alpha + dashLengths [n] * onePixAlpha); + alpha += dashLengths [n] * onePixAlpha; n = (n + 1) % numDashLengths; if ((n & 1) != 0) { const Line segment (line.getStart() + (delta * lastAlpha).toFloat(), - line.getStart() + (delta * alpha).toFloat()); + line.getStart() + (delta * jmin (1.0, alpha)).toFloat()); if (lineThickness != 1.0f) drawLine (segment, lineThickness);