diff --git a/src/App.scss b/src/App.scss
index 6b8875bf..9225075b 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -375,7 +375,6 @@ nav {
user-select: none;
color: var(--text);
border: none;
- border-radius: var(--roundness);
cursor: pointer;
background-color: var(--background);
box-shadow: var(--shadow);
@@ -511,7 +510,6 @@ textarea {
--_padding: 0.5em;
border: none;
- border-radius: var(--roundness);
background-color: var(--background);
color: var(--text);
box-shadow: var(--shadow);
@@ -617,6 +615,17 @@ textarea {
}
}
+.input,
+.button-default {
+ --_roundness-left: var(--roundness);
+ --_roundness-right: var(--roundness);
+
+ border-top-left-radius: var(--_roundness-left);
+ border-bottom-left-radius: var(--_roundness-left);
+ border-top-right-radius: var(--_roundness-right);
+ border-bottom-right-radius: var(--_roundness-right);
+}
+
// Textareas should have stock line-height + vertical padding instead of huge line-height
textarea.input {
padding: var(--_padding);
@@ -662,22 +671,20 @@ option {
display: inline-flex;
vertical-align: middle;
- button,
- .button-dropdown {
+ > * {
+ --_roundness-left: 0;
+ --_roundness-right: 0;
+
position: relative;
flex: 1 1 auto;
+ }
- &:not(:last-child),
- &:not(:last-child) .button-default {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- }
+ > *:first-child {
+ --_roundness-left: var(--roundness);
+ }
- &:not(:first-child),
- &:not(:first-child) .button-default {
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
- }
+ > *:last-child {
+ --_roundness-right: var(--roundness);
}
}
diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js
index 52c3e70a..1e33338f 100644
--- a/src/components/font_control/font_control.js
+++ b/src/components/font_control/font_control.js
@@ -1,13 +1,19 @@
-import { set } from 'lodash'
+import { set, clone } from 'lodash'
import Select from '../select/select.vue'
import Checkbox from 'src/components/checkbox/checkbox.vue'
import Popover from 'src/components/popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
-import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'
+import {
+ faExclamationTriangle,
+ faKeyboard,
+ faFont
+} from '@fortawesome/free-solid-svg-icons'
library.add(
- faExclamationTriangle
+ faExclamationTriangle,
+ faKeyboard,
+ faFont
)
const PRESET_FONTS = new Set(['serif', 'sans-serif', 'monospace', 'inherit'])
@@ -24,20 +30,38 @@ export default {
emits: ['update:modelValue'],
data () {
return {
- localValue: this.modelValue,
- customFamily: '',
+ manualEntry: true,
+ localValue: clone(this.modelValue),
+ familyCustomLocal: null,
availableOptions: [
this.noInherit ? '' : 'inherit',
- 'local',
- ...(this.options || []),
'serif',
+ 'sans-serif',
'monospace',
- 'sans-serif'
+ 'local',
+ ...(this.options || [])
].filter(_ => _)
}
},
beforeUpdate () {
- this.localValue = this.modelValue
+ this.localValue = clone(this.modelValue)
+ if (this.familyCustomLocal === null && !this.isInvalidFamily(this.modelValue?.family)) {
+ this.familyCustomLocal = this.modelValue?.family
+ }
+ },
+ methods: {
+ lookupLocalFonts () {
+ if (!this.fontsList) {
+ this.$store.dispatch('queryLocalFonts')
+ }
+ this.toggleManualEntry()
+ },
+ isInvalidFamily (value) {
+ return PRESET_FONTS.has(value) || (value === '')
+ },
+ toggleManualEntry () {
+ this.manualEntry = !this.manualEntry
+ }
},
computed: {
present () {
@@ -46,32 +70,39 @@ export default {
defaultValue () {
return this.localValue || this.fallback || {}
},
+ fontsListCapable () {
+ return this.$store.state.interface.browserSupport.localFonts
+ },
+ fontsList () {
+ return this.$store.state.interface.localFonts
+ },
family: {
get () {
return this.defaultValue.family
},
set (v) {
+ this.familyCustomLocal = ''
set(this.localValue, 'family', v)
this.$emit('update:modelValue', this.localValue)
}
},
familyCustom: {
get () {
- return this.customFamily
+ return this.familyCustomLocal
},
set (v) {
- this.customFamily = v
- if (!PRESET_FONTS.has(this.customFamily)) {
+ this.familyCustomLocal = v
+ if (!this.isInvalidFamily(v)) {
set(this.localValue, 'family', v)
- this.$emit('update:modelValue', this.customFamily)
+ this.$emit('update:modelValue', this.localValue)
}
}
},
invalidCustom () {
- return PRESET_FONTS.has(this.customFamily)
+ return this.isInvalidFamily(this.familyCustomLocal)
},
isCustom () {
- return !PRESET_FONTS.has(this.family)
+ return !PRESET_FONTS.has(this.defaultValue.family)
},
preset: {
get () {
diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue
index e73862a0..f1b126be 100644
--- a/src/components/font_control/font_control.vue
+++ b/src/components/font_control/font_control.vue
@@ -10,15 +10,6 @@
>
{{ label }}
- {{ ' ' }}
-
-
-- + {{ $t('settings.style.themes3.define') }} + +
++ + + {{ ' ' }} + + + + + + + + .font-control { - input.custom-font { - min-width: 12em; + .custom-font { + min-width: 20em; + max-width: 20em; } } diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss index 5e633120..4cb37c1e 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss @@ -10,6 +10,10 @@ margin-right: 0.25em; } + .btn-group .btn { + margin: 0; + } + .style-control { display: flex; align-items: baseline; diff --git a/src/i18n/en.json b/src/i18n/en.json index bd18c6ef..4b216165 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -746,14 +746,19 @@ "more_settings": "More settings", "style": { "themes3": { - "define": "Include in theme", + "define": "Override", "font": { "local": "Local font (must be installed on computer)", "serif": "Serif (browser default)", "sans-serif": "Sans-serif (browser default)", "monospace": "Monospace (browser default)", "inherit": "Same as parent component", - "invalid_custom_reserved": "This is a reserved font name, it will not be saved as custom font - use dropdown instead" + "invalid_custom_reserved": "Empty or reserved font name, it will not be saved as custom font - use dropdown instead", + "font_list_unavailable": "Couldn't get locally installed fonts: {error}", + "lookup_local_fonts": "Load list of fonts installed on this computer", + "enter_manually": "Enter font name family manually", + "entry": "Font's {fontFamily}", + "select": "Select local font" } }, "switcher": { diff --git a/src/modules/interface.js b/src/modules/interface.js index e21b4204..bee503c5 100644 --- a/src/modules/interface.js +++ b/src/modules/interface.js @@ -1,4 +1,5 @@ const defaultState = { + localFonts: null, themeApplied: false, temporaryChangesTimeoutId: null, // used for temporary options that revert after a timeout temporaryChangesConfirm: () => {}, // used for applying temporary options @@ -17,7 +18,8 @@ const defaultState = { cssFilter: window.CSS && window.CSS.supports && ( window.CSS.supports('filter', 'drop-shadow(0 0)') || window.CSS.supports('-webkit-filter', 'drop-shadow(0 0)') - ) + ), + localFonts: typeof window.queryLocalFonts === 'function' }, layoutType: 'normal', globalNotices: [], @@ -104,6 +106,10 @@ const interfaceMod = { }, setLastTimeline (state, value) { state.lastTimeline = value + }, + setFontsList (state, value) { + console.log(value) + state.localFonts = new Set(value.map(font => font.family)) } }, actions: { @@ -178,6 +184,22 @@ const interfaceMod = { commit('setLayoutType', wideLayout ? 'wide' : normalOrMobile) } }, + queryLocalFonts ({ commit, dispatch }) { + window + .queryLocalFonts() + .then((fonts) => { + commit('setFontsList', fonts) + }) + .catch((e) => { + dispatch('pushGlobalNotice', { + messageKey: 'settings.style.themes3.font.font_list_unavailable', + messageArgs: { + error: e + }, + level: 'error' + }) + }) + }, setLastTimeline ({ commit }, value) { commit('setLastTimeline', value) }