-
Notifications
You must be signed in to change notification settings - Fork 411
New addon: flag-menu
#8561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
New addon: flag-menu
#8561
Conversation
mxmou
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This addon should be enabled by default - otherwise touchscreen users can't use 60fps and mute-project at all without enabling it first.
After the React 18 update, it will be possible to navigate Scratch's context menus using arrow keys. It would be nice if the addon replicated that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A notice explaining how to access 60 FPS mode on a touch screen would be helpful. Same with mute-project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That addon already has so much notice text. 🤣
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add this information to the description instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Platform-specific descriptions, which I suggested in #3930 (comment), would also be useful here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to add "touchscreenOnly": true" to notices within this PR it's fine to me.
Perhaps enabled by default only for mobile versions of the extension? That's too much work, though. I'll just make it enabled by default.
Ideally, I would make the context menu an instance of a React context menu so that React can take care of this. Not sure how, though. May just have to stick with custom handlers. We don't use React within our project, do we? (We probably should.) |
|
Okay, I got all those kinks worked out! |
Writing the event handlers is probably easier, especially because we currently need to support both versions of React. That's what I did when updating the context menu API. After the React 18 update is out, we can experiment with using Scratch's context menu component. Speaking of React 18: if you don't mind, I'll push some changes to make the addon's styling work there. |
Sure, you know more about it than I do. |
addons/flag-menu/userscript.js
Outdated
| let greenFlag; | ||
|
|
||
| const contextMenuClass = | ||
| addon.tab.scratchClass("context-menu_context-menu") || addon.tab.scratchClass("context-menu_context-menu-content"); // React 16 || React 18 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scratch updated React to 18 with the Face Sensing update, so this can be simplified. The style of the context menu was also changed, so some things may need to be tweaked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, Scratch's new context menu has no transition for fading in/out, it just appears immediately, plus there's arrow-key navigation and the width was reduced. Every other property applies automatically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scratch updated React to 18 with the Face Sensing update, so this can be simplified.
Done. I've also removed the opacity transition.
The React 18 update has been released, so we no longer need to support React 16.
I don't know if Scratch uses this anymore, but I know how to program arrow-key navigation myself, though. |
Still working on an implementation that skips over invisible menu items, but here's what I have now. I think it's alright. export default async function ({ addon, console, msg }) {
let greenFlag;
+ let contextMenuOpen = false;
const contextMenu = Object.assign(document.createElement("nav"), {
role: "menu",
className: addon.tab.scratchClass("context-menu_context-menu-content", { others: "sa-flag-context-menu" }),
});
+ const setHighlightedItem = (menuitem) => {
+ const currentItem = contextMenu.querySelector("[data-highlighted]");
+ if (currentItem) {
+ currentItem.removeAttribute("data-highlighted");
+ currentItem.tabIndex = -1;
+ }
+ if (menuitem) {
+ menuitem.setAttribute("data-highlighted", "");
+ menuitem.tabIndex = 0;
+ menuitem.focus();
+ }
+ }
const createItem = (id, textContent = "") => {
const item = Object.assign(document.createElement("div"), {
role: "menuitem",
className: addon.tab.scratchClass("context-menu_menu-item"),
id,
textContent,
});
- item.addEventListener("mouseenter", () => item.setAttribute("data-highlighted", ""));
- item.addEventListener("mouseleave", () => item.removeAttribute("data-highlighted"));
+ item.addEventListener("mouseenter", () => setHighlightedItem(item));
+ item.addEventListener("mouseleave", () => setHighlightedItem());
return item;
};
contextMenu.append(
createItem("sa-flag-menu-turbo", msg("turbo-on")),
createItem("sa-flag-menu-fps"),
createItem("sa-flag-menu-mute")
);
function closeContextMenu() {
+ contextMenuOpen = false;
+ setHighlightedItem();
contextMenu.classList.remove("sa-flag-menu-open");
}
document.addEventListener("mousedown", (e) => {
if (!e.target.closest("[role='menu']")) closeContextMenu();
});
document.addEventListener("keydown", (e) => {
- if (e.key === "Escape") closeContextMenu();
+ if (!contextMenuOpen) return;
+ switch (e.key) {
+ case "Escape":
+ case "Tab":
+ closeContextMenu();
+ break;
+ case "Enter":
+ case " ":
+ if (contextMenu.querySelector("[data-highlighted]")) contextMenu.querySelector("[data-highlighted]").click();
+ break;
+ case "ArrowUp":
+ const previousItem = contextMenu.querySelector("[data-highlighted]")?.previousElementSibling ?? contextMenu.lastElementChild;
+ setHighlightedItem(previousItem, true);
+ break;
+ case "ArrowDown":
+ const nextItem = contextMenu.querySelector("[data-highlighted]")?.nextElementSibling ?? contextMenu.firstElementChild;
+ setHighlightedItem(nextItem, true);
+ break;
+ case "Home":
+ case "PageUp":
+ setHighlightedItem(contextMenu.firstElementChild, true);
+ break;
+ case "End":
+ case "PageDown":
+ setHighlightedItem(contextMenu.lastElementChild, true);
+ break;
+ }
});
contextMenu.querySelector("#sa-flag-menu-turbo").addEventListener("click", () => {
closeContextMenu();
greenFlag.dispatchEvent(new MouseEvent("click", { bubbles: true, shiftKey: true }));
});
contextMenu.querySelector("#sa-flag-menu-fps").addEventListener("click", () => {
closeContextMenu();
greenFlag.dispatchEvent(new MouseEvent("click", { bubbles: true, altKey: true }));
});
contextMenu.querySelector("#sa-flag-menu-mute").addEventListener("click", () => {
closeContextMenu();
greenFlag.dispatchEvent(new MouseEvent("click", { bubbles: true, ctrlKey: true }));
});
contextMenu.style.visibility = "hidden"; // Setting visibility here fixes a visual glitch on dynamic enable
addon.tab.displayNoneWhileDisabled(contextMenu);
addon.self.addEventListener("disabled", closeContextMenu);
addon.tab.traps.vm.on("TURBO_MODE_ON", () => {
contextMenu.querySelector("#sa-flag-menu-turbo").textContent = msg("turbo-off");
});
addon.tab.traps.vm.on("TURBO_MODE_OFF", () => {
contextMenu.querySelector("#sa-flag-menu-turbo").textContent = msg("turbo-on");
});
while (true) {
greenFlag = await addon.tab.waitForElement("[class^='green-flag_green-flag']", {
markAsSeen: true,
reduxEvents: ["scratch-gui/mode/SET_PLAYER", "fontsLoaded/SET_FONTS_LOADED", "scratch-gui/locales/SELECT_LOCALE"],
});
greenFlag.parentElement.appendChild(contextMenu);
greenFlag.addEventListener("contextmenu", (e) => {
if (addon.self.disabled) return;
e.preventDefault();
+ contextMenuOpen = true;
contextMenu.classList.add("sa-flag-menu-open");
contextMenu.style.left = e.clientX + "px";
contextMenu.style.top = e.clientY + "px";
});
}
} |
Resolves #7230
Changes
It looks like this:

*Context menu items (other than toggle turbo mode) only appear if their respective addons are enabled.
Tests
Tested on Edge 140 with a mouse and with DevTools Device Emulation.
To do:
touchstartinstead ofcontextmenueditor-dark-modecompatibilityeditor-compactcompatibility