diff --git a/lib/widgets/editor/tree_widgets/constraint_zones_tree.dart b/lib/widgets/editor/tree_widgets/constraint_zones_tree.dart index 802657ad..1b584efd 100644 --- a/lib/widgets/editor/tree_widgets/constraint_zones_tree.dart +++ b/lib/widgets/editor/tree_widgets/constraint_zones_tree.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:pathplanner/path/constraints_zone.dart'; import 'package:pathplanner/path/pathplanner_path.dart'; import 'package:pathplanner/path/waypoint.dart'; +import 'package:pathplanner/util/wpimath/math_util.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/item_count.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/tree_card_node.dart'; import 'package:pathplanner/widgets/number_text_field.dart'; @@ -319,69 +320,143 @@ class _ConstraintZonesTreeState extends State { ), ), const SizedBox(height: 12), - Slider( - value: constraintZones[zoneIdx].minWaypointRelativePos.toDouble(), - secondaryTrackValue: - constraintZones[zoneIdx].maxWaypointRelativePos.toDouble(), - min: 0.0, - max: waypoints.length - 1.0, - divisions: (waypoints.length - 1) * 20, - label: constraintZones[zoneIdx] - .minWaypointRelativePos - .toStringAsFixed(2), - onChangeStart: (value) { - _sliderChangeStart = value; - }, - onChangeEnd: (value) { - widget.undoStack.add(Change( - _sliderChangeStart, - () { - constraintZones[zoneIdx].minWaypointRelativePos = value; - widget.onPathChanged?.call(); - }, - (oldValue) { - constraintZones[zoneIdx].minWaypointRelativePos = oldValue; - widget.onPathChanged?.call(); - }, - )); - }, - onChanged: (value) { - if (value <= constraintZones[zoneIdx].maxWaypointRelativePos) { - constraintZones[zoneIdx].minWaypointRelativePos = value; - widget.onPathChangedNoSim?.call(); - } - }, + Row( + children: [ + Expanded( + child: Slider( + value: + constraintZones[zoneIdx].minWaypointRelativePos.toDouble(), + secondaryTrackValue: + constraintZones[zoneIdx].maxWaypointRelativePos.toDouble(), + min: 0.0, + max: waypoints.length - 1.0, + label: constraintZones[zoneIdx] + .minWaypointRelativePos + .toStringAsFixed(2), + onChangeStart: (value) { + _sliderChangeStart = value; + }, + onChangeEnd: (value) { + widget.undoStack.add(Change( + _sliderChangeStart, + () { + constraintZones[zoneIdx].minWaypointRelativePos = value; + widget.onPathChanged?.call(); + }, + (oldValue) { + constraintZones[zoneIdx].minWaypointRelativePos = + oldValue; + widget.onPathChanged?.call(); + }, + )); + }, + onChanged: (value) { + if (value <= + constraintZones[zoneIdx].maxWaypointRelativePos) { + constraintZones[zoneIdx].minWaypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + } + }, + ), + ), + SizedBox( + width: 75, + child: NumberTextField( + initialValue: constraintZones[zoneIdx].minWaypointRelativePos, + precision: 2, + label: 'Start Pos', + onSubmitted: (value) { + if (value != null) { + final maxVal = + constraintZones[zoneIdx].maxWaypointRelativePos; + final val = MathUtil.clamp(value, 0.0, maxVal); + widget.undoStack.add(Change( + constraintZones[zoneIdx].minWaypointRelativePos, + () { + constraintZones[zoneIdx].minWaypointRelativePos = val; + widget.onPathChanged?.call(); + }, + (oldValue) { + constraintZones[zoneIdx].minWaypointRelativePos = + oldValue; + widget.onPathChanged?.call(); + }, + )); + } + }, + ), + ), + const SizedBox(width: 4), + ], ), - Slider( - value: constraintZones[zoneIdx].maxWaypointRelativePos.toDouble(), - min: 0.0, - max: waypoints.length - 1.0, - divisions: (waypoints.length - 1) * 20, - label: constraintZones[zoneIdx] - .maxWaypointRelativePos - .toStringAsFixed(2), - onChangeStart: (value) { - _sliderChangeStart = value; - }, - onChangeEnd: (value) { - widget.undoStack.add(Change( - _sliderChangeStart, - () { - constraintZones[zoneIdx].maxWaypointRelativePos = value; - widget.onPathChanged?.call(); - }, - (oldValue) { - constraintZones[zoneIdx].maxWaypointRelativePos = oldValue; - widget.onPathChanged?.call(); - }, - )); - }, - onChanged: (value) { - if (value >= constraintZones[zoneIdx].minWaypointRelativePos) { - constraintZones[zoneIdx].maxWaypointRelativePos = value; - widget.onPathChangedNoSim?.call(); - } - }, + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: Slider( + value: + constraintZones[zoneIdx].maxWaypointRelativePos.toDouble(), + min: 0.0, + max: waypoints.length - 1.0, + label: constraintZones[zoneIdx] + .maxWaypointRelativePos + .toStringAsFixed(2), + onChangeStart: (value) { + _sliderChangeStart = value; + }, + onChangeEnd: (value) { + widget.undoStack.add(Change( + _sliderChangeStart, + () { + constraintZones[zoneIdx].maxWaypointRelativePos = value; + widget.onPathChanged?.call(); + }, + (oldValue) { + constraintZones[zoneIdx].maxWaypointRelativePos = + oldValue; + widget.onPathChanged?.call(); + }, + )); + }, + onChanged: (value) { + if (value >= + constraintZones[zoneIdx].minWaypointRelativePos) { + constraintZones[zoneIdx].maxWaypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + } + }, + ), + ), + SizedBox( + width: 75, + child: NumberTextField( + initialValue: constraintZones[zoneIdx].maxWaypointRelativePos, + precision: 2, + label: 'End Pos', + onSubmitted: (value) { + if (value != null) { + final minVal = + constraintZones[zoneIdx].minWaypointRelativePos; + final val = + MathUtil.clamp(value, minVal, waypoints.length - 1); + widget.undoStack.add(Change( + constraintZones[zoneIdx].maxWaypointRelativePos, + () { + constraintZones[zoneIdx].maxWaypointRelativePos = val; + widget.onPathChanged?.call(); + }, + (oldValue) { + constraintZones[zoneIdx].maxWaypointRelativePos = + oldValue; + widget.onPathChanged?.call(); + }, + )); + } + }, + ), + ), + const SizedBox(width: 4), + ], ), ], ); diff --git a/lib/widgets/editor/tree_widgets/event_markers_tree.dart b/lib/widgets/editor/tree_widgets/event_markers_tree.dart index 111e10cf..c0bb2ae5 100644 --- a/lib/widgets/editor/tree_widgets/event_markers_tree.dart +++ b/lib/widgets/editor/tree_widgets/event_markers_tree.dart @@ -6,10 +6,12 @@ import 'package:pathplanner/pages/project/project_page.dart'; import 'package:pathplanner/path/event_marker.dart'; import 'package:pathplanner/path/pathplanner_path.dart'; import 'package:pathplanner/path/waypoint.dart'; +import 'package:pathplanner/util/wpimath/math_util.dart'; import 'package:pathplanner/widgets/editor/info_card.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/commands/command_group_widget.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/item_count.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/tree_card_node.dart'; +import 'package:pathplanner/widgets/number_text_field.dart'; import 'package:undo/undo.dart'; class EventMarkersTree extends StatefulWidget { @@ -338,73 +340,140 @@ class _EventMarkersTreeState extends State { ], ), ), - Slider( - value: markers[markerIdx].waypointRelativePos.toDouble(), - secondaryTrackValue: - markers[markerIdx].endWaypointRelativePos?.toDouble(), - min: 0.0, - max: waypoints.length - 1.0, - divisions: (waypoints.length - 1) * 20, - label: markers[markerIdx].waypointRelativePos.toStringAsFixed(2), - onChangeStart: (value) { - _sliderChangeStart = value; - }, - onChangeEnd: (value) { - widget.undoStack.add(Change( - _sliderChangeStart, - () { - markers[markerIdx].waypointRelativePos = value; - widget.onPathChangedNoSim?.call(); - }, - (oldValue) { - markers[markerIdx].waypointRelativePos = oldValue; - widget.onPathChangedNoSim?.call(); - }, - )); - }, - onChanged: (value) { - if (!markers[markerIdx].isZoned || - value <= markers[markerIdx].endWaypointRelativePos!) { - setState(() { - markers[markerIdx].waypointRelativePos = value; - widget.onPathChangedNoSim?.call(); - }); - } - }, - ), - const SizedBox(height: 12), - if (markers[markerIdx].isZoned) ...[ - Slider( - value: markers[markerIdx].endWaypointRelativePos!.toDouble(), - min: 0.0, - max: waypoints.length - 1.0, - divisions: (waypoints.length - 1) * 20, - label: - markers[markerIdx].endWaypointRelativePos!.toStringAsFixed(2), - onChangeStart: (value) { - _sliderChangeStart = value; - }, - onChangeEnd: (value) { - widget.undoStack.add(Change( - _sliderChangeStart, - () { - markers[markerIdx].endWaypointRelativePos = value; - widget.onPathChangedNoSim?.call(); + Row( + children: [ + Expanded( + child: Slider( + value: markers[markerIdx].waypointRelativePos.toDouble(), + secondaryTrackValue: + markers[markerIdx].endWaypointRelativePos?.toDouble(), + min: 0.0, + max: waypoints.length - 1.0, + label: + markers[markerIdx].waypointRelativePos.toStringAsFixed(2), + onChangeStart: (value) { + _sliderChangeStart = value; }, - (oldValue) { - markers[markerIdx].endWaypointRelativePos = oldValue; - widget.onPathChangedNoSim?.call(); + onChangeEnd: (value) { + widget.undoStack.add(Change( + _sliderChangeStart, + () { + markers[markerIdx].waypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + }, + (oldValue) { + markers[markerIdx].waypointRelativePos = oldValue; + widget.onPathChangedNoSim?.call(); + }, + )); }, - )); - }, - onChanged: (value) { - if (value >= markers[markerIdx].waypointRelativePos) { - setState(() { - markers[markerIdx].endWaypointRelativePos = value; - widget.onPathChangedNoSim?.call(); - }); - } - }, + onChanged: (value) { + if (!markers[markerIdx].isZoned || + value <= markers[markerIdx].endWaypointRelativePos!) { + setState(() { + markers[markerIdx].waypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + }); + } + }, + ), + ), + SizedBox( + width: 75, + child: NumberTextField( + initialValue: markers[markerIdx].waypointRelativePos, + precision: 2, + label: markers[markerIdx].isZoned ? 'Start Pos' : 'Position', + onSubmitted: (value) { + if (value != null) { + final maxVal = markers[markerIdx].isZoned + ? markers[markerIdx].endWaypointRelativePos! + : (waypoints.length - 1); + final val = MathUtil.clamp(value, 0.0, maxVal); + widget.undoStack.add(Change( + markers[markerIdx].waypointRelativePos, + () { + markers[markerIdx].waypointRelativePos = val; + widget.onPathChangedNoSim?.call(); + }, + (oldValue) { + markers[markerIdx].waypointRelativePos = oldValue; + widget.onPathChangedNoSim?.call(); + }, + )); + } + }, + ), + ), + const SizedBox(width: 4), + ], + ), + const SizedBox(height: 8), + if (markers[markerIdx].isZoned) ...[ + Row( + children: [ + Expanded( + child: Slider( + value: markers[markerIdx].endWaypointRelativePos!.toDouble(), + min: 0.0, + max: waypoints.length - 1.0, + label: markers[markerIdx] + .endWaypointRelativePos! + .toStringAsFixed(2), + onChangeStart: (value) { + _sliderChangeStart = value; + }, + onChangeEnd: (value) { + widget.undoStack.add(Change( + _sliderChangeStart, + () { + markers[markerIdx].endWaypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + }, + (oldValue) { + markers[markerIdx].endWaypointRelativePos = oldValue; + widget.onPathChangedNoSim?.call(); + }, + )); + }, + onChanged: (value) { + if (value >= markers[markerIdx].waypointRelativePos) { + setState(() { + markers[markerIdx].endWaypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + }); + } + }, + ), + ), + SizedBox( + width: 75, + child: NumberTextField( + initialValue: markers[markerIdx].endWaypointRelativePos!, + precision: 2, + label: 'End Pos', + onSubmitted: (value) { + if (value != null) { + final minVal = markers[markerIdx].waypointRelativePos; + final val = + MathUtil.clamp(value, minVal, waypoints.length - 1); + widget.undoStack.add(Change( + markers[markerIdx].endWaypointRelativePos, + () { + markers[markerIdx].endWaypointRelativePos = val; + widget.onPathChangedNoSim?.call(); + }, + (oldValue) { + markers[markerIdx].endWaypointRelativePos = oldValue; + widget.onPathChangedNoSim?.call(); + }, + )); + } + }, + ), + ), + const SizedBox(width: 4), + ], ), const SizedBox(height: 12), ], diff --git a/lib/widgets/editor/tree_widgets/point_towards_zones_tree.dart b/lib/widgets/editor/tree_widgets/point_towards_zones_tree.dart index 149dcb17..6a97848f 100644 --- a/lib/widgets/editor/tree_widgets/point_towards_zones_tree.dart +++ b/lib/widgets/editor/tree_widgets/point_towards_zones_tree.dart @@ -286,64 +286,129 @@ class _PointTowardsZonesTreeState extends State { ), ), const SizedBox(height: 12), - Slider( - value: zones[zoneIdx].minWaypointRelativePos.toDouble(), - secondaryTrackValue: zones[zoneIdx].maxWaypointRelativePos.toDouble(), - min: 0.05, - max: waypoints.length - 1.05, - divisions: ((waypoints.length - 1) * 20) - 2, - label: zones[zoneIdx].minWaypointRelativePos.toStringAsFixed(2), - onChangeStart: (value) { - _sliderChangeStart = value; - }, - onChangeEnd: (value) { - widget.undoStack.add(Change( - _sliderChangeStart, - () { - zones[zoneIdx].minWaypointRelativePos = value; - widget.onPathChanged?.call(); - }, - (oldValue) { - zones[zoneIdx].minWaypointRelativePos = oldValue; - widget.onPathChanged?.call(); - }, - )); - }, - onChanged: (value) { - if (value <= zones[zoneIdx].maxWaypointRelativePos) { - zones[zoneIdx].minWaypointRelativePos = value; - widget.onPathChangedNoSim?.call(); - } - }, + Row( + children: [ + Expanded( + child: Slider( + value: zones[zoneIdx].minWaypointRelativePos.toDouble(), + secondaryTrackValue: + zones[zoneIdx].maxWaypointRelativePos.toDouble(), + min: 0.0, + max: waypoints.length - 1.0, + label: zones[zoneIdx].minWaypointRelativePos.toStringAsFixed(2), + onChangeStart: (value) { + _sliderChangeStart = value; + }, + onChangeEnd: (value) { + widget.undoStack.add(Change( + _sliderChangeStart, + () { + zones[zoneIdx].minWaypointRelativePos = value; + widget.onPathChanged?.call(); + }, + (oldValue) { + zones[zoneIdx].minWaypointRelativePos = oldValue; + widget.onPathChanged?.call(); + }, + )); + }, + onChanged: (value) { + if (value <= zones[zoneIdx].maxWaypointRelativePos) { + zones[zoneIdx].minWaypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + } + }, + ), + ), + SizedBox( + width: 75, + child: NumberTextField( + initialValue: zones[zoneIdx].minWaypointRelativePos, + precision: 2, + label: 'Start Pos', + onSubmitted: (value) { + if (value != null) { + final maxVal = zones[zoneIdx].maxWaypointRelativePos; + final val = MathUtil.clamp(value, 0.0, maxVal); + widget.undoStack.add(Change( + zones[zoneIdx].minWaypointRelativePos, + () { + zones[zoneIdx].minWaypointRelativePos = val; + widget.onPathChanged?.call(); + }, + (oldValue) { + zones[zoneIdx].minWaypointRelativePos = oldValue; + widget.onPathChanged?.call(); + }, + )); + } + }, + ), + ), + const SizedBox(width: 4), + ], ), - Slider( - value: zones[zoneIdx].maxWaypointRelativePos.toDouble(), - min: 0.05, - max: waypoints.length - 1.05, - divisions: ((waypoints.length - 1) * 20) - 2, - label: zones[zoneIdx].maxWaypointRelativePos.toStringAsFixed(2), - onChangeStart: (value) { - _sliderChangeStart = value; - }, - onChangeEnd: (value) { - widget.undoStack.add(Change( - _sliderChangeStart, - () { - zones[zoneIdx].maxWaypointRelativePos = value; - widget.onPathChanged?.call(); - }, - (oldValue) { - zones[zoneIdx].maxWaypointRelativePos = oldValue; - widget.onPathChanged?.call(); - }, - )); - }, - onChanged: (value) { - if (value >= zones[zoneIdx].minWaypointRelativePos) { - zones[zoneIdx].maxWaypointRelativePos = value; - widget.onPathChangedNoSim?.call(); - } - }, + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: Slider( + value: zones[zoneIdx].maxWaypointRelativePos.toDouble(), + min: 0.0, + max: waypoints.length - 1.0, + label: zones[zoneIdx].maxWaypointRelativePos.toStringAsFixed(2), + onChangeStart: (value) { + _sliderChangeStart = value; + }, + onChangeEnd: (value) { + widget.undoStack.add(Change( + _sliderChangeStart, + () { + zones[zoneIdx].maxWaypointRelativePos = value; + widget.onPathChanged?.call(); + }, + (oldValue) { + zones[zoneIdx].maxWaypointRelativePos = oldValue; + widget.onPathChanged?.call(); + }, + )); + }, + onChanged: (value) { + if (value >= zones[zoneIdx].minWaypointRelativePos) { + zones[zoneIdx].maxWaypointRelativePos = value; + widget.onPathChangedNoSim?.call(); + } + }, + ), + ), + SizedBox( + width: 75, + child: NumberTextField( + initialValue: zones[zoneIdx].maxWaypointRelativePos, + precision: 2, + label: 'End Pos', + onSubmitted: (value) { + if (value != null) { + final minVal = zones[zoneIdx].minWaypointRelativePos; + final val = + MathUtil.clamp(value, minVal, waypoints.length - 1.0); + widget.undoStack.add(Change( + zones[zoneIdx].maxWaypointRelativePos, + () { + zones[zoneIdx].maxWaypointRelativePos = val; + widget.onPathChanged?.call(); + }, + (oldValue) { + zones[zoneIdx].maxWaypointRelativePos = oldValue; + widget.onPathChanged?.call(); + }, + )); + } + }, + ), + ), + const SizedBox(width: 4), + ], ), ], ); diff --git a/lib/widgets/editor/tree_widgets/rotation_targets_tree.dart b/lib/widgets/editor/tree_widgets/rotation_targets_tree.dart index f3b0bc71..cfb1f9d4 100644 --- a/lib/widgets/editor/tree_widgets/rotation_targets_tree.dart +++ b/lib/widgets/editor/tree_widgets/rotation_targets_tree.dart @@ -205,6 +205,7 @@ class _RotationTargetsTreeState extends State { arrowKeyIncrement: 0.1, minValue: 0.0, maxValue: (waypoints.length - 1.0), + precision: 2, onSubmitted: (value) { if (value != null) { setState(() { @@ -223,7 +224,6 @@ class _RotationTargetsTreeState extends State { value: rotations[targetIdx].waypointRelativePos.toDouble(), min: 0.0, max: waypoints.length - 1.0, - divisions: (waypoints.length - 1) * 20, label: rotations[targetIdx].waypointRelativePos.toStringAsFixed(2), onChangeStart: (value) { _sliderChangeStart = value; diff --git a/test/widgets/editor/tree_widgets/constraint_zones_tree_test.dart b/test/widgets/editor/tree_widgets/constraint_zones_tree_test.dart index d8052d6b..9d51f389 100644 --- a/test/widgets/editor/tree_widgets/constraint_zones_tree_test.dart +++ b/test/widgets/editor/tree_widgets/constraint_zones_tree_test.dart @@ -526,4 +526,84 @@ void main() { await widgetTester.pump(); expect(path.constraintZones.length, 2); }); + + testWidgets('start pos text field', (widgetTester) async { + path.constraintZones = [ + ConstraintsZone( + constraints: PathConstraints(), + minWaypointRelativePos: 0.25, + maxWaypointRelativePos: 0.75, + ), + ]; + + await widgetTester.pumpWidget(MaterialApp( + home: Scaffold( + body: ConstraintZonesTree( + path: path, + initiallySelectedZone: 0, + onPathChanged: () => pathChanged = true, + onZoneHovered: (value) => hoveredZone = value, + onZoneSelected: (value) => selectedZone = value, + undoStack: undoStack, + ), + ), + )); + + var textField = find.widgetWithText(NumberTextField, 'Start Pos'); + + expect(textField, findsOneWidget); + + await widgetTester.enterText(textField, '0.4'); + await widgetTester.testTextInput.receiveAction(TextInputAction.done); + await widgetTester.pumpAndSettle(); + + expect(pathChanged, true); + expect( + path.constraintZones.first.minWaypointRelativePos, closeTo(0.4, 0.001)); + + undoStack.undo(); + await widgetTester.pump(); + expect(path.constraintZones.first.minWaypointRelativePos, + closeTo(0.25, 0.001)); + }); + + testWidgets('end pos text field', (widgetTester) async { + path.constraintZones = [ + ConstraintsZone( + constraints: PathConstraints(), + minWaypointRelativePos: 0.25, + maxWaypointRelativePos: 0.75, + ), + ]; + + await widgetTester.pumpWidget(MaterialApp( + home: Scaffold( + body: ConstraintZonesTree( + path: path, + initiallySelectedZone: 0, + onPathChanged: () => pathChanged = true, + onZoneHovered: (value) => hoveredZone = value, + onZoneSelected: (value) => selectedZone = value, + undoStack: undoStack, + ), + ), + )); + + var textField = find.widgetWithText(NumberTextField, 'End Pos'); + + expect(textField, findsOneWidget); + + await widgetTester.enterText(textField, '0.6'); + await widgetTester.testTextInput.receiveAction(TextInputAction.done); + await widgetTester.pumpAndSettle(); + + expect(pathChanged, true); + expect( + path.constraintZones.first.maxWaypointRelativePos, closeTo(0.6, 0.001)); + + undoStack.undo(); + await widgetTester.pump(); + expect(path.constraintZones.first.maxWaypointRelativePos, + closeTo(0.75, 0.001)); + }); } diff --git a/test/widgets/editor/tree_widgets/event_markers_tree_test.dart b/test/widgets/editor/tree_widgets/event_markers_tree_test.dart index 86636c55..b8394404 100644 --- a/test/widgets/editor/tree_widgets/event_markers_tree_test.dart +++ b/test/widgets/editor/tree_widgets/event_markers_tree_test.dart @@ -11,6 +11,7 @@ import 'package:pathplanner/widgets/editor/info_card.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/commands/command_group_widget.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/event_markers_tree.dart'; import 'package:pathplanner/widgets/editor/tree_widgets/tree_card_node.dart'; +import 'package:pathplanner/widgets/number_text_field.dart'; import 'package:undo/undo.dart'; void main() { @@ -380,4 +381,81 @@ void main() { await widgetTester.pump(); expect(path.eventMarkers.length, 2); }); + + testWidgets('start pos text field', (widgetTester) async { + path.eventMarkers = [ + EventMarker( + command: SequentialCommandGroup(commands: []), + waypointRelativePos: 0.25, + endWaypointRelativePos: 0.75, + ), + ]; + + await widgetTester.pumpWidget(MaterialApp( + home: Scaffold( + body: EventMarkersTree( + path: path, + onPathChangedNoSim: () => pathChanged = true, + onMarkerHovered: (value) => hoveredMarker = value, + onMarkerSelected: (value) => selectedMarker = value, + undoStack: undoStack, + initiallySelectedMarker: 0, + ), + ), + )); + + var textField = find.widgetWithText(NumberTextField, 'Start Pos'); + + expect(textField, findsOneWidget); + + await widgetTester.enterText(textField, '0.1'); + await widgetTester.testTextInput.receiveAction(TextInputAction.done); + await widgetTester.pumpAndSettle(); + + expect(pathChanged, true); + expect(path.eventMarkers.first.waypointRelativePos, closeTo(0.1, 0.001)); + + undoStack.undo(); + await widgetTester.pump(); + expect(path.eventMarkers.first.waypointRelativePos, closeTo(0.25, 0.001)); + }); + + testWidgets('end pos text field', (widgetTester) async { + path.eventMarkers = [ + EventMarker( + command: SequentialCommandGroup(commands: []), + waypointRelativePos: 0.25, + endWaypointRelativePos: 0.75, + ), + ]; + + await widgetTester.pumpWidget(MaterialApp( + home: Scaffold( + body: EventMarkersTree( + path: path, + onPathChangedNoSim: () => pathChanged = true, + onMarkerHovered: (value) => hoveredMarker = value, + onMarkerSelected: (value) => selectedMarker = value, + undoStack: undoStack, + initiallySelectedMarker: 0, + ), + ), + )); + + var textField = find.widgetWithText(NumberTextField, 'End Pos'); + + expect(textField, findsOneWidget); + + await widgetTester.enterText(textField, '0.9'); + await widgetTester.testTextInput.receiveAction(TextInputAction.done); + await widgetTester.pumpAndSettle(); + + expect(pathChanged, true); + expect(path.eventMarkers.first.endWaypointRelativePos, closeTo(0.9, 0.001)); + + undoStack.undo(); + await widgetTester.pump(); + expect( + path.eventMarkers.first.endWaypointRelativePos, closeTo(0.75, 0.001)); + }); } diff --git a/test/widgets/editor/tree_widgets/point_towards_zones_tree_test.dart b/test/widgets/editor/tree_widgets/point_towards_zones_tree_test.dart index 03719f65..f5ba1d4e 100644 --- a/test/widgets/editor/tree_widgets/point_towards_zones_tree_test.dart +++ b/test/widgets/editor/tree_widgets/point_towards_zones_tree_test.dart @@ -505,4 +505,82 @@ void main() { await widgetTester.pump(); expect(path.pointTowardsZones.length, 2); }); + + testWidgets('start pos text field', (widgetTester) async { + path.pointTowardsZones = [ + PointTowardsZone( + minWaypointRelativePos: 0.25, + maxWaypointRelativePos: 0.75, + ) + ]; + + await widgetTester.pumpWidget(MaterialApp( + home: Scaffold( + body: PointTowardsZonesTree( + path: path, + initiallySelectedZone: 0, + onPathChanged: () => pathChanged = true, + onZoneHovered: (value) => hoveredZone = value, + onZoneSelected: (value) => selectedZone = value, + undoStack: undoStack, + ), + ), + )); + + var textField = find.widgetWithText(NumberTextField, 'Start Pos'); + + expect(textField, findsOneWidget); + + await widgetTester.enterText(textField, '0.4'); + await widgetTester.testTextInput.receiveAction(TextInputAction.done); + await widgetTester.pumpAndSettle(); + + expect(pathChanged, true); + expect(path.pointTowardsZones.first.minWaypointRelativePos, + closeTo(0.4, 0.001)); + + undoStack.undo(); + await widgetTester.pump(); + expect(path.pointTowardsZones.first.minWaypointRelativePos, + closeTo(0.25, 0.001)); + }); + + testWidgets('end pos text field', (widgetTester) async { + path.pointTowardsZones = [ + PointTowardsZone( + minWaypointRelativePos: 0.25, + maxWaypointRelativePos: 0.75, + ) + ]; + + await widgetTester.pumpWidget(MaterialApp( + home: Scaffold( + body: PointTowardsZonesTree( + path: path, + initiallySelectedZone: 0, + onPathChanged: () => pathChanged = true, + onZoneHovered: (value) => hoveredZone = value, + onZoneSelected: (value) => selectedZone = value, + undoStack: undoStack, + ), + ), + )); + + var textField = find.widgetWithText(NumberTextField, 'End Pos'); + + expect(textField, findsOneWidget); + + await widgetTester.enterText(textField, '0.6'); + await widgetTester.testTextInput.receiveAction(TextInputAction.done); + await widgetTester.pumpAndSettle(); + + expect(pathChanged, true); + expect(path.pointTowardsZones.first.maxWaypointRelativePos, + closeTo(0.6, 0.001)); + + undoStack.undo(); + await widgetTester.pump(); + expect(path.pointTowardsZones.first.maxWaypointRelativePos, + closeTo(0.75, 0.001)); + }); }