Commit 0e973160 authored by Grant's avatar Grant
Browse files

add pixel picker (fixes #55)

right click & middle click now get triggered by keybinds (uses auxclick event)
parent 7964954a
Loading
Loading
Loading
Loading
+53 −12
Original line number Diff line number Diff line
@@ -33,10 +33,6 @@ const Cursor = () => {
  const { cursor } = useAppContext();
  const [color, setColor] = useState<string>();

  useEffect(() => {
    console.log("color", color);
  }, [color]);

  useEffect(() => {
    if (typeof cursor.color === "number") {
      const color = Canvas.instance?.Pallete.getColor(cursor.color);
@@ -71,17 +67,19 @@ const CanvasInner = () => {
    useAppContext();
  const PanZoom = useContext(RendererContext);

  const handlePixelWhois = useCallback(
    ({ clientX, clientY }: { clientX: number; clientY: number }) => {
  /**
   * Is the canvas coordinate within the bounds of the canvas?
   */
  const isCoordInCanvas = useCallback(
    (x: number, y: number): boolean => {
      if (!canvas.current) {
        console.warn(
          "[CanvasWrapper#handlePixelWhois] canvas instance does not exist"
          "[CanvasWrapper#isCoordInCanvas] canvas instance does not exist"
        );
        return;
        return false;
      }

      const [x, y] = canvas.current.screenToPos(clientX, clientY);
      if (x < 0 || y < 0) return; // discard if out of bounds
      if (x < 0 || y < 0) return false; // not positive, impossible to be on canvas

      // canvas size can dynamically change, so we need to check the current config
      // we're depending on canvas.instance's config so we don't have to use a react dependency
@@ -92,14 +90,31 @@ const CanvasInner = () => {
          },
        } = canvas.current.getConfig();

        if (x >= width || y >= height) return; // out of bounds
        if (x >= width || y >= height) return false; // out of bounds
      } else {
        // although this should never happen, log it
        console.warn(
          "[CanvasWrapper#handlePixelWhois] canvas config is not available yet"
          "[CanvasWrapper#isCoordInCanvas] canvas config is not available yet"
        );
      }

      return true;
    },
    [canvas.current]
  );

  const handlePixelWhois = useCallback(
    ({ clientX, clientY }: { clientX: number; clientY: number }) => {
      if (!canvas.current) {
        console.warn(
          "[CanvasWrapper#handlePixelWhois] canvas instance does not exist"
        );
        return;
      }

      const [x, y] = canvas.current.screenToPos(clientX, clientY);
      if (!isCoordInCanvas(x, y)) return; // out of bounds

      // .......
      // .......
      // .......
@@ -114,6 +129,30 @@ const CanvasInner = () => {
    [canvas.current]
  );

  const handlePickPixel = useCallback(
    ({ clientX, clientY }: { clientX: number; clientY: number }) => {
      if (!canvas.current) {
        console.warn(
          "[CanvasWrapper#handlePickPixel] canvas instance does not exist"
        );
        return;
      }

      const [x, y] = canvas.current.screenToPos(clientX, clientY);
      if (!isCoordInCanvas(x, y)) return; // out of bounds

      const pixel = canvas.current.getPixel(x, y);
      if (!pixel) return;

      // no need to use canvas#setCursor as Palette.tsx already does that
      setCursor((v) => ({
        ...v,
        color: pixel.color,
      }));
    },
    [canvas.current]
  );

  useEffect(() => {
    if (!canvasRef.current) return;
    canvas.current = new Canvas(canvasRef.current!, PanZoom);
@@ -126,9 +165,11 @@ const CanvasInner = () => {
    });

    KeybindManager.on("PIXEL_WHOIS", handlePixelWhois);
    KeybindManager.on("PICK_COLOR", handlePickPixel);

    return () => {
      KeybindManager.off("PIXEL_WHOIS", handlePixelWhois);
      KeybindManager.off("PICK_COLOR", handlePickPixel);
      canvas.current!.destroy();
    };
  }, [PanZoom]);
+8 −15
Original line number Diff line number Diff line
@@ -3,21 +3,14 @@ import { useAppContext } from "../../contexts/AppContext";
import { Canvas } from "../../lib/canvas";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faXmark } from "@fortawesome/free-solid-svg-icons";
import { IPaletteContext } from "@sc07-canvas/lib/src/net";
import { KeybindManager } from "../../lib/keybinds";

export const Palette = () => {
  const { config, user, setCursor } = useAppContext<true>();
  const [pallete, setPallete] = useState<IPaletteContext>({});
  const { config, user, cursor, setCursor } = useAppContext<true>();

  useEffect(() => {
    Canvas.instance?.updatePallete(pallete);

    setCursor((v) => ({
      ...v,
      color: pallete.color,
    }));
  }, [pallete]);
    Canvas.instance?.updateCursor(cursor.color);
  }, [cursor]);

  useEffect(() => {
    const handleDeselect = () => {
@@ -42,8 +35,8 @@ export const Palette = () => {
          className="pallete-color--deselect"
          title="Deselect Color"
          onClick={() => {
            setPallete(({ color, ...pallete }) => {
              return pallete;
            setCursor(({ color, ...cursor }) => {
              return cursor;
            });
          }}
        >
@@ -53,7 +46,7 @@ export const Palette = () => {
          <button
            key={color.id}
            aria-label={color.name}
            className={["pallete-color", color.id === pallete.color && "active"]
            className={["pallete-color", color.id === cursor.color && "active"]
              .filter((a) => a)
              .join(" ")}
            style={{
@@ -61,9 +54,9 @@ export const Palette = () => {
            }}
            title={color.name}
            onClick={() => {
              setPallete((pallete) => {
              setCursor((cursor) => {
                return {
                  ...pallete,
                  ...cursor,
                  color: color.id,
                };
              });
+16 −12
Original line number Diff line number Diff line
import EventEmitter from "eventemitter3";
import {
  ClientConfig,
  IPaletteContext,
  IPosition,
  Pixel,
} from "@sc07-canvas/lib/src/net";
import { ClientConfig, IPosition, Pixel } from "@sc07-canvas/lib/src/net";
import Network from "./network";
import {
  ClickEvent,
@@ -34,7 +29,7 @@ export class Canvas extends EventEmitter<CanvasEvents> {
  private canvas: HTMLCanvasElement;
  private PanZoom: PanZoom;

  private cursor = { x: -1, y: -1 };
  private cursor: { x: number; y: number; color?: number } = { x: -1, y: -1 };
  private pixels: {
    [x_y: string]: { color: number; type: "full" | "pending" };
  } = {};
@@ -174,6 +169,10 @@ export class Canvas extends EventEmitter<CanvasEvents> {
    return pixels;
  }

  getPixel(x: number, y: number): { color: number } | undefined {
    return this.pixels[x + "_" + y];
  }

  handleLongPress = (clientX: number, clientY: number) => {
    KeybindManager.handleInteraction(
      {
@@ -289,16 +288,15 @@ export class Canvas extends EventEmitter<CanvasEvents> {
    getRenderer().usePixel({ x, y, hex: palette?.hex || "null" });
  };

  palleteCtx: IPaletteContext = {};
  Pallete = {
    getColor: (colorId: number) => {
      return this.config.pallete.colors.find((c) => c.id === colorId);
    },

    getSelectedColor: () => {
      if (!this.palleteCtx.color) return undefined;
      if (!this.cursor.color) return undefined;

      return this.Pallete.getColor(this.palleteCtx.color);
      return this.Pallete.getColor(this.cursor.color);
    },

    getColorFromHex: (hex: string) => {
@@ -306,8 +304,14 @@ export class Canvas extends EventEmitter<CanvasEvents> {
    },
  };

  updatePallete(pallete: IPaletteContext) {
    this.palleteCtx = pallete;
  /**
   * Changes the cursor color as tracked by the Canvas instance
   *
   * @see Toolbar/Palette.tsx
   * @param color
   */
  updateCursor(color?: number) {
    this.cursor.color = color;
  }

  place(x: number, y: number) {
+10 −1
Original line number Diff line number Diff line
@@ -59,6 +59,11 @@ const KEYBINDS = enforceObjectType({
      key: "Escape",
    },
  ],
  PICK_COLOR: [
    {
      key: "MCLICK",
    },
  ],
});

class KeybindManager_ extends EventEmitter<{
@@ -72,7 +77,11 @@ class KeybindManager_ extends EventEmitter<{
      passive: false,
    });
    document.addEventListener("keyup", this.handleKeyup);
    document.addEventListener("click", this.handleClick);
    document.addEventListener("click", this.handleClick); // only gets triggered for left click
    document.addEventListener("auxclick", (e) => {
      if (e.button === 0) return; // left button still triggers this
      this.handleClick(e);
    });
  }

  destroy() {
+0 −4
Original line number Diff line number Diff line
@@ -57,10 +57,6 @@ export interface IPosition {
  y: number;
}

export interface IPaletteContext {
  color?: number;
}

// other

export type Pixel = {