From 6f1e520f093044b000cc35cfcc8813cc5f000f30 Mon Sep 17 00:00:00 2001 From: yricky Date: Fri, 30 Aug 2024 22:41:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=85=A8=E5=B1=80=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=BC=80=E5=8F=91=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../me/yricky/abcde/content/AbcUniSearch.kt | 170 ++++++++++++++++++ .../me/yricky/abcde/content/SettingsPanel.kt | 86 ++++++--- .../me/yricky/abcde/desktop/DesktopUtils.kt | 4 +- .../kotlin/me/yricky/abcde/page/AbcView.kt | 16 +- .../kotlin/me/yricky/abcde/ui/windowFrame.kt | 3 +- 5 files changed, 253 insertions(+), 26 deletions(-) create mode 100644 abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/AbcUniSearch.kt diff --git a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/AbcUniSearch.kt b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/AbcUniSearch.kt new file mode 100644 index 0000000..bfdf01c --- /dev/null +++ b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/AbcUniSearch.kt @@ -0,0 +1,170 @@ +package me.yricky.abcde.content + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import me.yricky.abcde.HapSession +import me.yricky.abcde.page.AbcView +import me.yricky.abcde.ui.LazyColumnWithScrollBar +import me.yricky.abcde.ui.codeStyle +import me.yricky.abcde.ui.defineStr +import me.yricky.oh.abcd.AbcBuf +import me.yricky.oh.abcd.cfm.AbcClass +import me.yricky.oh.abcd.cfm.AbcMethod +import me.yricky.oh.abcd.cfm.ClassItem +import me.yricky.oh.abcd.isa.calledStrings + +class AbcUniSearchState( + val abc:AbcBuf, + val searchScope:CoroutineScope +){ + var filterText by mutableStateOf("") + + + class SearchSession( + val job:Job, + val filterText:String, + val searchTargets:Map>, + val progress:State, + val finished:State + ){ + } + + fun startSearch( + target: Set + ):SearchSession{ + val progress = mutableFloatStateOf(0f) + val targets = target.associateWith { mutableStateListOf() } + val finished = mutableStateOf(false) + val job = searchScope.launch(Dispatchers.Default) { + abc.classes.values.forEachIndexed { i,c -> + targets.entries.forEach { + it.value.addAll(it.key.find(filterText,c)) + } + progress.value = i / abc.classes.size.toFloat() + } + finished.value = true + } + return SearchSession(job,filterText,targets,progress,finished) + } + + var session by mutableStateOf(null) + + sealed class SearchTarget{ + abstract fun find(filterText:String,classItem: ClassItem):List + } + object ClassPath:SearchTarget(){ + override fun find(filterText:String,classItem: ClassItem): List { + return if(classItem.name.contains(filterText)){ + listOf(ClassResult(classItem)) + } else { + emptyList() + } + } + + } + object MethodName:SearchTarget(){ + override fun find(filterText: String, classItem: ClassItem): List { + return when(classItem){ + is AbcClass -> classItem.methods.filter { it.name.contains(filterText) }.map { MethodResult(classItem,it) } + else -> emptyList() + } + } + + } + object ASM:SearchTarget(){ + override fun find(filterText: String, classItem: ClassItem): List { + return when(classItem){ + is AbcClass -> classItem.methods.filter { + it.codeItem?.asm?.list?.any { + it.calledStrings.any { it.contains(filterText) } + } ?: false + }.map { CodeResult(classItem,it) } + else -> emptyList() + } + } + + } + + sealed class SearchResult() + class ClassResult(val classItem: ClassItem):SearchResult() + class MethodResult(val classItem: AbcClass,val method: AbcMethod):SearchResult() + class CodeResult(val classItem: AbcClass,val method: AbcMethod):SearchResult() +} + +@Composable +fun AbcUniSearchStateView( + hapSession: HapSession, + abc:AbcView, + state:AbcUniSearchState +){ + Column { + Row( + Modifier.fillMaxWidth().height(40.dp) + .border(1.dp,MaterialTheme.colorScheme.surfaceVariant), + verticalAlignment = Alignment.CenterVertically + ) { + BasicTextField( + state.filterText,{state.filterText = it}, + textStyle = codeStyle, + singleLine = true, + maxLines = 1, + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary) + ) + Button({ + state.session?.job?.cancel() + state.session = state.startSearch(setOf(AbcUniSearchState.MethodName,AbcUniSearchState.ASM)) + }){ + Text("查询") + } + } + + state.session?.let { + AnimatedVisibility(!it.finished.value) { + LinearProgressIndicator(progress = { it.progress.value }, modifier = Modifier.fillMaxWidth()) + } + LazyColumnWithScrollBar { + it.searchTargets.forEach { t, u -> + items(u){ + when(it){ + is AbcUniSearchState.ClassResult -> { + Text("class:${it.classItem.name}", modifier = Modifier.clickable { + if(it.classItem is AbcClass){ + hapSession.openClass(abc.hap,it.classItem) + } + }) + } + is AbcUniSearchState.MethodResult -> { + Text("method:${it.method.defineStr(true)}", modifier = Modifier.clickable { + hapSession.openCode(abc.hap,it.method) + }) + } + is AbcUniSearchState.CodeResult -> { + Text("code:${it.method.defineStr(true)}", modifier = Modifier.clickable { + hapSession.openCode(abc.hap,it.method) + }) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/SettingsPanel.kt b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/SettingsPanel.kt index d815293..bef9619 100644 --- a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/SettingsPanel.kt +++ b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/content/SettingsPanel.kt @@ -1,22 +1,18 @@ package me.yricky.abcde.content +import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch import me.yricky.abcde.desktop.DesktopUtils -import me.yricky.abcde.ui.ABCDEWindow -import me.yricky.abcde.ui.Icons -import me.yricky.abcde.ui.LocalAppConfig -import me.yricky.abcde.ui.isDarkTheme +import me.yricky.abcde.ui.* @Composable fun SettingsPanel(show:Boolean,onDismiss:()->Unit){ @@ -26,12 +22,16 @@ fun SettingsPanel(show:Boolean,onDismiss:()->Unit){ icon = Icons.editorConfig(), title = "设置" ){ - Column(Modifier.fillMaxSize().padding(8.dp)) { - OutlinedCard() { - val cfg = LocalAppConfig.current + val cfg = LocalAppConfig.current + val scope = rememberCoroutineScope() + Column(Modifier.fillMaxSize().padding(horizontal = 8.dp)) { + ConfigGroup("外观",Modifier.padding(top=8.dp)) { var newDensity by remember { mutableStateOf(cfg.density) } - val scope = rememberCoroutineScope() - Text("DPI缩放:${String.format("%.02f",newDensity)}", modifier = Modifier.padding(horizontal = 8.dp)) + Text( + "DPI缩放:${String.format("%.02f",newDensity)}", + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(start = 8.dp, top = 8.dp) + ) Slider( value = newDensity, onValueChange = { newDensity = it }, @@ -42,20 +42,62 @@ fun SettingsPanel(show:Boolean,onDismiss:()->Unit){ } } }, + valueRange = 0.5f .. 3f, steps = 49 ) - Text("颜色主题") - Icon(if(isDarkTheme()) Icons.darkTheme() else Icons.lightTheme(), null, Modifier.size(28.dp).clip( - CircleShape - ).clickable { - scope.launch { - DesktopUtils.AppConfig.edit{ - it.copy(darkTheme = it.darkTheme?.not() ?: false) + + Row( + Modifier.height(40.dp).clickable { + scope.launch { + DesktopUtils.AppConfig.edit{ + it.copy(darkTheme = it.darkTheme?.not() ?: false) + } } - } - }.padding(4.dp)) + }.padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + "颜色主题",Modifier.weight(1f), + style = MaterialTheme.typography.titleMedium, + ) + Icon(if(isDarkTheme()) Icons.darkTheme() else Icons.lightTheme(), null, Modifier.size(20.dp).clip( + CircleShape + )) + } } + ConfigGroup("进阶设置",Modifier.padding(top=8.dp)){ + Row( + Modifier.height(40.dp).clickable { + scope.launch { + DesktopUtils.AppConfig.edit{ + it.copy(futureFeature = it.futureFeature.not()) + } + } + }.padding(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + "启用开发中功能",Modifier.weight(1f), + style = MaterialTheme.typography.titleMedium, + ) + Checkbox(cfg.futureFeature,{}) + } + } + } + } +} + +@Composable +fun ConfigGroup( + title:String, + modifier: Modifier = Modifier, + content:@Composable ColumnScope.()->Unit +){ + Column(modifier) { + Text(title, style = MaterialTheme.typography.titleLarge, modifier = Modifier.padding(start = 8.dp)) + OutlinedCard { + content() } } } \ No newline at end of file diff --git a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/desktop/DesktopUtils.kt b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/desktop/DesktopUtils.kt index 3c5a276..77ad89b 100644 --- a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/desktop/DesktopUtils.kt +++ b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/desktop/DesktopUtils.kt @@ -79,7 +79,9 @@ object DesktopUtils { @SerialName("historyList") val historyList:List = emptyList(), @SerialName("darkTheme") - val darkTheme:Boolean? = true + val darkTheme:Boolean? = true, + @SerialName("futureFeature") + val futureFeature:Boolean = false, ){ companion object{ suspend fun edit(action: (AppConfig) -> AppConfig){ diff --git a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/page/AbcView.kt b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/page/AbcView.kt index 83938e9..0c950da 100644 --- a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/page/AbcView.kt +++ b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/page/AbcView.kt @@ -10,11 +10,14 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.yricky.abcde.AppState import me.yricky.abcde.HapSession +import me.yricky.abcde.content.AbcUniSearchState +import me.yricky.abcde.content.AbcUniSearchStateView import me.yricky.abcde.ui.* import me.yricky.abcde.util.TreeModel import me.yricky.oh.abcd.AbcBuf @@ -33,7 +36,8 @@ class AbcView(val abc: AbcBuf,override val hap:HapView? = null):AttachHapPage() @Composable override fun Page(modifier: Modifier, hapSession: HapSession, appState: AppState) { val scope = rememberCoroutineScope() - VerticalTabAndContent(modifier, listOf(composeSelectContent{ _: Boolean -> + val appCfg = LocalAppConfig.current + VerticalTabAndContent(modifier, listOfNotNull(composeSelectContent{ _: Boolean -> Image(Icons.clazz(), null, Modifier.fillMaxSize(), colorFilter = grayColorFilter) } to composeContent{ Column(Modifier.fillMaxSize().padding(end = 4.dp)) { @@ -110,10 +114,18 @@ class AbcView(val abc: AbcBuf,override val hap:HapView? = null):AttachHapPage() scope.launch(Dispatchers.Default) { realCkSum = realCheckSum.value } }) } - } + }, if(appCfg.futureFeature) { + composeSelectContent { + Image(Icons.search(), null, Modifier.fillMaxSize(), colorFilter = grayColorFilter) + } to composeContent { + AbcUniSearchStateView(hapSession, this, searchState) + } + } else null )) } + val searchState = AbcUniSearchState(abc, CoroutineScope(Dispatchers.Default)) + private val classMap get()= abc.classes var filter by mutableStateOf("") private set diff --git a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/ui/windowFrame.kt b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/ui/windowFrame.kt index 30810ae..40cf9f5 100644 --- a/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/ui/windowFrame.kt +++ b/abcdecoder/src/jvmMain/kotlin/me/yricky/abcde/ui/windowFrame.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.LocalScrollbarStyle import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.* @@ -75,7 +76,7 @@ fun ABCDEWindow( LaunchedEffect(null){ window.background = java.awt.Color(bgColor.value.toInt()) } - Box(Modifier.background(MaterialTheme.colorScheme.background)) { + Surface(color = bgColor) { windowScope.content() } }