Skip to content

Commit

Permalink
Setting up slider choice attachment
Browse files Browse the repository at this point in the history
  • Loading branch information
jatinchowdhury18 committed Mar 20, 2024
1 parent 0db4467 commit ee16a95
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ struct StringLiteral
: actual_size (sl_detail::num_str_len (int_value))
{
// N is not large enough to hold this number!
#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L
jassert (N >= actual_size);
#endif

if constexpr (std::is_signed_v<IntType>)
sl_detail::sint_to_str (chars.data(), actual_size, static_cast<int64_t> (int_value));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace chowdsp
{
SliderChoiceAttachment::SliderChoiceAttachment (ChoiceParameter& param,
PluginState& pluginState,
juce::Slider& paramSlider)
: SliderChoiceAttachment (param, pluginState.getParameterListeners(), paramSlider, pluginState.undoManager)
{
}

SliderChoiceAttachment::SliderChoiceAttachment (ChoiceParameter& param,
ParameterListeners& listeners,
juce::Slider& paramSlider,
juce::UndoManager* undoManager)
: slider (&paramSlider),
attachment (param, listeners, ParameterAttachmentHelpers::SetValueCallback { *this }),
um (undoManager)
{
slider->valueFromTextFunction = [&p = static_cast<juce::RangedAudioParameter&> (param)] (const juce::String& text)
{
return (double) p.convertFrom0to1 (p.getValueForText (text));
};
slider->textFromValueFunction = [&p = static_cast<juce::RangedAudioParameter&> (param)] (double value)
{
return p.getText (p.convertTo0to1 ((float) value), 0);
};
slider->setDoubleClickReturnValue (true, param.getDefaultIndex());

slider->setRange (0.0, static_cast<double> (param.choices.size() - 1), 1.0);

setValue (param.getIndex());
slider->valueChanged();
slider->addListener (this);
}

SliderChoiceAttachment::~SliderChoiceAttachment()
{
if (slider != nullptr)
slider->removeListener (this);
}

void SliderChoiceAttachment::setValue (int newValue)
{
if (slider != nullptr)
{
juce::ScopedValueSetter svs { skipSliderChangedCallback, true };
slider->setValue (static_cast<double> (newValue), juce::sendNotificationSync);
}
}

void SliderChoiceAttachment::sliderValueChanged (juce::Slider*)
{
if (skipSliderChangedCallback)
return;

attachment.setValueAsPartOfGesture (static_cast<int> (slider->getValue()));
}

void SliderChoiceAttachment::sliderDragStarted (juce::Slider*)
{
valueAtStartOfGesture = attachment.param->getIndex();
attachment.beginGesture();
}

void SliderChoiceAttachment::sliderDragEnded (juce::Slider*)
{
if (um != nullptr)
{
um->beginNewTransaction();
um->perform (
new ParameterAttachmentHelpers::ParameterChangeAction<ChoiceParameter> (
*attachment.param,
valueAtStartOfGesture,
attachment.param->getIndex()));
}

attachment.endGesture();
}
} // namespace chowdsp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

namespace chowdsp
{
class SliderChoiceAttachment : private juce::Slider::Listener
{
public:
/** Creates an attachment for a given parameter, using the undo manager from the plugin state. */
SliderChoiceAttachment (ChoiceParameter& param,
PluginState& pluginState,
juce::Slider& paramSlider);

/** Creates an attachment for a given parameter. */
SliderChoiceAttachment (ChoiceParameter& param,
ParameterListeners& listeners,
juce::Slider& paramSlider,
juce::UndoManager* undoManager);

SliderChoiceAttachment() = default;
SliderChoiceAttachment (SliderChoiceAttachment&&) noexcept = default;
SliderChoiceAttachment& operator= (SliderChoiceAttachment&&) noexcept = default;

~SliderChoiceAttachment() override;

/** Sets the initial value of the slider */
void setValue (int newValue);

/** Returns the attached parameter */
[[nodiscard]] const ChoiceParameter* getParameter() const { return attachment.param; }

private:
void sliderValueChanged (juce::Slider*) override;
void sliderDragStarted (juce::Slider*) override;
void sliderDragEnded (juce::Slider*) override;

juce::Slider* slider = nullptr;
ParameterAttachment<ChoiceParameter,
ParameterAttachmentHelpers::SetValueCallback<SliderChoiceAttachment>>
attachment;
juce::UndoManager* um = nullptr;

bool skipSliderChangedCallback = false;
int valueAtStartOfGesture = 0;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderChoiceAttachment)
};
} // namespace chowdsp
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
#include "Backend/chowdsp_ParameterListeners.cpp"

#include "Frontend/chowdsp_SliderAttachment.cpp"
#include "Frontend/chowdsp_SliderChoiceAttachment.cpp"
#include "Frontend/chowdsp_ComboBoxAttachment.cpp"
#include "Frontend/chowdsp_ButtonAttachment.cpp"
1 change: 1 addition & 0 deletions modules/plugin/chowdsp_plugin_state/chowdsp_plugin_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4324) // struct was padded warning

#include "Frontend/chowdsp_ParameterAttachment.h"
#include "Frontend/chowdsp_SliderAttachment.h"
#include "Frontend/chowdsp_SliderChoiceAttachment.h"
#include "Frontend/chowdsp_ComboBoxAttachment.h"
#include "Frontend/chowdsp_ButtonAttachment.h"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,97 @@ TEST_CASE ("Slider Attachment Test", "[plugin][state][attachments]")
}
}

TEST_CASE ("Slider Choice Attachment Test", "[plugin][state][attachments]")
{
struct Params : chowdsp::ParamHolder
{
Params()
{
add (param);
}
chowdsp::ChoiceParameter::Ptr param { "choice", "Choice", juce::StringArray { "Zero", "One", "Two" }, 1 };
};
using State = chowdsp::PluginStateImpl<Params>;

SECTION ("Setup Test")
{
State state;
auto& param = state.params.param;

juce::Slider slider;
chowdsp::SliderChoiceAttachment attach { param, state, slider };

REQUIRE_MESSAGE (static_cast<int> (slider.getValue()) == param->getIndex(), "Incorrect slider value after setup!");
REQUIRE_MESSAGE (static_cast<int> (slider.getRange().getStart()) == 0, "Slider range start is incorrect!");
REQUIRE_MESSAGE (static_cast<int> (slider.getRange().getEnd()) == param->choices.size() - 1, "Slider range end is incorrect!");

REQUIRE_MESSAGE (slider.textFromValueFunction (param->getIndex()) == param->getCurrentValueAsText(), "Slider text from value function is incorrect!");
REQUIRE_MESSAGE (static_cast<int> (slider.valueFromTextFunction (param->getCurrentValueAsText())) == param->getIndex(), "Slider value from text function is incorrect!");
}

SECTION ("Slider Change Test")
{
State state;
const auto& param = state.params.param;

juce::Slider slider;
chowdsp::SliderChoiceAttachment attach { state.params.param, state, slider };

static constexpr int newValue = 0;

{
juce::Slider::ScopedDragNotification scopedDrag { slider };
slider.setValue ((double) newValue, juce::sendNotificationSync);
}

REQUIRE_MESSAGE (param->getIndex() == newValue, "Parameter value after slider drag is incorrect!");
}

SECTION ("Host Change Test")
{
State state;
auto& param = state.params.param;

juce::Slider slider;
chowdsp::SliderChoiceAttachment attach { param, state, slider };

static constexpr int newValue = 2;

chowdsp::ParameterTypeHelpers::setValue (newValue, *param);
state.getParameterListeners().updateBroadcastersFromMessageThread();
REQUIRE_MESSAGE (static_cast<int> (slider.getValue()) == newValue, "Slider value after parameter change is incorrect!");
}

SECTION ("With Undo/Redo Test")
{
juce::UndoManager um { 100 };

State state { &um };
const auto& param = state.params.param;

juce::Slider slider;
chowdsp::SliderChoiceAttachment attach { state.params.param, state, slider };

const auto originalValue = param->getIndex();
static constexpr int newValue = 0;

{
juce::Slider::ScopedDragNotification scopedDrag { slider };
slider.setValue ((double) newValue, juce::sendNotificationSync);
}

REQUIRE_MESSAGE (param->getIndex() == newValue, "Parameter value after slider drag is incorrect!");
REQUIRE_MESSAGE (um.canUndo(), "Slider drag is not creating undoable action!");

um.undo();
REQUIRE_MESSAGE (param->getIndex() == originalValue, "Parameter value after undo action is incorrect!");
REQUIRE_MESSAGE (um.canRedo(), "Slider drag undo is not creating redoable action!");

um.redo();
REQUIRE_MESSAGE (param->getIndex() == newValue, "Parameter value after redo action is incorrect!");
}
}

TEST_CASE ("ComboBox Attachment Test", "[state][attachments]")
{
struct Params : chowdsp::ParamHolder
Expand Down Expand Up @@ -258,3 +349,32 @@ TEST_CASE ("Button Attachment Test", "[state][attachments]")
REQUIRE_MESSAGE (param->get() == newValue, "Parameter value after redo action is incorrect!");
}
}

TEST_CASE ("Custom Parameter Attachment Test", "[state][attachments]")
{
struct Params : chowdsp::ParamHolder
{
Params()
{
add (param);
}

chowdsp::BoolParameter::Ptr param { "bool", "Bool", true };
};

using State = chowdsp::PluginStateImpl<Params>;

SECTION ("Setup Test")
{
State state;
auto& param = state.params.param;

juce::Component comp;
chowdsp::ParameterAttachment<chowdsp::BoolParameter> attach { param, state, [&comp] (bool val) { comp.setVisible (val); } };

REQUIRE (! comp.isVisible());

attach.manuallyTriggerUpdate();
REQUIRE (comp.isVisible());
}
}

0 comments on commit ee16a95

Please sign in to comment.