diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/AppToolWindowFactory.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ValkyrieToolWindow.kt similarity index 92% rename from idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/AppToolWindowFactory.kt rename to idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ValkyrieToolWindow.kt index a7be36ce..f0ef3dcd 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/AppToolWindowFactory.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ValkyrieToolWindow.kt @@ -11,18 +11,16 @@ import io.github.composegears.valkyrie.ui.ValkyriePlugin import io.github.composegears.valkyrie.ui.di.Koin import io.github.composegears.valkyrie.ui.foundation.theme.ValkyrieTheme -class AppToolWindowFactory : +class ValkyrieToolWindow : ToolWindowFactory, DumbAware { - init { - Koin.start() - } - override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { System.setProperty("compose.swing.render.on.graphics", "true") System.setProperty("compose.interop.blending", "true") + Koin.start(project) + toolWindow.addComposePanel { ValkyrieTheme( project = project, @@ -32,6 +30,10 @@ class AppToolWindowFactory : } } } + + companion object { + const val ID = "Valkyrie" + } } private fun ToolWindow.addComposePanel( @@ -41,7 +43,7 @@ private fun ToolWindow.addComposePanel( ) = PluginWindow(content = content) .also { contentManager.addContent(contentManager.factory.createContent(it, displayName, isLockable)) } -private class PluginWindow( +class PluginWindow( height: Int = 800, width: Int = 800, y: Int = 0, diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/ExportHereAction.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/ExportHereAction.kt new file mode 100644 index 00000000..bbf4cad7 --- /dev/null +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/ExportHereAction.kt @@ -0,0 +1,34 @@ +package io.github.composegears.valkyrie.action + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import io.github.composegears.valkyrie.parser.svgxml.PackageExtractor +import io.github.composegears.valkyrie.service.PersistentSettings.Companion.persistentSettings + +class ExportHereAction : AnAction() { + + override fun getActionUpdateThread() = ActionUpdateThread.BGT + + override fun actionPerformed(event: AnActionEvent) { + val file = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return + val project = event.project ?: return + + if (file.isDirectory) { + val predictedPackage = PackageExtractor.getFrom(path = file.path) + + project.persistentSettings.stateWithNotify { + if (predictedPackage != null) { + packageName = predictedPackage + } + iconPackDestination = file.path + } + } + } + + override fun update(event: AnActionEvent) { + val file = event.getData(CommonDataKeys.VIRTUAL_FILE) + event.presentation.isEnabled = file?.isDirectory == true && event.project != null + } +} diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/ImportFromDirectoryOrFileAction.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/ImportFromDirectoryOrFileAction.kt new file mode 100644 index 00000000..1e11a9bd --- /dev/null +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/ImportFromDirectoryOrFileAction.kt @@ -0,0 +1,69 @@ +package io.github.composegears.valkyrie.action + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.wm.ToolWindowManager +import io.github.composegears.valkyrie.ValkyrieToolWindow +import io.github.composegears.valkyrie.action.dialog.RequiredIconPackModeDialog +import io.github.composegears.valkyrie.parser.svgxml.util.isSvg +import io.github.composegears.valkyrie.parser.svgxml.util.isXml +import io.github.composegears.valkyrie.service.GlobalEventsHandler.Companion.globalEventsHandler +import io.github.composegears.valkyrie.service.GlobalEventsHandler.PluginEvents.ImportIcons +import io.github.composegears.valkyrie.service.PersistentSettings.Companion.persistentSettings +import io.github.composegears.valkyrie.ui.domain.model.Mode + +class ImportFromDirectoryOrFileAction : AnAction() { + + override fun getActionUpdateThread() = ActionUpdateThread.BGT + + override fun actionPerformed(event: AnActionEvent) { + val project = event.project ?: return + + val toolWindow = ToolWindowManager + .getInstance(project) + .getToolWindow(ValkyrieToolWindow.ID) ?: return + + val files = event + .getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) + ?.filterNotNull() ?: return + + val settings = project.persistentSettings.state + val eventsHandler = project.globalEventsHandler + + if (settings.mode == Mode.Unspecified.name || settings.mode == Mode.Simple.name) { + RequiredIconPackModeDialog(onOk = toolWindow::show).showAndGet() + } else { + toolWindow.show() + eventsHandler.send(ImportIcons(paths = files.map(VirtualFile::toNioPath))) + } + } + + override fun update(event: AnActionEvent) { + val files = event + .getData(CommonDataKeys.VIRTUAL_FILE_ARRAY) + ?.filterNotNull() + + if (files.isNullOrEmpty() || event.project == null) { + event.presentation.isVisible = false + } else { + if (files.size == 1) { + val file = files.first() + + when { + file.isDirectory -> event.presentation.text = "Import All" + else -> event.presentation.text = "Import Icon" + } + event.presentation.isEnabled = when { + file.isDirectory -> file.children.any { it.extension.isXml || it.extension.isSvg } + else -> file.extension.isXml || file.extension.isSvg + } + } else { + event.presentation.text = "Import All" + event.presentation.isEnabled = files.all { it.extension.isXml || it.extension.isSvg } + } + } + } +} diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/dialog/RequiredIconPackModeDialog.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/dialog/RequiredIconPackModeDialog.kt new file mode 100644 index 00000000..b4219f98 --- /dev/null +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/action/dialog/RequiredIconPackModeDialog.kt @@ -0,0 +1,35 @@ +package io.github.composegears.valkyrie.action.dialog + +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.intellij.openapi.ui.DialogWrapper +import io.github.composegears.valkyrie.PluginWindow +import io.github.composegears.valkyrie.ui.foundation.theme.ValkyrieTheme + +class RequiredIconPackModeDialog( + private val onOk: () -> Unit, +) : DialogWrapper(true) { + init { + title = "Valkyrie Warning" + init() + } + + override fun createCenterPanel() = PluginWindow { + ValkyrieTheme { + Text( + modifier = Modifier + .padding(vertical = 16.dp), + style = MaterialTheme.typography.bodySmall, + text = "Please setup IconPack mode before using this feature.", + ) + } + } + + override fun doOKAction() { + super.doOKAction() + onOk() + } +} diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/editor/ImageVectorPreviewEditorProvider.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/editor/ImageVectorPreviewEditorProvider.kt index b98df2d4..52369bca 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/editor/ImageVectorPreviewEditorProvider.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/editor/ImageVectorPreviewEditorProvider.kt @@ -7,7 +7,7 @@ import com.intellij.openapi.fileEditor.impl.text.QuickDefinitionProvider import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import io.github.composegears.valkyrie.settings.PersistentSettings +import io.github.composegears.valkyrie.service.PersistentSettings.Companion.persistentSettings class ImageVectorPreviewEditorProvider : FileEditorProvider, @@ -18,7 +18,7 @@ class ImageVectorPreviewEditorProvider : override fun getPolicy(): FileEditorPolicy = FileEditorPolicy.HIDE_DEFAULT_EDITOR override fun accept(project: Project, file: VirtualFile): Boolean { - val showImageVectorPreview = PersistentSettings.persistentSettings.showImageVectorPreview + val showImageVectorPreview = project.persistentSettings.state.showImageVectorPreview if (file.extension != "kt" || !showImageVectorPreview) return false diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/GlobalEventsHandler.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/GlobalEventsHandler.kt new file mode 100644 index 00000000..a9406874 --- /dev/null +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/GlobalEventsHandler.kt @@ -0,0 +1,27 @@ +package io.github.composegears.valkyrie.service + +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import java.nio.file.Path +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow + +class GlobalEventsHandler { + + private val _events = MutableSharedFlow(replay = 1) + val events = _events.asSharedFlow() + + fun send(event: PluginEvents) { + _events.tryEmit(event) + } + + sealed interface PluginEvents { + data class ImportIcons(val paths: List) : PluginEvents + } + + companion object { + @JvmStatic + val Project.globalEventsHandler: GlobalEventsHandler + get() = service() + } +} diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/PersistentSettings.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/PersistentSettings.kt similarity index 62% rename from idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/PersistentSettings.kt rename to idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/PersistentSettings.kt index 657b380f..ca4da8ac 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/PersistentSettings.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/PersistentSettings.kt @@ -1,18 +1,30 @@ -package io.github.composegears.valkyrie.settings +package io.github.composegears.valkyrie.service import com.intellij.openapi.components.BaseState -import com.intellij.openapi.components.Service import com.intellij.openapi.components.SimplePersistentStateComponent import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.openapi.components.service -import io.github.composegears.valkyrie.settings.PersistentSettings.ValkyrieState +import com.intellij.openapi.project.Project +import io.github.composegears.valkyrie.service.PersistentSettings.ValkyrieState import io.github.composegears.valkyrie.ui.domain.model.Mode -@Service @State(name = "Valkyrie.Settings", storages = [Storage("valkyrie_settings.xml")]) class PersistentSettings : SimplePersistentStateComponent(ValkyrieState()) { + private val listeners = mutableListOf<() -> Unit>() + + fun addListener(listener: () -> Unit) { + listeners += listener + } + + private fun notifyListeners() = listeners.forEach { it() } + + fun stateWithNotify(action: ValkyrieState.() -> Unit) { + state.action() + notifyListeners() + } + class ValkyrieState : BaseState() { var mode: String? by string(Mode.Unspecified.name) @@ -30,7 +42,7 @@ class PersistentSettings : SimplePersistentStateComponent(Valkyri companion object { @JvmStatic - val persistentSettings: ValkyrieState - get() = service().state + val Project.persistentSettings: PersistentSettings + get() = service() } } diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt index 415732dd..2f183b59 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt @@ -1,25 +1,35 @@ package io.github.composegears.valkyrie.settings +import com.intellij.openapi.project.Project import io.github.composegears.valkyrie.generator.imagevector.OutputFormat +import io.github.composegears.valkyrie.service.PersistentSettings +import io.github.composegears.valkyrie.service.PersistentSettings.Companion.persistentSettings import io.github.composegears.valkyrie.ui.domain.model.Mode import io.github.composegears.valkyrie.ui.extension.or -import io.github.composegears.valkyrie.ui.extension.updateState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow -class InMemorySettings { - private val _settings = MutableStateFlow(value = PersistentSettings.persistentSettings.toValkyriesSettings()) +class InMemorySettings(project: Project) { + private val persistentSettings = project.persistentSettings + + init { + persistentSettings.addListener { + _settings.value = persistentSettings.state.toValkyriesSettings() + } + } + + private val _settings = MutableStateFlow(persistentSettings.state.toValkyriesSettings()) val settings = _settings.asStateFlow() var uiState: Map = emptyMap() private set val current: ValkyriesSettings - get() = settings.value + get() = _settings.value fun update(action: PersistentSettings.ValkyrieState.() -> Unit) { - action(PersistentSettings.persistentSettings) - _settings.updateState { PersistentSettings.persistentSettings.toValkyriesSettings() } + action(persistentSettings.state) + _settings.value = persistentSettings.state.toValkyriesSettings() } fun clear() = update { diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/ValkyriePlugin.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/ValkyriePlugin.kt index 61184bca..549fbe30 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/ValkyriePlugin.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/ValkyriePlugin.kt @@ -3,13 +3,17 @@ package io.github.composegears.valkyrie.ui import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import com.composegears.tiamat.Navigation import com.composegears.tiamat.rememberNavController +import io.github.composegears.valkyrie.service.GlobalEventsHandler.Companion.globalEventsHandler +import io.github.composegears.valkyrie.service.GlobalEventsHandler.PluginEvents import io.github.composegears.valkyrie.settings.InMemorySettings import io.github.composegears.valkyrie.ui.domain.model.Mode.IconPack import io.github.composegears.valkyrie.ui.domain.model.Mode.Simple import io.github.composegears.valkyrie.ui.domain.model.Mode.Unspecified +import io.github.composegears.valkyrie.ui.foundation.theme.LocalProject import io.github.composegears.valkyrie.ui.screen.intro.IntroScreen import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.IconPackConversionScreen import io.github.composegears.valkyrie.ui.screen.mode.iconpack.creation.IconPackCreationScreen @@ -17,6 +21,8 @@ import io.github.composegears.valkyrie.ui.screen.mode.simple.conversion.SimpleCo import io.github.composegears.valkyrie.ui.screen.mode.simple.setup.SimpleModeSetupScreen import io.github.composegears.valkyrie.ui.screen.preview.CodePreviewScreen import io.github.composegears.valkyrie.ui.screen.settings.SettingsScreen +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import org.koin.compose.koinInject @Composable @@ -24,6 +30,7 @@ fun ValkyriePlugin( modifier: Modifier = Modifier, ) { val inMemorySettings = koinInject() + val project = LocalProject.current val navController = rememberNavController( destinations = arrayOf( @@ -63,6 +70,26 @@ fun ValkyriePlugin( }, ) + LaunchedEffect(Unit) { + project.globalEventsHandler + .events + .onEach { event -> + when (event) { + is PluginEvents.ImportIcons -> { + navController.editBackStack { + clear() + add(IntroScreen) + } + navController.navigate( + dest = IconPackConversionScreen, + navArgs = event.paths, + ) + } + } + } + .launchIn(this) + } + DisposableEffect(Unit) { onDispose { inMemorySettings.updateUIState(navController.getSavedState()) diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/di/Koin.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/di/Koin.kt index e25ae7b1..76749e84 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/di/Koin.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/di/Koin.kt @@ -1,6 +1,7 @@ package io.github.composegears.valkyrie.ui.di import com.composegears.tiamat.koin.tiamatViewModelOf +import com.intellij.openapi.project.Project import io.github.composegears.valkyrie.settings.InMemorySettings import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.IconPackConversionViewModel import io.github.composegears.valkyrie.ui.screen.mode.iconpack.existingpack.ui.viewmodel.ExistingPackViewModel @@ -14,9 +15,12 @@ import org.koin.dsl.module object Koin { - fun start() { + fun start(project: Project) { startKoin { - modules(appModule) + modules( + appModule, + provideProjectModule(project), + ) } } } @@ -34,3 +38,7 @@ private val appModule = module { singleOf(::InMemorySettings) } + +private fun provideProjectModule(project: Project) = module { + single { project } +} diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/foundation/theme/Theme.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/foundation/theme/Theme.kt index ff305fd8..49b1bcbc 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/foundation/theme/Theme.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/foundation/theme/Theme.kt @@ -25,8 +25,6 @@ fun ValkyrieTheme( currentComponent: Component, content: @Composable () -> Unit, ) { - val intelliJTheme = rememberIntelliJTheme() - val rootContent = @Composable { CompositionLocalProvider( LocalProject provides project, @@ -35,14 +33,21 @@ fun ValkyrieTheme( ) } + ValkyrieTheme(rootContent) +} + +@Composable +fun ValkyrieTheme(content: @Composable () -> Unit) { + val intelliJTheme = rememberIntelliJTheme() + when (intelliJTheme.theme) { Theme.DARK -> IntellijDarkTheme( background = intelliJTheme.background, onBackground = intelliJTheme.onBackground, primary = intelliJTheme.primary, - content = rootContent, + content = content, ) - Theme.LIGHT -> IntellijLightTheme(content = rootContent) + Theme.LIGHT -> IntellijLightTheme(content = content) } } diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionScreen.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionScreen.kt index 517fffd7..3fc6348e 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionScreen.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionScreen.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.unit.dp import com.composegears.tiamat.koin.koinTiamatViewModel +import com.composegears.tiamat.navArgsOrNull import com.composegears.tiamat.navController import com.composegears.tiamat.navDestination import com.composegears.tiamat.navigationSlideInOut @@ -53,13 +54,16 @@ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.ui.Bat import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.ui.IconPackPickerStateUi import io.github.composegears.valkyrie.ui.screen.preview.CodePreviewScreen import io.github.composegears.valkyrie.ui.screen.settings.SettingsScreen +import java.nio.file.Path import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import org.koin.core.parameter.parametersOf -val IconPackConversionScreen by navDestination { +val IconPackConversionScreen by navDestination> { val navController = navController() + val navArgs = navArgsOrNull() - val viewModel = koinTiamatViewModel() + val viewModel = koinTiamatViewModel { parametersOf(navArgs.orEmpty()) } val state by viewModel.state.collectAsState() LaunchedEffect(Unit) { diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionViewModel.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionViewModel.kt index cbd7b994..b2d11544 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionViewModel.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionViewModel.kt @@ -19,6 +19,7 @@ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.IconPa import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.util.toPainterOrNull import java.nio.file.Path import kotlin.io.path.extension +import kotlin.io.path.isDirectory import kotlin.io.path.isRegularFile import kotlin.io.path.listDirectoryEntries import kotlin.io.path.name @@ -26,7 +27,6 @@ import kotlin.io.path.nameWithoutExtension import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -34,6 +34,7 @@ import kotlinx.coroutines.withContext class IconPackConversionViewModel( private val inMemorySettings: InMemorySettings, + paths: List, ) : TiamatViewModel() { private val _state = MutableStateFlow(IconsPickering) @@ -42,7 +43,15 @@ class IconPackConversionViewModel( private val _events = MutableSharedFlow() val events = _events.asSharedFlow() - private val valkyriesSettings: StateFlow = inMemorySettings.settings + init { + if (paths.isNotEmpty()) { + if (paths.size == 1 && paths.first().isDirectory()) { + pickerEvent(PickerEvent.PickDirectory(paths.first())) + } else { + pickerEvent(PickerEvent.PickFiles(paths)) + } + } + } fun pickerEvent(events: PickerEvent) { when (events) { @@ -109,6 +118,7 @@ class IconPackConversionViewModel( val icon = icons.first { it.iconName == iconName }.cast() + val settings = inMemorySettings.current val iconResult = runCatching { val parserOutput = SvgXmlParser.toIrImageVector(icon.path) @@ -117,10 +127,10 @@ class IconPackConversionViewModel( kotlinName = iconName.value, config = ImageVectorGeneratorConfig( packageName = icon.iconPack.iconPackage, - packName = valkyriesSettings.value.iconPackName, + packName = settings.iconPackName, nestedPackName = icon.iconPack.currentNestedPack, - outputFormat = valkyriesSettings.value.outputFormat, - generatePreview = valkyriesSettings.value.generatePreview, + outputFormat = settings.outputFormat, + generatePreview = settings.generatePreview, ), ) }.getOrDefault(ImageVectorSpecOutput.empty) @@ -130,8 +140,6 @@ class IconPackConversionViewModel( } fun export() = viewModelScope.launch { - val settings = inMemorySettings.current - withContext(Dispatchers.Default) { val icons = when (val state = _state.value) { is BatchProcessing.IconPackCreationState -> state.icons @@ -140,6 +148,8 @@ class IconPackConversionViewModel( _state.updateState { BatchProcessing.ExportingState } + val settings = inMemorySettings.current + icons .filterIsInstance() .forEach { icon -> @@ -151,10 +161,10 @@ class IconPackConversionViewModel( kotlinName = icon.iconName.value, config = ImageVectorGeneratorConfig( packageName = icon.iconPack.iconPackage, - packName = valkyriesSettings.value.iconPackName, + packName = settings.iconPackName, nestedPackName = iconPack.currentNestedPack, - outputFormat = valkyriesSettings.value.outputFormat, - generatePreview = valkyriesSettings.value.generatePreview, + outputFormat = settings.outputFormat, + generatePreview = settings.generatePreview, ), ) @@ -171,10 +181,10 @@ class IconPackConversionViewModel( kotlinName = icon.iconName.value, config = ImageVectorGeneratorConfig( packageName = icon.iconPack.iconPackage, - packName = valkyriesSettings.value.iconPackName, + packName = settings.iconPackName, nestedPackName = "", - outputFormat = valkyriesSettings.value.outputFormat, - generatePreview = valkyriesSettings.value.generatePreview, + outputFormat = settings.outputFormat, + generatePreview = settings.generatePreview, ), ) diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/creation/common/inputhandler/InputHandler.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/creation/common/inputhandler/InputHandler.kt index 1acc7f27..09f5d88d 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/creation/common/inputhandler/InputHandler.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/creation/common/inputhandler/InputHandler.kt @@ -1,5 +1,6 @@ package io.github.composegears.valkyrie.ui.screen.mode.iconpack.creation.common.inputhandler +import io.github.composegears.valkyrie.settings.ValkyriesSettings import io.github.composegears.valkyrie.ui.domain.validation.IconPackValidationUseCase import io.github.composegears.valkyrie.ui.domain.validation.InputState import io.github.composegears.valkyrie.ui.domain.validation.PackageValidationUseCase @@ -93,6 +94,12 @@ abstract class BasicInputHandler(initialState: InputFieldState) : InputHandler { _state.updateState { inputFieldState } } + fun invalidate(settings: ValkyriesSettings) { + _state.updateState { provideInputFieldState(settings) } + } + + abstract fun provideInputFieldState(settings: ValkyriesSettings): InputFieldState + private suspend fun validate() { val inputFieldState = _state.value val packageResult = packageValidationUseCase(inputFieldState.packageName.text) diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/existingpack/ui/viewmodel/ExistingPackInputHandler.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/existingpack/ui/viewmodel/ExistingPackInputHandler.kt index dcfe15a3..d7d1bef1 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/existingpack/ui/viewmodel/ExistingPackInputHandler.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/existingpack/ui/viewmodel/ExistingPackInputHandler.kt @@ -1,14 +1,22 @@ package io.github.composegears.valkyrie.ui.screen.mode.iconpack.existingpack.ui.viewmodel +import io.github.composegears.valkyrie.settings.ValkyriesSettings import io.github.composegears.valkyrie.ui.domain.validation.InputState import io.github.composegears.valkyrie.ui.screen.mode.iconpack.creation.common.inputhandler.BasicInputHandler import io.github.composegears.valkyrie.ui.screen.mode.iconpack.creation.common.packedit.model.InputFieldState -class ExistingPackInputHandler : - BasicInputHandler( - initialState = InputFieldState( - iconPackName = InputState(), - packageName = InputState(), - nestedPacks = emptyList(), - ), - ) +class ExistingPackInputHandler : BasicInputHandler(initialState = existingPackInputFieldState) { + + override fun provideInputFieldState( + settings: ValkyriesSettings, + ): InputFieldState = existingPackInputFieldState + + companion object { + private val existingPackInputFieldState: InputFieldState + get() = InputFieldState( + iconPackName = InputState(), + packageName = InputState(), + nestedPacks = emptyList(), + ) + } +} diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackInputHandler.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackInputHandler.kt index 28f4b689..3c952fb7 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackInputHandler.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackInputHandler.kt @@ -9,17 +9,25 @@ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.creation.common.p class NewPackInputHandler( settings: ValkyriesSettings, -) : BasicInputHandler( - InputFieldState( - iconPackName = InputState(text = settings.iconPackName), - packageName = InputState( - text = PackageExtractor.getFrom(path = settings.iconPackDestination) ?: settings.packageName, - ), - nestedPacks = settings.nestedPacks.mapIndexed { index, nestedPack -> - NestedPack( - id = index.toString(), - inputFieldState = InputState(text = nestedPack), +) : BasicInputHandler(initialState = settings.newPackInputFieldState) { + + override fun provideInputFieldState( + settings: ValkyriesSettings, + ): InputFieldState = settings.newPackInputFieldState + + companion object { + private val ValkyriesSettings.newPackInputFieldState: InputFieldState + get() = InputFieldState( + iconPackName = InputState(text = iconPackName), + packageName = InputState( + text = PackageExtractor.getFrom(path = iconPackDestination) ?: packageName, + ), + nestedPacks = nestedPacks.mapIndexed { index, nestedPack -> + NestedPack( + id = index.toString(), + inputFieldState = InputState(text = nestedPack), + ) + }, ) - }, - ), -) + } +} diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackViewModel.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackViewModel.kt index 935ffe3b..55310e6e 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackViewModel.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/newpack/ui/viewmodel/NewPackViewModel.kt @@ -6,6 +6,7 @@ import io.github.composegears.valkyrie.generator.iconpack.IconPackGenerator import io.github.composegears.valkyrie.generator.iconpack.IconPackGeneratorConfig import io.github.composegears.valkyrie.parser.svgxml.PackageExtractor import io.github.composegears.valkyrie.settings.InMemorySettings +import io.github.composegears.valkyrie.settings.ValkyriesSettings import io.github.composegears.valkyrie.ui.extension.updateState import io.github.composegears.valkyrie.ui.screen.mode.iconpack.creation.common.packedit.model.InputChange import io.github.composegears.valkyrie.ui.screen.mode.iconpack.creation.common.packedit.model.PackEditState @@ -30,14 +31,16 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch class NewPackViewModel( private val inMemorySettings: InMemorySettings, ) : TiamatViewModel() { - private val settings = inMemorySettings.current - private var inputHandler = NewPackInputHandler(inMemorySettings.current) + private val currentSettings = inMemorySettings.current + private var inputHandler = NewPackInputHandler(currentSettings) private val _events = MutableSharedFlow() val events = _events.asSharedFlow() @@ -45,13 +48,7 @@ class NewPackViewModel( private val currentState: NewPackModeState get() = state.value - private val _state = MutableStateFlow( - ChooseExportDirectoryState( - iconPackDestination = settings.iconPackDestination, - predictedPackage = PackageExtractor.getFrom(path = settings.iconPackDestination).orEmpty(), - nextAvailable = settings.iconPackDestination.isNotEmpty(), - ), - ) + private val _state = MutableStateFlow(currentSettings.toChooseDirectoryState()) val state = _state.asStateFlow() init { @@ -69,6 +66,12 @@ class NewPackViewModel( } } } + inMemorySettings.settings + .onEach { settings -> + _state.updateState { settings.toChooseDirectoryState() } + inputHandler.invalidate(settings) + } + .launchIn(viewModelScope) } fun onAction(action: NewPackAction) { @@ -139,4 +142,10 @@ class NewPackViewModel( _events.emit(OnSettingsUpdated) } } + + private fun ValkyriesSettings.toChooseDirectoryState() = ChooseExportDirectoryState( + iconPackDestination = iconPackDestination, + predictedPackage = PackageExtractor.getFrom(path = iconPackDestination).orEmpty(), + nextAvailable = iconPackDestination.isNotEmpty(), + ) } diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/GeneralSettingsScreen.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/GeneralSettingsScreen.kt index 65f2b3cc..a89d4013 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/GeneralSettingsScreen.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/GeneralSettingsScreen.kt @@ -34,7 +34,6 @@ import com.composegears.tiamat.koin.koinSharedTiamatViewModel import com.composegears.tiamat.navController import com.composegears.tiamat.navDestination import io.github.composegears.valkyrie.generator.imagevector.OutputFormat -import io.github.composegears.valkyrie.settings.InMemorySettings import io.github.composegears.valkyrie.settings.ValkyriesSettings import io.github.composegears.valkyrie.ui.domain.model.Mode.IconPack import io.github.composegears.valkyrie.ui.domain.model.Mode.Simple @@ -50,15 +49,12 @@ import io.github.composegears.valkyrie.ui.foundation.theme.PreviewTheme import io.github.composegears.valkyrie.ui.platform.rememberCurrentProject import io.github.composegears.valkyrie.ui.screen.intro.IntroScreen import io.github.composegears.valkyrie.ui.screen.settings.SettingsViewModel -import org.koin.compose.koinInject val GeneralSettingsScreen by navDestination { val navController = navController() - val inMemorySettings = koinInject() - val settings by inMemorySettings.settings.collectAsState() - - val settingsViewModel = koinSharedTiamatViewModel() + val viewModel = koinSharedTiamatViewModel() + val settings by viewModel.settings.collectAsState() var showClearSettingsDialog by rememberMutableState { false } @@ -68,7 +64,7 @@ val GeneralSettingsScreen by navDestination { showClearSettingsDialog = true }, onChangeMode = { - settingsViewModel.resetMode() + viewModel.resetMode() openIntro(navController) }, ) @@ -76,7 +72,7 @@ val GeneralSettingsScreen by navDestination { if (showClearSettingsDialog) { ClearSettingsDialog( onClear = { - settingsViewModel.clearSettings() + viewModel.clearSettings() showClearSettingsDialog = false openIntro(navController) diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/ImageVectorPreviewSettingsScreen.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/ImageVectorPreviewSettingsScreen.kt index 1cef7a28..356b9759 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/ImageVectorPreviewSettingsScreen.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/ImageVectorPreviewSettingsScreen.kt @@ -21,24 +21,20 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.composegears.tiamat.koin.koinSharedTiamatViewModel import com.composegears.tiamat.navDestination -import io.github.composegears.valkyrie.settings.InMemorySettings import io.github.composegears.valkyrie.ui.foundation.VerticalSpacer import io.github.composegears.valkyrie.ui.foundation.dim import io.github.composegears.valkyrie.ui.foundation.theme.PreviewTheme import io.github.composegears.valkyrie.ui.screen.settings.SettingsViewModel import io.github.composegears.valkyrie.ui.screen.settings.model.SettingsAction import io.github.composegears.valkyrie.ui.screen.settings.model.SettingsAction.UpdateImageVectorPreview -import org.koin.compose.koinInject val ImageVectorPreviewSettingsScreen by navDestination { - val inMemorySettings = koinInject() - val settings by inMemorySettings.settings.collectAsState() - - val settingsViewModel = koinSharedTiamatViewModel() + val viewModel = koinSharedTiamatViewModel() + val settings by viewModel.settings.collectAsState() ImageVectorPreviewSettingsUi( showImageVectorPreview = settings.showImageVectorPreview, - onAction = settingsViewModel::onAction, + onAction = viewModel::onAction, ) } diff --git a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/export/ImageVectorExportSettingsScreen.kt b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/export/ImageVectorExportSettingsScreen.kt index 16ab030d..31624331 100644 --- a/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/export/ImageVectorExportSettingsScreen.kt +++ b/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/settings/tabs/export/ImageVectorExportSettingsScreen.kt @@ -22,23 +22,19 @@ import androidx.compose.ui.unit.sp import com.composegears.tiamat.koin.koinSharedTiamatViewModel import com.composegears.tiamat.navDestination import io.github.composegears.valkyrie.generator.imagevector.OutputFormat -import io.github.composegears.valkyrie.settings.InMemorySettings import io.github.composegears.valkyrie.ui.foundation.VerticalSpacer import io.github.composegears.valkyrie.ui.foundation.dim import io.github.composegears.valkyrie.ui.foundation.theme.PreviewTheme import io.github.composegears.valkyrie.ui.screen.settings.SettingsViewModel import io.github.composegears.valkyrie.ui.screen.settings.model.SettingsAction import io.github.composegears.valkyrie.ui.screen.settings.model.SettingsAction.UpdatePreviewGeneration -import org.koin.compose.koinInject val ImageVectorExportSettingsScreen by navDestination { - val inMemorySettings = koinInject() - val settings by inMemorySettings.settings.collectAsState() - - val settingsViewModel = koinSharedTiamatViewModel() + val viewModel = koinSharedTiamatViewModel() + val settings by viewModel.settings.collectAsState() ImageVectorExportSettingsUi( - onAction = settingsViewModel::onAction, + onAction = viewModel::onAction, outputFormat = settings.outputFormat, generatePreview = settings.generatePreview, ) diff --git a/idea-plugin/src/main/resources/META-INF/plugin.xml b/idea-plugin/src/main/resources/META-INF/plugin.xml index 092a5938..e45689b4 100644 --- a/idea-plugin/src/main/resources/META-INF/plugin.xml +++ b/idea-plugin/src/main/resources/META-INF/plugin.xml @@ -30,16 +30,43 @@ Allows to create organized icon pack with an extension property of you pack obje org.jetbrains.kotlin + + + + + + + + + + + + + + + - + + +