Skip to content

Commit

Permalink
Move Settings Screen to a File
Browse files Browse the repository at this point in the history
Also, refactor Settings screen and ExplorerState / ToolPaths which makes things cleaner.
  • Loading branch information
alonalbert committed May 5, 2024
1 parent b13cc79 commit 593750c
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 143 deletions.
92 changes: 2 additions & 90 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/KotlinExplorer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.SwingPanel
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.Key.Companion.D
import androidx.compose.ui.input.key.Key.Companion.F
import androidx.compose.ui.input.key.Key.Companion.G
Expand Down Expand Up @@ -57,10 +56,7 @@ import org.jetbrains.jewel.intui.window.decoratedWindow
import org.jetbrains.jewel.intui.window.styling.dark
import org.jetbrains.jewel.intui.window.styling.light
import org.jetbrains.jewel.ui.ComponentStyling
import org.jetbrains.jewel.ui.component.DefaultButton
import org.jetbrains.jewel.ui.component.Icon
import org.jetbrains.jewel.ui.component.Text
import org.jetbrains.jewel.ui.component.TextField
import org.jetbrains.jewel.window.DecoratedWindow
import org.jetbrains.jewel.window.TitleBar
import org.jetbrains.jewel.window.newFullscreenControls
Expand Down Expand Up @@ -323,89 +319,6 @@ private fun applyTheme(textArea: RSyntaxTextArea) {
}
}

@Composable
private fun ErrorIcon() {
Icon(
"icons/error.svg",
iconClass = Settings::class.java,
contentDescription = "Error",
tint = Color(0xffee4056)
)
}

@Composable
private fun ValidIcon() {
Icon(
"icons/done.svg",
iconClass = Settings::class.java,
contentDescription = "Valid",
tint = Color(0xff3369d6)
)
}


@Composable
private fun Settings(
explorerState: ExplorerState,
onSaveClick: () -> Unit
) {
var androidHome by remember { mutableStateOf(explorerState.toolPaths.androidHome.toString()) }
var kotlinHome by remember { mutableStateOf(explorerState.toolPaths.kotlinHome.toString()) }

Box(modifier = Modifier.fillMaxSize()) {
Column(modifier = Modifier.align(Alignment.Center)) {
Row {
Text(
"Android home directory: ",
modifier = Modifier.align(Alignment.CenterVertically)
)
TextField(
androidHome,
{ text -> androidHome = text },
modifier = Modifier.defaultMinSize(minWidth = 360.dp),
trailingIcon = {
if (!explorerState.toolPaths.isAndroidHomeValid) {
ErrorIcon()
} else {
ValidIcon()
}
}
)
}
Spacer(Modifier.height(8.dp))
Row {
Text(
"Kotlin home directory: ",
modifier = Modifier.align(Alignment.CenterVertically)
)
TextField(
kotlinHome,
{ text -> kotlinHome = text },
modifier = Modifier.defaultMinSize(minWidth = 360.dp),
trailingIcon = {
if (!explorerState.toolPaths.isKotlinHomeValid) {
ErrorIcon()
} else {
ValidIcon()
}
}
)
}
Spacer(Modifier.height(8.dp))
DefaultButton(
{
explorerState.settings.entries["ANDROID_HOME"] = androidHome
explorerState.settings.entries["KOTLIN_HOME"] = kotlinHome
explorerState.reloadToolPathsFromSettings()
onSaveClick()
}
) {
Text("Save")
}
}
}
}

private fun RSyntaxTextArea.updateStyle(explorerState: ExplorerState) {
val presentation = explorerState.presentationMode
font = font.deriveFont(if (presentation) FontSizePresentationMode else FontSizeEditingMode)
Expand All @@ -420,7 +333,7 @@ private fun updateTextArea(textArea: RSyntaxTextArea, text: String) {
fun main() = application {
val explorerState = remember { ExplorerState() }

Runtime.getRuntime().addShutdownHook(Thread { writeState(explorerState) })
Runtime.getRuntime().addShutdownHook(Thread { explorerState.writeState() })

val themeDefinition = if (KotlinExplorerTheme.System.isDark()) {
JewelTheme.darkThemeDefinition()
Expand Down Expand Up @@ -473,5 +386,4 @@ private fun ExplorerState.setWindowState(windowState: WindowState) {
windowPosX = windowState.position.x.value.toInt()
windowPosY = windowState.position.y.value.toInt()
windowPlacement = windowState.placement

}
}
29 changes: 4 additions & 25 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/Paths.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,16 @@ package dev.romainguy.kotlin.explorer

import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.stream.Stream
import kotlin.io.path.exists
import kotlin.io.path.extension
import kotlin.jvm.optionals.getOrElse

fun createToolPaths(settings: Settings): ToolPaths {
val androidHome = Paths.get(settings.entries.getOrElse("ANDROID_HOME") {
System.getenv("ANDROID_HOME") ?: System.getProperty("user.home")
})
val kotlinHome = Paths.get(settings.entries.getOrElse("KOTLIN_HOME") {
System.getenv("KOTLIN_HOME") ?: System.getProperty("user.home")
})

return ToolPaths(settings, androidHome, kotlinHome)
}

class ToolPaths internal constructor(
settings: Settings,
val androidHome: Path,
val kotlinHome: Path
) {
class ToolPaths(settingsDirectory: Path, androidHome: Path, kotlinHome: Path) {
val tempDirectory = Files.createTempDirectory("kotlin-explorer")!!
val platform: Path
val d8: Path
val adb: Path
val adb: Path = androidHome.resolve(if (isWindows) "platform-tools/adb.exe" else "platform-tools/adb")
val dexdump: Path
val kotlinc: Path
val kotlinLibs: List<Path>
Expand All @@ -57,7 +41,6 @@ class ToolPaths internal constructor(
private set

init {
adb = androidHome.resolve(if (isWindows) "platform-tools/adb.exe" else "platform-tools/adb")
val buildToolsDirectory = listIfExists(androidHome.resolve("build-tools"))
.sorted { p1, p2 ->
p2.toString().compareTo(p1.toString())
Expand Down Expand Up @@ -92,16 +75,12 @@ class ToolPaths internal constructor(
.getOrElse { lib.resolve("annotations.jar") }
)

sourceFile = settings.directory.resolve("source-code.kt")
sourceFile = settingsDirectory.resolve("source-code.kt")

isAndroidHomeValid = adb.exists() && d8.exists() && dexdump.exists()
isKotlinHomeValid = kotlinc.exists()
isValid = adb.exists() && d8.exists() && dexdump.exists() && kotlinc.exists()
}
}

private fun listIfExists(path: Path) = if (path.exists()) {
Files.list(path)
} else {
Stream.empty()
}
private fun listIfExists(path: Path) = if (path.exists()) Files.list(path) else Stream.empty()
105 changes: 105 additions & 0 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/Settings.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2023 Romain Guy
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@file:Suppress("FunctionName")

package dev.romainguy.kotlin.explorer

import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import org.jetbrains.jewel.ui.component.DefaultButton
import org.jetbrains.jewel.ui.component.Icon
import org.jetbrains.jewel.ui.component.Text
import org.jetbrains.jewel.ui.component.TextField

@Composable
fun Settings(
explorerState: ExplorerState,
onSaveClick: () -> Unit
) {
val androidHome = remember { mutableStateOf(explorerState.androidHome) }
val kotlinHome = remember { mutableStateOf(explorerState.kotlinHome) }

Box(modifier = Modifier.fillMaxSize()) {
Column(modifier = Modifier.align(Alignment.Center)) {
StringSetting("Android home directory: ", androidHome) { explorerState.toolPaths.isAndroidHomeValid }
Spacer(Modifier.height(8.dp))
StringSetting("Kotlin home directory: ", kotlinHome) { explorerState.toolPaths.isKotlinHomeValid }

DefaultButton({ explorerState.saveState(androidHome, kotlinHome, onSaveClick) }) {
Text("Save")
}
}
}
}

private fun ExplorerState.saveState(
androidHome: MutableState<String>,
kotlinHome: MutableState<String>,
onSaveClick: () -> Unit
) {
this.androidHome = androidHome.value
this.kotlinHome = kotlinHome.value
this.reloadToolPathsFromSettings()
onSaveClick()
}

@Composable
private fun StringSetting(title: String, state: MutableState<String>, isValid: () -> Boolean) {
Row {
Text(
title,
modifier = Modifier
.alignByBaseline()
.defaultMinSize(minWidth = 160.dp),
)
TextField(
value = state.value,
onValueChange = { state.value = it },
modifier = Modifier
.alignByBaseline()
.defaultMinSize(minWidth = 360.dp),
trailingIcon = { if (isValid()) ValidIcon() else ErrorIcon() }
)
}
}

@Composable
private fun ErrorIcon() {
Icon(
"icons/error.svg",
iconClass = ExplorerState::class.java,
contentDescription = "Error",
tint = Color(0xffee4056)
)
}

@Composable
private fun ValidIcon() {
Icon(
"icons/done.svg",
iconClass = ExplorerState::class.java,
contentDescription = "Valid",
tint = Color(0xff3369d6)
)
}
50 changes: 22 additions & 28 deletions src/jvmMain/kotlin/dev/romainguy/kotlin/explorer/State.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import java.nio.file.Paths
import kotlin.io.path.exists
import kotlin.io.path.readLines

private const val AndroidHome = "ANDROID_HOME"
private const val KotlinHome = "KOTLIN_HOME"
private const val Optimize = "OPTIMIZE"
private const val Presentation = "PRESENTATION"
private const val ShowLineNumbers = "SHOW_LINE_NUMBERS"
Expand All @@ -37,10 +39,14 @@ private const val WindowHeight = "WINDOW_HEIGHT"
private const val Placement = "WINDOW_PLACEMENT"

@Stable
class ExplorerState(
val settings: Settings = Settings()
) {
var toolPaths by mutableStateOf(createToolPaths(settings))
class ExplorerState {
private val directory = settingsPath()
private val file: Path = directory.resolve("settings")
private val entries: MutableMap<String, String> = readSettings(file)

var androidHome by StringState(AndroidHome, System.getenv("ANDROID_HOME") ?: System.getProperty("user.home"))
var kotlinHome by StringState(KotlinHome, System.getenv("KOTLIN_HOME") ?: System.getProperty("user.home"))
var toolPaths by mutableStateOf(createToolPaths())
var optimize by BooleanState(Optimize, true)
var presentationMode by BooleanState(Presentation, false)
var showLineNumbers by BooleanState(ShowLineNumbers, true)
Expand All @@ -54,37 +60,39 @@ class ExplorerState(
var windowPlacement by SettingsState(Placement, Floating) { WindowPlacement.valueOf(this) }

fun reloadToolPathsFromSettings() {
toolPaths = createToolPaths(settings)
toolPaths = createToolPaths()
}

private fun createToolPaths() = ToolPaths(directory, Path.of(androidHome), Path.of(kotlinHome))

private inner class BooleanState(key: String, initialValue: Boolean) :
SettingsState<Boolean>(key, initialValue, { toBoolean() })

private inner class IntState(key: String, initialValue: Int) :
SettingsState<Int>(key, initialValue, { toInt() })

private inner class StringState(key: String, initialValue: String) :
SettingsState<String>(key, initialValue, { this })

private open inner class SettingsState<T>(private val key: String, initialValue: T, parse: String.() -> T) :
MutableState<T> {
private val state = mutableStateOf(settings.entries[key]?.parse() ?: initialValue)
private val state = mutableStateOf(entries[key]?.parse() ?: initialValue)
override var value: T
get() = state.value
set(value) {
settings.entries[key] = value.toString()
entries[key] = value.toString()
state.value = value
}

override fun component1() = state.component1()

override fun component2() = state.component2()
}
}

data class Settings(
val directory: Path = settingsPath(),
val file: Path = directory.resolve("settings"),
val entries: MutableMap<String, String> = readSettings(file)
) {
fun getValue(name: String, defaultValue: String) = entries[name] ?: defaultValue
fun writeState() {
Files.writeString(toolPaths.sourceFile, sourceCode)
Files.writeString(file, entries.map { (key, value) -> "$key=$value" }.joinToString("\n"))
}
}

private fun settingsPath() = Paths.get(System.getProperty("user.home"), ".kotlin-explorer").apply {
Expand All @@ -110,17 +118,3 @@ private fun readSourceCode(toolPaths: ToolPaths) = if (toolPaths.sourceFile.exis
} else {
"fun square(a: Int): Int {\n return a * a\n}\n"
}

fun writeState(state: ExplorerState) {
Files.writeString(
state.toolPaths.sourceFile,
state.sourceCode
)
Files.writeString(
state.settings.file,
state.settings.entries
.map { "${it.key}=${it.value}" }
.joinToString("\n")
)
}

0 comments on commit 593750c

Please sign in to comment.