From 8178e78b8ed4553b98a61922ad0ded34eb43f881 Mon Sep 17 00:00:00 2001 From: Martin Vladic Date: Sat, 9 Nov 2024 17:43:50 +0100 Subject: [PATCH] Implemented: [LVGL] Include font fallback #620 --- packages/eez-studio-types/index.d.ts | 2 +- .../features/font/FontEditor.tsx | 108 +---- .../project-editor/features/font/Glyphs.tsx | 2 - .../features/font/font-extract/index.ts | 2 + .../features/font/font-extract/lvgl.ts | 6 +- .../project-editor/features/font/font.tsx | 381 +++++++++++++++--- .../lvgl-runtime/common/src/studio_api.cpp | 13 +- .../flow/runtime/lvgl_runtime_v8.3.wasm | Bin 1999882 -> 2000079 bytes .../flow/runtime/lvgl_runtime_v9.0.wasm | Bin 1961484 -> 1961681 bytes packages/project-editor/lvgl/build.ts | 5 +- packages/project-editor/lvgl/page-runtime.ts | 31 +- packages/project-editor/project/assets.ts | 26 ++ .../ui-components/PropertyGrid/Property.tsx | 28 +- 13 files changed, 415 insertions(+), 189 deletions(-) diff --git a/packages/eez-studio-types/index.d.ts b/packages/eez-studio-types/index.d.ts index 7adc3d22..5bcb028f 100644 --- a/packages/eez-studio-types/index.d.ts +++ b/packages/eez-studio-types/index.d.ts @@ -732,7 +732,7 @@ export interface IWasmFlowRuntime { _lvglGetObjRelY(obj: number): number; _lvglGetObjWidth(obj: number): number; _lvglGetObjHeight(obj: number): number; - _lvglLoadFont(font_file_path: number): number; + _lvglLoadFont(font_file_path: number, fallback_user_font: number, fallback_builtin_font: number): number; _lvglFreeFont(font_ptr: number): void; _lvglAddObjectFlowCallback(obj: number, filter: number, flow_state: number, component_index: number, output_or_property_index: number, userDataValuePtr: number): void; _lvglSetImgbuttonImageSrc(obj: number, statE: number, img_src: number): void; diff --git a/packages/project-editor/features/font/FontEditor.tsx b/packages/project-editor/features/font/FontEditor.tsx index a95c70ed..e23d3ecc 100644 --- a/packages/project-editor/features/font/FontEditor.tsx +++ b/packages/project-editor/features/font/FontEditor.tsx @@ -38,12 +38,9 @@ import { showGenericDialog } from "project-editor/core/util"; import { GlyphSelectFieldType } from "project-editor/features/font/GlyphSelectFieldType"; import { Font, - getEncodings, Glyph, GlyphSource, - removeDuplicates, - requiredRangesOrSymbols, - validateRanges + onEditGlyphs } from "project-editor/features/font/font"; import { @@ -65,8 +62,7 @@ export const FontEditor = observer( makeObservable(this, { onSelectGlyph: action.bound, onAddGlyph: action.bound, - onDeleteGlyph: action.bound, - onEditGlyphs: action.bound + onDeleteGlyph: action.bound }); } @@ -405,104 +401,6 @@ export const FontEditor = observer( } } - async onEditGlyphs() { - const result = await showGenericDialog(this.context, { - dialogDefinition: { - title: "Add or Remove Characters", - fields: [ - { - name: "ranges", - type: "string", - validators: [ - validateRanges, - requiredRangesOrSymbols - ], - formText: - "Ranges and/or characters to include. Example: 32-127,140,160-170,200,210-255" - }, - { - name: "symbols", - type: "string", - validators: [requiredRangesOrSymbols], - formText: - "List of characters to include. Example: abc01234äöüčćšđ" - } - ] - }, - values: { - ranges: this.font.lvglRanges, - symbols: this.font.lvglSymbols - } - }); - - try { - let relativeFilePath = this.font.source!.filePath; - let absoluteFilePath = - this.context.getAbsoluteFilePath(relativeFilePath); - - const encodingsBeforeDeduplication = getEncodings( - result.values.ranges - )!; - - const { encodings, symbols } = removeDuplicates( - encodingsBeforeDeduplication, - result.values.symbols - ); - result.values.symbols = symbols; - - const fontProperties = await extractFont({ - name: this.font.name, - absoluteFilePath, - embeddedFontFile: this.font.embeddedFontFile, - relativeFilePath, - renderingEngine: "LVGL", - bpp: this.font.bpp, - size: this.font.source!.size!, - threshold: 128, - createGlyphs: true, - encodings, - symbols: result.values.symbols, - createBlankGlyphs: false, - doNotAddGlyphIfNotFound: false, - lvglVersion: - this.context.project.settings.general.lvglVersion, - lvglInclude: this.context.project.settings.build.lvglInclude - }); - - this.context.updateObject(this.font, { - lvglBinFile: fontProperties.lvglBinFile, - lvglSourceFile: fontProperties.lvglSourceFile, - lvglGlyphs: { - encodings, - symbols: result.values.symbols - } - }); - - this.font.loadLvglGlyphs(this.context); - - notification.info( - `Font ${this.font.name} successfully modified.` - ); - } catch (err) { - let errorMessage; - if (err) { - if (err.message) { - errorMessage = err.message; - } else { - errorMessage = err.toString(); - } - } - - if (errorMessage) { - notification.error( - `Modifying ${Font.name} failed: ${errorMessage}!` - ); - } else { - notification.error(`Modifying ${Font.name} failed!`); - } - } - } - onCreateShadow = async () => { const result = await dialog.showOpenDialog(getCurrentWindow(), { properties: ["openFile"], @@ -678,7 +576,7 @@ export const FontEditor = observer( onAddGlyph={this.onAddGlyph} onEditGlyphs={ this.context.projectTypeTraits.isLVGL - ? this.onEditGlyphs + ? () => onEditGlyphs(this.font) : undefined } onDeleteGlyph={this.onDeleteGlyph} diff --git a/packages/project-editor/features/font/Glyphs.tsx b/packages/project-editor/features/font/Glyphs.tsx index 8834e296..eee933d0 100644 --- a/packages/project-editor/features/font/Glyphs.tsx +++ b/packages/project-editor/features/font/Glyphs.tsx @@ -229,8 +229,6 @@ export const GlyphComponent = observer( refDiv = React.createRef(); setCanvas() { - console.log("setCanvas"); - const { glyph } = this.props; const canvas = document.createElement("canvas"); diff --git a/packages/project-editor/features/font/font-extract/index.ts b/packages/project-editor/features/font/font-extract/index.ts index 3d21287f..9fb0ed50 100644 --- a/packages/project-editor/features/font/font-extract/index.ts +++ b/packages/project-editor/features/font/font-extract/index.ts @@ -70,6 +70,8 @@ export interface Params { getAllGlyphs?: boolean; lvglVersion?: string; lvglInclude?: string; + opts_string?: string; + lv_fallback?: string; } export interface IFontExtract { diff --git a/packages/project-editor/features/font/font-extract/lvgl.ts b/packages/project-editor/features/font/font-extract/lvgl.ts index e7b0c68b..111b3038 100644 --- a/packages/project-editor/features/font/font-extract/lvgl.ts +++ b/packages/project-editor/features/font/font-extract/lvgl.ts @@ -63,7 +63,11 @@ export class ExtractFont implements IFontExtract { lv_include: this.params.lvglInclude, no_kerning: false, no_prefilter: false, - fast_kerning: false + fast_kerning: false, + opts_string: this.params.opts_string, + lv_fallback: this.params.lv_fallback + ? this.params.lv_fallback + : undefined }; // wait for !extractBusy diff --git a/packages/project-editor/features/font/font.tsx b/packages/project-editor/features/font/font.tsx index 485b284b..0e32da75 100644 --- a/packages/project-editor/features/font/font.tsx +++ b/packages/project-editor/features/font/font.tsx @@ -939,6 +939,56 @@ registerClass("FontSource", FontSource); //////////////////////////////////////////////////////////////////////////////// +const EmptySpacePropertyGridUI = observer( + class EmptySpacePropertyGridUI extends React.Component { + render() { + if (this.props.objects.length > 1) { + return null; + } + + return ( + + + +
+ + + ); + } + } +); + +const EditGlyphsPropertyGridUI = observer( + class EditGlyphsPropertyGridUI extends React.Component { + render() { + if (this.props.objects.length > 1) { + return null; + } + + return ( + + + +
+ +
+ + + ); + } + } +); + +//////////////////////////////////////////////////////////////////////////////// + const ExportFontFilePropertyGridUI = observer( class ExportFontFilePropertyGridUI extends React.Component { export = async () => { @@ -1006,12 +1056,8 @@ const ChangeBitsPerPixel = observer( let absoluteFilePath = projectStore.getAbsoluteFilePath(relativeFilePath); - const encodingsBeforeDeduplication = getEncodings( - font.lvglRanges - )!; - - const { encodings, symbols } = removeDuplicates( - encodingsBeforeDeduplication, + const { encodings, symbols } = getLvglEncodingsAndSymbols( + font.lvglRanges, font.lvglSymbols ); @@ -1036,12 +1082,7 @@ const ChangeBitsPerPixel = observer( projectStore.updateObject(font, { bpp: result.values.bpp, - lvglBinFile: fontProperties.lvglBinFile, - lvglSourceFile: fontProperties.lvglSourceFile, - lvglGlyphs: { - encodings, - symbols - } + lvglBinFile: fontProperties.lvglBinFile }); font.loadLvglGlyphs(projectStore); @@ -1072,15 +1113,20 @@ const ChangeBitsPerPixel = observer( return null; } return ( -
- -
+ + + +
+ +
+ + ); } } @@ -1104,12 +1150,10 @@ export class Font extends EezObject { screenOrientation: string; alwaysBuild: boolean; - lvglGlyphs: { - encodings: EncodingRange[]; - symbols: string; - }; + lvglRanges: string; + lvglSymbols: string; lvglBinFile?: string; - lvglSourceFile?: string; + lvglFallbackFont: string; constructor() { super(); @@ -1137,9 +1181,10 @@ export class Font extends EezObject { glyphs: observable, screenOrientation: observable, alwaysBuild: observable, - lvglGlyphs: observable, + lvglRanges: observable, + lvglSymbols: observable, lvglBinFile: observable, - lvglSourceFile: observable + lvglFallbackFont: observable }); } @@ -1204,7 +1249,7 @@ export class Font extends EezObject { name: "changeBitsPerPixel", type: PropertyType.Any, computed: true, - propertyGridRowComponent: ChangeBitsPerPixel, + propertyGridFullRowComponent: ChangeBitsPerPixel, skipSearch: true, disabled: object => !isLVGLProject(object) }, @@ -1263,34 +1308,52 @@ export class Font extends EezObject { type: PropertyType.Any, hideInPropertyGrid: true }, + { + name: "emptySpace", + type: PropertyType.Any, + computed: true, + propertyGridFullRowComponent: EmptySpacePropertyGridUI, + skipSearch: true, + disabled: object => !isLVGLProject(object) + }, { name: "lvglRanges", displayName: "Ranges", type: PropertyType.String, - computed: true, + readOnlyInPropertyGrid: true, disabled: object => !isLVGLProject(object) }, { name: "lvglSymbols", displayName: "Symbols", type: PropertyType.String, + readOnlyInPropertyGrid: true, + disabled: object => !isLVGLProject(object) + }, + { + name: "editGlyphs", + type: PropertyType.Any, computed: true, + propertyGridFullRowComponent: EditGlyphsPropertyGridUI, + skipSearch: true, disabled: object => !isLVGLProject(object) }, { - name: "lvglBinFile", + name: "lvglFallbackFont", + displayName: "Fallback font", type: PropertyType.String, - hideInPropertyGrid: true, - skipSearch: true + disabled: object => !isLVGLProject(object), + formText: object => + "E.g. lv_font_montserrat_24 or ui_font_my_custom_font" }, { - name: "lvglSourceFile", + name: "lvglBinFile", type: PropertyType.String, hideInPropertyGrid: true, skipSearch: true }, { - name: "customUI", + name: "exportFontFile", type: PropertyType.Any, computed: true, propertyGridRowComponent: ExportFontFilePropertyGridUI, @@ -1303,12 +1366,54 @@ export class Font extends EezObject { propertiesPanelLabel: (font: Font) => { return `Font: ${font.name}`; }, - beforeLoadHook: (font: Font, fontJs: Partial) => { + beforeLoadHook: ( + font: Font, + fontJs: Partial & { + lvglSourceFile?: string; + lvglGlyphs?: { + encodings: EncodingRange[]; + symbols: string; + }; + } + ) => { if ((fontJs as any).renderEngine != undefined) { fontJs.renderingEngine = (fontJs as any).renderEngine; } else if (fontJs.renderingEngine == undefined) { fontJs.renderingEngine = "freetype"; } + + if ( + !fontJs.lvglRanges && + !fontJs.lvglSymbols && + fontJs.lvglGlyphs + ) { + function getLvglRanges() { + const encodings = fontJs.lvglGlyphs!.encodings; + if (encodings && encodings.length > 0) { + return encodings + .map(encoding => + encoding.from != encoding.to + ? `${encoding.from} - ${encoding.to}` + : `${encoding.from}` + ) + .join(","); + } + + return ""; + } + + function getLvglSymbols() { + return fontJs.lvglGlyphs!.symbols ?? ""; + } + + fontJs.lvglRanges = getLvglRanges(); + fontJs.lvglSymbols = getLvglSymbols(); + delete fontJs.lvglGlyphs; + } + + if (fontJs.lvglSourceFile) { + delete fontJs.lvglSourceFile; + } }, afterLoadHook: (font: Font, project) => { try { @@ -1632,9 +1737,15 @@ export class Font extends EezObject { } async loadLvglGlyphs(projectStore: ProjectStore) { - if (!this.lvglGlyphs) { + if (!this.lvglRanges && !this.lvglSymbols) { return; } + + const { encodings, symbols } = getLvglEncodingsAndSymbols( + this.lvglRanges, + this.lvglSymbols + ); + const fontProperties = await extractFont({ name: this.name, absoluteFilePath: projectStore.getAbsoluteFilePath( @@ -1647,8 +1758,8 @@ export class Font extends EezObject { size: this.source!.size!, threshold: this.threshold, createGlyphs: true, - encodings: this.lvglGlyphs.encodings, - symbols: this.lvglGlyphs.symbols, + encodings, + symbols, createBlankGlyphs: false, doNotAddGlyphIfNotFound: false, getAllGlyphs: true @@ -1669,7 +1780,7 @@ export class Font extends EezObject { } async migrateLvglFont(projectStore: ProjectStore) { - if (!this.lvglGlyphs) { + if (!this.lvglRanges && !this.lvglSymbols) { return; } @@ -1677,6 +1788,11 @@ export class Font extends EezObject { return; } + const { encodings, symbols } = getLvglEncodingsAndSymbols( + this.lvglRanges, + this.lvglSymbols + ); + // migrate from assets folder to the embedded asset const absoluteFilePath = projectStore.getAbsoluteFilePath( @@ -1692,8 +1808,8 @@ export class Font extends EezObject { size: this.source!.size!, threshold: this.threshold, createGlyphs: true, - encodings: this.lvglGlyphs.encodings, - symbols: this.lvglGlyphs.symbols, + encodings, + symbols, createBlankGlyphs: false, doNotAddGlyphIfNotFound: false, getAllGlyphs: true @@ -1703,7 +1819,6 @@ export class Font extends EezObject { this.source!.filePath = path.basename(absoluteFilePath); this.embeddedFontFile = fontProperties.embeddedFontFile; this.lvglBinFile = fontProperties.lvglBinFile; - this.lvglSourceFile = fontProperties.lvglSourceFile; projectStore.setModified(Symbol()); }); } @@ -1718,6 +1833,11 @@ export class Font extends EezObject { return; } + const { encodings, symbols } = getLvglEncodingsAndSymbols( + this.lvglRanges, + this.lvglSymbols + ); + const fontProperties = await extractFont({ name: name || this.name, absoluteFilePath: projectStore.getAbsoluteFilePath( @@ -1730,8 +1850,8 @@ export class Font extends EezObject { size: this.source!.size!, threshold: this.threshold, createGlyphs: true, - encodings: this.lvglGlyphs.encodings, - symbols: this.lvglGlyphs.symbols, + encodings, + symbols, createBlankGlyphs: false, doNotAddGlyphIfNotFound: false, getAllGlyphs: true, @@ -1740,28 +1860,69 @@ export class Font extends EezObject { }); projectStore.updateObject(this, { - lvglBinFile: fontProperties.lvglBinFile, - lvglSourceFile: fontProperties.lvglSourceFile + lvglBinFile: fontProperties.lvglBinFile }); } - get lvglRanges() { - const encodings = this.lvglGlyphs?.encodings; - if (encodings && encodings.length > 0) { - return encodings - .map(encoding => - encoding.from != encoding.to - ? `${encoding.from} - ${encoding.to}` - : `${encoding.from}` - ) - .join(","); + async getLvglSourceFile() { + if (!this.embeddedFontFile) { + return undefined; } - return ""; - } + const projectStore = ProjectEditor.getProjectStore(this); + + const { encodings, symbols } = getLvglEncodingsAndSymbols( + this.lvglRanges, + this.lvglSymbols + ); + + let opts_string = ""; + + // + opts_string += `--bpp ${this.bpp} --size ${this.source! + .size!} --no-compress --font ${this.source!.filePath}`; + + const lvglSymbols = this.lvglSymbols.replace(/\s/g, ""); + if (lvglSymbols) { + opts_string += ` --symbols ${lvglSymbols}`; + } + + const lvglRanges = this.lvglRanges.replace(/\s/g, ""); + if (lvglRanges) { + opts_string += ` --range ${lvglRanges}`; + } + + if (this.lvglFallbackFont) { + opts_string += ` --lv-fallback ${this.lvglFallbackFont}`; + } + + opts_string += " --format lvgl"; + + // + const fontProperties = await extractFont({ + name: this.name, + absoluteFilePath: projectStore.getAbsoluteFilePath( + this.source!.filePath + ), + embeddedFontFile: this.embeddedFontFile, + relativeFilePath: this.source!.filePath, + renderingEngine: this.renderingEngine, + bpp: this.bpp, + size: this.source!.size!, + threshold: this.threshold, + createGlyphs: true, + encodings, + symbols, + createBlankGlyphs: false, + doNotAddGlyphIfNotFound: false, + getAllGlyphs: true, + lvglVersion: projectStore.project.settings.general.lvglVersion, + lvglInclude: projectStore.project.settings.build.lvglInclude, + opts_string, + lv_fallback: this.lvglFallbackFont + }); - get lvglSymbols() { - return this.lvglGlyphs?.symbols ?? ""; + return fontProperties.lvglSourceFile; } } @@ -1877,6 +2038,14 @@ export function removeDuplicates(encodings: EncodingRange[], symbols: string) { return { encodings, symbols: symbolsDeduplicated }; } +export function getLvglEncodingsAndSymbols( + lvglRanges: string, + lvglSymbols: string +) { + const encodingsBeforeDeduplication = getEncodings(lvglRanges)!; + return removeDuplicates(encodingsBeforeDeduplication, lvglSymbols); +} + //////////////////////////////////////////////////////////////////////////////// export function rebuildLvglFonts( @@ -1891,6 +2060,94 @@ export function rebuildLvglFonts( //////////////////////////////////////////////////////////////////////////////// +export async function onEditGlyphs(font: Font) { + const projectStore = ProjectEditor.getProjectStore(font); + + const result = await showGenericDialog(projectStore, { + dialogDefinition: { + title: "Add or Remove Characters", + fields: [ + { + name: "ranges", + type: "string", + validators: [validateRanges, requiredRangesOrSymbols], + formText: + "Ranges and/or characters to include. Example: 32-127,140,160-170,200,210-255" + }, + { + name: "symbols", + type: "string", + validators: [requiredRangesOrSymbols], + formText: + "List of characters to include. Example: abc01234äöüčćšđ" + } + ] + }, + values: { + ranges: font.lvglRanges, + symbols: font.lvglSymbols + } + }); + + try { + let relativeFilePath = font.source!.filePath; + let absoluteFilePath = + projectStore.getAbsoluteFilePath(relativeFilePath); + + const { encodings, symbols } = getLvglEncodingsAndSymbols( + result.values.ranges, + result.values.symbols + ); + + const fontProperties = await extractFont({ + name: font.name, + absoluteFilePath, + embeddedFontFile: font.embeddedFontFile, + relativeFilePath, + renderingEngine: "LVGL", + bpp: font.bpp, + size: font.source!.size!, + threshold: 128, + createGlyphs: true, + encodings, + symbols, + createBlankGlyphs: false, + doNotAddGlyphIfNotFound: false, + lvglVersion: projectStore.project.settings.general.lvglVersion, + lvglInclude: projectStore.project.settings.build.lvglInclude + }); + + projectStore.updateObject(font, { + lvglBinFile: fontProperties.lvglBinFile, + lvglRanges: result.values.ranges, + lvglSymbols: result.values.symbols + }); + + font.loadLvglGlyphs(projectStore); + + notification.info(`Font ${font.name} successfully modified.`); + } catch (err) { + let errorMessage; + if (err) { + if (err.message) { + errorMessage = err.message; + } else { + errorMessage = err.toString(); + } + } + + if (errorMessage) { + notification.error( + `Modifying ${font.name} failed: ${errorMessage}!` + ); + } else { + notification.error(`Modifying ${font.name} failed!`); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + const feature: ProjectEditorFeature = { name: "eezstudio-project-feature-font", version: "0.1.0", @@ -1917,7 +2174,7 @@ const feature: ProjectEditorFeature = { }, toJsHook: (jsObject: Project, project: Project) => { jsObject.fonts?.forEach(font => { - if (font.lvglGlyphs) { + if (font.lvglRanges || font.lvglSymbols) { font.glyphs = []; } else { font.glyphs.forEach(glyph => { diff --git a/packages/project-editor/flow/runtime/cpp/lvgl-runtime/common/src/studio_api.cpp b/packages/project-editor/flow/runtime/cpp/lvgl-runtime/common/src/studio_api.cpp index 97a20958..56a5c126 100644 --- a/packages/project-editor/flow/runtime/cpp/lvgl-runtime/common/src/studio_api.cpp +++ b/packages/project-editor/flow/runtime/cpp/lvgl-runtime/common/src/studio_api.cpp @@ -843,12 +843,19 @@ EM_PORT_API(int16_t) lvglGetObjHeight(lv_obj_t *obj) { return lv_obj_get_height(obj); } -EM_PORT_API(lv_font_t *) lvglLoadFont(const char *font_file_path) { +EM_PORT_API(lv_font_t *) lvglLoadFont(const char *font_file_path, lv_font_t *fallback_user_font, int32_t fallback_builtin_font) { + lv_font_t *font; #if LVGL_VERSION_MAJOR >= 9 - return lv_binfont_create(font_file_path); + font = lv_binfont_create(font_file_path); #else - return lv_font_load(font_file_path); + font = lv_font_load(font_file_path); #endif + if (fallback_user_font) { + font->fallback = fallback_user_font; + } else if (fallback_builtin_font != -1) { + font->fallback = BUILT_IN_FONTS[fallback_builtin_font]; + } + return font; } EM_PORT_API(void) lvglFreeFont(lv_font_t *font) { diff --git a/packages/project-editor/flow/runtime/lvgl_runtime_v8.3.wasm b/packages/project-editor/flow/runtime/lvgl_runtime_v8.3.wasm index 37faa2887d6963f67755b7995b7d7d87530ce981..513bbda55cca75baed2ff4d566c39b2c7825896d 100644 GIT binary patch delta 303 zcmW;Fy-EW?6b0a!an~K)O?IL&?*HBkV!~o4q?PTpu(OkGQws|L6|_jSvykcZ6$Fbk zzJp@p16XM58+el9YtH4Idq1DP-%VeBv~*s7b<65&e%n;Drjm=MQs>9cjgqB34dtPE z?65~Jd1EEW9Vu;_KY!m|o$2mCupvX?SoH(xFOLrQ>T7%XQiAhkQE)*8t%VW0$|j6o!djDeVe0tyeof6{xM zv&$(fzDiV{KV{eQ>W)S`qeW@7JnZuQR;2PC-QGUVH;*87-bz+kW1S5)sn}wh0YgUY cu*)9%9B{}HV~#oDlnG~?bHQcl?dlW$0RcQr?f?J) diff --git a/packages/project-editor/flow/runtime/lvgl_runtime_v9.0.wasm b/packages/project-editor/flow/runtime/lvgl_runtime_v9.0.wasm index ec2bc5267723ee0c9216bc7a1057f487209da5e0..a874926e51c7245fdcf7fa72d348e8c27f30a369 100644 GIT binary patch delta 308 zcmW;Fy-vbV7zW^T4*j7XXbWoXFa5rRMAFTL!9mg(cL#3*CI=G+2U8_sF>yRIcVObc z;NmSXlIR7vdI7{2hG%}_DHqO?X@H`xz~>I{#-v298yTZSmhn5_X&%8^^>7g)7?$?(6KnKzkaVc zsTHRbQ~JOeC6vuLr+lOY7yF*z0S&0YkUs1PF6)p9-!x+;9ttW0eQ7pt@_f0QDrkWP pHaOry8$37y4ISu$4*`VGgFYO?37kR%XAnaIDP)kZchkaO`~$yCU4{Sv delta 163 zcmWN=y$ymu7=U5#1Jn~mQNjO$j@%ONLq|nnb;0BsJ55-_#Dc<-3G8JFC-8le=hIiW zZ}s%F8oYdc$>dSj);McaYmKDmd;iPj9X4g4k|uMKyRHy=^cir-5y#Y=FyxdoMx1lO Xm`kp>X2O&kZn@*02Oit5I7Qb##-~G5 diff --git a/packages/project-editor/lvgl/build.ts b/packages/project-editor/lvgl/build.ts index 37503ef4..603039bf 100644 --- a/packages/project-editor/lvgl/build.ts +++ b/packages/project-editor/lvgl/build.ts @@ -2074,7 +2074,8 @@ ${source}`; await Promise.all( this.fonts.map(font => (async () => { - if (font.lvglSourceFile) { + const lvglSourceFile = await font.getLvglSourceFile(); + if (lvglSourceFile) { const output = getName( "ui_font_", font.name || "", @@ -2093,7 +2094,7 @@ ${source}`; : "") + output + ".c", - font.lvglSourceFile + lvglSourceFile ); } catch (err) { this.project._store.outputSectionsStore.write( diff --git a/packages/project-editor/lvgl/page-runtime.ts b/packages/project-editor/lvgl/page-runtime.ts index a7f0c36d..4813769c 100644 --- a/packages/project-editor/lvgl/page-runtime.ts +++ b/packages/project-editor/lvgl/page-runtime.ts @@ -30,7 +30,8 @@ import type { import { Project, ProjectType, - findBitmap + findBitmap, + findFontByVarName } from "project-editor/project/project"; import { getClassesDerivedFrom, @@ -51,6 +52,7 @@ import { LV_ANIM_OFF } from "project-editor/lvgl/lvgl-constants"; import { + BUILT_IN_FONTS, pad_bottom_property_info, pad_left_property_info, pad_right_property_info, @@ -248,7 +250,32 @@ export abstract class LVGLPageRuntime { const fontPathStr = this.wasm.allocateUTF8("M:" + fontMemPtr); - let fontPtr = this.wasm._lvglLoadFont(fontPathStr); + let fallbackUserFont = 0; + let fallbackBuiltinFont = -1; + if (font.lvglFallbackFont) { + if (font.lvglFallbackFont.startsWith("ui_font_")) { + const fallbackFont = findFontByVarName( + this.project, + font.lvglFallbackFont + ); + + if (fallbackFont) { + fallbackUserFont = this.getFontPtr(fallbackFont); + } + } else if (font.lvglFallbackFont.startsWith("lv_font_")) { + fallbackBuiltinFont = BUILT_IN_FONTS.indexOf( + font.lvglFallbackFont + .slice("lv_font_".length) + .toUpperCase() + ); + } + } + + let fontPtr = this.wasm._lvglLoadFont( + fontPathStr, + fallbackUserFont, + fallbackBuiltinFont + ); this.wasm._free(fontPathStr); diff --git a/packages/project-editor/project/assets.ts b/packages/project-editor/project/assets.ts index e694ea1a..d8c49ab2 100644 --- a/packages/project-editor/project/assets.ts +++ b/packages/project-editor/project/assets.ts @@ -110,6 +110,32 @@ export function findFont(project: Project, name: string | undefined) { return findAsset(project, "fonts", name); } +export function findFontByVarName( + project: Project, + varName: string | undefined +) { + const allAssets = project._assets.maps["name"].allAssets; + for (const key of allAssets.keys()) { + if (key.startsWith("fonts/")) { + const fontName = key.slice("fonts/".length); + const fontVarName = getName( + "ui_font_", + fontName, + NamingConvention.UnderscoreLowerCase + ); + if (fontVarName == varName) { + const objects = allAssets.get(key); + if (objects && objects.length === 1) { + return objects[0] as Font; + } + return undefined; + } + } + } + + return undefined; +} + export function getAssetFullName( object: T, separator?: string diff --git a/packages/project-editor/ui-components/PropertyGrid/Property.tsx b/packages/project-editor/ui-components/PropertyGrid/Property.tsx index b84587f0..278f2317 100644 --- a/packages/project-editor/ui-components/PropertyGrid/Property.tsx +++ b/packages/project-editor/ui-components/PropertyGrid/Property.tsx @@ -889,18 +889,24 @@ export const Property = observer( ); } else { + const formText = getFormText(this.props); return ( - (this.input = ref)} - type="text" - className={classNames("form-control", { - "font-monospace": propertyInfo.monospaceFont - })} - value={this.displayValue} - onChange={this.onChange} - onKeyDown={this.onKeyDown} - readOnly={readOnly || propertyInfo.computed} - /> + <> + (this.input = ref)} + type="text" + className={classNames("form-control", { + "font-monospace": propertyInfo.monospaceFont + })} + value={this.displayValue} + onChange={this.onChange} + onKeyDown={this.onKeyDown} + readOnly={readOnly || propertyInfo.computed} + /> + {formText && ( +
{formText}
+ )} + ); } } else if (propertyInfo.type === PropertyType.Number) {