mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
[WIP] Improve SceneEditor
This commit is contained in:
@@ -77,6 +77,7 @@ class App extends Component {
|
||||
{
|
||||
currentProject && currentProject.hasLayoutNamed(sceneOpened) && (
|
||||
<SceneEditorContainer
|
||||
key={sceneOpened}
|
||||
project={currentProject}
|
||||
layout={currentProject.getLayout(sceneOpened)}
|
||||
initialInstances={currentProject.getLayout(sceneOpened).getInitialInstances()}
|
||||
|
@@ -7,12 +7,20 @@ export default class KeyboardShortcuts {
|
||||
|
||||
shouldMultiSelect() { return this.shiftPressed; }
|
||||
|
||||
shouldScrollHorizontally() { return this.altPressed; }
|
||||
|
||||
shouldZoom() { return this.metaPressed; }
|
||||
|
||||
_onKeyDown = (evt) => {
|
||||
if ( evt.metaKey ) this.metaPressed = true;
|
||||
if ( evt.altKey ) this.altPressed = true;
|
||||
if ( evt.which === 17 ) this.ctrlPressed = true;
|
||||
if ( evt.which === 16 ) this.shiftPressed = true;
|
||||
}
|
||||
|
||||
_onKeyUp = (evt) => {
|
||||
if ( !evt.metaKey ) this.metaPressed = false;
|
||||
if ( !evt.altKey ) this.altPressed = false;
|
||||
if ( evt.which === 17 ) this.ctrlPressed = false;
|
||||
if ( evt.which === 16 ) this.shiftPressed = false;
|
||||
}
|
||||
@@ -27,7 +35,7 @@ export default class KeyboardShortcuts {
|
||||
unmount() {
|
||||
if (!document) return;
|
||||
|
||||
document.removeEventListener(this._onKeyDown);
|
||||
document.removeEventListener(this._onKeyUp);
|
||||
document.removeEventListener('keydown', this._onKeyDown);
|
||||
document.removeEventListener('keyup', this._onKeyUp);
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import RaisedButton from 'material-ui/RaisedButton';
|
||||
import gesture from 'pixi-simple-gesture';
|
||||
|
||||
import SceneRenderer from './SceneRenderer';
|
||||
import ViewPosition from './ViewPosition';
|
||||
import InstancesSelection from './InstancesSelection';
|
||||
import KeyboardShortcuts from './KeyboardShortcuts';
|
||||
import HighlightedInstance from './HighlightedInstance';
|
||||
import SelectionRectangle from './SelectionRectangle';
|
||||
const gd = global.gd;
|
||||
const PIXI = global.PIXI;
|
||||
|
||||
@@ -26,12 +28,30 @@ export default class SceneEditorContainer extends Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.pixiRenderer = PIXI.autoDetectRenderer(500, 500);
|
||||
this.pixiRenderer = PIXI.autoDetectRenderer(800, 800);
|
||||
this.refs.canvasArea.appendChild(this.pixiRenderer.view);
|
||||
this.pixiRenderer.view.addEventListener('contextmenu', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
this.backgroundArea = new PIXI.Container();
|
||||
this.backgroundArea.hitArea = new PIXI.Rectangle(0, 0, 800, 800); //TODO:Resize
|
||||
gesture.panable(this.backgroundArea);
|
||||
this.backgroundArea.on('mousedown', this._onBackgroundClicked);
|
||||
this.backgroundArea.on('panmove', (event) => this._onMakeSelectionRectangle(event.data.global.x, event.data.global.y));
|
||||
this.backgroundArea.on('panend', (event) => this._onEndSelectionRectangle());
|
||||
|
||||
this.pixiRenderer.view.onmousewheel = (event) => {
|
||||
if (this.keyboardShortcuts.shouldZoom()) {
|
||||
this.viewPosition.zoomBy(event.wheelDelta / 5000);
|
||||
} else if (this.keyboardShortcuts.shouldScrollHorizontally()) {
|
||||
this.viewPosition.scrollBy(-event.wheelDelta / 20, 0);
|
||||
} else {
|
||||
this.viewPosition.scrollBy(0, -event.wheelDelta / 20);
|
||||
}
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
this.pixiContainer = new PIXI.Container();
|
||||
this.viewPosition = new ViewPosition({
|
||||
onPanMoveView: this._onPanMoveView,
|
||||
@@ -46,6 +66,12 @@ export default class SceneEditorContainer extends Component {
|
||||
onOutInstance: this._onOutInstance,
|
||||
onInstanceClicked: this._onInstanceClicked,
|
||||
});
|
||||
this.selectionRectangle = new SelectionRectangle({
|
||||
instances: this.props.initialInstances,
|
||||
getInstanceWidth: this.sceneRenderer.getInstanceWidth,
|
||||
getInstanceHeight: this.sceneRenderer.getInstanceHeight,
|
||||
toSceneCoordinates: this.viewPosition.toSceneCoordinates,
|
||||
});
|
||||
this.instancesSelection = new InstancesSelection({
|
||||
getInstanceWidth: this.sceneRenderer.getInstanceWidth,
|
||||
getInstanceHeight: this.sceneRenderer.getInstanceHeight,
|
||||
@@ -56,7 +82,9 @@ export default class SceneEditorContainer extends Component {
|
||||
});
|
||||
this.keyboardShortcuts = new KeyboardShortcuts();
|
||||
|
||||
this.pixiContainer.addChild(this.backgroundArea);
|
||||
this.pixiContainer.addChild(this.viewPosition.getPixiContainer());
|
||||
this.pixiContainer.addChild(this.selectionRectangle.getPixiObject());
|
||||
this.viewPosition.getPixiContainer().addChild(this.sceneRenderer.getPixiContainer());
|
||||
this.viewPosition.getPixiContainer().addChild(this.highlightedInstance.getPixiObject());
|
||||
this.viewPosition.getPixiContainer().addChild(this.instancesSelection.getPixiContainer());
|
||||
@@ -76,6 +104,21 @@ export default class SceneEditorContainer extends Component {
|
||||
throw new Error("Changing project/layout/initialInstances is not supported yet")
|
||||
}
|
||||
|
||||
_onBackgroundClicked = () => {
|
||||
console.log("Background clicked");
|
||||
if (!this.keyboardShortcuts.shouldMultiSelect())
|
||||
this.instancesSelection.clearSelection();
|
||||
}
|
||||
|
||||
_onMakeSelectionRectangle = (x, y) => {
|
||||
this.selectionRectangle.makeSelectionRectangle(x, y);
|
||||
}
|
||||
|
||||
_onEndSelectionRectangle = () => {
|
||||
const instancesSelected = this.selectionRectangle.endSelectionRectangle();
|
||||
instancesSelected.forEach(instance => this.instancesSelection.selectInstance(instance));
|
||||
}
|
||||
|
||||
_onInstanceClicked = (instance) => {
|
||||
if (!this.keyboardShortcuts.shouldMultiSelect())
|
||||
this.instancesSelection.clearSelection();
|
||||
@@ -139,6 +182,7 @@ export default class SceneEditorContainer extends Component {
|
||||
this.sceneRenderer.render();
|
||||
this.highlightedInstance.render();
|
||||
this.instancesSelection.render();
|
||||
this.selectionRectangle.render();
|
||||
this.pixiRenderer.render(this.pixiContainer);
|
||||
this.nextFrame = requestAnimationFrame(this.renderScene);
|
||||
}
|
||||
@@ -150,6 +194,7 @@ export default class SceneEditorContainer extends Component {
|
||||
return (
|
||||
<div>
|
||||
<RaisedButton label="Delete selection" onClick={this.deleteSelection} />
|
||||
<RaisedButton label="Move" onClick={() => this.viewPosition.scrollBy(50, 40)} />
|
||||
<div ref="canvasArea" />
|
||||
</div>
|
||||
)
|
||||
|
87
newIDE/src/SceneEditor/SelectionRectangle.js
Normal file
87
newIDE/src/SceneEditor/SelectionRectangle.js
Normal file
@@ -0,0 +1,87 @@
|
||||
const gd = global.gd;
|
||||
const PIXI = global.PIXI;
|
||||
import gesture from 'pixi-simple-gesture';
|
||||
|
||||
export default class SelectionRectangle {
|
||||
constructor({instances, getInstanceWidth, getInstanceHeight, toSceneCoordinates, toCanvasCoordinates}) {
|
||||
this.instances = instances;
|
||||
this.getInstanceWidth = getInstanceWidth;
|
||||
this.getInstanceHeight = getInstanceHeight;
|
||||
this.toSceneCoordinates = toSceneCoordinates;
|
||||
|
||||
this.pixiRectangle = new PIXI.Graphics();
|
||||
this.pixiRectangle.hitArea = new PIXI.Rectangle(0, 0, 0, 0);
|
||||
this.selectionRectangleStart = null;
|
||||
this.selectionRectangleEnd = null;
|
||||
this._instancesInSelectionRectangle = [];
|
||||
|
||||
this.selector = new gd.InitialInstanceJSFunctor();
|
||||
this.selector.invoke = (instancePtr) => {
|
||||
const instance = gd.wrapPointer(instancePtr, gd.InitialInstance);
|
||||
const x = instance.getX();
|
||||
const y = instance.getY();
|
||||
const instanceHeight = this.getInstanceHeight(instance);
|
||||
const instanceWidth = this.getInstanceWidth(instance);
|
||||
|
||||
const selectionSceneStart = toSceneCoordinates(this.selectionRectangleStart.x, this.selectionRectangleStart.y);
|
||||
const selectionSceneEnd = toSceneCoordinates(this.selectionRectangleEnd.x, this.selectionRectangleEnd.y);
|
||||
|
||||
if (selectionSceneStart[0] <= x && x + instanceWidth <= selectionSceneEnd[0] &&
|
||||
selectionSceneStart[1] <= y && y + instanceHeight <= selectionSceneEnd[1]) {
|
||||
this._instancesInSelectionRectangle.push(instance);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
makeSelectionRectangle = (lastX, lastY) => {
|
||||
if (!this.selectionRectangleStart)
|
||||
this.selectionRectangleStart = {x: lastX, y: lastY};
|
||||
|
||||
this.selectionRectangleEnd = {x: lastX, y: lastY};
|
||||
}
|
||||
|
||||
endSelectionRectangle = () => {
|
||||
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;
|
||||
}
|
||||
|
||||
this.instances.iterateOverInstances(this.selector);
|
||||
|
||||
this.selectionRectangleStart = null;
|
||||
return this._instancesInSelectionRectangle;
|
||||
}
|
||||
|
||||
getPixiObject() {
|
||||
return this.pixiRectangle;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.selectionRectangleStart) {
|
||||
this.pixiRectangle.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let x1 = this.selectionRectangleStart.x;
|
||||
let y1 = this.selectionRectangleStart.y;
|
||||
let x2 = this.selectionRectangleEnd.x;
|
||||
let y2 = this.selectionRectangleEnd.y;
|
||||
|
||||
this.pixiRectangle.visible = true;
|
||||
this.pixiRectangle.clear();
|
||||
this.pixiRectangle.beginFill(0x6868E8);
|
||||
this.pixiRectangle.lineStyle(1, 0x6868E8, 1);
|
||||
this.pixiRectangle.fillAlpha = 0.1;
|
||||
this.pixiRectangle.alpha = 0.8;
|
||||
this.pixiRectangle.drawRect(Math.min(x1, x2), Math.min(y1, y2),
|
||||
Math.abs(x2 - x1), Math.abs(y2 - y1));
|
||||
this.pixiRectangle.endFill();
|
||||
}
|
||||
}
|
@@ -5,9 +5,44 @@ export default class ViewPosition {
|
||||
constructor({onPanMoveView}) {
|
||||
this.viewX = 0;
|
||||
this.viewY = 0;
|
||||
this.pixiContainer = new PIXI.Container();
|
||||
gesture.panable(this.pixiContainer);
|
||||
this.pixiContainer.on('panmove', (event) => onPanMoveView(event.deltaX, event.deltaY));
|
||||
this._zoomFactor = 1;
|
||||
this._pixiContainer = new PIXI.Container();
|
||||
|
||||
// gesture.panable(this._pixiContainer);
|
||||
// this._pixiContainer.on('panmove', (event) => onPanMoveView(event.deltaX, event.deltaY));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert a point from the canvas coordinates (For example, the mouse position) to the
|
||||
* "world" coordinates.
|
||||
*/
|
||||
toSceneCoordinates = (x, y) => {
|
||||
x /= Math.abs(this._pixiContainer.scale.x);
|
||||
y /= Math.abs(this._pixiContainer.scale.y);
|
||||
|
||||
var viewRotation = 0;
|
||||
var tmp = x;
|
||||
x = Math.cos(viewRotation/180*3.14159)*x - Math.sin(viewRotation/180*3.14159)*y;
|
||||
y = Math.sin(viewRotation/180*3.14159)*tmp + Math.cos(viewRotation/180*3.14159)*y;
|
||||
|
||||
return [x+this.viewX, y+this.viewY];
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a point from the "world" coordinates (For example, an object position) to the
|
||||
* canvas coordinates.
|
||||
*/
|
||||
toCanvasCoordinates = (x, y) => {
|
||||
var viewRotation = -0;
|
||||
var tmp = x;
|
||||
x = Math.cos(viewRotation/180*3.14159)*x - Math.sin(viewRotation/180*3.14159)*y;
|
||||
y = Math.sin(viewRotation/180*3.14159)*tmp + Math.cos(viewRotation/180*3.14159)*y;
|
||||
|
||||
x *= Math.abs(this._pixiContainer.scale.x);
|
||||
y *= Math.abs(this._pixiContainer.scale.y);
|
||||
|
||||
return [x-this.viewX, y-this.viewY];
|
||||
}
|
||||
|
||||
scrollBy(x, y) {
|
||||
@@ -15,12 +50,18 @@ export default class ViewPosition {
|
||||
this.viewY += y;
|
||||
}
|
||||
|
||||
zoomBy(value) {
|
||||
this._zoomFactor = Math.max(Math.min(this._zoomFactor + value, 10), 0.05);
|
||||
}
|
||||
|
||||
getPixiContainer() {
|
||||
return this.pixiContainer;
|
||||
return this._pixiContainer;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.pixiContainer.position.x = -this.viewX;
|
||||
this.pixiContainer.position.y = -this.viewY;
|
||||
this._pixiContainer.position.x = -this.viewX;
|
||||
this._pixiContainer.position.y = -this.viewY;
|
||||
this._pixiContainer.scale.x = this._zoomFactor;
|
||||
this._pixiContainer.scale.y = this._zoomFactor;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user