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