From 3739fe48294027e056f1edffde417c1f8e1f84d1 Mon Sep 17 00:00:00 2001 From: ed Date: Mon, 6 Sep 2021 10:03:13 +0100 Subject: [PATCH] Accessibility: Added ScopedDragNotification for sending slider drag notifications --- .../juce_gui_basics/widgets/juce_Slider.cpp | 186 +++++++++--------- modules/juce_gui_basics/widgets/juce_Slider.h | 21 ++ 2 files changed, 116 insertions(+), 91 deletions(-) diff --git a/modules/juce_gui_basics/widgets/juce_Slider.cpp b/modules/juce_gui_basics/widgets/juce_Slider.cpp index 4e4b4868ed..637e2d60df 100644 --- a/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -373,16 +373,6 @@ public: owner.onDragEnd(); } - struct DragInProgress - { - DragInProgress (Pimpl& p) : owner (p) { owner.sendDragStart(); } - ~DragInProgress() { owner.sendDragEnd(); } - - Pimpl& owner; - - JUCE_DECLARE_NON_COPYABLE (DragInProgress) - }; - void incrementOrDecrement (double delta) { if (style == IncDecButtons) @@ -395,7 +385,7 @@ public: } else { - DragInProgress drag (*this); + ScopedDragNotification drag (owner); setValue (newValue, sendNotificationSync); } } @@ -409,9 +399,13 @@ public: setValue (currentValue.getValue(), dontSendNotification); } else if (value.refersToSameSourceAs (valueMin)) + { setMinValue (valueMin.getValue(), dontSendNotification, true); + } else if (value.refersToSameSourceAs (valueMax)) + { setMaxValue (valueMax.getValue(), dontSendNotification, true); + } } void textChanged() @@ -420,7 +414,7 @@ public: if (newValue != static_cast (currentValue.getValue())) { - DragInProgress drag (*this); + ScopedDragNotification drag (owner); setValue (newValue, sendNotificationSync); } @@ -868,7 +862,7 @@ public: popupDisplay->stopTimer(); } - currentDrag.reset (new DragInProgress (*this)); + currentDrag = std::make_unique (owner); mouseDrag (e); } } @@ -1046,7 +1040,7 @@ public: { if (canDoubleClickToValue()) { - DragInProgress drag (*this); + ScopedDragNotification drag (owner); setValue (doubleClickReturnValue, sendNotificationSync); } } @@ -1089,7 +1083,7 @@ public: { auto newValue = value + jmax (normRange.interval, std::abs (delta)) * (delta < 0 ? -1.0 : 1.0); - DragInProgress drag (*this); + ScopedDragNotification drag (owner); setValue (owner.snapValue (newValue, notDragging), sendNotificationSync); } } @@ -1260,7 +1254,7 @@ public: int pixelsForFullDragExtent = 250; Time lastMouseWheelTime; Rectangle sliderRect; - std::unique_ptr currentDrag; + std::unique_ptr currentDrag; TextEntryBoxPosition textBoxPos; String textSuffix; @@ -1351,80 +1345,6 @@ public: std::unique_ptr popupDisplay; Component* parentForPopupDisplay = nullptr; - //============================================================================== - class SliderAccessibilityHandler : public AccessibilityHandler - { - public: - explicit SliderAccessibilityHandler (Slider& sliderToWrap) - : AccessibilityHandler (sliderToWrap, - AccessibilityRole::slider, - AccessibilityActions{}, - AccessibilityHandler::Interfaces { std::make_unique (sliderToWrap) }), - slider (sliderToWrap) - { - } - - String getHelp() const override { return slider.getTooltip(); } - - private: - class ValueInterface : public AccessibilityValueInterface - { - public: - explicit ValueInterface (Slider& sliderToWrap) - : slider (sliderToWrap), - useMaxValue (slider.isTwoValue()) - { - } - - bool isReadOnly() const override { return false; } - - double getCurrentValue() const override - { - return useMaxValue ? slider.getMaximum() - : slider.getValue(); - } - - void setValue (double newValue) override - { - DragInProgress drag (*slider.pimpl); - - if (useMaxValue) - slider.setMaxValue (newValue, sendNotificationSync); - else - slider.setValue (newValue, sendNotificationSync); - } - - String getCurrentValueAsString() const override { return slider.getTextFromValue (getCurrentValue()); } - void setValueAsString (const String& newValue) override { setValue (slider.getValueFromText (newValue)); } - - AccessibleValueRange getRange() const override - { - return { { slider.getMinimum(), slider.getMaximum() }, - getStepSize() }; - } - - private: - double getStepSize() const - { - auto interval = slider.getInterval(); - - return interval != 0.0 ? interval - : slider.getRange().getLength() * 0.01; - } - - Slider& slider; - const bool useMaxValue; - - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueInterface) - }; - - Slider& slider; - - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderAccessibilityHandler) - }; - //============================================================================== static double smallestAngleBetween (double a1, double a2) noexcept { @@ -1434,6 +1354,17 @@ public: } }; +//============================================================================== +Slider::ScopedDragNotification::ScopedDragNotification (Slider& s) + : sliderBeingDragged (s) +{ + sliderBeingDragged.pimpl->sendDragStart(); +} + +Slider::ScopedDragNotification::~ScopedDragNotification() +{ + sliderBeingDragged.pimpl->sendDragEnd(); +} //============================================================================== Slider::Slider() @@ -1754,9 +1685,82 @@ void Slider::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel } //============================================================================== +class SliderAccessibilityHandler : public AccessibilityHandler +{ +public: + explicit SliderAccessibilityHandler (Slider& sliderToWrap) + : AccessibilityHandler (sliderToWrap, + AccessibilityRole::slider, + AccessibilityActions{}, + AccessibilityHandler::Interfaces { std::make_unique (sliderToWrap) }), + slider (sliderToWrap) + { + } + + String getHelp() const override { return slider.getTooltip(); } + +private: + class ValueInterface : public AccessibilityValueInterface + { + public: + explicit ValueInterface (Slider& sliderToWrap) + : slider (sliderToWrap), + useMaxValue (slider.isTwoValue()) + { + } + + bool isReadOnly() const override { return false; } + + double getCurrentValue() const override + { + return useMaxValue ? slider.getMaximum() + : slider.getValue(); + } + + void setValue (double newValue) override + { + Slider::ScopedDragNotification drag (slider); + + if (useMaxValue) + slider.setMaxValue (newValue, sendNotificationSync); + else + slider.setValue (newValue, sendNotificationSync); + } + + String getCurrentValueAsString() const override { return slider.getTextFromValue (getCurrentValue()); } + void setValueAsString (const String& newValue) override { setValue (slider.getValueFromText (newValue)); } + + AccessibleValueRange getRange() const override + { + return { { slider.getMinimum(), slider.getMaximum() }, + getStepSize() }; + } + + private: + double getStepSize() const + { + auto interval = slider.getInterval(); + + return interval != 0.0 ? interval + : slider.getRange().getLength() * 0.01; + } + + Slider& slider; + const bool useMaxValue; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueInterface) + }; + + Slider& slider; + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderAccessibilityHandler) +}; + std::unique_ptr Slider::createAccessibilityHandler() { - return std::make_unique (*this); + return std::make_unique (*this); } } // namespace juce diff --git a/modules/juce_gui_basics/widgets/juce_Slider.h b/modules/juce_gui_basics/widgets/juce_Slider.h index 146f5bd744..b0cd3b1793 100644 --- a/modules/juce_gui_basics/widgets/juce_Slider.h +++ b/modules/juce_gui_basics/widgets/juce_Slider.h @@ -886,6 +886,27 @@ public: Rectangle textBoxBounds; }; + //============================================================================== + /** An RAII class for sending slider listener drag messages. + + This is useful if you are programatically updating the slider's value and want + to imitate a mouse event, for example in a custom AccessibilityHandler. + + @see Slider::Listener + */ + class JUCE_API ScopedDragNotification + { + public: + explicit ScopedDragNotification (Slider&); + ~ScopedDragNotification(); + + private: + Slider& sliderBeingDragged; + + JUCE_DECLARE_NON_MOVEABLE (ScopedDragNotification) + JUCE_DECLARE_NON_COPYABLE (ScopedDragNotification) + }; + //============================================================================== /** This abstract base class is implemented by LookAndFeel classes to provide slider drawing functionality.