Popup Menu from Toolbar


#1

Juce Friends,
Hello from Texas, lots of posts by me, eh? :slight_smile:

I’m trying to create a popup menu when you click an item on the toolbar. I don’t know how to reference the toolbar button as a button listener, so I’m kind of lost.

My two toolbar buttons are being created like this:

        ToolbarItemComponent* createItem (const int itemId)
        {
            switch (itemId)
            {
            case infoButton:
                return new ToolbarButton (itemId, T("info"), Drawable::createFromImageData (BinaryData::letterI_png, BinaryData::letterI_pngSize), 0);

            case logoutButton:
                return new ToolbarButton (itemId, T("logout"), Drawable::createFromImageData (BinaryData::letterL_png, BinaryData::letterL_pngSize), 0);

            default:
                break;
            }

            return 0;
        }

And here is my button clicked handler. Just don’t know how to link the two so that an item on the tool bar (ToolbarButton) will pop up a menu, and so forth.

void MainMenuComponent::buttonClicked (Button* buttonThatWasClicked)
{
    if (buttonThatWasClicked == dbL)
    {
        AuthComponent* theauth;
        theauth = new AuthComponent();
        addAndMakeVisible(theauth);

        theMainWindow->setName(T("Application Login Screen"));
        theMainWindow->setContentComponent(theauth);
        theMainWindow->setFullScreen(false);

    }
    if (buttonThatWasClicked == dbI)
    {
	PopupMenu m;
        m.addItem (1, T("About Application"));
	m.addSeparator();
        m.addItem (2, T("Themes"));
	m.addSeparator();
        m.addItem (3, T("Video Tutorials"));

        int result = m.show();

        if (result == 0) {
etc., etc.

Any help greatly appreciated.


#2

Just use addButtonListener to add the listener when you create the button!


#3

Thanks for the response. I don’t know exactly how to implement this though.

  1. The cpp file is creating the component and the ToolBar is added to the component. The cpp file includes ToolBar.h.
    ToolBar.h is where the Button Items are created. What exactly is the syntax for adding the ButtonListener, is it something like this below? I don’t know the object name of the Button to reference in the call to the AddButtonListener:
    ToolbarItemComponent* createItem (const int itemId)
    {
    switch (itemId)
    {
    case infoButton:
    return new ToolbarButton (itemId, T(“info”), Drawable::createFromImageData (BinaryData::letterI_png, BinaryData::letterI_pngSize), 0);
    addButtonListener(this);

  2. As I understand, you need to have the class inherit the button listener in order to use the AddButtonListener. When I try to do this with the ToolbarItemFactory class, I get a compile error. Can you please help? The top of my class looks like this.

class DemoToolbarItemFactory : public ToolbarItemFactory,
public ButtonListener
{
public:
DemoToolbarItemFactory() {}
~DemoToolbarItemFactory() {}


#4

You get a compile error? What compile error? Is it that you have a pure virtual method? When you subclass ButtonListener, you also need to implement the listen method. That acts as a ‘callback’ when the button gets pushed. Like:

void buttonClicked (Button* buttonClicked) { PopupMenu m; etc.

Do you have the juce docs link? Every time you use a new class, you should scan through it - it usually says how to use it, and often has a small example. http://www.rawmaterialsoftware.com/juce/api/classButtonListener.html

Bruce


#5

Thanks a million for your reply, Bruce. It has been helpful and a learning experience. Yes, I’ve been studying the documentation and juce amalgamated cpp and header files. I think I am close but I still don’t have it.

To answer your question, here are the compile errors:
In file included from MainMenuUI.cpp:6:
ToolBar.h: In member function ‘void DemoToolbarItemFactory::buttonClicked(juce::Button*)’:
ToolBar.h:14: error: comparison between distinct pointer types ‘juce::Button*’ and ‘juce::Drawable*’ lacks a cast
In file included from MainMenuUI.cpp:6:
ToolBar.h: In member function ‘virtual juce::ToolbarItemComponent* DemoToolbarItemFactory::createItem(int)’:
ToolBar.h:57: error: ‘class juce::Drawable’ has no member named ‘addButtonListener’

Here is the source code for ToolBar.h:

#include "includes.h"
Drawable* test;

class DemoToolbarItemFactory   : public ToolbarItemFactory
{
        public:
        DemoToolbarItemFactory() {}
        ~DemoToolbarItemFactory() {}


	void buttonClicked (Button* buttonClicked)
	{

		if(buttonClicked == test) {

			PopupMenu m;


		}

	}

        enum DemoToolbarItemIds
        {
            infoButton = 1,
            logoutButton = 2,
        };

        void getAllToolbarItemIds (Array <int>& ids)
        {
            ids.add (infoButton);
            ids.add (logoutButton);
            ids.add (separatorBarId);
            ids.add (spacerId);
            ids.add (flexibleSpacerId);
        }

        void getDefaultItemSet (Array <int>& ids)
        {
            ids.add (separatorBarId);
            ids.add (flexibleSpacerId);
            ids.add (flexibleSpacerId);
            ids.add (separatorBarId);
            ids.add (infoButton);
            ids.add (separatorBarId);
            ids.add (logoutButton);
            ids.add (separatorBarId);
        }

        ToolbarItemComponent* createItem (const int itemId)
        {
            switch (itemId)
            {
            case infoButton:
                //return new ToolbarButton (itemId, T("info"), Drawable::createFromImageData (BinaryData::letterI_png, BinaryData::letterI_pngSize), 0);
                return new ToolbarButton (itemId, T("info"), test, 0);
                test->addButtonListener (this);

            case logoutButton:
                return new ToolbarButton (itemId, T("logout"), Drawable::createFromImageData (BinaryData::letterL_png, BinaryData::letterL_pngSize), 0);

            default:
                break;
            }

            return 0;
        }
    private:

};

DemoToolbarItemFactory factory;

Based on my research, I know that the Toolbar is actually a button and I should be able to add a button listener. I just don’t know how to reference the name of the created button in the call to buttonClicked. A ToolbarButton is a drawable, I see, but I don’t know what the name of that toolbarbutton is when you use buttonClicked. I’m also not sure if I am using addButtonListener correctly. You see that I am trying to use “test” as the name of the button.

Thank you in advance. When I’m finished with this, I’m going to post example code for this so that future users can use this as an example on how to have popup menus take action on a toolbarbutton.

Jason


#6

Ok, Jason. There’s a number of shennanigans in your code. I’ll point you the ones that jump out at me, but I suspect you should take a good look at some more C++ books and try to really get your head around object oriented programming. Please excuse me amusing myself as I write this.

OK, so, from the top.
test is a pointer to a drawable. I hope this is there just to test compilation? In your code, that pointer doesn’t get initialized. Consider using a stack object, like:

Drawable test();

That will be safer - even for tests - especially for tests, since pointer problems don’t help track things down.

Then you’re declaring a class DemoToolbarItemFactory, subclassed from ToolbarItemFactory. ToolbarItemFactory doesn’t inherit anything, as far as I can tell. So, your new class will be able to call methods:
it declares,
that ToolbarItemFactory declares.

You’ve declared a ‘buttonClicked’ method. That is not a method of ToolbarItemFactory, so it exists only in DemoToolbarItemFactory. Only other code that knows about (has included the header for) your class will ever be able to call that. In essence, that means your code only.

You’ve overriden getAllToolbarItemIds, getDefaultItemSet and createItem. Any code that knows about ToolbarItemFactory’s (most of juce) will able to call them.

But the code in createItem gets a bit wiggy.

Here’s the usual structure of a switch"
switch (int blahBlah)
{
case 1:
dostuff
break;

case 2:
{
do stuff in a sub-section so you can use local variables
}
break;

case 3:
do more stuff
// Caution - fall-through. Mark well, since we drop down and also do the case 4 stuff
case 4:
do even more stuff
break;

case 5:
case 6:
apparently, there’s stuff left to do
break;

default:
stuff
break;
}
Note - every case has a break. In the one case I chose to not to break right after, I understand it will fall through. In your code, you chose to return right from the case. That’s OK, I suppose. I tend not to do that - I would put a toolbarbutton into a pointer and return it at the end - then I can also check if it worked/caught any cases and do something if it didn’t.

Returning early isn’t wrong, but not putting a break could well screw up the compiler and is bad juju.

But that case is also where it goes off the other rail.
return new ToolbarButton (itemId, T(“info”), test, 0);

This creates a Toolbar button with an unitialized pointer for a drawable. That’s not a blank pointer, that’s some random bits, left over from whatever else you’ve been doing on your computer. It might be (worst case) a memory address in your program’s legal space, but more likely it’s be cruft. Your commented out alternative:
return new ToolbarButton (itemId, T(“info”), Drawable::createFromImageData (BinaryData::letterI_png, BinaryData::letterI_pngSize), 0);
Is more likely to be happy. In that line, you have returned a button from the function. The function is now gone, kaput, shuffled off this mortal coil etc.

That means your coup-de-grace never even takes the stage. Most compilers would probably optimize is out, if they were willing to compile it.
test->addButtonListener (this);

OK, so this says:
using the instance pointed to be the pointer ‘test’, which is an object of type ‘Drawable’, execute the method addButtonListener, with the parameter of ‘this’, to whit a pointer to an instance of DemoToolbarItemFactory.
C++ is silly isn’t it? I’m not dim, but C++ was the hardest thing I ever had to learn.

So, putting aside the fact that test may well be an unitialized pointer, and the worst thing it could do then would be to try to actually do it:

  • Drawable is not a button, so doesn’t have an addButtonListener method.
  • DemoToolbarItemFactory is not a buttonlistener, so a pointer to one can’t be used as a parameter to addButtonListener anyway.

This is all the C++ compiler protecting you.

So, deep breath. Let me try to guess at what you actually need.

Pick a class to be your ‘controller’. That class should be able to access your data, or send a command to a class about the button press. Make that class - it could be one you already have, like the window, or your app inherit from buttonlistener.

When you make the factory, make sure it can access the controller class. Do, maybe, something like this:

[code]ToolbarItemComponent* createItem (const int itemId)
{
ToolbarItemComponent* newItem = NULL;
switch (itemID)
{
case 1:
newItem = new ToolbarButton (itemId, T(“info”), Drawable::createFromImageData (BinaryData::letterI_png, BinaryData::letterI_pngSize), 0);
break;
etc
}

if (newItem)
{
newItem-> addButtonListener (jController);
}

return newItem;
}[/code]

Now in your controller class, implement a buttonClicked method. In fact, if you don’t, since it inherits from buttonListener, the compiler will throw an error.

C++, believe it or not, is your friend.

Hope that helps,

Bruce


#7

Thanks Bruce. I don’t take your suggestions personally, I am here eager to learn, that is all that counts. By the way, the DemoToolbarItemFactory class I have implemented was taken from the juce demo source code for the Toolbar demo, in “WidgetsDemo.cpp.”

Yes, the pointer to a drawable was just test code. :slight_smile:

So after I posted, I figured out the usage of the ToobarButton in the case statement. Then I saw your reply. I am definitely making some good progress and should have this done soon. I will take all of your suggestions on creating a class. Here is what I did, and it seems to work so far.

I declared at the top of the .h file:
ToolbarButton* button1Info;

Then the implementation of creating the ToolbarButton in createItem:

    ToolbarItemComponent* createItem (const int itemId)
    {
        switch (itemId)
        {
        case infoButton:
            button1Info = new ToolbarButton (itemId, T("info"), Drawable::createFromImageData (BinaryData::letterI_png, BinaryData::letterI_pngSize), 0);
            return button1Info;

/* etc, etc, etc. */

This does allow me to reference ‘button1Info’ in buttonClicked, like this (and it does compile):

void buttonClicked (Button* buttonClicked)
{

            if(buttonClicked == button1Info) {

Now I need to figure out the rest of what you said. Thanks, the journey continues…

Jason


#8

Ok, cool. I’m not sure that holding a pointer to the button like that is the best approach though. What happens when you have 20 of them and you forget to set one.

I forgot to mention - did you know that toolbar buttons can trigger commands, like normal buttons? That way your button commands will just show up in a CommandManager, and you can also control when they are enabled.

Keep plugging!

Bruce


#9

Bruce,

What did you mean when you said, “Pick a class to be your ‘controller’. That class should be able to access your data, or send a command to a class about the button press. Make that class - it could be one you already have, like the window, or your app inherit from buttonlistener”, for the way I have implemented my ToolBar.h? Are you suggesting that I re-name the DemoToolbarItemFactory class and remove some of the functions within?

I added the ToolbarItemFactory class to inherit “ButtonListener” and “Component”, and it does compiled, but I get a runtime error of “Floating point exception.”

For my question above, maybe I am not even implementing the relation between the header and cpp file correctly.

My cpp file has access to the ToolBar.h data because it has an include directive of #include “ToolBar.h”.

Then the cpp file has the statements for creating the toolbar, like this:

// Create toolbar and image factory for ToolBarButton items
addAndMakeVisible (toolbar = new Toolbar());
toolbar->addDefaultItems(factory);

Then the ToolBar.h actually creates the menus and the functionality for the menu options is controlled from within ToolBar.h, or later from somewhere else if I want to add an ApplicationCommandManager. I’d first like to understand how to implement the menu this way within a toolbar, then add the ApplicationCommandManager later.

Here is my latest ToolBar.h:

#include "includes.h"

ToolbarButton* button1Info;
ToolbarButton* button2Logout;

class DemoToolbarItemFactory   : public ToolbarItemFactory,
				 public ButtonListener,
				 public Component
{
        public:
        DemoToolbarItemFactory() {}
        ~DemoToolbarItemFactory() {}


	void buttonClicked (Button* buttonClicked)
	{

		if(buttonClicked == button1Info) {

			PopupMenu m;

			m.addItem (1, T("About LAVA"));
        		m.addSeparator();
        		m.addItem (2, T("Themes"));
        		m.addSeparator();
        		m.addItem (3, T("Video Tutorials"));

        		int result = m.show();

        		if (result == 0) {



			} else if (result == 1) {
                		// user picked 'About LAVA'
                		AlertWindow::showMessageBox(AlertWindow::NoIcon, T("About LAVA"),T("LAVA version 1.00 Beta\nDeveloped by:  VIPER Lab, Sipera
 Systems"));
			} else if (result == 2) {
				// user picked Themes
				ThemeComponent* themes;
				themes = new ThemeComponent();
				addAndMakeVisible(themes);
			} else if (result == 3) {

				// user picked item 3

			}

		}

		if(buttonClicked == button2Logout) {


		}

	}

        enum DemoToolbarItemIds
        {
            infoButton = 1,
            logoutButton = 2,
        };

        void getAllToolbarItemIds (Array <int>& ids)
        {
            ids.add (infoButton);
            ids.add (logoutButton);
            ids.add (separatorBarId);
            ids.add (spacerId);
            ids.add (flexibleSpacerId);
        }

        void getDefaultItemSet (Array <int>& ids)
        {
            ids.add (separatorBarId);
            ids.add (flexibleSpacerId);
            ids.add (flexibleSpacerId);
            ids.add (separatorBarId);
            ids.add (infoButton);
            ids.add (separatorBarId);
            ids.add (logoutButton);
            ids.add (separatorBarId);
        }

        ToolbarItemComponent* createItem (const int itemId)
        {
            switch (itemId)
            {
            case infoButton:
		button1Info = new ToolbarButton (itemId, T("info"), Drawable::createFromImageData (BinaryData::letterI_png, BinaryData::letterI_pngSize), 0)
;
		button1Info->addButtonListener(this);
		return button1Info;

            case logoutButton:
		button2Logout = new ToolbarButton (itemId, T("logout"), Drawable::createFromImageData (BinaryData::letterL_png, BinaryData::letterL_pngSize)
, 0);
		return button2Logout;

            default:
                break;
            }

            return 0;
        }
    private:

};

DemoToolbarItemFactory factory;

#10

Look, dude, you really need to do some lower level thinking first. Ideally some reading.

You keep talking about what your files are doing. That doesn’t make sense. Your objects need to do the work.

I don’t see why your factory would be a component, unless you’re just trying random stuff to stop the errors? The factory has a job - making toolbar items. Why would it also deal with the button pushes? That might make more sense if you took your window, for instance, and make it the factory and listener. But what wl the button press do? Something connected with your application or document, right?

Anyway, take a think about your objects and responsiblities. It may seem quicker to just ‘get it working’ then go back, but Jules is a smart dude - if you follow some logic, things tend to work for you.

Bruce


#11

I understand. Just a bit frustrating and I appreciate your patience. I will research this and do some reading. I have a very good C++ book, the Deitel “C++ How to Program” book :-). I am definitely having trouble with the object-oriented aspect of C++ programming. Thanks for your help. I will post the final code once I have it resolved.

:oops:

Jason


#12

I’ve been banging my head on this problem, on how to correctly create a toolbar class and add a toolbar object, so that I can add a toolbar to any given component with 1 line of code. I’ve been banging my head on this, reading a good book on how to create classes, but now I’m stuck because I don’t know how this should work in juce.

I want to be able to add a toolbar to a given component, so I first created a new ToolbarWorker class. Problem is that the toolbar doesn’t show up in the component unless I do addAndMakeVisible() within the parent component. Yet the ToolbarWorker constructor is already doing addAndMakeVisible for the toolbar, why can’t this create the object.

Here is ToolbarWorker.ccp

#include "ToolbarWorker.h"

ToolbarWorker::ToolbarWorker()
	: toolbar(0)
{

	addAndMakeVisible(toolbar = new Toolbar());

}

ToolbarWorker::~ToolbarWorker()
{
	deleteAndZero(toolbar);

}

void ToolbarWorker::resized()
{

	toolbar->setBounds(0,0,getWidth(), 30);

}

ToolbarWorker.h:

#ifndef TOOLBARWORKER_H
#define TOOLBARWORKER_H
#include "includes.h"

class ToolbarWorker  : public Component
{
public:
	ToolbarWorker();
	~ToolbarWorker();

	void resized();

private:
	Toolbar* toolbar;
};
#endif

In the main component, what I’d like to be able to do is add a ToolbarWorker object that takes care of everything, like this:
ToolbarWorker* tb;
tb = new ToolbarWorker();

But this doesn’t have the result of making the toolbar visible.

I can add the Toolbar by doing something like this in the main component:
addAndMakeVisible(tb = new ToolbarWorker());

But I thought that this kind of defeats the purpose because I also have to add other things like:
tb->setBounds(0,0,getWidth(), 30);

which is already being done in ToolbarWorker.h and I also have to add the variables as private members in the header file for the main component.

Basically, is addAndMakeVisible() in the parent component the only way to do this? Or can I somehow get by with creating a ToolbarWorker object that actually makes itself visible in the parent, within its own constructor?

Please take it easy on me.


#13

Never mind my last question. I assume that the only way to make a component visible within the parent is to use a variation of addAndMakeVisible() within that parent component’s constructor.


#14

If you don’t make it in the parent i.e. if the parent doesn’t make it - remember that addAndMakeVisible has ‘add’ in it - it’s becoming the parent, then your toolbar doesn’t have a parent and can’t be viewed anywhere. Just creating your ToolBarWorker in the parent component does not make any links between them. Parentage, in the Component sense, is a Juce metaphor, and the way to set it is with the ‘add…’ functions.

Your ToolBarWorker is indeed making a ToolBar visible, but since it itself isn’t visible (nor does it have a parent) it’s irrelevant.

Are you trying to swim against the stream a bit maybe? If you go with the flow a bit it may be easier. You’re a lot closer. Trying to do things with one line of code is a bit Quixotic.

Bruce


#15

I finally have everything working. I finally have a working example of a Toolbar that has two icon buttons in it, where the icons popup menus and do different things.

Many thanks, gratitude, and props to Bruce Wheaton, for his help but also in pushing me to be better and learn. This really wasn’t too hard to implement once I took a step back, did some reading, and learned how the objects should interact with each other. It’s been quite a journey and I have to a admit that the object-oriented nature of C++ has been hard to grasp. Quite a bit different from C, which I have been working a lot with in the past.

So…I’m going to post my code under a new post, “Example Toolbar with buttons and popup menu”. I hope that this example code will help others and save some of them the pain that I have had to go through…then again, a little pain can sometimes help you grow.
:mrgreen: