Change project's name, heavy refactoring

This commit is contained in:
Carl-Robert Linnupuu 2023-03-12 20:56:44 +00:00
parent c0cea7cc43
commit 483abe146b
80 changed files with 969 additions and 968 deletions

View file

@ -0,0 +1,299 @@
package ee.carlrobert.codegpt.ide.toolwindow.components;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
// https://tips4java.wordpress.com/2009/09/27/component-border/
/**
* The ComponentBorder class allows you to place a real component in
* the space reserved for painting the Border of a component.
*
* This class takes advantage of the knowledge that all Swing components are
* also Containers. By default the layout manager is null, so we should be
* able to place a child component anywhere in the parent component. In order
* to prevent the child component from painting over top of the parent
* component a Border is added to the parent componet such that the insets of
* the Border will reserve space for the child component to be painted without
* affecting the parent component.
*/
public class ComponentBorder implements Border
{
public enum Edge
{
TOP,
LEFT,
BOTTOM,
RIGHT;
}
public static final float LEADING = 0.0f;
public static final float CENTER = 0.5f;
public static final float TRAILING = 1.0f;
private JComponent parent;
private JComponent component;
private Edge edge;
private float alignment;
private int gap = 5;
private boolean adjustInsets = true;
private Insets borderInsets = new Insets(0, 0, 0, 0);
/**
* Convenience constructor that uses the default edge (Edge.RIGHT) and
* alignment (CENTER).
*
* @param component the component to be added in the Border area
*/
public ComponentBorder(JComponent component)
{
this(component, Edge.RIGHT);
}
/**
* Convenience constructor that uses the default alignment (CENTER).
*
* @param component the component to be added in the Border area
* @param edge a valid Edge enum of TOP, LEFT, BOTTOM, RIGHT
*/
public ComponentBorder(JComponent component, Edge edge)
{
this(component, edge, CENTER);
}
/**
* Main constructor to create a ComponentBorder.
*
* @param component the component to be added in the Border area
* @param edge a valid Edge enum of TOP, LEFT, BOTTOM, RIGHT
* @param alignment the alignment of the component along the
* specified Edge. Must be in the range 0 - 1.0.
*/
public ComponentBorder(JComponent component, Edge edge, float alignment )
{
this.component = component;
component.setSize( component.getPreferredSize() );
component.setCursor(Cursor.getDefaultCursor());
setEdge( edge );
setAlignment( alignment );
}
public boolean isAdjustInsets()
{
return adjustInsets;
}
public void setAdjustInsets(boolean adjustInsets)
{
this.adjustInsets = adjustInsets;
}
/**
* Get the component alignment along the Border Edge
*
* @return the alignment
*/
public float getAlignment()
{
return alignment;
}
/**
* Set the component alignment along the Border Edge
*
* @param alignment a value in the range 0 - 1.0. Standard values would be
* CENTER (default), LEFT and RIGHT.
*/
public void setAlignment(float alignment)
{
this.alignment = alignment > 1.0f ? 1.0f : alignment < 0.0f ? 0.0f : alignment;
}
/**
* Get the Edge the component is positioned along
*
* @return the Edge
*/
public Edge getEdge()
{
return edge;
}
/**
* Set the Edge the component is positioned along
*
* @param edge the Edge the component is position on.
*/
public void setEdge(Edge edge)
{
this.edge = edge;
}
/**
* Get the gap between the border component and the parent component
*
* @return the gap in pixels.
*/
public int getGap()
{
return gap;
}
/**
* Set the gap between the border component and the parent component
*
* @param gap the gap in pixels (default is 5)
*/
public void setGap(int gap)
{
this.gap = gap;
}
//
// Implement the Border interface
//
public Insets getBorderInsets(Component c)
{
return borderInsets;
}
public boolean isBorderOpaque()
{
return false;
}
/**
* In this case a real component is to be painted. Setting the location
* of the component will cause it to be painted at that location.
*/
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
{
float x2 = (width - component.getWidth()) * component.getAlignmentX() + x;
float y2 = (height - component.getHeight()) * component.getAlignmentY() + y;
component.setLocation((int)x2, (int)y2);
}
/*
* Install this Border on the specified component by replacing the
* existing Border with a CompoundBorder containing the original Border
* and our ComponentBorder
*
* This method should only be invoked once all the properties of this
* class have been set. Installing the Border more than once will cause
* unpredictable results.
*/
public void install(JComponent parent)
{
this.parent = parent;
determineInsetsAndAlignment();
// Add this Border to the parent
Border current = parent.getBorder();
if (current == null)
{
parent.setBorder(this);
}
else
{
CompoundBorder compound = new CompoundBorder(current, this);
parent.setBorder(compound);
}
// Add component to the parent
parent.add(component);
}
/**
* The insets need to be determined so they are included in the preferred
* size of the component the Border is attached to.
*
* The alignment of the component is determined here so it doesn't need
* to be recalculated every time the Border is painted.
*/
private void determineInsetsAndAlignment()
{
borderInsets = new Insets(0, 0, 0, 0);
// The insets will only be updated for the edge the component will be
// diplayed on.
//
// The X, Y alignment of the component is controlled by both the edge
// and alignment parameters
if (edge == Edge.TOP)
{
borderInsets.top = component.getPreferredSize().height + gap;
component.setAlignmentX(alignment);
component.setAlignmentY(0.0f);
}
else if (edge == Edge.BOTTOM)
{
borderInsets.bottom = component.getPreferredSize().height + gap;
component.setAlignmentX(alignment);
component.setAlignmentY(1.0f);
}
else if (edge == Edge.LEFT)
{
borderInsets.left = component.getPreferredSize().width + gap;
component.setAlignmentX(0.0f);
component.setAlignmentY(alignment);
}
else if (edge == Edge.RIGHT)
{
borderInsets.right = component.getPreferredSize().width + gap;
component.setAlignmentX(1.0f);
component.setAlignmentY(alignment);
}
if (adjustInsets)
adjustBorderInsets();
}
/*
* The complimentary edges of the Border may need to be adjusted to allow
* the component to fit completely in the bounds of the parent component.
*/
private void adjustBorderInsets()
{
Insets parentInsets = parent.getInsets();
// May need to adust the height of the parent component to fit
// the component in the Border
if (edge == Edge.RIGHT || edge == Edge.LEFT)
{
int parentHeight = parent.getPreferredSize().height - parentInsets.top - parentInsets.bottom;
int diff = component.getHeight() - parentHeight;
if (diff > 0)
{
int topDiff = (int)(diff * alignment);
int bottomDiff = diff - topDiff;
borderInsets.top += topDiff;
borderInsets.bottom += bottomDiff;
}
}
// May need to adust the width of the parent component to fit
// the component in the Border
if (edge == Edge.TOP || edge == Edge.BOTTOM)
{
int parentWidth = parent.getPreferredSize().width - parentInsets.left - parentInsets.right;
int diff = component.getWidth() - parentWidth;
if (diff > 0)
{
int leftDiff = (int)(diff * alignment);
int rightDiff = diff - leftDiff;
borderInsets.left += leftDiff;
borderInsets.right += rightDiff;
}
}
}
}

View file

@ -0,0 +1,35 @@
package ee.carlrobert.codegpt.ide.toolwindow.components;
import com.intellij.icons.AllIcons;
import javax.swing.JButton;
import javax.swing.SwingConstants;
public class GenerateButton extends JButton {
public GenerateButton() {
initialize();
}
public void setMode(Mode mode, Runnable onClick) {
var isStopMode = mode == Mode.STOP;
setIcon(isStopMode ? AllIcons.Actions.Suspend : AllIcons.Actions.Refresh);
setText(isStopMode ? "Stop generating" : "Regenerate response");
for (var listener : getActionListeners()) {
removeActionListener(listener);
}
addActionListener(e -> onClick.run());
}
private void initialize() {
setVerticalTextPosition(SwingConstants.CENTER);
setHorizontalTextPosition(SwingConstants.RIGHT);
setVerticalAlignment(SwingConstants.CENTER);
setAlignmentY(0.5f);
setVisible(false);
}
public enum Mode {
STOP,
REFRESH
}
}

View file

@ -0,0 +1,42 @@
package ee.carlrobert.codegpt.ide.toolwindow.components;
import static java.util.stream.Collectors.toList;
import icons.Icons;
import java.awt.GridBagLayout;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
public class LandingView {
private static final List<String> questions = List.of("How do I make an HTTP request in Javascript?",
"What is the difference between px, dip, dp, and sp?",
"How do I undo the most recent local commits in Git?",
"What is the difference between stack and heap?");
public List<JPanel> getQuestionPanels() {
return questions.stream()
.map(this::createQuestionPanel)
.collect(toList());
}
public JPanel createImageIconPanel() {
var imageIconPanel = new JPanel();
imageIconPanel.setLayout(new GridBagLayout());
var imageIconLabel = new JLabel(Icons.SunImageIcon);
imageIconLabel.setHorizontalAlignment(JLabel.CENTER);
imageIconPanel.add(imageIconLabel);
return imageIconPanel;
}
private JPanel createQuestionPanel(String question) {
var panel = new JPanel();
panel.setLayout(new GridBagLayout());
var label = new JLabel(question, SwingConstants.CENTER);
label.setHorizontalAlignment(JLabel.CENTER);
panel.add(label);
return panel;
}
}

View file

@ -0,0 +1,31 @@
package ee.carlrobert.codegpt.ide.toolwindow.components;
import com.intellij.openapi.roots.ui.componentsList.components.ScrollablePanel;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.util.ui.JBUI;
import java.awt.Adjustable;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.JScrollBar;
import javax.swing.ScrollPaneConstants;
public class ScrollPane extends JBScrollPane {
public ScrollPane(ScrollablePanel scrollablePanel) {
super(scrollablePanel);
setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
setBorder(JBUI.Borders.empty(0, 8));
setViewportBorder(null);
}
public void scrollToBottom() {
JScrollBar verticalBar = getVerticalScrollBar();
verticalBar.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
Adjustable adjustable = e.getAdjustable();
adjustable.setValue(adjustable.getMaximum());
verticalBar.removeAdjustmentListener(this);
}
});
}
}

View file

@ -0,0 +1,68 @@
package ee.carlrobert.codegpt.ide.toolwindow.components;
import com.intellij.ui.JBColor;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
public class SwingUtils {
public static JTextArea createTextArea(String selectedText) {
var textArea = new JTextArea();
textArea.append(selectedText);
textArea.setLineWrap(true);
textArea.setEditable(false);
textArea.setFont(textArea.getFont().deriveFont(Font.ITALIC));
textArea.setWrapStyleWord(true);
textArea.setBackground(JBColor.PanelBackground);
return textArea;
}
public static JLabel createIconLabel(Icon icon, String text) {
var iconLabel = new JLabel(icon);
iconLabel.setText(text);
iconLabel.setFont(iconLabel.getFont().deriveFont(iconLabel.getFont().getStyle() | Font.BOLD));
iconLabel.setIconTextGap(8);
return iconLabel;
}
public static JButton createIconButton(Icon icon) {
var button = new JButton(icon);
button.setBorder(BorderFactory.createEmptyBorder());
button.setContentAreaFilled(false);
button.setPreferredSize(new Dimension(icon.getIconWidth(), icon.getIconHeight()));
return button;
}
public static Box justifyLeft(Component component) {
Box box = Box.createHorizontalBox();
box.add(component);
box.add(Box.createHorizontalGlue());
return box;
}
public static void addShiftEnterInputMap(JTextArea textArea, Runnable onEnter) {
var input = textArea.getInputMap();
var enterStroke = KeyStroke.getKeyStroke("ENTER");
var shiftEnterStroke = KeyStroke.getKeyStroke("shift ENTER");
input.put(shiftEnterStroke, "insert-break");
input.put(enterStroke, "text-submit");
var actions = textArea.getActionMap();
actions.put("text-submit", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
onEnter.run();
}
});
}
}

View file

@ -0,0 +1,75 @@
package ee.carlrobert.codegpt.ide.toolwindow.components;
import static ee.carlrobert.codegpt.ide.toolwindow.components.SwingUtils.createIconButton;
import com.intellij.icons.AllIcons;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.UIUtil;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.IOException;
import javax.swing.JButton;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.Theme;
import org.fife.ui.rtextarea.CaretStyle;
public class SyntaxTextArea extends RSyntaxTextArea {
public SyntaxTextArea(boolean isReadOnly, boolean withBlockCaret, String syntax) {
super("");
setStyles(isReadOnly, withBlockCaret, syntax);
}
public void displayCopyButton() {
ComponentBorder cb = new ComponentBorder(createCopyButton());
cb.setAlignment(TOP_ALIGNMENT);
cb.install(this);
}
public void hideCaret() {
getCaret().setVisible(false);
}
public void changeStyleViaThemeXml() {
var baseThemePath = "/org/fife/ui/rsyntaxtextarea/themes/";
try {
Theme theme = Theme.load(getClass().getResourceAsStream(
UIUtil.isUnderDarcula() ? baseThemePath + "dark.xml" : baseThemePath + "idea.xml"));
theme.apply(this);
} catch (IOException e) {
e.printStackTrace();
}
}
private void setStyles(boolean isReadOnly, boolean withBlockCaret, String syntax) {
setMargin(JBUI.insets(5));
setAntiAliasingEnabled(true);
setEnabled(true);
setEditable(!isReadOnly);
setPaintTabLines(false);
setHighlightCurrentLine(false);
setLineWrap(true);
if (withBlockCaret) {
setCaretStyle(0, CaretStyle.BLOCK_STYLE);
getCaret().setVisible(true);
}
setSyntaxEditingStyle(syntax);
changeStyleViaThemeXml();
}
private void copyToClipboard() {
StringSelection stringSelection = new StringSelection(getText().trim());
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
}
private JButton createCopyButton() {
var button = createIconButton(AllIcons.General.InlineCopy);
button.addActionListener(e -> {
copyToClipboard();
button.setIcon(AllIcons.General.InspectionsOK);
});
return button;
}
}

View file

@ -0,0 +1,57 @@
package ee.carlrobert.codegpt.ide.toolwindow.components;
import static ee.carlrobert.codegpt.ide.toolwindow.components.SwingUtils.addShiftEnterInputMap;
import static ee.carlrobert.codegpt.ide.toolwindow.components.SwingUtils.createIconButton;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBTextArea;
import com.intellij.util.ui.JBUI;
import icons.Icons;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.JButton;
import javax.swing.JScrollPane;
public class TextArea extends JBTextArea {
public TextArea(Runnable onSubmit, JScrollPane textAreaScrollPane) {
super("Ask me anything...");
setForeground(JBColor.GRAY);
setMargin(JBUI.insets(5));
addFocusListener(getFocusListener());
addSubmitButton(onSubmit, textAreaScrollPane);
addShiftEnterInputMap(this, onSubmit);
}
private void addSubmitButton(Runnable onSubmit, JScrollPane textAreaScrollPane) {
var button = createSubmitButton(e -> onSubmit.run());
ComponentBorder cb = new ComponentBorder(button);
cb.setAdjustInsets(true);
cb.install(textAreaScrollPane);
}
private JButton createSubmitButton(ActionListener submitButtonListener) {
var button = createIconButton(Icons.SendImageIcon);
button.addActionListener(submitButtonListener);
return button;
}
private FocusListener getFocusListener() {
return new FocusListener() {
public void focusGained(FocusEvent e) {
if (getText().equals("Ask me anything...")) {
setText("");
setForeground(JBColor.BLACK);
}
}
public void focusLost(FocusEvent e) {
if (getText().isEmpty()) {
setForeground(JBColor.GRAY);
setText("Ask me anything...");
}
}
};
}
}