Skip to content

Commit d99ceab

Browse files
authored
Merge pull request #17 from FxRayHughes/master
feat(development): 重构模块选择界面并添加新功能
2 parents 9c3dbd7 + b0e4679 commit d99ceab

35 files changed

+3277
-1227
lines changed

src/main/kotlin/org/tabooproject/development/ProjectBuilder.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProgressManager
99
import com.intellij.openapi.project.Project
1010
import com.intellij.openapi.roots.ui.configuration.ModulesProvider
1111
import com.intellij.openapi.util.ThrowableComputable
12+
import org.tabooproject.development.step.BasicConfigurationStep
1213
import org.tabooproject.development.step.ConfigurationPropertiesStep
1314
import org.tabooproject.development.step.OptionalPropertiesStep
1415
import org.tabooproject.development.util.Assets
@@ -55,14 +56,19 @@ class ProjectBuilder : AbstractNewProjectWizardBuilder() {
5556
}
5657

5758
override fun createWizardSteps(wizardContext: WizardContext, modulesProvider: ModulesProvider): Array<ModuleWizardStep> {
58-
return arrayOf(ConfigurationPropertiesStep(wizardContext), OptionalPropertiesStep())
59+
return arrayOf(
60+
BasicConfigurationStep(wizardContext),
61+
ConfigurationPropertiesStep(wizardContext),
62+
OptionalPropertiesStep()
63+
)
5964
}
6065

6166
override fun cleanup() {
6267
// 在清理前保存当前配置为默认设置
6368
saveCurrentConfigurationAsDefaults()
6469

6570
super.cleanup()
71+
BasicConfigurationStep.refreshTemporaryData()
6672
ConfigurationPropertiesStep.refreshTemporaryData()
6773
OptionalPropertiesStep.refreshTemporaryData()
6874
}

src/main/kotlin/org/tabooproject/development/Utils.kt

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,30 @@ private inline fun <reified T : PsiElement> PsiElement.findParent(
8181
}
8282

8383
fun readFromUrl(url: String): String? {
84-
val client = createOkHttpClientWithSystemProxy {
85-
connectTimeout(10, TimeUnit.SECONDS)
86-
readTimeout(10, TimeUnit.SECONDS)
87-
}
84+
return try {
85+
val client = createOkHttpClientWithSystemProxy {
86+
connectTimeout(30, TimeUnit.SECONDS) // 增加超时时间
87+
readTimeout(30, TimeUnit.SECONDS)
88+
writeTimeout(30, TimeUnit.SECONDS)
89+
// 添加重试机制
90+
retryOnConnectionFailure(true)
91+
}
8892

89-
val response = client
90-
.newCall(getRequest(url))
91-
.execute()
92-
.takeIf { it.isSuccessful } ?: throw IOException("Failed to get $url")
93+
val response = client
94+
.newCall(getRequest(url))
95+
.execute()
9396

94-
return response.body?.string()
97+
if (!response.isSuccessful) {
98+
throw IOException("HTTP ${response.code}: ${response.message} for URL: $url")
99+
}
100+
101+
response.body?.string()
102+
} catch (e: Exception) {
103+
// 记录详细错误信息但不抛出异常,让调用方决定如何处理
104+
println("Network request failed for $url: ${e.message}")
105+
e.printStackTrace()
106+
null
107+
}
95108
}
96109

97110
fun PsiClass.getContainingPackageName(): String? {

src/main/kotlin/org/tabooproject/development/component/CheckModuleList.kt

Lines changed: 135 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.intellij.openapi.Disposable
44
import com.intellij.packageDependencies.ui.TreeModel
55
import com.intellij.ui.*
66
import com.intellij.ui.CheckboxTree.CheckboxTreeCellRenderer
7+
import com.intellij.util.ui.JBUI
78
import org.tabooproject.development.step.ConfigurationPropertiesStep
89
import org.tabooproject.development.step.Module
910
import javax.swing.JScrollPane
@@ -38,35 +39,63 @@ class CheckModuleList : JScrollPane(), Disposable {
3839
if (value.userObject is Module) {
3940
val module = value.userObject as Module
4041

42+
// 模块名称 - 使用更突出的颜色和字体
4143
textRenderer.append(
4244
ColoredText.singleFragment(
4345
module.name, SimpleTextAttributes(
4446
SimpleTextAttributes.STYLE_BOLD,
45-
JBColor.BLACK
47+
if (selected) JBColor.WHITE else JBColor(0x2D5AA0, 0x4A9EFF) // 蓝色主题
4648
)
4749
)
4850
)
4951

50-
textRenderer.append(" ")
52+
// 添加合适的间距
53+
textRenderer.append(" ")
5154

55+
// 模块描述 - 使用更柔和的灰色
56+
val description = module.desc?.let {
57+
if (it.length > 50) "${it.take(50)}..." else it
58+
} ?: "No description"
59+
5260
textRenderer.append(
5361
ColoredText.singleFragment(
54-
module.desc ?: module.name, SimpleTextAttributes(
55-
SimpleTextAttributes.STYLE_PLAIN,
56-
JBColor.GRAY
62+
description, SimpleTextAttributes(
63+
SimpleTextAttributes.STYLE_ITALIC,
64+
if (selected) JBColor.LIGHT_GRAY else JBColor.GRAY
5765
)
5866
)
5967
)
6068
}
6169
} else if (value is DefaultMutableTreeNode) {
62-
textRenderer.append(value.userObject.toString())
70+
// 分类标题 - 使用更突出的样式
71+
textRenderer.append(
72+
ColoredText.singleFragment(
73+
value.userObject.toString(), SimpleTextAttributes(
74+
SimpleTextAttributes.STYLE_BOLD,
75+
if (selected) JBColor.WHITE else JBColor(0x1A472A, 0x5FB865) // 绿色分类标题
76+
)
77+
)
78+
)
6379
}
6480
}
6581
}, root)
6682

6783
init {
6884
checkBoxList.model = treeNode
6985
checkBoxList.isFocusable = false
86+
87+
// 设置树的行高和样式
88+
checkBoxList.rowHeight = 24 // 增加行高让内容更易读
89+
checkBoxList.isRootVisible = false // 隐藏根节点
90+
checkBoxList.showsRootHandles = true // 显示展开/折叠句柄
91+
92+
// 默认展开所有分类节点
93+
javax.swing.SwingUtilities.invokeLater {
94+
for (i in 0 until checkBoxList.rowCount) {
95+
checkBoxList.expandRow(i)
96+
}
97+
}
98+
7099
checkBoxList.addCheckboxTreeListener(object : CheckboxTreeListener {
71100
override fun nodeStateChanged(node: CheckedTreeNode) {
72101
if (node.userObject !is Module) return
@@ -79,9 +108,35 @@ class CheckModuleList : JScrollPane(), Disposable {
79108
onModuleSelectionChanged?.invoke(export())
80109
}
81110
})
111+
112+
// 添加双击监听器用于展开/收起分类节点
113+
checkBoxList.addMouseListener(object : java.awt.event.MouseAdapter() {
114+
override fun mouseClicked(e: java.awt.event.MouseEvent?) {
115+
if (e?.clickCount == 2) {
116+
val path = checkBoxList.getPathForLocation(e.x, e.y)
117+
if (path != null) {
118+
val node = path.lastPathComponent
119+
if (node is DefaultMutableTreeNode && node.userObject !is Module) {
120+
// 这是一个分类节点,切换展开/收起状态
121+
if (checkBoxList.isExpanded(path)) {
122+
checkBoxList.collapsePath(path)
123+
} else {
124+
checkBoxList.expandPath(path)
125+
}
126+
}
127+
}
128+
}
129+
}
130+
})
131+
82132
setFocusable(false)
83133
autoscrolls = true
84134
setViewportView(checkBoxList)
135+
136+
// 设置滚动条样式
137+
verticalScrollBarPolicy = VERTICAL_SCROLLBAR_AS_NEEDED
138+
horizontalScrollBarPolicy = HORIZONTAL_SCROLLBAR_AS_NEEDED
139+
border = JBUI.Borders.empty()
85140

86141
updateUI()
87142
}
@@ -90,36 +145,69 @@ class CheckModuleList : JScrollPane(), Disposable {
90145
* 设置模块数据
91146
*/
92147
fun setModules(modules: Map<String, List<Module>>) {
148+
println("CheckModuleList.setModules 被调用,收到 ${modules.size} 个分类")
149+
modules.forEach { (category, moduleList) ->
150+
println(" 分类 '$category': ${moduleList.size} 个模块")
151+
}
152+
93153
root.removeAllChildren()
94-
modules.map {
95-
DefaultMutableTreeNode(it.key).apply {
96-
it.value.forEach {
97-
add(CheckedTreeNode(it).apply {
98-
isChecked = false
99-
isFocusable = false
100-
})
101-
}
154+
155+
// 按分类名称排序,确保一致的显示顺序
156+
val sortedModules = modules.toSortedMap()
157+
158+
sortedModules.forEach { (categoryName, moduleList) ->
159+
val categoryNode = DefaultMutableTreeNode(categoryName).apply {
102160
isFocusable = false
103161
}
104-
}.forEach {
105-
root.add(it)
162+
163+
// 按模块名称排序
164+
moduleList.sortedBy { it.name }.forEach { module ->
165+
val moduleNode = CheckedTreeNode(module).apply {
166+
isChecked = false
167+
isFocusable = false
168+
}
169+
categoryNode.add(moduleNode)
170+
}
171+
172+
root.add(categoryNode)
106173
}
107174

175+
// 强制重新构建树模型
176+
treeNode?.reload()
108177
updateUI()
178+
179+
// 在EDT中展开所有分类节点以提供更好的用户体验
180+
javax.swing.SwingUtilities.invokeLater {
181+
for (i in 0 until checkBoxList.rowCount) {
182+
checkBoxList.expandRow(i)
183+
}
184+
println("CheckModuleList 展开了 ${checkBoxList.rowCount}")
185+
}
109186
}
110187

111188
/**
112189
* 批量设置模块选中状态
113190
*/
114191
fun setSelectedModules(moduleIds: List<String>) {
115-
// 先清除所有选择
192+
println("CheckModuleList.setSelectedModules: 设置选中模块 ${moduleIds}")
193+
194+
// 先清空ConfigurationPropertiesStep中的模块列表
195+
ConfigurationPropertiesStep.property.modules.clear()
196+
197+
// 清除所有选择
116198
clearAllSelections(root)
117199

118200
// 然后设置指定的模块为选中
119201
moduleIds.forEach { moduleId ->
120202
findAndSetModuleSelection(root, moduleId, true)
121203
}
204+
205+
// 强制刷新UI
122206
treeNode?.reload()
207+
checkBoxList.repaint()
208+
checkBoxList.updateUI()
209+
210+
println("CheckModuleList.setSelectedModules: 完成后实际选中模块数量 ${export().size}")
123211

124212
// 通知选择变更
125213
onModuleSelectionChanged?.invoke(export())
@@ -157,12 +245,40 @@ class CheckModuleList : JScrollPane(), Disposable {
157245
*/
158246
private fun clearAllSelections(node: CheckedTreeNode) {
159247
node.isChecked = false
248+
// 递归处理所有子节点
160249
for (i in 0 until node.childCount) {
161-
val child = node.getChildAt(i) as? CheckedTreeNode ?: continue
162-
clearAllSelections(child)
250+
val child = node.getChildAt(i) as? DefaultMutableTreeNode ?: continue
251+
// 如果是分类节点,继续递归处理其模块节点
252+
for (j in 0 until child.childCount) {
253+
val moduleNode = child.getChildAt(j) as? CheckedTreeNode ?: continue
254+
moduleNode.isChecked = false
255+
// 从配置中移除该模块
256+
if (moduleNode.userObject is Module) {
257+
ConfigurationPropertiesStep.property.modules.remove(moduleNode.userObject as Module)
258+
}
259+
}
163260
}
164261
}
165262

263+
/**
264+
* 取消选中单个模块
265+
*/
266+
fun unselectModule(moduleId: String) {
267+
println("CheckModuleList.unselectModule: 取消选中模块 ${moduleId}")
268+
269+
// 查找并取消选中指定模块
270+
findAndSetModuleSelection(root, moduleId, false)
271+
272+
// 刷新UI
273+
treeNode?.reload()
274+
checkBoxList.repaint()
275+
276+
println("CheckModuleList.unselectModule: 完成后实际选中模块数量 ${export().size}")
277+
278+
// 通知选择变更
279+
onModuleSelectionChanged?.invoke(export())
280+
}
281+
166282
/**
167283
* 导出当前选中的模块
168284
*/

src/main/kotlin/org/tabooproject/development/component/CheckModulePanel.kt

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,63 @@ class CheckModulePanel(
3939
Disposer.register(this, displayModuleList as Disposable)
4040

4141
layout = BorderLayout()
42-
preferredSize = Dimension(600, 400)
42+
preferredSize = Dimension(800, 480) // 增加高度,充分利用空间
4343

44-
val leftPanel = JPanel(BorderLayout()).apply {
45-
border = JBUI.Borders.empty(10)
46-
add(checkModuleScrollPane, BorderLayout.CENTER)
44+
// 创建美观的边框和标题
45+
val leftPanel = panel {
46+
group("Available Modules", indent = false) {
47+
row {
48+
scrollCell(checkModuleList)
49+
.apply {
50+
component.preferredSize = Dimension(380, 410) // 增加高度
51+
}
52+
}
53+
}
54+
}.apply {
55+
border = JBUI.Borders.empty(10, 10, 5, 5)
4756
}
4857

49-
val rightPanel = JPanel(BorderLayout()).apply {
50-
border = JBUI.Borders.empty(10)
51-
add(displayModuleScrollPane, BorderLayout.CENTER)
58+
val rightPanel = panel {
59+
group("Selected Modules", indent = false) {
60+
row {
61+
scrollCell(displayModuleList)
62+
.apply {
63+
component.preferredSize = Dimension(350, 410) // 增加高度与左侧对齐
64+
}
65+
}
66+
}
67+
}.apply {
68+
border = JBUI.Borders.empty(10, 5, 5, 10)
5269
}
5370

54-
add(leftPanel, BorderLayout.WEST)
55-
add(rightPanel, BorderLayout.EAST)
71+
add(leftPanel, BorderLayout.CENTER) // 左侧占据主要空间
72+
add(rightPanel, BorderLayout.EAST) // 右侧固定宽度
5673

5774
// 设置模块选择回调,连接到外部回调
5875
checkModuleList.onModuleSelectionChanged = { modules ->
5976
displayModuleList.setModules(modules)
6077
onModuleSelectionChanged?.invoke(modules)
6178
}
79+
80+
// 设置右侧列表的点击取消选中回调
81+
displayModuleList.onModuleRemoved = { module ->
82+
println("DisplayModuleList: 尝试移除模块 ${module.name} (${module.id})")
83+
// 在左侧树中取消选中该模块,不重新设置整个列表
84+
checkModuleList.unselectModule(module.id)
85+
}
86+
87+
// 优化滚动条设置
88+
checkModuleScrollPane.apply {
89+
verticalScrollBarPolicy = JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
90+
horizontalScrollBarPolicy = JBScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
91+
border = JBUI.Borders.empty()
92+
}
93+
94+
displayModuleScrollPane.apply {
95+
verticalScrollBarPolicy = JBScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
96+
horizontalScrollBarPolicy = JBScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED
97+
border = JBUI.Borders.empty()
98+
}
6299
}
63100

64101
/**

0 commit comments

Comments
 (0)