From 20e50cf90fd70da16a224ab8ea22a7c9ff3071ea Mon Sep 17 00:00:00 2001 From: alcomposer <alex.w.mitchell@gmail.com> Date: Tue, 12 Sep 2023 02:17:04 +0930 Subject: [PATCH 1/5] small cleanup splitview - fix off-by-1 error in splitview, make focus-outline it's own component --- Source/CanvasViewport.h | 6 +++ Source/PluginEditor.cpp | 2 +- Source/ResizableTabbedComponent.cpp | 38 ++++++-------- Source/ResizableTabbedComponent.h | 2 - Source/SplitView.cpp | 81 ++++++++++++----------------- Source/SplitView.h | 12 ++--- Source/Tabbar.cpp | 9 ++++ Source/Tabbar.h | 2 + 8 files changed, 71 insertions(+), 81 deletions(-) diff --git a/Source/CanvasViewport.h b/Source/CanvasViewport.h index 0a5f9bdcb..95aff5c68 100644 --- a/Source/CanvasViewport.h +++ b/Source/CanvasViewport.h @@ -46,6 +46,9 @@ class CanvasViewport : public Viewport { e.originalComponent->setMouseCursor(MouseCursor::DraggingHandCursor); downPosition = viewport->getViewPosition(); downCanvasOrigin = viewport->cnv->canvasOrigin; + + for (auto* object : viewport->cnv->objects) + object->setBufferedToImage(true); } void mouseDrag(MouseEvent const& e) override @@ -59,6 +62,9 @@ class CanvasViewport : public Viewport { void mouseUp(MouseEvent const& e) override { e.originalComponent->setMouseCursor(MouseCursor::NormalCursor); + for (auto* object : viewport->cnv->objects){ + object->setBufferedToImage(false); + } } private: diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 9570dba18..be835b67c 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -383,7 +383,7 @@ void PluginEditor::resized() palettes->setBounds(0, toolbarHeight, palettes->getWidth(), getHeight() - toolbarHeight - (statusbar->getHeight())); - splitView.setBounds(paletteWidth, toolbarHeight, (getWidth() - sidebar->getWidth() - paletteWidth) + 1, getHeight() - toolbarHeight - Statusbar::statusbarHeight); + splitView.setBounds(paletteWidth, toolbarHeight, (getWidth() - sidebar->getWidth() - paletteWidth), getHeight() - toolbarHeight - Statusbar::statusbarHeight); sidebar->setBounds(getWidth() - sidebar->getWidth(), toolbarHeight, sidebar->getWidth(), getHeight() - toolbarHeight - Statusbar::statusbarHeight); statusbar->setBounds(0, getHeight() - Statusbar::statusbarHeight, getWidth(), statusbar->getHeight()); diff --git a/Source/ResizableTabbedComponent.cpp b/Source/ResizableTabbedComponent.cpp index 0f0d81ae6..0a8f2102b 100644 --- a/Source/ResizableTabbedComponent.cpp +++ b/Source/ResizableTabbedComponent.cpp @@ -15,7 +15,6 @@ #include "Utility/ObjectDragAndDrop.h" - #define ENABLE_SPLITS_DROPZONE_DEBUGGING 0 ResizableTabbedComponent::ResizableTabbedComponent(PluginEditor* editor, TabComponent* mainTabComponent) @@ -266,19 +265,11 @@ int ResizableTabbedComponent::findZoneFromSource(SourceDetails const& dragSource return -1; } -void ResizableTabbedComponent::mouseDrag(MouseEvent const& e) -{ -} - void ResizableTabbedComponent::mouseDown(MouseEvent const& e) { editor->splitView.setFocus(this); } -void ResizableTabbedComponent::mouseMove(MouseEvent const& e) -{ -} - void ResizableTabbedComponent::setBoundsWithFactors(Rectangle<int> bounds) { if (resizerLeft) @@ -373,6 +364,7 @@ void ResizableTabbedComponent::paintOverChildren(Graphics& g) void ResizableTabbedComponent::itemDragEnter(SourceDetails const& dragSourceDetails) { + editor->splitView.setFocus(this); // if we are dragging a tabbar, update the highlight split if(dynamic_cast<TabBarButtonComponent*>(dragSourceDetails.sourceComponent.get())) { isDragAndDropOver = true; @@ -383,27 +375,21 @@ void ResizableTabbedComponent::itemDragEnter(SourceDetails const& dragSourceDeta void ResizableTabbedComponent::itemDragExit(SourceDetails const& dragSourceDetails) { - isDragAndDropOver = false; - repaint(); + if(dynamic_cast<TabBarButtonComponent*>(dragSourceDetails.sourceComponent.get())) { + isDragAndDropOver = false; + repaint(); + } } void ResizableTabbedComponent::itemDragMove(SourceDetails const& dragSourceDetails) { - activeZone = DropZones::None; - // if we are dragging from a palette or automation item, highlight the dragged over split - if (dynamic_cast<ObjectDragAndDrop*>(dragSourceDetails.sourceComponent.get())) { - isDragAndDropOver = false; - editor->splitView.setFocus(this); - } // if we are dragging a tabbed window or from the document browser - else if (auto sourceTabButton = static_cast<TabBarButtonComponent*>(dragSourceDetails.sourceComponent.get())) { + if (auto sourceTabButton = dynamic_cast<TabBarButtonComponent*>(dragSourceDetails.sourceComponent.get())) { auto sourceTabContent = sourceTabButton->getTabComponent(); int sourceNumTabs = sourceTabContent->getNumTabs(); auto zone = findZoneFromSource(dragSourceDetails); - editor->splitView.setFocus(this); - if (editor->splitView.canSplit() && sourceNumTabs > 1) { if (activeZone != zone) { activeZone = zone; @@ -411,8 +397,16 @@ void ResizableTabbedComponent::itemDragMove(SourceDetails const& dragSourceDetai // std::cout << "dragging over: " << getZoneName(zone) << std::endl; } } else if (sourceTabButton->getTabComponent() != tabComponent.get()) { - activeZone = zone == DropZones::TabBar ? DropZones::None : DropZones::Centre; - repaint(); + auto foundZone = zone == DropZones::TabBar ? DropZones::None : DropZones::Centre; + if (activeZone != foundZone){ + activeZone = foundZone; + repaint(); + } + } else if (sourceTabButton->getTabComponent() == tabComponent.get()) { + if (activeZone != DropZones::None) { + activeZone = DropZones::None; + repaint(); + } } } } diff --git a/Source/ResizableTabbedComponent.h b/Source/ResizableTabbedComponent.h index 7a4bac2d3..9cb6d2376 100644 --- a/Source/ResizableTabbedComponent.h +++ b/Source/ResizableTabbedComponent.h @@ -20,9 +20,7 @@ class ResizableTabbedComponent : public Component ~ResizableTabbedComponent(); - void mouseDrag(MouseEvent const& e) override; void mouseDown(MouseEvent const& e) override; - void mouseMove(MouseEvent const& e) override; void resized() override; void paintOverChildren(Graphics& g) override; diff --git a/Source/SplitView.cpp b/Source/SplitView.cpp index a90052270..d1f8b4d67 100644 --- a/Source/SplitView.cpp +++ b/Source/SplitView.cpp @@ -14,63 +14,45 @@ #include "PluginProcessor.h" #include "Sidebar/Sidebar.h" -class FadeAnimation : private Timer { +class SplitViewFocusOutline : public Component + , public ComponentListener { public: - explicit FadeAnimation(SplitView* splitView) - : splitView(splitView) + SplitViewFocusOutline() { + setInterceptsMouseClicks(false, false); } - float fadeIn() + void setActive(ResizableTabbedComponent* tabComponent) { - targetAlpha = 0.3f; - if (!isTimerRunning() && currentAlpha < targetAlpha) - startTimerHz(60); + if (tabbedComponent != tabComponent) { + if (tabbedComponent) + tabbedComponent->removeComponentListener(this); - return currentAlpha; + tabComponent->addComponentListener(this); + setBounds(tabComponent->getBounds()); + tabbedComponent = tabComponent; + } } - float fadeOut() + void componentMovedOrResized(Component& component, bool moved, bool resized) override { - targetAlpha = 0.0f; - if (!isTimerRunning() && currentAlpha > targetAlpha) - startTimerHz(60); - - return currentAlpha; + if (&component == tabbedComponent) { + setBounds(component.getBounds()); + } } -private: - void timerCallback() override + void paint(Graphics& g) { - float const stepSize = 0.025f; - if (targetAlpha > currentAlpha) { - currentAlpha += stepSize; - if (currentAlpha >= targetAlpha) { - currentAlpha = targetAlpha; - stopTimer(); - } - } else if (targetAlpha < currentAlpha) { - currentAlpha -= stepSize; - if (currentAlpha <= targetAlpha) { - currentAlpha = targetAlpha; - stopTimer(); - } - } - if (splitView != nullptr) - splitView->repaint(); + g.setColour(findColour(PlugDataColour::objectSelectedOutlineColourId).withAlpha(0.3f)); + g.drawRect(getLocalBounds(), 2.5f); } private: - SplitView* splitView; - float currentAlpha = 0.0f; - float targetAlpha = 0.0f; + SafePointer<ResizableTabbedComponent> tabbedComponent; }; SplitView::SplitView(PluginEditor* parent) : editor(parent) - , fadeAnimation(new FadeAnimation(this)) - , fadeAnimationLeft(new FadeAnimation(this)) - , fadeAnimationRight(new FadeAnimation(this)) { rootComponent = new ResizableTabbedComponent(editor); splits.add(rootComponent); @@ -80,6 +62,10 @@ SplitView::SplitView(PluginEditor* parent) // either we check if the tabcomponent is welcome mode, or we check if it's nullptr down the line activeTabComponent = rootComponent; + focusOutline = std::make_unique<SplitViewFocusOutline>(); + addChildComponent(focusOutline.get()); + focusOutline->setAlwaysOnTop(true); + addMouseListener(this, true); } @@ -117,6 +103,9 @@ void SplitView::removeSplit(TabComponent* toRemove) } } delete toBeRemoved; + + if (splits.size() == 1) + focusOutline->setVisible(false); } void SplitView::addSplit(ResizableTabbedComponent* split) @@ -166,7 +155,11 @@ void SplitView::setFocus(ResizableTabbedComponent* selectedTabComponent) { if (activeTabComponent != selectedTabComponent) { activeTabComponent = selectedTabComponent; - repaint(); + if (splits.size() > 1) { + focusOutline->setActive(activeTabComponent); + focusOutline->setVisible(true); + } else + focusOutline->setVisible(false); } } @@ -197,16 +190,6 @@ void SplitView::closeEmptySplits() } } -void SplitView::paintOverChildren(Graphics& g) -{ - if (splits.size() > 1) { - g.setColour(findColour(PlugDataColour::objectSelectedOutlineColourId).withAlpha(0.3f)); - auto screenBounds = activeTabComponent->getScreenBounds(); - auto b = getLocalArea(nullptr, screenBounds); - g.drawRect(b, 2.5f); - } -} - ResizableTabbedComponent* SplitView::getSplitAtScreenPosition(Point<int> position) { for (auto* split : splits) { diff --git a/Source/SplitView.h b/Source/SplitView.h index b79b11ab6..5207a6a5b 100644 --- a/Source/SplitView.h +++ b/Source/SplitView.h @@ -6,12 +6,14 @@ #pragma once +#include <JuceHeader.h> + #include "SplitViewResizer.h" #include "ResizableTabbedComponent.h" class PluginEditor; class Canvas; -class FadeAnimation; +class SplitViewFocusOutline; class SplitView : public Component { public: explicit SplitView(PluginEditor* parent); @@ -31,8 +33,6 @@ class SplitView : public Component { void closeEmptySplits(); - void paintOverChildren(Graphics& g) override; - int getTabComponentSplitIndex(TabComponent* tabComponent); ResizableTabbedComponent* getSplitAtScreenPosition(Point<int> position); @@ -52,12 +52,10 @@ class SplitView : public Component { SafePointer<ResizableTabbedComponent> activeTabComponent = nullptr; ResizableTabbedComponent* rootComponent; - std::unique_ptr<FadeAnimation> fadeAnimation; - std::unique_ptr<FadeAnimation> fadeAnimationLeft; - std::unique_ptr<FadeAnimation> fadeAnimationRight; - bool splitviewIndicator = false; + std::unique_ptr<SplitViewFocusOutline> focusOutline; + PluginEditor* editor; std::unique_ptr<Component> splitViewResizer; diff --git a/Source/Tabbar.cpp b/Source/Tabbar.cpp index f6a16f982..47730e9b8 100644 --- a/Source/Tabbar.cpp +++ b/Source/Tabbar.cpp @@ -165,6 +165,7 @@ void ButtonBar::itemDragEnter(SourceDetails const& dragSourceDetails) { if (auto* tab = dynamic_cast<TabBarButtonComponent*>(dragSourceDetails.sourceComponent.get())) { ghostTabAnimator.cancelAllAnimations(false); + owner.setFocused(); // if this tabbar is DnD on itself, we don't need to add a new tab // we move the existing tab if (tab->getTabComponent() == &owner) { @@ -293,6 +294,14 @@ TabComponent::~TabComponent() tabs.reset(); } +void TabComponent::setFocused() +{ + for (auto * split : editor->splitView.splits){ + if (split->getTabComponent() == this) + editor->splitView.setFocus(split); + } +} + int TabComponent::getCurrentTabIndex() { return tabs->getCurrentTabIndex(); diff --git a/Source/Tabbar.h b/Source/Tabbar.h index 5f7697fd9..13e3a9b46 100644 --- a/Source/Tabbar.h +++ b/Source/Tabbar.h @@ -284,6 +284,8 @@ class TabComponent : public Component Canvas* getCurrentCanvas(); + void setFocused(); + PluginEditor* getEditor(); Image tabSnapshot; From 1745fe3a832a146a5a7981e35cdf1834ea5d05e3 Mon Sep 17 00:00:00 2001 From: alcomposer <alex.w.mitchell@gmail.com> Date: Tue, 12 Sep 2023 07:54:12 +0930 Subject: [PATCH 2/5] adding tab's add's - not subtracts --- Source/SplitView.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Source/SplitView.cpp b/Source/SplitView.cpp index d1f8b4d67..bd01f8204 100644 --- a/Source/SplitView.cpp +++ b/Source/SplitView.cpp @@ -24,6 +24,8 @@ class SplitViewFocusOutline : public Component void setActive(ResizableTabbedComponent* tabComponent) { + setVisible(true); + if (tabbedComponent != tabComponent) { if (tabbedComponent) tabbedComponent->removeComponentListener(this); @@ -155,11 +157,7 @@ void SplitView::setFocus(ResizableTabbedComponent* selectedTabComponent) { if (activeTabComponent != selectedTabComponent) { activeTabComponent = selectedTabComponent; - if (splits.size() > 1) { - focusOutline->setActive(activeTabComponent); - focusOutline->setVisible(true); - } else - focusOutline->setVisible(false); + focusOutline->setActive(activeTabComponent); } } From 787479d732b42a0b613a46eae16562c15e829d76 Mon Sep 17 00:00:00 2001 From: alcomposer <alex.w.mitchell@gmail.com> Date: Tue, 12 Sep 2023 16:57:57 +0930 Subject: [PATCH 3/5] fix [hack] for crash when dragging a tab to the right edge of a tabbar from another tabbar. We need to have our own TabButtonBar, that is able to work correctly with DnD & Overflow This fix forces all tabs dragged into the right edge of a tabbar to be added before the last. Which stops the index being incorrect if the the tabbar goes into overflow. --- Source/Tabbar.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Source/Tabbar.cpp b/Source/Tabbar.cpp index 47730e9b8..634255e19 100644 --- a/Source/Tabbar.cpp +++ b/Source/Tabbar.cpp @@ -25,8 +25,12 @@ class ButtonBar::GhostTab : public Component { void setTabButtonToGhost(TabBarButton* tabButton) { tab = tabButton; - setBounds(tab->getBounds()); - repaint(); + // this should never happen, if it does then the index for the tab is wrong ( getTab(idx) will return nullptr ) + // which can happen if the tabbar goes into overflow, because we don't know exactly when that will happen + if (tab){ + setBounds(tab->getBounds()); + repaint(); + } } int getIndex() @@ -181,10 +185,14 @@ void ButtonBar::itemDragEnter(SourceDetails const& dragSourceDetails) // WARNING: because we are using the overflow (show extra items menu) // we need to find out how many tabs are visible, not how many there are all together auto targetTabPos = getWidth() / (getNumVisibleTabs() + 1); - auto tabPos = dragSourceDetails.localPosition.getX() / targetTabPos; + // FIXME: This is a hack. When tab is added to tabbar right edge, and it goes into overflow + // we don't know when that will happen. + // So we force the right most tab to think it's always one less. + auto tabPos = jmin(dragSourceDetails.localPosition.getX() / targetTabPos, getNumVisibleTabs() - 1); inOtherSplit = true; auto unusedComponent = std::make_unique<Component>(); owner.addTab(tab->getButtonText(), unusedComponent.get(), tabPos); + auto* fakeTab = getTabButton(tabPos); tab->getProperties().set("dragged", var(true)); ghostTab->setTabButtonToGhost(fakeTab); From 439d27b14ad21a1eeedd2d58fb8c89348ef89c29 Mon Sep 17 00:00:00 2001 From: alcomposer <alex.w.mitchell@gmail.com> Date: Tue, 12 Sep 2023 17:12:30 +0930 Subject: [PATCH 4/5] overflow menu can reduce the 4 tabs to 2 in some geometry situations, not 3, so take that into account --- Source/Tabbar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tabbar.cpp b/Source/Tabbar.cpp index 634255e19..34d1c28bb 100644 --- a/Source/Tabbar.cpp +++ b/Source/Tabbar.cpp @@ -188,7 +188,7 @@ void ButtonBar::itemDragEnter(SourceDetails const& dragSourceDetails) // FIXME: This is a hack. When tab is added to tabbar right edge, and it goes into overflow // we don't know when that will happen. // So we force the right most tab to think it's always one less. - auto tabPos = jmin(dragSourceDetails.localPosition.getX() / targetTabPos, getNumVisibleTabs() - 1); + auto tabPos = jmin(dragSourceDetails.localPosition.getX() / targetTabPos, getNumVisibleTabs() - 2); inOtherSplit = true; auto unusedComponent = std::make_unique<Component>(); owner.addTab(tab->getButtonText(), unusedComponent.get(), tabPos); From 7247b8cf4ce2d9803b8f38ce1d8b6f25ee634717 Mon Sep 17 00:00:00 2001 From: alcomposer <alex.w.mitchell@gmail.com> Date: Wed, 13 Sep 2023 00:12:21 +0930 Subject: [PATCH 5/5] we also need to make sure the tabPos doens't go below 0 --- Source/Tabbar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Tabbar.cpp b/Source/Tabbar.cpp index 34d1c28bb..01a6383d5 100644 --- a/Source/Tabbar.cpp +++ b/Source/Tabbar.cpp @@ -187,8 +187,10 @@ void ButtonBar::itemDragEnter(SourceDetails const& dragSourceDetails) auto targetTabPos = getWidth() / (getNumVisibleTabs() + 1); // FIXME: This is a hack. When tab is added to tabbar right edge, and it goes into overflow // we don't know when that will happen. - // So we force the right most tab to think it's always one less. + // So we force the right most tab to think it's always two less when it gets added auto tabPos = jmin(dragSourceDetails.localPosition.getX() / targetTabPos, getNumVisibleTabs() - 2); + tabPos = jmax(0, tabPos); + inOtherSplit = true; auto unusedComponent = std::make_unique<Component>(); owner.addTab(tab->getButtonText(), unusedComponent.get(), tabPos);