diff --git a/server/src/emoji-panel/EmojiPanel.cpp b/server/src/emoji-panel/EmojiPanel.cpp index e746f1cc..cfa6ec39 100644 --- a/server/src/emoji-panel/EmojiPanel.cpp +++ b/server/src/emoji-panel/EmojiPanel.cpp @@ -26,6 +26,7 @@ constexpr float kPanelScale = 2.0f / 3.0f; constexpr float kHeaderHeight = 58.0f; constexpr float kNavTop = 66.0f; constexpr float kNavHeight = 58.0f; +constexpr float kNavTitleFontSize = 24.0f; constexpr float kSearchTop = 142.0f; constexpr float kSearchHeight = 52.0f; constexpr float kContentTop = 218.0f; @@ -48,7 +49,6 @@ constexpr UINT_PTR kClipboardPollTimerId = 44; constexpr UINT kClipboardPollMs = 400; constexpr float kClipboardRowHeight = 80.0f; constexpr float kClipboardRowGap = 8.0f; -constexpr float kClipboardTitleFontSize = 24.0f; constexpr float kClipboardHintFontSize = 24.0f; constexpr float kClipboardButtonFontSize = 22.0f; constexpr float kClipboardItemFontSize = 22.0f; @@ -2235,21 +2235,21 @@ void EmojiPanel::Render(DeviceResources &resources) } else { + // Keep these in step with the tab labels in emoji_panel_icons.cpp, + // which the tabs fall back to when no icon font has the glyph. const wchar_t *title = L""; if (page_ == Page::Sticker) - title = L"Sticker"; + title = L"贴纸"; else if (page_ == Page::Gif) title = L"GIF"; else if (page_ == Page::Kaomoji) - title = L"Kaomoji"; + title = L"颜文字"; else if (page_ == Page::Symbols) - title = L"Symbols"; + title = L"符号"; else if (page_ == Page::Clipboard) title = L"剪贴板"; - DrawText(resources, title, {bounds_.x + 70.0f, bounds_.y + kNavTop, 280.0f, kNavHeight}, - page_ == Page::Clipboard ? kClipboardTitleFontSize : 18.0f, text, - page_ == Page::Clipboard ? CjkUiFont() : L"Segoe UI", DWRITE_TEXT_ALIGNMENT_LEADING, - DWRITE_FONT_WEIGHT_SEMI_BOLD); + DrawText(resources, title, {bounds_.x + 70.0f, bounds_.y + kNavTop, 280.0f, kNavHeight}, kNavTitleFontSize, + text, CjkUiFont(), DWRITE_TEXT_ALIGNMENT_LEADING, DWRITE_FONT_WEIGHT_SEMI_BOLD); } } else diff --git a/ui/src/Fonts.cpp b/ui/src/Fonts.cpp index dcf5160c..cb75d873 100644 --- a/ui/src/Fonts.cpp +++ b/ui/src/Fonts.cpp @@ -31,7 +31,11 @@ bool FontFamilyExists(IDWriteFactory *factory, const wchar_t *name) IDWriteFactory *SharedFactory() { + // Called from several window threads, so the lazy creation needs a lock of + // its own; the factory is never reset, so the returned pointer stays valid. + static std::mutex mutex; static ComPtr factory; + std::lock_guard lock(mutex); if (!factory) { DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), @@ -81,23 +85,27 @@ struct IconFontEntry ComPtr font; }; -// Installed icon fonts, in preference order. Built once per process, so a font -// installed while the process runs is only picked up after a restart. +// Installed icon fonts, in preference order. Kept once the probe finds one, so +// a font installed while the process runs is only picked up after a restart. An +// empty result is not kept: a transient DirectWrite failure on the first call +// would otherwise degrade every icon to its text label for the whole process. +// Callers must hold the ResolveIconGlyph mutex, its only caller. const std::vector &IconFonts() { - static const std::vector entries = []() { - std::vector list; - IDWriteFactory *factory = SharedFactory(); - if (ComPtr fluent = FindFont(factory, kFluentIconsFamily)) - { - list.push_back({kFluentIconsFamily, false, std::move(fluent)}); - } - if (ComPtr mdl2 = FindFont(factory, kMdl2IconsFamily)) - { - list.push_back({kMdl2IconsFamily, true, std::move(mdl2)}); - } - return list; - }(); + static std::vector entries; + if (!entries.empty()) + { + return entries; + } + IDWriteFactory *factory = SharedFactory(); + if (ComPtr fluent = FindFont(factory, kFluentIconsFamily)) + { + entries.push_back({kFluentIconsFamily, false, std::move(fluent)}); + } + if (ComPtr mdl2 = FindFont(factory, kMdl2IconsFamily)) + { + entries.push_back({kMdl2IconsFamily, true, std::move(mdl2)}); + } return entries; } @@ -139,7 +147,14 @@ IconGlyph ResolveIconGlyph(wchar_t fluentCodepoint, wchar_t mdl2Codepoint) } IconGlyph resolved; - for (const IconFontEntry &entry : IconFonts()) + const std::vector &fonts = IconFonts(); + if (fonts.empty()) + { + // No icon font found yet. Report the text fallback but do not cache it, + // so a later call can still pick one up once DirectWrite recovers. + return resolved; + } + for (const IconFontEntry &entry : fonts) { const wchar_t codepoint = (entry.isMdl2 && mdl2Codepoint != 0) ? mdl2Codepoint : fluentCodepoint; if (FontHasCodepoint(entry.font.Get(), codepoint))