diff --git a/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt b/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt index 6c4b5505..e96a7956 100644 --- a/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt +++ b/web/src/main/kotlin/world/phantasmal/web/questEditor/loading/AreaAssetLoader.kt @@ -383,6 +383,7 @@ class AreaAssetLoader(private val assetLoader: AssetLoader) : DisposableContaine 120, 121, 122, + 137, 155, 156, 170, @@ -430,6 +431,7 @@ class AreaAssetLoader(private val assetLoader: AssetLoader) : DisposableContaine "s_m_0_3l_6_igzvga", "s_n_0_en_3_iiawrz", "a_k_0_1k_3_ihdi9s", + "s_n_0_c_1_jbhy8b", ), ), // Forest 1 diff --git a/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt b/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt index e5bd30d4..9e0c92e3 100644 --- a/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt +++ b/web/src/main/kotlin/world/phantasmal/web/questEditor/rendering/input/QuestInputManager.kt @@ -1,6 +1,9 @@ package world.phantasmal.web.questEditor.rendering.input +import kotlinx.browser.document import kotlinx.browser.window +import org.w3c.dom.HTMLElement +import org.w3c.dom.events.Event import org.w3c.dom.events.FocusEvent import org.w3c.dom.events.KeyboardEvent import org.w3c.dom.pointerevents.PointerEvent @@ -30,6 +33,13 @@ class QuestInputManager( private var state: State private var onPointerUpListener: Disposable? = null private var onPointerMoveListener: Disposable? = null + private var contextMenuListener: Disposable? = null + private val pointerDragging: Boolean get() = onPointerUpListener != null + + /** + * Prevents events from triggering while dragging the pointer. + */ + private val pointerTrap = document.createElement("div") as HTMLElement private val cameraInputManager: OrbitalCameraInputManager @@ -76,12 +86,26 @@ class QuestInputManager( observe(questEditorStore.selectedEntity) { returnToIdleState() } observe(questEditorStore.questEditingEnabled) { entityManipulationEnabled = it } + + pointerTrap.className = "pw-quest-editor-input-manager-pointer-trap" + pointerTrap.hidden = true + pointerTrap.style.zIndex = "1000" + pointerTrap.style.position = "fixed" + pointerTrap.style.left = "0" + pointerTrap.style.top = "0" + pointerTrap.style.width = "100%" + pointerTrap.style.height = "100%" + pointerTrap.addEventListener("contextmenu", ::onContextMenu) + + window.document.body?.appendChild(pointerTrap) } override fun dispose() { cameraInputManager.dispose() onPointerUpListener?.dispose() onPointerMoveListener?.dispose() + contextMenuListener?.dispose() + window.document.body?.removeChild(pointerTrap) super.dispose() } @@ -120,6 +144,13 @@ class QuestInputManager( // Stop listening to canvas move events and start listening to window move events. onPointerMoveListener?.dispose() onPointerMoveListener = window.disposableListener("pointermove", ::onPointerMove) + + pointerTrap.hidden = false + // Add this listener in addition to the pointer trap to avoid context menu from triggering + // when dragging and releasing the pointer in a different window. + if (contextMenuListener == null) { + contextMenuListener = window.disposableListener("contextmenu", ::onContextMenu) + } } private fun onPointerUp(e: PointerEvent) { @@ -143,6 +174,14 @@ class QuestInputManager( onPointerMoveListener?.dispose() onPointerMoveListener = renderContext.canvas.disposableListener("pointermove", ::onPointerMove) + + window.setTimeout({ + if (!pointerDragging) { + pointerTrap.hidden = true + contextMenuListener?.dispose() + contextMenuListener = null + } + }, 0) } } @@ -207,7 +246,15 @@ class QuestInputManager( state = state.processEvent(EntityDropEvt(e, pointerDevicePosition)) } + // Avoid context menu from popping up when dragging and releasing mouse outside of 3D view. + private fun onContextMenu(e: Event) { + e.preventDefault() + e.stopPropagation() + } + private fun processPointerEvent(e: PointerEvent) { + e.stopPropagation() + processPointerEvent(e.type, e.clientX, e.clientY) }