diff --git a/src/main/java/ee/carlrobert/codegpt/settings/GeneralSettingsState.java b/src/main/java/ee/carlrobert/codegpt/settings/GeneralSettingsState.java index 3c9c0b72..1962b9b6 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/GeneralSettingsState.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/GeneralSettingsState.java @@ -7,6 +7,7 @@ public class GeneralSettingsState { private String displayName = ""; private String avatarBase64 = ""; private ServiceType selectedService = null; + private int chatFontSizeOffset = 0; public String getDisplayName() { if (displayName == null || displayName.isEmpty()) { @@ -38,4 +39,12 @@ public class GeneralSettingsState { public void setSelectedService(ServiceType selectedService) { this.selectedService = selectedService; } + + public int getChatFontSizeOffset() { + return chatFontSizeOffset; + } + + public void setChatFontSizeOffset(int chatFontSizeOffset) { + this.chatFontSizeOffset = chatFontSizeOffset; + } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatFontSize.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatFontSize.java new file mode 100644 index 00000000..76c7f908 --- /dev/null +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatFontSize.java @@ -0,0 +1,40 @@ +package ee.carlrobert.codegpt.toolwindow.chat; + +import com.intellij.util.ui.JBUI; +import ee.carlrobert.codegpt.settings.GeneralSettings; +import javax.swing.JTextPane; + +public final class ChatFontSize { + + private static final int MIN_OFFSET = -4; + private static final int MAX_OFFSET = 8; + + private ChatFontSize() { + } + + public static boolean canAdjust(int delta) { + int offset = getOffset(); + return delta < 0 ? offset > MIN_OFFSET : offset < MAX_OFFSET; + } + + public static void adjust(int delta) { + var state = GeneralSettings.getCurrentState(); + state.setChatFontSizeOffset(clampOffset(state.getChatFontSizeOffset() + delta)); + } + + public static void apply(JTextPane textPane) { + textPane.setFont(JBUI.Fonts.label().deriveFont((float) getFontSize())); + } + + private static int getFontSize() { + return Math.max(8, JBUI.Fonts.label().getSize() + getOffset()); + } + + private static int getOffset() { + return clampOffset(GeneralSettings.getCurrentState().getChatFontSizeOffset()); + } + + private static int clampOffset(int offset) { + return Math.max(MIN_OFFSET, Math.min(MAX_OFFSET, offset)); + } +} diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java index 13ec35df..f024f56d 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java @@ -1,8 +1,10 @@ package ee.carlrobert.codegpt.toolwindow.chat; +import com.intellij.icons.AllIcons; import com.intellij.ide.BrowserUtil; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionUpdateThread; import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultCompactActionGroup; @@ -39,6 +41,7 @@ import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.AttachImageNotifier; import java.awt.CardLayout; import java.nio.file.Path; import java.util.Set; +import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JPanel; import org.jetbrains.annotations.NotNull; @@ -199,6 +202,19 @@ public class ChatToolWindowPanel extends SimpleToolWindowPanel { actionGroup.addSeparator(); actionGroup.add(new OpenInEditorAction()); actionGroup.addSeparator(); + actionGroup.add(new AdjustChatFontSizeAction( + "Decrease chat font size", + "Decrease the chat message font size", + AllIcons.General.Remove, + -1, + tabbedPane)); + actionGroup.add(new AdjustChatFontSizeAction( + "Increase chat font size", + "Increase the chat message font size", + AllIcons.General.Add, + 1, + tabbedPane)); + actionGroup.addSeparator(); actionGroup.add(new SelectedPersonaActionLink(project)); var toolbar = ActionManager.getInstance() @@ -207,6 +223,39 @@ public class ChatToolWindowPanel extends SimpleToolWindowPanel { return toolbar; } + private static class AdjustChatFontSizeAction extends DumbAwareAction { + + private final int delta; + private final ChatToolWindowTabbedPane tabbedPane; + + AdjustChatFontSizeAction( + String text, + String description, + Icon icon, + int delta, + ChatToolWindowTabbedPane tabbedPane) { + super(text, description, icon); + this.delta = delta; + this.tabbedPane = tabbedPane; + } + + @Override + public @NotNull ActionUpdateThread getActionUpdateThread() { + return ActionUpdateThread.EDT; + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + ChatFontSize.adjust(delta); + tabbedPane.refreshChatFontSize(); + } + + @Override + public void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabled(ChatFontSize.canAdjust(delta)); + } + } + private static class SelectedPersonaActionLink extends DumbAwareAction implements CustomComponentAction { diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java index 3d7ae559..deeaef14 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java @@ -202,6 +202,12 @@ public class ChatToolWindowTabPanel implements Disposable { userInputPanel.requestFocus(); } + public void refreshChatFontSize() { + toolWindowScrollablePanel.refreshChatFontSize(); + rootPanel.revalidate(); + rootPanel.repaint(); + } + public void displayLandingView() { toolWindowScrollablePanel.displayLandingView(getLandingView()); totalTokensPanel.updateConversationTokens(conversation); diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java index 1c2c73dc..3cea4362 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabbedPane.java @@ -64,6 +64,12 @@ public class ChatToolWindowTabbedPane extends JBTabbedPane { return activeTabMapping; } + public void refreshChatFontSize() { + activeTabMapping.values().forEach(ChatToolWindowTabPanel::refreshChatFontSize); + repaint(); + revalidate(); + } + public void setTabLifecycleCallbacks(Runnable onTabsOpened, Runnable onAllTabsClosed) { this.onTabsOpened = onTabsOpened; this.onAllTabsClosed = onAllTabsClosed; diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatMessageResponseBody.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatMessageResponseBody.java index 2436c1ba..4b3afd2b 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatMessageResponseBody.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatMessageResponseBody.java @@ -38,6 +38,7 @@ import ee.carlrobert.codegpt.settings.service.FeatureType; import ee.carlrobert.codegpt.settings.models.ModelSettings; import ee.carlrobert.codegpt.settings.service.ServiceType; import ee.carlrobert.codegpt.telemetry.TelemetryAction; +import ee.carlrobert.codegpt.toolwindow.chat.ChatFontSize; import ee.carlrobert.codegpt.toolwindow.chat.editor.ResponseEditorPanel; import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.CopyAction; import ee.carlrobert.codegpt.toolwindow.chat.editor.header.DefaultHeaderPanel; @@ -59,6 +60,8 @@ import ee.carlrobert.codegpt.ui.UIUtil; import ee.carlrobert.codegpt.ui.hover.PsiLinkHoverPreview; import ee.carlrobert.codegpt.util.EditorUtil; import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Container; import java.util.Locale; import java.util.Objects; import javax.swing.DefaultListModel; @@ -276,6 +279,23 @@ public class ChatMessageResponseBody extends JPanel { revalidate(); } + public void refreshFontSize() { + refreshFontSize(contentPanel); + revalidate(); + repaint(); + } + + private static void refreshFontSize(Component component) { + if (component instanceof JTextPane textPane) { + ChatFontSize.apply(textPane); + } + if (component instanceof Container container) { + for (var child : container.getComponents()) { + refreshFontSize(child); + } + } + } + private void displayErrorMessage(String message, HyperlinkListener hyperlinkListener) { ApplicationManager.getApplication().invokeLater(() -> { if (webpageListPanel != null) { @@ -526,6 +546,7 @@ public class ChatMessageResponseBody extends JPanel { UIUtil.handleHyperlinkClicked(event); }); + ChatFontSize.apply(textPane); if (caretVisible) { textPane.getCaret().setVisible(true); textPane.setCaretPosition(textPane.getDocument().getLength()); diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatToolWindowScrollablePanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatToolWindowScrollablePanel.java index 52dcfb88..eee7beff 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatToolWindowScrollablePanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ui/ChatToolWindowScrollablePanel.java @@ -3,6 +3,8 @@ package ee.carlrobert.codegpt.toolwindow.chat.ui; import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel; import com.intellij.openapi.roots.ui.componentsList.layout.VerticalStackLayout; import ee.carlrobert.codegpt.toolwindow.ui.ResponseMessagePanel; +import java.awt.Component; +import java.awt.Container; import java.awt.Rectangle; import java.util.Arrays; import java.util.HashMap; @@ -72,6 +74,24 @@ public class ChatToolWindowScrollablePanel extends ScrollablePanel { revalidate(); } + public void refreshChatFontSize() { + for (var component : getComponents()) { + refreshChatFontSize(component); + } + update(); + } + + private static void refreshChatFontSize(Component component) { + if (component instanceof ChatMessageResponseBody responseBody) { + responseBody.refreshFontSize(); + } + if (component instanceof Container container) { + for (var child : container.getComponents()) { + refreshChatFontSize(child); + } + } + } + public JPanel getLastComponent() { var comp = getComponents()[getComponentCount() - 1]; if (comp instanceof JPanel panel) {