The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

736 lines
25KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. static const Identifier tableColumnProperty { "_tableColumnId" };
  21. static const Identifier tableAccessiblePlaceholderProperty { "_accessiblePlaceholder" };
  22. class TableListBox::RowComp : public Component,
  23. public TooltipClient
  24. {
  25. public:
  26. RowComp (TableListBox& tlb) noexcept
  27. : owner (tlb)
  28. {
  29. setFocusContainerType (FocusContainerType::focusContainer);
  30. }
  31. void paint (Graphics& g) override
  32. {
  33. if (auto* tableModel = owner.getModel())
  34. {
  35. tableModel->paintRowBackground (g, row, getWidth(), getHeight(), isSelected);
  36. auto& headerComp = owner.getHeader();
  37. const auto numColumns = jmin ((int) columnComponents.size(), headerComp.getNumColumns (true));
  38. const auto clipBounds = g.getClipBounds();
  39. for (int i = 0; i < numColumns; ++i)
  40. {
  41. if (columnComponents[(size_t) i]->getProperties().contains (tableAccessiblePlaceholderProperty))
  42. {
  43. auto columnRect = headerComp.getColumnPosition (i).withHeight (getHeight());
  44. if (columnRect.getX() >= clipBounds.getRight())
  45. break;
  46. if (columnRect.getRight() > clipBounds.getX())
  47. {
  48. Graphics::ScopedSaveState ss (g);
  49. if (g.reduceClipRegion (columnRect))
  50. {
  51. g.setOrigin (columnRect.getX(), 0);
  52. tableModel->paintCell (g, row, headerComp.getColumnIdOfIndex (i, true),
  53. columnRect.getWidth(), columnRect.getHeight(), isSelected);
  54. }
  55. }
  56. }
  57. }
  58. }
  59. }
  60. void update (int newRow, bool isNowSelected)
  61. {
  62. jassert (newRow >= 0);
  63. if (newRow != row || isNowSelected != isSelected)
  64. {
  65. row = newRow;
  66. isSelected = isNowSelected;
  67. repaint();
  68. }
  69. auto* tableModel = owner.getModel();
  70. if (tableModel != nullptr && row < owner.getNumRows())
  71. {
  72. const ComponentDeleter deleter { columnForComponent };
  73. const auto numColumns = owner.getHeader().getNumColumns (true);
  74. while (numColumns < (int) columnComponents.size())
  75. columnComponents.pop_back();
  76. while ((int) columnComponents.size() < numColumns)
  77. columnComponents.emplace_back (nullptr, deleter);
  78. for (int i = 0; i < numColumns; ++i)
  79. {
  80. auto columnId = owner.getHeader().getColumnIdOfIndex (i, true);
  81. auto originalComp = std::move (columnComponents[(size_t) i]);
  82. auto oldCustomComp = originalComp != nullptr && ! originalComp->getProperties().contains (tableAccessiblePlaceholderProperty)
  83. ? std::move (originalComp)
  84. : std::unique_ptr<Component, ComponentDeleter> { nullptr, deleter };
  85. auto compToRefresh = oldCustomComp != nullptr && columnId == static_cast<int> (oldCustomComp->getProperties()[tableColumnProperty])
  86. ? std::move (oldCustomComp)
  87. : std::unique_ptr<Component, ComponentDeleter> { nullptr, deleter };
  88. columnForComponent.erase (compToRefresh.get());
  89. std::unique_ptr<Component, ComponentDeleter> newCustomComp { tableModel->refreshComponentForCell (row,
  90. columnId,
  91. isSelected,
  92. compToRefresh.release()),
  93. deleter };
  94. auto columnComp = [&]
  95. {
  96. // We got a result from refreshComponentForCell, so use that
  97. if (newCustomComp != nullptr)
  98. return std::move (newCustomComp);
  99. // There was already a placeholder component for this column
  100. if (originalComp != nullptr)
  101. return std::move (originalComp);
  102. // Create a new placeholder component to use
  103. std::unique_ptr<Component, ComponentDeleter> comp { new Component, deleter };
  104. comp->setInterceptsMouseClicks (false, false);
  105. comp->getProperties().set (tableAccessiblePlaceholderProperty, true);
  106. return comp;
  107. }();
  108. columnForComponent.emplace (columnComp.get(), i);
  109. // In order for navigation to work correctly on macOS, the number of child
  110. // accessibility elements on each row must match the number of header accessibility
  111. // elements.
  112. columnComp->setFocusContainerType (FocusContainerType::focusContainer);
  113. columnComp->getProperties().set (tableColumnProperty, columnId);
  114. addAndMakeVisible (*columnComp);
  115. columnComponents[(size_t) i] = std::move (columnComp);
  116. resizeCustomComp (i);
  117. }
  118. }
  119. else
  120. {
  121. columnComponents.clear();
  122. }
  123. }
  124. void resized() override
  125. {
  126. for (auto i = (int) columnComponents.size(); --i >= 0;)
  127. resizeCustomComp (i);
  128. }
  129. void resizeCustomComp (int index)
  130. {
  131. if (auto& c = columnComponents[(size_t) index])
  132. {
  133. c->setBounds (owner.getHeader()
  134. .getColumnPosition (index)
  135. .withY (0)
  136. .withHeight (getHeight()));
  137. }
  138. }
  139. void mouseDown (const MouseEvent& e) override
  140. {
  141. isDragging = false;
  142. selectRowOnMouseUp = false;
  143. if (isEnabled())
  144. {
  145. if (! isSelected)
  146. {
  147. owner.selectRowsBasedOnModifierKeys (row, e.mods, false);
  148. auto columnId = owner.getHeader().getColumnIdAtX (e.x);
  149. if (columnId != 0)
  150. if (auto* m = owner.getModel())
  151. m->cellClicked (row, columnId, e);
  152. }
  153. else
  154. {
  155. selectRowOnMouseUp = true;
  156. }
  157. }
  158. }
  159. void mouseDrag (const MouseEvent& e) override
  160. {
  161. if (isEnabled()
  162. && owner.getModel() != nullptr
  163. && e.mouseWasDraggedSinceMouseDown()
  164. && ! isDragging)
  165. {
  166. SparseSet<int> rowsToDrag;
  167. if (owner.selectOnMouseDown || owner.isRowSelected (row))
  168. rowsToDrag = owner.getSelectedRows();
  169. else
  170. rowsToDrag.addRange (Range<int>::withStartAndLength (row, 1));
  171. if (rowsToDrag.size() > 0)
  172. {
  173. auto dragDescription = owner.getModel()->getDragSourceDescription (rowsToDrag);
  174. if (! (dragDescription.isVoid() || (dragDescription.isString() && dragDescription.toString().isEmpty())))
  175. {
  176. isDragging = true;
  177. owner.startDragAndDrop (e, rowsToDrag, dragDescription, true);
  178. }
  179. }
  180. }
  181. }
  182. void mouseUp (const MouseEvent& e) override
  183. {
  184. if (selectRowOnMouseUp && e.mouseWasClicked() && isEnabled())
  185. {
  186. owner.selectRowsBasedOnModifierKeys (row, e.mods, true);
  187. auto columnId = owner.getHeader().getColumnIdAtX (e.x);
  188. if (columnId != 0)
  189. if (TableListBoxModel* m = owner.getModel())
  190. m->cellClicked (row, columnId, e);
  191. }
  192. }
  193. void mouseDoubleClick (const MouseEvent& e) override
  194. {
  195. auto columnId = owner.getHeader().getColumnIdAtX (e.x);
  196. if (columnId != 0)
  197. if (auto* m = owner.getModel())
  198. m->cellDoubleClicked (row, columnId, e);
  199. }
  200. String getTooltip() override
  201. {
  202. auto columnId = owner.getHeader().getColumnIdAtX (getMouseXYRelative().getX());
  203. if (columnId != 0)
  204. if (auto* m = owner.getModel())
  205. return m->getCellTooltip (row, columnId);
  206. return {};
  207. }
  208. Component* findChildComponentForColumn (int columnId) const
  209. {
  210. const auto index = (size_t) owner.getHeader().getIndexOfColumnId (columnId, true);
  211. if (isPositiveAndBelow (index, columnComponents.size()))
  212. return columnComponents[index].get();
  213. return nullptr;
  214. }
  215. int getColumnNumberOfComponent (const Component* comp) const
  216. {
  217. const auto iter = columnForComponent.find (comp);
  218. return iter != columnForComponent.cend() ? iter->second : -1;
  219. }
  220. std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
  221. {
  222. return std::make_unique<RowAccessibilityHandler> (*this);
  223. }
  224. //==============================================================================
  225. class RowAccessibilityHandler : public AccessibilityHandler
  226. {
  227. public:
  228. RowAccessibilityHandler (RowComp& rowComp)
  229. : AccessibilityHandler (rowComp,
  230. AccessibilityRole::row,
  231. getListRowAccessibilityActions (rowComp),
  232. { std::make_unique<RowComponentCellInterface> (*this) }),
  233. rowComponent (rowComp)
  234. {
  235. }
  236. String getTitle() const override
  237. {
  238. if (auto* m = rowComponent.owner.ListBox::model)
  239. return m->getNameForRow (rowComponent.row);
  240. return {};
  241. }
  242. String getHelp() const override { return rowComponent.getTooltip(); }
  243. AccessibleState getCurrentState() const override
  244. {
  245. if (auto* m = rowComponent.owner.getModel())
  246. if (rowComponent.row >= m->getNumRows())
  247. return AccessibleState().withIgnored();
  248. auto state = AccessibilityHandler::getCurrentState();
  249. if (rowComponent.owner.multipleSelection)
  250. state = state.withMultiSelectable();
  251. else
  252. state = state.withSelectable();
  253. if (rowComponent.isSelected)
  254. return state.withSelected();
  255. return state;
  256. }
  257. private:
  258. class RowComponentCellInterface : public AccessibilityCellInterface
  259. {
  260. public:
  261. RowComponentCellInterface (RowAccessibilityHandler& handler)
  262. : owner (handler)
  263. {
  264. }
  265. int getDisclosureLevel() const override { return 0; }
  266. const AccessibilityHandler* getTableHandler() const override { return owner.rowComponent.owner.getAccessibilityHandler(); }
  267. private:
  268. RowAccessibilityHandler& owner;
  269. };
  270. private:
  271. RowComp& rowComponent;
  272. };
  273. //==============================================================================
  274. class ComponentDeleter
  275. {
  276. public:
  277. explicit ComponentDeleter (std::map<const Component*, int>& locations)
  278. : columnForComponent (&locations) {}
  279. void operator() (Component* comp) const
  280. {
  281. columnForComponent->erase (comp);
  282. if (comp != nullptr)
  283. delete comp;
  284. }
  285. private:
  286. std::map<const Component*, int>* columnForComponent;
  287. };
  288. TableListBox& owner;
  289. std::map<const Component*, int> columnForComponent;
  290. std::vector<std::unique_ptr<Component, ComponentDeleter>> columnComponents;
  291. int row = -1;
  292. bool isSelected = false, isDragging = false, selectRowOnMouseUp = false;
  293. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RowComp)
  294. };
  295. //==============================================================================
  296. class TableListBox::Header : public TableHeaderComponent
  297. {
  298. public:
  299. Header (TableListBox& tlb) : owner (tlb) {}
  300. void addMenuItems (PopupMenu& menu, int columnIdClicked)
  301. {
  302. if (owner.isAutoSizeMenuOptionShown())
  303. {
  304. menu.addItem (autoSizeColumnId, TRANS("Auto-size this column"), columnIdClicked != 0);
  305. menu.addItem (autoSizeAllId, TRANS("Auto-size all columns"), owner.getHeader().getNumColumns (true) > 0);
  306. menu.addSeparator();
  307. }
  308. TableHeaderComponent::addMenuItems (menu, columnIdClicked);
  309. }
  310. void reactToMenuItem (int menuReturnId, int columnIdClicked)
  311. {
  312. switch (menuReturnId)
  313. {
  314. case autoSizeColumnId: owner.autoSizeColumn (columnIdClicked); break;
  315. case autoSizeAllId: owner.autoSizeAllColumns(); break;
  316. default: TableHeaderComponent::reactToMenuItem (menuReturnId, columnIdClicked); break;
  317. }
  318. }
  319. private:
  320. TableListBox& owner;
  321. enum { autoSizeColumnId = 0xf836743, autoSizeAllId = 0xf836744 };
  322. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Header)
  323. };
  324. //==============================================================================
  325. TableListBox::TableListBox (const String& name, TableListBoxModel* const m)
  326. : ListBox (name, nullptr), model (m)
  327. {
  328. ListBox::assignModelPtr (this);
  329. setHeader (std::make_unique<Header> (*this));
  330. }
  331. TableListBox::~TableListBox()
  332. {
  333. }
  334. void TableListBox::setModel (TableListBoxModel* newModel)
  335. {
  336. if (model != newModel)
  337. {
  338. model = newModel;
  339. updateContent();
  340. }
  341. }
  342. void TableListBox::setHeader (std::unique_ptr<TableHeaderComponent> newHeader)
  343. {
  344. if (newHeader == nullptr)
  345. {
  346. jassertfalse; // you need to supply a real header for a table!
  347. return;
  348. }
  349. Rectangle<int> newBounds (100, 28);
  350. if (header != nullptr)
  351. newBounds = header->getBounds();
  352. header = newHeader.get();
  353. header->setBounds (newBounds);
  354. setHeaderComponent (std::move (newHeader));
  355. header->addListener (this);
  356. }
  357. int TableListBox::getHeaderHeight() const noexcept
  358. {
  359. return header->getHeight();
  360. }
  361. void TableListBox::setHeaderHeight (int newHeight)
  362. {
  363. header->setSize (header->getWidth(), newHeight);
  364. resized();
  365. }
  366. void TableListBox::autoSizeColumn (int columnId)
  367. {
  368. auto width = model != nullptr ? model->getColumnAutoSizeWidth (columnId) : 0;
  369. if (width > 0)
  370. header->setColumnWidth (columnId, width);
  371. }
  372. void TableListBox::autoSizeAllColumns()
  373. {
  374. for (int i = 0; i < header->getNumColumns (true); ++i)
  375. autoSizeColumn (header->getColumnIdOfIndex (i, true));
  376. }
  377. void TableListBox::setAutoSizeMenuOptionShown (bool shouldBeShown) noexcept
  378. {
  379. autoSizeOptionsShown = shouldBeShown;
  380. }
  381. Rectangle<int> TableListBox::getCellPosition (int columnId, int rowNumber, bool relativeToComponentTopLeft) const
  382. {
  383. auto headerCell = header->getColumnPosition (header->getIndexOfColumnId (columnId, true));
  384. if (relativeToComponentTopLeft)
  385. headerCell.translate (header->getX(), 0);
  386. return getRowPosition (rowNumber, relativeToComponentTopLeft)
  387. .withX (headerCell.getX())
  388. .withWidth (headerCell.getWidth());
  389. }
  390. Component* TableListBox::getCellComponent (int columnId, int rowNumber) const
  391. {
  392. if (auto* rowComp = dynamic_cast<RowComp*> (getComponentForRowNumber (rowNumber)))
  393. return rowComp->findChildComponentForColumn (columnId);
  394. return nullptr;
  395. }
  396. void TableListBox::scrollToEnsureColumnIsOnscreen (int columnId)
  397. {
  398. auto& scrollbar = getHorizontalScrollBar();
  399. auto pos = header->getColumnPosition (header->getIndexOfColumnId (columnId, true));
  400. auto x = scrollbar.getCurrentRangeStart();
  401. auto w = scrollbar.getCurrentRangeSize();
  402. if (pos.getX() < x)
  403. x = pos.getX();
  404. else if (pos.getRight() > x + w)
  405. x += jmax (0.0, pos.getRight() - (x + w));
  406. scrollbar.setCurrentRangeStart (x);
  407. }
  408. int TableListBox::getNumRows()
  409. {
  410. return model != nullptr ? model->getNumRows() : 0;
  411. }
  412. void TableListBox::paintListBoxItem (int, Graphics&, int, int, bool)
  413. {
  414. }
  415. Component* TableListBox::refreshComponentForRow (int rowNumber, bool rowSelected, Component* existingComponentToUpdate)
  416. {
  417. if (existingComponentToUpdate == nullptr)
  418. existingComponentToUpdate = new RowComp (*this);
  419. static_cast<RowComp*> (existingComponentToUpdate)->update (rowNumber, rowSelected);
  420. return existingComponentToUpdate;
  421. }
  422. void TableListBox::selectedRowsChanged (int row)
  423. {
  424. if (model != nullptr)
  425. model->selectedRowsChanged (row);
  426. }
  427. void TableListBox::deleteKeyPressed (int row)
  428. {
  429. if (model != nullptr)
  430. model->deleteKeyPressed (row);
  431. }
  432. void TableListBox::returnKeyPressed (int row)
  433. {
  434. if (model != nullptr)
  435. model->returnKeyPressed (row);
  436. }
  437. void TableListBox::backgroundClicked (const MouseEvent& e)
  438. {
  439. if (model != nullptr)
  440. model->backgroundClicked (e);
  441. }
  442. void TableListBox::listWasScrolled()
  443. {
  444. if (model != nullptr)
  445. model->listWasScrolled();
  446. }
  447. void TableListBox::tableColumnsChanged (TableHeaderComponent*)
  448. {
  449. setMinimumContentWidth (header->getTotalWidth());
  450. repaint();
  451. updateColumnComponents();
  452. }
  453. void TableListBox::tableColumnsResized (TableHeaderComponent*)
  454. {
  455. setMinimumContentWidth (header->getTotalWidth());
  456. repaint();
  457. updateColumnComponents();
  458. }
  459. void TableListBox::tableSortOrderChanged (TableHeaderComponent*)
  460. {
  461. if (model != nullptr)
  462. model->sortOrderChanged (header->getSortColumnId(),
  463. header->isSortedForwards());
  464. }
  465. void TableListBox::tableColumnDraggingChanged (TableHeaderComponent*, int columnIdNowBeingDragged_)
  466. {
  467. columnIdNowBeingDragged = columnIdNowBeingDragged_;
  468. repaint();
  469. }
  470. void TableListBox::resized()
  471. {
  472. ListBox::resized();
  473. header->resizeAllColumnsToFit (getVisibleContentWidth());
  474. setMinimumContentWidth (header->getTotalWidth());
  475. }
  476. void TableListBox::updateColumnComponents() const
  477. {
  478. auto firstRow = getRowContainingPosition (0, 0);
  479. for (int i = firstRow + getNumRowsOnScreen() + 2; --i >= firstRow;)
  480. if (auto* rowComp = dynamic_cast<RowComp*> (getComponentForRowNumber (i)))
  481. rowComp->resized();
  482. }
  483. template <typename FindIndex>
  484. Optional<AccessibilityTableInterface::Span> findRecursively (const AccessibilityHandler& handler,
  485. Component* outermost,
  486. FindIndex&& findIndexOfComponent)
  487. {
  488. for (auto* comp = &handler.getComponent(); comp != outermost; comp = comp->getParentComponent())
  489. {
  490. const auto result = findIndexOfComponent (comp);
  491. if (result != -1)
  492. return AccessibilityTableInterface::Span { result, 1 };
  493. }
  494. return nullopt;
  495. }
  496. std::unique_ptr<AccessibilityHandler> TableListBox::createAccessibilityHandler()
  497. {
  498. class TableInterface : public AccessibilityTableInterface
  499. {
  500. public:
  501. explicit TableInterface (TableListBox& tableListBoxToWrap)
  502. : tableListBox (tableListBoxToWrap)
  503. {
  504. }
  505. int getNumRows() const override
  506. {
  507. if (auto* tableModel = tableListBox.getModel())
  508. return tableModel->getNumRows();
  509. return 0;
  510. }
  511. int getNumColumns() const override
  512. {
  513. return tableListBox.getHeader().getNumColumns (true);
  514. }
  515. const AccessibilityHandler* getRowHandler (int row) const override
  516. {
  517. if (isPositiveAndBelow (row, getNumRows()))
  518. if (auto* rowComp = tableListBox.getComponentForRowNumber (row))
  519. return rowComp->getAccessibilityHandler();
  520. return nullptr;
  521. }
  522. const AccessibilityHandler* getCellHandler (int row, int column) const override
  523. {
  524. if (isPositiveAndBelow (row, getNumRows()) && isPositiveAndBelow (column, getNumColumns()))
  525. if (auto* cellComponent = tableListBox.getCellComponent (tableListBox.getHeader().getColumnIdOfIndex (column, true), row))
  526. return cellComponent->getAccessibilityHandler();
  527. return nullptr;
  528. }
  529. const AccessibilityHandler* getHeaderHandler() const override
  530. {
  531. if (tableListBox.hasAccessibleHeaderComponent())
  532. return tableListBox.headerComponent->getAccessibilityHandler();
  533. return nullptr;
  534. }
  535. Optional<Span> getRowSpan (const AccessibilityHandler& handler) const override
  536. {
  537. if (tableListBox.isParentOf (&handler.getComponent()))
  538. return findRecursively (handler, &tableListBox, [&] (auto* c) { return tableListBox.getRowNumberOfComponent (c); });
  539. return nullopt;
  540. }
  541. Optional<Span> getColumnSpan (const AccessibilityHandler& handler) const override
  542. {
  543. if (const auto rowSpan = getRowSpan (handler))
  544. if (auto* rowComponent = dynamic_cast<RowComp*> (tableListBox.getComponentForRowNumber (rowSpan->begin)))
  545. return findRecursively (handler, &tableListBox, [&] (auto* c) { return rowComponent->getColumnNumberOfComponent (c); });
  546. return nullopt;
  547. }
  548. void showCell (const AccessibilityHandler& handler) const override
  549. {
  550. const auto row = getRowSpan (handler);
  551. const auto col = getColumnSpan (handler);
  552. if (row.hasValue() && col.hasValue())
  553. {
  554. tableListBox.scrollToEnsureRowIsOnscreen (row->begin);
  555. tableListBox.scrollToEnsureColumnIsOnscreen (col->begin);
  556. }
  557. }
  558. private:
  559. TableListBox& tableListBox;
  560. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableInterface)
  561. };
  562. return std::make_unique<AccessibilityHandler> (*this,
  563. AccessibilityRole::table,
  564. AccessibilityActions{},
  565. AccessibilityHandler::Interfaces { std::make_unique<TableInterface> (*this) });
  566. }
  567. //==============================================================================
  568. void TableListBoxModel::cellClicked (int, int, const MouseEvent&) {}
  569. void TableListBoxModel::cellDoubleClicked (int, int, const MouseEvent&) {}
  570. void TableListBoxModel::backgroundClicked (const MouseEvent&) {}
  571. void TableListBoxModel::sortOrderChanged (int, bool) {}
  572. int TableListBoxModel::getColumnAutoSizeWidth (int) { return 0; }
  573. void TableListBoxModel::selectedRowsChanged (int) {}
  574. void TableListBoxModel::deleteKeyPressed (int) {}
  575. void TableListBoxModel::returnKeyPressed (int) {}
  576. void TableListBoxModel::listWasScrolled() {}
  577. String TableListBoxModel::getCellTooltip (int /*rowNumber*/, int /*columnId*/) { return {}; }
  578. var TableListBoxModel::getDragSourceDescription (const SparseSet<int>&) { return {}; }
  579. Component* TableListBoxModel::refreshComponentForCell (int, int, bool, Component* existingComponentToUpdate)
  580. {
  581. ignoreUnused (existingComponentToUpdate);
  582. jassert (existingComponentToUpdate == nullptr); // indicates a failure in the code that recycles the components
  583. return nullptr;
  584. }
  585. } // namespace juce