mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-01 04:46:43 +08:00
fix(jetbrains): keep active list section rows compact
Use equal-height ActiveList rows by default while preserving variable-height provider settings. When sections are present, equalize only the row body so headers such as Today and Yesterday do not inflate every row in worktree session lists.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/kilo-jetbrains": patch
|
||||
---
|
||||
|
||||
Use equal-height rows by default in Agent Manager and settings lists, keeping variable-height rows only for provider settings.
|
||||
+1
-1
@@ -352,7 +352,7 @@ internal class SkillSourcesView(
|
||||
) : Stack(ai.kilocode.client.ui.layout.StackAxis.VERTICAL, UiStyle.Gap.sm()) {
|
||||
private val view = ActiveListView(
|
||||
KiloBundle.message("settings.agentBehavior.skills.sources.empty"),
|
||||
ActiveListConfig.Preferred.copy(description = false, selection = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION),
|
||||
ActiveListConfig(description = false, selection = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION),
|
||||
) { key, id ->
|
||||
if (id == EDIT_CELL) edit(key)
|
||||
}
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ internal data class ActiveListBadge(val text: String, val style: UiStyle.Badge.S
|
||||
internal enum class ActiveListRowHeight { EQUAL, PREFERRED }
|
||||
|
||||
internal data class ActiveListConfig(
|
||||
val height: ActiveListRowHeight,
|
||||
val height: ActiveListRowHeight = ActiveListRowHeight.EQUAL,
|
||||
val description: Boolean = true,
|
||||
val descriptionIndent: Boolean = true,
|
||||
val tooltip: Boolean = true,
|
||||
|
||||
+28
-3
@@ -15,6 +15,7 @@ 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.Dimension
|
||||
import javax.swing.JList
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.ListCellRenderer
|
||||
@@ -24,7 +25,8 @@ internal class ActiveListRenderer(
|
||||
private val model: CollectionListModel<ActiveListItem>,
|
||||
private val cfg: ActiveListConfig = ActiveListConfig.Equal,
|
||||
) : JPanel(BorderLayout()), ListCellRenderer<ActiveListItem> {
|
||||
private val sep = GroupHeaderSeparator(JBUI.CurrentTheme.Popup.separatorLabelInsets())
|
||||
private val insets = JBUI.CurrentTheme.Popup.separatorLabelInsets()
|
||||
private val sep = GroupHeaderSeparator(insets)
|
||||
private val top = JPanel(BorderLayout()).apply {
|
||||
border = JBUI.Borders.empty()
|
||||
add(sep, BorderLayout.NORTH)
|
||||
@@ -55,6 +57,7 @@ internal class ActiveListRenderer(
|
||||
add(actions, BorderLayout.EAST)
|
||||
}
|
||||
private val wrap = PickerRow()
|
||||
private var bodyHeight: Int? = null
|
||||
|
||||
init {
|
||||
isOpaque = true
|
||||
@@ -81,8 +84,7 @@ internal class ActiveListRenderer(
|
||||
val active = selected && (focused || list.hasFocus() || (list as? ActiveListActive)?.active() == true)
|
||||
val fg = UIUtil.getListForeground(active, active || focused)
|
||||
val weak = if (active) fg else UiStyle.Colors.weak()
|
||||
val current = model.items.getOrNull(index)
|
||||
val section = if (current === value) activeListSectionTitle(model.items, index) else null
|
||||
val section = activeListSectionTitle(model.items, index)
|
||||
|
||||
background = list.background
|
||||
top.background = list.background
|
||||
@@ -90,6 +92,7 @@ internal class ActiveListRenderer(
|
||||
sep.caption = section
|
||||
sep.setHideLine(index == 0)
|
||||
top.isVisible = section != null
|
||||
top.setPreferredSize(section?.let { Dimension(0, sep.getFontMetrics(sep.font).height + insets.top + insets.bottom) })
|
||||
|
||||
title.clear()
|
||||
title.append(value.title, SimpleTextAttributes(SimpleTextAttributes.STYLE_BOLD, fg))
|
||||
@@ -118,10 +121,32 @@ internal class ActiveListRenderer(
|
||||
syncCells(value, active && list.isEnabled, list.isEnabled)
|
||||
cellPane.isVisible = cells.isVisible
|
||||
actions.isVisible = trail.isVisible || cellPane.isVisible
|
||||
val height = bodyHeight
|
||||
wrap.setPreferredSize(height?.let { Dimension(0, it) })
|
||||
top.invalidate()
|
||||
return this
|
||||
}
|
||||
|
||||
fun setBodyHeight(height: Int?) {
|
||||
if (bodyHeight == height) return
|
||||
bodyHeight = height
|
||||
}
|
||||
|
||||
fun bodyPreferredHeight(
|
||||
list: JList<out ActiveListItem>,
|
||||
value: ActiveListItem,
|
||||
index: Int,
|
||||
selected: Boolean,
|
||||
focused: Boolean,
|
||||
): Int {
|
||||
val fixed = bodyHeight
|
||||
bodyHeight = null
|
||||
getListCellRendererComponent(list, value, index, selected, focused)
|
||||
val height = wrap.preferredSize.height
|
||||
bodyHeight = fixed
|
||||
return height
|
||||
}
|
||||
|
||||
private fun syncBadges(item: ActiveListItem) {
|
||||
val items = item.badges
|
||||
while (badges.componentCount > items.size) badges.remove(badges.componentCount - 1)
|
||||
|
||||
+16
-1
@@ -42,6 +42,7 @@ internal class ActiveListView(
|
||||
private val onCell: (String, String) -> Unit,
|
||||
) : Stack(StackAxis.VERTICAL), Scrollable {
|
||||
private val model = CollectionListModel<ActiveListItem>()
|
||||
private val renderer = ActiveListRenderer(model, cfg)
|
||||
internal val list: JBList<ActiveListItem> = object : JBList<ActiveListItem>(model), ActiveListActive {
|
||||
override fun active(): Boolean = popups > 0
|
||||
|
||||
@@ -82,7 +83,7 @@ internal class ActiveListView(
|
||||
|
||||
init {
|
||||
list.putClientProperty(AnimatedIcon.ANIMATION_IN_RENDERER_ALLOWED, true)
|
||||
list.cellRenderer = ActiveListRenderer(model, cfg)
|
||||
list.cellRenderer = renderer
|
||||
list.registerKeyboardAction(
|
||||
{ open(enter()) },
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),
|
||||
@@ -314,12 +315,26 @@ internal class ActiveListView(
|
||||
@RequiresEdt
|
||||
private fun syncCellHeight(rows: List<ActiveListItem>) {
|
||||
checkEdt()
|
||||
renderer.setBodyHeight(null)
|
||||
if (cfg.height == ActiveListRowHeight.PREFERRED) {
|
||||
if (list.fixedCellHeight == -1) return
|
||||
list.fixedCellHeight = -1
|
||||
list.revalidate()
|
||||
return
|
||||
}
|
||||
if (rows.any { it.section != null }) {
|
||||
val height = rows.indices.maxOfOrNull { idx ->
|
||||
renderer.bodyPreferredHeight(list, rows[idx], idx, true, true)
|
||||
}
|
||||
renderer.setBodyHeight(height)
|
||||
if (list.fixedCellHeight == -1) {
|
||||
list.revalidate()
|
||||
return
|
||||
}
|
||||
list.fixedCellHeight = -1
|
||||
list.revalidate()
|
||||
return
|
||||
}
|
||||
val height = rows.indices.maxOfOrNull { idx ->
|
||||
list.cellRenderer.getListCellRendererComponent(list, rows[idx], idx, true, true).preferredSize.height
|
||||
} ?: -1
|
||||
|
||||
+31
@@ -11,6 +11,7 @@ import ai.kilocode.client.ui.list.ActiveListCell
|
||||
import ai.kilocode.client.ui.list.ActiveListConfig
|
||||
import ai.kilocode.client.ui.list.ActiveListItem
|
||||
import ai.kilocode.client.ui.list.ActiveListRenderer
|
||||
import ai.kilocode.client.ui.list.ActiveListRowHeight
|
||||
import ai.kilocode.client.ui.list.ActiveListSelection
|
||||
import ai.kilocode.client.ui.list.ActiveListView
|
||||
import ai.kilocode.client.ui.list.activeListCellAt
|
||||
@@ -84,6 +85,30 @@ class SettingsListViewTest : BasePlatformTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
fun `test active list config defaults to equal row height`() {
|
||||
assertEquals(ActiveListRowHeight.EQUAL, ActiveListConfig().height)
|
||||
assertEquals(ActiveListRowHeight.PREFERRED, ActiveListConfig.Preferred.height)
|
||||
}
|
||||
|
||||
fun `test equal rows keep section headers out of body height`() {
|
||||
edt {
|
||||
val view = ActiveListView("Empty") { _, _ -> }
|
||||
view.update(listOf(
|
||||
sectionItem("first", "First", "Today"),
|
||||
sectionItem("second", "Second", "Today"),
|
||||
sectionItem("third", "Third", "Yesterday"),
|
||||
))
|
||||
layout(view)
|
||||
|
||||
val first = view.list.getCellBounds(0, 0)
|
||||
val second = view.list.getCellBounds(1, 1)
|
||||
val third = view.list.getCellBounds(2, 2)
|
||||
|
||||
assertTrue("fixed=${view.list.fixedCellHeight} first=${first.height} second=${second.height} third=${third.height}", first.height > second.height)
|
||||
assertTrue("fixed=${view.list.fixedCellHeight} first=${first.height} second=${second.height} third=${third.height}", third.height > second.height)
|
||||
}
|
||||
}
|
||||
|
||||
fun `test filtering recalculates equal row height for visible rows`() {
|
||||
edt {
|
||||
val view = ActiveListView("Empty") { _, _ -> }
|
||||
@@ -501,6 +526,12 @@ class SettingsListViewTest : BasePlatformTestCase() {
|
||||
override val cells = cells.toList()
|
||||
}
|
||||
|
||||
private fun sectionItem(id: String, name: String, group: String) = object : ActiveListItem {
|
||||
override val key = id
|
||||
override val title = name
|
||||
override val section = group
|
||||
}
|
||||
|
||||
private fun layout(view: ActiveListView) {
|
||||
view.list.size = Dimension(320, 160)
|
||||
view.list.doLayout()
|
||||
|
||||
Reference in New Issue
Block a user