delegate handlers

This commit is contained in:
sfja 2025-05-07 17:13:43 +02:00
parent 505e869e1f
commit 6ead8c97e2
5 changed files with 127 additions and 9 deletions

View File

@ -26,6 +26,20 @@ export class CanvasRenderer implements Renderer {
g.fillRect(x, y, w, h); g.fillRect(x, y, w, h);
} }
strokeRect(
x: number,
y: number,
w: number,
h: number,
color: string,
lineWidth: number,
): void {
const { g } = this;
g.strokeStyle = color;
g.lineWidth = lineWidth;
g.strokeRect(x, y, w, h);
}
fillCirc(x: number, y: number, radius: number, color: string): void { fillCirc(x: number, y: number, radius: number, color: string): void {
const { g } = this; const { g } = this;
g.fillStyle = color; g.fillStyle = color;
@ -40,9 +54,12 @@ export class CanvasRenderer implements Renderer {
y: number, y: number,
w = data.width, w = data.width,
h = data.height, h = data.height,
alpha = 1,
): void { ): void {
const { g } = this; const { g } = this;
g.globalAlpha = alpha;
g.drawImage(data, x, y, w, h); g.drawImage(data, x, y, w, h);
g.globalAlpha = 1.0;
} }
} }

View File

@ -169,6 +169,24 @@ class TransformingRenderer implements Renderer {
const { r, t: { s, ox, oy } } = this; const { r, t: { s, ox, oy } } = this;
r.fillRect(x * s + ox, y * s + oy, w * s, h * s, color); r.fillRect(x * s + ox, y * s + oy, w * s, h * s, color);
} }
strokeRect(
x: number,
y: number,
w: number,
h: number,
color: string,
lineWidth: number,
): void {
const { r, t: { s, ox, oy } } = this;
r.strokeRect(
x * s + ox,
y * s + oy,
w * s,
h * s,
color,
lineWidth * s,
);
}
fillCirc(x: number, y: number, radius: number, color: string): void { fillCirc(x: number, y: number, radius: number, color: string): void {
const { r, t: { s, ox, oy } } = this; const { r, t: { s, ox, oy } } = this;
r.fillCirc(x * s + ox, y * s + oy, radius * s, color); r.fillCirc(x * s + ox, y * s + oy, radius * s, color);
@ -179,6 +197,7 @@ class TransformingRenderer implements Renderer {
y: number, y: number,
w = data.width, w = data.width,
h = data.width, h = data.width,
alpha = 1,
): void { ): void {
const { r, t: { s, ox, oy } } = this; const { r, t: { s, ox, oy } } = this;
@ -188,6 +207,7 @@ class TransformingRenderer implements Renderer {
y * s + oy, y * s + oy,
w * s, w * s,
h * s, h * s,
alpha,
); );
} }
} }

View File

@ -15,8 +15,8 @@ export class Painter implements Renderer {
); );
} }
render(r: Renderer, x: number, y: number) { render(r: Renderer, x: number, y: number, alpha = 1) {
r.putImage(this.c, x, y, this.c.width, this.c.height); r.putImage(this.c, x, y, this.c.width, this.c.height, alpha);
} }
get width(): number { get width(): number {
@ -31,6 +31,16 @@ export class Painter implements Renderer {
fillRect(x: number, y: number, w: number, h: number, color: string): void { fillRect(x: number, y: number, w: number, h: number, color: string): void {
this.r.fillRect(x, y, w, h, color); this.r.fillRect(x, y, w, h, color);
} }
strokeRect(
x: number,
y: number,
w: number,
h: number,
color: string,
lineWidth: number,
): void {
this.r.strokeRect(x, y, w, h, color, lineWidth);
}
fillCirc(x: number, y: number, radius: number, color: string): void { fillCirc(x: number, y: number, radius: number, color: string): void {
this.r.fillCirc(x, y, radius, color); this.r.fillCirc(x, y, radius, color);
} }
@ -40,7 +50,8 @@ export class Painter implements Renderer {
y: number, y: number,
w = data.width, w = data.width,
h = data.height, h = data.height,
alpha = 1,
): void { ): void {
this.r.putImage(data, x, y, w, h); this.r.putImage(data, x, y, w, h, alpha);
} }
} }

View File

@ -10,6 +10,16 @@ export interface Renderer {
get height(): N; get height(): N;
clear(color: string): void; clear(color: string): void;
fillRect(x: N, y: N, w: N, h: N, color: string): void; fillRect(x: N, y: N, w: N, h: N, color: string): void;
strokeRect(x: N, y: N, w: N, h: N, color: string, lineWidth: number): void;
fillCirc(x: N, y: N, radius: N, color: string): void; fillCirc(x: N, y: N, radius: N, color: string): void;
putImage(data: RendererImage, x: N, y: N, w?: N, h?: N): void; putImage(data: RendererImage, x: N, y: N): void;
putImage(data: RendererImage, x: N, y: N, w: N, h: N): void;
putImage(
data: RendererImage,
x: N,
y: N,
w: N,
h: N,
alpha: number,
): void;
} }

View File

@ -20,9 +20,10 @@ export class Simulator {
render(r: Renderer) { render(r: Renderer) {
hover: { hover: {
if (this.toolbar.hover(this.mouse.x, this.mouse.y) === "break") { if (this.toolbar.hover() === "break") {
break hover; break hover;
} }
document.body.style.cursor = "default";
} }
r.clear("black"); r.clear("black");
@ -54,6 +55,14 @@ class Toolbar {
this.mouse.addOnPress(() => { this.mouse.addOnPress(() => {
const { x, y } = this.mouse; const { x, y } = this.mouse;
if (
!(x >= 0 && y >= this.lastHeight - 200 && x < this.lastWidth &&
y < this.lastHeight)
) {
return "bubble";
}
for (const [i, component] of this.previews.entries()) { for (const [i, component] of this.previews.entries()) {
if ( if (
x >= i * 128 + 96 - 48 && x >= i * 128 + 96 - 48 &&
@ -65,7 +74,8 @@ class Toolbar {
return "stop"; return "stop";
} }
} }
return "bubble"; this.tooltip.deselect();
return "stop";
}); });
} }
@ -74,7 +84,8 @@ class Toolbar {
this.previews = this.tools as unknown as Component[]; this.previews = this.tools as unknown as Component[];
} }
hover(x: number, y: number): "continue" | "break" { hover(): "continue" | "break" {
const { x, y } = this.mouse;
for (const [i, component] of this.previews.entries()) { for (const [i, component] of this.previews.entries()) {
if ( if (
x >= i * 128 + 96 - 48 && x >= i * 128 + 96 - 48 &&
@ -83,6 +94,7 @@ class Toolbar {
y < this.lastHeight - 100 + component.height * 32 + 32 y < this.lastHeight - 100 + component.height * 32 + 32
) { ) {
this.hoveringComponentIdx = i; this.hoveringComponentIdx = i;
document.body.style.cursor = "pointer";
return "break"; return "break";
} }
} }
@ -96,6 +108,14 @@ class Toolbar {
r.fillRect(0, r.height - 200, r.width, 200, "#aaa"); r.fillRect(0, r.height - 200, r.width, 200, "#aaa");
for (const [i, component] of this.previews.entries()) { for (const [i, component] of this.previews.entries()) {
r.strokeRect(
i * 128 + 96 - 48,
r.height - 100 - 48,
96,
96,
"#777",
1,
);
if (i === this.hoveringComponentIdx) { if (i === this.hoveringComponentIdx) {
r.fillRect( r.fillRect(
i * 128 + 96 - 48, i * 128 + 96 - 48,
@ -113,6 +133,8 @@ class Toolbar {
class Tooltip { class Tooltip {
private selectedComponent?: Component; private selectedComponent?: Component;
private shouldHover = false;
constructor( constructor(
private circuit: Circuit, private circuit: Circuit,
public mouse: Mouse, public mouse: Mouse,
@ -125,18 +147,37 @@ class Tooltip {
const x = Math.floor(this.mouse.x / 32); const x = Math.floor(this.mouse.x / 32);
const y = Math.floor(this.mouse.y / 32); const y = Math.floor(this.mouse.y / 32);
this.circuit.place(this.selectedComponent, x, y); this.circuit.place(this.selectedComponent, x, y);
this.selectedComponent = undefined;
return "stop"; return "stop";
}); });
} }
hover(): "continue" | "break" {
if (!this.selectedComponent) {
return "continue";
}
const x = Math.floor(this.mouse.x / 32);
const y = Math.floor(this.mouse.y / 32);
if (!this.circuit.placeIsOccupied(x, y)) {
this.shouldHover = true;
return "break";
}
return "continue";
}
render(r: Renderer): void { render(r: Renderer): void {
this.selectedComponent?.render(r, this.mouse.x, this.mouse.y); const x = Math.floor(this.mouse.x / 32);
const y = Math.floor(this.mouse.y / 32);
this.selectedComponent?.renderTransparent(r, x * 32 + 16, y * 32 + 16);
} }
select(component: Component): void { select(component: Component): void {
this.selectedComponent = component; this.selectedComponent = component;
} }
deselect(): void {
this.selectedComponent = undefined;
}
} }
type PlacedComponent = { type PlacedComponent = {
@ -173,6 +214,14 @@ class Circuit {
}); });
} }
placeIsOccupied(x: number, y: number): boolean {
return this.components.some((c) => c.x == x && c.y == y);
}
hover(): "continue" | "break" {
return "continue";
}
place(component: Component, x: number, y: number): void { place(component: Component, x: number, y: number): void {
this.components.push({ component, x, y }); this.components.push({ component, x, y });
} }
@ -188,6 +237,7 @@ interface Component {
get width(): number; get width(): number;
get height(): number; get height(): number;
render(r: Renderer, x: number, y: number): void; render(r: Renderer, x: number, y: number): void;
renderTransparent(r: Renderer, x: number, y: number): void;
click?(x: number, y: number): void; click?(x: number, y: number): void;
} }
@ -223,6 +273,11 @@ class SwitchComponent implements Component, ComponentFactory {
graphic.render(r, x - 48, y - 32); graphic.render(r, x - 48, y - 32);
} }
renderTransparent(r: Renderer, x: number, y: number): void {
const graphic = this.switchOn ? this.graphicOn : this.graphicOff;
graphic.render(r, x - 48, y - 32, 0.5);
}
click(x: number, y: number): void { click(x: number, y: number): void {
const onButton = Math.sqrt( const onButton = Math.sqrt(
(x - 16) ** 2 + (y - 16) ** 2, (x - 16) ** 2 + (y - 16) ** 2,
@ -261,4 +316,9 @@ class LedComponent implements Component, ComponentFactory {
const graphic = this.switchOn ? this.graphicOn : this.graphicOff; const graphic = this.switchOn ? this.graphicOn : this.graphicOff;
graphic.render(r, x - 48, y - 32); graphic.render(r, x - 48, y - 32);
} }
renderTransparent(r: Renderer, x: number, y: number): void {
const graphic = this.switchOn ? this.graphicOn : this.graphicOff;
graphic.render(r, x - 48, y - 32, 0.5);
}
} }