11package com.abeade.plugin.figma
22
33import com.abeade.plugin.figma.ui.ImportDialog
4+ import com.abeade.plugin.figma.ui.PreviewPanel
45import com.abeade.plugin.figma.ui.autocomplete.Autocomplete
56import com.abeade.plugin.figma.utils.EMPTY
67import com.abeade.plugin.figma.utils.containsAny
78import com.abeade.plugin.figma.utils.findFirstOf
9+ import com.abeade.plugin.figma.utils.isValidEntry
810import com.intellij.icons.AllIcons
911import com.intellij.ide.util.PropertiesComponent
12+ import com.intellij.notification.Notification
13+ import com.intellij.notification.NotificationType
14+ import com.intellij.notification.Notifications
1015import com.intellij.openapi.ui.DialogWrapper
1116import com.intellij.openapi.ui.ValidationInfo
17+ import com.intellij.openapi.ui.popup.JBPopup
18+ import com.intellij.openapi.ui.popup.JBPopupFactory
1219import com.intellij.ui.IdeBorderFactory
20+ import com.intellij.vcs.commit.NonModalCommitPanel.Companion.showAbove
1321import java.awt.Desktop
22+ import java.awt.Dimension
1423import java.awt.event.*
1524import java.io.File
25+ import java.io.IOException
1626import java.net.URI
1727import java.util.zip.ZipFile
28+ import javax.imageio.ImageIO
1829import javax.swing.*
1930import javax.swing.event.DocumentEvent
2031import javax.swing.event.DocumentListener
@@ -90,6 +101,27 @@ class ImportDialogWrapper(
90101 Desktop .getDesktop().browse(URI (" https://github.com/abeade/figma-import-plugin" ))
91102 }
92103 })
104+ ldpiIconViewLabel.icon = AllIcons .General .InspectionsEye
105+ mdpiIconViewLabel.icon = AllIcons .General .InspectionsEye
106+ hdpiIconViewLabel.icon = AllIcons .General .InspectionsEye
107+ xhdpiIconViewLabel.icon = AllIcons .General .InspectionsEye
108+ xxhdpiIconViewLabel.icon = AllIcons .General .InspectionsEye
109+ xxxhdpiIconViewLabel.icon = AllIcons .General .InspectionsEye
110+ val previewTooltipText = " Click here to view this image"
111+ ldpiIconViewLabel.toolTipText = previewTooltipText
112+ mdpiIconViewLabel.toolTipText = previewTooltipText
113+ hdpiIconViewLabel.toolTipText = previewTooltipText
114+ xhdpiIconViewLabel.toolTipText = previewTooltipText
115+ xxhdpiIconViewLabel.toolTipText = previewTooltipText
116+ xxxhdpiIconViewLabel.toolTipText = previewTooltipText
117+ previewLabel.icon = AllIcons .General .InspectionsEye
118+ previewLabel.toolTipText = " Click here to view the images in this file"
119+ previewLabel.isVisible = false
120+ previewLabel.addMouseListener(object : MouseAdapter () {
121+ override fun mouseClicked (e : MouseEvent ? ) {
122+ file?.let { FilePreviewDialogWrapper (it).show() }
123+ }
124+ })
93125 }
94126 updateLabels()
95127 return dialog.mainPanel
@@ -242,17 +274,25 @@ class ImportDialogWrapper(
242274 updateLabelField(dialog.xxxhdpiLabel, dialog.xxxhdpiField)
243275 zipFilesList?.let {
244276 val skip = propertiesComponent.isTrueValue(SKIP_KEY )
245- updateIconField(dialog.ldpiIconLabel, dialog.ldpiField, it, skip, Density .LDPI )
246- updateIconField(dialog.mdpiIconLabel, dialog.mdpiField, it, skip, Density .MDPI )
247- updateIconField(dialog.hdpiIconLabel, dialog.hdpiField, it, skip, Density .HDPI )
248- updateIconField(dialog.xhdpiIconLabel, dialog.xhdpiField, it, skip, Density .XHDPI )
249- updateIconField(dialog.xxhdpiIconLabel, dialog.xxhdpiField, it, skip, Density .XXHDPI )
250- updateIconField(dialog.xxxhdpiIconLabel, dialog.xxxhdpiField, it, skip, Density .XXXHDPI )
277+ updateIconField(dialog.ldpiIconLabel, dialog.ldpiIconViewLabel, dialog.ldpiField, it, skip, Density .LDPI )
278+ updateIconField(dialog.mdpiIconLabel, dialog.mdpiIconViewLabel, dialog.mdpiField, it, skip, Density .MDPI )
279+ updateIconField(dialog.hdpiIconLabel, dialog.hdpiIconViewLabel, dialog.hdpiField, it, skip, Density .HDPI )
280+ updateIconField(dialog.xhdpiIconLabel, dialog.xhdpiIconViewLabel, dialog.xhdpiField, it, skip, Density .XHDPI )
281+ updateIconField(dialog.xxhdpiIconLabel, dialog.xxhdpiIconViewLabel, dialog.xxhdpiField, it, skip, Density .XXHDPI )
282+ updateIconField(dialog.xxxhdpiIconLabel, dialog.xxxhdpiIconViewLabel, dialog.xxxhdpiField, it, skip, Density .XXXHDPI )
283+ } ? : run {
284+ dialog.ldpiIconViewLabel.isVisible = false
285+ dialog.mdpiIconViewLabel.isVisible = false
286+ dialog.hdpiIconViewLabel.isVisible = false
287+ dialog.xhdpiIconViewLabel.isVisible = false
288+ dialog.xxhdpiIconViewLabel.isVisible = false
289+ dialog.xxxhdpiIconViewLabel.isVisible = false
251290 }
252291 }
253292
254293 private fun updateIconField (
255294 iconLabel : JLabel ,
295+ viewIconLabel : JLabel ,
256296 field : JTextField ,
257297 filesList : MutableList <String >,
258298 skipWhenNotExists : Boolean ,
@@ -262,21 +302,67 @@ class ImportDialogWrapper(
262302 val suffix = field.text
263303 if (suffix.isBlank()) {
264304 iconLabel.icon = AllIcons .General .Warning
305+ viewIconLabel.isVisible = false
265306 iconLabel.toolTipText = " Empty suffix. Density will be skipped"
266307 } else if (! destinationExists && skipWhenNotExists) {
267308 iconLabel.icon = AllIcons .General .Warning
309+ viewIconLabel.isVisible = false
268310 iconLabel.toolTipText = " Density folder not found, resource will be skipped<br/>You can change this in plugin settings"
269311 } else {
270- if (filesList.any { it.substringBeforeLast(' .' ).endsWith(suffix) }) {
312+ val file = filesList.firstOrNull { it.substringBeforeLast(' .' ).endsWith(suffix) }
313+ if (file != null ) {
271314 iconLabel.icon = AllIcons .General .InspectionsOK
272315 iconLabel.toolTipText = " Resource found"
316+ viewIconLabel.isVisible = true
317+ viewIconLabel.mouseListeners.forEach { viewIconLabel.removeMouseListener(it) }
318+ viewIconLabel.addMouseListener(object : MouseAdapter () {
319+ override fun mouseClicked (e : MouseEvent ? ) {
320+ showPreviewPopup(file, viewIconLabel)
321+ }
322+ })
273323 } else {
274324 iconLabel.icon = AllIcons .General .Error
325+ viewIconLabel.isVisible = false
275326 iconLabel.toolTipText = " Resource not found"
276327 }
277328 }
278329 }
279330
331+ private fun showPreviewPopup (fileName : String , target : JComponent ): JBPopup ? =
332+ getBufferedImage(fileName)?.let { image ->
333+ val previewPanel = PreviewPanel ()
334+ previewPanel.quickDrawPanel.setImage(image)
335+ previewPanel.labelSize.text = " ${image.width} x ${image.height} "
336+ val popupBuilder = JBPopupFactory .getInstance().createComponentPopupBuilder(previewPanel.mainPanel, null )
337+ popupBuilder.createPopup().apply {
338+ size = Dimension (
339+ /* width = */ minOf(image.width, MAX_PREVIEW_SIZE ) + WIDTH_PREVIEW_MARGIN ,
340+ /* height = */ minOf(image.height, MAX_PREVIEW_SIZE ) + HEIGHT_PREVIEW_MARGIN
341+ )
342+ showAbove(target)
343+ }
344+ }
345+
346+ private fun getBufferedImage (densityFile : String ) = try {
347+ file?.let { selectedZip ->
348+ ZipFile (selectedZip).use { zipFile ->
349+ zipFile.entries().toList().find { it.name.endsWith(densityFile) }?.let { entry ->
350+ ImageIO .read(zipFile.getInputStream(entry))
351+ }
352+ }
353+ }
354+ } catch (e: IOException ) {
355+ Notifications .Bus .notify(
356+ Notification (
357+ " Figma import" ,
358+ " Error loading preview image" ,
359+ " An error occurred processing loading image file $densityFile " ,
360+ NotificationType .ERROR
361+ )
362+ )
363+ null
364+ }
365+
280366 private fun Density.getFolder (): String {
281367 val preValue = dialog.qualifierBeforeField.text.sanitizeQualifier()
282368 val preModifier = if (preValue.isBlank()) " " else " $preValue$QUALIFIER_SEPARATOR "
@@ -301,12 +387,10 @@ class ImportDialogWrapper(
301387 if (result == JFileChooser .APPROVE_OPTION ) {
302388 file = fileDialog.selectedFile
303389 dialog.fileField.text = fileDialog.selectedFile.toString()
390+ dialog.previewLabel.isVisible = true
304391 ZipFile (fileDialog.selectedFile).use { zipFile ->
305392 zipFilesList = zipFile.entries().asSequence()
306- .filter {
307- ! it.isDirectory && (it.name.endsWith(" .png" , true ) ||
308- it.name.endsWith(" .jpg" , true ))
309- }
393+ .filter { it.isValidEntry() }
310394 .fold(mutableListOf ()) { list, entry -> list.apply { add(File (entry.name).name) } }
311395 }
312396 zipFilesList?.let {
@@ -354,5 +438,9 @@ class ImportDialogWrapper(
354438
355439 private const val QUALIFIER_SEPARATOR = " -"
356440 private const val FOLDER_DRAWABLE = " drawable$QUALIFIER_SEPARATOR "
441+
442+ private const val MAX_PREVIEW_SIZE = 400
443+ private const val WIDTH_PREVIEW_MARGIN = 4
444+ private const val HEIGHT_PREVIEW_MARGIN = 26
357445 }
358446}
0 commit comments