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 |
package org.jetbrains.compose.desktop.preview.internal |
||||||
|
|
||||||
import org.gradle.api.Project |
import org.gradle.api.Project |
||||||
import org.jetbrains.compose.desktop.application.dsl.Application |
import org.jetbrains.compose.desktop.application.dsl.ConfigurationSource |
||||||
import org.jetbrains.compose.desktop.application.internal.javaHomeOrDefault |
|
||||||
import org.jetbrains.compose.desktop.application.internal.provider |
|
||||||
import org.jetbrains.compose.desktop.preview.tasks.AbstractConfigureDesktopPreviewTask |
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() { |
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) { |
private fun registerConfigurePreviewTask(project: Project, config: ConfigurationSource, targetName: String = "") { |
||||||
app._configurationSource?.let { configSource -> |
project.tasks.register( |
||||||
dependsOn(configSource.jarTaskName) |
previewTaskName(targetName), |
||||||
previewClasspath = configSource.runtimeClasspath(project) |
AbstractConfigureDesktopPreviewTask::class.java |
||||||
javaHome.set(provider { app.javaHomeOrDefault() }) |
) { previewTask -> |
||||||
jvmArgs.set(provider { app.jvmArgs }) |
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