ListBox correct implementation


#1

I am trying to create a sample HelloWorld app using ListBox but the ListBoxModel member of paintListBoxItem is not drawing any of the rows. Can someone help me understand what I am missing? I thought that the correct way to do this is to create a subclass of ListBoxModel, but I guess I’m not doing it correctly. Any help greatly appreciated.

HelloWorldComponent.cpp:

#include "HelloWorldComponent.h"
class MyListBoxModel : public ListBoxModel
{
public:
        MyListBoxModel();
        ~MyListBoxModel();

        void paintListBoxItem(int rowNumber, Graphics& g, int width, int height, bool rowIsSelected)
        {
                if(rowIsSelected) {
                        g.fillAll(Colours::lightblue);
                }
                g.setColour(Colours::black);
                g.setFont(height * 0.7f);
                g.drawText("Row Number " + String(rowNumber + 1), 5, 0, width, height, Justification::centredLeft, true);

        }
        int getNumRows()
        {
                return 30;
        }
};
MyListBoxModel* blah;

HelloWorldComponent::HelloWorldComponent()
		: lBox(0)
		
{
  	lBox = new ListBox("Test",blah);
	lBox->setModel(blah);
        addAndMakeVisible(lBox);

	setSize (600, 400);
}
void HelloWorldComponent::resized()
{
        lBox->setBounds (proportionOfWidth (0.1893f), proportionOfHeight (0.2076f), proportionOfWidth (0.6502f), proportionOfHeight (0.5741f));
}

HelloWorldComponent.h:

#ifndef HELLOWORLDCOMPONENT_H 
#define HELLOWORLDCOMPONENT_H

#include "includes.h"
class HelloWorldComponent  : public Component, 
                             public ButtonListener
			     //public ListBox,
		 	     //public ListBoxModel
			
{
public:
	HelloWorldComponent ();
	~HelloWorldComponent();

	void paint (Graphics& g);
	void resized();
	void buttonClicked (Button* buttonThatWasClicked);
	//void paintListBoxItem(int rowNumber, Graphics& g, int width, int height, bool rowIsSelected);
	//int getNumRows();

	juce_UseDebuggingNewOperator

private:
	ListBox* lBox;

	HelloWorldComponent (const HelloWorldComponent&);
	const HelloWorldComponent& operator= (const HelloWorldComponent&);
};
#endif

However, when I create a class object that inherits both ListBox and ListBoxModel (as delineated below), everything works fine. I copied this implementation from the Juce Demo application. This is I assume because I am passing “this” pointer to the setModel. This code below works, but I wanted the main content component to be able to control the calls to paintListBoxItem. That is why I am trying for the first implementation example.

HelloWorldComponent2.cpp:

#include "HelloWorldComponent.h"

class MyListBox : public ListBox,
                           public ListBoxModel
{
public:
        MyListBox()
                : ListBox ("Testing", 0)
        {
                setModel(this);
        }
        ~MyListBox()
        {

        }
        int getNumRows()
        {
                return numRows;
        }
        void paintListBoxItem(int rowNumber, Graphics& g, int width, int height, bool rowIsSelected)
        {
		if(rowIsSelected) {
			g.fillAll(Colours::lightblue);
		}
		g.setColour(Colours::black);
		g.setFont(height * 0.7f);
		g.drawText("Row Number " + String(rowNumber + 1), 5, 0, width, height, Justification::centredLeft, true);
		g.drawText(text, 5, 0, width, height, Justification::centredLeft, true);

        }
        void paint(Graphics& g)
        {
                g.fillAll(Colours::white.withAlpha(0.7f));

        }
	void listBoxItemClicked(int row, const MouseEvent& e)
	{
		val = row;

	}
};
MyListBox* lbox;


HelloWorldComponent::HelloWorldComponent()
{

	lbox = new MyListBox();
	addAndMakeVisible(lbox);


}

HelloWorldComponent::~HelloWorldComponent()
{
	deleteAllChildren();

}

void HelloWorldComponent::paint (Graphics& g)
{
	g.fillAll (Colour (0xffa18a8a));


}

void HelloWorldComponent::resized()
{
	lbox->setBounds (proportionOfWidth (0.1893f), proportionOfHeight (0.2076f), proportionOfWidth (0.6502f), proportionOfHeight (0.5741f));
}
void HelloWorldComponent::buttonClicked (Button* buttonThatWasClicked)
{


}

HelloWorldComponent2.h:

#ifndef HELLOWORLDCOMPONENT_H 
#define HELLOWORLDCOMPONENT_H

#include "includes.h"

class HelloWorldComponent  : public Component,
                             public ButtonListener
			
{
public:
	HelloWorldComponent ();
	~HelloWorldComponent();

	void paint (Graphics& g);
	void resized();
	void buttonClicked (Button* buttonThatWasClicked);

	juce_UseDebuggingNewOperator

private:

	HelloWorldComponent (const HelloWorldComponent&);
	const HelloWorldComponent& operator= (const HelloWorldComponent&);
};
#endif

#2

when you call setModel (blah), it looks like you’re just giving it an uninitialised pointer (unless you’ve not posted the important bit of code that actually creates your model object??)


#3

I didn’t implement the new model object creation. I’ve added it like the following: “blah = new MyListBoxModel();”

However, the rows are still not painting in the ListBox, for some reason.

Here is how it is implemented in the constructor for HelloWorldComponent now:

HelloWorldComponent::HelloWorldComponent()
                : lBox(0),
                  blah(0)

{
        blah = new MyListBoxModel();
        lBox = new ListBox("Test",blah);
        lBox->setModel(blah);
        addAndMakeVisible(lBox);
        setSize (600, 400);

}

HelloWorldComponent.h:

#ifndef HELLOWORLDCOMPONENT_H 
#define HELLOWORLDCOMPONENT_H

#include "includes.h"
#include "MyListBoxModel.h"

class HelloWorldComponent  : public Component, 
                             public ButtonListener
			
{
public:
	HelloWorldComponent ();
	~HelloWorldComponent();

	void paint (Graphics& g);
	void resized();
	void buttonClicked (Button* buttonThatWasClicked);

	juce_UseDebuggingNewOperator

private:
	ListBox* lBox;
	MyListBoxModel* blah;

	HelloWorldComponent (const HelloWorldComponent&);
	const HelloWorldComponent& operator= (const HelloWorldComponent&);
};
#endif

I had to create a new .h file and cpp files, as the following:

MyListBoxModel.cpp:

#include "includes.h"
#include "MyListBoxModel.h"

MyListBoxModel::MyListBoxModel()
{

}
MyListBoxModel::~MyListBoxModel()
{

}
void MyListBoxModel::paintListBoxItem(int rowNumber, Graphics& g, int width, int height, bool rowIsSelected)
{
                if(rowIsSelected) {
                        g.fillAll(Colours::lightblue);
                }
                g.setColour(Colours::black);
                g.setFont(height * 0.7f);
                g.drawText("Row Number " + String(rowNumber + 1), 5, 0, width, height, Justification::centredLeft, true);

}

int MyListBoxModel::getNumRows()
{
	return 30;
}

MyListBoxModel.h:

#include "includes.h"

class MyListBoxModel : public ListBoxModel
{
public:
        MyListBoxModel();
        ~MyListBoxModel();
        
	void paintListBoxItem(int rowNumber, Graphics& g, int width, int height, bool rowIsSelected);
        int getNumRows();

};

#4

I don’t know. Use your debugger and step through everything, watching what happens, and you’ll probably spot what’s going wrong.


#5

Will do. Thanks for confirming that it looks correct.


#6

To resolve this issue, I ended up dropping trying to get the first implementation working, and just kept with the original one that was working, from the Juce Demo project example. I created two separate ListBox objects within the component, and then made them visible or not based on where the Component was, and what I needed it to do. But one thing I did differently - I pass a numeric parameter to the constructor for the ListBox to tell it how to do some custom things within that ListBox.

Like so, basically:

MyListBox* lbox;
MyListBox* lbox2;

and:

lbox = new MyListBox(0);
addAndMakeVisible(lbox);
lbox2 = new MyListBox(1);