@@ -22,11 +22,49 @@ import {ASTNode, Marker} from 'blockly/core';
2222export class LineCursor extends Marker {
2323 override type = 'cursor' ;
2424
25+ /** Has the cursor been installed in a workspace's marker manager? */
26+ private installed = false ;
27+
28+ /** Old Cursor instance, saved during installation. */
29+ private oldCursor : Blockly . Cursor | null = null ;
30+
2531 /**
26- * Constructor for a line cursor.
32+ * @param workspace The workspace this cursor belongs to .
2733 */
28- constructor ( ) {
34+ constructor ( public readonly workspace : Blockly . WorkspaceSvg ) {
2935 super ( ) ;
36+ // Bind selectListener to facilitate future install/uninstall.
37+ this . selectListener = this . selectListener . bind ( this ) ;
38+ }
39+
40+ /**
41+ * Install this LineCursor in its workspace's marker manager and set
42+ * up the select listener. The original cursor (if any) is saved
43+ * for future use by .uninstall(), and its location is used to set
44+ * this one's.
45+ */
46+ install ( ) {
47+ if ( this . installed ) throw new Error ( 'LineCursor already installed' ) ;
48+ const markerManager = this . workspace . getMarkerManager ( ) ;
49+ this . oldCursor = markerManager . getCursor ( ) ;
50+ markerManager . setCursor ( this ) ;
51+ if ( this . oldCursor ) this . setCurNode ( this . oldCursor . getCurNode ( ) ) ;
52+ this . workspace . addChangeListener ( this . selectListener ) ;
53+ this . installed = true ;
54+ }
55+
56+ /**
57+ * Remove the select listener and uninstall this LineCursor from its
58+ * workspace's marker manager, restoring any previously-existing
59+ * cursor. Does not attempt to adjust original cursor's location.
60+ */
61+ uninstall ( ) {
62+ if ( ! this . installed ) throw new Error ( 'LineCursor not yet installed' ) ;
63+ this . workspace . removeChangeListener ( this . selectListener . bind ( this ) ) ;
64+ if ( this . oldCursor ) {
65+ this . workspace . getMarkerManager ( ) . setCursor ( this . oldCursor ) ;
66+ }
67+ this . installed = false ;
3068 }
3169
3270 /**
@@ -410,25 +448,24 @@ export class LineCursor extends Marker {
410448 * if so, update the cursor location (and any highlighting) to
411449 * match.
412450 *
413- * This works reasonably well but has some glitches, most notably
414- * that if the cursor is not on a block (e.g. it is on a connection
415- * or the workspace) then it will remain visible in its previous
416- * location until a cursor key is pressed.
451+ * Doing this only when getCurNode would naturally be called works
452+ * reasonably well but has some glitches, most notably that if the
453+ * cursor was not on a block (e.g. it was on a connection or the
454+ * workspace) when the user selected a block then it will remain
455+ * visible in its previous location until some keyboard navigation occurs.
456+ *
457+ * To ameliorate this, the LineCursor constructor adds an event
458+ * listener that calls getCurNode in response to SELECTED events.
417459 *
418- * TODO(#97): Remove this hack once Blockly is modified to update
419- * the cursor/focus itself.
460+ * Remove this hack once Blockly is modified to update the
461+ * cursor/focus itself.
420462 *
421463 * @returns The current field, connection, or block the cursor is on.
422464 */
423465 override getCurNode ( ) : ASTNode {
424466 const curNode = super . getCurNode ( ) ;
425467 const selected = Blockly . common . getSelected ( ) ;
426- if (
427- ( selected ?. workspace as Blockly . WorkspaceSvg )
428- ?. getMarkerManager ( )
429- . getCursor ( ) !== this
430- )
431- return curNode ;
468+ if ( selected ?. workspace !== this . workspace ) return curNode ;
432469
433470 // Selected item is on workspace that this cursor belongs to.
434471 const curLocation = curNode ?. getLocation ( ) ;
@@ -511,6 +548,17 @@ export class LineCursor extends Marker {
511548
512549 drawer . draw ( oldNode , newNode ) ;
513550 }
551+
552+ /**
553+ * Event listener that syncs the cursor location to the selected
554+ * block on SELECTED events.
555+ */
556+ private selectListener ( event : Blockly . Events . Abstract ) {
557+ if ( event . type !== Blockly . Events . SELECTED ) return ;
558+ const selectedEvent = event as Blockly . Events . Selected ;
559+ if ( selectedEvent . workspaceId !== this . workspace . id ) return ;
560+ this . getCurNode ( ) ;
561+ }
514562}
515563
516564export const registrationName = 'LineCursor' ;
@@ -521,18 +569,3 @@ Blockly.registry.register(registrationType, registrationName, LineCursor);
521569export const pluginInfo = {
522570 [ registrationType . toString ( ) ] : registrationName ,
523571} ;
524-
525- /**
526- * Install this cursor on the marker manager in the same position as
527- * the previous cursor.
528- *
529- * @param markerManager The currently active marker manager.
530- */
531- export function installCursor ( markerManager : Blockly . MarkerManager ) {
532- const oldCurNode = markerManager . getCursor ( ) ?. getCurNode ( ) ;
533- const lineCursor = new LineCursor ( ) ;
534- markerManager . setCursor ( lineCursor ) ;
535- if ( oldCurNode ) {
536- markerManager . getCursor ( ) ?. setCurNode ( oldCurNode ) ;
537- }
538- }
0 commit comments