TableListBoxTutorial How can I get the row changed


#1

Hi, in the TableListBoxTutorial I don’t find the way to have the “table” row changed.
I also don’t understand why the table is a tableListBoxModel ? Can it be possible to declare table as a tableListBox and apply the TableListBoxTutorial model ?
Cheers,
Seb (French user)


#2

Hi @nseaprotector, I’m a user too.
TableListBoxTutorial just a name of a class.
You can create your own “tableListBox” inherits component TableListBoxTutorial.
About " the way to have the “table” row changed" you can use this virtual function
virtual void cellDoubleClicked (int rowNumber, int columnId, const MouseEvent &)
Just override it to do whatever you want


#3

Thanks for your help. I guess my beginner’s problem, it is because I have a little of difficulty with the basic concept.
So is it the good way ?

class table : public TableTutorialComponent

{
public:

void 	selectedRowsChanged (int lastRowSelected) override
{
    Logger::writeToLog("Row Changed");
}

};


#4

Yes @nseaprotector, That is the good way :slight_smile:


#5

Or since, the TableTutorialComponent apparently is not a class in the JUCE library itself, just add selectedRowsChanged directly into that…? There should be no need to add another level of inheritance for that.


#6

Or since, the TableTutorialComponent apparently is not a class in the JUCE library itself, just add selectedRowsChanged directly into that…? There should be no need to add another level of inheritance for that.

Ok, I see what you mean, but how I get it in my mainComponent class ? I need a listener ?


#7

The Listener classes are so called Interfaces. You use them using multiple inheritance, simply add it to any class where you want to be notified to the list of classes you are deriving from.
So your mainComponent can be a Listener as well, depending on what you want to do.


#8

In the tableCustom Class :
void selectedRowsChanged ( int lastRowSelected ) override
{
selectedRowChanged (lastRowSelected);
}
void selectedRowChanged (int rowSelected)
{

}

This is the mainComponent of the tutorial:
class MainComponent : public Component
{
public:
//==============================================================================
MainComponent()
{
addAndMakeVisible (table);

    setSize (800, 600);
}

~MainComponent() {}

void paint (Graphics& g) override
{
    g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
}

void resized() override
{
    table.setBounds (getLocalBounds());
}

private:
//==============================================================================
TableTutorialComponent table;


#9

I guess the good way is:

void selectedRowsChanged	(	int 	lastRowSelected	) override
{
    rowSelected = lastRowSelected;
}

int selectedRowChanged ()
{
    return rowSelected;
}

private:
int rowSelected;
But i don’t know when there’s a change in my main class ?
For the moment I can have it in the main with:
table.selectedRowChanged();
I think, I need a listener to know when there’s a change in my table ?


#10

Yes, using Listeners would be great, but JUCE does not currently provide an easy way to broadcast something like an int. (I don’t consider using Values or ValueTrees that easy for a simple task like this.) At the moment you could solve your problem by passing in a pointer or reference of your MainComponent into your tablelistbox class, so you could access methods or public variables of that when the row changes. That however isn’t a great design to use because it ties your MainComponent and the TableListBoxTutorial classes together.

Another option would be to make your tablelistbox thing a Changebroadcaster and your MainComponent a subclass of ChangeListener. Changebroadcaster however only supports sending a notification that something happened but not what was changed to what. So in your ChangeListener callback you would need to fetch the changed table row value separately in some manner.

Yet another option would be to add a std::function<void(int)> member into your table class. The MainComponent would set a callback on that is called by the table class when the row changes.


#11

Yes, thanks a lot ! I finally use static values and changeBroadcaster with success ! :sunny:
for exemple in my tableModel :

                    void listBoxItemDoubleClicked	(int 	row,const MouseEvent &)	override
            {
                doubleClickBank = true; // to know what change it is
                rowSelectedBank = row; //to send the row
                sendChangeMessage(); //to signal a change
                Logger::writeToLog("table model double clic");
            } 

And in my Main:

               void changeListenerCallback (ChangeBroadcaster* source) override
        {
        if(doubleClickBank) //Is it the double click change ?
        {
            doubleClickBank = false; //reset the change identifier
            Logger::writeToLog("Change double click" + String(rowSelectedBank); 
        }

#12

Are doubleClickBank and rowSelectedBank global variables? You shouldn’t really be using those at all. You should figure out some other way to pass the information that a double click happened in the tablelistbox. Even making the tablelistbox know about your maincomponent via a reference or pointer would be better than global variables.

Some ways you can do it, roughly in order of best to worst :

  • A proper broadcaster/listener mechanism with exactly the callbacks and data types that are needed. The most difficult and the most amount of code to implement.
  • Use std::function with a signature that can deliver the needed information. In your case, something like std::function<void(TableBoxEvent, int)> if you need to know about the what happened in the tablebox in addition to the row that was changed. You would need to add an enum TableBoxEvent to your code that has the possible event types like Click and DoubleClick. If you just need the row, std::function<void(int)> is enough.
  • Make the tablelistbox have a public Value member that is updated when clicks, doubleclicks etc happen in the tablelistbox. Make your maincomponent a Value::Listener. Values can by default contain anything a Juce var variant can contain.
  • Have the information about what happened available from a public variable or method in the tablelistbox that is a ChangeBroadcaster. Then in your changeListenerCallback cast the ChangeBroadcaster to your tablelistbox so you can access those public variables or methods.
  • Make your tablelistbox know about your maincomponent via a pointer or reference by passing the pointer through the constructor of the tablelistbox. Then the tablelistbox can directly call the public methods of your maincomponent or access its public variables.
  • Use global or static variables. Just don’t.

Hmm, maybe I should do an example project that uses all those…


#13

doubleClickBank and rowSelectBank are global static in my project. It work, and I don’t understand why it’s a bad way ? Perhaps for the POO, I guess ?

I see what you mean in the best worst, I’m a beginner in C++ but I don’t found an exemple to understand how implement this correctly. (Sorry if my english is not good enough)


#14

Global variables are considered bad practice in programming because any code anywhere can be changing them. Your solution may work for now but changes can become difficult to do in the future, for example if you need multiple tablelistboxes.


#15

Wow, this was a lot of work, but here is my project that demonstrates the various techniques, hopefully with enough comments to explain what is going on :sweat: :


#16

Whaou, you are extraordinary ! Amazing ! Thanks a lot !
I guess it can help a lot of dummies like me <3
Really helpful.
I’m gonna learn it to make a better code in my midi Synth editor for the yamaha SY77 and for my next project. Thanks a lot.


#17

It was kind of interesting to do. So many ways to do the same thing… :wink: And I think at least a couple are even missing.