Skip to content

Commit

Permalink
frontend(ViewPort): undo/redo part 2 (it mostly works now?)
Browse files Browse the repository at this point in the history
  • Loading branch information
JackDotJS committed Jul 30, 2024
1 parent a3e2c9f commit cf58b46
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 71 deletions.
34 changes: 26 additions & 8 deletions src/renderer/src/components/viewport/ViewPort.module.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.viewport {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
Expand All @@ -23,6 +24,7 @@
backdrop-filter: invert();
mask-image: radial-gradient(closest-side, #000 calc(100% - 1.5px), #FFF 100%);
mask-mode: luminance;
z-index: 9999;
}

.brushCursor:not(.cursorVisible) {
Expand All @@ -32,24 +34,40 @@
.canvasWrapper {
/* cursor: none; */
display: flex;
flex-direction: row;
position: relative;
justify-content: center;
align-items: center;
gap: 1rem;
}

.canvas, .preCanvas, .debugCanvas {
.canvasWrapper:before {
content: '';
pointer-events: none;
display: block;
position: absolute;
width: 100%;
height: 100%;
border: 1px solid rgba(0,0,0,0.25);
}

.canvas, .preCanvas {
user-select: none;
}

.canvas {
position: relative;
touch-action: none;
border: 1px solid black;
box-shadow: rgba(0,0,0,0.25) 3px 3px 6px;
background-image: repeating-conic-gradient(lightgray 0% 25%, white 0% 50%);
background-size: 20px 20px;
background-image: repeating-conic-gradient(#EEE 0% 25%, white 0% 50%);
background-size: 16px 16px;
}

.preCanvas {
border: 4px solid orange;
touch-action: none;
pointer-events: none;
position: absolute;
}

.debugCanvas {
border: 4px solid teal;
.historyDebugger > canvas {
border: 1px solid orange;
}
179 changes: 119 additions & 60 deletions src/renderer/src/components/viewport/ViewPort.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import style from './ViewPort.module.css';
import { subscribeEvent } from '@renderer/state/GlobalEventEmitter';

interface HistoryItem {
data: ImageData,
beforeData: ImageData,
afterData: ImageData,
x: number,
y: number
}
Expand All @@ -15,24 +16,24 @@ const ViewPort = (): JSXElement => {
const [ drawing, setDrawing ] = createSignal(false);
const [ brushColor, setBrushColor ] = createSignal(`#000000`);
const [ eraserMode, setEraserMode ] = createSignal(false);
const [ historyStep, setHistoryStep ] = createSignal(0);
const [ historyStep, setHistoryStep ] = createSignal(-1);
const [ history, setHistory ] = createSignal<HistoryItem[]>([]);

let lastPosX = 0;
let lastPosY = 0;
let lastSize = brushSize();

let repeaterMode = `reverse`;

const bbox = {
top: 0,
left: 0,
right: 0,
bottom: 0
};

const history: HistoryItem[] = [];

let canvasElem!: HTMLCanvasElement;
let preCanvasElem!: HTMLCanvasElement;
let debugCanvasElem!: HTMLCanvasElement;

let cursorElem!: HTMLDivElement;
let canvasWrapperElem!: HTMLDivElement;
Expand All @@ -51,8 +52,6 @@ const ViewPort = (): JSXElement => {
canvasElem.style.width = valAsNumber + `px`;
preCanvasElem.width = valAsNumber;
preCanvasElem.style.width = valAsNumber + `px`;
canvasElem.width = valAsNumber;
canvasElem.style.width = valAsNumber + `px`;
};

const setHeight = (value: string): void => {
Expand All @@ -61,8 +60,6 @@ const ViewPort = (): JSXElement => {
canvasElem.style.height = valAsNumber + `px`;
preCanvasElem.height = valAsNumber;
preCanvasElem.style.height = valAsNumber + `px`;
canvasElem.height = valAsNumber;
canvasElem.style.height = valAsNumber + `px`;
};

const updateCursor = (ev: PointerEvent): void => {
Expand Down Expand Up @@ -155,9 +152,8 @@ const ViewPort = (): JSXElement => {

const ctxMain = canvasElem.getContext(`2d`);
const ctxPre = preCanvasElem.getContext(`2d`);
const ctxDebug = debugCanvasElem.getContext(`2d`);

if (ctxMain == null || ctxPre == null || ctxDebug == null) return;
if (ctxMain == null || ctxPre == null) return;

// clamp bounding box to canvas bounds
bbox.top = Math.min(canvasElem.height, Math.max(bbox.top, 0));
Expand All @@ -177,83 +173,122 @@ const ViewPort = (): JSXElement => {

ctxMain.drawImage(preCanvasElem, 0, 0);

// const afterData = ctxMain.getImageData(
// bbox.left,
// bbox.top,
// bboxWidth,
// bboxHeight
// );
const afterData = ctxMain.getImageData(
bbox.left,
bbox.top,
bboxWidth,
bboxHeight
);

if (bboxWidth !== 0 && bboxHeight !== 0) {
ctxDebug.clearRect(0, 0, debugCanvasElem.width, debugCanvasElem.height);
// ctxDebug.clearRect(0, 0, debugCanvasElem.width, debugCanvasElem.height);

addHistoryStep(beforeData, bbox.left, bbox.top);
addHistoryStep(beforeData, afterData, bbox.left, bbox.top);
// history.push({
// data: afterData,
// x: bbox.left,
// y: bbox.top
// });

ctxDebug.putImageData(beforeData, 0, 0);

ctxDebug.beginPath();
ctxDebug.lineWidth = 1;
ctxDebug.strokeStyle = `#FF0000`;
ctxDebug.rect(
0,
0,
bboxWidth,
bboxHeight
);
ctxDebug.stroke();
// ctxDebug.putImageData(beforeData, 0, 0);

// ctxDebug.beginPath();
// ctxDebug.lineWidth = 1;
// ctxDebug.strokeStyle = `#FF0000`;
// ctxDebug.rect(
// 0,
// 0,
// bboxWidth,
// bboxHeight
// );
// ctxDebug.stroke();
}

ctxPre.clearRect(0, 0, preCanvasElem.width, preCanvasElem.height);
};

const addHistoryStep = (data: ImageData, x: number, y: number): void => {
if (history.length > (historyStep() + 1)) {
console.debug(`overwriting history`);
history.splice(historyStep() + 1);
const addHistoryStep = (
beforeData: ImageData,
afterData: ImageData,
x: number,
y: number
): void => {
if (historyStep() !== -1) {
// console.debug(history().length, historyStep());

if (history().length > (historyStep()) && repeaterMode === `reverse`) {
console.debug(`overwriting history`);
setHistory((old) => {
old.splice(historyStep());
return old;
});
} else {
setHistoryStep((old) => old + 1);
}
} else {
setHistoryStep((old) => old + 1);
}

setHistoryStep((old) => old + 1);
repeaterMode = `forward`;

history.push({ data, x, y });
setHistory((old) => {
return [...old, { beforeData, afterData, x, y }];
});
};

onMount(() => {
setWidth(`600`);
setHeight(`400`);
widthElem.value = canvasElem.width.toString();
heightElem.value = canvasElem.height.toString();

subscribeEvent(`generic.undo`, null, () => {
// if ((historyStep() - 1) < 0) return;

setHistoryStep((old) => old - 1);
if ((historyStep() - 1) < 0 && repeaterMode === `reverse`) {
console.debug(`reached end of undo history`);
return;
}

if (repeaterMode === `reverse`) {
setHistoryStep((old) => old - 1);
}

repeaterMode = `reverse`;

const newData = history[historyStep()];
const newData = history()[historyStep()];

console.debug(historyStep(), newData);

const ctx = canvasElem.getContext(`2d`);

if (ctx != null) {
ctx.putImageData(newData.data, newData.x, newData.y);
ctx.putImageData(newData.beforeData, newData.x, newData.y);
}
});

subscribeEvent(`generic.redo`, null, () => {
// if ((historyStep() + 1) === history.length) return;

setHistoryStep((old) => old + 1);
if ((historyStep() + 1) === history().length && repeaterMode === `forward`) {
console.debug(`reached end of redo history`);
return;
}

if (repeaterMode === `forward`) {
setHistoryStep((old) => old + 1);
}

repeaterMode = `forward`;

const newData = history[historyStep()];
const newData = history()[historyStep()];

console.debug(historyStep(), newData);

const ctx = canvasElem.getContext(`2d`);

if (ctx != null) {
ctx.putImageData(newData.data, newData.x, newData.y);
ctx.putImageData(newData.afterData, newData.x, newData.y);
}
});

preCanvasElem.addEventListener(`pointerdown`, startDrawing);
canvasElem.addEventListener(`pointerdown`, startDrawing);

window.addEventListener(`pointermove`, (ev) => {
Expand Down Expand Up @@ -307,27 +342,51 @@ const ViewPort = (): JSXElement => {
classList={{ [style.cursorVisible]: cursorVisible() }}
ref={cursorElem}
/>
<canvas
width={600}
height={400}
class={style.preCanvas}
ref={preCanvasElem}
/>
<canvas
width={600}
height={400}
<canvas
class={style.canvas}
onPointerEnter={() => setCursorVisible(true)}
onPointerLeave={() => setCursorVisible(false)}
ref={canvasElem}
/>
<canvas
width={600}
height={400}
class={style.debugCanvas}
ref={debugCanvasElem}
class={style.preCanvas}
ref={preCanvasElem}
/>
</div>
{/* <div class={style.historyDebugger}>
<For each={history()}>
{(item) => {
const newCanvasElem1 = document.createElement(`canvas`);
const newCanvasElem2 = document.createElement(`canvas`);
newCanvasElem1.classList.add(`before`);
newCanvasElem2.classList.add(`after`);
newCanvasElem1.width = item.beforeData.width;
newCanvasElem1.height = item.beforeData.height;
newCanvasElem2.width = item.afterData.width;
newCanvasElem2.height = item.afterData.height;
const ctx1 = newCanvasElem1.getContext(`2d`);
const ctx2 = newCanvasElem2.getContext(`2d`);
if (ctx1 != null) {
ctx1.putImageData(item.beforeData, 0, 0);
}
if (ctx2 != null) {
ctx2.putImageData(item.afterData, 0, 0);
}
return (
<>
{newCanvasElem1}
{newCanvasElem2}
</>
);
}}
</For>
</div> */}
</div>
);
};
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/src/util/keyComboListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ const emitKeyCombo = (newKeyCombo = currentKeyCombo): void => {

//console.debug(`new keyCombo`, keycombo, listeners);

console.debug(`options open state: `, state.optionsOpen);
// console.debug(`options open state: `, state.optionsOpen);

if (!state.optionsOpen) {
console.debug(config.keymap, newKeyCombo);
// console.debug(config.keymap, newKeyCombo);
for (const kmItem of config.keymap) {
if (!kmItem.enabled) continue;
// bunch of checks to see if both arrays have the same
Expand Down Expand Up @@ -99,7 +99,7 @@ window.addEventListener(`click`, (ev: MouseEvent) => {
if (ev.target == null) return;
currentTarget = ev.target as Element;

console.debug(`new keycombo target:`, currentTarget);
// console.debug(`new keycombo target:`, currentTarget);
});

window.addEventListener(`contextmenu`, (ev: MouseEvent) => {
Expand Down

0 comments on commit cf58b46

Please sign in to comment.