I try to draw button in a ListBox. But it doesn’t show. Area prepared for it, when I use
removeFromLeft, but no button shown. Other elements like AudioThumbnail or rectangles are drawn.
It shows if I do not use ListBox.
class TracksListModel : public ListBoxModel
virtual int getNumRows() override;
void paintListBoxItem (int rowNumber, Graphics& g,
int width, int height, bool rowIsSelected) override
tracks[rowNumber]->setBounds(0, rowNumber * height, width, height);
void add(TrackComponent *track);
class TrackComponent : public Component
void paint(Graphics &g) override
auto area = getLocalBounds();
auto top = area.removeFromTop(20);
void paintIfFileLoaded(Graphics& g, Rectangle<int> area)
Is there any mistake I made?
This is what I have now:
Ok, if I change this:
tracks[rowNumber]->paintEntireComponent (g, false);
But there is no interaction with button on hover, and on click.
Any ideas how to make it work?
The rule written in stone is: never alter the UI from within your paint methods.
In your code, for example, don’t call setBounds() and neither addAndMakeVisible() from within paint().
If you need to react to changes in position or size, override the moved() or the resized() methods of Component instead.
But what if I want to react to mouse events?
If you want to react to mouse events, you capture the event in its callback, change whatever you need to change, and call repaint(). The painting mechanism is asynchronous. You call repaint() on a component to mark it as dirty, so that soon, but later, the message loop will call paint() on it. You never call paint() directly, and you never call things from paint() that would call repaint(). Think about it -paint() calls something that calls repaint(), that makes paint() be called later, that calls something that calls repaint(), that makes paint() be called later… and so on. paint() should only draw whatever is in the component at any moment, without changing its content.
In addition to what @kamedin said, also note that
Component, which is the base class of all JUCE widgets, is also derived from
MouseListener which has all sorts of callbacks for mouse events.
If you want to react to mouse events on your own class derived from
Component, you can simply override those callbacks coming from
If you want to react to mouse events coming also from other widgets, you can register yours as the listener for those events too via the other
addMouseListener() method, and inside the callbacks you can determine which
Component received the event by looking at the
eventComponent member of their
Actually I’m not want to react, I need reaction from children components as it work if I just add TextButton to top MainComponent.
Yes, I tried, but
mouseMove is not triggered in ListBox’s components.
I see your point. I can devise two approaches
Add a mouse listener for your ListBox, catching also events for child components (it’s an argument given to addMouseListener() ) then every time you receive a mouse event you can find which row component it affects using ListBox::getRowContainingPosition() and ListBox::getComponentForRowNumber(), then act accordingly
Depending on the amount of mouse interaction that you want to implement, the approach above can become tricky, in which case I’d abandon the idea of having tracks as rows of a ListBox, and implement them as Components that you directly manage inside a parent panel that serves as their “container”, which you can embed in a Viewport to make it scrollable
I think I will try second approach you suggested.
Thank you for your help.