diff --git a/editor/src/messages/layout/layout_message_handler.rs b/editor/src/messages/layout/layout_message_handler.rs index 4eeafb5e..9bb786d8 100644 --- a/editor/src/messages/layout/layout_message_handler.rs +++ b/editor/src/messages/layout/layout_message_handler.rs @@ -121,7 +121,10 @@ impl LayoutMessageHandler { }; (|| { - let update_value = value.as_object().expect("ColorInput update was not of type: object"); + let Some(update_value) = value.as_object() else { + warn!("ColorInput update was not of type: object"); + return Message::NoOp; + }; // None let is_none = update_value.get("none").and_then(|x| x.as_bool()); @@ -154,7 +157,8 @@ impl LayoutMessageHandler { return (color_button.on_update.callback)(color_button); } - panic!("ColorInput update was not able to be parsed with color data: {color_button:?}"); + warn!("ColorInput update was not able to be parsed with color data: {color_button:?}"); + Message::NoOp })() } }; diff --git a/frontend/src/io-managers/input.ts b/frontend/src/io-managers/input.ts index 94d3494e..c2956b49 100644 --- a/frontend/src/io-managers/input.ts +++ b/frontend/src/io-managers/input.ts @@ -22,7 +22,7 @@ export const PRESS_REPEAT_DELAY_MS = 400; export const PRESS_REPEAT_INTERVAL_MS = 72; export const PRESS_REPEAT_INTERVAL_RAPID_MS = 10; -type EventName = keyof HTMLElementEventMap | keyof WindowEventHandlersEventMap | "modifyinputfield"; +type EventName = keyof HTMLElementEventMap | keyof WindowEventHandlersEventMap | "modifyinputfield" | "pointerlockchange" | "pointerlockerror"; type EventListenerTarget = { addEventListener: typeof window.addEventListener; removeEventListener: typeof window.removeEventListener; @@ -35,6 +35,7 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli let viewportPointerInteractionOngoing = false; let textToolInteractiveInputElement = undefined as undefined | HTMLDivElement; let canvasFocused = true; + let inPointerLock = false; // Event listeners @@ -55,6 +56,8 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli { target: window.document, eventName: "contextmenu", action: (e: MouseEvent) => onContextMenu(e) }, { target: window.document, eventName: "fullscreenchange", action: () => fullscreen.fullscreenModeChanged() }, { target: window.document.body, eventName: "paste", action: (e: ClipboardEvent) => onPaste(e) }, + { target: window.document, eventName: "pointerlockchange", action: onPointerLockChange }, + { target: window.document, eventName: "pointerlockerror", action: onPointerLockChange }, ]; // Event bindings @@ -209,9 +212,9 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli } function onPotentialDoubleClick(e: MouseEvent) { - if (textToolInteractiveInputElement) return; + if (textToolInteractiveInputElement || inPointerLock) return; - // Allow only double-clicks + // Allow only repeated increments of double-clicks (not 1, 3, 5, etc.) if (e.detail % 2 == 1) return; // `e.buttons` is always 0 in the `mouseup` event, so we have to convert from `e.button` instead @@ -226,6 +229,10 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli editor.handle.onDoubleClick(e.clientX, e.clientY, buttons, modifiers); } + function onPointerLockChange() { + inPointerLock = Boolean(window.document.pointerLockElement); + } + // Mouse events function onWheelScroll(e: WheelEvent) {