| 
							- /*
 -   ==============================================================================
 - 
 -    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
 - 
 - #include "../../Application/jucer_CommonHeaders.h"
 - #include "../../Application/jucer_Application.h"
 - 
 - //==============================================================================
 - class MessagesPopupWindow  : public Component,
 -                              private ComponentMovementWatcher
 - {
 - public:
 -     MessagesPopupWindow (Component& target, Component& parent, Project& project)
 -         : ComponentMovementWatcher (&parent),
 -           targetComponent (target),
 -           parentComponent (parent),
 -           messagesListComponent (*this, project)
 -     {
 -         parentComponent.addAndMakeVisible (this);
 -         setAlwaysOnTop (true);
 - 
 -         addAndMakeVisible (viewport);
 -         viewport.setScrollBarsShown (true, false);
 -         viewport.setViewedComponent (&messagesListComponent, false);
 -         viewport.setWantsKeyboardFocus (false);
 - 
 -         setOpaque (true);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         g.fillAll (findColour (secondaryBackgroundColourId));
 -     }
 - 
 -     void resized() override
 -     {
 -         viewport.setBounds (getLocalBounds());
 -     }
 - 
 -     bool isListShowing() const
 -     {
 -         return messagesListComponent.getRequiredHeight() > 0;
 -     }
 - 
 -     void updateBounds (bool animate)
 -     {
 -         auto targetBounds = parentComponent.getLocalArea (&targetComponent, targetComponent.getLocalBounds());
 - 
 -         auto height = jmin (messagesListComponent.getRequiredHeight(), maxHeight);
 -         auto yPos = jmax (indent, targetBounds.getY() - height);
 - 
 -         Rectangle<int> bounds (targetBounds.getX(), yPos,
 -                                jmin (width, parentComponent.getWidth() - targetBounds.getX() - indent), targetBounds.getY() - yPos);
 - 
 -         auto& animator = Desktop::getInstance().getAnimator();
 - 
 -         if (animate)
 -         {
 -             setBounds (bounds.withY (targetBounds.getY()));
 -             animator.animateComponent (this, bounds, 1.0f, 150, false, 1.0, 1.0);
 -         }
 -         else
 -         {
 -             if (animator.isAnimating (this))
 -                 animator.cancelAnimation (this, false);
 - 
 -             setBounds (bounds);
 -         }
 - 
 -         messagesListComponent.resized();
 -     }
 - 
 - private:
 -     //==============================================================================
 -     class MessagesListComponent  : public Component,
 -                                    private ValueTree::Listener,
 -                                    private AsyncUpdater
 -     {
 -     public:
 -         MessagesListComponent (MessagesPopupWindow& o, Project& currentProject)
 -             : owner (o),
 -               project (currentProject)
 -         {
 -             messagesTree = project.getProjectMessages();
 -             messagesTree.addListener (this);
 - 
 -             setOpaque (true);
 - 
 -             messagesChanged();
 -         }
 - 
 -         void resized() override
 -         {
 -             auto bounds = getLocalBounds();
 -             auto numMessages = messages.size();
 - 
 -             for (size_t i = 0; i < numMessages; ++i)
 -             {
 -                 messages[i]->setBounds (bounds.removeFromTop (messageHeight));
 - 
 -                 if (numMessages > 1 && i != (numMessages - 1))
 -                     bounds.removeFromTop (messageSpacing);
 -             }
 -         }
 - 
 -         void paint (Graphics& g) override
 -         {
 -             g.fillAll (findColour (backgroundColourId).contrasting (0.2f));
 -         }
 - 
 -         int getRequiredHeight() const
 -         {
 -             auto numMessages = (int) messages.size();
 - 
 -             if (numMessages > 0)
 -                 return (numMessages * messageHeight) + ((numMessages - 1) * messageSpacing);
 - 
 -             return 0;
 -         }
 - 
 -         void updateSize (int parentWidth)
 -         {
 -             setSize (parentWidth, getRequiredHeight());
 -         }
 - 
 -     private:
 -         static constexpr int messageHeight = 65;
 -         static constexpr int messageSpacing = 2;
 - 
 -         //==============================================================================
 -         struct MessageComponent  : public Component
 -         {
 -             MessageComponent (MessagesListComponent& listComponent,
 -                               const Identifier& messageToDisplay,
 -                               std::vector<ProjectMessages::MessageAction> messageActions)
 -                : message (messageToDisplay)
 -             {
 -                 for (auto& action : messageActions)
 -                 {
 -                     auto button = std::make_unique<TextButton> (action.first);
 -                     addAndMakeVisible (*button);
 -                     button->onClick = action.second;
 - 
 -                     buttons.push_back (std::move (button));
 -                 }
 - 
 -                 icon = (ProjectMessages::getTypeForMessage (message) == ProjectMessages::Ids::warning ? getIcons().warning : getIcons().info);
 - 
 -                 messageTitleLabel.setText (ProjectMessages::getTitleForMessage (message), dontSendNotification);
 -                 messageTitleLabel.setFont (Font (11.0f).boldened());
 -                 addAndMakeVisible (messageTitleLabel);
 - 
 -                 messageDescriptionLabel.setText (ProjectMessages::getDescriptionForMessage (message), dontSendNotification);
 -                 messageDescriptionLabel.setFont (Font (11.0f));
 -                 messageDescriptionLabel.setJustificationType (Justification::topLeft);
 -                 addAndMakeVisible (messageDescriptionLabel);
 - 
 -                 dismissButton.setShape (getLookAndFeel().getCrossShape (1.0f), false, true, false);
 -                 addAndMakeVisible (dismissButton);
 - 
 -                 dismissButton.onClick = [this, &listComponent]
 -                 {
 -                     listComponent.messagesTree.getChildWithName (ProjectMessages::getTypeForMessage (message))
 -                                               .getChildWithName (message)
 -                                               .setProperty (ProjectMessages::Ids::isVisible, false, nullptr);
 -                 };
 -             }
 - 
 -             void paint (Graphics& g) override
 -             {
 -                 g.fillAll (findColour (secondaryBackgroundColourId).contrasting (0.1f));
 - 
 -                 auto bounds = getLocalBounds().reduced (5);
 - 
 -                 g.setColour (findColour (defaultIconColourId));
 -                 g.fillPath (icon, icon.getTransformToScaleToFit (bounds.removeFromTop (messageTitleHeight)
 -                                                                        .removeFromLeft (messageTitleHeight).toFloat(), true));
 -             }
 - 
 -             void resized() override
 -             {
 -                 auto bounds = getLocalBounds().reduced (5);
 - 
 -                 auto topSlice = bounds.removeFromTop (messageTitleHeight);
 - 
 -                 topSlice.removeFromLeft (messageTitleHeight + 5);
 -                 topSlice.removeFromRight (5);
 - 
 -                 dismissButton.setBounds (topSlice.removeFromRight (messageTitleHeight));
 -                 messageTitleLabel.setBounds (topSlice);
 -                 bounds.removeFromTop (5);
 - 
 -                 auto numButtons = (int) buttons.size();
 - 
 -                 if (numButtons > 0)
 -                 {
 -                     auto buttonBounds = bounds.removeFromBottom (buttonHeight);
 - 
 -                     auto buttonWidth = roundToInt ((float) buttonBounds.getWidth() / 3.5f);
 -                     auto requiredWidth = (numButtons * buttonWidth) + ((numButtons - 1) * buttonSpacing);
 -                     buttonBounds.reduce ((buttonBounds.getWidth() - requiredWidth) / 2, 0);
 - 
 -                     for (auto& b : buttons)
 -                     {
 -                         b->setBounds (buttonBounds.removeFromLeft (buttonWidth));
 -                         buttonBounds.removeFromLeft (buttonSpacing);
 -                     }
 - 
 -                     bounds.removeFromBottom (5);
 -                 }
 - 
 -                 messageDescriptionLabel.setBounds (bounds);
 -             }
 - 
 -             static constexpr int messageTitleHeight = 11;
 -             static constexpr int buttonHeight = messageHeight / 4;
 -             static constexpr int buttonSpacing = 5;
 - 
 -             Identifier message;
 - 
 -             Path icon;
 -             Label messageTitleLabel, messageDescriptionLabel;
 -             std::vector<std::unique_ptr<TextButton>> buttons;
 -             ShapeButton dismissButton { {},
 -                                         findColour (treeIconColourId),
 -                                         findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)),
 -                                         findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) };
 - 
 -             JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageComponent)
 -         };
 - 
 -         //==============================================================================
 -         void valueTreePropertyChanged   (ValueTree&, const Identifier&) override  { messagesChanged(); }
 -         void valueTreeChildAdded        (ValueTree&, ValueTree&)        override  { messagesChanged(); }
 -         void valueTreeChildRemoved      (ValueTree&, ValueTree&, int)   override  { messagesChanged(); }
 -         void valueTreeChildOrderChanged (ValueTree&, int, int)          override  { messagesChanged(); }
 -         void valueTreeParentChanged     (ValueTree&)                    override  { messagesChanged(); }
 -         void valueTreeRedirected        (ValueTree&)                    override  { messagesChanged(); }
 - 
 -         void handleAsyncUpdate() override
 -         {
 -             messagesChanged();
 -         }
 - 
 -         void messagesChanged()
 -         {
 -             auto listWasShowing = (getHeight() > 0);
 - 
 -             auto warningsTree = messagesTree.getChildWithName (ProjectMessages::Ids::warning);
 -             auto notificationsTree = messagesTree.getChildWithName (ProjectMessages::Ids::notification);
 - 
 -             auto removePredicate = [warningsTree, notificationsTree] (std::unique_ptr<MessageComponent>& messageComponent)
 -             {
 -                 for (int i = 0; i < warningsTree.getNumChildren(); ++i)
 -                 {
 -                     auto child = warningsTree.getChild (i);
 - 
 -                     if (child.getType() == messageComponent->message
 -                         && child.getProperty (ProjectMessages::Ids::isVisible))
 -                     {
 -                         return false;
 -                     }
 -                 }
 - 
 -                 for (int i = 0; i < notificationsTree.getNumChildren(); ++i)
 -                 {
 -                     auto child = notificationsTree.getChild (i);
 - 
 -                     if (child.getType() == messageComponent->message
 -                         && child.getProperty (ProjectMessages::Ids::isVisible))
 -                     {
 -                         return false;
 -                     }
 -                 }
 - 
 -                 return true;
 -             };
 - 
 -             messages.erase (std::remove_if (std::begin (messages), std::end (messages), removePredicate),
 -                                             std::end (messages));
 - 
 -             for (int i = 0; i < warningsTree.getNumChildren(); ++i)
 -             {
 -                 auto child = warningsTree.getChild (i);
 - 
 -                 if (! child.getProperty (ProjectMessages::Ids::isVisible))
 -                     continue;
 - 
 -                 if (std::find_if (std::begin (messages), std::end (messages),
 -                                   [child] (const std::unique_ptr<MessageComponent>& messageComponent) { return messageComponent->message == child.getType(); })
 -                     == std::end (messages))
 -                 {
 -                     messages.push_back (std::make_unique<MessageComponent> (*this, child.getType(), project.getMessageActions (child.getType())));
 -                     addAndMakeVisible (*messages.back());
 -                 }
 -             }
 - 
 -             for (int i = 0; i < notificationsTree.getNumChildren(); ++i)
 -             {
 -                 auto child = notificationsTree.getChild (i);
 - 
 -                 if (! child.getProperty (ProjectMessages::Ids::isVisible))
 -                     continue;
 - 
 -                 if (std::find_if (std::begin (messages), std::end (messages),
 -                                   [child] (const std::unique_ptr<MessageComponent>& messageComponent) { return messageComponent->message == child.getType(); })
 -                     == std::end (messages))
 -                 {
 -                     messages.push_back (std::make_unique<MessageComponent> (*this, child.getType(), project.getMessageActions (child.getType())));
 -                     addAndMakeVisible (*messages.back());
 -                 }
 -             }
 - 
 -             auto isNowShowing = (messages.size() > 0);
 - 
 -             owner.updateBounds (isNowShowing != listWasShowing);
 -             updateSize (owner.getWidth());
 -         }
 - 
 -         //==============================================================================
 -         MessagesPopupWindow& owner;
 -         Project& project;
 - 
 -         ValueTree messagesTree;
 -         std::vector<std::unique_ptr<MessageComponent>> messages;
 - 
 -         JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessagesListComponent)
 -     };
 - 
 -     //==============================================================================
 -     void componentMovedOrResized (bool, bool) override
 -     {
 -         if (isListShowing())
 -             updateBounds (false);
 -     }
 - 
 -     using ComponentMovementWatcher::componentMovedOrResized;
 - 
 -     void componentPeerChanged() override
 -     {
 -         if (isListShowing())
 -             updateBounds (false);
 -     }
 - 
 -     void componentVisibilityChanged() override
 -     {
 -         if (isListShowing())
 -             updateBounds (false);
 -     }
 - 
 -     using ComponentMovementWatcher::componentVisibilityChanged;
 - 
 -     //==============================================================================
 -     static constexpr int maxHeight = 500, width = 350, indent = 20;
 - 
 -     Component& targetComponent;
 -     Component& parentComponent;
 - 
 -     Viewport viewport;
 -     MessagesListComponent messagesListComponent;
 - 
 -     //==============================================================================
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessagesPopupWindow)
 - };
 - 
 - //==============================================================================
 - class ProjectMessagesComponent  : public Component
 - {
 - public:
 -     ProjectMessagesComponent()
 -     {
 -         setFocusContainerType (FocusContainerType::focusContainer);
 -         setTitle ("Project Messages");
 - 
 -         addAndMakeVisible (warningsComponent);
 -         addAndMakeVisible (notificationsComponent);
 - 
 -         warningsComponent.addMouseListener (this, true);
 -         notificationsComponent.addMouseListener (this, true);
 - 
 -         setOpaque (true);
 -     }
 - 
 -     //==============================================================================
 -     void resized() override
 -     {
 -         auto b = getLocalBounds();
 - 
 -         warningsComponent.setBounds (b.removeFromLeft (b.getWidth() / 2).reduced (5));
 -         notificationsComponent.setBounds (b.reduced (5));
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         auto backgroundColour = findColour (backgroundColourId);
 - 
 -         if (isMouseDown || isMouseOver)
 -             backgroundColour = backgroundColour.overlaidWith (findColour (defaultHighlightColourId)
 -                                                                  .withAlpha (isMouseDown ? 1.0f : 0.8f));
 - 
 -         g.fillAll (backgroundColour);
 -     }
 - 
 -     //==============================================================================
 -     void mouseEnter (const MouseEvent&) override
 -     {
 -         isMouseOver = true;
 -         repaint();
 -     }
 - 
 -     void mouseExit (const MouseEvent&) override
 -     {
 -         isMouseOver = false;
 -         repaint();
 -     }
 - 
 -     void mouseDown (const MouseEvent&) override
 -     {
 -         isMouseDown = true;
 -         repaint();
 -     }
 - 
 -     void mouseUp (const MouseEvent&) override
 -     {
 -         isMouseDown = false;
 -         repaint();
 - 
 -         showOrHideMessagesWindow();
 -     }
 - 
 -     std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
 -     {
 -         return std::make_unique<AccessibilityHandler> (*this,
 -                                                        AccessibilityRole::button,
 -                                                        AccessibilityActions().addAction (AccessibilityActionType::press,
 -                                                                                          [this] { showOrHideMessagesWindow(); }));
 -     }
 - 
 -     //==============================================================================
 -     void setProject (Project* newProject)
 -     {
 -         if (currentProject != newProject)
 -         {
 -             currentProject = newProject;
 - 
 -             if (currentProject != nullptr)
 -             {
 -                 if (auto* projectWindow = ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (currentProject->getFile()))
 -                     messagesWindow = std::make_unique<MessagesPopupWindow> (*this, *projectWindow, *currentProject);
 - 
 -                 auto projectMessagesTree = currentProject->getProjectMessages();
 - 
 -                 warningsComponent.setTree (projectMessagesTree.getChildWithName (ProjectMessages::Ids::warning));
 -                 notificationsComponent.setTree (projectMessagesTree.getChildWithName (ProjectMessages::Ids::notification));
 -             }
 -             else
 -             {
 -                 warningsComponent.setTree ({});
 -                 notificationsComponent.setTree ({});
 -             }
 -         }
 -     }
 - 
 -     void numMessagesChanged()
 -     {
 -         const auto total = warningsComponent.getNumMessages()
 -                            + notificationsComponent.getNumMessages();
 - 
 -         setHelpText (String (total) + (total == 1 ? " message" : " messages"));
 -     }
 - 
 -     void showOrHideMessagesWindow()
 -     {
 -         if (messagesWindow != nullptr)
 -             showOrHideAllMessages (! messagesWindow->isListShowing());
 -     }
 - 
 - private:
 -     //==============================================================================
 -     struct MessageCountComponent  : public Component,
 -                                     private ValueTree::Listener
 -     {
 -         MessageCountComponent (ProjectMessagesComponent& o, Path pathToUse)
 -           : owner (o),
 -             path (pathToUse)
 -         {
 -             setInterceptsMouseClicks (false, false);
 -         }
 - 
 -         void paint (Graphics& g) override
 -         {
 -             auto b = getLocalBounds().toFloat();
 - 
 -             g.setColour (findColour ((owner.isMouseDown || owner.isMouseOver) ? defaultHighlightedTextColourId : treeIconColourId));
 -             g.fillPath (path, path.getTransformToScaleToFit (b.removeFromLeft (b.getWidth() / 2.0f), true));
 - 
 -             b.removeFromLeft (5);
 -             g.drawFittedText (String (numMessages), b.getSmallestIntegerContainer(), Justification::centredLeft, 1);
 -         }
 - 
 -         void setTree (ValueTree tree)
 -         {
 -             messagesTree = tree;
 - 
 -             if (messagesTree.isValid())
 -                 messagesTree.addListener (this);
 - 
 -             updateNumMessages();
 -         }
 - 
 -         void updateNumMessages()
 -         {
 -             numMessages = messagesTree.getNumChildren();
 -             owner.numMessagesChanged();
 -             repaint();
 -         }
 - 
 -         int getNumMessages() const noexcept  { return numMessages; }
 - 
 -     private:
 -         void valueTreeChildAdded   (ValueTree&, ValueTree&)        override  { updateNumMessages(); }
 -         void valueTreeChildRemoved (ValueTree&, ValueTree&, int)   override  { updateNumMessages(); }
 - 
 -         ProjectMessagesComponent& owner;
 -         ValueTree messagesTree;
 - 
 -         Path path;
 -         int numMessages = 0;
 -     };
 - 
 -     void showOrHideAllMessages (bool shouldBeVisible)
 -     {
 -         if (currentProject != nullptr)
 -         {
 -             auto messagesTree = currentProject->getProjectMessages();
 - 
 -             auto setVisible = [shouldBeVisible] (ValueTree subTree)
 -             {
 -                 for (int i = 0; i < subTree.getNumChildren(); ++i)
 -                     subTree.getChild (i).setProperty (ProjectMessages::Ids::isVisible, shouldBeVisible, nullptr);
 -             };
 - 
 -             setVisible (messagesTree.getChildWithName (ProjectMessages::Ids::warning));
 -             setVisible (messagesTree.getChildWithName (ProjectMessages::Ids::notification));
 -         }
 -     }
 - 
 -     //==============================================================================
 -     Project* currentProject = nullptr;
 -     bool isMouseOver = false, isMouseDown = false;
 - 
 -     MessageCountComponent warningsComponent { *this, getIcons().warning },
 -                           notificationsComponent { *this, getIcons().info };
 - 
 -     std::unique_ptr<MessagesPopupWindow> messagesWindow;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectMessagesComponent)
 - };
 
 
  |