Skip to content

Commit 14a192a

Browse files
feat(chat-font): add independent font settings for AI chat
Allow the AI chat view to use its own font family, size, and hinting policy, independent of the editor's Default Font. - The new "Use default font" toggle (on by default) preserves the existing behavior of tracking the editor font live - When disabled, ChatFont/{Family,SizePt,Sharpen} settings take over, with the app UI font as the initial fallback so the picker shows something sensible on first use - All four ChatFont signals are wired into applyChatFont() so the view stays live regardless of which mode is active - Hinting policy is applied in both branches to keep thin fonts sharp in the Qt widget layer, matching the Scintilla editor side
1 parent 84961c9 commit 14a192a

5 files changed

Lines changed: 152 additions & 10 deletions

File tree

src/ApplicationSettings.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "ApplicationSettings.h"
2020

21+
#include <QApplication>
2122
#include <QFont>
2223
#include <QFontDatabase>
2324
#include <QStandardPaths>
@@ -85,6 +86,20 @@ CREATE_SETTING(Editor, DefaultEOLMode, defaultEOLMode, QString, QStringLiteral("
8586
CREATE_SETTING(Editor, URLHighlighting, urlHighlighting, bool, true)
8687
CREATE_SETTING(Editor, ShowLineNumbers, showLineNumbers, bool, true)
8788

89+
// AI chat-view font. UseDefault=true follows the editor's Default Font (current
90+
// behavior); when false the chat uses its own Family/SizePt/Sharpen. The
91+
// Family/SizePt fallbacks are the application's default UI font (what the picker
92+
// shows the first time the user unchecks "Use default font") — NOT the editor
93+
// font. See AcpSessionView::chatFont().
94+
CREATE_SETTING(ChatFont, ChatFontUseDefault, chatFontUseDefault, bool, true)
95+
CREATE_SETTING(ChatFont, ChatFontFamily, chatFontFamily, QString, []() {
96+
return QApplication::font().family();
97+
})
98+
CREATE_SETTING(ChatFont, ChatFontSizePt, chatFontSizePt, int, []() {
99+
return QApplication::font().pointSize();
100+
})
101+
CREATE_SETTING(ChatFont, ChatFontSharpen, chatFontSharpen, bool, true)
102+
88103
static QString defaultShellCommand()
89104
{
90105
#ifdef Q_OS_WIN

src/ApplicationSettings.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,16 @@ class ApplicationSettings : public QSettings
133133
DEFINE_SETTING(URLHighlighting, urlHighlighting, bool)
134134
DEFINE_SETTING(ShowLineNumbers, showLineNumbers, bool)
135135

136+
// AI chat-view font. UseDefault=true (the default) means the chat follows
137+
// the editor's Default Font (FontName/FontSize/FontHinting) and tracks it
138+
// live — the historical behavior. When false, the chat uses its own
139+
// Family/SizePt/Sharpen instead. Plumbed into AcpSessionView::chatFont();
140+
// Sharpen maps to the same QFont hinting policy as the editor's FontHinting.
141+
DEFINE_SETTING(ChatFontUseDefault, chatFontUseDefault, bool);
142+
DEFINE_SETTING(ChatFontFamily, chatFontFamily, QString);
143+
DEFINE_SETTING(ChatFontSizePt, chatFontSizePt, int);
144+
DEFINE_SETTING(ChatFontSharpen, chatFontSharpen, bool);
145+
136146
DEFINE_SETTING(ShellCommand, shellCommand, QString)
137147
DEFINE_SETTING(TerminalFont, terminalFont, QString)
138148

src/dialogs/PreferencesDialog.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,41 @@ PreferencesDialog::PreferencesDialog(ApplicationSettings *settings, QWidget *par
130130
MapSettingToCheckBox(ui->checkBoxShowLineNumbers, &ApplicationSettings::showLineNumbers, &ApplicationSettings::setShowLineNumbers, &ApplicationSettings::showLineNumbersChanged);
131131
MapSettingToCheckBox(ui->checkBoxFontHinting, &ApplicationSettings::fontHinting, &ApplicationSettings::setFontHinting, &ApplicationSettings::fontHintingChanged);
132132

133+
// --- Chat Font section -----------------------------------------------------
134+
// Mirrors the Default Font wiring above. "Use default font" (checked by
135+
// default) makes the AI chat follow the editor's Default Font; unchecking it
136+
// enables a separate family/size/sharpen for the chat only.
137+
MapSettingToCheckBox(ui->checkBoxChatUseDefaultFont, &ApplicationSettings::chatFontUseDefault, &ApplicationSettings::setChatFontUseDefault, &ApplicationSettings::chatFontUseDefaultChanged);
138+
139+
ui->fcbChatFont->setCurrentFont(QFont(settings->chatFontFamily()));
140+
connect(ui->fcbChatFont, &QFontComboBox::currentFontChanged, this, [=](const QFont &f) {
141+
settings->setChatFontFamily(f.family());
142+
});
143+
connect(settings, &ApplicationSettings::chatFontFamilyChanged, this, [=](QString name) {
144+
ui->fcbChatFont->setCurrentFont(QFont(name));
145+
});
146+
147+
ui->spbChatFontSize->setValue(settings->chatFontSizePt());
148+
connect(ui->spbChatFontSize, QOverload<int>::of(&QSpinBox::valueChanged), settings, &ApplicationSettings::setChatFontSizePt);
149+
connect(settings, &ApplicationSettings::chatFontSizePtChanged, ui->spbChatFontSize, &QSpinBox::setValue);
150+
151+
MapSettingToCheckBox(ui->checkBoxChatFontHinting, &ApplicationSettings::chatFontSharpen, &ApplicationSettings::setChatFontSharpen, &ApplicationSettings::chatFontSharpenChanged);
152+
153+
// The 3 custom controls (+ their labels) are only meaningful when NOT
154+
// following the editor font. Keep them in sync from both sides (the checkbox
155+
// itself and any external settings change) so the enabled state never drifts.
156+
auto syncChatFontEnabled = [=]() {
157+
const bool custom = !ui->checkBoxChatUseDefaultFont->isChecked();
158+
ui->fcbChatFont->setEnabled(custom);
159+
ui->spbChatFontSize->setEnabled(custom);
160+
ui->checkBoxChatFontHinting->setEnabled(custom);
161+
ui->labelChatFont->setEnabled(custom);
162+
ui->labelChatFontSize->setEnabled(custom);
163+
};
164+
syncChatFontEnabled();
165+
connect(ui->checkBoxChatUseDefaultFont, &QCheckBox::toggled, this, syncChatFontEnabled);
166+
connect(settings, &ApplicationSettings::chatFontUseDefaultChanged, this, syncChatFontEnabled);
167+
133168

134169
QButtonGroup *buttonGroup = new QButtonGroup(this);
135170
buttonGroup->addButton(ui->radioFollowCurrentDirectory, ApplicationSettings::FollowCurrentDocument);

src/dialogs/PreferencesDialog.ui

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,65 @@
228228
</property>
229229
</widget>
230230
</item>
231+
<item>
232+
<widget class="QGroupBox" name="groupBoxChatFont">
233+
<property name="title">
234+
<string>Chat Font</string>
235+
</property>
236+
<layout class="QFormLayout" name="formLayoutChatFont">
237+
<item row="0" column="0" colspan="2">
238+
<widget class="QCheckBox" name="checkBoxChatUseDefaultFont">
239+
<property name="toolTip">
240+
<string>When enabled, the AI chat uses the editor's Default Font. Disable to choose a separate font for the chat.</string>
241+
</property>
242+
<property name="text">
243+
<string>Use default font</string>
244+
</property>
245+
</widget>
246+
</item>
247+
<item row="1" column="0">
248+
<widget class="QLabel" name="labelChatFont">
249+
<property name="text">
250+
<string>Font</string>
251+
</property>
252+
</widget>
253+
</item>
254+
<item row="1" column="1">
255+
<widget class="QFontComboBox" name="fcbChatFont"/>
256+
</item>
257+
<item row="2" column="0">
258+
<widget class="QLabel" name="labelChatFontSize">
259+
<property name="text">
260+
<string>Font Size</string>
261+
</property>
262+
</widget>
263+
</item>
264+
<item row="2" column="1">
265+
<widget class="QSpinBox" name="spbChatFontSize">
266+
<property name="suffix">
267+
<string>pt</string>
268+
</property>
269+
<property name="minimum">
270+
<number>2</number>
271+
</property>
272+
<property name="maximum">
273+
<number>48</number>
274+
</property>
275+
</widget>
276+
</item>
277+
<item row="3" column="0" colspan="2">
278+
<widget class="QCheckBox" name="checkBoxChatFontHinting">
279+
<property name="toolTip">
280+
<string>Snap glyphs to the pixel grid for sharper text. Disable to keep the font's exact outline. Affects the AI chat.</string>
281+
</property>
282+
<property name="text">
283+
<string>Sharpen fonts (hinting)</string>
284+
</property>
285+
</widget>
286+
</item>
287+
</layout>
288+
</widget>
289+
</item>
231290
<item>
232291
<widget class="QGroupBox" name="groupBox_2">
233292
<property name="title">

src/widgets/AcpSessionView.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -627,16 +627,27 @@ void AcpSessionView::buildUi()
627627
connect(m_effortCombo, qOverload<int>(&QComboBox::currentIndexChanged),
628628
this, &AcpSessionView::onEffortComboChanged);
629629

630-
// Apply the Default Font preference to transcript + input, and follow
631-
// live preference edits. Chrome (banner, selectors, buttons) keeps the
632-
// system font.
630+
// Apply the chat font preference to transcript + input, and follow live
631+
// preference edits. The chat font may be independent of the editor font:
632+
// when "Use default font" is on it tracks the editor's Default Font (the
633+
// first three signals); when off it uses the ChatFont/* settings (the last
634+
// four). Connecting all of them keeps the view live either way. Chrome
635+
// (banner, selectors, buttons) keeps the system font.
633636
if (auto *settings = appSettings()) {
634637
connect(settings, &ApplicationSettings::fontNameChanged,
635638
this, &AcpSessionView::applyChatFont);
636639
connect(settings, &ApplicationSettings::fontSizeChanged,
637640
this, &AcpSessionView::applyChatFont);
638641
connect(settings, &ApplicationSettings::fontHintingChanged,
639642
this, &AcpSessionView::applyChatFont);
643+
connect(settings, &ApplicationSettings::chatFontUseDefaultChanged,
644+
this, &AcpSessionView::applyChatFont);
645+
connect(settings, &ApplicationSettings::chatFontFamilyChanged,
646+
this, &AcpSessionView::applyChatFont);
647+
connect(settings, &ApplicationSettings::chatFontSizePtChanged,
648+
this, &AcpSessionView::applyChatFont);
649+
connect(settings, &ApplicationSettings::chatFontSharpenChanged,
650+
this, &AcpSessionView::applyChatFont);
640651
}
641652
applyChatFont();
642653

@@ -1873,13 +1884,25 @@ void AcpSessionView::rebuildAttachIcon()
18731884
QFont AcpSessionView::chatFont() const
18741885
{
18751886
auto *settings = appSettings();
1876-
QFont f = settings ? QFont(settings->fontName(), settings->fontSize())
1877-
: QFont();
1878-
// Mirror the editor's glyph-hinting policy: these are plain Qt widgets, so
1879-
// Scintilla's platform-layer flag doesn't reach them — set it on the QFont
1880-
// directly. Without it, thin fonts (e.g. Lilex) look blurry here while the
1881-
// Scintilla editor looks sharp. See EditorManager / PlatQt for the editor side.
1882-
f.setHintingPreference(settings && !settings->fontHinting()
1887+
if (!settings) return QFont();
1888+
1889+
// Two modes (see ApplicationSettings ChatFont group). The hinting policy is
1890+
// set on the QFont directly in BOTH branches: these are plain Qt widgets, so
1891+
// Scintilla's platform-layer flag doesn't reach them — without it, thin fonts
1892+
// (e.g. Lilex) look blurry here while the Scintilla editor looks sharp. See
1893+
// EditorManager / PlatQt for the editor side.
1894+
if (settings->chatFontUseDefault()) {
1895+
// Default mode: follow the editor's Default Font (historical behavior).
1896+
QFont f(settings->fontName(), settings->fontSize());
1897+
f.setHintingPreference(!settings->fontHinting()
1898+
? QFont::PreferNoHinting
1899+
: QFont::PreferFullHinting);
1900+
return f;
1901+
}
1902+
1903+
// Custom mode: a chat-specific family/size/sharpen, independent of the editor.
1904+
QFont f(settings->chatFontFamily(), settings->chatFontSizePt());
1905+
f.setHintingPreference(!settings->chatFontSharpen()
18831906
? QFont::PreferNoHinting
18841907
: QFont::PreferFullHinting);
18851908
return f;

0 commit comments

Comments
 (0)