scratch-paint/src/helper/style-path.js

268 lines
10 KiB
JavaScript
Raw Normal View History

2017-10-12 18:35:30 -04:00
import paper from '@scratch/paper';
import {getSelectedLeafItems} from './selection';
import {isPGTextItem, isPointTextItem} from './item';
import {isGroup} from './group';
import {getItems} from './selection';
const MIXED = 'scratch-paint/style-path/mixed';
// Check if the item color matches the incoming color. If the item color is a gradient, we assume
// that the incoming color never matches, since we don't support gradients yet.
const _colorMatch = function (itemColor, incomingColor) {
// @todo check whether the gradient has changed when we support gradients
if (itemColor && itemColor.type === 'gradient') return false;
// Either both are null or both are the same color when converted to CSS.
return (!itemColor && !incomingColor) ||
(itemColor && incomingColor && itemColor.toCSS() === new paper.Color(incomingColor).toCSS());
2017-12-19 11:59:54 -05:00
};
// 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
* @param {string} colorString New color, css format
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
2017-10-30 11:13:29 -04:00
* @return {boolean} Whether the color application actually changed visibly.
*/
const applyFillColorToSelection = function (colorString, textEditTargetId) {
const items = _getColorStateListeners(textEditTargetId);
2017-10-05 18:12:22 -04:00
let changed = false;
for (let item of items) {
if (isPointTextItem(item) && !colorString) {
colorString = 'rgba(0,0,0,0)';
} else if (item.parent instanceof paper.CompoundPath) {
item = item.parent;
}
if (!_colorMatch(item.fillColor, colorString)) {
changed = true;
item.fillColor = colorString;
}
}
return changed;
};
/**
* Called when setting stroke color
* @param {string} colorString New color, css format
* @param {?string} textEditTargetId paper.Item.id of text editing target, if any
2017-10-30 11:13:29 -04:00
* @return {boolean} Whether the color application actually changed visibly.
*/
const applyStrokeColorToSelection = function (colorString, textEditTargetId) {
const items = _getColorStateListeners(textEditTargetId);
2017-10-05 18:12:22 -04:00
let changed = false;
for (let item of items) {
if (item.parent instanceof paper.CompoundPath) {
item = item.parent;
}
if (isPGTextItem(item)) {
if (item.children) {
for (const child of item.children) {
if (child.children) {
for (const path of child.children) {
if (!path.data.isPGGlyphRect) {
if (!_colorMatch(path.strokeColor, colorString)) {
2017-10-05 18:12:22 -04:00
changed = true;
path.strokeColor = colorString;
}
}
}
} else if (!child.data.isPGGlyphRect) {
2017-10-05 18:12:22 -04:00
if (child.strokeColor !== colorString) {
changed = true;
child.strokeColor = colorString;
}
}
}
} else if (!item.data.isPGGlyphRect) {
if (!_colorMatch(item.strokeColor, colorString)) {
2017-10-05 18:12:22 -04:00
changed = true;
item.strokeColor = colorString;
}
}
} else if (!_colorMatch(item.strokeColor, colorString)) {
2017-10-05 18:12:22 -04:00
changed = true;
item.strokeColor = colorString;
}
}
return changed;
};
/**
* Called when setting stroke width
* @param {number} value New stroke width
* @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, textEditTargetId) {
let changed = false;
const items = _getColorStateListeners(textEditTargetId);
for (let item of items) {
if (item.parent instanceof paper.CompoundPath) {
item = item.parent;
}
if (isGroup(item)) {
continue;
2017-10-05 18:12:22 -04:00
} else if (item.strokeWidth !== value) {
item.strokeWidth = value;
changed = true;
}
}
return changed;
};
/**
* Get state of colors and stroke width for selection
2017-10-03 15:04:53 -04:00
* @param {!Array<paper.Item>} selectedItems Selected paper items
* @return {object} Object of strokeColor, strokeWidth, fillColor of the selection.
* Gives MIXED when there are mixed values for a color, and null for transparent.
* Gives null when there are mixed values for stroke width.
*/
2017-10-03 15:04:53 -04:00
const getColorsFromSelection = function (selectedItems) {
let selectionFillColorString;
let selectionStrokeColorString;
let selectionStrokeWidth;
let firstChild = true;
for (let item of selectedItems) {
if (item.parent instanceof paper.CompoundPath) {
// Compound path children inherit fill and stroke color from their parent.
item = item.parent;
}
let itemFillColorString;
let itemStrokeColorString;
// handle pgTextItems differently by going through their children
if (isPGTextItem(item)) {
for (const child of item.children) {
for (const path of child.children) {
if (!path.data.isPGGlyphRect) {
if (path.fillColor) {
itemFillColorString = path.fillColor.toCSS();
}
if (path.strokeColor) {
itemStrokeColorString = path.strokeColor.toCSS();
}
// check every style against the first of the items
if (firstChild) {
firstChild = false;
selectionFillColorString = itemFillColorString;
selectionStrokeColorString = itemStrokeColorString;
selectionStrokeWidth = path.strokeWidth;
}
if (itemFillColorString !== selectionFillColorString) {
selectionFillColorString = MIXED;
}
if (itemStrokeColorString !== selectionStrokeColorString) {
selectionStrokeColorString = MIXED;
}
if (selectionStrokeWidth !== path.strokeWidth) {
selectionStrokeWidth = null;
}
}
}
}
} else if (!isGroup(item)) {
if (item.fillColor) {
// hack bc text items with null fill can't be detected by fill-hitTest anymore
if (isPointTextItem(item) && item.fillColor.toCSS() === 'rgba(0,0,0,0)') {
itemFillColorString = null;
2017-11-06 15:06:05 -05:00
} else if (item.fillColor.type === 'gradient') {
itemFillColorString = MIXED;
} else {
itemFillColorString = item.fillColor.toCSS();
}
}
if (item.strokeColor) {
2017-11-06 15:06:05 -05:00
if (item.strokeColor.type === 'gradient') {
itemStrokeColorString = MIXED;
} else {
itemStrokeColorString = item.strokeColor.toCSS();
}
}
// check every style against the first of the items
if (firstChild) {
firstChild = false;
selectionFillColorString = itemFillColorString;
selectionStrokeColorString = itemStrokeColorString;
selectionStrokeWidth = item.strokeWidth;
}
if (itemFillColorString !== selectionFillColorString) {
selectionFillColorString = MIXED;
}
if (itemStrokeColorString !== selectionStrokeColorString) {
selectionStrokeColorString = MIXED;
}
if (selectionStrokeWidth !== item.strokeWidth) {
selectionStrokeWidth = null;
}
}
}
return {
fillColor: selectionFillColorString ? selectionFillColorString : null,
strokeColor: selectionStrokeColorString ? selectionStrokeColorString : null,
2017-10-03 13:45:19 -04:00
strokeWidth: selectionStrokeWidth || (selectionStrokeWidth === null) ? selectionStrokeWidth : 0
};
};
const styleBlob = function (path, options) {
if (options.isEraser) {
path.fillColor = 'white';
2017-10-05 18:12:22 -04:00
} else if (options.fillColor) {
path.fillColor = options.fillColor;
} else {
2017-10-05 18:12:22 -04:00
// Make sure something visible is drawn
path.fillColor = 'black';
}
};
const stylePath = function (path, strokeColor, strokeWidth) {
// Make sure a visible line is drawn
path.setStrokeColor(
(strokeColor === MIXED || strokeColor === null) ? 'black' : strokeColor);
path.setStrokeWidth(
strokeWidth === null || strokeWidth === 0 ? 1 : strokeWidth);
};
const styleCursorPreview = function (path, options) {
if (options.isEraser) {
path.fillColor = 'white';
path.strokeColor = 'cornflowerblue';
path.strokeWidth = 1;
2017-10-05 18:12:22 -04:00
} else if (options.fillColor) {
path.fillColor = options.fillColor;
} else {
2017-10-05 18:12:22 -04:00
// Make sure something visible is drawn
path.fillColor = 'black';
}
};
2017-10-19 16:52:24 -04:00
const styleShape = function (path, options) {
path.fillColor = options.fillColor;
path.strokeColor = options.strokeColor;
path.strokeWidth = options.strokeWidth;
};
export {
applyFillColorToSelection,
applyStrokeColorToSelection,
applyStrokeWidthToSelection,
getColorsFromSelection,
MIXED,
styleBlob,
2017-10-19 16:52:24 -04:00
styleShape,
stylePath,
styleCursorPreview
};