In openrndr-template it’s very easy to have multiple programs in one project: we clone that repo, then have multiple Kotlin programs under under src/main/kotlin/. Each main method shows a green clickable triangle in IntelliJ Idea. Click it and the program runs.
But what about openrndr-js-template? With the JS template there is no green triangle next to the main method. The way to run the program is to execute ./gradlew browserDevelopmentRun --continuous or ./gradlew jsRun -t, depending on the version you’re using. Once you execute that command, the build system keeps running and any time you save changes to your program, the web page displaying your OPENRNDR program reloads automatically in the web browser.
What if you would like to have several programs? The obvious approach is to clone the repo again for each new program you want to create. But that feels less convenient than having several programs in a folder.
Easy switching of programs with Kotlin / JS
Here one approach I came up with: change TemplateProgram.kt to be just one line:
fun main() = p1
Then create your first program (p1.kt):
import org.openrndr.application
import org.openrndr.color.ColorRGBa
val p1 = application {
program {
extend {
drawer.clear(ColorRGBa.PINK)
}
}
}
and your second program (p2.kt):
import org.openrndr.application
import org.openrndr.color.ColorRGBa
val p2 = application {
program {
extend {
drawer.clear(ColorRGBa.GREEN)
}
}
}
Now, to decide which program runs, you only need to change the main method to choose between p1 and p2.
This way you could have as many programs as you want and quickly jump between them.
Alternative solution
An approach proposed by user ephemient in the Kotlin Slack:
I’ve ended up creating separate Gradle subprojects, each depending on the common library. then :foo:jsRun:bar:jsRun etc. work as expected
I would like to improve this so the user can choose which program runs from the HTML page. There could be a clickable menu or a dropdown, which would reload the page passing the name of the function to run to the Kotlin program.
I figured out I can pass information from HTML to the program like this: in the HTML I add a data attribute:
This is what I came up with. I moved all the logic into the main method, which takes care of running whatever program you specify in a GET parameter in the URL, and also creates a clickable menu of programs you can run.
import kotlinx.browser.document
import kotlinx.browser.window
import org.w3c.dom.url.URLSearchParams
// Add your application functions here
val myApps = listOf(
::bouncyBubbles,
::justGreen,
)
fun main() {
// Create a map where function names point to the actual functions
val appMap = myApps.associateBy { it.toString().lines()[1].trim().dropLast(3) }
// Take the GET argument from the URL specifying which program to run. If missing take the first.
val currentProgram = URLSearchParams(window.location.search).get("program") ?: appMap.keys.first()
// Launch the selected program
appMap[currentProgram]!!.invoke()
// Create a div with clickable links
val div = document.createElement("div")
div.innerHTML = appMap.keys.joinToString(" ") { programName ->
if (programName != currentProgram)
"""<a href="?program=$programName">$programName</a>"""
else
"""<span>$programName</span>"""
}
// Prepend the div at the top of the HTML
document.body?.prepend(div)
}
The user needs to update the myApps list, and create functions as in @bluehut 's example (ideally one per file).
fun bouncyBubbles() = application { ... }
fun justGreen() = application { ... }
It’s also pretty close to live coding. When I save my changes to my OPENRNDR Kotlin programs the browser refreshes in ~2 seconds (on my 2014 laptop).
It refreshes twice though. I wish I know why.Workaround: add sourceMaps = false inside commonWebpackConfig in build.gradle.kts. Then it refreshes just once But source maps are gone, which may be useful for debugging.
Next, I’m adding those links as an overlay on top of the visuals (so one can use the full window for visuals), and a source button to view the syntax-highlighted source code of the running program
And here the live demo that gets built automatically by a workflow on GitHub any time I commit changes
ps. I really wanted to have
val myApps = listOf(
::bouncyBubbles,
::justGreen,
::fabulousPink,
)
instead of
val myApps = mapOf(
"bouncyBubbles" to ::bouncyBubbles,
"justGreen" to ::justGreen,
"fabulousPink" to ::fabulousPink,
)
because repeating words is a potential source of typos. I did have a working version looking like the first solution, but when doing a production release, the system renames variables and methods to one-character names, and the whole thing falls apart because function names are no longer found.
Even better would be not needing that list/map at all, so the user could create programs, and they would be somehow detected.
Maybe doable with Gradle, but auto-generating source code files sounds a bit too magical , even to me XD. Another option would be to add tweak openrndr-js to enable multi-app usage…