Displays all installed font families with an optional "monospaced only" + * filter. A live preview renders sample text including Claude Code TUI symbols + * (⏵ ◐) so the user can immediately see whether the chosen font has adequate + * Unicode coverage. + * + *
Usage: + *
{@code
+ * TerminalFontChooserDialog dlg = new TerminalFontChooserDialog(parent, fontName, fontSize);
+ * dlg.setVisible(true);
+ * if (dlg.isConfirmed()) {
+ * String name = dlg.getFontName(); // "" = Auto
+ * int size = dlg.getFontSize();
+ * }
+ * }
+ */
+final class TerminalFontChooserDialog extends JDialog {
+
+ private static final String AUTO_LABEL = "Auto (detect best available)";
+ private static final String PREVIEW_TEXT = "AaBbCc 0O1Il → ⏵ ◐ ╔═╗ ░▒▓";
+
+ private final DefaultListModelFont selection: JediTerm uses a single {@link Font} with no + * per-glyph fallback, so the chosen font must cover all Unicode characters + * used by the Claude Code TUI (U+23F5 prompt marker, U+25D0 spinner, + * box-drawing, dingbats). Font resolution is delegated to + * {@link io.github.nbclaudecodegui.settings.ClaudeCodePreferences#resolveTerminalFontName()}, + * which prefers Adwaita Mono — the only widely-available FOSS monospace font + * with full coverage of those characters, pre-installed on most Linux GNOME desktops. */ final class NetBeansSettingsProvider extends DefaultSettingsProvider { + @Override + public float getTerminalFontSize() { + return ClaudeCodePreferences.getTerminalFontSize(); + } + + @Override + public Font getTerminalFont() { + return new Font(ClaudeCodePreferences.resolveTerminalFontName(), + Font.PLAIN, (int) getTerminalFontSize()); + } + @Override public TerminalColor getDefaultForeground() { Color fg = UIManager.getColor("EditorPane.foreground"); diff --git a/src/test/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferencesTest.java b/src/test/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferencesTest.java index a520ea71..8274762c 100644 --- a/src/test/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferencesTest.java +++ b/src/test/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferencesTest.java @@ -21,6 +21,8 @@ void tearDown() { ClaudeCodePreferences.setSessionDockMode(ClaudeCodePreferences.DEFAULT_SESSION_DOCK_MODE); ClaudeCodePreferences.setMarkdownPreviewDockMode(ClaudeCodePreferences.DEFAULT_MARKDOWN_PREVIEW_DOCK_MODE); ClaudeCodePreferences.setFileDiffDockMode(ClaudeCodePreferences.DEFAULT_FILE_DIFF_DOCK_MODE); + ClaudeCodePreferences.setTerminalFontName(ClaudeCodePreferences.DEFAULT_TERMINAL_FONT_NAME); + ClaudeCodePreferences.setTerminalFontSize(ClaudeCodePreferences.DEFAULT_TERMINAL_FONT_SIZE); } @Test @@ -155,4 +157,46 @@ void fileDiffDockModeNullFallsBackToDefault() { ClaudeCodePreferences.setFileDiffDockMode(null); assertEquals(DockMode.EDITOR, ClaudeCodePreferences.getFileDiffDockMode()); } + + @Test + void terminalFontNameDefaultIsEmpty() { + assertEquals("", ClaudeCodePreferences.DEFAULT_TERMINAL_FONT_NAME); + assertEquals("", ClaudeCodePreferences.getTerminalFontName()); + } + + @Test + void terminalFontNameRoundTrip() { + ClaudeCodePreferences.setTerminalFontName("Adwaita Mono"); + assertEquals("Adwaita Mono", ClaudeCodePreferences.getTerminalFontName()); + } + + @Test + void terminalFontNameNullStoredAsEmpty() { + ClaudeCodePreferences.setTerminalFontName(null); + assertEquals("", ClaudeCodePreferences.getTerminalFontName()); + } + + @Test + void terminalFontSizeDefaultIs14() { + assertEquals(14, ClaudeCodePreferences.DEFAULT_TERMINAL_FONT_SIZE); + assertEquals(14, ClaudeCodePreferences.getTerminalFontSize()); + } + + @Test + void terminalFontSizeRoundTrip() { + ClaudeCodePreferences.setTerminalFontSize(20); + assertEquals(20, ClaudeCodePreferences.getTerminalFontSize()); + } + + @Test + void terminalFontSizeClampedToMin() { + ClaudeCodePreferences.setTerminalFontSize(5); + assertEquals(8, ClaudeCodePreferences.getTerminalFontSize()); + } + + @Test + void terminalFontSizeClampedToMax() { + ClaudeCodePreferences.setTerminalFontSize(100); + assertEquals(72, ClaudeCodePreferences.getTerminalFontSize()); + } } diff --git a/src/test/java/io/github/nbclaudecodegui/ui/NetBeansSettingsProviderTest.java b/src/test/java/io/github/nbclaudecodegui/ui/NetBeansSettingsProviderTest.java new file mode 100644 index 00000000..8756d335 --- /dev/null +++ b/src/test/java/io/github/nbclaudecodegui/ui/NetBeansSettingsProviderTest.java @@ -0,0 +1,51 @@ +package io.github.nbclaudecodegui.ui; + +import io.github.nbclaudecodegui.settings.ClaudeCodePreferences; +import java.awt.Font; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for the font selection logic in {@link NetBeansSettingsProvider}. + */ +class NetBeansSettingsProviderTest { + + private final NetBeansSettingsProvider provider = new NetBeansSettingsProvider(); + + @AfterEach + void tearDown() { + ClaudeCodePreferences.setTerminalFontName(ClaudeCodePreferences.DEFAULT_TERMINAL_FONT_NAME); + ClaudeCodePreferences.setTerminalFontSize(ClaudeCodePreferences.DEFAULT_TERMINAL_FONT_SIZE); + } + + @Test + void autoModeReturnsNonNullFontWithNoDialogFamily() { + ClaudeCodePreferences.setTerminalFontName(""); + Font f = provider.getTerminalFont(); + assertNotNull(f); + // "Dialog" is Java's fallback for unknown fonts; auto-detection must + // always resolve to a real font (at minimum "Monospaced" is available) + assertNotEquals("Dialog", f.getFamily(), + "Auto mode must not fall back to the Dialog family"); + } + + @Test + void explicitFontNameUsedDirectly() { + ClaudeCodePreferences.setTerminalFontName("Monospaced"); + Font f = provider.getTerminalFont(); + assertEquals("Monospaced", f.getFamily()); + } + + @Test + void fontSizeReadFromPreferences() { + ClaudeCodePreferences.setTerminalFontSize(20); + assertEquals(20.0f, provider.getTerminalFontSize()); + assertEquals(20, provider.getTerminalFont().getSize()); + } + + @Test + void defaultFontSizeIs14() { + assertEquals(14.0f, provider.getTerminalFontSize()); + } +}