524 lines
20 KiB
C
524 lines
20 KiB
C
|
/*
|
||
|
==============================================================================
|
||
|
|
||
|
This file is part of the JUCE examples.
|
||
|
Copyright (c) 2020 - Raw Material Software Limited
|
||
|
|
||
|
The code included in this file is provided under the terms of the ISC license
|
||
|
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||
|
To use, copy, modify, and/or distribute this software for any purpose with or
|
||
|
without fee is hereby granted provided that the above copyright notice and
|
||
|
this permission notice appear in all copies.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES,
|
||
|
WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR
|
||
|
PURPOSE, ARE DISCLAIMED.
|
||
|
|
||
|
==============================================================================
|
||
|
*/
|
||
|
|
||
|
/*******************************************************************************
|
||
|
The block below describes the properties of this PIP. A PIP is a short snippet
|
||
|
of code that can be read by the Projucer and used to generate a JUCE project.
|
||
|
|
||
|
BEGIN_JUCE_PIP_METADATA
|
||
|
|
||
|
name: MenusDemo
|
||
|
version: 1.0.0
|
||
|
vendor: JUCE
|
||
|
website: http://juce.com
|
||
|
description: Showcases menu features.
|
||
|
|
||
|
dependencies: juce_core, juce_data_structures, juce_events, juce_graphics,
|
||
|
juce_gui_basics, juce_gui_extra
|
||
|
exporters: xcode_mac, vs2019, linux_make, androidstudio, xcode_iphone
|
||
|
|
||
|
moduleFlags: JUCE_STRICT_REFCOUNTEDPOINTER=1
|
||
|
|
||
|
type: Component
|
||
|
mainClass: MenusDemo
|
||
|
|
||
|
useLocalCopy: 1
|
||
|
|
||
|
END_JUCE_PIP_METADATA
|
||
|
|
||
|
*******************************************************************************/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "../Assets/DemoUtilities.h"
|
||
|
|
||
|
//==============================================================================
|
||
|
/**
|
||
|
This struct contains a header component that will be used when the burger menu
|
||
|
is enabled. It contains an icon that can be used to show the side panel containing
|
||
|
the menu.
|
||
|
*/
|
||
|
struct BurgerMenuHeader : public Component
|
||
|
{
|
||
|
BurgerMenuHeader (SidePanel& sp)
|
||
|
: sidePanel (sp)
|
||
|
{
|
||
|
static const unsigned char burgerMenuPathData[]
|
||
|
= { 110,109,0,0,128,64,0,0,32,65,108,0,0,224,65,0,0,32,65,98,254,212,232,65,0,0,32,65,0,0,240,65,252,
|
||
|
169,17,65,0,0,240,65,0,0,0,65,98,0,0,240,65,8,172,220,64,254,212,232,65,0,0,192,64,0,0,224,65,0,0,
|
||
|
192,64,108,0,0,128,64,0,0,192,64,98,16,88,57,64,0,0,192,64,0,0,0,64,8,172,220,64,0,0,0,64,0,0,0,65,
|
||
|
98,0,0,0,64,252,169,17,65,16,88,57,64,0,0,32,65,0,0,128,64,0,0,32,65,99,109,0,0,224,65,0,0,96,65,108,
|
||
|
0,0,128,64,0,0,96,65,98,16,88,57,64,0,0,96,65,0,0,0,64,4,86,110,65,0,0,0,64,0,0,128,65,98,0,0,0,64,
|
||
|
254,212,136,65,16,88,57,64,0,0,144,65,0,0,128,64,0,0,144,65,108,0,0,224,65,0,0,144,65,98,254,212,232,
|
||
|
65,0,0,144,65,0,0,240,65,254,212,136,65,0,0,240,65,0,0,128,65,98,0,0,240,65,4,86,110,65,254,212,232,
|
||
|
65,0,0,96,65,0,0,224,65,0,0,96,65,99,109,0,0,224,65,0,0,176,65,108,0,0,128,64,0,0,176,65,98,16,88,57,
|
||
|
64,0,0,176,65,0,0,0,64,2,43,183,65,0,0,0,64,0,0,192,65,98,0,0,0,64,254,212,200,65,16,88,57,64,0,0,208,
|
||
|
65,0,0,128,64,0,0,208,65,108,0,0,224,65,0,0,208,65,98,254,212,232,65,0,0,208,65,0,0,240,65,254,212,
|
||
|
200,65,0,0,240,65,0,0,192,65,98,0,0,240,65,2,43,183,65,254,212,232,65,0,0,176,65,0,0,224,65,0,0,176,
|
||
|
65,99,101,0,0 };
|
||
|
|
||
|
Path p;
|
||
|
p.loadPathFromData (burgerMenuPathData, sizeof (burgerMenuPathData));
|
||
|
burgerButton.setShape (p, true, true, false);
|
||
|
|
||
|
burgerButton.onClick = [this] { showOrHide(); };
|
||
|
addAndMakeVisible (burgerButton);
|
||
|
}
|
||
|
|
||
|
~BurgerMenuHeader() override
|
||
|
{
|
||
|
sidePanel.showOrHide (false);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
void paint (Graphics& g) override
|
||
|
{
|
||
|
auto titleBarBackgroundColour = getLookAndFeel().findColour (ResizableWindow::backgroundColourId)
|
||
|
.darker();
|
||
|
|
||
|
g.setColour (titleBarBackgroundColour);
|
||
|
g.fillRect (getLocalBounds());
|
||
|
}
|
||
|
|
||
|
void resized() override
|
||
|
{
|
||
|
auto r = getLocalBounds();
|
||
|
|
||
|
burgerButton.setBounds (r.removeFromRight (40).withSizeKeepingCentre (20, 20));
|
||
|
|
||
|
titleLabel.setFont (Font ((float) getHeight() * 0.5f, Font::plain));
|
||
|
titleLabel.setBounds (r);
|
||
|
}
|
||
|
|
||
|
void showOrHide()
|
||
|
{
|
||
|
sidePanel.showOrHide (! sidePanel.isPanelShowing());
|
||
|
}
|
||
|
|
||
|
SidePanel& sidePanel;
|
||
|
|
||
|
Label titleLabel { "titleLabel", "JUCE Demo" };
|
||
|
ShapeButton burgerButton { "burgerButton", Colours::lightgrey, Colours::lightgrey, Colours::white };
|
||
|
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BurgerMenuHeader)
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
class MenusDemo : public Component,
|
||
|
public ApplicationCommandTarget,
|
||
|
public MenuBarModel
|
||
|
{
|
||
|
public:
|
||
|
//==============================================================================
|
||
|
/** A list of the commands that this demo responds to. */
|
||
|
enum CommandIDs
|
||
|
{
|
||
|
menuPositionInsideWindow = 1,
|
||
|
menuPositionGlobalMenuBar,
|
||
|
menuPositionBurgerMenu,
|
||
|
outerColourRed,
|
||
|
outerColourGreen,
|
||
|
outerColourBlue,
|
||
|
innerColourRed,
|
||
|
innerColourGreen,
|
||
|
innerColourBlue
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
/** Represents the possible menu positions. */
|
||
|
enum class MenuBarPosition
|
||
|
{
|
||
|
window,
|
||
|
global,
|
||
|
burger
|
||
|
};
|
||
|
|
||
|
//==============================================================================
|
||
|
MenusDemo()
|
||
|
{
|
||
|
menuBar.reset (new MenuBarComponent (this));
|
||
|
addAndMakeVisible (menuBar.get());
|
||
|
setApplicationCommandManagerToWatch (&commandManager);
|
||
|
commandManager.registerAllCommandsForTarget (this);
|
||
|
|
||
|
// this ensures that commands invoked on the DemoRunner application are correctly
|
||
|
// forwarded to this demo
|
||
|
commandManager.setFirstCommandTarget (this);
|
||
|
|
||
|
// this lets the command manager use keypresses that arrive in our window to send out commands
|
||
|
addKeyListener (commandManager.getKeyMappings());
|
||
|
|
||
|
addChildComponent (menuHeader);
|
||
|
addAndMakeVisible (outerCommandTarget);
|
||
|
addAndMakeVisible (sidePanel);
|
||
|
|
||
|
setWantsKeyboardFocus (true);
|
||
|
|
||
|
setSize (500, 500);
|
||
|
}
|
||
|
|
||
|
~MenusDemo() override
|
||
|
{
|
||
|
#if JUCE_MAC
|
||
|
MenuBarModel::setMacMainMenu (nullptr);
|
||
|
#endif
|
||
|
|
||
|
commandManager.setFirstCommandTarget (nullptr);
|
||
|
}
|
||
|
|
||
|
void resized() override
|
||
|
{
|
||
|
auto b = getLocalBounds();
|
||
|
|
||
|
if (menuBarPosition == MenuBarPosition::window)
|
||
|
{
|
||
|
menuBar->setBounds (b.removeFromTop (LookAndFeel::getDefaultLookAndFeel()
|
||
|
.getDefaultMenuBarHeight()));
|
||
|
}
|
||
|
else if (menuBarPosition == MenuBarPosition::burger)
|
||
|
{
|
||
|
menuHeader.setBounds (b.removeFromTop (40));
|
||
|
}
|
||
|
|
||
|
outerCommandTarget.setBounds (b);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
StringArray getMenuBarNames() override
|
||
|
{
|
||
|
return { "Menu Position", "Outer Colour", "Inner Colour" };
|
||
|
}
|
||
|
|
||
|
PopupMenu getMenuForIndex (int menuIndex, const String& /*menuName*/) override
|
||
|
{
|
||
|
PopupMenu menu;
|
||
|
|
||
|
if (menuIndex == 0)
|
||
|
{
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::menuPositionInsideWindow);
|
||
|
#if JUCE_MAC
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::menuPositionGlobalMenuBar);
|
||
|
#endif
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::menuPositionBurgerMenu);
|
||
|
}
|
||
|
else if (menuIndex == 1)
|
||
|
{
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::outerColourRed);
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::outerColourGreen);
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::outerColourBlue);
|
||
|
}
|
||
|
else if (menuIndex == 2)
|
||
|
{
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::innerColourRed);
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::innerColourGreen);
|
||
|
menu.addCommandItem (&commandManager, CommandIDs::innerColourBlue);
|
||
|
}
|
||
|
|
||
|
return menu;
|
||
|
}
|
||
|
|
||
|
void menuItemSelected (int /*menuItemID*/, int /*topLevelMenuIndex*/) override {}
|
||
|
|
||
|
//==============================================================================
|
||
|
// The following methods implement the ApplicationCommandTarget interface, allowing
|
||
|
// this window to publish a set of actions it can perform, and which can be mapped
|
||
|
// onto menus, keypresses, etc.
|
||
|
|
||
|
ApplicationCommandTarget* getNextCommandTarget() override
|
||
|
{
|
||
|
return &outerCommandTarget;
|
||
|
}
|
||
|
|
||
|
void getAllCommands (Array<CommandID>& c) override
|
||
|
{
|
||
|
Array<CommandID> commands { CommandIDs::menuPositionInsideWindow,
|
||
|
CommandIDs::menuPositionGlobalMenuBar,
|
||
|
CommandIDs::menuPositionBurgerMenu };
|
||
|
c.addArray (commands);
|
||
|
}
|
||
|
|
||
|
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
|
||
|
{
|
||
|
switch (commandID)
|
||
|
{
|
||
|
case CommandIDs::menuPositionInsideWindow:
|
||
|
result.setInfo ("Inside Window", "Places the menu bar inside the application window", "Menu", 0);
|
||
|
result.setTicked (menuBarPosition == MenuBarPosition::window);
|
||
|
result.addDefaultKeypress ('w', ModifierKeys::shiftModifier);
|
||
|
break;
|
||
|
case CommandIDs::menuPositionGlobalMenuBar:
|
||
|
result.setInfo ("Global", "Uses a global menu bar", "Menu", 0);
|
||
|
result.setTicked (menuBarPosition == MenuBarPosition::global);
|
||
|
result.addDefaultKeypress ('g', ModifierKeys::shiftModifier);
|
||
|
break;
|
||
|
case CommandIDs::menuPositionBurgerMenu:
|
||
|
result.setInfo ("Burger Menu", "Uses a burger menu", "Menu", 0);
|
||
|
result.setTicked (menuBarPosition == MenuBarPosition::burger);
|
||
|
result.addDefaultKeypress ('b', ModifierKeys::shiftModifier);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool perform (const InvocationInfo& info) override
|
||
|
{
|
||
|
switch (info.commandID)
|
||
|
{
|
||
|
case CommandIDs::menuPositionInsideWindow:
|
||
|
setMenuBarPosition (MenuBarPosition::window);
|
||
|
break;
|
||
|
case CommandIDs::menuPositionGlobalMenuBar:
|
||
|
setMenuBarPosition (MenuBarPosition::global);
|
||
|
break;
|
||
|
case CommandIDs::menuPositionBurgerMenu:
|
||
|
setMenuBarPosition (MenuBarPosition::burger);
|
||
|
break;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void setMenuBarPosition (MenuBarPosition newPosition)
|
||
|
{
|
||
|
if (menuBarPosition != newPosition)
|
||
|
{
|
||
|
menuBarPosition = newPosition;
|
||
|
|
||
|
if (menuBarPosition != MenuBarPosition::burger)
|
||
|
sidePanel.showOrHide (false);
|
||
|
|
||
|
#if JUCE_MAC
|
||
|
MenuBarModel::setMacMainMenu (menuBarPosition == MenuBarPosition::global ? this : nullptr);
|
||
|
#endif
|
||
|
|
||
|
menuBar->setVisible (menuBarPosition == MenuBarPosition::window);
|
||
|
burgerMenu.setModel (menuBarPosition == MenuBarPosition::burger ? this : nullptr);
|
||
|
menuHeader.setVisible (menuBarPosition == MenuBarPosition::burger);
|
||
|
|
||
|
sidePanel.setContent (menuBarPosition == MenuBarPosition::burger ? &burgerMenu : nullptr, false);
|
||
|
menuItemsChanged();
|
||
|
|
||
|
resized();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
#if JUCE_DEMO_RUNNER
|
||
|
ApplicationCommandManager& commandManager = getGlobalCommandManager();
|
||
|
#else
|
||
|
ApplicationCommandManager commandManager;
|
||
|
#endif
|
||
|
|
||
|
std::unique_ptr<MenuBarComponent> menuBar;
|
||
|
MenuBarPosition menuBarPosition = MenuBarPosition::window;
|
||
|
|
||
|
SidePanel sidePanel { "Menu", 300, false };
|
||
|
|
||
|
BurgerMenuComponent burgerMenu;
|
||
|
BurgerMenuHeader menuHeader { sidePanel };
|
||
|
|
||
|
//==============================================================================
|
||
|
/**
|
||
|
Command messages that aren't handled in the main component will be passed
|
||
|
to this class to respond to.
|
||
|
*/
|
||
|
class OuterCommandTarget : public Component,
|
||
|
public ApplicationCommandTarget
|
||
|
{
|
||
|
public:
|
||
|
OuterCommandTarget (ApplicationCommandManager& m)
|
||
|
: commandManager (m),
|
||
|
innerCommandTarget (commandManager)
|
||
|
{
|
||
|
commandManager.registerAllCommandsForTarget (this);
|
||
|
|
||
|
addAndMakeVisible (innerCommandTarget);
|
||
|
}
|
||
|
|
||
|
void resized() override
|
||
|
{
|
||
|
innerCommandTarget.setBounds (getLocalBounds().reduced (50));
|
||
|
}
|
||
|
|
||
|
void paint (Graphics& g) override
|
||
|
{
|
||
|
g.fillAll (currentColour);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
ApplicationCommandTarget* getNextCommandTarget() override
|
||
|
{
|
||
|
return &innerCommandTarget;
|
||
|
}
|
||
|
|
||
|
void getAllCommands (Array<CommandID>& c) override
|
||
|
{
|
||
|
Array<CommandID> commands { CommandIDs::outerColourRed,
|
||
|
CommandIDs::outerColourGreen,
|
||
|
CommandIDs::outerColourBlue };
|
||
|
|
||
|
c.addArray (commands);
|
||
|
}
|
||
|
|
||
|
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
|
||
|
{
|
||
|
switch (commandID)
|
||
|
{
|
||
|
case CommandIDs::outerColourRed:
|
||
|
result.setInfo ("Red", "Sets the outer colour to red", "Outer", 0);
|
||
|
result.setTicked (currentColour == Colours::red);
|
||
|
result.addDefaultKeypress ('r', ModifierKeys::commandModifier);
|
||
|
break;
|
||
|
case CommandIDs::outerColourGreen:
|
||
|
result.setInfo ("Green", "Sets the outer colour to green", "Outer", 0);
|
||
|
result.setTicked (currentColour == Colours::green);
|
||
|
result.addDefaultKeypress ('g', ModifierKeys::commandModifier);
|
||
|
break;
|
||
|
case CommandIDs::outerColourBlue:
|
||
|
result.setInfo ("Blue", "Sets the outer colour to blue", "Outer", 0);
|
||
|
result.setTicked (currentColour == Colours::blue);
|
||
|
result.addDefaultKeypress ('b', ModifierKeys::commandModifier);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool perform (const InvocationInfo& info) override
|
||
|
{
|
||
|
switch (info.commandID)
|
||
|
{
|
||
|
case CommandIDs::outerColourRed:
|
||
|
currentColour = Colours::red;
|
||
|
break;
|
||
|
case CommandIDs::outerColourGreen:
|
||
|
currentColour = Colours::green;
|
||
|
break;
|
||
|
case CommandIDs::outerColourBlue:
|
||
|
currentColour = Colours::blue;
|
||
|
break;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
repaint();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
//==============================================================================
|
||
|
/**
|
||
|
Command messages that aren't handled in the OuterCommandTarget will be passed
|
||
|
to this class to respond to.
|
||
|
*/
|
||
|
struct InnerCommandTarget : public Component,
|
||
|
public ApplicationCommandTarget
|
||
|
{
|
||
|
InnerCommandTarget (ApplicationCommandManager& m)
|
||
|
: commandManager (m)
|
||
|
{
|
||
|
commandManager.registerAllCommandsForTarget (this);
|
||
|
}
|
||
|
|
||
|
void paint (Graphics& g) override
|
||
|
{
|
||
|
g.fillAll (currentColour);
|
||
|
}
|
||
|
|
||
|
//==============================================================================
|
||
|
ApplicationCommandTarget* getNextCommandTarget() override
|
||
|
{
|
||
|
// this will return the next parent component that is an ApplicationCommandTarget
|
||
|
return findFirstTargetParentComponent();
|
||
|
}
|
||
|
|
||
|
void getAllCommands (Array<CommandID>& c) override
|
||
|
{
|
||
|
Array<CommandID> commands { CommandIDs::innerColourRed,
|
||
|
CommandIDs::innerColourGreen,
|
||
|
CommandIDs::innerColourBlue };
|
||
|
|
||
|
c.addArray (commands);
|
||
|
}
|
||
|
|
||
|
void getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) override
|
||
|
{
|
||
|
switch (commandID)
|
||
|
{
|
||
|
case CommandIDs::innerColourRed:
|
||
|
result.setInfo ("Red", "Sets the inner colour to red", "Inner", 0);
|
||
|
result.setTicked (currentColour == Colours::red);
|
||
|
result.addDefaultKeypress ('r', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
|
||
|
break;
|
||
|
case CommandIDs::innerColourGreen:
|
||
|
result.setInfo ("Green", "Sets the inner colour to green", "Inner", 0);
|
||
|
result.setTicked (currentColour == Colours::green);
|
||
|
result.addDefaultKeypress ('g', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
|
||
|
break;
|
||
|
case CommandIDs::innerColourBlue:
|
||
|
result.setInfo ("Blue", "Sets the inner colour to blue", "Inner", 0);
|
||
|
result.setTicked (currentColour == Colours::blue);
|
||
|
result.addDefaultKeypress ('b', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool perform (const InvocationInfo& info) override
|
||
|
{
|
||
|
switch (info.commandID)
|
||
|
{
|
||
|
case CommandIDs::innerColourRed:
|
||
|
currentColour = Colours::red;
|
||
|
break;
|
||
|
case CommandIDs::innerColourGreen:
|
||
|
currentColour = Colours::green;
|
||
|
break;
|
||
|
case CommandIDs::innerColourBlue:
|
||
|
currentColour = Colours::blue;
|
||
|
break;
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
repaint();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
ApplicationCommandManager& commandManager;
|
||
|
|
||
|
Colour currentColour { Colours::blue };
|
||
|
};
|
||
|
|
||
|
ApplicationCommandManager& commandManager;
|
||
|
InnerCommandTarget innerCommandTarget;
|
||
|
|
||
|
Colour currentColour { Colours::red };
|
||
|
};
|
||
|
|
||
|
OuterCommandTarget outerCommandTarget { commandManager };
|
||
|
|
||
|
//==============================================================================
|
||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MenusDemo)
|
||
|
};
|