44 * SPDX-License-Identifier: Apache-2.0
55 */
66
7- import type { BlockSvg } from '../block_svg.js' ;
7+ import { BlockSvg } from '../block_svg.js' ;
8+ import type { Field } from '../field.js' ;
89import type { INavigable } from '../interfaces/i_navigable.js' ;
910import type { INavigationPolicy } from '../interfaces/i_navigation_policy.js' ;
10- import type { RenderedConnection } from '../rendered_connection .js' ;
11+ import { WorkspaceSvg } from '../workspace_svg .js' ;
1112
1213/**
1314 * Set of rules controlling keyboard navigation from a block.
@@ -24,7 +25,8 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
2425 for ( const field of input . fieldRow ) {
2526 return field ;
2627 }
27- if ( input . connection ) return input . connection as RenderedConnection ;
28+ if ( input . connection ?. targetBlock ( ) )
29+ return input . connection . targetBlock ( ) as BlockSvg ;
2830 }
2931
3032 return null ;
@@ -38,12 +40,14 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
3840 * which it is attached.
3941 */
4042 getParent ( current : BlockSvg ) : INavigable < unknown > | null {
41- const topBlock = current . getTopStackBlock ( ) ;
43+ if ( current . previousConnection ?. targetBlock ( ) ) {
44+ const surroundParent = current . getSurroundParent ( ) ;
45+ if ( surroundParent ) return surroundParent ;
46+ } else if ( current . outputConnection ?. targetBlock ( ) ) {
47+ return current . outputConnection . targetBlock ( ) ;
48+ }
4249
43- return (
44- ( this . getParentConnection ( topBlock ) ?. targetConnection ?. getParentInput ( )
45- ?. connection as RenderedConnection ) ?? topBlock
46- ) ;
50+ return current . workspace ;
4751 }
4852
4953 /**
@@ -54,21 +58,40 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
5458 * block, or its next connection.
5559 */
5660 getNextSibling ( current : BlockSvg ) : INavigable < unknown > | null {
57- const nextConnection = current . nextConnection ;
58- if ( ! current . outputConnection ?. targetConnection && ! nextConnection ) {
59- // If this block has no connected output connection and no next
60- // connection, it must be the last block in the stack, so its next sibling
61- // is the first block of the next stack on the workspace.
62- const topBlocks = current . workspace . getTopBlocks ( true ) ;
63- let targetIndex = topBlocks . indexOf ( current . getRootBlock ( ) ) + 1 ;
64- if ( targetIndex >= topBlocks . length ) {
65- targetIndex = 0 ;
61+ if ( current . nextConnection ?. targetBlock ( ) ) {
62+ return current . nextConnection ?. targetBlock ( ) ;
63+ }
64+
65+ const parent = this . getParent ( current ) ;
66+ let navigatingCrossStacks = false ;
67+ let siblings : ( BlockSvg | Field ) [ ] = [ ] ;
68+ if ( parent instanceof BlockSvg ) {
69+ for ( let i = 0 , input ; ( input = parent . inputList [ i ] ) ; i ++ ) {
70+ if ( input . connection ) {
71+ siblings . push ( ...input . fieldRow ) ;
72+ const child = input . connection . targetBlock ( ) ;
73+ if ( child ) {
74+ siblings . push ( child as BlockSvg ) ;
75+ }
76+ }
6677 }
67- const previousBlock = topBlocks [ targetIndex ] ;
68- return this . getParentConnection ( previousBlock ) ?? previousBlock ;
78+ } else if ( parent instanceof WorkspaceSvg ) {
79+ siblings = parent . getTopBlocks ( true ) ;
80+ navigatingCrossStacks = true ;
81+ } else {
82+ return null ;
6983 }
7084
71- return nextConnection ;
85+ const currentIndex = siblings . indexOf (
86+ navigatingCrossStacks ? current . getRootBlock ( ) : current ,
87+ ) ;
88+ if ( currentIndex >= 0 && currentIndex < siblings . length - 1 ) {
89+ return siblings [ currentIndex + 1 ] ;
90+ } else if ( currentIndex === siblings . length - 1 && navigatingCrossStacks ) {
91+ return siblings [ 0 ] ;
92+ }
93+
94+ return null ;
7295 }
7396
7497 /**
@@ -79,40 +102,45 @@ export class BlockNavigationPolicy implements INavigationPolicy<BlockSvg> {
79102 * connection/block of the previous block stack if it is a root block.
80103 */
81104 getPreviousSibling ( current : BlockSvg ) : INavigable < unknown > | null {
82- const parentConnection = this . getParentConnection ( current ) ;
83- if ( parentConnection ) return parentConnection ;
84-
85- // If this block has no output/previous connection, it must be a root block,
86- // so its previous sibling is the last connection of the last block of the
87- // previous stack on the workspace.
88- const topBlocks = current . workspace . getTopBlocks ( true ) ;
89- let targetIndex = topBlocks . indexOf ( current . getRootBlock ( ) ) - 1 ;
90- if ( targetIndex < 0 ) {
91- targetIndex = topBlocks . length - 1 ;
105+ if ( current . previousConnection ?. targetBlock ( ) ) {
106+ return current . previousConnection ?. targetBlock ( ) ;
92107 }
93108
94- const lastBlock = topBlocks [ targetIndex ]
95- . getDescendants ( true )
96- . reverse ( )
97- . pop ( ) ;
109+ const parent = this . getParent ( current ) ;
110+ let navigatingCrossStacks = false ;
111+ let siblings : ( BlockSvg | Field ) [ ] = [ ] ;
112+ if ( parent instanceof BlockSvg ) {
113+ for ( let i = 0 , input ; ( input = parent . inputList [ i ] ) ; i ++ ) {
114+ if ( input . connection ) {
115+ siblings . push ( ...input . fieldRow ) ;
116+ const child = input . connection . targetBlock ( ) ;
117+ if ( child ) {
118+ siblings . push ( child as BlockSvg ) ;
119+ }
120+ }
121+ }
122+ } else if ( parent instanceof WorkspaceSvg ) {
123+ siblings = parent . getTopBlocks ( true ) ;
124+ navigatingCrossStacks = true ;
125+ } else {
126+ return null ;
127+ }
98128
99- return lastBlock ?. nextConnection ?? lastBlock ?? null ;
100- }
129+ const currentIndex = siblings . indexOf ( current ) ;
130+ let result : INavigable < any > | null = null ;
131+ if ( currentIndex >= 1 ) {
132+ result = siblings [ currentIndex - 1 ] ;
133+ } else if ( currentIndex === 0 && navigatingCrossStacks ) {
134+ result = siblings [ siblings . length - 1 ] ;
135+ }
101136
102- /**
103- * Gets the parent connection on a block.
104- * This is either an output connection, previous connection or undefined.
105- * If both connections exist return the one that is actually connected
106- * to another block.
107- *
108- * @param block The block to find the parent connection on.
109- * @returns The connection connecting to the parent of the block.
110- */
111- protected getParentConnection ( block : BlockSvg ) {
112- if ( ! block . outputConnection || block . previousConnection ?. isConnected ( ) ) {
113- return block . previousConnection ;
137+ // If navigating to a previous stack, our previous sibling is the last
138+ // block in it.
139+ if ( navigatingCrossStacks && result instanceof BlockSvg ) {
140+ return result . lastConnectionInStack ( false ) ?. getSourceBlock ( ) ?? result ;
114141 }
115- return block . outputConnection ;
142+
143+ return result ;
116144 }
117145
118146 /**
0 commit comments