@@ -110,60 +110,66 @@ export class LineCursor extends Marker {
110110 }
111111
112112 /**
113- * Decides if the previous and next methods should traverse the given node.
114- * The previous and next method only traverse previous connections, next
115- * connections and blocks.
113+ * Returns true iff the given node represents the "beginning of a
114+ * new line of code" (and thus can be visited by pressing the
115+ * up/down arrow keys). Specifically, if the node is for:
116+ *
117+ * - Any block that is not a value block.
118+ * - A top-level value block (one that is unconnected).
119+ * - An unconnected next statement input.
120+ * - An unconnected 'next' connection - the "blank line at the end".
121+ * This is to facilitate connecting additional blocks to a
122+ * stack/substack.
116123 *
117124 * @param node The AST node to check.
118125 * @returns True if the node should be visited, false otherwise.
119126 * @protected
120127 */
121128 validLineNode ( node : ASTNode | null ) : boolean {
122- if ( ! node ) {
123- return false ;
124- }
125- let isValid = false ;
129+ if ( ! node ) return false ;
126130 const location = node . getLocation ( ) ;
127131 const type = node && node . getType ( ) ;
128- if ( type == ASTNode . types . BLOCK ) {
129- if ( ( location as Blockly . Block ) . outputConnection === null ) {
130- isValid = true ;
131- }
132- } else if (
133- type == ASTNode . types . INPUT &&
134- ( location as Blockly . Connection ) . type == Blockly . NEXT_STATEMENT
135- ) {
136- isValid = true ;
137- } else if ( type == ASTNode . types . NEXT ) {
138- isValid = true ;
132+ switch ( type ) {
133+ case ASTNode . types . BLOCK :
134+ return ! ( ( location as Blockly . Block ) . outputConnection ?. isConnected ( ) ) ;
135+ case ASTNode . types . INPUT :
136+ const connection = ( location as Blockly . Connection ) ;
137+ return connection . type === Blockly . NEXT_STATEMENT && ! connection . isConnected ( ) ;
138+ case ASTNode . types . NEXT :
139+ return ! ( ( location as Blockly . Connection ) . isConnected ( ) ) ;
140+ default :
141+ return false ;
139142 }
140- return isValid ;
141143 }
142144
143145 /**
144- * Decides if the in and out methods should traverse the given node.
145- * The in and out method only traverse fields and input connections.
146+ * Returns true iff the given node can be visited by the cursor when
147+ * using the left/right arrow keys. Specifically, if the node is for:
148+ *
149+ * - Any block.
150+ * - Any field.
151+ * - Any unconnected next or input connection. This is to
152+ * facilitate connecting additional blocks.
146153 *
147154 * @param node The AST node to check whether it is valid.
148155 * @returns True if the node should be visited, false otherwise.
149156 * @protected
150157 */
151158 validInLineNode ( node : ASTNode | null ) : boolean {
152- if ( ! node ) {
153- return false ;
154- }
155- let isValid = false ;
159+ if ( ! node ) return false ;
156160 const location = node . getLocation ( ) ;
157161 const type = node && node . getType ( ) ;
158- if ( type == ASTNode . types . FIELD ) {
159- isValid = true ;
160- } else if (
161- type == ASTNode . types . INPUT &&
162- ( location as Blockly . Connection ) . type == Blockly . INPUT_VALUE
163- ) {
164- isValid = true ;
162+ switch ( type ) {
163+ case ASTNode . types . BLOCK :
164+ return true ;
165+ case ASTNode . types . INPUT :
166+ case ASTNode . types . NEXT :
167+ return ! ( ( location as Blockly . Connection ) . isConnected ( ) ) ;
168+ case ASTNode . types . FIELD :
169+ return true ;
170+ default :
171+ return false ;
165172 }
166- return isValid ;
167173 }
168174
169175 /**
@@ -395,60 +401,59 @@ export class LineCursor extends Marker {
395401 }
396402
397403 /**
398- * Set the location of the marker and call the update method.
399- * Setting isStack to true will only work if the newLocation is the top most
400- * output or previous connection on a stack.
404+ * Set the location of the marker and draw it.
401405 *
402406 * Overrides drawing logic to call `setSelected` if the location is
403- * a block, for testing on October 28 2024.
407+ * a block, or `addSelect` if it's a shadow block (since shadow
408+ * blocks can't be selected).
409+ *
410+ * TODO(#142): The selection and fake-selection code was originally
411+ * a hack added for testing on October 28 2024, because the default
412+ * drawer behaviour was to draw a box around the block and all
413+ * attached child blocks, which was confusing when navigating
414+ * stacks.
415+ *
416+ * Since then we have decided that we probably _do_ in most cases
417+ * want navigating to a block to select the block, but more
418+ * particularly that we want navigation to move _focus_. Replace
419+ * this selection hack with non-hacky changing of focus once that's
420+ * possible.
404421 *
405422 * @param newNode The new location of the marker.
406423 */
407- setCurNode ( newNode : ASTNode ) {
408- const oldNode = ( this as any ) . curNode ;
409- ( this as any ) . curNode = newNode ;
410- const drawer = ( this as any ) . drawer ;
424+ override setCurNode ( newNode : ASTNode ) {
425+ const oldNode = this . getCurNode ( ) ;
426+ super . setCurNode ( newNode ) ;
427+ const drawer = this . getDrawer ( ) ;
428+
411429 if ( ! drawer ) {
412430 console . error ( 'could not find a drawer' ) ;
413431 return ;
414432 }
415433
416- const newNodeIsFieldColour =
417- newNode ?. getType ( ) == ASTNode . types . FIELD &&
418- ( newNode . getLocation ( ) as Blockly . Field ) instanceof FieldColour ;
419- const oldNodeIsFieldColour =
420- oldNode ?. getType ( ) == ASTNode . types . FIELD &&
421- ( oldNode . getLocation ( ) as Blockly . Field ) instanceof FieldColour ;
422-
423- if ( newNode ?. getType ( ) == ASTNode . types . BLOCK ) {
424- drawer . hide ( ) ;
425- const block = newNode . getLocation ( ) as Blockly . BlockSvg ;
426- Blockly . common . setSelected ( block ) ;
427- } else if ( newNodeIsFieldColour ) {
428- drawer . hide ( ) ;
429-
430- if ( oldNode ?. getType ( ) == ASTNode . types . BLOCK ) {
434+ // If old node was a block, unselect it or remove fake selection.
435+ if ( oldNode ?. getType ( ) === ASTNode . types . BLOCK ) {
436+ const block = oldNode . getLocation ( ) as Blockly . BlockSvg ;
437+ if ( ! block . isShadow ( ) ) {
431438 Blockly . common . setSelected ( null ) ;
432- } else if ( oldNodeIsFieldColour ) {
433- const field = oldNode . getLocation ( ) as FieldColour ;
434- const block = field . getSourceBlock ( ) as Blockly . BlockSvg ;
439+ } else {
435440 block . removeSelect ( ) ;
436441 }
442+ }
437443
438- const field = newNode . getLocation ( ) as FieldColour ;
439- const block = field . getSourceBlock ( ) as Blockly . BlockSvg ;
440- block . addSelect ( ) ;
441- } else {
442- if ( oldNode ?. getType ( ) == ASTNode . types . BLOCK ) {
443- Blockly . common . setSelected ( null ) ;
444- } else if ( oldNodeIsFieldColour ) {
445- const field = oldNode . getLocation ( ) as FieldColour ;
446- const block = field . getSourceBlock ( ) as Blockly . BlockSvg ;
447- block . removeSelect ( ) ;
444+ // If new node is a block, select it or make it look selected.
445+ if ( newNode ?. getType ( ) === ASTNode . types . BLOCK ) {
446+ drawer . hide ( ) ;
447+ const block = newNode . getLocation ( ) as Blockly . BlockSvg ;
448+ if ( ! block . isShadow ( ) ) {
449+ Blockly . common . setSelected ( block ) ;
450+ } else {
451+ block . addSelect ( ) ;
448452 }
449-
450- drawer . draw ( oldNode , newNode ) ;
453+ return ;
451454 }
455+
456+ drawer . draw ( oldNode , newNode ) ;
452457 }
453458}
454459
0 commit comments