Double+Click Enter Value

Is there a way to be able to Double+Click and Enter value for a knob? Is this a Juce Feature?

Double%20Click%20Value

1 Like

you can inherit from Slider, and override some of the MouseListener methods to intercept the default behavior. just be sure to call the appropriate Slider::mouse___ methods to retain the default behavior once you’ve added your custom behavior.

I don’t think you need to override anything. JUCE sliders have a text entry box:
https://docs.juce.com/master/classSlider.html#a59e3fd9bc86e488070c12889747e7bbe

how are you going to get it to appear when you double click unless you specify that it should appear when you double click? that’s not the default behavior for Sliders, right?

Sliders can have a text box, but it’s always visible, and doesn’t auto-hide after editing or when pressing ESC. And it has to be contained entirely inside the Slider’s bounds. Anything outside is cut off. So when knobs are side-by-side, and the text box should be wide, it’ll overlap with the neighboring Slider, which means that the Sliders themselves have to overlap

I think we will just use a pop-up window entry.

Just wondering how other people are allow ing users to enter in the values for knobs and sliders?

1 Like

Yes you’re right @matkatmusic I thought there was already a text box there. @MIDIculous I’ve only ever used the standard feature you describe. But I do implement my own popups due to earlier limitations in the JUCE ones (which have since been addressed). It would be trivial to allow double clicking on them I guess.

if you override most of the mouseListener methods on your custom slider, you can eat all the mouse events. you can also hide all the default labels/thumbs and stuff in the constructor and design it however you want. add your own custom labels that only appear when double clicked, or when the user does a backflip, etc. the Slider is just a component with customized mouse behavior and graphics. so treat it like any other component that can have child components.

it’s not always visible.

https://docs.juce.com/master/classSlider.html#a5bc748a21e72fe14153bc9fe5ac03e77

If you added double-clicking/text entry on the popup labels then you wouldn’t have to override the slider’s mouse listener methods. Slightly different functionality, but also a little cleaner, perhaps. Either approach would work fine in end I guess.

Is there a way to not have the text box as a child of the Slider, but as a child of some other Component? As it is, the problem is that the text box draw outside of the Slider’s bounds, so in order to show long numbers in the text box, the Slider would need large bounds, overlapping with its siblings. Then you need custom hit testing, to figure out which Slider should receive clicks in the overlap area.

use the class that’ll hold all of your sliders to also provide the labels and handle all the mouse interaction delegation.

for( auto& slider : sliders ) 
{ 
    slider.setInterceptsMouseStuff(false, false); 
    slider.addMouseListener(this); 
}

void mouseMove(const MouseEvent& e)
{
   for(auto& slider : sliders) 
   {
       if( slider.contains(e.getPosition() ) { do whatever, then slider.mouseMove(e); }
   }
}

Thanks, I appreciate the help. But I don’t have just one class that holds all the sliders. There are classes for sections of the GUI, and some Sliders are right at the border of a section. So the section Component cannot be the Label parent, either. To apply this, I would have to catch mouse events on my top-level Component, and then crawl the view hierarchy on every mouse move/click/… :neutral_face:

so change your GUI…

Pop up menu will solve it easier. Thanks guys for the help.

I had this same problem. Providing my snippet both people having the same problem and to see if there are more elegant solutions :slight_smile:

// -----------------------------------------------------------------------------
// having to inherit everything feels so 90's...
// A window that when modal gets destroyed by clicking outside
class resizable_win_destroyed_clicking : public juce::ResizableWindow {
public:
  using ResizableWindow::ResizableWindow;

  virtual void inputAttemptWhenModal() override
  {
    setVisible (false);
    exitModalState (0);
  }
};
// A slider that allows data entry on the center of the slider and doesn't have
// the width limited to the slider size.
class slider_w_data_entry : public juce::Slider {
public:
  template <class... Ts>
  slider_w_data_entry (Ts&&... args) : _edit_win {"", true}
  {
    _edit.setScrollbarsShown (false);
    _edit.setReadOnly (false);
    _edit.onEscapeKey = [this]() {
      _edit_win.setVisible (false);
      _edit_win.exitModalState (0);
    };
    _edit.onReturnKey = [this]() {
      setValue (getValueFromText (_edit.getText()));
      _edit.onEscapeKey();
    };
    _edit.onFocusLost = [this]() { _edit.onEscapeKey(); };
  }

  ~slider_w_data_entry()
  {
    _edit_win.setLookAndFeel (nullptr);
    _edit.setLookAndFeel (nullptr);
  }

  void lookAndFeelChanged() override
  {
    Slider::lookAndFeelChanged();
    _edit_win.setLookAndFeel (&getLookAndFeel());
    _edit.setLookAndFeel (&getLookAndFeel());
  }

  void parentHierarchyChanged() override
  {
    // make the window be children of the main window
    Slider::parentHierarchyChanged();
    auto top = getTopLevelComponent();
    if (top) {
      top->addChildComponent (_edit_win);
    }
  }

  void mouseDown (juce::MouseEvent const& e) override
  {
    juce::ModifierKeys mods = juce::ModifierKeys::getCurrentModifiersRealtime();
    if (mods.isRightButtonDown() && isEnabled()) {
      adjust_positions();
      _edit_win.setContentNonOwned (&_edit, false);
      _edit_win.setVisible (true);
      _edit_win.enterModalState (true, nullptr, false);
      _edit.grabKeyboardFocus();
    }
    else {
      Slider::mouseDown (e);
    }
  }

private:
  void adjust_positions()
  {
    auto b = getBounds();
    _edit.setText (getTextFromValue (getValue()), juce::dontSendNotification);
    b = b.withSizeKeepingCentre (
      std::max (
        b.getWidth(),
        _edit.getTextWidth() + _edit.getBorder().getLeftAndRight()),
      _edit.getTextHeight());
    if (b.getX() < 0) {
      b = b.withX (0);
    }
    auto top = getTopLevelComponent();
    if (top) {
      auto rdiff = top->getLocalBounds().getRight() - b.getRight();
      if (rdiff < 0) {
        b = b.withX (b.getX() + x_r);
      }
    }
    _edit_win.setBounds (b);
    _edit.setBounds (b);
  }

  resizable_win_destroyed_clicking _edit_win;
  juce::TextEditor                 _edit;
};