diff --git a/extensions/extensions.json b/extensions/extensions.json index d8ed71e33d..f57e0c3c11 100644 --- a/extensions/extensions.json +++ b/extensions/extensions.json @@ -7,6 +7,7 @@ "files", "pointerlock", "cursor", + "legobrainbiker/MoreMouseControls", "runtime-options", "fetch", "text", diff --git a/extensions/legobrainbiker/MoreMouseControls.js b/extensions/legobrainbiker/MoreMouseControls.js new file mode 100644 index 0000000000..fc37564b1a --- /dev/null +++ b/extensions/legobrainbiker/MoreMouseControls.js @@ -0,0 +1,185 @@ +// Name: More Mouse Controls +// ID: legobrainbikerMoreMouseControls +// Description: Adds more options to detect properties of the cursor, including its position, clicking, and scrolling. +// By: legobrainbiker +// License: MPL-2.0 +(function (Scratch) { + "use strict"; + const iconURI = + "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIzNi4zOTI4MSIgaGVpZ2h0PSIzNS40MjY1OSIgdmlld0JveD0iMCwwLDM2LjM5MjgxLDM1LjQyNjU5Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTU2Ljg4NTQxLC0xNTcuNjM4NDcpIj48ZyBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCI+PHBhdGggZD0iTTE2Mi44NjU0NCwxNjcuNDYzMDljMCwtMi4wNDY3NCAxLjc0NjIsLTMuNzA1OTQgMy45MDAyNCwtMy43MDU5NGMyLjE1NDA0LDAgMy45MDAyNCwxLjY1OTIxIDMuOTAwMjQsMy43MDU5NGMwLDIuMDQ2NzQgLTEuNzQ2MiwzLjcwNTk1IC0zLjkwMDI0LDMuNzA1OTVjLTIuMTU0MDQsMCAtMy45MDAyNCwtMS42NTkyMSAtMy45MDAyNCwtMy43MDU5NXoiIGZpbGw9Im5vbmUiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0xNTguMTM1NDEsMTY3LjQ2MzA5YzAsLTQuNzM1NjMgMy44NjM5LC04LjU3NDYyIDguNjMwMjcsLTguNTc0NjJjNC43NjYzNywwIDguNjMwMjcsMy44Mzg5OSA4LjYzMDI3LDguNTc0NjJjMCw0LjczNTYzIC0zLjg2MzksOC41NzQ2MiAtOC42MzAyNyw4LjU3NDYyYy00Ljc2NjM3LDAgLTguNjMwMjcsLTMuODM4OTkgLTguNjMwMjcsLTguNTc0NjJ6IiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVjYXA9ImJ1dHQiLz48cGF0aCBkPSJNMTY3LjE0OTM5LDE3MC45MDY1YzEuNzMxMTMsMy41NzM5NSA3LjA5NTEzLDE0LjY0ODAxIDkuODg1MzksMjAuNDA4NTVjMS4wNDI2OSwyLjE1MjY1IDIuMzMzMzgsMS4xMzM2NiAyLjc1MjMyLC0wLjU0MjA4YzAuNDY3NjksLTEuODcwNzUgMS41NTMyMywtNi4yMTI5MiAyLjE0OTI5LC04LjU5NzE0YzAuMjQ3MDcsLTAuOTg4MjggMS4yMjc3NCwtMS45Mzk5OCAyLjAwNjEzLC0yLjIyNTM5YzEuMTc1NDYsLTAuNDMxIDQuNjg3NzgsLTEuNzE4ODUgNy41ODU3MSwtMi43ODE0MmMyLjMzMSwtMC44NTQ3IDIuMzU5MTIsLTIuMTQzNTMgLTAuNDQ3MjksLTIuOTk3NjVjLTYuNTM1MzMsLTEuOTg5MDEgLTE3Ljk1NzAzLC01LjQ2NTE4IC0yMS41ODI1MiwtNi41Njg1OWMtMi4xNzUxNCwtMC42NjIgLTMuNDAzNTEsMS4xMjY3MyAtMi4zNDkwMywzLjMwMzczeiIgZmlsbD0iI2EzYTNhMyIgc3Ryb2tlLXdpZHRoPSIzLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPjwvZz48L2c+PC9zdmc+"; + class moreMouseControls { + constructor() { + this.mouseX = 0; + this.mouseY = 0; + this.mouseWheelDelta = 0; + this.mouseWheelDeltaX = 0; + this.mouseZoomDelta = 0; + this.buttons = []; + this.contextMenuDissabled = false; + document.addEventListener("contextmenu", (e) => { + if (this.contextMenuDissabled) { + e.preventDefault(); + } + }); + this.scrollDissabled = false; + document.addEventListener( + "wheel", + (e) => { + if (this.scrollDissabled) { + e.preventDefault(); + } + if (e.ctrlKey) { + this.mouseZoomDelta = e.deltaY; + this.mouseWheelDelta = 0; + this.mouseWheelDeltaX = 0; + } else { + this.mouseZoomDelta = 0; + this.mouseWheelDelta = e.deltaY; + this.mouseWheelDeltaX = e.deltaX; + } + Scratch.vm.runtime.startHats( + "legobrainbikerMoreMouseControls_onScroll" + ); + }, + { passive: false } + ); + document.addEventListener("mouseup", (e) => { + this.buttons[e.button] = false; + }); + document.addEventListener("mousedown", (e) => { + this.buttons[e.button] = true; + }); + document.addEventListener("mousemove", (e) => { + this.mouseX = e.clientX; + this.mouseY = e.clientY; + }); + document.addEventListener("mouseleave", (e) => { + this.mouseX = NaN; + this.mouseY = NaN; + // this could be problomatic for some projects. it's not great to add another edge case + }); + } + getInfo() { + return { + id: "legobrainbikerMoreMouseControls", + name: Scratch.translate("More Mouse Controls"), + menuIconURI: iconURI, + blockIconURI: iconURI, + color1: "#99bbbb", + color2: "#88bbbb", + color3: "#88bbbb", + blocks: [ + { + opcode: "onMouseUp", + blockType: Scratch.BlockType.HAT, + text: Scratch.translate("when mouse button [button] is released"), + isEdgeActivated: true, + arguments: { + button: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "0", + }, + }, + }, + { + opcode: "onMouseDown", + blockType: Scratch.BlockType.HAT, + text: Scratch.translate("when mouse button [button] is pressed"), + isEdgeActivated: true, + arguments: { + button: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "0", + }, + }, + }, + { + opcode: "buttonPressed", + blockType: Scratch.BlockType.BOOLEAN, + text: Scratch.translate("mouse button [button] is pressed"), + arguments: { + button: { + type: Scratch.ArgumentType.NUMBER, + defaultValue: "0", + }, + }, + }, + { + opcode: "toggleContextMenu", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("toggle context menu"), + }, + { + opcode: "toggleScroll", + blockType: Scratch.BlockType.COMMAND, + text: Scratch.translate("toggle scrolling and zooming"), + }, + { + opcode: "onScroll", + blockType: Scratch.BlockType.EVENT, + text: Scratch.translate("when scrolling"), + isEdgeActivated: false, + }, + { + opcode: "scrollAmount", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("scroll amount"), + }, + { + opcode: "horozontalScrollAmount", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("horozontal scroll amount"), + }, + { + opcode: "zoomAmount", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("zoom amount"), + }, + { + opcode: "mouseGlobalX", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("mouse global x position"), + }, + { + opcode: "mouseGlobalY", + blockType: Scratch.BlockType.REPORTER, + text: Scratch.translate("mouse global y position"), + }, + ], + }; + } + onMouseUp(args) { + return !this.buttonPressed(args); + } + onMouseDown(args) { + return this.buttonPressed(args); + } + buttonPressed({ button }) { + return this.buttons[Scratch.Cast.toNumber(button)] || false; + } + toggleContextMenu() { + this.contextMenuDissabled = !this.contextMenuDissabled; + } + toggleScroll() { + this.scrollDissabled = !this.scrollDissabled; + } + scrollAmount() { + return -this.mouseWheelDelta; + } + horozontalScrollAmount() { + return this.mouseWheelDeltaX; + } + zoomAmount() { + return -this.mouseZoomDelta; + } + mouseGlobalX() { + const canvas = Scratch.renderer.canvas.getBoundingClientRect(); + return this.mouseX - canvas.x - canvas.width / 2; + } + mouseGlobalY() { + const canvas = Scratch.renderer.canvas.getBoundingClientRect(); + return -(this.mouseY - canvas.y - canvas.height / 2); + } + } + Scratch.extensions.register(new moreMouseControls()); +})(Scratch); diff --git a/images/legobrainbiker/MoreMouseControls.svg b/images/legobrainbiker/MoreMouseControls.svg new file mode 100644 index 0000000000..0d46c87595 --- /dev/null +++ b/images/legobrainbiker/MoreMouseControls.svg @@ -0,0 +1 @@ + \ No newline at end of file