@@ -0,0 +1,25 @@ | |||||
#pragma once | |||||
#include <app/common.hpp> | |||||
#include <app/SvgSwitch.hpp> | |||||
namespace rack { | |||||
namespace app { | |||||
/** A ParamWidget that behaves like a Switch but draws SVG frames for mouse up/down state. | |||||
*/ | |||||
struct SvgLatch : SvgSwitch { | |||||
struct Internal; | |||||
Internal* internal; | |||||
SvgLatch(); | |||||
~SvgLatch(); | |||||
void onDragStart(const DragStartEvent& e) override; | |||||
void onDragEnd(const DragEndEvent& e) override; | |||||
void onChange(const ChangeEvent& e) override; | |||||
}; | |||||
} // namespace app | |||||
} // namespace rack |
@@ -6,6 +6,7 @@ | |||||
#include <app/SvgPort.hpp> | #include <app/SvgPort.hpp> | ||||
#include <app/ModuleLightWidget.hpp> | #include <app/ModuleLightWidget.hpp> | ||||
#include <app/SvgSwitch.hpp> | #include <app/SvgSwitch.hpp> | ||||
#include <app/SvgLatch.hpp> | |||||
#include <app/SvgScrew.hpp> | #include <app/SvgScrew.hpp> | ||||
#include <app/AudioWidget.hpp> | #include <app/AudioWidget.hpp> | ||||
#include <app/MidiWidget.hpp> | #include <app/MidiWidget.hpp> | ||||
@@ -840,6 +841,13 @@ struct LEDButton : app::SvgSwitch { | |||||
} | } | ||||
}; | }; | ||||
struct LEDLatch : app::SvgLatch { | |||||
LEDLatch() { | |||||
addFrame(Svg::load(asset::system("res/ComponentLibrary/LEDButton.svg"))); | |||||
addFrame(Svg::load(asset::system("res/ComponentLibrary/LEDButton_1.svg"))); | |||||
} | |||||
}; | |||||
template <typename TLight> | template <typename TLight> | ||||
struct LEDLightButton : LEDButton { | struct LEDLightButton : LEDButton { | ||||
app::ModuleLightWidget* light; | app::ModuleLightWidget* light; | ||||
@@ -856,6 +864,22 @@ struct LEDLightButton : LEDButton { | |||||
} | } | ||||
}; | }; | ||||
template <typename TLight> | |||||
struct LEDLightLatch : LEDLatch { | |||||
app::ModuleLightWidget* light; | |||||
LEDLightLatch() { | |||||
light = new TLight; | |||||
// Move center of light to center of box | |||||
light->box.pos = box.size.div(2).minus(light->box.size.div(2)); | |||||
addChild(light); | |||||
} | |||||
app::ModuleLightWidget* getLight() { | |||||
return light; | |||||
} | |||||
}; | |||||
struct BefacoSwitch : app::SvgSwitch { | struct BefacoSwitch : app::SvgSwitch { | ||||
BefacoSwitch() { | BefacoSwitch() { | ||||
addFrame(Svg::load(asset::system("res/ComponentLibrary/BefacoSwitch_0.svg"))); | addFrame(Svg::load(asset::system("res/ComponentLibrary/BefacoSwitch_0.svg"))); | ||||
@@ -87,6 +87,7 @@ Directly including Rack headers other than rack.hpp in your plugin is unsupporte | |||||
#include <app/ModuleWidget.hpp> | #include <app/ModuleWidget.hpp> | ||||
#include <app/SvgSlider.hpp> | #include <app/SvgSlider.hpp> | ||||
#include <app/SvgButton.hpp> | #include <app/SvgButton.hpp> | ||||
#include <app/SvgLatch.hpp> | |||||
#include <engine/Param.hpp> | #include <engine/Param.hpp> | ||||
#include <engine/ParamHandle.hpp> | #include <engine/ParamHandle.hpp> | ||||
@@ -0,0 +1,74 @@ | |||||
#include <app/SvgLatch.hpp> | |||||
namespace rack { | |||||
namespace app { | |||||
struct SvgLatch::Internal { | |||||
bool unlatch = false; | |||||
}; | |||||
SvgLatch::SvgLatch() { | |||||
internal = new Internal; | |||||
} | |||||
SvgLatch::~SvgLatch() { | |||||
delete internal; | |||||
} | |||||
void SvgLatch::onDragStart(const DragStartEvent& e) { | |||||
ParamWidget::onDragStart(e); | |||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | |||||
return; | |||||
// Set value | |||||
engine::ParamQuantity* pq = getParamQuantity(); | |||||
if (pq) { | |||||
if (pq->isMax()) { | |||||
internal->unlatch = true; | |||||
} | |||||
else { | |||||
pq->setMax(); | |||||
} | |||||
} | |||||
// Set down frame | |||||
if (frames.size() >= 2) { | |||||
sw->setSvg(frames[1]); | |||||
fb->setDirty(); | |||||
} | |||||
} | |||||
void SvgLatch::onDragEnd(const DragEndEvent& e) { | |||||
ParamWidget::onDragEnd(e); | |||||
if (e.button != GLFW_MOUSE_BUTTON_LEFT) | |||||
return; | |||||
// Set value | |||||
engine::ParamQuantity* pq = getParamQuantity(); | |||||
if (pq) { | |||||
if (internal->unlatch) { | |||||
internal->unlatch = false; | |||||
pq->setMin(); | |||||
} | |||||
} | |||||
// Set up frame | |||||
if (frames.size() >= 1) { | |||||
sw->setSvg(frames[0]); | |||||
fb->setDirty(); | |||||
} | |||||
} | |||||
void SvgLatch::onChange(const ChangeEvent& e) { | |||||
// Bypass SvgSwitch behavior | |||||
Switch::onChange(e); | |||||
} | |||||
} // namespace app | |||||
} // namespace rack |
@@ -36,7 +36,7 @@ void SvgSwitch::onChange(const ChangeEvent& e) { | |||||
int index = (int) std::round(pq->getValue() - pq->getMinValue()); | int index = (int) std::round(pq->getValue() - pq->getMinValue()); | ||||
index = math::clamp(index, 0, (int) frames.size() - 1); | index = math::clamp(index, 0, (int) frames.size() - 1); | ||||
sw->setSvg(frames[index]); | sw->setSvg(frames[index]); | ||||
fb->dirty = true; | |||||
fb->setDirty(); | |||||
} | } | ||||
ParamWidget::onChange(e); | ParamWidget::onChange(e); | ||||
} | } | ||||
@@ -10,8 +10,9 @@ namespace app { | |||||
struct Switch::Internal { | struct Switch::Internal { | ||||
/** Hysteresis state for momentary switch */ | |||||
/** Whether momentary switch was pressed this frame. */ | |||||
bool momentaryPressed = false; | bool momentaryPressed = false; | ||||
/** Whether momentary switch was released this frame. */ | |||||
bool momentaryReleased = false; | bool momentaryReleased = false; | ||||
}; | }; | ||||