Skip to content

Commit f6c00db

Browse files
committed
chore: compose desktop changes
1 parent 644a7b2 commit f6c00db

File tree

10 files changed

+155
-36
lines changed

10 files changed

+155
-36
lines changed

compose/desktop/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ kotlin {
1919
implementation(compose.runtime)
2020
implementation(compose.foundation)
2121
implementation(compose.material3)
22+
implementation(compose.materialIconsExtended)
2223
implementation(compose.components.resources)
2324
implementation(compose.components.uiToolingPreview)
2425
// project.dependencies.detektPlugins(libs.detekt.compose.rules)

compose/desktop/src/jvmMain/kotlin/Components.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@ import androidx.compose.ui.geometry.CornerRadius
1212
import androidx.compose.ui.graphics.Color
1313
import androidx.compose.ui.graphics.PathEffect
1414
import androidx.compose.ui.graphics.drawscope.Stroke
15+
import androidx.compose.ui.graphics.painter.Painter
16+
import androidx.compose.ui.graphics.toAwtImage
1517
import androidx.compose.ui.platform.LocalDensity
18+
import androidx.compose.ui.unit.Density
1619
import androidx.compose.ui.unit.Dp
20+
import androidx.compose.ui.unit.LayoutDirection
1721
import androidx.compose.ui.unit.dp
22+
import java.awt.image.BufferedImage
23+
import java.io.File
24+
import javax.imageio.ImageIO
1825

1926
fun Modifier.debug(color: Color = Color.Red) =
2027
then(if (debug) dashedBorder(1.dp, color, 8.dp) else this)
@@ -44,3 +51,11 @@ fun scrollingBox(modifier: Modifier = Modifier, content: @Composable () -> Unit)
4451
content()
4552
}
4653
}
54+
55+
fun Painter.toPngImage(file: File) {
56+
val img = toAwtImage(Density(1f), LayoutDirection.Ltr)
57+
BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB).run {
58+
graphics.drawImage(img, 0, 0, null)
59+
ImageIO.write(this, "png", file)
60+
}
61+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,118 @@
1+
@file:OptIn(ExperimentalComposeUiApi::class)
12

3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Box
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.lazy.LazyColumn
8+
import androidx.compose.foundation.lazy.items
9+
import androidx.compose.foundation.shape.RoundedCornerShape
10+
import androidx.compose.material.OutlinedButton
11+
import androidx.compose.material.Text
12+
import androidx.compose.material.icons.Icons
13+
import androidx.compose.material.icons.filled.FileUpload
14+
import androidx.compose.material3.Icon
15+
import androidx.compose.runtime.*
16+
import androidx.compose.ui.*
17+
import androidx.compose.ui.graphics.Color
18+
import androidx.compose.ui.unit.dp
19+
import androidx.compose.ui.unit.sp
20+
import java.net.URI
21+
import java.nio.file.LinkOption
22+
import java.nio.file.Path
23+
import kotlin.io.path.exists
24+
import kotlin.io.path.toPath
25+
26+
private object Colors {
27+
val default = Color.Gray
28+
val active = Color(29, 117, 223, 255)
29+
val fileItemBg = Color(233, 30, 99, 255)
30+
val fileItemFg = Color.White
31+
}
32+
33+
@Composable
34+
fun DragDropBox(modifier: Modifier = Modifier, onDrop: (DragData) -> Unit) {
35+
var isDragging by remember { mutableStateOf(false) }
36+
val dndColor = if (isDragging) Colors.active else Colors.default
37+
var fdOpen by remember { mutableStateOf(false) }
38+
39+
// val (textField, setTextField) = remember { mutableStateOf(TextFieldValue()) }
40+
41+
Box(
42+
modifier =
43+
modifier
44+
.dashedBorder(strokeWidth = 2.dp, color = dndColor, cornerRadius = 8.dp)
45+
.onExternalDrag(
46+
onDragStart = { isDragging = true },
47+
onDragExit = { isDragging = false },
48+
onDrop = {
49+
isDragging = false
50+
onDrop(it.dragData)
51+
})) {
52+
Column(modifier = Modifier.align(Alignment.Center)) {
53+
Text(
54+
"Drag & drop files here",
55+
modifier = Modifier.padding(20.dp),
56+
color = dndColor,
57+
fontSize = 14.sp)
58+
59+
OutlinedButton(
60+
modifier = modifier.align(Alignment.CenterHorizontally).padding(10.dp),
61+
enabled = isDragging.not(),
62+
shape = RoundedCornerShape(10.dp),
63+
onClick = { fdOpen = !fdOpen }) {
64+
Icon(Icons.Filled.FileUpload, "Upload")
65+
Text("Select")
66+
}
67+
68+
if (fdOpen) {
69+
FileDialog { files ->
70+
val data =
71+
object : DragData.FilesList {
72+
override fun readFiles() = files.map { it.toURI().toString() }
73+
}
74+
75+
onDrop(data)
76+
fdOpen = false
77+
}
78+
// DisposableEffect(isOpen) {// open FileChooser}
79+
}
80+
}
81+
}
82+
}
83+
84+
@Composable
85+
fun FileListView(modifier: Modifier = Modifier, files: List<Path>) {
86+
LazyColumn(modifier) {
87+
items(files) {
88+
Box(
89+
modifier =
90+
Modifier.padding(5.dp).background(Colors.fileItemBg, RoundedCornerShape(10.dp))) {
91+
Text(
92+
modifier = Modifier.padding(4.dp),
93+
text = it.fileName.toString(),
94+
color = Colors.fileItemFg,
95+
fontSize = 14.sp)
96+
}
97+
}
98+
}
99+
}
100+
101+
@Composable
102+
fun DragDropListView() {
103+
Box(modifier = Modifier, contentAlignment = Alignment.Center) {
104+
Column {
105+
var droppedPaths by remember { mutableStateOf(emptyList<Path>()) }
106+
DragDropBox { dragData ->
107+
if (dragData is DragData.FilesList) {
108+
val newPaths =
109+
dragData.readFiles().mapNotNull {
110+
URI(it).toPath().takeIf { it.exists(LinkOption.NOFOLLOW_LINKS) }
111+
}
112+
droppedPaths = (newPaths + droppedPaths).distinct()
113+
}
114+
}
115+
FileListView(files = droppedPaths)
116+
}
117+
}
118+
}

compose/desktop/src/jvmMain/kotlin/FileChooser.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import java.io.File
66
import javax.swing.JFileChooser
77
import javax.swing.UIManager
88
import javax.swing.filechooser.FileNameExtensionFilter
9+
import javax.swing.filechooser.FileSystemView
910

1011
@Composable
1112
fun FileDialog(parent: Frame? = null, onClose: (result: List<File>) -> Unit) =
@@ -25,20 +26,20 @@ fun FileDialog(parent: Frame? = null, onClose: (result: List<File>) -> Unit) =
2526
},
2627
dispose = FileDialog::dispose)
2728

28-
fun fileChooser(parent: Frame? = null): List<File> {
29+
fun fileChooser(parent: Frame? = null, onClose: (result: List<File>) -> Unit) {
2930
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
3031
val chooser =
31-
JFileChooser().apply {
32-
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
32+
JFileChooser(FileSystemView.getFileSystemView()).apply {
33+
currentDirectory = File(System.getProperty("user.dir"))
34+
fileSelectionMode = JFileChooser.FILES_AND_DIRECTORIES
3335
isMultiSelectionEnabled = true
3436
dialogTitle = "Select a folder"
3537
approveButtonText = "Select"
3638
approveButtonToolTipText = "Select current directory as save destination"
3739
fileFilter = FileNameExtensionFilter("JPG & GIF Images", "jpg", "gif")
3840
}
3941

40-
chooser.showOpenDialog(parent)
4142
val files = chooser.selectedFiles.toList()
4243
chooser.isVisible = false
43-
return files
44+
onClose(files)
4445
}

compose/desktop/src/jvmMain/kotlin/Main.kt

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@file:OptIn(ExperimentalComposeUiApi::class)
2+
13
import androidx.compose.animation.AnimatedVisibility
24
import androidx.compose.desktop.ui.tooling.preview.Preview
35
import androidx.compose.foundation.ExperimentalFoundationApi
@@ -6,14 +8,11 @@ import androidx.compose.foundation.basicMarquee
68
import androidx.compose.foundation.layout.*
79
import androidx.compose.foundation.text.selection.SelectionContainer
810
import androidx.compose.material.Button
9-
import androidx.compose.material.FloatingActionButton
1011
import androidx.compose.material.MaterialTheme
1112
import androidx.compose.material.Text
12-
import androidx.compose.material.icons.Icons
13-
import androidx.compose.material.icons.filled.KeyboardArrowUp
14-
import androidx.compose.material3.Icon
1513
import androidx.compose.runtime.*
1614
import androidx.compose.ui.Alignment
15+
import androidx.compose.ui.ExperimentalComposeUiApi
1716
import androidx.compose.ui.Modifier
1817
import androidx.compose.ui.graphics.Color
1918
import androidx.compose.ui.res.painterResource
@@ -44,7 +43,6 @@ fun App() {
4443
val coroutineScope = rememberCoroutineScope()
4544
var text by remember { mutableStateOf(AnnotatedString("Hello, Compose!")) }
4645
var showImage by remember { mutableStateOf(false) }
47-
var isOpen by remember { mutableStateOf(false) }
4846
val resource = resourcesDir.resolve("resource.txt")
4947

5048
Column(
@@ -97,20 +95,7 @@ fun App() {
9795
AnimatedVisibility(visible = showImage) {
9896
Image(painter = painterResource("svg/idea-logo.svg"), contentDescription = "Logo")
9997
}
100-
101-
FloatingActionButton(
102-
onClick = { isOpen = !isOpen },
103-
modifier = Modifier.padding(5.dp).size(30.dp).align(Alignment.End)) {
104-
Icon(Icons.Default.KeyboardArrowUp, "File")
105-
}
106-
107-
if (isOpen) {
108-
FileDialog {
109-
println("Selected Files: $it")
110-
isOpen = false
111-
}
112-
// DisposableEffect(isOpen) {// open FileChooser}
113-
}
98+
DragDropListView()
11499
}
115100
}
116101

gradle/build-logic/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ idea {
2626
kotlin {
2727
compilerOptions {
2828
jvmTarget = dslJavaVersion.map(JvmTarget::fromTarget)
29-
freeCompilerArgs.appendAll("-Xcontext-receivers", "-Xjdk-release=${dslJavaVersion.get()}")
29+
freeCompilerArgs.addAll("-Xcontext-receivers", "-Xjdk-release=${dslJavaVersion.get()}")
3030
optIn =
3131
listOf(
3232
"kotlin.ExperimentalStdlibApi",

gradle/build-logic/src/main/kotlin/common/ProjectExtns.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ fun KotlinCommonCompilerOptions.configureKotlinCommon() {
403403
allWarningsAsErrors = false
404404
suppressWarnings = false
405405
verbose = false
406-
freeCompilerArgs.appendAll(
406+
freeCompilerArgs.addAll(
407407
"-Xcontext-receivers",
408408
"-Xexpect-actual-classes",
409409
"-Xskip-prerelease-check",
@@ -455,7 +455,7 @@ fun KotlinJvmCompilerOptions.configureKotlinJvm() {
455455
verbose = true
456456
allWarningsAsErrors = false
457457
suppressWarnings = false
458-
freeCompilerArgs.appendAll(
458+
freeCompilerArgs.addAll(
459459
"-Xadd-modules=$addModules",
460460
"-Xjsr305=strict",
461461
"-Xjvm-default=all",
@@ -478,7 +478,7 @@ fun KotlinJvmCompilerOptions.configureKotlinJvm() {
478478

479479
context(Project)
480480
fun KotlinNativeCompilerOptions.configureKotlinNative() {
481-
freeCompilerArgs.appendAll(
481+
freeCompilerArgs.addAll(
482482
// "-Xverbose-phases=Linker"
483483
// "-Xruntime-logs=gc=info"
484484
)
@@ -550,7 +550,7 @@ fun KotlinTestReport.configureTestReport() {}
550550
context(Project)
551551
fun KotlinJsCompilerOptions.configureKotlinJs() {
552552
useEsClasses = true
553-
freeCompilerArgs.appendAll("-Xir-per-file")
553+
freeCompilerArgs.addAll("-Xir-per-file")
554554
// target = "es2015"
555555
// sourceMap = true
556556
// sourceMapEmbedSources = "always"

gradle/libs.versions.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ kotlin-jvmtarget = "22"
66
kotlin-dsl-jvmtarget = "21"
77
kotlin-api-version = "2.0"
88
kotlin-lang-version = "2.0"
9-
gradle = "8.8-rc-1"
9+
gradle = "8.8-rc-2"
1010
java-vendor = "Oracle"
1111
java-jvmArguments = "--enable-preview"
1212
java-addModules = "jdk.incubator.vector"
@@ -41,7 +41,7 @@ kotlinx-multik = "0.2.3"
4141
kotlinx-dataframe = "0.13.1"
4242
kotlinx-kandy = "0.5.0"
4343
karakum = "1.0.0-alpha.40-K2"
44-
seskar = "2.66.0"
44+
seskar = "2.80.0"
4545
ktor = "3.0.0-beta-1"
4646
ktor-cohort = "2.5.1"
4747
otel = "1.38.0"
@@ -83,7 +83,7 @@ google-auto-service = "1.1.1"
8383
google-tink = "1.13.0"
8484
graalvm = "24.0.1"
8585
jspecify = "0.3.0"
86-
rsocket = "0.15.4"
86+
rsocket = "0.16.0"
8787
jctools = "4.0.1"
8888
benasher44-uuid = "0.8.4"
8989
kotlinx-uuid = "0.0.25"
@@ -133,7 +133,7 @@ compose-routing = "0.2.14"
133133
async-profiler = "3.0"
134134
ap-loader-all = "3.0-9"
135135
openjdk-jmc = "9.0.0"
136-
airlift-aircompressor = "0.25"
136+
airlift-aircompressor = "0.27"
137137
airlift-security = "248"
138138
directory-keystore = "1.1.0"
139139
maven-mima = "3.0.0-alpha-3"
@@ -185,7 +185,7 @@ kmp-hierarchy = "1.1"
185185
jetbrains-compose = "1.6.10"
186186
kobweb = "0.18.0"
187187
detekt = "1.23.6"
188-
detekt-compose-rules = "0.4.1"
188+
detekt-compose-rules = "0.4.2"
189189

190190
[libraries]
191191
# External plugins for precompiled script plugins

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-rc-1-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-rc-2-all.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

gradlew

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
# Darwin, MinGW, and NonStop.
5656
#
5757
# (3) This script is generated from the Groovy template
58-
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
58+
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
5959
# within the Gradle project.
6060
#
6161
# You can find Gradle at https://github.com/gradle/gradle/.

0 commit comments

Comments
 (0)