From bd5e48099d2720cfeab8ba189527b299c5ac7917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Thu, 4 Aug 2022 14:49:07 +0100 Subject: [PATCH 1/2] Allow to only recognize inputs inside the hue ring Adds a new option to toggle between the always win pan gesture recognizer with a new recognizer that checks if the pointer event is inside the ring. This allows direct users of the `ColorPickerHueRing` to be able to let events that are in the bounds of the hue ring but not in the ring to reach parents of the widget. --- lib/src/palette.dart | 75 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/lib/src/palette.dart b/lib/src/palette.dart index 81f34c4..31c4d01 100644 --- a/lib/src/palette.dart +++ b/lib/src/palette.dart @@ -1332,6 +1332,7 @@ class ColorPickerHueRing extends StatelessWidget { Key? key, this.displayThumbColor = true, this.strokeWidth = 5.0, + this.gesturesOnlyInside = false, }) : super(key: key); final HSVColor hsvColor; @@ -1339,6 +1340,9 @@ class ColorPickerHueRing extends StatelessWidget { final bool displayThumbColor; final double strokeWidth; + /// Whether to recognize gestures only when inside the hue ring or anywhere in it’s bounds + final bool gesturesOnlyInside; + void _handleGesture(Offset position, BuildContext context, double height, double width) { RenderBox? getBox = context.findRenderObject() as RenderBox?; if (getBox == null) return; @@ -1361,19 +1365,36 @@ class ColorPickerHueRing extends StatelessWidget { double width = constraints.maxWidth; double height = constraints.maxHeight; + final gestureInitializer = (PanGestureRecognizer instance) { + instance + ..onDown = ((details) => _handleGesture(details.globalPosition, context, height, width)) + ..onUpdate = + ((details) => _handleGesture(details.globalPosition, context, height, width)); + }; + return RawGestureDetector( - gestures: { - _AlwaysWinPanGestureRecognizer: GestureRecognizerFactoryWithHandlers<_AlwaysWinPanGestureRecognizer>( - () => _AlwaysWinPanGestureRecognizer(), - (_AlwaysWinPanGestureRecognizer instance) { - instance - ..onDown = ((details) => _handleGesture(details.globalPosition, context, height, width)) - ..onUpdate = ((details) => _handleGesture(details.globalPosition, context, height, width)); - }, - ), - }, + gestures: !gesturesOnlyInside + ? { + _AlwaysWinPanGestureRecognizer: + GestureRecognizerFactoryWithHandlers<_AlwaysWinPanGestureRecognizer>( + () => _AlwaysWinPanGestureRecognizer(), + gestureInitializer, + ), + } + : { + _InsideRingPanGestureRecognizer: + GestureRecognizerFactoryWithHandlers<_InsideRingPanGestureRecognizer>( + () => _InsideRingPanGestureRecognizer( + width: width, + height: height, + strokeWidth: strokeWidth, + ), + gestureInitializer, + ), + }, child: CustomPaint( - painter: HueRingPainter(hsvColor, displayThumbColor: displayThumbColor, strokeWidth: strokeWidth), + painter: HueRingPainter(hsvColor, + displayThumbColor: displayThumbColor, strokeWidth: strokeWidth), ), ); }, @@ -1392,6 +1413,38 @@ class _AlwaysWinPanGestureRecognizer extends PanGestureRecognizer { String get debugDescription => 'alwaysWin'; } +/// Pan gesture recgonizer that only accepts pointers inside the hue ring +class _InsideRingPanGestureRecognizer extends PanGestureRecognizer { + final Offset center; + final double radio; + final double strokeWidth; + + _InsideRingPanGestureRecognizer({ + required width, + required height, + required this.strokeWidth, + }) : center = Offset(width / 2, height / 2), + radio = width <= height ? width / 2 : height / 2, + super(); + + @override + void addAllowedPointer(event) { + final x = pow(event.localPosition.dx - center.dx, 2); + final y = pow(event.localPosition.dy - center.dy, 2); + final bool passTest = x + y <= pow(radio, 2) && x + y >= pow(radio - strokeWidth, 2); + + if (passTest) { + super.addAllowedPointer(event); + resolve(GestureDisposition.accepted); + } else { + resolve(GestureDisposition.rejected); + } + } + + @override + String get debugDescription => 'insideRing'; +} + /// Uppercase text formater class UpperCaseTextFormatter extends TextInputFormatter { @override From ac44bb038e34c7b2225fdf8822fa1889e2a32ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Capucho?= Date: Thu, 4 Aug 2022 14:50:54 +0100 Subject: [PATCH 2/2] Add an onEnd event to the hue ring This event is triggered when the users finishes the pan gesture on the ring. This allows direct users of `ColorPickerHueRing` to be notified when the user stops interacting with the hue ring and act accordingly. --- lib/src/palette.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/src/palette.dart b/lib/src/palette.dart index 31c4d01..a7f66c0 100644 --- a/lib/src/palette.dart +++ b/lib/src/palette.dart @@ -1333,6 +1333,7 @@ class ColorPickerHueRing extends StatelessWidget { this.displayThumbColor = true, this.strokeWidth = 5.0, this.gesturesOnlyInside = false, + this.onEnd, }) : super(key: key); final HSVColor hsvColor; @@ -1343,6 +1344,9 @@ class ColorPickerHueRing extends StatelessWidget { /// Whether to recognize gestures only when inside the hue ring or anywhere in it’s bounds final bool gesturesOnlyInside; + /// Callback called when the user finishes interacting with color picker + final VoidCallback? onEnd; + void _handleGesture(Offset position, BuildContext context, double height, double width) { RenderBox? getBox = context.findRenderObject() as RenderBox?; if (getBox == null) return; @@ -1369,7 +1373,8 @@ class ColorPickerHueRing extends StatelessWidget { instance ..onDown = ((details) => _handleGesture(details.globalPosition, context, height, width)) ..onUpdate = - ((details) => _handleGesture(details.globalPosition, context, height, width)); + ((details) => _handleGesture(details.globalPosition, context, height, width)) + ..onEnd = (_) => onEnd?.call(); }; return RawGestureDetector(