Skip to content

Commit

Permalink
Merge pull request #676 from 3DStreet/undo
Browse files Browse the repository at this point in the history
Undo/redo position/rotation/scale
  • Loading branch information
kfarr authored Jun 28, 2024
2 parents 251287b + a30ce2a commit afec8fd
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 84 deletions.
3 changes: 0 additions & 3 deletions src/editor/components/components/CommonComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import PropertyRow from './PropertyRow';
import Collapsible from '../Collapsible';
import classnames from 'classnames';
import {
updateEntity,
getEntityClipboardRepresentation,
printEntity
} from '../../lib/entity';
Expand Down Expand Up @@ -67,10 +66,8 @@ export default class CommonComponents extends React.Component {
}
return (
<PropertyRow
onChange={updateEntity}
key={componentName}
name={componentName}
showHelp={true}
schema={schema}
data={data}
isSingle={true}
Expand Down
56 changes: 56 additions & 0 deletions src/editor/components/components/UndoRedo/UndoRedo.component.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useEffect, useState } from 'react';
import { faRotateLeft, faRotateRight } from '@fortawesome/free-solid-svg-icons';
import posthog from 'posthog-js';
import { AwesomeIcon } from '../AwesomeIcon';
import { Button } from '../Button';
import Events from '../../../lib/Events';

export const UndoRedo = () => {
const [undoDisabled, setUndoDisabled] = useState(
AFRAME.INSPECTOR.history.undos.length === 0
);
const [redoDisabled, setRedoDisabled] = useState(
AFRAME.INSPECTOR.history.redos.length === 0
);
const handleUndoClick = () => {
AFRAME.INSPECTOR.undo();
posthog.capture('undo_clicked');
};

const handleRedoClick = () => {
AFRAME.INSPECTOR.redo();
posthog.capture('redo_clicked');
};

useEffect(() => {
const listener = () => {
setUndoDisabled(AFRAME.INSPECTOR.history.undos.length === 0);
setRedoDisabled(AFRAME.INSPECTOR.history.redos.length === 0);
};
Events.on('historychanged', listener);
return () => {
Events.off('historychanged', listener);
};
}, []);

return (
<>
<Button
variant="toolbtn"
onClick={handleUndoClick}
leadingIcon={<AwesomeIcon icon={faRotateLeft} />}
disabled={undoDisabled}
>
<div className="hideInLowResolution">Undo</div>
</Button>
<Button
variant="toolbtn"
onClick={handleRedoClick}
leadingIcon={<AwesomeIcon icon={faRotateRight} />}
disabled={redoDisabled}
>
<div className="hideInLowResolution">Redo</div>
</Button>
</>
);
};
1 change: 1 addition & 0 deletions src/editor/components/components/UndoRedo/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { UndoRedo } from './UndoRedo.component.jsx';
6 changes: 6 additions & 0 deletions src/editor/components/scenegraph/Toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SavingModal } from '../modals/SavingModal';
import { uploadThumbnailImage } from '../modals/ScreenshotModal/ScreenshotModal.component.jsx';
import { sendMetric } from '../../services/ga.js';
import posthog from 'posthog-js';
import { UndoRedo } from '../components/UndoRedo/UndoRedo.component.jsx';
// const LOCALSTORAGE_MOCAP_UI = "aframeinspectormocapuienabled";

function filterHelpers(scene, visible) {
Expand Down Expand Up @@ -363,13 +364,15 @@ export default class Toolbar extends Component {
if (this.state.isPlaying) {
AFRAME.scenes[0].pause();
this.setState((prevState) => ({ ...prevState, isPlaying: false }));
Events.emit('sceneplayingtoggle', false);
AFRAME.scenes[0].isPlaying = true;
document.getElementById('aframeInspectorMouseCursor').play();
return;
}
AFRAME.scenes[0].isPlaying = false;
AFRAME.scenes[0].play();
this.setState((prevState) => ({ ...prevState, isPlaying: true }));
Events.emit('sceneplayingtoggle', true);
};

toggleSaveActionState = () => {
Expand Down Expand Up @@ -472,6 +475,9 @@ export default class Toolbar extends Component {
<ProfileButton />
</div>
</div>
<div className="undoRedoActions">
<UndoRedo />
</div>
</div>
);
}
Expand Down
6 changes: 0 additions & 6 deletions src/editor/components/widgets/NumberWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ export default class NumberWidget extends React.Component {
this.distance = 0;
this.onMouseDownValue = 0;
this.prevPointer = [0, 0];

this.setValue(this.props.value);
this.onBlur();
}

onMouseMove = (event) => {
Expand Down Expand Up @@ -128,7 +125,6 @@ export default class NumberWidget extends React.Component {

onBlur = () => {
this.setValue(parseFloat(this.input.current.value));
this.setState({ class: '' });
};

onChange = (e) => {
Expand All @@ -140,7 +136,6 @@ export default class NumberWidget extends React.Component {

// enter.
if (event.keyCode === 13) {
this.setValue(parseFloat(this.input.current.value));
this.input.current.blur();
return;
}
Expand Down Expand Up @@ -176,7 +171,6 @@ export default class NumberWidget extends React.Component {
onKeyDown={this.onKeyDown}
onChange={this.onChange}
onMouseDown={this.onMouseDown}
onFocus={this.onFocus}
onBlur={this.onBlur}
/>
</div>
Expand Down
20 changes: 19 additions & 1 deletion src/editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Events from './lib/Events';
import { AssetsLoader } from './lib/assetsLoader';
import { initCameras } from './lib/cameras';
import { createEntity } from './lib/entity';
import { History } from './lib/history';
import { Shortcuts } from './lib/shortcuts';
import { Viewport } from './lib/viewport';
import { firebaseConfig } from './services/firebase.js';
Expand All @@ -16,7 +17,7 @@ import posthog from 'posthog-js';
function Inspector() {
this.assetsLoader = new AssetsLoader();
this.exporters = { gltf: new GLTFExporter() };
this.history = require('./lib/history');
this.history = new History();
this.isFirstOpen = true;
this.modules = {};
this.on = Events.on;
Expand Down Expand Up @@ -88,6 +89,7 @@ Inspector.prototype = {
this.sceneHelpers.userData.source = 'INSPECTOR';
this.sceneHelpers.visible = true;
this.inspectorActive = false;
this.debugUndoRedo = false;

this.viewport = new Viewport(this);
Events.emit('windowresize');
Expand Down Expand Up @@ -217,12 +219,28 @@ Inspector.prototype = {
});
});

this.sceneEl.addEventListener('newScene', () => {
this.history.clear();
});

document.addEventListener('child-detached', (event) => {
var entity = event.detail.el;
AFRAME.INSPECTOR.removeObject(entity.object3D);
});
},

execute: function (cmd, optionalName) {
this.history.execute(cmd, optionalName);
},

undo: function () {
this.history.undo();
},

redo: function () {
this.history.redo();
},

selectById: function (id) {
if (id === this.camera.id) {
this.select(this.camera);
Expand Down
18 changes: 9 additions & 9 deletions src/editor/lib/TransformControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -1296,28 +1296,28 @@
// Trim decimals.
if (_mode === 'translate') {
scope.object.position.x = parseFloat(
scope.object.position.x.toFixed(5)
scope.object.position.x.toFixed(3)
);
scope.object.position.y = parseFloat(
scope.object.position.y.toFixed(5)
scope.object.position.y.toFixed(3)
);
scope.object.position.z = parseFloat(
scope.object.position.z.toFixed(5)
scope.object.position.z.toFixed(3)
);
} else if (_mode === 'rotate') {
scope.object.rotation.x = parseFloat(
scope.object.rotation.x.toFixed(5)
scope.object.rotation.x.toFixed(3)
);
scope.object.rotation.y = parseFloat(
scope.object.rotation.y.toFixed(5)
scope.object.rotation.y.toFixed(3)
);
scope.object.rotation.z = parseFloat(
scope.object.rotation.z.toFixed(5)
scope.object.rotation.z.toFixed(3)
);
} else {
scope.object.scale.x = parseFloat(scope.object.scale.x.toFixed(5));
scope.object.scale.y = parseFloat(scope.object.scale.y.toFixed(5));
scope.object.scale.z = parseFloat(scope.object.scale.z.toFixed(5));
scope.object.scale.x = parseFloat(scope.object.scale.x.toFixed(3));
scope.object.scale.y = parseFloat(scope.object.scale.y.toFixed(3));
scope.object.scale.z = parseFloat(scope.object.scale.z.toFixed(3));
}

scope.update();
Expand Down
15 changes: 15 additions & 0 deletions src/editor/lib/command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @param editor pointer to main editor object used to initialize
* each command object with a reference to the editor
* @constructor
*/

export class Command {
constructor(editor) {
this.id = -1;
this.updatable = false;
this.type = '';
this.name = '';
this.editor = editor;
}
}
105 changes: 105 additions & 0 deletions src/editor/lib/commands/EntityUpdateCommand.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import Events from '../Events';
import { Command } from '../command.js';

function updateEntity(entity, component, property, value) {
if (property) {
if (value === null || value === undefined) {
// Remove property.
entity.removeAttribute(component, property);
} else {
// Set property.
entity.setAttribute(component, property, value);
}
} else {
if (value === null || value === undefined) {
// Remove component.
entity.removeAttribute(component);
} else {
// Set component.
entity.setAttribute(component, value);
}
}
}

/**
* @param editor Editor
* @param payload: entity, component, property, value.
* @constructor
*/
export class EntityUpdateCommand extends Command {
constructor(editor, payload) {
super(editor);

this.type = 'EntityUpdateCommand';
this.name = 'Update Entity';
this.updatable =
payload.component === 'position' ||
payload.component === 'rotation' ||
payload.component === 'scale';

this.entity = payload.entity;
this.component = payload.component;
this.property = payload.property;

const component = AFRAME.components[payload.component];
if (component) {
if (payload.property) {
if (component.schema[payload.property]) {
this.newValue = component.schema[payload.property].stringify(
payload.value
);
this.oldValue = component.schema[payload.property].stringify(
payload.entity.getAttribute(payload.component, payload.property)
);
if (this.editor.debugUndoRedo) {
console.log(this.component, this.oldValue, this.newValue);
}
}
} else {
this.newValue = component.schema.stringify(payload.value);
this.oldValue = component.schema.stringify(
payload.entity.getAttribute(payload.component)
);
if (this.editor.debugUndoRedo) {
console.log(this.component, this.oldValue, this.newValue);
}
}
}
}

execute() {
if (this.editor.debugUndoRedo) {
console.log(
'execute',
this.entity,
this.component,
this.property,
this.newValue
);
}
updateEntity(this.entity, this.component, this.property, this.newValue);
Events.emit('entityupdate', {
entity: this.entity,
component: this.component,
property: this.property,
value: this.newValue
});
}

undo() {
updateEntity(this.entity, this.component, this.property, this.oldValue);
Events.emit('entityupdate', {
entity: this.entity,
component: this.component,
property: this.property,
value: this.oldValue
});
}

update(command) {
if (this.editor.debugUndoRedo) {
console.log('update', command);
}
this.newValue = command.newValue;
}
}
1 change: 1 addition & 0 deletions src/editor/lib/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EntityUpdateCommand } from './EntityUpdateCommand.js';
Loading

0 comments on commit afec8fd

Please sign in to comment.