#include "Knob.h" namespace serum { Knob::Knob (const juce::String& name, std::function 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(); 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