JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_TreeView.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29static int getItemDepth (const TreeViewItem* item)
30{
31 if (item == nullptr || item->getOwnerView() == nullptr)
32 return 0;
33
34 auto depth = item->getOwnerView()->isRootItemVisible() ? 0 : -1;
35
36 for (auto* parent = item->getParentItem(); parent != nullptr; parent = parent->getParentItem())
37 ++depth;
38
39 return depth;
40}
41
42//==============================================================================
44 public TooltipClient
45{
46public:
48 : item (itemToRepresent),
49 customComponent (item.createItemComponent())
50 {
51 if (hasCustomComponent())
52 addAndMakeVisible (*customComponent);
53 }
54
55 void paint (Graphics& g) override
56 {
57 item.draw (g, getWidth(), mouseIsOverButton);
58 }
59
60 void resized() override
61 {
62 if (hasCustomComponent())
63 {
64 auto itemPosition = item.getItemPosition (false);
65
66 customComponent->setBounds (getLocalBounds().withX (itemPosition.getX())
67 .withWidth (itemPosition.getWidth()));
68 }
69 }
70
71 void setMouseIsOverButton (bool isOver)
72 {
73 mouseIsOverButton = isOver;
74 repaint();
75 }
76
77 TreeViewItem& getRepresentedItem() const noexcept
78 {
79 return item;
80 }
81
82 String getTooltip() override
83 {
84 return item.getTooltip();
85 }
86
87private:
88 //==============================================================================
89 class ItemAccessibilityHandler final : public AccessibilityHandler
90 {
91 public:
92 explicit ItemAccessibilityHandler (ItemComponent& comp)
94 AccessibilityRole::treeItem,
95 getAccessibilityActions (comp),
96 { std::make_unique<ItemCellInterface> (comp) }),
97 itemComponent (comp)
98 {
99 }
100
101 String getTitle() const override
102 {
103 return itemComponent.getRepresentedItem().getAccessibilityName();
104 }
105
106 String getHelp() const override
107 {
108 return itemComponent.getRepresentedItem().getTooltip();
109 }
110
111 AccessibleState getCurrentState() const override
112 {
113 auto& treeItem = itemComponent.getRepresentedItem();
114
116
117 if (auto* tree = treeItem.getOwnerView())
118 {
119 if (tree->isMultiSelectEnabled())
120 state = state.withMultiSelectable();
121 else
122 state = state.withSelectable();
123 }
124
125 if (treeItem.mightContainSubItems())
126 {
127 state = state.withExpandable();
128
129 if (treeItem.isOpen())
130 state = state.withExpanded();
131 else
132 state = state.withCollapsed();
133 }
134
135 if (treeItem.isSelected())
136 state = state.withSelected();
137
138 return state;
139 }
140
142 {
143 public:
144 explicit ItemCellInterface (ItemComponent& c) : itemComponent (c) {}
145
146 int getDisclosureLevel() const override
147 {
148 return getItemDepth (&itemComponent.getRepresentedItem());
149 }
150
152 {
153 const auto& representedItem = itemComponent.getRepresentedItem();
154 const auto* tree = representedItem.getOwnerView();
155
156 if (tree == nullptr)
157 return {};
158
159 const auto numSubItems = representedItem.isOpen() ? representedItem.getNumSubItems() : 0;
160
162 result.reserve ((size_t) numSubItems);
163
164 for (auto i = 0; i < numSubItems; ++i)
165 {
166 result.push_back ([&]() -> const AccessibilityHandler*
167 {
168 if (auto* subItem = representedItem.getSubItem (i))
169 if (auto* component = tree->getItemComponent (subItem))
170 return component->getAccessibilityHandler();
171
172 return nullptr;
173 }());
174 }
175
176 return result;
177 }
178
179 const AccessibilityHandler* getTableHandler() const override
180 {
181 if (auto* tree = itemComponent.getRepresentedItem().getOwnerView())
182 return tree->getAccessibilityHandler();
183
184 return nullptr;
185 }
186
187 private:
188 ItemComponent& itemComponent;
189 };
190
191 private:
192 static AccessibilityActions getAccessibilityActions (ItemComponent& itemComponent)
193 {
194 auto onFocus = [&itemComponent]
195 {
196 auto& treeItem = itemComponent.getRepresentedItem();
197
198 if (auto* tree = treeItem.getOwnerView())
199 tree->scrollToKeepItemVisible (&treeItem);
200 };
201
202 auto onPress = [&itemComponent]
203 {
204 itemComponent.getRepresentedItem().itemClicked (generateMouseEvent (itemComponent, { ModifierKeys::leftButtonModifier }));
205 };
206
207 auto onShowMenu = [&itemComponent]
208 {
209 itemComponent.getRepresentedItem().itemClicked (generateMouseEvent (itemComponent, { ModifierKeys::popupMenuClickModifier }));
210 };
211
212 auto onToggle = [&itemComponent, onFocus]
213 {
214 if (auto* handler = itemComponent.getAccessibilityHandler())
215 {
216 auto isSelected = handler->getCurrentState().isSelected();
217
218 if (! isSelected)
219 onFocus();
220
221 itemComponent.getRepresentedItem().setSelected (! isSelected, true);
222 }
223 };
224
225 auto actions = AccessibilityActions().addAction (AccessibilityActionType::focus, std::move (onFocus))
226 .addAction (AccessibilityActionType::press, std::move (onPress))
227 .addAction (AccessibilityActionType::showMenu, std::move (onShowMenu))
228 .addAction (AccessibilityActionType::toggle, std::move (onToggle));
229
230 return actions;
231 }
232
233 ItemComponent& itemComponent;
234
235 static MouseEvent generateMouseEvent (ItemComponent& itemComp, ModifierKeys mods)
236 {
237 auto topLeft = itemComp.getRepresentedItem().getItemPosition (false).toFloat().getTopLeft();
238
239 return { Desktop::getInstance().getMainMouseSource(), topLeft, mods,
241 MouseInputSource::defaultTiltX, MouseInputSource::defaultTiltY,
242 &itemComp, &itemComp, Time::getCurrentTime(), topLeft, Time::getCurrentTime(), 0, false };
243 }
244
245 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemAccessibilityHandler)
246 };
247
248 std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
249 {
250 if (hasCustomComponent() && customComponent->getAccessibilityHandler() != nullptr)
252
253 return std::make_unique<ItemAccessibilityHandler> (*this);
254 }
255
256 bool hasCustomComponent() const noexcept { return customComponent.get() != nullptr; }
257
258 TreeViewItem& item;
259 std::unique_ptr<Component> customComponent;
260
261 bool mouseIsOverButton = false;
262
263 //==============================================================================
265};
266
267//==============================================================================
269 public TooltipClient,
270 public AsyncUpdater
271{
272public:
273 ContentComponent (TreeView& tree) : owner (tree)
274 {
275 }
276
277 //==============================================================================
278 void resized() override
279 {
281 }
282
284 {
285 if (auto* itemComponent = getItemComponentAt (getMouseXYRelative()))
286 return itemComponent->getRepresentedItem().getTooltip();
287
288 return owner.getTooltip();
289 }
290
291 void mouseDown (const MouseEvent& e) override { mouseDownInternal (e.getEventRelativeTo (this)); }
292 void mouseUp (const MouseEvent& e) override { mouseUpInternal (e.getEventRelativeTo (this)); }
293 void mouseDoubleClick (const MouseEvent& e) override { mouseDoubleClickInternal (e.getEventRelativeTo (this));}
294 void mouseDrag (const MouseEvent& e) override { mouseDragInternal (e.getEventRelativeTo (this));}
295 void mouseMove (const MouseEvent& e) override { mouseMoveInternal (e.getEventRelativeTo (this)); }
296 void mouseExit (const MouseEvent& e) override { mouseExitInternal (e.getEventRelativeTo (this)); }
297
298 //==============================================================================
299 ItemComponent* getItemComponentAt (Point<int> p)
300 {
301 auto iter = std::find_if (itemComponents.cbegin(), itemComponents.cend(),
302 [p] (const auto& c)
303 {
304 return c->getBounds().contains (p);
305 });
306
307 if (iter != itemComponents.cend())
308 return iter->get();
309
310 return nullptr;
311 }
312
313 ItemComponent* getComponentForItem (const TreeViewItem* item) const
314 {
315 const auto iter = std::find_if (itemComponents.begin(), itemComponents.end(),
316 [item] (const auto& c)
317 {
318 return &c->getRepresentedItem() == item;
319 });
320
321 if (iter != itemComponents.end())
322 return iter->get();
323
324 return nullptr;
325 }
326
327 void itemBeingDeleted (const TreeViewItem* item)
328 {
329 const auto iter = std::find_if (itemComponents.begin(), itemComponents.end(),
330 [item] (const auto& c)
331 {
332 return &c->getRepresentedItem() == item;
333 });
334
335 if (iter != itemComponents.end())
336 {
337 if (itemUnderMouse == iter->get())
338 itemUnderMouse = nullptr;
339
340 if (isMouseDraggingInChildComp (*(iter->get())))
341 owner.hideDragHighlight();
342
343 itemComponents.erase (iter);
344 }
345 }
346
347 const TreeViewItem* getItemForItemComponent (const Component* comp) const
348 {
349 const auto iter = itemForItemComponent.find (comp);
350 return iter != itemForItemComponent.cend() ? iter->second : nullptr;
351 }
352
353 void updateComponents()
354 {
356
357 for (auto* treeItem : getAllVisibleItems())
358 {
359 if (auto* itemComp = getComponentForItem (treeItem))
360 {
362 }
363 else
364 {
365 std::unique_ptr<ItemComponent, Deleter> newComp { new ItemComponent (*treeItem), Deleter { itemForItemComponent } };
366 itemForItemComponent.emplace (newComp.get(), treeItem);
367
369 newComp->addMouseListener (this, treeItem->customComponentUsesTreeViewMouseHandler());
370 componentsToKeep.insert (newComp.get());
371
372 itemComponents.push_back (std::move (newComp));
373 }
374 }
375
376 auto removePredicate = [&] (auto& item)
377 {
378 if (item == nullptr)
379 return true;
380
381 return componentsToKeep.find (item.get()) == componentsToKeep.end()
382 && ! isMouseDraggingInChildComp (*item);
383 };
384
385 const auto iter = std::remove_if (itemComponents.begin(), itemComponents.end(), std::move (removePredicate));
386 itemComponents.erase (iter, itemComponents.end());
387
388 for (auto& comp : itemComponents)
389 {
390 auto& treeItem = comp->getRepresentedItem();
391 comp->setBounds ({ 0, treeItem.y, getWidth(), treeItem.itemHeight });
392 }
393 }
394
395private:
396 //==============================================================================
397 struct ScopedDisableViewportScroll
398 {
399 explicit ScopedDisableViewportScroll (ItemComponent& c)
400 : item (&c)
401 {
402 item->setViewportIgnoreDragFlag (true);
403 }
404
405 ~ScopedDisableViewportScroll()
406 {
407 if (item != nullptr)
408 item->setViewportIgnoreDragFlag (false);
409 }
410
412
413 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedDisableViewportScroll)
414 };
415
416 //==============================================================================
417 std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
418 {
420 }
421
422 void mouseDownInternal (const MouseEvent& e)
423 {
424 updateItemUnderMouse (e);
425
426 isDragging = false;
427 scopedScrollDisabler = nullopt;
428 needSelectionOnMouseUp = false;
429
430 if (! isEnabled())
431 return;
432
433 if (auto* itemComponent = getItemComponentAt (e.getPosition()))
434 {
435 auto& item = itemComponent->getRepresentedItem();
436 auto pos = item.getItemPosition (false);
437
438 // (if the open/close buttons are hidden, we'll treat clicks to the left of the item
439 // as selection clicks)
440 if (e.x < pos.getX() && owner.openCloseButtonsVisible)
441 {
442 // (clicks to the left of an open/close button are ignored)
443 if (e.x >= pos.getX() - owner.getIndentSize())
444 item.setOpen (! item.isOpen());
445 }
446 else
447 {
448 // mouse-down inside the body of the item..
449 if (! owner.isMultiSelectEnabled())
450 item.setSelected (true, true);
451 else if (item.isSelected())
452 needSelectionOnMouseUp = ! e.mods.isPopupMenu();
453 else
454 selectBasedOnModifiers (item, e.mods);
455
456 if (e.x >= pos.getX())
457 item.itemClicked (e.withNewPosition (e.position - pos.getPosition().toFloat()));
458 }
459 }
460 }
461
462 void mouseUpInternal (const MouseEvent& e)
463 {
464 updateItemUnderMouse (e);
465
466 if (isEnabled() && needSelectionOnMouseUp && e.mouseWasClicked())
467 if (auto* itemComponent = getItemComponentAt (e.getPosition()))
468 selectBasedOnModifiers (itemComponent->getRepresentedItem(), e.mods);
469 }
470
471 void mouseDoubleClickInternal (const MouseEvent& e)
472 {
473 if (isEnabled() && e.getNumberOfClicks() != 3) // ignore triple clicks
474 {
475 if (auto* itemComponent = getItemComponentAt (e.getPosition()))
476 {
477 auto& item = itemComponent->getRepresentedItem();
478 auto pos = item.getItemPosition (false);
479
480 if (e.x >= pos.getX() || ! owner.openCloseButtonsVisible)
481 item.itemDoubleClicked (e.withNewPosition (e.position - pos.getPosition().toFloat()));
482 }
483 }
484 }
485
486 void mouseDragInternal (const MouseEvent& e)
487 {
488 if (isEnabled()
489 && ! (isDragging || e.mouseWasClicked()
490 || e.getDistanceFromDragStart() < 5
491 || e.mods.isPopupMenu()))
492 {
493 isDragging = true;
494
495 if (auto* itemComponent = getItemComponentAt (e.getMouseDownPosition()))
496 {
497 auto& item = itemComponent->getRepresentedItem();
498 auto pos = item.getItemPosition (false);
499
500 if (e.getMouseDownX() >= pos.getX())
501 {
502 auto dragDescription = item.getDragSourceDescription();
503
504 if (! (dragDescription.isVoid() || (dragDescription.isString() && dragDescription.toString().isEmpty())))
505 {
507 {
508 pos.setSize (pos.getWidth(), item.itemHeight);
509
510 const auto additionalScale = 2.0f;
512 true,
514
515 dragImage.multiplyAllAlphas (0.6f);
516
517 auto imageOffset = pos.getPosition() - e.getPosition();
518 dragContainer->startDragging (dragDescription, &owner, { dragImage, additionalScale }, true, &imageOffset, &e.source);
519
520 scopedScrollDisabler.emplace (*itemComponent);
521 }
522 else
523 {
524 // to be able to do a drag-and-drop operation, the treeview needs to
525 // be inside a component which is also a DragAndDropContainer.
527 }
528 }
529 }
530 }
531 }
532 }
533
534 void mouseMoveInternal (const MouseEvent& e) { updateItemUnderMouse (e); }
535 void mouseExitInternal (const MouseEvent& e) { updateItemUnderMouse (e); }
536
537 static bool isMouseDraggingInChildComp (const Component& comp)
538 {
539 for (auto& ms : Desktop::getInstance().getMouseSources())
540 if (ms.isDragging())
541 if (auto* underMouse = ms.getComponentUnderMouse())
542 return (&comp == underMouse || comp.isParentOf (underMouse));
543
544 return false;
545 }
546
547 void updateItemUnderMouse (const MouseEvent& e)
548 {
549 if (! owner.openCloseButtonsVisible)
550 return;
551
552 auto* newItem = [this, &e]() -> ItemComponent*
553 {
554 if (auto* itemComponent = getItemComponentAt (e.getPosition()))
555 {
556 auto& item = itemComponent->getRepresentedItem();
557
558 if (item.mightContainSubItems())
559 {
560 const auto xPos = item.getItemPosition (false).getX();
561
562 if (xPos - owner.getIndentSize() <= e.x && e.x < xPos)
563 return itemComponent;
564 }
565 }
566
567 return nullptr;
568 }();
569
570 if (itemUnderMouse != newItem)
571 {
572 if (itemUnderMouse != nullptr)
573 itemUnderMouse->setMouseIsOverButton (false);
574
575 if (newItem != nullptr)
576 newItem->setMouseIsOverButton (true);
577
578 itemUnderMouse = newItem;
579 }
580 }
581
582 void handleAsyncUpdate() override
583 {
584 owner.updateVisibleItems();
585 }
586
587 //==============================================================================
588 void selectBasedOnModifiers (TreeViewItem& item, const ModifierKeys modifiers)
589 {
590 TreeViewItem* firstSelected = nullptr;
591
592 if (modifiers.isShiftDown() && ((firstSelected = owner.getSelectedItem (0)) != nullptr))
593 {
594 auto* lastSelected = owner.getSelectedItem (owner.getNumSelectedItems() - 1);
595
596 if (lastSelected == nullptr)
597 {
599 return;
600 }
601
603 auto rowEnd = lastSelected->getRowNumberInTree();
604
605 if (rowStart > rowEnd)
607
608 auto ourRow = item.getRowNumberInTree();
609 auto otherEnd = ourRow < rowEnd ? rowStart : rowEnd;
610
611 if (ourRow > otherEnd)
613
614 for (int i = ourRow; i <= otherEnd; ++i)
615 owner.getItemOnRow (i)->setSelected (true, false);
616 }
617 else
618 {
619 const auto cmd = modifiers.isCommandDown();
620 item.setSelected ((! cmd) || ! item.isSelected(), ! cmd);
621 }
622 }
623
624 static TreeViewItem* getNextVisibleItem (TreeViewItem* item, bool forwards)
625 {
626 if (item == nullptr || item->ownerView == nullptr)
627 return nullptr;
628
629 auto* nextItem = item->ownerView->getItemOnRow (item->getRowNumberInTree() + (forwards ? 1 : -1));
630
631 return nextItem == item->ownerView->rootItem && ! item->ownerView->rootItemVisible ? nullptr
632 : nextItem;
633 }
634
635 template <typename Fn>
636 static void forEachDepthFirst (TreeViewItem* item, bool includeItem, Fn&& callback)
637 {
638 if (includeItem)
639 callback (item);
640
641 if (item->isOpen())
642 for (auto i = 0; i < item->getNumSubItems(); ++i)
643 forEachDepthFirst (item->getSubItem (i), true, callback);
644 }
645
646 std::vector<TreeViewItem*> collectAllItems() const
647 {
648 size_t count{};
649 forEachDepthFirst (owner.rootItem, owner.rootItemVisible, [&] (auto*) { ++count; });
650
652 allItems.reserve (count);
653 forEachDepthFirst (owner.rootItem, owner.rootItemVisible, [&] (auto* item) { allItems.push_back (item); });
654
655 return allItems;
656 }
657
658 std::vector<TreeViewItem*> getAllVisibleItems() const
659 {
660 if (owner.rootItem == nullptr)
661 return {};
662
663 const auto visibleTop = -getY();
665 auto allItems = collectAllItems();
666
667 const auto lower = std::lower_bound (allItems.begin(), allItems.end(), visibleTop, [] (TreeViewItem* item, const auto y)
668 {
669 return item->y + item->getItemHeight() < y;
670 });
671
672 const auto upper = std::upper_bound (allItems.begin(), allItems.end(), visibleBottom, [] (const auto y, TreeViewItem* item)
673 {
674 return y < item->y;
675 });
676
677 const std::ptrdiff_t padding = 2;
678
679 const auto frontToErase = std::max (padding, std::distance (allItems.begin(), lower)) - padding;
680 const auto backToErase = std::max (padding, std::distance (upper, allItems.end())) - padding;
681
682 allItems.erase (allItems.begin(), std::next (allItems.begin(), frontToErase));
683 allItems.erase (std::prev (allItems.end(), backToErase), allItems.end());
684
685 return allItems;
686 }
687
688 //==============================================================================
689 class Deleter
690 {
691 public:
693 : itemForItemComponent (&map) {}
694
695 void operator() (ItemComponent* ptr) const
696 {
697 itemForItemComponent->erase (ptr);
698
699 if (ptr != nullptr)
700 delete ptr;
701 }
702
703 private:
704 std::map<const Component*, const TreeViewItem*>* itemForItemComponent = nullptr;
705 };
706
707 //==============================================================================
708 TreeView& owner;
709
712 ItemComponent* itemUnderMouse = nullptr;
713 Optional<ScopedDisableViewportScroll> scopedScrollDisabler;
714 bool isDragging = false, needSelectionOnMouseUp = false;
715
717};
718
719//==============================================================================
721 private AsyncUpdater
722{
723public:
724 explicit TreeViewport (TreeView& treeView) : owner (treeView) {}
725
727 {
728 const auto hasScrolledSideways = (newVisibleArea.getX() != lastX);
729
730 lastX = newVisibleArea.getX();
731 updateComponents (hasScrolledSideways);
732
733 structureChanged = true;
735 }
736
737 bool keyPressed (const KeyPress& key) override
738 {
739 if (auto* tree = getParentComponent())
740 if (tree->keyPressed (key))
741 return true;
742
743 return Viewport::keyPressed (key);
744 }
745
746 ContentComponent* getContentComp() const noexcept
747 {
748 return static_cast<ContentComponent*> (getViewedComponent());
749 }
750
751 enum class Async { yes, no };
752
753 void recalculatePositions (Async useAsyncUpdate, std::optional<Point<int>> viewportPosition)
754 {
755 needsRecalculating = true;
756 viewportAfterRecalculation = std::move (viewportPosition);
757
758 if (useAsyncUpdate == Async::yes)
760 else
761 handleAsyncUpdate();
762 }
763
764private:
765 std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
766 {
768 }
769
770 void handleAsyncUpdate() override
771 {
772 if (std::exchange (structureChanged, false))
773 {
774 if (auto* handler = owner.getAccessibilityHandler())
775 handler->notifyAccessibilityEvent (AccessibilityEvent::structureChanged);
776 }
777
778 if (std::exchange (needsRecalculating, false))
779 {
780 if (auto* root = owner.rootItem)
781 {
782 const auto startY = owner.rootItemVisible ? 0 : -root->itemHeight;
783
784 root->updatePositions (startY);
785 getViewedComponent()->setSize (jmax (getMaximumVisibleWidth(), root->totalWidth + 50),
786 root->totalHeight + startY);
787 }
788 else
789 {
790 getViewedComponent()->setSize (0, 0);
791 }
792
793 updateComponents (false);
794
795 if (const auto viewportPosition = std::exchange (viewportAfterRecalculation, {}))
797 }
798 }
799
800 void updateComponents (bool triggerResize)
801 {
802 if (auto* content = getContentComp())
803 {
804 if (triggerResize)
805 content->resized();
806 else
807 content->updateComponents();
808 }
809
810 repaint();
811 }
812
813 TreeView& owner;
814 int lastX = -1;
815 bool structureChanged = false, needsRecalculating = false;
816 std::optional<Point<int>> viewportAfterRecalculation;
817
819};
820
821//==============================================================================
823{
824 if (rootItem != newRootItem)
825 {
826 if (newRootItem != nullptr)
827 {
828 // can't use a tree item in more than one tree at once..
829 jassert (newRootItem->ownerView == nullptr);
830
831 if (newRootItem->ownerView != nullptr)
832 newRootItem->ownerView->setRootItem (nullptr);
833 }
834
835 if (rootItem != nullptr)
836 rootItem->setOwnerView (nullptr);
837
838 rootItem = newRootItem;
839
840 if (newRootItem != nullptr)
841 newRootItem->setOwnerView (this);
842
843 if (rootItem != nullptr && (defaultOpenness || ! rootItemVisible))
844 {
845 rootItem->setOpen (false); // force a re-open
846 rootItem->setOpen (true);
847 }
848
849 viewport->recalculatePositions (TreeViewport::Async::no, {});
850 }
851}
852
854{
855 const std::unique_ptr<TreeViewItem> deleter (rootItem);
856 setRootItem (nullptr);
857}
858
860{
861 rootItemVisible = shouldBeVisible;
862
863 if (rootItem != nullptr && (defaultOpenness || ! rootItemVisible))
864 {
865 rootItem->setOpen (false); // force a re-open
866 rootItem->setOpen (true);
867 }
868
869 updateVisibleItems();
870}
871
877
879{
880 if (indentSize != newIndentSize)
881 {
882 indentSize = newIndentSize;
883 resized();
884 }
885}
886
888{
889 return indentSize >= 0 ? indentSize
890 : getLookAndFeel().getTreeViewIndentSize (*this);
891}
892
894{
895 if (defaultOpenness != isOpenByDefault)
896 {
897 defaultOpenness = isOpenByDefault;
898 updateVisibleItems();
899 }
900}
901
903{
904 multiSelectEnabled = canMultiSelect;
905}
906
908{
909 if (openCloseButtonsVisible != shouldBeVisible)
910 {
911 openCloseButtonsVisible = shouldBeVisible;
912 updateVisibleItems();
913 }
914}
915
917{
918 return viewport.get();
919}
920
921//==============================================================================
923{
924 if (rootItem != nullptr)
925 rootItem->deselectAllRecursively (nullptr);
926}
927
929{
930 return rootItem != nullptr ? rootItem->countSelectedItemsRecursively (maximumDepthToSearchTo) : 0;
931}
932
933TreeViewItem* TreeView::getSelectedItem (const int index) const noexcept
934{
935 return rootItem != nullptr ? rootItem->getSelectedItemWithIndex (index) : nullptr;
936}
937
939{
940 return rootItem != nullptr ? (rootItem->getNumRows() - (rootItemVisible ? 0 : 1)) : 0;
941}
942
944{
945 if (! rootItemVisible)
946 ++index;
947
948 if (rootItem != nullptr && index >= 0)
949 return rootItem->getItemOnRow (index);
950
951 return nullptr;
952}
953
954TreeViewItem* TreeView::getItemAt (int y) const noexcept
955{
956 if (auto* contentComp = viewport->getContentComp())
957 if (auto* itemComponent = contentComp->getItemComponentAt (contentComp->getLocalPoint (this, Point<int> (0, y))))
958 return &itemComponent->getRepresentedItem();
959
960 return nullptr;
961}
962
964{
965 if (rootItem == nullptr)
966 return nullptr;
967
968 return rootItem->findItemFromIdentifierString (identifierString);
969}
970
972{
973 return viewport->getContentComp()->getComponentForItem (item);
974}
975
976//==============================================================================
977static void addAllSelectedItemIds (TreeViewItem* item, XmlElement& parent)
978{
979 if (item->isSelected())
980 parent.createNewChildElement ("SELECTED")->setAttribute ("id", item->getItemIdentifierString());
981
982 auto numSubItems = item->getNumSubItems();
983
984 for (int i = 0; i < numSubItems; ++i)
985 addAllSelectedItemIds (item->getSubItem (i), parent);
986}
987
989{
990 if (rootItem != nullptr)
991 {
992 if (auto rootOpenness = rootItem->getOpennessState (false))
993 {
995 rootOpenness->setAttribute ("scrollPos", viewport->getViewPositionY());
996
997 addAllSelectedItemIds (rootItem, *rootOpenness);
998 return rootOpenness;
999 }
1000 }
1001
1002 return {};
1003}
1004
1006{
1007 if (rootItem != nullptr)
1008 {
1009 rootItem->restoreOpennessState (newState);
1010
1012 {
1014
1015 for (auto* e : newState.getChildWithTagNameIterator ("SELECTED"))
1016 if (auto* item = rootItem->findItemFromIdentifierString (e->getStringAttribute ("id")))
1017 item->setSelected (true, false);
1018 }
1019
1020 const auto scrollPos = newState.hasAttribute ("scrollPos")
1021 ? std::make_optional<Point<int>> (viewport->getViewPositionX(), newState.getIntAttribute ("scrollPos"))
1022 : std::nullopt;
1023
1024 updateVisibleItems (std::move (scrollPos));
1025 }
1026}
1027
1028//==============================================================================
1033
1035{
1036 viewport->setBounds (getLocalBounds());
1037 updateVisibleItems();
1038}
1039
1041{
1042 repaint();
1043}
1044
1046{
1048
1049 if (numRowsInTree > 0)
1050 {
1051 int rowSelected = 0;
1052
1053 if (auto* firstSelected = getSelectedItem (0))
1054 rowSelected = firstSelected->getRowNumberInTree();
1055
1056 rowSelected = jlimit (0, numRowsInTree - 1, rowSelected + delta);
1057
1058 for (;;)
1059 {
1060 if (auto* item = getItemOnRow (rowSelected))
1061 {
1062 if (! item->canBeSelected())
1063 {
1064 // if the row we want to highlight doesn't allow it, try skipping
1065 // to the next item..
1066 auto nextRowToTry = jlimit (0, numRowsInTree - 1, rowSelected + (delta < 0 ? -1 : 1));
1067
1069 {
1071 continue;
1072 }
1073
1074 break;
1075 }
1076
1077 item->setSelected (true, true);
1079 }
1080
1081 break;
1082 }
1083 }
1084}
1085
1087{
1088 if (item != nullptr && item->ownerView == this)
1089 {
1090 updateVisibleItems();
1091
1092 item = item->getDeepestOpenParentItem();
1093
1094 auto y = item->y;
1095 auto viewTop = viewport->getViewPositionY();
1096
1097 if (y < viewTop)
1098 {
1099 viewport->setViewPosition (viewport->getViewPositionX(), y);
1100 }
1101 else if (y + item->itemHeight > viewTop + viewport->getViewHeight())
1102 {
1103 viewport->setViewPosition (viewport->getViewPositionX(),
1104 (y + item->itemHeight) - viewport->getViewHeight());
1105 }
1106 }
1107}
1108
1109bool TreeView::toggleOpenSelectedItem()
1110{
1111 if (auto* firstSelected = getSelectedItem (0))
1112 {
1113 if (firstSelected->mightContainSubItems())
1114 {
1115 firstSelected->setOpen (! firstSelected->isOpen());
1116 return true;
1117 }
1118 }
1119
1120 return false;
1121}
1122
1123void TreeView::moveOutOfSelectedItem()
1124{
1125 if (auto* firstSelected = getSelectedItem (0))
1126 {
1127 if (firstSelected->isOpen())
1128 {
1129 firstSelected->setOpen (false);
1130 }
1131 else
1132 {
1133 auto* parent = firstSelected->parentItem;
1134
1135 if ((! rootItemVisible) && parent == rootItem)
1136 parent = nullptr;
1137
1138 if (parent != nullptr)
1139 {
1140 parent->setSelected (true, true);
1141 scrollToKeepItemVisible (parent);
1142 }
1143 }
1144 }
1145}
1146
1147void TreeView::moveIntoSelectedItem()
1148{
1149 if (auto* firstSelected = getSelectedItem (0))
1150 {
1151 if (firstSelected->isOpen() || ! firstSelected->mightContainSubItems())
1152 moveSelectedRow (1);
1153 else
1154 firstSelected->setOpen (true);
1155 }
1156}
1157
1158void TreeView::moveByPages (int numPages)
1159{
1160 if (auto* currentItem = getSelectedItem (0))
1161 {
1162 auto pos = currentItem->getItemPosition (false);
1163 auto targetY = pos.getY() + numPages * (getHeight() - pos.getHeight());
1164 auto currentRow = currentItem->getRowNumberInTree();
1165
1166 for (;;)
1167 {
1169 currentItem = getSelectedItem (0);
1170
1171 if (currentItem == nullptr)
1172 break;
1173
1174 auto y = currentItem->getItemPosition (false).getY();
1175
1176 if ((numPages < 0 && y <= targetY) || (numPages > 0 && y >= targetY))
1177 break;
1178
1179 auto newRow = currentItem->getRowNumberInTree();
1180
1181 if (newRow == currentRow)
1182 break;
1183
1185 }
1186 }
1187}
1188
1190{
1191 if (rootItem != nullptr)
1192 {
1193 if (key == KeyPress::upKey) { moveSelectedRow (-1); return true; }
1194 if (key == KeyPress::downKey) { moveSelectedRow (1); return true; }
1195 if (key == KeyPress::homeKey) { moveSelectedRow (-0x3fffffff); return true; }
1196 if (key == KeyPress::endKey) { moveSelectedRow (0x3fffffff); return true; }
1197 if (key == KeyPress::pageUpKey) { moveByPages (-1); return true; }
1198 if (key == KeyPress::pageDownKey) { moveByPages (1); return true; }
1199 if (key == KeyPress::returnKey) { return toggleOpenSelectedItem(); }
1200 if (key == KeyPress::leftKey) { moveOutOfSelectedItem(); return true; }
1201 if (key == KeyPress::rightKey) { moveIntoSelectedItem(); return true; }
1202 }
1203
1204 return false;
1205}
1206
1207void TreeView::updateVisibleItems (std::optional<Point<int>> viewportPosition)
1208{
1209 viewport->recalculatePositions (TreeViewport::Async::yes, std::move (viewportPosition));
1210}
1211
1212//==============================================================================
1214{
1215 InsertPoint (TreeView& view, const StringArray& files,
1217 : pos (dragSourceDetails.localPosition),
1218 item (view.getItemAt (dragSourceDetails.localPosition.y))
1219 {
1220 if (item != nullptr)
1221 {
1222 auto itemPos = item->getItemPosition (true);
1223 insertIndex = item->getIndexInParent();
1224 auto oldY = pos.y;
1225 pos.y = itemPos.getY();
1226
1227 if (item->getNumSubItems() == 0 || ! item->isOpen())
1228 {
1229 if (files.size() > 0 ? item->isInterestedInFileDrag (files)
1231 {
1232 // Check if we're trying to drag into an empty group item..
1233 if (oldY > itemPos.getY() + itemPos.getHeight() / 4
1234 && oldY < itemPos.getBottom() - itemPos.getHeight() / 4)
1235 {
1236 insertIndex = 0;
1237 pos.x = itemPos.getX() + view.getIndentSize();
1238 pos.y = itemPos.getBottom();
1239 return;
1240 }
1241 }
1242 }
1243
1244 if (oldY > itemPos.getCentreY())
1245 {
1246 pos.y += item->getItemHeight();
1247
1248 while (item->isLastOfSiblings() && item->getParentItem() != nullptr
1249 && item->getParentItem()->getParentItem() != nullptr)
1250 {
1251 if (pos.x > itemPos.getX())
1252 break;
1253
1254 item = item->getParentItem();
1255 itemPos = item->getItemPosition (true);
1256 insertIndex = item->getIndexInParent();
1257 }
1258
1259 ++insertIndex;
1260 }
1261
1262 pos.x = itemPos.getX();
1263 item = item->getParentItem();
1264 }
1265 else if (auto* root = view.getRootItem())
1266 {
1267 // If they're dragging beyond the bottom of the list, then insert at the end of the root item..
1268 item = root;
1269 insertIndex = root->getNumSubItems();
1270 pos = root->getItemPosition (true).getBottomLeft();
1271 pos.x += view.getIndentSize();
1272 }
1273 }
1274
1275 Point<int> pos;
1276 TreeViewItem* item;
1277 int insertIndex = 0;
1278};
1279
1280//==============================================================================
1282{
1283public:
1285 {
1286 setSize (100, 12);
1287 setAlwaysOnTop (true);
1288 setInterceptsMouseClicks (false, false);
1289 }
1290
1291 void setTargetPosition (const InsertPoint& insertPos, const int width) noexcept
1292 {
1293 lastItem = insertPos.item;
1294 lastIndex = insertPos.insertIndex;
1295 auto offset = getHeight() / 2;
1296 setBounds (insertPos.pos.x - offset, insertPos.pos.y - offset,
1297 width - (insertPos.pos.x - offset), getHeight());
1298 }
1299
1300 void paint (Graphics& g) override
1301 {
1302 Path p;
1303 auto h = (float) getHeight();
1304 p.addEllipse (2.0f, 2.0f, h - 4.0f, h - 4.0f);
1305 p.startNewSubPath (h - 2.0f, h / 2.0f);
1306 p.lineTo ((float) getWidth(), h / 2.0f);
1307
1309 g.strokePath (p, PathStrokeType (2.0f));
1310 }
1311
1312 TreeViewItem* lastItem = nullptr;
1313 int lastIndex = 0;
1314
1315private:
1317};
1318
1319//==============================================================================
1321{
1322public:
1324 {
1325 setAlwaysOnTop (true);
1326 setInterceptsMouseClicks (false, false);
1327 }
1328
1329 void setTargetPosition (TreeViewItem* const item) noexcept
1330 {
1331 setBounds (item->getItemPosition (true)
1332 .withHeight (item->getItemHeight()));
1333 }
1334
1335 void paint (Graphics& g) override
1336 {
1338 g.drawRoundedRectangle (1.0f, 1.0f, (float) getWidth() - 2.0f, (float) getHeight() - 2.0f, 3.0f, 2.0f);
1339 }
1340
1341private:
1343};
1344
1345//==============================================================================
1347{
1348 viewport = std::make_unique<TreeViewport> (*this);
1349 addAndMakeVisible (viewport.get());
1350 viewport->setViewedComponent (new ContentComponent (*this));
1351
1352 setWantsKeyboardFocus (true);
1354}
1355
1357{
1358 if (rootItem != nullptr)
1359 rootItem->setOwnerView (nullptr);
1360}
1361
1362//==============================================================================
1363void TreeView::showDragHighlight (const InsertPoint& insertPos) noexcept
1364{
1365 beginDragAutoRepeat (100);
1366
1367 if (dragInsertPointHighlight == nullptr)
1368 {
1369 dragInsertPointHighlight = std::make_unique<InsertPointHighlight>();
1370 dragTargetGroupHighlight = std::make_unique<TargetGroupHighlight>();
1371
1372 addAndMakeVisible (dragInsertPointHighlight.get());
1373 addAndMakeVisible (dragTargetGroupHighlight.get());
1374 }
1375
1376 dragInsertPointHighlight->setTargetPosition (insertPos, viewport->getViewWidth());
1377 dragTargetGroupHighlight->setTargetPosition (insertPos.item);
1378}
1379
1380void TreeView::hideDragHighlight() noexcept
1381{
1382 dragInsertPointHighlight = nullptr;
1383 dragTargetGroupHighlight = nullptr;
1384}
1385
1386void TreeView::handleDrag (const StringArray& files, const SourceDetails& dragSourceDetails)
1387{
1388 const auto scrolled = viewport->autoScroll (dragSourceDetails.localPosition.x,
1389 dragSourceDetails.localPosition.y, 20, 10);
1390
1391 InsertPoint insertPos (*this, files, dragSourceDetails);
1392
1393 if (insertPos.item != nullptr)
1394 {
1395 if (scrolled || dragInsertPointHighlight == nullptr
1396 || dragInsertPointHighlight->lastItem != insertPos.item
1397 || dragInsertPointHighlight->lastIndex != insertPos.insertIndex)
1398 {
1399 if (files.size() > 0 ? insertPos.item->isInterestedInFileDrag (files)
1400 : insertPos.item->isInterestedInDragSource (dragSourceDetails))
1401 showDragHighlight (insertPos);
1402 else
1403 hideDragHighlight();
1404 }
1405 }
1406 else
1407 {
1408 hideDragHighlight();
1409 }
1410}
1411
1412void TreeView::handleDrop (const StringArray& files, const SourceDetails& dragSourceDetails)
1413{
1414 hideDragHighlight();
1415
1416 InsertPoint insertPos (*this, files, dragSourceDetails);
1417
1418 if (insertPos.item == nullptr)
1419 insertPos.item = rootItem;
1420
1421 if (insertPos.item != nullptr)
1422 {
1423 if (files.size() > 0)
1424 {
1425 if (insertPos.item->isInterestedInFileDrag (files))
1426 insertPos.item->filesDropped (files, insertPos.insertIndex);
1427 }
1428 else
1429 {
1430 if (insertPos.item->isInterestedInDragSource (dragSourceDetails))
1431 insertPos.item->itemDropped (dragSourceDetails, insertPos.insertIndex);
1432 }
1433 }
1434}
1435
1436//==============================================================================
1438{
1439 return true;
1440}
1441
1442void TreeView::fileDragEnter (const StringArray& files, int x, int y)
1443{
1444 fileDragMove (files, x, y);
1445}
1446
1447void TreeView::fileDragMove (const StringArray& files, int x, int y)
1448{
1449 handleDrag (files, SourceDetails (var(), this, { x, y }));
1450}
1451
1453{
1454 hideDragHighlight();
1455}
1456
1457void TreeView::filesDropped (const StringArray& files, int x, int y)
1458{
1459 handleDrop (files, SourceDetails (var(), this, { x, y }));
1460}
1461
1462bool TreeView::isInterestedInDragSource (const SourceDetails& /*dragSourceDetails*/)
1463{
1464 return true;
1465}
1466
1471
1476
1477void TreeView::itemDragExit (const SourceDetails& /*dragSourceDetails*/)
1478{
1479 hideDragHighlight();
1480}
1481
1486
1487//==============================================================================
1489{
1491 {
1492 public:
1493 explicit TableInterface (TreeView& treeViewToWrap) : treeView (treeViewToWrap) {}
1494
1495 int getNumRows() const override { return treeView.getNumRowsInTree(); }
1496 int getNumColumns() const override { return 1; }
1497
1498 const AccessibilityHandler* getHeaderHandler() const override
1499 {
1500 return nullptr;
1501 }
1502
1503 const AccessibilityHandler* getRowHandler (int row) const override
1504 {
1505 if (auto* itemComp = treeView.getItemComponent (treeView.getItemOnRow (row)))
1506 return itemComp->getAccessibilityHandler();
1507
1508 return nullptr;
1509 }
1510
1511 const AccessibilityHandler* getCellHandler (int, int) const override
1512 {
1513 return nullptr;
1514 }
1515
1516 Optional<Span> getRowSpan (const AccessibilityHandler& handler) const override
1517 {
1518 auto* item = getItemForHandler (handler);
1519
1520 if (item == nullptr)
1521 return nullopt;
1522
1523 const auto rowNumber = item->getRowNumberInTree();
1524
1525 return rowNumber != -1 ? makeOptional (Span { rowNumber, 1 })
1526 : nullopt;
1527 }
1528
1529 Optional<Span> getColumnSpan (const AccessibilityHandler&) const override
1530 {
1531 return Span { 0, 1 };
1532 }
1533
1534 void showCell (const AccessibilityHandler& cellHandler) const override
1535 {
1536 treeView.scrollToKeepItemVisible (getItemForHandler (cellHandler));
1537 }
1538
1539 private:
1540 const TreeViewItem* getItemForHandler (const AccessibilityHandler& handler) const
1541 {
1542 for (auto* comp = &handler.getComponent(); comp != &treeView; comp = comp->getParentComponent())
1543 if (auto* result = treeView.viewport->getContentComp()->getItemForItemComponent (comp))
1544 return result;
1545
1546 return nullptr;
1547 }
1548
1549 TreeView& treeView;
1550
1552 };
1553
1554 return std::make_unique<AccessibilityHandler> (*this,
1555 AccessibilityRole::tree,
1557 AccessibilityHandler::Interfaces { std::make_unique<TableInterface> (*this) });
1558}
1559
1560//==============================================================================
1562{
1563 static int nextUID = 0;
1564 uid = nextUID++;
1565}
1566
1568{
1569 if (ownerView != nullptr)
1570 ownerView->viewport->getContentComp()->itemBeingDeleted (this);
1571}
1572
1574{
1575 return {};
1576}
1577
1581
1583{
1584 return subItems.size();
1585}
1586
1587TreeViewItem* TreeViewItem::getSubItem (const int index) const noexcept
1588{
1589 return subItems[index];
1590}
1591
1593{
1594 if (ownerView != nullptr)
1595 {
1596 if (! subItems.isEmpty())
1597 {
1598 removeAllSubItemsFromList();
1600 }
1601 }
1602 else
1603 {
1604 removeAllSubItemsFromList();
1605 }
1606}
1607
1608void TreeViewItem::removeAllSubItemsFromList()
1609{
1610 for (int i = subItems.size(); --i >= 0;)
1611 removeSubItemFromList (i, true);
1612}
1613
1615{
1616 if (newItem != nullptr)
1617 {
1618 newItem->parentItem = nullptr;
1619 newItem->setOwnerView (ownerView);
1620 newItem->y = 0;
1621 newItem->itemHeight = newItem->getItemHeight();
1622 newItem->totalHeight = 0;
1623 newItem->itemWidth = newItem->getItemWidth();
1624 newItem->totalWidth = 0;
1625 newItem->parentItem = this;
1626
1627 if (ownerView != nullptr)
1628 {
1629 subItems.insert (insertPosition, newItem);
1631
1632 if (newItem->isOpen())
1633 newItem->itemOpennessChanged (true);
1634 }
1635 else
1636 {
1637 subItems.insert (insertPosition, newItem);
1638
1639 if (newItem->isOpen())
1640 newItem->itemOpennessChanged (true);
1641 }
1642 }
1643}
1644
1646{
1647 if (ownerView != nullptr)
1648 {
1649 if (removeSubItemFromList (index, deleteItem))
1651 }
1652 else
1653 {
1654 removeSubItemFromList (index, deleteItem);
1655 }
1656}
1657
1658bool TreeViewItem::removeSubItemFromList (int index, bool deleteItem)
1659{
1660 if (auto* child = subItems[index])
1661 {
1662 child->parentItem = nullptr;
1663 subItems.remove (index, deleteItem);
1664
1665 return true;
1666 }
1667
1668 return false;
1669}
1670
1672{
1673 return openness;
1674}
1675
1677{
1678 auto wasOpen = isOpen();
1679 openness = newOpenness;
1680 auto isNowOpen = isOpen();
1681
1682 if (isNowOpen != wasOpen)
1683 {
1686 }
1687}
1688
1690{
1691 if (openness == Openness::opennessDefault)
1692 return ownerView != nullptr && ownerView->defaultOpenness;
1693
1694 return openness == Openness::opennessOpen;
1695}
1696
1698{
1699 if (isOpen() != shouldBeOpen)
1700 setOpenness (shouldBeOpen ? Openness::opennessOpen
1701 : Openness::opennessClosed);
1702}
1703
1704bool TreeViewItem::isFullyOpen() const noexcept
1705{
1706 if (! isOpen())
1707 return false;
1708
1709 for (auto* i : subItems)
1710 if (! i->isFullyOpen())
1711 return false;
1712
1713 return true;
1714}
1715
1716void TreeViewItem::restoreToDefaultOpenness()
1717{
1718 setOpenness (Openness::opennessDefault);
1719}
1720
1722{
1723 return selected;
1724}
1725
1726void TreeViewItem::deselectAllRecursively (TreeViewItem* itemToIgnore)
1727{
1728 if (this != itemToIgnore)
1729 setSelected (false, false);
1730
1731 for (auto* i : subItems)
1732 i->deselectAllRecursively (itemToIgnore);
1733}
1734
1736 const bool deselectOtherItemsFirst,
1737 const NotificationType notify)
1738{
1740 return;
1741
1743 getTopLevelItem()->deselectAllRecursively (this);
1744
1745 if (shouldBeSelected != selected)
1746 {
1747 selected = shouldBeSelected;
1748
1749 if (ownerView != nullptr)
1750 {
1751 ownerView->repaint();
1752
1753 if (selected)
1754 {
1755 if (auto* itemComponent = ownerView->getItemComponent (this))
1756 if (auto* itemHandler = itemComponent->getAccessibilityHandler())
1757 itemHandler->grabFocus();
1758 }
1759
1760 if (auto* handler = ownerView->getAccessibilityHandler())
1761 handler->notifyAccessibilityEvent (AccessibilityEvent::rowSelectionChanged);
1762 }
1763
1764 if (notify != dontSendNotification)
1766 }
1767}
1768
1770{
1771}
1772
1773void TreeViewItem::paintOpenCloseButton (Graphics& g, const Rectangle<float>& area, Colour backgroundColour, bool isMouseOver)
1774{
1776 .drawTreeviewPlusMinusBox (g, area, backgroundColour, isOpen(), isMouseOver);
1777}
1778
1780{
1782 g.drawLine (line);
1783}
1784
1786{
1788 g.drawLine (line);
1789}
1790
1792{
1793}
1794
1796{
1798 setOpen (! isOpen());
1799}
1800
1804
1806{
1807 return {};
1808}
1809
1811{
1812 auto tooltipString = getTooltip();
1813
1814 return tooltipString.isNotEmpty()
1815 ? tooltipString
1816 : "Level " + String (getItemDepth (this)) + " row " + String (getIndexInParent());
1817}
1818
1822
1824{
1825 return {};
1826}
1827
1829{
1830 return false;
1831}
1832
1833void TreeViewItem::filesDropped (const StringArray& /*files*/, int /*insertIndex*/)
1834{
1835}
1836
1838{
1839 return false;
1840}
1841
1842void TreeViewItem::itemDropped (const DragAndDropTarget::SourceDetails& /*dragSourceDetails*/, int /*insertIndex*/)
1843{
1844}
1845
1847{
1848 auto indentX = getIndentX();
1849 auto width = itemWidth;
1850
1851 if (ownerView != nullptr && width < 0)
1852 width = ownerView->viewport->getViewWidth() - indentX;
1853
1854 Rectangle<int> r (indentX, y, jmax (0, width), totalHeight);
1855
1856 if (relativeToTreeViewTopLeft && ownerView != nullptr)
1857 r -= ownerView->viewport->getViewPosition();
1858
1859 return r;
1860}
1861
1863{
1864 if (ownerView != nullptr)
1865 ownerView->updateVisibleItems();
1866}
1867
1869{
1870 if (ownerView != nullptr && areAllParentsOpen())
1871 if (auto* component = ownerView->getItemComponent (this))
1872 component->repaint();
1873}
1874
1876{
1877 return parentItem == nullptr
1878 || (parentItem->isOpen() && parentItem->areAllParentsOpen());
1879}
1880
1881void TreeViewItem::updatePositions (int newY)
1882{
1883 y = newY;
1884 itemHeight = getItemHeight();
1885 totalHeight = itemHeight;
1886 itemWidth = getItemWidth();
1887 totalWidth = jmax (itemWidth, 0) + getIndentX();
1888
1889 if (isOpen())
1890 {
1891 newY += totalHeight;
1892
1893 for (auto* i : subItems)
1894 {
1895 i->updatePositions (newY);
1896 newY += i->totalHeight;
1897 totalHeight += i->totalHeight;
1898 totalWidth = jmax (totalWidth, i->totalWidth);
1899 }
1900 }
1901}
1902
1903const TreeViewItem* TreeViewItem::getDeepestOpenParentItem() const noexcept
1904{
1905 auto* result = this;
1906 auto* item = this;
1907
1908 while (item->parentItem != nullptr)
1909 {
1910 item = item->parentItem;
1911
1912 if (! item->isOpen())
1913 result = item;
1914 }
1915
1916 return result;
1917}
1918
1919void TreeViewItem::setOwnerView (TreeView* const newOwner) noexcept
1920{
1921 ownerView = newOwner;
1922
1923 for (auto* i : subItems)
1924 {
1925 i->setOwnerView (newOwner);
1926 i->ownerViewChanged (newOwner);
1927 }
1928}
1929
1930int TreeViewItem::getIndentX() const noexcept
1931{
1932 if (ownerView == nullptr)
1933 return 0;
1934
1935 int x = ownerView->rootItemVisible ? 1 : 0;
1936
1937 if (! ownerView->openCloseButtonsVisible)
1938 --x;
1939
1940 for (auto* p = parentItem; p != nullptr; p = p->parentItem)
1941 ++x;
1942
1943 return x * ownerView->getIndentSize();
1944}
1945
1947{
1948 drawsInLeftMargin = canDrawInLeftMargin;
1949}
1950
1952{
1953 drawsInRightMargin = canDrawInRightMargin;
1954}
1955
1956bool TreeViewItem::areLinesDrawn() const
1957{
1958 return drawLinesSet ? drawLinesInside
1959 : (ownerView != nullptr && ownerView->getLookAndFeel().areLinesDrawnForTreeView (*ownerView));
1960}
1961
1963{
1964 return parentItem == nullptr
1965 || parentItem->subItems.getLast() == this;
1966}
1967
1969{
1970 return parentItem == nullptr ? 0
1971 : parentItem->subItems.indexOf (this);
1972}
1973
1974TreeViewItem* TreeViewItem::getTopLevelItem() noexcept
1975{
1976 return parentItem == nullptr ? this
1977 : parentItem->getTopLevelItem();
1978}
1979
1980int TreeViewItem::getNumRows() const noexcept
1981{
1982 int num = 1;
1983
1984 if (isOpen())
1985 for (auto* i : subItems)
1986 num += i->getNumRows();
1987
1988 return num;
1989}
1990
1991TreeViewItem* TreeViewItem::getItemOnRow (int index) noexcept
1992{
1993 if (index == 0)
1994 return this;
1995
1996 if (index > 0 && isOpen())
1997 {
1998 --index;
1999
2000 for (auto* i : subItems)
2001 {
2002 if (index == 0)
2003 return i;
2004
2005 auto numRows = i->getNumRows();
2006
2007 if (numRows > index)
2008 return i->getItemOnRow (index);
2009
2010 index -= numRows;
2011 }
2012 }
2013
2014 return nullptr;
2015}
2016
2017int TreeViewItem::countSelectedItemsRecursively (int depth) const noexcept
2018{
2019 int total = isSelected() ? 1 : 0;
2020
2021 if (depth != 0)
2022 for (auto* i : subItems)
2023 total += i->countSelectedItemsRecursively (depth - 1);
2024
2025 return total;
2026}
2027
2028TreeViewItem* TreeViewItem::getSelectedItemWithIndex (int index) noexcept
2029{
2030 if (isSelected())
2031 {
2032 if (index == 0)
2033 return this;
2034
2035 --index;
2036 }
2037
2038 if (index >= 0)
2039 {
2040 for (auto* i : subItems)
2041 {
2042 if (auto* found = i->getSelectedItemWithIndex (index))
2043 return found;
2044
2045 index -= i->countSelectedItemsRecursively (-1);
2046 }
2047 }
2048
2049 return nullptr;
2050}
2051
2053{
2054 if (parentItem != nullptr && ownerView != nullptr)
2055 {
2056 if (! parentItem->isOpen())
2057 return parentItem->getRowNumberInTree();
2058
2059 auto n = 1 + parentItem->getRowNumberInTree();
2060
2061 auto ourIndex = parentItem->subItems.indexOf (this);
2062 jassert (ourIndex >= 0);
2063
2064 while (--ourIndex >= 0)
2065 n += parentItem->subItems [ourIndex]->getNumRows();
2066
2067 if (parentItem->parentItem == nullptr
2068 && ! ownerView->rootItemVisible)
2069 --n;
2070
2071 return n;
2072 }
2073
2074 return 0;
2075}
2076
2078{
2079 drawLinesInside = drawLines;
2080 drawLinesSet = true;
2081}
2082
2083static String escapeSlashesInTreeViewItemName (const String& s)
2084{
2085 return s.replaceCharacter ('/', '\\');
2086}
2087
2089{
2090 String s;
2091
2092 if (parentItem != nullptr)
2093 s = parentItem->getItemIdentifierString();
2094
2095 return s + "/" + escapeSlashesInTreeViewItemName (getUniqueName());
2096}
2097
2098TreeViewItem* TreeViewItem::findItemFromIdentifierString (const String& identifierString)
2099{
2100 auto thisId = "/" + escapeSlashesInTreeViewItemName (getUniqueName());
2101
2102 if (thisId == identifierString)
2103 return this;
2104
2105 if (identifierString.startsWith (thisId + "/"))
2106 {
2107 auto remainingPath = identifierString.substring (thisId.length());
2108
2109 const auto wasOpen = isOpen();
2110 setOpen (true);
2111
2112 for (auto* i : subItems)
2113 if (auto* item = i->findItemFromIdentifierString (remainingPath))
2114 return item;
2115
2116 setOpen (wasOpen);
2117 }
2118
2119 return nullptr;
2120}
2121
2123{
2124 if (e.hasTagName ("CLOSED"))
2125 {
2126 setOpen (false);
2127 }
2128 else if (e.hasTagName ("OPEN"))
2129 {
2130 setOpen (true);
2131
2133 items.addArray (subItems);
2134
2135 for (auto* n : e.getChildIterator())
2136 {
2137 auto id = n->getStringAttribute ("id");
2138
2139 for (int i = 0; i < items.size(); ++i)
2140 {
2141 auto* ti = items.getUnchecked (i);
2142
2143 if (ti->getUniqueName() == id)
2144 {
2145 ti->restoreOpennessState (*n);
2146 items.remove (i);
2147 break;
2148 }
2149 }
2150 }
2151
2152 // for any items that weren't mentioned in the XML, reset them to default:
2153 for (auto* i : items)
2154 i->restoreToDefaultOpenness();
2155 }
2156}
2157
2162
2164{
2165 auto name = getUniqueName();
2166
2167 if (name.isNotEmpty())
2168 {
2170
2171 if (isOpen())
2172 {
2173 if (canReturnNull && ownerView != nullptr && ownerView->defaultOpenness && isFullyOpen())
2174 return nullptr;
2175
2176 e = std::make_unique<XmlElement> ("OPEN");
2177
2178 for (int i = subItems.size(); --i >= 0;)
2179 e->prependChildElement (subItems.getUnchecked (i)->getOpennessState (true).release());
2180 }
2181 else
2182 {
2183 if (canReturnNull && ownerView != nullptr && ! ownerView->defaultOpenness)
2184 return nullptr;
2185
2186 e = std::make_unique<XmlElement> ("CLOSED");
2187 }
2188
2189 e->setAttribute ("id", name);
2190 return e;
2191 }
2192
2193 // trying to save the openness for an element that has no name - this won't
2194 // work because it needs the names to identify what to open.
2196 return {};
2197}
2198
2199//==============================================================================
2200TreeViewItem::OpennessRestorer::OpennessRestorer (TreeViewItem& item)
2201 : treeViewItem (item),
2202 oldOpenness (item.getOpennessState())
2203{
2204}
2205
2206TreeViewItem::OpennessRestorer::~OpennessRestorer()
2207{
2208 if (oldOpenness != nullptr)
2209 treeViewItem.restoreOpennessState (*oldOpenness);
2210}
2211
2212void TreeViewItem::draw (Graphics& g, int width, bool isMouseOverButton)
2213{
2214 if (ownerView == nullptr)
2215 return;
2216
2217 const auto indent = getIndentX();
2218 const auto itemW = (itemWidth < 0 || drawsInRightMargin) ? width - indent : itemWidth;
2219
2220 {
2221 Graphics::ScopedSaveState ss (g);
2222 g.setOrigin (indent, 0);
2223
2224 if (g.reduceClipRegion (drawsInLeftMargin ? -indent : 0, 0,
2225 drawsInLeftMargin ? itemW + indent : itemW, itemHeight))
2226 {
2227 if (isSelected())
2229 else
2230 g.fillAll ((getRowNumberInTree() % 2 == 0) ? ownerView->findColour (TreeView::oddItemsColourId)
2231 : ownerView->findColour (TreeView::evenItemsColourId));
2232
2233 paintItem (g, itemWidth < 0 ? width - indent : itemWidth, itemHeight);
2234 }
2235 }
2236
2237 const auto halfH = (float) itemHeight * 0.5f;
2238 const auto indentWidth = ownerView->getIndentSize();
2239 const auto depth = getItemDepth (this);
2240
2241 if (depth >= 0 && ownerView->openCloseButtonsVisible)
2242 {
2243 auto x = ((float) depth + 0.5f) * (float) indentWidth;
2244 const auto parentLinesDrawn = parentItem != nullptr && parentItem->areLinesDrawn();
2245
2246 if (parentLinesDrawn)
2247 paintVerticalConnectingLine (g, Line<float> (x, 0, x, isLastOfSiblings() ? halfH : (float) itemHeight));
2248
2249 if (parentLinesDrawn || (parentItem == nullptr && areLinesDrawn()))
2250 paintHorizontalConnectingLine (g, Line<float> (x, halfH, x + (float) indentWidth * 0.5f, halfH));
2251
2252 {
2253 auto* p = parentItem;
2254 auto d = depth;
2255
2256 while (p != nullptr && --d >= 0)
2257 {
2258 x -= (float) indentWidth;
2259
2260 if ((p->parentItem == nullptr || p->parentItem->areLinesDrawn()) && ! p->isLastOfSiblings())
2261 p->paintVerticalConnectingLine (g, Line<float> (x, 0, x, (float) itemHeight));
2262
2263 p = p->parentItem;
2264 }
2265 }
2266
2268 {
2269 auto backgroundColour = ownerView->findColour (TreeView::backgroundColourId);
2270
2271 paintOpenCloseButton (g, Rectangle<float> ((float) (depth * indentWidth), 0, (float) indentWidth, (float) itemHeight),
2272 backgroundColour.isTransparent() ? Colours::white : backgroundColour,
2274 }
2275 }
2276}
2277
2278} // namespace juce
A simple wrapper for building a collection of supported accessibility actions and corresponding callb...
An abstract interface which represents a UI element that supports a cell interface.
Base class for accessible Components.
virtual AccessibleState getCurrentState() const
Returns the current state of the UI element.
const Component & getComponent() const noexcept
Returns the Component that this handler represents.
An abstract interface which represents a UI element that supports a table interface.
AccessibleState withExpandable() const noexcept
Sets the expandable flag and returns the new state.
AccessibleState withAccessibleOffscreen() const noexcept
Sets the accessible offscreen flag and returns the new state.
AccessibleState withSelectable() const noexcept
Sets the selectable flag and returns the new state.
AccessibleState withSelected() const noexcept
Sets the selected flag and returns the new state.
AccessibleState withExpanded() const noexcept
Sets the expanded flag and returns the new state.
AccessibleState withCollapsed() const noexcept
Sets the collapsed flag and returns the new state.
AccessibleState withMultiSelectable() const noexcept
Sets the multiSelectable flag and returns the new state.
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
ElementType getUnchecked(int index) const
Returns one of the elements in the array, without checking the index passed in.
Definition juce_Array.h:252
void addArray(const Type *elementsToAdd, int numElementsToAdd)
Adds elements from an array to the end of this array.
Definition juce_Array.h:583
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:215
void remove(int indexToRemove)
Removes an element from the array.
Definition juce_Array.h:742
Has a callback method that is triggered asynchronously.
void triggerAsyncUpdate()
Causes the callback to be triggered at a later time.
Represents a colour, also including a transparency value.
Definition juce_Colour.h:38
The base class for all JUCE user-interface objects.
void setInterceptsMouseClicks(bool allowClicksOnThisComponent, bool allowClicksOnChildComponents) noexcept
Changes the default return value for the hitTest() method.
Component * getParentComponent() const noexcept
Returns the component which this component is inside.
Image createComponentSnapshot(Rectangle< int > areaToGrab, bool clipImageToComponentBounds=true, float scaleFactor=1.0f)
Generates a snapshot of part of this component.
bool isOpaque() const noexcept
Returns true if no parts of this component are transparent.
void setFocusContainerType(FocusContainerType containerType) noexcept
Sets whether this component is a container for components that can have their focus traversed,...
int getHeight() const noexcept
Returns the component's height in pixels.
static float JUCE_CALLTYPE getApproximateScaleFactorForComponent(const Component *targetComponent)
Returns the approximate scale factor for a given component by traversing its parent hierarchy and app...
void addAndMakeVisible(Component *child, int zOrder=-1)
Adds a child component to this one, and also makes the child visible if it isn't already.
void setAlwaysOnTop(bool shouldStayOnTop)
Sets whether the component should always be kept at the front of its siblings.
AccessibilityHandler * getAccessibilityHandler()
Returns the accessibility handler for this component, or nullptr if this component is not accessible.
void setOpaque(bool shouldBeOpaque)
Indicates whether any parts of the component might be transparent.
void repaint()
Marks the whole component as needing to be redrawn.
Component() noexcept
Creates a component.
@ focusContainer
The component will act as a top-level component within which focus is passed around.
Point< int > getMouseXYRelative() const
Returns the mouse's current position, relative to this component.
int getY() const noexcept
Returns the y coordinate of the top of this component.
bool isParentOf(const Component *possibleChild) const noexcept
Checks whether a component is anywhere inside this component or its children.
void setBounds(int x, int y, int width, int height)
Changes the component's position and size.
void setSize(int newWidth, int newHeight)
Changes the size of the component.
void setWantsKeyboardFocus(bool wantsFocus) noexcept
Sets a flag to indicate whether this component wants keyboard focus or not.
Colour findColour(int colourID, bool inheritFromParent=false) const
Looks for a colour that has been registered with the given colour ID number.
int getWidth() const noexcept
Returns the component's width in pixels.
bool isEnabled() const noexcept
Returns true if the component (and all its parents) are enabled.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
Rectangle< int > getLocalBounds() const noexcept
Returns the component's bounds, relative to its own origin.
int getParentHeight() const noexcept
Returns the height of the component's parent.
MouseInputSource getMainMouseSource() const noexcept
Returns the main mouse input device that the system is using.
static Desktop &JUCE_CALLTYPE getInstance()
There's only one desktop object, and this method will return it.
static DragAndDropContainer * findParentDragContainerFor(Component *childComponent)
Utility to find the DragAndDropContainer for a given Component.
Contains details about the source of a drag-and-drop operation.
A graphics context, used for drawing a component or image.
void setColour(Colour newColour)
Changes the current drawing colour.
void drawLine(float startX, float startY, float endX, float endY) const
Draws a line between two points.
void strokePath(const Path &path, const PathStrokeType &strokeType, const AffineTransform &transform={}) const
Draws a path's outline using the currently selected colour or brush.
void fillAll() const
Fills the context's entire clip region with the current colour or brush.
void drawRoundedRectangle(float x, float y, float width, float height, float cornerSize, float lineThickness) const
Uses the current colour or brush to draw the outline of a rectangle with rounded corners.
Represents a key press, including any modifier keys that are needed.
static const int homeKey
key-code for the home key
static const int upKey
key-code for the cursor-up key
static const int endKey
key-code for the end key
static const int rightKey
key-code for the cursor-right key
static const int downKey
key-code for the cursor-down key
static const int returnKey
key-code for the return key
static const int leftKey
key-code for the cursor-left key
static const int pageUpKey
key-code for the page-up key
static const int pageDownKey
key-code for the page-down key
Represents a line.
Definition juce_Line.h:47
@ leftButtonModifier
Left mouse button flag.
@ popupMenuClickModifier
Popup menu flag - on windows this is the same as rightButtonModifier, on the Mac it's the same as (ri...
Contains position and status information about a mouse event.
MouseEvent getEventRelativeTo(Component *newComponent) const noexcept
Creates a version of this event that is relative to a different component.
static constexpr float defaultRotation
A default value for rotation, which is used when a device doesn't support it.
static constexpr float defaultTiltX
Default values for tilt, which are used when a device doesn't support it.
static constexpr float defaultOrientation
A default value for orientation, which is used when a device doesn't support it.
static constexpr float defaultPressure
A default value for pressure, which is used when a device doesn't support it, or for mouse-moves,...
A simple optional type.
Describes a type of stroke used to render a solid outline along a path.
A path is a sequence of lines and curves that may either form a closed shape or be open-ended.
Definition juce_Path.h:65
void startNewSubPath(float startX, float startY)
Begins a new subpath with a given starting position.
void addEllipse(float x, float y, float width, float height)
Adds an ellipse to the path.
void lineTo(float endX, float endY)
Adds a line from the shape's last position to a new end-point.
A pair of (x, y) coordinates.
Definition juce_Point.h:42
ValueType y
The point's Y coordinate.
Definition juce_Point.h:252
ValueType x
The point's X coordinate.
Definition juce_Point.h:251
Manages a rectangle and allows geometric operations to be performed on it.
Rectangle withHeight(ValueType newHeight) const noexcept
Returns a rectangle which has the same position and width as this one, but with a different height.
Rectangle< float > toFloat() const noexcept
Casts this rectangle to a Rectangle<float>.
Point< ValueType > getTopLeft() const noexcept
Returns the rectangle's top-left position as a Point.
Rectangle withWidth(ValueType newWidth) const noexcept
Returns a rectangle which has the same position and height as this one, but with a different width.
String getTooltip() override
Returns the tooltip assigned to this object.
A non-owning view over contiguous objects stored in an Array or vector or other similar container.
Definition juce_Span.h:96
A special array for holding a list of strings.
int size() const noexcept
Returns the number of strings in the array.
The JUCE String class!
Definition juce_String.h:53
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
Returns a string with all occurrences of a character replaced with a different one.
static Time JUCE_CALLTYPE getCurrentTime() noexcept
Returns a Time object that is set to the current system time.
Components that want to use pop-up tooltips should implement this interface.
An item in a TreeView.
virtual bool canBeSelected() const
You can override this method to return false if you don't want to allow the user to select this item.
TreeView * getOwnerView() const noexcept
Returns the TreeView to which this item belongs.
virtual String getUniqueName() const
Returns a string to uniquely identify this item.
void removeSubItem(int index, bool deleteItem=true)
Removes one of the sub-items.
bool isSelected() const noexcept
True if this item is currently selected.
void setDrawsInRightMargin(bool canDrawInRightMargin) noexcept
Sets a flag to indicate that the item wants to be allowed to draw all the way across to the right edg...
void clearSubItems()
Removes any sub-items.
virtual void itemDropped(const DragAndDropTarget::SourceDetails &dragSourceDetails, int insertIndex)
When a things are dropped into this item, this callback is invoked.
void setDrawsInLeftMargin(bool canDrawInLeftMargin) noexcept
Sets a flag to indicate that the item wants to be allowed to draw all the way across to the left edge...
int getNumSubItems() const noexcept
Returns the number of sub-items that have been added to this item.
virtual bool mightContainSubItems()=0
Tells the tree whether this item can potentially be opened.
virtual void itemClicked(const MouseEvent &)
Called when the user clicks on this item.
void setOpenness(Openness newOpenness)
Opens or closes the item.
int getIndexInParent() const noexcept
Returns the index of this item in its parent's sub-items.
virtual void paintItem(Graphics &g, int width, int height)
Draws the item's contents.
virtual void itemDoubleClicked(const MouseEvent &)
Called when the user double-clicks on this item.
virtual int getItemWidth() const
Must return the width required by this item.
void restoreOpennessState(const XmlElement &xml)
Restores the openness of this item and all its sub-items from a saved state.
virtual bool isInterestedInDragSource(const DragAndDropTarget::SourceDetails &dragSourceDetails)
If you want your item to act as a DragAndDropTarget, implement this method and return true.
virtual void paintOpenCloseButton(Graphics &, const Rectangle< float > &area, Colour backgroundColour, bool isMouseOver)
Draws the item's open/close button.
virtual void paintHorizontalConnectingLine(Graphics &, const Line< float > &line)
Draws the line that connects this item to the vertical line extending below its parent.
virtual void itemOpennessChanged(bool isNowOpen)
Called when an item is opened or closed.
bool isOpen() const noexcept
True if this item is currently open in the TreeView.
void setLinesDrawnForSubItems(bool shouldDrawLines) noexcept
Changes whether lines are drawn to connect any sub-items to this item.
void treeHasChanged() const noexcept
Sends a signal to the TreeView to make it refresh itself.
int getRowNumberInTree() const noexcept
Returns the row number of this item in the tree.
void setOpen(bool shouldBeOpen)
Opens or closes the item.
TreeViewItem()
Constructor.
void repaintItem() const
Sends a repaint message to redraw just this item.
Openness getOpenness() const noexcept
Returns the openness state of this item.
virtual int getItemHeight() const
Must return the height required by this item.
virtual bool isInterestedInFileDrag(const StringArray &files)
If you want your item to be able to have files drag-and-dropped onto it, implement this method and re...
std::unique_ptr< XmlElement > getOpennessState() const
Saves the current state of open/closed nodes so it can be restored later.
virtual ~TreeViewItem()
Destructor.
virtual void ownerViewChanged(TreeView *newOwner)
Called when the owner view changes.
virtual void itemSelectionChanged(bool isNowSelected)
Called when the item is selected or deselected.
virtual void filesDropped(const StringArray &files, int insertIndex)
When files are dropped into this item, this callback is invoked.
virtual String getTooltip()
The item can return a tool tip string here if it wants to.
void addSubItem(TreeViewItem *newItem, int insertPosition=-1)
Adds a sub-item.
bool isLastOfSiblings() const noexcept
Returns true if this item is the last of its parent's sub-items.
TreeViewItem * getSubItem(int index) const noexcept
Returns one of the item's sub-items.
virtual String getAccessibilityName()
Use this to set the name for this item that will be read out by accessibility clients.
TreeViewItem * getParentItem() const noexcept
Returns the item within which this item is contained.
Openness
An enum of states to describe the explicit or implicit openness of an item.
String getItemIdentifierString() const
Creates a string that can be used to uniquely retrieve this item in the tree.
virtual var getDragSourceDescription()
To allow items from your TreeView to be dragged-and-dropped, implement this method.
Rectangle< int > getItemPosition(bool relativeToTreeViewTopLeft) const noexcept
Returns the rectangle that this item occupies.
virtual void paintVerticalConnectingLine(Graphics &, const Line< float > &line)
Draws the line that extends vertically up towards one of its parents, or down to one of its children.
bool areAllParentsOpen() const noexcept
Returns true if all the item's parent nodes are open.
void setSelected(bool shouldBeSelected, bool deselectOtherItemsFirst, NotificationType shouldNotify=sendNotification)
Selects or deselects the item.
void mouseDoubleClick(const MouseEvent &e) override
Called when a mouse button has been double-clicked on a component.
void mouseDown(const MouseEvent &e) override
Called when a mouse button is pressed.
void mouseMove(const MouseEvent &e) override
Called when the mouse moves inside a component.
void mouseDrag(const MouseEvent &e) override
Called when the mouse is moved while a button is held down.
String getTooltip() override
Returns the string that this object wants to show as its tooltip.
void mouseUp(const MouseEvent &e) override
Called when a mouse button is released.
void resized() override
Called when this component's size has been changed.
void mouseExit(const MouseEvent &e) override
Called when the mouse moves out of a component.
void paint(Graphics &g) override
Components can override this method to draw their content.
std::vector< const AccessibilityHandler * > getDisclosedRows() const override
Returns a list of the accessibility elements that are disclosed by this element, if any.
const AccessibilityHandler * getTableHandler() const override
Returns the AccessibilityHandler of the table which contains the cell.
int getDisclosureLevel() const override
Returns the indentation level for the cell.
void paint(Graphics &g) override
Components can override this method to draw their content.
void resized() override
Called when this component's size has been changed.
String getTooltip() override
Returns the string that this object wants to show as its tooltip.
void paint(Graphics &g) override
Components can override this method to draw their content.
void visibleAreaChanged(const Rectangle< int > &newVisibleArea) override
Callback method that is called when the visible area changes.
bool keyPressed(const KeyPress &key) override
Called when a key is pressed.
A tree-view component.
TreeViewItem * findItemFromIdentifierString(const String &identifierString) const
Searches the tree for an item with the specified identifier.
bool isInterestedInFileDrag(const StringArray &) override
Callback to check whether this target is interested in the set of files being offered.
void scrollToKeepItemVisible(const TreeViewItem *item)
Tries to scroll the tree so that this item is on-screen somewhere.
TreeViewItem * getSelectedItem(int index) const noexcept
Returns one of the selected items in the tree.
Component * getItemComponent(const TreeViewItem *item) const
Returns the component that currently represents a given TreeViewItem.
void filesDropped(const StringArray &, int, int) override
Callback to indicate that the user has dropped the files onto this component.
TreeViewItem * getItemOnRow(int index) const
Returns the item on a particular row of the tree.
void colourChanged() override
This method is called when a colour is changed by the setColour() method, or when the look-and-feel i...
@ dragAndDropIndicatorColourId
The colour to use for the drag-and-drop target position indicator.
@ oddItemsColourId
The colour to use to fill the background of the odd numbered items.
@ linesColourId
The colour to draw the lines with.
@ selectedItemBackgroundColourId
The colour to use to fill the background of any selected items.
@ backgroundColourId
A background colour to fill the component with.
TreeViewItem * getRootItem() const noexcept
Returns the tree's root item.
void moveSelectedRow(int deltaRows)
Moves the selected row up or down by the specified number of rows.
void setDefaultOpenness(bool isOpenByDefault)
Sets whether items are open or closed by default.
int getNumRowsInTree() const
Returns the number of rows the tree is using, depending on which items are open.
TreeView(const String &componentName={})
Creates an empty TreeView.
TreeViewItem * getItemAt(int yPosition) const noexcept
Returns the item that contains a given y-position relative to the top of the TreeView component.
Viewport * getViewport() const noexcept
Returns the TreeView's Viewport object.
void fileDragMove(const StringArray &, int, int) override
Callback to indicate that the user is dragging some files over this component.
void clearSelectedItems()
Deselects any items that are currently selected.
void resized() override
Called when this component's size has been changed.
std::unique_ptr< AccessibilityHandler > createAccessibilityHandler() override
Override this method to return a custom AccessibilityHandler for this component.
void paint(Graphics &) override
Components can override this method to draw their content.
void itemDragEnter(const SourceDetails &) override
Callback to indicate that something is being dragged over this component.
void restoreOpennessState(const XmlElement &newState, bool restoreStoredSelection)
Restores a previously saved arrangement of open/closed nodes.
bool isInterestedInDragSource(const SourceDetails &) override
Callback to check whether this target is interested in the type of object being dragged.
std::unique_ptr< XmlElement > getOpennessState(bool alsoIncludeScrollPosition) const
Saves the current state of open/closed nodes so it can be restored later.
bool keyPressed(const KeyPress &) override
Called when a key is pressed.
void fileDragEnter(const StringArray &, int, int) override
Callback to indicate that some files are being dragged over this component.
bool isMultiSelectEnabled() const noexcept
Returns whether multi-select has been enabled for the tree.
int getNumSelectedItems(int maximumDepthToSearchTo=-1) const noexcept
Returns the number of items that are currently selected.
void itemDropped(const SourceDetails &) override
Callback to indicate that the user has dropped something onto this component.
void enablementChanged() override
Callback to indicate that this component has been enabled or disabled.
void itemDragMove(const SourceDetails &) override
Callback to indicate that the user is dragging something over this component.
void setIndentSize(int newIndentSize)
Changes the distance by which each nested level of the tree is indented.
void fileDragExit(const StringArray &) override
Callback to indicate that the mouse has moved away from this component.
int getIndentSize() noexcept
Returns the number of pixels by which each nested level of the tree is indented.
void setOpenCloseButtonsVisible(bool shouldBeVisible)
Sets a flag to indicate whether to hide the open/close buttons.
void deleteRootItem()
This will remove and delete the current root item.
~TreeView() override
Destructor.
void setMultiSelectEnabled(bool canMultiSelect)
This sets a flag to indicate that the tree can be used for multi-selection.
void itemDragExit(const SourceDetails &) override
Callback to indicate that something has been dragged off the edge of this component.
void setRootItem(TreeViewItem *newRootItem)
Sets the item that is displayed in the TreeView.
void setRootItemVisible(bool shouldBeVisible)
Changes whether the tree's root item is shown or not.
A Viewport is used to contain a larger child component, and allows the child to be automatically scro...
Component * getViewedComponent() const noexcept
Returns the component that's currently being used inside the Viewport.
bool keyPressed(const KeyPress &) override
Called when a key is pressed.
void setViewPosition(int xPixelsOffset, int yPixelsOffset)
Changes the position of the viewed component.
int getMaximumVisibleWidth() const
Returns the width available within this component for the contents.
Used to build a tree of elements representing an XML document.
XmlElement * createNewChildElement(StringRef tagName)
Creates a new element with the given name and returns it, after adding it as a child element.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
Iterator< GetNextElement > getChildIterator() const
Allows iterating the children of an XmlElement using range-for syntax.
void setAttribute(const Identifier &attributeName, const String &newValue)
Adds a named attribute to the element.
A variant class, that can be used to hold a range of primitive values.
T count(T... args)
T distance(T... args)
T exchange(T... args)
T find_if(T... args)
T insert(T... args)
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
T lower_bound(T... args)
typedef float
T max(T... args)
JUCE Namespace.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
@ rowSelectionChanged
Indicates that the selection of rows in a list or table has changed.
@ structureChanged
Indicates that the structure of the UI elements has changed in a significant way.
NotificationType
These enums are used in various classes to indicate whether a notification event should be sent out.
@ dontSendNotification
No notification message should be sent.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
@ showMenu
Represents the user showing a contextual menu for a UI element.
@ focus
Indicates that the UI element has received focus.
@ toggle
Represents a "toggle" action.
@ press
Represents a "press" action.
AccessibilityRole
The list of available roles for an AccessibilityHandler object.
T next(T... args)
T prev(T... args)
T push_back(T... args)
T remove_if(T... args)
T reserve(T... args)
Utility struct which holds one or more accessibility interfaces.
T swap(T... args)
T upper_bound(T... args)