feat!: Make everything ISelectable focusable (#9004)

* feat!: Make bubbles, comments, and icons focusable

* feat!: Make ISelectable and ICopyable focusable.

* feat: Consolidate selection calls.

Now everything is based on focus with selection only being used as a
proxy.

* feat: Invert responsibility for setSelected().

Now setSelected() is only for quasi-external use.

* feat: Push up shadow check to getters.

Needed new common-level helper.

* chore: Lint fixes.

* feat!: Allow IFocusableNode to disable focus.

* chore: post-merge lint fixes

* fix: Fix tests + text bubble focusing.

This fixed then regressed a circular dependency causing the node and
advanced compilation steps to fail. This investigation is ongoing.

* fix: Clean up & fix imports.

This ensures the node and advanced compilation test steps now pass.

* fix: Lint fixes + revert commented out logic.

* chore: Remove unnecessary cast.

Addresses reviewer comment.

* fix: Some issues and a bunch of clean-ups.

This addresses a bunch of review comments, and fixes selecting workspace
comments.

* chore: Lint fix.

* fix: Remove unnecessary shadow consideration.

* chore: Revert import.

* chore: Some doc updates & added a warn statement.
This commit is contained in:
Ben Henning
2025-05-09 08:16:14 -07:00
committed by GitHub
parent 92cad53cfe
commit 4074cee31b
34 changed files with 380 additions and 209 deletions

View File

@@ -14,7 +14,6 @@
*/
import {BlockSvg} from '../block_svg.js';
import * as common from '../common.js';
import {ConnectionType} from '../connection_type.js';
import {Field} from '../field.js';
import {FieldCheckbox} from '../field_checkbox.js';
@@ -561,12 +560,7 @@ export class LineCursor extends Marker {
* @returns The current field, connection, or block the cursor is on.
*/
override getCurNode(): INavigable<any> | null {
if (!this.updateCurNodeFromFocus()) {
// Fall back to selection if focus fails to sync. This can happen for
// non-focusable nodes or for cases when focus may not properly propagate
// (such as for mouse clicks).
this.updateCurNodeFromSelection();
}
this.updateCurNodeFromFocus();
return super.getCurNode();
}
@@ -593,60 +587,18 @@ export class LineCursor extends Marker {
}
}
/**
* Updates the current node to match the selection.
*
* Clears the current node if it's on a block but the selection is null.
* Sets the node to a block if selected for our workspace.
* For shadow blocks selections the parent is used by default (unless we're
* already on the shadow block via keyboard) as that's where the visual
* selection is.
*/
private updateCurNodeFromSelection() {
const curNode = super.getCurNode();
const selected = common.getSelected();
if (selected === null && curNode instanceof BlockSvg) {
this.setCurNode(null);
return;
}
if (selected?.workspace !== this.workspace) {
return;
}
if (selected instanceof BlockSvg) {
let block: BlockSvg | null = selected;
if (selected.isShadow()) {
// OK if the current node is on the parent OR the shadow block.
// The former happens for clicks, the latter for keyboard nav.
if (curNode && (curNode === block || curNode === block.getParent())) {
return;
}
block = block.getParent();
}
if (block) {
this.setCurNode(block);
}
}
}
/**
* Updates the current node to match what's currently focused.
*
* @returns Whether the current node has been set successfully from the
* current focused node.
*/
private updateCurNodeFromFocus(): boolean {
private updateCurNodeFromFocus() {
const focused = getFocusManager().getFocusedNode();
if (focused instanceof BlockSvg) {
const block: BlockSvg | null = focused;
if (block && block.workspace === this.workspace) {
this.setCurNode(block);
return true;
}
}
return false;
}
/**