├── .idea ├── .gitignore ├── vcs.xml ├── kotlinc.xml ├── misc.xml ├── gradle.xml └── uiDesigner.xml ├── Zip └── Dimens-Generating-3.1.0.zip ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle.kts ├── gradle.properties ├── src └── main │ ├── kotlin │ └── site │ │ └── wenlong │ │ └── dimens │ │ ├── ui │ │ ├── Colors.kt │ │ ├── Font.kt │ │ └── UI.kt │ │ ├── base │ │ ├── BaseDialogWrapper.kt │ │ └── BaseAnAction.kt │ │ ├── update │ │ ├── VersionEnum.kt │ │ ├── Update.kt │ │ └── UpdateBalloon.kt │ │ ├── constant │ │ └── Constant.kt │ │ ├── languages │ │ ├── LanguagesFactory.kt │ │ ├── Text.kt │ │ ├── Chinese.kt │ │ └── English.kt │ │ ├── ext │ │ ├── PluginExt.kt │ │ ├── MessagesExt.kt │ │ └── CalculateExt.kt │ │ ├── action │ │ ├── generate │ │ │ ├── DimensGenerate.kt │ │ │ └── DimensGenerateDialog.kt │ │ └── converter │ │ │ ├── DimensConverter.kt │ │ │ └── DimensConverterDialog.kt │ │ └── storages │ │ └── Configuration.kt │ └── resources │ └── META-INF │ ├── plugin.xml │ ├── pluginIcon.svg │ └── icons │ └── pluginIcon.svg ├── .gitignore ├── LICENSE ├── .run └── Run IDE with Plugin.run.xml ├── gradlew.bat ├── README-zh.md ├── README.md └── gradlew /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /Zip/Dimens-Generating-3.1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wenlong-Guo/Dimens-Generating/HEAD/Zip/Dimens-Generating-3.1.0.zip -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wenlong-Guo/Dimens-Generating/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | } 6 | } 7 | 8 | rootProject.name = "Dimens-Generating" -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.stdlib.default.dependency=false 2 | # TODO temporary workaround for Kotlin 1.8.20+ (https://jb.gg/intellij-platform-kotlin-oom) 3 | kotlin.incremental.useClasspathSnapshot=false 4 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/ui/Colors.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.ui 2 | 3 | import com.intellij.ui.JBColor 4 | 5 | /** 6 | * 颜色 7 | * 8 | * @author : 郭文龙 9 | * @Email : guowenlong20000@sina.com 10 | * @date : 2023/5/7 23:27 11 | */ 12 | object Colors { 13 | val COLOR_BACKGROUND = JBColor(0x404040, 0x404040) 14 | } -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/base/BaseDialogWrapper.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.base 2 | 3 | import com.intellij.openapi.ui.DialogWrapper 4 | 5 | /** 6 | * DialogWrapper的基类 7 | * 8 | * @author : 郭文龙 9 | * @Email : guowenlong20000@sina.com 10 | * @date : 2023/5/6 21:00 11 | */ 12 | abstract class BaseDialogWrapper(canBeParent: Boolean) : DialogWrapper(canBeParent) { 13 | abstract fun showDialog() 14 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/update/VersionEnum.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.update 2 | 3 | /** 4 | * 版本 5 | * 6 | * @author : 郭文龙 7 | * @Email : guowenlong20000@sina.com 8 | * @date : 2023/5/20 22:07 9 | */ 10 | enum class VersionEnum(val version: String, val content: String) { 11 | V310("3.1.0", "1.Replace Java with Kotlin language.
2.Build the project using Gradle.
3.Add update notes feature."), 12 | UNKNOWN("0.0.0", "") 13 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/constant/Constant.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.constant 2 | 3 | /** 4 | * 常量 5 | * 6 | * @author : 郭文龙 7 | * @Email : guowenlong20000@sina.com 8 | * @date : 2023/5/6 20:27 9 | */ 10 | object Constant { 11 | const val DIMENS_XML_NAME = "dimens.xml" 12 | const val LAYOUT_FOLDER_NAME = "layout" 13 | const val LAYOUT_XML_NAME = ".xml" 14 | const val PLUGINS_NAME = "Dimens Generating" 15 | const val DP = "dp" 16 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/languages/LanguagesFactory.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.languages 2 | 3 | /** 4 | * 语言工厂类 5 | * 6 | * @author : 郭文龙 7 | * @Email : guowenlong20000@sina.com 8 | * @date : 2023/5/7 0:55 9 | */ 10 | object LanguagesFactory { 11 | fun createText(languagesEnum: Int): Text { 12 | return when (languagesEnum) { 13 | 1 -> Chinese 14 | 0 -> English 15 | else -> English 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/ui/Font.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.ui 2 | 3 | import java.awt.Font 4 | import javax.swing.JComponent 5 | 6 | /** 7 | * 文本 8 | * 9 | * @author : 郭文龙 10 | * @Email : guowenlong20000@sina.com 11 | * @date : 2023/5/8 14:15 12 | */ 13 | 14 | fun JComponent.titleFont() { 15 | this.font = Font(this.font.name, Font.PLAIN, 18) 16 | } 17 | 18 | fun JComponent.secondTitleFont() { 19 | this.font = Font(this.font.name, Font.PLAIN, 16) 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/base/BaseAnAction.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.base 2 | 3 | import com.intellij.openapi.actionSystem.AnAction 4 | import com.intellij.openapi.actionSystem.AnActionEvent 5 | import site.wenlong.dimens.storages.Configuration 6 | 7 | /** 8 | * AnAction的基类 9 | * 10 | * @author : 郭文龙 11 | * @Email : guowenlong20000@sina.com 12 | * @date : 2023/5/6 20:22 13 | */ 14 | abstract class BaseAnAction : AnAction() { 15 | val configuration = Configuration.getInstance() 16 | } -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/ext/PluginExt.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.ext 2 | 3 | import com.intellij.ide.plugins.IdeaPluginDescriptor 4 | import com.intellij.ide.plugins.PluginManagerCore 5 | import com.intellij.openapi.extensions.PluginId 6 | 7 | /** 8 | * 本插件信息 9 | * 10 | * @author : 郭文龙 11 | * @Email : guowenlong20000@sina.com 12 | * @date : 2023/5/20 1:30 13 | */ 14 | const val INITIAL_VERSION = "0.0.0" 15 | 16 | const val PLUGIN_ID = "com.guowenlong.dimens" 17 | 18 | const val VERSION_PROPERTY = "$PLUGIN_ID.version" 19 | 20 | val descriptor: IdeaPluginDescriptor by lazy { PluginManagerCore.getPlugin(PluginId.getId(PLUGIN_ID))!! } 21 | 22 | val version: String by lazy { descriptor.version } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/ext/MessagesExt.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.ext 2 | 3 | import com.intellij.openapi.application.ApplicationManager 4 | import com.intellij.openapi.ui.Messages 5 | import site.wenlong.dimens.constant.Constant 6 | 7 | /** 8 | * Messages扩展 9 | * 10 | * @author : 郭文龙 11 | * @Email : guowenlong20000@sina.com 12 | * @date : 2023/5/6 22:58 13 | */ 14 | 15 | fun showErrorMessage(message: String) { 16 | ApplicationManager.getApplication().invokeLater { 17 | 18 | Messages.showMessageDialog( 19 | message, 20 | Constant.PLUGINS_NAME, 21 | Messages.getErrorIcon() 22 | ) 23 | } 24 | } 25 | 26 | fun showMessage(message: String) { 27 | ApplicationManager.getApplication().invokeLater { 28 | Messages.showMessageDialog( 29 | message, 30 | Constant.PLUGINS_NAME, 31 | Messages.getInformationIcon() 32 | ) 33 | } 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Wenlong Guo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/ext/CalculateExt.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.ext 2 | 3 | import java.text.DecimalFormat 4 | import java.text.NumberFormat 5 | import java.util.* 6 | 7 | /** 8 | * 计算 9 | * 10 | * @author : 郭文龙 11 | * @Email : guowenlong20000@sina.com 12 | * @date : 2023/5/10 20:34 13 | */ 14 | fun targetDimension(dimens: Float, length: Int, scale: Float): String? { 15 | println("dimens: ${dimens}") 16 | println("length: ${length}") 17 | println("scale: ${scale}") 18 | 19 | return formatDecimal((dimens / scale).toDouble(), length) 20 | } 21 | 22 | fun scale(originDimension: Float, toDimension: Float): Float { 23 | return originDimension / toDimension 24 | } 25 | 26 | fun formatDecimal(originNumber: Double, length: Int): String? { 27 | val sb = StringBuilder("0.") 28 | for (l in 0 until length) { 29 | sb.append("#") 30 | } 31 | val decimal = DecimalFormat(sb.toString()).format(originNumber) 32 | val numberFormat = NumberFormat.getNumberInstance(Locale.getDefault()) 33 | numberFormat.isGroupingUsed = false 34 | return numberFormat.format(java.lang.Double.valueOf(decimal)) 35 | } -------------------------------------------------------------------------------- /.run/Run IDE with Plugin.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 17 | 19 | true 20 | true 21 | false 22 | false 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/languages/Text.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.languages 2 | 3 | /** 4 | * 文本接口 5 | * 6 | * @author : 郭文龙 7 | * @Email : guowenlong20000@sina.com 8 | * @date : 2023/5/6 23:06 9 | */ 10 | 11 | interface Text { 12 | val titleOne: String 13 | val cover: String 14 | val decimal: String 15 | val minWidth: String 16 | val folder: String 17 | val bit: String 18 | val more: String 19 | val titleTwo: String 20 | val single: String 21 | val multi: String 22 | val advancedOption: String 23 | val genarate: String 24 | val tipsErrorFile: String 25 | val layoutTipsErrorFile: String 26 | val tipsErrorNumber: String 27 | val tipsCreateFileFailed: String 28 | val tipsCreateFileError: String 29 | val tipsGenerateSuccess: String 30 | val tipsGenerateFail: String 31 | val tipsConvertSuccess: String 32 | val tipsConvertFail: String 33 | val tipsDimensExists: String 34 | val tipsInputEmpty: String 35 | val tipsInputZero: String 36 | val tipsConvertTitle: String 37 | val tipsConvertTip1: String 38 | val tipsConvertDP: String 39 | val tipsConvertSP: String 40 | val tipsConvertEP1: String 41 | val tipsConvertEP2: String 42 | val tipsConvertButton: String 43 | val language :String 44 | val converting :String 45 | val exceptionOfFileError :String 46 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/action/generate/DimensGenerate.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.action.generate 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent 4 | import com.intellij.openapi.actionSystem.PlatformDataKeys 5 | import com.intellij.openapi.project.Project 6 | import com.intellij.openapi.ui.Messages 7 | import com.intellij.openapi.vfs.VirtualFile 8 | import site.wenlong.dimens.base.BaseAnAction 9 | import site.wenlong.dimens.constant.Constant 10 | import site.wenlong.dimens.ext.showErrorMessage 11 | import site.wenlong.dimens.languages.LanguagesFactory.createText 12 | 13 | /** 14 | * 生成dimens文件 15 | * 16 | * @author : 郭文龙 17 | * @Email : guowenlong20000@sina.com 18 | * @date : 2023/5/7 0:58 19 | */ 20 | class DimensGenerate : BaseAnAction() { 21 | override fun actionPerformed(event: AnActionEvent) { 22 | val currentFile = event.getData(PlatformDataKeys.VIRTUAL_FILE) ?: throw Exception("plugins compatibility error") 23 | val project = event.getData(PlatformDataKeys.PROJECT) ?: throw Exception("plugins compatibility error") 24 | when { 25 | isDimensFile(currentFile) -> { 26 | DimensGenerateDialog(currentFile, project).showFrame() 27 | } 28 | 29 | else -> { 30 | showErrorMessage(createText(configuration.languageIndex).tipsErrorFile) 31 | } 32 | } 33 | } 34 | 35 | private fun isDimensFile(currentFile: VirtualFile): Boolean { 36 | return currentFile.name == Constant.DIMENS_XML_NAME 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/action/converter/DimensConverter.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.action.converter 2 | 3 | import com.intellij.openapi.actionSystem.AnActionEvent 4 | import com.intellij.openapi.actionSystem.PlatformDataKeys 5 | import com.intellij.openapi.ui.Messages 6 | import com.intellij.openapi.vfs.VirtualFile 7 | import site.wenlong.dimens.base.BaseAnAction 8 | import site.wenlong.dimens.constant.Constant 9 | import site.wenlong.dimens.ext.showErrorMessage 10 | import site.wenlong.dimens.languages.LanguagesFactory 11 | 12 | /** 13 | * 转换layout.xml或layout文件夹下所有layout的action 14 | * 15 | * @author : 郭文龙 16 | * @Email : guowenlong20000@sina.com 17 | * @date : 2023/5/6 20:52 18 | */ 19 | class DimensConverter : BaseAnAction() { 20 | override fun actionPerformed(event: AnActionEvent) { 21 | val currentFile = event.getData(PlatformDataKeys.VIRTUAL_FILE) ?: throw Exception("plugins compatibility error") 22 | val project = event.getData(PlatformDataKeys.PROJECT) ?: throw Exception("plugins compatibility error") 23 | when { 24 | isLayoutFolder(currentFile) -> { 25 | DimensConverterDialog(currentFile, project, true).showFrame() 26 | } 27 | 28 | isLayoutXml(currentFile) -> { 29 | DimensConverterDialog(currentFile, project, false).showFrame() 30 | } 31 | 32 | else -> { 33 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).layoutTipsErrorFile) 34 | } 35 | } 36 | } 37 | 38 | /** 39 | * 是否是layout文件夹 40 | */ 41 | private fun isLayoutFolder(currentFile: VirtualFile?): Boolean { 42 | return currentFile != null && currentFile.name == Constant.LAYOUT_FOLDER_NAME 43 | } 44 | 45 | /** 46 | * 是否是layout.xml文件 47 | */ 48 | private fun isLayoutXml(currentFile: VirtualFile?): Boolean { 49 | return currentFile != null && currentFile.name.endsWith(Constant.LAYOUT_XML_NAME) 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/storages/Configuration.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.storages 2 | 3 | import com.intellij.openapi.application.ApplicationManager 4 | import com.intellij.openapi.components.PersistentStateComponent 5 | import com.intellij.openapi.components.State 6 | import com.intellij.openapi.components.Storage 7 | import com.intellij.util.xmlb.XmlSerializerUtil 8 | 9 | /** 10 | * 存储数据 11 | * 12 | * @author : 郭文龙 13 | * @Email : guowenlong20000@sina.com 14 | * @date : 2023/5/6 20:24 15 | */ 16 | @State(name = "Configuration", storages = [Storage(value = "\$APP_CONFIG$/Configuration.xml")]) 17 | class Configuration : PersistentStateComponent { 18 | companion object { 19 | fun getInstance(): Configuration { 20 | return ApplicationManager.getApplication().getService(Configuration::class.java) 21 | } 22 | } 23 | 24 | /** 25 | * 是否覆盖 26 | */ 27 | var isOverride = false 28 | 29 | /** 30 | * 是否保留小数点 31 | */ 32 | var isKeepDecimal = false 33 | 34 | /** 35 | * 是否设置最小宽度 36 | */ 37 | var isMinWidth = false 38 | 39 | /** 40 | * 是否设置重命名文件件中间内容 41 | */ 42 | var isReName = false 43 | 44 | /** 45 | * 是否是单个生成 46 | */ 47 | var isSingle = false 48 | 49 | /** 50 | * 小数点位数 51 | */ 52 | var decimalPlace = 2 53 | 54 | /** 55 | * 原始宽度 56 | */ 57 | var originWidth = 360f 58 | 59 | /** 60 | * 文件夹中间名字 61 | */ 62 | var folderName = "sw" 63 | 64 | /** 65 | * 单个生成的dimens文件的宽度基准 66 | */ 67 | var single = "400" 68 | 69 | /** 70 | * 多个生成的dimens文件的宽度基准集合 71 | */ 72 | var multi = "300,320,340,360,380,400,420,440,460,480,500" 73 | 74 | /** 75 | * 语言角标 76 | */ 77 | var languageIndex = 0 78 | 79 | /** 80 | * dp dip的前缀 81 | */ 82 | var dp = "length_" 83 | 84 | /** 85 | * sp 的前缀 86 | */ 87 | var sp = "font_" 88 | 89 | override fun getState(): Configuration = this 90 | 91 | override fun loadState(state: Configuration) = XmlSerializerUtil.copyBean(state, this) 92 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/update/Update.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.update 2 | 3 | import com.intellij.ide.util.PropertiesComponent 4 | import com.intellij.openapi.project.Project 5 | import com.intellij.openapi.startup.StartupActivity 6 | import site.wenlong.dimens.ext.INITIAL_VERSION 7 | import site.wenlong.dimens.ext.VERSION_PROPERTY 8 | import site.wenlong.dimens.ext.version 9 | 10 | /** 11 | * 更新监查 12 | * 13 | * @author : 郭文龙 14 | * @Email : guowenlong20000@sina.com 15 | * @date : 2023/5/20 1:27 16 | */ 17 | class Update : StartupActivity { 18 | 19 | private val properties: PropertiesComponent by lazy { PropertiesComponent.getInstance() } 20 | 21 | private val lastVersionString by lazy { properties.getValue(VERSION_PROPERTY, INITIAL_VERSION) } 22 | 23 | private val currentVersionString by lazy { version } 24 | 25 | override fun runActivity(project: Project) { 26 | if (isUpdated()) { 27 | UpdateBalloon().showBalloonWithLink( 28 | "" + 29 | "Dimens Generating has benn updated to V$currentVersionString" + 30 | "", 31 | "" + 32 | "Thank you for downloading Dimens Generating!" + 33 | " 
" + 34 | "If you find it helpful, please click the button at the bottom to give me a Star." + 35 | " 
" + 36 | "This is the biggest encouragement for me." + 37 | "
 
" + 38 | "Update Notes : " + 39 | "
 
" + 40 | updateNotes(currentVersionString) + 41 | "
" + 42 | "
", 43 | "https://github.com/Wenlong-Guo/Dimens-Generating" 44 | ) 45 | } 46 | } 47 | 48 | private fun isUpdated(): Boolean = currentVersionString != lastVersionString 49 | 50 | private fun updateNotes(currentVersionString: String): String { 51 | VersionEnum.values().find { 52 | currentVersionString == it.version 53 | }?.also { 54 | return it.content 55 | } 56 | return VersionEnum.UNKNOWN.content 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/languages/Chinese.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.languages 2 | 3 | /** 4 | * 中文 5 | * 6 | * @author : 郭文龙 7 | * @Email : guowenlong20000@sina.com 8 | * @date : 2023/5/6 23:05 9 | */ 10 | object Chinese : Text { 11 | override val titleOne: String = "设置选项" 12 | 13 | override val cover: String = "是否覆盖已有文件" 14 | 15 | override val decimal: String = "保留几位小数的位数" 16 | 17 | override val minWidth: String = "设置所选中的dimens.xml的最小宽度(默认屏幕最小宽度360dp)" 18 | 19 | override val folder: String = "设置限定符名字(默认sw)(注意:命名不影响文件生成的规则)" 20 | 21 | override val bit: String = "位" 22 | 23 | override val more: String = "更多" 24 | 25 | override val titleTwo: String = "生成文件模式" 26 | 27 | override val single: String = "单个生成" 28 | 29 | override val multi: String = "多个生成" 30 | 31 | override val genarate: String = "生成Dimens" 32 | 33 | override val advancedOption: String = "扩展选项" 34 | 35 | override val tipsErrorFile: String = "您选择的文件不是dimens.xml,请重新选择" 36 | 37 | override val layoutTipsErrorFile: String = "您选择的文件不是layout文件夹或者xml文件,请重新选择" 38 | 39 | override val tipsErrorNumber: String = "请输入正确的数字" 40 | 41 | override val tipsCreateFileFailed: String = "文件不存在或者创建文件夹失败" 42 | 43 | override val tipsCreateFileError: String = "生成xml文件或文件夹异常,请提交问题到github,感谢" 44 | 45 | override val tipsGenerateSuccess: String = "生成成功" 46 | 47 | override val tipsConvertSuccess: String = "转换成功" 48 | 49 | override val tipsConvertFail: String = "转换失败" 50 | 51 | override val tipsDimensExists: String = 52 | "已经存在%s文件夹的dimens.xml文件\n请在Dimens Generating Tools的菜单中勾选可以覆盖源文件\n或者备份后删除重新生成" 53 | 54 | override val tipsGenerateFail: String = "生成失败" 55 | 56 | override val tipsInputEmpty: String = "不允许输入内容为空" 57 | 58 | override val tipsInputZero: String = "不允许输入数字为0" 59 | 60 | override val tipsConvertTitle: String = "转换设置" 61 | 62 | override val tipsConvertTip1: String = "小贴士:1.转换需要一点点时间\n 2.px不会转换" 63 | 64 | override val tipsConvertDP: String = "dp转换的前缀" 65 | 66 | override val tipsConvertSP: String = "sp转换的前缀" 67 | 68 | override val tipsConvertEP1: String = "(例如:length_ ,生成后为length_1)" 69 | 70 | override val tipsConvertEP2: String = "(例如:font_ ,生成后为font_1)" 71 | 72 | override val tipsConvertButton: String = "转换" 73 | 74 | override val language: String = "语言" 75 | 76 | override val converting: String = "转换中" 77 | 78 | override val exceptionOfFileError: String = "文件内部错误" 79 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/update/UpdateBalloon.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.update 2 | 3 | import com.intellij.ide.util.PropertiesComponent 4 | import com.intellij.openapi.ui.popup.Balloon 5 | import com.intellij.openapi.ui.popup.JBPopupFactory 6 | import com.intellij.ui.awt.RelativePoint 7 | import com.intellij.util.ui.JBUI 8 | import site.wenlong.dimens.ext.VERSION_PROPERTY 9 | import site.wenlong.dimens.ext.version 10 | import java.awt.* 11 | import java.awt.event.MouseAdapter 12 | import java.awt.event.MouseEvent 13 | import java.net.URI 14 | import javax.swing.BoxLayout 15 | import javax.swing.JLabel 16 | import javax.swing.JPanel 17 | 18 | 19 | /** 20 | * 更新提示 21 | * 22 | * @author : 郭文龙 23 | * @Email : guowenlong20000@sina.com 24 | * @date : 2023/5/20 22:53 25 | */ 26 | class UpdateBalloon { 27 | 28 | private val properties: PropertiesComponent by lazy { PropertiesComponent.getInstance() } 29 | 30 | private val currentVersionString by lazy { version } 31 | 32 | var balloon: Balloon? = null 33 | 34 | fun showBalloonWithLink(title: String, message: String, linkUrl: String) { 35 | val panel = JPanel() 36 | panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS) 37 | val titleLabel = JLabel(title) 38 | val messageLabel = JLabel(message) 39 | panel.add(titleLabel) 40 | panel.add(messageLabel) 41 | val linkLabel = JLabel("Please give me a Star. Thank you. ") 42 | linkLabel.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR) 43 | linkLabel.addMouseListener(object : MouseAdapter() { 44 | override fun mouseClicked(e: MouseEvent?) { 45 | try { 46 | Desktop.getDesktop().browse(URI(linkUrl)) 47 | properties.setValue(VERSION_PROPERTY, currentVersionString) 48 | balloon?.dispose() 49 | } catch (ex: Exception) { 50 | ex.printStackTrace() 51 | } 52 | } 53 | }) 54 | panel.add(linkLabel) 55 | balloon = JBPopupFactory.getInstance() 56 | .createBalloonBuilder(panel) 57 | .setCloseButtonEnabled(false) 58 | .setShadow(true) 59 | .setHideOnAction(false) 60 | .setContentInsets(JBUI.insets(30)) 61 | .setHideOnClickOutside(false) 62 | .setHideOnFrameResize(false) 63 | .createBalloon() 64 | val point = Point(Toolkit.getDefaultToolkit().screenSize.width - 60, 60) 65 | val rPoint = RelativePoint(point) 66 | balloon?.show(rPoint, Balloon.Position.above) 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/ui/UI.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.ui 2 | 3 | import com.intellij.openapi.util.IconLoader 4 | import com.intellij.ui.components.labels.LinkLabel 5 | import com.intellij.util.IconUtil 6 | import com.intellij.util.ui.JBFont 7 | import com.intellij.util.ui.JBUI 8 | import com.intellij.util.ui.UIUtil 9 | import net.miginfocom.layout.CC 10 | import net.miginfocom.layout.LC 11 | import net.miginfocom.layout.LayoutUtil 12 | import net.miginfocom.swing.MigLayout 13 | import java.awt.Color 14 | import java.awt.image.RGBImageFilter 15 | import javax.swing.Icon 16 | import javax.swing.border.Border 17 | 18 | /** 19 | * UI 20 | */ 21 | object UI { 22 | 23 | // 使用`get() = ...`以保证获得实时`ScaledFont` 24 | val defaultFont: JBFont get() = JBFont.create(UIUtil.getLabelFont(UIUtil.FontSize.NORMAL)) 25 | 26 | data class FontPair(val primary: JBFont, val phonetic: JBFont) 27 | 28 | @JvmStatic 29 | fun Icon.disabled(): Icon = IconUtil.filterIcon(this, { DisabledFilter() }, null) 30 | 31 | private class DisabledFilter(color: Color = JBUI.CurrentTheme.Label.disabledForeground()) : RGBImageFilter() { 32 | private val rgb = color.rgb 33 | 34 | override fun filterRGB(x: Int, y: Int, argb: Int): Int { 35 | return argb and -0x1000000 or (rgb and 0x00ffffff) 36 | } 37 | } 38 | 39 | @JvmStatic 40 | fun getBordersColor(): Color = JBUI.CurrentTheme.Popup.borderColor(true) 41 | 42 | fun LinkLabel.setIcons(baseIcon: Icon) { 43 | icon = baseIcon 44 | disabledIcon = IconLoader.getDisabledIcon(baseIcon) 45 | setHoveringIcon(IconUtil.darker(baseIcon, 3)) 46 | } 47 | 48 | fun migLayout(gapX: String = "0!", gapY: String = "0!", insets: String = "0") = 49 | MigLayout(LC().fill().gridGap(gapX, gapY).insets(insets)) 50 | 51 | fun migLayoutVertical() = 52 | MigLayout(LC().flowY().fill().gridGap("0!", "0!").insets("0")) 53 | 54 | fun spanX(cells: Int = LayoutUtil.INF): CC = CC().spanX(cells) 55 | 56 | fun fill(): CC = CC().grow().push() 57 | fun fillX(): CC = CC().growX().pushX() 58 | fun fillY(): CC = CC().growY().pushY() 59 | 60 | fun wrap(): CC = CC().wrap() 61 | 62 | fun emptyBorder(topAndBottom: Int, leftAndRight: Int) = JBUI.Borders.empty(topAndBottom, leftAndRight) 63 | 64 | fun emptyBorder(offsets: Int) = JBUI.Borders.empty(offsets) 65 | 66 | fun lineAbove(): Border = JBUI.Borders.customLine(getBordersColor(), 1, 0, 0, 0) 67 | 68 | fun lineBelow(): Border = JBUI.Borders.customLine(getBordersColor(), 0, 0, 1, 0) 69 | 70 | fun lineToRight(): Border = JBUI.Borders.customLine(getBordersColor(), 0, 0, 0, 1) 71 | 72 | operator fun Border.plus(external: Border): Border = JBUI.Borders.merge(this, external, true) 73 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/languages/English.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.languages 2 | 3 | /** 4 | * 英语 5 | * 6 | * @author : 郭文龙 7 | * @Email : guowenlong20000@sina.com 8 | * @date : 2023/5/6 23:14 9 | */ 10 | object English : Text { 11 | override val titleOne = "Generate Options" 12 | 13 | override val cover = "Override existing files" 14 | 15 | override val decimal = "Keep the decimal point" 16 | 17 | override val minWidth = "Set the minimum width of the selected dimens.xml(Default 360dp)" 18 | 19 | override val folder = "Set qualifier name (default is \"sw\") (note: naming does not affect file generation rules)" 20 | 21 | override val bit = "bit" 22 | 23 | override val more = "more" 24 | 25 | override val titleTwo = "Generation Mode" 26 | 27 | override val single = "Single" 28 | 29 | override val multi = "Multiple" 30 | 31 | override val advancedOption = "advanced option" 32 | 33 | override val genarate = "Generate dimens" 34 | 35 | override val tipsErrorFile = "The file you selected is not dimens.xml, please re-select" 36 | 37 | override val layoutTipsErrorFile = 38 | "The file you selected is not layout folder or an xml file, please re-select" 39 | 40 | override val tipsErrorNumber = "Please enter the correct number" 41 | 42 | override val tipsCreateFileFailed: String = "File does not exist or folder creation failed" 43 | 44 | override val tipsCreateFileError: String = 45 | "Generate xml file or folder exception, please submit a question to github, thanks"; 46 | 47 | override val tipsGenerateSuccess: String = "Generated successfully" 48 | 49 | override val tipsGenerateFail: String = "Generated fail" 50 | 51 | override val tipsConvertSuccess: String = "Converted successfully" 52 | 53 | override val tipsConvertFail: String = "Converter fail" 54 | 55 | override val tipsDimensExists: String = 56 | "The dimens.xml file of the %s folder already exists.\n Please check the Dimens Generating Tools menu to overwrite the source file \n or delete and regenerate after backup." 57 | 58 | override val tipsInputEmpty: String = "Do not allow input to be empty" 59 | 60 | override val tipsInputZero: String = "Do not allow input numbers to be 0" 61 | 62 | override val tipsConvertTitle: String = "Convert Option" 63 | 64 | override val tipsConvertTip1: String = "Tips:need a little time and px will not be convert" 65 | 66 | override val tipsConvertDP: String = "dp conversion prefix" 67 | 68 | override val tipsConvertSP: String = "sp conversion prefix" 69 | 70 | override val tipsConvertEP1: String = "(For example: length_ ,generation is length_1)" 71 | 72 | override val tipsConvertEP2: String = "(For example: font_ ,generation is font_1)" 73 | 74 | override val tipsConvertButton: String = "Convert" 75 | 76 | override val language: String = "Language" 77 | 78 | override val converting: String = "Converting..." 79 | 80 | override val exceptionOfFileError: String = "File Error" 81 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | com.guowenlong.dimens 3 | Dimens Generating 4 | 3.1.0 5 | Guo Wenlong 6 | 7 | Dimens Tool 9 |
10 |

11 | 12 | GitHub | 13 | Issues | 14 | Jetbrains | 15 | README 16 | 17 |

18 |
19 |

A plugin that supports multiple screens by generating dimens.xml for any width screen size using minimum width qualifier.

20 |
21 |

Features: 22 |

    23 |
  • Generate a dimens.xml file of the specified size (can be customized) based on the selected dimens.xml file.
  • 24 |
  • When the file to be generated already exists, you can control whether to override.
  • 25 |
  • Can generate multiple files of the specified size at the same time.
  • 26 |
  • Transformation layout.xml or layout folder DP DIP SP in the resource file.
  • 27 |
28 |

29 |

Usage: 30 |

    31 |
  • Choose origin dimens.xml -> right click -> Generate Dimens -> Edit options -> Click "Generate/生成" button.
  • 32 |
  • Choose layout.xml or layout folder -> right click -> Converter Dimens -> Edit options -> Click "Converter/转换" button.
  • 33 | 34 |
35 |

36 |

Tips: 37 |

    38 |
  • Welcome to improve your opinion.
  • 39 |
  • If you feel good,please star,thank you very much.
  • 40 |
41 |

42 |

43 | To Do List: 44 |

    45 |
  • Support Japanese and Korean languages.
  • 46 |
  • Batch generate dp and sp with specified naming conventions in dimens.xml.
  • 47 |
  • Add folder naming convention for generating dimens.xml.
  • 48 |
  • Enhance conversion function and provide conversion utilities for px, dp, dip, and sp referenced in the code.
  • 49 |
  • View current phone screen information via adb.
  • 50 |
51 |

52 |
53 |

Send feedback

54 | ]]>
55 | 56 | 58 |
  • V3.1.0 ⑴add update notes feature
  • 59 |
  • V3.0.0 ⑴refactor project using `Kotlin` and `Gradle`
  • 60 |
  • V2.0.5 ⑴fix android studio compatibility
  • 61 |
  • V2.0.4 ⑴fix issue #18
  • 62 |
  • V2.0.3 ⑴fix some V2.0.0 Bugs
  • 63 |
  • V2.0.0 ⑴fix issue #6 ⑵Add Logo ⑶Transformation layout.xml or layout folder DP DIP SP in the resource file
  • 64 |
  • V1.2.0 ⑴Redraw the UI ⑵Add save configuration function ⑶Support English and Chinese (4)Fix bug where resource attribute cannot be converted starting with "dip"
  • 65 |
  • V1.1.0 ⑴Custom decimal bit ⑵Generate multiple default parameter ⑶Custom qualifier name
  • 66 |
  • V1.0.1 Solve the problem that the specified size of the generated file is invalid
  • 67 |
  • V1.0.0 First Version
  • 68 | 69 | ]]> 70 |
    71 | 72 | 73 | 74 | 75 | 77 | com.intellij.modules.lang 78 | com.intellij.modules.all 79 | com.intellij.modules.platform 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 91 | 92 | 93 | 94 | 96 | 97 | 98 | 99 | 100 |
    -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # Dimens-Generating 2 | 3 | --- 4 | 5 | [![Plugin Homepage][badge:plugin-homepage]][plugin-homepage] 6 | [![License][license-img]][license] 7 | [![Version][version-img]][plugin] 8 | [![Downloads][badge:downloads]][plugin-homepage] 9 | 10 | :ballot_box_with_check: 安卓最小宽度限定符插件.(生成多套dimens文件以及转换没有使用dimens的layout文件或者文件夹) 11 | 12 | [![Getting Started][badge:get-started-en]][get-started-en] 13 | [![开始使用][badge:get-started-zh]][get-started-zh] 14 | 15 | [//]: # ([![はじめに][badge:get-started-jp]][get-started-ja]) 16 | 17 | [//]: # ([![시작하기][badge:get-started-ko]][get-started-ko]) 18 | 19 | - [功能](#功能) 20 | - [用法](#用法) 21 | - [小贴士](#小贴士) 22 | - [变更说明](#变更说明) 23 | - [V3.1.0](#V310) 24 | - [V3.0.0](#V300) 25 | - [V2.0.5](#V205) 26 | - [V2.0.4](#V204) 27 | - [V2.0.3](#V203) 28 | - [V2.0.0](#V200) 29 | - [V1.2.0](#V120) 30 | - [V1.1.0](#V110) 31 | - [V1.0.1](#V101) 32 | - [V1.0.0](#V100) 33 | - [ScreenShot](#ScreenShot) 34 | - [License](#License) 35 | - [About My Github](#About-My-Github) 36 | - [About Me](#About-Me) 37 | 38 | ## 功能 39 | 40 | * 根据所选的dimens.xml文件生成指定尺寸(可自定义)的dimens.xml文件。 41 | * 当要生成的文件已经存在时,可以控制是否覆盖。 42 | * 可以同时生成多个指定大小的文件。 43 | * 在资源文件中,将layout.xml文件或layout文件夹中的DP、DIP或SP进行转换。 44 | 45 | ## 用法 46 | 47 | * 选择dimens.xml -> 右键 -> Generate Dimens -> 编辑选项 -> 点击 "Generate/生成" 按钮。 48 | * 选择layout.xml或者layout文件夹 -> 右键 -> Converter Dimens -> 编辑选项 -> 点击 "Converter/转换" 按钮。 49 | 50 | ## 小贴士 51 | 52 | * 欢迎提出您的意见和建议。 53 | * 如果您感觉该产品不错,请为我们点赞,非常感谢。 54 | 55 | ## 变更说明 56 | 57 | ### V3.1.0 58 | 1. 功能:增加更新日志提示的功能 59 | 60 | ### V3.0.0 61 | 1. 重构:使用 `Kotlin` 和 `Gradle` 构建项目。 62 | 63 | ### V2.0.5 64 | 1. 修复:与 Android Studio 兼容性问题 65 | 66 | ### V2.0.4 67 | 1. 修复 修复 issue #18 68 | 69 | ### V2.0.3 70 | 1. 修复:修复了一些 V2.0.0 中的错误。 71 | 72 | ### V2.0.0 73 | 1. 修复:修复 issue #6 74 | 2. 功能:添加 `Logo` 75 | 3. 功能:在资源文件中,将`layout.xml`或`layout folder`中的`DP` `DIP` `SP`进行转换。 76 | 77 | ### V1.2.0 78 | 1. 重新设计了用户界面 79 | 2. 添加了保存配置函数 80 | 3. 支持`英语`和`中文` 81 | 4. 修复了资源属性以“dip”开头时无法转换的错误。 82 | 83 | ### V1.1.0 84 | 1. 功能:自定义小数点位数 85 | 2. 功能:生成多个默认参数 86 | 3. 功能:自定义限定符名称 87 | 88 | ### V1.0.1 89 | 1. 修正:解决了生成文件的指定大小无效的问题。 90 | 91 | ### V1.0.0 92 | 1. 初始版本:生成 `dimens.xml`。 93 | 94 | ## ScreenShot 95 | 96 | ![ScreenShot](https://plugins.jetbrains.com/files/11290/screenshot_19610.png) 97 | 98 | ## To Do List 99 | 100 | * 支持日语和韩语 101 | * 在dimens.xml批量生成指定命名规则的dp和sp 102 | * 增加生成dimens.xml的文件夹命名规则 103 | * 增强转换功能 附赠代码中引用的px dp dip sp的转换工具类 104 | * 通过adb查看当前手机的屏幕信息 105 | 106 | License 107 | ------- 108 | 109 | Copyright 2023 Wenlong Guo 110 | 111 | Licensed under the Apache License, Version 2.0 (the "License"); 112 | you may not use this file except in compliance with the License. 113 | You may obtain a copy of the License at 114 | 115 | http://www.apache.org/licenses/LICENSE-2.0 116 | 117 | Unless required by applicable law or agreed to in writing, software 118 | distributed under the License is distributed on an "AS IS" BASIS, 119 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 120 | See the License for the specific language governing permissions and 121 | limitations under the License. 122 | 123 | ## About Me 124 |
    125 |   126 | 127 | 128 | 129 |   130 | 131 | 132 | 133 | 访问量统计 134 | 135 |
    136 | 137 | - 📍   北京(Beijing) 138 | - 👨‍🎓  NKY. 139 | - 👩‍💻 8 years of work. 140 | - 🏢 待业 (求内推) 141 | - ☎️ 17600133786 142 | - wx : xiaoguo9745 143 | - 📧 [guowenlong20000@sina.com](mailto:guowenlong20000@sina.com) 144 | - ℹ️ 24k纯90后 没几年就35毕业了,毕业前开始总结安卓的经验,也算是给自己的这些年一个交代 145 | 146 | ## About My Github 147 | 148 |
    149 |
    150 | 151 | 152 |
    153 |

    154 | GIF 155 | 156 | [license-img]: https://img.shields.io/badge/License-MIT-blue.svg 157 | [license]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/LICENSE 158 | [version-img]: https://img.shields.io/badge/Jetbrains%20Plugins-V3.1.0-blue.svg 159 | [plugin]: https://plugins.jetbrains.com/plugin/11290 160 | 161 | [badge:plugin-homepage]: https://img.shields.io/badge/plugin--homepage-Dimens--Generating-blue 162 | [badge:downloads]: https://img.shields.io/jetbrains/plugin/d/11290.svg?style=flat-square&colorB=blue 163 | 164 | [plugin-homepage]: https://plugins.jetbrains.com/plugin/11290-dimens-generating 165 | [dimens-generating-plugin]: https://plugins.jetbrains.com/plugin/11290-dimens-generating 166 | [plugin-homepage]: https://plugins.jetbrains.com/plugin/8579-translation 167 | 168 | [badge:get-started-en]: https://img.shields.io/badge/Get%20Started-English-4CAF50?style=flat-square 169 | [badge:get-started-zh]: https://img.shields.io/badge/%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8-%E4%B8%AD%E6%96%87-2196F3?style=flat-square 170 | [badge:get-started-jp]: https://img.shields.io/badge/%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB-%E6%97%A5%E6%9C%AC%E8%AA%9E-009688?style=flat-square 171 | [badge:get-started-ko]: https://img.shields.io/badge/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%ED%95%9C%EA%B5%AD%EC%96%B4-7CB342?style=flat-square 172 | 173 | [get-started-en]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README.md 174 | [get-started-zh]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README-zh.md 175 | [get-started-ja]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README-ja.md 176 | [get-started-ko]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README-ko.md 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dimens-Generating 2 | 3 | --- 4 | 5 | [![Plugin Homepage][badge:plugin-homepage]][plugin-homepage] 6 | [![License][license-img]][license] 7 | [![Version][version-img]][plugin] 8 | [![Downloads][badge:downloads]][plugin-homepage] 9 | 10 | :ballot_box_with_check: A plugin that supports multiple screens by generating dimens.xml for any width screen size using minimum width qualifier. 11 | 12 | [![Getting Started][badge:get-started-en]][get-started-en] 13 | [![开始使用][badge:get-started-zh]][get-started-zh] 14 | 15 | [//]: # ([![はじめに][badge:get-started-jp]][get-started-ja]) 16 | 17 | [//]: # ([![시작하기][badge:get-started-ko]][get-started-ko]) 18 | 19 | - [Features](#Features) 20 | - [Usage](#Usage) 21 | - [Tips](#Tips) 22 | - [Change Notes](#Change-Notes) 23 | - [V3.1.0](#V310) 24 | - [V3.0.0](#V300) 25 | - [V2.0.5](#V205) 26 | - [V2.0.4](#V204) 27 | - [V2.0.3](#V203) 28 | - [V2.0.0](#V200) 29 | - [V1.2.0](#V120) 30 | - [V1.1.0](#V110) 31 | - [V1.0.1](#V101) 32 | - [V1.0.0](#V100) 33 | - [ScreenShot](#ScreenShot) 34 | - [License](#License) 35 | - [About My Github](#About-My-Github) 36 | - [About Me](#About-Me) 37 | 38 | ## Features 39 | 40 | * Generate a dimens.xml file of the specified size (can be customized) based on the selected dimens.xml file. 41 | * When the file to be generated already exists, you can control whether to override. 42 | * Can generate multiple files of the specified size at the same time. 43 | * Transformation layout.xml or layout folder DP DIP SP in the resource file. 44 | 45 | ## Usage 46 | 47 | * Choose origin dimens.xml -> right click -> Generate Dimens -> Edit options -> Click "Generate/生成" button. 48 | * Choose layout.xml or layout folder -> right click -> Converter Dimens -> Edit options -> Click "Converter/转换" button. 49 | 50 | ## Tips 51 | 52 | * Welcome to improve your opinion. 53 | * If you feel good,please star,thank you very much. 54 | 55 | ## Change Notes 56 | 57 | ### V3.1.0 58 | 1. feature : Add `Update notes` feature. 59 | 60 | ### V3.0.0 61 | 1. refactor : Building a project using `Kotlin` through `Gradle`. 62 | 63 | ### V2.0.5 64 | 1. fix android studio compatibility 65 | 66 | ### V2.0.4 67 | 1. fix : issue #18 68 | 69 | ### V2.0.3 70 | 1. fix : some V2.0.0 Bugs 71 | 72 | ### V2.0.0 73 | 1. fix : issue #6 74 | 2. feature : Add `Logo` 75 | 3. feature : Transformation `layout.xml` or `layout folder` `DP` `DIP` `SP` in the resource file 76 | 77 | ### V1.2.0 78 | 1. Redraw the UI 79 | 2. Add save configuration function 80 | 3. Support `English` and `Chinese` 81 | 4. Fix bug where resource attribute cannot be converted starting with "dip" 82 | 83 | ### V1.1.0 84 | 1. feature : Custom decimal bit 85 | 2. feature : Generate multiple default parameter 86 | 3. feature : Custom qualifier name 87 | 88 | ### V1.0.1 89 | 1. fix : Solve the problem that the specified size of the generated file is invalid 90 | 91 | ### V1.0.0 92 | 1. First Version generate `dimens.xml` 93 | 94 | 95 | ## ScreenShot 96 | 97 | ![ScreenShot](https://plugins.jetbrains.com/files/11290/screenshot_19610.png) 98 | 99 | ## To Do List 100 | 101 | * Support Japanese and Korean languages 102 | * Batch generate dp and sp with specified naming conventions in dimens.xml 103 | * Add folder naming convention for generating dimens.xml 104 | * Enhance conversion function and provide conversion utilities for px, dp, dip, and sp referenced in the code 105 | * View current phone screen information via adb. 106 | 107 | License 108 | ------- 109 | 110 | Copyright 2018 Wenlong Guo 111 | 112 | Licensed under the Apache License, Version 2.0 (the "License"); 113 | you may not use this file except in compliance with the License. 114 | You may obtain a copy of the License at 115 | 116 | http://www.apache.org/licenses/LICENSE-2.0 117 | 118 | Unless required by applicable law or agreed to in writing, software 119 | distributed under the License is distributed on an "AS IS" BASIS, 120 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 121 | See the License for the specific language governing permissions and 122 | limitations under the License. 123 | 124 | ## About Me 125 |
    126 |   127 | 128 | 129 | 130 |   131 | 132 | 133 | 134 | 访问量统计 135 | 136 |
    137 | 138 | - 📍   北京(Beijing) 139 | - 👨‍🎓  NKY. 140 | - 👩‍💻 8 years of work. 141 | - 🏢 待业 (求内推) 142 | - ☎️ 17600133786 143 | - wx : xiaoguo9745 144 | - 📧 [guowenlong20000@sina.com](mailto:guowenlong20000@sina.com) 145 | - ℹ️ 24k纯90后 没几年就35毕业了,毕业前开始总结安卓的经验,也算是给自己的这些年一个交代 146 | 147 | ## About My Github 148 | 149 |
    150 |
    151 | 152 | 153 |
    154 |

    155 | GIF 156 | 157 | [license-img]: https://img.shields.io/badge/License-MIT-blue.svg 158 | [license]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/LICENSE 159 | [version-img]: https://img.shields.io/badge/Jetbrains%20Plugins-V3.1.0-blue.svg 160 | [plugin]: https://plugins.jetbrains.com/plugin/11290 161 | 162 | [badge:plugin-homepage]: https://img.shields.io/badge/plugin--homepage-Dimens--Generating-blue 163 | [badge:downloads]: https://img.shields.io/jetbrains/plugin/d/11290.svg?style=flat-square&colorB=blue 164 | 165 | [plugin-homepage]: https://plugins.jetbrains.com/plugin/11290-dimens-generating 166 | [dimens-generating-plugin]: https://plugins.jetbrains.com/plugin/11290-dimens-generating 167 | [plugin-homepage]: https://plugins.jetbrains.com/plugin/8579-translation 168 | 169 | [badge:get-started-en]: https://img.shields.io/badge/Get%20Started-English-4CAF50?style=flat-square 170 | [badge:get-started-zh]: https://img.shields.io/badge/%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8-%E4%B8%AD%E6%96%87-2196F3?style=flat-square 171 | [badge:get-started-jp]: https://img.shields.io/badge/%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB-%E6%97%A5%E6%9C%AC%E8%AA%9E-009688?style=flat-square 172 | [badge:get-started-ko]: https://img.shields.io/badge/%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-%ED%95%9C%EA%B5%AD%EC%96%B4-7CB342?style=flat-square 173 | 174 | [get-started-en]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README.md 175 | [get-started-zh]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README-zh.md 176 | [get-started-ja]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README-ja.md 177 | [get-started-ko]: https://github.com/Wenlong-Guo/Dimens-Generating/blob/master/README-ko.md 178 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/action/converter/DimensConverterDialog.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.action.converter 2 | 3 | import com.intellij.openapi.progress.ProgressIndicator 4 | import com.intellij.openapi.progress.ProgressManager 5 | import com.intellij.openapi.progress.Task 6 | import com.intellij.openapi.project.Project 7 | import com.intellij.openapi.vfs.VirtualFile 8 | import com.intellij.openapi.vfs.VirtualFileManager 9 | import net.miginfocom.swing.MigLayout 10 | import org.w3c.dom.Element 11 | import org.w3c.dom.Node 12 | import org.w3c.dom.NodeList 13 | import site.wenlong.dimens.ext.showErrorMessage 14 | import site.wenlong.dimens.ext.showMessage 15 | import site.wenlong.dimens.languages.LanguagesFactory.createText 16 | import site.wenlong.dimens.storages.Configuration 17 | import site.wenlong.dimens.ui.Colors 18 | import site.wenlong.dimens.ui.secondTitleFont 19 | import site.wenlong.dimens.ui.titleFont 20 | import java.awt.BorderLayout 21 | import java.awt.Dimension 22 | import java.awt.FlowLayout 23 | import java.awt.Panel 24 | import java.awt.event.ItemEvent 25 | import java.io.File 26 | import java.util.* 27 | import javax.swing.* 28 | import javax.swing.event.DocumentEvent 29 | import javax.swing.event.DocumentListener 30 | import javax.xml.parsers.DocumentBuilderFactory 31 | import javax.xml.transform.TransformerFactory 32 | import javax.xml.transform.dom.DOMSource 33 | import javax.xml.transform.stream.StreamResult 34 | 35 | 36 | /** 37 | * 转换layout.xml或layout文件夹下所有layout的对话框 38 | * 39 | * @author : 郭文龙 40 | * @Email : guowenlong20000@sina.com 41 | * @date : 2023/5/6 20:48 42 | */ 43 | class DimensConverterDialog( 44 | private val currentFile: VirtualFile, 45 | private val project: Project, 46 | private val isFolder: Boolean 47 | ) : JFrame() { 48 | private val configuration = Configuration.getInstance() 49 | 50 | private val languageJComboBox by lazy { 51 | JComboBox().also { 52 | it.addItem("English") 53 | it.addItem("中文") 54 | } 55 | } 56 | 57 | private val titleLabel by lazy { 58 | JLabel(createText(configuration.languageIndex).tipsConvertTitle).also { 59 | it.isOpaque = true 60 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 61 | it.background = Colors.COLOR_BACKGROUND 62 | it.titleFont() 63 | } 64 | } 65 | 66 | private val language by lazy { 67 | JLabel(createText(configuration.languageIndex).language).also { 68 | it.isOpaque = true 69 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 70 | it.background = Colors.COLOR_BACKGROUND 71 | it.preferredSize = Dimension(100, preferredSize.height) 72 | } 73 | } 74 | 75 | private val tips by lazy { 76 | JLabel(createText(configuration.languageIndex).tipsConvertTip1).also { 77 | it.isOpaque = true 78 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 79 | it.background = Colors.COLOR_BACKGROUND 80 | } 81 | } 82 | 83 | private val convertDp by lazy { 84 | JLabel(createText(configuration.languageIndex).tipsConvertDP).also { 85 | it.isOpaque = true 86 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 87 | it.background = Colors.COLOR_BACKGROUND 88 | it.secondTitleFont() 89 | } 90 | } 91 | 92 | private val convertDpTextField by lazy { 93 | JTextField(configuration.dp).also { 94 | it.isOpaque = true 95 | it.border = BorderFactory.createEmptyBorder(5, 10, 5, 10) 96 | it.preferredSize = Dimension(100, preferredSize.height) 97 | } 98 | } 99 | 100 | private val convertDpTips by lazy { 101 | JLabel(createText(configuration.languageIndex).tipsConvertEP1).also { 102 | it.isOpaque = true 103 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 104 | it.background = Colors.COLOR_BACKGROUND 105 | } 106 | } 107 | 108 | private val convertSp by lazy { 109 | JLabel(createText(configuration.languageIndex).tipsConvertSP).also { 110 | it.isOpaque = true 111 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 112 | it.background = Colors.COLOR_BACKGROUND 113 | it.secondTitleFont() 114 | } 115 | } 116 | private val convertSpTextField by lazy { 117 | JTextField(configuration.sp).also { 118 | it.isOpaque = true 119 | it.border = BorderFactory.createEmptyBorder(5, 10, 5, 10) 120 | it.preferredSize = Dimension(100, preferredSize.height) 121 | } 122 | } 123 | 124 | private val convertSpTips by lazy { 125 | JLabel(createText(configuration.languageIndex).tipsConvertEP2).also { 126 | it.isOpaque = true 127 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 128 | it.background = Colors.COLOR_BACKGROUND 129 | } 130 | } 131 | 132 | private val converter by lazy { 133 | JButton(createText(configuration.languageIndex).tipsConvertButton).also { 134 | it.border = BorderFactory.createEmptyBorder(10, 20, 10, 20) 135 | it.secondTitleFont() 136 | } 137 | } 138 | 139 | init { 140 | val root = JPanel() 141 | val migLayout = MigLayout("wrap") 142 | root.layout = migLayout 143 | 144 | root.add(titleLabel) 145 | 146 | val languagePanel = Panel() 147 | val languageBoxLayout = BoxLayout(languagePanel, BoxLayout.X_AXIS) 148 | languagePanel.background = Colors.COLOR_BACKGROUND 149 | languagePanel.foreground = Colors.COLOR_BACKGROUND 150 | languagePanel.layout = languageBoxLayout 151 | languagePanel.add(language) 152 | languagePanel.add(languageJComboBox) 153 | root.add(languagePanel) 154 | 155 | root.add(tips) 156 | 157 | root.add(convertDp) 158 | val dpPanel = JPanel() 159 | val dpLayout = BoxLayout(dpPanel, BoxLayout.X_AXIS) 160 | dpPanel.layout = dpLayout 161 | dpPanel.add(convertDpTextField, BorderLayout.CENTER) 162 | dpPanel.border = BorderFactory.createEmptyBorder(0, 10, 0, 10) 163 | dpPanel.add(convertDpTextField) 164 | dpPanel.add(convertDpTips) 165 | root.add(dpPanel) 166 | 167 | root.add(convertSp) 168 | val spPanel = JPanel() 169 | val spLayout = BoxLayout(spPanel, BoxLayout.X_AXIS) 170 | spPanel.layout = spLayout 171 | spPanel.add(convertSpTextField, BorderLayout.CENTER) 172 | spPanel.border = BorderFactory.createEmptyBorder(0, 10, 0, 10) 173 | spPanel.add(convertSpTextField) 174 | spPanel.add(convertSpTips) 175 | root.add(spPanel) 176 | 177 | val buttonJPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 20)) 178 | buttonJPanel.add(converter) 179 | buttonJPanel.preferredSize = Dimension(600, preferredSize.height) 180 | 181 | root.add(buttonJPanel) 182 | 183 | add(root) 184 | 185 | pack() 186 | initSaveConfiguration() 187 | initClick() 188 | initTextField() 189 | initData(currentFile, project) 190 | } 191 | 192 | private fun initSaveConfiguration() { 193 | addWindowStateListener { configuration.state } 194 | } 195 | 196 | private fun initTextField() { 197 | convertDpTextField.document.addDocumentListener(object : DocumentListener { 198 | override fun insertUpdate(e: DocumentEvent) { 199 | configuration.dp = convertDpTextField.text.toString() 200 | } 201 | 202 | override fun removeUpdate(e: DocumentEvent) { 203 | configuration.dp = convertDpTextField.text.toString() 204 | } 205 | 206 | override fun changedUpdate(e: DocumentEvent) {} 207 | }) 208 | 209 | convertSpTextField.document.addDocumentListener(object : DocumentListener { 210 | override fun insertUpdate(e: DocumentEvent) { 211 | configuration.sp = convertSpTextField.text.toString() 212 | } 213 | 214 | override fun removeUpdate(e: DocumentEvent) { 215 | configuration.sp = convertSpTextField.text.toString() 216 | } 217 | 218 | override fun changedUpdate(e: DocumentEvent) {} 219 | }) 220 | } 221 | 222 | private fun initClick() { 223 | languageJComboBox.addItemListener { e: ItemEvent -> 224 | if (e.stateChange == ItemEvent.SELECTED) { 225 | configuration.languageIndex = languageJComboBox.selectedIndex 226 | toggleLanguage() 227 | } 228 | } 229 | } 230 | 231 | fun showFrame() { 232 | title = createText(configuration.languageIndex).tipsConvertTitle 233 | setSize(640, 450) 234 | setLocationRelativeTo(null) 235 | isVisible = true 236 | } 237 | 238 | private fun toggleLanguage() { 239 | title = createText(configuration.languageIndex).tipsConvertTitle 240 | titleLabel.text = createText(configuration.languageIndex).tipsConvertTitle 241 | language.text = createText(configuration.languageIndex).language 242 | tips.text = createText(configuration.languageIndex).tipsConvertTip1 243 | convertDp.text = createText(configuration.languageIndex).tipsConvertDP 244 | convertDpTips.text = createText(configuration.languageIndex).tipsConvertEP1 245 | convertSp.text = createText(configuration.languageIndex).tipsConvertSP 246 | convertSpTips.text = createText(configuration.languageIndex).tipsConvertEP2 247 | converter.text = createText(configuration.languageIndex).tipsConvertButton 248 | } 249 | 250 | private fun initData(currentFile: VirtualFile, project: Project) { 251 | languageJComboBox.selectedIndex = configuration.languageIndex 252 | converter.addActionListener { 253 | if (isFolder) { 254 | convertLayoutFolder(currentFile, project) 255 | } else { 256 | convertOneLayoutXmlFile(currentFile, project) 257 | } 258 | dispose() 259 | } 260 | } 261 | 262 | private fun convertOneLayoutXmlFile(currentFile: VirtualFile, project: Project) { 263 | ProgressManager.getInstance() 264 | .run(object : Task.Backgroundable(project, createText(configuration.languageIndex).converting) { 265 | override fun run(progressIndicator: ProgressIndicator) { 266 | try { 267 | convert(currentFile) 268 | VirtualFileManager.getInstance().syncRefresh() 269 | showMessage(createText(configuration.languageIndex).tipsConvertSuccess) 270 | } catch (e: Exception) { 271 | showErrorMessage(createText(configuration.languageIndex).tipsConvertFail) 272 | } 273 | } 274 | }) 275 | } 276 | 277 | private fun convertLayoutFolder(currentFile: VirtualFile, project: Project?) { 278 | ProgressManager.getInstance() 279 | .run(object : Task.Backgroundable(project, createText(configuration.languageIndex).converting) { 280 | override fun run(progressIndicator: ProgressIndicator) { 281 | try { 282 | val children = currentFile.children 283 | for (child in children) { 284 | convert(child) 285 | } 286 | showMessage(createText(configuration.languageIndex).tipsConvertSuccess) 287 | } catch (e: Exception) { 288 | showErrorMessage(createText(configuration.languageIndex).tipsConvertFail) 289 | } 290 | } 291 | }) 292 | } 293 | 294 | @Throws(Exception::class) 295 | private fun convert(child: VirtualFile) { 296 | val factory = DocumentBuilderFactory.newInstance() 297 | val builder = factory.newDocumentBuilder() 298 | val file = File( 299 | child.canonicalPath ?: throw Exception(createText(configuration.languageIndex).exceptionOfFileError) 300 | ) 301 | val document = builder.parse(file) 302 | val root: Element = document.documentElement 303 | traverseNode(root.childNodes) 304 | val transformerFactory = TransformerFactory.newInstance() 305 | val transformer = transformerFactory.newTransformer() 306 | val source = DOMSource(document) 307 | val result = StreamResult(file) 308 | transformer.transform(source, result) 309 | } 310 | 311 | private fun traverseNode(nodeList: NodeList) { 312 | for (i in 0 until nodeList.length) { 313 | val node: Node = nodeList.item(i) 314 | if (node.nodeType == Element.ELEMENT_NODE) { 315 | // 处理元素节点 316 | val element = node as Element 317 | println("Element name: ${element.nodeName}") 318 | traverseAttributes(element) 319 | 320 | // 遍历子节点 321 | if (element.hasChildNodes()) { 322 | traverseNode(element.childNodes) 323 | } 324 | } 325 | } 326 | } 327 | 328 | private fun traverseAttributes(element: Element) { 329 | for (i in 0 until element.attributes.length) { 330 | val item = element.attributes.item(i) 331 | println("Attribute name: ${item.nodeName}") 332 | println("Attribute value: ${item.nodeValue}") 333 | when { 334 | item.nodeValue.endsWith("dip") -> { 335 | item.nodeValue = 336 | "@dimen/" + configuration.dp + item.nodeValue.substring(0, item.nodeValue.length - 3) 337 | } 338 | 339 | item.nodeValue.endsWith("dp") -> { 340 | item.nodeValue = 341 | "@dimen/" + configuration.dp + item.nodeValue.substring(0, item.nodeValue.length - 2) 342 | } 343 | 344 | item.nodeValue.endsWith("sp") -> { 345 | item.nodeValue = 346 | "@dimen/" + configuration.sp + item.nodeValue.substring(0, item.nodeValue.length - 2) 347 | } 348 | 349 | item.nodeValue.endsWith("px") -> { 350 | //todo 后续版本完善 351 | } 352 | } 353 | } 354 | } 355 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 241 | 242 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/icons/pluginIcon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 241 | 242 | -------------------------------------------------------------------------------- /src/main/kotlin/site/wenlong/dimens/action/generate/DimensGenerateDialog.kt: -------------------------------------------------------------------------------- 1 | package site.wenlong.dimens.action.generate 2 | 3 | import com.intellij.openapi.project.Project 4 | import com.intellij.openapi.vfs.VirtualFile 5 | import com.intellij.openapi.vfs.VirtualFileManager 6 | import net.miginfocom.swing.MigLayout 7 | import org.w3c.dom.Document 8 | import org.w3c.dom.Element 9 | import org.w3c.dom.NodeList 10 | import site.wenlong.dimens.constant.Constant 11 | import site.wenlong.dimens.ext.showErrorMessage 12 | import site.wenlong.dimens.ext.showMessage 13 | import site.wenlong.dimens.ext.targetDimension 14 | import site.wenlong.dimens.languages.LanguagesFactory 15 | import site.wenlong.dimens.storages.Configuration 16 | import site.wenlong.dimens.ui.Colors 17 | import site.wenlong.dimens.ui.secondTitleFont 18 | import site.wenlong.dimens.ui.titleFont 19 | import java.awt.Dimension 20 | import java.awt.FlowLayout 21 | import java.awt.Panel 22 | import java.awt.event.ItemEvent 23 | import java.io.File 24 | import javax.swing.* 25 | import javax.swing.event.ChangeEvent 26 | import javax.swing.event.DocumentEvent 27 | import javax.swing.event.DocumentListener 28 | import javax.xml.parsers.DocumentBuilderFactory 29 | import javax.xml.transform.TransformerFactory 30 | import javax.xml.transform.dom.DOMSource 31 | import javax.xml.transform.stream.StreamResult 32 | 33 | 34 | /** 35 | * 生成dimens文件的对话框 36 | * 37 | * @author : 郭文龙 38 | * @Email : guowenlong20000@sina.com 39 | * @date : 2023/5/6 20:12 40 | */ 41 | class DimensGenerateDialog(private val currentFile: VirtualFile, private val project: Project) : JFrame() { 42 | 43 | private val configuration = Configuration.getInstance() 44 | 45 | private val titleLabel by lazy { 46 | JLabel(LanguagesFactory.createText(configuration.languageIndex).titleOne).also { 47 | it.isOpaque = true 48 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 49 | it.background = Colors.COLOR_BACKGROUND 50 | it.titleFont() 51 | } 52 | } 53 | 54 | private val modeLabel by lazy { 55 | JLabel(LanguagesFactory.createText(configuration.languageIndex).titleTwo).also { 56 | it.isOpaque = true 57 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 58 | it.background = Colors.COLOR_BACKGROUND 59 | it.titleFont() 60 | } 61 | } 62 | 63 | private val languageJComboBox by lazy { 64 | JComboBox().also { 65 | it.addItem("English") 66 | it.addItem("中文") 67 | } 68 | } 69 | 70 | private val language by lazy { 71 | JLabel(LanguagesFactory.createText(configuration.languageIndex).language).also { 72 | it.isOpaque = true 73 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 74 | it.background = Colors.COLOR_BACKGROUND 75 | it.preferredSize = Dimension(100, preferredSize.height) 76 | } 77 | } 78 | 79 | private val overrideCheckBox by lazy { 80 | JCheckBox(LanguagesFactory.createText(configuration.languageIndex).cover).also { 81 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 82 | it.background = Colors.COLOR_BACKGROUND 83 | it.secondTitleFont() 84 | it.preferredSize = Dimension(500, preferredSize.height) 85 | } 86 | } 87 | 88 | private val decimalCheckBox by lazy { 89 | JCheckBox(LanguagesFactory.createText(configuration.languageIndex).decimal).also { 90 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 91 | it.background = Colors.COLOR_BACKGROUND 92 | it.secondTitleFont() 93 | it.preferredSize = Dimension(800, preferredSize.height) 94 | } 95 | } 96 | 97 | private val decimalTextField by lazy { 98 | JTextField(configuration.decimalPlace.toString()).also { 99 | it.isOpaque = true 100 | it.border = BorderFactory.createEmptyBorder(5, 10, 5, 10) 101 | it.preferredSize = Dimension(100, preferredSize.height) 102 | } 103 | } 104 | 105 | private val decimalLabel by lazy { 106 | JLabel(LanguagesFactory.createText(configuration.languageIndex).bit).also { 107 | it.isOpaque = true 108 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 109 | it.background = Colors.COLOR_BACKGROUND 110 | it.secondTitleFont() 111 | } 112 | } 113 | 114 | private val minWidthCheckBox by lazy { 115 | JCheckBox(LanguagesFactory.createText(configuration.languageIndex).minWidth).also { 116 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 117 | it.background = Colors.COLOR_BACKGROUND 118 | it.secondTitleFont() 119 | it.preferredSize = Dimension(800, preferredSize.height) 120 | } 121 | } 122 | 123 | private val minWidthTextField by lazy { 124 | JTextField(configuration.originWidth.toString()).also { 125 | it.isOpaque = true 126 | it.border = BorderFactory.createEmptyBorder(5, 10, 5, 10) 127 | it.preferredSize = Dimension(100, preferredSize.height) 128 | } 129 | } 130 | 131 | private val minWidthLabel by lazy { 132 | JLabel(Constant.DP).also { 133 | it.isOpaque = true 134 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 135 | it.background = Colors.COLOR_BACKGROUND 136 | it.secondTitleFont() 137 | } 138 | } 139 | 140 | private val folderCheckBox by lazy { 141 | JCheckBox(LanguagesFactory.createText(configuration.languageIndex).folder).also { 142 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 143 | it.background = Colors.COLOR_BACKGROUND 144 | it.secondTitleFont() 145 | it.preferredSize = Dimension(800, preferredSize.height) 146 | } 147 | } 148 | 149 | private val folderTextField by lazy { 150 | JTextField(configuration.folderName).also { 151 | it.isOpaque = true 152 | it.border = BorderFactory.createEmptyBorder(5, 10, 5, 10) 153 | it.preferredSize = Dimension(100, preferredSize.height) 154 | } 155 | } 156 | 157 | private val singleRadioButton by lazy { 158 | JRadioButton(LanguagesFactory.createText(configuration.languageIndex).single).also { 159 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 160 | it.background = Colors.COLOR_BACKGROUND 161 | it.secondTitleFont() 162 | it.preferredSize = Dimension(800, preferredSize.height) 163 | } 164 | } 165 | 166 | private val singleTextField by lazy { 167 | JTextField(configuration.single).also { 168 | it.isOpaque = true 169 | it.border = BorderFactory.createEmptyBorder(5, 10, 5, 10) 170 | it.preferredSize = Dimension(100, preferredSize.height) 171 | } 172 | } 173 | 174 | private val singleLabel by lazy { 175 | JLabel(Constant.DP).also { 176 | it.isOpaque = true 177 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 178 | it.background = Colors.COLOR_BACKGROUND 179 | it.secondTitleFont() 180 | } 181 | } 182 | 183 | private val multiRadioButton by lazy { 184 | JRadioButton(LanguagesFactory.createText(configuration.languageIndex).multi).also { 185 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 186 | it.background = Colors.COLOR_BACKGROUND 187 | it.secondTitleFont() 188 | it.preferredSize = Dimension(150, preferredSize.height) 189 | } 190 | } 191 | 192 | private val multiTextField by lazy { 193 | JTextField(configuration.multi).also { 194 | it.isOpaque = true 195 | it.border = BorderFactory.createEmptyBorder(5, 10, 5, 10) 196 | it.preferredSize = Dimension(750, preferredSize.height) 197 | } 198 | } 199 | 200 | private val multiLabel by lazy { 201 | JLabel(Constant.DP).also { 202 | it.isOpaque = true 203 | it.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) 204 | it.background = Colors.COLOR_BACKGROUND 205 | it.secondTitleFont() 206 | } 207 | } 208 | 209 | private val generateButton by lazy { 210 | JButton(LanguagesFactory.createText(configuration.languageIndex).genarate).also { 211 | it.border = BorderFactory.createEmptyBorder(10, 20, 10, 20) 212 | it.secondTitleFont() 213 | } 214 | } 215 | 216 | init { 217 | val root = JPanel() 218 | val migLayout = MigLayout("wrap") 219 | root.layout = migLayout 220 | 221 | root.add(titleLabel) 222 | 223 | val languagePanel = Panel() 224 | val languageBoxLayout = BoxLayout(languagePanel, BoxLayout.X_AXIS) 225 | languagePanel.background = Colors.COLOR_BACKGROUND 226 | languagePanel.foreground = Colors.COLOR_BACKGROUND 227 | languagePanel.layout = languageBoxLayout 228 | languagePanel.add(language) 229 | languagePanel.add(languageJComboBox) 230 | root.add(languagePanel) 231 | 232 | root.add(overrideCheckBox) 233 | 234 | val decimalPanel = Panel() 235 | val decimalBoxLayout = BoxLayout(decimalPanel, BoxLayout.X_AXIS) 236 | decimalPanel.background = Colors.COLOR_BACKGROUND 237 | decimalPanel.foreground = Colors.COLOR_BACKGROUND 238 | decimalPanel.layout = decimalBoxLayout 239 | decimalPanel.add(decimalCheckBox) 240 | decimalPanel.add(decimalTextField) 241 | decimalPanel.add(decimalLabel) 242 | root.add(decimalPanel) 243 | 244 | val minWidthPanel = Panel() 245 | val minWidthBoxLayout = BoxLayout(minWidthPanel, BoxLayout.X_AXIS) 246 | minWidthPanel.background = Colors.COLOR_BACKGROUND 247 | minWidthPanel.foreground = Colors.COLOR_BACKGROUND 248 | minWidthPanel.layout = minWidthBoxLayout 249 | minWidthPanel.add(minWidthCheckBox) 250 | minWidthPanel.add(minWidthTextField) 251 | minWidthPanel.add(minWidthLabel) 252 | root.add(minWidthPanel) 253 | 254 | val folderPanel = Panel() 255 | val folderBoxLayout = BoxLayout(folderPanel, BoxLayout.X_AXIS) 256 | folderPanel.background = Colors.COLOR_BACKGROUND 257 | folderPanel.foreground = Colors.COLOR_BACKGROUND 258 | folderPanel.layout = folderBoxLayout 259 | folderPanel.add(folderCheckBox) 260 | folderPanel.add(folderTextField) 261 | root.add(folderPanel) 262 | 263 | root.add(modeLabel) 264 | 265 | val singlePanel = Panel() 266 | val singleBoxLayout = BoxLayout(singlePanel, BoxLayout.X_AXIS) 267 | singlePanel.background = Colors.COLOR_BACKGROUND 268 | singlePanel.foreground = Colors.COLOR_BACKGROUND 269 | singlePanel.layout = singleBoxLayout 270 | singlePanel.add(singleRadioButton) 271 | singlePanel.add(singleTextField) 272 | singlePanel.add(singleLabel) 273 | root.add(singlePanel) 274 | 275 | val multiPanel = Panel() 276 | val multiBoxLayout = BoxLayout(multiPanel, BoxLayout.X_AXIS) 277 | multiPanel.background = Colors.COLOR_BACKGROUND 278 | multiPanel.foreground = Colors.COLOR_BACKGROUND 279 | multiPanel.layout = multiBoxLayout 280 | multiPanel.add(multiRadioButton) 281 | multiPanel.add(multiTextField) 282 | multiPanel.add(multiLabel) 283 | root.add(multiPanel) 284 | 285 | val buttonJPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 20)) 286 | buttonJPanel.add(generateButton) 287 | buttonJPanel.preferredSize = Dimension(900, preferredSize.height) 288 | root.add(buttonJPanel) 289 | 290 | add(root) 291 | 292 | pack() 293 | initSaveConfiguration() 294 | reStoreConfiguration() 295 | 296 | initClick() 297 | initJRadioButton() 298 | initCheckBox() 299 | initTextField() 300 | initData(currentFile, project) 301 | } 302 | 303 | private fun initCheckBox() { 304 | overrideCheckBox.addChangeListener { e: ChangeEvent -> 305 | val jCheckBox = e.source as JCheckBox 306 | configuration.isOverride = jCheckBox.isSelected 307 | } 308 | decimalCheckBox.addChangeListener { e: ChangeEvent -> 309 | val jCheckBox = e.source as JCheckBox 310 | configuration.isKeepDecimal = jCheckBox.isSelected 311 | } 312 | minWidthCheckBox.addChangeListener { e: ChangeEvent -> 313 | val jCheckBox = e.source as JCheckBox 314 | configuration.isMinWidth = jCheckBox.isSelected 315 | } 316 | folderCheckBox.addChangeListener { e: ChangeEvent -> 317 | val jCheckBox = e.source as JCheckBox 318 | configuration.isReName = jCheckBox.isSelected 319 | } 320 | } 321 | 322 | private fun initData(currentFile: VirtualFile, project: Project) { 323 | generateButton.addActionListener { 324 | var decimal = 0 325 | if (decimalTextField.text.isNullOrBlank()) { 326 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).tipsInputEmpty) 327 | return@addActionListener 328 | } else { 329 | decimal = if (configuration.isKeepDecimal) decimalTextField.text.toInt() else 0 330 | } 331 | 332 | var folderName = if (configuration.isReName) { 333 | folderTextField.text 334 | } else { 335 | Constant.LAYOUT_FOLDER_NAME 336 | } 337 | 338 | var minWidth = 0f 339 | when { 340 | minWidthTextField.text.isNullOrBlank() -> { 341 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).tipsInputEmpty) 342 | return@addActionListener 343 | } 344 | 345 | minWidthTextField.text == "0" -> { 346 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).tipsInputZero) 347 | return@addActionListener 348 | } 349 | 350 | else -> { 351 | minWidth = if (configuration.isMinWidth) minWidthTextField.text.toFloat() else 360f 352 | } 353 | } 354 | 355 | if (multiTextField.text.isNullOrBlank()) { 356 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).tipsInputEmpty) 357 | return@addActionListener 358 | } 359 | val split = multiTextField.text.split(",".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() 360 | val targets = split.map { 361 | if (it.trim() == "0") { 362 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).tipsInputZero) 363 | return@addActionListener 364 | } 365 | it.toFloat() 366 | } 367 | 368 | val target: Float 369 | if (singleTextField.text.isNullOrBlank()) { 370 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).tipsInputEmpty) 371 | return@addActionListener 372 | } else if (singleTextField.text == "0") { 373 | showErrorMessage(LanguagesFactory.createText(configuration.languageIndex).tipsInputZero) 374 | return@addActionListener 375 | } else { 376 | target = singleTextField.text.toFloat() 377 | } 378 | try { 379 | when { 380 | configuration.isSingle -> { 381 | generateSingle( 382 | currentFile, 383 | configuration.isOverride, 384 | decimal, 385 | minWidth, 386 | folderName, 387 | target 388 | ) 389 | } 390 | 391 | else -> { 392 | generateMulti( 393 | currentFile, 394 | configuration.isOverride, 395 | decimal, 396 | minWidth, 397 | folderName, 398 | targets 399 | ) 400 | } 401 | } 402 | VirtualFileManager.getInstance().syncRefresh() 403 | showMessage(LanguagesFactory.createText(configuration.languageIndex).tipsGenerateSuccess) 404 | } catch (e: Exception) { 405 | showErrorMessage(e.message ?: LanguagesFactory.createText(configuration.languageIndex).tipsGenerateFail) 406 | } 407 | currentFile.refresh(false,true) 408 | dispose() 409 | } 410 | } 411 | 412 | private fun generateMulti( 413 | currentFile: VirtualFile, 414 | isOverride: Boolean, 415 | decimal: Int, 416 | originDimens: Float, 417 | folderName: String, 418 | targetDimens: List 419 | ) { 420 | for (target in targetDimens) { 421 | generateSingle(currentFile, isOverride, decimal, originDimens, folderName, target) 422 | } 423 | } 424 | 425 | @Throws(Exception::class) 426 | private fun generateSingle( 427 | currentFile: VirtualFile, 428 | isOverride: Boolean, 429 | decimal: Int, 430 | originDimens: Float, 431 | folderName: String, 432 | targetDimens: Float 433 | ) { 434 | val factory = DocumentBuilderFactory.newInstance() 435 | val builder = factory.newDocumentBuilder() 436 | val file = File( 437 | currentFile.canonicalPath 438 | ?: throw Exception(LanguagesFactory.createText(configuration.languageIndex).exceptionOfFileError) 439 | ) 440 | val document = builder.parse(file) 441 | val scale = originDimens / targetDimens 442 | println("scale: $scale") 443 | transformerDimens(document.getElementsByTagName("dimen"), decimal, scale) 444 | generateDimens( 445 | document, currentFile.parent.parent.canonicalPath ?: throw Exception( 446 | LanguagesFactory.createText( 447 | configuration.languageIndex 448 | ).exceptionOfFileError 449 | ), "values-" + folderName + targetDimens.toInt() + "dp", isOverride 450 | ) 451 | } 452 | 453 | private fun transformerDimens(nodeList: NodeList, decimal: Int, scale: Float) { 454 | for (i in 0 until nodeList.length) { 455 | val item = nodeList.item(i) as Element 456 | println("Attribute name: ${item.nodeName}") 457 | println("Attribute value: ${item.nodeValue}") 458 | when { 459 | item.textContent.endsWith("dip") -> { 460 | val itemValue = item.textContent.substring(0, item.textContent.length - 3).toFloat() 461 | item.textContent = targetDimension(itemValue, decimal, scale) + "dp" 462 | } 463 | 464 | item.textContent.endsWith("dp") -> { 465 | val itemValue = item.textContent.substring(0, item.textContent.length - 2).toFloat() 466 | item.textContent = targetDimension(itemValue, decimal, scale) + "dp" 467 | } 468 | 469 | item.textContent.endsWith("sp") -> { 470 | val itemValue = item.textContent.substring(0, item.textContent.length - 2).toFloat() 471 | item.textContent = targetDimension(itemValue, decimal, scale) + "sp" 472 | } 473 | 474 | item.textContent.endsWith("px") -> { 475 | //todo 后续版本完善 476 | } 477 | } 478 | println("textContent: ${item.textContent}") 479 | } 480 | } 481 | 482 | @Throws(Exception::class) 483 | private fun generateDimens(document: Document, parentPath: String, folderName: String, isOverride: Boolean) { 484 | val folderPath = parentPath + File.separator + folderName 485 | val folderFile = File(folderPath) 486 | if (!folderFile.exists() && !folderFile.mkdir()) { 487 | throw Exception( 488 | LanguagesFactory.createText( 489 | configuration.languageIndex 490 | ).tipsCreateFileFailed 491 | ) 492 | } 493 | val dimensFile = File(folderPath + File.separator + Constant.DIMENS_XML_NAME) 494 | if (!isOverride && dimensFile.exists()) { 495 | throw Exception( 496 | String.format( 497 | LanguagesFactory.createText( 498 | configuration.languageIndex 499 | ).tipsDimensExists, folderName 500 | ) 501 | ) 502 | } 503 | if (!dimensFile.delete()) { 504 | println("删除文件失败失败:" + dimensFile.name) 505 | } 506 | val transformerFactory = TransformerFactory.newInstance() 507 | val transformer = transformerFactory.newTransformer() 508 | val source = DOMSource(document) 509 | val result = StreamResult(dimensFile) 510 | transformer.transform(source, result) 511 | } 512 | 513 | private fun initTextField() { 514 | decimalTextField.document.addDocumentListener(object : DocumentListener { 515 | override fun insertUpdate(e: DocumentEvent) { 516 | configuration.decimalPlace = decimalTextField.text.toInt() 517 | } 518 | 519 | override fun removeUpdate(e: DocumentEvent) { 520 | configuration.decimalPlace = decimalTextField.text.toInt() 521 | } 522 | 523 | override fun changedUpdate(e: DocumentEvent) {} 524 | }) 525 | minWidthTextField.document.addDocumentListener(object : DocumentListener { 526 | override fun insertUpdate(e: DocumentEvent) { 527 | configuration.originWidth = minWidthTextField.text.toFloat() 528 | } 529 | 530 | override fun removeUpdate(e: DocumentEvent) { 531 | configuration.originWidth = minWidthTextField.text.toFloat() 532 | } 533 | 534 | override fun changedUpdate(e: DocumentEvent) {} 535 | }) 536 | folderTextField.document.addDocumentListener(object : DocumentListener { 537 | override fun insertUpdate(e: DocumentEvent) { 538 | configuration.folderName = folderTextField.text.toString() 539 | } 540 | 541 | override fun removeUpdate(e: DocumentEvent) { 542 | configuration.folderName = folderTextField.text.toString() 543 | } 544 | 545 | override fun changedUpdate(e: DocumentEvent) {} 546 | }) 547 | singleTextField.document.addDocumentListener(object : DocumentListener { 548 | override fun insertUpdate(e: DocumentEvent) { 549 | configuration.single = singleTextField.text.toString() 550 | } 551 | 552 | override fun removeUpdate(e: DocumentEvent) { 553 | configuration.single = singleTextField.text.toString() 554 | } 555 | 556 | override fun changedUpdate(e: DocumentEvent) {} 557 | }) 558 | multiTextField.document.addDocumentListener(object : DocumentListener { 559 | override fun insertUpdate(e: DocumentEvent) { 560 | configuration.multi = multiTextField.text.toString() 561 | } 562 | 563 | override fun removeUpdate(e: DocumentEvent) { 564 | configuration.multi = multiTextField.text.toString() 565 | } 566 | 567 | override fun changedUpdate(e: DocumentEvent) {} 568 | }) 569 | } 570 | 571 | private fun initClick() { 572 | languageJComboBox.addItemListener { e: ItemEvent -> 573 | if (e.stateChange == ItemEvent.SELECTED) { 574 | configuration.languageIndex = languageJComboBox.selectedIndex 575 | toggleLanguage() 576 | } 577 | } 578 | } 579 | 580 | private fun toggleLanguage() { 581 | title = LanguagesFactory.createText(configuration.languageIndex).titleOne 582 | titleLabel.text = LanguagesFactory.createText(configuration.languageIndex).titleOne 583 | modeLabel.text = LanguagesFactory.createText(configuration.languageIndex).titleTwo 584 | language.text = LanguagesFactory.createText(configuration.languageIndex).language 585 | overrideCheckBox.text = LanguagesFactory.createText(configuration.languageIndex).cover 586 | decimalCheckBox.text = LanguagesFactory.createText(configuration.languageIndex).decimal 587 | decimalLabel.text = LanguagesFactory.createText(configuration.languageIndex).bit 588 | minWidthCheckBox.text = LanguagesFactory.createText(configuration.languageIndex).minWidth 589 | folderCheckBox.text = LanguagesFactory.createText(configuration.languageIndex).folder 590 | singleRadioButton.text = LanguagesFactory.createText(configuration.languageIndex).single 591 | multiRadioButton.text = LanguagesFactory.createText(configuration.languageIndex).multi 592 | generateButton.text = LanguagesFactory.createText(configuration.languageIndex).genarate 593 | } 594 | 595 | private fun initJRadioButton() { 596 | val bg = ButtonGroup() 597 | bg.add(singleRadioButton) 598 | bg.add(multiRadioButton) 599 | singleRadioButton.isSelected = configuration.isSingle 600 | multiRadioButton.isSelected = !configuration.isSingle 601 | singleRadioButton.addChangeListener { 602 | configuration.isSingle = singleRadioButton.isSelected 603 | } 604 | multiRadioButton.addChangeListener { 605 | configuration.isSingle = !multiRadioButton.isSelected 606 | } 607 | } 608 | 609 | private fun initSaveConfiguration() { 610 | addWindowStateListener { configuration.state } 611 | } 612 | 613 | private fun reStoreConfiguration() { 614 | languageJComboBox.selectedIndex = configuration.languageIndex 615 | overrideCheckBox.isSelected = configuration.isOverride 616 | decimalCheckBox.isSelected = configuration.isKeepDecimal 617 | minWidthCheckBox.isSelected = configuration.isMinWidth 618 | folderCheckBox.isSelected = configuration.isReName 619 | decimalTextField.text = configuration.decimalPlace.toString() 620 | minWidthTextField.text = configuration.originWidth.toString() 621 | folderTextField.text = configuration.folderName 622 | singleTextField.text = configuration.single 623 | multiTextField.text = configuration.multi 624 | toggleLanguage() 625 | } 626 | 627 | fun showFrame() { 628 | title = LanguagesFactory.createText(configuration.languageIndex).titleOne 629 | setSize(980, 600) 630 | setLocationRelativeTo(null) 631 | isVisible = true 632 | } 633 | } --------------------------------------------------------------------------------