Skip to content

Commit 1981d60

Browse files
committed
feat(gui): enhance planner result summary with actions and details #352
- Add global actions: Discard all and Accept all- Implement per-change actions: View, Discard, Accept, and Remove- Improve UI layout and styling for better readability - Introduce ChangeActionListener and GlobalActionListener interfaces
1 parent c33eac1 commit 1981d60

File tree

1 file changed

+183
-21
lines changed

1 file changed

+183
-21
lines changed
Lines changed: 183 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,43 @@
11
package cc.unitmesh.devti.gui.planner
22

3+
import com.intellij.icons.AllIcons
34
import com.intellij.openapi.project.Project
45
import com.intellij.openapi.vcs.changes.Change
6+
import com.intellij.ui.HyperlinkLabel
57
import com.intellij.ui.components.JBLabel
68
import com.intellij.ui.components.JBScrollPane
79
import com.intellij.util.ui.JBUI
810
import com.intellij.util.ui.UIUtil
911
import java.awt.BorderLayout
12+
import java.awt.FlowLayout
1013
import java.awt.GridLayout
14+
import java.awt.event.MouseAdapter
15+
import java.awt.event.MouseEvent
1116
import javax.swing.JPanel
17+
import javax.swing.event.HyperlinkEvent
18+
import javax.swing.event.HyperlinkListener
1219

1320
class PlannerResultSummary(
1421
private val project: Project,
1522
private var changes: List<Change>
1623
) : JPanel(BorderLayout()) {
17-
private val changesPanel = JPanel(GridLayout(0, 1, 0, 5))
24+
private val changesPanel = JPanel(GridLayout(0, 1, 0, 1))
1825
private val statsLabel = JBLabel("No changes")
26+
27+
interface ChangeActionListener {
28+
fun onView(change: Change)
29+
fun onDiscard(change: Change)
30+
fun onAccept(change: Change)
31+
fun onRemove(change: Change)
32+
}
33+
34+
interface GlobalActionListener {
35+
fun onDiscardAll()
36+
fun onAcceptAll()
37+
}
38+
39+
private var changeActionListener: ChangeActionListener? = null
40+
private var globalActionListener: GlobalActionListener? = null
1941

2042
init {
2143
background = JBUI.CurrentTheme.ToolWindow.background()
@@ -24,20 +46,61 @@ class PlannerResultSummary(
2446
val titlePanel = JPanel(BorderLayout()).apply {
2547
isOpaque = false
2648
border = JBUI.Borders.emptyBottom(10)
27-
add(JBLabel("Change list").apply {
28-
foreground = UIUtil.getLabelForeground()
29-
}, BorderLayout.WEST)
30-
add(statsLabel, BorderLayout.EAST)
49+
50+
val titleLabelPanel = JPanel(BorderLayout()).apply {
51+
isOpaque = false
52+
add(JBLabel("Change list").apply {
53+
foreground = UIUtil.getLabelForeground()
54+
font = JBUI.Fonts.label().asBold()
55+
}, BorderLayout.WEST)
56+
add(statsLabel, BorderLayout.EAST)
57+
}
58+
59+
val actionsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0)).apply {
60+
isOpaque = false
61+
62+
val discardAllButton = HyperlinkLabel("Discard all").apply {
63+
icon = AllIcons.Actions.Cancel
64+
addHyperlinkListener(object : HyperlinkListener {
65+
override fun hyperlinkUpdate(e: HyperlinkEvent) {
66+
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
67+
globalActionListener?.onDiscardAll()
68+
}
69+
}
70+
})
71+
}
72+
73+
val acceptAllButton = HyperlinkLabel("Accept all").apply {
74+
icon = AllIcons.Actions.Commit
75+
addHyperlinkListener(object : HyperlinkListener {
76+
override fun hyperlinkUpdate(e: HyperlinkEvent) {
77+
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
78+
globalActionListener?.onAcceptAll()
79+
}
80+
}
81+
})
82+
}
83+
84+
add(discardAllButton)
85+
add(acceptAllButton)
86+
}
87+
88+
add(titleLabelPanel, BorderLayout.WEST)
89+
add(actionsPanel, BorderLayout.EAST)
3190
}
3291

3392
add(titlePanel, BorderLayout.NORTH)
3493

3594
changesPanel.isOpaque = false
36-
add(JBScrollPane(changesPanel).apply {
95+
changesPanel.border = JBUI.Borders.empty(1)
96+
97+
val scrollPane = JBScrollPane(changesPanel).apply {
3798
border = JBUI.Borders.empty()
3899
background = background
39-
}, BorderLayout.CENTER)
40-
100+
viewport.background = background
101+
}
102+
103+
add(scrollPane, BorderLayout.CENTER)
41104
updateChanges(changes.toMutableList())
42105
}
43106

@@ -46,27 +109,19 @@ class PlannerResultSummary(
46109
changesPanel.removeAll()
47110

48111
if (changes.isEmpty()) {
49-
statsLabel.text = "No changes"
112+
statsLabel.text = " No changes"
50113
changesPanel.add(JBLabel("No code changes").apply {
51114
foreground = UIUtil.getLabelDisabledForeground()
115+
border = JBUI.Borders.empty(10)
52116
})
53117
} else {
54-
statsLabel.text = "total ${changes.size} files changed"
55-
118+
statsLabel.text = " (Total ${changes.size} files changed)"
56119
changes.forEach { change ->
57120
val filePath = change.virtualFile?.path ?: "Unknown"
58121
val fileName = filePath.substringAfterLast('/')
59122

60-
val changeType = when {
61-
change.type == Change.Type.NEW -> "Add"
62-
change.type == Change.Type.DELETED -> "Delete"
63-
change.type == Change.Type.MOVED -> "Move"
64-
else -> "Modify"
65-
}
66-
67-
changesPanel.add(JBLabel("$changeType: $fileName").apply {
68-
toolTipText = filePath
69-
})
123+
val changePanel = createChangeItemPanel(change, fileName, filePath)
124+
changesPanel.add(changePanel)
70125
}
71126
}
72127

@@ -77,4 +132,111 @@ class PlannerResultSummary(
77132
revalidate()
78133
repaint()
79134
}
135+
136+
private fun createChangeItemPanel(change: Change, fileName: String, filePath: String): JPanel {
137+
return JPanel(BorderLayout()).apply {
138+
isOpaque = true
139+
background = UIUtil.getListBackground()
140+
border = JBUI.Borders.empty(5, 8)
141+
142+
val changeIcon = when (change.type) {
143+
Change.Type.NEW -> AllIcons.Actions.New
144+
Change.Type.DELETED -> AllIcons.Actions.GC
145+
Change.Type.MOVED -> AllIcons.Actions.Forward
146+
else -> AllIcons.Actions.Edit
147+
}
148+
149+
val infoPanel = JPanel(BorderLayout()).apply {
150+
isOpaque = false
151+
152+
val fileLabel = JBLabel(fileName, changeIcon, JBLabel.LEFT).apply {
153+
toolTipText = filePath
154+
}
155+
156+
add(fileLabel, BorderLayout.CENTER)
157+
}
158+
159+
val actionsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 2, 0)).apply {
160+
isOpaque = false
161+
162+
val viewButton = HyperlinkLabel("").apply {
163+
icon = AllIcons.Actions.Preview
164+
toolTipText = "View changes"
165+
addHyperlinkListener(object : HyperlinkListener {
166+
override fun hyperlinkUpdate(e: HyperlinkEvent) {
167+
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
168+
changeActionListener?.onView(change)
169+
}
170+
}
171+
})
172+
}
173+
174+
val discardButton = HyperlinkLabel("").apply {
175+
icon = AllIcons.Actions.Cancel
176+
toolTipText = "Discard changes"
177+
addHyperlinkListener(object : HyperlinkListener {
178+
override fun hyperlinkUpdate(e: HyperlinkEvent) {
179+
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
180+
changeActionListener?.onDiscard(change)
181+
}
182+
}
183+
})
184+
}
185+
186+
val acceptButton = HyperlinkLabel("").apply {
187+
icon = AllIcons.Actions.Commit
188+
toolTipText = "Accept changes"
189+
addHyperlinkListener(object : HyperlinkListener {
190+
override fun hyperlinkUpdate(e: HyperlinkEvent) {
191+
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
192+
changeActionListener?.onAccept(change)
193+
}
194+
}
195+
})
196+
}
197+
198+
val removeButton = HyperlinkLabel("").apply {
199+
icon = AllIcons.Actions.GC
200+
toolTipText = "Remove from list"
201+
addHyperlinkListener(object : HyperlinkListener {
202+
override fun hyperlinkUpdate(e: HyperlinkEvent) {
203+
if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) {
204+
changeActionListener?.onRemove(change)
205+
}
206+
}
207+
})
208+
}
209+
210+
add(viewButton)
211+
add(discardButton)
212+
add(acceptButton)
213+
add(removeButton)
214+
}
215+
216+
add(infoPanel, BorderLayout.CENTER)
217+
add(actionsPanel, BorderLayout.EAST)
218+
219+
// Add hover effect
220+
addMouseListener(object : MouseAdapter() {
221+
override fun mouseEntered(e: MouseEvent) {
222+
background = UIUtil.getListSelectionBackground(false)
223+
repaint()
224+
}
225+
226+
override fun mouseExited(e: MouseEvent) {
227+
background = UIUtil.getListBackground()
228+
repaint()
229+
}
230+
})
231+
}
232+
}
233+
234+
// Set action listeners
235+
fun setChangeActionListener(listener: ChangeActionListener) {
236+
this.changeActionListener = listener
237+
}
238+
239+
fun setGlobalActionListener(listener: GlobalActionListener) {
240+
this.globalActionListener = listener
241+
}
80242
}

0 commit comments

Comments
 (0)