Hi,
We’re hitting an assert in ListBox::checkModelPtrIsValid() (juce_ListBox.cpp:72) when a TableListBox gets deleted while the accessibility focus is inside one of its cell components. It happens on 8.0.15, and I’ve just checked that it still happens on develop (1ffaca0).
TableListBox is its own ListBoxModel (private base), and the constructor points the ListBox at that base with assignModelPtr (this). The destructor is empty, so the ListBoxModel base, and its sharedState with it, is destroyed before the ListBox base. ~ListBox() then resets the viewport, which deletes the rows and the cell components.
When a cell component’s AccessibilityHandler is destroyed, it calls giveAwayFocus() → hasFocus (true) → isParentOf (currentlyFocusedHandler). If the focused handler is another component in the table, walking up from it goes through RowAccessibilityHandler::getCurrentState(), and that calls owner.getListBoxModel(). model is still non-null but weakModelPtr has expired, so the assert fires. If you continue, getNumRows() is called on the dead base, and in my test that ends with “Pure virtual function called!”.
Stack (trimmed):
juce::ListBox::checkModelPtrIsValid() const juce_ListBox.cpp:72
juce::ListBox::getListBoxModel() const juce_ListBox.h:236
ListBox::RowComponent::RowAccessibilityHandler::getCurrentState juce_ListBox.cpp:263
juce::AccessibilityHandler::isIgnored()
juce::getUnignoredAncestor(…)
juce::AccessibilityHandler::getParent()
juce::AccessibilityHandler::isParentOf(…)
juce::AccessibilityHandler::hasFocus(true)
juce::AccessibilityHandler::giveAwayFocus()
juce::AccessibilityHandler::~AccessibilityHandler()
… ~Label … ~Slider
juce::TableListBox::RowComp::~RowComp()
juce::ListBox::RowComponent::~RowComponent()
juce::ListBox::ListViewport::~ListViewport()
juce::ListBox::~ListBox()
juce::TableListBox::~TableListBox()
In practice: click a Slider inside a table cell, then delete the table. Our model stays alive the whole time. The problem is the internal ListBoxModel base, not the user model.
Minimal repro attached (TableListBoxRepro.cpp, a single-file GUI app that only needs juce_gui_basics). Run it as a Debug build. After the window opens, it resizes the window so that the slider’s text box Label gets its accessibility handler (those are created lazily). Then it gives the focus to the slider in row 1 and deletes the table while the model is still alive. You can also run it with --manual: click a slider, then click “Destroy table”.
The fix we’re using locally:
TableListBox::~TableListBox()
{
ListBox::assignModelPtr (nullptr);
}
With a null model, getCurrentState() skips the getNumRows() check, and model and weakModelPtr stay in sync. That fixes the repro. A more general option would be for ~ListBox() to give away the accessibility focus before it resets the viewport, but I haven’t tried that.
Thanks!
TableListBoxRepro.cpp (4.2 KB)