Rtl text tool (#651)

This commit is contained in:
DD Liu 2018-09-05 17:19:40 -04:00 committed by GitHub
parent 3701f99d93
commit 6f5c47686d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 32 deletions

View file

@ -34,20 +34,25 @@ class TextMode extends React.Component {
}
}
componentWillReceiveProps (nextProps) {
if (this.tool && nextProps.colorState !== this.props.colorState) {
this.tool.setColorState(nextProps.colorState);
}
if (this.tool && nextProps.selectedItems !== this.props.selectedItems) {
this.tool.onSelectionChanged(nextProps.selectedItems);
}
if (this.tool && !nextProps.textEditTarget && this.props.textEditTarget) {
this.tool.onTextEditCancelled();
}
if (this.tool && !nextProps.viewBounds.equals(this.props.viewBounds)) {
this.tool.onViewBoundsChanged(nextProps.viewBounds);
}
if (this.tool && nextProps.font !== this.props.font) {
this.tool.setFont(nextProps.font);
if (this.tool) {
if (nextProps.colorState !== this.props.colorState) {
this.tool.setColorState(nextProps.colorState);
}
if (nextProps.selectedItems !== this.props.selectedItems) {
this.tool.onSelectionChanged(nextProps.selectedItems);
}
if (!nextProps.textEditTarget && this.props.textEditTarget) {
this.tool.onTextEditCancelled();
}
if (!nextProps.viewBounds.equals(this.props.viewBounds)) {
this.tool.onViewBoundsChanged(nextProps.viewBounds);
}
if (nextProps.font !== this.props.font) {
this.tool.setFont(nextProps.font);
}
if (nextProps.rtl !== this.props.rtl) {
this.tool.setRtl(nextProps.rtl);
}
}
if (nextProps.isTextModeActive && !this.props.isTextModeActive) {
@ -97,6 +102,7 @@ class TextMode extends React.Component {
this.props.changeFont,
nextProps.isBitmap
);
this.tool.setRtl(this.props.rtl);
this.tool.setColorState(nextProps.colorState);
this.tool.setFont(nextProps.font);
this.tool.activate();
@ -142,6 +148,7 @@ TextMode.propTypes = {
onChangeFillColor: PropTypes.func.isRequired,
onChangeStrokeColor: PropTypes.func.isRequired,
onUpdateImage: PropTypes.func.isRequired,
rtl: PropTypes.bool,
selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),
setSelectedItems: PropTypes.func.isRequired,
setTextEditTarget: PropTypes.func.isRequired,
@ -156,6 +163,7 @@ const mapStateToProps = (state, ownProps) => ({
isTextModeActive: ownProps.isBitmap ?
state.scratchPaint.mode === Modes.BIT_TEXT :
state.scratchPaint.mode === Modes.TEXT,
rtl: state.scratchPaint.layout.rtl,
selectedItems: state.scratchPaint.selectedItems,
textEditTarget: state.scratchPaint.textEditTarget,
viewBounds: state.scratchPaint.viewBounds

View file

@ -143,17 +143,37 @@ class TextTool extends paper.Tool {
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})`;
this.calculateMatrix(viewMtx);
}
calculateMatrix (viewMtx) {
const textBoxMtx = this.textBox.matrix;
const calculated = new paper.Matrix();
// In RTL, the element is moved relative to its parent's right edge instead of its left
// edge. We need to correct for this in order for the element to overlap the object in paper.
let tx = 0;
if (this.rtl && this.element.parentElement) {
tx = -this.element.parentElement.clientWidth;
}
// The transform origin in paper is x at justification side, y at the baseline of the text.
// The offset from (0, 0) to the upper left corner is recorded by internalBounds
// (so this.textBox.internalBounds.y is negative).
// Move the transform origin down to the text baseline to match paper
this.element.style.transformOrigin = `${-this.textBox.internalBounds.x}px ${-this.textBox.internalBounds.y}px`;
// Start by translating the element up so that its (0, 0) is now at the text baseline, like in paper
calculated.translate(tx, this.textBox.internalBounds.y);
calculated.append(viewMtx);
calculated.append(textBoxMtx);
this.element.style.transform = `matrix(${calculated.a}, ${calculated.b}, ${calculated.c}, ${calculated.d},
${calculated.tx}, ${calculated.ty})`;
}
setColorState (colorState) {
this.colorState = colorState;
}
/** @param {boolean} isRtl True if paint editor is in right-to-left layout (e.g. Hebrew language) */
setRtl (isRtl) {
this.rtl = isRtl;
}
handleMouseMove (event) {
const hitResults = paper.project.hitTestAll(event.point, this.getTextEditHitOptions());
if (hitResults.length) {
@ -283,6 +303,12 @@ class TextTool extends paper.Tool {
// Prevent line from wrapping
this.element.style.width = `${this.textBox.internalBounds.width + 1}px`;
this.element.style.height = `${this.textBox.internalBounds.height}px`;
// The transform origin needs to be updated in RTL because this.textBox.internalBounds.x
// changes as you type
if (this.rtl) {
this.element.style.transformOrigin =
`${-this.textBox.internalBounds.x}px ${-this.textBox.internalBounds.y}px`;
}
}
beginSelect () {
if (this.textBox) {
@ -308,18 +334,17 @@ class TextTool extends paper.Tool {
this.element.style.fontSize = `${this.textBox.fontSize}px`;
this.element.style.lineHeight = this.textBox.leading / this.textBox.fontSize;
const viewMtx = paper.view.matrix;
this.element.style.display = 'initial';
this.element.value = textBox.content ? textBox.content : '';
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(${textBox.matrix.a}, ${textBox.matrix.b}, ${textBox.matrix.c}, ${textBox.matrix.d},
${textBox.matrix.tx}, ${textBox.matrix.ty})`;
this.calculateMatrix(paper.view.matrix);
if (this.rtl) {
// make both the textbox and the textarea element grow to the left
this.textBox.justification = 'right';
} else {
this.textBox.justification = 'left';
}
this.element.focus({preventScroll: true});
this.eventListener = this.handleTextInput.bind(this);
this.element.addEventListener('input', this.eventListener);

View file

@ -30,12 +30,16 @@ class Playground extends React.Component {
'handleUpdateName',
'handleUpdateImage'
]);
// Append ?dir=rtl to URL to get RTL layout
const match = location.search.match(/dir=([^&]+)/);
const rtl = match && match[1] == 'rtl';
this.state = {
name: 'meow',
rotationCenterX: 20,
rotationCenterY: 400,
imageFormat: 'svg', // 'svg', 'png', or 'jpg'
image: svgString // svg string or data URI
image: svgString, // svg string or data URI
rtl: rtl
};
}
handleUpdateName (name) {