@@ -11,8 +11,22 @@ import com.intellij.util.ui.JBUI
11
11
import java.awt.*
12
12
import java.awt.event.MouseAdapter
13
13
import java.awt.event.MouseEvent
14
- import javax.swing.BorderFactory
15
14
import javax.swing.JPanel
15
+ import javax.swing.JList
16
+ import javax.swing.ListCellRenderer
17
+ import javax.swing.UIManager
18
+ import javax.swing.JTextField
19
+ import javax.swing.JScrollPane
20
+ import javax.swing.DefaultListModel
21
+ import javax.swing.BorderFactory
22
+ import javax.swing.event.DocumentEvent
23
+ import javax.swing.event.DocumentListener
24
+ import com.intellij.openapi.ui.popup.JBPopupFactory
25
+ import com.intellij.openapi.ui.popup.JBPopup
26
+ import com.intellij.openapi.roots.ProjectRootManager
27
+ import com.intellij.openapi.vfs.VfsUtil
28
+ import com.intellij.util.ui.UIUtil
29
+ import org.jetbrains.annotations.NotNull
16
30
17
31
class WorkspacePanel (
18
32
private val project : Project ,
@@ -24,6 +38,14 @@ class WorkspacePanel(
24
38
init {
25
39
border = JBUI .Borders .empty()
26
40
41
+ filesPanel.isOpaque = false
42
+ filesPanel.add(createAddButton())
43
+
44
+ add(filesPanel, BorderLayout .NORTH )
45
+ isOpaque = false
46
+ }
47
+
48
+ private fun createAddButton (): JBLabel {
27
49
val addButton = JBLabel (AllIcons .General .Add )
28
50
addButton.cursor = Cursor (Cursor .HAND_CURSOR )
29
51
addButton.toolTipText = " Add files to workspace"
@@ -32,15 +54,19 @@ class WorkspacePanel(
32
54
addButton.isOpaque = true
33
55
addButton.addMouseListener(object : MouseAdapter () {
34
56
override fun mouseClicked (e : MouseEvent ) {
35
- addFile( )
57
+ showFileSearchPopup(e.component )
36
58
}
37
59
})
38
-
39
- filesPanel.isOpaque = false
40
- filesPanel.add(addButton)
41
-
42
- add(filesPanel, BorderLayout .NORTH )
43
- isOpaque = false
60
+ return addButton
61
+ }
62
+
63
+ private fun showFileSearchPopup (component : Component ) {
64
+ val popup = FileSearchPopup (project) { files ->
65
+ for (file in files) {
66
+ addFileToWorkspace(file)
67
+ }
68
+ }
69
+ popup.show(component)
44
70
}
45
71
46
72
private fun addFile () {
@@ -65,19 +91,7 @@ class WorkspacePanel(
65
91
66
92
private fun updateFilesPanel () {
67
93
filesPanel.removeAll()
68
-
69
- val addButton = JBLabel (AllIcons .General .Add )
70
- addButton.cursor = Cursor (Cursor .HAND_CURSOR )
71
- addButton.toolTipText = " Add files to workspace"
72
- addButton.border = JBUI .Borders .empty(2 , 4 )
73
- addButton.background = JBColor (0xEDF4FE , 0x313741 )
74
- addButton.isOpaque = true
75
- addButton.addMouseListener(object : MouseAdapter () {
76
- override fun mouseClicked (e : MouseEvent ) {
77
- addFile()
78
- }
79
- })
80
- filesPanel.add(addButton)
94
+ filesPanel.add(createAddButton())
81
95
82
96
for (filePresentation in workspaceFiles) {
83
97
val fileLabel = FileItemPanel (project, filePresentation) {
@@ -111,6 +125,147 @@ class WorkspacePanel(
111
125
}
112
126
}
113
127
128
+ class FileSearchPopup (
129
+ private val project : Project ,
130
+ private val onFilesSelected : (List <VirtualFile >) -> Unit
131
+ ) {
132
+ private var popup: JBPopup ? = null
133
+ private val fileListModel = DefaultListModel <FileItem >()
134
+ private val fileList = JList (fileListModel)
135
+ private val searchField = JTextField ()
136
+ private val contentPanel = JPanel (BorderLayout ())
137
+ private val allProjectFiles = mutableListOf<FileItem >()
138
+
139
+ init {
140
+ loadProjectFiles()
141
+ setupUI()
142
+ }
143
+
144
+ private fun loadProjectFiles () {
145
+ val projectRootManager = ProjectRootManager .getInstance(project)
146
+ val roots = projectRootManager.contentRoots
147
+
148
+ roots.forEach { root ->
149
+ VfsUtil .collectChildrenRecursively(root).forEach { file ->
150
+ if (! file.isDirectory) {
151
+ allProjectFiles.add(FileItem (file))
152
+ }
153
+ }
154
+ }
155
+
156
+ updateFileList(" " )
157
+ }
158
+
159
+ private fun setupUI () {
160
+ // Setup search field
161
+ searchField.document.addDocumentListener(object : DocumentListener {
162
+ override fun insertUpdate (e : DocumentEvent ) = updateSearch()
163
+ override fun removeUpdate (e : DocumentEvent ) = updateSearch()
164
+ override fun changedUpdate (e : DocumentEvent ) = updateSearch()
165
+
166
+ private fun updateSearch () {
167
+ updateFileList(searchField.text)
168
+ }
169
+ })
170
+
171
+ // Setup file list
172
+ fileList.cellRenderer = FileListCellRenderer ()
173
+ fileList.addMouseListener(object : MouseAdapter () {
174
+ override fun mouseClicked (e : MouseEvent ) {
175
+ if (e.clickCount == 2 ) {
176
+ val selectedFiles = fileList.selectedValuesList.map { it.file }
177
+ if (selectedFiles.isNotEmpty()) {
178
+ onFilesSelected(selectedFiles)
179
+ popup?.cancel()
180
+ }
181
+ }
182
+ }
183
+ })
184
+
185
+ // Layout components
186
+ contentPanel.add(searchField, BorderLayout .NORTH )
187
+ contentPanel.add(JScrollPane (fileList), BorderLayout .CENTER )
188
+ contentPanel.preferredSize = Dimension (400 , 300 )
189
+ }
190
+
191
+ private fun updateFileList (searchText : String ) {
192
+ fileListModel.clear()
193
+
194
+ val filteredFiles = if (searchText.isBlank()) {
195
+ allProjectFiles
196
+ } else {
197
+ allProjectFiles.filter { item ->
198
+ item.file.name.contains(searchText, ignoreCase = true ) ||
199
+ item.file.path.contains(searchText, ignoreCase = true )
200
+ }
201
+ }
202
+
203
+ filteredFiles.forEach { fileListModel.addElement(it) }
204
+ }
205
+
206
+ fun show (component : Component ) {
207
+ popup = JBPopupFactory .getInstance()
208
+ .createComponentPopupBuilder(contentPanel, searchField)
209
+ .setTitle(" Search Files" )
210
+ .setMovable(true )
211
+ .setResizable(true )
212
+ .setRequestFocus(true )
213
+ .createPopup()
214
+
215
+ popup?.showUnderneathOf(component)
216
+ }
217
+
218
+ data class FileItem (val file : VirtualFile ) {
219
+ val icon = file.fileType.icon
220
+ val name = file.name
221
+ val path = file.path
222
+ }
223
+
224
+ class FileListCellRenderer : ListCellRenderer <FileItem > {
225
+ private val noBorderFocus = BorderFactory .createEmptyBorder(1 , 1 , 1 , 1 )
226
+
227
+ @NotNull
228
+ override fun getListCellRendererComponent (
229
+ list : JList <out FileItem >? ,
230
+ value : FileItem ? ,
231
+ index : Int ,
232
+ isSelected : Boolean ,
233
+ cellHasFocus : Boolean
234
+ ): Component {
235
+ val panel = JPanel (BorderLayout ())
236
+ value?.let {
237
+ val fileLabel = JBLabel (it.name, it.icon, JBLabel .LEFT )
238
+ val pathLabel = JBLabel (it.path, JBLabel .LEFT )
239
+ pathLabel.font = UIUtil .getFont(UIUtil .FontSize .SMALL , pathLabel.font)
240
+ pathLabel.foreground = UIUtil .getContextHelpForeground()
241
+
242
+ val infoPanel = JPanel (BorderLayout ())
243
+ infoPanel.add(fileLabel, BorderLayout .NORTH )
244
+ infoPanel.add(pathLabel, BorderLayout .SOUTH )
245
+ infoPanel.isOpaque = false
246
+
247
+ panel.add(infoPanel, BorderLayout .CENTER )
248
+
249
+ if (isSelected) {
250
+ panel.background = list?.selectionBackground
251
+ panel.foreground = list?.selectionForeground
252
+ } else {
253
+ panel.background = list?.background
254
+ panel.foreground = list?.foreground
255
+ }
256
+
257
+ panel.border = if (cellHasFocus) {
258
+ UIManager .getBorder(" List.focusCellHighlightBorder" ) ? : noBorderFocus
259
+ } else {
260
+ noBorderFocus
261
+ }
262
+ }
263
+
264
+ return panel
265
+ }
266
+ }
267
+ }
268
+
114
269
class FileItemPanel (
115
270
private val project : Project ,
116
271
private val filePresentation : FilePresentation ,
@@ -227,4 +382,3 @@ class WrapLayout : FlowLayout {
227
382
}
228
383
}
229
384
}
230
-
0 commit comments