fix(jetbrains): make prompt controls native

This commit is contained in:
kirillk
2026-05-03 00:01:43 -04:00
parent 16548c7488
commit 7d2cb5ed6e
5 changed files with 128 additions and 22 deletions
@@ -21,6 +21,7 @@ import javax.swing.Box
import javax.swing.BoxLayout
import javax.swing.Icon
import javax.swing.JButton
import javax.swing.JComponent
import javax.swing.ScrollPaneConstants
/**
@@ -90,13 +91,11 @@ class PromptPanel(
}
}
private val reset = JButton(AllIcons.Actions.Cancel).apply {
UiStyle.Buttons.icon(this)
isFocusPainted = false
private val reset = UiStyle.Buttons.HoverIcon().apply {
icon = AllIcons.Actions.Cancel
toolTipText = KiloBundle.message("model.picker.reset")
accessibleContext.accessibleName = KiloBundle.message("model.picker.reset")
isVisible = false
preferredSize = JBUI.size(UiStyle.Size.BUTTON, UiStyle.Size.BUTTON)
addActionListener { onReset() }
}
@@ -115,11 +114,11 @@ class PromptPanel(
border = JBUI.Borders.emptyTop(UiStyle.Space.SM)
}
bar.add(mode)
bar.add(Box.createHorizontalStrut(UiStyle.Gap.small()))
bar.add(Box.createHorizontalStrut(UiStyle.Gap.xs()))
bar.add(model)
bar.add(Box.createHorizontalStrut(UiStyle.Gap.small()))
bar.add(Box.createHorizontalStrut(UiStyle.Gap.xs()))
bar.add(reasoning)
bar.add(Box.createHorizontalStrut(UiStyle.Gap.small()))
bar.add(Box.createHorizontalStrut(UiStyle.Gap.xs()))
bar.add(reset)
bar.add(Box.createHorizontalGlue())
bar.add(button)
@@ -152,6 +151,8 @@ class PromptPanel(
internal fun resetVisibleForTest() = reset.isVisible
internal fun resetForTest(): JComponent = reset
override fun applyStyle(style: SessionStyle) {
this.style = style
editor.font = style.transcriptFont
@@ -7,7 +7,6 @@ import com.intellij.openapi.ui.popup.ListPopup
import com.intellij.openapi.ui.popup.PopupShowOptions
import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.EmptyIcon
import java.awt.Cursor
import java.awt.event.MouseAdapter
@@ -15,13 +14,13 @@ import java.awt.event.MouseEvent
import javax.swing.Icon
/**
* Clickable label-style dropdown picker with a rounded outline.
* Clickable label-style dropdown picker with a native filled background.
*
* Shows the selected item's display text with an up-arrow. On click,
* opens a list popup above the picker. Disabled (greyed out, not
* clickable) when no items are loaded.
*/
class ReasoningPicker : JBLabel() {
class ReasoningPicker : UiStyle.Pickers.Label() {
private companion object {
val checked: Icon = AllIcons.Actions.Checked
@@ -38,7 +37,6 @@ class ReasoningPicker : JBLabel() {
private var selected: Item? = null
init {
border = UiStyle.Borders.picker()
isEnabled = false
isVisible = false
text = " "
@@ -3,13 +3,12 @@ package ai.kilocode.client.session.ui.mode
import ai.kilocode.client.ui.UiStyle
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.PopupShowOptions
import com.intellij.ui.components.JBLabel
import java.awt.Cursor
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.ListSelectionModel
class ModePicker : JBLabel() {
class ModePicker : UiStyle.Pickers.Label() {
data class Item(
val id: String,
@@ -26,7 +25,6 @@ class ModePicker : JBLabel() {
private var selected: Item? = null
init {
border = UiStyle.Borders.picker()
isEnabled = false
text = " "
@@ -15,7 +15,6 @@ import com.intellij.ui.ListUtil
import com.intellij.ui.SearchTextField
import com.intellij.ui.ScrollPaneFactory
import com.intellij.ui.ScrollingUtil
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBList
import com.intellij.ui.popup.AbstractPopup
import com.intellij.util.ui.JBUI
@@ -46,7 +45,7 @@ private const val MODEL_PICKER_MAX_WIDTH = 760
private const val MODEL_PICKER_MAX_VISIBLE_ROWS = 10
private const val MODEL_PICKER_EMPTY_LIST_HEIGHT = 120
class ModelPicker : JBLabel() {
class ModelPicker : UiStyle.Pickers.Label() {
data class Item(
val id: String,
@@ -70,7 +69,6 @@ class ModelPicker : JBLabel() {
private var selected: Item? = null
init {
border = UiStyle.Borders.picker()
isEnabled = false
text = " "
@@ -2,11 +2,17 @@ package ai.kilocode.client.ui
import com.intellij.openapi.editor.colors.EditorColorsManager
import com.intellij.ui.JBColor
import com.intellij.ui.RoundedLineBorder
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Dimension
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.RenderingHints
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.JButton
import javax.swing.JComponent
import javax.swing.UIManager
@@ -69,6 +75,14 @@ object UiStyle {
fun running(): Color = JBColor.namedColor("ProgressBar.foreground", UIUtil.getLabelForeground())
fun picker(): Color = JBColor.lazy {
UIManager.getColor("ComboBoxButton.background")
?: UIManager.getColor("ComboBox.nonEditableBackground")
?: UIUtil.getPanelBackground()
}
fun pickerHover(): Color = JBUI.CurrentTheme.ActionButton.hoverBackground()
internal fun contrast(base: Color, delta: Int): Color {
val step = if (bright(base)) -delta else delta
return Color(
@@ -116,10 +130,7 @@ object UiStyle {
fun warning(): Border = JBUI.Borders.customLine(Colors.warning(), 1)
fun picker(): Border = JBUI.Borders.compound(
RoundedLineBorder(Colors.line(), JBUI.scale(Space.MD)),
JBUI.Borders.empty(Space.XS, Space.LG),
)!!
fun picker(): Border = JBUI.Borders.empty(Space.XS, Space.LG)
fun user(): Border = JBUI.Borders.compound(
JBUI.Borders.customLineTop(Colors.line()),
@@ -167,6 +178,8 @@ object UiStyle {
}
object Gap {
fun xs() = JBUI.scale(Space.XS)
fun inline() = JBUI.scale(Space.MD)
fun regular() = JBUI.scale(Space.LG)
@@ -183,6 +196,57 @@ object UiStyle {
}
object Buttons {
class HoverIcon : JButton() {
private var over = false
init {
isFocusable = false
setRequestFocusEnabled(false)
isContentAreaFilled = false
isBorderPainted = false
isOpaque = false
border = JBUI.Borders.empty()
addMouseListener(object : MouseAdapter() {
override fun mouseEntered(e: MouseEvent) {
sync(true)
}
override fun mouseExited(e: MouseEvent) {
sync(false)
}
})
}
override fun getPreferredSize(): Dimension = JBUI.size(Size.BUTTON, Size.BUTTON)
override fun getMinimumSize(): Dimension = preferredSize
override fun getMaximumSize(): Dimension = preferredSize
override fun paintComponent(g: Graphics) {
if (isEnabled && over) paintHover(g)
super.paintComponent(g)
}
private fun paintHover(g: Graphics) {
val g2 = g.create() as Graphics2D
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g2.color = JBUI.CurrentTheme.ActionButton.hoverBackground()
val arc = JBUI.scale(JBUI.getInt("Button.arc", 6))
g2.fillRoundRect(0, 0, width, height, arc, arc)
} finally {
g2.dispose()
}
}
private fun sync(value: Boolean) {
if (over == value) return
over = value
repaint()
}
}
fun icon(button: JButton) {
button.isFocusable = false
button.setRequestFocusEnabled(false)
@@ -193,6 +257,53 @@ object UiStyle {
}
}
object Pickers {
open class Label : JBLabel() {
private var over = false
init {
border = Borders.picker()
background = Colors.picker()
// The custom rounded fill needs parent background around the corners.
isOpaque = false
addMouseListener(object : MouseAdapter() {
override fun mouseEntered(e: MouseEvent) {
sync(true)
}
override fun mouseExited(e: MouseEvent) {
sync(false)
}
})
}
override fun updateUI() {
super.updateUI()
border = Borders.picker()
background = Colors.picker()
}
override fun paintComponent(g: Graphics) {
val g2 = g.create() as Graphics2D
try {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g2.color = if (isEnabled && over) Colors.pickerHover() else Colors.picker()
val arc = JBUI.scale(JBUI.getInt("Button.arc", 6))
g2.fillRoundRect(0, 0, width, height, arc, arc)
} finally {
g2.dispose()
}
super.paintComponent(g)
}
private fun sync(value: Boolean) {
if (over == value) return
over = value
repaint()
}
}
}
object Components {
fun transparent(component: JComponent) {
component.isOpaque = false