Skip to content

Conversation

@RoboErikG
Copy link
Contributor

@RoboErikG RoboErikG commented Jun 16, 2025

Fixes #585

This updates the enter handler to do more selective checks when deciding whether or not to handle an enter key. Also simplifies getState() slightly to remove an unnecessary parameter.

In general, this is aiming to reach parity between mouse and keyboard behavior when on a read-only workspace. Specific changes to behavior:

  • Enables creating a workspace cursor ('w' key) while in read-only mode.
  • Enables enter key to "click" on icons while in read-only mode.
  • Enabled navigating to the toolbox while in read-only mode.
  • Factors out most of the checks for whether or not an enter will be handled to the precondition.
  • Returns false from the precondition and callback more often when the shortcut isn't actually handled.

TODO for RaspberryPiFoundation/blockly#8915
Add additional tests for enter and click with a read-only workspace and ensure both modalities are consistent.

@RoboErikG RoboErikG marked this pull request as ready for review June 17, 2025 19:42
@RoboErikG RoboErikG requested a review from a team as a code owner June 17, 2025 19:56
@RoboErikG RoboErikG requested review from maribethb and removed request for a team June 17, 2025 19:56
this.oldCutShortcut.callback(workspace, e, shortcut, scope);
if (didCut) {
this.copyWorkspace = workspace;
this.copyWorkspace = workspace.isFlyout
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just checking, is this change supposed to be here? it doesn't seem related to the PR title

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, not sure how this slipped in.

case Constants.STATE.WORKSPACE:
// The main workspace may or may not handle it depending on what's
// selected, so always pass it through to the callback.
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The precondition should really try to give an accurate answer. The keyboard shortcut help display could be depending on the precondition being accurate. Rachel envisions a contextual help where only keyboard shortcuts that would currently do anything are shown.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some cases there's no API to ask if it would be allowed and some of them we don't even know if it did anything. For example, the Icon.onClick() method doesn't have a return and there's no isClickable() method, but it only opens the mutator if the workspace is editable.

Because of that, it didn't seem like it was worth making the precondition overly complex if we're going to have to handle it anyway, but I'll factor out the logic to check all the known cases.

flyoutCursor = this.navigation.getFlyoutCursor(
workspace.targetWorkspace,
);
if (targetWorkspace.isReadOnly()) return false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to check this again since you're already checking it in the precondition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. Thanks for catching!

} else if (curNode instanceof icons.Icon) {
curNode.onClick();
}
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite the comment in the precondition, it seems like now we're saying the keyboard shortcut for enter will always be handled regardless of whether the workspace is in read-only mode or not. This doesn't seem correct to me, e.g. the field editors shouldn't open if the workspace is in read-only mode.

Based on #585 and playing with the current mouse behavior for read-only mode, I think only icons should be special cased, and the rest of these should continue not doing anything.

It would be helpful if you can add in the PR descriptions which cases you intended to make interactable in read-only, as then I know if we're on the same page about the intentions which may be different than being on the same page about the actual result of the code (which I may be misreading, for example).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is we don't know if it was handled or not for all cases, specifically icons which have an on click() method that doesn't return anything. I've added additional return false statements where we do know.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that's fine for icons.

For connections and workspace, I don't think the enter key should do anything in read-only mode, but I think this does actually cause them to do something. I can't tell if you intended them to change behavior and we should discuss that intention, or if that is unintentional, or if I'm wrong and the behavior hasn't changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I'd misunderstood that path. Added a check to shouldHandle for this case.

for (const input of block.inputList) {
for (const field of input.fieldRow) {
if (field.isClickable() && field.isFullBlockField()) {
if (field.isFullBlockField()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove this check? if the field isn't clickable, it shouldn't be "enter"able either, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd removed it because field.showEditor() calls this.isClickable() before showing the editor so it had seemed redundant. That said, I'm adding it back so that this method returns whether or not it handled the return correctly.

if (!curNode) return false;
if (curNode instanceof Field && !curNode.isClickable()) return false;
// Returning true is sometimes incorrect for icons, but there's no API to check.
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still going to be a behavior change for pressing enter on other cases, if I'm reading this correctly. If the workspace is read-only then pressing enter shouldn't open the toolbox. It doesn't before this change but I think this change will cause it to open because of this precondition + you're not checking it in the callback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that was intentional to make it consistent with mouse which still opens the toolbox on click.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err, ignore my previous comment. It's intentional that pressing 't' opens the toolbox on a read only workspace. I've updated it so pressing enter on the ws or a connection won't since that's an insert action.

} else if (curNode instanceof icons.Icon) {
curNode.onClick();
}
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that's fine for icons.

For connections and workspace, I don't think the enter key should do anything in read-only mode, but I think this does actually cause them to do something. I can't tell if you intended them to change behavior and we should discuss that intention, or if that is unintentional, or if I'm wrong and the behavior hasn't changed.

} else if (curNode instanceof icons.Icon) {
curNode.onClick();
}
return true;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, you shouldn't return true as the default, because if the current node isn't an instance of any of these things (e.g. custom focusable object, or workspace comment), you shouldn't say we've handled the shortcut because we haven't. Those custom objects could register their own shortcut handler for enter and if you return true that could cause theirs to not be executed.

I realize this is the old behavior, but I think that's a bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to return false by default and true for each case we handle.

Copy link
Collaborator

@maribethb maribethb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, but it would be helpful if you can make the pr description more informative for future reference when reading the changelog, e.g.:

  • Enables creating a workspace cursor ('w' key) while in readonly mode
  • Enables enter key to "click" on icons while in readonly mode
  • Returns false from the keyboard shortcut callback more often when the shortcut isn't actually handled

@RoboErikG RoboErikG merged commit fd1e4fb into RaspberryPiFoundation:main Jun 18, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blockly icons (e.g., comments) cannot be opened via keyboard in readonly mode

2 participants