diff --git a/lib/features/workspace/grid_cell_editor.dart b/lib/features/workspace/grid_cell_editor.dart index 6ce501d..3414f00 100644 --- a/lib/features/workspace/grid_cell_editor.dart +++ b/lib/features/workspace/grid_cell_editor.dart @@ -77,8 +77,34 @@ class _GridCellEditorState extends material.State { super.dispose(); } - void _handleKeyEvent(KeyEvent event) { - if (event is! KeyDownEvent) return; + bool _finished = false; + + void _commit( + String value, { + bool moveNextCol = false, + bool movePrevCol = false, + bool moveNextRow = false, + bool movePrevRow = false, + }) { + if (_finished) return; + _finished = true; + widget.onCommit( + value, + moveNextCol: moveNextCol, + movePrevCol: movePrevCol, + moveNextRow: moveNextRow, + movePrevRow: movePrevRow, + ); + } + + void _cancel() { + if (_finished) return; + _finished = true; + widget.onCancel(); + } + + KeyEventResult _handleKeyEvent(material.FocusNode node, KeyEvent event) { + if (event is! KeyDownEvent) return KeyEventResult.ignored; final isShift = HardwareKeyboard.instance.isShiftPressed; final isAlt = HardwareKeyboard.instance.isAltPressed; @@ -87,8 +113,8 @@ class _GridCellEditorState extends material.State { // Alt+N / Ctrl+Alt+N -> Set NULL if (event.logicalKey == LogicalKeyboardKey.keyN && (isAlt || (isControl && isAlt))) { - widget.onCommit('NULL'); - return; + _commit('NULL'); + return KeyEventResult.handled; } // Alt+Enter or Ctrl+Enter -> Open Inspector @@ -96,35 +122,37 @@ class _GridCellEditorState extends material.State { event.logicalKey == LogicalKeyboardKey.numpadEnter) && (isAlt || isControl)) { widget.onOpenInspector?.call(); - return; + return KeyEventResult.handled; } // Enter / Shift+Enter -> Commit and navigate row if (event.logicalKey == LogicalKeyboardKey.enter || event.logicalKey == LogicalKeyboardKey.numpadEnter) { if (isShift) { - widget.onCommit(_controller.text, movePrevRow: true); + _commit(_controller.text, movePrevRow: true); } else { - widget.onCommit(_controller.text, moveNextRow: true); + _commit(_controller.text, moveNextRow: true); } - return; + return KeyEventResult.handled; } // Tab / Shift+Tab -> Commit and navigate col if (event.logicalKey == LogicalKeyboardKey.tab) { if (isShift) { - widget.onCommit(_controller.text, movePrevCol: true); + _commit(_controller.text, movePrevCol: true); } else { - widget.onCommit(_controller.text, moveNextCol: true); + _commit(_controller.text, moveNextCol: true); } - return; + return KeyEventResult.handled; } // Escape -> Cancel if (event.logicalKey == LogicalKeyboardKey.escape) { - widget.onCancel(); - return; + _cancel(); + return KeyEventResult.handled; } + + return KeyEventResult.ignored; } @override @@ -149,7 +177,7 @@ class _GridCellEditorState extends material.State { child: material.Row( children: [ material.Expanded( - child: material.KeyboardListener( + child: material.Focus( focusNode: _focusNode, onKeyEvent: _handleKeyEvent, autofocus: true, @@ -167,7 +195,7 @@ class _GridCellEditorState extends material.State { contentPadding: material.EdgeInsets.zero, ), onSubmitted: (value) { - widget.onCommit(value, moveNextRow: true); + _commit(value, moveNextRow: true); }, ), ), diff --git a/test/features/workspace/grid_cell_editor_test.dart b/test/features/workspace/grid_cell_editor_test.dart index 56f70ab..db57609 100644 --- a/test/features/workspace/grid_cell_editor_test.dart +++ b/test/features/workspace/grid_cell_editor_test.dart @@ -49,6 +49,43 @@ void main() { expect(movedRow, isTrue); }); + testWidgets('invokes onCommit exactly once on Enter key press without double commit', + (tester) async { + int commitCount = 0; + String? committed; + bool movedRow = false; + + await tester.pumpWidget( + queryaThemeTestShell( + child: material.Material( + child: GridCellEditor( + initialValue: 'Hello', + width: 200, + height: 36, + onCommit: (val, + {moveNextCol = false, + movePrevCol = false, + moveNextRow = false, + movePrevRow = false}) { + commitCount++; + committed = val; + movedRow = moveNextRow; + }, + onCancel: () {}, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + await tester.sendKeyEvent(LogicalKeyboardKey.enter); + await tester.pumpAndSettle(); + + expect(commitCount, 1); + expect(committed, 'Hello'); + expect(movedRow, isTrue); + }); + testWidgets('triggers onCancel when Escape key is pressed', (tester) async { bool cancelled = false;