mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
7 Commits
a05b0cd0ef
...
dynamical-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
21b36b8798 | ||
![]() |
9b09aa2e51 | ||
![]() |
12e7af4952 | ||
![]() |
66005be895 | ||
![]() |
a7f524b3a7 | ||
![]() |
a1178364e6 | ||
![]() |
4c6bc2c1e3 |
@@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
import { roundPosition } from '../Utils/GridHelpers';
|
||||
import { unserializeFromJSObject } from '../Utils/Serializer';
|
||||
import { type InstancesEditorSettings } from './InstancesEditorSettings';
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
@@ -51,6 +52,67 @@ export default class InstancesAdder {
|
||||
this._instancesEditorSettings = instancesEditorSettings;
|
||||
}
|
||||
|
||||
addSerializedInstances = ({
|
||||
position,
|
||||
copyReferential,
|
||||
serializedInstances,
|
||||
preventSnapToGrid = false,
|
||||
addInstancesInTheForeground = false,
|
||||
}: {|
|
||||
position: [number, number],
|
||||
copyReferential: [number, number],
|
||||
serializedInstances: Array<Object>,
|
||||
preventSnapToGrid?: boolean,
|
||||
addInstancesInTheForeground?: boolean,
|
||||
|}): Array<gdInitialInstance> => {
|
||||
this._zOrderFinder.reset();
|
||||
this._instances.iterateOverInstances(this._zOrderFinder);
|
||||
const sceneForegroundZOrder = this._zOrderFinder.getHighestZOrder() + 1;
|
||||
|
||||
let addedInstancesLowestZOrder = null;
|
||||
|
||||
const newInstances = serializedInstances.map(serializedInstance => {
|
||||
const instance = new gd.InitialInstance();
|
||||
unserializeFromJSObject(instance, serializedInstance);
|
||||
const desiredPosition = [
|
||||
instance.getX() - copyReferential[0] + position[0],
|
||||
instance.getY() - copyReferential[1] + position[1],
|
||||
];
|
||||
const newPos = preventSnapToGrid
|
||||
? desiredPosition
|
||||
: roundPositionsToGrid(desiredPosition, this._instancesEditorSettings);
|
||||
instance.setX(newPos[0]);
|
||||
instance.setY(newPos[1]);
|
||||
if (addInstancesInTheForeground) {
|
||||
if (
|
||||
addedInstancesLowestZOrder === null ||
|
||||
addedInstancesLowestZOrder > instance.getZOrder()
|
||||
) {
|
||||
addedInstancesLowestZOrder = instance.getZOrder();
|
||||
}
|
||||
}
|
||||
const newInstance = this._instances
|
||||
.insertInitialInstance(instance)
|
||||
.resetPersistentUuid();
|
||||
instance.delete();
|
||||
return newInstance;
|
||||
});
|
||||
|
||||
if (addInstancesInTheForeground && addedInstancesLowestZOrder !== null) {
|
||||
newInstances.forEach(instance => {
|
||||
instance.setZOrder(
|
||||
instance.getZOrder() -
|
||||
// Flow is not happy with addedInstancesLowestZOrder possible null value
|
||||
// so 0 is used as a fallback.
|
||||
(addedInstancesLowestZOrder || 0) +
|
||||
sceneForegroundZOrder
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return newInstances;
|
||||
};
|
||||
|
||||
/**
|
||||
* Immediately create new instance at the specified position
|
||||
* (specified in scene coordinates).
|
||||
|
@@ -27,6 +27,7 @@ type Props = {|
|
||||
onRotateEnd: () => void,
|
||||
toCanvasCoordinates: (x: number, y: number) => [number, number],
|
||||
screenType: ScreenType,
|
||||
showHandles: boolean,
|
||||
|};
|
||||
|
||||
const getButtonSizes = (screenType: ScreenType) => {
|
||||
@@ -77,6 +78,7 @@ export default class SelectedInstances {
|
||||
onRotateEnd: () => void;
|
||||
toCanvasCoordinates: (x: number, y: number) => [number, number];
|
||||
_screenType: ScreenType;
|
||||
showHandles: boolean;
|
||||
|
||||
pixiContainer = new PIXI.Container();
|
||||
rectanglesContainer = new PIXI.Container();
|
||||
@@ -93,6 +95,7 @@ export default class SelectedInstances {
|
||||
onRotateEnd,
|
||||
toCanvasCoordinates,
|
||||
screenType,
|
||||
showHandles,
|
||||
}: Props) {
|
||||
this.instanceMeasurer = instanceMeasurer;
|
||||
this.onResize = onResize;
|
||||
@@ -102,33 +105,36 @@ export default class SelectedInstances {
|
||||
this.toCanvasCoordinates = toCanvasCoordinates;
|
||||
this.instancesSelection = instancesSelection;
|
||||
this._screenType = screenType;
|
||||
this.showHandles = showHandles;
|
||||
|
||||
this.pixiContainer.addChild(this.rectanglesContainer);
|
||||
|
||||
for (const resizeGrabbingLocation of resizeGrabbingLocationValues) {
|
||||
const resizeButton = new PIXI.Graphics();
|
||||
this.resizeButtons[resizeGrabbingLocation] = resizeButton;
|
||||
if (showHandles) {
|
||||
for (const resizeGrabbingLocation of resizeGrabbingLocationValues) {
|
||||
const resizeButton = new PIXI.Graphics();
|
||||
this.resizeButtons[resizeGrabbingLocation] = resizeButton;
|
||||
this._makeButton(
|
||||
resizeButton,
|
||||
event => {
|
||||
this.onResize(event.deltaX, event.deltaY, resizeGrabbingLocation);
|
||||
},
|
||||
() => {
|
||||
this.onResizeEnd();
|
||||
},
|
||||
resizeGrabbingIconNames[resizeGrabbingLocation]
|
||||
);
|
||||
}
|
||||
this._makeButton(
|
||||
resizeButton,
|
||||
this.rotateButton,
|
||||
event => {
|
||||
this.onResize(event.deltaX, event.deltaY, resizeGrabbingLocation);
|
||||
this.onRotate(event.deltaX, event.deltaY);
|
||||
},
|
||||
() => {
|
||||
this.onResizeEnd();
|
||||
this.onRotateEnd();
|
||||
},
|
||||
resizeGrabbingIconNames[resizeGrabbingLocation]
|
||||
'url("res/actions/rotate24_black.png"),auto'
|
||||
);
|
||||
}
|
||||
this._makeButton(
|
||||
this.rotateButton,
|
||||
event => {
|
||||
this.onRotate(event.deltaX, event.deltaY);
|
||||
},
|
||||
() => {
|
||||
this.onRotateEnd();
|
||||
},
|
||||
'url("res/actions/rotate24_black.png"),auto'
|
||||
);
|
||||
}
|
||||
|
||||
setScreenType(screenType: ScreenType) {
|
||||
@@ -260,9 +266,10 @@ export default class SelectedInstances {
|
||||
this.rectanglesContainer.removeChild(this.selectedRectangles.pop());
|
||||
}
|
||||
|
||||
// If there are no unlocked instances, hide the resize buttons.
|
||||
const show =
|
||||
selection.filter(instance => !instance.isLocked()).length !== 0;
|
||||
if (!this.showHandles) return;
|
||||
|
||||
// If there is any unlocked instances, show the resize buttons.
|
||||
const show = selection.some(instance => !instance.isLocked());
|
||||
|
||||
// Position the resize buttons.
|
||||
for (const grabbingLocation of resizeGrabbingLocationValues) {
|
||||
|
@@ -4,6 +4,39 @@ import Rectangle from '../Utils/Rectangle';
|
||||
import { type InstanceMeasurer } from './InstancesRenderer';
|
||||
const gd: libGDevelop = global.gd;
|
||||
|
||||
const getRectangleNormalizedBoundaries = ({
|
||||
startX,
|
||||
startY,
|
||||
endX,
|
||||
endY,
|
||||
}: {
|
||||
startX: number,
|
||||
startY: number,
|
||||
endX: number,
|
||||
endY: number,
|
||||
}) => {
|
||||
let normalizedStartX = startX;
|
||||
let normalizedStartY = startY;
|
||||
let normalizedEndX = endX;
|
||||
let normalizedEndY = endY;
|
||||
if (normalizedStartX > normalizedEndX) {
|
||||
const tmp = normalizedStartX;
|
||||
normalizedStartX = normalizedEndX;
|
||||
normalizedEndX = tmp;
|
||||
}
|
||||
if (normalizedStartY > normalizedEndY) {
|
||||
const tmp = normalizedStartY;
|
||||
normalizedStartY = normalizedEndY;
|
||||
normalizedEndY = tmp;
|
||||
}
|
||||
return {
|
||||
startX: normalizedStartX,
|
||||
startY: normalizedStartY,
|
||||
endX: normalizedEndX,
|
||||
endY: normalizedEndY,
|
||||
};
|
||||
};
|
||||
|
||||
export default class SelectionRectangle {
|
||||
instances: gdInitialInstancesContainer;
|
||||
instanceMeasurer: InstanceMeasurer;
|
||||
@@ -12,9 +45,13 @@ export default class SelectionRectangle {
|
||||
pixiRectangle: PIXI.Graphics;
|
||||
selectionRectangleStart: { x: number, y: number } | null;
|
||||
selectionRectangleEnd: { x: number, y: number } | null;
|
||||
temporarySelectionRectangleStart: { x: number, y: number } | null;
|
||||
temporarySelectionRectangleEnd: { x: number, y: number } | null;
|
||||
_instancesInSelectionRectangle: gdInitialInstance[];
|
||||
_temporaryInstancesInSelectionRectangle: gdInitialInstance[];
|
||||
|
||||
selector: gdInitialInstanceJSFunctor;
|
||||
temporarySelector: gdInitialInstanceJSFunctor;
|
||||
/**
|
||||
* Used to check if an instance is in the selection rectangle
|
||||
*/
|
||||
@@ -38,6 +75,7 @@ export default class SelectionRectangle {
|
||||
this.selectionRectangleStart = null;
|
||||
this.selectionRectangleEnd = null;
|
||||
this._instancesInSelectionRectangle = [];
|
||||
this._temporaryInstancesInSelectionRectangle = [];
|
||||
|
||||
this._temporaryAABB = new Rectangle();
|
||||
this.selector = new gd.InitialInstanceJSFunctor();
|
||||
@@ -71,6 +109,41 @@ export default class SelectionRectangle {
|
||||
this._instancesInSelectionRectangle.push(instance);
|
||||
}
|
||||
};
|
||||
this.temporarySelector = new gd.InitialInstanceJSFunctor();
|
||||
// $FlowFixMe - invoke is not writable
|
||||
this.temporarySelector.invoke = instancePtr => {
|
||||
// $FlowFixMe - wrapPointer is not exposed
|
||||
const instance = gd.wrapPointer(instancePtr, gd.InitialInstance);
|
||||
const instanceAABB = this.instanceMeasurer.getInstanceAABB(
|
||||
instance,
|
||||
this._temporaryAABB
|
||||
);
|
||||
|
||||
const {
|
||||
temporarySelectionRectangleEnd,
|
||||
temporarySelectionRectangleStart,
|
||||
} = this;
|
||||
if (!temporarySelectionRectangleStart || !temporarySelectionRectangleEnd)
|
||||
return;
|
||||
|
||||
const selectionSceneStart = toSceneCoordinates(
|
||||
temporarySelectionRectangleStart.x,
|
||||
temporarySelectionRectangleStart.y
|
||||
);
|
||||
const selectionSceneEnd = toSceneCoordinates(
|
||||
temporarySelectionRectangleEnd.x,
|
||||
temporarySelectionRectangleEnd.y
|
||||
);
|
||||
|
||||
if (
|
||||
selectionSceneStart[0] <= instanceAABB.left &&
|
||||
instanceAABB.right <= selectionSceneEnd[0] &&
|
||||
selectionSceneStart[1] <= instanceAABB.top &&
|
||||
instanceAABB.bottom <= selectionSceneEnd[1]
|
||||
) {
|
||||
this._temporaryInstancesInSelectionRectangle.push(instance);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
hasStartedSelectionRectangle() {
|
||||
@@ -82,28 +155,55 @@ export default class SelectionRectangle {
|
||||
this.selectionRectangleEnd = { x, y };
|
||||
};
|
||||
|
||||
updateSelectionRectangle = (lastX: number, lastY: number) => {
|
||||
updateSelectionRectangle = (
|
||||
lastX: number,
|
||||
lastY: number
|
||||
): Array<gdInitialInstance> => {
|
||||
if (!this.selectionRectangleStart)
|
||||
this.selectionRectangleStart = { x: lastX, y: lastY };
|
||||
|
||||
this.selectionRectangleEnd = { x: lastX, y: lastY };
|
||||
this._temporaryInstancesInSelectionRectangle.length = 0;
|
||||
const normalizedSelectionRectangle = getRectangleNormalizedBoundaries({
|
||||
startX: this.selectionRectangleStart.x,
|
||||
startY: this.selectionRectangleStart.y,
|
||||
endX: this.selectionRectangleEnd.x,
|
||||
endY: this.selectionRectangleEnd.y,
|
||||
});
|
||||
this.temporarySelectionRectangleStart = {
|
||||
x: normalizedSelectionRectangle.startX,
|
||||
y: normalizedSelectionRectangle.startY,
|
||||
};
|
||||
this.temporarySelectionRectangleEnd = {
|
||||
x: normalizedSelectionRectangle.endX,
|
||||
y: normalizedSelectionRectangle.endY,
|
||||
};
|
||||
|
||||
this.instances.iterateOverInstances(
|
||||
// $FlowFixMe - gd.castObject is not supporting typings.
|
||||
this.temporarySelector
|
||||
);
|
||||
return this._temporaryInstancesInSelectionRectangle;
|
||||
};
|
||||
|
||||
endSelectionRectangle = () => {
|
||||
endSelectionRectangle = (): Array<gdInitialInstance> => {
|
||||
if (!this.selectionRectangleStart || !this.selectionRectangleEnd) return [];
|
||||
|
||||
this._instancesInSelectionRectangle.length = 0;
|
||||
if (this.selectionRectangleStart.x > this.selectionRectangleEnd.x) {
|
||||
const tmp = this.selectionRectangleStart.x;
|
||||
this.selectionRectangleStart.x = this.selectionRectangleEnd.x;
|
||||
this.selectionRectangleEnd.x = tmp;
|
||||
}
|
||||
if (this.selectionRectangleStart.y > this.selectionRectangleEnd.y) {
|
||||
const tmp = this.selectionRectangleStart.y;
|
||||
this.selectionRectangleStart.y = this.selectionRectangleEnd.y;
|
||||
this.selectionRectangleEnd.y = tmp;
|
||||
}
|
||||
|
||||
const normalizedSelectionRectangle = getRectangleNormalizedBoundaries({
|
||||
startX: this.selectionRectangleStart.x,
|
||||
startY: this.selectionRectangleStart.y,
|
||||
endX: this.selectionRectangleEnd.x,
|
||||
endY: this.selectionRectangleEnd.y,
|
||||
});
|
||||
this.selectionRectangleStart = {
|
||||
x: normalizedSelectionRectangle.startX,
|
||||
y: normalizedSelectionRectangle.startY,
|
||||
};
|
||||
this.selectionRectangleEnd = {
|
||||
x: normalizedSelectionRectangle.endX,
|
||||
y: normalizedSelectionRectangle.endY,
|
||||
};
|
||||
this.instances.iterateOverInstances(
|
||||
// $FlowFixMe - gd.castObject is not supporting typings.
|
||||
this.selector
|
||||
|
@@ -103,6 +103,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
lastContextMenuY = 0;
|
||||
lastCursorX = 0;
|
||||
lastCursorY = 0;
|
||||
instancesTemporarySelection = new InstancesSelection();
|
||||
fpsLimiter = new FpsLimiter(28);
|
||||
canvasArea: ?HTMLDivElement;
|
||||
pixiRenderer: PIXI.Renderer;
|
||||
@@ -112,6 +113,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
_instancesAdder: InstancesAdder;
|
||||
selectionRectangle: SelectionRectangle;
|
||||
selectedInstances: SelectedInstances;
|
||||
temporarySelectedInstances: SelectedInstances;
|
||||
highlightedInstance: HighlightedInstance;
|
||||
instancesResizer: InstancesResizer;
|
||||
instancesRotator: InstancesRotator;
|
||||
@@ -383,6 +385,18 @@ export default class InstancesEditor extends Component<Props> {
|
||||
onResizeEnd: this._onResizeEnd,
|
||||
onRotate: this._onRotate,
|
||||
onRotateEnd: this._onRotateEnd,
|
||||
showHandles: true,
|
||||
instanceMeasurer: this.instancesRenderer.getInstanceMeasurer(),
|
||||
toCanvasCoordinates: this.viewPosition.toCanvasCoordinates,
|
||||
screenType: this.props.screenType,
|
||||
});
|
||||
this.temporarySelectedInstances = new SelectedInstances({
|
||||
instancesSelection: this.instancesTemporarySelection,
|
||||
onResize: () => {},
|
||||
onResizeEnd: () => {},
|
||||
onRotate: () => {},
|
||||
onRotateEnd: () => {},
|
||||
showHandles: false,
|
||||
instanceMeasurer: this.instancesRenderer.getInstanceMeasurer(),
|
||||
toCanvasCoordinates: this.viewPosition.toCanvasCoordinates,
|
||||
screenType: this.props.screenType,
|
||||
@@ -425,6 +439,9 @@ export default class InstancesEditor extends Component<Props> {
|
||||
this.pixiContainer.addChild(this.windowBorder.getPixiObject());
|
||||
this.pixiContainer.addChild(this.windowMask.getPixiObject());
|
||||
this.pixiContainer.addChild(this.selectedInstances.getPixiContainer());
|
||||
this.pixiContainer.addChild(
|
||||
this.temporarySelectedInstances.getPixiContainer()
|
||||
);
|
||||
this.pixiContainer.addChild(this.highlightedInstance.getPixiObject());
|
||||
this.pixiContainer.addChild(this.statusBar.getPixiObject());
|
||||
}
|
||||
@@ -488,6 +505,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
|
||||
if (nextProps.screenType !== this.props.screenType) {
|
||||
this.selectedInstances.setScreenType(this.props.screenType);
|
||||
this.temporarySelectedInstances.setScreenType(this.props.screenType);
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -549,12 +567,26 @@ export default class InstancesEditor extends Component<Props> {
|
||||
};
|
||||
|
||||
/**
|
||||
* Immediately add instances for the specified objects at the given
|
||||
* Immediately add serialized instances at the given
|
||||
* position (in scene coordinates).
|
||||
*/
|
||||
addSerializedInstances = (options: {|
|
||||
position: [number, number],
|
||||
copyReferential: [number, number],
|
||||
serializedInstances: Array<Object>,
|
||||
preventSnapToGrid?: boolean,
|
||||
addInstancesInTheForeground?: boolean,
|
||||
|}): Array<gdInitialInstance> => {
|
||||
return this._instancesAdder.addSerializedInstances(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Immediately add instances for the specified objects at the given
|
||||
* position (in scene coordinates) given their names.
|
||||
*/
|
||||
addInstances = (
|
||||
pos /*: [number, number] */,
|
||||
objectNames /*: Array<string> */
|
||||
pos: [number, number],
|
||||
objectNames: Array<string>
|
||||
): Array<gdInitialInstance> => {
|
||||
return this._instancesAdder.addInstances(pos, objectNames);
|
||||
};
|
||||
@@ -594,7 +626,15 @@ export default class InstancesEditor extends Component<Props> {
|
||||
|
||||
this.scrollBy(-sceneDeltaX, -sceneDeltaY);
|
||||
} else {
|
||||
this.selectionRectangle.updateSelectionRectangle(x, y);
|
||||
const temporarySelectedInstances = this.selectionRectangle.updateSelectionRectangle(
|
||||
x,
|
||||
y
|
||||
);
|
||||
this.instancesTemporarySelection.selectInstances({
|
||||
instances: temporarySelectedInstances,
|
||||
multiSelect: false,
|
||||
layersVisibility: this._getLayersVisibility(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -622,6 +662,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
});
|
||||
instancesSelected = this.props.instancesSelection.getSelectedInstances();
|
||||
this.props.onInstancesSelected(instancesSelected);
|
||||
this.instancesTemporarySelection.clearSelection();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -946,6 +987,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
this.instancesRenderer.render();
|
||||
this.highlightedInstance.render();
|
||||
this.selectedInstances.render();
|
||||
this.temporarySelectedInstances.render();
|
||||
this.selectionRectangle.render();
|
||||
this.windowBorder.render();
|
||||
this.windowMask.render();
|
||||
|
@@ -316,6 +316,55 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
[copyObject, deleteObject]
|
||||
);
|
||||
|
||||
const addSerializedObjectToObjectsContainer = React.useCallback(
|
||||
({
|
||||
objectName,
|
||||
positionObjectName,
|
||||
objectType,
|
||||
global,
|
||||
serializedObject,
|
||||
}: {|
|
||||
objectName: string,
|
||||
positionObjectName: string,
|
||||
objectType: string,
|
||||
global: boolean,
|
||||
serializedObject: Object,
|
||||
|}): ObjectWithContext => {
|
||||
const newName = newNameGenerator(
|
||||
objectName,
|
||||
name =>
|
||||
objectsContainer.hasObjectNamed(name) ||
|
||||
project.hasObjectNamed(name),
|
||||
''
|
||||
);
|
||||
|
||||
const newObject = global
|
||||
? project.insertNewObject(
|
||||
project,
|
||||
objectType,
|
||||
newName,
|
||||
project.getObjectPosition(positionObjectName) + 1
|
||||
)
|
||||
: objectsContainer.insertNewObject(
|
||||
project,
|
||||
objectType,
|
||||
newName,
|
||||
objectsContainer.getObjectPosition(positionObjectName) + 1
|
||||
);
|
||||
|
||||
unserializeFromJSObject(
|
||||
newObject,
|
||||
serializedObject,
|
||||
'unserializeFrom',
|
||||
project
|
||||
);
|
||||
newObject.setName(newName); // Unserialization has overwritten the name.
|
||||
|
||||
return { object: newObject, global };
|
||||
},
|
||||
[objectsContainer, project]
|
||||
);
|
||||
|
||||
const paste = React.useCallback(
|
||||
(objectWithContext: ObjectWithContext): ?ObjectWithContext => {
|
||||
if (!Clipboard.has(CLIPBOARD_KIND)) return null;
|
||||
@@ -336,42 +385,20 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
);
|
||||
if (!name || !type || !copiedObject) return;
|
||||
|
||||
const newName = newNameGenerator(
|
||||
name,
|
||||
name =>
|
||||
objectsContainer.hasObjectNamed(name) ||
|
||||
project.hasObjectNamed(name),
|
||||
''
|
||||
);
|
||||
|
||||
const newObject = global
|
||||
? project.insertNewObject(
|
||||
project,
|
||||
type,
|
||||
newName,
|
||||
project.getObjectPosition(pasteObject.getName()) + 1
|
||||
)
|
||||
: objectsContainer.insertNewObject(
|
||||
project,
|
||||
type,
|
||||
newName,
|
||||
objectsContainer.getObjectPosition(pasteObject.getName()) + 1
|
||||
);
|
||||
|
||||
unserializeFromJSObject(
|
||||
newObject,
|
||||
copiedObject,
|
||||
'unserializeFrom',
|
||||
project
|
||||
);
|
||||
newObject.setName(newName); // Unserialization has overwritten the name.
|
||||
const newObjectWithContext = addSerializedObjectToObjectsContainer({
|
||||
objectName: name,
|
||||
positionObjectName: pasteObject.getName(),
|
||||
objectType: type,
|
||||
serializedObject: copiedObject,
|
||||
global,
|
||||
});
|
||||
|
||||
onObjectModified(false);
|
||||
if (onObjectPasted) onObjectPasted(newObject);
|
||||
if (onObjectPasted) onObjectPasted(newObjectWithContext.object);
|
||||
|
||||
return { object: newObject, global };
|
||||
return newObjectWithContext;
|
||||
},
|
||||
[objectsContainer, onObjectModified, onObjectPasted, project]
|
||||
[addSerializedObjectToObjectsContainer, onObjectModified, onObjectPasted]
|
||||
);
|
||||
|
||||
const editName = React.useCallback(
|
||||
@@ -383,19 +410,25 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
[]
|
||||
);
|
||||
|
||||
const pasteAndRename = React.useCallback(
|
||||
(objectWithContext: ObjectWithContext) => {
|
||||
editName(paste(objectWithContext));
|
||||
},
|
||||
[editName, paste]
|
||||
);
|
||||
|
||||
const duplicateObject = React.useCallback(
|
||||
(objectWithContext: ObjectWithContext) => {
|
||||
copyObject(objectWithContext);
|
||||
pasteAndRename(objectWithContext);
|
||||
const { object, global } = objectWithContext;
|
||||
|
||||
const type = object.getType();
|
||||
const name = object.getName();
|
||||
const serializedObject = serializeToJSObject(object);
|
||||
|
||||
const newObjectWithContext = addSerializedObjectToObjectsContainer({
|
||||
objectName: name,
|
||||
positionObjectName: name,
|
||||
objectType: type,
|
||||
serializedObject,
|
||||
global,
|
||||
});
|
||||
|
||||
editName(newObjectWithContext);
|
||||
},
|
||||
[copyObject, pasteAndRename]
|
||||
[addSerializedObjectToObjectsContainer, editName]
|
||||
);
|
||||
|
||||
const rename = React.useCallback(
|
||||
|
@@ -24,10 +24,7 @@ import SetupGridDialog from './SetupGridDialog';
|
||||
import ScenePropertiesDialog from './ScenePropertiesDialog';
|
||||
import { type ObjectEditorTab } from '../ObjectEditor/ObjectEditorDialog';
|
||||
import Toolbar from './Toolbar';
|
||||
import {
|
||||
serializeToJSObject,
|
||||
unserializeFromJSObject,
|
||||
} from '../Utils/Serializer';
|
||||
import { serializeToJSObject } from '../Utils/Serializer';
|
||||
import Clipboard, { SafeExtractor } from '../Utils/Clipboard';
|
||||
import Window from '../Utils/Window';
|
||||
import FullSizeInstancesEditorWithScrollbars from '../InstancesEditor/FullSizeInstancesEditorWithScrollbars';
|
||||
@@ -157,7 +154,10 @@ type State = {|
|
||||
selectedObjectTags: SelectedTags,
|
||||
|};
|
||||
|
||||
type CopyCutPasteOptions = { useLastCursorPosition?: boolean };
|
||||
type CopyCutPasteOptions = {|
|
||||
useLastCursorPosition?: boolean,
|
||||
pasteInTheForeground?: boolean,
|
||||
|};
|
||||
|
||||
export default class SceneEditor extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
@@ -1126,7 +1126,10 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
return contextMenuItems;
|
||||
};
|
||||
|
||||
copySelection = ({ useLastCursorPosition }: CopyCutPasteOptions = {}) => {
|
||||
copySelection = ({
|
||||
useLastCursorPosition,
|
||||
pasteInTheForeground,
|
||||
}: CopyCutPasteOptions = {}) => {
|
||||
const serializedSelection = this.instancesSelection
|
||||
.getSelectedInstances()
|
||||
.map(instance => serializeToJSObject(instance));
|
||||
@@ -1138,33 +1141,29 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
Clipboard.set(INSTANCES_CLIPBOARD_KIND, {
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
pasteInTheForeground: !!pasteInTheForeground,
|
||||
instances: serializedSelection,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
cutSelection = (options: CopyCutPasteOptions = {}) => {
|
||||
this.copySelection(options);
|
||||
cutSelection = ({ useLastCursorPosition }: CopyCutPasteOptions = {}) => {
|
||||
this.copySelection({ useLastCursorPosition, pasteInTheForeground: true });
|
||||
this.deleteSelection();
|
||||
};
|
||||
|
||||
duplicateSelection = () => {
|
||||
const { editor } = this;
|
||||
if (!editor) return;
|
||||
const serializedSelection = this.instancesSelection
|
||||
.getSelectedInstances()
|
||||
.map(instance => serializeToJSObject(instance));
|
||||
|
||||
if (!this.editor) return;
|
||||
|
||||
const newInstances = serializedSelection.map(serializedInstance => {
|
||||
const instance = new gd.InitialInstance();
|
||||
unserializeFromJSObject(instance, serializedInstance);
|
||||
instance.setX(instance.getX() + 2 * MOVEMENT_BIG_DELTA);
|
||||
instance.setY(instance.getY() + 2 * MOVEMENT_BIG_DELTA);
|
||||
const newInstance = this.props.initialInstances
|
||||
.insertInitialInstance(instance)
|
||||
.resetPersistentUuid();
|
||||
instance.delete();
|
||||
return newInstance;
|
||||
const newInstances = editor.addSerializedInstances({
|
||||
position: [0, 0],
|
||||
copyReferential: [-2 * MOVEMENT_BIG_DELTA, -2 * MOVEMENT_BIG_DELTA],
|
||||
serializedInstances: serializedSelection,
|
||||
preventSnapToGrid: true,
|
||||
});
|
||||
this._onInstancesAdded(newInstances);
|
||||
this.instancesSelection.clearSelection();
|
||||
@@ -1176,11 +1175,12 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
paste = ({ useLastCursorPosition }: CopyCutPasteOptions = {}) => {
|
||||
if (!this.editor) return;
|
||||
const { editor } = this;
|
||||
if (!editor) return;
|
||||
|
||||
const position = useLastCursorPosition
|
||||
? this.editor.getLastCursorSceneCoordinates()
|
||||
: this.editor.getLastContextMenuSceneCoordinates();
|
||||
? editor.getLastCursorSceneCoordinates()
|
||||
: editor.getLastContextMenuSceneCoordinates();
|
||||
|
||||
const clipboardContent = Clipboard.get(INSTANCES_CLIPBOARD_KIND);
|
||||
const instancesContent = SafeExtractor.extractArrayProperty(
|
||||
@@ -1189,19 +1189,20 @@ export default class SceneEditor extends React.Component<Props, State> {
|
||||
);
|
||||
const x = SafeExtractor.extractNumberProperty(clipboardContent, 'x');
|
||||
const y = SafeExtractor.extractNumberProperty(clipboardContent, 'y');
|
||||
const pasteInTheForeground =
|
||||
SafeExtractor.extractBooleanProperty(
|
||||
clipboardContent,
|
||||
'pasteInTheForeground'
|
||||
) || false;
|
||||
if (x === null || y === null || instancesContent === null) return;
|
||||
|
||||
const newInstances = instancesContent.map(serializedInstance => {
|
||||
const instance = new gd.InitialInstance();
|
||||
unserializeFromJSObject(instance, serializedInstance);
|
||||
instance.setX(instance.getX() - x + position[0]);
|
||||
instance.setY(instance.getY() - y + position[1]);
|
||||
const newInstance = this.props.initialInstances
|
||||
.insertInitialInstance(instance)
|
||||
.resetPersistentUuid();
|
||||
instance.delete();
|
||||
return newInstance;
|
||||
const newInstances = editor.addSerializedInstances({
|
||||
position,
|
||||
copyReferential: [x, y],
|
||||
serializedInstances: instancesContent,
|
||||
addInstancesInTheForeground: pasteInTheForeground,
|
||||
});
|
||||
|
||||
this._onInstancesAdded(newInstances);
|
||||
this.instancesSelection.clearSelection();
|
||||
this.instancesSelection.selectInstances({
|
||||
|
Reference in New Issue
Block a user