|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 7 End-User License
- Agreement and JUCE Privacy Policy.
-
- End User License Agreement: www.juce.com/juce-7-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- struct PropertyPanel::SectionComponent final : public Component
- {
- SectionComponent (const String& sectionTitle,
- const Array<PropertyComponent*>& newProperties,
- bool sectionIsOpen,
- int extraPadding)
- : Component (sectionTitle),
- isOpen (sectionIsOpen),
- padding (extraPadding)
- {
- lookAndFeelChanged();
-
- propertyComps.addArray (newProperties);
-
- for (auto* propertyComponent : propertyComps)
- {
- addAndMakeVisible (propertyComponent);
- propertyComponent->refresh();
- }
- }
-
- ~SectionComponent() override
- {
- propertyComps.clear();
- }
-
- void paint (Graphics& g) override
- {
- if (titleHeight > 0)
- getLookAndFeel().drawPropertyPanelSectionHeader (g, getName(), isOpen, getWidth(), titleHeight);
- }
-
- void resized() override
- {
- auto y = titleHeight;
-
- for (auto* propertyComponent : propertyComps)
- {
- propertyComponent->setBounds (1, y, getWidth() - 2, propertyComponent->getPreferredHeight());
- y = propertyComponent->getBottom() + padding;
- }
- }
-
- void lookAndFeelChanged() override
- {
- titleHeight = getLookAndFeel().getPropertyPanelSectionHeaderHeight (getName());
- resized();
- repaint();
- }
-
- int getPreferredHeight() const
- {
- auto y = titleHeight;
-
- auto numComponents = propertyComps.size();
-
- if (numComponents > 0 && isOpen)
- {
- for (auto* propertyComponent : propertyComps)
- y += propertyComponent->getPreferredHeight();
-
- y += (numComponents - 1) * padding;
- }
-
- return y;
- }
-
- void setOpen (bool open)
- {
- if (isOpen != open)
- {
- isOpen = open;
-
- for (auto* propertyComponent : propertyComps)
- propertyComponent->setVisible (open);
-
- if (auto* propertyPanel = findParentComponentOfClass<PropertyPanel>())
- propertyPanel->resized();
- }
- }
-
- void refreshAll() const
- {
- for (auto* propertyComponent : propertyComps)
- propertyComponent->refresh();
- }
-
- void mouseUp (const MouseEvent& e) override
- {
- if (e.getMouseDownX() < titleHeight
- && e.x < titleHeight
- && e.getNumberOfClicks() != 2)
- mouseDoubleClick (e);
- }
-
- void mouseDoubleClick (const MouseEvent& e) override
- {
- if (e.y < titleHeight)
- setOpen (! isOpen);
- }
-
- OwnedArray<PropertyComponent> propertyComps;
- int titleHeight;
- bool isOpen;
- int padding;
-
- JUCE_DECLARE_NON_COPYABLE (SectionComponent)
- };
-
- //==============================================================================
- struct PropertyPanel::PropertyHolderComponent final : public Component
- {
- PropertyHolderComponent() {}
-
- void paint (Graphics&) override {}
-
- void updateLayout (int width)
- {
- auto y = 0;
-
- for (auto* section : sections)
- {
- section->setBounds (0, y, width, section->getPreferredHeight());
- y = section->getBottom();
- }
-
- setSize (width, y);
- repaint();
- }
-
- void refreshAll() const
- {
- for (auto* section : sections)
- section->refreshAll();
- }
-
- void insertSection (int indexToInsertAt, SectionComponent* newSection)
- {
- sections.insert (indexToInsertAt, newSection);
- addAndMakeVisible (newSection, 0);
- }
-
- SectionComponent* getSectionWithNonEmptyName (int targetIndex) const noexcept
- {
- auto index = 0;
- for (auto* section : sections)
- {
- if (section->getName().isNotEmpty())
- if (index++ == targetIndex)
- return section;
- }
-
- return nullptr;
- }
-
- OwnedArray<SectionComponent> sections;
-
- JUCE_DECLARE_NON_COPYABLE (PropertyHolderComponent)
- };
-
-
- //==============================================================================
- PropertyPanel::PropertyPanel()
- {
- init();
- }
-
- PropertyPanel::PropertyPanel (const String& name) : Component (name)
- {
- init();
- }
-
- void PropertyPanel::init()
- {
- messageWhenEmpty = TRANS ("(nothing selected)");
-
- addAndMakeVisible (viewport);
- viewport.setViewedComponent (propertyHolderComponent = new PropertyHolderComponent());
- viewport.setFocusContainerType (FocusContainerType::keyboardFocusContainer);
- }
-
- PropertyPanel::~PropertyPanel()
- {
- clear();
- }
-
- //==============================================================================
- void PropertyPanel::paint (Graphics& g)
- {
- if (isEmpty())
- {
- g.setColour (Colours::black.withAlpha (0.5f));
- g.setFont (14.0f);
- g.drawText (messageWhenEmpty, getLocalBounds().withHeight (30),
- Justification::centred, true);
- }
- }
-
- void PropertyPanel::resized()
- {
- viewport.setBounds (getLocalBounds());
- updatePropHolderLayout();
- }
-
- //==============================================================================
- void PropertyPanel::clear()
- {
- if (! isEmpty())
- {
- propertyHolderComponent->sections.clear();
- updatePropHolderLayout();
- }
- }
-
- bool PropertyPanel::isEmpty() const
- {
- return propertyHolderComponent->sections.size() == 0;
- }
-
- int PropertyPanel::getTotalContentHeight() const
- {
- return propertyHolderComponent->getHeight();
- }
-
- void PropertyPanel::addProperties (const Array<PropertyComponent*>& newProperties,
- int extraPaddingBetweenComponents)
- {
- if (isEmpty())
- repaint();
-
- propertyHolderComponent->insertSection (-1, new SectionComponent ({}, newProperties, true, extraPaddingBetweenComponents));
- updatePropHolderLayout();
- }
-
- void PropertyPanel::addSection (const String& sectionTitle,
- const Array<PropertyComponent*>& newProperties,
- bool shouldBeOpen,
- int indexToInsertAt,
- int extraPaddingBetweenComponents)
- {
- jassert (sectionTitle.isNotEmpty());
-
- if (isEmpty())
- repaint();
-
- propertyHolderComponent->insertSection (indexToInsertAt, new SectionComponent (sectionTitle,
- newProperties,
- shouldBeOpen,
- extraPaddingBetweenComponents));
-
- updatePropHolderLayout();
- }
-
- void PropertyPanel::updatePropHolderLayout() const
- {
- auto maxWidth = viewport.getMaximumVisibleWidth();
- propertyHolderComponent->updateLayout (maxWidth);
-
- auto newMaxWidth = viewport.getMaximumVisibleWidth();
- if (maxWidth != newMaxWidth)
- {
- // need to do this twice because of scrollbars changing the size, etc.
- propertyHolderComponent->updateLayout (newMaxWidth);
- }
- }
-
- void PropertyPanel::refreshAll() const
- {
- propertyHolderComponent->refreshAll();
- }
-
- //==============================================================================
- StringArray PropertyPanel::getSectionNames() const
- {
- StringArray s;
-
- for (auto* section : propertyHolderComponent->sections)
- {
- if (section->getName().isNotEmpty())
- s.add (section->getName());
- }
-
- return s;
- }
-
- bool PropertyPanel::isSectionOpen (int sectionIndex) const
- {
- if (auto* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex))
- return s->isOpen;
-
- return false;
- }
-
- void PropertyPanel::setSectionOpen (int sectionIndex, bool shouldBeOpen)
- {
- if (auto* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex))
- s->setOpen (shouldBeOpen);
- }
-
- void PropertyPanel::setSectionEnabled (int sectionIndex, bool shouldBeEnabled)
- {
- if (auto* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex))
- s->setEnabled (shouldBeEnabled);
- }
-
- void PropertyPanel::removeSection (int sectionIndex)
- {
- if (auto* s = propertyHolderComponent->getSectionWithNonEmptyName (sectionIndex))
- {
- propertyHolderComponent->sections.removeObject (s);
- updatePropHolderLayout();
- }
- }
-
- //==============================================================================
- std::unique_ptr<XmlElement> PropertyPanel::getOpennessState() const
- {
- auto xml = std::make_unique<XmlElement> ("PROPERTYPANELSTATE");
-
- xml->setAttribute ("scrollPos", viewport.getViewPositionY());
-
- auto sections = getSectionNames();
- for (auto s : sections)
- {
- if (s.isNotEmpty())
- {
- auto* e = xml->createNewChildElement ("SECTION");
- e->setAttribute ("name", s);
- e->setAttribute ("open", isSectionOpen (sections.indexOf (s)) ? 1 : 0);
- }
- }
-
- return xml;
- }
-
- void PropertyPanel::restoreOpennessState (const XmlElement& xml)
- {
- if (xml.hasTagName ("PROPERTYPANELSTATE"))
- {
- auto sections = getSectionNames();
-
- for (auto* e : xml.getChildWithTagNameIterator ("SECTION"))
- {
- setSectionOpen (sections.indexOf (e->getStringAttribute ("name")),
- e->getBoolAttribute ("open"));
- }
-
- viewport.setViewPosition (viewport.getViewPositionX(),
- xml.getIntAttribute ("scrollPos", viewport.getViewPositionY()));
- }
- }
-
- //==============================================================================
- void PropertyPanel::setMessageWhenEmpty (const String& newMessage)
- {
- if (messageWhenEmpty != newMessage)
- {
- messageWhenEmpty = newMessage;
- repaint();
- }
- }
-
- const String& PropertyPanel::getMessageWhenEmpty() const noexcept
- {
- return messageWhenEmpty;
- }
-
- } // namespace juce
|