Offset the pasted item from the original when on the same costume

This commit is contained in:
DD 2017-11-01 18:10:03 -04:00
parent cc920e8969
commit a5a60b0884
5 changed files with 97 additions and 18 deletions

View file

@ -122,7 +122,7 @@ const ModeToolsComponent = props => {
onClick={props.onCopyToClipboard} onClick={props.onCopyToClipboard}
/> />
<LabeledIconButton <LabeledIconButton
disabled={!(props.clipboard.length > 0)} disabled={!(props.clipboardItems.length > 0)}
imgSrc={pasteIcon} imgSrc={pasteIcon}
title={props.intl.formatMessage(messages.paste)} title={props.intl.formatMessage(messages.paste)}
onClick={props.onPasteFromClipboard} onClick={props.onPasteFromClipboard}
@ -153,7 +153,7 @@ const ModeToolsComponent = props => {
ModeToolsComponent.propTypes = { ModeToolsComponent.propTypes = {
brushValue: PropTypes.number, brushValue: PropTypes.number,
className: PropTypes.string, className: PropTypes.string,
clipboard: PropTypes.arrayOf(PropTypes.array), clipboardItems: PropTypes.arrayOf(PropTypes.array),
eraserValue: PropTypes.number, eraserValue: PropTypes.number,
intl: intlShape.isRequired, intl: intlShape.isRequired,
mode: PropTypes.string.isRequired, mode: PropTypes.string.isRequired,
@ -167,7 +167,7 @@ ModeToolsComponent.propTypes = {
const mapStateToProps = state => ({ const mapStateToProps = state => ({
mode: state.scratchPaint.mode, mode: state.scratchPaint.mode,
brushValue: state.scratchPaint.brushMode.brushSize, brushValue: state.scratchPaint.brushMode.brushSize,
clipboard: state.scratchPaint.clipboard, clipboardItems: state.scratchPaint.clipboard.items,
eraserValue: state.scratchPaint.eraserMode.brushSize, eraserValue: state.scratchPaint.eraserMode.brushSize,
selectedItems: state.scratchPaint.selectedItems selectedItems: state.scratchPaint.selectedItems
}); });

View file

@ -5,7 +5,7 @@ import PaintEditorComponent from '../components/paint-editor/paint-editor.jsx';
import {changeMode} from '../reducers/modes'; import {changeMode} from '../reducers/modes';
import {undo, redo, undoSnapshot} from '../reducers/undo'; import {undo, redo, undoSnapshot} from '../reducers/undo';
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items'; import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
import {setClipboardItems} from '../reducers/clipboard'; import {incrementPasteOffset, setClipboardItems} from '../reducers/clipboard';
import {getGuideLayer, getBackgroundGuideLayer} from '../helper/layer'; import {getGuideLayer, getBackgroundGuideLayer} from '../helper/layer';
import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRedo} from '../helper/undo'; import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRedo} from '../helper/undo';
@ -115,14 +115,17 @@ class PaintEditor extends React.Component {
handlePasteFromClipboard () { handlePasteFromClipboard () {
clearSelection(this.props.clearSelectedItems); clearSelection(this.props.clearSelectedItems);
if (this.props.clipboard.length > 0) { if (this.props.clipboardItems.length > 0) {
for (let i = 0; i < this.props.clipboard.length; i++) { for (let i = 0; i < this.props.clipboardItems.length; i++) {
const item = paper.Base.importJSON(this.props.clipboard[i]); const item = paper.Base.importJSON(this.props.clipboardItems[i]);
if (item) { if (item) {
item.selected = true; item.selected = true;
} }
paper.project.getActiveLayer().addChild(item); const placedItem = paper.project.getActiveLayer().addChild(item);
placedItem.position.x += 10 * this.props.pasteOffset;
placedItem.position.y += 10 * this.props.pasteOffset;
} }
this.props.incrementPasteOffset();
this.props.setSelectedItems(); this.props.setSelectedItems();
paper.project.view.update(); paper.project.view.update();
this.handleUpdateSvg(); this.handleUpdateSvg();
@ -175,13 +178,15 @@ class PaintEditor extends React.Component {
PaintEditor.propTypes = { PaintEditor.propTypes = {
clearSelectedItems: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired,
clipboard: PropTypes.arrayOf(PropTypes.array), clipboardItems: PropTypes.arrayOf(PropTypes.array),
incrementPasteOffset: PropTypes.func.isRequired,
name: PropTypes.string, name: PropTypes.string,
onKeyPress: PropTypes.func.isRequired, onKeyPress: PropTypes.func.isRequired,
onRedo: PropTypes.func.isRequired, onRedo: PropTypes.func.isRequired,
onUndo: PropTypes.func.isRequired, onUndo: PropTypes.func.isRequired,
onUpdateName: PropTypes.func.isRequired, onUpdateName: PropTypes.func.isRequired,
onUpdateSvg: PropTypes.func.isRequired, onUpdateSvg: PropTypes.func.isRequired,
pasteOffset: PropTypes.number,
rotationCenterX: PropTypes.number, rotationCenterX: PropTypes.number,
rotationCenterY: PropTypes.number, rotationCenterY: PropTypes.number,
setClipboardItems: PropTypes.func.isRequired, setClipboardItems: PropTypes.func.isRequired,
@ -198,7 +203,8 @@ PaintEditor.propTypes = {
const mapStateToProps = state => ({ const mapStateToProps = state => ({
selectedItems: state.scratchPaint.selectedItems, selectedItems: state.scratchPaint.selectedItems,
undoState: state.scratchPaint.undo, undoState: state.scratchPaint.undo,
clipboard: state.scratchPaint.clipboard clipboardItems: state.scratchPaint.clipboard.items,
pasteOffset: state.scratchPaint.clipboard.pasteOffset
}); });
const mapDispatchToProps = dispatch => ({ const mapDispatchToProps = dispatch => ({
onKeyPress: event => { onKeyPress: event => {
@ -229,6 +235,9 @@ const mapDispatchToProps = dispatch => ({
}, },
setClipboardItems: items => { setClipboardItems: items => {
dispatch(setClipboardItems(items)); dispatch(setClipboardItems(items));
},
incrementPasteOffset: () => {
dispatch(incrementPasteOffset());
} }
}); });

View file

@ -13,6 +13,7 @@ import {deleteSelection, getSelectedLeafItems} from '../helper/selection';
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items'; import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
import {pan, resetZoom, zoomOnFixedPoint} from '../helper/view'; import {pan, resetZoom, zoomOnFixedPoint} from '../helper/view';
import {clearHoveredItem} from '../reducers/hover'; import {clearHoveredItem} from '../reducers/hover';
import {clearPasteOffset} from '../reducers/clipboard';
import styles from './paper-canvas.css'; import styles from './paper-canvas.css';
@ -50,6 +51,7 @@ class PaperCanvas extends React.Component {
this.props.clearUndo(); this.props.clearUndo();
this.props.clearSelectedItems(); this.props.clearSelectedItems();
this.props.clearHoveredItem(); this.props.clearHoveredItem();
this.props.clearPasteOffset();
if (newProps.svg) { if (newProps.svg) {
// Store the zoom/pan and restore it after importing a new SVG // Store the zoom/pan and restore it after importing a new SVG
const oldZoom = paper.project.view.zoom; const oldZoom = paper.project.view.zoom;
@ -160,6 +162,7 @@ class PaperCanvas extends React.Component {
PaperCanvas.propTypes = { PaperCanvas.propTypes = {
canvasRef: PropTypes.func, canvasRef: PropTypes.func,
clearHoveredItem: PropTypes.func.isRequired, clearHoveredItem: PropTypes.func.isRequired,
clearPasteOffset: PropTypes.func.isRequired,
clearSelectedItems: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired,
clearUndo: PropTypes.func.isRequired, clearUndo: PropTypes.func.isRequired,
mode: PropTypes.oneOf(Object.values(Modes)), mode: PropTypes.oneOf(Object.values(Modes)),
@ -189,6 +192,9 @@ const mapDispatchToProps = dispatch => ({
}, },
clearHoveredItem: () => { clearHoveredItem: () => {
dispatch(clearHoveredItem()); dispatch(clearHoveredItem());
},
clearPasteOffset: () => {
dispatch(clearPasteOffset());
} }
}); });

View file

@ -1,7 +1,12 @@
import log from '../log/log'; import log from '../log/log';
const SET = 'scratch-paint/clipboard/SET'; const SET = 'scratch-paint/clipboard/SET';
const initialState = []; const INCREMENT_PASTE_OFFSET = 'scratch-paint/clipboard/INCREMENT_PASTE_OFFSET';
const CLEAR_PASTE_OFFSET = 'scratch-paint/clipboard/CLEAR_PASTE_OFFSET';
const initialState = {
items: [],
pasteOffset: 0
};
const reducer = function (state, action) { const reducer = function (state, action) {
if (typeof state === 'undefined') state = initialState; if (typeof state === 'undefined') state = initialState;
@ -11,7 +16,20 @@ const reducer = function (state, action) {
log.warn(`Invalid clipboard item format`); log.warn(`Invalid clipboard item format`);
return state; return state;
} }
return action.clipboardItems; return {
items: action.clipboardItems,
pasteOffset: 1
};
case INCREMENT_PASTE_OFFSET:
return {
items: state.items,
pasteOffset: state.pasteOffset + 1
};
case CLEAR_PASTE_OFFSET:
return {
items: state.items,
pasteOffset: 0
};
default: default:
return state; return state;
} }
@ -25,7 +43,21 @@ const setClipboardItems = function (clipboardItems) {
}; };
}; };
const incrementPasteOffset = function () {
return {
type: INCREMENT_PASTE_OFFSET
};
};
const clearPasteOffset = function () {
return {
type: CLEAR_PASTE_OFFSET
};
};
export { export {
reducer as default, reducer as default,
setClipboardItems setClipboardItems,
incrementPasteOffset,
clearPasteOffset
}; };

View file

@ -1,11 +1,12 @@
/* eslint-env jest */ /* eslint-env jest */
import clipboardReducer from '../../src/reducers/clipboard'; import clipboardReducer from '../../src/reducers/clipboard';
import {setClipboardItems} from '../../src/reducers/clipboard'; import {clearPasteOffset, incrementPasteOffset, setClipboardItems} from '../../src/reducers/clipboard';
test('initialState', () => { test('initialState', () => {
let defaultState; let defaultState;
expect(clipboardReducer(defaultState /* state */, {type: 'anything'} /* action */)).toBeDefined(); expect(clipboardReducer(defaultState /* state */, {type: 'anything'} /* action */).items).toBeDefined();
expect(clipboardReducer(defaultState /* state */, {type: 'anything'} /* action */).pasteOffset).toBeDefined();
}); });
test('setClipboardItems', () => { test('setClipboardItems', () => {
@ -13,14 +14,45 @@ test('setClipboardItems', () => {
const newSelected1 = ['selected1', 'selected2']; const newSelected1 = ['selected1', 'selected2'];
const newSelected2 = ['selected1', 'selected3']; const newSelected2 = ['selected1', 'selected3'];
expect(clipboardReducer(defaultState /* state */, setClipboardItems(newSelected1) /* action */)) expect(clipboardReducer(defaultState /* state */, setClipboardItems(newSelected1) /* action */).items)
.toEqual(newSelected1); .toEqual(newSelected1);
expect(clipboardReducer(newSelected1, setClipboardItems(newSelected2) /* action */)) expect(clipboardReducer(defaultState /* state */, setClipboardItems(newSelected1) /* action */).pasteOffset)
.toEqual(1);
expect(clipboardReducer(newSelected1, setClipboardItems(newSelected2) /* action */).items)
.toEqual(newSelected2); .toEqual(newSelected2);
expect(clipboardReducer(defaultState /* state */, setClipboardItems(newSelected1) /* action */).pasteOffset)
.toEqual(1);
});
test('incrementPasteOffset', () => {
const origState = {
items: ['selected1', 'selected2'],
pasteOffset: 1
};
expect(clipboardReducer(origState /* state */, incrementPasteOffset() /* action */).pasteOffset)
.toEqual(2);
expect(clipboardReducer(origState, incrementPasteOffset() /* action */).items)
.toEqual(origState.items);
});
test('clearPasteOffset', () => {
const origState = {
items: ['selected1', 'selected2'],
pasteOffset: 1
};
expect(clipboardReducer(origState /* state */, clearPasteOffset() /* action */).pasteOffset)
.toEqual(0);
expect(clipboardReducer(origState, clearPasteOffset() /* action */).items)
.toEqual(origState.items);
}); });
test('invalidSetClipboardItems', () => { test('invalidSetClipboardItems', () => {
const origState = ['selected1', 'selected2']; const origState = {
items: ['selected1', 'selected2'],
pasteOffset: 1
};
const nothingSelected = []; const nothingSelected = [];
expect(clipboardReducer(origState /* state */, setClipboardItems() /* action */)) expect(clipboardReducer(origState /* state */, setClipboardItems() /* action */))