/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2015 - ROLI Ltd. Permission is granted to use this software under the terms of either: a) the GPL v2 (or any later version) b) the Affero GPL v3 Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are available: visit www.juce.com for more information. ============================================================================== */ //============================================================================== class InfoButton : public Button { public: InfoButton (PropertyComponent& comp) : Button (String()), associatedComponent (comp) { tooltip = associatedComponent.getTooltip(); auto stringWidth = Font (14.0f).getStringWidthFloat (tooltip); int maxWidth = 300; if (stringWidth > maxWidth) { width = maxWidth; numLines += static_cast (stringWidth / width); } else { width = roundToInt (stringWidth); } } void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override { auto bounds = getLocalBounds().toFloat().reduced (2); const auto& icon = getIcons().info; g.setColour (findColour (treeIconColourId).withMultipliedAlpha (isMouseOverButton || isButtonDown ? 1.0f : 0.5f)); if (isButtonDown) g.fillEllipse (bounds); else g.fillPath (icon, RectanglePlacement (RectanglePlacement::centred) .getTransformToFit (icon.getBounds(), bounds)); } void clicked() override { auto* w = new InfoWindow (tooltip); w->setSize (width, w->getHeight() * numLines + 10); CallOutBox::launchAsynchronously (w, getScreenBounds(), nullptr); } PropertyComponent& associatedComponent; private: String tooltip; int width; int numLines = 1; //============================================================================== struct InfoWindow : public Component { InfoWindow (String s) : stringToDisplay (s) { setSize (150, 14); } void paint (Graphics& g) override { g.fillAll (findColour (secondaryBackgroundColourId)); g.setColour (findColour (defaultTextColourId)); g.setFont (Font (14.0f)); g.drawFittedText (stringToDisplay, getLocalBounds(), Justification::centred, 10, 1.0f); } String stringToDisplay; }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InfoButton) }; //============================================================================== class PropertyGroupComponent : public Component { public: PropertyGroupComponent (String name, Icon icon) : header (name, icon) { addAndMakeVisible (header); } void setProperties (const PropertyListBuilder& newProps) { infoButtons.clear(); properties.clear(); properties.addArray (newProps.components); for (auto i = properties.size(); --i >= 0;) { auto* prop = properties.getUnchecked (i); addAndMakeVisible (prop); if (! prop->getTooltip().isEmpty()) { addAndMakeVisible (infoButtons.add (new InfoButton (*prop))); prop->setTooltip (String()); // set the tooltip to empty so it only displays when its button is clicked } } } int updateSize (int x, int y, int width) { header.setBounds (0, 0, width, 40); auto height = header.getHeight() + 5; for (auto i = 0; i < properties.size(); ++i) { auto* pp = properties.getUnchecked (i); auto propertyHeight = pp->getPreferredHeight() + (getHeightMultiplier (pp) * pp->getPreferredHeight()); InfoButton* buttonToUse = nullptr; for (auto* b : infoButtons) if (&b->associatedComponent == pp) buttonToUse = b; if (buttonToUse != nullptr) { buttonToUse->setSize (20, 20); buttonToUse->setCentrePosition (20, height + (propertyHeight / 2)); } pp->setBounds (40, height, width - 50, propertyHeight); resizeContentIfChoicePropertyComponent (pp); height += pp->getHeight() + 10; } height += 16; setBounds (x, y, width, jmax (height, getParentHeight())); return height; } void paint (Graphics& g) override { g.setColour (findColour (secondaryBackgroundColourId)); g.fillRect (getLocalBounds()); } int getHeightMultiplier (PropertyComponent* pp) { auto availableTextWidth = ProjucerLookAndFeel::getTextWidthForPropertyComponent (pp); auto font = ProjucerLookAndFeel::getPropertyComponentFont(); auto nameWidth = font.getStringWidthFloat (pp->getName()); return static_cast (nameWidth / availableTextWidth); } void resizeContentIfChoicePropertyComponent (PropertyComponent* pp) { if (auto* choiceComp = dynamic_cast (pp)) { auto* box = choiceComp->getChildComponent (0); auto bounds = box->getBounds(); box->setBounds (bounds.withSizeKeepingCentre (box->getWidth(), pp->getPreferredHeight())); } } OwnedArray properties; OwnedArray infoButtons; ContentViewHeader header; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertyGroupComponent) }; //============================================================================== class ConfigTreeItemBase : public JucerTreeViewBase, public ValueTree::Listener { public: ConfigTreeItemBase() {} void showSettingsPage (Component* content) { content->setComponentID (getUniqueName()); ScopedPointer comp (content); if (ProjectContentComponent* pcc = getProjectContentComponent()) pcc->setEditorComponent (comp.release(), nullptr); } void closeSettingsPage() { if (ProjectContentComponent* pcc = getProjectContentComponent()) { if (auto* content = dynamic_cast (pcc->getEditorComponent())) if (content->getViewedComponent()->getComponentID() == getUniqueName()) pcc->hideEditor(); } } void deleteAllSelectedItems() override { TreeView* const tree = getOwnerView(); jassert (tree->getNumSelectedItems() <= 1); // multi-select should be disabled if (ConfigTreeItemBase* s = dynamic_cast (tree->getSelectedItem (0))) s->deleteItem(); } void itemOpennessChanged (bool isNowOpen) override { if (isNowOpen) refreshSubItems(); } void valueTreePropertyChanged (ValueTree&, const Identifier&) override {} void valueTreeChildAdded (ValueTree&, ValueTree&) override {} void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {} void valueTreeChildOrderChanged (ValueTree&, int, int) override {} void valueTreeParentChanged (ValueTree&) override {} virtual bool isProjectSettings() const { return false; } virtual bool isModulesList() const { return false; } static void updateSize (Component& comp, PropertyGroupComponent& group) { const auto width = jmax (550, comp.getParentWidth() - 12); auto y = 0; y += group.updateSize (12, y, width - 12); y = jmax (comp.getParentHeight(), y); comp.setSize (width, y); } private: };