Skip to content

Commit

Permalink
Configurable subtitle font family
Browse files Browse the repository at this point in the history
  • Loading branch information
killergerbah committed Jun 30, 2021
1 parent 521416d commit 069fa57
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 50 deletions.
6 changes: 5 additions & 1 deletion client/src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,14 +253,18 @@ function App() {

const handleOpenCopyHistory = useCallback(() => setCopyHistoryOpen(copyHistoryOpen => !copyHistoryOpen), []);
const handleCloseCopyHistory = useCallback(() => setCopyHistoryOpen(false), []);
const handleOpenSettings = useCallback(() => setSettingsDialogOpen(true), []);
const handleOpenSettings = useCallback(() => {
setDisableKeyEvents(true);
setSettingsDialogOpen(true);
}, []);
const handleOpenHelp = useCallback(() => setHelpDialogOpen(true), []);
const handleCloseHelp = useCallback(() => setHelpDialogOpen(false), []);
const handleAlertClosed = useCallback(() => setAlertOpen(false), []);
const handleImageDialogClosed = useCallback(() => setImageDialogOpen(false), []);
const handleCloseSettings = useCallback((newSettings) => {
settingsProvider.settings = newSettings;
setSettingsDialogOpen(false);
setDisableKeyEvents(false);
extension.publishMessage({command: 'subtitleSettings', value: settingsProvider.subtitleSettings});
extension.publishMessage({command: 'ankiSettings', value: settingsProvider.ankiSettings});
}, [extension, settingsProvider]);
Expand Down
57 changes: 55 additions & 2 deletions client/src/components/SettingsDialog.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useCallback, useState, useEffect } from 'react';
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { makeStyles } from '@material-ui/styles';
import { computeStyles } from '../services/Util';
import Button from '@material-ui/core/Button';
import Checkbox from '@material-ui/core/Checkbox';
import CustomFieldDialog from './CustomFieldDialog';
Expand Down Expand Up @@ -37,6 +38,24 @@ const useStyles = makeStyles((theme) => ({
marginBottom: theme.spacing(1)
},
},
subtitlePreview: {
backgroundImage: `linear-gradient(45deg, ${theme.palette.action.disabledBackground} 25%, transparent 25%), linear-gradient(-45deg, ${theme.palette.action.disabledBackground} 25%, transparent 25%), linear-gradient(45deg, transparent 75%, ${theme.palette.action.disabledBackground} 75%), linear-gradient(-45deg, transparent 75%,${theme.palette.action.disabledBackground} 75%)`,
backgroundSize: '20px 20px',
backgroundPosition: '0 0, 0 10px, 10px -10px, -10px 0px',
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
maxWidth: '100%',
padding: 10
},
subtitlePreviewInput: {
border: 'none',
width: '100%',
textAlign: 'center',
backgroundColor: 'rgba(0,0,0,0)',
'&:focus': {
outline: 'none'
}
},
addFieldButton: {
width: "100%"
}
Expand Down Expand Up @@ -118,6 +137,8 @@ export default function SettingsDialog({anki, open, settings, onClose}) {
const [subtitleOutlineThickness, setSubtitleOutlineThickness] = useState(settings.subtitleOutlineThickness);
const [subtitleBackgroundColor, setSubtitleBackgroundColor] = useState(settings.subtitleBackgroundColor);
const [subtitleBackgroundOpacity, setSubtitleBackgroundOpacity] = useState(settings.subtitleBackgroundOpacity);
const [subtitleFontFamily, setSubtitleFontFamily] = useState(settings.subtitleFontFamily);
const [subtitlePreview, setSubtitlePreview] = useState(settings.subtitlePreview);
const [themeType, setThemeType] = useState(settings.themeType);

const handleAnkiConnectUrlChange = useCallback((e) => {
Expand All @@ -140,6 +161,8 @@ export default function SettingsDialog({anki, open, settings, onClose}) {
const handleSubtitleOutlineThicknessChange = useCallback((e) => setSubtitleOutlineThickness(e.target.value), []);
const handleSubtitleBackgroundColorChange = useCallback((e) => setSubtitleBackgroundColor(e.target.value), []);
const handleSubtitleBackgroundOpacityChange = useCallback((e) => setSubtitleBackgroundOpacity(e.target.value), []);
const handleSubtitleFontFamilyChange = useCallback((e) => setSubtitleFontFamily(e.target.value), []);
const handleSubtitlePreviewChange = useCallback((e) => setSubtitlePreview(e.target.value), []);
const handleAddCustomField = useCallback((customFieldName) => {
setCustomFields(oldCustomFields => {
const newCustomFields = {};
Expand All @@ -165,6 +188,15 @@ export default function SettingsDialog({anki, open, settings, onClose}) {
}), []);
const handlePreferMp3Change = useCallback((e) => setPreferMp3(e.target.checked), []);
const handleThemeTypeChange = useCallback((e) => setThemeType(e.target.value), []);
const subtitlePreviewStyles = useMemo(() => computeStyles({
subtitleColor: subtitleColor,
subtitleSize: subtitleSize,
subtitleOutlineThickness: subtitleOutlineThickness,
subtitleOutlineColor: subtitleOutlineColor,
subtitleBackgroundOpacity: subtitleBackgroundOpacity,
subtitleBackgroundColor: subtitleBackgroundColor,
subtitleFontFamily: subtitleFontFamily
}), [subtitleColor, subtitleSize, subtitleOutlineThickness, subtitleOutlineColor, subtitleBackgroundOpacity, subtitleBackgroundColor, subtitleFontFamily]);

useEffect(() => {
let canceled = false;
Expand Down Expand Up @@ -244,11 +276,13 @@ export default function SettingsDialog({anki, open, settings, onClose}) {
subtitleOutlineColor: subtitleOutlineColor,
subtitleBackgroundColor: subtitleBackgroundColor,
subtitleBackgroundOpacity: Number(subtitleBackgroundOpacity),
subtitleFontFamily: subtitleFontFamily,
subtitlePreview: subtitlePreview,
customAnkiFields: customFields,
preferMp3: preferMp3,
themeType: themeType
});
}, [onClose, ankiConnectUrl, deck, noteType, sentenceField, definitionField, audioField, imageField, wordField, sourceField, customFields, preferMp3, subtitleSize, subtitleColor, subtitleOutlineThickness, subtitleOutlineColor, subtitleBackgroundColor, subtitleBackgroundOpacity, themeType]);
}, [onClose, ankiConnectUrl, deck, noteType, sentenceField, definitionField, audioField, imageField, wordField, sourceField, customFields, preferMp3, subtitleSize, subtitleColor, subtitleOutlineThickness, subtitleOutlineColor, subtitleBackgroundColor, subtitleBackgroundOpacity, subtitleFontFamily, subtitlePreview, themeType]);

const customFieldInputs = Object.keys(customFields).map(customFieldName => {
return (
Expand Down Expand Up @@ -453,6 +487,25 @@ export default function SettingsDialog({anki, open, settings, onClose}) {
onChange={handleSubtitleBackgroundOpacityChange}
/>
</div>
<div className={classes.subtitleSetting}>
<TextField
type="text"
label="Subtitle Font Family"
placeholder="Inherited"
fullWidth
value={subtitleFontFamily}
color="secondary"
onChange={handleSubtitleFontFamilyChange}
/>
</div>
<div className={classes.subtitlePreview}>
<input
value={subtitlePreview}
className={classes.subtitlePreviewInput}
onChange={handleSubtitlePreviewChange}
style={subtitlePreviewStyles}
/>
</div>
</FormGroup>
</Grid>
<Grid item>
Expand Down
74 changes: 27 additions & 47 deletions client/src/components/VideoPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { isMobile } from 'react-device-detect';
import { makeStyles } from '@material-ui/core/styles';
import { useWindowSize } from '../hooks/useWindowSize';
import { arrayEquals } from '../services/Util'
import { arrayEquals, computeStyles } from '../services/Util'
import { KeyBindings } from '@project/common';
import Alert from './Alert';
import Clock from '../services/Clock';
Expand All @@ -28,48 +28,15 @@ const useStyles = makeStyles({
}
});

// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
function makeSubtitleStyles(subtitleSettings) {
return {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
}
}

function makeSubtitleStyles(
subtitleSize,
subtitleColor,
subtitleOutlineThickness,
subtitleOutlineColor,
subtitleBackgroundColor,
subtitleBackgroundOpacity
) {
const styles = {
position: 'absolute',
paddingLeft: 20,
paddingRight: 20,
bottom: 100,
textAlign: 'center',
color: subtitleColor,
fontSize: Number(subtitleSize),
...computeStyles(subtitleSettings)
};

if (subtitleOutlineThickness > 0) {
const thickness = subtitleOutlineThickness;
const color = subtitleOutlineColor;
styles['textShadow'] = `0 0 ${thickness}px ${color}, 0 0 ${thickness}px ${color}, 0 0 ${thickness}px ${color}, 0 0 ${thickness}px ${color}`;
}

if (subtitleBackgroundOpacity > 0) {
const opacity = subtitleBackgroundOpacity;
const color = subtitleBackgroundColor;
const {r, g, b} = hexToRgb(color);
styles['backgroundColor'] = `rgba(${r}, ${g}, ${b}, ${opacity})`
}

return styles;
}

function notifyReady(element, playerChannel, setAudioTracks, setSelectedAudioTrack) {
Expand Down Expand Up @@ -516,22 +483,35 @@ export default function VideoPlayer(props) {
}, [playerChannel, playing]);

const handleAlertClosed = useCallback(() => setAlertOpen(false), []);
const {subtitleSize, subtitleColor, subtitleOutlineThickness, subtitleOutlineColor, subtitleBackgroundColor, subtitleBackgroundOpacity} = settingsProvider.subtitleSettings;
const subtitleStyles = useMemo(() => makeSubtitleStyles(
const {
subtitleSize,
subtitleColor,
subtitleOutlineThickness,
subtitleOutlineColor,
subtitleBackgroundColor,
subtitleBackgroundOpacity
), [
subtitleSize,
subtitleColor,
subtitleOutlineThickness,
subtitleOutlineColor,
subtitleBackgroundColor,
subtitleBackgroundOpacity
]);
subtitleBackgroundOpacity,
subtitleFontFamily
} = settingsProvider.subtitleSettings;
const subtitleStyles = useMemo(
() => makeSubtitleStyles({
subtitleSize,
subtitleColor,
subtitleOutlineThickness,
subtitleOutlineColor,
subtitleBackgroundColor,
subtitleBackgroundOpacity,
subtitleFontFamily
}),
[
subtitleSize,
subtitleColor,
subtitleOutlineThickness,
subtitleOutlineColor,
subtitleBackgroundColor,
subtitleBackgroundOpacity,
subtitleFontFamily
]
);

return (
<div
Expand Down
25 changes: 25 additions & 0 deletions client/src/services/SettingsProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const defaultSubtitleOutlineThickness = 0;
const defaultSubtitleOutlineColor = "#000000";
const defaultSubtitleBackgroundColor = "#000000";
const defaultSubtitleBackgroundOpacity = 0.5;
const defaultSubtitleFontFamily = "";
const defaultSubtitlePreview = "アあ安"
const defaultVolume = 100;

const ankiConnectUrlKey = "ankiConnectUrl";
Expand All @@ -23,6 +25,8 @@ const subtitleOutlineThicknessKey = "subtitleOutlineThickness";
const subtitleOutlineColorKey = "subtitleOutlineColor";
const subtitleBackgroundColorKey = "subtitleBackgroundColor";
const subtitleBackgroundOpacityKey = "subtitleBackgroundOpacity";
const subtitleFontFamilyKey = "subtitleFontFamily";
const subtitlePreviewKey = "subtitlePreview";
const volumeKey = "volume";
const preferMp3Key = "preferMp3";
const themeTypeKey = "themeType";
Expand All @@ -47,6 +51,8 @@ export default class SettingsProvider {
subtitleOutlineColor: this.subtitleOutlineColor,
subtitleBackgroundColor: this.subtitleBackgroundColor,
subtitleBackgroundOpacity : this.subtitleBackgroundOpacity,
subtitleFontFamily: this.subtitleFontFamily,
subtitlePreview: this.subtitlePreview,
preferMp3: this.preferMp3,
themeType: this.themeType
};
Expand All @@ -69,6 +75,8 @@ export default class SettingsProvider {
this.subtitleOutlineColor = newSettings.subtitleOutlineColor;
this.subtitleBackgroundColor = newSettings.subtitleBackgroundColor;
this.subtitleBackgroundOpacity = newSettings.subtitleBackgroundOpacity;
this.subtitleFontFamily = newSettings.subtitleFontFamily;
this.subtitlePreview = newSettings.subtitlePreview;
this.customAnkiFields = newSettings.customAnkiFields;
this.preferMp3 = newSettings.preferMp3;
this.themeType = newSettings.themeType;
Expand All @@ -82,6 +90,7 @@ export default class SettingsProvider {
subtitleOutlineColor: this.subtitleOutlineColor,
subtitleBackgroundColor: this.subtitleBackgroundColor,
subtitleBackgroundOpacity : this.subtitleBackgroundOpacity,
subtitleFontFamily: this.subtitleFontFamily,
};
}

Expand Down Expand Up @@ -236,6 +245,22 @@ export default class SettingsProvider {
localStorage.setItem(subtitleBackgroundOpacityKey, subtitleBackgroundOpacity);
}

get subtitleFontFamily() {
return localStorage.getItem(subtitleFontFamilyKey) || defaultSubtitleFontFamily;
}

set subtitleFontFamily(subtitleFontFamily) {
localStorage.setItem(subtitleFontFamilyKey, subtitleFontFamily);
}

get subtitlePreview() {
return localStorage.getItem(subtitlePreviewKey) || defaultSubtitlePreview;
}

set subtitlePreview(subtitlePreview) {
localStorage.setItem(subtitlePreviewKey, subtitlePreview);
}

get volume() {
return localStorage.getItem(volumeKey) || defaultVolume;
}
Expand Down
44 changes: 44 additions & 0 deletions client/src/services/Util.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,47 @@ export function keysAreEqual(a, b) {

return true;
}

// https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
}
}

export function computeStyles({
subtitleColor,
subtitleSize,
subtitleOutlineThickness,
subtitleOutlineColor,
subtitleBackgroundOpacity,
subtitleBackgroundColor,
subtitleFontFamily
}) {
const styles = {
color: subtitleColor,
fontSize: Number(subtitleSize),
};

if (subtitleOutlineThickness > 0) {
const thickness = subtitleOutlineThickness;
const color = subtitleOutlineColor;
styles['textShadow'] = `0 0 ${thickness}px ${color}, 0 0 ${thickness}px ${color}, 0 0 ${thickness}px ${color}, 0 0 ${thickness}px ${color}`;
}

if (subtitleBackgroundOpacity > 0) {
const opacity = subtitleBackgroundOpacity;
const color = subtitleBackgroundColor;
const {r, g, b} = hexToRgb(color);
styles['backgroundColor'] = `rgba(${r}, ${g}, ${b}, ${opacity})`
}

if (subtitleFontFamily && subtitleFontFamily.length > 0) {
styles['fontFamily'] = subtitleFontFamily;
}

return styles;
}
6 changes: 6 additions & 0 deletions extension/src/services/SubtitleContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ export default class SubtitleContainer {
} else {
div.style.backgroundColor = "";
}

if (this.subtitleSettings.subtitleFontFamily && this.subtitleSettings.subtitleFontFamily.length > 0) {
div.style.fontFamily = this.subtitleSettings.subtitleFontFamily;
} else {
div.style.fontFamily = "";
}
}
}

Expand Down

0 comments on commit 069fa57

Please sign in to comment.