Initial commit

This commit is contained in:
Hugo Mårdbrink 2025-11-30 15:44:22 +01:00
commit a6272848f9
379 changed files with 74829 additions and 0 deletions

View file

@ -0,0 +1,271 @@
import { Ok, Error } from "./gleam.mjs";
class PaintCanvas extends HTMLElement {
// Open an issue if you are in need of any other attributes :)
static observedAttributes = ["width", "height", "style", "picture"];
constructor() {
super();
// Create a canvas
this.canvas = document.createElement("canvas");
const style = document.createElement("style");
style.textContent = `
:host {
display: inline-block;
}
`;
this.shadow = this.attachShadow({ mode: "open" });
this.shadow.appendChild(style);
this.shadow.appendChild(this.canvas);
this.ctx = this.canvas.getContext("2d");
}
attributeChangedCallback(name, _oldValue, newValue) {
if (name === "picture") {
this.picture = newValue;
return;
} else if (name === "width") {
this.width = newValue;
} else if (name === "height") {
this.height = newValue;
}
}
drawPicture() {
if (!this.pictureString) {
return;
}
this.ctx.reset();
const display =
window.PAINT_STATE[
"display_on_rendering_context_with_default_drawing_state"
];
display(this.pictureString, this.ctx);
}
set picture(value) {
this.pictureString = value;
this.drawPicture();
}
set width(value) {
this.canvas.width = value;
this.drawPicture();
}
set height(value) {
this.canvas.height = value;
this.drawPicture();
}
get width() {
return this.canvas.width;
}
get height() {
return this.canvas.height;
}
}
export function define_web_component() {
window.customElements.define("paint-canvas", PaintCanvas);
}
export function get_rendering_context(selector) {
// TODO: Handle the case where the canvas element is not found.
return document.querySelector(selector).getContext("2d");
}
export function setup_request_animation_frame(callback) {
window.requestAnimationFrame((time) => {
callback(time);
});
}
export function setup_input_handler(event_name, callback) {
window.addEventListener(event_name, callback);
}
export function get_key_code(event) {
return event.keyCode;
}
export function set_global(state, id) {
if (typeof window.PAINT_STATE == "undefined") {
window.PAINT_STATE = {};
}
window.PAINT_STATE[id] = state;
}
export function get_global(id) {
if (!window.PAINT_STATE) {
return new Error(undefined);
}
if (!(id in window.PAINT_STATE)) {
return new Error(undefined);
}
return new Ok(window.PAINT_STATE[id]);
}
export function get_width(ctx) {
return ctx.canvas.clientWidth;
}
export function get_height(ctx) {
return ctx.canvas.clientHeight;
}
// Based on https://stackoverflow.com/questions/17130395/real-mouse-position-in-canvas
export function mouse_pos(ctx, event) {
// Calculate the scaling of the canvas vs its content
const rect = ctx.canvas.getBoundingClientRect();
const scaleX = ctx.canvas.width / rect.width;
const scaleY = ctx.canvas.height / rect.height;
return [
(event.clientX - rect.left) * scaleX,
(event.clientY - rect.top) * scaleY,
];
}
// if check_pressed is true, the function will return true if the button was pressed
// if check_pressed is false, the function will return true if the button was released
export function check_mouse_button(
event,
previous_event,
button_index,
check_pressed,
) {
let previous_buttons = previous_event?.buttons ?? 0;
let current_buttons = event.buttons;
// ~001 &&
// 011
// -----
// 010 found the newly pressed!
//
// 011 &&
// ~001
// -----
// 010 found the newly released!
if (check_pressed) {
previous_buttons = ~previous_buttons;
} else {
current_buttons = ~current_buttons;
}
let button = previous_buttons & current_buttons & (1 << button_index);
return !!button;
}
export function reset(ctx) {
ctx.reset();
}
export function arc(ctx, radius, start, end, fill, stroke) {
ctx.beginPath();
ctx.arc(0, 0, radius, start, end);
if (fill) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
export function polygon(ctx, points, closed, fill, stroke) {
ctx.beginPath();
ctx.moveTo(0, 0);
let started = false;
for (const point of points) {
let x = point[0];
let y = point[1];
if (started) {
ctx.lineTo(x, y);
} else {
ctx.moveTo(x, y);
started = true;
}
}
if (closed) {
ctx.closePath();
}
if (fill && closed) {
ctx.fill();
}
if (stroke) {
ctx.stroke();
}
}
export function text(ctx, text, style) {
ctx.font = style;
ctx.fillText(text, 0, 0);
}
export function save(ctx) {
ctx.save();
}
export function restore(ctx) {
ctx.restore();
}
export function set_fill_colour(ctx, css_colour) {
ctx.fillStyle = css_colour;
}
export function set_stroke_color(ctx, css_color) {
ctx.strokeStyle = css_color;
}
export function set_line_width(ctx, width) {
ctx.lineWidth = width;
}
export function translate(ctx, x, y) {
ctx.translate(x, y);
}
export function scale(ctx, x, y) {
ctx.scale(x, y);
}
export function rotate(ctx, radians) {
ctx.rotate(radians);
}
export function reset_transform(ctx) {
ctx.resetTransform();
}
export function draw_image(ctx, image, width_px, height_px) {
ctx.drawImage(image, 0, 0, width_px, height_px);
}
export function image_from_query(selector) {
return document.querySelector(selector);
}
export function image_from_src(src) {
const image = new Image();
image.src = src;
return image;
}
export function on_image_load(image, callback) {
if (image.complete) {
callback();
} else {
image.addEventListener("load", callback);
}
}
export function set_image_smoothing_enabled(ctx, value) {
ctx.imageSmoothingEnabled = value;
}