Skip to content

Fix nudge and textbox creation #672

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 3 commits into from
Jun 10, 2022
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
11 changes: 8 additions & 3 deletions editor/src/input/input_preprocessor_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,18 @@ impl MessageHandler<InputPreprocessorMessage, ()> for InputPreprocessorMessageHa
}

impl InputPreprocessorMessageHandler {
fn translate_mouse_event(&mut self, new_state: MouseState, allow_first_button_down: bool, responses: &mut VecDeque<Message>) {
fn translate_mouse_event(&mut self, mut new_state: MouseState, allow_first_button_down: bool, responses: &mut VecDeque<Message>) {
for (bit_flag, key) in [(MouseKeys::LEFT, Key::Lmb), (MouseKeys::RIGHT, Key::Rmb), (MouseKeys::MIDDLE, Key::Mmb)] {
// Calculate the intersection between the two key states
let old_down = self.mouse.mouse_keys & bit_flag == bit_flag;
let new_down = new_state.mouse_keys & bit_flag == bit_flag;
if !old_down && new_down && (allow_first_button_down || self.mouse.mouse_keys != MouseKeys::NONE) {
responses.push_back(InputMapperMessage::KeyDown(key).into());
if !old_down && new_down {
if allow_first_button_down || self.mouse.mouse_keys != MouseKeys::NONE {
responses.push_back(InputMapperMessage::KeyDown(key).into());
} else {
// Required to stop a keyup being emitted for a keydown outside canvas
new_state.mouse_keys ^= bit_flag;
}
}
if old_down && !new_down {
responses.push_back(InputMapperMessage::KeyUp(key).into());
Expand Down
2 changes: 1 addition & 1 deletion frontend/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<body>
<noscript>JavaScript is required</noscript>
<!-- tabindex is used to allow the app to have focus while inside of it -->
<div id="app" tabindex="0"></div>
<div data-app id="app" tabindex="0"></div>
</body>

</html>
33 changes: 26 additions & 7 deletions frontend/src/io-managers/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ type EventListenerTarget = {
};

export function createInputManager(editor: Editor, container: HTMLElement, dialog: DialogState, document: PortfolioState, fullscreen: FullscreenState): () => void {
const app = window.document.querySelector("[data-app]") as HTMLElement | undefined;
app?.focus();

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const listeners: { target: EventListenerTarget; eventName: EventName; action: (event: any) => void; options?: boolean | AddEventListenerOptions }[] = [
{ target: window, eventName: "resize", action: (): void => onWindowResize(container) },
Expand All @@ -27,10 +30,20 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
{ target: window, eventName: "wheel", action: (e: WheelEvent): void => onMouseScroll(e), options: { passive: false } },
{ target: window, eventName: "modifyinputfield", action: (e: CustomEvent): void => onModifyInputField(e) },
{ target: window.document.body, eventName: "paste", action: (e: ClipboardEvent): void => onPaste(e) },
{
target: app as EventListenerTarget,
eventName: "blur",
action: (): void => blurApp(),
},
];

let viewportPointerInteractionOngoing = false;
let textInput = undefined as undefined | HTMLDivElement;
let canvasFocused = true;

function blurApp(): void {
canvasFocused = false;
}

// Keyboard events

Expand Down Expand Up @@ -66,8 +79,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
if (e.ctrlKey && e.shiftKey && key === "j") return false;

// Don't redirect tab or enter if not in canvas (to allow navigating elements)
const inCanvas = e.target instanceof Element && e.target.closest("[data-canvas]");
if (!inCanvas && ["tab", "enter", " ", "arrowdown", "arrowup", "arrowleft", "arrowright"].includes(key.toLowerCase())) return false;
if (!canvasFocused && ["tab", "enter", " ", "arrowdown", "arrowup", "arrowleft", "arrowright"].includes(key.toLowerCase())) return false;

// Redirect to the backend
return true;
Expand Down Expand Up @@ -113,13 +125,20 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
const inFloatingMenu = e.target instanceof Element && e.target.closest("[data-floating-menu-content]");
if (!viewportPointerInteractionOngoing && inFloatingMenu) return;

const { target } = e;
const newInCanvas = (target instanceof Element && target.closest("[data-canvas]")) instanceof Element && !targetIsTextField(window.document.activeElement);
if (newInCanvas && !canvasFocused) {
canvasFocused = true;
app?.focus();
}

const modifiers = makeKeyboardModifiersBitfield(e);
editor.instance.on_mouse_move(e.clientX, e.clientY, e.buttons, modifiers);
}

function onPointerDown(e: PointerEvent): void {
const { target } = e;
const inCanvas = target instanceof Element && target.closest("[data-canvas]");
const isTargetingCanvas = target instanceof Element && target.closest("[data-canvas]");
const inDialog = target instanceof Element && target.closest("[data-dialog-modal] [data-floating-menu-content]");
const inTextInput = target === textInput;

Expand All @@ -131,7 +150,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo

if (!inTextInput) {
if (textInput) editor.instance.on_change_text(textInputCleanup(textInput.innerText));
else if (inCanvas) viewportPointerInteractionOngoing = true;
else viewportPointerInteractionOngoing = isTargetingCanvas instanceof Element;
}

if (viewportPointerInteractionOngoing) {
Expand Down Expand Up @@ -168,7 +187,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo

function onMouseScroll(e: WheelEvent): void {
const { target } = e;
const inCanvas = target instanceof Element && target.closest("[data-canvas]");
const isTargetingCanvas = target instanceof Element && target.closest("[data-canvas]");

// Redirect vertical scroll wheel movement into a horizontal scroll on a horizontally scrollable element
// There seems to be no possible way to properly employ the browser's smooth scrolling interpolation
Expand All @@ -178,7 +197,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
return;
}

if (inCanvas) {
if (isTargetingCanvas) {
e.preventDefault();
const modifiers = makeKeyboardModifiersBitfield(e);
editor.instance.on_mouse_scroll(e.clientX, e.clientY, e.buttons, e.deltaX, e.deltaY, e.deltaZ, modifiers);
Expand Down Expand Up @@ -246,7 +265,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
});
}

function targetIsTextField(target: EventTarget | null): boolean {
function targetIsTextField(target: EventTarget | HTMLElement | null): boolean {
return target instanceof HTMLElement && (target.nodeName === "INPUT" || target.nodeName === "TEXTAREA" || target.isContentEditable);
}

Expand Down