Browse Source

GUI: Added a SidePanel component class which is useful for auxiliary UI components on mobile

tags/2021-05-28
ed 8 years ago
parent
commit
1b962aa2f2
8 changed files with 475 additions and 1 deletions
  1. +1
    -0
      modules/juce_gui_basics/juce_gui_basics.cpp
  2. +1
    -0
      modules/juce_gui_basics/juce_gui_basics.h
  3. +233
    -0
      modules/juce_gui_basics/layout/juce_SidePanel.cpp
  4. +188
    -0
      modules/juce_gui_basics/layout/juce_SidePanel.h
  5. +2
    -1
      modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h
  6. +38
    -0
      modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp
  7. +5
    -0
      modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h
  8. +7
    -0
      modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp

+ 1
- 0
modules/juce_gui_basics/juce_gui_basics.cpp View File

@@ -202,6 +202,7 @@ namespace juce
#include "layout/juce_ResizableCornerComponent.cpp" #include "layout/juce_ResizableCornerComponent.cpp"
#include "layout/juce_ResizableEdgeComponent.cpp" #include "layout/juce_ResizableEdgeComponent.cpp"
#include "layout/juce_ScrollBar.cpp" #include "layout/juce_ScrollBar.cpp"
#include "layout/juce_SidePanel.cpp"
#include "layout/juce_StretchableLayoutManager.cpp" #include "layout/juce_StretchableLayoutManager.cpp"
#include "layout/juce_StretchableLayoutResizerBar.cpp" #include "layout/juce_StretchableLayoutResizerBar.cpp"
#include "layout/juce_StretchableObjectResizer.cpp" #include "layout/juce_StretchableObjectResizer.cpp"


+ 1
- 0
modules/juce_gui_basics/juce_gui_basics.h View File

@@ -263,6 +263,7 @@ namespace juce
#include "windows/juce_ThreadWithProgressWindow.h" #include "windows/juce_ThreadWithProgressWindow.h"
#include "windows/juce_TooltipWindow.h" #include "windows/juce_TooltipWindow.h"
#include "layout/juce_MultiDocumentPanel.h" #include "layout/juce_MultiDocumentPanel.h"
#include "layout/juce_SidePanel.h"
#include "filebrowser/juce_FileBrowserListener.h" #include "filebrowser/juce_FileBrowserListener.h"
#include "filebrowser/juce_DirectoryContentsList.h" #include "filebrowser/juce_DirectoryContentsList.h"
#include "filebrowser/juce_DirectoryContentsDisplayComponent.h" #include "filebrowser/juce_DirectoryContentsDisplayComponent.h"


+ 233
- 0
modules/juce_gui_basics/layout/juce_SidePanel.cpp View File

@@ -0,0 +1,233 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-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
{
SidePanel::SidePanel (StringRef title, int width, bool positionOnLeft,
Component* contentToDisplay, bool deleteComponentWhenNoLongerNeeded)
: titleLabel ("titleLabel", title),
isOnLeft (positionOnLeft),
panelWidth (width)
{
lookAndFeelChanged();
addAndMakeVisible (titleLabel);
dismissButton.addListener (this);
addAndMakeVisible (dismissButton);
Desktop::getInstance().addGlobalMouseListener (this);
if (contentToDisplay != nullptr)
setContent (contentToDisplay, deleteComponentWhenNoLongerNeeded);
setOpaque (false);
}
SidePanel::~SidePanel()
{
if (parent != nullptr)
parent->removeComponentListener (this);
}
void SidePanel::setContent (Component* newContent, bool deleteComponentWhenNoLongerNeeded)
{
if (contentComponent.get() != newContent)
{
if (deleteComponentWhenNoLongerNeeded)
contentComponent.setOwned (newContent);
else
contentComponent.setNonOwned (newContent);
addAndMakeVisible (contentComponent);
}
}
void SidePanel::showOrHide (bool show)
{
if (parent != nullptr)
{
isShowing = show;
Desktop::getInstance().getAnimator().animateComponent (this, calculateBoundsInParent (*parent),
1.0f, 250, true, 1.0, 0.0);
}
}
void SidePanel::resized()
{
auto bounds = getLocalBounds();
calculateAndRemoveShadowBounds (bounds);
auto titleBounds = bounds.removeFromTop (titleBarHeight);
dismissButton.setBounds (isOnLeft ? titleBounds.removeFromRight (30).withTrimmedRight (10)
: titleBounds.removeFromLeft (30).withTrimmedLeft (10));
titleLabel.setBounds (isOnLeft ? titleBounds.withTrimmedRight (40)
: titleBounds.withTrimmedLeft (40));
if (contentComponent != nullptr)
contentComponent->setBounds (bounds);
}
void SidePanel::paint (Graphics& g)
{
auto& lf = getLookAndFeel();
auto bgColour = lf.findColour (SidePanel::backgroundColour);
auto shadowColour = lf.findColour (SidePanel::shadowBaseColour);
g.setGradientFill (ColourGradient (shadowColour.withAlpha (0.7f), (isOnLeft ? shadowArea.getTopLeft()
: shadowArea.getTopRight()).toFloat(),
shadowColour.withAlpha (0.0f), (isOnLeft ? shadowArea.getTopRight()
: shadowArea.getTopLeft()).toFloat(), false));
g.fillRect (shadowArea);
g.excludeClipRegion (shadowArea);
g.fillAll (bgColour);
}
void SidePanel::parentHierarchyChanged()
{
auto* newParent = getParentComponent();
if ((newParent != nullptr) && (parent != newParent))
{
if (parent != nullptr)
parent->removeComponentListener (this);
parent = newParent;
parent->addComponentListener (this);
}
}
void SidePanel::mouseDrag (const MouseEvent& e)
{
if (shouldResize)
{
auto currentMouseDragX = static_cast<int> (e.position.x);
if (isOnLeft)
{
amountMoved = startingBounds.getRight() - currentMouseDragX;
setBounds (getBounds().withX (startingBounds.getX() - jmax (amountMoved, 0)));
}
else
{
amountMoved = currentMouseDragX - startingBounds.getX();
setBounds (getBounds().withX (startingBounds.getX() + jmax (amountMoved, 0)));
}
}
else
{
auto relativeMouseDownPosition = getLocalPoint (e.eventComponent, e.getMouseDownPosition());
auto relativeMouseDragPosition = getLocalPoint (e.eventComponent, e.getPosition());
if (! getLocalBounds().contains (relativeMouseDownPosition)
&& getLocalBounds().contains (relativeMouseDragPosition))
{
shouldResize = true;
startingBounds = getBounds();
}
}
}
void SidePanel::mouseUp (const MouseEvent&)
{
if (shouldResize)
{
showOrHide (amountMoved < (panelWidth / 2));
amountMoved = 0;
shouldResize = false;
}
}
//==========================================================================
void SidePanel::lookAndFeelChanged()
{
auto& lf = getLookAndFeel();
dismissButton.setShape (lf.getSidePanelDismissButtonShape (*this), false, true, false);
dismissButton.setColours (lf.findColour (SidePanel::dismissButtonNormalColour),
lf.findColour (SidePanel::dismissButtonOverColour),
lf.findColour (SidePanel::dismissButtonDownColour));
titleLabel.setFont (lf.getSidePanelTitleFont (*this));
titleLabel.setColour (Label::textColourId, findColour (SidePanel::titleTextColour));
titleLabel.setJustificationType (lf.getSidePanelTitleJustification (*this));
}
void SidePanel::componentMovedOrResized (Component& component, bool wasMoved, bool wasResized)
{
ignoreUnused (wasMoved);
if (wasResized && (&component == parent))
setBounds (calculateBoundsInParent (component));
}
void SidePanel::buttonClicked (Button*)
{
showOrHide (false);
}
Rectangle<int> SidePanel::calculateBoundsInParent (Component& parentComp) const
{
auto parentBounds = parentComp.getBounds();
if (isOnLeft)
{
return isShowing ? parentBounds.removeFromLeft (panelWidth)
: parentBounds.withX (parentBounds.getX() - panelWidth).withWidth (panelWidth);
}
return isShowing ? parentBounds.removeFromRight (panelWidth)
: parentBounds.withX (parentBounds.getRight()).withWidth (panelWidth);
}
void SidePanel::calculateAndRemoveShadowBounds (Rectangle<int>& bounds)
{
shadowArea = isOnLeft ? bounds.removeFromRight (shadowWidth)
: bounds.removeFromLeft (shadowWidth);
}
bool SidePanel::isMouseEventInThisOrChildren (Component* eventComponent)
{
if (eventComponent == this)
return true;
for (auto& child : getChildren())
if (eventComponent == child)
return true;
return false;
}
} // namespace juce

+ 188
- 0
modules/juce_gui_basics/layout/juce_SidePanel.h View File

@@ -0,0 +1,188 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-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
{
//==============================================================================
/**
A component that is positioned on either the left- or right-hand side of its parent,
containing a header and some content. This sort of component is typically used for
navigation and forms in mobile applications.
When triggered with the showOrHide() method, the SidePanel will animate itself to its
new position. This component also contains some logic to reactively resize and dismiss
itself when the user drags it.
*/
//==============================================================================
class SidePanel : public Component,
private ComponentListener,
private Button::Listener
{
public:
//==============================================================================
/** Creates a SidePanel component.
@param title the text to use for the SidePanel's title bar
@param width the width of the SidePanel
@param positionOnLeft if true, the SidePanel will be positioned on the left of its parent component and
if false, the SidePanel will be positioned on the right of its parent component
@param contentComponent the component to add to this SidePanel - this content will take up the full
size of the SidePanel, minus the height of the title bar. You can pass nullptr
to this if you like and set the content component later using the setContent() method
@param deleteComponentWhenNoLongerNeeded if true, the component will be deleted automatically when
the SidePanel is deleted or when a different component is added. If false,
the caller must manage the lifetime of the component
*/
SidePanel (StringRef title, int width, bool positionOnLeft,
Component* contentComponent = nullptr, bool deleteComponentWhenNoLongerNeeded = true);
/** Destructor */
~SidePanel();
//==============================================================================
/** Sets the component that this SidePanel will contain.
This will add the given component to this SidePanel and position it below the title bar.
(Don't add or remove any child components directly using the normal
Component::addChildComponent() methods).
@param newViewedComponent the component to add to this SidePanel, or null to remove
the current component.
@param deleteComponentWhenNoLongerNeeded if true, the component will be deleted automatically when
the SidePanel is deleted or when a different component is added. If false,
the caller must manage the lifetime of the component
@see getContent
*/
void setContent (Component* newContentComponent,
bool deleteComponentWhenNoLongerNeeded = true);
/** Returns the component that's currently being used inside the SidePanel.
@see setViewedComponent
*/
Component* getContent() { return contentComponent.get(); }
/** Shows or hides the SidePanel.
This will animate the SidePanel to either its full width or to be hidden on the
left- or right-hand side of its parent component depending on the value of positionOnLeft
that was passed to the constructor.
@param show if true, this will show the SidePanel and if false the SidePanel will be hidden
*/
void showOrHide (bool show);
//==============================================================================
/** Returns true if the SidePanel is currently showing. */
bool isPanelShowing() const noexcept { return isShowing; }
/** Returns true if the SidePanel is positioned on the left of its parent. */
bool isPanelOnLeft() const noexcept { return isOnLeft; }
/** Sets the width of the shadow that will be drawn on the side of the panel. */
void setShadowWidth (int newWidth) noexcept { shadowWidth = newWidth; }
/** Sets the height of the title bar at the top of the SidePanel. */
void setTitleBarHeight (int newHeight) noexcept { titleBarHeight = newHeight; }
//==============================================================================
void resized() override;
void paint (Graphics& g) override;
void parentHierarchyChanged() override;
void mouseDrag (const MouseEvent&) override;
void mouseUp (const MouseEvent&) override;
//==============================================================================
/** This abstract base class is implemented by LookAndFeel classes to provide
SidePanel drawing functionality.
*/
struct JUCE_API LookAndFeelMethods
{
virtual ~LookAndFeelMethods() {}
virtual Font getSidePanelTitleFont (SidePanel&) = 0;
virtual Justification getSidePanelTitleJustification (SidePanel&) = 0;
virtual Path getSidePanelDismissButtonShape (SidePanel&) = 0;
};
/** A set of colour IDs to use to change the colour of various aspects of the SidePanel.
These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
methods.
@see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
*/
enum ColourIds
{
backgroundColour = 0x100f001,
titleTextColour = 0x100f002,
shadowBaseColour = 0x100f003,
dismissButtonNormalColour = 0x100f004,
dismissButtonOverColour = 0x100f004,
dismissButtonDownColour = 0x100f005
};
private:
//==========================================================================
Component* parent = nullptr;
OptionalScopedPointer<Component> contentComponent;
Label titleLabel;
ShapeButton dismissButton {"dismissButton", Colours::lightgrey, Colours::lightgrey, Colours::white};
Rectangle<int> shadowArea;
bool isOnLeft = false;
bool isShowing = false;
int panelWidth = 0;
int shadowWidth = 15;
int titleBarHeight = 40;
Rectangle<int> startingBounds;
bool shouldResize = false;
int amountMoved = 0;
//==========================================================================
void lookAndFeelChanged() override;
void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override;
void buttonClicked (Button*) override;
Rectangle<int> calculateBoundsInParent (Component&) const;
void calculateAndRemoveShadowBounds (Rectangle<int>& bounds);
bool isMouseEventInThisOrChildren (Component*);
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SidePanel)
};
} // namespace juce

+ 2
- 1
modules/juce_gui_basics/lookandfeel/juce_LookAndFeel.h View File

@@ -100,7 +100,8 @@ class JUCE_API LookAndFeel : public ScrollBar::LookAndFeelMethods,
public StretchableLayoutResizerBar::LookAndFeelMethods, public StretchableLayoutResizerBar::LookAndFeelMethods,
public ExtraLookAndFeelBaseClasses::KeyMappingEditorComponentMethods, public ExtraLookAndFeelBaseClasses::KeyMappingEditorComponentMethods,
public ExtraLookAndFeelBaseClasses::AudioDeviceSelectorComponentMethods, public ExtraLookAndFeelBaseClasses::AudioDeviceSelectorComponentMethods,
public ExtraLookAndFeelBaseClasses::LassoComponentMethods
public ExtraLookAndFeelBaseClasses::LassoComponentMethods,
public SidePanel::LookAndFeelMethods
{ {
public: public:
//============================================================================== //==============================================================================


+ 38
- 0
modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp View File

@@ -212,6 +212,13 @@ LookAndFeel_V2::LookAndFeel_V2()
FileSearchPathListComponent::backgroundColourId, 0xffffffff, FileSearchPathListComponent::backgroundColourId, 0xffffffff,
FileChooserDialogBox::titleTextColourId, 0xff000000, FileChooserDialogBox::titleTextColourId, 0xff000000,
SidePanel::backgroundColour, 0xffffffff,
SidePanel::titleTextColour, 0xff000000,
SidePanel::shadowBaseColour, 0xff000000,
SidePanel::dismissButtonNormalColour, textButtonColour,
SidePanel::dismissButtonOverColour, textButtonColour,
SidePanel::dismissButtonDownColour, 0xff4444ff,
}; };
for (int i = 0; i < numElementsInArray (standardColours); i += 2) for (int i = 0; i < numElementsInArray (standardColours); i += 2)
@@ -2775,6 +2782,37 @@ void LookAndFeel_V2::drawKeymapChangeButton (Graphics& g, int width, int height,
g.drawRect (0, 0, width, height); g.drawRect (0, 0, width, height);
} }
} }
//==============================================================================
Font LookAndFeel_V2::getSidePanelTitleFont (SidePanel&)
{
return Font (18.0f);
}
Justification LookAndFeel_V2::getSidePanelTitleJustification (SidePanel& panel)
{
return panel.isPanelOnLeft() ? Justification::centredRight
: Justification::centredLeft;
}
Path LookAndFeel_V2::getSidePanelDismissButtonShape (SidePanel& panel)
{
Path p;
if (panel.isPanelOnLeft())
{
p.startNewSubPath (10, 0);
p.lineTo (0, 5);
p.lineTo (10, 10);
}
else
{
p.startNewSubPath (0, 10);
p.lineTo (10, 5);
p.lineTo (0, 0);
}
return p;
}
//============================================================================== //==============================================================================
void LookAndFeel_V2::drawBevel (Graphics& g, const int x, const int y, const int width, const int height, void LookAndFeel_V2::drawBevel (Graphics& g, const int x, const int y, const int width, const int height,


+ 5
- 0
modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h View File

@@ -323,6 +323,11 @@ public:
void drawKeymapChangeButton (Graphics&, int width, int height, Button&, const String& keyDescription) override; void drawKeymapChangeButton (Graphics&, int width, int height, Button&, const String& keyDescription) override;
//==============================================================================
Font getSidePanelTitleFont (SidePanel&) override;
Justification getSidePanelTitleJustification (SidePanel&) override;
Path getSidePanelDismissButtonShape (SidePanel&) override;
//============================================================================== //==============================================================================
/** Draws a 3D raised (or indented) bevel using two colours. /** Draws a 3D raised (or indented) bevel using two colours.


+ 7
- 0
modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V4.cpp View File

@@ -1439,6 +1439,13 @@ void LookAndFeel_V4::initialiseColours()
FileSearchPathListComponent::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuBackground).getARGB(), FileSearchPathListComponent::backgroundColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::menuBackground).getARGB(),
FileChooserDialogBox::titleTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(), FileChooserDialogBox::titleTextColourId, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
SidePanel::backgroundColour, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).getARGB(),
SidePanel::titleTextColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultText).getARGB(),
SidePanel::shadowBaseColour, currentColourScheme.getUIColour (ColourScheme::UIColour::widgetBackground).darker().getARGB(),
SidePanel::dismissButtonNormalColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).getARGB(),
SidePanel::dismissButtonOverColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).darker().getARGB(),
SidePanel::dismissButtonDownColour, currentColourScheme.getUIColour (ColourScheme::UIColour::defaultFill).brighter().getARGB(),
}; };
for (int i = 0; i < numElementsInArray (coloursToUse); i += 2) for (int i = 0; i < numElementsInArray (coloursToUse); i += 2)


Loading…
Cancel
Save