From febcee9d66a5417eb42400418747ddab483342f8 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:52:55 +0200 Subject: [PATCH 1/5] Fire the onSlidingStart on tap handler for iOS --- src/ios/RNCSliderManager.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ios/RNCSliderManager.m b/src/ios/RNCSliderManager.m index 8722a373..0e3a1735 100644 --- a/src/ios/RNCSliderManager.m +++ b/src/ios/RNCSliderManager.m @@ -61,7 +61,14 @@ - (void)tapHandler:(UITapGestureRecognizer *)gesture { float value = slider.minimumValue + (rangeWidth * sliderPercent); [slider setValue:discreteValue(slider, value) animated: YES]; - + + + if (slider.onRNCSliderSlidingStart) { + slider.onRNCSliderSlidingStart(@{ + @"value": @(slider.value), + }); + } + // Trigger onValueChange to address https://github.com/react-native-community/react-native-slider/issues/212 if (slider.onRNCSliderValueChange) { slider.onRNCSliderValueChange(@{ From a0afadb87c1b1c3a645fdd0bbf3812a77c99651c Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Wed, 20 Oct 2021 15:54:02 +0200 Subject: [PATCH 2/5] Send a lastValue instead of value when firing onSlidingStart The Start event of Slider for Android sends the lastValue which acts as a point where the thumb was when slider was clicked. This value should also be sent on iOS platform to keep the behavior consisent. --- src/ios/RNCSliderManager.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ios/RNCSliderManager.m b/src/ios/RNCSliderManager.m index 0e3a1735..8b13f760 100644 --- a/src/ios/RNCSliderManager.m +++ b/src/ios/RNCSliderManager.m @@ -58,6 +58,7 @@ - (void)tapHandler:(UITapGestureRecognizer *)gesture { CGPoint touchPoint = [gesture locationInView:slider]; float rangeWidth = slider.maximumValue - slider.minimumValue; float sliderPercent = touchPoint.x / slider.bounds.size.width; + slider.lastValue = slider.value; float value = slider.minimumValue + (rangeWidth * sliderPercent); [slider setValue:discreteValue(slider, value) animated: YES]; @@ -65,7 +66,7 @@ - (void)tapHandler:(UITapGestureRecognizer *)gesture { if (slider.onRNCSliderSlidingStart) { slider.onRNCSliderSlidingStart(@{ - @"value": @(slider.value), + @"value": @(slider.lastValue), }); } From 7a8068df0fd7afdd872c79e7dd46c1123b3f86e3 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Wed, 20 Oct 2021 17:06:43 +0200 Subject: [PATCH 3/5] Use a state machine for drag and tap events on Windows The state machine for handling the Start->Update->Complete events is used to avoid multiplication of events when flags for sending status are used. This solution was implemented because there's no clear way to override the default drag/tap events on the slider. So to avoid reimplementing the whole architecture the flags and state machines has been used. --- src/windows/SliderWindows/SliderView.cpp | 70 +++++++++++++++++------- src/windows/SliderWindows/SliderView.h | 15 ++++- 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/src/windows/SliderWindows/SliderView.cpp b/src/windows/SliderWindows/SliderView.cpp index c917a8dd..c043cc4f 100644 --- a/src/windows/SliderWindows/SliderView.cpp +++ b/src/windows/SliderWindows/SliderView.cpp @@ -183,56 +183,86 @@ namespace winrt::SliderWindows::implementation { m_updating = false; } - void SliderView::OnValueChangedHandler(winrt::IInspectable const& /*sender*/, - xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& args){ - - if (!m_updating) { + void SliderView::OnValueChangedHandler( winrt::IInspectable const& /*sender*/, + xaml::Controls::Primitives::RangeBaseValueChangedEventArgs const& args ) + { + if( !m_updating ) + { m_reactContext.DispatchEvent( *this, L"topChange", - [&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept { + [&]( winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter ) noexcept + { eventDataWriter.WriteObjectBegin(); { - WriteProperty(eventDataWriter, L"value", args.NewValue()); + WriteProperty( eventDataWriter, L"value", args.NewValue() ); } eventDataWriter.WriteObjectEnd(); - }); + } ); + onValueChangeSent = true; } } - void SliderView::OnManipulationStartingHandler(winrt::IInspectable const& sender, - xaml::Input::ManipulationStartingRoutedEventArgs const& args) { - if (!m_updating) { + void SliderView::OnManipulationStartingHandler( winrt::IInspectable const& sender, + xaml::Input::ManipulationStartingRoutedEventArgs const& args ) + { + if( !m_updating ) + { auto self = sender.try_as(); m_reactContext.DispatchEvent( *this, L"topSlidingStart", - [&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept { + [&]( winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter ) noexcept + { eventDataWriter.WriteObjectBegin(); { - WriteProperty(eventDataWriter, L"value", self.Value()); + WriteProperty( eventDataWriter, L"value", self.Value() ); } eventDataWriter.WriteObjectEnd(); - }); + } ); + + if( onValueChangeSent ) + { + m_reactContext.DispatchEvent( + *this, + L"topSlidingComplete", + [&]( winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter ) noexcept + { + eventDataWriter.WriteObjectBegin(); + { + WriteProperty( eventDataWriter, L"value", self.Value() ); + } + eventDataWriter.WriteObjectEnd(); + } ); + onSlidingCompleteSent = true; + } + onValueChangeSent = false; + onSlidingStartSent = true; + onSlidingCompleteSent = false; } } - - void SliderView::OnManipulationCompletedHandler(winrt::IInspectable const& sender, - xaml::Input::ManipulationCompletedRoutedEventArgs const& args) { - if (!m_updating) { + + void SliderView::OnManipulationCompletedHandler( winrt::IInspectable const& sender, + xaml::Input::ManipulationCompletedRoutedEventArgs const& args ) + { + if( !m_updating && onSlidingCompleteSent == false ) + { auto self = sender.try_as(); m_reactContext.DispatchEvent( *this, L"topSlidingComplete", - [&](winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter) noexcept { + [&]( winrt::Microsoft::ReactNative::IJSValueWriter const& eventDataWriter ) noexcept + { eventDataWriter.WriteObjectBegin(); { - WriteProperty(eventDataWriter, L"value", self.Value()); + WriteProperty( eventDataWriter, L"value", self.Value() ); } eventDataWriter.WriteObjectEnd(); - }); + } ); + onSlidingCompleteSent = true; + onValueChangeSent = false; } } diff --git a/src/windows/SliderWindows/SliderView.h b/src/windows/SliderWindows/SliderView.h index 8b7114f4..d830a0a4 100644 --- a/src/windows/SliderWindows/SliderView.h +++ b/src/windows/SliderWindows/SliderView.h @@ -8,9 +8,16 @@ #include "NativeModules.h" namespace winrt::SliderWindows::implementation { - + namespace xaml = winrt::Windows::UI::Xaml; - + + enum class EventType + { + Start, + Change, + Complete + }; + class SliderView : public SliderViewT { public: SliderView(Microsoft::ReactNative::IReactContext const& reactContext); @@ -35,10 +42,14 @@ namespace winrt::SliderWindows::implementation { winrt::Windows::Foundation::IInspectable const& sender, xaml::Input::ManipulationCompletedRoutedEventArgs const& args); + void LaunchEvents(const EventType& eventType) const noexcept; + const double CalculateStepFrequencyPercentageValue(const double& stepPropertyValue) const noexcept; double m_maxValue, m_minValue; double m_value; + + bool onValueChangeSent, onSlidingStartSent, onSlidingCompleteSent; }; } From 8ded39ea255e269bfd18da6f2b9e426a3d289bec Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Wed, 20 Oct 2021 18:17:53 +0200 Subject: [PATCH 4/5] Remove unused LaunchEvents() method --- src/windows/SliderWindows/SliderView.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/windows/SliderWindows/SliderView.h b/src/windows/SliderWindows/SliderView.h index d830a0a4..e9835efb 100644 --- a/src/windows/SliderWindows/SliderView.h +++ b/src/windows/SliderWindows/SliderView.h @@ -42,8 +42,6 @@ namespace winrt::SliderWindows::implementation { winrt::Windows::Foundation::IInspectable const& sender, xaml::Input::ManipulationCompletedRoutedEventArgs const& args); - void LaunchEvents(const EventType& eventType) const noexcept; - const double CalculateStepFrequencyPercentageValue(const double& stepPropertyValue) const noexcept; double m_maxValue, m_minValue; From c5725f9dab96f32c27922876aa5d7e05813b8e59 Mon Sep 17 00:00:00 2001 From: Bartosz Klonowski <70535775+BartoszKlonowski@users.noreply.github.com> Date: Wed, 20 Oct 2021 20:47:54 +0200 Subject: [PATCH 5/5] Remove redundant EventType enum The `EventType` enum has been previously used to recognize the action performed on the Slider. But due to using flags for a state machine this enum is no longer required and can be safely removed. --- src/windows/SliderWindows/SliderView.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/windows/SliderWindows/SliderView.h b/src/windows/SliderWindows/SliderView.h index e9835efb..c8c4dc90 100644 --- a/src/windows/SliderWindows/SliderView.h +++ b/src/windows/SliderWindows/SliderView.h @@ -11,13 +11,6 @@ namespace winrt::SliderWindows::implementation { namespace xaml = winrt::Windows::UI::Xaml; - enum class EventType - { - Start, - Change, - Complete - }; - class SliderView : public SliderViewT { public: SliderView(Microsoft::ReactNative::IReactContext const& reactContext); @@ -53,4 +46,4 @@ namespace winrt::SliderWindows::implementation { namespace winrt::SliderWindows::factory_implementation { struct SliderView : SliderViewT {}; -} \ No newline at end of file +}