executeHandleArrowKey function
bool
executeHandleArrowKey(
- LogicalKeyboardKey key,
- FluentDocument document, {
- bool ctrl = false,
- bool shift = false,
Implementation
bool executeHandleArrowKey(
LogicalKeyboardKey key,
FluentDocument document, {
bool ctrl = false,
bool shift = false,
}) {
final cursor = document.cursor;
final current = shift
? CaretStop(cursor.focusId, cursor.focusOffset)
: CaretStop(cursor.anchorId, cursor.anchorOffset);
final root = document.content;
final stops = document.caretStops;
late final NavigationResult result;
late final bool isVertical;
if (!shift && !cursor.isCollapsed) {
final anchorIdx = findStopIndex(stops, cursor.anchorId, cursor.anchorOffset);
final focusIdx = findStopIndex(stops, cursor.focusId, cursor.focusOffset);
if (anchorIdx >= 0 && focusIdx >= 0) {
final start = anchorIdx <= focusIdx
? CaretStop(cursor.anchorId, cursor.anchorOffset)
: CaretStop(cursor.focusId, cursor.focusOffset);
final end = anchorIdx <= focusIdx
? CaretStop(cursor.focusId, cursor.focusOffset)
: CaretStop(cursor.anchorId, cursor.anchorOffset);
if (key == LogicalKeyboardKey.arrowLeft ||
key == LogicalKeyboardKey.arrowUp) {
cursor.batchUpdate(() {
cursor.moveTo(start.fragmentId, start.offset);
});
document.syncPendingFontWithCursor();
document.selectionManager.collapse();
_syncSelectionManager(document);
document.cursorOnlyUpdate();
return true;
}
if (key == LogicalKeyboardKey.arrowRight ||
key == LogicalKeyboardKey.arrowDown) {
cursor.batchUpdate(() {
cursor.moveTo(end.fragmentId, end.offset);
});
document.syncPendingFontWithCursor();
document.selectionManager.collapse();
_syncSelectionManager(document);
document.cursorOnlyUpdate();
return true;
}
}
}
if (key == LogicalKeyboardKey.arrowLeft) {
result = ctrl
? moveWordLeft(root, current,
stops: stops, cachedLines: document.logicalLines)
: moveLeft(root, current, stops: stops);
isVertical = false;
} else if (key == LogicalKeyboardKey.arrowRight) {
result = ctrl
? moveWordRight(root, current,
stops: stops, cachedLines: document.logicalLines)
: moveRight(root, current, stops: stops);
isVertical = false;
} else if (key == LogicalKeyboardKey.arrowUp ||
key == LogicalKeyboardKey.arrowDown) {
final currentNode = document.nodeById(current.fragmentId);
final isBlockNode = currentNode is HorizontalRule || currentNode is FluentImage;
if (isBlockNode) {
final allStops = document.caretStops;
final currentIdx = findStopIndex(allStops, current.fragmentId, current.offset);
if (currentIdx >= 0) {
if (key == LogicalKeyboardKey.arrowUp) {
if (currentIdx > 0) {
result = NavigationResult(position: allStops[currentIdx - 1], preferredX: -1.0);
} else {
result = NavigationResult.none;
}
} else { // arrowDown
int nextIdx = currentIdx + 1;
while (nextIdx < allStops.length &&
allStops[nextIdx].fragmentId == current.fragmentId) {
nextIdx++;
}
if (nextIdx < allStops.length) {
result = NavigationResult(position: allStops[nextIdx], preferredX: -1.0);
} else {
result = NavigationResult.none;
}
}
isVertical = true;
}
} else {
final currentContainerId = document.findLogicalContainerId(current.fragmentId);
final containerOrder = document.containerOrder;
final containerIdx = containerOrder.indexOf(currentContainerId ?? '');
final candidateIds = <String>{};
if (containerIdx >= 0) {
candidateIds.add(containerOrder[containerIdx]);
String? nearestStructure(String? id) {
if (id == null) return null;
String? pid = id;
while (pid != null) {
final node = document.nodeById(pid);
if (node is FluentTable || node is FluentList) return pid;
pid = document.findParentCached(pid);
}
return null;
}
bool isInsideStructure(String? containerId, String structureId) {
if (containerId == null) return false;
String? pid = containerId;
while (pid != null) {
if (pid == structureId) return true;
pid = document.findParentCached(pid);
}
return false;
}
String? cellFor(String? containerId) {
if (containerId == null) return null;
String? pid = containerId;
while (pid != null) {
final node = document.nodeById(pid);
if (node is FluentCell) return pid;
pid = document.findParentCached(pid);
}
return null;
}
final currentEnclosing = nearestStructure(currentContainerId);
if (currentEnclosing != null) {
final enclosingNode = document.nodeById(currentEnclosing);
if (enclosingNode is FluentTable) {
final table = enclosingNode;
final currentCellId = cellFor(currentContainerId);
if (currentCellId != null) {
int rowIndex = -1;
int logicalCol = -1;
for (int r = 0; r < table.rows.length; r++) {
final row = table.rows[r];
int col = 0;
for (int c = 0; c < row.cells.length; c++) {
if (row.cells[c].id == currentCellId) {
rowIndex = r;
logicalCol = col;
break;
}
col += row.cells[c].colSpan;
}
if (rowIndex >= 0) break;
}
if (rowIndex >= 0 && logicalCol >= 0) {
for (final id in containerOrder) {
if (isInsideStructure(id, currentCellId)) {
candidateIds.add(id);
}
}
if (rowIndex > 0) {
final aboveCellId = _findCellAtLogicalCol(
table.rows[rowIndex - 1], logicalCol);
if (aboveCellId != null) {
for (final id in containerOrder) {
if (isInsideStructure(id, aboveCellId)) {
candidateIds.add(id);
}
}
}
}
if (rowIndex < table.rows.length - 1) {
final belowCellId = _findCellAtLogicalCol(
table.rows[rowIndex + 1], logicalCol);
if (belowCellId != null) {
for (final id in containerOrder) {
if (isInsideStructure(id, belowCellId)) {
candidateIds.add(id);
}
}
}
}
}
}
} else if (enclosingNode is FluentList) {
for (final id in containerOrder) {
if (isInsideStructure(id, currentEnclosing)) {
candidateIds.add(id);
}
}
}
if (containerIdx > 0) {
for (int i = containerIdx - 1; i >= 0; i--) {
final id = containerOrder[i];
if (!isInsideStructure(id, currentEnclosing)) {
candidateIds.add(id);
break;
}
}
}
if (containerIdx < containerOrder.length - 1) {
for (int i = containerIdx + 1; i < containerOrder.length; i++) {
final id = containerOrder[i];
if (!isInsideStructure(id, currentEnclosing)) {
candidateIds.add(id);
break;
}
}
}
} else {
if (containerIdx > 0) candidateIds.add(containerOrder[containerIdx - 1]);
if (containerIdx < containerOrder.length - 1) {
candidateIds.add(containerOrder[containerIdx + 1]);
}
}
}
final candidateStops = candidateIds.isNotEmpty
? candidateIds
.expand<CaretStop>((id) => document.stopsByContainer[id] ?? [])
.toList()
: stops;
final pref = _adjustPreferredXForBlockImage(document, current, cursor.preferredX);
if (key == LogicalKeyboardKey.arrowUp) {
result = moveUp(root, current, pref,
document.resolveCaretX, document.resolveCaretY,
stops: candidateStops, allStops: stops,
parentResolver: document.findParentCached,
containerResolver: document.findLogicalContainerId,
topLevelIndexResolver: document.topLevelIndexOf);
} else {
result = moveDown(root, current, pref,
document.resolveCaretX, document.resolveCaretY,
stops: candidateStops, allStops: stops,
parentResolver: document.findParentCached,
containerResolver: document.findLogicalContainerId,
topLevelIndexResolver: document.topLevelIndexOf);
}
isVertical = true;
}
} else {
return false;
}
final newPos = result.position;
if (newPos == null) return true;
if (shift) {
cursor.batchUpdate(() {
cursor.focusTo(newPos.fragmentId, newPos.offset);
cursor.preferredX = isVertical ? result.preferredX : -1.0;
});
} else {
cursor.batchUpdate(() {
cursor.moveTo(newPos.fragmentId, newPos.offset);
if (isVertical) cursor.preferredX = result.preferredX;
});
document.syncPendingFontWithCursor();
}
_syncSelectionManager(document);
document.cursorOnlyUpdate();
return true;
}