diff --git a/GDJS/Runtime/InGameEditor/InGameEditor.ts b/GDJS/Runtime/InGameEditor/InGameEditor.ts index c97f07d013..ea5daec876 100644 --- a/GDJS/Runtime/InGameEditor/InGameEditor.ts +++ b/GDJS/Runtime/InGameEditor/InGameEditor.ts @@ -30,6 +30,8 @@ namespace gdjs { const Q_KEY = 81; const E_KEY = 69; const F_KEY = 70; + const Y_KEY = 90; + const Z_KEY = 90; let hasWindowFocus = true; if (typeof window !== 'undefined') { @@ -111,6 +113,22 @@ namespace gdjs { ); }; + const isControlPressedOnly = (inputManager: gdjs.InputManager) => { + return ( + isControlOrCmdPressed(inputManager) && + !isShiftPressed(inputManager) && + !isAltPressed(inputManager) + ); + }; + + const isControlPlusShiftPressedOnly = (inputManager: gdjs.InputManager) => { + return ( + isControlOrCmdPressed(inputManager) && + isShiftPressed(inputManager) && + !isAltPressed(inputManager) + ); + }; + const isSpacePressed = (inputManager: gdjs.InputManager) => inputManager.isKeyPressed(SPACE_KEY); @@ -436,6 +454,10 @@ namespace gdjs { this._editorId = editorId; } + getEditedInstanceDataList(): InstanceData[] { + return this._editedInstanceDataList; + } + setEditedInstanceDataList(editedInstanceDataList: InstanceData[]) { this._editedInstanceDataList = editedInstanceDataList; } @@ -1502,6 +1524,34 @@ namespace gdjs { debuggerClient.sendOpenContextMenu(cursorX, cursorY); } + private _handleShortcuts() { + const inputManager = this._runtimeGame.getInputManager(); + if (isControlPressedOnly(inputManager)) { + if (inputManager.wasKeyReleased(Z_KEY)) { + this._sendUndo(); + } else if (inputManager.wasKeyReleased(Y_KEY)) { + this._sendRedo(); + } + } + if (isControlPlusShiftPressedOnly(inputManager)) { + if (inputManager.wasKeyReleased(Z_KEY)) { + this._sendRedo(); + } + } + } + + private _sendUndo() { + const debuggerClient = this._runtimeGame._debuggerClient; + if (!debuggerClient) return; + debuggerClient.sendUndo(); + } + + private _sendRedo() { + const debuggerClient = this._runtimeGame._debuggerClient; + if (!debuggerClient) return; + debuggerClient.sendRedo(); + } + cancelDragNewInstance() { const editedInstanceContainer = this._getEditedInstanceContainer(); if (!editedInstanceContainer) return; @@ -1932,6 +1982,7 @@ namespace gdjs { this._updateSelectionControls(); this._updateInnerAreaOutline(); this._handleContextMenu(); + this._handleShortcuts(); this._wasMovingSelectionLastFrame = !!this._selectionControlsMovementTotalDelta; if (!this._selectionControlsMovementTotalDelta) { diff --git a/GDJS/Runtime/debugger-client/abstract-debugger-client.ts b/GDJS/Runtime/debugger-client/abstract-debugger-client.ts index 394ef788a6..4763a44c69 100644 --- a/GDJS/Runtime/debugger-client/abstract-debugger-client.ts +++ b/GDJS/Runtime/debugger-client/abstract-debugger-client.ts @@ -298,6 +298,18 @@ namespace gdjs { ); } } + } else if (data.command === 'hotReloadAllInstances') { + if (runtimeGame._inGameEditor) { + const editedInstanceContainer = + runtimeGame._inGameEditor._getEditedInstanceContainer(); + if (editedInstanceContainer) { + that._hotReloader.hotReloadRuntimeInstances( + runtimeGame._inGameEditor.getEditedInstanceDataList(), + data.payload.instances, + editedInstanceContainer + ); + } + } } else if (data.command === 'switchForInGameEdition') { if (!this._runtimegame.isInGameEdition()) return; @@ -738,6 +750,34 @@ namespace gdjs { ); } + sendUndo(): void { + const inGameEditor = this._runtimegame._inGameEditor; + if (!inGameEditor) { + return; + } + this._sendMessage( + circularSafeStringify({ + command: 'undo', + editorId: inGameEditor.getEditorId(), + payload: {}, + }) + ); + } + + sendRedo(): void { + const inGameEditor = this._runtimegame._inGameEditor; + if (!inGameEditor) { + return; + } + this._sendMessage( + circularSafeStringify({ + command: 'redo', + editorId: inGameEditor.getEditorId(), + payload: {}, + }) + ); + } + /** * Send profiling results. * @param framesAverageMeasures The measures made for each frames. diff --git a/GDJS/Runtime/debugger-client/hot-reloader.ts b/GDJS/Runtime/debugger-client/hot-reloader.ts index 8a22fe26d4..a88804eab9 100644 --- a/GDJS/Runtime/debugger-client/hot-reloader.ts +++ b/GDJS/Runtime/debugger-client/hot-reloader.ts @@ -1451,6 +1451,28 @@ namespace gdjs { } } + hotReloadRuntimeInstances( + oldInstances: InstanceData[], + newInstances: InstanceData[], + runtimeInstanceContainer: RuntimeInstanceContainer + ): void { + const projectData: ProjectData = gdjs.projectData; + const objects: Array = []; + runtimeInstanceContainer._objects.values(objects); + projectData.layouts; + this._hotReloadRuntimeSceneInstances( + projectData, + projectData, + [], + objects, + objects, + oldInstances, + newInstances, + runtimeInstanceContainer + ); + gdjs.copyArray(oldInstances, newInstances); + } + _hotReloadRuntimeSceneInstances( oldProjectData: ProjectData, newProjectData: ProjectData, diff --git a/newIDE/app/src/SceneEditor/index.js b/newIDE/app/src/SceneEditor/index.js index eadedccc82..5fe69de21f 100644 --- a/newIDE/app/src/SceneEditor/index.js +++ b/newIDE/app/src/SceneEditor/index.js @@ -307,6 +307,14 @@ export default class SceneEditor extends React.Component { parsedMessage.payload.cursorX, parsedMessage.payload.cursorY ); + } else if (parsedMessage.command === 'undo') { + if (canUndo(this.state.history)) { + this.undo(); + } + } else if (parsedMessage.command === 'redo') { + if (canRedo(this.state.history)) { + this.redo(); + } } }, } @@ -782,6 +790,7 @@ export default class SceneEditor extends React.Component { if (this.editorDisplay) this.editorDisplay.instancesHandlers.forceRemountInstancesRenderers(); this.updateToolbar(); + this._sendHotReloadAllInstances(); } ); }; @@ -798,10 +807,25 @@ export default class SceneEditor extends React.Component { if (this.editorDisplay) this.editorDisplay.instancesHandlers.forceRemountInstancesRenderers(); this.updateToolbar(); + this._sendHotReloadAllInstances(); } ); }; + _sendHotReloadAllInstances = () => { + const { previewDebuggerServer } = this.props; + if (!previewDebuggerServer) return; + + previewDebuggerServer.getExistingDebuggerIds().forEach(debuggerId => { + previewDebuggerServer.sendMessage(debuggerId, { + command: 'hotReloadAllInstances', + payload: { + instances: serializeToJSObject(this.props.initialInstances), + }, + }); + }); + }; + _onObjectFolderOrObjectWithContextSelected = ( objectFolderOrObjectWithContext: ?ObjectFolderOrObjectWithContext = null ) => {