diff --git a/examples/component-demo/CoupledControls.h b/examples/component-demo/CoupledControls.h index 2efa215..64a69c5 100644 --- a/examples/component-demo/CoupledControls.h +++ b/examples/component-demo/CoupledControls.h @@ -63,18 +63,7 @@ struct CoupledControlsDemo : public sst::jucegui::components::WindowPanel idleTimer = std::make_unique(this); idleTimer->startTimer(1000.0 / 60.0); } - ~MixedControls() - { - idleTimer->stopTimer(); - for (const auto &k : knobs) - { - k->setSource(nullptr); - } - for (const auto &k : sliders) - { - k->setSource(nullptr); - } - } + ~MixedControls() { idleTimer->stopTimer(); } float ival = 0.f; void idle() diff --git a/examples/component-demo/CustomStyleDemo.h b/examples/component-demo/CustomStyleDemo.h index 6181813..376c4fd 100644 --- a/examples/component-demo/CustomStyleDemo.h +++ b/examples/component-demo/CustomStyleDemo.h @@ -89,13 +89,7 @@ struct CustomStyleDemo : public sst::jucegui::components::WindowPanel sources.push_back(std::move(d)); } } - ~SomeSliders() - { - for (const auto &k : knobs) - { - k->setSource(nullptr); - } - } + ~SomeSliders() {} void resized() override { auto b = getLocalBounds(); diff --git a/examples/component-demo/DraggableTextDemo.h b/examples/component-demo/DraggableTextDemo.h index 70902c1..a4ff573 100644 --- a/examples/component-demo/DraggableTextDemo.h +++ b/examples/component-demo/DraggableTextDemo.h @@ -39,13 +39,7 @@ struct DraggableTextDemo : public sst::jucegui::components::WindowPanel sources.push_back(std::move(d)); } } - ~SomeEditors() - { - for (const auto &k : knobs) - { - k->setSource(nullptr); - } - } + ~SomeEditors() {} void resized() override { auto b = getLocalBounds(); diff --git a/examples/component-demo/HSliderDemo.h b/examples/component-demo/HSliderDemo.h index d601e21..0786ed8 100644 --- a/examples/component-demo/HSliderDemo.h +++ b/examples/component-demo/HSliderDemo.h @@ -71,13 +71,7 @@ struct HSliderDemo : public sst::jucegui::components::WindowPanel sources.push_back(std::move(d)); } } - ~SomeSliders() - { - for (const auto &k : knobs) - { - k->setSource(nullptr); - } - } + ~SomeSliders() {} void resized() override { auto b = getLocalBounds(); diff --git a/examples/component-demo/KnobDemo.h b/examples/component-demo/KnobDemo.h index 4ebf9c6..ed3e9cc 100644 --- a/examples/component-demo/KnobDemo.h +++ b/examples/component-demo/KnobDemo.h @@ -61,13 +61,7 @@ struct KnobDemo : public sst::jucegui::components::WindowPanel sources.push_back(std::move(d)); } } - ~SomeKnobs() - { - for (const auto &k : knobs) - { - k->setSource(nullptr); - } - } + ~SomeKnobs() {} void resized() override { auto b = getLocalBounds(); diff --git a/examples/component-demo/MixerPrototype.h b/examples/component-demo/MixerPrototype.h index 3e766d4..69c0323 100644 --- a/examples/component-demo/MixerPrototype.h +++ b/examples/component-demo/MixerPrototype.h @@ -50,13 +50,7 @@ struct MixerProto : public sst::jucegui::components::WindowPanel addAndMakeVisible(*lab); } - ~Channel() - { - solo->setSource(nullptr); - pan->setSource(nullptr); - mute->setSource(nullptr); - level->setSource(nullptr); - } + ~Channel() {} std::unique_ptr level; std::unique_ptr pan; diff --git a/examples/component-demo/VSliderDemo.h b/examples/component-demo/VSliderDemo.h index 27552bf..dc028a1 100644 --- a/examples/component-demo/VSliderDemo.h +++ b/examples/component-demo/VSliderDemo.h @@ -66,13 +66,7 @@ struct VSliderDemo : public sst::jucegui::components::WindowPanel sources.push_back(std::move(d)); } } - ~SomeSliders() - { - for (const auto &k : knobs) - { - k->setSource(nullptr); - } - } + ~SomeSliders() {} void resized() override { auto b = getLocalBounds(); diff --git a/include/sst/jucegui/component-adapters/DiscreteToReference.h b/include/sst/jucegui/component-adapters/DiscreteToReference.h new file mode 100644 index 0000000..52196cf --- /dev/null +++ b/include/sst/jucegui/component-adapters/DiscreteToReference.h @@ -0,0 +1,67 @@ +/* + * sst-juce-gui - an open source library of juce widgets + * built by Surge Synth Team. + * + * Copyright 2023, various authors, as described in the GitHub + * transaction log. + * + * sst-basic-blocks is released under the MIT license, as described + * by "LICENSE.md" in this repository. This means you may use this + * in commercial software if you are a JUCE Licensee. If you use JUCE + * in the open source / GPL3 context, your combined work must be + * released under GPL3. + * + * All source in sst-juce-gui available at + * https://github.com/surge-synthesizer/sst-juce-gui + */ + +#ifndef INCLUDE_SST_JUCEGUI_COMPONENT_ADAPTERS_DISCRETETOREFERENCE_H +#define INCLUDE_SST_JUCEGUI_COMPONENT_ADAPTERS_DISCRETETOREFERENCE_H + +#include +#include + +#include "sst/jucegui/components/DiscreteParamEditor.h" + +namespace sst::jucegui::component_adapters +{ +template struct DiscreteToValueReference : data::Discrete +{ + static_assert(std::is_integral()); + std::unique_ptr widget; + V &underlyer; + static_assert(std::is_base_of::value); + DiscreteToValueReference(std::unique_ptr &wid, V &und) + : widget(std::move(wid)), underlyer(und) + { + setup(); + } + DiscreteToValueReference(V &und) : widget(std::make_unique()), underlyer(und) { setup(); } + + void setup() { widget->setSource(this); } + + std::string label; + void setLabel(const std::string &s) + { + label = s; + widget->repaint(); + } + std::string getLabel() const override { return label; } + + std::function onValueChanged{nullptr}; + int getValue() const override { return underlyer; } + void setValueFromGUI(const int &f) override + { + underlyer = f; + if (onValueChanged) + onValueChanged(f); + } + void setValueFromModel(const int &f) override + { + underlyer = f; + widget->repaint(); + } +}; +} // namespace sst::jucegui::component_adapters + +#endif // CONDUIT_DISCRETETOREFERENCE_H diff --git a/include/sst/jucegui/components/ComponentBase.h b/include/sst/jucegui/components/ComponentBase.h index bfe1ef8..6e32ba2 100644 --- a/include/sst/jucegui/components/ComponentBase.h +++ b/include/sst/jucegui/components/ComponentBase.h @@ -19,6 +19,7 @@ #define INCLUDE_SST_JUCEGUI_COMPONENTS_COMPONENTBASE_H #include +#include #include #include @@ -95,8 +96,10 @@ template struct Modulatable : public data::Continuous::DataListener virtual ~Modulatable() { - if (source) - source->removeGUIDataListener(this); + if (continuous()) + { + continuous()->removeGUIDataListener(this); + } } T *asT() { return static_cast(this); } @@ -119,21 +122,56 @@ template struct Modulatable : public data::Continuous::DataListener isEditingMod = b; asT()->repaint(); } - void setSource(data::ContinunousModulatable *s) + + data::Continuous *continuous() { - if (source) - source->removeGUIDataListener(this); + switch (source.index()) + { + case 0: + return std::get<0>(source); + case 1: + return std::get<1>(source); + } + assert(false); + return nullptr; + } + data::ContinunousModulatable *continuousModulatable() + { + if (std::holds_alternative(source)) + { + return std::get(source); + } + return nullptr; + } + + template void setSource(S *s) + { + if (continuous()) + continuous()->removeGUIDataListener(this); source = s; - if (source) - source->addGUIDataListener(this); + if (continuous()) + continuous()->addGUIDataListener(this); asT()->repaint(); } + void clearSource() + { + if (continuous()) + continuous()->removeGUIDataListener(this); + source = (data::ContinunousModulatable *)nullptr; + } + void dataChanged() override { asT()->repaint(); } + void sourceVanished(data::Continuous *s) override + { + assert(s == continuous()); + clearSource(); + } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Modulatable) - data::ContinunousModulatable *source{nullptr}; + std::variant source{ + (data::Continuous *)nullptr}; bool isEditingMod{false}; ModulationDisplay modulationDisplay{NONE}; }; diff --git a/include/sst/jucegui/components/DiscreteParamEditor.h b/include/sst/jucegui/components/DiscreteParamEditor.h index 28acd84..ee49e97 100644 --- a/include/sst/jucegui/components/DiscreteParamEditor.h +++ b/include/sst/jucegui/components/DiscreteParamEditor.h @@ -27,6 +27,11 @@ struct DiscreteParamEditor : public juce::Component, public data::Discrete::DataListener { void dataChanged() override { repaint(); } + void sourceVanished(data::Discrete *d) override + { + assert(d == data); + setSource(nullptr); + } void setSource(data::Discrete *d) { if (data) diff --git a/include/sst/jucegui/data/Continuous.h b/include/sst/jucegui/data/Continuous.h index 1c5d43e..56f1e98 100644 --- a/include/sst/jucegui/data/Continuous.h +++ b/include/sst/jucegui/data/Continuous.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "Labeled.h" @@ -28,16 +30,35 @@ namespace sst::jucegui::data { struct Continuous : public Labeled { - virtual ~Continuous() = default; + virtual ~Continuous() + { + supressListenerModification = true; + for (auto *dl : guilisteners) + { + dl->sourceVanished(this); + } + supressListenerModification = false; + }; struct DataListener { virtual ~DataListener() = default; // FIXME - in the future we may want this more fine grained virtual void dataChanged() = 0; + virtual void sourceVanished(Continuous *) = 0; }; - void addGUIDataListener(DataListener *l) { guilisteners.insert(l); } - void removeGUIDataListener(DataListener *l) { guilisteners.erase(l); } + bool supressListenerModification{false}; + void addGUIDataListener(DataListener *l) + { + assert(!supressListenerModification); + if (!supressListenerModification) + guilisteners.insert(l); + } + void removeGUIDataListener(DataListener *l) + { + if (!supressListenerModification) + guilisteners.erase(l); + } void addModelDataListener(DataListener *l) { modellisteners.insert(l); } void removeModelDataListener(DataListener *l) { modellisteners.erase(l); } diff --git a/include/sst/jucegui/data/Discrete.h b/include/sst/jucegui/data/Discrete.h index df7f2cc..2a90980 100644 --- a/include/sst/jucegui/data/Discrete.h +++ b/include/sst/jucegui/data/Discrete.h @@ -25,13 +25,18 @@ namespace sst::jucegui::data { struct Discrete : public Labeled { - virtual ~Discrete() = default; + virtual ~Discrete() + { + for (auto *l : guilisteners) + l->sourceVanished(this); + } struct DataListener { virtual ~DataListener() = default; // FIXME - in the future we may want this more fine grained virtual void dataChanged() = 0; + virtual void sourceVanished(Discrete *) = 0; }; void addGUIDataListener(DataListener *l) { guilisteners.insert(l); } void removeGUIDataListener(DataListener *l) { guilisteners.erase(l); } diff --git a/src/sst/jucegui/components/ContinuousParamEditor.cpp b/src/sst/jucegui/components/ContinuousParamEditor.cpp index 8f74561..188a24c 100644 --- a/src/sst/jucegui/components/ContinuousParamEditor.cpp +++ b/src/sst/jucegui/components/ContinuousParamEditor.cpp @@ -28,7 +28,7 @@ void ContinuousParamEditor::mouseDown(const juce::MouseEvent &e) return; jassert(settings()); - jassert(source); + jassert(continuous()); if (e.mods.isPopupMenu()) { @@ -39,10 +39,10 @@ void ContinuousParamEditor::mouseDown(const juce::MouseEvent &e) mouseMode = DRAG; onBeginEdit(); - if (isEditingMod) - mouseDownV0 = source->getModulationValuePM1(); + if (isEditingMod && continuousModulatable()) + mouseDownV0 = continuousModulatable()->getModulationValuePM1(); else - mouseDownV0 = source->getValue(); + mouseDownV0 = continuous()->getValue(); mouseDownY0 = e.position.y; mouseDownX0 = e.position.x; } @@ -62,7 +62,7 @@ void ContinuousParamEditor::mouseDoubleClick(const juce::MouseEvent &e) return; onBeginEdit(); - source->setValueFromGUI(source->getDefaultValue()); + continuous()->setValueFromGUI(continuous()->getDefaultValue()); onEndEdit(); repaint(); @@ -79,11 +79,11 @@ void ContinuousParamEditor::mouseDrag(const juce::MouseEvent &e) float dy = -(e.position.y - mouseDownY0); float dx = (e.position.x - mouseDownX0); float d = 0; - float minForScaling = source->getMin(); - float maxForScaling = source->getMax(); - if (isEditingMod) + float minForScaling = continuous()->getMin(); + float maxForScaling = continuous()->getMax(); + if (isEditingMod && continuousModulatable()) { - if (source->isModulationBipolar()) + if (continuousModulatable()->isModulationBipolar()) minForScaling = -1.0f; else minForScaling = 0.0f; @@ -100,18 +100,18 @@ void ContinuousParamEditor::mouseDrag(const juce::MouseEvent &e) } if (e.mods.isShiftDown()) d = d * 0.1; - if (isEditingMod) + if (isEditingMod && continuousModulatable()) { - if (source->isModulationBipolar()) + if (continuousModulatable()->isModulationBipolar()) d = d * 0.5; auto vn = std::clamp(mouseDownV0 + d, -1.f, 1.f); - source->setModulationValuePM1(vn); + continuousModulatable()->setModulationValuePM1(vn); mouseDownV0 = vn; } else { - auto vn = std::clamp(mouseDownV0 + d, source->getMin(), source->getMax()); - source->setValueFromGUI(vn); + auto vn = std::clamp(mouseDownV0 + d, continuous()->getMin(), continuous()->getMax()); + continuous()->setValueFromGUI(vn); mouseDownV0 = vn; } mouseDownX0 = e.position.x; @@ -129,25 +129,27 @@ void ContinuousParamEditor::mouseWheelMove(const juce::MouseEvent &e, return; onBeginEdit(); - if (isEditingMod) + if (isEditingMod && continuousModulatable()) { // fixme - callibration and sharing auto d = (wheel.isReversed ? -1 : 1) * wheel.deltaY * (2); if (e.mods.isShiftDown()) d = d * 0.1; - auto vn = std::clamp(source->getModulationValuePM1() + d, -1.f, 1.f); - source->setModulationValuePM1(vn); + auto vn = std::clamp(continuousModulatable()->getModulationValuePM1() + d, -1.f, 1.f); + continuousModulatable()->setModulationValuePM1(vn); } else { // fixme - callibration and sharing - auto d = (wheel.isReversed ? -1 : 1) * wheel.deltaY * (source->getMax() - source->getMin()); + auto d = (wheel.isReversed ? -1 : 1) * wheel.deltaY * + (continuous()->getMax() - continuous()->getMin()); if (e.mods.isShiftDown()) d = d * 0.1; - auto vn = std::clamp(source->getValue() + d, source->getMin(), source->getMax()); - source->setValueFromGUI(vn); + auto vn = std::clamp(continuous()->getValue() + d, continuous()->getMin(), + continuous()->getMax()); + continuous()->setValueFromGUI(vn); } onEndEdit(); repaint(); @@ -155,9 +157,9 @@ void ContinuousParamEditor::mouseWheelMove(const juce::MouseEvent &e, bool ContinuousParamEditor::processMouseActions() { - if (!source) + if (!continuous()) return false; - if (source->isHidden()) + if (continuous()->isHidden()) return false; if (!isEnabled()) return false; diff --git a/src/sst/jucegui/components/DraggableTextEditableValue.cpp b/src/sst/jucegui/components/DraggableTextEditableValue.cpp index a6a9a19..2813f2f 100644 --- a/src/sst/jucegui/components/DraggableTextEditableValue.cpp +++ b/src/sst/jucegui/components/DraggableTextEditableValue.cpp @@ -46,11 +46,11 @@ void DraggableTextEditableValue::setFromEditor() auto t = underlyingEditor->getText(); if (t.isEmpty()) { - source->setValueFromGUI(source->getDefaultValue()); + continuous()->setValueFromGUI(continuous()->getDefaultValue()); } else { - source->setValueAsString(t.toStdString()); + continuous()->setValueAsString(t.toStdString()); } underlyingEditor->setVisible(false); repaint(); @@ -73,7 +73,7 @@ void DraggableTextEditableValue::paint(juce::Graphics &g) g.fillRoundedRectangle(getLocalBounds().toFloat(), 3.f); g.setColour(getColour(Styles::bordercol)); g.drawRoundedRectangle(getLocalBounds().toFloat(), 3.f, 1.f); - if (source && !underlyingEditor->isVisible()) + if (continuous() && !underlyingEditor->isVisible()) { g.setFont(getFont(Styles::labelfont)); if (underlyingEditor->isVisible()) @@ -82,23 +82,24 @@ void DraggableTextEditableValue::paint(juce::Graphics &g) g.setColour(getColour(Styles::textoffcol)); else g.setColour(getColour(Styles::texthoveroffcol)); - g.drawText(source->getValueAsString(), getLocalBounds(), juce::Justification::centred); + g.drawText(continuous()->getValueAsString(), getLocalBounds(), + juce::Justification::centred); } } void DraggableTextEditableValue::mouseDown(const juce::MouseEvent &e) { onBeginEdit(); - valueOnMouseDown = source->getValue(); + valueOnMouseDown = continuous()->getValue(); } void DraggableTextEditableValue::mouseUp(const juce::MouseEvent &e) { onEndEdit(); } void DraggableTextEditableValue::mouseDrag(const juce::MouseEvent &e) { auto d = e.getDistanceFromDragStartY(); auto fac = 0.5f * (e.mods.isShiftDown() ? 0.1f : 1.f); - auto nv = valueOnMouseDown - fac * d * source->getFineQuantizedStepSize(); - nv = std::clamp(nv, source->getMin(), source->getMax()); - source->setValueFromGUI(nv); + auto nv = valueOnMouseDown - fac * d * continuous()->getFineQuantizedStepSize(); + nv = std::clamp(nv, continuous()->getMin(), continuous()->getMax()); + continuous()->setValueFromGUI(nv); repaint(); } void DraggableTextEditableValue::mouseWheelMove(const juce::MouseEvent &event, @@ -109,7 +110,7 @@ void DraggableTextEditableValue::mouseWheelMove(const juce::MouseEvent &event, void DraggableTextEditableValue::mouseDoubleClick(const juce::MouseEvent &e) { - underlyingEditor->setText(source->getValueAsString()); + underlyingEditor->setText(continuous()->getValueAsString()); underlyingEditor->setVisible(true); underlyingEditor->selectAll(); underlyingEditor->grabKeyboardFocus(); diff --git a/src/sst/jucegui/components/HSlider.cpp b/src/sst/jucegui/components/HSlider.cpp index 517cb50..b569c23 100644 --- a/src/sst/jucegui/components/HSlider.cpp +++ b/src/sst/jucegui/components/HSlider.cpp @@ -28,7 +28,7 @@ HSlider::~HSlider() = default; void HSlider::paint(juce::Graphics &g) { - if (!source) + if (!continuous()) { g.fillAll(juce::Colours::red); g.setColour(juce::Colours::white); @@ -36,7 +36,7 @@ void HSlider::paint(juce::Graphics &g) return; } - if (source->isHidden()) + if (continuous()->isHidden()) return; bool vCenter = !showLabel && !showValue; @@ -45,14 +45,14 @@ void HSlider::paint(juce::Graphics &g) { g.setColour(getColour(Styles::labeltextcol)); g.setFont(getFont(Styles::labeltextfont)); - g.drawText(source->getLabel(), getLocalBounds().reduced(2, 1), + g.drawText(continuous()->getLabel(), getLocalBounds().reduced(2, 1), juce::Justification::bottomLeft); } if (showValue) { g.setColour(getColour(Styles::valuetextcol)); g.setFont(getFont(Styles::valuetextfont)); - g.drawText(source->getValueAsString(), getLocalBounds().reduced(2, 1), + g.drawText(continuous()->getValueAsString(), getLocalBounds().reduced(2, 1), juce::Justification::bottomRight); } @@ -93,11 +93,11 @@ void HSlider::paint(juce::Graphics &g) g.fillRoundedRectangle(gutter.reduced(2), gutterheight * 0.25); } - auto v = source->getValue01(); + auto v = continuous()->getValue01(); auto w = (1 - v) * gutter.getWidth(); auto hc = gutter.withTrimmedLeft(gutter.getWidth() - w).withWidth(1).expanded(0, 4).getCentre(); - if (source->isBipolar()) + if (continuous()->isBipolar()) { auto t = hc.getX(); auto b = gutter.getWidth() / 2 + gutter.getX(); @@ -116,15 +116,17 @@ void HSlider::paint(juce::Graphics &g) auto hr = juce::Rectangle(2 * hanRadius, 2 * hanRadius).withCentre(hc); - auto mvplus = std::clamp(v + source->getModulationValuePM1(), 0.f, 1.f); - auto mvminus = std::clamp(v - source->getModulationValuePM1(), 0.f, 1.f); - auto hm = (1.0 - mvplus) * gutter.getWidth(); - auto mpc = - gutter.withTrimmedLeft(gutter.getWidth() - hm).withWidth(1).expanded(0, 4).getCentre(); - auto mpr = juce::Rectangle(2 * hanRadius, 2 * hanRadius).withCentre(mpc); - - if (isEditingMod) + juce::Point mpc{}; + juce::Rectangle mpr{}; + if (isEditingMod && continuousModulatable()) { + auto mvplus = std::clamp(v + continuousModulatable()->getModulationValuePM1(), 0.f, 1.f); + auto mvminus = std::clamp(v - continuousModulatable()->getModulationValuePM1(), 0.f, 1.f); + auto hm = (1.0 - mvplus) * gutter.getWidth(); + mpc = + gutter.withTrimmedLeft(gutter.getWidth() - hm).withWidth(1).expanded(0, 4).getCentre(); + mpr = juce::Rectangle(2 * hanRadius, 2 * hanRadius).withCentre(mpc); + // draw rules { auto t = hc.getX(); @@ -136,7 +138,7 @@ void HSlider::paint(juce::Graphics &g) g.fillRoundedRectangle(val, gutterheight * 0.25); } - if (source->isModulationBipolar()) + if (continuousModulatable()->isModulationBipolar()) { auto t = hc.getX(); auto b = (mvminus)*gutter.getWidth() + gutter.getX(); diff --git a/src/sst/jucegui/components/HSliderFilled.cpp b/src/sst/jucegui/components/HSliderFilled.cpp index e504350..b007b89 100644 --- a/src/sst/jucegui/components/HSliderFilled.cpp +++ b/src/sst/jucegui/components/HSliderFilled.cpp @@ -27,7 +27,7 @@ HSliderFilled::HSliderFilled() void HSliderFilled::paint(juce::Graphics &g) { - if (!source) + if (!continuous()) { g.fillAll(juce::Colours::red); g.setColour(juce::Colours::white); @@ -35,7 +35,7 @@ void HSliderFilled::paint(juce::Graphics &g) return; } - if (source->isHidden()) + if (continuous()->isHidden()) return; // Gutter @@ -66,11 +66,11 @@ void HSliderFilled::paint(juce::Graphics &g) g.fillRoundedRectangle(gutter.reduced(2), rectRad); } - auto v = source->getValue01(); + auto v = continuous()->getValue01(); auto w = (1 - v) * gutter.getWidth(); auto hc = gutter.withTrimmedLeft(gutter.getWidth() - w).withWidth(1).expanded(0, 4).getCentre(); - if (source->isBipolar()) + if (continuous()->isBipolar()) { auto t = hc.getX(); auto b = gutter.getWidth() / 2 + gutter.getX(); @@ -89,15 +89,17 @@ void HSliderFilled::paint(juce::Graphics &g) auto hr = juce::Rectangle(2, gutter.getHeight()).withCentre(hc); - auto mvplus = std::clamp(v + source->getModulationValuePM1(), 0.f, 1.f); - auto mvminus = std::clamp(v - source->getModulationValuePM1(), 0.f, 1.f); - auto hm = (1.0 - mvplus) * gutter.getWidth(); - auto mpc = - gutter.withTrimmedLeft(gutter.getWidth() - hm).withWidth(1).expanded(0, 4).getCentre(); - auto mpr = juce::Rectangle(2 * hanRadius, 2 * hanRadius).withCentre(mpc); - - if (isEditingMod) + juce::Point mpc; + juce::Rectangle mpr; + if (continuousModulatable() && isEditingMod) { + auto mvplus = std::clamp(v + continuousModulatable()->getModulationValuePM1(), 0.f, 1.f); + auto mvminus = std::clamp(v - continuousModulatable()->getModulationValuePM1(), 0.f, 1.f); + auto hm = (1.0 - mvplus) * gutter.getWidth(); + mpc = + gutter.withTrimmedLeft(gutter.getWidth() - hm).withWidth(1).expanded(0, 4).getCentre(); + mpr = juce::Rectangle(2 * hanRadius, 2 * hanRadius).withCentre(mpc); + // draw rules { auto t = hc.getX(); @@ -109,7 +111,7 @@ void HSliderFilled::paint(juce::Graphics &g) g.fillRoundedRectangle(val, rectRad); } - if (source->isModulationBipolar()) + if (continuousModulatable()->isModulationBipolar()) { auto t = hc.getX(); auto b = (mvminus)*gutter.getWidth() + gutter.getX(); diff --git a/src/sst/jucegui/components/Knob.cpp b/src/sst/jucegui/components/Knob.cpp index 71093b4..ac55e80 100644 --- a/src/sst/jucegui/components/Knob.cpp +++ b/src/sst/jucegui/components/Knob.cpp @@ -28,14 +28,20 @@ Knob::~Knob() = default; void Knob::paint(juce::Graphics &g) { auto b = getLocalBounds(); - knobPainter(g, this, source); - + if (continuousModulatable()) + { + knobPainter(g, this, continuousModulatable()); + } + else + { + knobPainter(g, this, continuous()); + } if (drawLabel) { auto textarea = b.withTrimmedTop(b.getWidth()); g.setColour(getColour(Styles::labeltextcol)); g.setFont(getFont(Styles::labeltextfont)); - g.drawText(source->getLabel(), textarea, juce::Justification::centred); + g.drawText(continuous()->getLabel(), textarea, juce::Justification::centred); } } diff --git a/src/sst/jucegui/components/KnobPainter.hxx b/src/sst/jucegui/components/KnobPainter.hxx index f3e93f7..813b7f7 100644 --- a/src/sst/jucegui/components/KnobPainter.hxx +++ b/src/sst/jucegui/components/KnobPainter.hxx @@ -6,12 +6,15 @@ #define CONDUIT_KNOBPAINTER_H #include +#include namespace sst::jucegui::components { template void knobPainter(juce::Graphics &g, T* that, S *source) { + constexpr bool supportsMod = std::is_base_of_v; + if (!source) { g.fillAll(juce::Colours::red); @@ -152,16 +155,19 @@ void knobPainter(juce::Graphics &g, T* that, S *source) g.fillPath(pIn); // modulation arcs - if (that->isEditingMod) + if constexpr (supportsMod) { - pIn = modPath(5, source->getValue01(), source->getModulationValuePM1(), 1); - g.setColour(that->getColour(T::Styles::modvalcol)); - g.fillPath(pIn); - if (source->isModulationBipolar()) + if (that->isEditingMod) { - pIn = modPath(5, source->getValue01(), source->getModulationValuePM1(), -1); - g.setColour(that->getColour(T::Styles::modvalnegcol)); + pIn = modPath(5, source->getValue01(), source->getModulationValuePM1(), 1); + g.setColour(that->getColour(T::Styles::modvalcol)); g.fillPath(pIn); + if (source->isModulationBipolar()) + { + pIn = modPath(5, source->getValue01(), source->getModulationValuePM1(), -1); + g.setColour(that->getColour(T::Styles::modvalnegcol)); + g.fillPath(pIn); + } } } diff --git a/src/sst/jucegui/components/VSlider.cpp b/src/sst/jucegui/components/VSlider.cpp index 51faf8b..e1932f3 100644 --- a/src/sst/jucegui/components/VSlider.cpp +++ b/src/sst/jucegui/components/VSlider.cpp @@ -27,13 +27,13 @@ VSlider::~VSlider() = default; void VSlider::paint(juce::Graphics &g) { - if (!source) + if (!continuous()) { g.fillAll(juce::Colours::red); return; } - if (source->isHidden()) + if (continuous()->isHidden()) return; // Gutter @@ -65,11 +65,11 @@ void VSlider::paint(juce::Graphics &g) g.fillRoundedRectangle(gutter.reduced(2), gutterwidth * 0.25); } - auto v = source->getValue01(); + auto v = continuous()->getValue01(); auto h = (1.0 - v) * gutter.getHeight(); auto hc = gutter.withTrimmedTop(h).withHeight(1).expanded(0, 4).getCentre(); - if (source->isBipolar()) + if (continuous()->isBipolar()) { auto t = hc.getY(); auto b = gutter.getHeight() / 2 + gutter.getY(); @@ -88,8 +88,8 @@ void VSlider::paint(juce::Graphics &g) auto hr = juce::Rectangle(2 * hanRadius, 2 * hanRadius).withCentre(hc); - auto mvplus = std::clamp(v + source->getModulationValuePM1(), 0.f, 1.f); - auto mvminus = std::clamp(v - source->getModulationValuePM1(), 0.f, 1.f); + auto mvplus = std::clamp(v + continuousModulatable()->getModulationValuePM1(), 0.f, 1.f); + auto mvminus = std::clamp(v - continuousModulatable()->getModulationValuePM1(), 0.f, 1.f); auto hm = (1.0 - mvplus) * gutter.getHeight(); auto mpc = gutter.withTrimmedTop(hm).withHeight(1).expanded(0, 4).getCentre(); auto mpr = juce::Rectangle(2 * hanRadius, 2 * hanRadius).withCentre(mpc); @@ -107,7 +107,7 @@ void VSlider::paint(juce::Graphics &g) g.fillRoundedRectangle(val, gutterwidth * 0.25); } - if (source->isModulationBipolar()) + if (continuousModulatable()->isModulationBipolar()) { auto t = hc.getY(); auto b = (1 - mvminus) * gutter.getHeight() + gutter.getY();