mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -05:00
parent
7f216defd2
commit
0240abcfe3
12 changed files with 553 additions and 8 deletions
|
@ -13,7 +13,8 @@ class Dropdown extends React.Component {
|
|||
super(props);
|
||||
bindAll(this, [
|
||||
'handleClosePopover',
|
||||
'handleToggleOpenState'
|
||||
'handleToggleOpenState',
|
||||
'isOpen'
|
||||
]);
|
||||
this.state = {
|
||||
isOpen: false
|
||||
|
@ -25,9 +26,16 @@ class Dropdown extends React.Component {
|
|||
});
|
||||
}
|
||||
handleToggleOpenState () {
|
||||
const newState = !this.state.isOpen;
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen
|
||||
isOpen: newState
|
||||
});
|
||||
if (newState && this.props.onOpen) {
|
||||
this.props.onOpen();
|
||||
}
|
||||
}
|
||||
isOpen () {
|
||||
return this.state.isOpen;
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
|
@ -35,7 +43,8 @@ class Dropdown extends React.Component {
|
|||
body={this.props.popoverContent}
|
||||
isOpen={this.state.isOpen}
|
||||
preferPlace="below"
|
||||
onOuterAction={this.handleClosePopover}
|
||||
onOuterAction={this.props.onOuterAction ?
|
||||
this.props.onOuterAction : this.handleClosePopover}
|
||||
{...this.props}
|
||||
>
|
||||
<div
|
||||
|
@ -62,6 +71,8 @@ class Dropdown extends React.Component {
|
|||
Dropdown.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
onOpen: PropTypes.func,
|
||||
onOuterAction: PropTypes.func,
|
||||
popoverContent: PropTypes.node.isRequired
|
||||
};
|
||||
|
||||
|
|
80
src/components/font-dropdown/font-dropdown.css
Normal file
80
src/components/font-dropdown/font-dropdown.css
Normal file
|
@ -0,0 +1,80 @@
|
|||
@import "../../css/colors.css";
|
||||
@import "../../css/units.css";
|
||||
|
||||
.mod-menu-item {
|
||||
display: flex;
|
||||
margin: 0 -$grid-unit;
|
||||
min-width: 6.25rem;
|
||||
padding: calc(2 * $grid-unit);
|
||||
padding-left: calc(3 * $grid-unit);
|
||||
padding-right: calc(3 * $grid-unit);
|
||||
white-space: nowrap;
|
||||
width: 8.5rem;
|
||||
cursor: pointer;
|
||||
transition: 0.1s ease;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mod-menu-item:hover {
|
||||
background: $motion-primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.mod-context-menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mod-unselect {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.font-dropdown {
|
||||
align-items: center;
|
||||
color: $text-primary;
|
||||
display: flex;
|
||||
font-size: 0.625rem;
|
||||
justify-content: space-between;
|
||||
width: 8.5rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.serif {
|
||||
font-family: 'Serif';
|
||||
}
|
||||
|
||||
.sans-serif {
|
||||
font-family: 'Sans Serif';
|
||||
}
|
||||
|
||||
.serif {
|
||||
font-family: 'Serif';
|
||||
}
|
||||
|
||||
.handwriting {
|
||||
font-family: 'Handwriting';
|
||||
}
|
||||
|
||||
.marker {
|
||||
font-family: 'Marker';
|
||||
}
|
||||
|
||||
.curly {
|
||||
font-family: 'Curly';
|
||||
}
|
||||
|
||||
.pixel {
|
||||
font-family: 'Pixel';
|
||||
}
|
||||
|
||||
.chinese {
|
||||
font-family: "Microsoft YaHei", "微软雅黑", STXihei, "华文细黑";
|
||||
}
|
||||
|
||||
.japanese {
|
||||
font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic";
|
||||
}
|
||||
|
||||
.korean {
|
||||
font-family: "Malgun Gothic";
|
||||
}
|
129
src/components/font-dropdown/font-dropdown.jsx
Normal file
129
src/components/font-dropdown/font-dropdown.jsx
Normal file
|
@ -0,0 +1,129 @@
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import Button from '../button/button.jsx';
|
||||
import Dropdown from '../dropdown/dropdown.jsx';
|
||||
import InputGroup from '../input-group/input-group.jsx';
|
||||
import Fonts from '../../lib/fonts';
|
||||
import styles from './font-dropdown.css';
|
||||
|
||||
const ModeToolsComponent = props => (
|
||||
<Dropdown
|
||||
className={classNames(styles.modUnselect, styles.fontDropdown)}
|
||||
enterExitTransitionDurationMs={60}
|
||||
popoverContent={
|
||||
<InputGroup className={styles.modContextMenu}>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverSansSerif}
|
||||
>
|
||||
<span className={styles.sansSerif}>
|
||||
{props.getTranslatedFontName(Fonts.SANS_SERIF)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverSerif}
|
||||
>
|
||||
<span className={styles.serif}>
|
||||
{props.getTranslatedFontName(Fonts.SERIF)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverHandwriting}
|
||||
>
|
||||
<span className={styles.handwriting}>
|
||||
{props.getTranslatedFontName(Fonts.HANDWRITING)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverMarker}
|
||||
>
|
||||
<span className={styles.marker}>
|
||||
{props.getTranslatedFontName(Fonts.MARKER)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverCurly}
|
||||
>
|
||||
<span className={styles.curly}>
|
||||
{props.getTranslatedFontName(Fonts.CURLY)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverPixel}
|
||||
>
|
||||
<span className={styles.pixel}>
|
||||
{props.getTranslatedFontName(Fonts.PIXEL)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverChinese}
|
||||
>
|
||||
<span className={styles.chinese}>
|
||||
{props.getTranslatedFontName(Fonts.CHINESE)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverJapanese}
|
||||
>
|
||||
<span className={styles.japanese}>
|
||||
{props.getTranslatedFontName(Fonts.JAPANESE)}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
className={classNames(styles.modMenuItem)}
|
||||
onClick={props.onChoose}
|
||||
onMouseOver={props.onHoverKorean}
|
||||
>
|
||||
<span className={styles.korean}>
|
||||
{props.getTranslatedFontName(Fonts.KOREAN)}
|
||||
</span>
|
||||
</Button>
|
||||
</InputGroup>
|
||||
}
|
||||
ref={props.componentRef}
|
||||
tipSize={.01}
|
||||
onOpen={props.onOpenDropdown}
|
||||
onOuterAction={props.onClickOutsideDropdown}
|
||||
>
|
||||
<span className={props.getFontStyle(props.font)}>
|
||||
{props.getTranslatedFontName(props.font)}
|
||||
</span>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
ModeToolsComponent.propTypes = {
|
||||
componentRef: PropTypes.func.isRequired,
|
||||
font: PropTypes.string,
|
||||
getFontStyle: PropTypes.func.isRequired,
|
||||
getTranslatedFontName: PropTypes.func.isRequired,
|
||||
onChoose: PropTypes.func.isRequired,
|
||||
onClickOutsideDropdown: PropTypes.func,
|
||||
onHoverChinese: PropTypes.func,
|
||||
onHoverCurly: PropTypes.func,
|
||||
onHoverHandwriting: PropTypes.func,
|
||||
onHoverJapanese: PropTypes.func,
|
||||
onHoverKorean: PropTypes.func,
|
||||
onHoverMarker: PropTypes.func,
|
||||
onHoverPixel: PropTypes.func,
|
||||
onHoverSansSerif: PropTypes.func,
|
||||
onHoverSerif: PropTypes.func,
|
||||
onOpenDropdown: PropTypes.func
|
||||
};
|
||||
export default ModeToolsComponent;
|
|
@ -8,6 +8,7 @@ import {changeBrushSize} from '../../reducers/brush-mode';
|
|||
import {changeBrushSize as changeEraserSize} from '../../reducers/eraser-mode';
|
||||
import {changeBitBrushSize} from '../../reducers/bit-brush-size';
|
||||
|
||||
import FontDropdown from '../../containers/font-dropdown.jsx';
|
||||
import LiveInputHOC from '../forms/live-input-hoc.jsx';
|
||||
import {defineMessages, injectIntl, intlShape} from 'react-intl';
|
||||
import Input from '../forms/input.jsx';
|
||||
|
@ -186,6 +187,16 @@ const ModeToolsComponent = props => {
|
|||
</InputGroup>
|
||||
</div>
|
||||
);
|
||||
case Modes.TEXT:
|
||||
return (
|
||||
<div className={classNames(props.className, styles.modeTools)}>
|
||||
<InputGroup>
|
||||
<FontDropdown
|
||||
onUpdateImage={props.onUpdateImage}
|
||||
/>
|
||||
</InputGroup>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
// Leave empty for now, if mode not supported
|
||||
return (
|
||||
|
@ -214,6 +225,7 @@ ModeToolsComponent.propTypes = {
|
|||
onFlipVertical: PropTypes.func.isRequired,
|
||||
onPasteFromClipboard: PropTypes.func.isRequired,
|
||||
onPointPoints: PropTypes.func.isRequired,
|
||||
onUpdateImage: PropTypes.func.isRequired,
|
||||
selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item))
|
||||
};
|
||||
|
||||
|
|
|
@ -168,8 +168,6 @@ $border-radius: 0.25rem;
|
|||
background: transparent;
|
||||
border: none;
|
||||
display: none;
|
||||
font-family: Helvetica;
|
||||
font-size: 30px;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
padding: 0px;
|
||||
|
|
231
src/containers/font-dropdown.jsx
Normal file
231
src/containers/font-dropdown.jsx
Normal file
|
@ -0,0 +1,231 @@
|
|||
import paper from '@scratch/paper';
|
||||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {defineMessages, injectIntl, intlShape} from 'react-intl';
|
||||
|
||||
import FontDropdownComponent from '../components/font-dropdown/font-dropdown.jsx';
|
||||
import Fonts from '../lib/fonts';
|
||||
import {changeFont} from '../reducers/font';
|
||||
import {getSelectedLeafItems} from '../helper/selection';
|
||||
import styles from '../components/font-dropdown/font-dropdown.css';
|
||||
|
||||
const messages = defineMessages({
|
||||
sansSerif: {
|
||||
defaultMessage: 'Sans Serif',
|
||||
description: 'Name of the sans serif font',
|
||||
id: 'paint.modeTools.sansSerif'
|
||||
},
|
||||
serif: {
|
||||
defaultMessage: 'Serif',
|
||||
description: 'Name of the serif font',
|
||||
id: 'paint.modeTools.serif'
|
||||
},
|
||||
handwriting: {
|
||||
defaultMessage: 'Handwriting',
|
||||
description: 'Name of the handwriting font',
|
||||
id: 'paint.modeTools.handwriting'
|
||||
},
|
||||
marker: {
|
||||
defaultMessage: 'Marker',
|
||||
description: 'Name of the marker font',
|
||||
id: 'paint.modeTools.marker'
|
||||
},
|
||||
curly: {
|
||||
defaultMessage: 'Curly',
|
||||
description: 'Name of the curly font',
|
||||
id: 'paint.modeTools.curly'
|
||||
},
|
||||
pixel: {
|
||||
defaultMessage: 'Pixel',
|
||||
description: 'Name of the pixelated font',
|
||||
id: 'paint.modeTools.pixel'
|
||||
}
|
||||
});
|
||||
class ModeToolsComponent extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
bindAll(this, [
|
||||
'getFontStyle',
|
||||
'getTranslatedFontName',
|
||||
'handleChangeFontSerif',
|
||||
'handleChangeFontSansSerif',
|
||||
'handleChangeFontHandwriting',
|
||||
'handleChangeFontMarker',
|
||||
'handleChangeFontCurly',
|
||||
'handleChangeFontPixel',
|
||||
'handleChangeFontChinese',
|
||||
'handleChangeFontJapanese',
|
||||
'handleChangeFontKorean',
|
||||
'handleOpenDropdown',
|
||||
'handleClickOutsideDropdown',
|
||||
'setDropdown',
|
||||
'handleChoose'
|
||||
]);
|
||||
}
|
||||
getFontStyle (font) {
|
||||
switch (font) {
|
||||
case Fonts.SERIF:
|
||||
return styles.serif;
|
||||
case Fonts.SANS_SERIF:
|
||||
return styles.sansSerif;
|
||||
case Fonts.HANDWRITING:
|
||||
return styles.handwriting;
|
||||
case Fonts.MARKER:
|
||||
return styles.marker;
|
||||
case Fonts.CURLY:
|
||||
return styles.curly;
|
||||
case Fonts.PIXEL:
|
||||
return styles.pixel;
|
||||
case Fonts.CHINESE:
|
||||
return styles.chinese;
|
||||
case Fonts.JAPANESE:
|
||||
return styles.japanese;
|
||||
case Fonts.KOREAN:
|
||||
return styles.korean;
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
getTranslatedFontName (font) {
|
||||
switch (font) {
|
||||
case Fonts.SERIF:
|
||||
return this.props.intl.formatMessage(messages.serif);
|
||||
case Fonts.SANS_SERIF:
|
||||
return this.props.intl.formatMessage(messages.sansSerif);
|
||||
case Fonts.HANDWRITING:
|
||||
return this.props.intl.formatMessage(messages.handwriting);
|
||||
case Fonts.MARKER:
|
||||
return this.props.intl.formatMessage(messages.marker);
|
||||
case Fonts.CURLY:
|
||||
return this.props.intl.formatMessage(messages.curly);
|
||||
case Fonts.PIXEL:
|
||||
return this.props.intl.formatMessage(messages.pixel);
|
||||
case Fonts.CHINESE:
|
||||
return '中文';
|
||||
case Fonts.KOREAN:
|
||||
return '한국어';
|
||||
case Fonts.JAPANESE:
|
||||
return '日本語';
|
||||
default:
|
||||
return font;
|
||||
}
|
||||
}
|
||||
handleChangeFontSansSerif () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.SANS_SERIF);
|
||||
}
|
||||
}
|
||||
handleChangeFontSerif () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.SERIF);
|
||||
}
|
||||
}
|
||||
handleChangeFontHandwriting () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.HANDWRITING);
|
||||
}
|
||||
}
|
||||
handleChangeFontMarker () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.MARKER);
|
||||
}
|
||||
}
|
||||
handleChangeFontCurly () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.CURLY);
|
||||
}
|
||||
}
|
||||
handleChangeFontPixel () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.PIXEL);
|
||||
}
|
||||
}
|
||||
handleChangeFontChinese () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.CHINESE);
|
||||
}
|
||||
}
|
||||
handleChangeFontJapanese () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.JAPANESE);
|
||||
}
|
||||
}
|
||||
handleChangeFontKorean () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.props.changeFont(Fonts.KOREAN);
|
||||
}
|
||||
}
|
||||
handleChoose () {
|
||||
if (this.dropDown.isOpen()) {
|
||||
this.dropDown.handleClosePopover();
|
||||
this.props.onUpdateImage();
|
||||
}
|
||||
}
|
||||
handleOpenDropdown () {
|
||||
this.savedFont = this.props.font;
|
||||
this.savedSelection = getSelectedLeafItems();
|
||||
}
|
||||
handleClickOutsideDropdown (e) {
|
||||
e.stopPropagation();
|
||||
this.dropDown.handleClosePopover();
|
||||
|
||||
// Cancel font change
|
||||
for (const item of this.savedSelection) {
|
||||
if (item instanceof paper.PointText) {
|
||||
item.font = this.savedFont;
|
||||
}
|
||||
}
|
||||
|
||||
this.props.changeFont(this.savedFont);
|
||||
this.savedFont = null;
|
||||
this.savedSelection = null;
|
||||
}
|
||||
setDropdown (element) {
|
||||
this.dropDown = element;
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<FontDropdownComponent
|
||||
componentRef={this.setDropdown}
|
||||
font={this.props.font}
|
||||
getFontStyle={this.getFontStyle}
|
||||
getTranslatedFontName={this.getTranslatedFontName}
|
||||
onChoose={this.handleChoose}
|
||||
onClickOutsideDropdown={this.handleClickOutsideDropdown}
|
||||
onHoverChinese={this.handleChangeFontChinese}
|
||||
onHoverCurly={this.handleChangeFontCurly}
|
||||
onHoverHandwriting={this.handleChangeFontHandwriting}
|
||||
onHoverJapanese={this.handleChangeFontJapanese}
|
||||
onHoverKorean={this.handleChangeFontKorean}
|
||||
onHoverMarker={this.handleChangeFontMarker}
|
||||
onHoverPixel={this.handleChangeFontPixel}
|
||||
onHoverSansSerif={this.handleChangeFontSansSerif}
|
||||
onHoverSerif={this.handleChangeFontSerif}
|
||||
onOpenDropdown={this.handleOpenDropdown}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ModeToolsComponent.propTypes = {
|
||||
changeFont: PropTypes.func.isRequired,
|
||||
font: PropTypes.string,
|
||||
intl: intlShape.isRequired,
|
||||
onUpdateImage: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
font: state.scratchPaint.font
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
changeFont: font => {
|
||||
dispatch(changeFont(font));
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(injectIntl(ModeToolsComponent));
|
|
@ -208,6 +208,7 @@ class ModeTools extends React.Component {
|
|||
onFlipVertical={this.handleFlipVertical}
|
||||
onPasteFromClipboard={this.handlePasteFromClipboard}
|
||||
onPointPoints={this.handlePointPoints}
|
||||
onUpdateImage={this.props.onUpdateImage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,9 +3,11 @@ import PropTypes from 'prop-types';
|
|||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import Fonts from '../lib/fonts';
|
||||
import Modes from '../lib/modes';
|
||||
import {MIXED} from '../helper/style-path';
|
||||
|
||||
import {changeFont} from '../reducers/font';
|
||||
import {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-color';
|
||||
import {changeStrokeColor} from '../reducers/stroke-color';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
|
@ -42,6 +44,9 @@ class TextMode extends React.Component {
|
|||
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 (nextProps.isTextModeActive && !this.props.isTextModeActive) {
|
||||
this.activateTool();
|
||||
|
@ -54,6 +59,7 @@ class TextMode extends React.Component {
|
|||
}
|
||||
activateTool () {
|
||||
clearSelection(this.props.clearSelectedItems);
|
||||
|
||||
// If fill and stroke color are both mixed/transparent/absent, set fill to default and stroke to transparent.
|
||||
// If exactly one of fill or stroke color is set, set the other one to transparent.
|
||||
// This way the tool won't draw an invisible state, or be unclear about what will be drawn.
|
||||
|
@ -69,14 +75,21 @@ class TextMode extends React.Component {
|
|||
} else if (fillColorPresent && !strokeColorPresent) {
|
||||
this.props.onChangeStrokeColor(null);
|
||||
}
|
||||
if (!this.props.font || Object.keys(Fonts).map(key => Fonts[key])
|
||||
.indexOf(this.props.font) < 0) {
|
||||
this.props.changeFont(Fonts.SANS_SERIF);
|
||||
}
|
||||
|
||||
this.tool = new TextTool(
|
||||
this.props.textArea,
|
||||
this.props.setSelectedItems,
|
||||
this.props.clearSelectedItems,
|
||||
this.props.onUpdateImage,
|
||||
this.props.setTextEditTarget,
|
||||
this.props.changeFont
|
||||
);
|
||||
this.tool.setColorState(this.props.colorState);
|
||||
this.tool.setFont(this.props.font);
|
||||
this.tool.activate();
|
||||
}
|
||||
deactivateTool () {
|
||||
|
@ -95,12 +108,14 @@ class TextMode extends React.Component {
|
|||
}
|
||||
|
||||
TextMode.propTypes = {
|
||||
changeFont: PropTypes.func.isRequired,
|
||||
clearSelectedItems: PropTypes.func.isRequired,
|
||||
colorState: PropTypes.shape({
|
||||
fillColor: PropTypes.string,
|
||||
strokeColor: PropTypes.string,
|
||||
strokeWidth: PropTypes.number
|
||||
}).isRequired,
|
||||
font: PropTypes.string,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isTextModeActive: PropTypes.bool.isRequired,
|
||||
onChangeFillColor: PropTypes.func.isRequired,
|
||||
|
@ -116,12 +131,16 @@ TextMode.propTypes = {
|
|||
|
||||
const mapStateToProps = state => ({
|
||||
colorState: state.scratchPaint.color,
|
||||
font: state.scratchPaint.font,
|
||||
isTextModeActive: state.scratchPaint.mode === Modes.TEXT,
|
||||
selectedItems: state.scratchPaint.selectedItems,
|
||||
textEditTarget: state.scratchPaint.textEditTarget,
|
||||
viewBounds: state.scratchPaint.viewBounds
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
changeFont: font => {
|
||||
dispatch(changeFont(font));
|
||||
},
|
||||
clearSelectedItems: () => {
|
||||
dispatch(clearSelectedItems());
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import paper from '@scratch/paper';
|
||||
import Modes from '../../lib/modes';
|
||||
import {clearSelection} from '../selection';
|
||||
import {clearSelection, getSelectedLeafItems} from '../selection';
|
||||
import BoundingBoxTool from '../selection-tools/bounding-box-tool';
|
||||
import NudgeTool from '../selection-tools/nudge-tool';
|
||||
import {hoverBounds} from '../guides';
|
||||
|
@ -36,14 +36,16 @@ class TextTool extends paper.Tool {
|
|||
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
|
||||
* @param {!function} onUpdateImage A callback to call when the image visibly changes
|
||||
* @param {!function} setTextEditTarget Call to set text editing target whenever text editing is active
|
||||
* @param {!function} changeFont Call to change the font in the dropdown
|
||||
*/
|
||||
constructor (textAreaElement, setSelectedItems, clearSelectedItems, onUpdateImage, setTextEditTarget) {
|
||||
constructor (textAreaElement, setSelectedItems, clearSelectedItems, onUpdateImage, setTextEditTarget, changeFont) {
|
||||
super();
|
||||
this.element = textAreaElement;
|
||||
this.setSelectedItems = setSelectedItems;
|
||||
this.clearSelectedItems = clearSelectedItems;
|
||||
this.onUpdateImage = onUpdateImage;
|
||||
this.setTextEditTarget = setTextEditTarget;
|
||||
this.changeFont = changeFont;
|
||||
this.boundingBoxTool = new BoundingBoxTool(Modes.TEXT, setSelectedItems, clearSelectedItems, onUpdateImage);
|
||||
this.nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateImage);
|
||||
this.lastEvent = null;
|
||||
|
@ -99,6 +101,20 @@ class TextTool extends paper.Tool {
|
|||
onSelectionChanged (selectedItems) {
|
||||
this.boundingBoxTool.onSelectionChanged(selectedItems);
|
||||
}
|
||||
setFont (font) {
|
||||
this.font = font;
|
||||
if (this.textBox) {
|
||||
this.textBox.font = font;
|
||||
}
|
||||
const selected = getSelectedLeafItems();
|
||||
for (const item of selected) {
|
||||
if (item instanceof paper.PointText) {
|
||||
item.font = font;
|
||||
}
|
||||
}
|
||||
this.element.style.fontFamily = font;
|
||||
this.setSelectedItems();
|
||||
}
|
||||
// Allow other tools to cancel text edit mode
|
||||
onTextEditCancelled () {
|
||||
this.endTextEdit();
|
||||
|
@ -193,7 +209,7 @@ class TextTool extends paper.Tool {
|
|||
this.textBox = new paper.PointText({
|
||||
point: event.point,
|
||||
content: '',
|
||||
font: 'Helvetica',
|
||||
font: this.font,
|
||||
fontSize: 30,
|
||||
fillColor: this.colorState.fillColor,
|
||||
// Default leading for both the HTML text area and paper.PointText
|
||||
|
@ -272,6 +288,11 @@ class TextTool extends paper.Tool {
|
|||
beginTextEdit (initialText, matrix) {
|
||||
this.mode = TextTool.TEXT_EDIT_MODE;
|
||||
this.setTextEditTarget(this.textBox.id);
|
||||
if (this.font !== this.textBox.font) {
|
||||
this.changeFont(this.textBox.font);
|
||||
}
|
||||
this.element.style.fontSize = `${this.textBox.fontSize}px`;
|
||||
this.element.style.lineHeight = this.textBox.leading / this.textBox.fontSize;
|
||||
|
||||
const viewMtx = paper.view.matrix;
|
||||
|
||||
|
|
13
src/lib/fonts.js
Normal file
13
src/lib/fonts.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
const Fonts = {
|
||||
SANS_SERIF: 'Sans Serif',
|
||||
SERIF: 'Serif',
|
||||
HANDWRITING: 'Handwriting',
|
||||
MARKER: 'Marker',
|
||||
CURLY: 'Curly',
|
||||
PIXEL: 'Pixel',
|
||||
CHINESE: '"Microsoft YaHei", "微软雅黑", STXihei, "华文细黑"',
|
||||
JAPANESE: '"ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", Osaka, "メイリオ", Meiryo, "MS Pゴシック", "MS PGothic"',
|
||||
KOREAN: 'Malgun Gothic'
|
||||
};
|
||||
|
||||
export default Fonts;
|
28
src/reducers/font.js
Normal file
28
src/reducers/font.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import Fonts from '../lib/fonts';
|
||||
|
||||
const CHANGE_FONT = 'scratch-paint/fonts/CHANGE_FONT';
|
||||
const initialState = Fonts.SANS_SERIF;
|
||||
|
||||
const reducer = function (state, action) {
|
||||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
case CHANGE_FONT:
|
||||
if (!action.font) return state;
|
||||
return action.font;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
// Action creators ==================================
|
||||
const changeFont = function (font) {
|
||||
return {
|
||||
type: CHANGE_FONT,
|
||||
font: font
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
reducer as default,
|
||||
changeFont
|
||||
};
|
|
@ -5,6 +5,7 @@ import brushModeReducer from './brush-mode';
|
|||
import eraserModeReducer from './eraser-mode';
|
||||
import colorReducer from './color';
|
||||
import clipboardReducer from './clipboard';
|
||||
import fontReducer from './font';
|
||||
import formatReducer from './format';
|
||||
import hoverReducer from './hover';
|
||||
import modalsReducer from './modals';
|
||||
|
@ -20,6 +21,7 @@ export default combineReducers({
|
|||
color: colorReducer,
|
||||
clipboard: clipboardReducer,
|
||||
eraserMode: eraserModeReducer,
|
||||
font: fontReducer,
|
||||
format: formatReducer,
|
||||
hoveredItemId: hoverReducer,
|
||||
modals: modalsReducer,
|
||||
|
|
Loading…
Reference in a new issue