mirror of
https://github.com/google/blockly.git
synced 2026-01-07 00:50:27 +01:00
feat: Make navigation looping configurable (#9511)
* feat: Make navigation looping configurable * chore: Add TODO to clean up API
This commit is contained in:
@@ -181,7 +181,8 @@ function getBlockNavigationCandidates(
|
||||
* `delta` relative to the current element's stack when navigating backwards.
|
||||
*/
|
||||
export function navigateStacks(current: ISelectable, delta: number) {
|
||||
const stacks: IFocusableNode[] = (current.workspace as WorkspaceSvg)
|
||||
const workspace = current.workspace as WorkspaceSvg;
|
||||
const stacks: IFocusableNode[] = workspace
|
||||
.getTopBoundedElements(true)
|
||||
.filter((element: IBoundedElement) => isFocusableNode(element));
|
||||
const currentIndex = stacks.indexOf(
|
||||
@@ -189,12 +190,15 @@ export function navigateStacks(current: ISelectable, delta: number) {
|
||||
);
|
||||
const targetIndex = currentIndex + delta;
|
||||
let result: IFocusableNode | null = null;
|
||||
const loop = workspace.getCursor().getNavigationLoops();
|
||||
if (targetIndex >= 0 && targetIndex < stacks.length) {
|
||||
result = stacks[targetIndex];
|
||||
} else if (targetIndex < 0) {
|
||||
} else if (loop && targetIndex < 0) {
|
||||
result = stacks[stacks.length - 1];
|
||||
} else if (targetIndex >= stacks.length) {
|
||||
} else if (loop && targetIndex >= stacks.length) {
|
||||
result = stacks[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// When navigating to a previous block stack, our previous sibling is the last
|
||||
|
||||
@@ -42,6 +42,9 @@ export class LineCursor extends Marker {
|
||||
/** Locations to try moving the cursor to after a deletion. */
|
||||
private potentialNodes: IFocusableNode[] | null = null;
|
||||
|
||||
/** Whether or not navigation loops around when reaching the end. */
|
||||
private navigationLoops = true;
|
||||
|
||||
/**
|
||||
* @param workspace The workspace this cursor belongs to.
|
||||
*/
|
||||
@@ -64,7 +67,7 @@ export class LineCursor extends Marker {
|
||||
const newNode = this.getNextNode(
|
||||
curNode,
|
||||
this.getValidationFunction(NavigationDirection.NEXT),
|
||||
true,
|
||||
this.getNavigationLoops(),
|
||||
);
|
||||
|
||||
if (newNode) {
|
||||
@@ -89,7 +92,7 @@ export class LineCursor extends Marker {
|
||||
const newNode = this.getNextNode(
|
||||
curNode,
|
||||
this.getValidationFunction(NavigationDirection.IN),
|
||||
true,
|
||||
this.getNavigationLoops(),
|
||||
);
|
||||
|
||||
if (newNode) {
|
||||
@@ -112,7 +115,7 @@ export class LineCursor extends Marker {
|
||||
const newNode = this.getPreviousNode(
|
||||
curNode,
|
||||
this.getValidationFunction(NavigationDirection.PREVIOUS),
|
||||
true,
|
||||
this.getNavigationLoops(),
|
||||
);
|
||||
|
||||
if (newNode) {
|
||||
@@ -137,7 +140,7 @@ export class LineCursor extends Marker {
|
||||
const newNode = this.getPreviousNode(
|
||||
curNode,
|
||||
this.getValidationFunction(NavigationDirection.OUT),
|
||||
true,
|
||||
this.getNavigationLoops(),
|
||||
);
|
||||
|
||||
if (newNode) {
|
||||
@@ -158,12 +161,12 @@ export class LineCursor extends Marker {
|
||||
const inNode = this.getNextNode(
|
||||
curNode,
|
||||
this.getValidationFunction(NavigationDirection.IN),
|
||||
true,
|
||||
this.getNavigationLoops(),
|
||||
);
|
||||
const nextNode = this.getNextNode(
|
||||
curNode,
|
||||
this.getValidationFunction(NavigationDirection.NEXT),
|
||||
true,
|
||||
this.getNavigationLoops(),
|
||||
);
|
||||
|
||||
return inNode === nextNode;
|
||||
@@ -219,11 +222,22 @@ export class LineCursor extends Marker {
|
||||
getNextNode(
|
||||
node: IFocusableNode | null,
|
||||
isValid: (p1: IFocusableNode | null) => boolean,
|
||||
// TODO: Consider deprecating and removing this argument.
|
||||
loop: boolean,
|
||||
): IFocusableNode | null {
|
||||
if (!node || (!loop && this.getLastNode() === node)) return null;
|
||||
const originalLoop = this.getNavigationLoops();
|
||||
this.setNavigationLoops(loop);
|
||||
|
||||
return this.getNextNodeImpl(node, isValid);
|
||||
let result: IFocusableNode | null;
|
||||
if (!node || (!loop && this.getLastNode() === node)) {
|
||||
result = null;
|
||||
} else {
|
||||
result = this.getNextNodeImpl(node, isValid);
|
||||
}
|
||||
|
||||
this.setNavigationLoops(originalLoop);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,11 +287,22 @@ export class LineCursor extends Marker {
|
||||
getPreviousNode(
|
||||
node: IFocusableNode | null,
|
||||
isValid: (p1: IFocusableNode | null) => boolean,
|
||||
// TODO: Consider deprecating and removing this argument.
|
||||
loop: boolean,
|
||||
): IFocusableNode | null {
|
||||
if (!node || (!loop && this.getFirstNode() === node)) return null;
|
||||
const originalLoop = this.getNavigationLoops();
|
||||
this.setNavigationLoops(loop);
|
||||
|
||||
return this.getPreviousNodeImpl(node, isValid);
|
||||
let result: IFocusableNode | null;
|
||||
if (!node || (!loop && this.getFirstNode() === node)) {
|
||||
result = null;
|
||||
} else {
|
||||
result = this.getPreviousNodeImpl(node, isValid);
|
||||
}
|
||||
|
||||
this.setNavigationLoops(originalLoop);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,6 +563,24 @@ export class LineCursor extends Marker {
|
||||
const first = this.getFirstNode();
|
||||
return this.getPreviousNode(first, () => true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether or not navigation should loop around when reaching the end
|
||||
* of the workspace.
|
||||
*
|
||||
* @param loops True if navigation should loop around, otherwise false.
|
||||
*/
|
||||
setNavigationLoops(loops: boolean) {
|
||||
this.navigationLoops = loops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not navigation loops around when reaching the end of
|
||||
* the workspace.
|
||||
*/
|
||||
getNavigationLoops(): boolean {
|
||||
return this.navigationLoops;
|
||||
}
|
||||
}
|
||||
|
||||
registry.register(registry.Type.CURSOR, registry.DEFAULT, LineCursor);
|
||||
|
||||
Reference in New Issue
Block a user