From c1050025e41dd456c822301555b1b3a20577d03e Mon Sep 17 00:00:00 2001 From: Erik Pasternak Date: Thu, 20 Feb 2025 15:05:04 -0800 Subject: [PATCH 1/4] fix: #2463 update event handling for field-bitmap to support touch This includes debug logging. A followup commit will remove the debugging. --- plugins/field-bitmap/src/field-bitmap.ts | 81 ++++++++++++++++++------ 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/plugins/field-bitmap/src/field-bitmap.ts b/plugins/field-bitmap/src/field-bitmap.ts index 70b4fea421..6e340f416d 100644 --- a/plugins/field-bitmap/src/field-bitmap.ts +++ b/plugins/field-bitmap/src/field-bitmap.ts @@ -41,7 +41,7 @@ export class FieldBitmap extends Blockly.Field { private valToPaintWith?: number; buttonOptions: Buttons; pixelSize: number; - pixelColours: {empty: string; filled: string}; + pixelColours: { empty: string; filled: string }; fieldHeight?: number; /** @@ -60,8 +60,8 @@ export class FieldBitmap extends Blockly.Field { this.SERIALIZABLE = true; this.CURSOR = 'default'; - this.buttonOptions = {...DEFAULT_BUTTONS, ...config?.buttons}; - this.pixelColours = {...DEFAULT_PIXEL_COLOURS, ...config?.colours}; + this.buttonOptions = { ...DEFAULT_BUTTONS, ...config?.buttons }; + this.pixelColours = { ...DEFAULT_PIXEL_COLOURS, ...config?.colours }; // Configure value, height, and width const currentValue = this.getValue(); @@ -292,11 +292,15 @@ export class FieldBitmap extends Blockly.Field { // This prevents the normal max-height from adding a scroll bar for large images. Blockly.DropDownDiv.getContentDiv().classList.add('contains-bitmap-editor'); - this.bindEvent(dropdownEditor, 'mouseup', this.onMouseUp); - this.bindEvent(dropdownEditor, 'mouseleave', this.onMouseUp); - this.bindEvent(dropdownEditor, 'dragstart', (e: Event) => { - e.preventDefault(); - }); + this.bindEvent(dropdownEditor, 'pointermove', this.onPointerMove); + this.bindEvent(dropdownEditor, 'pointerup', this.onPointerUp); + this.bindEvent(dropdownEditor, 'pointerleave', this.onPointerUp); + this.bindEvent(dropdownEditor, 'pointerdown', this.onPointerDown); + this.bindEvent(dropdownEditor, 'pointercancel', this.onPointerUp); + // Stop the browser from intercepting touch events and cancelling the event + this.bindEvent(dropdownEditor, 'touchmove', (e: Event) => { + e.preventDefault(); + }); this.editorPixels = []; for (let r = 0; r < this.imgHeight; r++) { @@ -314,16 +318,23 @@ export class FieldBitmap extends Blockly.Field { ? this.pixelColours.filled : this.pixelColours.empty; - // Handle clicking a pixel - this.bindEvent(button, 'mousedown', () => { - this.onMouseDownInPixel(r, c); - return true; - }); + // Set the custom data attributes for row and column indices + button.setAttribute('data-row', r.toString()); + button.setAttribute('data-col', c.toString()); + + // // Handle clicking a pixel + // this.bindEvent(button, 'mousedown', () => { + // console.log("handling pointer down") + // this.onMouseDownInPixel(r, c); + // return true; + // }); + + // // Handle dragging into a pixel when mouse is down + // this.bindEvent(button, 'mouseenter', () => { + // console.log("handling pointer enter"); + // this.onMouseEnterPixel(r, c); + // }); - // Handle dragging into a pixel when mouse is down - this.bindEvent(button, 'mouseenter', () => { - this.onMouseEnterPixel(r, c); - }); } pixelContainer.appendChild(rowDiv); } @@ -472,6 +483,35 @@ export class FieldBitmap extends Blockly.Field { return newVal; } + /** + * + * @param e + */ + private onPointerDown(e: PointerEvent) { + const currentElement = document.elementFromPoint(e.clientX, e.clientY); + const rowIndex = currentElement?.getAttribute('data-row'); + const colIndex = currentElement?.getAttribute('data-col'); + console.log("pointer down on " + rowIndex + ", " + colIndex + " element " + currentElement); + if (rowIndex && colIndex) { + this.onMouseDownInPixel(parseInt(rowIndex), parseInt(colIndex)); + } + } + + /** + * + * @param e + */ + private onPointerMove(e: PointerEvent) { + const currentElement = document.elementFromPoint(e.clientX, e.clientY); + const rowIndex = currentElement?.getAttribute('data-row'); + const colIndex = currentElement?.getAttribute('data-col'); + console.log("pointer moving on " + rowIndex + ", " + colIndex); + if (rowIndex && colIndex) { + this.onMouseEnterPixel(parseInt(rowIndex), parseInt(colIndex)); + } + e.preventDefault(); + } + /** * Called when a mousedown event occurs within the bounds of a pixel. * @@ -508,7 +548,8 @@ export class FieldBitmap extends Blockly.Field { * Resets mouse state (e.g. After either a mouseup event or if the mouse * leaves the editor area). */ - private onMouseUp() { + private onPointerUp(e: PointerEvent) { + console.log("onMouseUp " + e.pointerType); this.mouseIsDown = false; this.valToPaintWith = undefined; } @@ -594,10 +635,10 @@ export class FieldBitmap extends Blockly.Field { private bindEvent( element: HTMLElement, eventName: string, - callback: (e: Event) => void, + callback: (e: PointerEvent) => void, ) { this.boundEvents.push( - Blockly.browserEvents.conditionalBind(element, eventName, this, callback), + Blockly.browserEvents.bind(element, eventName, this, callback), ); } From bef02193bc1febd435f64c6a40efc4bf60e37dbf Mon Sep 17 00:00:00 2001 From: Erik Pasternak Date: Fri, 21 Feb 2025 09:49:36 -0800 Subject: [PATCH 2/4] Cleanup of pointer changes to the bitmap field --- plugins/field-bitmap/src/field-bitmap.ts | 65 ++++++++++-------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/plugins/field-bitmap/src/field-bitmap.ts b/plugins/field-bitmap/src/field-bitmap.ts index 6e340f416d..a0a87e5138 100644 --- a/plugins/field-bitmap/src/field-bitmap.ts +++ b/plugins/field-bitmap/src/field-bitmap.ts @@ -37,7 +37,7 @@ export class FieldBitmap extends Blockly.Field { private editorPixels: HTMLElement[][] | null = null; private blockDisplayPixels: SVGElement[][] | null = null; /** Stateful variables */ - private mouseIsDown = false; + private pointerIsDown = false; private valToPaintWith?: number; buttonOptions: Buttons; pixelSize: number; @@ -293,14 +293,15 @@ export class FieldBitmap extends Blockly.Field { Blockly.DropDownDiv.getContentDiv().classList.add('contains-bitmap-editor'); this.bindEvent(dropdownEditor, 'pointermove', this.onPointerMove); - this.bindEvent(dropdownEditor, 'pointerup', this.onPointerUp); - this.bindEvent(dropdownEditor, 'pointerleave', this.onPointerUp); - this.bindEvent(dropdownEditor, 'pointerdown', this.onPointerDown); - this.bindEvent(dropdownEditor, 'pointercancel', this.onPointerUp); - // Stop the browser from intercepting touch events and cancelling the event + this.bindEvent(dropdownEditor, 'pointerup', this.onPointerEnd); + this.bindEvent(dropdownEditor, 'pointerleave', this.onPointerEnd); + this.bindEvent(dropdownEditor, 'pointerdown', this.onPointerStart); + this.bindEvent(dropdownEditor, 'pointercancel', this.onPointerEnd); + // Stop the browser from handling touch events and cancelling the event. this.bindEvent(dropdownEditor, 'touchmove', (e: Event) => { e.preventDefault(); - }); + }); + this.editorPixels = []; for (let r = 0; r < this.imgHeight; r++) { @@ -321,20 +322,6 @@ export class FieldBitmap extends Blockly.Field { // Set the custom data attributes for row and column indices button.setAttribute('data-row', r.toString()); button.setAttribute('data-col', c.toString()); - - // // Handle clicking a pixel - // this.bindEvent(button, 'mousedown', () => { - // console.log("handling pointer down") - // this.onMouseDownInPixel(r, c); - // return true; - // }); - - // // Handle dragging into a pixel when mouse is down - // this.bindEvent(button, 'mouseenter', () => { - // console.log("handling pointer enter"); - // this.onMouseEnterPixel(r, c); - // }); - } pixelContainer.appendChild(rowDiv); } @@ -487,13 +474,14 @@ export class FieldBitmap extends Blockly.Field { * * @param e */ - private onPointerDown(e: PointerEvent) { + private onPointerStart(e: PointerEvent) { const currentElement = document.elementFromPoint(e.clientX, e.clientY); const rowIndex = currentElement?.getAttribute('data-row'); const colIndex = currentElement?.getAttribute('data-col'); - console.log("pointer down on " + rowIndex + ", " + colIndex + " element " + currentElement); if (rowIndex && colIndex) { - this.onMouseDownInPixel(parseInt(rowIndex), parseInt(colIndex)); + this.onPointerDownInPixel(parseInt(rowIndex), parseInt(colIndex)); + this.pointerIsDown = true; + e.preventDefault(); } } @@ -502,40 +490,40 @@ export class FieldBitmap extends Blockly.Field { * @param e */ private onPointerMove(e: PointerEvent) { + if (!this.pointerIsDown) { + return; + } const currentElement = document.elementFromPoint(e.clientX, e.clientY); const rowIndex = currentElement?.getAttribute('data-row'); const colIndex = currentElement?.getAttribute('data-col'); - console.log("pointer moving on " + rowIndex + ", " + colIndex); if (rowIndex && colIndex) { - this.onMouseEnterPixel(parseInt(rowIndex), parseInt(colIndex)); + this.updatePixelValue(parseInt(rowIndex), parseInt(colIndex)); } e.preventDefault(); } /** - * Called when a mousedown event occurs within the bounds of a pixel. + * Starts an interaction with the bitmap dropdown when there's a pointerdown + * within one of the pixels in the editor. * * @param r Row number of grid. * @param c Column number of grid. */ - private onMouseDownInPixel(r: number, c: number) { + private onPointerDownInPixel(r: number, c: number) { // Toggle that pixel to the opposite of its value const newPixelValue = 1 - this.getPixel(r, c); this.setPixel(r, c, newPixelValue); - this.mouseIsDown = true; + this.pointerIsDown = true; this.valToPaintWith = newPixelValue; } /** - * Called when the mouse drags over a pixel in the editor. + * Sets the specified pixel in the editor to the current value being painted. * * @param r Row number of grid. * @param c Column number of grid. */ - private onMouseEnterPixel(r: number, c: number) { - if (!this.mouseIsDown) { - return; - } + private updatePixelValue(r: number, c: number) { if ( this.valToPaintWith !== undefined && this.getPixel(r, c) !== this.valToPaintWith @@ -545,12 +533,11 @@ export class FieldBitmap extends Blockly.Field { } /** - * Resets mouse state (e.g. After either a mouseup event or if the mouse - * leaves the editor area). + * Resets pointer state (e.g. After either a pointerup event or if the + * gesture is canceled). */ - private onPointerUp(e: PointerEvent) { - console.log("onMouseUp " + e.pointerType); - this.mouseIsDown = false; + private onPointerEnd(e: PointerEvent) { + this.pointerIsDown = false; this.valToPaintWith = undefined; } From 54b66efbae4655d454eb4579d6651bc36813c7f8 Mon Sep 17 00:00:00 2001 From: Erik Pasternak Date: Fri, 21 Feb 2025 10:01:33 -0800 Subject: [PATCH 3/4] clean up comments --- plugins/field-bitmap/src/field-bitmap.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/field-bitmap/src/field-bitmap.ts b/plugins/field-bitmap/src/field-bitmap.ts index a0a87e5138..28ba15df8f 100644 --- a/plugins/field-bitmap/src/field-bitmap.ts +++ b/plugins/field-bitmap/src/field-bitmap.ts @@ -471,8 +471,10 @@ export class FieldBitmap extends Blockly.Field { } /** + * Checks if a down event is on a pixel in this editor and if it is starts an + * edit gesture. * - * @param e + * @param e The down event. */ private onPointerStart(e: PointerEvent) { const currentElement = document.elementFromPoint(e.clientX, e.clientY); @@ -486,8 +488,10 @@ export class FieldBitmap extends Blockly.Field { } /** + * Updates the editor if we're in an edit gesture and the pointer is over a + * pixel. * - * @param e + * @param e The move event. */ private onPointerMove(e: PointerEvent) { if (!this.pointerIsDown) { @@ -536,7 +540,7 @@ export class FieldBitmap extends Blockly.Field { * Resets pointer state (e.g. After either a pointerup event or if the * gesture is canceled). */ - private onPointerEnd(e: PointerEvent) { + private onPointerEnd() { this.pointerIsDown = false; this.valToPaintWith = undefined; } From 82d5a48fde202fc8163663de0c93f464d13f09bd Mon Sep 17 00:00:00 2001 From: Erik Pasternak Date: Fri, 21 Feb 2025 10:23:27 -0800 Subject: [PATCH 4/4] Prettier format cleanup --- plugins/field-bitmap/src/field-bitmap.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/field-bitmap/src/field-bitmap.ts b/plugins/field-bitmap/src/field-bitmap.ts index 28ba15df8f..e19156c66c 100644 --- a/plugins/field-bitmap/src/field-bitmap.ts +++ b/plugins/field-bitmap/src/field-bitmap.ts @@ -41,7 +41,7 @@ export class FieldBitmap extends Blockly.Field { private valToPaintWith?: number; buttonOptions: Buttons; pixelSize: number; - pixelColours: { empty: string; filled: string }; + pixelColours: {empty: string; filled: string}; fieldHeight?: number; /** @@ -60,8 +60,8 @@ export class FieldBitmap extends Blockly.Field { this.SERIALIZABLE = true; this.CURSOR = 'default'; - this.buttonOptions = { ...DEFAULT_BUTTONS, ...config?.buttons }; - this.pixelColours = { ...DEFAULT_PIXEL_COLOURS, ...config?.colours }; + this.buttonOptions = {...DEFAULT_BUTTONS, ...config?.buttons}; + this.pixelColours = {...DEFAULT_PIXEL_COLOURS, ...config?.colours}; // Configure value, height, and width const currentValue = this.getValue(); @@ -299,10 +299,9 @@ export class FieldBitmap extends Blockly.Field { this.bindEvent(dropdownEditor, 'pointercancel', this.onPointerEnd); // Stop the browser from handling touch events and cancelling the event. this.bindEvent(dropdownEditor, 'touchmove', (e: Event) => { - e.preventDefault(); + e.preventDefault(); }); - this.editorPixels = []; for (let r = 0; r < this.imgHeight; r++) { this.editorPixels.push([]); @@ -473,7 +472,7 @@ export class FieldBitmap extends Blockly.Field { /** * Checks if a down event is on a pixel in this editor and if it is starts an * edit gesture. - * + * * @param e The down event. */ private onPointerStart(e: PointerEvent) { @@ -490,7 +489,7 @@ export class FieldBitmap extends Blockly.Field { /** * Updates the editor if we're in an edit gesture and the pointer is over a * pixel. - * + * * @param e The move event. */ private onPointerMove(e: PointerEvent) {