mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
WIP: Handle deletion from in-game editor
This commit is contained in:
@@ -4,6 +4,8 @@ namespace gdjs {
|
||||
const RIGHT_KEY = 39;
|
||||
const DOWN_KEY = 40;
|
||||
const ALT_KEY = 18;
|
||||
const DEL_KEY = 46;
|
||||
const BACKSPACE_KEY = 8;
|
||||
const LEFT_ALT_KEY = gdjs.InputManager.getLocationAwareKeyCode(ALT_KEY, 1);
|
||||
const RIGHT_ALT_KEY = gdjs.InputManager.getLocationAwareKeyCode(ALT_KEY, 2);
|
||||
const SHIFT_KEY = 16;
|
||||
@@ -57,6 +59,13 @@ namespace gdjs {
|
||||
);
|
||||
};
|
||||
|
||||
const shouldDeleteSelection = (inputManager: gdjs.InputManager) => {
|
||||
return (
|
||||
inputManager.isKeyPressed(DEL_KEY) ||
|
||||
inputManager.isKeyPressed(BACKSPACE_KEY)
|
||||
);
|
||||
};
|
||||
|
||||
const shouldScrollHorizontally = isAltPressed;
|
||||
|
||||
const shouldZoom = (inputManager: gdjs.InputManager) => {
|
||||
@@ -90,6 +99,7 @@ namespace gdjs {
|
||||
} | null = null;
|
||||
private _lastCursorX: number = 0;
|
||||
private _lastCursorY: number = 0;
|
||||
private _wasManipulatingSelectionLastFrame = false;
|
||||
private _isManipulatingSelection = false;
|
||||
private _selectedObjects: Array<gdjs.RuntimeObject> = [];
|
||||
|
||||
@@ -175,7 +185,17 @@ namespace gdjs {
|
||||
}: {
|
||||
objectUnderCursor: ObjectUnderCursor | null;
|
||||
}) {
|
||||
const currentScene = this._runtimeGame.getSceneStack().getCurrentScene();
|
||||
const inputManager = this._runtimeGame.getInputManager();
|
||||
if (!currentScene) return;
|
||||
|
||||
if (
|
||||
this._wasManipulatingSelectionLastFrame &&
|
||||
!this._isManipulatingSelection
|
||||
) {
|
||||
// Just finished dragging/editing the selection.
|
||||
this._sendSelectionUpdate();
|
||||
}
|
||||
|
||||
// Left click: select the object under the cursor.
|
||||
if (
|
||||
@@ -193,6 +213,19 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDeleteSelection(inputManager)) {
|
||||
const removedObjects = this._selectedObjects;
|
||||
removedObjects.forEach((object) => {
|
||||
object.deleteFromScene(currentScene);
|
||||
});
|
||||
this._selectedObjects = [];
|
||||
this._sendSelectionUpdate({
|
||||
removedObjects,
|
||||
});
|
||||
}
|
||||
|
||||
this._wasManipulatingSelectionLastFrame = this._isManipulatingSelection;
|
||||
}
|
||||
|
||||
private _updateSelectionOutline({
|
||||
@@ -289,8 +322,6 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
this._isManipulatingSelection = false;
|
||||
|
||||
this._sendSelectionUpdate();
|
||||
});
|
||||
|
||||
this._currentTransformControls = {
|
||||
@@ -312,23 +343,25 @@ namespace gdjs {
|
||||
}
|
||||
}
|
||||
|
||||
private _sendSelectionUpdate() {
|
||||
private _sendSelectionUpdate(options?: {
|
||||
removedObjects: Array<gdjs.RuntimeObject>;
|
||||
}) {
|
||||
const debuggerClient = this._runtimeGame._debuggerClient;
|
||||
if (!debuggerClient) return;
|
||||
|
||||
const instancesSelection = this._selectedObjects
|
||||
const getPersistentUuidsFromObjects = (
|
||||
objects: Array<gdjs.RuntimeObject>
|
||||
): Array<InstancePersistentUuidData> =>
|
||||
objects
|
||||
.map((object) => {
|
||||
if (!object.persistentUuid) return null;
|
||||
|
||||
return { persistentUuid: object.persistentUuid };
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
const updatedInstances = this._selectedObjects
|
||||
.map((object) => {
|
||||
if (!object.persistentUuid) return null;
|
||||
|
||||
return { persistentUuid: object.persistentUuid };
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
const instanceUpdates = this._selectedObjects
|
||||
.map((object) => {
|
||||
const rendererObject = object.getRendererObject();
|
||||
if (!rendererObject) return null;
|
||||
|
||||
if (object instanceof gdjs.RuntimeObject3D) {
|
||||
if (!object.persistentUuid) return null;
|
||||
|
||||
@@ -353,9 +386,12 @@ namespace gdjs {
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
debuggerClient.sendInstancesUpdated({
|
||||
instanceUpdates,
|
||||
instancesSelection,
|
||||
debuggerClient.sendInstanceChanges({
|
||||
updatedInstances,
|
||||
selectedInstances: getPersistentUuidsFromObjects(this._selectedObjects),
|
||||
removedInstances: options
|
||||
? getPersistentUuidsFromObjects(options.removedObjects)
|
||||
: [],
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -637,16 +637,15 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
|
||||
sendInstancesUpdated(update: {
|
||||
instanceUpdates: Array<InstanceData>;
|
||||
instancesSelection: Array<{
|
||||
persistentUuid: string;
|
||||
}>;
|
||||
sendInstanceChanges(changes: {
|
||||
updatedInstances: Array<InstanceData>;
|
||||
selectedInstances: Array<InstancePersistentUuidData>;
|
||||
removedInstances: Array<InstancePersistentUuidData>;
|
||||
}): void {
|
||||
this._sendMessage(
|
||||
circularSafeStringify({
|
||||
command: 'updateInstances',
|
||||
payload: update,
|
||||
payload: changes,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
4
GDJS/Runtime/types/project-data.d.ts
vendored
4
GDJS/Runtime/types/project-data.d.ts
vendored
@@ -238,9 +238,11 @@ declare interface ExternalLayoutData {
|
||||
instances: InstanceData[];
|
||||
}
|
||||
|
||||
declare interface InstanceData {
|
||||
declare interface InstancePersistentUuidData {
|
||||
persistentUuid: string;
|
||||
}
|
||||
|
||||
declare interface InstanceData extends InstancePersistentUuidData {
|
||||
layer: string;
|
||||
locked: boolean;
|
||||
name: string;
|
||||
|
@@ -100,17 +100,19 @@ export const localPreviewDebuggerServer: PreviewDebuggerServer = {
|
||||
|
||||
ipcRenderer.on('debugger-message-received', (event, { id, message }) => {
|
||||
console.info('Processing message received for debugger');
|
||||
let parsedMessage = null;
|
||||
try {
|
||||
const parsedMessage = JSON.parse(message);
|
||||
callbacksList.forEach(({ onHandleParsedMessage }) =>
|
||||
onHandleParsedMessage({ id, parsedMessage })
|
||||
);
|
||||
parsedMessage = JSON.parse(message);
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
'Error while parsing message received from debugger client:',
|
||||
e
|
||||
);
|
||||
}
|
||||
|
||||
callbacksList.forEach(({ onHandleParsedMessage }) =>
|
||||
onHandleParsedMessage({ id, parsedMessage })
|
||||
);
|
||||
});
|
||||
ipcRenderer.send('debugger-start-server');
|
||||
});
|
||||
|
@@ -90,6 +90,16 @@ const gd: libGDevelop = global.gd;
|
||||
const BASE_LAYER_NAME = '';
|
||||
const INSTANCES_CLIPBOARD_KIND = 'Instances';
|
||||
|
||||
type InstancePersistentUuidData = {|
|
||||
persistentUuid: string,
|
||||
|};
|
||||
|
||||
type InstanceChanges = {|
|
||||
updatedInstances: Array<any>, // TODO
|
||||
selectedInstances: Array<InstancePersistentUuidData>,
|
||||
removedInstances: Array<InstancePersistentUuidData>,
|
||||
|};
|
||||
|
||||
export type EditorId =
|
||||
| 'objects-list'
|
||||
| 'properties'
|
||||
@@ -263,7 +273,11 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
onConnectionOpened: () => {},
|
||||
onConnectionErrored: () => {},
|
||||
onServerStateChanged: () => {},
|
||||
onHandleParsedMessage: this.onReceiveMessageFromGame.bind(this),
|
||||
onHandleParsedMessage: ({ id, parsedMessage }) => {
|
||||
if (parsedMessage.command === 'updateInstances') {
|
||||
this.onReceiveInstanceChanges(parsedMessage.payload);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -281,54 +295,78 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
return this.state.instancesEditorSettings;
|
||||
}
|
||||
|
||||
onReceiveMessageFromGame({
|
||||
id,
|
||||
parsedMessage,
|
||||
}: {
|
||||
id: number,
|
||||
parsedMessage: {| command: string, payload: any |},
|
||||
}) {
|
||||
onReceiveInstanceChanges(changes: InstanceChanges) {
|
||||
// TODO: ensure this works for external layouts too.
|
||||
if (parsedMessage.command === 'updateInstances') {
|
||||
// TODO: adapt this to get all instances in one shot.
|
||||
const modifiedInstances: gdInitialInstance[] = [];
|
||||
parsedMessage.payload.instanceUpdates.forEach(instanceData => {
|
||||
const { persistentUuid, x, y, z } = instanceData;
|
||||
|
||||
// TODO: adapt all of this to get all instances in one shot.
|
||||
const modifiedInstances: gdInitialInstance[] = [];
|
||||
changes.updatedInstances.forEach(instanceData => {
|
||||
const { persistentUuid, x, y, z } = instanceData;
|
||||
const instance = getInstanceInLayoutWithPersistentUuid(
|
||||
this.props.initialInstances,
|
||||
persistentUuid
|
||||
);
|
||||
if (!instance) return;
|
||||
instance.setX(x);
|
||||
instance.setY(y);
|
||||
instance.setZ(z);
|
||||
|
||||
modifiedInstances.push(instance);
|
||||
});
|
||||
this._onInstancesMoved(modifiedInstances);
|
||||
|
||||
const newlySelectedInstances = changes.selectedInstances
|
||||
.map(selectedInstanceData => {
|
||||
const { persistentUuid } = selectedInstanceData;
|
||||
const instance = getInstanceInLayoutWithPersistentUuid(
|
||||
this.props.initialInstances,
|
||||
persistentUuid
|
||||
);
|
||||
if (!instance) return;
|
||||
instance.setX(x);
|
||||
instance.setY(y);
|
||||
instance.setZ(z);
|
||||
return instance || null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
modifiedInstances.push(instance);
|
||||
});
|
||||
this._onInstancesMoved(modifiedInstances);
|
||||
const justRemovedInstances = changes.removedInstances
|
||||
.map(removedInstanceData => {
|
||||
const { persistentUuid } = removedInstanceData;
|
||||
const instance = getInstanceInLayoutWithPersistentUuid(
|
||||
this.props.initialInstances,
|
||||
persistentUuid
|
||||
);
|
||||
return instance || null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
console.log("payload", parsedMessage.payload);
|
||||
const newlySelectedInstances = parsedMessage.payload.instancesSelection
|
||||
.map(instanceSelectionData => {
|
||||
const { persistentUuid } = instanceSelectionData;
|
||||
console.log('selecting', persistentUuid);
|
||||
const instance = getInstanceInLayoutWithPersistentUuid(
|
||||
justRemovedInstances.forEach(instance => {
|
||||
this.props.initialInstances.removeInstance(instance);
|
||||
});
|
||||
if (justRemovedInstances.length) {
|
||||
this.setState(
|
||||
{
|
||||
selectedObjectFolderOrObjectsWithContext: [],
|
||||
history: saveToHistory(
|
||||
this.state.history,
|
||||
this.props.initialInstances,
|
||||
persistentUuid
|
||||
);
|
||||
return instance || null;
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
this.instancesSelection.selectInstances({
|
||||
instances: newlySelectedInstances,
|
||||
multiSelect: false,
|
||||
layersLocks: null,
|
||||
ignoreSeal: true
|
||||
});
|
||||
this.setState({ lastSelectionType: 'instance' });
|
||||
this.updateToolbar();
|
||||
'DELETE'
|
||||
),
|
||||
},
|
||||
() => {
|
||||
this.updateToolbar();
|
||||
this.forceUpdatePropertiesEditor();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Selecting', newlySelectedInstances.length);
|
||||
console.log("Changes:", changes)
|
||||
this.instancesSelection.selectInstances({
|
||||
instances: newlySelectedInstances,
|
||||
multiSelect: false,
|
||||
layersLocks: null,
|
||||
ignoreSeal: true,
|
||||
});
|
||||
this.setState({ lastSelectionType: 'instance' });
|
||||
this.updateToolbar();
|
||||
}
|
||||
|
||||
onResourceExternallyChanged = async (resourceInfo: {|
|
||||
|
Reference in New Issue
Block a user