mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -05:00
commit
6e9a2ef178
6 changed files with 109 additions and 57 deletions
|
@ -11,7 +11,6 @@ import SelectMode from '../../containers/select-mode.jsx';
|
|||
import LineMode from '../../containers/line-mode.jsx';
|
||||
import PenMode from '../../containers/pen-mode.jsx';
|
||||
import RectMode from '../../containers/rect-mode.jsx';
|
||||
import RoundedRectMode from '../../containers/rounded-rect-mode.jsx';
|
||||
import OvalMode from '../../containers/oval-mode.jsx';
|
||||
|
||||
import FillColorIndicatorComponent from '../../containers/fill-color-indicator.jsx';
|
||||
|
@ -159,9 +158,6 @@ class PaintEditorComponent extends React.Component {
|
|||
<RectMode
|
||||
onUpdateSvg={this.props.onUpdateSvg}
|
||||
/>
|
||||
<RoundedRectMode
|
||||
onUpdateSvg={this.props.onUpdateSvg}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
|
|
@ -5,10 +5,9 @@ import bindAll from 'lodash.bindall';
|
|||
import Modes from '../modes/modes';
|
||||
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import {clearHoveredItem, setHoveredItem} from '../reducers/hover';
|
||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||
|
||||
import {getSelectedLeafItems} from '../helper/selection';
|
||||
import {clearSelection, getSelectedLeafItems} from '../helper/selection';
|
||||
import RectTool from '../helper/tools/rect-tool';
|
||||
import RectModeComponent from '../components/rect-mode/rect-mode.jsx';
|
||||
|
||||
|
@ -26,8 +25,8 @@ class RectMode extends React.Component {
|
|||
}
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {
|
||||
this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);
|
||||
if (this.tool && nextProps.colorState !== this.props.colorState) {
|
||||
this.tool.setColorState(nextProps.colorState);
|
||||
}
|
||||
|
||||
if (nextProps.isRectModeActive && !this.props.isRectModeActive) {
|
||||
|
@ -40,13 +39,13 @@ class RectMode extends React.Component {
|
|||
return nextProps.isRectModeActive !== this.props.isRectModeActive;
|
||||
}
|
||||
activateTool () {
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
this.tool = new RectTool(
|
||||
this.props.setHoveredItem,
|
||||
this.props.clearHoveredItem,
|
||||
this.props.setSelectedItems,
|
||||
this.props.clearSelectedItems,
|
||||
this.props.onUpdateSvg
|
||||
);
|
||||
this.tool.setColorState(this.props.colorState);
|
||||
this.tool.activate();
|
||||
}
|
||||
deactivateTool () {
|
||||
|
@ -65,27 +64,23 @@ class RectMode extends React.Component {
|
|||
}
|
||||
|
||||
RectMode.propTypes = {
|
||||
clearHoveredItem: PropTypes.func.isRequired,
|
||||
clearSelectedItems: PropTypes.func.isRequired,
|
||||
colorState: PropTypes.shape({
|
||||
fillColor: PropTypes.string,
|
||||
strokeColor: PropTypes.string,
|
||||
strokeWidth: PropTypes.number
|
||||
}).isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
hoveredItemId: PropTypes.number,
|
||||
isRectModeActive: PropTypes.bool.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
setHoveredItem: PropTypes.func.isRequired,
|
||||
setSelectedItems: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isRectModeActive: state.scratchPaint.mode === Modes.RECT,
|
||||
hoveredItemId: state.scratchPaint.hoveredItemId
|
||||
colorState: state.scratchPaint.color,
|
||||
isRectModeActive: state.scratchPaint.mode === Modes.RECT
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setHoveredItem: hoveredItemId => {
|
||||
dispatch(setHoveredItem(hoveredItemId));
|
||||
},
|
||||
clearHoveredItem: () => {
|
||||
dispatch(clearHoveredItem());
|
||||
},
|
||||
clearSelectedItems: () => {
|
||||
dispatch(clearSelectedItems());
|
||||
},
|
||||
|
|
|
@ -84,8 +84,8 @@ class BoundingBoxTool {
|
|||
|
||||
const hitProperties = {
|
||||
hitResult: hitResult,
|
||||
clone: event.modifiers.alt,
|
||||
multiselect: event.modifiers.shift
|
||||
clone: clone,
|
||||
multiselect: multiselect
|
||||
};
|
||||
if (this.mode === BoundingBoxModes.MOVE) {
|
||||
this._modeMap[this.mode].onMouseDown(hitProperties);
|
||||
|
|
|
@ -126,22 +126,18 @@ const clearSelection = function (dispatchClearSelect) {
|
|||
* @return {Array<paper.Item>} in increasing Z order.
|
||||
*/
|
||||
const getSelectedRootItems = function () {
|
||||
const allItems = paper.project.selectedItems;
|
||||
const itemsAndGroups = [];
|
||||
const allItems = getAllSelectableRootItems();
|
||||
const items = [];
|
||||
|
||||
for (let i = 0; i < allItems.length; i++) {
|
||||
const item = allItems[i];
|
||||
if ((isGroup(item) && !isGroup(item.parent)) ||
|
||||
!isGroup(item.parent)) {
|
||||
if (item.data && !item.data.isSelectionBound) {
|
||||
itemsAndGroups.push(item);
|
||||
}
|
||||
for (const item of allItems) {
|
||||
if (item.selected) {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
// sort items by index (0 at bottom)
|
||||
itemsAndGroups.sort((a, b) => parseFloat(a.index) - parseFloat(b.index));
|
||||
return itemsAndGroups;
|
||||
items.sort((a, b) => parseFloat(a.index) - parseFloat(b.index));
|
||||
return items;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -155,7 +151,7 @@ const getSelectedLeafItems = function () {
|
|||
|
||||
for (let i = 0; i < allItems.length; i++) {
|
||||
const item = allItems[i];
|
||||
if (!isGroup(item) && item.data && !item.data.isSelectionBound) {
|
||||
if (!(item instanceof paper.Layer) && !isGroup(item) && item.data && !item.data.isSelectionBound) {
|
||||
items.push(item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -230,6 +230,12 @@ const styleCursorPreview = function (path, options) {
|
|||
}
|
||||
};
|
||||
|
||||
const styleShape = function (path, options) {
|
||||
path.fillColor = options.fillColor;
|
||||
path.strokeColor = options.strokeColor;
|
||||
path.strokeWidth = options.strokeWidth;
|
||||
};
|
||||
|
||||
export {
|
||||
applyFillColorToSelection,
|
||||
applyStrokeColorToSelection,
|
||||
|
@ -237,6 +243,7 @@ export {
|
|||
getColorsFromSelection,
|
||||
MIXED,
|
||||
styleBlob,
|
||||
styleShape,
|
||||
stylePath,
|
||||
styleCursorPreview
|
||||
};
|
||||
|
|
|
@ -1,53 +1,111 @@
|
|||
import paper from '@scratch/paper';
|
||||
import log from '../../log/log';
|
||||
import Modes from '../../modes/modes';
|
||||
import {styleShape} from '../style-path';
|
||||
import {clearSelection} from '../selection';
|
||||
import BoundingBoxTool from '../selection-tools/bounding-box-tool';
|
||||
|
||||
/**
|
||||
* Tool for drawing rectangles.
|
||||
*/
|
||||
class RectTool extends paper.Tool {
|
||||
static get TOLERANCE () {
|
||||
return 6;
|
||||
}
|
||||
/**
|
||||
* @param {function} setHoveredItem Callback to set the hovered item
|
||||
* @param {function} clearHoveredItem Callback to clear the hovered item
|
||||
* @param {function} setSelectedItems Callback to set the set of selected items in the Redux state
|
||||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
super();
|
||||
this.setHoveredItem = setHoveredItem;
|
||||
this.clearHoveredItem = clearHoveredItem;
|
||||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.prevHoveredItemId = null;
|
||||
this.boundingBoxTool = new BoundingBoxTool(Modes.RECT, setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
|
||||
// We have to set these functions instead of just declaring them because
|
||||
// paper.js tools hook up the listeners in the setter functions.
|
||||
this.onMouseDown = this.handleMouseDown;
|
||||
this.onMouseMove = this.handleMouseMove;
|
||||
this.onMouseDrag = this.handleMouseDrag;
|
||||
this.onMouseUp = this.handleMouseUp;
|
||||
|
||||
this.downPoint = null;
|
||||
this.rect = null;
|
||||
this.colorState = null;
|
||||
this.isBoundingBoxMode = null;
|
||||
}
|
||||
/**
|
||||
* To be called when the hovered item changes. When the select tool hovers over a
|
||||
* new item, it compares against this to see if a hover item change event needs to
|
||||
* be fired.
|
||||
* @param {paper.Item} prevHoveredItemId ID of the highlight item that indicates the mouse is
|
||||
* over a given item currently
|
||||
*/
|
||||
setPrevHoveredItemId (prevHoveredItemId) {
|
||||
this.prevHoveredItemId = prevHoveredItemId;
|
||||
getHitOptions () {
|
||||
return {
|
||||
segments: true,
|
||||
stroke: true,
|
||||
curves: true,
|
||||
fill: true,
|
||||
guide: false,
|
||||
match: hitResult =>
|
||||
(hitResult.item.data && hitResult.item.data.isHelperItem) ||
|
||||
hitResult.item.selected, // Allow hits on bounding box and selected only
|
||||
tolerance: RectTool.TOLERANCE / paper.view.zoom
|
||||
};
|
||||
}
|
||||
handleMouseDown () {
|
||||
log.warn('Rectangle tool not yet implemented');
|
||||
setColorState (colorState) {
|
||||
this.colorState = colorState;
|
||||
}
|
||||
handleMouseMove () {
|
||||
handleMouseDown (event) {
|
||||
if (this.boundingBoxTool.onMouseDown(event, false /* clone */, false /* multiselect */, this.getHitOptions())) {
|
||||
this.isBoundingBoxMode = true;
|
||||
} else {
|
||||
this.isBoundingBoxMode = false;
|
||||
clearSelection(this.clearSelectedItems);
|
||||
}
|
||||
}
|
||||
handleMouseDrag () {
|
||||
handleMouseDrag (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
|
||||
if (this.isBoundingBoxMode) {
|
||||
this.boundingBoxTool.onMouseDrag(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.rect) {
|
||||
this.rect.remove();
|
||||
}
|
||||
|
||||
const rect = new paper.Rectangle(event.downPoint, event.point);
|
||||
if (event.modifiers.shift) {
|
||||
rect.height = rect.width;
|
||||
}
|
||||
this.rect = new paper.Path.Rectangle(rect);
|
||||
|
||||
if (event.modifiers.alt) {
|
||||
this.rect.position = event.downPoint;
|
||||
}
|
||||
|
||||
styleShape(this.rect, this.colorState);
|
||||
}
|
||||
handleMouseUp () {
|
||||
handleMouseUp (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
|
||||
if (this.isBoundingBoxMode) {
|
||||
this.boundingBoxTool.onMouseUp(event);
|
||||
this.isBoundingBoxMode = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.rect) {
|
||||
if (this.rect.area < RectTool.TOLERANCE / paper.view.zoom) {
|
||||
// Tiny rectangle created unintentionally?
|
||||
this.rect.remove();
|
||||
this.rect = null;
|
||||
} else {
|
||||
this.rect.selected = true;
|
||||
this.boundingBoxTool.setSelectionBounds();
|
||||
this.onUpdateSvg();
|
||||
this.rect = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
deactivateTool () {
|
||||
this.boundingBoxTool.removeBoundsPath();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue