| @@ -632,36 +632,20 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| class TreeView::TreeViewport : public Viewport, | class TreeView::TreeViewport : public Viewport, | ||||
| private Timer | |||||
| private AsyncUpdater | |||||
| { | { | ||||
| public: | 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<int>& newVisibleArea) override | void visibleAreaChanged (const Rectangle<int>& newVisibleArea) override | ||||
| { | { | ||||
| const auto hasScrolledSideways = (newVisibleArea.getX() != lastX); | const auto hasScrolledSideways = (newVisibleArea.getX() != lastX); | ||||
| lastX = newVisibleArea.getX(); | lastX = newVisibleArea.getX(); | ||||
| updateComponents (hasScrolledSideways); | updateComponents (hasScrolledSideways); | ||||
| startTimer (50); | |||||
| } | |||||
| ContentComponent* getContentComp() const noexcept | |||||
| { | |||||
| return static_cast<ContentComponent*> (getViewedComponent()); | |||||
| structureChanged = true; | |||||
| triggerAsyncUpdate(); | |||||
| } | } | ||||
| bool keyPressed (const KeyPress& key) override | bool keyPressed (const KeyPress& key) override | ||||
| @@ -673,22 +657,76 @@ public: | |||||
| return Viewport::keyPressed (key); | return Viewport::keyPressed (key); | ||||
| } | } | ||||
| ContentComponent* getContentComp() const noexcept | |||||
| { | |||||
| return static_cast<ContentComponent*> (getViewedComponent()); | |||||
| } | |||||
| enum class Async { yes, no }; | |||||
| void recalculatePositions (Async useAsyncUpdate) | |||||
| { | |||||
| needsRecalculating = true; | |||||
| if (useAsyncUpdate == Async::yes) | |||||
| triggerAsyncUpdate(); | |||||
| else | |||||
| handleAsyncUpdate(); | |||||
| } | |||||
| private: | private: | ||||
| std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override | std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override | ||||
| { | { | ||||
| return createIgnoredAccessibilityHandler (*this); | 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); | 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; | int lastX = -1; | ||||
| bool structureChanged = false, needsRecalculating = false; | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeViewport) | JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TreeViewport) | ||||
| }; | }; | ||||
| @@ -696,7 +734,7 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| TreeView::TreeView (const String& name) : Component (name) | TreeView::TreeView (const String& name) : Component (name) | ||||
| { | { | ||||
| viewport = std::make_unique<TreeViewport>(); | |||||
| viewport = std::make_unique<TreeViewport> (*this); | |||||
| addAndMakeVisible (viewport.get()); | addAndMakeVisible (viewport.get()); | ||||
| viewport->setViewedComponent (new ContentComponent (*this)); | viewport->setViewedComponent (new ContentComponent (*this)); | ||||
| @@ -737,7 +775,7 @@ void TreeView::setRootItem (TreeViewItem* const newRootItem) | |||||
| rootItem->setOpen (true); | rootItem->setOpen (true); | ||||
| } | } | ||||
| updateVisibleItems(); | |||||
| viewport->recalculatePositions (TreeViewport::Async::no); | |||||
| } | } | ||||
| } | } | ||||
| @@ -1097,20 +1135,7 @@ bool TreeView::keyPressed (const KeyPress& key) | |||||
| void TreeView::updateVisibleItems() | 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 | int TreeViewItem::getIndentX() const noexcept | ||||
| { | { | ||||
| if (ownerView == nullptr) | |||||
| return 0; | |||||
| int x = ownerView->rootItemVisible ? 1 : 0; | int x = ownerView->rootItemVisible ? 1 : 0; | ||||
| if (! ownerView->openCloseButtonsVisible) | if (! ownerView->openCloseButtonsVisible) | ||||
| @@ -2053,6 +2081,9 @@ TreeViewItem::OpennessRestorer::~OpennessRestorer() | |||||
| void TreeViewItem::draw (Graphics& g, int width, bool isMouseOverButton) | void TreeViewItem::draw (Graphics& g, int width, bool isMouseOverButton) | ||||
| { | { | ||||
| if (ownerView == nullptr) | |||||
| return; | |||||
| const auto indent = getIndentX(); | const auto indent = getIndentX(); | ||||
| const auto itemW = (itemWidth < 0 || drawsInRightMargin) ? width - indent : itemWidth; | const auto itemW = (itemWidth < 0 || drawsInRightMargin) ? width - indent : itemWidth; | ||||