Skip to content

Frontend refactor to move response handler, key input handling, and more into new utilities folder #260

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/web/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module.exports = {
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
"no-param-reassign": ["error", { props: false }],
"import/prefer-default-export": "off",
"max-len": ["error", { code: 200, tabWidth: 4 }],
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/no-use-before-define": "off",
Expand Down
44 changes: 4 additions & 40 deletions client/web/src/components/panels/Document.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@

<script lang="ts">
import { defineComponent } from "vue";
import { ResponseType, registerResponseHandler, Response, UpdateCanvas, SetActiveTool, ExportDocument, SetCanvasZoom, SetRotation } from "../../response-handler";
import { makeModifiersBitfield } from "@/utilities/input";
import { ResponseType, registerResponseHandler, Response, UpdateCanvas, SetActiveTool, ExportDocument, SetCanvasZoom, SetRotation } from "../../utilities/response-handler";
import LayoutRow from "../layout/LayoutRow.vue";
import LayoutCol from "../layout/LayoutCol.vue";
import WorkingColors from "../widgets/WorkingColors.vue";
Expand All @@ -217,26 +218,6 @@ const modeMenuEntries: SectionsOfMenuListEntries = [

const wasm = import("../../../wasm/pkg");

function redirectKeyboardEventToBackend(e: KeyboardEvent): boolean {
// Don't redirect user input from text entry into HTML elements
const target = e.target as HTMLElement;
if (target.nodeName === "INPUT" || target.nodeName === "TEXTAREA" || target.isContentEditable) return false;

// Don't redirect a fullscreen request
if (e.key.toLowerCase() === "f11") return false;

// Don't redirect debugging tools
if (e.key.toLowerCase() === "f12") return false;
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "c") return false;

return true;
}

function makeModifiersBitfield(control: boolean, shift: boolean, alt: boolean): number {
// eslint-disable-next-line no-bitwise
return Number(control) | (Number(shift) << 1) | (Number(alt) << 2);
}

export default defineComponent({
methods: {
async viewportResize() {
Expand Down Expand Up @@ -273,28 +254,11 @@ export default defineComponent({
const { set_rotation } = await wasm;
set_rotation(newRotation * (Math.PI / 180));
},
async keyDown(e: KeyboardEvent) {
if (redirectKeyboardEventToBackend(e)) {
e.preventDefault();
const { on_key_down } = await wasm;
const modifiers = makeModifiersBitfield(e.ctrlKey, e.shiftKey, e.altKey);
on_key_down(e.key, modifiers);
}
},
async keyUp(e: KeyboardEvent) {
if (redirectKeyboardEventToBackend(e)) {
e.preventDefault();
const { on_key_up } = await wasm;
const modifiers = makeModifiersBitfield(e.ctrlKey, e.shiftKey, e.altKey);
on_key_up(e.key, modifiers);
}
},
async selectTool(toolName: string) {
const { select_tool } = await wasm;
select_tool(toolName);
},
async viewModeChanged(toolIndex: number) {
console.log(toolIndex);
function todo(_: number) {
return _;
}
Expand Down Expand Up @@ -341,10 +305,10 @@ export default defineComponent({
}
});

window.addEventListener("keyup", (e: KeyboardEvent) => this.keyUp(e));
// TODO: Move event listeners to `main.ts`
const canvas = this.$refs.canvas as HTMLDivElement;
canvas.addEventListener("wheel", this.canvasMouseScroll, { passive: false });
window.addEventListener("keydown", (e: KeyboardEvent) => this.keyDown(e));

window.addEventListener("resize", () => this.viewportResize());
window.addEventListener("DOMContentLoaded", () => this.viewportResize());

Expand Down
2 changes: 1 addition & 1 deletion client/web/src/components/panels/LayerTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@

<script lang="ts">
import { defineComponent } from "vue";
import { ResponseType, registerResponseHandler, Response, ExpandFolder, LayerPanelEntry } from "../../response-handler";
import { ResponseType, registerResponseHandler, Response, ExpandFolder, LayerPanelEntry } from "../../utilities/response-handler";
import LayoutRow from "../layout/LayoutRow.vue";
import LayoutCol from "../layout/LayoutCol.vue";
import Separator, { SeparatorType } from "../widgets/Separator.vue";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@

<script lang="ts">
import { defineComponent } from "vue";
import { clamp, hsvToRgb, rgbToHsv, isRGB } from "../../../lib/utils";
import { hsvToRgb, rgbToHsv, isRGB } from "@/utilities/color";
import { clamp } from "@/utilities/math";

const enum ColorPickerState {
Idle = "Idle",
Expand Down
6 changes: 3 additions & 3 deletions client/web/src/components/widgets/inputs/SwatchPairInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@
</style>

<script lang="ts">
import { rgbToDecimalRgb } from "@/lib/utils";
import { rgbToDecimalRgb } from "@/utilities/color";
import { defineComponent } from "vue";
import ColorPicker from "../floating-menus/ColorPicker.vue";
import FloatingMenu, { MenuDirection, MenuType } from "../floating-menus/FloatingMenu.vue";
import { ResponseType, registerResponseHandler, Response, UpdateWorkingColors } from "../../../response-handler";
import { ResponseType, registerResponseHandler, Response, UpdateWorkingColors } from "../../../utilities/response-handler";

const wasm = import("../../../../wasm/pkg");
const wasm = import("@/../wasm/pkg");

export default defineComponent({
components: {
Expand Down
2 changes: 1 addition & 1 deletion client/web/src/components/workspace/Panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ import Minimap from "../panels/Minimap.vue";
import IconButton from "../widgets/buttons/IconButton.vue";
import PopoverButton, { PopoverButtonIcon } from "../widgets/buttons/PopoverButton.vue";
import { MenuDirection } from "../widgets/floating-menus/FloatingMenu.vue";
import { ResponseType, registerResponseHandler, Response } from "../../response-handler";
import { ResponseType, registerResponseHandler, Response } from "../../utilities/response-handler";
const wasm = import("../../../wasm/pkg");
Expand Down
2 changes: 1 addition & 1 deletion client/web/src/components/workspace/Workspace.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

<script lang="ts">
import { defineComponent } from "vue";
import { ResponseType, registerResponseHandler, Response, SetActiveDocument, NewDocument, CloseDocument } from "../../response-handler";
import { ResponseType, registerResponseHandler, Response, SetActiveDocument, NewDocument, CloseDocument } from "../../utilities/response-handler";
import LayoutRow from "../layout/LayoutRow.vue";
import LayoutCol from "../layout/LayoutCol.vue";
import Panel from "./Panel.vue";
Expand Down
8 changes: 5 additions & 3 deletions client/web/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { createApp } from "vue";
import { handleKeyUp, handleKeyDown } from "@/utilities/input";
import App from "./App.vue";
import { attachResponseHandlerToPage } from "./response-handler";

// Bind global browser events
document.addEventListener("contextmenu", (e) => e.preventDefault());
window.addEventListener("keyup", (e: KeyboardEvent) => handleKeyUp(e));
window.addEventListener("keydown", (e: KeyboardEvent) => handleKeyDown(e));

attachResponseHandlerToPage();

// Initialize the Vue application
createApp(App).mount("#app");
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ export function rgbToDecimalRgb(rgb: RGB) {
return { r, g, b, a: rgb.a };
}

export function clamp(value: number, min = 0, max = 1) {
return Math.max(min, Math.min(value, max));
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isRGB(data: any): data is RGB {
if (typeof data !== "object" || data === null) return false;
Expand Down
45 changes: 45 additions & 0 deletions client/web/src/utilities/input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const wasm = import("@/../wasm/pkg");

export function shouldRedirectKeyboardEventToBackend(e: KeyboardEvent): boolean {
// Don't redirect user input from text entry into HTML elements
const target = e.target as HTMLElement;
if (target.nodeName === "INPUT" || target.nodeName === "TEXTAREA" || target.isContentEditable) return false;

// Don't redirect a fullscreen request
if (e.key.toLowerCase() === "f11") return false;

// Don't redirect a reload request
if (e.key.toLowerCase() === "f5") return false;

// Don't redirect debugging tools
if (e.key.toLowerCase() === "f12") return false;
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "c") return false;
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "i") return false;
if (e.ctrlKey && e.shiftKey && e.key.toLowerCase() === "j") return false;

// Redirect to the backend
return true;
}

export async function handleKeyDown(e: KeyboardEvent) {
if (shouldRedirectKeyboardEventToBackend(e)) {
e.preventDefault();
const { on_key_down } = await wasm;
const modifiers = makeModifiersBitfield(e.ctrlKey, e.shiftKey, e.altKey);
on_key_down(e.key, modifiers);
}
}

export async function handleKeyUp(e: KeyboardEvent) {
if (shouldRedirectKeyboardEventToBackend(e)) {
e.preventDefault();
const { on_key_up } = await wasm;
const modifiers = makeModifiersBitfield(e.ctrlKey, e.shiftKey, e.altKey);
on_key_up(e.key, modifiers);
}
}

export function makeModifiersBitfield(control: boolean, shift: boolean, alt: boolean): number {
// eslint-disable-next-line no-bitwise
return Number(control) | (Number(shift) << 1) | (Number(alt) << 2);
}
3 changes: 3 additions & 0 deletions client/web/src/utilities/math.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function clamp(value: number, min = 0, max = 1) {
return Math.max(min, Math.min(value, max));
}
4 changes: 4 additions & 0 deletions client/web/src/utilities/response-handler-binding.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This file is instantiated by wasm-bindgen in `client\web\wasm\src\lib.rs` and re-exports the `handleResponse` function to
// provide access to the global copy of `response-handler.ts` with its shared state, not an isolated duplicate with empty state

export { handleResponse } from "@/utilities/response-handler";
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { reactive } from "vue";

/* eslint-disable @typescript-eslint/no-explicit-any */

type ResponseCallback = (responseData: Response) => void;
type ResponseMap = {
[response: string]: ResponseCallback | undefined;
};
declare global {
interface Window {
responseMap: ResponseMap;
}
}

const state = reactive({
responseMap: {} as ResponseMap,
});

export enum ResponseType {
UpdateCanvas = "UpdateCanvas",
Expand All @@ -25,17 +26,13 @@ export enum ResponseType {
SetRotation = "SetRotation",
}

export function attachResponseHandlerToPage() {
window.responseMap = {};
}

export function registerResponseHandler(responseType: ResponseType, callback: ResponseCallback) {
window.responseMap[responseType] = callback;
state.responseMap[responseType] = callback;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function handleResponse(responseType: string, responseData: any) {
const callback = window.responseMap[responseType];
const callback = state.responseMap[responseType];
const data = parseResponse(responseType, responseData);

if (callback && data) {
Expand Down Expand Up @@ -97,6 +94,7 @@ export interface Color {
alpha: number;
}
function newColor(input: any): Color {
// TODO: Possibly change this in the Rust side to avoid any pitfalls
return { red: input.red * 255, green: input.green * 255, blue: input.blue * 255, alpha: input.alpha };
}

Expand Down
2 changes: 1 addition & 1 deletion client/web/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn send_response(response_type: String, response_data: FrontendMessage) {
let _ = handleResponse(response_type, response_data).map_err(|error| log::error!("javascript threw an error: {:?}", error));
}

#[wasm_bindgen(module = "/../src/response-handler.ts")]
#[wasm_bindgen(module = "/../src/utilities/response-handler-binding.ts")]
extern "C" {
#[wasm_bindgen(catch)]
fn handleResponse(responseType: String, responseData: JsValue) -> Result<(), JsValue>;
Expand Down