Refactor TiltPresetPicker to use a circular D-pad for tilt controls; enhance gesture handling for smoother input
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
+90
-18
@@ -349,40 +349,112 @@ class _SparkleShapePicker extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Preset controls for feeding an external tilt stream.
|
/// Circular D-pad control for feeding the external tilt stream.
|
||||||
class _TiltPresetPicker extends StatelessWidget {
|
class _TiltPresetPicker extends StatelessWidget {
|
||||||
const _TiltPresetPicker({required this.onChanged});
|
const _TiltPresetPicker({required this.onChanged});
|
||||||
|
|
||||||
final ValueChanged<Offset> onChanged;
|
final ValueChanged<Offset> onChanged;
|
||||||
|
|
||||||
|
static const double _tiltAmount = 0.7;
|
||||||
|
static const double _deadZoneRatio = 0.18;
|
||||||
|
|
||||||
|
void _emitDragTilt(Offset localPosition, Size size) {
|
||||||
|
final Offset center = Offset(size.width / 2.0, size.height / 2.0);
|
||||||
|
final double radius = size.shortestSide / 2.0;
|
||||||
|
final Offset delta = localPosition - center;
|
||||||
|
final double distance = delta.distance;
|
||||||
|
|
||||||
|
if (radius <= 0 || distance <= radius * _deadZoneRatio) {
|
||||||
|
onChanged(Offset.zero);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Offset normalized = delta / radius;
|
||||||
|
final double normalizedDistance = normalized.distance;
|
||||||
|
if (normalizedDistance > 1.0) {
|
||||||
|
normalized = normalized / normalizedDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChanged(normalized * _tiltAmount);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 176,
|
||||||
|
height: 176,
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
|
final Size size = constraints.biggest;
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onPanDown: (DragDownDetails details) {
|
||||||
|
_emitDragTilt(details.localPosition, size);
|
||||||
|
},
|
||||||
|
onPanUpdate: (DragUpdateDetails details) {
|
||||||
|
_emitDragTilt(details.localPosition, size);
|
||||||
|
},
|
||||||
|
onPanEnd: (_) => onChanged(Offset.zero),
|
||||||
|
onPanCancel: () => onChanged(Offset.zero),
|
||||||
|
child: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Align(
|
||||||
child: FilledButton.tonalIcon(
|
alignment: Alignment.topCenter,
|
||||||
onPressed: () => onChanged(const Offset(-0.7, 0.0)),
|
child: IconButton.filledTonal(
|
||||||
icon: const Icon(Icons.keyboard_double_arrow_left),
|
tooltip: 'Tilt Up',
|
||||||
label: const Text('Tilt Left'),
|
onPressed: () =>
|
||||||
|
onChanged(const Offset(0.0, -_tiltAmount)),
|
||||||
|
icon: const Icon(Icons.keyboard_arrow_up),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
Align(
|
||||||
Expanded(
|
alignment: Alignment.centerLeft,
|
||||||
child: OutlinedButton.icon(
|
child: IconButton.filledTonal(
|
||||||
|
tooltip: 'Tilt Left',
|
||||||
|
onPressed: () =>
|
||||||
|
onChanged(const Offset(-_tiltAmount, 0.0)),
|
||||||
|
icon: const Icon(Icons.keyboard_arrow_left),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: IconButton.outlined(
|
||||||
|
tooltip: 'Center Tilt',
|
||||||
onPressed: () => onChanged(Offset.zero),
|
onPressed: () => onChanged(Offset.zero),
|
||||||
icon: const Icon(Icons.restart_alt),
|
icon: const Icon(Icons.adjust),
|
||||||
label: const Text('Center'),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
Align(
|
||||||
Expanded(
|
alignment: Alignment.centerRight,
|
||||||
child: FilledButton.tonalIcon(
|
child: IconButton.filledTonal(
|
||||||
onPressed: () => onChanged(const Offset(0.7, 0.0)),
|
tooltip: 'Tilt Right',
|
||||||
icon: const Icon(Icons.keyboard_double_arrow_right),
|
onPressed: () =>
|
||||||
label: const Text('Tilt Right'),
|
onChanged(const Offset(_tiltAmount, 0.0)),
|
||||||
|
icon: const Icon(Icons.keyboard_arrow_right),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: IconButton.filledTonal(
|
||||||
|
tooltip: 'Tilt Down',
|
||||||
|
onPressed: () =>
|
||||||
|
onChanged(const Offset(0.0, _tiltAmount)),
|
||||||
|
icon: const Icon(Icons.keyboard_arrow_down),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user