From fc7f63567ea83466cc6a50cd638729d281db003a Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 6 Sep 2021 12:45:49 -0400 Subject: [PATCH] Visual Feedback when dragging over droppable targets (#5002) Closes #4995 --- src/gui/SurgeGUIEditor.cpp | 54 ++++++++++++++++++++++ src/gui/SurgeGUIEditor.h | 4 ++ src/gui/widgets/ModulationSourceButton.cpp | 14 +++++- src/gui/widgets/ModulationSourceButton.h | 1 + 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/gui/SurgeGUIEditor.cpp b/src/gui/SurgeGUIEditor.cpp index 1e82182f49f..38d1f95eeea 100644 --- a/src/gui/SurgeGUIEditor.cpp +++ b/src/gui/SurgeGUIEditor.cpp @@ -3811,6 +3811,53 @@ void SurgeGUIEditor::promptForMiniEdit(const std::string &value, const std::stri miniEdit->grabFocus(); } +bool SurgeGUIEditor::modSourceButtonDraggedOver(Surge::Widgets::ModulationSourceButton *msb, + const juce::Point &pt) +{ + juce::Component *target = nullptr; + + auto isDroppable = [msb](juce::Component *c) { + auto tMCI = dynamic_cast(c); + if (tMCI) + return true; + return false; + }; + auto recC = [isDroppable, msb, pt](juce::Component *p, auto rec) -> juce::Component * { + for (auto kid : p->getChildren()) + { + if (kid && kid->isVisible() && kid != msb && kid->getBounds().contains(pt)) + { + if (isDroppable(kid)) + return kid; + + auto q = rec(kid, rec); + if (q) + return q; + } + } + return nullptr; + }; + target = recC(frame.get(), recC); + auto tMCI = dynamic_cast(target); + if (tMCI != modSourceDragOverTarget) + { + if (modSourceDragOverTarget) + { + modSourceDragOverTarget->setModulationState(priorModulationState); + modSourceDragOverTarget->asJuceComponent()->repaint(); + } + modSourceDragOverTarget = tMCI; + + if (tMCI) + { + priorModulationState = tMCI->modulationState; + tMCI->setModulationState( + Surge::Widgets::ModulatableControlInterface::MODULATED_BY_ACTIVE); + tMCI->asJuceComponent()->repaint(); + } + } + return tMCI != nullptr; +} void SurgeGUIEditor::modSourceButtonDroppedAt(Surge::Widgets::ModulationSourceButton *msb, const juce::Point &pt) { @@ -3855,6 +3902,13 @@ void SurgeGUIEditor::modSourceButtonDroppedAt(Surge::Widgets::ModulationSourceBu } else if (tMCI) { + if (modSourceDragOverTarget) + { + tMCI->setModulationState(priorModulationState); + tMCI->asJuceComponent()->repaint(); + + modSourceDragOverTarget = nullptr; + } openModTypeinOnDrop(msb->getCurrentModSource(), tMCI, tMCI->asControlValueInterface()->getTag(), msb->getCurrentModIndex()); } diff --git a/src/gui/SurgeGUIEditor.h b/src/gui/SurgeGUIEditor.h index 986d4be89da..cacca63bacd 100644 --- a/src/gui/SurgeGUIEditor.h +++ b/src/gui/SurgeGUIEditor.h @@ -302,6 +302,10 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener, void mappingFileDropped(const std::string &fn); std::string tuningToHtml(); + Surge::Widgets::ModulatableControlInterface *modSourceDragOverTarget{nullptr}; + Surge::Widgets::ModulatableControlInterface::ModulationState priorModulationState; + bool modSourceButtonDraggedOver(Surge::Widgets::ModulationSourceButton *msb, + const juce::Point &); // true if over a droppable target void modSourceButtonDroppedAt(Surge::Widgets::ModulationSourceButton *msb, const juce::Point &); void swapControllers(int t1, int t2); diff --git a/src/gui/widgets/ModulationSourceButton.cpp b/src/gui/widgets/ModulationSourceButton.cpp index f5f359d3e22..a0f385e198a 100644 --- a/src/gui/widgets/ModulationSourceButton.cpp +++ b/src/gui/widgets/ModulationSourceButton.cpp @@ -62,7 +62,7 @@ void ModulationSourceButton::paint(juce::Graphics &g) : skin->getColor(Colors::ModSource::Unused::TextHover); } - if (ActiveModSource) + if (ActiveModSource || transientArmed) { FrameCol = skin->getColor(Colors::ModSource::Armed::Border); if (isHovered) @@ -361,6 +361,7 @@ void ModulationSourceButton::mouseDown(const juce::MouseEvent &event) mouseDownBounds = getBounds(); componentDragger.startDraggingComponent(this, event); + setMouseCursor(juce::MouseCursor::DraggingHandCursor); } void ModulationSourceButton::mouseDoubleClick(const juce::MouseEvent &event) @@ -427,6 +428,7 @@ void ModulationSourceButton::onSkinChanged() void ModulationSourceButton::mouseUp(const juce::MouseEvent &event) { + transientArmed = false; if (mouseMode == CLICK || mouseMode == CLICK_ARROW) { notifyValueChanged(); @@ -434,6 +436,7 @@ void ModulationSourceButton::mouseUp(const juce::MouseEvent &event) if (mouseMode == DRAG_COMPONENT_HAPPEN) { + setMouseCursor(juce::MouseCursor::NormalCursor); auto sge = firstListenerOfType(); auto q = event.position.translated(getBounds().getX(), getBounds().getY()); @@ -514,6 +517,15 @@ void ModulationSourceButton::mouseDrag(const juce::MouseEvent &event) mouseMode = DRAG_COMPONENT_HAPPEN; componentDragger.dragComponent(this, event, nullptr); + auto sge = firstListenerOfType(); + auto q = event.position.translated(getBounds().getX(), getBounds().getY()); + auto ota = transientArmed; + if (sge) + { + /* transientArmed = */ sge->modSourceButtonDraggedOver(this, q.toInt()); + } + if (ota != transientArmed) + repaint(); everDragged = true; } diff --git a/src/gui/widgets/ModulationSourceButton.h b/src/gui/widgets/ModulationSourceButton.h index 039b10c3b28..c31fb8ace04 100644 --- a/src/gui/widgets/ModulationSourceButton.h +++ b/src/gui/widgets/ModulationSourceButton.h @@ -107,6 +107,7 @@ struct ModulationSourceButton : public juce::Component, void setState(int s) { state = s; } int getState() const { return state; } + bool transientArmed{false}; // armed in drop state bool secondaryHoverActive{false};