JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_Slider.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29static double getStepSize (const Slider& slider)
30{
31 const auto interval = slider.getInterval();
32
33 return ! approximatelyEqual (interval, 0.0) ? interval
34 : slider.getRange().getLength() * 0.01;
35}
36
37class Slider::Pimpl : public AsyncUpdater, // this needs to be public otherwise it will cause an
38 // error when JUCE_DLL_BUILD=1
39 private Value::Listener
40{
41public:
43 : owner (s),
44 style (sliderStyle),
45 textBoxPos (textBoxPosition)
46 {
47 rotaryParams.startAngleRadians = MathConstants<float>::pi * 1.2f;
48 rotaryParams.endAngleRadians = MathConstants<float>::pi * 2.8f;
49 rotaryParams.stopAtEnd = true;
50 }
51
52 ~Pimpl() override
53 {
54 currentValue.removeListener (this);
55 valueMin.removeListener (this);
56 valueMax.removeListener (this);
57 popupDisplay.reset();
58 }
59
60 //==============================================================================
61 void registerListeners()
62 {
63 currentValue.addListener (this);
64 valueMin.addListener (this);
65 valueMax.addListener (this);
66 }
67
68 bool isHorizontal() const noexcept
69 {
70 return style == LinearHorizontal
71 || style == LinearBar
72 || style == TwoValueHorizontal
73 || style == ThreeValueHorizontal;
74 }
75
76 bool isVertical() const noexcept
77 {
78 return style == LinearVertical
79 || style == LinearBarVertical
80 || style == TwoValueVertical
81 || style == ThreeValueVertical;
82 }
83
84 bool isRotary() const noexcept
85 {
86 return style == Rotary
87 || style == RotaryHorizontalDrag
88 || style == RotaryVerticalDrag
90 }
91
92 bool isBar() const noexcept
93 {
94 return style == LinearBar
95 || style == LinearBarVertical;
96 }
97
98 bool isTwoValue() const noexcept
99 {
100 return style == TwoValueHorizontal
101 || style == TwoValueVertical;
102 }
103
104 bool isThreeValue() const noexcept
105 {
106 return style == ThreeValueHorizontal
107 || style == ThreeValueVertical;
108 }
109
110 bool incDecDragDirectionIsHorizontal() const noexcept
111 {
112 return incDecButtonMode == incDecButtonsDraggable_Horizontal
113 || (incDecButtonMode == incDecButtonsDraggable_AutoDirection && incDecButtonsSideBySide);
114 }
115
116 float getPositionOfValue (double value) const
117 {
118 if (isHorizontal() || isVertical())
119 return getLinearSliderPos (value);
120
121 jassertfalse; // not a valid call on a slider that doesn't work linearly!
122 return 0.0f;
123 }
124
125 void setNumDecimalPlacesToDisplay (int decimalPlacesToDisplay)
126 {
127 fixedNumDecimalPlaces = jmax (0, decimalPlacesToDisplay);
128 numDecimalPlaces = fixedNumDecimalPlaces;
129 }
130
131 int getNumDecimalPlacesToDisplay() const
132 {
133 return fixedNumDecimalPlaces == -1 ? numDecimalPlaces : fixedNumDecimalPlaces;
134 }
135
136 void updateRange()
137 {
138 if (fixedNumDecimalPlaces == -1)
139 {
140 // figure out the number of DPs needed to display all values at this
141 // interval setting.
142 numDecimalPlaces = 7;
143
144 if (! approximatelyEqual (normRange.interval, 0.0))
145 {
146 int v = std::abs (roundToInt (normRange.interval * 10000000));
147
148 while ((v % 10) == 0 && numDecimalPlaces > 0)
149 {
150 --numDecimalPlaces;
151 v /= 10;
152 }
153 }
154 }
155
156 // keep the current values inside the new range..
157 if (style != TwoValueHorizontal && style != TwoValueVertical)
158 {
159 setValue (getValue(), dontSendNotification);
160 }
161 else
162 {
163 setMinValue (getMinValue(), dontSendNotification, false);
164 setMaxValue (getMaxValue(), dontSendNotification, false);
165 }
166
167 updateText();
168 }
169
170 void setRange (double newMin, double newMax, double newInt)
171 {
173 normRange.skew, normRange.symmetricSkew);
174 updateRange();
175 }
176
177 void setNormalisableRange (NormalisableRange<double> newRange)
178 {
179 normRange = newRange;
180 updateRange();
181 }
182
183 double getValue() const
184 {
185 // for a two-value style slider, you should use the getMinValue() and getMaxValue()
186 // methods to get the two values.
187 jassert (style != TwoValueHorizontal && style != TwoValueVertical);
188
189 return currentValue.getValue();
190 }
191
192 void setValue (double newValue, NotificationType notification)
193 {
194 // for a two-value style slider, you should use the setMinValue() and setMaxValue()
195 // methods to set the two values.
196 jassert (style != TwoValueHorizontal && style != TwoValueVertical);
197
198 newValue = constrainedValue (newValue);
199
200 if (style == ThreeValueHorizontal || style == ThreeValueVertical)
201 {
202 jassert (static_cast<double> (valueMin.getValue()) <= static_cast<double> (valueMax.getValue()));
203
204 newValue = jlimit (static_cast<double> (valueMin.getValue()),
205 static_cast<double> (valueMax.getValue()),
206 newValue);
207 }
208
209 if (! approximatelyEqual (newValue, lastCurrentValue))
210 {
211 if (valueBox != nullptr)
212 valueBox->hideEditor (true);
213
214 lastCurrentValue = newValue;
215
216 // Need to do this comparison because the Value will use equalsWithSameType to compare
217 // the new and old values, so will generate unwanted change events if the type changes.
218 // Cast to double before comparing, to prevent comparing as another type (e.g. String).
219 if (! approximatelyEqual (static_cast<double> (currentValue.getValue()), newValue))
220 currentValue = newValue;
221
222 updateText();
223 owner.repaint();
224
225 triggerChangeMessage (notification);
226 }
227 }
228
229 void setMinValue (double newValue, NotificationType notification, bool allowNudgingOfOtherValues)
230 {
231 // The minimum value only applies to sliders that are in two- or three-value mode.
232 jassert (style == TwoValueHorizontal || style == TwoValueVertical
233 || style == ThreeValueHorizontal || style == ThreeValueVertical);
234
235 newValue = constrainedValue (newValue);
236
237 if (style == TwoValueHorizontal || style == TwoValueVertical)
238 {
239 if (allowNudgingOfOtherValues && newValue > static_cast<double> (valueMax.getValue()))
240 setMaxValue (newValue, notification, false);
241
242 newValue = jmin (static_cast<double> (valueMax.getValue()), newValue);
243 }
244 else
245 {
246 if (allowNudgingOfOtherValues && newValue > lastCurrentValue)
247 setValue (newValue, notification);
248
249 newValue = jmin (lastCurrentValue, newValue);
250 }
251
252 if (! approximatelyEqual (lastValueMin, newValue))
253 {
254 lastValueMin = newValue;
255 valueMin = newValue;
256 owner.repaint();
257 updatePopupDisplay();
258
259 triggerChangeMessage (notification);
260 }
261 }
262
263 void setMaxValue (double newValue, NotificationType notification, bool allowNudgingOfOtherValues)
264 {
265 // The maximum value only applies to sliders that are in two- or three-value mode.
266 jassert (style == TwoValueHorizontal || style == TwoValueVertical
267 || style == ThreeValueHorizontal || style == ThreeValueVertical);
268
269 newValue = constrainedValue (newValue);
270
271 if (style == TwoValueHorizontal || style == TwoValueVertical)
272 {
273 if (allowNudgingOfOtherValues && newValue < static_cast<double> (valueMin.getValue()))
274 setMinValue (newValue, notification, false);
275
276 newValue = jmax (static_cast<double> (valueMin.getValue()), newValue);
277 }
278 else
279 {
280 if (allowNudgingOfOtherValues && newValue < lastCurrentValue)
281 setValue (newValue, notification);
282
283 newValue = jmax (lastCurrentValue, newValue);
284 }
285
286 if (! approximatelyEqual (lastValueMax, newValue))
287 {
288 lastValueMax = newValue;
289 valueMax = newValue;
290 owner.repaint();
291 updatePopupDisplay();
292
293 triggerChangeMessage (notification);
294 }
295 }
296
297 void setMinAndMaxValues (double newMinValue, double newMaxValue, NotificationType notification)
298 {
299 // The maximum value only applies to sliders that are in two- or three-value mode.
300 jassert (style == TwoValueHorizontal || style == TwoValueVertical
301 || style == ThreeValueHorizontal || style == ThreeValueVertical);
302
305
306 newMinValue = constrainedValue (newMinValue);
307 newMaxValue = constrainedValue (newMaxValue);
308
309 if (! approximatelyEqual (lastValueMax, newMaxValue) || ! approximatelyEqual (lastValueMin, newMinValue))
310 {
311 lastValueMax = newMaxValue;
312 lastValueMin = newMinValue;
313 valueMin = newMinValue;
314 valueMax = newMaxValue;
315 owner.repaint();
316
317 triggerChangeMessage (notification);
318 }
319 }
320
321 double getMinValue() const
322 {
323 // The minimum value only applies to sliders that are in two- or three-value mode.
324 jassert (style == TwoValueHorizontal || style == TwoValueVertical
325 || style == ThreeValueHorizontal || style == ThreeValueVertical);
326
327 return valueMin.getValue();
328 }
329
330 double getMaxValue() const
331 {
332 // The maximum value only applies to sliders that are in two- or three-value mode.
333 jassert (style == TwoValueHorizontal || style == TwoValueVertical
334 || style == ThreeValueHorizontal || style == ThreeValueVertical);
335
336 return valueMax.getValue();
337 }
338
339 void triggerChangeMessage (NotificationType notification)
340 {
342 {
343 owner.valueChanged();
344
347 else
349 }
350 }
351
352 void handleAsyncUpdate() override
353 {
355
357 listeners.callChecked (checker, [&] (Slider::Listener& l) { l.sliderValueChanged (&owner); });
358
359 if (checker.shouldBailOut())
360 return;
361
362 NullCheckedInvocation::invoke (owner.onValueChange);
363
364 if (checker.shouldBailOut())
365 return;
366
367 if (auto* handler = owner.getAccessibilityHandler())
368 handler->notifyAccessibilityEvent (AccessibilityEvent::valueChanged);
369 }
370
371 void sendDragStart()
372 {
373 owner.startedDragging();
374
376 listeners.callChecked (checker, [&] (Slider::Listener& l) { l.sliderDragStarted (&owner); });
377
378 if (checker.shouldBailOut())
379 return;
380
381 NullCheckedInvocation::invoke (owner.onDragStart);
382 }
383
384 void sendDragEnd()
385 {
386 owner.stoppedDragging();
387 sliderBeingDragged = -1;
388
389 Component::BailOutChecker checker (&owner);
390 listeners.callChecked (checker, [&] (Slider::Listener& l) { l.sliderDragEnded (&owner); });
391
392 if (checker.shouldBailOut())
393 return;
394
395 NullCheckedInvocation::invoke (owner.onDragEnd);
396 }
397
398 void incrementOrDecrement (double delta)
399 {
400 if (style == IncDecButtons)
401 {
402 auto newValue = owner.snapValue (getValue() + delta, notDragging);
403
404 if (currentDrag != nullptr)
405 {
406 setValue (newValue, sendNotificationSync);
407 }
408 else
409 {
410 ScopedDragNotification drag (owner);
411 setValue (newValue, sendNotificationSync);
412 }
413 }
414 }
415
416 void valueChanged (Value& value) override
417 {
418 if (value.refersToSameSourceAs (currentValue))
419 {
420 if (style != TwoValueHorizontal && style != TwoValueVertical)
421 setValue (currentValue.getValue(), dontSendNotification);
422 }
423 else if (value.refersToSameSourceAs (valueMin))
424 {
425 setMinValue (valueMin.getValue(), dontSendNotification, true);
426 }
427 else if (value.refersToSameSourceAs (valueMax))
428 {
429 setMaxValue (valueMax.getValue(), dontSendNotification, true);
430 }
431 }
432
433 void textChanged()
434 {
435 auto newValue = owner.snapValue (owner.getValueFromText (valueBox->getText()), notDragging);
436
437 if (! approximatelyEqual (newValue, static_cast<double> (currentValue.getValue())))
438 {
439 ScopedDragNotification drag (owner);
440 setValue (newValue, sendNotificationSync);
441 }
442
443 updateText(); // force a clean-up of the text, needed in case setValue() hasn't done this.
444 }
445
446 void updateText()
447 {
448 if (valueBox != nullptr)
449 {
450 auto newValue = owner.getTextFromValue (currentValue.getValue());
451
452 if (newValue != valueBox->getText())
453 valueBox->setText (newValue, dontSendNotification);
454 }
455
456 updatePopupDisplay();
457 }
458
459 double constrainedValue (double value) const
460 {
461 return normRange.snapToLegalValue (value);
462 }
463
464 float getLinearSliderPos (double value) const
465 {
466 double pos;
467
468 if (normRange.end <= normRange.start)
469 pos = 0.5;
470 else if (value < normRange.start)
471 pos = 0.0;
472 else if (value > normRange.end)
473 pos = 1.0;
474 else
475 pos = owner.valueToProportionOfLength (value);
476
477 if (isVertical() || style == IncDecButtons)
478 pos = 1.0 - pos;
479
480 jassert (pos >= 0 && pos <= 1.0);
481 return (float) (sliderRegionStart + pos * sliderRegionSize);
482 }
483
484 void setSliderStyle (SliderStyle newStyle)
485 {
486 if (style != newStyle)
487 {
488 style = newStyle;
489
490 owner.repaint();
491 owner.lookAndFeelChanged();
493 }
494 }
495
496 void setVelocityModeParameters (double sensitivity, int threshold,
497 double offset, bool userCanPressKeyToSwapMode,
499 {
500 velocityModeSensitivity = sensitivity;
501 velocityModeOffset = offset;
502 velocityModeThreshold = threshold;
503 userKeyOverridesVelocity = userCanPressKeyToSwapMode;
504 modifierToSwapModes = newModifierToSwapModes;
505 }
506
507 void setIncDecButtonsMode (IncDecButtonMode mode)
508 {
509 if (incDecButtonMode != mode)
510 {
511 incDecButtonMode = mode;
512 owner.lookAndFeelChanged();
513 }
514 }
515
516 void setTextBoxStyle (TextEntryBoxPosition newPosition,
517 bool isReadOnly,
520 {
521 if (textBoxPos != newPosition
522 || editableText != (! isReadOnly)
523 || textBoxWidth != textEntryBoxWidth
524 || textBoxHeight != textEntryBoxHeight)
525 {
526 textBoxPos = newPosition;
527 editableText = ! isReadOnly;
528 textBoxWidth = textEntryBoxWidth;
529 textBoxHeight = textEntryBoxHeight;
530
531 owner.repaint();
532 owner.lookAndFeelChanged();
533 }
534 }
535
536 void setTextBoxIsEditable (bool shouldBeEditable)
537 {
538 editableText = shouldBeEditable;
539 updateTextBoxEnablement();
540 }
541
542 void showTextBox()
543 {
544 jassert (editableText); // this should probably be avoided in read-only sliders.
545
546 if (valueBox != nullptr)
547 valueBox->showEditor();
548 }
549
550 void hideTextBox (bool discardCurrentEditorContents)
551 {
552 if (valueBox != nullptr)
553 {
554 valueBox->hideEditor (discardCurrentEditorContents);
555
557 updateText();
558 }
559 }
560
561 void setTextValueSuffix (const String& suffix)
562 {
563 if (textSuffix != suffix)
564 {
565 textSuffix = suffix;
566 updateText();
567 }
568 }
569
570 void updateTextBoxEnablement()
571 {
572 if (valueBox != nullptr)
573 {
574 bool shouldBeEditable = editableText && owner.isEnabled();
575
576 if (valueBox->isEditable() != shouldBeEditable) // (to avoid changing the single/double click flags unless we need to)
577 valueBox->setEditable (shouldBeEditable);
578 }
579 }
580
581 void lookAndFeelChanged (LookAndFeel& lf)
582 {
583 if (textBoxPos != NoTextBox)
584 {
585 auto previousTextBoxContent = (valueBox != nullptr ? valueBox->getText()
586 : owner.getTextFromValue (currentValue.getValue()));
587
588 valueBox.reset();
589 valueBox.reset (lf.createSliderTextBox (owner));
590 owner.addAndMakeVisible (valueBox.get());
591
592 valueBox->setWantsKeyboardFocus (false);
593 valueBox->setText (previousTextBoxContent, dontSendNotification);
594 valueBox->setTooltip (owner.getTooltip());
595 updateTextBoxEnablement();
596 valueBox->onTextChange = [this] { textChanged(); };
597
598 if (style == LinearBar || style == LinearBarVertical)
599 {
600 valueBox->addMouseListener (&owner, false);
601 valueBox->setMouseCursor (MouseCursor::ParentCursor);
602 }
603 }
604 else
605 {
606 valueBox.reset();
607 }
608
609 if (style == IncDecButtons)
610 {
611 incButton.reset (lf.createSliderButton (owner, true));
612 decButton.reset (lf.createSliderButton (owner, false));
613
614 auto tooltip = owner.getTooltip();
615
616 auto setupButton = [&] (Button& b, bool isIncrement)
617 {
618 owner.addAndMakeVisible (b);
619 b.onClick = [this, isIncrement] { incrementOrDecrement (isIncrement ? normRange.interval : -normRange.interval); };
620
621 if (incDecButtonMode != incDecButtonsNotDraggable)
622 b.addMouseListener (&owner, false);
623 else
624 b.setRepeatSpeed (300, 100, 20);
625
626 b.setTooltip (tooltip);
627 b.setAccessible (false);
628 };
629
630 setupButton (*incButton, true);
631 setupButton (*decButton, false);
632 }
633 else
634 {
635 incButton.reset();
636 decButton.reset();
637 }
638
639 owner.setComponentEffect (lf.getSliderEffect (owner));
640
641 owner.resized();
642 owner.repaint();
643 }
644
645 void showPopupMenu()
646 {
647 PopupMenu m;
648 m.setLookAndFeel (&owner.getLookAndFeel());
649 m.addItem (1, TRANS ("Velocity-sensitive mode"), true, isVelocityBased);
650 m.addSeparator();
651
652 if (isRotary())
653 {
654 PopupMenu rotaryMenu;
655 rotaryMenu.addItem (2, TRANS ("Use circular dragging"), true, style == Rotary);
656 rotaryMenu.addItem (3, TRANS ("Use left-right dragging"), true, style == RotaryHorizontalDrag);
657 rotaryMenu.addItem (4, TRANS ("Use up-down dragging"), true, style == RotaryVerticalDrag);
658 rotaryMenu.addItem (5, TRANS ("Use left-right/up-down dragging"), true, style == RotaryHorizontalVerticalDrag);
659
660 m.addSubMenu (TRANS ("Rotary mode"), rotaryMenu);
661 }
662
663 m.showMenuAsync (PopupMenu::Options(),
664 ModalCallbackFunction::forComponent (sliderMenuCallback, &owner));
665 }
666
667 static void sliderMenuCallback (int result, Slider* slider)
668 {
669 if (slider != nullptr)
670 {
671 switch (result)
672 {
673 case 1: slider->setVelocityBasedMode (! slider->getVelocityBasedMode()); break;
674 case 2: slider->setSliderStyle (Rotary); break;
675 case 3: slider->setSliderStyle (RotaryHorizontalDrag); break;
676 case 4: slider->setSliderStyle (RotaryVerticalDrag); break;
677 case 5: slider->setSliderStyle (RotaryHorizontalVerticalDrag); break;
678 default: break;
679 }
680 }
681 }
682
683 int getThumbIndexAt (const MouseEvent& e)
684 {
685 if (isTwoValue() || isThreeValue())
686 {
687 auto mousePos = isVertical() ? e.position.y : e.position.x;
688
689 auto normalPosDistance = std::abs (getLinearSliderPos (currentValue.getValue()) - mousePos);
690 auto minPosDistance = std::abs (getLinearSliderPos (valueMin.getValue()) + (isVertical() ? 0.1f : -0.1f) - mousePos);
691 auto maxPosDistance = std::abs (getLinearSliderPos (valueMax.getValue()) + (isVertical() ? -0.1f : 0.1f) - mousePos);
692
693 if (isTwoValue())
694 return maxPosDistance <= minPosDistance ? 2 : 1;
695
697 return 1;
698
700 return 2;
701 }
702
703 return 0;
704 }
705
706 //==============================================================================
707 void handleRotaryDrag (const MouseEvent& e)
708 {
709 auto dx = e.position.x - (float) sliderRect.getCentreX();
710 auto dy = e.position.y - (float) sliderRect.getCentreY();
711
712 if (dx * dx + dy * dy > 25.0f)
713 {
714 auto angle = std::atan2 ((double) dx, (double) -dy);
715
716 while (angle < 0.0)
718
719 if (rotaryParams.stopAtEnd && e.mouseWasDraggedSinceMouseDown())
720 {
721 if (std::abs (angle - lastAngle) > MathConstants<double>::pi)
722 {
723 if (angle >= lastAngle)
725 else
727 }
728
729 if (angle >= lastAngle)
730 angle = jmin (angle, (double) jmax (rotaryParams.startAngleRadians, rotaryParams.endAngleRadians));
731 else
732 angle = jmax (angle, (double) jmin (rotaryParams.startAngleRadians, rotaryParams.endAngleRadians));
733 }
734 else
735 {
736 while (angle < rotaryParams.startAngleRadians)
738
739 if (angle > rotaryParams.endAngleRadians)
740 {
741 if (smallestAngleBetween (angle, rotaryParams.startAngleRadians)
742 <= smallestAngleBetween (angle, rotaryParams.endAngleRadians))
743 angle = rotaryParams.startAngleRadians;
744 else
745 angle = rotaryParams.endAngleRadians;
746 }
747 }
748
749 auto proportion = (angle - rotaryParams.startAngleRadians) / (rotaryParams.endAngleRadians - rotaryParams.startAngleRadians);
750 valueWhenLastDragged = owner.proportionOfLengthToValue (jlimit (0.0, 1.0, proportion));
751 lastAngle = angle;
752 }
753 }
754
755 void handleAbsoluteDrag (const MouseEvent& e)
756 {
757 auto mousePos = (isHorizontal() || style == RotaryHorizontalDrag) ? e.position.x : e.position.y;
758 double newPos = 0;
759
760 if (style == RotaryHorizontalDrag
761 || style == RotaryVerticalDrag
762 || style == IncDecButtons
763 || ((style == LinearHorizontal || style == LinearVertical || style == LinearBar || style == LinearBarVertical)
764 && ! snapsToMousePos))
765 {
766 auto mouseDiff = (style == RotaryHorizontalDrag
767 || style == LinearHorizontal
768 || style == LinearBar
769 || (style == IncDecButtons && incDecDragDirectionIsHorizontal()))
770 ? e.position.x - mouseDragStartPos.x
771 : mouseDragStartPos.y - e.position.y;
772
773 newPos = owner.valueToProportionOfLength (valueOnMouseDown)
774 + mouseDiff * (1.0 / pixelsForFullDragExtent);
775
776 if (style == IncDecButtons)
777 {
778 incButton->setState (mouseDiff < 0 ? Button::buttonNormal : Button::buttonDown);
779 decButton->setState (mouseDiff > 0 ? Button::buttonNormal : Button::buttonDown);
780 }
781 }
782 else if (style == RotaryHorizontalVerticalDrag)
783 {
784 auto mouseDiff = (e.position.x - mouseDragStartPos.x)
785 + (mouseDragStartPos.y - e.position.y);
786
787 newPos = owner.valueToProportionOfLength (valueOnMouseDown)
788 + mouseDiff * (1.0 / pixelsForFullDragExtent);
789 }
790 else
791 {
792 newPos = (mousePos - (float) sliderRegionStart) / (double) sliderRegionSize;
793
794 if (isVertical())
795 newPos = 1.0 - newPos;
796 }
797
798 newPos = (isRotary() && ! rotaryParams.stopAtEnd) ? newPos - std::floor (newPos)
799 : jlimit (0.0, 1.0, newPos);
800 valueWhenLastDragged = owner.proportionOfLengthToValue (newPos);
801 }
802
803 void handleVelocityDrag (const MouseEvent& e)
804 {
805 bool hasHorizontalStyle =
806 (isHorizontal() || style == RotaryHorizontalDrag
807 || (style == IncDecButtons && incDecDragDirectionIsHorizontal()));
808
810 ? (e.position.x - mousePosWhenLastDragged.x) + (mousePosWhenLastDragged.y - e.position.y)
811 : (hasHorizontalStyle ? e.position.x - mousePosWhenLastDragged.x
812 : e.position.y - mousePosWhenLastDragged.y);
813
814 auto maxSpeed = jmax (200.0, (double) sliderRegionSize);
815 auto speed = jlimit (0.0, maxSpeed, (double) std::abs (mouseDiff));
816
817 if (! approximatelyEqual (speed, 0.0))
818 {
819 speed = 0.2 * velocityModeSensitivity
820 * (1.0 + std::sin (MathConstants<double>::pi * (1.5 + jmin (0.5, velocityModeOffset
821 + jmax (0.0, (double) (speed - velocityModeThreshold))
822 / maxSpeed))));
823
824 if (mouseDiff < 0)
825 speed = -speed;
826
827 if (isVertical() || style == RotaryVerticalDrag
828 || (style == IncDecButtons && ! incDecDragDirectionIsHorizontal()))
829 speed = -speed;
830
831 auto newPos = owner.valueToProportionOfLength (valueWhenLastDragged) + speed;
832 newPos = (isRotary() && ! rotaryParams.stopAtEnd) ? newPos - std::floor (newPos)
833 : jlimit (0.0, 1.0, newPos);
834 valueWhenLastDragged = owner.proportionOfLengthToValue (newPos);
835
836 e.source.enableUnboundedMouseMovement (true, false);
837 }
838 }
839
840 void mouseDown (const MouseEvent& e)
841 {
842 incDecDragged = false;
843 useDragEvents = false;
844 mouseDragStartPos = mousePosWhenLastDragged = e.position;
845 currentDrag.reset();
846 popupDisplay.reset();
847
848 if (owner.isEnabled())
849 {
850 if (e.mods.isPopupMenu() && menuEnabled)
851 {
852 showPopupMenu();
853 }
854 else if (canDoubleClickToValue()
855 && (singleClickModifiers != ModifierKeys() && e.mods.withoutMouseButtons() == singleClickModifiers))
856 {
857 mouseDoubleClick();
858 }
859 else if (normRange.end > normRange.start)
860 {
861 useDragEvents = true;
862
863 if (valueBox != nullptr)
864 valueBox->hideEditor (true);
865
866 sliderBeingDragged = getThumbIndexAt (e);
867
868 minMaxDiff = static_cast<double> (valueMax.getValue()) - static_cast<double> (valueMin.getValue());
869
870 if (! isTwoValue())
871 lastAngle = rotaryParams.startAngleRadians
872 + (rotaryParams.endAngleRadians - rotaryParams.startAngleRadians)
873 * owner.valueToProportionOfLength (currentValue.getValue());
874
875 valueWhenLastDragged = (sliderBeingDragged == 2 ? valueMax
876 : (sliderBeingDragged == 1 ? valueMin
877 : currentValue)).getValue();
878 valueOnMouseDown = valueWhenLastDragged;
879
880 if (showPopupOnDrag || showPopupOnHover)
881 {
882 showPopupDisplay();
883
884 if (popupDisplay != nullptr)
885 popupDisplay->stopTimer();
886 }
887
888 currentDrag = std::make_unique<ScopedDragNotification> (owner);
889 mouseDrag (e);
890 }
891 }
892 }
893
894 void mouseDrag (const MouseEvent& e)
895 {
896 if (useDragEvents && normRange.end > normRange.start
897 && ! ((style == LinearBar || style == LinearBarVertical)
898 && e.mouseWasClicked() && valueBox != nullptr && valueBox->isEditable()))
899 {
901
902 if (style == Rotary)
903 {
904 handleRotaryDrag (e);
905 }
906 else
907 {
908 if (style == IncDecButtons && ! incDecDragged)
909 {
910 if (e.getDistanceFromDragStart() < 10 || ! e.mouseWasDraggedSinceMouseDown())
911 return;
912
913 incDecDragged = true;
914 mouseDragStartPos = e.position;
915 }
916
917 if (isAbsoluteDragMode (e.mods) || (normRange.end - normRange.start) / sliderRegionSize < normRange.interval)
918 {
920 handleAbsoluteDrag (e);
921 }
922 else
923 {
925 handleVelocityDrag (e);
926 }
927 }
928
929 valueWhenLastDragged = jlimit (normRange.start, normRange.end, valueWhenLastDragged);
930
931 if (sliderBeingDragged == 0)
932 {
933 setValue (owner.snapValue (valueWhenLastDragged, dragMode),
934 sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationSync);
935 }
936 else if (sliderBeingDragged == 1)
937 {
938 setMinValue (owner.snapValue (valueWhenLastDragged, dragMode),
939 sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true);
940
941 if (e.mods.isShiftDown())
942 setMaxValue (getMinValue() + minMaxDiff, dontSendNotification, true);
943 else
944 minMaxDiff = static_cast<double> (valueMax.getValue()) - static_cast<double> (valueMin.getValue());
945 }
946 else if (sliderBeingDragged == 2)
947 {
948 setMaxValue (owner.snapValue (valueWhenLastDragged, dragMode),
949 sendChangeOnlyOnRelease ? dontSendNotification : sendNotificationAsync, true);
950
951 if (e.mods.isShiftDown())
952 setMinValue (getMaxValue() - minMaxDiff, dontSendNotification, true);
953 else
954 minMaxDiff = static_cast<double> (valueMax.getValue()) - static_cast<double> (valueMin.getValue());
955 }
956
957 mousePosWhenLastDragged = e.position;
958 }
959 }
960
961 void mouseUp()
962 {
963 if (owner.isEnabled()
964 && useDragEvents
965 && (normRange.end > normRange.start)
966 && (style != IncDecButtons || incDecDragged))
967 {
968 restoreMouseIfHidden();
969
970 if (sendChangeOnlyOnRelease && ! approximatelyEqual (valueOnMouseDown, static_cast<double> (currentValue.getValue())))
971 triggerChangeMessage (sendNotificationAsync);
972
973 currentDrag.reset();
974 popupDisplay.reset();
975
976 if (style == IncDecButtons)
977 {
978 incButton->setState (Button::buttonNormal);
979 decButton->setState (Button::buttonNormal);
980 }
981 }
982 else if (popupDisplay != nullptr)
983 {
984 popupDisplay->startTimer (200);
985 }
986
987 currentDrag.reset();
988 }
989
990 void mouseMove()
991 {
992 // this is a workaround for a bug where the popup display being dismissed triggers
993 // a mouse move causing it to never be hidden
994 auto shouldShowPopup = showPopupOnHover
995 && (Time::getMillisecondCounterHiRes() - lastPopupDismissal) > 250;
996
998 && ! isTwoValue()
999 && ! isThreeValue())
1000 {
1001 if (owner.isMouseOver (true))
1002 {
1003 if (popupDisplay == nullptr)
1004 showPopupDisplay();
1005
1006 if (popupDisplay != nullptr && popupHoverTimeout != -1)
1007 popupDisplay->startTimer (popupHoverTimeout);
1008 }
1009 }
1010 }
1011
1012 void mouseExit()
1013 {
1014 popupDisplay.reset();
1015 }
1016
1017 bool keyPressed (const KeyPress& key)
1018 {
1019 if (key.getModifiers().isAnyModifierKeyDown())
1020 return false;
1021
1022 const auto getInterval = [this]
1023 {
1024 if (auto* accessibility = owner.getAccessibilityHandler())
1025 if (auto* valueInterface = accessibility->getValueInterface())
1026 return valueInterface->getRange().getInterval();
1027
1028 return getStepSize (owner);
1029 };
1030
1031 const auto valueChange = [&]
1032 {
1033 if (key == KeyPress::rightKey || key == KeyPress::upKey)
1034 return getInterval();
1035
1036 if (key == KeyPress::leftKey || key == KeyPress::downKey)
1037 return -getInterval();
1038
1039 return 0.0;
1040 }();
1041
1043 return false;
1044
1045 setValue (getValue() + valueChange, sendNotificationSync);
1046 return true;
1047 }
1048
1049 void showPopupDisplay()
1050 {
1051 if (style == IncDecButtons)
1052 return;
1053
1054 if (popupDisplay == nullptr)
1055 {
1056 popupDisplay.reset (new PopupDisplayComponent (owner, parentForPopupDisplay == nullptr));
1057
1058 if (parentForPopupDisplay != nullptr)
1059 parentForPopupDisplay->addChildComponent (popupDisplay.get());
1060 else
1061 popupDisplay->addToDesktop (ComponentPeer::windowIsTemporary
1064
1065 updatePopupDisplay();
1066 popupDisplay->setVisible (true);
1067 }
1068 }
1069
1070 void updatePopupDisplay()
1071 {
1072 if (popupDisplay == nullptr)
1073 return;
1074
1075 const auto valueToShow = [this]
1076 {
1081
1083 return getValue();
1084
1085 if (sliderBeingDragged == 2)
1086 return getMaxValue();
1087
1088 if (sliderBeingDragged == 1)
1089 return getMinValue();
1090
1091 return getValue();
1092 }();
1093
1094 popupDisplay->updatePosition (owner.getTextFromValue (valueToShow));
1095 }
1096
1097 bool canDoubleClickToValue() const
1098 {
1099 return doubleClickToValue
1100 && style != IncDecButtons
1101 && normRange.start <= doubleClickReturnValue
1102 && normRange.end >= doubleClickReturnValue;
1103 }
1104
1105 void mouseDoubleClick()
1106 {
1107 if (canDoubleClickToValue())
1108 {
1109 ScopedDragNotification drag (owner);
1110 setValue (doubleClickReturnValue, sendNotificationSync);
1111 }
1112 }
1113
1114 double getMouseWheelDelta (double value, double wheelAmount)
1115 {
1116 if (style == IncDecButtons)
1117 return normRange.interval * wheelAmount;
1118
1119 auto proportionDelta = wheelAmount * 0.15;
1120 auto currentPos = owner.valueToProportionOfLength (value);
1121 auto newPos = currentPos + proportionDelta;
1122 newPos = (isRotary() && ! rotaryParams.stopAtEnd) ? newPos - std::floor (newPos)
1123 : jlimit (0.0, 1.0, newPos);
1124 return owner.proportionOfLengthToValue (newPos) - value;
1125 }
1126
1127 bool mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)
1128 {
1129 if (scrollWheelEnabled
1130 && style != TwoValueHorizontal
1131 && style != TwoValueVertical)
1132 {
1133 // sometimes duplicate wheel events seem to be sent, so since we're going to
1134 // bump the value by a minimum of the interval, avoid doing this twice..
1135 if (e.eventTime != lastMouseWheelTime)
1136 {
1137 lastMouseWheelTime = e.eventTime;
1138
1139 if (normRange.end > normRange.start && ! e.mods.isAnyMouseButtonDown())
1140 {
1141 if (valueBox != nullptr)
1142 valueBox->hideEditor (false);
1143
1144 auto value = static_cast<double> (currentValue.getValue());
1145 auto delta = getMouseWheelDelta (value, (std::abs (wheel.deltaX) > std::abs (wheel.deltaY)
1146 ? -wheel.deltaX : wheel.deltaY)
1147 * (wheel.isReversed ? -1.0f : 1.0f));
1148 if (! approximatelyEqual (delta, 0.0))
1149 {
1150 auto newValue = value + jmax (normRange.interval, std::abs (delta)) * (delta < 0 ? -1.0 : 1.0);
1151
1152 ScopedDragNotification drag (owner);
1153 setValue (owner.snapValue (newValue, notDragging), sendNotificationSync);
1154 }
1155 }
1156 }
1157
1158 return true;
1159 }
1160
1161 return false;
1162 }
1163
1164 void modifierKeysChanged (const ModifierKeys& modifiers)
1165 {
1166 if (style != IncDecButtons && style != Rotary && isAbsoluteDragMode (modifiers))
1167 restoreMouseIfHidden();
1168 }
1169
1170 bool isAbsoluteDragMode (ModifierKeys mods) const
1171 {
1172 return isVelocityBased == (userKeyOverridesVelocity && mods.testFlags (modifierToSwapModes));
1173 }
1174
1175 void restoreMouseIfHidden()
1176 {
1177 for (auto& ms : Desktop::getInstance().getMouseSources())
1178 {
1179 if (ms.isUnboundedMouseMovementEnabled())
1180 {
1181 ms.enableUnboundedMouseMovement (false);
1182
1183 auto pos = sliderBeingDragged == 2 ? getMaxValue()
1184 : (sliderBeingDragged == 1 ? getMinValue()
1185 : static_cast<double> (currentValue.getValue()));
1186 Point<float> mousePos;
1187
1188 if (isRotary())
1189 {
1190 mousePos = ms.getLastMouseDownPosition();
1191
1192 auto delta = (float) (pixelsForFullDragExtent * (owner.valueToProportionOfLength (valueOnMouseDown)
1193 - owner.valueToProportionOfLength (pos)));
1194
1195 if (style == RotaryHorizontalDrag) mousePos += Point<float> (-delta, 0.0f);
1196 else if (style == RotaryVerticalDrag) mousePos += Point<float> (0.0f, delta);
1197 else mousePos += Point<float> (delta / -2.0f, delta / 2.0f);
1198
1200 mouseDragStartPos = mousePosWhenLastDragged = owner.getLocalPoint (nullptr, mousePos);
1201 valueOnMouseDown = valueWhenLastDragged;
1202 }
1203 else
1204 {
1205 auto pixelPos = (float) getLinearSliderPos (pos);
1206
1207 mousePos = owner.localPointToGlobal (Point<float> (isHorizontal() ? pixelPos : ((float) owner.getWidth() / 2.0f),
1208 isVertical() ? pixelPos : ((float) owner.getHeight() / 2.0f)));
1209 }
1210
1211 const_cast <MouseInputSource&> (ms).setScreenPosition (mousePos);
1212 }
1213 }
1214 }
1215
1216 //==============================================================================
1217 void paint (Graphics& g, LookAndFeel& lf)
1218 {
1219 if (style != IncDecButtons)
1220 {
1221 if (isRotary())
1222 {
1223 auto sliderPos = (float) owner.valueToProportionOfLength (lastCurrentValue);
1224 jassert (sliderPos >= 0 && sliderPos <= 1.0f);
1225
1226 lf.drawRotarySlider (g,
1227 sliderRect.getX(), sliderRect.getY(),
1228 sliderRect.getWidth(), sliderRect.getHeight(),
1229 sliderPos, rotaryParams.startAngleRadians,
1230 rotaryParams.endAngleRadians, owner);
1231 }
1232 else
1233 {
1234 lf.drawLinearSlider (g,
1235 sliderRect.getX(), sliderRect.getY(),
1236 sliderRect.getWidth(), sliderRect.getHeight(),
1237 getLinearSliderPos (lastCurrentValue),
1238 getLinearSliderPos (lastValueMin),
1239 getLinearSliderPos (lastValueMax),
1240 style, owner);
1241 }
1242 }
1243 }
1244
1245 //==============================================================================
1246 void resized (LookAndFeel& lf)
1247 {
1248 auto layout = lf.getSliderLayout (owner);
1249 sliderRect = layout.sliderBounds;
1250
1251 if (valueBox != nullptr)
1252 valueBox->setBounds (layout.textBoxBounds);
1253
1254 if (isHorizontal())
1255 {
1256 sliderRegionStart = layout.sliderBounds.getX();
1257 sliderRegionSize = layout.sliderBounds.getWidth();
1258 }
1259 else if (isVertical())
1260 {
1261 sliderRegionStart = layout.sliderBounds.getY();
1262 sliderRegionSize = layout.sliderBounds.getHeight();
1263 }
1264 else if (style == IncDecButtons)
1265 {
1266 resizeIncDecButtons();
1267 }
1268 }
1269
1270 //==============================================================================
1271 void resizeIncDecButtons()
1272 {
1273 auto buttonRect = sliderRect;
1274
1275 if (textBoxPos == TextBoxLeft || textBoxPos == TextBoxRight)
1276 buttonRect.expand (-2, 0);
1277 else
1278 buttonRect.expand (0, -2);
1279
1280 incDecButtonsSideBySide = buttonRect.getWidth() > buttonRect.getHeight();
1281
1282 if (incDecButtonsSideBySide)
1283 {
1284 decButton->setBounds (buttonRect.removeFromLeft (buttonRect.getWidth() / 2));
1285 decButton->setConnectedEdges (Button::ConnectedOnRight);
1286 incButton->setConnectedEdges (Button::ConnectedOnLeft);
1287 }
1288 else
1289 {
1290 decButton->setBounds (buttonRect.removeFromBottom (buttonRect.getHeight() / 2));
1291 decButton->setConnectedEdges (Button::ConnectedOnTop);
1292 incButton->setConnectedEdges (Button::ConnectedOnBottom);
1293 }
1294
1295 incButton->setBounds (buttonRect);
1296 }
1297
1298 //==============================================================================
1299 Slider& owner;
1300 SliderStyle style;
1301
1303 Value currentValue, valueMin, valueMax;
1304 double lastCurrentValue = 0, lastValueMin = 0, lastValueMax = 0;
1305 NormalisableRange<double> normRange { 0.0, 10.0 };
1306 double doubleClickReturnValue = 0;
1307 double valueWhenLastDragged = 0, valueOnMouseDown = 0, lastAngle = 0;
1308 double velocityModeSensitivity = 1.0, velocityModeOffset = 0, minMaxDiff = 0;
1309 int velocityModeThreshold = 1;
1310 RotaryParameters rotaryParams;
1311 Point<float> mouseDragStartPos, mousePosWhenLastDragged;
1312 int sliderRegionStart = 0, sliderRegionSize = 1;
1313 int sliderBeingDragged = -1;
1314 int pixelsForFullDragExtent = 250;
1315 Time lastMouseWheelTime;
1316 Rectangle<int> sliderRect;
1318
1319 TextEntryBoxPosition textBoxPos;
1320 String textSuffix;
1321 int numDecimalPlaces = 7;
1322 int fixedNumDecimalPlaces = -1;
1323 int textBoxWidth = 80, textBoxHeight = 20;
1324 IncDecButtonMode incDecButtonMode = incDecButtonsNotDraggable;
1326
1327 bool editableText = true;
1328 bool doubleClickToValue = false;
1329 bool isVelocityBased = false;
1330 bool userKeyOverridesVelocity = true;
1331 bool incDecButtonsSideBySide = false;
1332 bool sendChangeOnlyOnRelease = false;
1333 bool showPopupOnDrag = false;
1334 bool showPopupOnHover = false;
1335 bool menuEnabled = false;
1336 bool useDragEvents = false;
1337 bool incDecDragged = false;
1338 bool scrollWheelEnabled = true;
1339 bool snapsToMousePos = true;
1340
1341 int popupHoverTimeout = 2000;
1342 double lastPopupDismissal = 0.0;
1343
1344 ModifierKeys singleClickModifiers;
1345
1346 std::unique_ptr<Label> valueBox;
1347 std::unique_ptr<Button> incButton, decButton;
1348
1349 //==============================================================================
1351 public Timer
1352 {
1354 : owner (s),
1355 font (s.getLookAndFeel().getSliderPopupFont (s))
1356 {
1357 if (isOnDesktop)
1359
1360 setAlwaysOnTop (true);
1361 setAllowedPlacement (owner.getLookAndFeel().getSliderPopupPlacement (s));
1363 }
1364
1365 ~PopupDisplayComponent() override
1366 {
1367 if (owner.pimpl != nullptr)
1368 owner.pimpl->lastPopupDismissal = Time::getMillisecondCounterHiRes();
1369 }
1370
1371 void paintContent (Graphics& g, int w, int h) override
1372 {
1373 g.setFont (font);
1374 g.setColour (owner.findColour (TooltipWindow::textColourId, true));
1376 }
1377
1378 void getContentSize (int& w, int& h) override
1379 {
1380 w = font.getStringWidth (text) + 18;
1381 h = (int) (font.getHeight() * 1.6f);
1382 }
1383
1384 void updatePosition (const String& newText)
1385 {
1386 text = newText;
1388 repaint();
1389 }
1390
1391 void timerCallback() override
1392 {
1393 stopTimer();
1394 owner.pimpl->popupDisplay.reset();
1395 }
1396
1397 private:
1398 //==============================================================================
1399 Slider& owner;
1400 Font font;
1401 String text;
1402
1404 };
1405
1407 Component* parentForPopupDisplay = nullptr;
1408
1409 //==============================================================================
1410 static double smallestAngleBetween (double a1, double a2) noexcept
1411 {
1412 return jmin (std::abs (a1 - a2),
1413 std::abs (a1 + MathConstants<double>::twoPi - a2),
1414 std::abs (a2 + MathConstants<double>::twoPi - a1));
1415 }
1416};
1417
1418//==============================================================================
1419Slider::ScopedDragNotification::ScopedDragNotification (Slider& s)
1420 : sliderBeingDragged (s)
1421{
1422 sliderBeingDragged.pimpl->sendDragStart();
1423}
1424
1425Slider::ScopedDragNotification::~ScopedDragNotification()
1426{
1427 if (sliderBeingDragged.pimpl != nullptr)
1428 sliderBeingDragged.pimpl->sendDragEnd();
1429}
1430
1431//==============================================================================
1433{
1435}
1436
1437Slider::Slider (const String& name) : Component (name)
1438{
1440}
1441
1443{
1444 init (style, textBoxPos);
1445}
1446
1447void Slider::init (SliderStyle style, TextEntryBoxPosition textBoxPos)
1448{
1449 setWantsKeyboardFocus (false);
1451
1452 pimpl.reset (new Pimpl (*this, style, textBoxPos));
1453
1455 updateText();
1456
1457 pimpl->registerListeners();
1458}
1459
1461
1462//==============================================================================
1463void Slider::addListener (Listener* l) { pimpl->listeners.add (l); }
1464void Slider::removeListener (Listener* l) { pimpl->listeners.remove (l); }
1465
1466//==============================================================================
1467Slider::SliderStyle Slider::getSliderStyle() const noexcept { return pimpl->style; }
1468void Slider::setSliderStyle (SliderStyle newStyle) { pimpl->setSliderStyle (newStyle); }
1469
1471{
1472 // make sure the values are sensible..
1473 jassert (p.startAngleRadians >= 0 && p.endAngleRadians >= 0);
1474 jassert (p.startAngleRadians < MathConstants<float>::pi * 4.0f
1475 && p.endAngleRadians < MathConstants<float>::pi * 4.0f);
1476
1477 pimpl->rotaryParams = p;
1478}
1479
1480void Slider::setRotaryParameters (float startAngleRadians, float endAngleRadians, bool stopAtEnd) noexcept
1481{
1482 setRotaryParameters ({ startAngleRadians, endAngleRadians, stopAtEnd });
1483}
1484
1486{
1487 return pimpl->rotaryParams;
1488}
1489
1490void Slider::setVelocityBasedMode (bool vb) { pimpl->isVelocityBased = vb; }
1491bool Slider::getVelocityBasedMode() const noexcept { return pimpl->isVelocityBased; }
1492bool Slider::getVelocityModeIsSwappable() const noexcept { return pimpl->userKeyOverridesVelocity; }
1493int Slider::getVelocityThreshold() const noexcept { return pimpl->velocityModeThreshold; }
1494double Slider::getVelocitySensitivity() const noexcept { return pimpl->velocityModeSensitivity; }
1495double Slider::getVelocityOffset() const noexcept { return pimpl->velocityModeOffset; }
1496
1498 double offset, bool userCanPressKeyToSwapMode,
1499 ModifierKeys::Flags modifierToSwapModes)
1500{
1501 jassert (threshold >= 0);
1502 jassert (sensitivity > 0);
1503 jassert (offset >= 0);
1504
1505 pimpl->setVelocityModeParameters (sensitivity, threshold, offset,
1506 userCanPressKeyToSwapMode, modifierToSwapModes);
1507}
1508
1509double Slider::getSkewFactor() const noexcept { return pimpl->normRange.skew; }
1510bool Slider::isSymmetricSkew() const noexcept { return pimpl->normRange.symmetricSkew; }
1511
1512void Slider::setSkewFactor (double factor, bool symmetricSkew)
1513{
1514 pimpl->normRange.skew = factor;
1515 pimpl->normRange.symmetricSkew = symmetricSkew;
1516}
1517
1519{
1520 pimpl->normRange.setSkewForCentre (sliderValueToShowAtMidPoint);
1521}
1522
1523int Slider::getMouseDragSensitivity() const noexcept { return pimpl->pixelsForFullDragExtent; }
1524
1526{
1528
1529 pimpl->pixelsForFullDragExtent = distanceForFullScaleDrag;
1530}
1531
1532void Slider::setIncDecButtonsMode (IncDecButtonMode mode) { pimpl->setIncDecButtonsMode (mode); }
1533
1534Slider::TextEntryBoxPosition Slider::getTextBoxPosition() const noexcept { return pimpl->textBoxPos; }
1535int Slider::getTextBoxWidth() const noexcept { return pimpl->textBoxWidth; }
1536int Slider::getTextBoxHeight() const noexcept { return pimpl->textBoxHeight; }
1537
1539{
1540 pimpl->setTextBoxStyle (newPosition, isReadOnly, textEntryBoxWidth, textEntryBoxHeight);
1541}
1542
1543bool Slider::isTextBoxEditable() const noexcept { return pimpl->editableText; }
1544void Slider::setTextBoxIsEditable (const bool shouldBeEditable) { pimpl->setTextBoxIsEditable (shouldBeEditable); }
1545void Slider::showTextBox() { pimpl->showTextBox(); }
1547
1549{
1550 pimpl->sendChangeOnlyOnRelease = onlyNotifyOnRelease;
1551}
1552
1553bool Slider::getSliderSnapsToMousePosition() const noexcept { return pimpl->snapsToMousePos; }
1555
1557{
1558 pimpl->showPopupOnDrag = showOnDrag;
1559 pimpl->showPopupOnHover = showOnHover;
1560 pimpl->parentForPopupDisplay = parent;
1561 pimpl->popupHoverTimeout = hoverTimeout;
1562}
1563
1564Component* Slider::getCurrentPopupDisplay() const noexcept { return pimpl->popupDisplay.get(); }
1565
1566//==============================================================================
1568void Slider::lookAndFeelChanged() { pimpl->lookAndFeelChanged (getLookAndFeel()); }
1569void Slider::enablementChanged() { repaint(); pimpl->updateTextBoxEnablement(); }
1570
1571//==============================================================================
1572NormalisableRange<double> Slider::getNormalisableRange() const noexcept { return pimpl->normRange; }
1573Range<double> Slider::getRange() const noexcept { return { pimpl->normRange.start, pimpl->normRange.end }; }
1574double Slider::getMaximum() const noexcept { return pimpl->normRange.end; }
1575double Slider::getMinimum() const noexcept { return pimpl->normRange.start; }
1576double Slider::getInterval() const noexcept { return pimpl->normRange.interval; }
1577
1578void Slider::setRange (double newMin, double newMax, double newInt) { pimpl->setRange (newMin, newMax, newInt); }
1579void Slider::setRange (Range<double> newRange, double newInt) { pimpl->setRange (newRange.getStart(), newRange.getEnd(), newInt); }
1581
1582double Slider::getValue() const { return pimpl->getValue(); }
1583Value& Slider::getValueObject() noexcept { return pimpl->currentValue; }
1584Value& Slider::getMinValueObject() noexcept { return pimpl->valueMin; }
1585Value& Slider::getMaxValueObject() noexcept { return pimpl->valueMax; }
1586
1588{
1589 pimpl->setValue (newValue, notification);
1590}
1591
1592double Slider::getMinValue() const { return pimpl->getMinValue(); }
1593double Slider::getMaxValue() const { return pimpl->getMaxValue(); }
1594
1596{
1597 pimpl->setMinValue (newValue, notification, allowNudgingOfOtherValues);
1598}
1599
1601{
1602 pimpl->setMaxValue (newValue, notification, allowNudgingOfOtherValues);
1603}
1604
1606{
1607 pimpl->setMinAndMaxValues (newMinValue, newMaxValue, notification);
1608}
1609
1611{
1612 pimpl->doubleClickToValue = isDoubleClickEnabled;
1613 pimpl->doubleClickReturnValue = valueToSetOnDoubleClick;
1614 pimpl->singleClickModifiers = mods;
1615}
1616
1617double Slider::getDoubleClickReturnValue() const noexcept { return pimpl->doubleClickReturnValue; }
1618bool Slider::isDoubleClickReturnEnabled() const noexcept { return pimpl->doubleClickToValue; }
1619
1621{
1622 pimpl->updateText();
1623}
1624
1626{
1627 pimpl->setTextValueSuffix (suffix);
1628}
1629
1631{
1632 return pimpl->textSuffix;
1633}
1634
1636{
1637 auto getText = [this] (double val)
1638 {
1639 if (textFromValueFunction != nullptr)
1640 return textFromValueFunction (val);
1641
1644
1645 return String (roundToInt (val));
1646 };
1647
1648 return getText (v) + getTextValueSuffix();
1649}
1650
1652{
1653 auto t = text.trimStart();
1654
1655 if (t.endsWith (getTextValueSuffix()))
1656 t = t.substring (0, t.length() - getTextValueSuffix().length());
1657
1658 if (valueFromTextFunction != nullptr)
1659 return valueFromTextFunction (t);
1660
1661 while (t.startsWithChar ('+'))
1662 t = t.substring (1).trimStart();
1663
1664 return t.initialSectionContainingOnly ("0123456789.,-")
1665 .getDoubleValue();
1666}
1667
1669{
1670 return pimpl->normRange.convertFrom0to1 (proportion);
1671}
1672
1674{
1675 return pimpl->normRange.convertTo0to1 (value);
1676}
1677
1679{
1680 return attemptedValue;
1681}
1682
1684{
1685 return pimpl->getNumDecimalPlacesToDisplay();
1686}
1687
1689{
1690 pimpl->setNumDecimalPlacesToDisplay (decimalPlacesToDisplay);
1691 updateText();
1692}
1693
1694//==============================================================================
1695int Slider::getThumbBeingDragged() const noexcept { return pimpl->sliderBeingDragged; }
1699
1700//==============================================================================
1701void Slider::setPopupMenuEnabled (bool menuEnabled) { pimpl->menuEnabled = menuEnabled; }
1702void Slider::setScrollWheelEnabled (bool enabled) { pimpl->scrollWheelEnabled = enabled; }
1703
1704bool Slider::isScrollWheelEnabled() const noexcept { return pimpl->scrollWheelEnabled; }
1705bool Slider::isHorizontal() const noexcept { return pimpl->isHorizontal(); }
1706bool Slider::isVertical() const noexcept { return pimpl->isVertical(); }
1707bool Slider::isRotary() const noexcept { return pimpl->isRotary(); }
1708bool Slider::isBar() const noexcept { return pimpl->isBar(); }
1709bool Slider::isTwoValue() const noexcept { return pimpl->isTwoValue(); }
1710bool Slider::isThreeValue() const noexcept { return pimpl->isThreeValue(); }
1711
1712float Slider::getPositionOfValue (double value) const { return pimpl->getPositionOfValue (value); }
1713
1714//==============================================================================
1715void Slider::paint (Graphics& g) { pimpl->paint (g, getLookAndFeel()); }
1716void Slider::resized() { pimpl->resized (getLookAndFeel()); }
1717
1719
1720void Slider::mouseDown (const MouseEvent& e) { pimpl->mouseDown (e); }
1721void Slider::mouseUp (const MouseEvent&) { pimpl->mouseUp(); }
1722void Slider::mouseMove (const MouseEvent&) { pimpl->mouseMove(); }
1723void Slider::mouseExit (const MouseEvent&) { pimpl->mouseExit(); }
1724
1725// If popup display is enabled and set to show on mouse hover, this makes sure
1726// it is shown when dragging the mouse over a slider and releasing
1727void Slider::mouseEnter (const MouseEvent&) { pimpl->mouseMove(); }
1728
1730bool Slider::keyPressed (const KeyPress& k) { return pimpl->keyPressed (k); }
1731
1733{
1734 if (isEnabled())
1735 pimpl->modifierKeysChanged (modifiers);
1736}
1737
1739{
1740 if (isEnabled())
1741 pimpl->mouseDrag (e);
1742}
1743
1745{
1746 if (isEnabled())
1747 pimpl->mouseDoubleClick();
1748}
1749
1751{
1752 if (! (isEnabled() && pimpl->mouseWheelMove (e, wheel)))
1754}
1755
1756//==============================================================================
1758{
1759public:
1762 AccessibilityRole::slider,
1764 AccessibilityHandler::Interfaces { std::make_unique<ValueInterface> (sliderToWrap) }),
1765 slider (sliderToWrap)
1766 {
1767 }
1768
1769 String getHelp() const override { return slider.getTooltip(); }
1770
1771private:
1772 class ValueInterface final : public AccessibilityValueInterface
1773 {
1774 public:
1775 explicit ValueInterface (Slider& sliderToWrap)
1776 : slider (sliderToWrap),
1777 useMaxValue (slider.isTwoValue())
1778 {
1779 }
1780
1781 bool isReadOnly() const override { return false; }
1782
1783 double getCurrentValue() const override
1784 {
1785 return useMaxValue ? slider.getMaximum()
1786 : slider.getValue();
1787 }
1788
1789 void setValue (double newValue) override
1790 {
1791 Slider::ScopedDragNotification drag (slider);
1792
1793 if (useMaxValue)
1794 slider.setMaxValue (newValue, sendNotificationSync);
1795 else
1796 slider.setValue (newValue, sendNotificationSync);
1797 }
1798
1799 String getCurrentValueAsString() const override { return slider.getTextFromValue (getCurrentValue()); }
1800 void setValueAsString (const String& newValue) override { setValue (slider.getValueFromText (newValue)); }
1801
1802 AccessibleValueRange getRange() const override
1803 {
1804 return { { slider.getMinimum(), slider.getMaximum() },
1805 getStepSize (slider) };
1806 }
1807
1808 private:
1809 Slider& slider;
1810 const bool useMaxValue;
1811
1812 //==============================================================================
1814 };
1815
1816 Slider& slider;
1817
1818 //==============================================================================
1819 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderAccessibilityHandler)
1820};
1821
1823{
1824 return std::make_unique<SliderAccessibilityHandler> (*this);
1825}
1826
1827} // namespace juce
T atan2(T... args)
T begin(T... args)
A simple wrapper for building a collection of supported accessibility actions and corresponding callb...
Base class for accessible Components.
An abstract interface representing the value of an accessibility element.
static AffineTransform scale(float factorX, float factorY) noexcept
Returns a new transform which is a re-scale about the origin.
Has a callback method that is triggered asynchronously.
void triggerAsyncUpdate()
Causes the callback to be triggered at a later time.
void cancelPendingUpdate() noexcept
This will stop any pending updates from happening.
A component for showing a message or other graphics inside a speech-bubble-shaped outline,...
void setPosition(Component *componentToPointTo, int distanceFromTarget=15, int arrowLength=10)
Moves and resizes the bubble to point at a given component.
void setAllowedPlacement(int newPlacement)
Tells the bubble which positions it's allowed to put itself in, relative to the point at which it's p...
@ windowIsTemporary
Indicates that the window is a temporary popup, like a menu, tooltip, etc.
@ windowIgnoresKeyPresses
Tells the window not to catch any keypresses.
@ windowIgnoresMouseClicks
Indicates that the window should let mouse clicks pass through it (may not be possible on some platfo...
A class to keep an eye on a component and check for it being deleted.
The base class for all JUCE user-interface objects.
void setLookAndFeel(LookAndFeel *newLookAndFeel)
Sets the look and feel to use for this component.
void setTransform(const AffineTransform &transform)
Sets a transform matrix to be applied to this component.
void setRepaintsOnMouseActivity(bool shouldRepaint) noexcept
Causes automatic repaints when the mouse enters or exits this component.
int getHeight() const noexcept
Returns the component's height in pixels.
Point< int > getLocalPoint(const Component *sourceComponent, Point< int > pointRelativeToSourceComponent) const
Converts a point to be relative to this component's coordinate space.
static float JUCE_CALLTYPE getApproximateScaleFactorForComponent(const Component *targetComponent)
Returns the approximate scale factor for a given component by traversing its parent hierarchy and app...
void addAndMakeVisible(Component *child, int zOrder=-1)
Adds a child component to this one, and also makes the child visible if it isn't already.
FocusChangeType
Enumeration used by the focusGained() and focusLost() methods.
void setAlwaysOnTop(bool shouldStayOnTop)
Sets whether the component should always be kept at the front of its siblings.
AccessibilityHandler * getAccessibilityHandler()
Returns the accessibility handler for this component, or nullptr if this component is not accessible.
void repaint()
Marks the whole component as needing to be redrawn.
Rectangle< int > getScreenBounds() const
Returns the bounds of this component, relative to the screen's top-left.
bool isOnDesktop() const noexcept
Returns true if this component is currently showing on the desktop.
void setWantsKeyboardFocus(bool wantsFocus) noexcept
Sets a flag to indicate whether this component wants keyboard focus or not.
bool isMouseOver(bool includeChildren=false) const
Returns true if the mouse is currently over this component.
int getWidth() const noexcept
Returns the component's width in pixels.
void mouseWheelMove(const MouseEvent &event, const MouseWheelDetails &wheel) override
Called when the mouse-wheel is moved.
bool isEnabled() const noexcept
Returns true if the component (and all its parents) are enabled.
Point< int > localPointToGlobal(Point< int > localPoint) const
Converts a point relative to this component's top-left into a screen coordinate.
LookAndFeel & getLookAndFeel() const noexcept
Finds the appropriate look-and-feel to use for this component.
void setComponentEffect(ImageEffectFilter *newEffect)
Adds an effect filter to alter the component's appearance.
void addChildComponent(Component *child, int zOrder=-1)
Adds a child component to this one.
void invalidateAccessibilityHandler()
Invalidates the AccessibilityHandler that is currently being used for this component.
Represents a particular font, including its size, style, etc.
Definition juce_Font.h:42
float getHeight() const noexcept
Returns the total height of this font, in pixels.
int getStringWidth(const String &text) const
Returns the total width of a string as it would be drawn using this font.
A graphics context, used for drawing a component or image.
void drawFittedText(const String &text, int x, int y, int width, int height, Justification justificationFlags, int maximumNumberOfLines, float minimumHorizontalScale=0.0f) const
Tries to draw a text string inside a given space.
void setFont(const Font &newFont)
Changes the font to use for subsequent text-drawing functions.
void setColour(Colour newColour)
Changes the current drawing colour.
@ centred
Indicates that the item should be centred vertically and horizontally.
Represents a key press, including any modifier keys that are needed.
static const int upKey
key-code for the cursor-up key
static const int rightKey
key-code for the cursor-right key
static const int downKey
key-code for the cursor-down key
static const int leftKey
key-code for the cursor-left key
static ModalComponentManager::Callback * forComponent(void(*functionToCall)(int, ComponentType *), ComponentType *component)
This is a utility function to create a ModalComponentManager::Callback that will call a static functi...
Represents the state of the mouse buttons and modifier keys.
Flags
Flags that represent the different keys.
@ ctrlAltCommandModifiers
Represents a combination of all the alt, ctrl and command key modifiers.
@ ParentCursor
Indicates that the component's parent's cursor should be used.
Contains position and status information about a mouse event.
Represents a mapping between an arbitrary range of values and a normalised 0->1 range.
ValueType skew
An optional skew factor that alters the way values are distribute across the range.
ValueType end
The maximum value of the non-normalised range.
bool symmetricSkew
If true, the skew factor applies from the middle of the slider to each of its ends.
ValueType snapToLegalValue(ValueType v) const noexcept
Takes a non-normalised value and snaps it based on either the interval property of this NormalisableR...
ValueType start
The minimum value of the non-normalised range.
ValueType interval
The snapping interval that should be used (for a non-normalised value).
ValueType y
The point's Y coordinate.
Definition juce_Point.h:252
ValueType x
The point's X coordinate.
Definition juce_Point.h:251
A general-purpose range object, that simply represents any linear range with a start and end point.
Definition juce_Range.h:40
Manages a rectangle and allows geometric operations to be performed on it.
Rectangle< float > toFloat() const noexcept
Casts this rectangle to a Rectangle<float>.
ValueType getX() const noexcept
Returns the x coordinate of the rectangle's left-hand-side.
ValueType getCentreX() const noexcept
Returns the x coordinate of the rectangle's centre.
ValueType getCentreY() const noexcept
Returns the y coordinate of the rectangle's centre.
ValueType getWidth() const noexcept
Returns the width of the rectangle.
void expand(ValueType deltaX, ValueType deltaY) noexcept
Expands the rectangle by a given amount.
Rectangle reduced(ValueType deltaX, ValueType deltaY) const noexcept
Returns a rectangle that is smaller than this one by a given amount.
Point< ValueType > getConstrainedPoint(Point< ValueType > point) const noexcept
Returns the nearest point to the specified point that lies within this rectangle.
ValueType getY() const noexcept
Returns the y coordinate of the rectangle's top edge.
ValueType getHeight() const noexcept
Returns the height of the rectangle.
String getTooltip() override
Returns the tooltip assigned to this object.
String getHelp() const override
Some help text for the UI element (if required).
A class for receiving callbacks from a Slider.
void handleAsyncUpdate() override
Called back to do whatever your class needs to do.
void valueChanged(Value &value) override
Called when a Value object is changed.
An RAII class for sending slider listener drag messages.
A slider control for changing a value.
Definition juce_Slider.h:54
int getVelocityThreshold() const noexcept
Returns the velocity threshold setting.
void setSliderStyle(SliderStyle newStyle)
Changes the type of slider interface being used.
DragMode
Describes the type of mouse-dragging that is happening when a value is being changed.
@ notDragging
Dragging is not active.
@ velocityDrag
The dragging value change is relative to the velocity of the mouse movement.
@ absoluteDrag
The dragging corresponds directly to the value that is displayed.
void setPopupDisplayEnabled(bool shouldShowOnMouseDrag, bool shouldShowOnMouseHover, Component *parentComponentToUse, int hoverTimeout=2000)
If enabled, this gives the slider a pop-up bubble which appears while the slider is being dragged or ...
int getTextBoxWidth() const noexcept
Returns the width used for the text-box.
double getMinimum() const noexcept
Returns the current minimum value.
double getVelocityOffset() const noexcept
Returns the velocity offset setting.
void setMaxValue(double newValue, NotificationType notification=sendNotificationAsync, bool allowNudgingOfOtherValues=false)
For a slider with two or three thumbs, this sets the lower of its values.
void setMinAndMaxValues(double newMinValue, double newMaxValue, NotificationType notification=sendNotificationAsync)
For a slider with two or three thumbs, this sets the minimum and maximum thumb positions.
bool isRotary() const noexcept
True if the slider is in a rotary mode.
std::function< void()> onDragStart
You can assign a lambda to this callback object to have it called when the slider's drag begins.
double getMaxValue() const
For a slider with two or three thumbs, this returns the higher of its values.
virtual String getTextFromValue(double value)
Turns the slider's current value into a text string.
void addListener(Listener *listener)
Adds a listener to be called when this slider's value changes.
Value & getMaxValueObject() noexcept
For a slider with two or three thumbs, this returns the higher of its values.
void setDoubleClickReturnValue(bool shouldDoubleClickBeEnabled, double valueToSetOnDoubleClick, ModifierKeys singleClickModifiers=ModifierKeys::altModifier)
This lets you choose whether double-clicking or single-clicking with a specified key modifier moves t...
float getPositionOfValue(double value) const
Returns the X or Y coordinate of a value along the slider's length.
void focusOfChildComponentChanged(FocusChangeType) override
Called to indicate a change in whether or not this component is the parent of the currently-focused c...
std::function< void()> onValueChange
You can assign a lambda to this callback object to have it called when the slider value is changed.
IncDecButtonMode
Used by setIncDecButtonsMode().
bool getSliderSnapsToMousePosition() const noexcept
Returns true if setSliderSnapsToMousePosition() has been enabled.
void setRotaryParameters(RotaryParameters newParameters) noexcept
Changes the properties of a rotary slider.
void setVelocityModeParameters(double sensitivity=1.0, int threshold=1, double offset=0.0, bool userCanPressKeyToSwapMode=true, ModifierKeys::Flags modifiersToSwapModes=ModifierKeys::ctrlAltCommandModifiers)
Changes aspects of the scaling used when in velocity-sensitive mode.
virtual double valueToProportionOfLength(double value)
Allows a user-defined mapping of value to the position of the slider along its length.
std::function< String(double)> textFromValueFunction
You can assign a lambda that will be used to convert the slider's normalised position to a textual va...
bool getVelocityBasedMode() const noexcept
Returns true if velocity-based mode is active.
double getDoubleClickReturnValue() const noexcept
Returns the values last set by setDoubleClickReturnValue() method.
Range< double > getRange() const noexcept
Returns the slider's range.
void setSkewFactor(double factor, bool symmetricSkew=false)
Sets up a skew factor to alter the way values are distributed.
virtual double proportionOfLengthToValue(double proportion)
Allows a user-defined mapping of distance along the slider to its value.
float startAngleRadians
The angle (in radians, clockwise from the top) at which the slider's minimum value is represented.
int getMouseDragSensitivity() const noexcept
Returns the current sensitivity value set by setMouseDragSensitivity().
void colourChanged() override
This method is called when a colour is changed by the setColour() method, or when the look-and-feel i...
void setTextBoxIsEditable(bool shouldBeEditable)
Makes the text-box editable.
void mouseDrag(const MouseEvent &) override
Called when the mouse is moved while a button is held down.
void setScrollWheelEnabled(bool enabled)
This can be used to stop the mouse scroll-wheel from moving the slider.
void setChangeNotificationOnlyOnRelease(bool onlyNotifyOnRelease)
Tells the slider whether to keep sending change messages while the user is dragging the slider.
double getSkewFactor() const noexcept
Returns the current skew factor.
void hideTextBox(bool discardCurrentEditorContents)
If the text-box currently has focus and is being edited, this resets it and takes keyboard focus away...
void lookAndFeelChanged() override
Called to let the component react to a change in the look-and-feel setting.
String getTextValueSuffix() const
Returns the suffix that was set by setTextValueSuffix().
virtual double snapValue(double attemptedValue, DragMode dragMode)
This can be overridden to allow the slider to snap to user-definable values.
bool isDoubleClickReturnEnabled() const noexcept
Returns true if double-clicking to reset to a default value is enabled.
bool getVelocityModeIsSwappable() const noexcept
Returns the velocity user key setting.
void setIncDecButtonsMode(IncDecButtonMode mode)
When the style is IncDecButtons, this lets you turn on a mode where the mouse can be dragged on the b...
double getInterval() const noexcept
Returns the current step-size for values.
void mouseMove(const MouseEvent &) override
Called when the mouse moves inside a component.
void setVelocityBasedMode(bool isVelocityBased)
Changes the way the mouse is used when dragging the slider.
double getMaximum() const noexcept
Returns the current maximum value.
Component * getCurrentPopupDisplay() const noexcept
If a popup display is enabled and is currently visible, this returns the component that is being show...
void mouseExit(const MouseEvent &) override
Called when the mouse moves out of a component.
std::function< void()> onDragEnd
You can assign a lambda to this callback object to have it called when the slider's drag ends.
double getValue() const
Returns the slider's current value.
void showTextBox()
If the text-box is editable, this will give it the focus so that the user can type directly into it.
void resized() override
Called when this component's size has been changed.
~Slider() override
Destructor.
bool isHorizontal() const noexcept
True if the slider moves horizontally.
SliderStyle
The types of slider available.
Definition juce_Slider.h:62
@ LinearBarVertical
A vertical bar slider with the text label drawn on top of it.
Definition juce_Slider.h:66
@ IncDecButtons
A pair of buttons that increment or decrement the slider's value by the increment set in setRange().
Definition juce_Slider.h:75
@ ThreeValueHorizontal
A horizontal slider that has three thumbs instead of one, so it can show a minimum and maximum value,...
Definition juce_Slider.h:82
@ TwoValueHorizontal
A horizontal slider that has two thumbs instead of one, so it can show a minimum and maximum value.
Definition juce_Slider.h:77
@ Rotary
A rotary control that you move by dragging the mouse in a circular motion, like a knob.
Definition juce_Slider.h:67
@ LinearBar
A horizontal bar slider with the text label drawn on top of it.
Definition juce_Slider.h:65
@ LinearHorizontal
A traditional horizontal slider.
Definition juce_Slider.h:63
@ RotaryVerticalDrag
A rotary control that you move by dragging the mouse up-and-down.
Definition juce_Slider.h:71
@ LinearVertical
A traditional vertical slider.
Definition juce_Slider.h:64
@ RotaryHorizontalDrag
A rotary control that you move by dragging the mouse left-to-right.
Definition juce_Slider.h:69
@ ThreeValueVertical
A vertical slider that has three thumbs instead of one, so it can show a minimum and maximum value,...
Definition juce_Slider.h:85
@ RotaryHorizontalVerticalDrag
A rotary control that you move by dragging the mouse up-and-down or left-to-right.
Definition juce_Slider.h:73
@ TwoValueVertical
A vertical slider that has two thumbs instead of one, so it can show a minimum and maximum value.
Definition juce_Slider.h:79
double getMinValue() const
For a slider with two or three thumbs, this returns the lower of its values.
void mouseDoubleClick(const MouseEvent &) override
Called when a mouse button has been double-clicked on a component.
bool keyPressed(const KeyPress &) override
Called when a key is pressed.
bool isSymmetricSkew() const noexcept
Returns the whether the skew is symmetric from the midpoint to both sides.
void mouseDown(const MouseEvent &) override
Called when a mouse button is pressed.
Value & getMinValueObject() noexcept
For a slider with two or three thumbs, this returns the lower of its values.
float endAngleRadians
The angle (in radians, clockwise from the top) at which the slider's maximum value is represented.
void enablementChanged() override
Callback to indicate that this component has been enabled or disabled.
bool isThreeValue() const noexcept
True if the slider has three thumbs.
bool isVertical() const noexcept
True if the slider moves vertically.
int getTextBoxHeight() const noexcept
Returns the height used for the text-box.
bool isScrollWheelEnabled() const noexcept
Returns true if the scroll wheel can move the slider.
void removeListener(Listener *listener)
Removes a previously-registered listener.
void mouseUp(const MouseEvent &) override
Called when a mouse button is released.
void setNumDecimalPlacesToDisplay(int decimalPlacesToDisplay)
Modifies the best number of decimal places to use when displaying this slider's value.
bool stopAtEnd
Determines what happens when a circular drag action rotates beyond the minimum or maximum angle.
void modifierKeysChanged(const ModifierKeys &) override
Called when a modifier key is pressed or released.
std::function< double(const String &)> valueFromTextFunction
You can assign a lambda that will be used to convert textual values to the slider's normalised positi...
virtual void stoppedDragging()
Callback to indicate that the user has just stopped dragging the slider.
NormalisableRange< double > getNormalisableRange() const noexcept
Returns the slider's normalisable range.
bool isBar() const noexcept
True if the slider is in a linear bar mode.
void setPopupMenuEnabled(bool menuEnabled)
If this is set to true, then right-clicking on the slider will pop-up a menu to let the user change t...
virtual void valueChanged()
Callback to indicate that the user has just moved the slider.
bool isTextBoxEditable() const noexcept
Returns true if the text-box is read-only.
void setMinValue(double newValue, NotificationType notification=sendNotificationAsync, bool allowNudgingOfOtherValues=false)
For a slider with two or three thumbs, this sets the lower of its values.
TextEntryBoxPosition getTextBoxPosition() const noexcept
Returns the status of the text-box.
void setTextBoxStyle(TextEntryBoxPosition newPosition, bool isReadOnly, int textEntryBoxWidth, int textEntryBoxHeight)
Changes the location and properties of the text-entry box.
RotaryParameters getRotaryParameters() const noexcept
Changes the properties of a rotary slider.
virtual double getValueFromText(const String &text)
Subclasses can override this to convert a text string to a value.
void setValue(double newValue, NotificationType notification=sendNotificationAsync)
Changes the slider's current value.
int getThumbBeingDragged() const noexcept
Returns a number to indicate which thumb is currently being dragged by the mouse.
void setSkewFactorFromMidPoint(double sliderValueToShowAtMidPoint)
Sets up a skew factor to alter the way values are distributed.
Value & getValueObject() noexcept
Returns the Value object that represents the slider's current position.
std::unique_ptr< AccessibilityHandler > createAccessibilityHandler() override
Override this method to return a custom AccessibilityHandler for this component.
virtual void startedDragging()
Callback to indicate that the user is about to start dragging the slider.
void paint(Graphics &) override
Components can override this method to draw their content.
Slider()
Creates a slider.
void setTextValueSuffix(const String &suffix)
Sets a suffix to append to the end of the numeric value when it's displayed as a string.
void setSliderSnapsToMousePosition(bool shouldSnapToMouse)
This lets you change whether the slider thumb jumps to the mouse position when you click.
TextEntryBoxPosition
The position of the slider's text-entry box.
Definition juce_Slider.h:94
@ TextBoxRight
Puts the text box to the right of the slider, vertically centred.
Definition juce_Slider.h:97
@ NoTextBox
Doesn't display a text box.
Definition juce_Slider.h:95
@ TextBoxLeft
Puts the text box to the left of the slider, vertically centred.
Definition juce_Slider.h:96
void mouseWheelMove(const MouseEvent &, const MouseWheelDetails &) override
Called when the mouse-wheel is moved.
double getVelocitySensitivity() const noexcept
Returns the velocity sensitivity setting.
SliderStyle getSliderStyle() const noexcept
Returns the slider's current style.
int getNumDecimalPlacesToDisplay() const noexcept
Returns the best number of decimal places to use when displaying this slider's value.
void setRange(double newMinimum, double newMaximum, double newInterval=0)
Sets the limits that the slider's value can take.
void setMouseDragSensitivity(int distanceForFullScaleDrag)
Sets the distance the mouse has to move to drag the slider across the full extent of its range.
bool isTwoValue() const noexcept
True if the slider has two thumbs.
void updateText()
This can be called to force the text box to update its contents.
void setNormalisableRange(NormalisableRange< double > newNormalisableRange)
Sets a NormalisableRange to use for the Slider values.
void mouseEnter(const MouseEvent &) override
Called when the mouse first enters a component.
Structure defining rotary parameters for a slider.
The JUCE String class!
Definition juce_String.h:53
String trimStart() const
Returns a copy of this string with any whitespace characters removed from the start.
String substring(int startIndex, int endIndex) const
Returns a subsection of the string.
static double getMillisecondCounterHiRes() noexcept
Returns the number of millisecs since a fixed event (usually system startup).
Makes repeated callbacks to a virtual method at a specified time interval.
Definition juce_Timer.h:52
void stopTimer() noexcept
Stops the timer.
@ textColourId
The colour to use for the text.
Receives callbacks when a Value object changes.
Definition juce_Value.h:139
Represents a shared variant value.
Definition juce_Value.h:51
void addListener(Listener *listener)
Adds a listener to receive callbacks when the value changes.
void removeListener(Listener *listener)
Removes a listener that was previously added with addListener().
bool refersToSameSourceAs(const Value &other) const
Returns true if this object and the other one use the same underlying ValueSource object.
var getValue() const
Returns the current value.
T end(T... args)
T find(T... args)
T floor(T... args)
T get(T... args)
#define TRANS(stringLiteral)
Uses the LocalisedStrings class to translate the given string literal.
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
typedef int
typedef float
JUCE Namespace.
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
@ valueChanged
Indicates that the UI element's value has changed.
NotificationType
These enums are used in various classes to indicate whether a notification event should be sent out.
@ sendNotificationAsync
Requests an asynchronous notification.
@ sendNotificationSync
Requests a synchronous notification.
@ dontSendNotification
No notification message should be sent.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
Contains status information about a mouse wheel event.
T reset(T... args)
T sin(T... args)
Utility struct which holds one or more accessibility interfaces.
Commonly used mathematical constants.
static constexpr FloatType twoPi
A predefined value for 2 * Pi.
static constexpr FloatType pi
A predefined value for Pi.
void paintContent(Graphics &g, int w, int h) override
Subclasses should override this to draw their bubble's contents.
void timerCallback() override
The user-defined callback routine that actually gets called periodically.
void getContentSize(int &w, int &h) override
Subclasses should override this to return the size of the content they want to draw inside the bubble...
T swap(T... args)