From 70968d46c89ba4fd6d176d6e3a00c974337bd70d Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 13 Oct 2021 14:26:04 +0100 Subject: [PATCH] TreeView: Coalesce item position recalculation to fix performance issues with large trees --- .../juce_gui_basics/widgets/juce_TreeView.cpp | 115 +++++++++++------- 1 file changed, 73 insertions(+), 42 deletions(-) diff --git a/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/modules/juce_gui_basics/widgets/juce_TreeView.cpp index 6abd5359e8..55da678b54 100644 --- a/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -632,36 +632,20 @@ private: //============================================================================== class TreeView::TreeViewport : public Viewport, - private Timer + private AsyncUpdater { public: - TreeViewport() = default; - - void updateComponents (bool triggerResize) - { - if (auto* tvc = getContentComp()) - { - if (triggerResize) - tvc->resized(); - else - tvc->updateComponents(); - } - - repaint(); - } + explicit TreeViewport (TreeView& treeView) : owner (treeView) {} void visibleAreaChanged (const Rectangle& newVisibleArea) override { const auto hasScrolledSideways = (newVisibleArea.getX() != lastX); + lastX = newVisibleArea.getX(); updateComponents (hasScrolledSideways); - startTimer (50); - } - - ContentComponent* getContentComp() const noexcept - { - return static_cast (getViewedComponent()); + structureChanged = true; + triggerAsyncUpdate(); } bool keyPressed (const KeyPress& key) override @@ -673,22 +657,76 @@ public: return Viewport::keyPressed (key); } + ContentComponent* getContentComp() const noexcept + { + return static_cast (getViewedComponent()); + } + + enum class Async { yes, no }; + + void recalculatePositions (Async useAsyncUpdate) + { + needsRecalculating = true; + + if (useAsyncUpdate == Async::yes) + triggerAsyncUpdate(); + else + handleAsyncUpdate(); + } + private: std::unique_ptr createAccessibilityHandler() override { return createIgnoredAccessibilityHandler (*this); } - void timerCallback() override + void handleAsyncUpdate() override { - stopTimer(); - - if (auto* tree = getParentComponent()) - if (auto* handler = tree->getAccessibilityHandler()) + if (structureChanged) + { + if (auto* handler = owner.getAccessibilityHandler()) handler->notifyAccessibilityEvent (AccessibilityEvent::structureChanged); + + structureChanged = false; + } + + if (needsRecalculating) + { + if (auto* root = owner.rootItem) + { + const auto startY = owner.rootItemVisible ? 0 : -root->itemHeight; + + root->updatePositions (startY); + getViewedComponent()->setSize (jmax (getMaximumVisibleWidth(), root->totalWidth + 50), + root->totalHeight + startY); + } + else + { + getViewedComponent()->setSize (0, 0); + } + + updateComponents (false); + + needsRecalculating = false; + } + } + + void updateComponents (bool triggerResize) + { + if (auto* content = getContentComp()) + { + if (triggerResize) + content->resized(); + else + content->updateComponents(); + } + + repaint(); } + TreeView& owner; int lastX = -1; + bool structureChanged = false, needsRecalculating = false; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeViewport) }; @@ -696,7 +734,7 @@ private: //============================================================================== TreeView::TreeView (const String& name) : Component (name) { - viewport = std::make_unique(); + viewport = std::make_unique (*this); addAndMakeVisible (viewport.get()); viewport->setViewedComponent (new ContentComponent (*this)); @@ -737,7 +775,7 @@ void TreeView::setRootItem (TreeViewItem* const newRootItem) rootItem->setOpen (true); } - updateVisibleItems(); + viewport->recalculatePositions (TreeViewport::Async::no); } } @@ -1097,20 +1135,7 @@ bool TreeView::keyPressed (const KeyPress& key) void TreeView::updateVisibleItems() { - if (rootItem != nullptr) - { - rootItem->updatePositions (rootItemVisible ? 0 : -rootItem->itemHeight); - - viewport->getViewedComponent() - ->setSize (jmax (viewport->getMaximumVisibleWidth(), rootItem->totalWidth + 50), - rootItem->totalHeight - (rootItemVisible ? 0 : rootItem->itemHeight)); - } - else - { - viewport->getViewedComponent()->setSize (0, 0); - } - - viewport->updateComponents (false); + viewport->recalculatePositions (TreeViewport::Async::yes); } //============================================================================== @@ -1774,6 +1799,9 @@ void TreeViewItem::setOwnerView (TreeView* const newOwner) noexcept int TreeViewItem::getIndentX() const noexcept { + if (ownerView == nullptr) + return 0; + int x = ownerView->rootItemVisible ? 1 : 0; if (! ownerView->openCloseButtonsVisible) @@ -2053,6 +2081,9 @@ TreeViewItem::OpennessRestorer::~OpennessRestorer() void TreeViewItem::draw (Graphics& g, int width, bool isMouseOverButton) { + if (ownerView == nullptr) + return; + const auto indent = getIndentX(); const auto itemW = (itemWidth < 0 || drawsInRightMargin) ? width - indent : itemWidth;