|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2020 - 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 6 End-User License
- Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
-
- End User License Agreement: www.juce.com/juce-6-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.
-
- ==============================================================================
- */
-
- #pragma once
-
- //==============================================================================
- class ConcertinaHeader : public Component,
- public ChangeBroadcaster
- {
- public:
- ConcertinaHeader (String n, Path p)
- : Component (n), name (n), iconPath (p)
- {
- setTitle (getName());
-
- panelIcon = Icon (iconPath, Colours::white);
-
- nameLabel.setText (name, dontSendNotification);
- nameLabel.setJustificationType (Justification::centredLeft);
- nameLabel.setInterceptsMouseClicks (false, false);
- nameLabel.setAccessible (false);
- nameLabel.setColour (Label::textColourId, Colours::white);
-
- addAndMakeVisible (nameLabel);
- }
-
- void resized() override
- {
- auto b = getLocalBounds().toFloat();
-
- iconBounds = b.removeFromLeft (b.getHeight()).reduced (7, 7);
- arrowBounds = b.removeFromRight (b.getHeight());
- nameLabel.setBounds (b.toNearestInt());
- }
-
- void paint (Graphics& g) override
- {
- g.setColour (findColour (defaultButtonBackgroundColourId));
- g.fillRoundedRectangle (getLocalBounds().reduced (2, 3).toFloat(), 2.0f);
-
- g.setColour (Colours::white);
- g.fillPath (ProjucerLookAndFeel::getArrowPath (arrowBounds,
- getParentComponent()->getBoundsInParent().getY() == yPosition ? 2 : 0,
- true, Justification::centred));
-
- panelIcon.draw (g, iconBounds.toFloat(), false);
- }
-
- void mouseUp (const MouseEvent& e) override
- {
- if (! e.mouseWasDraggedSinceMouseDown())
- sendChangeMessage();
- }
-
- std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
- {
- return std::make_unique<AccessibilityHandler> (*this,
- AccessibilityRole::button,
- AccessibilityActions().addAction (AccessibilityActionType::press,
- [this] { sendChangeMessage(); }));
- }
-
- int direction = 0;
- int yPosition = 0;
-
- private:
- String name;
- Label nameLabel;
-
- Path iconPath;
- Icon panelIcon;
-
- Rectangle<float> arrowBounds, iconBounds;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaHeader)
- };
-
- //==============================================================================
- class FindPanel : public Component,
- private Timer,
- private FocusChangeListener
- {
- public:
- FindPanel (std::function<void (const String&)> cb)
- : callback (cb)
- {
- addAndMakeVisible (editor);
- editor.onTextChange = [this] { startTimer (250); };
- editor.onFocusLost = [this]
- {
- isFocused = false;
- repaint();
- };
-
- Desktop::getInstance().addFocusChangeListener (this);
-
- lookAndFeelChanged();
- }
-
- ~FindPanel() override
- {
- Desktop::getInstance().removeFocusChangeListener (this);
- }
-
- void paintOverChildren (Graphics& g) override
- {
- if (! isFocused)
- return;
-
- g.setColour (findColour (defaultHighlightColourId));
-
- Path p;
- p.addRoundedRectangle (getLocalBounds().reduced (2), 3.0f);
- g.strokePath (p, PathStrokeType (2.0f));
- }
-
-
- void resized() override
- {
- editor.setBounds (getLocalBounds().reduced (2));
- }
-
- private:
- TextEditor editor;
- bool isFocused = false;
- std::function<void (const String&)> callback;
-
- //==============================================================================
- void lookAndFeelChanged() override
- {
- editor.setTextToShowWhenEmpty ("Filter...", findColour (widgetTextColourId).withAlpha (0.3f));
- }
-
- void globalFocusChanged (Component* focusedComponent) override
- {
- if (focusedComponent == &editor)
- {
- isFocused = true;
- repaint();
- }
- }
-
- void timerCallback() override
- {
- stopTimer();
- callback (editor.getText());
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FindPanel)
- };
-
- //==============================================================================
- class ConcertinaTreeComponent : public Component
- {
- public:
- class AdditionalComponents
- {
- public:
- enum Type
- {
- addButton = (1 << 0),
- settingsButton = (1 << 1),
- findPanel = (1 << 2)
- };
-
- AdditionalComponents with (Type t)
- {
- auto copy = *this;
- copy.componentTypes |= t;
-
- return copy;
- }
-
- bool has (Type t) const noexcept
- {
- return (componentTypes & t) != 0;
- }
-
- private:
- int componentTypes = 0;
- };
-
- ConcertinaTreeComponent (const String& name,
- TreePanelBase* tree,
- AdditionalComponents additionalComponents)
- : Component (name),
- treeToDisplay (tree)
- {
- setTitle (getName());
- setFocusContainerType (FocusContainerType::focusContainer);
-
- if (additionalComponents.has (AdditionalComponents::addButton))
- {
- addButton = std::make_unique<IconButton> ("Add", getIcons().plus);
- addAndMakeVisible (addButton.get());
- addButton->onClick = [this] { showAddMenu(); };
- }
-
- if (additionalComponents.has (AdditionalComponents::settingsButton))
- {
- settingsButton = std::make_unique<IconButton> ("Settings", getIcons().settings);
- addAndMakeVisible (settingsButton.get());
- settingsButton->onClick = [this] { showSettings(); };
- }
-
- if (additionalComponents.has (AdditionalComponents::findPanel))
- {
- findPanel = std::make_unique<FindPanel> ([this] (const String& filter) { treeToDisplay->rootItem->setSearchFilter (filter); });
- addAndMakeVisible (findPanel.get());
- }
-
- addAndMakeVisible (treeToDisplay.get());
- }
-
- void resized() override
- {
- auto bounds = getLocalBounds();
-
- if (addButton != nullptr || settingsButton != nullptr || findPanel != nullptr)
- {
- auto bottomSlice = bounds.removeFromBottom (25);
- bottomSlice.removeFromRight (3);
-
- if (addButton != nullptr)
- addButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
-
- if (settingsButton != nullptr)
- settingsButton->setBounds (bottomSlice.removeFromRight (25).reduced (2));
-
- if (findPanel != nullptr)
- findPanel->setBounds (bottomSlice.reduced (2));
- }
-
- treeToDisplay->setBounds (bounds);
- }
-
- TreePanelBase* getTree() const noexcept { return treeToDisplay.get(); }
-
- private:
- std::unique_ptr<TreePanelBase> treeToDisplay;
- std::unique_ptr<IconButton> addButton, settingsButton;
- std::unique_ptr<FindPanel> findPanel;
-
- void showAddMenu()
- {
- auto numSelected = treeToDisplay->tree.getNumSelectedItems();
-
- if (numSelected > 1)
- return;
-
- if (numSelected == 0)
- {
- if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
- root->showPopupMenu (addButton->getScreenBounds().getCentre());
- }
- else
- {
- if (auto* item = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getSelectedItem (0)))
- item->showAddMenu (addButton->getScreenBounds().getCentre());
- }
- }
-
- void showSettings()
- {
- if (auto* root = dynamic_cast<JucerTreeViewBase*> (treeToDisplay->tree.getRootItem()))
- {
- treeToDisplay->tree.clearSelectedItems();
- root->showDocument();
- }
- }
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConcertinaTreeComponent)
- };
-
-
- //==============================================================================
- struct ProjectSettingsComponent : public Component,
- private ChangeListener
- {
- ProjectSettingsComponent (Project& p)
- : project (p),
- group (project.getProjectFilenameRootString(),
- Icon (getIcons().settings, Colours::transparentBlack))
- {
- setTitle ("Project Settings");
- setFocusContainerType (FocusContainerType::focusContainer);
-
- addAndMakeVisible (group);
-
- updatePropertyList();
- project.addChangeListener (this);
- }
-
- ~ProjectSettingsComponent() override
- {
- project.removeChangeListener (this);
- }
-
- void resized() override
- {
- group.updateSize (12, 0, getWidth() - 24);
- group.setBounds (getLocalBounds().reduced (12, 0));
- }
-
- void updatePropertyList()
- {
- PropertyListBuilder props;
- project.createPropertyEditors (props);
- group.setProperties (props);
- group.setName ("Project Settings");
-
- lastProjectType = project.getProjectTypeString();
- parentSizeChanged();
- }
-
- void changeListenerCallback (ChangeBroadcaster*) override
- {
- if (lastProjectType != project.getProjectTypeString())
- updatePropertyList();
- }
-
- void parentSizeChanged() override
- {
- auto width = jmax (550, getParentWidth());
- auto y = group.updateSize (12, 0, width - 12);
-
- y = jmax (getParentHeight(), y);
-
- setSize (width, y);
- }
-
- Project& project;
- var lastProjectType;
- PropertyGroupComponent group;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSettingsComponent)
- };
-
- //==============================================================================
- struct FileTreePanel : public TreePanelBase
- {
- FileTreePanel (Project& p)
- : TreePanelBase (&p, "fileTreeState")
- {
- tree.setMultiSelectEnabled (true);
- setRoot (std::make_unique<TreeItemTypes::GroupItem> (p.getMainGroup()));
- tree.setRootItemVisible (false);
- }
-
- void updateMissingFileStatuses()
- {
- if (auto* p = dynamic_cast<TreeItemTypes::FileTreeItemBase*> (rootItem.get()))
- p->checkFileStatus();
- }
- };
-
- struct ModuleTreePanel : public TreePanelBase
- {
- ModuleTreePanel (Project& p)
- : TreePanelBase (&p, "moduleTreeState")
- {
- tree.setMultiSelectEnabled (false);
- setRoot (std::make_unique<TreeItemTypes::EnabledModulesItem> (p));
- tree.setRootItemVisible (false);
- }
- };
-
- struct ExportersTreePanel : public TreePanelBase
- {
- ExportersTreePanel (Project& p)
- : TreePanelBase (&p, "exportersTreeState")
- {
- tree.setMultiSelectEnabled (false);
- setRoot (std::make_unique<TreeItemTypes::ExportersTreeRoot> (p));
- tree.setRootItemVisible (false);
- }
- };
-
- //==============================================================================
- class Sidebar : public Component,
- private ChangeListener
- {
- public:
- Sidebar (Project* p)
- : project (p)
- {
- setFocusContainerType (FocusContainerType::focusContainer);
-
- if (project != nullptr)
- buildConcertina();
- }
-
- ~Sidebar() override
- {
- TreePanelBase* panels[] = { getFileTreePanel(), getModuleTreePanel(), getExportersTreePanel() };
-
- for (auto* panel : panels)
- if (panel != nullptr)
- panel->saveOpenness();
- }
-
- void paint (Graphics& g) override
- {
- g.fillAll (findColour (secondaryBackgroundColourId));
- }
-
- void resized() override
- {
- concertinaPanel.setBounds (getLocalBounds().withTrimmedBottom (3));
- }
-
- TreePanelBase* getTreeWithSelectedItems()
- {
- for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0; --i)
- {
- if (auto* treeComponent = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (i)))
- {
- if (auto* base = treeComponent->getTree())
- if (base->tree.getNumSelectedItems() != 0)
- return base;
- }
- }
-
- return nullptr;
- }
-
- FileTreePanel* getFileTreePanel() { return getPanel<FileTreePanel> (0); }
- ModuleTreePanel* getModuleTreePanel() { return getPanel<ModuleTreePanel> (1); }
- ExportersTreePanel* getExportersTreePanel() { return getPanel<ExportersTreePanel> (2); }
-
- void showPanel (int panelIndex)
- {
- jassert (isPositiveAndBelow (panelIndex, concertinaPanel.getNumPanels()));
-
- concertinaPanel.expandPanelFully (concertinaPanel.getPanel (panelIndex), true);
- }
-
- private:
- //==============================================================================
- template <typename PanelType>
- PanelType* getPanel (int panelIndex)
- {
- if (auto* panel = dynamic_cast<ConcertinaTreeComponent*> (concertinaPanel.getPanel (panelIndex)))
- return dynamic_cast<PanelType*> (panel->getTree());
-
- return nullptr;
- }
-
- void changeListenerCallback (ChangeBroadcaster* source) override
- {
- const auto pointerMatches = [source] (const std::unique_ptr<ConcertinaHeader>& header) { return header.get() == source; };
- const auto it = std::find_if (headers.begin(), headers.end(), pointerMatches);
- const auto index = (int) std::distance (headers.begin(), it);
-
- if (index != (int) headers.size())
- concertinaPanel.expandPanelFully (concertinaPanel.getPanel (index), true);
- }
-
- void buildConcertina()
- {
- for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0 ; --i)
- concertinaPanel.removePanel (concertinaPanel.getPanel (i));
-
- headers.clear();
-
- auto addPanel = [this] (const String& name,
- TreePanelBase* tree,
- ConcertinaTreeComponent::AdditionalComponents components,
- const Path& icon)
- {
- if (project != nullptr)
- concertinaPanel.addPanel (-1, new ConcertinaTreeComponent (name, tree, components), true);
-
- headers.push_back (std::make_unique<ConcertinaHeader> (name, icon));
- };
-
- using AdditionalComponents = ConcertinaTreeComponent::AdditionalComponents;
-
- addPanel ("File Explorer", new FileTreePanel (*project),
- AdditionalComponents{}
- .with (AdditionalComponents::addButton)
- .with (AdditionalComponents::findPanel),
- getIcons().fileExplorer);
-
- addPanel ("Modules", new ModuleTreePanel (*project),
- AdditionalComponents{}
- .with (AdditionalComponents::addButton)
- .with (AdditionalComponents::settingsButton),
- getIcons().modules);
-
- addPanel ("Exporters", new ExportersTreePanel (*project),
- AdditionalComponents{}.with (AdditionalComponents::addButton),
- getIcons().exporter);
-
- for (int i = 0; i < concertinaPanel.getNumPanels(); ++i)
- {
- auto* p = concertinaPanel.getPanel (i);
- auto* h = headers[(size_t) i].get();
- p->addMouseListener (this, true);
-
- h->addChangeListener (this);
- h->yPosition = i * 30;
-
- concertinaPanel.setCustomPanelHeader (p, h, false);
- concertinaPanel.setPanelHeaderSize (p, 30);
- }
-
- addAndMakeVisible (concertinaPanel);
- }
-
- void mouseDown (const MouseEvent& e) override
- {
- for (auto i = concertinaPanel.getNumPanels() - 1; i >= 0; --i)
- {
- if (auto* p = concertinaPanel.getPanel (i))
- {
- if (! (p->isParentOf (e.eventComponent)))
- {
- auto* base = dynamic_cast<TreePanelBase*> (p);
-
- if (base == nullptr)
- if (auto* concertina = dynamic_cast<ConcertinaTreeComponent*> (p))
- base = concertina->getTree();
-
- if (base != nullptr)
- base->tree.clearSelectedItems();
- }
- }
- }
- }
-
- //==============================================================================
- ConcertinaPanel concertinaPanel;
- std::vector<std::unique_ptr<ConcertinaHeader>> headers;
- Project* project = nullptr;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Sidebar)
- };
|