diff --git a/packages/kilo-jetbrains/AGENTS.md b/packages/kilo-jetbrains/AGENTS.md index c3e6df981c7..30548b1221e 100644 --- a/packages/kilo-jetbrains/AGENTS.md +++ b/packages/kilo-jetbrains/AGENTS.md @@ -384,23 +384,18 @@ Use `Align` (`ai.kilocode.client.ui.Align`) when a single Swing component must b "Bounded preferred" means the child's preferred size coerced into the effective `[min, max]` range. If available space is smaller than the effective minimum, the layout shrinks the child to available space to avoid overflow. -**Kotlin-style factory extensions** on `Component`: +**Factory extension** on `Component`: ```kotlin -child.align(HAlign.LEFT, VAlign.TOP) // explicit modes -child.alignCenter() // CENTER / CENTER (replaces CenterShrinkPanel) -child.alignLeft(VAlign.CENTER) // LEFT + custom V -child.alignRight(VAlign.CENTER) // RIGHT + custom V -child.alignTop(HAlign.CENTER) // TOP + custom H -child.alignBottom() // BOTTOM + FIT horizontal -child.track() // TRACK / TRACK — always fills all space -child.trackX(VAlign.TOP) // TRACK horizontal, TOP vertical -child.trackY(HAlign.CENTER) // CENTER horizontal, TRACK vertical +child.align(HAlign.LEFT, VAlign.TOP) // left-aligned, top-pinned +child.align(HAlign.CENTER, VAlign.CENTER) // centered (replaces CenterShrinkPanel) +child.align(HAlign.TRACK, VAlign.CENTER) // fill width, center vertically +child.align(HAlign.TRACK, VAlign.TRACK) // fill all available space ``` **Rules:** -- Prefer the factory extensions over creating one-off `JPanel(FlowLayout(...))` or `BorderLayoutPanel` wrappers just to control alignment. +- Prefer `child.align(h, v)` over creating one-off `JPanel(FlowLayout(...))` or `BorderLayoutPanel` wrappers just to control alignment. - Use `TRACK` when the child must occupy all available space on an axis and must not reserve any space in the parent's size negotiation on that axis. Use `FIT` when you want to fill available space but still respect child min/max constraints. - All non-TRACK modes include the child's min, preferred, and max sizes in the wrapper's own min/preferred/max size. This means parent layout managers see the child constraints through the wrapper. - Do not use `Align` for spacing, padding, borders, colors, or multi-child layout — use `JBUI.Borders.empty(...)`, `UiStyle.Gap`, or an appropriate layout manager for those concerns. diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/EmptySessionPanel.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/EmptySessionPanel.kt index 6f83dc6bc51..aca701f9aa1 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/EmptySessionPanel.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/ui/EmptySessionPanel.kt @@ -11,8 +11,10 @@ import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget import ai.kilocode.client.session.ui.style.SessionUiStyle import ai.kilocode.client.session.controller.SessionController import ai.kilocode.client.ui.Align +import ai.kilocode.client.ui.HAlign import ai.kilocode.client.ui.UiStyle -import ai.kilocode.client.ui.alignCenter +import ai.kilocode.client.ui.VAlign +import ai.kilocode.client.ui.align import ai.kilocode.rpc.dto.SessionDto import com.intellij.icons.AllIcons import com.intellij.openapi.Disposable @@ -53,7 +55,7 @@ class EmptySessionPanel( recents: List, private val history: () -> Unit = {}, ) : BorderLayoutPanel(), Disposable, SessionEditorStyleTarget { - val view: Align = alignCenter() + val view: Align = align(HAlign.CENTER, VAlign.CENTER) private val model = DefaultListModel() private var hover = -1 @@ -134,7 +136,7 @@ class EmptySessionPanel( val header = BorderLayoutPanel(0, gap).apply { isOpaque = false add(logo, BorderLayout.NORTH) - add(description.alignCenter(), BorderLayout.CENTER) + add(description.align(HAlign.CENTER, VAlign.CENTER), BorderLayout.CENTER) } val recent = BorderLayoutPanel().apply { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PermissionView.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PermissionView.kt index db375b29b4c..3f18126ed4b 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PermissionView.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/session/views/PermissionView.kt @@ -10,7 +10,10 @@ import ai.kilocode.client.session.ui.style.SessionEditorStyle import ai.kilocode.client.session.ui.style.SessionEditorStyleTarget import ai.kilocode.client.session.ui.style.SessionUiStyle import ai.kilocode.client.session.ui.style.SessionUiStyle.View.CARD_LAYOUT_GAP +import ai.kilocode.client.ui.HAlign import ai.kilocode.client.ui.UiStyle +import ai.kilocode.client.ui.VAlign +import ai.kilocode.client.ui.align import ai.kilocode.rpc.dto.PermissionReplyDto import com.intellij.icons.AllIcons import com.intellij.ui.ColorUtil @@ -25,7 +28,6 @@ import java.awt.BorderLayout import java.awt.Component import java.awt.FlowLayout import javax.swing.BoxLayout -import javax.swing.JComponent import javax.swing.JPanel import javax.swing.text.html.StyleSheet @@ -128,37 +130,30 @@ class PermissionView( val actionLbl = JBLabel(action).apply { font = UiStyle.Fonts.bold() - alignmentY = Component.CENTER_ALIGNMENT } - row.add(actionLbl, BorderLayout.WEST) + row.add(actionLbl.align(HAlign.LEFT, VAlign.CENTER), BorderLayout.WEST) if (!target.isNullOrBlank()) { val pane = targetPane(target) panes.add(pane) - row.add(pane, BorderLayout.CENTER) + row.add(pane.align(HAlign.TRACK, VAlign.CENTER), BorderLayout.CENTER) } if (diffs.isNotEmpty()) { - val changes = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply { + val changes = JPanel(FlowLayout(FlowLayout.LEFT, 0, 0)).apply { isOpaque = false - alignmentY = Component.CENTER_ALIGNMENT } for (diff in diffs) { val dv = PermissionDiffView(diff) diffViews.add(dv) changes.add(dv) } - row.add(changes, BorderLayout.EAST) + row.add(changes.align(HAlign.RIGHT, VAlign.CENTER), BorderLayout.EAST) } body.add(row) } - private fun JComponent.withGap(left: Int, right: Boolean) = JBUI.Panels.simplePanel(this).apply { - isOpaque = false - border = JBUI.Borders.empty(0, left, 0, if (right) UiStyle.Gap.sm() else 0) - } - private fun targetPane(text: String) = JBHtmlPane( JBHtmlPaneStyleConfiguration {}, JBHtmlPaneConfiguration { diff --git a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/Align.kt b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/Align.kt index c1669e8876f..e54f94f97cd 100644 --- a/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/Align.kt +++ b/packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/ui/Align.kt @@ -23,13 +23,12 @@ enum class VAlign { TRACK, FIT, TOP, CENTER, BOTTOM } * Wrapper min/preferred/max sizes are computed by combining the per-axis child contribution * (zero for TRACK axes) with the panel insets. * - * Use the Kotlin-style factory extensions for concise call sites: + * Use the factory extension for concise call sites: * ``` - * label.alignCenter() - * button.alignRight(VAlign.CENTER) + * label.align(HAlign.CENTER, VAlign.CENTER) + * button.align(HAlign.RIGHT, VAlign.CENTER) * panel.align(HAlign.LEFT, VAlign.TOP) - * content.track() - * scrollable.trackX(VAlign.TOP) + * scrollable.align(HAlign.TRACK, VAlign.TOP) * ``` */ class Align( @@ -130,23 +129,7 @@ private fun placeAxis(mode: Any, avail: Int, min: Int, pref: Int, max: Int): Pai private fun bounded(value: Int, min: Int, max: Int) = value.coerceIn(min, maxOf(min, max)) // --------------------------------------------------------------------------- -// Kotlin-style factory extensions +// Factory extension // --------------------------------------------------------------------------- -fun Component.align(h: HAlign = HAlign.FIT, v: VAlign = VAlign.FIT) = Align(this, h, v) - -fun Component.alignCenter() = Align(this, HAlign.CENTER, VAlign.CENTER) - -fun Component.alignLeft(v: VAlign = VAlign.FIT) = Align(this, HAlign.LEFT, v) - -fun Component.alignRight(v: VAlign = VAlign.FIT) = Align(this, HAlign.RIGHT, v) - -fun Component.alignTop(h: HAlign = HAlign.FIT) = Align(this, h, VAlign.TOP) - -fun Component.alignBottom(h: HAlign = HAlign.FIT) = Align(this, h, VAlign.BOTTOM) - -fun Component.track() = Align(this, HAlign.TRACK, VAlign.TRACK) - -fun Component.trackX(v: VAlign = VAlign.FIT) = Align(this, HAlign.TRACK, v) - -fun Component.trackY(h: HAlign = HAlign.FIT) = Align(this, h, VAlign.TRACK) +fun Component.align(h: HAlign, v: VAlign) = Align(this, h, v) diff --git a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/AlignTest.kt b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/AlignTest.kt index 841e9f237e2..de78c958739 100644 --- a/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/AlignTest.kt +++ b/packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/ui/AlignTest.kt @@ -298,56 +298,56 @@ class AlignTest : BasePlatformTestCase() { assertEquals(30 + ins.top + ins.bottom, ps.height) } - // ------ factory helpers ------ + // ------ align() factory ------ fun `test align extension returns Align wrapping child`() { val child = JBLabel("x") assertSame(child, child.align(HAlign.LEFT, VAlign.TOP).getComponent(0)) } - fun `test alignCenter produces CENTER CENTER`() { + fun `test align CENTER CENTER produces centered layout`() { val child = child(pref = 40 x 20) - val wrap = child.alignCenter() + val wrap = child.align(HAlign.CENTER, VAlign.CENTER) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(80, 40, 40, 20, child) } - fun `test alignRight produces RIGHT with given VAlign`() { + fun `test align RIGHT TOP positions at top-right`() { val child = child(pref = 40 x 20) - val wrap = child.alignRight(VAlign.TOP) + val wrap = child.align(HAlign.RIGHT, VAlign.TOP) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(160, 0, 40, 20, child) } - fun `test alignLeft with default FIT vertical fills height`() { + fun `test align LEFT FIT fills height`() { val child = child(pref = 40 x 20) - val wrap = child.alignLeft() + val wrap = child.align(HAlign.LEFT, VAlign.FIT) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(0, 0, 40, 100, child) } - fun `test alignTop with CENTER horizontal centers and pins to top`() { + fun `test align CENTER TOP centers horizontally and pins to top`() { val child = child(pref = 40 x 20) - val wrap = child.alignTop(HAlign.CENTER) + val wrap = child.align(HAlign.CENTER, VAlign.TOP) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(80, 0, 40, 20, child) } - fun `test alignBottom with default FIT horizontal fills width and pins to bottom`() { + fun `test align FIT BOTTOM fills width and pins to bottom`() { val child = child(pref = 40 x 20) - val wrap = child.alignBottom() + val wrap = child.align(HAlign.FIT, VAlign.BOTTOM) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(0, 80, 200, 20, child) } - fun `test track fills all space and wrapper preferred is inset-only`() { + fun `test align TRACK TRACK fills all space and wrapper preferred is inset-only`() { val child = child(pref = 40 x 20, max = 60 x 30) - val wrap = child.track() + val wrap = child.align(HAlign.TRACK, VAlign.TRACK) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(0, 0, 200, 100, child) @@ -356,17 +356,17 @@ class AlignTest : BasePlatformTestCase() { assertEquals(ins.top + ins.bottom, wrap.preferredSize.height) } - fun `test trackX fills width only, V respects preferred`() { + fun `test align TRACK TOP fills width only, V respects preferred`() { val child = child(pref = 40 x 20) - val wrap = child.trackX(VAlign.TOP) + val wrap = child.align(HAlign.TRACK, VAlign.TOP) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(0, 0, 200, 20, child) } - fun `test trackY fills height only, H respects preferred`() { + fun `test align CENTER TRACK fills height only, H respects preferred`() { val child = child(pref = 40 x 20) - val wrap = child.trackY(HAlign.CENTER) + val wrap = child.align(HAlign.CENTER, VAlign.TRACK) wrap.setBounds(0, 0, 200, 100) wrap.doLayout() assertBounds(80, 0, 40, 100, child)