From 4b40a316ad0f674daa0e442910872b1253fec8a0 Mon Sep 17 00:00:00 2001 From: Microck Date: Sat, 21 Feb 2026 16:48:58 +0000 Subject: [PATCH 1/3] feat: add rectangle drawing tool --- celstomp/celstomp-app.js | 4 +++ celstomp/css/components/tools.css | 16 +++++++++ celstomp/js/input/pointer-events.js | 48 +++++++++++++++++++++++++ celstomp/js/ui/interaction-shortcuts.js | 11 +++--- celstomp/js/ui/ui-components.js | 8 ++++- celstomp/parts/modals.js | 11 +++--- 6 files changed, 87 insertions(+), 11 deletions(-) diff --git a/celstomp/celstomp-app.js b/celstomp/celstomp-app.js index 3d924b6..8285eed 100644 --- a/celstomp/celstomp-app.js +++ b/celstomp/celstomp-app.js @@ -358,6 +358,7 @@ bctx.fillRect(0, 0, contentW, contentH); bctx.strokeRect(0, 0, contentW, contentH); drawRectSelectionOverlay(fxctx); + drawRectToolPreview(fxctx); } function onionCompositeOperation() { @@ -404,6 +405,9 @@ function clearFx() { fxctx.setTransform(1, 0, 0, 1, 0, 0); fxctx.clearRect(0, 0, fxCanvas.width, fxCanvas.height); + setTransform(fxctx); + drawRectSelectionOverlay(fxctx); + drawRectToolPreview(fxctx); } function wireBrushButtonRightClick() { diff --git a/celstomp/css/components/tools.css b/celstomp/css/components/tools.css index f8c57f3..11dd82b 100644 --- a/celstomp/css/components/tools.css +++ b/celstomp/css/components/tools.css @@ -126,6 +126,22 @@ cursor: pointer; } +#islandToolsSlot #toolSeg > label svg { + width: 20px; + height: 20px; + color: rgba(255,255,255,0.85); + position: relative; + z-index: 1; +} + +#islandToolsSlot #toolSeg > label:has(svg) { + background-image: none; +} + +#islandToolsSlot #toolSeg > label:has(svg)::before { + display: none; +} + #islandToolsSlot #toolSeg > label::before{ content: ""; width: 20px; diff --git a/celstomp/js/input/pointer-events.js b/celstomp/js/input/pointer-events.js index b0e5534..9199774 100644 --- a/celstomp/js/input/pointer-events.js +++ b/celstomp/js/input/pointer-events.js @@ -12,6 +12,8 @@ let brushSize = 3; let autofill = false; let trailPoints = []; +let rectToolStart = null; +let rectToolPreview = null; function pressure(e) { const pid = Number.isFinite(e?.pointerId) ? e.pointerId : -1; @@ -266,6 +268,17 @@ function startStroke(e) { pickCanvasColorAtEvent(e); return; } + if (tool === "rect") { + isDrawing = true; + const hex = colorToHex(currentColor); + strokeHex = activeLayer === LAYER.FILL ? fillWhite : hex; + activeSubColor[activeLayer] = strokeHex; + ensureSublayer(activeLayer, strokeHex); + renderLayerSwatches(activeLayer); + rectToolStart = { x, y }; + rectToolPreview = { x, y }; + return; + } if (tool === "rect-select") { isDrawing = true; beginRectSelect(e); @@ -396,6 +409,11 @@ function continueStroke(e) { x: x, y: y }; + if (tool === "rect") { + rectToolPreview = { x, y }; + queueRenderAll(); + return; + } if (tool === "rect-select") { updateRectSelect(e); lastPt = { @@ -468,6 +486,24 @@ function endStroke() { strokeHex = null; queueRenderAll(); updateTimelineHasContent(currentFrame); + if (tool === "rect" && rectToolStart && rectToolPreview) { + const hex = strokeHex || activeSubColor?.[activeLayer] || colorToHex(currentColor); + const off = getFrameCanvas(activeLayer, currentFrame, hex); + const ctx = off.getContext("2d"); + ctx.strokeStyle = hex; + ctx.lineWidth = Math.max(1, brushSize); + ctx.lineCap = "round"; + ctx.beginPath(); + ctx.rect(rectToolStart.x, rectToolStart.y, rectToolPreview.x - rectToolStart.x, rectToolPreview.y - rectToolStart.y); + ctx.stroke(); + off._hasContent = true; + rectToolStart = null; + rectToolPreview = null; + queueRenderAll(); + lastPt = null; + stabilizedPt = null; + return; + } if (tool === "rect-select") { endRectSelect(); lastPt = null; @@ -1487,3 +1523,15 @@ function fillFromLineart(F) { updateTimelineHasContent(F); return true; } + +function drawRectToolPreview(ctx) { + if (!rectToolStart || !rectToolPreview) return; + ctx.save(); + ctx.strokeStyle = colorToHex(currentColor); + ctx.lineWidth = Math.max(1, brushSize) / Math.max(getZoom(), 1); + ctx.setLineDash([4 / Math.max(getZoom(), 1), 2 / Math.max(getZoom(), 1)]); + ctx.beginPath(); + ctx.rect(rectToolStart.x, rectToolStart.y, rectToolPreview.x - rectToolStart.x, rectToolPreview.y - rectToolStart.y); + ctx.stroke(); + ctx.restore(); +} diff --git a/celstomp/js/ui/interaction-shortcuts.js b/celstomp/js/ui/interaction-shortcuts.js index 5359ed6..6b72e45 100644 --- a/celstomp/js/ui/interaction-shortcuts.js +++ b/celstomp/js/ui/interaction-shortcuts.js @@ -601,11 +601,12 @@ function wireKeyboardShortcuts() { 1: "brush", 2: "eraser", 3: "fill-brush", - 4: "fill-eraser", - 5: "lasso-fill", - 6: "lasso-erase", - 7: "rect-select", - 8: "eyedropper" + 4: "rect", + 5: "fill-eraser", + 6: "lasso-fill", + 7: "lasso-erase", + 8: "rect-select", + 9: "eyedropper" }; document.addEventListener("keydown", e => { if (e.defaultPrevented) return; diff --git a/celstomp/js/ui/ui-components.js b/celstomp/js/ui/ui-components.js index 3614e29..bc0e8b0 100644 --- a/celstomp/js/ui/ui-components.js +++ b/celstomp/js/ui/ui-components.js @@ -4,6 +4,7 @@ const tools = [ { id: 'tool-brush', val: 'brush', label: 'Brush', checked: true }, { id: 'tool-eraser', val: 'eraser', label: 'Eraser' }, + { id: 'tool-rect', val: 'rect', label: 'Rect', icon: '' }, { id: 'tool-fillbrush', val: 'fill-brush', label: 'Fill Brush' }, { id: 'tool-filleraser', val: 'fill-eraser', label: 'Eraser Fill' }, { id: 'tool-lassoFill', val: 'lasso-fill', label: 'Lasso Fill' }, @@ -27,7 +28,12 @@ const lbl = document.createElement('label'); lbl.htmlFor = t.id; lbl.dataset.tool = t.val; - lbl.textContent = t.label; + lbl.setAttribute('aria-label', t.label); + if (t.icon) { + lbl.innerHTML = t.icon; + } else { + lbl.textContent = t.label; + } if (t.val === 'brush') lbl.id = 'toolBrushLabel'; if (t.val === 'eraser') lbl.id = 'toolEraserLabel'; diff --git a/celstomp/parts/modals.js b/celstomp/parts/modals.js index 5351362..32999f8 100644 --- a/celstomp/parts/modals.js +++ b/celstomp/parts/modals.js @@ -53,11 +53,12 @@ document.getElementById('part-modals').innerHTML = `
1Brush
2Eraser
3Fill Brush
-
4Fill Eraser
-
5Lasso Fill
-
6Lasso Erase
-
7Rect Select
-
8Eyedropper
+
4Rect
+
5Fill Eraser
+
6Lasso Fill
+
7Lasso Erase
+
8Rect Select
+
9Eyedropper

Navigation

From 016e8e471e002cba7d6da83dd6d8919c806297eb Mon Sep 17 00:00:00 2001 From: Microck Date: Sat, 21 Feb 2026 16:59:55 +0000 Subject: [PATCH 2/3] feat: add rectangle drawing tool Includes keybindings for both line and rect tools to avoid merge conflicts with PR #52 --- celstomp/js/ui/interaction-shortcuts.js | 13 +++++++------ celstomp/parts/modals.js | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/celstomp/js/ui/interaction-shortcuts.js b/celstomp/js/ui/interaction-shortcuts.js index 6b72e45..3c9bc2d 100644 --- a/celstomp/js/ui/interaction-shortcuts.js +++ b/celstomp/js/ui/interaction-shortcuts.js @@ -600,13 +600,14 @@ function wireKeyboardShortcuts() { const toolByKey = { 1: "brush", 2: "eraser", - 3: "fill-brush", + 3: "line", 4: "rect", - 5: "fill-eraser", - 6: "lasso-fill", - 7: "lasso-erase", - 8: "rect-select", - 9: "eyedropper" + 5: "fill-brush", + 6: "fill-eraser", + 7: "lasso-fill", + 8: "lasso-erase", + 9: "rect-select", + 0: "eyedropper" }; document.addEventListener("keydown", e => { if (e.defaultPrevented) return; diff --git a/celstomp/parts/modals.js b/celstomp/parts/modals.js index 32999f8..4395994 100644 --- a/celstomp/parts/modals.js +++ b/celstomp/parts/modals.js @@ -52,13 +52,14 @@ document.getElementById('part-modals').innerHTML = `

Tools

1Brush
2Eraser
-
3Fill Brush
+
3Line
4Rect
-
5Fill Eraser
-
6Lasso Fill
-
7Lasso Erase
-
8Rect Select
-
9Eyedropper
+
5Fill Brush
+
6Fill Eraser
+
7Lasso Fill
+
8Lasso Erase
+
9Rect Select
+
0Eyedropper

Navigation

From bba6f2a5580c8d8d3a53360c3228103967774773 Mon Sep 17 00:00:00 2001 From: Microck Date: Sat, 21 Feb 2026 17:03:45 +0000 Subject: [PATCH 3/3] feat: add rectangle drawing tool --- celstomp/js/ui/interaction-shortcuts.js | 13 ++++++------- celstomp/parts/modals.js | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/celstomp/js/ui/interaction-shortcuts.js b/celstomp/js/ui/interaction-shortcuts.js index 3c9bc2d..6b72e45 100644 --- a/celstomp/js/ui/interaction-shortcuts.js +++ b/celstomp/js/ui/interaction-shortcuts.js @@ -600,14 +600,13 @@ function wireKeyboardShortcuts() { const toolByKey = { 1: "brush", 2: "eraser", - 3: "line", + 3: "fill-brush", 4: "rect", - 5: "fill-brush", - 6: "fill-eraser", - 7: "lasso-fill", - 8: "lasso-erase", - 9: "rect-select", - 0: "eyedropper" + 5: "fill-eraser", + 6: "lasso-fill", + 7: "lasso-erase", + 8: "rect-select", + 9: "eyedropper" }; document.addEventListener("keydown", e => { if (e.defaultPrevented) return; diff --git a/celstomp/parts/modals.js b/celstomp/parts/modals.js index 4395994..32999f8 100644 --- a/celstomp/parts/modals.js +++ b/celstomp/parts/modals.js @@ -52,14 +52,13 @@ document.getElementById('part-modals').innerHTML = `

Tools

1Brush
2Eraser
-
3Line
+
3Fill Brush
4Rect
-
5Fill Brush
-
6Fill Eraser
-
7Lasso Fill
-
8Lasso Erase
-
9Rect Select
-
0Eyedropper
+
5Fill Eraser
+
6Lasso Fill
+
7Lasso Erase
+
8Rect Select
+
9Eyedropper

Navigation