mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -05:00
Offset the pasted item from the original when on the same costume
This commit is contained in:
parent
cc920e8969
commit
a5a60b0884
5 changed files with 97 additions and 18 deletions
|
@ -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
|
||||||
});
|
});
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 */))
|
||||||
|
|
Loading…
Reference in a new issue