Desktop: Implement pointer lock for NumberInput (#3638)
* Desktop: Implement pointer lock for NumberInput * add shift and ctrl modifiers * fixup
This commit is contained in:
parent
6616d1bfb1
commit
73682b482b
|
|
@ -5,7 +5,7 @@ use std::sync::mpsc::{Receiver, Sender, SyncSender};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use winit::application::ApplicationHandler;
|
use winit::application::ApplicationHandler;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||||
use winit::event::{ButtonSource, ElementState, MouseButton, WindowEvent};
|
use winit::event::{ButtonSource, ElementState, MouseButton, WindowEvent};
|
||||||
use winit::event_loop::{ActiveEventLoop, ControlFlow};
|
use winit::event_loop::{ActiveEventLoop, ControlFlow};
|
||||||
use winit::window::WindowId;
|
use winit::window::WindowId;
|
||||||
|
|
@ -27,6 +27,8 @@ pub(crate) struct App {
|
||||||
window_size: PhysicalSize<u32>,
|
window_size: PhysicalSize<u32>,
|
||||||
window_maximized: bool,
|
window_maximized: bool,
|
||||||
window_fullscreen: bool,
|
window_fullscreen: bool,
|
||||||
|
pointer_position: PhysicalPosition<f64>,
|
||||||
|
pointer_lock_position: Option<PhysicalPosition<f64>>,
|
||||||
ui_scale: f64,
|
ui_scale: f64,
|
||||||
app_event_receiver: Receiver<AppEvent>,
|
app_event_receiver: Receiver<AppEvent>,
|
||||||
app_event_scheduler: AppEventScheduler,
|
app_event_scheduler: AppEventScheduler,
|
||||||
|
|
@ -84,6 +86,8 @@ impl App {
|
||||||
window_size: PhysicalSize { width: 0, height: 0 },
|
window_size: PhysicalSize { width: 0, height: 0 },
|
||||||
window_maximized: false,
|
window_maximized: false,
|
||||||
window_fullscreen: false,
|
window_fullscreen: false,
|
||||||
|
pointer_position: Default::default(),
|
||||||
|
pointer_lock_position: Default::default(),
|
||||||
ui_scale: 1.,
|
ui_scale: 1.,
|
||||||
app_event_receiver,
|
app_event_receiver,
|
||||||
app_event_scheduler,
|
app_event_scheduler,
|
||||||
|
|
@ -329,6 +333,12 @@ impl App {
|
||||||
window.clipboard_write(content);
|
window.clipboard_write(content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DesktopFrontendMessage::PointerLock => {
|
||||||
|
self.pointer_lock_position = Some(self.pointer_position);
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.start_pointer_lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
DesktopFrontendMessage::WindowClose => {
|
DesktopFrontendMessage::WindowClose => {
|
||||||
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
self.app_event_scheduler.schedule(AppEvent::CloseWindow);
|
||||||
}
|
}
|
||||||
|
|
@ -480,6 +490,26 @@ impl ApplicationHandler for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _window_id: WindowId, event: WindowEvent) {
|
||||||
|
// Handle pointer lock release
|
||||||
|
if let Some(pointer_lock_position) = self.pointer_lock_position
|
||||||
|
&& let WindowEvent::PointerButton {
|
||||||
|
state: ElementState::Released,
|
||||||
|
button: ButtonSource::Mouse(MouseButton::Left),
|
||||||
|
..
|
||||||
|
} = event
|
||||||
|
{
|
||||||
|
self.pointer_lock_position = None;
|
||||||
|
if let Some(window) = &self.window {
|
||||||
|
window.end_pointer_lock();
|
||||||
|
}
|
||||||
|
self.cef_context.handle_window_event(&WindowEvent::PointerMoved {
|
||||||
|
device_id: None,
|
||||||
|
position: pointer_lock_position,
|
||||||
|
primary: true,
|
||||||
|
source: winit::event::PointerSource::Mouse,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
self.cef_context.handle_window_event(&event);
|
self.cef_context.handle_window_event(&event);
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
|
|
@ -556,6 +586,13 @@ impl ApplicationHandler for App {
|
||||||
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
|
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WindowEvent::PointerMoved { position, .. } | WindowEvent::PointerLeft { position: Some(position), .. } | WindowEvent::PointerEntered { position, .. }
|
||||||
|
if self.pointer_lock_position.is_none() =>
|
||||||
|
{
|
||||||
|
self.pointer_position = position;
|
||||||
|
}
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -563,6 +600,15 @@ impl ApplicationHandler for App {
|
||||||
self.cef_context.work();
|
self.cef_context.work();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn device_event(&mut self, _event_loop: &dyn ActiveEventLoop, _device_id: Option<winit::event::DeviceId>, event: winit::event::DeviceEvent) {
|
||||||
|
if self.pointer_lock_position.is_some()
|
||||||
|
&& let winit::event::DeviceEvent::PointerMotion { delta: (x, y) } = event
|
||||||
|
{
|
||||||
|
let message = DesktopWrapperMessage::PointerLockMove { x, y };
|
||||||
|
self.app_event_scheduler.schedule(AppEvent::DesktopWrapperMessage(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) {
|
||||||
// Set a timeout in case we miss any cef schedule requests
|
// Set a timeout in case we miss any cef schedule requests
|
||||||
let timeout = Instant::now() + Duration::from_millis(10);
|
let timeout = Instant::now() + Duration::from_millis(10);
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,16 @@ impl Window {
|
||||||
self.winit_window.set_cursor(cursor);
|
self.winit_window.set_cursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn start_pointer_lock(&self) {
|
||||||
|
let _ = self.winit_window.set_cursor_grab(winit::window::CursorGrabMode::Locked);
|
||||||
|
self.winit_window.set_cursor_visible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn end_pointer_lock(&self) {
|
||||||
|
let _ = self.winit_window.set_cursor_grab(winit::window::CursorGrabMode::None);
|
||||||
|
self.winit_window.set_cursor_visible(true);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn update_menu(&self, entries: Vec<MenuItem>) {
|
pub(crate) fn update_menu(&self, entries: Vec<MenuItem>) {
|
||||||
self.native_handle.update_menu(entries);
|
self.native_handle.update_menu(entries);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -172,5 +172,9 @@ pub(super) fn handle_desktop_wrapper_message(dispatcher: &mut DesktopWrapperMess
|
||||||
dispatcher.queue_editor_message(message);
|
dispatcher.queue_editor_message(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DesktopWrapperMessage::PointerLockMove { x, y } => {
|
||||||
|
let message = AppWindowMessage::PointerLockMove { x, y };
|
||||||
|
dispatcher.queue_editor_message(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,9 @@ pub(super) fn intercept_frontend_message(dispatcher: &mut DesktopWrapperMessageD
|
||||||
FrontendMessage::TriggerClipboardWrite { content } => {
|
FrontendMessage::TriggerClipboardWrite { content } => {
|
||||||
dispatcher.respond(DesktopFrontendMessage::ClipboardWrite { content });
|
dispatcher.respond(DesktopFrontendMessage::ClipboardWrite { content });
|
||||||
}
|
}
|
||||||
|
FrontendMessage::WindowPointerLock => {
|
||||||
|
dispatcher.respond(DesktopFrontendMessage::PointerLock);
|
||||||
|
}
|
||||||
FrontendMessage::WindowClose => {
|
FrontendMessage::WindowClose => {
|
||||||
dispatcher.respond(DesktopFrontendMessage::WindowClose);
|
dispatcher.respond(DesktopFrontendMessage::WindowClose);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ pub enum DesktopFrontendMessage {
|
||||||
ClipboardWrite {
|
ClipboardWrite {
|
||||||
content: String,
|
content: String,
|
||||||
},
|
},
|
||||||
|
PointerLock,
|
||||||
WindowClose,
|
WindowClose,
|
||||||
WindowMinimize,
|
WindowMinimize,
|
||||||
WindowMaximize,
|
WindowMaximize,
|
||||||
|
|
@ -132,6 +133,10 @@ pub enum DesktopWrapperMessage {
|
||||||
ClipboardReadResult {
|
ClipboardReadResult {
|
||||||
content: Option<String>,
|
content: Option<String>,
|
||||||
},
|
},
|
||||||
|
PointerLockMove {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
|
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ use super::app_window_message_handler::AppWindowPlatform;
|
||||||
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum AppWindowMessage {
|
pub enum AppWindowMessage {
|
||||||
UpdatePlatform { platform: AppWindowPlatform },
|
UpdatePlatform { platform: AppWindowPlatform },
|
||||||
|
PointerLock,
|
||||||
|
PointerLockMove { x: f64, y: f64 },
|
||||||
Close,
|
Close,
|
||||||
Minimize,
|
Minimize,
|
||||||
Maximize,
|
Maximize,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ impl MessageHandler<AppWindowMessage, ()> for AppWindowMessageHandler {
|
||||||
self.platform = platform;
|
self.platform = platform;
|
||||||
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
|
responses.add(FrontendMessage::UpdatePlatform { platform: self.platform });
|
||||||
}
|
}
|
||||||
|
AppWindowMessage::PointerLock => {
|
||||||
|
responses.add(FrontendMessage::WindowPointerLock);
|
||||||
|
}
|
||||||
|
AppWindowMessage::PointerLockMove { x, y } => {
|
||||||
|
responses.add(FrontendMessage::WindowPointerLockMove { x, y });
|
||||||
|
}
|
||||||
AppWindowMessage::Close => {
|
AppWindowMessage::Close => {
|
||||||
responses.add(FrontendMessage::WindowClose);
|
responses.add(FrontendMessage::WindowClose);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -364,6 +364,11 @@ pub enum FrontendMessage {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Window prefix: cause the application window to do something
|
// Window prefix: cause the application window to do something
|
||||||
|
WindowPointerLock,
|
||||||
|
WindowPointerLockMove {
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
},
|
||||||
WindowClose,
|
WindowClose,
|
||||||
WindowMinimize,
|
WindowMinimize,
|
||||||
WindowMaximize,
|
WindowMaximize,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher, onMount, onDestroy } from "svelte";
|
import { createEventDispatcher, onMount, onDestroy, getContext } from "svelte";
|
||||||
|
|
||||||
import { evaluateMathExpression } from "@graphite/../wasm/pkg/graphite_wasm";
|
import { evaluateMathExpression } from "@graphite/../wasm/pkg/graphite_wasm";
|
||||||
|
import type { Editor } from "@graphite/editor";
|
||||||
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS } from "@graphite/io-managers/input";
|
import { PRESS_REPEAT_DELAY_MS, PRESS_REPEAT_INTERVAL_MS } from "@graphite/io-managers/input";
|
||||||
import type { NumberInputMode, NumberInputIncrementBehavior, ActionShortcut } from "@graphite/messages";
|
import type { NumberInputMode, NumberInputIncrementBehavior, ActionShortcut } from "@graphite/messages";
|
||||||
import { browserVersion, isDesktop } from "@graphite/utility-functions/platform";
|
import { browserVersion, isDesktop } from "@graphite/utility-functions/platform";
|
||||||
|
|
@ -16,6 +17,8 @@
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{ value: number | undefined; startHistoryTransaction: undefined }>();
|
const dispatch = createEventDispatcher<{ value: number | undefined; startHistoryTransaction: undefined }>();
|
||||||
|
|
||||||
|
const editor = getContext<Editor>("editor");
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
/// When `value` is not provided (i.e. it's `undefined`), a dash is displayed.
|
/// When `value` is not provided (i.e. it's `undefined`), a dash is displayed.
|
||||||
export let value: number | undefined = undefined;
|
export let value: number | undefined = undefined;
|
||||||
|
|
@ -80,6 +83,8 @@
|
||||||
let initialValueBeforeDragging: number | undefined = undefined;
|
let initialValueBeforeDragging: number | undefined = undefined;
|
||||||
// Stores the total value change during the process of dragging the slider. Set to 0 when not dragging.
|
// Stores the total value change during the process of dragging the slider. Set to 0 when not dragging.
|
||||||
let cumulativeDragDelta = 0;
|
let cumulativeDragDelta = 0;
|
||||||
|
// Track whether the Shift key is currently held down.
|
||||||
|
let shiftKeyDown = false;
|
||||||
// Track whether the Ctrl key is currently held down.
|
// Track whether the Ctrl key is currently held down.
|
||||||
let ctrlKeyDown = false;
|
let ctrlKeyDown = false;
|
||||||
|
|
||||||
|
|
@ -91,17 +96,20 @@
|
||||||
...(mode === "Range" ? { "--progress-factor": Math.min(Math.max((rangeSliderValueAsRendered - rangeMin) / (rangeMax - rangeMin), 0), 1) } : {}),
|
...(mode === "Range" ? { "--progress-factor": Math.min(Math.max((rangeSliderValueAsRendered - rangeMin) / (rangeMax - rangeMin), 0), 1) } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Keep track of the Ctrl key being held down.
|
// Keep track of the Shift and Ctrl key being held down.
|
||||||
const trackCtrl = (e: KeyboardEvent | MouseEvent) => (ctrlKeyDown = e.ctrlKey);
|
const trackShiftAndCtrl = (e: KeyboardEvent | MouseEvent) => {
|
||||||
|
shiftKeyDown = e.shiftKey;
|
||||||
|
ctrlKeyDown = e.ctrlKey;
|
||||||
|
};
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
addEventListener("keydown", trackCtrl);
|
addEventListener("keydown", trackShiftAndCtrl);
|
||||||
addEventListener("keyup", trackCtrl);
|
addEventListener("keyup", trackShiftAndCtrl);
|
||||||
addEventListener("mousemove", trackCtrl);
|
addEventListener("mousemove", trackShiftAndCtrl);
|
||||||
});
|
});
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
removeEventListener("keydown", trackCtrl);
|
removeEventListener("keydown", trackShiftAndCtrl);
|
||||||
removeEventListener("keyup", trackCtrl);
|
removeEventListener("keyup", trackShiftAndCtrl);
|
||||||
removeEventListener("mousemove", trackCtrl);
|
removeEventListener("mousemove", trackShiftAndCtrl);
|
||||||
clearTimeout(repeatTimeout);
|
clearTimeout(repeatTimeout);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -369,6 +377,9 @@
|
||||||
|
|
||||||
// Enter dragging state
|
// Enter dragging state
|
||||||
if (usePointerLock) target.requestPointerLock();
|
if (usePointerLock) target.requestPointerLock();
|
||||||
|
if (isDesktop()) {
|
||||||
|
editor.handle.appWindowPointerLock();
|
||||||
|
}
|
||||||
initialValueBeforeDragging = value;
|
initialValueBeforeDragging = value;
|
||||||
cumulativeDragDelta = 0;
|
cumulativeDragDelta = 0;
|
||||||
|
|
||||||
|
|
@ -412,19 +423,16 @@
|
||||||
|
|
||||||
// Calculate and then update the dragged value offset, slowed down by 10x when Shift is held.
|
// Calculate and then update the dragged value offset, slowed down by 10x when Shift is held.
|
||||||
if (ignoredFirstMovement && initialValueBeforeDragging !== undefined) {
|
if (ignoredFirstMovement && initialValueBeforeDragging !== undefined) {
|
||||||
const CHANGE_PER_DRAG_PX = 0.1;
|
pointerLockMoveUpdate(e.movementX, e.shiftKey, e.ctrlKey, initialValueBeforeDragging);
|
||||||
const CHANGE_PER_DRAG_PX_SLOW = CHANGE_PER_DRAG_PX / 10;
|
}
|
||||||
|
ignoredFirstMovement = true;
|
||||||
const dragDelta = e.movementX * (e.shiftKey ? CHANGE_PER_DRAG_PX_SLOW : CHANGE_PER_DRAG_PX);
|
};
|
||||||
cumulativeDragDelta += dragDelta;
|
// On desktop we don't get `pointermove` events while in pointer lock (cef doesn't support pointer lock).
|
||||||
|
// We have to listen for our custom `pointerlockmove` events instead.
|
||||||
const combined = initialValueBeforeDragging + cumulativeDragDelta;
|
const pointerLockMove = (e: Event) => {
|
||||||
const combineSnapped = e.ctrlKey ? Math.round(combined) : combined;
|
if (ignoredFirstMovement && initialValueBeforeDragging !== undefined && e instanceof CustomEvent) {
|
||||||
|
const delta = (e.detail as { x: number }).x;
|
||||||
const newValue = updateValue(combineSnapped);
|
pointerLockMoveUpdate(delta, shiftKeyDown, ctrlKeyDown, initialValueBeforeDragging);
|
||||||
|
|
||||||
// If the value was altered within the `updateValue()` call, we need to rectify the cumulative drag delta to account for the change.
|
|
||||||
if (newValue !== undefined) cumulativeDragDelta -= combineSnapped - newValue;
|
|
||||||
}
|
}
|
||||||
ignoredFirstMovement = true;
|
ignoredFirstMovement = true;
|
||||||
};
|
};
|
||||||
|
|
@ -443,14 +451,32 @@
|
||||||
// Clean up the event listeners.
|
// Clean up the event listeners.
|
||||||
removeEventListener("pointerup", pointerUp);
|
removeEventListener("pointerup", pointerUp);
|
||||||
removeEventListener("pointermove", pointerMove);
|
removeEventListener("pointermove", pointerMove);
|
||||||
|
removeEventListener("pointerlockmove", pointerLockMove);
|
||||||
if (usePointerLock) document.removeEventListener("pointerlockchange", pointerLockChange);
|
if (usePointerLock) document.removeEventListener("pointerlockchange", pointerLockChange);
|
||||||
};
|
};
|
||||||
|
|
||||||
addEventListener("pointerup", pointerUp);
|
addEventListener("pointerup", pointerUp);
|
||||||
addEventListener("pointermove", pointerMove);
|
addEventListener("pointermove", pointerMove);
|
||||||
|
addEventListener("pointerlockmove", pointerLockMove);
|
||||||
if (usePointerLock) document.addEventListener("pointerlockchange", pointerLockChange);
|
if (usePointerLock) document.addEventListener("pointerlockchange", pointerLockChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pointerLockMoveUpdate(delta: number, slow: boolean, snapping: boolean, initialValue: number) {
|
||||||
|
const CHANGE_PER_DRAG_PX = 0.1;
|
||||||
|
const CHANGE_PER_DRAG_PX_SLOW = CHANGE_PER_DRAG_PX / 10;
|
||||||
|
|
||||||
|
const dragDelta = delta * (slow ? CHANGE_PER_DRAG_PX_SLOW : CHANGE_PER_DRAG_PX);
|
||||||
|
cumulativeDragDelta += dragDelta;
|
||||||
|
|
||||||
|
const combined = initialValue + cumulativeDragDelta;
|
||||||
|
const combineSnapped = snapping ? Math.round(combined) : combined;
|
||||||
|
|
||||||
|
const newValue = updateValue(combineSnapped);
|
||||||
|
|
||||||
|
// If the value was altered within the `updateValue()` call, we need to rectify the cumulative drag delta to account for the change.
|
||||||
|
if (newValue !== undefined) cumulativeDragDelta -= combineSnapped - newValue;
|
||||||
|
}
|
||||||
|
|
||||||
// ===============================
|
// ===============================
|
||||||
// RANGE MODE: DRAGGING THE SLIDER
|
// RANGE MODE: DRAGGING THE SLIDER
|
||||||
// ===============================
|
// ===============================
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
import { type Editor } from "@graphite/editor";
|
import { type Editor } from "@graphite/editor";
|
||||||
import { TriggerClipboardRead } from "@graphite/messages";
|
import { TriggerClipboardRead, WindowPointerLockMove } from "@graphite/messages";
|
||||||
import { type DialogState } from "@graphite/state-providers/dialog";
|
import { type DialogState } from "@graphite/state-providers/dialog";
|
||||||
import { type DocumentState } from "@graphite/state-providers/document";
|
import { type DocumentState } from "@graphite/state-providers/document";
|
||||||
import { type FullscreenState } from "@graphite/state-providers/fullscreen";
|
import { type FullscreenState } from "@graphite/state-providers/fullscreen";
|
||||||
|
|
@ -500,6 +500,12 @@ export function createInputManager(editor: Editor, dialog: DialogState, portfoli
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pointer lock movement events on desktop
|
||||||
|
editor.subscriptions.subscribeJsMessage(WindowPointerLockMove, (data) => {
|
||||||
|
const event = new CustomEvent("pointerlockmove", { detail: data });
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
});
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
|
|
||||||
function potentiallyRestoreCanvasFocus(e: Event) {
|
function potentiallyRestoreCanvasFocus(e: Event) {
|
||||||
|
|
|
||||||
|
|
@ -315,8 +315,6 @@ export class UpdateFullscreen extends JsMessage {
|
||||||
readonly fullscreen!: boolean;
|
readonly fullscreen!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CloseWindow extends JsMessage {}
|
|
||||||
|
|
||||||
export class UpdateViewportHolePunch extends JsMessage {
|
export class UpdateViewportHolePunch extends JsMessage {
|
||||||
readonly active!: boolean;
|
readonly active!: boolean;
|
||||||
}
|
}
|
||||||
|
|
@ -332,6 +330,11 @@ export class UpdateUIScale extends JsMessage {
|
||||||
readonly scale!: number;
|
readonly scale!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class WindowPointerLockMove extends JsMessage {
|
||||||
|
readonly x!: number;
|
||||||
|
readonly y!: number;
|
||||||
|
}
|
||||||
|
|
||||||
// Rust enum `Key`
|
// Rust enum `Key`
|
||||||
export type KeyRaw = string;
|
export type KeyRaw = string;
|
||||||
// Serde converts a Rust `Key` enum variant into this format with both the `Key` variant name (called `RawKey` in TS) and the localized `label` for the key
|
// Serde converts a Rust `Key` enum variant into this format with both the `Key` variant name (called `RawKey` in TS) and the localized `label` for the key
|
||||||
|
|
@ -1728,6 +1731,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
UpdatePlatform,
|
UpdatePlatform,
|
||||||
UpdateMaximized,
|
UpdateMaximized,
|
||||||
UpdateFullscreen,
|
UpdateFullscreen,
|
||||||
|
WindowPointerLockMove,
|
||||||
UpdatePropertiesPanelLayout,
|
UpdatePropertiesPanelLayout,
|
||||||
UpdatePropertiesPanelState,
|
UpdatePropertiesPanelState,
|
||||||
UpdateStatusBarHintsLayout,
|
UpdateStatusBarHintsLayout,
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,13 @@ impl EditorHandle {
|
||||||
self.dispatch(NodeGraphMessage::AddSecondaryExport);
|
self.dispatch(NodeGraphMessage::AddSecondaryExport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Start Pointer Lock
|
||||||
|
#[wasm_bindgen(js_name = appWindowPointerLock)]
|
||||||
|
pub fn app_window_pointer_lock(&self) {
|
||||||
|
let message = AppWindowMessage::PointerLock;
|
||||||
|
self.dispatch(message);
|
||||||
|
}
|
||||||
|
|
||||||
/// Minimizes the application window to the taskbar or dock
|
/// Minimizes the application window to the taskbar or dock
|
||||||
#[wasm_bindgen(js_name = appWindowMinimize)]
|
#[wasm_bindgen(js_name = appWindowMinimize)]
|
||||||
pub fn app_window_minimize(&self) {
|
pub fn app_window_minimize(&self) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue