Skip to content
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

Get the monitor from the VM and set its contents directly (as opposed to using right click > Import) #1

Open
wants to merge 2 commits into
base: drag-drop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 13 additions & 41 deletions addons/drag-drop/userscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,55 +57,27 @@ export default async function ({ addon, global, console }) {
'div[class*="selector_wrapper"] input[class*="action-menu_file-input"]'
);

const {
traps: { vm },
redux,
} = addon.tab;
async function listMonitorsDroppable() {
while (true) {
const listMonitor = await addon.tab.waitForElement('div[class*="monitor_list-monitor"]', { markAsSeen: true });
const monitorName = listMonitor.querySelector('div[class*="monitor_list-header"]').textContent;
const monitor = redux.state.scratchGui.monitors.valueSeq().find((monitor) => {
if (!monitor.visible || monitor.opcode !== "data_listcontents") return false;
const label = monitor.spriteName ? `${monitor.spriteName}: ${monitor.params.LIST}` : monitor.params.LIST;
return label === monitorName;
});
const target = monitor.targetId ? vm.runtime.getTargetById(monitor.targetId) : vm.runtime.getTargetForStage();
const variable = target.variables[monitor.id];
const canDrop = () => {
// Don't show drop indicator if in fullscreen/player mode
return !listMonitor.closest('div[class*="stage_full-screen"], .guiPlayer');
};
const handleDrop = async (files) => {
const contextMenuBefore = document.querySelector("body > .react-contextmenu.react-contextmenu--visible");
// Simulate a right click on the list monitor
listMonitor.dispatchEvent(new MouseEvent("contextmenu", { bubbles: true }));
// Get the right click menu that opened (monitor context menus are
// children of <body>)
const contextMenuAfter = document.querySelector("body > .react-contextmenu.react-contextmenu--visible");
// `contextMenuAfter` is only null if the context menu was already open
// for the list monitor, in which case we can use the context menu from
// before the simulated right click
const contextMenu = contextMenuAfter === null ? contextMenuBefore : contextMenuAfter;
// Sometimes the menu flashes open, so force hide it.
contextMenu.style.display = "none";
// Override DOM methods to import the text file directly
// See: https://github.com/LLK/scratch-gui/blob/develop/src/lib/import-csv.js#L21-L22
const appendChild = document.body.appendChild;
document.body.appendChild = (fileInput) => {
// Restore appendChild to <body>
document.body.appendChild = appendChild;
if (fileInput instanceof HTMLInputElement) {
document.body.appendChild(fileInput);
// Prevent Scratch from opening the file input dialog
fileInput.click = () => {};
// Insert files from the drop event into the file input
fileInput.files = files;
fileInput.dispatchEvent(new Event("change"));
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
contextMenu.style.display = null;
contextMenu.style.opacity = 0;
contextMenu.style.pointerEvents = "none";
});
});
} else {
// The next call for `appendChild` SHOULD be the file input, but if
// it's not, then make `appendChild` behave as normal.
console.error('File input was not immediately given to appendChild upon clicking "Import"!');
return appendChild(fileInput);
}
};
// Simulate clicking on the "Import" option
contextMenu.children[0].click();
variable.value = (await Promise.all(Array.from(files, (file) => file.text()))).join("\n").split(/\r?\n/);
};
droppable(listMonitor, handleDrop, canDrop);
}
Expand Down