Skip to content
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
47 changes: 34 additions & 13 deletions desktop/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ use winit::keyboard::ModifiersState;
use crate::ui::{InputEvent, MULTICLICK_ALLOWED_TRAVEL, MULTICLICK_TIMEOUT, PINCH_ZOOM_SPEED, SCROLL_LINE_HEIGHT, SCROLL_LINE_WIDTH, SCROLL_SPEED_X, SCROLL_SPEED_Y};
use crate::wrapper::messages::{EditorPointerState, InputMessage, ModifierKeys, MouseKeys, ScrollDelta};

// Marks input event as observe-only, meaning it should not result in any input messages being send by the frontend.
// And should instead be ignored by frontend input processing and only be used to update the UI state (hover, cursor).
// TODO(Timon): Remove and find a less hacky solution
trait ObserveOnlyExt {
fn observe_only(self) -> Self;
}
impl ObserveOnlyExt for crate::ui::InputEventBuilder {
fn observe_only(self) -> Self {
self.num_lock(true)
}
}

pub(crate) struct InputState {
start: Instant,
viewport_info: Option<ViewportInfo>,
Expand Down Expand Up @@ -96,13 +108,18 @@ impl InputState {
};
match route {
Route::Ui => ui_callback(InputEvent::pointer().position(*position).moved().modifiers(self.modifiers).build()),
Route::Editor => editor_callback(InputMessage::PointerMove {
editor_mouse_state: match source {
PointerSource::TabletTool { kind, data } => self.tablet_pointer_state(kind, data),
_ => self.pointer_state(),
},
modifier_keys: self.modifier_keys(),
}),
Route::Editor => {
if !self.pointer_locked() {
ui_callback(InputEvent::pointer().position(*position).moved().modifiers(self.modifiers).observe_only().build());
}
editor_callback(InputMessage::PointerMove {
editor_mouse_state: match source {
PointerSource::TabletTool { kind, data } => self.tablet_pointer_state(kind, data),
_ => self.pointer_state(),
},
modifier_keys: self.modifier_keys(),
});
}
}
}
WindowEvent::PointerEntered { position, .. } => {
Expand Down Expand Up @@ -160,15 +177,19 @@ impl InputState {
let count = mouse_button.map_or(1, |button| self.click_tracker.input(*position, button, *state));

let back_or_forward = matches!(mouse_button, Some(MouseButton::Back | MouseButton::Forward));
let pointer = InputEvent::pointer().position(*position);
let input = match state {
ElementState::Pressed => pointer.pressed(button.clone(), count),
ElementState::Released => pointer.released(button.clone(), count),
}
.modifiers(self.modifiers);
if self.pointer_locked() || keys.is_empty() || !(back_or_forward || route == Route::Editor) {
let pointer = InputEvent::pointer().position(*position);
let input = match state {
ElementState::Pressed => pointer.pressed(button.clone(), count),
ElementState::Released => pointer.released(button.clone(), count),
};
ui_callback(input.modifiers(self.modifiers).build());
ui_callback(input.build());
return;
}
if route == Route::Editor {
ui_callback(input.observe_only().build());
Comment thread
timon-schelling marked this conversation as resolved.
}

let editor_mouse_state = match button {
ButtonSource::TabletTool { kind, data, .. } => self.tablet_pointer_state(kind, data),
Expand Down
13 changes: 11 additions & 2 deletions frontend/src/utility-functions/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,19 @@ export async function onKeyUp(e: KeyboardEvent, editor: EditorWrapper, dialogSto

// Pointer events

// On desktop, num lock marks events as observe-only, do not redirect them to the editor.
function isObserveOnly(e: MouseEvent): boolean {
return import.meta.env.MODE === "native" && e.getModifierState("NumLock");
}

// While any pointer button is already down, additional button down events are not reported, but they are sent as `pointermove` events and these are handled in the backend
export function onPointerMove(e: PointerEvent, editor: EditorWrapper, documentStore: DocumentStore) {
potentiallyRestoreCanvasFocus(e);

if (!e.buttons) viewportPointerInteractionOngoing = false;

if (isObserveOnly(e)) return;

// Don't redirect pointer movement to the backend if there's no ongoing interaction and it's over a floating menu, or the graph overlay, on top of the canvas
// TODO: A better approach is to pass along a boolean to the backend's input preprocessor so it can know if it's being occluded by the GUI.
// TODO: This would allow it to properly decide to act on removing hover focus from something that was hovered in the canvas before moving over the GUI.
Expand All @@ -134,6 +141,8 @@ export function onPointerDown(e: PointerEvent, editor: EditorWrapper, dialogStor
potentiallyRestoreCanvasFocus(e);
potentiallyClearTextSelection(e);

if (isObserveOnly(e)) return;

const inFloatingMenu = e.target instanceof Element && e.target.closest("[data-floating-menu-content]");
const isTargetingCanvas = !inFloatingMenu && e.target instanceof Element && e.target.closest("[data-viewport], [data-viewport-container], [data-node-graph]");
const inDialog = e.target instanceof Element && e.target.closest("[data-dialog] [data-floating-menu-content]");
Expand Down Expand Up @@ -172,7 +181,7 @@ export function onPointerUp(e: PointerEvent, editor: EditorWrapper) {

if (!e.buttons) viewportPointerInteractionOngoing = false;

if (textToolInteractiveInputElement) return;
if (isObserveOnly(e) || textToolInteractiveInputElement) return;

const modifiers = makeKeyboardModifiersBitfield(e);
editor.onMouseUp(e.clientX, e.clientY, e.buttons, modifiers, ...pointerAttributes(e));
Expand All @@ -181,7 +190,7 @@ export function onPointerUp(e: PointerEvent, editor: EditorWrapper) {
// Mouse events

export function onPotentialDoubleClick(e: MouseEvent, editor: EditorWrapper) {
if (textToolInteractiveInputElement || inPointerLock) return;
if (isObserveOnly(e) || textToolInteractiveInputElement || inPointerLock) return;

// Allow only events within the viewport or node graph boundaries
const isTargetingCanvas = e.target instanceof Element && e.target.closest("[data-viewport], [data-viewport-container], [data-node-graph]");
Expand Down
Loading