Listbox with custom components


#1

Hello. i’ve checked the forum but haven’t undestood how to implement it.

Assume I have a component with a checkbox, label and a button inside one. And I want to create a list box with such items
I guess that I need to use class derived ListBoxModel, but don’t know how to do it the right way. Can anybody post a code sample please?


#2

Prerequisite: Keep your data separate from your custom component. Your data could contain thousands of items, the ListBox will only ask you to allocate, update or delete the custom components it needs to display whats currently on the screen.

To provide the ListBox with your custom component you need to implement refreshComponentForRow:

Component* refreshComponentForRow(int rowNumber, bool isRowSelected, Component* existingComponentToUpdate)
{
	CustomComponent *row = static_cast<CustomComponent*>(existingComponentToUpdate);

	if(rowNumber < m_data->size())
	{
		if(!row)
			row = new CustomComponent(node);

		/* Update all properties of your custom component with the data for the current row  */
		row.nameLabel.setText(m_data[rowNumber].getName());
	}
	else
	{
		// Nothing to display, free the custom component
		delete existingComponentToUpdate;
		row = nullptr;
	}

	return row;
}

#3

Ok. Should it be like

[code]
class ListBoxItemComponent : public Component
{
Label lb;
TextButton tb;
ToggleButton togBut;
Component* refreshComponentForRow(int rowNumber, bool isRowSelected, Component* existingComponentToUpdate)
{
CustomComponent row = static_cast<CustomComponent>(existingComponentToUpdate);

   if(rowNumber < m_data->size())
   {
      if(!row)
         row = new CustomComponent(node);

      /* Update all properties of your custom component with the data for the current row  */
      row.nameLabel.setText(m_data[rowNumber].getName());
   }
   else
   {
      // Nothing to display, free the custom component
      delete existingComponentToUpdate;
      row = nullptr;
   }

   return row;
}

};[/code]

Questions:
Where should I use buttons and label
Should I create a ListBoxModel ?


#4

No, you should create a separate custom Component subclass and a ListBox subclass. Implement the resized method to position your controls. This is the custom component class declaration:

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

	void resized();

	Label& getLb() { return m_lb; }
	ToggleButton& getTogBut() { return m_togBut; }
	TextButton& getTb() { return m_tb; }
	
private:
	Label m_lb;
    TextButton tb;
    ToggleButton m_togBut;
};

You need to store your data somewhere separate:

class Data
{
public:
	Data() {}

	String getName() { return m_name; }

private:
	String m_name;
};

refreshComponentForRow is part of your ListBox subclass:

class CustomListBox
: public ListBox
, public ListBoxModel
, 
{
public:
	CustomListBox() : ListBox("Custom ListBox", this)
	{
	}

	int getNumRows();
	Component* refreshComponentForRow(int rowNumber, bool isRowSelected, Component* existingComponentToUpdate);

private:
	std::vector<Data> m_data;
};

#5

Thank you. It works!
But can you also explain what is the purpose to use setModel method of ListBox? Does it have advantages?


#6

If you do not want to subclass ListBox you could also inherit from ListBoxModel only. You provide an instance of that class to a standard ListBox using setModel(). In this example setModel was called implicitly during construction:

CustomListBox() : ListBox("Custom ListBox", this)

I personally find it convenient to subclass both ListBox and ListBoxModel because usually I do more than just providing the model. Changing colors or overwriting paint().


#7

Forgot to mention - I don’t inherit from ListBox, but only from Component and ListBoxModel
I keep a listbox pointer inside one:

class ProductsListComponent : public Component, public ListBoxModel { private: ScopedPointer<ListBox> m_productsListBox; public: Component * refreshComponentForRow(int row, bool isSelected, Component* existingComponentToUpdate); }
and I don’t provide a model explicitly nether by base constructor calling nor by setModel()

ProductsListComponent::ProductsListComponent() : m_productsListBox(nullptr) { ///... there is no any setModel() at all }

But it works as I want (buttons appear in every listbox item). It makes with refreshComponentForRow()
So what’s the model role here?
Thnx


#8

I can’t tell from the code you posted why it works for you. You need to provide more code if I should make a informed statement.


#9

Sorry, I’m mistaken. You’re right. I’m setting a model while calling constructor of ListBox member