diff --git a/lib/widgets/swipe_question_tile.dart b/lib/widgets/swipe_question_tile.dart index 31e8330..1cec265 100644 --- a/lib/widgets/swipe_question_tile.dart +++ b/lib/widgets/swipe_question_tile.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:math' as math; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -76,6 +77,39 @@ class _SwipeQuestionTileState extends State double get _snappedVerticalOffset => _snappedSliderValue / _sliderMax * _maxVerticalDrag; + void _applyVerticalInputDelta(double dy) { + setState(() { + _verticalOffset -= dy; + _verticalOffset = _verticalOffset.clamp( + -_maxVerticalDrag, + _maxVerticalDrag, + ); + if (_atZero) { + _dragOffset = 0; + } + _maybeTriggerSnapFeedback(_snappedSliderValue); + }); + } + + /// One wheel notch / trackpad tick → one integer on the [-10, 10] scale. + void _stepSliderFromScroll(double scrollDy) { + if (scrollDy == 0) { + return; + } + final int next = (_snappedSliderValue + (scrollDy > 0 ? -1 : 1)) + .clamp(_sliderMin, _sliderMax); + if (next == _snappedSliderValue) { + return; + } + setState(() { + _verticalOffset = next / _sliderMax * _maxVerticalDrag; + if (_atZero) { + _dragOffset = 0; + } + _maybeTriggerSnapFeedback(next); + }); + } + void _maybeTriggerSnapFeedback(int snapped) { if (snapped == _lastSnappedValue) { return; @@ -216,34 +250,34 @@ class _SwipeQuestionTileState extends State bottom: 8, left: constraints.maxWidth * 0.22, right: constraints.maxWidth * 0.22, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onVerticalDragUpdate: widget.busy || _acting - ? null - : (DragUpdateDetails details) { - setState(() { - _verticalOffset -= details.delta.dy; - _verticalOffset = - _verticalOffset.clamp( - -_maxVerticalDrag, - _maxVerticalDrag, - ); - if (_atZero) { - _dragOffset = 0; - } - _maybeTriggerSnapFeedback( - _snappedSliderValue, - ); - }); - }, - child: Center( - child: Transform.translate( - offset: - Offset(0, -_snappedVerticalOffset), - child: QuestionGuidGlyph( - guid: widget.questionId, - size: _glyphSize, - displayValue: _snappedSliderValue, + child: Listener( + onPointerSignal: (PointerSignalEvent event) { + if (widget.busy || _acting) { + return; + } + if (event is PointerScrollEvent) { + _stepSliderFromScroll( + event.scrollDelta.dy, + ); + } + }, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onVerticalDragUpdate: widget.busy || _acting + ? null + : (DragUpdateDetails details) => + _applyVerticalInputDelta( + details.delta.dy, + ), + child: Center( + child: Transform.translate( + offset: + Offset(0, -_snappedVerticalOffset), + child: QuestionGuidGlyph( + guid: widget.questionId, + size: _glyphSize, + displayValue: _snappedSliderValue, + ), ), ), ),