mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -05:00
Delete extra tools
This commit is contained in:
parent
87d9386906
commit
bda357bca6
4 changed files with 0 additions and 763 deletions
|
@ -1,177 +0,0 @@
|
|||
import paper from '@scratch/paper';
|
||||
import {getHoveredItem} from '../hover';
|
||||
import {expandBy} from '../math';
|
||||
|
||||
class FillTool extends paper.Tool {
|
||||
static get TOLERANCE () {
|
||||
return 2;
|
||||
}
|
||||
/**
|
||||
* @param {function} setHoveredItem Callback to set the hovered item
|
||||
* @param {function} clearHoveredItem Callback to clear the hovered item
|
||||
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
|
||||
*/
|
||||
constructor (setHoveredItem, clearHoveredItem, onUpdateSvg) {
|
||||
super();
|
||||
this.setHoveredItem = setHoveredItem;
|
||||
this.clearHoveredItem = clearHoveredItem;
|
||||
this.onUpdateSvg = 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.onMouseMove = this.handleMouseMove;
|
||||
this.onMouseUp = this.handleMouseUp;
|
||||
|
||||
// Color to fill with
|
||||
this.fillColor = null;
|
||||
// The path that's being hovered over.
|
||||
this.fillItem = null;
|
||||
// If we're hovering over a hole in a compound path, we can't just recolor it. This is the
|
||||
// added item that's the same shape as the hole that's drawn over the hole when we fill a hole.
|
||||
this.addedFillItem = null;
|
||||
this.fillItemOrigColor = null;
|
||||
this.prevHoveredItemId = null;
|
||||
}
|
||||
getHitOptions () {
|
||||
const isAlmostClosedPath = function (item) {
|
||||
return item instanceof paper.Path && item.segments.length > 2 &&
|
||||
item.lastSegment.point.getDistance(item.firstSegment.point) < 8;
|
||||
};
|
||||
return {
|
||||
segments: true,
|
||||
stroke: true,
|
||||
curves: true,
|
||||
fill: true,
|
||||
guide: false,
|
||||
match: function (hitResult) {
|
||||
return (hitResult.item instanceof paper.Path || hitResult.item instanceof paper.PointText) &&
|
||||
(hitResult.item.hasFill() || hitResult.item.closed || isAlmostClosedPath(hitResult.item));
|
||||
},
|
||||
hitUnfilledPaths: true,
|
||||
tolerance: FillTool.TOLERANCE / paper.view.zoom
|
||||
};
|
||||
}
|
||||
setFillColor (fillColor) {
|
||||
this.fillColor = fillColor;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
handleMouseMove (event) {
|
||||
const hoveredItem = getHoveredItem(event, this.getHitOptions(), true /* subselect */);
|
||||
if ((!hoveredItem && this.prevHoveredItemId) || // There is no longer a hovered item
|
||||
(hoveredItem && !this.prevHoveredItemId) || // There is now a hovered item
|
||||
(hoveredItem && this.prevHoveredItemId &&
|
||||
hoveredItem.id !== this.prevHoveredItemId)) { // hovered item changed
|
||||
this.setHoveredItem(hoveredItem ? hoveredItem.id : null);
|
||||
}
|
||||
const hitItem = hoveredItem ? hoveredItem.data.origItem : null;
|
||||
// Still hitting the same thing
|
||||
if ((!hitItem && !this.fillItem) || this.fillItem === hitItem) {
|
||||
return;
|
||||
}
|
||||
if (this.fillItem) {
|
||||
if (this.addedFillItem) {
|
||||
this.addedFillItem.remove();
|
||||
this.addedFillItem = null;
|
||||
} else {
|
||||
this._setFillItemColor(this.fillItemOrigColor);
|
||||
}
|
||||
this.fillItemOrigColor = null;
|
||||
this.fillItem = null;
|
||||
}
|
||||
if (hitItem) {
|
||||
this.fillItem = hitItem;
|
||||
this.fillItemOrigColor = hitItem.fillColor;
|
||||
if (hitItem.parent instanceof paper.CompoundPath && hitItem.area < 0) { // hole
|
||||
if (!this.fillColor) {
|
||||
// Hole filled with transparent is no-op
|
||||
this.fillItem = null;
|
||||
this.fillItemOrigColor = null;
|
||||
return;
|
||||
}
|
||||
// Make an item to fill the hole
|
||||
this.addedFillItem = hitItem.clone();
|
||||
this.addedFillItem.setClockwise(true);
|
||||
this.addedFillItem.data.noHover = true;
|
||||
this.addedFillItem.data.origItem = hitItem;
|
||||
// This usually fixes it so there isn't a teeny tiny gap in between the fill and the outline
|
||||
// when filling in a hole
|
||||
expandBy(this.addedFillItem, .1);
|
||||
this.addedFillItem.insertAbove(hitItem.parent);
|
||||
} else if (this.fillItem.parent instanceof paper.CompoundPath) {
|
||||
this.fillItemOrigColor = hitItem.parent.fillColor;
|
||||
}
|
||||
this._setFillItemColor(this.fillColor);
|
||||
}
|
||||
}
|
||||
handleMouseUp (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
if (this.fillItem) {
|
||||
// If the hole we're filling in is the same color as the parent, and parent has no outline, remove the hole
|
||||
if (this.addedFillItem &&
|
||||
this._noStroke(this.fillItem.parent) &&
|
||||
this.addedFillItem.fillColor.type !== 'gradient' &&
|
||||
this.fillItem.parent.fillColor.toCSS() === this.addedFillItem.fillColor.toCSS()) {
|
||||
this.addedFillItem.remove();
|
||||
this.addedFillItem = null;
|
||||
let parent = this.fillItem.parent;
|
||||
this.fillItem.remove();
|
||||
parent = parent.reduce();
|
||||
parent.fillColor = this.fillColor;
|
||||
} else if (this.addedFillItem) {
|
||||
// Fill in a hole.
|
||||
this.addedFillItem.data.noHover = false;
|
||||
} else if (!this.fillColor &&
|
||||
this.fillItem.data &&
|
||||
this.fillItem.data.origItem) {
|
||||
// Filling a hole filler with transparent returns it to being gone
|
||||
// instead of making a shape that's transparent
|
||||
const group = this.fillItem.parent;
|
||||
this.fillItem.remove();
|
||||
if (!(group instanceof paper.Layer) && group.children.length === 1) {
|
||||
group.reduce();
|
||||
}
|
||||
}
|
||||
|
||||
this.clearHoveredItem();
|
||||
this.fillItem = null;
|
||||
this.addedFillItem = null;
|
||||
this.fillItemOrigColor = null;
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
}
|
||||
_noStroke (item) {
|
||||
return !item.strokeColor ||
|
||||
item.strokeColor.alpha === 0 ||
|
||||
item.strokeWidth === 0;
|
||||
}
|
||||
_setFillItemColor (color) {
|
||||
if (this.addedFillItem) {
|
||||
this.addedFillItem.fillColor = color;
|
||||
} else if (this.fillItem.parent instanceof paper.CompoundPath) {
|
||||
this.fillItem.parent.fillColor = color;
|
||||
} else {
|
||||
this.fillItem.fillColor = color;
|
||||
}
|
||||
}
|
||||
deactivateTool () {
|
||||
if (this.fillItem) {
|
||||
this._setFillItemColor(this.fillItemOrigColor);
|
||||
this.fillItemOrigColor = null;
|
||||
this.fillItem = null;
|
||||
}
|
||||
this.clearHoveredItem();
|
||||
this.setHoveredItem = null;
|
||||
this.clearHoveredItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
export default FillTool;
|
|
@ -1,133 +0,0 @@
|
|||
import paper from '@scratch/paper';
|
||||
import Modes from '../../lib/modes';
|
||||
import {styleShape} from '../style-path';
|
||||
import {clearSelection} from '../selection';
|
||||
import BoundingBoxTool from '../selection-tools/bounding-box-tool';
|
||||
import NudgeTool from '../selection-tools/nudge-tool';
|
||||
|
||||
/**
|
||||
* Tool for drawing ovals.
|
||||
*/
|
||||
class OvalTool extends paper.Tool {
|
||||
static get TOLERANCE () {
|
||||
return 6;
|
||||
}
|
||||
/**
|
||||
* @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 (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
super();
|
||||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.boundingBoxTool = new BoundingBoxTool(Modes.OVAL, setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
const nudgeTool = new NudgeTool(this.boundingBoxTool, 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.onMouseDrag = this.handleMouseDrag;
|
||||
this.onMouseUp = this.handleMouseUp;
|
||||
this.onKeyUp = nudgeTool.onKeyUp;
|
||||
this.onKeyDown = nudgeTool.onKeyDown;
|
||||
|
||||
this.oval = null;
|
||||
this.colorState = null;
|
||||
this.isBoundingBoxMode = null;
|
||||
this.active = false;
|
||||
}
|
||||
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: OvalTool.TOLERANCE / paper.view.zoom
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Should be called if the selection changes to update the bounds of the bounding box.
|
||||
* @param {Array<paper.Item>} selectedItems Array of selected items.
|
||||
*/
|
||||
onSelectionChanged (selectedItems) {
|
||||
this.boundingBoxTool.onSelectionChanged(selectedItems);
|
||||
}
|
||||
setColorState (colorState) {
|
||||
this.colorState = colorState;
|
||||
}
|
||||
handleMouseDown (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
this.active = true;
|
||||
|
||||
if (this.boundingBoxTool.onMouseDown(event, false /* clone */, false /* multiselect */, this.getHitOptions())) {
|
||||
this.isBoundingBoxMode = true;
|
||||
} else {
|
||||
this.isBoundingBoxMode = false;
|
||||
clearSelection(this.clearSelectedItems);
|
||||
this.oval = new paper.Shape.Ellipse({
|
||||
point: event.downPoint,
|
||||
size: 0
|
||||
});
|
||||
styleShape(this.oval, this.colorState);
|
||||
}
|
||||
}
|
||||
handleMouseDrag (event) {
|
||||
if (event.event.button > 0 || !this.active) return; // only first mouse button
|
||||
|
||||
if (this.isBoundingBoxMode) {
|
||||
this.boundingBoxTool.onMouseDrag(event);
|
||||
return;
|
||||
}
|
||||
|
||||
const downPoint = new paper.Point(event.downPoint.x, event.downPoint.y);
|
||||
const point = new paper.Point(event.point.x, event.point.y);
|
||||
if (event.modifiers.shift) {
|
||||
this.oval.size = new paper.Point(event.downPoint.x - event.point.x, event.downPoint.x - event.point.x);
|
||||
} else {
|
||||
this.oval.size = downPoint.subtract(point);
|
||||
}
|
||||
if (event.modifiers.alt) {
|
||||
this.oval.position = downPoint;
|
||||
} else {
|
||||
this.oval.position = downPoint.subtract(this.oval.size.multiply(0.5));
|
||||
}
|
||||
|
||||
}
|
||||
handleMouseUp (event) {
|
||||
if (event.event.button > 0 || !this.active) return; // only first mouse button
|
||||
|
||||
if (this.isBoundingBoxMode) {
|
||||
this.boundingBoxTool.onMouseUp(event);
|
||||
this.isBoundingBoxMode = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.oval) {
|
||||
if (Math.abs(this.oval.size.width * this.oval.size.height) < OvalTool.TOLERANCE / paper.view.zoom) {
|
||||
// Tiny oval created unintentionally?
|
||||
this.oval.remove();
|
||||
this.oval = null;
|
||||
} else {
|
||||
const ovalPath = this.oval.toPath(true /* insert */);
|
||||
this.oval.remove();
|
||||
this.oval = null;
|
||||
|
||||
ovalPath.selected = true;
|
||||
this.setSelectedItems();
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
}
|
||||
this.active = false;
|
||||
}
|
||||
deactivateTool () {
|
||||
this.boundingBoxTool.removeBoundsPath();
|
||||
}
|
||||
}
|
||||
|
||||
export default OvalTool;
|
|
@ -1,127 +0,0 @@
|
|||
import paper from '@scratch/paper';
|
||||
import Modes from '../../lib/modes';
|
||||
import {styleShape} from '../style-path';
|
||||
import {clearSelection} from '../selection';
|
||||
import BoundingBoxTool from '../selection-tools/bounding-box-tool';
|
||||
import NudgeTool from '../selection-tools/nudge-tool';
|
||||
|
||||
/**
|
||||
* Tool for drawing rectangles.
|
||||
*/
|
||||
class RectTool extends paper.Tool {
|
||||
static get TOLERANCE () {
|
||||
return 6;
|
||||
}
|
||||
/**
|
||||
* @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 (setSelectedItems, clearSelectedItems, onUpdateSvg) {
|
||||
super();
|
||||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.boundingBoxTool = new BoundingBoxTool(Modes.RECT, setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
const nudgeTool = new NudgeTool(this.boundingBoxTool, 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.onMouseDrag = this.handleMouseDrag;
|
||||
this.onMouseUp = this.handleMouseUp;
|
||||
this.onKeyUp = nudgeTool.onKeyUp;
|
||||
this.onKeyDown = nudgeTool.onKeyDown;
|
||||
|
||||
this.rect = null;
|
||||
this.colorState = null;
|
||||
this.isBoundingBoxMode = null;
|
||||
this.active = false;
|
||||
}
|
||||
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
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Should be called if the selection changes to update the bounds of the bounding box.
|
||||
* @param {Array<paper.Item>} selectedItems Array of selected items.
|
||||
*/
|
||||
onSelectionChanged (selectedItems) {
|
||||
this.boundingBoxTool.onSelectionChanged(selectedItems);
|
||||
}
|
||||
setColorState (colorState) {
|
||||
this.colorState = colorState;
|
||||
}
|
||||
handleMouseDown (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
this.active = true;
|
||||
|
||||
if (this.boundingBoxTool.onMouseDown(event, false /* clone */, false /* multiselect */, this.getHitOptions())) {
|
||||
this.isBoundingBoxMode = true;
|
||||
} else {
|
||||
this.isBoundingBoxMode = false;
|
||||
clearSelection(this.clearSelectedItems);
|
||||
}
|
||||
}
|
||||
handleMouseDrag (event) {
|
||||
if (event.event.button > 0 || !this.active) 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 (event) {
|
||||
if (event.event.button > 0 || !this.active) 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.setSelectedItems();
|
||||
this.onUpdateSvg();
|
||||
this.rect = null;
|
||||
}
|
||||
}
|
||||
this.active = false;
|
||||
}
|
||||
deactivateTool () {
|
||||
this.boundingBoxTool.removeBoundsPath();
|
||||
}
|
||||
}
|
||||
|
||||
export default RectTool;
|
|
@ -1,326 +0,0 @@
|
|||
import paper from '@scratch/paper';
|
||||
import Modes from '../../lib/modes';
|
||||
import {clearSelection} from '../selection';
|
||||
import BoundingBoxTool from '../selection-tools/bounding-box-tool';
|
||||
import NudgeTool from '../selection-tools/nudge-tool';
|
||||
import {hoverBounds} from '../guides';
|
||||
|
||||
/**
|
||||
* Tool for adding text. Text elements have limited editability; they can't be reshaped,
|
||||
* drawn on or erased. This way they can preserve their ability to have the text edited.
|
||||
*/
|
||||
class TextTool extends paper.Tool {
|
||||
static get TOLERANCE () {
|
||||
return 6;
|
||||
}
|
||||
static get TEXT_EDIT_MODE () {
|
||||
return 'TEXT_EDIT_MODE';
|
||||
}
|
||||
static get SELECT_MODE () {
|
||||
return 'SELECT_MODE';
|
||||
}
|
||||
/** Clicks registered within this amount of time are registered as double clicks */
|
||||
static get DOUBLE_CLICK_MILLIS () {
|
||||
return 250;
|
||||
}
|
||||
/** Typing with no pauses longer than this amount of type will count as 1 action */
|
||||
static get TYPING_TIMEOUT_MILLIS () {
|
||||
return 1000;
|
||||
}
|
||||
static get TEXT_PADDING () {
|
||||
return 8;
|
||||
}
|
||||
/**
|
||||
* @param {HTMLTextAreaElement} textAreaElement dom element for the editable text field
|
||||
* @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
|
||||
* @param {!function} setTextEditTarget Call to set text editing target whenever text editing is active
|
||||
*/
|
||||
constructor (textAreaElement, setSelectedItems, clearSelectedItems, onUpdateSvg, setTextEditTarget) {
|
||||
super();
|
||||
this.element = textAreaElement;
|
||||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.onUpdateSvg = onUpdateSvg;
|
||||
this.setTextEditTarget = setTextEditTarget;
|
||||
this.boundingBoxTool = new BoundingBoxTool(Modes.TEXT, setSelectedItems, clearSelectedItems, onUpdateSvg);
|
||||
this.nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateSvg);
|
||||
this.lastEvent = null;
|
||||
|
||||
// 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.onMouseDrag = this.handleMouseDrag;
|
||||
this.onMouseUp = this.handleMouseUp;
|
||||
this.onMouseMove = this.handleMouseMove;
|
||||
this.onKeyUp = this.handleKeyUp;
|
||||
this.onKeyDown = this.handleKeyDown;
|
||||
|
||||
this.textBox = null;
|
||||
this.guide = null;
|
||||
this.colorState = null;
|
||||
this.mode = null;
|
||||
this.active = false;
|
||||
this.lastTypeEvent = null;
|
||||
|
||||
// If text selected and then activate this tool, switch to text edit mode for that text
|
||||
// If double click on text while in select mode, does mode change to text mode? Text fully selected by default
|
||||
}
|
||||
getBoundingBoxHitOptions () {
|
||||
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: TextTool.TOLERANCE / paper.view.zoom
|
||||
};
|
||||
}
|
||||
getTextEditHitOptions () {
|
||||
return {
|
||||
class: paper.PointText,
|
||||
segments: true,
|
||||
stroke: true,
|
||||
curves: true,
|
||||
fill: true,
|
||||
guide: false,
|
||||
match: hitResult => hitResult.item && !hitResult.item.selected, // Unselected only
|
||||
tolerance: TextTool.TOLERANCE / paper.view.zoom
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Called when the selection changes to update the bounds of the bounding box.
|
||||
* @param {Array<paper.Item>} selectedItems Array of selected items.
|
||||
*/
|
||||
onSelectionChanged (selectedItems) {
|
||||
this.boundingBoxTool.onSelectionChanged(selectedItems);
|
||||
}
|
||||
// Allow other tools to cancel text edit mode
|
||||
onTextEditCancelled () {
|
||||
this.endTextEdit();
|
||||
if (this.textBox) {
|
||||
this.mode = TextTool.SELECT_MODE;
|
||||
this.textBox.selected = true;
|
||||
this.setSelectedItems();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Called when the view matrix changes
|
||||
* @param {paper.Matrix} viewMtx applied to paper.view
|
||||
*/
|
||||
onViewBoundsChanged (viewMtx) {
|
||||
if (this.mode !== TextTool.TEXT_EDIT_MODE) {
|
||||
return;
|
||||
}
|
||||
const matrix = this.textBox.matrix;
|
||||
this.element.style.transform =
|
||||
`translate(0px, ${this.textBox.internalBounds.y}px)
|
||||
matrix(${viewMtx.a}, ${viewMtx.b}, ${viewMtx.c}, ${viewMtx.d},
|
||||
${viewMtx.tx}, ${viewMtx.ty})
|
||||
matrix(${matrix.a}, ${matrix.b}, ${matrix.c}, ${matrix.d},
|
||||
${matrix.tx}, ${matrix.ty})`;
|
||||
}
|
||||
setColorState (colorState) {
|
||||
this.colorState = colorState;
|
||||
}
|
||||
handleMouseMove (event) {
|
||||
const hitResults = paper.project.hitTestAll(event.point, this.getTextEditHitOptions());
|
||||
if (hitResults.length) {
|
||||
document.body.style.cursor = 'text';
|
||||
} else {
|
||||
document.body.style.cursor = 'auto';
|
||||
}
|
||||
}
|
||||
handleMouseDown (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
this.active = true;
|
||||
|
||||
const lastMode = this.mode;
|
||||
|
||||
// Check if double clicked
|
||||
let doubleClicked = false;
|
||||
if (this.lastEvent) {
|
||||
if ((event.event.timeStamp - this.lastEvent.event.timeStamp) < TextTool.DOUBLE_CLICK_MILLIS) {
|
||||
doubleClicked = true;
|
||||
} else {
|
||||
doubleClicked = false;
|
||||
}
|
||||
}
|
||||
this.lastEvent = event;
|
||||
|
||||
const doubleClickHitTest = paper.project.hitTest(event.point, this.getBoundingBoxHitOptions());
|
||||
if (doubleClicked &&
|
||||
this.mode === TextTool.SELECT_MODE &&
|
||||
doubleClickHitTest) {
|
||||
// Double click in select mode moves you to text edit mode
|
||||
clearSelection(this.clearSelectedItems);
|
||||
this.textBox = doubleClickHitTest.item;
|
||||
this.beginTextEdit(this.textBox.content, this.textBox.matrix);
|
||||
} else if (
|
||||
this.boundingBoxTool.onMouseDown(
|
||||
event, false /* clone */, false /* multiselect */, this.getBoundingBoxHitOptions())) {
|
||||
// In select mode staying in select mode
|
||||
return;
|
||||
}
|
||||
|
||||
// We clicked away from the item, so end the current mode
|
||||
if (lastMode === TextTool.SELECT_MODE) {
|
||||
clearSelection(this.clearSelectedItems);
|
||||
this.mode = null;
|
||||
} else if (lastMode === TextTool.TEXT_EDIT_MODE) {
|
||||
this.endTextEdit();
|
||||
}
|
||||
|
||||
const hitResults = paper.project.hitTestAll(event.point, this.getTextEditHitOptions());
|
||||
if (hitResults.length) {
|
||||
// Clicking a different text item to begin text edit mode on that item
|
||||
clearSelection(this.clearSelectedItems);
|
||||
this.textBox = hitResults[0].item;
|
||||
this.beginTextEdit(this.textBox.content, this.textBox.matrix);
|
||||
} else if (lastMode === TextTool.TEXT_EDIT_MODE) {
|
||||
// In text mode clicking away to begin select mode
|
||||
if (this.textBox) {
|
||||
this.mode = TextTool.SELECT_MODE;
|
||||
this.textBox.selected = true;
|
||||
this.setSelectedItems();
|
||||
}
|
||||
} else {
|
||||
// In no mode or select mode clicking away to begin text edit mode
|
||||
this.textBox = new paper.PointText({
|
||||
point: event.point,
|
||||
content: '',
|
||||
font: 'Helvetica',
|
||||
fontSize: 30,
|
||||
fillColor: this.colorState.fillColor,
|
||||
// Default leading for both the HTML text area and paper.PointText
|
||||
// is 120%, but for some reason they are slightly off from each other.
|
||||
// This value was obtained experimentally.
|
||||
// (Don't round to 34.6, the text area will start to scroll.)
|
||||
leading: 34.61
|
||||
});
|
||||
this.beginTextEdit(this.textBox.content, this.textBox.matrix);
|
||||
}
|
||||
}
|
||||
handleMouseDrag (event) {
|
||||
if (event.event.button > 0 || !this.active) return; // only first mouse button
|
||||
|
||||
if (this.mode === TextTool.SELECT_MODE) {
|
||||
this.boundingBoxTool.onMouseDrag(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
handleMouseUp (event) {
|
||||
if (event.event.button > 0 || !this.active) return; // only first mouse button
|
||||
|
||||
if (this.mode === TextTool.SELECT_MODE) {
|
||||
this.boundingBoxTool.onMouseUp(event);
|
||||
this.isBoundingBoxMode = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.active = false;
|
||||
}
|
||||
handleKeyUp (event) {
|
||||
if (this.mode === TextTool.SELECT_MODE) {
|
||||
this.nudgeTool.onKeyUp(event);
|
||||
}
|
||||
}
|
||||
handleKeyDown (event) {
|
||||
if (event.event.target instanceof HTMLInputElement) {
|
||||
// Ignore nudge if a text input field is focused
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.mode === TextTool.SELECT_MODE) {
|
||||
this.nudgeTool.onKeyUp(event);
|
||||
}
|
||||
}
|
||||
handleTextInput (event) {
|
||||
// Save undo state if you paused typing for long enough.
|
||||
if (this.lastTypeEvent && event.timeStamp - this.lastTypeEvent.timeStamp > TextTool.TYPING_TIMEOUT_MILLIS) {
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
this.lastTypeEvent = event;
|
||||
if (this.mode === TextTool.TEXT_EDIT_MODE) {
|
||||
this.textBox.content = this.element.value;
|
||||
}
|
||||
this.resizeGuide();
|
||||
}
|
||||
resizeGuide () {
|
||||
if (this.guide) this.guide.remove();
|
||||
this.guide = hoverBounds(this.textBox, TextTool.TEXT_PADDING);
|
||||
this.guide.dashArray = [4, 4];
|
||||
this.element.style.width = `${this.textBox.internalBounds.width}px`;
|
||||
this.element.style.height = `${this.textBox.internalBounds.height}px`;
|
||||
}
|
||||
/**
|
||||
* @param {string} initialText Text to initialize the text area with
|
||||
* @param {paper.Matrix} matrix Transform matrix for the element. Defaults
|
||||
* to the identity matrix.
|
||||
*/
|
||||
beginTextEdit (initialText, matrix) {
|
||||
this.mode = TextTool.TEXT_EDIT_MODE;
|
||||
this.setTextEditTarget(this.textBox.id);
|
||||
|
||||
const viewMtx = paper.view.matrix;
|
||||
|
||||
this.element.style.display = 'initial';
|
||||
this.element.value = initialText ? initialText : '';
|
||||
this.element.style.transformOrigin =
|
||||
`${-this.textBox.internalBounds.x}px ${-this.textBox.internalBounds.y}px`;
|
||||
this.element.style.transform =
|
||||
`translate(0px, ${this.textBox.internalBounds.y}px)
|
||||
matrix(${viewMtx.a}, ${viewMtx.b}, ${viewMtx.c}, ${viewMtx.d},
|
||||
${viewMtx.tx}, ${viewMtx.ty})
|
||||
matrix(${matrix.a}, ${matrix.b}, ${matrix.c}, ${matrix.d},
|
||||
${matrix.tx}, ${matrix.ty})`;
|
||||
this.element.focus({preventScroll: true});
|
||||
this.eventListener = this.handleTextInput.bind(this);
|
||||
this.element.addEventListener('input', this.eventListener);
|
||||
this.resizeGuide();
|
||||
}
|
||||
endTextEdit () {
|
||||
if (this.mode !== TextTool.TEXT_EDIT_MODE) {
|
||||
return;
|
||||
}
|
||||
this.mode = null;
|
||||
|
||||
// Remove invisible textboxes
|
||||
if (this.textBox && this.textBox.content.trim() === '') {
|
||||
this.textBox.remove();
|
||||
this.textBox = null;
|
||||
}
|
||||
|
||||
// Remove guide
|
||||
if (this.guide) {
|
||||
this.guide.remove();
|
||||
this.guide = null;
|
||||
this.setTextEditTarget();
|
||||
}
|
||||
this.element.style.display = 'none';
|
||||
if (this.eventListener) {
|
||||
this.element.removeEventListener('input', this.eventListener);
|
||||
this.eventListener = null;
|
||||
}
|
||||
this.lastTypeEvent = null;
|
||||
|
||||
// If you finished editing a textbox, save undo state
|
||||
if (this.textBox && this.textBox.content.trim().length) {
|
||||
this.onUpdateSvg();
|
||||
}
|
||||
}
|
||||
deactivateTool () {
|
||||
if (this.textBox && this.textBox.content.trim() === '') {
|
||||
this.textBox.remove();
|
||||
this.textBox = null;
|
||||
}
|
||||
this.endTextEdit();
|
||||
this.boundingBoxTool.removeBoundsPath();
|
||||
}
|
||||
}
|
||||
|
||||
export default TextTool;
|
Loading…
Reference in a new issue