mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 13:32:28 -05:00
Keyboard shortcuts (#623)
This commit is contained in:
parent
97f669423a
commit
4474ec3aa1
10 changed files with 255 additions and 131 deletions
|
@ -27,6 +27,7 @@
|
|||
"classnames": "2.2.5",
|
||||
"keymirror": "0.1.1",
|
||||
"lodash.bindall": "4.4.0",
|
||||
"lodash.omit": "4.5.0",
|
||||
"minilog": "3.1.0",
|
||||
"parse-color": "1.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
|
|
|
@ -317,7 +317,7 @@ ModeToolsComponent.propTypes = {
|
|||
clipboardItems: PropTypes.arrayOf(PropTypes.array),
|
||||
eraserValue: PropTypes.number,
|
||||
fillBitmapShapes: PropTypes.bool,
|
||||
format: PropTypes.oneOf(Object.keys(Formats)).isRequired,
|
||||
format: PropTypes.oneOf(Object.keys(Formats)),
|
||||
hasSelectedUncurvedPoints: PropTypes.bool,
|
||||
hasSelectedUnpointedPoints: PropTypes.bool,
|
||||
intl: intlShape.isRequired,
|
||||
|
|
138
src/containers/copy-paste-hoc.jsx
Normal file
138
src/containers/copy-paste-hoc.jsx
Normal file
|
@ -0,0 +1,138 @@
|
|||
import paper from '@scratch/paper';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import omit from 'lodash.omit';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {
|
||||
clearSelection,
|
||||
getSelectedLeafItems,
|
||||
getSelectedRootItems
|
||||
} from '../helper/selection';
|
||||
import {isBitmap} from '../lib/format';
|
||||
import Formats from '../lib/format';
|
||||
import Modes from '../lib/modes';
|
||||
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
import {incrementPasteOffset, setClipboardItems} from '../reducers/clipboard';
|
||||
|
||||
const CopyPasteHOC = function (WrappedComponent) {
|
||||
class CopyPasteWrapper extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'handleCopy',
|
||||
'handlePaste'
|
||||
]);
|
||||
}
|
||||
handleCopy () {
|
||||
let selectedItems = [];
|
||||
if (this.props.mode === Modes.RESHAPE) {
|
||||
const leafItems = getSelectedLeafItems();
|
||||
// Copy root of compound paths
|
||||
for (const item of leafItems) {
|
||||
if (item.parent && item.parent instanceof paper.CompoundPath) {
|
||||
selectedItems.push(item.parent);
|
||||
} else {
|
||||
selectedItems.push(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selectedItems = getSelectedRootItems();
|
||||
}
|
||||
if (selectedItems.length > 0) {
|
||||
const clipboardItems = [];
|
||||
for (let i = 0; i < selectedItems.length; i++) {
|
||||
const jsonItem = selectedItems[i].exportJSON({asString: false});
|
||||
clipboardItems.push(jsonItem);
|
||||
}
|
||||
this.props.setClipboardItems(clipboardItems);
|
||||
}
|
||||
}
|
||||
// Returns true if anything was pasted, false if nothing changed
|
||||
handlePaste () {
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
|
||||
if (this.props.clipboardItems.length === 0) return false;
|
||||
|
||||
let items = [];
|
||||
for (let i = 0; i < this.props.clipboardItems.length; i++) {
|
||||
const item = paper.Base.importJSON(this.props.clipboardItems[i]);
|
||||
if (item) {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
if (!items.length) return false;
|
||||
// If pasting a group or non-raster to bitmap, rasterize first
|
||||
if (isBitmap(this.props.format) && !(items.length === 1 && items[0] instanceof paper.Raster)) {
|
||||
const group = new paper.Group(items);
|
||||
items = [group.rasterize()];
|
||||
group.remove();
|
||||
}
|
||||
for (const item of items) {
|
||||
const placedItem = paper.project.getActiveLayer().addChild(item);
|
||||
placedItem.selected = true;
|
||||
placedItem.position.x += 10 * this.props.pasteOffset;
|
||||
placedItem.position.y += 10 * this.props.pasteOffset;
|
||||
}
|
||||
this.props.incrementPasteOffset();
|
||||
this.props.setSelectedItems(this.props.format);
|
||||
return true;
|
||||
}
|
||||
render () {
|
||||
const componentProps = omit(this.props, [
|
||||
'clearSelectedItems',
|
||||
'clipboardItems',
|
||||
'incrementPasteOffset',
|
||||
'pasteOffset',
|
||||
'setClipboardItems',
|
||||
'setSelectedItems']);
|
||||
return (
|
||||
<WrappedComponent
|
||||
onCopyToClipboard={this.handleCopy}
|
||||
onPasteFromClipboard={this.handlePaste}
|
||||
{...componentProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CopyPasteWrapper.propTypes = {
|
||||
clearSelectedItems: PropTypes.func.isRequired,
|
||||
clipboardItems: PropTypes.arrayOf(PropTypes.array),
|
||||
format: PropTypes.oneOf(Object.keys(Formats)),
|
||||
incrementPasteOffset: PropTypes.func.isRequired,
|
||||
mode: PropTypes.oneOf(Object.keys(Modes)),
|
||||
pasteOffset: PropTypes.number,
|
||||
setClipboardItems: PropTypes.func.isRequired,
|
||||
setSelectedItems: PropTypes.func.isRequired
|
||||
};
|
||||
const mapStateToProps = state => ({
|
||||
clipboardItems: state.scratchPaint.clipboard.items,
|
||||
format: state.scratchPaint.format,
|
||||
mode: state.scratchPaint.mode,
|
||||
pasteOffset: state.scratchPaint.clipboard.pasteOffset
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setClipboardItems: items => {
|
||||
dispatch(setClipboardItems(items));
|
||||
},
|
||||
incrementPasteOffset: () => {
|
||||
dispatch(incrementPasteOffset());
|
||||
},
|
||||
clearSelectedItems: () => {
|
||||
dispatch(clearSelectedItems());
|
||||
},
|
||||
setSelectedItems: format => {
|
||||
dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));
|
||||
}
|
||||
});
|
||||
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(CopyPasteWrapper);
|
||||
};
|
||||
|
||||
export default CopyPasteHOC;
|
|
@ -10,7 +10,7 @@ import {changeFont} from '../reducers/font';
|
|||
import {getSelectedLeafItems} from '../helper/selection';
|
||||
import styles from '../components/font-dropdown/font-dropdown.css';
|
||||
|
||||
class ModeToolsComponent extends React.Component {
|
||||
class FontDropdown extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
|
@ -164,7 +164,7 @@ class ModeToolsComponent extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
ModeToolsComponent.propTypes = {
|
||||
FontDropdown.propTypes = {
|
||||
changeFont: PropTypes.func.isRequired,
|
||||
font: PropTypes.string,
|
||||
onUpdateImage: PropTypes.func.isRequired
|
||||
|
@ -182,4 +182,4 @@ const mapDispatchToProps = dispatch => ({
|
|||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ModeToolsComponent);
|
||||
)(FontDropdown);
|
||||
|
|
|
@ -4,11 +4,11 @@ import PropTypes from 'prop-types';
|
|||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
|
||||
import CopyPasteHOC from './copy-paste-hoc.jsx';
|
||||
import ModeToolsComponent from '../components/mode-tools/mode-tools.jsx';
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
import {incrementPasteOffset, setClipboardItems} from '../reducers/clipboard';
|
||||
import {
|
||||
clearSelection,
|
||||
deleteSelection,
|
||||
getSelectedLeafItems,
|
||||
getSelectedRootItems,
|
||||
|
@ -29,7 +29,6 @@ class ModeTools extends React.Component {
|
|||
'_getSelectedUnpointedPoints',
|
||||
'hasSelectedUncurvedPoints',
|
||||
'hasSelectedUnpointedPoints',
|
||||
'handleCopyToClipboard',
|
||||
'handleCurvePoints',
|
||||
'handleFlipHorizontal',
|
||||
'handleFlipVertical',
|
||||
|
@ -191,57 +190,22 @@ class ModeTools extends React.Component {
|
|||
this._handleFlip(1, -1, selectedItems);
|
||||
}
|
||||
}
|
||||
handlePasteFromClipboard () {
|
||||
if (this.props.onPasteFromClipboard()) {
|
||||
this.props.onUpdateImage();
|
||||
}
|
||||
}
|
||||
handleDelete () {
|
||||
if (deleteSelection(this.props.mode, this.props.onUpdateImage)) {
|
||||
this.props.setSelectedItems(this.props.format);
|
||||
}
|
||||
}
|
||||
handleCopyToClipboard () {
|
||||
const selectedItems = getSelectedRootItems();
|
||||
if (selectedItems.length > 0) {
|
||||
const clipboardItems = [];
|
||||
for (let i = 0; i < selectedItems.length; i++) {
|
||||
const jsonItem = selectedItems[i].exportJSON({asString: false});
|
||||
clipboardItems.push(jsonItem);
|
||||
}
|
||||
this.props.setClipboardItems(clipboardItems);
|
||||
}
|
||||
}
|
||||
handlePasteFromClipboard () {
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
|
||||
if (this.props.clipboardItems.length > 0) {
|
||||
let items = [];
|
||||
for (let i = 0; i < this.props.clipboardItems.length; i++) {
|
||||
const item = paper.Base.importJSON(this.props.clipboardItems[i]);
|
||||
if (item) {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
if (!items.length) return;
|
||||
// If pasting a group or non-raster to bitmap, rasterize firsts
|
||||
if (isBitmap(this.props.format) && !(items.length === 1 && items[0] instanceof paper.Raster)) {
|
||||
const group = new paper.Group(items);
|
||||
items = [group.rasterize()];
|
||||
group.remove();
|
||||
}
|
||||
for (const item of items) {
|
||||
const placedItem = paper.project.getActiveLayer().addChild(item);
|
||||
placedItem.selected = true;
|
||||
placedItem.position.x += 10 * this.props.pasteOffset;
|
||||
placedItem.position.y += 10 * this.props.pasteOffset;
|
||||
}
|
||||
this.props.incrementPasteOffset();
|
||||
this.props.setSelectedItems(this.props.format);
|
||||
this.props.onUpdateImage();
|
||||
}
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<ModeToolsComponent
|
||||
hasSelectedUncurvedPoints={this.hasSelectedUncurvedPoints()}
|
||||
hasSelectedUnpointedPoints={this.hasSelectedUnpointedPoints()}
|
||||
onCopyToClipboard={this.handleCopyToClipboard}
|
||||
onCopyToClipboard={this.props.onCopyToClipboard}
|
||||
onCurvePoints={this.handleCurvePoints}
|
||||
onDelete={this.handleDelete}
|
||||
onFlipHorizontal={this.handleFlipHorizontal}
|
||||
|
@ -255,17 +219,14 @@ class ModeTools extends React.Component {
|
|||
}
|
||||
|
||||
ModeTools.propTypes = {
|
||||
clearSelectedItems: PropTypes.func.isRequired,
|
||||
clipboardItems: PropTypes.arrayOf(PropTypes.array),
|
||||
format: PropTypes.oneOf(Object.keys(Formats)).isRequired,
|
||||
incrementPasteOffset: PropTypes.func.isRequired,
|
||||
format: PropTypes.oneOf(Object.keys(Formats)),
|
||||
mode: PropTypes.oneOf(Object.keys(Modes)),
|
||||
onCopyToClipboard: PropTypes.func.isRequired,
|
||||
onPasteFromClipboard: PropTypes.func.isRequired,
|
||||
onUpdateImage: PropTypes.func.isRequired,
|
||||
pasteOffset: PropTypes.number,
|
||||
// Listen on selected items to update hasSelectedPoints
|
||||
selectedItems:
|
||||
PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)), // eslint-disable-line react/no-unused-prop-types
|
||||
setClipboardItems: PropTypes.func.isRequired,
|
||||
setSelectedItems: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
@ -291,7 +252,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
export default CopyPasteHOC(connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ModeTools);
|
||||
)(ModeTools));
|
||||
|
|
|
@ -5,6 +5,8 @@ import log from '../log/log';
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import PaintEditorComponent from '../components/paint-editor/paint-editor.jsx';
|
||||
import CopyPasteHOC from './copy-paste-hoc.jsx';
|
||||
import SelectionHOC from './selection-hoc.jsx';
|
||||
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {changeFormat} from '../reducers/format';
|
||||
|
@ -16,12 +18,14 @@ import {updateViewBounds} from '../reducers/view-bounds';
|
|||
import {setLayout} from '../reducers/layout';
|
||||
|
||||
import {getRaster, hideGuideLayers, showGuideLayers} from '../helper/layer';
|
||||
import {commitSelectionToBitmap, convertToBitmap, convertToVector, getHitBounds} from '../helper/bitmap';
|
||||
import {commitSelectionToBitmap, convertToBitmap, convertToVector, getHitBounds,
|
||||
selectAllBitmap} from '../helper/bitmap';
|
||||
import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRedo} from '../helper/undo';
|
||||
import {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order';
|
||||
import {groupSelection, ungroupSelection} from '../helper/group';
|
||||
import {scaleWithStrokes} from '../helper/math';
|
||||
import {getSelectedLeafItems} from '../helper/selection';
|
||||
import {clearSelection, deleteSelection, getSelectedLeafItems,
|
||||
selectAllItems, selectAllSegments} from '../helper/selection';
|
||||
import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, SVG_ART_BOARD_WIDTH, SVG_ART_BOARD_HEIGHT} from '../helper/view';
|
||||
import {resetZoom, zoomOnSelection} from '../helper/view';
|
||||
import EyeDropperTool from '../helper/tools/eye-dropper';
|
||||
|
@ -57,6 +61,7 @@ class PaintEditor extends React.Component {
|
|||
'canRedo',
|
||||
'canUndo',
|
||||
'switchMode',
|
||||
'onKeyPress',
|
||||
'onMouseDown',
|
||||
'setCanvas',
|
||||
'setTextArea',
|
||||
|
@ -73,14 +78,7 @@ class PaintEditor extends React.Component {
|
|||
this.props.setLayout(this.props.rtl ? 'rtl' : 'ltr');
|
||||
}
|
||||
componentDidMount () {
|
||||
document.addEventListener('keydown', (/* event */) => {
|
||||
// Don't activate keyboard shortcuts during text editing
|
||||
if (!this.props.textEditing) {
|
||||
// @todo disabling keyboard shortcuts because there is a bug
|
||||
// that is interfering with text editing.
|
||||
// this.props.onKeyPress(event);
|
||||
}
|
||||
});
|
||||
document.addEventListener('keydown', this.onKeyPress);
|
||||
// document listeners used to detect if a mouse is down outside of the
|
||||
// canvas, and should therefore stop the eye dropper
|
||||
document.addEventListener('mousedown', this.onMouseDown);
|
||||
|
@ -119,7 +117,7 @@ class PaintEditor extends React.Component {
|
|||
}
|
||||
}
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('keydown', this.props.onKeyPress);
|
||||
document.removeEventListener('keydown', this.onKeyPress);
|
||||
this.stopEyeDroppingLoop();
|
||||
document.removeEventListener('mousedown', this.onMouseDown);
|
||||
document.removeEventListener('touchstart', this.onMouseDown);
|
||||
|
@ -313,6 +311,56 @@ class PaintEditor extends React.Component {
|
|||
setTextArea (element) {
|
||||
this.setState({textArea: element});
|
||||
}
|
||||
onKeyPress (event) {
|
||||
// Don't activate keyboard shortcuts during text editing
|
||||
if (this.props.textEditing) return;
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
} else if (event.key === 'Delete' || event.key === 'Backspace') {
|
||||
if (deleteSelection(this.props.mode, this.handleUpdateImage)) {
|
||||
this.handleSetSelectedItems();
|
||||
}
|
||||
} else if (event.metaKey || event.ctrlKey) {
|
||||
if (event.shiftKey && event.key === 'z') {
|
||||
this.handleRedo();
|
||||
} else if (event.key === 'z') {
|
||||
this.handleUndo();
|
||||
} else if (event.key === 'c') {
|
||||
this.props.onCopyToClipboard();
|
||||
} else if (event.key === 'v') {
|
||||
this.changeToASelectMode();
|
||||
if (this.props.onPasteFromClipboard()) {
|
||||
this.handleUpdateImage();
|
||||
}
|
||||
} else if (event.key === 'a') {
|
||||
this.changeToASelectMode();
|
||||
event.preventDefault();
|
||||
this.selectAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
changeToASelectMode () {
|
||||
if (isBitmap(this.props.format)) {
|
||||
if (this.props.mode !== Modes.BIT_SELECT) {
|
||||
this.props.changeMode(Modes.BIT_SELECT);
|
||||
}
|
||||
} else if (this.props.mode !== Modes.SELECT && this.props.mode !== Modes.RESHAPE) {
|
||||
this.props.changeMode(Modes.SELECT);
|
||||
}
|
||||
}
|
||||
selectAll () {
|
||||
if (isBitmap(this.props.format)) {
|
||||
selectAllBitmap(this.props.clearSelectedItems);
|
||||
this.handleSetSelectedItems();
|
||||
} else if (this.props.mode === Modes.RESHAPE) {
|
||||
if (selectAllSegments()) this.handleSetSelectedItems();
|
||||
} else {
|
||||
// Disable lint for easier to read logic
|
||||
if (selectAllItems()) this.handleSetSelectedItems(); // eslint-disable-line no-lonely-if
|
||||
}
|
||||
}
|
||||
onMouseDown (event) {
|
||||
if (event.target === paper.view.element &&
|
||||
document.activeElement instanceof HTMLInputElement) {
|
||||
|
@ -431,8 +479,9 @@ PaintEditor.propTypes = {
|
|||
isEyeDropping: PropTypes.bool,
|
||||
mode: PropTypes.oneOf(Object.keys(Modes)).isRequired,
|
||||
name: PropTypes.string,
|
||||
onCopyToClipboard: PropTypes.func.isRequired,
|
||||
onDeactivateEyeDropper: PropTypes.func.isRequired,
|
||||
onKeyPress: PropTypes.func.isRequired,
|
||||
onPasteFromClipboard: PropTypes.func.isRequired,
|
||||
onRedo: PropTypes.func.isRequired,
|
||||
onUndo: PropTypes.func.isRequired,
|
||||
onUpdateImage: PropTypes.func.isRequired,
|
||||
|
@ -471,27 +520,6 @@ const mapStateToProps = state => ({
|
|||
viewBounds: state.scratchPaint.viewBounds
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onKeyPress: event => {
|
||||
if (event.key === 'e') {
|
||||
dispatch(changeMode(Modes.ERASER));
|
||||
} else if (event.key === 'b') {
|
||||
dispatch(changeMode(Modes.BRUSH));
|
||||
} else if (event.key === 'l') {
|
||||
dispatch(changeMode(Modes.LINE));
|
||||
} else if (event.key === 's') {
|
||||
dispatch(changeMode(Modes.SELECT));
|
||||
} else if (event.key === 'w') {
|
||||
dispatch(changeMode(Modes.RESHAPE));
|
||||
} else if (event.key === 'f') {
|
||||
dispatch(changeMode(Modes.FILL));
|
||||
} else if (event.key === 't') {
|
||||
dispatch(changeMode(Modes.TEXT));
|
||||
} else if (event.key === 'c') {
|
||||
dispatch(changeMode(Modes.OVAL));
|
||||
} else if (event.key === 'r') {
|
||||
dispatch(changeMode(Modes.RECT));
|
||||
}
|
||||
},
|
||||
changeMode: mode => {
|
||||
dispatch(changeMode(mode));
|
||||
},
|
||||
|
@ -531,7 +559,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
export default SelectionHOC(CopyPasteHOC(connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(PaintEditor);
|
||||
)(PaintEditor)));
|
||||
|
|
|
@ -4,16 +4,13 @@ import React from 'react';
|
|||
import {connect} from 'react-redux';
|
||||
import paper from '@scratch/paper';
|
||||
import Formats from '../lib/format';
|
||||
import {isBitmap} from '../lib/format';
|
||||
import Modes from '../lib/modes';
|
||||
import log from '../log/log';
|
||||
|
||||
import {performSnapshot} from '../helper/undo';
|
||||
import {undoSnapshot, clearUndoState} from '../reducers/undo';
|
||||
import {isGroup, ungroupItems} from '../helper/group';
|
||||
import {clearRaster, getRaster, setupLayers} from '../helper/layer';
|
||||
import {deleteSelection, getSelectedLeafItems} from '../helper/selection';
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
import {clearSelectedItems} from '../reducers/selected-items';
|
||||
import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, resetZoom} from '../helper/view';
|
||||
import {ensureClockwise, scaleWithStrokes} from '../helper/math';
|
||||
import {clearHoveredItem} from '../reducers/hover';
|
||||
|
@ -28,12 +25,10 @@ class PaperCanvas extends React.Component {
|
|||
bindAll(this, [
|
||||
'setCanvas',
|
||||
'importSvg',
|
||||
'handleKeyDown',
|
||||
'switchCostume'
|
||||
]);
|
||||
}
|
||||
componentDidMount () {
|
||||
document.addEventListener('keydown', this.handleKeyDown);
|
||||
paper.setup(this.canvas);
|
||||
resetZoom();
|
||||
this.props.updateViewBounds(paper.view.matrix);
|
||||
|
@ -57,19 +52,6 @@ class PaperCanvas extends React.Component {
|
|||
}
|
||||
componentWillUnmount () {
|
||||
paper.remove();
|
||||
document.removeEventListener('keydown', this.handleKeyDown);
|
||||
}
|
||||
handleKeyDown (event) {
|
||||
if (event.target instanceof HTMLInputElement) {
|
||||
// Ignore delete if a text input field is focused
|
||||
return;
|
||||
}
|
||||
// Backspace, delete
|
||||
if (event.key === 'Delete' || event.key === 'Backspace') {
|
||||
if (deleteSelection(this.props.mode, this.props.onUpdateImage)) {
|
||||
this.props.setSelectedItems(this.props.format);
|
||||
}
|
||||
}
|
||||
}
|
||||
switchCostume (format, image, rotationCenterX, rotationCenterY) {
|
||||
for (const layer of paper.project.layers) {
|
||||
|
@ -231,18 +213,14 @@ PaperCanvas.propTypes = {
|
|||
clearPasteOffset: PropTypes.func.isRequired,
|
||||
clearSelectedItems: PropTypes.func.isRequired,
|
||||
clearUndo: PropTypes.func.isRequired,
|
||||
format: PropTypes.oneOf(Object.keys(Formats)), // Internal, up-to-date data format
|
||||
image: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.instanceOf(HTMLImageElement)
|
||||
]),
|
||||
imageFormat: PropTypes.string, // The incoming image's data format, used during import. The user could switch this.
|
||||
imageId: PropTypes.string,
|
||||
mode: PropTypes.oneOf(Object.keys(Modes)),
|
||||
onUpdateImage: PropTypes.func.isRequired,
|
||||
rotationCenterX: PropTypes.number,
|
||||
rotationCenterY: PropTypes.number,
|
||||
setSelectedItems: PropTypes.func.isRequired,
|
||||
undoSnapshot: PropTypes.func.isRequired,
|
||||
updateViewBounds: PropTypes.func.isRequired
|
||||
};
|
||||
|
@ -257,9 +235,6 @@ const mapDispatchToProps = dispatch => ({
|
|||
clearUndo: () => {
|
||||
dispatch(clearUndoState());
|
||||
},
|
||||
setSelectedItems: format => {
|
||||
dispatch(setSelectedItems(getSelectedLeafItems(), isBitmap(format)));
|
||||
},
|
||||
clearSelectedItems: () => {
|
||||
dispatch(clearSelectedItems());
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import paper from '@scratch/paper';
|
||||
import {createCanvas, clearRaster, getRaster, hideGuideLayers, showGuideLayers} from './layer';
|
||||
import {getGuideColor} from './guides';
|
||||
import {clearSelection} from './selection';
|
||||
import {inlineSvgFonts} from 'scratch-svg-renderer';
|
||||
|
||||
const forEachLinePoint = function (point1, point2, callback) {
|
||||
|
@ -347,7 +348,7 @@ const convertToBitmap = function (clearSelectedItems, onUpdateImage) {
|
|||
// @todo if the active layer contains only rasters, drawing them directly to the raster layer
|
||||
// would be more efficient.
|
||||
|
||||
clearSelectedItems();
|
||||
clearSelection(clearSelectedItems);
|
||||
|
||||
// Export svg
|
||||
const guideLayers = hideGuideLayers(true /* includeRaster */);
|
||||
|
@ -392,7 +393,7 @@ const convertToBitmap = function (clearSelectedItems, onUpdateImage) {
|
|||
};
|
||||
|
||||
const convertToVector = function (clearSelectedItems, onUpdateImage) {
|
||||
clearSelectedItems();
|
||||
clearSelection(clearSelectedItems);
|
||||
const trimmedRaster = trim_(getRaster());
|
||||
if (trimmedRaster) {
|
||||
paper.project.activeLayer.addChild(trimmedRaster);
|
||||
|
@ -722,6 +723,20 @@ const commitSelectionToBitmap = function (selection, bitmap) {
|
|||
commitArbitraryTransformation_(selection, bitmap);
|
||||
};
|
||||
|
||||
const selectAllBitmap = function (clearSelectedItems) {
|
||||
clearSelection(clearSelectedItems);
|
||||
|
||||
// Pull raster to active layer
|
||||
const raster = getRaster();
|
||||
raster.guide = false;
|
||||
raster.locked = false;
|
||||
raster.parent = paper.project.activeLayer;
|
||||
raster.selected = true;
|
||||
|
||||
// Clear raster layer
|
||||
clearRaster();
|
||||
};
|
||||
|
||||
export {
|
||||
commitSelectionToBitmap,
|
||||
convertToBitmap,
|
||||
|
@ -736,5 +751,6 @@ export {
|
|||
forEachLinePoint,
|
||||
flipBitmapHorizontal,
|
||||
flipBitmapVertical,
|
||||
scaleBitmap
|
||||
scaleBitmap,
|
||||
selectAllBitmap
|
||||
};
|
||||
|
|
|
@ -108,30 +108,34 @@ const setItemSelection = function (item, state, fullySelected) {
|
|||
}
|
||||
_setGroupSelection(item, state, fullySelected);
|
||||
}
|
||||
// @todo: Update toolbar state on change
|
||||
|
||||
};
|
||||
|
||||
/** @return {boolean} true if anything was selected */
|
||||
const selectAllItems = function () {
|
||||
const items = getAllSelectableRootItems();
|
||||
if (items.length === 0) return false;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
setItemSelection(items[i], true);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/** @return {boolean} true if anything was selected */
|
||||
const selectAllSegments = function () {
|
||||
const items = getAllSelectableRootItems();
|
||||
if (items.length === 0) return false;
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
selectItemSegments(items[i], true);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/** @param {!function} dispatchClearSelect Function to update the Redux select state */
|
||||
const clearSelection = function (dispatchClearSelect) {
|
||||
paper.project.deselectAll();
|
||||
// @todo: Update toolbar state on change
|
||||
dispatchClearSelect();
|
||||
};
|
||||
|
||||
|
@ -148,6 +152,14 @@ const getSelectedRootItems = function () {
|
|||
for (const item of allItems) {
|
||||
if (item.selected) {
|
||||
items.push(item);
|
||||
} else if (item instanceof paper.CompoundPath) {
|
||||
// Consider a compound path selected if any of its paths are selected
|
||||
for (const child of item.children) {
|
||||
if (child.selected) {
|
||||
items.push(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,13 +424,10 @@ const selectRootItem = function () {
|
|||
}
|
||||
};
|
||||
|
||||
const shouldShowSelectAll = function () {
|
||||
return paper.project.getItems({class: paper.PathItem}).length > 0;
|
||||
};
|
||||
|
||||
export {
|
||||
getItems,
|
||||
getAllRootItems,
|
||||
getAllSelectableRootItems,
|
||||
selectAllItems,
|
||||
selectAllSegments,
|
||||
clearSelection,
|
||||
|
@ -429,6 +438,5 @@ export {
|
|||
getSelectedRootItems,
|
||||
getSelectedSegments,
|
||||
processRectangularSelection,
|
||||
selectRootItem,
|
||||
shouldShowSelectAll
|
||||
selectRootItem
|
||||
};
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import PaintEditor from './containers/paint-editor.jsx';
|
||||
import SelectionHOC from './containers/selection-hoc.jsx';
|
||||
import ScratchPaintReducer from './reducers/scratch-paint-reducer';
|
||||
|
||||
const Wrapped = SelectionHOC(PaintEditor);
|
||||
|
||||
export {
|
||||
Wrapped as default,
|
||||
PaintEditor as default,
|
||||
ScratchPaintReducer
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue