mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2025-01-10 14:42:13 -05:00
Move the text edit target to the state and make fill work
This commit is contained in:
parent
7a0a0784e1
commit
8d61a7b060
13 changed files with 131 additions and 59 deletions
|
@ -30,7 +30,7 @@ class FillColorIndicator extends React.Component {
|
||||||
}
|
}
|
||||||
handleChangeFillColor (newColor) {
|
handleChangeFillColor (newColor) {
|
||||||
// Apply color and update redux, but do not update svg until picker closes.
|
// Apply color and update redux, but do not update svg until picker closes.
|
||||||
const isDifferent = applyFillColorToSelection(newColor);
|
const isDifferent = applyFillColorToSelection(newColor, this.props.textEditTarget);
|
||||||
this._hasChanged = this._hasChanged || isDifferent;
|
this._hasChanged = this._hasChanged || isDifferent;
|
||||||
this.props.onChangeFillColor(newColor);
|
this.props.onChangeFillColor(newColor);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,8 @@ const mapStateToProps = state => ({
|
||||||
disabled: state.scratchPaint.mode === Modes.LINE,
|
disabled: state.scratchPaint.mode === Modes.LINE,
|
||||||
fillColor: state.scratchPaint.color.fillColor,
|
fillColor: state.scratchPaint.color.fillColor,
|
||||||
fillColorModalVisible: state.scratchPaint.modals.fillColor,
|
fillColorModalVisible: state.scratchPaint.modals.fillColor,
|
||||||
isEyeDropping: state.scratchPaint.color.eyeDropper.active
|
isEyeDropping: state.scratchPaint.color.eyeDropper.active,
|
||||||
|
textEditTarget: state.scratchPaint.textEditTarget
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
@ -76,7 +77,8 @@ FillColorIndicator.propTypes = {
|
||||||
isEyeDropping: PropTypes.bool.isRequired,
|
isEyeDropping: PropTypes.bool.isRequired,
|
||||||
onChangeFillColor: PropTypes.func.isRequired,
|
onChangeFillColor: PropTypes.func.isRequired,
|
||||||
onCloseFillColor: PropTypes.func.isRequired,
|
onCloseFillColor: PropTypes.func.isRequired,
|
||||||
onUpdateSvg: PropTypes.func.isRequired
|
onUpdateSvg: PropTypes.func.isRequired,
|
||||||
|
textEditTarget: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -49,7 +49,12 @@ class PaintEditor extends React.Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.addEventListener('keydown', this.props.onKeyPress);
|
document.addEventListener('keydown', event => {
|
||||||
|
// Don't activate keyboard shortcuts during text editing
|
||||||
|
if (!this.props.textEditing) {
|
||||||
|
this.props.onKeyPress(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
// document listeners used to detect if a mouse is down outside of the
|
// document listeners used to detect if a mouse is down outside of the
|
||||||
// canvas, and should therefore stop the eye dropper
|
// canvas, and should therefore stop the eye dropper
|
||||||
document.addEventListener('mousedown', this.onMouseDown);
|
document.addEventListener('mousedown', this.onMouseDown);
|
||||||
|
@ -248,6 +253,7 @@ PaintEditor.propTypes = {
|
||||||
setSelectedItems: PropTypes.func.isRequired,
|
setSelectedItems: PropTypes.func.isRequired,
|
||||||
svg: PropTypes.string,
|
svg: PropTypes.string,
|
||||||
svgId: PropTypes.string,
|
svgId: PropTypes.string,
|
||||||
|
textEditing: PropTypes.bool.isRequired,
|
||||||
undoSnapshot: PropTypes.func.isRequired,
|
undoSnapshot: PropTypes.func.isRequired,
|
||||||
undoState: PropTypes.shape({
|
undoState: PropTypes.shape({
|
||||||
stack: PropTypes.arrayOf(PropTypes.object).isRequired,
|
stack: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
@ -262,12 +268,11 @@ const mapStateToProps = state => ({
|
||||||
pasteOffset: state.scratchPaint.clipboard.pasteOffset,
|
pasteOffset: state.scratchPaint.clipboard.pasteOffset,
|
||||||
previousTool: state.scratchPaint.color.eyeDropper.previousTool,
|
previousTool: state.scratchPaint.color.eyeDropper.previousTool,
|
||||||
selectedItems: state.scratchPaint.selectedItems,
|
selectedItems: state.scratchPaint.selectedItems,
|
||||||
|
textEditing: state.scratchPaint.textEditTarget !== null,
|
||||||
undoState: state.scratchPaint.undo
|
undoState: state.scratchPaint.undo
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
onKeyPress: event => {
|
onKeyPress: event => {
|
||||||
// TODO if text tool not in text edit mode
|
|
||||||
// TODO unfocus other text input fields
|
|
||||||
if (event.key === 'e') {
|
if (event.key === 'e') {
|
||||||
dispatch(changeMode(Modes.ERASER));
|
dispatch(changeMode(Modes.ERASER));
|
||||||
} else if (event.key === 'b') {
|
} else if (event.key === 'b') {
|
||||||
|
@ -276,6 +281,16 @@ const mapDispatchToProps = dispatch => ({
|
||||||
dispatch(changeMode(Modes.LINE));
|
dispatch(changeMode(Modes.LINE));
|
||||||
} else if (event.key === 's') {
|
} else if (event.key === 's') {
|
||||||
dispatch(changeMode(Modes.SELECT));
|
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));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
clearSelectedItems: () => {
|
clearSelectedItems: () => {
|
||||||
|
|
|
@ -30,7 +30,7 @@ class StrokeColorIndicator extends React.Component {
|
||||||
}
|
}
|
||||||
handleChangeStrokeColor (newColor) {
|
handleChangeStrokeColor (newColor) {
|
||||||
// Apply color and update redux, but do not update svg until picker closes.
|
// Apply color and update redux, but do not update svg until picker closes.
|
||||||
const isDifferent = applyStrokeColorToSelection(newColor);
|
const isDifferent = applyStrokeColorToSelection(newColor, this.props.textEditTarget);
|
||||||
this._hasChanged = this._hasChanged || isDifferent;
|
this._hasChanged = this._hasChanged || isDifferent;
|
||||||
this.props.onChangeStrokeColor(newColor);
|
this.props.onChangeStrokeColor(newColor);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,8 @@ const mapStateToProps = state => ({
|
||||||
disabled: state.scratchPaint.mode === Modes.BRUSH,
|
disabled: state.scratchPaint.mode === Modes.BRUSH,
|
||||||
isEyeDropping: state.scratchPaint.color.eyeDropper.active,
|
isEyeDropping: state.scratchPaint.color.eyeDropper.active,
|
||||||
strokeColor: state.scratchPaint.color.strokeColor,
|
strokeColor: state.scratchPaint.color.strokeColor,
|
||||||
strokeColorModalVisible: state.scratchPaint.modals.strokeColor
|
strokeColorModalVisible: state.scratchPaint.modals.strokeColor,
|
||||||
|
textEditTarget: state.scratchPaint.textEditTarget
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
@ -76,7 +77,8 @@ StrokeColorIndicator.propTypes = {
|
||||||
onCloseStrokeColor: PropTypes.func.isRequired,
|
onCloseStrokeColor: PropTypes.func.isRequired,
|
||||||
onUpdateSvg: PropTypes.func.isRequired,
|
onUpdateSvg: PropTypes.func.isRequired,
|
||||||
strokeColor: PropTypes.string,
|
strokeColor: PropTypes.string,
|
||||||
strokeColorModalVisible: PropTypes.bool.isRequired
|
strokeColorModalVisible: PropTypes.bool.isRequired,
|
||||||
|
textEditTarget: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -15,7 +15,9 @@ class StrokeWidthIndicator extends React.Component {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
handleChangeStrokeWidth (newWidth) {
|
handleChangeStrokeWidth (newWidth) {
|
||||||
applyStrokeWidthToSelection(newWidth, this.props.onUpdateSvg);
|
if (applyStrokeWidthToSelection(newWidth, this.props.textEditTarget)) {
|
||||||
|
this.props.onUpdateSvg();
|
||||||
|
}
|
||||||
this.props.onChangeStrokeWidth(newWidth);
|
this.props.onChangeStrokeWidth(newWidth);
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
|
@ -31,7 +33,8 @@ class StrokeWidthIndicator extends React.Component {
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
disabled: state.scratchPaint.mode === Modes.BRUSH,
|
disabled: state.scratchPaint.mode === Modes.BRUSH,
|
||||||
strokeWidth: state.scratchPaint.color.strokeWidth
|
strokeWidth: state.scratchPaint.color.strokeWidth,
|
||||||
|
textEditTarget: state.scratchPaint.textEditTarget
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
onChangeStrokeWidth: strokeWidth => {
|
onChangeStrokeWidth: strokeWidth => {
|
||||||
|
@ -43,7 +46,8 @@ StrokeWidthIndicator.propTypes = {
|
||||||
disabled: PropTypes.bool.isRequired,
|
disabled: PropTypes.bool.isRequired,
|
||||||
onChangeStrokeWidth: PropTypes.func.isRequired,
|
onChangeStrokeWidth: PropTypes.func.isRequired,
|
||||||
onUpdateSvg: PropTypes.func.isRequired,
|
onUpdateSvg: PropTypes.func.isRequired,
|
||||||
strokeWidth: PropTypes.number
|
strokeWidth: PropTypes.number,
|
||||||
|
textEditTarget: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {MIXED} from '../helper/style-path';
|
||||||
import {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-color';
|
import {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-color';
|
||||||
import {changeStrokeColor} from '../reducers/stroke-color';
|
import {changeStrokeColor} from '../reducers/stroke-color';
|
||||||
import {changeMode} from '../reducers/modes';
|
import {changeMode} from '../reducers/modes';
|
||||||
|
import {setTextEditTarget} from '../reducers/text-edit-target';
|
||||||
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
|
||||||
|
|
||||||
import {clearSelection, getSelectedLeafItems} from '../helper/selection';
|
import {clearSelection, getSelectedLeafItems} from '../helper/selection';
|
||||||
|
@ -65,7 +66,8 @@ class TextMode extends React.Component {
|
||||||
this.tool = new TextTool(
|
this.tool = new TextTool(
|
||||||
this.props.setSelectedItems,
|
this.props.setSelectedItems,
|
||||||
this.props.clearSelectedItems,
|
this.props.clearSelectedItems,
|
||||||
this.props.onUpdateSvg
|
this.props.onUpdateSvg,
|
||||||
|
this.props.setTextEditTarget,
|
||||||
);
|
);
|
||||||
this.tool.setColorState(this.props.colorState);
|
this.tool.setColorState(this.props.colorState);
|
||||||
this.tool.activate();
|
this.tool.activate();
|
||||||
|
@ -98,7 +100,8 @@ TextMode.propTypes = {
|
||||||
onChangeStrokeColor: PropTypes.func.isRequired,
|
onChangeStrokeColor: PropTypes.func.isRequired,
|
||||||
onUpdateSvg: PropTypes.func.isRequired,
|
onUpdateSvg: PropTypes.func.isRequired,
|
||||||
selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),
|
selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),
|
||||||
setSelectedItems: PropTypes.func.isRequired
|
setSelectedItems: PropTypes.func.isRequired,
|
||||||
|
setTextEditTarget: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
@ -113,6 +116,9 @@ const mapDispatchToProps = dispatch => ({
|
||||||
setSelectedItems: () => {
|
setSelectedItems: () => {
|
||||||
dispatch(setSelectedItems(getSelectedLeafItems()));
|
dispatch(setSelectedItems(getSelectedLeafItems()));
|
||||||
},
|
},
|
||||||
|
setTextEditTarget: targetId => {
|
||||||
|
dispatch(setTextEditTarget(targetId));
|
||||||
|
},
|
||||||
handleMouseDown: () => {
|
handleMouseDown: () => {
|
||||||
dispatch(changeMode(Modes.TEXT));
|
dispatch(changeMode(Modes.TEXT));
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,6 +42,7 @@ const hoverBounds = function (item, expandBy) {
|
||||||
rect.strokeColor = GUIDE_BLUE;
|
rect.strokeColor = GUIDE_BLUE;
|
||||||
rect.fillColor = null;
|
rect.fillColor = null;
|
||||||
rect.data.isHelperItem = true;
|
rect.data.isHelperItem = true;
|
||||||
|
rect.data.origItem = item;
|
||||||
rect.bringToFront();
|
rect.bringToFront();
|
||||||
|
|
||||||
return rect;
|
return rect;
|
||||||
|
|
|
@ -30,7 +30,6 @@ const getHoveredItem = function (event, hitOptions, subselect) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBoundsItem(item)) {
|
if (isBoundsItem(item)) {
|
||||||
return hoverBounds(item);
|
return hoverBounds(item);
|
||||||
} else if (!subselect && isGroupChild(item)) {
|
} else if (!subselect && isGroupChild(item)) {
|
||||||
|
|
|
@ -33,15 +33,16 @@ const isGroupItem = function (item) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const isPGTextItem = function (item) {
|
||||||
|
return getRootItem(item).data.isPGTextItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const isPointTextItem = function (item) {
|
const isPointTextItem = function (item) {
|
||||||
return item.className === 'PointText';
|
return item.className === 'PointText';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const isPGTextItem = function (item) {
|
|
||||||
return getRootItem(item).data.isPGTextItem;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setPivot = function (item, point) {
|
const setPivot = function (item, point) {
|
||||||
if (isBoundsItem(item)) {
|
if (isBoundsItem(item)) {
|
||||||
item.pivot = item.globalToLocal(point);
|
item.pivot = item.globalToLocal(point);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import paper from '@scratch/paper';
|
||||||
import {getSelectedLeafItems} from './selection';
|
import {getSelectedLeafItems} from './selection';
|
||||||
import {isPGTextItem, isPointTextItem} from './item';
|
import {isPGTextItem, isPointTextItem} from './item';
|
||||||
import {isGroup} from './group';
|
import {isGroup} from './group';
|
||||||
|
import {getItems} from './selection';
|
||||||
|
|
||||||
const MIXED = 'scratch-paint/style-path/mixed';
|
const MIXED = 'scratch-paint/style-path/mixed';
|
||||||
|
|
||||||
|
@ -15,56 +16,51 @@ const _colorMatch = function (itemColor, incomingColor) {
|
||||||
(itemColor && incomingColor && itemColor.toCSS() === new paper.Color(incomingColor).toCSS());
|
(itemColor && incomingColor && itemColor.toCSS() === new paper.Color(incomingColor).toCSS());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Selected items and currently active text edit items respond to color changes.
|
||||||
|
const _getColorStateListeners = function (textEditTargetId) {
|
||||||
|
const items = getSelectedLeafItems();
|
||||||
|
if (textEditTargetId) {
|
||||||
|
const matches = getItems({
|
||||||
|
match: item => item.id === textEditTargetId
|
||||||
|
});
|
||||||
|
if (matches.length) {
|
||||||
|
items.push(matches[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when setting fill color
|
* Called when setting fill color
|
||||||
* @param {string} colorString New color, css format
|
* @param {string} colorString New color, css format
|
||||||
|
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
|
||||||
* @return {boolean} Whether the color application actually changed visibly.
|
* @return {boolean} Whether the color application actually changed visibly.
|
||||||
*/
|
*/
|
||||||
const applyFillColorToSelection = function (colorString) {
|
const applyFillColorToSelection = function (colorString, textEditTargetId) {
|
||||||
const items = getSelectedLeafItems();
|
const items = _getColorStateListeners(textEditTargetId);
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (item.parent instanceof paper.CompoundPath) {
|
|
||||||
item = item.parent;
|
|
||||||
}
|
|
||||||
if (isPGTextItem(item)) {
|
|
||||||
for (const child of item.children) {
|
|
||||||
if (child.children) {
|
|
||||||
for (const path of child.children) {
|
|
||||||
if (!path.data.isPGGlyphRect) {
|
|
||||||
if (!_colorMatch(path.fillColor, colorString)) {
|
|
||||||
changed = true;
|
|
||||||
path.fillColor = colorString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!child.data.isPGGlyphRect) {
|
|
||||||
if (!_colorMatch(child.fillColor, colorString)) {
|
|
||||||
changed = true;
|
|
||||||
child.fillColor = colorString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isPointTextItem(item) && !colorString) {
|
if (isPointTextItem(item) && !colorString) {
|
||||||
colorString = 'rgba(0,0,0,0)';
|
colorString = 'rgba(0,0,0,0)';
|
||||||
|
} else if (item.parent instanceof paper.CompoundPath) {
|
||||||
|
item = item.parent;
|
||||||
}
|
}
|
||||||
if (!_colorMatch(item.fillColor, colorString)) {
|
if (!_colorMatch(item.fillColor, colorString)) {
|
||||||
changed = true;
|
changed = true;
|
||||||
item.fillColor = colorString;
|
item.fillColor = colorString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return changed;
|
return changed;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when setting stroke color
|
* Called when setting stroke color
|
||||||
* @param {string} colorString New color, css format
|
* @param {string} colorString New color, css format
|
||||||
|
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
|
||||||
* @return {boolean} Whether the color application actually changed visibly.
|
* @return {boolean} Whether the color application actually changed visibly.
|
||||||
*/
|
*/
|
||||||
const applyStrokeColorToSelection = function (colorString) {
|
const applyStrokeColorToSelection = function (colorString, textEditTargetId) {
|
||||||
const items = getSelectedLeafItems();
|
const items = _getColorStateListeners(textEditTargetId);
|
||||||
let changed = false;
|
let changed = false;
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (item.parent instanceof paper.CompoundPath) {
|
if (item.parent instanceof paper.CompoundPath) {
|
||||||
|
@ -106,11 +102,12 @@ const applyStrokeColorToSelection = function (colorString) {
|
||||||
/**
|
/**
|
||||||
* Called when setting stroke width
|
* Called when setting stroke width
|
||||||
* @param {number} value New stroke width
|
* @param {number} value New stroke width
|
||||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
|
||||||
|
* @return {boolean} Whether the color application actually changed visibly.
|
||||||
*/
|
*/
|
||||||
const applyStrokeWidthToSelection = function (value, onUpdateSvg) {
|
const applyStrokeWidthToSelection = function (value, textEditTargetId) {
|
||||||
let changed = false;
|
let changed = false;
|
||||||
const items = getSelectedLeafItems();
|
const items = _getColorStateListeners(textEditTargetId);
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (item.parent instanceof paper.CompoundPath) {
|
if (item.parent instanceof paper.CompoundPath) {
|
||||||
item = item.parent;
|
item = item.parent;
|
||||||
|
@ -122,9 +119,7 @@ const applyStrokeWidthToSelection = function (value, onUpdateSvg) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (changed) {
|
return changed;
|
||||||
onUpdateSvg();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -38,14 +38,14 @@ class FillTool extends paper.Tool {
|
||||||
item.lastSegment.point.getDistance(item.firstSegment.point) < 8;
|
item.lastSegment.point.getDistance(item.firstSegment.point) < 8;
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
class: paper.Path,
|
|
||||||
segments: true,
|
segments: true,
|
||||||
stroke: true,
|
stroke: true,
|
||||||
curves: true,
|
curves: true,
|
||||||
fill: true,
|
fill: true,
|
||||||
guide: false,
|
guide: false,
|
||||||
match: function (hitResult) {
|
match: function (hitResult) {
|
||||||
return (hitResult.item.hasFill() || hitResult.item.closed || isAlmostClosedPath(hitResult.item));
|
return (hitResult.item instanceof paper.Path || hitResult.item instanceof paper.PointText) &&
|
||||||
|
(hitResult.item.hasFill() || hitResult.item.closed || isAlmostClosedPath(hitResult.item));
|
||||||
},
|
},
|
||||||
hitUnfilledPaths: true,
|
hitUnfilledPaths: true,
|
||||||
tolerance: FillTool.TOLERANCE / paper.view.zoom
|
tolerance: FillTool.TOLERANCE / paper.view.zoom
|
||||||
|
|
|
@ -31,12 +31,14 @@ class TextTool extends paper.Tool {
|
||||||
* @param {function} setSelectedItems Callback to set the set of selected items in the Redux state
|
* @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} 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
|
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||||
|
* @param {!function} setTextEditTarget Call to set text editing target whenever text editing is active
|
||||||
*/
|
*/
|
||||||
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg, setTextEditTarget) {
|
||||||
super();
|
super();
|
||||||
this.setSelectedItems = setSelectedItems;
|
this.setSelectedItems = setSelectedItems;
|
||||||
this.clearSelectedItems = clearSelectedItems;
|
this.clearSelectedItems = clearSelectedItems;
|
||||||
this.onUpdateSvg = onUpdateSvg;
|
this.onUpdateSvg = onUpdateSvg;
|
||||||
|
this.setTextEditTarget = setTextEditTarget;
|
||||||
this.boundingBoxTool = new BoundingBoxTool(Modes.TEXT, setSelectedItems, clearSelectedItems, onUpdateSvg);
|
this.boundingBoxTool = new BoundingBoxTool(Modes.TEXT, setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||||
this.nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateSvg);
|
this.nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateSvg);
|
||||||
this.lastEvent = null;
|
this.lastEvent = null;
|
||||||
|
@ -171,9 +173,11 @@ class TextTool extends paper.Tool {
|
||||||
if (this.mode === TextTool.TEXT_EDIT_MODE) {
|
if (this.mode === TextTool.TEXT_EDIT_MODE) {
|
||||||
this.guide = hoverBounds(this.textBox, TextTool.TEXT_PADDING);
|
this.guide = hoverBounds(this.textBox, TextTool.TEXT_PADDING);
|
||||||
this.guide.dashArray = [4, 4];
|
this.guide.dashArray = [4, 4];
|
||||||
|
this.setTextEditTarget(this.textBox.id);
|
||||||
} else if (this.guide) {
|
} else if (this.guide) {
|
||||||
this.guide.remove();
|
this.guide.remove();
|
||||||
this.guide = null;
|
this.guide = null;
|
||||||
|
this.setTextEditTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleMouseDrag (event) {
|
handleMouseDrag (event) {
|
||||||
|
@ -229,6 +233,7 @@ class TextTool extends paper.Tool {
|
||||||
if (this.guide) {
|
if (this.guide) {
|
||||||
this.guide.remove();
|
this.guide.remove();
|
||||||
this.guide = null;
|
this.guide = null;
|
||||||
|
this.setTextEditTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import clipboardReducer from './clipboard';
|
||||||
import hoverReducer from './hover';
|
import hoverReducer from './hover';
|
||||||
import modalsReducer from './modals';
|
import modalsReducer from './modals';
|
||||||
import selectedItemReducer from './selected-items';
|
import selectedItemReducer from './selected-items';
|
||||||
|
import textEditTargetReducer from './text-edit-target';
|
||||||
import undoReducer from './undo';
|
import undoReducer from './undo';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
|
@ -18,5 +19,6 @@ export default combineReducers({
|
||||||
hoveredItemId: hoverReducer,
|
hoveredItemId: hoverReducer,
|
||||||
modals: modalsReducer,
|
modals: modalsReducer,
|
||||||
selectedItems: selectedItemReducer,
|
selectedItems: selectedItemReducer,
|
||||||
|
textEditTarget: textEditTargetReducer,
|
||||||
undo: undoReducer
|
undo: undoReducer
|
||||||
});
|
});
|
||||||
|
|
40
src/reducers/text-edit-target.js
Normal file
40
src/reducers/text-edit-target.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import log from '../log/log';
|
||||||
|
|
||||||
|
const CHANGE_TEXT_EDIT_TARGET = 'scratch-paint/text-tool/CHANGE_TEXT_EDIT_TARGET';
|
||||||
|
const initialState = null;
|
||||||
|
|
||||||
|
const reducer = function (state, action) {
|
||||||
|
if (typeof state === 'undefined') state = initialState;
|
||||||
|
switch (action.type) {
|
||||||
|
case CHANGE_TEXT_EDIT_TARGET:
|
||||||
|
if (typeof action.textEditTargetId === 'undefined') {
|
||||||
|
log.warn(`Text edit target should not be set to undefined. Use null.`);
|
||||||
|
return state;
|
||||||
|
} else if (typeof action.textEditTargetId === 'undefined' || isNaN(action.textEditTargetId)) {
|
||||||
|
log.warn(`Text edit target should be an item ID number. Got: ${action.textEditTargetId}`);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
return action.textEditTargetId;
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Action creators ==================================
|
||||||
|
/**
|
||||||
|
* Set the currently-being-edited text field to the given item ID
|
||||||
|
* @param {?number} textEditTargetId The paper.Item ID of the active text field.
|
||||||
|
* Leave empty if there is no text editing target.
|
||||||
|
* @return {object} Redux action to change the text edit target.
|
||||||
|
*/
|
||||||
|
const setTextEditTarget = function (textEditTargetId) {
|
||||||
|
return {
|
||||||
|
type: CHANGE_TEXT_EDIT_TARGET,
|
||||||
|
textEditTargetId: textEditTargetId ? textEditTargetId : null
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
reducer as default,
|
||||||
|
setTextEditTarget
|
||||||
|
};
|
Loading…
Reference in a new issue