From b962d3cf01f070c36f149bfdf0bbabbe5d416de6 Mon Sep 17 00:00:00 2001 From: Tryanks Date: Wed, 29 Nov 2023 15:15:15 +0800 Subject: [PATCH 1/6] Add Settings Section into Settings Components --- .../com/eimsound/daw/components/Settings.kt | 14 ++++++++++++++ .../dialogs/settings/FileBrowserSettings.kt | 19 +++---------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt index 5a020d31..f5ac5ecf 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt @@ -17,6 +17,20 @@ private val EXPANDER_PADDING_VERTICAL = 8.dp private val EXPANDER_CARD_GAP = 4.dp private val LIST_HEIGHT = 36.dp +@Composable +fun SettingsSection( + title: String? = null, + content: @Composable () -> Unit +){ + Column { + if (title != null){ + Text(title, style = MaterialTheme.typography.bodyMedium) + Gap(8) + } + content() + } +} + @Composable fun SettingsCard( header: String = "", diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt index d1541ef0..866b8e34 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import com.eimsound.daw.Configuration import com.eimsound.daw.components.Gap import com.eimsound.daw.components.SettingTab +import com.eimsound.daw.components.SettingsSection import com.eimsound.daw.components.SettingsCard import com.eimsound.daw.components.SettingsListManager import com.eimsound.daw.utils.CurrentWindow @@ -30,7 +31,7 @@ internal object FileBrowserSettings : SettingTab { val window = CurrentWindow.current Column { - SettingSection("文件浏览器的自定义文件夹") { + SettingsSection("文件浏览器的自定义文件夹") { SettingsListManager( Configuration.fileBrowserCustomRoots, addButtonText = "添加文件夹", @@ -48,7 +49,7 @@ internal object FileBrowserSettings : SettingTab { ) } Gap(16) - SettingSection("文件浏览器的个性化配置项") { + SettingsSection("文件浏览器的个性化配置项") { SettingsCard("只显示受支持格式的文件") { Switch(checked = Configuration.fileBrowserShowSupFormatOnly, onCheckedChange = { Configuration.fileBrowserShowSupFormatOnly = it @@ -58,18 +59,4 @@ internal object FileBrowserSettings : SettingTab { } } } -} - -@Composable -private fun SettingSection( - title: String? = null, - content: @Composable () -> Unit -){ - Column { - if (title != null){ - Text(title, style = MaterialTheme.typography.bodyMedium) - Gap(8) - } - content() - } } \ No newline at end of file From dad79d573fc4ef34bb46b4f09f56a3982bc9e2d5 Mon Sep 17 00:00:00 2001 From: Tryanks Date: Wed, 29 Nov 2023 16:17:06 +0800 Subject: [PATCH 2/6] Change Selector items Type: List -> Collection --- .../src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt index f0637324..b3b6d37d 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/Menu.kt @@ -112,7 +112,7 @@ fun Menu( @OptIn(ExperimentalFoundationApi::class) @Composable fun Selector( - items: List, + items: Collection, selected: String, boxModifier: Modifier = Modifier, enabled: Boolean = true, From 233545d3fd09cbe96e6850ec6c7aa9df1a3c4dc8 Mon Sep 17 00:00:00 2001 From: Tryanks Date: Wed, 29 Nov 2023 21:06:33 +0800 Subject: [PATCH 3/6] Recon: AudioSettings Page --- .../com/eimsound/daw/components/Settings.kt | 52 +++++- .../window/dialogs/settings/AudioSettings.kt | 166 +++++++----------- .../dialogs/settings/FileBrowserSettings.kt | 11 +- 3 files changed, 125 insertions(+), 104 deletions(-) diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt index f5ac5ecf..70ab4470 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt @@ -1,13 +1,21 @@ package com.eimsound.daw.components +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.input.pointer.PointerIcon +import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -16,12 +24,52 @@ private val EXPANDER_PADDING_HORIZONTAL = 16.dp private val EXPANDER_PADDING_VERTICAL = 8.dp private val EXPANDER_CARD_GAP = 4.dp private val LIST_HEIGHT = 36.dp +private val CARD_HEIGHT = 64.dp +private val EXPAND_ICON_SIZE = 20.dp +private val MENU_MAX_WIDTH = 256.dp + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun SettingsMenu( + items: Collection?, + selected: T, + toString: (T) -> String = { it.toString() }, + onSelect: (T) -> Unit +) { + val textMeasurer = rememberTextMeasurer() + val itemsMap = items!!.associateBy { toString(it) } + val textWidth = (items.maxOfOrNull { textMeasurer.measure(toString(it)).size.width.dp } ?: 0.dp) + EXPANDER_PADDING_HORIZONTAL * 2 + val maxWidth = if (textWidth > MENU_MAX_WIDTH) MENU_MAX_WIDTH else textWidth + Selector( + itemsMap.keys, + selected = toString(selected), + content = { + CustomOutlinedTextField( + toString(selected), { }, + Modifier.width(maxWidth).height(LIST_HEIGHT).pointerHoverIcon(PointerIcon.Hand), + readOnly = true, + textStyle = MaterialTheme.typography.labelLarge.copy(LocalContentColor.current), + suffix = { + Icon(Icons.Filled.ExpandMore, "Expand", + Modifier.size(EXPAND_ICON_SIZE).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape) + .clickable { } + ) + }, + colors = TextFieldDefaults.colors( + unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, + focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, + ), + paddingValues = TextFieldDefaults.contentPaddingWithLabel(8.dp, 4.dp, 3.dp, 4.dp) + ) + }, + ) { itemsMap[it]?.let { selected -> onSelect(selected) } } +} @Composable fun SettingsSection( title: String? = null, content: @Composable () -> Unit -){ +) { Column { if (title != null){ Text(title, style = MaterialTheme.typography.bodyMedium) @@ -38,7 +86,7 @@ fun SettingsCard( ) { Surface( shape = MaterialTheme.shapes.small, - modifier = Modifier.padding(bottom = EXPANDER_CARD_GAP) + modifier = Modifier.padding(bottom = EXPANDER_CARD_GAP).height(CARD_HEIGHT) ) { Row( verticalAlignment = Alignment.CenterVertically, diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt index 15554cae..48c2d4fb 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt @@ -1,13 +1,15 @@ package com.eimsound.daw.window.dialogs.settings -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.SettingsInputComponent -import androidx.compose.material3.* +import androidx.compose.material3.Icon +import androidx.compose.material3.Switch +import androidx.compose.material3.Text import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.util.fastForEach import com.eimsound.audioprocessor.AudioPlayerManager import com.eimsound.daw.Configuration import com.eimsound.daw.api.EchoInMirror @@ -52,117 +54,85 @@ internal object AudioSettings : SettingTab { @Composable override fun content() { Column { - Row(verticalAlignment = Alignment.CenterVertically) { - Text("音频工厂:", Modifier.weight(1f)) - Menu({ close -> - AudioPlayerManager.instance.factories.forEach { (name, _) -> - MenuItem({ - close() - if (Configuration.audioDeviceFactoryName == name) return@MenuItem - EchoInMirror.player?.close() - Configuration.audioDeviceName = "" - Configuration.audioDeviceFactoryName = name - - reopenAudioDevice() - }, Configuration.audioDeviceFactoryName == name, modifier = Modifier.fillMaxWidth()) { - Text(name) - } + SettingsSection("设备与音频设置") { + SettingsCard("音频工厂") { + SettingsMenu( + AudioPlayerManager.instance.factories.keys, + Configuration.audioDeviceFactoryName + ) { + if (Configuration.audioDeviceFactoryName == it) return@SettingsMenu + EchoInMirror.player?.close() + Configuration.audioDeviceName = "" + Configuration.audioDeviceFactoryName = it + reopenAudioDevice() } - }, boxModifier = Modifier.weight(1f)) { - Text(Configuration.audioDeviceFactoryName, Modifier.fillMaxWidth()) } - } - Gap(8) - - Row(verticalAlignment = Alignment.CenterVertically) { - Text("音频设备:", Modifier.weight(1f)) - Menu({ close -> + SettingsCard("音频设备") { var playerNames by remember { mutableStateOf(emptyList()) } LaunchedEffect(Configuration.audioDeviceName) { playerNames = AudioPlayerManager.instance.factories[Configuration.audioDeviceFactoryName]!!.getPlayers() } - playerNames.fastForEach { playerName -> - MenuItem({ - close() - if (Configuration.audioDeviceName == playerName) return@MenuItem - EchoInMirror.player?.close() - Configuration.audioDeviceName = playerName - - reopenAudioDevice() - }, Configuration.audioDeviceName == playerName, modifier = Modifier.fillMaxWidth()) { - Text(playerName) - } + SettingsMenu( + playerNames, + Configuration.audioDeviceName + ) { + if (Configuration.audioDeviceName == it) return@SettingsMenu + EchoInMirror.player?.close() + Configuration.audioDeviceName = it + reopenAudioDevice() } - }, boxModifier = Modifier.weight(1f)) { - Text(Configuration.audioDeviceName, Modifier.fillMaxWidth()) } - } - Gap(8) - Row(verticalAlignment = Alignment.CenterVertically) { - Text("缓冲区大小:", Modifier.weight(1f)) - Menu({ close -> - EchoInMirror.player?.availableBufferSizes?.forEach { - MenuItem({ - close() - EchoInMirror.player?.close() - - reopenAudioDevice() - }, EchoInMirror.currentPosition.bufferSize == it, modifier = Modifier.fillMaxWidth()) { - Text("$it 个采样") - } + SettingsCard("缓冲区大小") { + SettingsMenu( + EchoInMirror.player?.availableBufferSizes?.toList(), + EchoInMirror.currentPosition.bufferSize, + { "$it 个采样" } + ) { + EchoInMirror.player?.close() + EchoInMirror.currentPosition.setSampleRateAndBufferSize( + EchoInMirror.currentPosition.sampleRate, it + ) + reopenAudioDevice() } - }, boxModifier = Modifier.weight(1f)) { - Text("${EchoInMirror.currentPosition.bufferSize} 个采样", Modifier.fillMaxWidth()) } - } - - Gap(8) - Row(verticalAlignment = Alignment.CenterVertically) { - Text("采样率:", Modifier.weight(1f)) - Menu({ close -> - val sampleRate = EchoInMirror.player?.sampleRate - EchoInMirror.player?.availableSampleRates?.fastForEach { - MenuItem({ - close() - EchoInMirror.player?.close() - Configuration.preferredSampleRate = it - - reopenAudioDevice() - }, sampleRate == it, modifier = Modifier.fillMaxWidth()) { - Text(it.toString()) - } + SettingsCard("采样率") { + var sampleRate by remember { mutableStateOf(EchoInMirror.currentPosition.sampleRate) } + SettingsMenu( + EchoInMirror.player?.availableSampleRates?.toList(), + sampleRate, + ) { + sampleRate = it + EchoInMirror.player?.close() + EchoInMirror.currentPosition.setSampleRateAndBufferSize( + it, EchoInMirror.currentPosition.bufferSize + ) + reopenAudioDevice() } - }, boxModifier = Modifier.weight(1f)) { - Text(EchoInMirror.player?.sampleRate?.toString() ?: "未知", Modifier.fillMaxWidth()) + } + if (SystemUtils.IS_OS_WINDOWS) SettingsCard("后台共享音频设备") { + Switch( + Configuration.stopAudioOutputOnBlur, + { + Configuration.stopAudioOutputOnBlur = it + Configuration.save() + } + ) + } + SettingsCard("对超过 0db 的音频进行削波") { + Switch( + Configuration.autoCutOver0db, + { + Configuration.autoCutOver0db = it + Configuration.save() + } + ) } } - - if (SystemUtils.IS_OS_WINDOWS) Row(verticalAlignment = Alignment.CenterVertically) { - Text("后台共享音频设备") - Checkbox( - Configuration.stopAudioOutputOnBlur, - { - Configuration.stopAudioOutputOnBlur = it - Configuration.save() - } - ) - } - - Row(verticalAlignment = Alignment.CenterVertically) { - Text("对超过 0db 的音频进行削波") - Checkbox( - Configuration.autoCutOver0db, - { - Configuration.autoCutOver0db = it - Configuration.save() - } - ) - } - + Gap(8) Latency("输入延迟", EchoInMirror.player?.inputLatency ?: 0) Latency("输出延迟", EchoInMirror.player?.outputLatency ?: 0) Gap(8) - EchoInMirror.player?.Controls() + EchoInMirror.player?.controls() } } } \ No newline at end of file diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt index 866b8e34..64ef01ff 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/FileBrowserSettings.kt @@ -51,10 +51,13 @@ internal object FileBrowserSettings : SettingTab { Gap(16) SettingsSection("文件浏览器的个性化配置项") { SettingsCard("只显示受支持格式的文件") { - Switch(checked = Configuration.fileBrowserShowSupFormatOnly, onCheckedChange = { - Configuration.fileBrowserShowSupFormatOnly = it - Configuration.save() - }) + Switch( + Configuration.fileBrowserShowSupFormatOnly, + { + Configuration.fileBrowserShowSupFormatOnly = it + Configuration.save() + } + ) } } } From 50dab5b3f8de8e30c2c84ec7b38876806e2d353b Mon Sep 17 00:00:00 2001 From: Tryanks Date: Tue, 5 Dec 2023 23:47:28 +0800 Subject: [PATCH 4/6] Resolve conflict --- .../daw/window/dialogs/settings/AudioSettings.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt index 48c2d4fb..e7325255 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/AudioSettings.kt @@ -89,9 +89,10 @@ internal object AudioSettings : SettingTab { { "$it 个采样" } ) { EchoInMirror.player?.close() - EchoInMirror.currentPosition.setSampleRateAndBufferSize( - EchoInMirror.currentPosition.sampleRate, it - ) +// EchoInMirror.currentPosition.setSampleRateAndBufferSize( +// EchoInMirror.currentPosition.sampleRate, it +// ) + reopenAudioDevice() } } @@ -103,9 +104,8 @@ internal object AudioSettings : SettingTab { ) { sampleRate = it EchoInMirror.player?.close() - EchoInMirror.currentPosition.setSampleRateAndBufferSize( - it, EchoInMirror.currentPosition.bufferSize - ) + Configuration.preferredSampleRate = it + reopenAudioDevice() } } @@ -132,7 +132,7 @@ internal object AudioSettings : SettingTab { Latency("输入延迟", EchoInMirror.player?.inputLatency ?: 0) Latency("输出延迟", EchoInMirror.player?.outputLatency ?: 0) Gap(8) - EchoInMirror.player?.controls() + EchoInMirror.player?.Controls() } } } \ No newline at end of file From 45947cc32caa45bdbeb717361857624847c8798a Mon Sep 17 00:00:00 2001 From: Tryanks Date: Mon, 11 Dec 2023 00:41:43 +0800 Subject: [PATCH 5/6] Recon: ShortcutKeySettings Page --- .../com/eimsound/daw/components/Settings.kt | 47 +++--- .../dialogs/settings/ShortcutKeySettings.kt | 139 +++++++++--------- 2 files changed, 96 insertions(+), 90 deletions(-) diff --git a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt index 70ab4470..e13341cd 100644 --- a/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt +++ b/components/src/commonMain/kotlin/com/eimsound/daw/components/Settings.kt @@ -1,5 +1,4 @@ package com.eimsound.daw.components -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -28,7 +27,6 @@ private val CARD_HEIGHT = 64.dp private val EXPAND_ICON_SIZE = 20.dp private val MENU_MAX_WIDTH = 256.dp -@OptIn(ExperimentalFoundationApi::class) @Composable fun SettingsMenu( items: Collection?, @@ -36,33 +34,34 @@ fun SettingsMenu( toString: (T) -> String = { it.toString() }, onSelect: (T) -> Unit ) { + if (items == null) return val textMeasurer = rememberTextMeasurer() - val itemsMap = items!!.associateBy { toString(it) } + val itemsMap = items.associateBy { toString(it) } val textWidth = (items.maxOfOrNull { textMeasurer.measure(toString(it)).size.width.dp } ?: 0.dp) + EXPANDER_PADDING_HORIZONTAL * 2 val maxWidth = if (textWidth > MENU_MAX_WIDTH) MENU_MAX_WIDTH else textWidth - Selector( + OutlinedDropdownSelector( + { itemsMap[it]?.let { selected -> onSelect(selected) } }, itemsMap.keys, selected = toString(selected), - content = { - CustomOutlinedTextField( - toString(selected), { }, - Modifier.width(maxWidth).height(LIST_HEIGHT).pointerHoverIcon(PointerIcon.Hand), - readOnly = true, - textStyle = MaterialTheme.typography.labelLarge.copy(LocalContentColor.current), - suffix = { - Icon(Icons.Filled.ExpandMore, "Expand", - Modifier.size(EXPAND_ICON_SIZE).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape) - .clickable { } - ) - }, - colors = TextFieldDefaults.colors( - unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, - ), - paddingValues = TextFieldDefaults.contentPaddingWithLabel(8.dp, 4.dp, 3.dp, 4.dp) - ) - }, - ) { itemsMap[it]?.let { selected -> onSelect(selected) } } + ) { + CustomOutlinedTextField( + toString(selected), { }, + Modifier.width(maxWidth).height(LIST_HEIGHT).pointerHoverIcon(PointerIcon.Hand), + readOnly = true, + textStyle = MaterialTheme.typography.labelLarge.copy(LocalContentColor.current), + suffix = { + Icon(Icons.Filled.ExpandMore, "Expand", + Modifier.size(EXPAND_ICON_SIZE).pointerHoverIcon(PointerIcon.Hand).clip(CircleShape) + .clickable { } + ) + }, + colors = TextFieldDefaults.colors( + unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, + focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, + ), + paddingValues = TextFieldDefaults.contentPaddingWithLabel(8.dp, 4.dp, 3.dp, 4.dp) + ) + } } @Composable diff --git a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/ShortcutKeySettings.kt b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/ShortcutKeySettings.kt index b3768295..6ec00afe 100644 --- a/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/ShortcutKeySettings.kt +++ b/daw/src/jvmMain/kotlin/com/eimsound/daw/window/dialogs/settings/ShortcutKeySettings.kt @@ -1,21 +1,25 @@ package com.eimsound.daw.window.dialogs.settings -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Keyboard import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.input.key.* +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEach -import com.eimsound.daw.api.EchoInMirror import com.eimsound.daw.api.Command +import com.eimsound.daw.api.EchoInMirror import com.eimsound.daw.api.sortedKeys -import com.eimsound.daw.components.ReadonlyTextField +import com.eimsound.daw.components.CustomOutlinedTextField import com.eimsound.daw.components.SettingTab +import com.eimsound.daw.components.SettingsCard +import com.eimsound.daw.components.SettingsSection import com.eimsound.daw.components.utils.clickableWithIcon import com.eimsound.daw.impl.CommandManagerImpl import com.eimsound.daw.utils.mutableStateSetOf @@ -37,22 +41,17 @@ internal object ShortcutKeySettings : SettingTab { @Composable override fun content() { var selectKey by remember { mutableStateOf("") } - Column { - val commandManager = EchoInMirror.commandManager as CommandManagerImpl - val commands = mutableMapOf() - - commandManager.commands.forEach { (key, command) -> - commands[command] = key - } - commandManager.customCommands.forEach { (key, commandName) -> - commands[commandManager.commandsMap[commandName]!!] = key - } - + val commandManager = EchoInMirror.commandManager as CommandManagerImpl + val commands = mutableMapOf() + commandManager.commands.forEach { (key, command) -> + commands[command] = key + } + commandManager.customCommands.forEach { (key, commandName) -> + commands[commandManager.commandsMap[commandName]!!] = key + } + SettingsSection("快捷键设置") { commands.forEach { (command, key) -> - Row( - verticalAlignment = Alignment.CenterVertically - ) { - Text(command.displayName, Modifier.weight(1f)) + SettingsCard(command.displayName){ var curKey by remember { mutableStateOf(key) } val keyCompose = remember { key.split(" ").map { Key(it.toLong()) }.toMutableStateSet() } val keyDown = remember { mutableStateSetOf() } @@ -91,60 +90,68 @@ internal object ShortcutKeySettings : SettingTab { commandManager.saveCustomShortcutKeys() } - ReadonlyTextField(Modifier - .weight(1f) - .onFocusChanged { - if (!it.isFocused && selectKey == curKey) done() + CustomOutlinedTextField( + keyCompose.sortedKeys().joinToString(separator = "+") { + var cur = it + when (it) { + Key.MetaLeft, Key.MetaRight -> if (SystemUtils.IS_OS_MAC) cur = Key.CtrlLeft + Key.CtrlLeft, Key.CtrlRight -> if (SystemUtils.IS_OS_MAC) cur = Key.MetaLeft } - .clickableWithIcon { - if (selectKey == curKey) { - done() - } else { - selectKey = curKey - preKeyCompose.clear() - keyDown.clear() - preKeyCompose.addAll(keyCompose) - keyCompose.clear() + KeyEvent.getKeyText(cur.nativeKeyCode) + }, + { + EchoInMirror.currentPosition.bpm = it.toDoubleOrNull()?.coerceIn(1.0, 600.0) ?: return@CustomOutlinedTextField + }, + Modifier.width(256.dp).height(36.dp) + .onFocusChanged { + if (!it.isFocused && selectKey == curKey) done() } - }.onKeyEvent { - if (selectKey != curKey) return@onKeyEvent false - var pressKey = it.key - when (pressKey) { - Key.MetaLeft, Key.MetaRight -> if (SystemUtils.IS_OS_MAC) pressKey = Key.CtrlLeft - Key.CtrlLeft, Key.CtrlRight -> if (SystemUtils.IS_OS_MAC) pressKey = Key.MetaLeft - Key.ShiftRight -> pressKey = Key.ShiftLeft - Key.AltRight -> pressKey = Key.AltLeft - Key.Backspace -> pressKey = Key.Delete - } - if (it.type == KeyEventType.KeyDown) { + .clickableWithIcon { + if (selectKey == curKey) { + done() + } else { + selectKey = curKey + preKeyCompose.clear() + keyDown.clear() + preKeyCompose.addAll(keyCompose) + keyCompose.clear() + } + }.onKeyEvent { + if (selectKey != curKey) return@onKeyEvent false + var pressKey = it.key when (pressKey) { - Key.Escape -> cancel() - Key.Enter -> return@onKeyEvent false - else -> { - keyDown.add(pressKey) - keyCompose.add(pressKey) + Key.MetaLeft, Key.MetaRight -> if (SystemUtils.IS_OS_MAC) pressKey = Key.CtrlLeft + Key.CtrlLeft, Key.CtrlRight -> if (SystemUtils.IS_OS_MAC) pressKey = Key.MetaLeft + Key.ShiftRight -> pressKey = Key.ShiftLeft + Key.AltRight -> pressKey = Key.AltLeft + Key.Backspace -> pressKey = Key.Delete + } + if (it.type == KeyEventType.KeyDown) { + when (pressKey) { + Key.Escape -> cancel() + Key.Enter -> return@onKeyEvent false + else -> { + keyDown.add(pressKey) + keyCompose.add(pressKey) + } } + } else if (it.type == KeyEventType.KeyUp) { + keyDown.remove(pressKey) + if (keyDown.isEmpty()) done() } - } else if (it.type == KeyEventType.KeyUp) { - keyDown.remove(pressKey) - if (keyDown.isEmpty()) done() - } - true - }) { - Text(keyCompose.sortedKeys().joinToString(separator = "+") { - var cur = it - when (it) { - Key.MetaLeft, Key.MetaRight -> if (SystemUtils.IS_OS_MAC) cur = Key.CtrlLeft - Key.CtrlLeft, Key.CtrlRight -> if (SystemUtils.IS_OS_MAC) cur = Key.MetaLeft - } - KeyEvent.getKeyText(cur.nativeKeyCode) - }, Modifier.fillMaxWidth()) - } - Spacer(Modifier.weight(0.5f)) + true + }, + singleLine = true, + textStyle = MaterialTheme.typography.labelLarge.copy(LocalContentColor.current), + colors = TextFieldDefaults.colors( + unfocusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, + focusedIndicatorColor = MaterialTheme.colorScheme.outlineVariant, + ), + keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), + paddingValues = TextFieldDefaults.contentPaddingWithLabel(6.dp, 6.dp, 3.dp, 4.dp) + ) } - Spacer(Modifier.height(10.dp)) } - } } } \ No newline at end of file From c4fded991633ab3cb21fc4b19950ddd9f84affd2 Mon Sep 17 00:00:00 2001 From: Tryanks Date: Mon, 11 Dec 2023 03:23:13 +0800 Subject: [PATCH 6/6] Recon: NativeAudioPluginSettings Page --- .../processors/NativeAudioPluginSettings.kt | 207 ++++++------------ 1 file changed, 66 insertions(+), 141 deletions(-) diff --git a/native/src/jvmMain/kotlin/com/eimsound/dsp/native/processors/NativeAudioPluginSettings.kt b/native/src/jvmMain/kotlin/com/eimsound/dsp/native/processors/NativeAudioPluginSettings.kt index 54acbf1b..ee450a26 100644 --- a/native/src/jvmMain/kotlin/com/eimsound/dsp/native/processors/NativeAudioPluginSettings.kt +++ b/native/src/jvmMain/kotlin/com/eimsound/dsp/native/processors/NativeAudioPluginSettings.kt @@ -1,26 +1,31 @@ package com.eimsound.dsp.native.processors -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.* -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material.icons.filled.SettingsInputHdmi +import androidx.compose.material3.Icon +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import com.eimsound.audioprocessor.NativeAudioPluginFactory -import com.eimsound.daw.components.AbsoluteElevation import com.eimsound.daw.components.Gap import com.eimsound.daw.components.SettingTab -import com.eimsound.daw.components.utils.clickableWithIcon -import com.eimsound.daw.utils.* +import com.eimsound.daw.components.SettingsListManager +import com.eimsound.daw.components.SettingsSection +import com.eimsound.daw.utils.CurrentWindow +import com.eimsound.daw.utils.openFolderBrowser import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import java.io.File import javax.swing.JFileChooser import javax.swing.filechooser.FileNameExtensionFilter @@ -65,143 +70,63 @@ class NativeAudioPluginSettings: SettingTab { @Composable override fun content() { - Row(horizontalArrangement = Arrangement.spacedBy(20.dp)) { - AbsoluteElevation { - val window = CurrentWindow.current - val apm = NativeAudioPluginFactoryImpl.instance!! - Column(Modifier.width(300.dp)) { - Row { - Text( - text = "搜索路径", - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.padding(bottom = 8.dp).weight(1F), - ) - IconButton( - onClick = { - openFolderBrowser(window) { file -> - if (file == null) return@openFolderBrowser - apm.scanPaths.add(file.absolutePath) - apm.saveAsync() - } - }, - modifier = Modifier.clip(CircleShape).size(28.dp) - ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = "Add", - tint = MaterialTheme.colorScheme.outline - ) - } - } - Card { - apm.scanPaths.forEach { - ListItem( - { Text(it) }, - Modifier.background(MaterialTheme.colorScheme.surface).clickableWithIcon { - if (apm.pluginIsFile) openInExplorer(File(it)) - }, - trailingContent = { - IconButton( - { - apm.scanPaths.remove(it) - apm.saveAsync() - }, - modifier = Modifier.size(24.dp).weight(1F) - ) { - Icon(Icons.Filled.Delete, contentDescription = "Delete") - } - } - ) + Column { + val window = CurrentWindow.current + val apm = NativeAudioPluginFactoryImpl.instance!! + + SettingsSection("搜索路径") { + SettingsListManager( + list = apm.scanPaths, + onAddButtonClick = { + openFolderBrowser(window) { file -> + if (file == null) return@openFolderBrowser + apm.scanPaths.add(file.absolutePath) + apm.saveAsync() } + }, + onDelete = { + apm.scanPaths.remove(it) + apm.saveAsync() } + ) + } - if (scanningJob != null) { - Gap(6) - Box { - Text( - text = "正在搜索... (${apm.scannedCount}/${apm.allScanCount})", - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.padding(bottom = 8.dp), - ) + Gap(8) + + SettingsSection("排除路径") { + SettingsListManager( + list = apm.skipList, + onAddButtonClick = { + val fileChooser = JFileChooser().apply { + isAcceptAllFileFilterUsed = false + fileFilter = FileNameExtensionFilter("Native Audio Plugin Format (${ + apm.pluginExtensions.joinToString(", ") { ".$it" } + })", *apm.pluginExtensions.toTypedArray()) } - if (apm.allScanCount != 0) LinearProgressIndicator( - modifier = Modifier.padding(vertical = 4.dp).fillMaxWidth(), - progress = apm.scannedCount.toFloat() / apm.allScanCount, - ) - Card { - apm.scanningPlugins.forEach { (k, v) -> - key(k) { - ListItem( - { Text(k) }, - Modifier.background(MaterialTheme.colorScheme.surface).clickableWithIcon { - if (apm.pluginIsFile) selectInExplorer(File(k)) - }, - trailingContent = { - IconButton( - { v.destroy() }, - modifier = Modifier.size(24.dp).weight(1F) - ) { - Icon(Icons.Filled.Cancel, contentDescription = "Cancel") - } - } - ) - } - } + fileChooser.showOpenDialog(window) + fileChooser.selectedFile?.let { + apm.skipList.add(it.absolutePath) + apm.saveAsync() } + }, + onDelete = { + apm.skipList.remove(it) + apm.saveAsync() } - } + ) + } - Column(Modifier.weight(1F)) { - Row { - Text( - text = "排除路径", - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.padding(bottom = 8.dp).weight(1F), - ) - IconButton( - onClick = { - val fileChooser = JFileChooser().apply { - isAcceptAllFileFilterUsed = false - fileFilter = FileNameExtensionFilter("Native Audio Plugin Format (${ - apm.pluginExtensions.joinToString(", ") { ".$it" } - })", *apm.pluginExtensions.toTypedArray()) - } - fileChooser.showOpenDialog(window) - fileChooser.selectedFile?.let { - apm.skipList.add(it.absolutePath) - apm.saveAsync() - } - }, - modifier = Modifier.clip(CircleShape).size(28.dp) - ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = "Add", - tint = MaterialTheme.colorScheme.outline - ) - } - } - Card { - apm.skipList.forEach { - ListItem( - { Text(it) }, - Modifier.background(MaterialTheme.colorScheme.surface).clickableWithIcon { - if (apm.pluginIsFile) selectInExplorer(File(it)) - }, - trailingContent = { - IconButton( - { - apm.skipList.remove(it) - apm.saveAsync() - }, - modifier = Modifier.size(24.dp).weight(1F) - ) { - Icon(Icons.Filled.Delete, contentDescription = "Delete") - } - } + if (scanningJob != null) { + Gap(8) + SettingsSection("正在搜索... (${apm.scannedCount}/${apm.allScanCount})") { + if (apm.allScanCount != 0) LinearProgressIndicator( + modifier = Modifier.padding(vertical = 4.dp).fillMaxWidth(), + progress = apm.scannedCount.toFloat() / apm.allScanCount, + ) + apm.scanningPlugins.keys.let { + if (it.isNotEmpty()) { + SettingsListManager( + list = it ) } }