From f8c82595023fa0ae5e7a43c887afffe309342ff6 Mon Sep 17 00:00:00 2001 From: xiaoqidun Date: Wed, 28 Oct 2026 16:43:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=AD=97=E4=BD=93=E5=A4=84=E7=90=86):=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AD=97=E4=BD=93=E9=A2=84=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E4=B8=8E=E6=90=9C=E7=B4=A2=E9=80=89=E4=B8=AD=E8=A1=8C=E4=B8=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/webui/ofdgo.js | 34 ++++++++++++++++++---------------- assets/webui/ofdgo_font.js | 30 +++++++++++++++++++++--------- 2 files changed, 39 insertions(+), 25 deletions(-) diff --git a/assets/webui/ofdgo.js b/assets/webui/ofdgo.js index 762ad58..8a75e12 100644 --- a/assets/webui/ofdgo.js +++ b/assets/webui/ofdgo.js @@ -4363,17 +4363,18 @@ function editorFonts(item) { return [...embedded.values(), ...fonts]; } -async function loadEditorFonts() { +async function loadEditorFonts(requestPermission = true) { const tab = documentTab(), target = tab.state; const tasks = []; - if (!state.fontCatalogLoading && fontManager.canReadLocal() && !fontManager.catalogLoaded && fontManager.permission !== "denied") { + if (!state.fontCatalogLoading && fontManager.canReadLocal() && !fontManager.catalogLoaded && fontManager.permission !== "denied" + && (requestPermission || fontManager.permission === "granted")) { target.fontCatalogLoading = true; tasks.push((async () => { try { await fontManager.queryLocal(); - if (isForegroundDocument(tab)) refreshEditorFonts(); + refreshEditorFonts(); } catch (err) { - if (isForegroundDocument(tab)) setStatus(err?.name === "NotAllowedError" ? "系统字体未授权" : String(err.message || err)); + if (requestPermission && isForegroundDocument(tab)) setStatus(err?.name === "NotAllowedError" ? "系统字体未授权" : String(err.message || err)); } finally { target.fontCatalogLoading = false; } @@ -4381,12 +4382,15 @@ async function loadEditorFonts() { } if (!state.fontFacesLoading && fontManager.userFonts.some(font => font.enabled && !font.faces)) { target.fontFacesLoading = true; + const wasmSeq = target.wasmSeq; + const current = () => !tab.closed && !target.wasmExited && target.wasmSeq === wasmSeq; tasks.push((async () => { try { - await fontManager.loadFaces(data => callTabWASM(tab, "ofdgoFontFaces", data)); - if (isForegroundDocument(tab)) refreshEditorFonts(); + await fontManager.loadFaces(data => callTabWASM(tab, "ofdgoFontFaces", data), current); + refreshEditorFonts(); } finally { target.fontFacesLoading = false; + if (!current() && state.ready) loadEditorFonts(false); } })()); } @@ -4394,11 +4398,7 @@ async function loadEditorFonts() { } function refreshEditorFonts() { - const open = fontPicker.open; - const selected = state.textFonts[Number(fontPicker.value)]; - const query = el.textFont.value === (selected?.fullName || selected?.name || "") ? "" : el.textFont.value; - updateTextFonts(canvasEditor.selected, true); - if (open && !el.textFont.disabled) { if (query) el.textFont.value = query; fontPicker.show(query, false); } + updateTextFonts(canvasEditor.selected, true, true); } async function readTextFont(font, tab = documentTab()) { @@ -4413,14 +4413,14 @@ async function readTextFont(font, tab = documentTab()) { return result.bytes; } -function updateTextFonts(item, refresh = false) { +function updateTextFonts(item, refresh = false, preserve = false) { item = selectedText(item); const rich = canvasEditor.input?.rich; if (rich) { const format = rich.state(), id = format.font; const key = `rich:${id ?? "mixed"}:${rich.faces.size}`; if (refresh || state.textFontID !== key) { - if (document.activeElement === el.textFont && fontPicker.open) return; + if (!preserve && document.activeElement === el.textFont && fontPicker.open) return; state.textFontID = key; state.textFonts = editorFonts(null); for (const font of rich.faces.keys()) { @@ -4429,7 +4429,7 @@ function updateTextFonts(item, refresh = false) { name: state.doc?.fonts?.find(value => value.id === font)?.fontName || (font === rich.editing.item.font ? rich.editing.item.fontName : "") || font, embedded: !rich.imports.has(font) }), richKey: font }); } - fontPicker.setFonts(state.textFonts, id === undefined ? null : state.textFonts.find(font => font.richKey === id)); + fontPicker.setFonts(state.textFonts, id === undefined ? null : state.textFonts.find(font => font.richKey === id), preserve); el.textFont.placeholder = id === undefined ? "混合" : ""; } return; @@ -4446,7 +4446,7 @@ function updateTextFonts(item, refresh = false) { state.textFonts.unshift(selected); } const mixed = (item?.items || item?.richText) && !id; - fontPicker.setFonts(state.textFonts, mixed ? null : selected || state.textFonts[0]); + fontPicker.setFonts(state.textFonts, mixed ? null : selected || state.textFonts[0], preserve); el.textFont.placeholder = mixed ? "混合" : ""; if (!id && !item?.items && !item?.richText) state.textDefaults.fontChoice = state.textFonts[Number(fontPicker.value)]; } @@ -5262,6 +5262,7 @@ async function boot() { updateControls(); updateLocalFontButton(); await fontManager.refreshPermission(); + loadEditorFonts(false); if (openSeq === state.openSeq) { setStatus(fontsRestored ? STATUS.ready : "字体读取失败"); setBusy(false); @@ -6281,7 +6282,8 @@ async function changeFont(font, changes) { } function renderFontList() { - updateTextFonts(canvasEditor.selected, true); + refreshEditorFonts(); + if (state.ready) loadEditorFonts(false); el.fontList.replaceChildren(); const fonts = fontManager.records(); if (!fonts.length) { diff --git a/assets/webui/ofdgo_font.js b/assets/webui/ofdgo_font.js index 9380b01..3f847ca 100644 --- a/assets/webui/ofdgo_font.js +++ b/assets/webui/ofdgo_font.js @@ -5,13 +5,14 @@ export class FontPicker { Object.assign(this, { input, toggle, list, onChange, onOpen, fonts: [], value: "", active: -1, open: false }); input.addEventListener("input", () => this.show(input.value)); input.addEventListener("focus", () => input.select()); - input.addEventListener("click", () => { if (!this.open) this.show(); }); + input.addEventListener("pointerdown", event => { if (event.button === 0 && !this.open) event.preventDefault(); }); + input.addEventListener("click", () => { if (!this.open) { input.focus(); input.select(); this.show(); } }); input.addEventListener("keydown", event => this.keyDown(event)); input.addEventListener("blur", () => { this.commitExact(); this.close(); }); toggle.addEventListener("pointerdown", event => event.preventDefault()); toggle.addEventListener("click", () => { if (this.open) this.close(); - else { input.focus(); this.show(); } + else { input.focus(); input.select(); this.show(); } }); list.addEventListener("pointerdown", event => { if (event.target.closest(".font-option")) event.preventDefault(); @@ -23,8 +24,9 @@ export class FontPicker { window.addEventListener("resize", () => this.close()); } - setFonts(fonts, selected) { - this.close(); + setFonts(fonts, selected, preserve = false) { + const open = preserve && this.open; + if (!open) this.close(); this.fonts = fonts; this.names = new Map(); for (const font of fonts) { @@ -33,7 +35,12 @@ export class FontPicker { } const key = font => font?.richKey || font?.id || font?.postscriptName; this.value = !fonts.length ? "" : selected === null ? "-1" : String(Math.max(0, fonts.findIndex(font => key(font) === key(selected)))); - this.restore(); + if (open) { + const active = this.matches[this.active]?.font; + this.show(this.query, false); + const index = active ? this.matches.findIndex(({ font }) => !font.disabled && key(font) === key(active)) : -1; + if (index >= 0) this.activate(index); + } else this.restore(); } setDisabled(disabled) { @@ -43,7 +50,11 @@ export class FontPicker { restore() { const font = this.fonts[Number(this.value)]; - this.input.value = font?.fullName || font?.name || ""; + const value = font?.fullName || font?.name || ""; + if (this.input.value !== value) { + this.input.value = value; + if (document.activeElement === this.input) this.input.select(); + } this.input.scrollLeft = 0; this.input.title = font ? [this.input.value, this.sourceLabel(font)].filter(Boolean).join(" · ") : ""; } @@ -56,6 +67,7 @@ export class FontPicker { show(query = "", load = true) { if (this.input.disabled) return; + this.query = query; const normalize = value => value.normalize("NFKC").toLocaleLowerCase().replace(/\s+/g, " ").trim(); const terms = normalize(query).split(" ").filter(Boolean); this.matches = this.fonts.map((font, index) => ({ font, index })).filter(({ font }) => { @@ -314,12 +326,12 @@ export class FontManager { }), ...this.catalog]; } - async loadFaces(inspect) { - while (true) { + async loadFaces(inspect, current = () => true) { + while (current()) { const file = this.userFonts.find(font => font.enabled && !font.faces); if (!file) return; try { file.faces = await inspect(await this.read(file)); } - catch { file.faces = []; } + catch { if (!current()) return; file.faces = []; } } }