/* ============================================================================== FXModule.cpp Created: 28 Dec 2023 8:09:18pm Author: binya ============================================================================== */ #include #include "FxModule.h" FxModule::FxModule(FxDragTarget* fxChain, APVTS& apvts, const juce::String& fxName, PluginParameters::FxTypes effectType, const juce::String& fxEnabledParameter, Component& displayComponent) : fxChain(fxChain), apvts(apvts), fxType(effectType), nameLabel("", fxName), mixControl(juce::Slider::RotaryVerticalDrag, juce::Slider::NoTextBox), enablementAttachment(apvts, fxEnabledParameter, fxEnabled), display(displayComponent), dragIcon(getOutlineFromSVG(BinaryData::IconDrag_svg)) { nameLabel.setInterceptsMouseClicks(false, false); addAndMakeVisible(&nameLabel); fxEnabled.onStateChange = [&] { display.setEnabled(fxEnabled.getToggleState()); for (auto rotary : rotaryReferences) rotary->setEnabled(fxEnabled.getToggleState()); for (auto& label : labels) label->setEnabled(fxEnabled.getToggleState()); }; fxEnabled.onStateChange(); // Set the initial state fxEnabled.setHelpText("Toggle " + fxName.toLowerCase()); addAndMakeVisible(&fxEnabled); addAndMakeVisible(&displayComponent); setRepaintsOnMouseActivity(true); addMouseListener(this, true); setHelpText(fxName + " module"); setBufferedToImage(true); } FxModule::FxModule(FxDragTarget* fxChain, APVTS& apvts, const juce::String& fxName, PluginParameters::FxTypes effectType, const juce::String& fxEnabledParameter, const juce::String& mixControlParameter, Component& displayComponent) : FxModule(fxChain, apvts, fxName, effectType, fxEnabledParameter, displayComponent) { setupRotary(mixControl, false); mixControl.getProperties().set(ComponentProps::ROTARY_UNIT, "%"); mixControlAttachment = std::make_unique(apvts, mixControlParameter, mixControl); } juce::Slider* FxModule::addRotary(const juce::String& parameter, const juce::String& label, juce::Point position, float width, const juce::String& unit) { auto rotary = std::make_unique(); CustomRotary* rawRotaryPtr = rotary.get(); auto attachment = std::make_unique(apvts, parameter, *rawRotaryPtr); attachments.push_back(std::move(attachment)); setupRotary(*rotary); rotary->getProperties().set(ComponentProps::ROTARY_UNIT, unit); addAndMakeVisible(rawRotaryPtr); rotaries.push_back(std::move(rotary)); auto rotaryLabel = std::make_unique("", label); rotaryLabel->setInterceptsMouseClicks(false, false); rotaryLabel->setJustificationType(juce::Justification::centredBottom); rotaryLabel->setEnabled(fxEnabled.getToggleState()); addAndMakeVisible(rotaryLabel.get()); labels.push_back(std::move(rotaryLabel)); rotaryPositions.push_back(position); rotaryWidths.push_back(width); return rawRotaryPtr; } void FxModule::setupRotary(CustomRotary& rotary, bool useTextbox) { rotary.setSliderStyle(juce::Slider::RotaryVerticalDrag); rotary.setMouseDragSensitivity(150); if (useTextbox) rotary.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 0, 0); rotary.addMouseListener(this, true); // We need this to pass drag events through the labels rotary.setEnabled(fxEnabled.getToggleState()); addAndMakeVisible(rotary); rotaryReferences.push_back(&rotary); } void FxModule::paint(juce::Graphics& g) { auto colors = getTheme(); auto bounds = getLocalBounds(); g.setColour(colors.background); g.fillAll(); // Draw the drag icon auto header = bounds.toFloat().removeFromTop(scalef(Layout::fxModuleHeader)); if (header.contains(getMouseXYRelative().toFloat())) { g.setColour(colors.darkerSlate); header.removeFromTop(scalef(25.f)); auto iconBounds = header.removeFromTop(scalef(28.f)); g.fillPath(dragIcon, dragIcon.getTransformToScaleToFit(iconBounds.toFloat(), true)); } } void FxModule::resized() { auto bounds = getLocalBounds().toFloat(); // Header auto mixPad = scale(43.f) * Layout::rotaryPadding; auto header = bounds.removeFromTop(scale(Layout::fxModuleHeader)).reduced(scale(21.f), scale(15.f) - mixPad); fxEnabled.setBounds(header.removeFromRight(scale(43.f)).reduced(0.f, mixPad).toNearestInt()); header.removeFromRight(scale(20.f) - mixPad); if (mixControlAttachment) mixControl.setBounds(header.removeFromRight(scale(43.f) + 2 * mixPad).toNearestInt()); header.removeFromRight(scale(20.f) - mixPad); nameLabel.setBounds(header.reduced(0.f, mixPad).toNearestInt()); nameLabel.setFont(getInriaSans().withHeight(scalef(41.4f))); // Controls display.setBounds(bounds.removeFromTop(scale(103.f)).toNearestInt()); bounds.reduce(0.f, scale(27.5f)); for (size_t i = 0; i < rotaries.size(); i++) { auto* rotary = rotaries[i].get(); auto [xPos, yPos] = rotaryPositions[i]; auto width = rotaryWidths[i]; auto padding = Layout::rotaryPadding * scale(width); rotary->setBounds(juce::Rectangle(bounds.getX() + scale(xPos), bounds.getY() + scale(yPos), scale(width), scale(width) * Layout::rotaryHeightRatio).expanded(padding).toNearestInt()); auto* label = labels[i].get(); auto labelBounds = juce::Rectangle(bounds.getX() + scale(xPos), bounds.getY() + scale(yPos) - scale(width) * 0.45f, scale(width), scale(width * 0.356f)).expanded(width, 0.f); label->setFont(getInriaSans().withHeight(scale(width) * 0.356f)); label->setBounds(labelBounds.toNearestInt()); } if (mixControlAttachment) mixControl.sendLookAndFeelChange(); for (auto& rotary : rotaries) rotary->sendLookAndFeelChange(); } void FxModule::enablementChanged() { setInterceptsMouseClicks(isEnabled(), isEnabled()); repaint(); } void FxModule::mouseDown(const juce::MouseEvent& event) { // We pass the events on the rotary number labels through to the rotary auto parent = event.originalComponent->getParentComponent(); for (auto rotary : rotaryReferences) if (parent == rotary) rotary->mouseDown(event.getEventRelativeTo(rotary)); } //============================================================================== void FxModule::mouseUp(const juce::MouseEvent& event) { if (dragging) { dragging = false; fxChain->dragEnded(); repaint(); } auto parent = event.originalComponent->getParentComponent(); for (auto rotary : rotaryReferences) if (parent == rotary) rotary->mouseUp(event.getEventRelativeTo(rotary)); } void FxModule::mouseMove(const juce::MouseEvent& /*event*/) { auto header = getLocalBounds().toFloat().removeFromTop(scale(Layout::fxModuleHeader)); auto mouseInHeader = header.contains(getMouseXYRelative().toFloat()); if (mouseInHeader) setMouseCursor(juce::MouseCursor::DraggingHandCursor); else setMouseCursor(juce::MouseCursor::NormalCursor); } void FxModule::mouseDrag(const juce::MouseEvent& event) { auto header = getLocalBounds().toFloat().removeFromTop(scale(Layout::fxModuleHeader)); auto mouseInHeader = header.contains(getMouseXYRelative().toFloat()); if (!dragging && event.eventComponent == this && mouseInHeader) { dragging = true; fxChain->dragStarted(this, event); repaint(); } auto parent = event.originalComponent->getParentComponent(); for (auto rotary : rotaryReferences) if (parent == rotary) rotary->mouseDrag(event.getEventRelativeTo(rotary)); } void FxModule::mouseEnter(const juce::MouseEvent&) { repaint(); } void FxModule::mouseExit(const juce::MouseEvent&) { repaint(); }