From b248a016a9cce97567f5d069a98911af14ecb765 Mon Sep 17 00:00:00 2001 From: dylan <> Date: Mon, 8 May 2023 23:14:01 -0700 Subject: [PATCH] Make mouse behavior better --- codetab.ts | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- mouse.ts | 12 ++++++---- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/codetab.ts b/codetab.ts index 2bb2a16..daaaf7c 100644 --- a/codetab.ts +++ b/codetab.ts @@ -177,6 +177,7 @@ const transformForPaste = (text: string) => { } const state = { + doubleClickTimer: 0, history: [] as Array<{code: string, anchor: number, focus: number}>, historyDebounce: 0, historyIndex: 0, @@ -222,6 +223,7 @@ const state = { } this.historyDebounce = historyDebounceFrames; }, + wordMode: false, scrollX: 0, scrollY: 0, anchor: 0, @@ -240,6 +242,34 @@ const state = { clampInRange(n: number) { return Math.max(0, Math.min(n, this.code.length)) }, + findNearestWordBoundaryLeft(index: number) { + if (index === this.code.length-1) { + return index; + } + const words1 = this.code.slice(0, index+1).split(/\b/g); + if (words1[words1.length-1].length === 1) { + return index; + } + const words = this.code.slice(0, index).split(/\b/g); + if (!words.length) { + return 0; + } + return index-words[words.length-1].length; + }, + findNearestWordBoundaryRight(index: number) { + if (index === 0) { + return index; + } + const words1 = this.code.slice(index-1).split(/\b/g); + if (words1[0].length === 1) { + return index; + } + const words = this.code.slice(index).split(/\b/g); + if (!words.length) { + return this.code.length; + } + return index+words[0].length; + }, setSelection(anchor: number | {x: number, y: number}, focus?: number | {x: number, y: number}) { if (typeof anchor !== "number") { anchor = gridToIndex(this.code, anchor.x, anchor.y); @@ -248,14 +278,36 @@ const state = { if (typeof focus !== "number") { focus = gridToIndex(this.code, focus.x, focus.y); } - this.anchor = this.clampInRange(anchor), + this.anchor = this.clampInRange(anchor); this.focus = this.clampInRange(focus); + if (this.wordMode) { + console.log('word mode', this.anchor, this.focus, this.findNearestWordBoundaryLeft(this.anchor), this.findNearestWordBoundaryRight(this.focus)); + if (this.anchor <= this.focus) { + this.anchor = this.findNearestWordBoundaryLeft(this.anchor); + this.focus = this.findNearestWordBoundaryRight(this.focus); + } else { + this.anchor = this.findNearestWordBoundaryRight(this.anchor); + this.focus = this.findNearestWordBoundaryLeft(this.focus); + } + } + this.anchor = this.clampInRange(this.anchor); + this.focus = this.clampInRange(this.focus); }, setFocus(focus: number | {x: number, y: number}) { if (typeof focus !== "number") { focus = gridToIndex(this.code, focus.x, focus.y); } this.focus = this.clampInRange(focus); + if (this.wordMode) { + if (this.anchor <= this.focus) { + this.anchor = this.findNearestWordBoundaryLeft(this.anchor); + this.focus = this.findNearestWordBoundaryRight(this.focus); + } else { + this.anchor = this.findNearestWordBoundaryRight(this.anchor); + this.focus = this.findNearestWordBoundaryLeft(this.focus); + } + } + this.focus = this.clampInRange(this.focus); }, insertText(text: string) { const {code, anchor, focus} = this; @@ -453,13 +505,23 @@ const update = async () => { state.snapshot(); } } + if (state.doubleClickTimer > 0) { + state.doubleClickTimer -= 1; + } - if (mouseDown()) { + if (mouseDown() && !shiftKeyDown()) { + if (state.doubleClickTimer > 0) { + state.wordMode = true; + } else { + state.doubleClickTimer = 10; + } const {x, y} = mousePos(); state.setSelection(pixelToIndex(state.code, x, y-8)); } else if (mouseHeld()) { const {x, y} = mousePos(); state.setFocus(pixelToIndex(state.code, x, y-8)); + } else { + state.wordMode = false; } const keyboardString = getKeyboardString(); diff --git a/mouse.ts b/mouse.ts index fa58888..c45841a 100644 --- a/mouse.ts +++ b/mouse.ts @@ -14,7 +14,7 @@ const mouseButtonsDown = { [M.MIDDLE]: false, }; const mouseEvents: Array<{ - type: "click" | "down" | "up" | "move", + type: "click" | "down" | "up" | "move" | "dblclick", button: typeof M[keyof typeof M], x: number, y: number, @@ -35,9 +35,9 @@ const eventPixelCoords = (evt: WindowMouseEvent) => { } } -// addEventListener("dblclick", (evt) => { -// console.log("dblclick", evt.button, evt.clientX, evt.clientY); -// }); +addEventListener("dblclick", (evt) => { + mouseEvents.push({type: "dblclick", button: evt.button, ...eventPixelCoords(evt)}); +}); addEventListener("click", (evt) => { mouseEvents.push({type: "click", button: evt.button, ...eventPixelCoords(evt)}); @@ -87,6 +87,10 @@ export const mouseClick = (button: number = M.LEFT) => { return mouseEvents.some(ev => ev.button === button && ev.type === "click"); } +export const mouseDoubleClick = (button: number = M.LEFT) => { + return mouseEvents.some(ev => ev.button === button && ev.type === "dblclick"); +} + export const mouseHeld = (button: number = M.LEFT) => { return mouseButtonsDown[button]; } \ No newline at end of file