Skip to content

Commit

Permalink
Implement APIs for custom fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin committed Aug 15, 2023
1 parent a4b85e3 commit 9163e75
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 11 deletions.
31 changes: 31 additions & 0 deletions src/components/font-dropdown/custom-font-button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import PropTypes from 'prop-types';
import Button from '../button/button.jsx';

class CustomFontButton extends React.Component {
constructor (props) {
super(props);
this.handleMouseOver = this.handleMouseOver.bind(this);
}
handleMouseOver () {
this.props.onMouseOver(this.props.font);
}
render () {
return (
<Button
{...this.props}
onMouseOver={this.handleMouseOver}
>
{this.props.children}
</Button>
);
}
}

CustomFontButton.propTypes = {
children: PropTypes.node.isRequired,
font: PropTypes.string.isRequired,
onMouseOver: PropTypes.func.isRequired
};

export default CustomFontButton;
34 changes: 34 additions & 0 deletions src/components/font-dropdown/font-dropdown.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import {FormattedMessage} from 'react-intl';

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 CustomFontButton from './custom-font-button.jsx';
import styles from './font-dropdown.css';

const DisplayFont = ({font, getFontName}) => (
Expand Down Expand Up @@ -118,6 +120,32 @@ const ModeToolsComponent = props => (
getFontName={props.getFontName}
/>
</Button>
{props.customFonts.map(font => (
<CustomFontButton
key={font.name}
font={font.family}
className={classNames(styles.modMenuItem)}
onClick={props.onChoose}
onMouseOver={props.onHoverCustom}
>
<DisplayFont
font={font.family}
getFontName={props.getFontName}
/>
</CustomFontButton>
))}
{props.onManageFonts && (
<Button
className={styles.modMenuItem}
onClick={props.onManageFonts}
>
<FormattedMessage
defaultMessage="Add more fonts..."
description="Button in costume editor font list to add more fonts"
id="tw.paint.fonts.more"
/>
</Button>
)}
</InputGroup>
}
ref={props.componentRef}
Expand All @@ -140,6 +168,12 @@ ModeToolsComponent.propTypes = {
getFontName: PropTypes.func.isRequired,
onChoose: PropTypes.func.isRequired,
onClickOutsideDropdown: PropTypes.func,
customFonts: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
family: PropTypes.string.isRequired
})).isRequired,
onHoverCustom: PropTypes.func.isRequired,
onManageFonts: PropTypes.func,
onHoverChinese: PropTypes.func,
onHoverCurly: PropTypes.func,
onHoverHandwriting: PropTypes.func,
Expand Down
2 changes: 2 additions & 0 deletions src/components/mode-tools/mode-tools.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ const ModeToolsComponent = props => {
<InputGroup>
<FontDropdown
onUpdateImage={props.onUpdateImage}
onManageFonts={props.onManageFonts}
/>
</InputGroup>
</div>
Expand Down Expand Up @@ -327,6 +328,7 @@ ModeToolsComponent.propTypes = {
onFillShapes: PropTypes.func.isRequired,
onFlipHorizontal: PropTypes.func.isRequired,
onFlipVertical: PropTypes.func.isRequired,
onManageFonts: PropTypes.func,
onOutlineShapes: PropTypes.func.isRequired,
onPasteFromClipboard: PropTypes.func.isRequired,
onPointPoints: PropTypes.func.isRequired,
Expand Down
3 changes: 3 additions & 0 deletions src/components/paint-editor/paint-editor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ const PaintEditorComponent = props => (
<InputGroup className={styles.modModeTools}>
<ModeToolsContainer
onUpdateImage={props.onUpdateImage}
onManageFonts={props.onManageFonts}
/>
</InputGroup>
</div> :
Expand All @@ -125,6 +126,7 @@ const PaintEditorComponent = props => (
<InputGroup className={styles.modModeTools}>
<ModeToolsContainer
onUpdateImage={props.onUpdateImage}
onManageFonts={props.onManageFonts}
/>
</InputGroup>
</div> : null
Expand Down Expand Up @@ -337,6 +339,7 @@ PaintEditorComponent.propTypes = {
isEyeDropping: PropTypes.bool,
name: PropTypes.string,
onChangeTheme: PropTypes.func.isRequired,
onManageFonts: PropTypes.func,
onRedo: PropTypes.func.isRequired,
onSwitchToBitmap: PropTypes.func.isRequired,
onSwitchToVector: PropTypes.func.isRequired,
Expand Down
52 changes: 42 additions & 10 deletions src/containers/font-dropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class FontDropdown extends React.Component {
super(props);
bindAll(this, [
'getFontName',
'handleHoverCustom',
'handleManageFonts',
'handleChangeFontSerif',
'handleChangeFontSansSerif',
'handleChangeFontHandwriting',
Expand All @@ -30,17 +32,35 @@ class FontDropdown extends React.Component {
]);
}
getFontName (font) {
switch (font) {
case Fonts.CHINESE:
return '中文';
case Fonts.KOREAN:
return '한국어';
case Fonts.JAPANESE:
return '日本語';
default:
return font;
const NATIVE_FONTS = Object.values(Fonts);
if (NATIVE_FONTS.includes(font)) {
switch (font) {
case Fonts.CHINESE:
return '中文';
case Fonts.KOREAN:
return '한국어';
case Fonts.JAPANESE:
return '日本語';
default:
return font;
}
}

const customFont = this.props.customFonts.find(i => i.family === font);
if (customFont) {
return customFont.name;
}
return font;
}
handleHoverCustom (family) {
if (this.dropDown.isOpen()) {
this.props.changeFont(family);
}
}
handleManageFonts () {
this.cancelFontChange();
this.props.onManageFonts();
}
handleChangeFontSansSerif () {
if (this.dropDown.isOpen()) {
this.props.changeFont(Fonts.SANS_SERIF);
Expand Down Expand Up @@ -98,6 +118,9 @@ class FontDropdown extends React.Component {
}
handleClickOutsideDropdown (e) {
e.stopPropagation();
this.cancelFontChange();
}
cancelFontChange () {
this.dropDown.handleClosePopover();

// Cancel font change
Expand All @@ -120,6 +143,9 @@ class FontDropdown extends React.Component {
componentRef={this.setDropdown}
font={this.props.font}
getFontName={this.getFontName}
customFonts={this.props.customFonts}
onHoverCustom={this.handleHoverCustom}
onManageFonts={this.props.onManageFonts && this.handleManageFonts}
onChoose={this.handleChoose}
onClickOutsideDropdown={this.handleClickOutsideDropdown}
onHoverChinese={this.handleChangeFontChinese}
Expand All @@ -139,12 +165,18 @@ class FontDropdown extends React.Component {

FontDropdown.propTypes = {
changeFont: PropTypes.func.isRequired,
customFonts: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
family: PropTypes.string.isRequired
})).isRequired,
onManageFonts: PropTypes.func,
font: PropTypes.string,
onUpdateImage: PropTypes.func.isRequired
};

const mapStateToProps = state => ({
font: state.scratchPaint.font
font: state.scratchPaint.font,
customFonts: state.scratchPaint.customFonts
});
const mapDispatchToProps = dispatch => ({
changeFont: font => {
Expand Down
2 changes: 2 additions & 0 deletions src/containers/mode-tools.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ class ModeTools extends React.Component {
onDelete={this.handleDelete}
onFlipHorizontal={this.handleFlipHorizontal}
onFlipVertical={this.handleFlipVertical}
onManageFonts={this.props.onManageFonts}
onPasteFromClipboard={this.handlePasteFromClipboard}
onPointPoints={this.handlePointPoints}
onUpdateImage={this.props.onUpdateImage}
Expand All @@ -232,6 +233,7 @@ ModeTools.propTypes = {
format: PropTypes.oneOf(Object.keys(Formats)),
mode: PropTypes.oneOf(Object.keys(Modes)),
onCopyToClipboard: PropTypes.func.isRequired,
onManageFonts: PropTypes.func,
onPasteFromClipboard: PropTypes.func.isRequired,
onUpdateImage: PropTypes.func.isRequired,
// Listen on selected items to update hasSelectedPoints
Expand Down
18 changes: 17 additions & 1 deletion src/containers/paint-editor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {setTextEditTarget} from '../reducers/text-edit-target';
import {updateViewBounds} from '../reducers/view-bounds';
import {setLayout} from '../reducers/layout';
import {setTheme as setReduxTheme} from '../reducers/theme';
import {setCustomFonts} from '../reducers/custom-fonts';

import {getSelectedLeafItems} from '../helper/selection';
import {convertToBitmap, convertToVector} from '../helper/bitmap';
Expand Down Expand Up @@ -96,6 +97,7 @@ class PaintEditor extends React.Component {
colorInfo: null
};
this.props.setLayout(this.props.rtl ? 'rtl' : 'ltr');
this.props.onCustomFontsChanged(this.props.customFonts);
resizeView(this.props.width, this.props.height);
}
componentDidMount () {
Expand All @@ -120,6 +122,9 @@ class PaintEditor extends React.Component {
if (this.props.theme !== newProps.theme) {
this.props.setReduxTheme('default');
}
if (this.props.customFonts !== newProps.customFonts) {
this.props.onCustomFontsChanged(newProps.customFonts);
}
}
componentDidUpdate (prevProps) {
if (this.props.isEyeDropping && !prevProps.isEyeDropping) {
Expand Down Expand Up @@ -343,6 +348,7 @@ class PaintEditor extends React.Component {
width={this.props.width}
zoomLevelId={this.props.zoomLevelId}
onChangeTheme={this.handleChangeTheme}
onManageFonts={this.props.onManageFonts}
onRedo={this.props.onRedo}
onSwitchToBitmap={this.props.handleSwitchToBitmap}
onSwitchToVector={this.props.handleSwitchToVector}
Expand All @@ -361,6 +367,12 @@ PaintEditor.propTypes = {
changeColorToEyeDropper: PropTypes.func,
changeMode: PropTypes.func.isRequired,
clearSelectedItems: PropTypes.func.isRequired,
customFonts: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
family: PropTypes.string.isRequired
})).isRequired,
onCustomFontsChanged: PropTypes.func.isRequired,
onManageFonts: PropTypes.func,
format: PropTypes.oneOf(Object.keys(Formats)), // Internal, up-to-date data format
fontInlineFn: PropTypes.func,
handleSwitchToBitmap: PropTypes.func.isRequired,
Expand Down Expand Up @@ -405,7 +417,8 @@ PaintEditor.propTypes = {
PaintEditor.defaultProps = {
width: 480,
height: 360,
theme: 'light'
theme: 'light',
customFonts: []
};

const mapStateToProps = state => ({
Expand All @@ -424,6 +437,9 @@ const mapDispatchToProps = dispatch => ({
clearSelectedItems: () => {
dispatch(clearSelectedItems());
},
onCustomFontsChanged: customFonts => {
dispatch(setCustomFonts(customFonts));
},
handleSwitchToBitmap: () => {
dispatch(changeFormat(Formats.BITMAP));
},
Expand Down
22 changes: 22 additions & 0 deletions src/reducers/custom-fonts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const SET_CUSTOM_FONTS = 'scratch-paint/tw/custom-fonts/SET_CUSTOM_FONTS';
const initialState = [];

const reducer = function (state, action) {
if (typeof state === 'undefined') state = initialState;
switch (action.type) {
case SET_CUSTOM_FONTS:
return action.fonts;
default:
return state;
}
};

const setCustomFonts = fonts => ({
type: SET_CUSTOM_FONTS,
fonts
});

export {
reducer as default,
setCustomFonts
};
2 changes: 2 additions & 0 deletions src/reducers/scratch-paint-reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import eraserModeReducer from './eraser-mode';
import colorReducer from './color';
import clipboardReducer from './clipboard';
import cursorReducer from './cursor';
import customFontsReducer from './custom-fonts';
import fillBitmapShapesReducer from './fill-bitmap-shapes';
import fillModeReducer from './fill-mode';
import fontReducer from './font';
Expand All @@ -29,6 +30,7 @@ export default combineReducers({
color: colorReducer,
clipboard: clipboardReducer,
cursor: cursorReducer,
customFonts: customFontsReducer,
eraserMode: eraserModeReducer,
fillBitmapShapes: fillBitmapShapesReducer,
fillMode: fillModeReducer,
Expand Down

0 comments on commit 9163e75

Please sign in to comment.