From 1a05806762c25625ccf315fc455298f81d7c2a6f Mon Sep 17 00:00:00 2001 From: AnsahMohammad Date: Thu, 21 Dec 2023 20:23:35 +0530 Subject: [PATCH 1/8] feat: table navigation using TAB key --- .../table_block_component/table_commands.dart | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/lib/src/editor/block_component/table_block_component/table_commands.dart b/lib/src/editor/block_component/table_block_component/table_commands.dart index 5ecf7d5bd..bd73476e4 100644 --- a/lib/src/editor/block_component/table_block_component/table_commands.dart +++ b/lib/src/editor/block_component/table_block_component/table_commands.dart @@ -8,6 +8,7 @@ final List tableCommands = [ _rightInTableCell, _upInTableCell, _downInTableCell, + _tabInTableCell, _backSpaceInTableCell, ]; @@ -41,6 +42,12 @@ final CommandShortcutEvent _downInTableCell = CommandShortcutEvent( handler: _downInTableCellHandler, ); +final CommandShortcutEvent _tabInTableCell = CommandShortcutEvent( + key: 'Navigate around the cells at same offset', + command: 'tab', + handler: _tabInTableCellHandler, +); + final CommandShortcutEvent _backSpaceInTableCell = CommandShortcutEvent( key: 'Stop at the beginning of the cell', command: 'backspace', @@ -152,6 +159,24 @@ CommandShortcutEventHandler _downInTableCellHandler = (editorState) { return KeyEventResult.ignored; }; +CommandShortcutEventHandler _tabInTableCellHandler = (editorState) { + final inTableNodes = _inTableNodes(editorState); + final selection = editorState.selection; + if (_hasSelectionAndTableCell(inTableNodes, selection)) { + final nextNode = _getNextNode(inTableNodes, 1, 0); + if (_nodeHasTextChild(nextNode)) { + editorState.selectionService.updateSelection( + Selection.single( + path: nextNode!.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + } + return KeyEventResult.handled; + } + return KeyEventResult.ignored; +}; + CommandShortcutEventHandler _backspaceInTableCellHandler = (editorState) { final selection = editorState.selection; if (selection == null || !selection.isCollapsed) { @@ -191,15 +216,28 @@ bool _hasSelectionAndTableCell( selection.isCollapsed && nodes.first.parent?.type == TableCellBlockKeys.type; -Node? _getNextNode(Iterable nodes, int colDiff, rowDiff) { +Node? _getNextNode(Iterable nodes, int colDiff, int rowDiff) { final cell = nodes.first.parent!; final col = cell.attributes[TableCellBlockKeys.colPosition]; final row = cell.attributes[TableCellBlockKeys.rowPosition]; - return cell.parent != null - ? getCellNode(cell.parent!, col + colDiff, row + rowDiff) - : null; + final table = cell.parent!; + + final numCols = table.children.last.attributes['colPosition'] + 1; + final numRows = table.children.last.attributes['rowPosition'] + 1; + + var nextCol = (col + colDiff) % numCols; + var nextRow = row + rowDiff + ((col + colDiff) ~/ numCols); + + if (isValidPosition(nextCol, nextRow, numCols, numRows)) { + return getCellNode(table, nextCol, nextRow); + } else { + return null; + } } +bool isValidPosition(int col, int row, int numCols, int numRows) => + col >= 0 && col < numCols && row >= 0 && row < numRows; + bool _nodeHasTextChild(Node? n) => n != null && n.children.isNotEmpty && From 9818a246c23ee7d77ef7731429993b4d6db9b78d Mon Sep 17 00:00:00 2001 From: AnsahMohammad Date: Thu, 21 Dec 2023 20:48:35 +0530 Subject: [PATCH 2/8] feat: shift+tab for reverse navigation in table --- .../table_block_component/table_commands.dart | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/src/editor/block_component/table_block_component/table_commands.dart b/lib/src/editor/block_component/table_block_component/table_commands.dart index bd73476e4..cde2f7a9d 100644 --- a/lib/src/editor/block_component/table_block_component/table_commands.dart +++ b/lib/src/editor/block_component/table_block_component/table_commands.dart @@ -9,6 +9,7 @@ final List tableCommands = [ _upInTableCell, _downInTableCell, _tabInTableCell, + _shiftTabInTableCell, _backSpaceInTableCell, ]; @@ -48,6 +49,12 @@ final CommandShortcutEvent _tabInTableCell = CommandShortcutEvent( handler: _tabInTableCellHandler, ); +final CommandShortcutEvent _shiftTabInTableCell = CommandShortcutEvent( + key: 'Navigate around the cells at same offset in reverse', + command: 'shift+tab', + handler: _shiftTabInTableCellHandler, +); + final CommandShortcutEvent _backSpaceInTableCell = CommandShortcutEvent( key: 'Stop at the beginning of the cell', command: 'backspace', @@ -177,6 +184,24 @@ CommandShortcutEventHandler _tabInTableCellHandler = (editorState) { return KeyEventResult.ignored; }; +CommandShortcutEventHandler _shiftTabInTableCellHandler = (editorState) { + final inTableNodes = _inTableNodes(editorState); + final selection = editorState.selection; + if (_hasSelectionAndTableCell(inTableNodes, selection)) { + final nextNode = _getPreviousNode(inTableNodes, 1, 0); + if (_nodeHasTextChild(nextNode)) { + editorState.selectionService.updateSelection( + Selection.single( + path: nextNode!.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + } + return KeyEventResult.handled; + } + return KeyEventResult.ignored; +}; + CommandShortcutEventHandler _backspaceInTableCellHandler = (editorState) { final selection = editorState.selection; if (selection == null || !selection.isCollapsed) { @@ -235,6 +260,25 @@ Node? _getNextNode(Iterable nodes, int colDiff, int rowDiff) { } } +Node? _getPreviousNode(Iterable nodes, int colDiff, int rowDiff) { + final cell = nodes.first.parent!; + final col = cell.attributes[TableCellBlockKeys.colPosition]; + final row = cell.attributes[TableCellBlockKeys.rowPosition]; + final table = cell.parent!; + + final numCols = table.children.last.attributes['colPosition'] + 1; + final numRows = table.children.last.attributes['rowPosition'] + 1; + + var prevCol = (col - colDiff + numCols) % numCols; + var prevRow = row - rowDiff - ((col - colDiff) < 0 ? 1 : 0); + + if (isValidPosition(prevCol, prevRow, numCols, numRows)) { + return getCellNode(table, prevCol, prevRow); + } else { + return null; + } +} + bool isValidPosition(int col, int row, int numCols, int numRows) => col >= 0 && col < numCols && row >= 0 && row < numRows; From bdffc293acbc4f3f3c0ccb62ebb37da52364d376 Mon Sep 17 00:00:00 2001 From: AnsahMohammad Date: Fri, 22 Dec 2023 06:57:54 +0530 Subject: [PATCH 3/8] chore: circular traversal for arrow left and right --- .../block_component/table_block_component/table_commands.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/editor/block_component/table_block_component/table_commands.dart b/lib/src/editor/block_component/table_block_component/table_commands.dart index cde2f7a9d..06a33eadc 100644 --- a/lib/src/editor/block_component/table_block_component/table_commands.dart +++ b/lib/src/editor/block_component/table_block_component/table_commands.dart @@ -94,7 +94,7 @@ CommandShortcutEventHandler _leftInTableCellHandler = (editorState) { final selection = editorState.selection; if (_hasSelectionAndTableCell(inTableNodes, selection) && selection!.start.offset == 0) { - final nextNode = _getNextNode(inTableNodes, -1, 0); + final nextNode = _getPreviousNode(inTableNodes, 1, 0); if (_nodeHasTextChild(nextNode)) { final target = nextNode!.childAtIndexOrNull(0)!; editorState.selectionService.updateSelection( From 8f6488eb1e3b1e41ccb740cabc2cc5ea338853ca Mon Sep 17 00:00:00 2001 From: AnsahMohammad Date: Tue, 26 Dec 2023 22:21:37 +0530 Subject: [PATCH 4/8] test: added tests for tab and shift-tab commands in Table --- .../table_commands_test.dart | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/test/new/block_component/table_block_component/table_commands_test.dart b/test/new/block_component/table_block_component/table_commands_test.dart index cdd0f7087..1ade2602f 100644 --- a/test/new/block_component/table_block_component/table_commands_test.dart +++ b/test/new/block_component/table_block_component/table_commands_test.dart @@ -304,5 +304,99 @@ void main() async { expect(selection.start.offset, 2); await editor.dispose(); }); + + testWidgets('tab key navigates to next cell', (tester) async { + final tableNode = TableNode.fromList([ + ['ab', 'cde'], + ['fghi', ''], + ]); + final editor = tester.editor..addNode(tableNode.node); + + await editor.startTesting(); + await tester.pumpAndSettle(); + + var cell00 = getCellNode(tableNode.node, 0, 0)!; + var cell10 = getCellNode(tableNode.node, 1, 0)!; + + await editor.updateSelection( + Selection.single( + path: cell00.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.tab); + + var selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + + await editor.updateSelection( + Selection.single( + path: cell00.childAtIndexOrNull(0)!.path, + startOffset: 1, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.tab); + + selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + + await editor.dispose(); + }); + + testWidgets('shift+tab key navigates to the previous cell', (tester) async { + final tableNode = TableNode.fromList([ + ['ab', 'cde'], + ['fghi', ''], + ]); + final editor = tester.editor..addNode(tableNode.node); + + await editor.startTesting(); + await tester.pumpAndSettle(); + + var cell01 = getCellNode(tableNode.node, 0, 1)!; + var cell10 = getCellNode(tableNode.node, 1, 0)!; + + await editor.updateSelection( + Selection.single( + path: cell01.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.shiftLeft); + await simulateKeyDownEvent(LogicalKeyboardKey.tab); + + var selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + + await editor.updateSelection( + Selection.single( + path: cell01.childAtIndexOrNull(0)!.path, + startOffset: 1, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.shiftLeft); + await simulateKeyDownEvent(LogicalKeyboardKey.tab); + + selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + + await editor.dispose(); + }); }); } From ac475ee0c3f3dcb01f3880c18d91d5b6b3fa6d77 Mon Sep 17 00:00:00 2001 From: AnsahMohammad Date: Tue, 26 Dec 2023 22:43:17 +0530 Subject: [PATCH 5/8] test: added tests for arrow left and right in table --- .../table_commands_test.dart | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/test/new/block_component/table_block_component/table_commands_test.dart b/test/new/block_component/table_block_component/table_commands_test.dart index 1ade2602f..e274fc496 100644 --- a/test/new/block_component/table_block_component/table_commands_test.dart +++ b/test/new/block_component/table_block_component/table_commands_test.dart @@ -305,6 +305,160 @@ void main() async { await editor.dispose(); }); + testWidgets('arrowLeft key on beginning of a cell', (tester) async { + final tableNode = TableNode.fromList([ + ['ab', 'cde'], + ['fghi', ''], + ]); + final editor = tester.editor..addNode(tableNode.node); + + await editor.startTesting(); + await tester.pumpAndSettle(); + + var cell10 = getCellNode(tableNode.node, 1, 0)!; + var cell00 = getCellNode(tableNode.node, 0, 0)!; + + await editor.updateSelection( + Selection.single( + path: cell10.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft); + + var selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell00.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 2); + + await editor.updateSelection( + Selection.single( + path: cell00.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft); + + selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell00.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + + await editor.dispose(); + }); + + testWidgets('arrowLeft key on middle of a cell', (tester) async { + final tableNode = TableNode.fromList([ + ['ab', 'cde'], + ['fghi', ''], + ]); + final editor = tester.editor..addNode(tableNode.node); + + await editor.startTesting(); + await tester.pumpAndSettle(); + + var cell10 = getCellNode(tableNode.node, 1, 0)!; + + await editor.updateSelection( + Selection.single( + path: cell10.childAtIndexOrNull(0)!.path, + startOffset: 1, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft); + + var selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + + await editor.dispose(); + }); + + testWidgets('arrowRight key on beginning of a cell', (tester) async { + final tableNode = TableNode.fromList([ + ['ab', 'cde'], + ['fghi', ''], + ]); + final editor = tester.editor..addNode(tableNode.node); + + await editor.startTesting(); + await tester.pumpAndSettle(); + + var cell00 = getCellNode(tableNode.node, 0, 0)!; + + await editor.updateSelection( + Selection.single( + path: cell00.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight); + + var selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell00.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 1); + + await editor.dispose(); + }); + + testWidgets('arrowRight key on end of a cell', (tester) async { + final tableNode = TableNode.fromList([ + ['ab', 'cde'], + ['fghi', 'jk'], + ]); + final editor = tester.editor..addNode(tableNode.node); + + await editor.startTesting(); + await tester.pumpAndSettle(); + + var cell00 = getCellNode(tableNode.node, 0, 0)!; + var cell10 = getCellNode(tableNode.node, 1, 0)!; + var cell11 = getCellNode(tableNode.node, 1, 1)!; + + await editor.updateSelection( + Selection.single( + path: cell00.childAtIndexOrNull(0)!.path, + startOffset: 2, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight); + + var selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + + // right arrow on last cell doesn't move anywehere + await editor.updateSelection( + Selection.single( + path: cell11.childAtIndexOrNull(0)!.path, + startOffset: 2, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight); + + selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell11.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 2); + + await editor.dispose(); + }); + testWidgets('tab key navigates to next cell', (tester) async { final tableNode = TableNode.fromList([ ['ab', 'cde'], From f44a616cb9bfd98104c2d551ca2401ef9e6f746c Mon Sep 17 00:00:00 2001 From: AnsahMohammad Date: Tue, 26 Dec 2023 22:47:14 +0530 Subject: [PATCH 6/8] test: added corner case for tab and shift tab --- .../table_commands_test.dart | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/test/new/block_component/table_block_component/table_commands_test.dart b/test/new/block_component/table_block_component/table_commands_test.dart index e274fc496..26735d4e4 100644 --- a/test/new/block_component/table_block_component/table_commands_test.dart +++ b/test/new/block_component/table_block_component/table_commands_test.dart @@ -462,7 +462,7 @@ void main() async { testWidgets('tab key navigates to next cell', (tester) async { final tableNode = TableNode.fromList([ ['ab', 'cde'], - ['fghi', ''], + ['fghi', 'jk'], ]); final editor = tester.editor..addNode(tableNode.node); @@ -471,6 +471,7 @@ void main() async { var cell00 = getCellNode(tableNode.node, 0, 0)!; var cell10 = getCellNode(tableNode.node, 1, 0)!; + var cell11 = getCellNode(tableNode.node, 1, 1)!; await editor.updateSelection( Selection.single( @@ -502,6 +503,22 @@ void main() async { expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); expect(selection.start.offset, 0); + // tab doesn't navigate when at last cell + await editor.updateSelection( + Selection.single( + path: cell11.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.tab); + + selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell11.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + await editor.dispose(); }); @@ -515,6 +532,7 @@ void main() async { await editor.startTesting(); await tester.pumpAndSettle(); + var cell00 = getCellNode(tableNode.node, 0, 0)!; var cell01 = getCellNode(tableNode.node, 0, 1)!; var cell10 = getCellNode(tableNode.node, 1, 0)!; @@ -550,6 +568,23 @@ void main() async { expect(selection.start.path, cell10.childAtIndexOrNull(0)!.path); expect(selection.start.offset, 0); + // shift+tab doesn't navigate when at first cell + await editor.updateSelection( + Selection.single( + path: cell00.childAtIndexOrNull(0)!.path, + startOffset: 0, + ), + ); + + await simulateKeyDownEvent(LogicalKeyboardKey.shiftLeft); + await simulateKeyDownEvent(LogicalKeyboardKey.tab); + + selection = editor.selection!; + + expect(selection.isCollapsed, true); + expect(selection.start.path, cell00.childAtIndexOrNull(0)!.path); + expect(selection.start.offset, 0); + await editor.dispose(); }); }); From 7b115929dd9c5760244529225b8f2de78761768a Mon Sep 17 00:00:00 2001 From: AnsahMohammad Date: Mon, 1 Jan 2024 21:49:24 +0530 Subject: [PATCH 7/8] refactor: made changes as per the review --- .../table_block_component/table_commands.dart | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/lib/src/editor/block_component/table_block_component/table_commands.dart b/lib/src/editor/block_component/table_block_component/table_commands.dart index 06a33eadc..b454f819c 100644 --- a/lib/src/editor/block_component/table_block_component/table_commands.dart +++ b/lib/src/editor/block_component/table_block_component/table_commands.dart @@ -245,38 +245,50 @@ Node? _getNextNode(Iterable nodes, int colDiff, int rowDiff) { final cell = nodes.first.parent!; final col = cell.attributes[TableCellBlockKeys.colPosition]; final row = cell.attributes[TableCellBlockKeys.rowPosition]; - final table = cell.parent!; + final table = cell.parent; + if (table == null) { + return null; + } - final numCols = table.children.last.attributes['colPosition'] + 1; - final numRows = table.children.last.attributes['rowPosition'] + 1; + final numCols = + table.children.last.attributes[TableCellBlockKeys.colPosition] + 1; + final numRows = + table.children.last.attributes[TableCellBlockKeys.rowPosition] + 1; + // Calculate the next column index, considering the column difference and wrapping around with modulo. var nextCol = (col + colDiff) % numCols; + + // Calculate the next row index, taking into account the row difference and adjusting for additional rows due to column change. var nextRow = row + rowDiff + ((col + colDiff) ~/ numCols); - if (isValidPosition(nextCol, nextRow, numCols, numRows)) { - return getCellNode(table, nextCol, nextRow); - } else { - return null; - } + return isValidPosition(nextCol, nextRow, numCols, numRows) + ? getCellNode(table, nextCol, nextRow) + : null; } Node? _getPreviousNode(Iterable nodes, int colDiff, int rowDiff) { final cell = nodes.first.parent!; final col = cell.attributes[TableCellBlockKeys.colPosition]; final row = cell.attributes[TableCellBlockKeys.rowPosition]; - final table = cell.parent!; + final table = cell.parent; + if (table == null) { + return null; + } - final numCols = table.children.last.attributes['colPosition'] + 1; - final numRows = table.children.last.attributes['rowPosition'] + 1; + final numCols = + table.children.last.attributes[TableCellBlockKeys.colPosition] + 1; + final numRows = + table.children.last.attributes[TableCellBlockKeys.rowPosition] + 1; + // Calculate the previous column index, ensuring it wraps within the table boundaries using modulo. var prevCol = (col - colDiff + numCols) % numCols; + + // Calculate the previous row index, considering table boundaries and adjusting for potential column underflow. var prevRow = row - rowDiff - ((col - colDiff) < 0 ? 1 : 0); - if (isValidPosition(prevCol, prevRow, numCols, numRows)) { - return getCellNode(table, prevCol, prevRow); - } else { - return null; - } + return isValidPosition(prevCol, prevRow, numCols, numRows) + ? getCellNode(table, prevCol, prevRow) + : null; } bool isValidPosition(int col, int row, int numCols, int numRows) => From 66d407770992de7a2a3da2913031c5a11ed926c1 Mon Sep 17 00:00:00 2001 From: Ansah Mohammad Date: Mon, 1 Jan 2024 21:50:37 +0530 Subject: [PATCH 8/8] refactor: Apply suggestions from code review Co-authored-by: Lucas.Xu --- .../table_block_component/table_commands.dart | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/src/editor/block_component/table_block_component/table_commands.dart b/lib/src/editor/block_component/table_block_component/table_commands.dart index b454f819c..02d8e1837 100644 --- a/lib/src/editor/block_component/table_block_component/table_commands.dart +++ b/lib/src/editor/block_component/table_block_component/table_commands.dart @@ -171,13 +171,14 @@ CommandShortcutEventHandler _tabInTableCellHandler = (editorState) { final selection = editorState.selection; if (_hasSelectionAndTableCell(inTableNodes, selection)) { final nextNode = _getNextNode(inTableNodes, 1, 0); - if (_nodeHasTextChild(nextNode)) { - editorState.selectionService.updateSelection( - Selection.single( - path: nextNode!.childAtIndexOrNull(0)!.path, + if (nextNode != null && _nodeHasTextChild(nextNode)) { + final firstChild = nextNode.childAtIndexOrNull(0); + if (firstChild != null) { + editorState.selection = Selection.single( + path: firstChild.path, startOffset: 0, - ), - ); + ); + } } return KeyEventResult.handled; } @@ -188,14 +189,15 @@ CommandShortcutEventHandler _shiftTabInTableCellHandler = (editorState) { final inTableNodes = _inTableNodes(editorState); final selection = editorState.selection; if (_hasSelectionAndTableCell(inTableNodes, selection)) { - final nextNode = _getPreviousNode(inTableNodes, 1, 0); - if (_nodeHasTextChild(nextNode)) { - editorState.selectionService.updateSelection( - Selection.single( - path: nextNode!.childAtIndexOrNull(0)!.path, + final previousNode = _getPreviousNode(inTableNodes, 1, 0); + if (previousNode != null && _nodeHasTextChild(previousNode)) { + final firstChild = previousNode.childAtIndexOrNull(0); + if (firstChild != null) { + editorState.selection = Selection.single( + path: firstChild.path, startOffset: 0, - ), - ); + ); + } } return KeyEventResult.handled; }