Add a floating value label near the cursor while dragging a knob, giving immediate feedback without altering slider behaviour. Inherit SettableTooltipClient on ToggleButton so setTooltip works consistently with knobs and combos. Introduce a shared layout constants namespace in PluginEditor so every panel, row and control position derives from one spacing system. Add tooltips to all knobs, combos, toggles, and buttons. Add Tab/Shift+Tab tab cycling via keyPressed, with a TooltipWindow for hover hints.
87 lines
2.4 KiB
C++
87 lines
2.4 KiB
C++
#include "Knob.h"
|
|
|
|
namespace serum
|
|
{
|
|
|
|
Knob::Knob (const juce::String& name, std::function<juce::String (float)> fmt)
|
|
: formatter (std::move (fmt))
|
|
{
|
|
setSliderStyle (juce::Slider::RotaryVerticalDrag);
|
|
setTextBoxStyle (juce::Slider::NoTextBox, false, 0, 0);
|
|
setRange (0.0, 1.0);
|
|
setDoubleClickReturnValue (true, 0.5);
|
|
setName (name);
|
|
setComponentID (name);
|
|
}
|
|
|
|
juce::String Knob::getTextFromValue (double value)
|
|
{
|
|
if (formatter)
|
|
return formatter ((float) value);
|
|
return formatPercent ((float) value);
|
|
}
|
|
|
|
void Knob::mouseDown (const juce::MouseEvent& e)
|
|
{
|
|
juce::Slider::mouseDown (e);
|
|
showValuePopup (e);
|
|
}
|
|
|
|
void Knob::mouseDrag (const juce::MouseEvent& e)
|
|
{
|
|
juce::Slider::mouseDrag (e);
|
|
showValuePopup (e);
|
|
}
|
|
|
|
void Knob::mouseUp (const juce::MouseEvent& e)
|
|
{
|
|
juce::Slider::mouseUp (e);
|
|
hideValuePopup();
|
|
}
|
|
|
|
void Knob::mouseExit (const juce::MouseEvent& e)
|
|
{
|
|
juce::Slider::mouseExit (e);
|
|
hideValuePopup();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// The floating readout lives as a child of the top-level component so it can
|
|
// follow the cursor outside this knob's own bounds without being clipped, and
|
|
// it never intercepts mouse clicks so it can't block interaction.
|
|
// ---------------------------------------------------------------------------
|
|
void Knob::showValuePopup (const juce::MouseEvent& e)
|
|
{
|
|
auto* topLevel = getTopLevelComponent();
|
|
if (topLevel == nullptr)
|
|
return;
|
|
|
|
if (valuePopup == nullptr)
|
|
{
|
|
valuePopup = std::make_unique<juce::Label>();
|
|
valuePopup->setAlwaysOnTop (true);
|
|
valuePopup->setInterceptsMouseClicks (false, false);
|
|
valuePopup->setColour (juce::Label::backgroundColourId, juce::Colours::transparentBlack);
|
|
valuePopup->setColour (juce::Label::textColourId, theme::text);
|
|
valuePopup->setFont (juce::Font (14.0f, juce::Font::bold));
|
|
valuePopup->setJustificationType (juce::Justification::centred);
|
|
topLevel->addAndMakeVisible (valuePopup.get());
|
|
}
|
|
|
|
valuePopup->setText (getTextFromValue (getValue()), juce::dontSendNotification);
|
|
|
|
constexpr int w = 84;
|
|
constexpr int h = 24;
|
|
constexpr int gap = 10;
|
|
|
|
const auto cursor = topLevel->getLocalPoint (nullptr, e.getScreenPosition());
|
|
valuePopup->setBounds (cursor.getX() - w / 2, cursor.getY() - h - gap, w, h);
|
|
}
|
|
|
|
void Knob::hideValuePopup()
|
|
{
|
|
valuePopup.reset();
|
|
}
|
|
|
|
} // namespace serum
|