Browse Source
Previously preview only worked in projects, that define compose.desktop.application {} DSL block Resolves #908pull/953/head
Alexey Tsvetkov
3 years ago
committed by
GitHub
38 changed files with 506 additions and 77 deletions
@ -1,19 +1,39 @@
|
||||
package org.jetbrains.compose.desktop.preview.internal |
||||
|
||||
import org.gradle.api.Project |
||||
import org.jetbrains.compose.desktop.application.dsl.Application |
||||
import org.jetbrains.compose.desktop.application.internal.javaHomeOrDefault |
||||
import org.jetbrains.compose.desktop.application.internal.provider |
||||
import org.jetbrains.compose.desktop.application.dsl.ConfigurationSource |
||||
import org.jetbrains.compose.desktop.preview.tasks.AbstractConfigureDesktopPreviewTask |
||||
import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID |
||||
import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID |
||||
import org.jetbrains.compose.internal.javaExt |
||||
import org.jetbrains.compose.internal.mppExt |
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType |
||||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget |
||||
|
||||
fun Project.initializePreview() { |
||||
plugins.withId(KOTLIN_MPP_PLUGIN_ID) { |
||||
mppExt.targets.all { target -> |
||||
if (target.platformType == KotlinPlatformType.jvm) { |
||||
val config = ConfigurationSource.KotlinMppTarget(target as KotlinJvmTarget) |
||||
registerConfigurePreviewTask(project, config, targetName = target.name) |
||||
} |
||||
} |
||||
} |
||||
plugins.withId(KOTLIN_JVM_PLUGIN_ID) { |
||||
val config = ConfigurationSource.GradleSourceSet(project.javaExt.sourceSets.getByName("main")) |
||||
registerConfigurePreviewTask(project, config) |
||||
} |
||||
} |
||||
|
||||
internal fun AbstractConfigureDesktopPreviewTask.configureConfigureDesktopPreviewTask(app: Application) { |
||||
app._configurationSource?.let { configSource -> |
||||
dependsOn(configSource.jarTaskName) |
||||
previewClasspath = configSource.runtimeClasspath(project) |
||||
javaHome.set(provider { app.javaHomeOrDefault() }) |
||||
jvmArgs.set(provider { app.jvmArgs }) |
||||
private fun registerConfigurePreviewTask(project: Project, config: ConfigurationSource, targetName: String = "") { |
||||
project.tasks.register( |
||||
previewTaskName(targetName), |
||||
AbstractConfigureDesktopPreviewTask::class.java |
||||
) { previewTask -> |
||||
previewTask.dependsOn(config.jarTask(project)) |
||||
previewTask.previewClasspath = config.runtimeClasspath(project) |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun previewTaskName(targetName: String) = |
||||
"configureDesktopPreview${targetName.capitalize()}" |
@ -0,0 +1,9 @@
|
||||
/* |
||||
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||
*/ |
||||
|
||||
package org.jetbrains.compose.internal |
||||
|
||||
internal const val KOTLIN_MPP_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform" |
||||
internal const val KOTLIN_JVM_PLUGIN_ID = "org.jetbrains.kotlin.jvm" |
@ -0,0 +1,9 @@
|
||||
subprojects { |
||||
repositories { |
||||
mavenLocal() |
||||
mavenCentral() |
||||
maven { |
||||
url 'https://maven.pkg.jetbrains.space/public/p/compose/dev' |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,10 @@
|
||||
plugins { |
||||
id 'org.jetbrains.kotlin.jvm' |
||||
id 'org.jetbrains.compose' |
||||
} |
||||
|
||||
dependencies { |
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib' |
||||
implementation compose.uiTooling |
||||
implementation compose.desktop.currentOs |
||||
} |
@ -0,0 +1,20 @@
|
||||
plugins { |
||||
id 'org.jetbrains.kotlin.multiplatform' |
||||
id 'org.jetbrains.compose' |
||||
} |
||||
|
||||
kotlin { |
||||
jvm('desktop') {} |
||||
|
||||
sourceSets { |
||||
commonMain.dependencies { |
||||
api compose.runtime |
||||
api compose.foundation |
||||
api compose.material |
||||
api compose.uiTooling |
||||
} |
||||
desktopMain.dependencies { |
||||
implementation compose.desktop.currentOs |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,21 @@
|
||||
/* |
||||
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||
*/ |
||||
|
||||
import androidx.compose.material.Text |
||||
import androidx.compose.material.Button |
||||
import androidx.compose.runtime.* |
||||
|
||||
@Composable |
||||
fun ExampleComposable() { |
||||
var text by remember { mutableStateOf("Hello, World!") } |
||||
|
||||
Button(onClick = { |
||||
text = "Hello, $platformName!" |
||||
}) { |
||||
Text(text) |
||||
} |
||||
} |
||||
|
||||
expect val platformName: String |
@ -0,0 +1,11 @@
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.desktop.ui.tooling.preview.Preview |
||||
|
||||
@Preview |
||||
@Composable |
||||
fun ExamplePreview() { |
||||
ExampleComposable() |
||||
} |
||||
|
||||
actual val platformName: String |
||||
get() = "Desktop" |
@ -0,0 +1,16 @@
|
||||
pluginManagement { |
||||
plugins { |
||||
id 'org.jetbrains.kotlin.multiplatform' version 'KOTLIN_VERSION_PLACEHOLDER' |
||||
id 'org.jetbrains.kotlin.jvm' version 'KOTLIN_VERSION_PLACEHOLDER' |
||||
id 'org.jetbrains.compose' version 'COMPOSE_VERSION_PLACEHOLDER' |
||||
} |
||||
repositories { |
||||
mavenLocal() |
||||
gradlePluginPortal() |
||||
maven { |
||||
url 'https://maven.pkg.jetbrains.space/public/p/compose/dev' |
||||
} |
||||
} |
||||
} |
||||
rootProject.name = 'jvmPreview' |
||||
include(':jvm', ':mpp') |
@ -1,5 +0,0 @@
|
||||
1. Run from `idea-plugin`: |
||||
``` |
||||
./gradlew runIde |
||||
``` |
||||
2. Open `idea-plugin/examples/desktop-project` with the test IDE. |
@ -1,24 +0,0 @@
|
||||
import org.jetbrains.compose.compose |
||||
|
||||
plugins { |
||||
// __KOTLIN_COMPOSE_VERSION__ |
||||
kotlin("jvm") version "1.5.21" |
||||
// __LATEST_COMPOSE_RELEASE_VERSION__ |
||||
id("org.jetbrains.compose") version "0.5.0-build262" |
||||
} |
||||
|
||||
repositories { |
||||
mavenCentral() |
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") |
||||
} |
||||
|
||||
dependencies { |
||||
implementation(compose.uiTooling) |
||||
implementation(compose.desktop.currentOs) |
||||
} |
||||
|
||||
compose.desktop { |
||||
application { |
||||
mainClass = "MainKt" |
||||
} |
||||
} |
@ -1,4 +0,0 @@
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 |
||||
kotlin.code.style=official |
||||
#org.gradle.unsafe.configuration-cache=true |
||||
#org.gradle.unsafe.configuration-cache-problems=warn |
@ -1,6 +0,0 @@
|
||||
pluginManagement { |
||||
repositories { |
||||
gradlePluginPortal() |
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") |
||||
} |
||||
} |
@ -0,0 +1,5 @@
|
||||
1. Run from `idea-plugin`: |
||||
``` |
||||
./gradlew runIde |
||||
``` |
||||
2. Open the project with the test IDE. |
@ -0,0 +1,21 @@
|
||||
buildscript { |
||||
repositories { |
||||
mavenLocal() |
||||
mavenCentral() |
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") |
||||
} |
||||
dependencies { |
||||
// __LATEST_COMPOSE_RELEASE_VERSION__ |
||||
classpath("org.jetbrains.compose:compose-gradle-plugin:0.5.0-build262") |
||||
// __KOTLIN_COMPOSE_VERSION__ |
||||
classpath(kotlin("gradle-plugin", version = "1.5.21")) |
||||
} |
||||
} |
||||
|
||||
subprojects { |
||||
repositories { |
||||
mavenLocal() |
||||
mavenCentral() |
||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") |
||||
} |
||||
} |
@ -0,0 +1,3 @@
|
||||
kotlin.code.style=official |
||||
#org.gradle.unsafe.configuration-cache=true |
||||
#org.gradle.unsafe.configuration-cache-problems=warn |
@ -0,0 +1,26 @@
|
||||
import org.jetbrains.compose.compose |
||||
|
||||
plugins { |
||||
kotlin("multiplatform") |
||||
id("org.jetbrains.compose") |
||||
} |
||||
|
||||
kotlin { |
||||
jvm("desktop") |
||||
|
||||
sourceSets { |
||||
named("commonMain") { |
||||
dependencies { |
||||
api(compose.runtime) |
||||
api(compose.foundation) |
||||
api(compose.material) |
||||
api(compose.uiTooling) |
||||
} |
||||
} |
||||
named("desktopMain") { |
||||
dependencies { |
||||
implementation(compose.desktop.currentOs) |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,15 @@
|
||||
import androidx.compose.material.Button |
||||
import androidx.compose.material.MaterialTheme |
||||
import androidx.compose.material.Text |
||||
import androidx.compose.runtime.* |
||||
|
||||
@Composable |
||||
fun App() { |
||||
MaterialTheme { |
||||
Button(onClick = {}) { |
||||
Text("Hello, ${getPlatformName()}!") |
||||
} |
||||
} |
||||
} |
||||
|
||||
expect fun getPlatformName(): String |
@ -0,0 +1,10 @@
|
||||
import androidx.compose.runtime.* |
||||
import androidx.compose.desktop.ui.tooling.preview.Preview |
||||
|
||||
actual fun getPlatformName(): String = "Desktop" |
||||
|
||||
@Preview |
||||
@Composable |
||||
fun DesktopAppPreview() { |
||||
App() |
||||
} |
@ -0,0 +1,12 @@
|
||||
import org.jetbrains.compose.compose |
||||
|
||||
plugins { |
||||
kotlin("jvm") |
||||
id("org.jetbrains.compose") |
||||
} |
||||
|
||||
dependencies { |
||||
implementation(compose.desktop.currentOs) |
||||
// todo: remove after update |
||||
implementation(compose.uiTooling) |
||||
} |
@ -0,0 +1,16 @@
|
||||
import androidx.compose.material.Text |
||||
import androidx.compose.material.Button |
||||
import androidx.compose.runtime.* |
||||
import androidx.compose.desktop.ui.tooling.preview.Preview |
||||
|
||||
@Preview |
||||
@Composable |
||||
fun ExamplePreview() { |
||||
var text by remember { mutableStateOf("Hello, World!") } |
||||
|
||||
Button(onClick = { |
||||
text = "Hello, Desktop!" |
||||
}) { |
||||
Text(text) |
||||
} |
||||
} |
@ -0,0 +1 @@
|
||||
include(":mpp-jvm", ":pure-jvm") |
@ -0,0 +1,91 @@
|
||||
/* |
||||
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers. |
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. |
||||
*/ |
||||
|
||||
package org.jetbrains.compose.desktop.ide.preview |
||||
|
||||
import com.intellij.openapi.externalSystem.model.DataNode |
||||
import com.intellij.openapi.externalSystem.model.ProjectKeys |
||||
import com.intellij.openapi.externalSystem.model.project.ModuleData |
||||
import com.intellij.openapi.externalSystem.service.project.ProjectDataManager |
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil |
||||
import com.intellij.openapi.module.Module |
||||
import com.intellij.openapi.project.Project |
||||
import com.intellij.util.concurrency.annotations.RequiresReadLock |
||||
import org.jetbrains.kotlin.idea.configuration.KotlinTargetData |
||||
import org.jetbrains.plugins.gradle.settings.GradleSettings |
||||
import org.jetbrains.plugins.gradle.util.GradleConstants |
||||
|
||||
internal val DEFAULT_CONFIGURE_PREVIEW_TASK_NAME = "configureDesktopPreview" |
||||
|
||||
internal interface ConfigurePreviewTaskNameProvider { |
||||
@RequiresReadLock |
||||
fun configurePreviewTaskNameOrNull(module: Module): String? |
||||
} |
||||
|
||||
internal class ConfigurePreviewTaskNameProviderImpl : ConfigurePreviewTaskNameProvider { |
||||
@RequiresReadLock |
||||
override fun configurePreviewTaskNameOrNull(module: Module): String? { |
||||
val modulePath = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return null |
||||
val moduleNode = moduleDataNodeOrNull(module.project, modulePath) |
||||
if (moduleNode != null) { |
||||
val target = ExternalSystemApiUtil.getChildren(moduleNode, KotlinTargetData.KEY).singleOrNull() |
||||
if (target != null) { |
||||
return previewTaskName(target.data.externalName) |
||||
} |
||||
} |
||||
|
||||
return null |
||||
} |
||||
|
||||
private fun previewTaskName(targetName: String = "") = |
||||
"$DEFAULT_CONFIGURE_PREVIEW_TASK_NAME${targetName.capitalize()}" |
||||
|
||||
private fun moduleDataNodeOrNull(project: Project, modulePath: String): DataNode<ModuleData>? { |
||||
val projectDataManager = ProjectDataManager.getInstance() |
||||
for (settings in GradleSettings.getInstance(project).linkedProjectsSettings) { |
||||
val projectInfo = projectDataManager.getExternalProjectData(project, GradleConstants.SYSTEM_ID, settings.externalProjectPath) |
||||
val projectNode = projectInfo?.externalProjectStructure ?: continue |
||||
val moduleNodes = ExternalSystemApiUtil.getChildren(projectNode, ProjectKeys.MODULE) |
||||
for (moduleNode in moduleNodes) { |
||||
val externalProjectPath = moduleNode.data.linkedExternalProjectPath |
||||
if (externalProjectPath == modulePath) { |
||||
return moduleNode |
||||
} |
||||
} |
||||
} |
||||
return null |
||||
} |
||||
} |
||||
|
||||
internal class ConfigurePreviewTaskNameCache( |
||||
private val provider: ConfigurePreviewTaskNameProvider |
||||
) : ConfigurePreviewTaskNameProvider { |
||||
private var cachedModuleId: String? = null |
||||
private var cachedTaskName: String? = null |
||||
|
||||
@RequiresReadLock |
||||
override fun configurePreviewTaskNameOrNull(module: Module): String? { |
||||
val externalProjectPath = ExternalSystemApiUtil.getExternalProjectPath(module) ?: return null |
||||
val moduleId = "$externalProjectPath#${module.name}" |
||||
|
||||
synchronized(this) { |
||||
if (moduleId == cachedModuleId) return cachedTaskName |
||||
} |
||||
|
||||
val taskName = provider.configurePreviewTaskNameOrNull(module) |
||||
synchronized(this) { |
||||
cachedTaskName = taskName |
||||
cachedModuleId = moduleId |
||||
} |
||||
return taskName |
||||
} |
||||
|
||||
fun invalidate() { |
||||
synchronized(this) { |
||||
cachedModuleId = null |
||||
cachedTaskName = null |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue