Skip to content

Commit

Permalink
Font styles (#1080)
Browse files Browse the repository at this point in the history
- Custom font package for styles
- Fixed some font not properly loading
- Fixed fonts sometimes not loading
  • Loading branch information
vassbo authored Dec 5, 2024
1 parent 1245732 commit 5fe36db
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 49 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,12 +175,12 @@
"axios": "^1.7.8",
"chord-transposer": "^3.0.9",
"cross-env": "^7.0.3",
"css-fonts": "^1.0.8",
"electron-store": "^8.0.1",
"electron-updater": "^6.3.1",
"exif": "^0.6.0",
"express": "^4.17.2",
"follow-redirects": "^1.15.2",
"font-list": "^1.4.5",
"genius-lyrics": "^4.4.7",
"grandiose": "vassbo/grandiose#9857c8e",
"jzz": "^1.8.7",
Expand Down
1 change: 1 addition & 0 deletions public/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,7 @@
"restart_for_change": "You have to restart the program for the change to take effect!",
"font": "Font",
"font_family": "Font family",
"font_style": "Font style",
"font_size": "Font size",
"border_radius": "Border radius",
"colors": "Colors",
Expand Down
2 changes: 1 addition & 1 deletion src/electron/data/import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,4 @@ async function decodeProto(filePath: string, fileContent: Buffer | null = null)
async function checkSpecial(file: any) {
if (file.extension === "pro") return await decodeProto("", file.content)
return
}
}
10 changes: 6 additions & 4 deletions src/electron/utils/responses.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// ----- FreeShow -----
// Respond to messages from the frontend

import getFonts from "css-fonts"
import { app, BrowserWindow, desktopCapturer, DesktopCapturerSource, Display, screen, shell, systemPreferences } from "electron"
import { getFonts } from "font-list"
import { machineIdSync } from "node-machine-id"
import os from "os"
import path from "path"
Expand Down Expand Up @@ -188,9 +188,11 @@ export const openURL = (url: string) => {

// GET_SYSTEM_FONTS
function loadFonts(data: any) {
getFonts({ disableQuoting: true })
.then((fonts: string[]) => toApp(MAIN, { channel: "GET_SYSTEM_FONTS", data: { ...data, fonts } }))
.catch((err: any) => console.log(err))
loadFontsAsync(data)
}
async function loadFontsAsync(data: any) {
const fonts = await getFonts()
toApp(MAIN, { channel: "GET_SYSTEM_FONTS", data: { ...data, fonts } })
}

// SEARCH_LYRICS
Expand Down
15 changes: 14 additions & 1 deletion src/frontend/components/edit/scripts/textStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,21 @@ export function addStyleString(oldStyle: string, style: any[]): string {
array.forEach((s, i) => {
if (s.split(":")[0].trim() === style[0] || !s.length) array.splice(i, 1)
})

// remove font if changing family
if (style[0] === "font-family") {
array = array.filter((a) => !a.includes("font:"))
}

// add new style
if (style[1] !== null) array.push(style.join(":"))
// add font to start so any font-size will override this
if (style[0] === "font") {
array.unshift(style.join(":"))

// place any font-family at the start (before font, just so the dropdown knows the font)
let fontFamilyIndex = array.findIndex((a) => a.includes("font-family"))
array.unshift(array.splice(fontFamilyIndex, 1)[0])
} else if (style[1] !== null) array.push(style.join(":"))

let newStyle: string = array.join(";")
if (newStyle.slice(-1) !== ";") newStyle += ";"
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/components/edit/tools/BoxStyle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@
// WIP shouldn't have fixed values
$: if (id === "text" && box?.edit?.default) {
box.edit.default[0].styleValue = getStyles(style)["font"] || ""
box.edit.default[4].hidden = !item?.auto
}
$: if (id === "text" && box?.edit?.style) {
Expand Down Expand Up @@ -169,6 +170,8 @@
}
}
// $: fontMetadata = $fontData[styles["font-family"]] || []
function getMirrorValues() {
if (!item?.mirror || !box) return
Expand Down
2 changes: 2 additions & 0 deletions src/frontend/components/edit/tools/EditValues.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,12 @@
class="customInput"
{...input.values || {}}
{value}
fontStyleValue={input.styleValue || ""}
disabled={input.disabled && (item?.[input.disabled] || edits[section].find((a) => a.id === input.disabled)?.value)}
enableNoColor={input.enableNoColor}
disableHold
on:click={(e) => valueChange(e, input)}
on:fontStyle={(e) => valueChange(e, { ...input, key: "font" })}
on:input={(e) => valueChange(e, input, true)}
on:change={(e) => valueChange(e, input)}
/>
Expand Down
5 changes: 3 additions & 2 deletions src/frontend/components/inputs/Dropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
let nextScrollTimeout: any = null
function wheel(e: any) {
if (disabled || nextScrollTimeout) return
if (disabled || arrow || nextScrollTimeout) return
e.preventDefault()
let index = options.findIndex((a) => (activeId ? a.id === activeId : a.name === value))
Expand Down Expand Up @@ -66,7 +66,7 @@
<svelte:window on:mousedown={mousedown} />

<div class:disabled class:center class:flags bind:this={self} class="dropdownElem {$$props.class || ''}" style="position: relative;{$$props.style || ''}">
<button {id} {title} on:click={() => (disabled ? null : (active = !active))} on:wheel={wheel}>
<button style={arrow ? "justify-content: center;" : ""} {id} {title} on:click={() => (disabled ? null : (active = !active))} on:wheel={wheel}>
{#if arrow}
<Icon id="expand" size={1.2} white />
{:else}
Expand All @@ -78,6 +78,7 @@
{#each options as option}
<span
id={formatId(option.name)}
style={option.style || ""}
on:click={() => {
if (disabled) return
active = false
Expand Down
119 changes: 85 additions & 34 deletions src/frontend/components/inputs/FontDropdown.svelte
Original file line number Diff line number Diff line change
@@ -1,48 +1,57 @@
<script lang="ts">
import type { Family } from "css-fonts"
import { createEventDispatcher, onMount } from "svelte"
import { slide } from "svelte/transition"
import { MAIN } from "../../../types/Channels"
import { systemFonts } from "../../stores"
import { dictionary, systemFonts } from "../../stores"
import { awaitRequest } from "../../utils/request"
import { formatSearch } from "../../utils/search"
import { removeDuplicates } from "../helpers/array"
import Dropdown from "./Dropdown.svelte"
export let value: string
export let fontStyleValue: string = ""
export let system: boolean = false
const dispatch = createEventDispatcher()
$: value = value.replaceAll("'", "")
let fonts: string[] = [
"CMGSans",
"Fantasy",
"Helvetica",
// "Monaco",
"Monospace",
// "sans-serif",
let fonts: Family[] = [
{ family: "CMGSans", default: 0, fonts: [{ name: "CMGSans", path: "", style: "", css: "font: 1em 'CMGSans'" }] },
// {family: "Arial"},
// "Helvetica",
// "Fantasy",
// "Monospace",
]
onMount(loadFonts)
async function loadFonts() {
if ($systemFonts.length) addFonts($systemFonts)
else {
let fonts: string[] = (await awaitRequest(MAIN, "GET_SYSTEM_FONTS"))?.fonts
if (!fonts) return
const dispatch = createEventDispatcher()
function setFont(family: string) {
dispatch("click", family ? "'" + family + "'" : "")
}
systemFonts.set(fonts)
addFonts(fonts)
}
onMount(() => {
if ($systemFonts.length) addFonts($systemFonts)
else loadSystemFonts()
})
async function loadSystemFonts() {
let fonts: Family[] = (await awaitRequest(MAIN, "GET_SYSTEM_FONTS"))?.fonts
if (!fonts) return
systemFonts.set(fonts)
addFonts(fonts)
}
$: if (active && !$systemFonts.length) loadFonts()
function addFonts(newFonts: string[]) {
function addFonts(newFonts: Family[]) {
// join and remove duplicates
fonts = removeDuplicates([...fonts, ...newFonts])
// sort
fonts = fonts.sort((a, b) => a.localeCompare(b))
fonts = fonts.sort((a, b) => a.family.localeCompare(b.family))
// add default app font
if (system) fonts = ["", ...fonts]
if (system) {
const noFont = { family: "", default: 0, fonts: [{ name: "", path: "", style: "", css: "" }] }
fonts = [noFont, ...fonts]
}
}
export let value: string
let active: boolean = false
let self: HTMLDivElement
Expand All @@ -51,10 +60,10 @@
if (nextScrollTimeout) return
e.preventDefault()
let index = fonts.findIndex((a) => a === value)
let index = fonts.findIndex((a) => a.family === value)
if (e.deltaY > 0) index = Math.min(fonts.length - 1, index + 1)
else index = Math.max(0, index - 1)
dispatch("click", fonts[index])
setFont(fonts[index].family)
// don't start timeout if scrolling with mouse
if (e.deltaY >= 100 || e.deltaY <= -100) return
Expand Down Expand Up @@ -91,11 +100,31 @@
searchValue = formatSearch(searchValue + e.key, true)
// scroll to first match
let firstMatch = fonts.find((a) => formatSearch(a, true).slice(0, searchValue.length).includes(searchValue))
if (!firstMatch) firstMatch = fonts.find((a) => formatSearch(a, true).includes(searchValue))
if (firstMatch) scrollToActive(firstMatch)
let firstMatch = fonts.find((a) => formatSearch(a.family, true).slice(0, searchValue.length).includes(searchValue))
if (!firstMatch) firstMatch = fonts.find((a) => formatSearch(a.family, true).includes(searchValue))
if (firstMatch) scrollToActive(firstMatch.family)
}
}
let activeFont: Family | null = null
$: activeFont = fonts.find((a) => a.family === value) || null
// FONT STYLE
// remove styles that can already be changed
// "Regular"
const commonStyles = ["Bold", "Italic", "Bold Italic"]
$: fontStyles = (activeFont?.fonts || []).filter((a) => !commonStyles.includes(a.style))
$: fontDataOptions = fontStyles.map((a) => ({
name: a.style,
// id: a.name,
style: a.css,
data: a.css
?.replace(/^font:\s*(.*);$/, "$1")
.replace("1em", "100px")
.trim(),
}))
</script>

<svelte:window
Expand All @@ -108,25 +137,40 @@
/>

<div bind:this={self} class="dropdownElem" title={value || ""} style={$$props.style || ""}>
<button style="font-family: {value};" on:click={() => (active = !active)} on:wheel={wheel}>
<button style={activeFont?.fonts[activeFont.default]?.css || `font-family: ${activeFont};`} on:click={() => (active = !active)} on:wheel={wheel}>
<p>{value || ""}</p>
</button>

<!-- FONT STYLE -->
{#if !system && fontStyles.length > 1}
<Dropdown
arrow
value={fontDataOptions.find((a, i) => (fontStyleValue ? a.data === fontStyleValue : i === fonts.find((a) => a.family === value)?.default))?.name || ""}
style="min-width: 40px !important;"
options={fontDataOptions}
title={$dictionary.settings?.font_style}
on:click={(e) => {
dispatch("fontStyle", e.detail?.data)
}}
/>
{/if}

{#if active}
<div class="dropdown" transition:slide={{ duration: 200 }}>
{#each fonts as font}
<span
id={formatId(font)}
id={formatId(font.family)}
on:click={() => {
active = false
// allow dropdown to close before updating, so svelte visual bug don't duplicate inputs on close transition in boxstyle edit etc.
setTimeout(() => {
dispatch("click", font)
setFont(font.family)
}, 50)
}}
class:active={font === value}
style="font-family: {font};"
class:active={font.family === value}
style={font.fonts[font.default]?.css || `font-family: ${font.family};`}
>
<p>{font || ""}</p>
<p>{font.family || ""}</p>
</span>
{/each}
</div>
Expand All @@ -137,6 +181,13 @@
.dropdownElem {
position: relative;
/* display: grid; */
display: flex;
}
.dropdownElem :global(.arrow) {
width: 130px !important;
text-transform: capitalize;
}
div {
Expand Down
3 changes: 2 additions & 1 deletion src/frontend/stores.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ----- FreeShow -----
// Here are all the global app variables

import type { Family } from "css-fonts"
import { Writable, writable } from "svelte/store"
import type { Bible } from "../types/Bible"
import type { Event } from "../types/Calendar"
Expand Down Expand Up @@ -125,7 +126,7 @@ export const notFound: Writable<any> = writable({ show: [], bible: [] })
export const toastMessages: Writable<string[]> = writable([])
export const alertMessage: Writable<string> = writable("")
export const popupData: Writable<any> = writable({})
export const systemFonts: Writable<string[]> = writable([])
export const systemFonts: Writable<Family[]> = writable([])
export const previousShow: Writable<any> = writable(null)
export const projectToolSize: Writable<number> = writable(150)
export const forceClock: Writable<boolean> = writable(false)
Expand Down
6 changes: 1 addition & 5 deletions src/frontend/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ export function receive(ID: ValidChannels, channels: any, id: string = "") {
let currentlyAwaiting: string[] = []
export async function awaitRequest(ID: ValidChannels, channel: string, data: any = null) {
let listenerId = ID + "_" + channel
if (channel === "GET_SYSTEM_FONTS") {
if (currentlyAwaiting.includes(listenerId)) return
} else {
listenerId += uid(5)
}
listenerId += uid(5)
currentlyAwaiting.push(listenerId)

send(ID, [channel], { ...data, listenerId })
Expand Down
1 change: 1 addition & 0 deletions src/types/Main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface Option {
extra?: string
data?: any
id?: string
style?: string // css style for the item
}

export interface NumberObject {
Expand Down

0 comments on commit 5fe36db

Please sign in to comment.