add click handler

This commit is contained in:
sfja 2026-05-18 16:17:10 +02:00
parent 60e589e689
commit a889c03929
5 changed files with 72 additions and 12 deletions

View File

@ -13,7 +13,7 @@ function App(): ReactElement {
<h1>nandsim</h1> <h1>nandsim</h1>
<div className="Editor"> <div className="Editor">
<Toolbar editor={editor} canvasRef={canvasRef} /> <Toolbar editor={editor} canvasRef={canvasRef} />
<Canvas editor={editor} canvasRef={canvasRef} /> <Canvas editor={editor} canvasRef={canvasRef} width={800} height={800} />
</div> </div>
</> </>
); );

View File

@ -2,9 +2,9 @@ import { useEffect, type ReactElement, type RefObject } from "react";
import { type Editor } from "./editor/Editor"; import { type Editor } from "./editor/Editor";
import { v2 } from "./editor/V2"; import { v2 } from "./editor/V2";
type Props = { editor: Editor; canvasRef: RefObject<HTMLCanvasElement | null> }; type Props = { editor: Editor; canvasRef: RefObject<HTMLCanvasElement | null>, width: number, height: number };
function Canvas({ editor, canvasRef }: Props): ReactElement { function Canvas({ editor, canvasRef, width, height }: Props): ReactElement {
useEffect(() => { useEffect(() => {
if (!canvasRef.current) return; if (!canvasRef.current) return;
@ -16,9 +16,9 @@ function Canvas({ editor, canvasRef }: Props): ReactElement {
<div className="Canvas"> <div className="Canvas">
<canvas <canvas
ref={canvasRef} ref={canvasRef}
width={1000} width={width}
height={1000} height={height}
style={{ width: 1000, height: 1000, backgroundColor: "black" }} style={{ width, height, backgroundColor: "black" }}
tabIndex={0} tabIndex={0}
onMouseDown={(ev) => { onMouseDown={(ev) => {
const pos = v2(ev.nativeEvent.offsetX, ev.nativeEvent.offsetY); const pos = v2(ev.nativeEvent.offsetX, ev.nativeEvent.offsetY);

View File

@ -83,7 +83,7 @@ export class Board {
this.hoveredOverOutput?.[0] === comp && this.hoveredOverOutput?.[0] === comp &&
this.hoveredOverOutput[1] === i this.hoveredOverOutput[1] === i
) { ) {
c.strokeStyle = `#bbbbbb`; c.strokeStyle = `#eee`;
c.lineWidth = 2; c.lineWidth = 2;
c.beginPath(); c.beginPath();
c.arc(x + w, y + (i + 1) * pinSpace, 5, 0, Math.PI * 2); c.arc(x + w, y + (i + 1) * pinSpace, 5, 0, Math.PI * 2);
@ -135,6 +135,55 @@ export class Board {
} }
} }
} }
handleMouseClick(
pos: V2,
inputPinClicked: (comp: Component, i: number) => void,
outputPinClicked: (comp: Component, i: number) => void,
componentClicked: (comp: Component) => void,
): "handled" | "not handled" {
for (const comp of this.components) {
const {
pos: { x, y },
def: {
size: { x: w, y: h },
inputs,
outputs,
},
} = comp;
if (
!pointInsideRect(
pos,
comp.pos.sub(v2(5, 5)),
comp.def.size.add(v2(10, 10)),
)
) {
continue;
}
{
const pinSpace = h / (inputs.length + 1);
for (let i = 0; i < inputs.length; ++i) {
if (v2(x, y + (i + 1) * pinSpace).distance(pos) < 5) {
inputPinClicked(comp, i);
return "handled";
}
}
}
{
const pinSpace = h / (outputs.length + 1);
for (let i = 0; i < outputs.length; ++i) {
if (v2(x + w, y + (i + 1) * pinSpace).distance(pos) < 5) {
outputPinClicked(comp, i);
return "handled";
}
}
}
componentClicked(comp);
return "handled";
}
return "not handled";
}
} }
export class ComponentRepo { export class ComponentRepo {

View File

@ -23,9 +23,20 @@ export class Normal implements State {
} }
onMouseDown(pos: V2): void { onMouseDown(pos: V2): void {
if (
this.cx.board.handleMouseClick(
pos.sub(this.cx.offset),
(comp, i) => {},
(comp, i) => {},
(comp) => {},
) === "handled"
) {
return;
} else {
this.cx.addSelectionRect(pos); this.cx.addSelectionRect(pos);
this.cx.transitionTo(new Selecting(this.cx)); this.cx.transitionTo(new Selecting(this.cx));
} }
}
onMouseMove(_deltaPos: V2, pos: V2): void { onMouseMove(_deltaPos: V2, pos: V2): void {
this.cx.board.updateMouseHover(pos.sub(this.cx.offset)); this.cx.board.updateMouseHover(pos.sub(this.cx.offset));

View File

@ -1,17 +1,17 @@
import type { Cx, Tool } from "../Cx"; import type { Cx, Tool } from "../Cx";
import type { V2_ } from "../V2"; import type { V2 } from "../V2";
import type { State } from "../State"; import type { State } from "../State";
import { Normal } from "./Normal"; import { Normal } from "./Normal";
export class Selecting implements State { export class Selecting implements State {
constructor(private cx: Cx) {} constructor(private cx: Cx) {}
onMouseUp(_pos: V2_): void { onMouseUp(_pos: V2): void {
this.cx.removeSelectionRect(); this.cx.removeSelectionRect();
this.cx.transitionTo(new Normal(this.cx)); this.cx.transitionTo(new Normal(this.cx));
} }
onMouseMove(deltaPos: V2_): void { onMouseMove(deltaPos: V2): void {
this.cx.moveSelectionRect(deltaPos); this.cx.moveSelectionRect(deltaPos);
} }