Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 19 additions & 5 deletions celstomp/celstomp-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@
const eraserSizeInput = $("eraserSize");
const toolOpacityRange = $("toolOpacityRange");
const toolAngleRange = $("toolAngleRange");
const toolOpacityRow = toolOpacityRange?.closest(".sideRangeRow") || null;
const toolAngleRow = toolAngleRange?.closest(".sideRangeRow") || null;
const brushFoldSection = toolFoldBrushesBtn?.closest(".toolFold") || null;
const eraserVal = $("eraserVal");


Expand Down Expand Up @@ -183,16 +186,25 @@
function refreshToolSettingsUI() {
const isBrush = tool === "brush";
const isEraser = tool === "eraser";
if (toolSettingsSection) toolSettingsSection.hidden = !(isBrush || isEraser);
if (!isBrush && !isEraser) return;
const isLine = tool === "line";
const isRect = tool === "rect";
const isShapeTool = isLine || isRect;
const showsBrushSettings = isBrush || isShapeTool;
if (toolSettingsSection) toolSettingsSection.hidden = !(showsBrushSettings || isEraser);
if (brushFoldSection) brushFoldSection.hidden = isShapeTool;
if (toolOpacityRow) toolOpacityRow.hidden = isShapeTool;
if (toolAngleRow) toolAngleRow.hidden = isShapeTool;
if (!showsBrushSettings && !isEraser) return;
const s = isEraser ? eraserSettings : brushSettings;
if (toolSettingsTitle) toolSettingsTitle.textContent = isEraser ? "Eraser" : "Brushes";
if (toolSettingsTitle) toolSettingsTitle.textContent = isEraser ? "Eraser" : isShapeTool ? "Shape Tool" : "Brushes";
safeSetValue(brushSizeInput, s.size);
safeSetValue(brushSizeNumInput, s.size);
safeSetValue(toolOpacityRange, Math.round(s.opacity * 100));
safeSetValue(toolAngleRange, s.angle);
const activeShape = document.querySelector('input[name="brushShape"][value="' + s.shape + '"]');
if (activeShape) activeShape.checked = true;
if (!isShapeTool) {
const activeShape = document.querySelector('input[name="brushShape"][value="' + s.shape + '"]');
if (activeShape) activeShape.checked = true;
}
}
function setFoldExpanded(btn, body, open) {
if (!btn || !body) return;
Expand Down Expand Up @@ -359,6 +371,7 @@
bctx.strokeRect(0, 0, contentW, contentH);
drawRectSelectionOverlay(fxctx);
drawLineToolPreview(fxctx);
drawRectToolPreview(fxctx);
}

function onionCompositeOperation() {
Expand Down Expand Up @@ -408,6 +421,7 @@
setTransform(fxctx);
drawRectSelectionOverlay(fxctx);
drawLineToolPreview(fxctx);
drawRectToolPreview(fxctx);
}

function wireBrushButtonRightClick() {
Expand Down
69 changes: 65 additions & 4 deletions celstomp/js/input/pointer-events.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ let brushSize = 3;
let autofill = false;

let trailPoints = [];
let rectToolStart = null;
let rectToolPreview = null;
let lineToolStart = null;
let lineToolPreview = null;

Expand Down Expand Up @@ -268,6 +270,18 @@ 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);
beginGlobalHistoryStep(activeLayer, currentFrame, strokeHex);
rectToolStart = { x, y };
rectToolPreview = { x, y };
return;
}
if (tool === "rect-select") {
isDrawing = true;
beginRectSelect(e);
Expand Down Expand Up @@ -347,6 +361,7 @@ function startStroke(e) {
activeSubColor[activeLayer] = strokeHex;
ensureSublayer(activeLayer, strokeHex);
renderLayerSwatches(activeLayer);
beginGlobalHistoryStep(activeLayer, currentFrame, strokeHex);
lineToolStart = { x, y };
lineToolPreview = { x, y };
return;
Expand Down Expand Up @@ -409,6 +424,11 @@ function continueStroke(e) {
x: x,
y: y
};
if (tool === "rect") {
rectToolPreview = { x, y };
queueRenderAll();
return;
}
if (tool === "rect-select") {
updateRectSelect(e);
lastPt = {
Expand Down Expand Up @@ -481,11 +501,34 @@ function continueStroke(e) {
function endStroke() {
if (!isDrawing) return;
isDrawing = false;
commitGlobalHistoryStep();
const endKey = strokeHex;
strokeHex = null;
queueRenderAll();
updateTimelineHasContent(currentFrame);
const finishingRect = tool === "rect" && rectToolStart && rectToolPreview;
const finishingLine = tool === "line" && lineToolStart && lineToolPreview;
if (!finishingRect && !finishingLine) {
commitGlobalHistoryStep();
}
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();
markFrameHasContent(activeLayer, currentFrame, hex);
markGlobalHistoryDirty();
commitGlobalHistoryStep();
rectToolStart = null;
rectToolPreview = null;
strokeHex = null;
queueRenderAll();
updateTimelineHasContent(currentFrame);
lastPt = null;
stabilizedPt = null;
return;
}
if (tool === "rect-select") {
endRectSelect();
lastPt = null;
Expand All @@ -505,12 +548,18 @@ function endStroke() {
ctx.lineTo(lineToolPreview.x, lineToolPreview.y);
ctx.stroke();
markFrameHasContent(activeLayer, currentFrame, hex);
markGlobalHistoryDirty();
commitGlobalHistoryStep();
lineToolStart = null;
lineToolPreview = null;
strokeHex = null;
queueRenderAll();
updateTimelineHasContent(currentFrame);
return;
}
strokeHex = null;
queueRenderAll();
updateTimelineHasContent(currentFrame);
if (tool === "lasso-erase" && lassoActive) {
lassoActive = false;
applyLassoErase();
Expand Down Expand Up @@ -1538,3 +1587,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);
ctx.globalAlpha = 0.5;
ctx.beginPath();
ctx.rect(rectToolStart.x, rectToolStart.y, rectToolPreview.x - rectToolStart.x, rectToolPreview.y - rectToolStart.y);
ctx.stroke();
ctx.restore();
}
4 changes: 2 additions & 2 deletions celstomp/js/tools/brush-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ function getBrushSizeForPreview(toolKind) {
function updateBrushPreview() {
if (!_brushPrevEl || !_brushPrevCanvas) return;
const toolKind = getActiveToolKindForPreview();
const showForTools = toolKind === "brush" || toolKind === "eraser" || toolKind === "line";
const showForTools = toolKind === "brush" || toolKind === "eraser" || toolKind === "line" || toolKind === "rect";
const isEraser = toolKind === "eraser";
if (!showForTools) {
_brushPrevEl.style.display = "none";
Expand Down Expand Up @@ -514,4 +514,4 @@ function openBrushCtxMenu(ev, anchorEl) {
function closeBrushCtxMenu() {
if (_brushCtxMenu) _brushCtxMenu.hidden = true;
_brushCtxState = null;
}
}
30 changes: 19 additions & 11 deletions celstomp/js/ui/interaction-shortcuts.js
Original file line number Diff line number Diff line change
Expand Up @@ -642,12 +642,13 @@ function wireKeyboardShortcuts() {
1: "brush",
2: "eraser",
3: "line",
4: "fill-brush",
5: "fill-eraser",
6: "lasso-fill",
7: "lasso-erase",
8: "rect-select",
9: "eyedropper"
4: "rect",
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;
Expand Down Expand Up @@ -739,42 +740,49 @@ function onWindowKeyDown(e) {
});
}
if (isDigit(4)) {
e.preventDefault();
pickTool({
id: "tool-rect",
value: "rect"
});
}
if (isDigit(5)) {
e.preventDefault();
pickTool({
id: "tool-fillbrush",
value: "fill-brush"
});
}
if (isDigit(5)) {
if (isDigit(6)) {
e.preventDefault();
pickTool({
id: "tool-filleraser",
value: "fill-eraser"
});
}
if (isDigit(6)) {
if (isDigit(7)) {
e.preventDefault();
pickTool({
id: "tool-lassoFill",
value: "lasso-fill"
});
}
if (isDigit(7)) {
if (isDigit(8)) {
e.preventDefault();
pickTool({
id: "tool-lassoErase",
altIds: [ "tool-lassoerase", "tool-lasso-erase" ],
value: "lasso-erase"
});
}
if (isDigit(8)) {
if (isDigit(9)) {
e.preventDefault();
pickTool({
id: "tool-rectSelect",
value: "rect-select"
});
}
if (isDigit(9)) {
if (isDigit(0)) {
e.preventDefault();
pickTool({
id: "tool-eyedropper",
Expand Down
1 change: 1 addition & 0 deletions celstomp/js/ui/ui-components.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{ id: 'tool-brush', val: 'brush', label: 'Brush', checked: true },
{ id: 'tool-eraser', val: 'eraser', label: 'Eraser' },
{ id: 'tool-line', val: 'line', label: 'Line', icon: '<svg viewBox="0 0 24 24" width="18" height="18"><line x1="4" y1="20" x2="20" y2="4" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"/></svg>' },
{ id: 'tool-rect', val: 'rect', label: 'Rect', icon: '<svg viewBox="0 0 24 24" width="18" height="18"><rect x="3" y="5" width="18" height="14" rx="2" fill="none" stroke="currentColor" stroke-width="2"/></svg>' },
{ 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' },
Expand Down
13 changes: 7 additions & 6 deletions celstomp/parts/modals.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ document.getElementById('part-modals').innerHTML = `
<div class="shortcutRow"><kbd>1</kbd><span>Brush</span></div>
<div class="shortcutRow"><kbd>2</kbd><span>Eraser</span></div>
<div class="shortcutRow"><kbd>3</kbd><span>Line</span></div>
<div class="shortcutRow"><kbd>4</kbd><span>Fill Brush</span></div>
<div class="shortcutRow"><kbd>5</kbd><span>Fill Eraser</span></div>
<div class="shortcutRow"><kbd>6</kbd><span>Lasso Fill</span></div>
<div class="shortcutRow"><kbd>7</kbd><span>Lasso Erase</span></div>
<div class="shortcutRow"><kbd>8</kbd><span>Rect Select</span></div>
<div class="shortcutRow"><kbd>9</kbd><span>Eyedropper</span></div>
<div class="shortcutRow"><kbd>4</kbd><span>Rect</span></div>
<div class="shortcutRow"><kbd>5</kbd><span>Fill Brush</span></div>
<div class="shortcutRow"><kbd>6</kbd><span>Fill Eraser</span></div>
<div class="shortcutRow"><kbd>7</kbd><span>Lasso Fill</span></div>
<div class="shortcutRow"><kbd>8</kbd><span>Lasso Erase</span></div>
<div class="shortcutRow"><kbd>9</kbd><span>Rect Select</span></div>
<div class="shortcutRow"><kbd>0</kbd><span>Eyedropper</span></div>
</div>
<div class="shortcutSection">
<h4>Navigation</h4>
Expand Down