IJPL-187512 [terminal] Implement font fallback

Because some fonts are missing some characters
used in popular shell themes, we need to have
a fallback font.

Since a fallback font can only be provided to the editor
through the FontPreferences API, we can't use
TerminalFontSettings directly anymore.
Still, we need it for TerminalOptionsConfigurable
because using FontPreferences there would be messy.

TerminalFontSettingsService already inherits
getFontPreferences from AppFontOptions,
but it only has a fallback family if it's not null
when the state is loaded (see copyState).
But we need it to be always available
except when it's not specified at all AND
the main font is also the default one.

Therefore, we override getFontPreferences
to ensure that a fallback is always provided
if necessary.

(cherry picked from commit 905fe55100ea8358fd9bea7679ccff84629da996)

IJ-CR-162755

GitOrigin-RevId: 77941a16d8c54e6cebd90cda1fe80b5bfe300570
This commit is contained in:
Sergei Tachenov
2025-05-12 14:48:31 +03:00
committed by intellij-monorepo-bot
parent 8374dcae78
commit 1fbd9e83d1
4 changed files with 30 additions and 3 deletions

View File

@@ -85,6 +85,7 @@ settings.environment.variables=&Environment variables:
settings.shell.path=&Shell path:
settings.tab.name=Default &tab name:
settings.font.name=&Font:
settings.fallback.font.name=Fallback:
settings.font.size=Size:
settings.line.height=Line height:
settings.column.width=Column width:

View File

@@ -49,6 +49,7 @@ class TerminalFontSettingsService : AppFontOptions<TerminalFontSettingsState>()
val preferences = fontPreferences
return TerminalFontSettings(
fontFamily = preferences.fontFamily,
fallbackFontFamily = preferences.effectiveFontFamilies.getOrNull(1) ?: FontPreferences.DEFAULT_FONT_NAME,
fontSize = TerminalFontSize.ofFloat(preferences.getSize2D(preferences.fontFamily)),
lineSpacing = TerminalLineSpacing.ofFloat(preferences.lineSpacing),
columnSpacing = columnSpacing,
@@ -73,6 +74,10 @@ class TerminalFontSettingsService : AppFontOptions<TerminalFontSettingsState>()
val boldSubFamily = FontFamilyService.getRecommendedBoldSubFamily(settings.fontFamily, regularSubFamily)
newPreferences.boldSubFamily = boldSubFamily
newPreferences.setFontSize(settings.fontFamily, settings.fontSize.floatValue)
if (settings.fallbackFontFamily != settings.fontFamily) {
newPreferences.addFontFamily(settings.fallbackFontFamily)
newPreferences.setFontSize(settings.fallbackFontFamily, settings.fontSize.floatValue)
}
newPreferences.lineSpacing = settings.lineSpacing.floatValue
// then apply the settings that aren't a part of FontPreferences
columnSpacing = settings.columnSpacing
@@ -84,6 +89,16 @@ class TerminalFontSettingsService : AppFontOptions<TerminalFontSettingsState>()
}
}
override fun getFontPreferences(): FontPreferences {
val result = super.getFontPreferences()
// The default fallback family isn't serialized, but we need to explicitly add it anyway if we're not using the default font.
// Otherwise, if the explicitly set font is missing some characters, the default font won't be used as a fallback.
if (result.effectiveFontFamilies.size == 1 && result.fontFamily != FontPreferences.DEFAULT_FONT_NAME) {
(result as FontPreferencesImpl).register(FontPreferences.DEFAULT_FONT_NAME, result.getSize2D(result.fontFamily))
}
return result
}
override fun createFontState(fontPreferences: FontPreferences): TerminalFontSettingsState =
TerminalFontSettingsState(fontPreferences).also {
it.COLUMN_SPACING = columnSpacing.floatValue
@@ -272,6 +287,7 @@ interface TerminalFontSettingsListener {
internal data class TerminalFontSettings(
val fontFamily: String,
val fallbackFontFamily: String,
val fontSize: TerminalFontSize,
val lineSpacing: TerminalLineSpacing,
val columnSpacing: TerminalColumnSpacing,
@@ -282,6 +298,7 @@ class TerminalFontSettingsState: AppEditorFontOptions.PersistentFontPreferences
@Suppress("unused") // for serialization
constructor(): super() {
LINE_SPACING = DEFAULT_TERMINAL_LINE_SPACING.floatValue // to ensure that values different from OUR default are saved
SECONDARY_FONT_FAMILY = FontPreferences.DEFAULT_FONT_NAME
}
constructor(fontPreferences: FontPreferences): super(fontPreferences)

View File

@@ -150,6 +150,16 @@ internal class TerminalOptionsConfigurable(private val project: Project) : Bound
setter = { fontSettings = fontSettings.copy(fontFamily = it) },
).toNullableProperty()
)
cell(fontComboBox())
.label(message("settings.fallback.font.name"))
.bind(
componentGet = { comboBox -> comboBox.fontName },
componentSet = {comboBox, value -> comboBox.fontName = value },
MutableProperty(
getter = { fontSettings.fallbackFontFamily },
setter = { fontSettings = fontSettings.copy(fallbackFontFamily = it) },
).toNullableProperty()
)
}
row {

View File

@@ -56,6 +56,7 @@ import com.jediterm.terminal.ui.AwtTransformers
import com.jediterm.terminal.util.CharUtils
import org.intellij.lang.annotations.MagicConstant
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.plugins.terminal.TerminalFontSettingsService
import org.jetbrains.plugins.terminal.block.output.TextAttributesProvider
import org.jetbrains.plugins.terminal.block.output.TextStyleAdapter
import org.jetbrains.plugins.terminal.block.session.TerminalModel
@@ -294,9 +295,7 @@ object TerminalUiUtils {
fun EditorImpl.applyFontSettings(newSettings: JBTerminalSystemSettingsProviderBase) {
colorsScheme.apply {
editorFontName = newSettings.terminalFont.fontName
setEditorFontSize(newSettings.terminalFont.size2D)
lineSpacing = newSettings.lineSpacing
fontPreferences = newSettings.fontPreferences
}
settings.apply {
characterGridWidthMultiplier = newSettings.columnSpacing