|
1 | 1 | export default async function ({ addon, console, msg }) { |
| 2 | + const Blockly = await addon.tab.traps.getBlockly(); |
2 | 3 | const vm = addon.tab.traps.vm; |
3 | 4 |
|
| 5 | + let counterElement; |
| 6 | + |
4 | 7 | const getBlockCount = () => { |
5 | | - let blockCount = 0; |
6 | | - let scriptCount = 0; |
7 | | - let sprites = new Set(vm.runtime.targets.map((i) => i.sprite.blocks._blocks)); |
8 | | - sprites.forEach((sprite, i) => { |
9 | | - scriptCount += Object.values(sprite).filter((o) => !o.parent).length; // Filter blocks that don't have a parent (meaning it's the top of a stack) |
10 | | - blockCount += Object.values(sprite).filter((o) => !o.shadow).length; // shadow blocks should be filtered out |
| 8 | + let allBlockCount = 0; // number of blocks in project |
| 9 | + let thisBlockCount = 0; // number of blocks in this sprite |
| 10 | + |
| 11 | + const targetBlocks = vm.runtime.targets.map((target) => { |
| 12 | + return [ |
| 13 | + target.id, |
| 14 | + Object.values(target.blocks._blocks) |
| 15 | + .filter((b) => !b.shadow).length // shadow blocks should be filtered out |
| 16 | + ]; |
11 | 17 | }); |
12 | | - return { |
13 | | - blockCount, |
14 | | - scriptCount, |
15 | | - spriteCount: sprites.size - 1, // Backdrop counts as a target so we can subtract it |
16 | | - }; |
| 18 | + |
| 19 | + // project block count |
| 20 | + for (const info of targetBlocks) allBlockCount += info[1]; |
| 21 | + |
| 22 | + // this sprite's block count |
| 23 | + const thisTargetID = vm.editingTarget?.id; |
| 24 | + const thisWS = targetBlocks.find((i) => i[0] === thisTargetID); |
| 25 | + if (thisWS) thisBlockCount += thisWS[1]; |
| 26 | + |
| 27 | + if (thisBlockCount === allBlockCount) return allBlockCount; |
| 28 | + return `${thisBlockCount} / ${allBlockCount}`; |
17 | 29 | }; |
18 | 30 |
|
19 | | - const addLiveBlockCount = async () => { |
20 | | - if (vm.editingTarget) { |
21 | | - let handler = null; |
22 | | - while (true) { |
23 | | - const topBar = await addon.tab.waitForElement("[class^='menu-bar_main-menu']", { |
24 | | - markAsSeen: true, |
25 | | - reduxEvents: [ |
26 | | - "scratch-gui/mode/SET_PLAYER", |
27 | | - "fontsLoaded/SET_FONTS_LOADED", |
28 | | - "scratch-gui/locales/SELECT_LOCALE", |
29 | | - ], |
30 | | - reduxCondition: (state) => !state.scratchGui.mode.isPlayerOnly, |
31 | | - }); |
32 | | - let display = topBar.appendChild(document.createElement("span")); |
33 | | - addon.tab.displayNoneWhileDisabled(display); |
34 | | - display.style.order = 1; |
35 | | - display.style.padding = "9px"; |
36 | | - display.innerText = msg("blocks", { num: getBlockCount().blockCount }); |
37 | | - let debounce; // debouncing values because of the way 'PROJECT_CHANGED' works |
38 | | - if (handler) { |
39 | | - vm.off("PROJECT_CHANGED", handler); |
40 | | - vm.runtime.off("PROJECT_LOADED", handler); |
| 31 | + const addCounter = () => { |
| 32 | + ReduxStore.subscribe(() => { |
| 33 | + if (!counterElement) { |
| 34 | + // init counter |
| 35 | + const topBar = document.querySelector("div[class^='menu-bar_main-menu']"); |
| 36 | + if (!topBar) return; |
| 37 | + |
| 38 | + counterElement = topBar.appendChild(document.createElement("span")); |
| 39 | + counterElement.style.order = 1; |
| 40 | + counterElement.style.padding = "9px"; |
| 41 | + |
| 42 | + addLiveBlockCount(); |
| 43 | + } else { |
| 44 | + // hide display if not in editor |
| 45 | + const state = ReduxStore.getState().scratchGui; |
| 46 | + counterElement.style.display = state.mode.isPlayerOnly ? "none" : ""; |
| 47 | + } |
| 48 | + }); |
| 49 | + } |
| 50 | + |
| 51 | + const addLiveBlockCount = () => { |
| 52 | + let lastWorkspaceID; |
| 53 | + let lastUpdateTime = 0; |
| 54 | + vm.on("workspaceUpdate", () => { |
| 55 | + const workspace = Blockly.mainWorkspace; |
| 56 | + const events = Blockly.Events; |
| 57 | + |
| 58 | + const blocklyHandler = (event) => { |
| 59 | + const now = Date.now(); |
| 60 | + if ( |
| 61 | + counterElement && |
| 62 | + now > lastUpdateTime + 1000 && // dont update the count multiple times in a second |
| 63 | + (event.type === events.DELETE || event.type === events.CREATE) |
| 64 | + ) { |
| 65 | + lasUpdateTime = now; |
| 66 | + counterElement.innerText = msg("blocks", { num: getBlockCount() }); |
41 | 67 | } |
42 | | - handler = async () => { |
43 | | - clearTimeout(debounce); |
44 | | - debounce = setTimeout(async () => { |
45 | | - display.innerText = msg("blocks", { num: getBlockCount().blockCount }); |
46 | | - }, 1000); |
47 | | - }; |
48 | | - vm.on("PROJECT_CHANGED", handler); |
49 | | - vm.runtime.on("PROJECT_LOADED", handler); |
| 68 | + }; |
| 69 | + |
| 70 | + if (lastWorkspaceID !== workspace.id) { |
| 71 | + workspace.addChangeListener(blocklyHandler); |
| 72 | + lastWorkspaceID = workspace.id; |
50 | 73 | } |
51 | | - } else { |
52 | | - let timeout = setTimeout(function () { |
53 | | - addLiveBlockCount(); |
54 | | - clearTimeout(timeout); |
55 | | - }, 1000); |
56 | | - } |
| 74 | + }); |
57 | 75 | }; |
58 | 76 |
|
59 | | - addLiveBlockCount(); |
| 77 | + addCounter(); |
60 | 78 | } |
0 commit comments