Skip to content

Commit 885dfa1

Browse files
author
a.iudin
committed
Refine lookup popup UI: remove type text, restyle list cells, and enhance prompt text field popup styling
1 parent 831e2a4 commit 885dfa1

File tree

3 files changed

+130
-26
lines changed

3 files changed

+130
-26
lines changed

src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/PromptTextField.kt

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ import com.intellij.ui.awt.RelativePoint
2525
import com.intellij.ui.components.JBList
2626
import com.intellij.ui.components.JBPanel
2727
import com.intellij.ui.components.JBScrollPane
28+
import com.intellij.ui.scale.JBUIScale
2829
import com.intellij.util.ui.JBUI
30+
import com.intellij.util.ui.UIUtil
2931
import ee.carlrobert.codegpt.CodeGPTBundle
3032
import ee.carlrobert.codegpt.CodeGPTKeys.IS_PROMPT_TEXT_FIELD_DOCUMENT
3133
import ee.carlrobert.codegpt.ui.textarea.header.tag.TagManager
@@ -39,10 +41,7 @@ import ee.carlrobert.codegpt.ui.textarea.popup.LookupListCellRenderer
3941
import ee.carlrobert.codegpt.ui.textarea.popup.LookupListModel
4042
import ee.carlrobert.codegpt.util.coroutines.runCatchingCancellable
4143
import kotlinx.coroutines.*
42-
import java.awt.BorderLayout
43-
import java.awt.Dimension
44-
import java.awt.GraphicsEnvironment
45-
import java.awt.Point
44+
import java.awt.*
4645
import java.awt.event.KeyAdapter
4746
import java.awt.event.KeyEvent
4847
import java.awt.event.MouseAdapter
@@ -128,6 +127,9 @@ class PromptTextField(
128127
.setResizable(true)
129128
.setCancelOnClickOutside(true)
130129
.setCancelOnWindowDeactivation(true)
130+
.setShowBorder(true)
131+
.setShowShadow(true)
132+
.setBorderColor(JBColor.border())
131133
.createPopup()
132134

133135
val relativePoint = calculateOptimalPopupPosition(editor, popupPanel)
@@ -170,6 +172,17 @@ class PromptTextField(
170172
selectionMode = ListSelectionModel.SINGLE_SELECTION
171173
if (model.size > 0) selectedIndex = 0
172174

175+
background = UIUtil.getListBackground()
176+
foreground = UIUtil.getListForeground()
177+
selectionBackground = UIUtil.getListSelectionBackground(true)
178+
selectionForeground = UIUtil.getListSelectionForeground(true)
179+
180+
fixedCellHeight = JBUIScale.scale(20)
181+
border = JBUI.Borders.empty()
182+
183+
isFocusTraversalPolicyProvider = false
184+
setFocusTraversalKeysEnabled(false)
185+
173186
addKeyListener(object : KeyAdapter() {
174187
override fun keyPressed(e: KeyEvent) {
175188
when (e.keyCode) {
@@ -227,8 +240,22 @@ class PromptTextField(
227240
}
228241

229242
private fun setupLayout() {
230-
border = JBUI.Borders.empty(5)
231-
add(JBScrollPane(itemsList), BorderLayout.CENTER)
243+
background = UIUtil.getListBackground()
244+
border = JBUI.Borders.empty(4)
245+
246+
isFocusTraversalPolicyProvider = false
247+
setFocusTraversalKeysEnabled(false)
248+
249+
val scrollPane = JBScrollPane(itemsList).apply {
250+
border = JBUI.Borders.empty()
251+
viewport.background = UIUtil.getListBackground()
252+
verticalScrollBarPolicy = JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
253+
horizontalScrollBarPolicy = JBScrollPane.HORIZONTAL_SCROLLBAR_NEVER
254+
isFocusTraversalPolicyProvider = false
255+
setFocusTraversalKeysEnabled(false)
256+
}
257+
258+
add(scrollPane, BorderLayout.CENTER)
232259

233260
addKeyListener(object : KeyAdapter() {
234261
override fun keyPressed(e: KeyEvent) {
@@ -257,6 +284,18 @@ class PromptTextField(
257284
})
258285
}
259286

287+
override fun paintComponent(g: Graphics) {
288+
val g2 = g.create() as Graphics2D
289+
try {
290+
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
291+
292+
g2.color = UIUtil.getListBackground()
293+
g2.fillRoundRect(0, 0, width, height, JBUIScale.scale(8), JBUIScale.scale(8))
294+
} finally {
295+
g2.dispose()
296+
}
297+
}
298+
260299
private fun redirectKeyToEditor(e: KeyEvent) {
261300
editor?.let { editor ->
262301
runInEdt {
@@ -284,21 +323,37 @@ class PromptTextField(
284323
}
285324

286325
override fun getPreferredSize(): Dimension {
287-
val maxWidth = 450
288-
val minWidth = 300
289-
val minHeight = 150
290-
val maxHeight = 400
326+
val maxWidth = JBUIScale.scale(450)
327+
val minWidth = JBUIScale.scale(300)
328+
val minHeight = JBUIScale.scale(120)
329+
val maxHeight = JBUIScale.scale(300)
291330

292-
val listSize = itemsList.preferredSize
293-
val contentWidth = listSize.width + 10
294-
val contentHeight = listSize.height + 10
331+
val itemCount = itemsList.model.size
332+
val itemHeight = JBUIScale.scale(20)
333+
val contentHeight = (itemCount * itemHeight) + 8
334+
val contentWidth = calculateOptimalWidth()
295335

296336
val width = minOf(maxOf(contentWidth, minWidth), maxWidth)
297337
val height = minOf(maxOf(contentHeight, minHeight), maxHeight)
298338

299339
return Dimension(width, height)
300340
}
301341

342+
private fun calculateOptimalWidth(): Int {
343+
var maxWidth = JBUIScale.scale(200)
344+
val fontMetrics = itemsList.getFontMetrics(itemsList.font)
345+
346+
for (i in 0 until itemsList.model.size) {
347+
val item = itemsList.model.getElementAt(i)
348+
val textWidth = fontMetrics.stringWidth(item.displayName)
349+
val iconWidth = item.icon?.iconWidth ?: 0
350+
val totalWidth = textWidth + iconWidth + JBUIScale.scale(32)
351+
maxWidth = maxOf(maxWidth, totalWidth)
352+
}
353+
354+
return maxWidth
355+
}
356+
302357
fun updateSearchFromEditor() {
303358
editor?.let { editor ->
304359
when (val result = getSearchTextAfterAt(editor)) {

src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/lookup/group/AbstractLookupGroupItem.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package ee.carlrobert.codegpt.ui.textarea.lookup.group
22

33
import com.intellij.codeInsight.lookup.LookupElement
44
import com.intellij.codeInsight.lookup.LookupElementPresentation
5-
import com.intellij.icons.AllIcons
65
import ee.carlrobert.codegpt.ui.textarea.lookup.AbstractLookupItem
76
import ee.carlrobert.codegpt.ui.textarea.lookup.LookupGroupItem
87

@@ -11,8 +10,6 @@ abstract class AbstractLookupGroupItem : AbstractLookupItem(), LookupGroupItem {
1110
override fun setPresentation(element: LookupElement, presentation: LookupElementPresentation) {
1211
presentation.itemText = displayName
1312
presentation.icon = icon
14-
presentation.setTypeText("", AllIcons.Icons.Ide.NextStep)
15-
presentation.isTypeIconRightAligned = true
1613
presentation.isItemTextBold = false
1714
}
1815

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
package ee.carlrobert.codegpt.ui.textarea.popup
22

3+
import com.intellij.icons.AllIcons
4+
import com.intellij.ui.JBColor
35
import com.intellij.ui.SimpleColoredComponent
46
import com.intellij.ui.SimpleTextAttributes
7+
import com.intellij.ui.scale.JBUIScale
58
import com.intellij.util.ui.JBUI
9+
import com.intellij.util.ui.UIUtil
10+
import ee.carlrobert.codegpt.ui.textarea.lookup.LoadingLookupItem
11+
import ee.carlrobert.codegpt.ui.textarea.lookup.LookupGroupItem
612
import ee.carlrobert.codegpt.ui.textarea.lookup.LookupItem
713
import java.awt.BorderLayout
814
import java.awt.Component
9-
import javax.swing.JList
10-
import javax.swing.JPanel
11-
import javax.swing.ListCellRenderer
15+
import java.awt.Dimension
16+
import javax.swing.*
1217

1318
class LookupListCellRenderer : ListCellRenderer<LookupItem> {
14-
19+
1520
override fun getListCellRendererComponent(
1621
list: JList<out LookupItem>,
1722
value: LookupItem,
@@ -20,26 +25,73 @@ class LookupListCellRenderer : ListCellRenderer<LookupItem> {
2025
cellHasFocus: Boolean
2126
): Component {
2227
val panel = JPanel(BorderLayout()).apply {
23-
border = JBUI.Borders.empty(4, 8)
28+
preferredSize = Dimension(list.width, ITEM_HEIGHT)
29+
border = JBUI.Borders.empty(0, 0, 0, 0)
2430
}
2531

2632
val component = SimpleColoredComponent().apply {
2733
icon = value.icon
28-
append(value.displayName, SimpleTextAttributes.REGULAR_ATTRIBUTES)
34+
iconTextGap = ICON_TEXT_GAP
2935
isOpaque = false
36+
ipad = JBUI.insets(TOP_BOTTOM_MARGIN, LEFT_MARGIN, TOP_BOTTOM_MARGIN, 0)
37+
38+
when (value) {
39+
is LoadingLookupItem -> {
40+
append(value.displayName, SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES)
41+
}
42+
43+
is LookupGroupItem -> {
44+
append(value.displayName, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES)
45+
}
46+
47+
else -> {
48+
append(value.displayName, SimpleTextAttributes.REGULAR_ATTRIBUTES)
49+
}
50+
}
3051
}
3152

3253
panel.add(component, BorderLayout.CENTER)
3354

55+
if (value is LookupGroupItem) {
56+
val arrowLabel = JLabel().apply {
57+
icon = AllIcons.Icons.Ide.NextStep
58+
horizontalAlignment = SwingConstants.CENTER
59+
verticalAlignment = SwingConstants.CENTER
60+
border = JBUI.Borders.empty(0, JBUIScale.scale(4), 0, RIGHT_MARGIN)
61+
isOpaque = false
62+
63+
if (isSelected) {
64+
foreground = UIUtil.getListSelectionForeground(true)
65+
} else {
66+
foreground = UIUtil.getListForeground()
67+
}
68+
}
69+
panel.add(arrowLabel, BorderLayout.EAST)
70+
} else {
71+
val spacer = Box.createHorizontalStrut(RIGHT_MARGIN + JBUIScale.scale(16))
72+
panel.add(spacer, BorderLayout.EAST)
73+
}
74+
3475
if (isSelected) {
35-
panel.background = list.selectionBackground
36-
component.foreground = list.selectionForeground
76+
panel.background = UIUtil.getListSelectionBackground(true)
77+
component.foreground = UIUtil.getListSelectionForeground(true)
3778
} else {
38-
panel.background = list.background
39-
component.foreground = list.foreground
79+
panel.background = UIUtil.getListBackground()
80+
component.foreground = when (value) {
81+
is LoadingLookupItem -> JBColor.GRAY
82+
else -> UIUtil.getListForeground()
83+
}
4084
}
4185

4286
panel.isOpaque = true
4387
return panel
4488
}
89+
90+
private companion object {
91+
val ITEM_HEIGHT = JBUIScale.scale(20)
92+
val ICON_TEXT_GAP = JBUIScale.scale(4)
93+
val LEFT_MARGIN = JBUIScale.scale(8)
94+
val RIGHT_MARGIN = JBUIScale.scale(8)
95+
val TOP_BOTTOM_MARGIN = JBUIScale.scale(2)
96+
}
4597
}

0 commit comments

Comments
 (0)