How To Make Click&Drag Number Box Slider (Without the Slider)?

I don’t know how this type of UI element is called but I need a simple Number or BumberBox on wich i can change the value with draging (horizontal or vertical).
what is the best way of doing this?
greets!

You can get that behaviour with a slider if you tweak its options - have a look at some of the variations of slider shown in the demo app on the ‘widgets’ page

Create a Component with a hidden Slider.

Rail

1 Like

well… how can i do this?

thanks but there is not one single example on how to change the values by draging on the number box…
what would be the workaround for this?

I’d have thought the LinearBarHorizontal style would do pretty much what you need? If you really don’t want it to show the bar you could make it transparent or give it a custom look+feel to make it look exactly how you want.

is the LookAndFeel_V2::getSliderLayout() the right thing to override?

You can see all the relevant lookandfeel methods in the Slider::LookAndFeelMethods class. Which ones you implement will obviously depend on what you want to do.

ah yes! LinearBarHorizontal with Alpha = 0 does it!!
but how can I get rid of the outline? where is it located?

many thanks!

No idea offhand, you can probably find it in the lookandfeel methods somewhere. Or set its colour to transparent.

ok thank you very much! found it.
if someone is interested:

you have to override
drawLabel (Graphics& g, Label& label)
this is how mine looks:

void OtherLookAndFeel::drawLabel (Graphics& g, Label& label)
{
    g.fillAll (label.findColour (Label::backgroundColourId));
    
    if (! label.isBeingEdited())
    {
        const float alpha = label.isEnabled() ? 1.0f : 0.5f;
        Font font (getLabelFont (label));
        font.setHeight (fontHeight);
        g.setColour (label.findColour (Label::textColourId).withMultipliedAlpha (alpha));
        g.setFont (font);
        
        Rectangle<int> textArea (label.getBorderSize().subtractedFrom (label.getLocalBounds()));
        
        g.drawFittedText (label.getText(), textArea, label.getJustificationType(),
                          jmax (1, (int) (textArea.getHeight() / font.getHeight())),
                          label.getMinimumHorizontalScale());
        
        g.setColour (Colours::transparentBlack);
    }
    else if (label.isEnabled())
    {
        g.setColour (Colours::transparentBlack);
    }
    
    g.drawRect (label.getLocalBounds());
}

and in the custom l+f:

setColour (Slider::thumbColourId, Colours::transparentBlack); //
setColour (Slider::trackColourId, Colours::transparentBlack); //
setColour (Slider::backgroundColourId, Colours::transparentBlack); //

3 Likes

I’ll give you the header of my class… which should give you some ideas… my class has some additional functionality you won’t require…

class CNumericLabelLF : public LookAndFeel_V3
{
public:
    CNumericLabelLF() {};

    void drawLinearSliderBackground (Graphics& , int , int , int , int , float , float , float , const Slider::SliderStyle , Slider& ) override
    {
    }

    void drawLinearSliderThumb (Graphics& , int , int , int , int , float , float , float , const Slider::SliderStyle , Slider& ) override
    {
    }

    Font getLabelFont (Label& label) override
    {
        return label.getFont().withHeight (11.0);
    }

private:

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CNumericLabelLF)
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

class CNumericSlider;

class CNumericLabel : public Label,
                      public Slider::Listener
{
public:

    CNumericLabel (const String& componentName = String(), const String& labelText = String());
    ~CNumericLabel();

    void resized() override;

    void sliderValueChanged (Slider* slider) override;
    void sliderDragEnded (Slider* slider) override;

    void setRange (int iMin, int iMax);

    void setDoubleClickReturnValue (int iDefault);

    void setValue (int iNewValue);

    void setIsTopValue (bool bIsMaxLabel)       { m_bIsMaxValue = bIsMaxLabel;          }

    int  getValue() noexcept                    { return m_iValue;                      }

    void setOppositeValue (int iOppositeValue)  { m_iOppositeValue = iOppositeValue;    }

    ////////////////////////////////////////////////////////////////////////////

    class CNumericSlider : public Slider
    {
    public:

        CNumericSlider (CNumericLabel* pOwner) : m_pOwner (pOwner)
        {
            setTextBoxStyle (Slider::NoTextBox, false, 70, 20);
            setSliderSnapsToMousePosition (false);
        }

        ~CNumericSlider() {}

        void mouseDown (const MouseEvent& event) override
        {
            unfocusAllComponents();

            Slider::mouseDown (event);
        }

        void mouseEnter (const MouseEvent& event) override
        {
            m_pOwner->setColour (Label::outlineColourId, Colours::grey);

            Slider::mouseEnter (event);
        }

        void mouseExit (const MouseEvent& event) override
        {
            m_pOwner->setColour (Label::outlineColourId, Colour (0x1000282));

            Slider::mouseExit (event);
        }

    private:

        CNumericLabel*  m_pOwner;


        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CNumericSlider)
    };

    ////////////////////////////////////////////////////////////////////////////

    //==============================================================================
    /** Receives callbacks when a CNumericLabel object changes.
	 */
    class  Listener
    {
    public:

        virtual ~Listener() {}

        /** Called when a CNumericLabel object is changed.
         */
        virtual void labelChanged (CNumericLabel* label) = 0;

        virtual void labelDragEnded (CNumericLabel* ) {}
    };

    /** Adds a listener to receive callbacks when the value changes.
     */
    void addListener (Listener* const listener);

    /** Removes a listener that was previously added with addListener(). */
    void removeListener (Listener* const listener);

    /** Updates the CNumericLabel's listeners.
     Call this to explicitly tell any registerd listeners that the value has changed.
	 */
	void updateListeners();

private:

    int     m_iValue;
    int     m_iMinValue;
    int     m_iMaxValue;
    int     m_iDefaultValue;

    bool    m_bIsMaxValue;
    int     m_iOppositeValue;

    ScopedPointer<CNumericSlider>   m_pHiddenSlider;

    CNumericLabelLF m_Look;

    ListenerList<Listener> m_Listeners;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CNumericLabel)
};

typedef CNumericLabel::Listener CNumericLabelListener;

Cheers,

Rail

1 Like

thank you very much for sharing this!!

In case anyone arrives here from Google like I did, there’s an easy way to make a draggable number box without overriding LookAndFeelMethods or writing any custom classes, simply:

slider.setSliderStyle(Slider::SliderStyle::LinearBarVertical);
slider.setSliderSnapsToMousePosition(false);
slider.setColour(Slider::trackColourId, Colours::transparentBlack);
slider.setSize(60, 20);
1 Like

Thanks, but the speed of the change wile dragging, isn’t it very fast, because of the small vertical distance? I think there is still the demand for a real label only mode.

Here’s a similar topic (I think) of what you may be shooting for. Check out my example at the bottom Ableton Live-Style Text Slider

@chkn Slider::setMouseDragSensitivity() allows you to change the vertical distance

You can also hold down the option key for finer control