Skip to content

Commit 3d04eb3

Browse files
committed
feat(composeui): scaffold first interactions with graphql
1 parent 5b7410a commit 3d04eb3

File tree

5 files changed

+117
-21
lines changed

5 files changed

+117
-21
lines changed

alchemist-composeui/build.gradle.kts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Libs.alchemist
12
import Util.devServer
23
import Util.webCommonConfiguration
34
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
@@ -35,6 +36,17 @@ kotlin {
3536
implementation(compose.foundation)
3637
implementation(compose.material)
3738
implementation(compose.components.resources)
39+
implementation(libs.androidx.lifecycle.runtime.compose)
40+
implementation(libs.androidx.lifecycle.viewmodel.compose)
41+
implementation(libs.apollo.runtime)
42+
api(alchemist("graphql"))
43+
}
44+
}
45+
46+
val jvmMain by getting {
47+
dependencies {
48+
implementation(compose.desktop.currentOs)
49+
implementation(libs.kotlin.coroutines.swing)
3850
}
3951
}
4052
}

alchemist-composeui/src/commonMain/kotlin/it/unibo/alchemist/boundary/composeui/App.kt

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,45 @@
99

1010
package it.unibo.alchemist.boundary.composeui
1111

12-
import androidx.compose.animation.AnimatedVisibility
12+
import androidx.compose.foundation.layout.Arrangement
1313
import androidx.compose.foundation.layout.Column
14+
import androidx.compose.foundation.layout.Row
1415
import androidx.compose.foundation.layout.fillMaxWidth
1516
import androidx.compose.material.Button
1617
import androidx.compose.material.MaterialTheme
1718
import androidx.compose.material.Text
1819
import androidx.compose.runtime.Composable
1920
import androidx.compose.runtime.getValue
20-
import androidx.compose.runtime.mutableStateOf
21-
import androidx.compose.runtime.remember
22-
import androidx.compose.runtime.setValue
2321
import androidx.compose.ui.Alignment
2422
import androidx.compose.ui.Modifier
23+
import androidx.compose.ui.unit.dp
24+
import androidx.lifecycle.compose.collectAsStateWithLifecycle
25+
import androidx.lifecycle.viewmodel.compose.viewModel
26+
import it.unibo.alchemist.boundary.composeui.viewmodels.SimulationStatusViewModel
2527

2628
/**
2729
* Application entry point, this will be rendered the same in all the platforms.
2830
*/
2931
@Composable
3032
fun app() {
3133
MaterialTheme {
32-
var showContent by remember { mutableStateOf(false) }
3334
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
34-
Button(onClick = { showContent = !showContent }) {
35-
Text("Click me!")
36-
}
37-
AnimatedVisibility(showContent) {
38-
val greeting = remember { getPlatform() }
39-
Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
40-
Text("Compose: $greeting")
41-
}
42-
}
35+
simulationStatus()
36+
}
37+
}
38+
}
39+
40+
@Composable
41+
fun simulationStatus(viewModel: SimulationStatusViewModel = viewModel { SimulationStatusViewModel() }) {
42+
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
43+
Text("Simulation status: ${uiState.status}")
44+
Text("Simulation time: ${uiState.time}")
45+
Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
46+
Button(onClick = { viewModel.pause() }) {
47+
Text("Pause")
48+
}
49+
Button(onClick = { viewModel.play() }) {
50+
Text("Play")
4351
}
4452
}
4553
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (C) 2010-2025, Danilo Pianini and contributors
3+
* listed, for each module, in the respective subproject's build.gradle.kts file.
4+
*
5+
* This file is part of Alchemist, and is distributed under the terms of the
6+
* GNU General Public License, with a linking exception,
7+
* as described in the file LICENSE in the Alchemist distribution's top directory.
8+
*/
9+
10+
package it.unibo.alchemist.boundary.composeui.viewmodels
11+
12+
import androidx.lifecycle.ViewModel
13+
import androidx.lifecycle.viewModelScope
14+
import it.unibo.alchemist.boundary.graphql.client.GraphQLClientFactory
15+
import it.unibo.alchemist.boundary.graphql.client.NodesSubscription
16+
import it.unibo.alchemist.boundary.graphql.client.PauseSimulationMutation
17+
import it.unibo.alchemist.boundary.graphql.client.PlaySimulationMutation
18+
import kotlinx.coroutines.flow.MutableStateFlow
19+
import kotlinx.coroutines.flow.asStateFlow
20+
import kotlinx.coroutines.flow.update
21+
import kotlinx.coroutines.launch
22+
23+
data class SimulationState(val status: String = "Loading", val time: Double = 0.0)
24+
25+
class SimulationStatusViewModel : ViewModel() {
26+
private val _uiState = MutableStateFlow(SimulationState())
27+
val uiState = _uiState.asStateFlow()
28+
29+
// TODO: parameterize the host and port and separate client in different file
30+
private val client = GraphQLClientFactory.subscriptionClient(
31+
"127.0.0.1",
32+
3000,
33+
)
34+
35+
fun pause() {
36+
viewModelScope.launch {
37+
client.mutation(PauseSimulationMutation()).execute()
38+
}
39+
}
40+
41+
fun play() {
42+
viewModelScope.launch {
43+
client.mutation(PlaySimulationMutation()).execute()
44+
}
45+
}
46+
47+
init {
48+
viewModelScope.launch {
49+
client.subscription(NodesSubscription())
50+
.toFlow()
51+
.collect { res ->
52+
_uiState.update {
53+
val data = res.dataOrThrow()
54+
SimulationState(
55+
status = data.simulation.status,
56+
time = data.simulation.time,
57+
)
58+
}
59+
}
60+
}
61+
}
62+
}

alchemist-composeui/src/jvmMain/kotlin/it/unibo/alchemist/boundary/composeui/ComposeMonitor.kt

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,33 @@
1010
package it.unibo.alchemist.boundary.composeui
1111

1212
import androidx.compose.ui.window.Window
13-
import androidx.compose.ui.window.application
13+
import androidx.compose.ui.window.awaitApplication
1414
import it.unibo.alchemist.boundary.OutputMonitor
1515
import it.unibo.alchemist.model.Environment
16+
import kotlinx.coroutines.launch
17+
import kotlinx.coroutines.runBlocking
18+
import kotlin.concurrent.thread
1619

1720
/**
1821
* Monitor extension that uses JVM Compose UI to display the simulation.
1922
*/
2023
class ComposeMonitor : OutputMonitor<Any, Nothing> {
2124
override fun initialized(environment: Environment<Any, Nothing>) {
22-
application {
23-
Window(
24-
onCloseRequest = { },
25-
title = "Alchemist",
26-
) {
27-
app()
25+
thread(
26+
name = "ComposeMonitor",
27+
isDaemon = true,
28+
) {
29+
runBlocking {
30+
launch {
31+
awaitApplication {
32+
Window(
33+
onCloseRequest = ::exitApplication,
34+
title = "Alchemist",
35+
) {
36+
app()
37+
}
38+
}
39+
}
2840
}
2941
}
3042
}

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ scalacache = "0.28.0"
2323

2424
[libraries]
2525
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
26+
androidx-lifecycle-viewmodel-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
2627
antlr4 = { module = "org.antlr:antlr4", version.ref = "antlr4" }
2728
antlr4-runtime = { module = "org.antlr:antlr4-runtime", version.ref = "antlr4" }
2829
apache-commons-cli = "commons-cli:commons-cli:1.9.0"
@@ -73,6 +74,7 @@ kotest-framework-engine = { module = "io.kotest:kotest-framework-engine", versio
7374
kotest-runner = { module = "io.kotest:kotest-runner-junit5-jvm", version.ref = "kotest" }
7475
kotlin-cli = "org.jetbrains.kotlinx:kotlinx-cli:0.3.6"
7576
kotlin-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
77+
kotlin-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
7678
kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
7779
kotlin-jvm-plugin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin", version.ref = "kotlin" }
7880
kotlin-multiplatform-plugin = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" }

0 commit comments

Comments
 (0)