diff --git a/experimental/examples/minesweeper/.gitignore b/experimental/examples/minesweeper/.gitignore index 639cdd514d..0903344480 100644 --- a/experimental/examples/minesweeper/.gitignore +++ b/experimental/examples/minesweeper/.gitignore @@ -1 +1,7 @@ local.properties +iosApp/Podfile.lock +iosApp/Pods/* +iosApp/Minesweeper.xcworkspace/* +iosApp/Minesweeper.xcodeproj/* +!iosApp/Minesweeper.xcodeproj/project.pbxproj +shared/shared.podspec diff --git a/experimental/examples/minesweeper/.run/desktopApp.run.xml b/experimental/examples/minesweeper/.run/desktopApp.run.xml new file mode 100644 index 0000000000..7af7e15f4b --- /dev/null +++ b/experimental/examples/minesweeper/.run/desktopApp.run.xml @@ -0,0 +1,28 @@ + + + + + + + + true + true + false + + + \ No newline at end of file diff --git a/experimental/examples/minesweeper/.run/iosApp (Android Studio).run.xml b/experimental/examples/minesweeper/.run/iosApp (Android Studio).run.xml new file mode 100644 index 0000000000..553dd92ab1 --- /dev/null +++ b/experimental/examples/minesweeper/.run/iosApp (Android Studio).run.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/experimental/examples/minesweeper/.run/iosApp.run.xml b/experimental/examples/minesweeper/.run/iosApp.run.xml new file mode 100644 index 0000000000..31a15653b2 --- /dev/null +++ b/experimental/examples/minesweeper/.run/iosApp.run.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/experimental/examples/minesweeper/.run/jsApp.run.xml b/experimental/examples/minesweeper/.run/jsApp.run.xml new file mode 100644 index 0000000000..6135d902e4 --- /dev/null +++ b/experimental/examples/minesweeper/.run/jsApp.run.xml @@ -0,0 +1,28 @@ + + + + + + + + true + true + false + + + \ No newline at end of file diff --git a/experimental/examples/minesweeper/README.md b/experimental/examples/minesweeper/README.md index 94504bdf43..f03076ebf3 100644 --- a/experimental/examples/minesweeper/README.md +++ b/experimental/examples/minesweeper/README.md @@ -1,20 +1,36 @@ # Minesweeper -A simple game powered by Compose Multiplatform +A simple game powered by Compose Multiplatform. -## Run native on MacOS -`./gradlew runDebugExecutableMacosX64` (Works on Intel processors) +Game can run on Android, iOS, desktop or in a browser. + +*Prerequisites*: to run on iOS and Android, you should have "Kotlin Multiplatform Mobile" plugin installed either + in Android Studio or in AppCode with [installed CocoaPods](https://kotlinlang.org/docs/native-cocoapods.html). + + +## How to run + +Choose a run configuration for an appropriate target in IDE and run it. + +![run-configurations.png](run-configurations.png) -## Run web assembly in browser -`./gradlew jsBrowserDevelopmentRun` +To run on iOS device, please correct `iosApp/Configuration/TeamId.xcconfig` with your Apple Team ID. +Alternatively, you may setup signing within XCode opening `iosApp/Minesweeper.xcworkspace` and then +using "Signing & Capabilities" tab of `Minesweeper` target. -## Run on iOS simulator -`./gradlew iosDeployIPhone8Debug` -`./gradlew iosDeployIPadDebug` +Then choose **iosApp** configuration in IDE and run it +(may also be referred as `Minesweeper` in the Run Configurations or `iosApp (Android Studio)` for Android studio). -## Run on iOS device -- Read about iOS target in [falling-balls-mpp/README.md](../falling-balls-mpp/README.md) -- `./gradlew iosDeployDeviceRelease` +## Run on desktop via Gradle + +`./gradlew desktopApp:run` + +## Run JS in browser with WebAssembly Skia via Gradle + +`./gradlew jsApp:jsBrowserDevelopmentRun` + +## Run native on MacOS +`./gradlew runDebugExecutableMacosX64` (Works on Intel processors) ## Credits
Icons made by Creaticca Creative Agency from www.flaticon.com
\ No newline at end of file diff --git a/experimental/examples/minesweeper/androidApp/build.gradle.kts b/experimental/examples/minesweeper/androidApp/build.gradle.kts new file mode 100644 index 0000000000..0c592a144c --- /dev/null +++ b/experimental/examples/minesweeper/androidApp/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + kotlin("multiplatform") + id("com.android.application") + id("org.jetbrains.compose") +} + +kotlin { + android() + sourceSets { + val androidMain by getting { + dependencies { + implementation(project(":shared")) + implementation("androidx.appcompat:appcompat:1.5.1") + implementation("androidx.activity:activity-compose:1.6.1") + } + } + } +} + +android { + compileSdk = 33 + defaultConfig { + applicationId = "org.jetbrains.ComposeMinesweeper" + minSdk = 24 + targetSdk = 33 + versionCode = 1 + versionName = "1.0" + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} diff --git a/experimental/examples/minesweeper/src/androidMain/AndroidManifest.xml b/experimental/examples/minesweeper/androidApp/src/main/AndroidManifest.xml similarity index 100% rename from experimental/examples/minesweeper/src/androidMain/AndroidManifest.xml rename to experimental/examples/minesweeper/androidApp/src/main/AndroidManifest.xml diff --git a/experimental/examples/minesweeper/src/androidMain/kotlin/MainActivity.kt b/experimental/examples/minesweeper/androidApp/src/main/kotlin/MainActivity.kt similarity index 89% rename from experimental/examples/minesweeper/src/androidMain/kotlin/MainActivity.kt rename to experimental/examples/minesweeper/androidApp/src/main/kotlin/MainActivity.kt index e3b0d1e9f3..4970ea42ed 100644 --- a/experimental/examples/minesweeper/src/androidMain/kotlin/MainActivity.kt +++ b/experimental/examples/minesweeper/androidApp/src/main/kotlin/MainActivity.kt @@ -3,13 +3,13 @@ package org.jetbrains.minesweeper import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import Game +import MainView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - Game() + MainView() } } } diff --git a/experimental/examples/minesweeper/src/androidMain/res/values/strings.xml b/experimental/examples/minesweeper/androidApp/src/main/res/values/strings.xml similarity index 100% rename from experimental/examples/minesweeper/src/androidMain/res/values/strings.xml rename to experimental/examples/minesweeper/androidApp/src/main/res/values/strings.xml diff --git a/experimental/examples/minesweeper/build.gradle.kts b/experimental/examples/minesweeper/build.gradle.kts index a5045597e5..0c00883af5 100644 --- a/experimental/examples/minesweeper/build.gradle.kts +++ b/experimental/examples/minesweeper/build.gradle.kts @@ -1,219 +1,18 @@ -import org.jetbrains.compose.compose -import org.jetbrains.compose.desktop.application.dsl.TargetFormat -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension -import org.jetbrains.compose.experimental.dsl.IOSDevices - plugins { - id("com.android.application") - kotlin("multiplatform") - id("org.jetbrains.compose") -} - -version = "1.0-SNAPSHOT" - -repositories { - mavenLocal() - google() - mavenCentral() - maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") -} - -kotlin { - android() - jvm("desktop") - js(IR) { - browser() - binaries.executable() - } - macosX64 { - binaries { - executable { - entryPoint = "main" - freeCompilerArgs += listOf( - "-linker-option", "-framework", "-linker-option", "Metal" - ) - } - } - } - macosArm64 { - binaries { - executable { - entryPoint = "main" - freeCompilerArgs += listOf( - "-linker-option", "-framework", "-linker-option", "Metal" - ) - } - } - } - - // Workaround for an issue: - // https://youtrack.jetbrains.com/issue/KT-53561/Invalid-LLVM-module-inlinable-function-call-in-a-function-with-debug-info-must-have-a-dbg-location - // Compose compiler produces nodes without line information sometimes that provokes Kotlin native compiler to report errors. - // TODO: remove workaround when switch to Kotlin 1.8 - val disableKonanVerification = "-Xverify-compiler=false" - - iosX64("uikitX64") { - binaries { - executable() { - entryPoint = "main" - freeCompilerArgs += listOf( - "-linker-option", "-framework", "-linker-option", "Metal", - "-linker-option", "-framework", "-linker-option", "CoreText", - "-linker-option", "-framework", "-linker-option", "CoreGraphics", - disableKonanVerification - ) - } - } - } - iosArm64("uikitArm64") { - binaries { - executable() { - entryPoint = "main" - freeCompilerArgs += listOf( - "-linker-option", "-framework", "-linker-option", "Metal", - "-linker-option", "-framework", "-linker-option", "CoreText", - "-linker-option", "-framework", "-linker-option", "CoreGraphics", - disableKonanVerification - ) - } - } - } - - sourceSets { - val commonMain by getting { - dependencies { - implementation(compose.ui) - implementation(compose.foundation) - implementation(compose.material) - implementation(compose.runtime) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3") - } - } - - val commonTest by getting { - dependencies { - implementation(kotlin("test-common")) - implementation(kotlin("test-annotations-common")) - } - } - - val androidMain by getting { - dependsOn(commonMain) - kotlin.srcDirs("src/jvmMain/kotlin") - dependencies { - implementation("androidx.appcompat:appcompat:1.5.1") - implementation("androidx.activity:activity-compose:1.5.0") - } - } - - val desktopMain by getting { - dependencies { - implementation(compose.desktop.currentOs) - } - } - - val jsMain by getting { - dependencies { - implementation(compose.web.core) - } - } - - val nativeMain by creating { - dependsOn(commonMain) - } - val macosMain by creating { - dependsOn(nativeMain) - } - val macosX64Main by getting { - dependsOn(macosMain) - } - val macosArm64Main by getting { - dependsOn(macosMain) - } - val uikitMain by creating { - dependsOn(nativeMain) - } - val uikitX64Main by getting { - dependsOn(uikitMain) - } - val uikitArm64Main by getting { - dependsOn(uikitMain) - } - } -} - -compose.desktop { - application { - mainClass = "Main_desktopKt" - nativeDistributions { - targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) - packageName = "Minesweeper" - packageVersion = "1.0.0" - - windows { - menuGroup = "Compose Examples" - // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html - upgradeUuid = "2bf169f9-d851-49f0-b3a1-308966d473ca" - } - } - } -} - -compose.experimental { - web.application {} - uikit.application { - bundleIdPrefix = "org.jetbrains" - projectName = "ComposeMinesweeper" - deployConfigurations { - simulator("IPhone8") { - //Usage: ./gradlew iosDeployIPhone8Debug - device = IOSDevices.IPHONE_8 - } - simulator("IPad") { - //Usage: ./gradlew iosDeployIPadDebug - device = IOSDevices.IPAD_MINI_6th_Gen - } - connectedDevice("Device") { - //First need specify your teamId here, or in local.properties (compose.ios.teamId=***) - //teamId="***" - //Usage: ./gradlew iosDeployDeviceRelease - } - } - } -} - -tasks.withType { - kotlinOptions.jvmTarget = "11" -} - -// a temporary workaround for a bug in jsRun invocation - see https://youtrack.jetbrains.com/issue/KT-48273 -afterEvaluate { - rootProject.extensions.configure { - versions.webpackDevServer.version = "4.0.0" - versions.webpackCli.version = "4.9.0" - nodeVersion = "16.0.0" - } -} - -android { - compileSdk = 32 - - defaultConfig { - minSdk = 26 - targetSdk = 32 - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - } - - sourceSets { - named("main") { - manifest.srcFile("src/androidMain/AndroidManifest.xml") - res.srcDirs("src/androidMain/res", "src/commonMain/resources") - } + // this is necessary to avoid the plugins to be loaded multiple times + // in each subproject's classloader + kotlin("jvm") apply false + kotlin("multiplatform") apply false + kotlin("android") apply false + id("com.android.application") apply false + id("com.android.library") apply false + id("org.jetbrains.compose") apply false +} + +allprojects { + repositories { + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") } } diff --git a/experimental/examples/minesweeper/desktopApp/build.gradle.kts b/experimental/examples/minesweeper/desktopApp/build.gradle.kts new file mode 100644 index 0000000000..730a5109ad --- /dev/null +++ b/experimental/examples/minesweeper/desktopApp/build.gradle.kts @@ -0,0 +1,35 @@ +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") +} + +kotlin { + jvm {} + sourceSets { + val jvmMain by getting { + dependencies { + implementation(compose.desktop.currentOs) + implementation(project(":shared")) + } + } + } +} + +compose.desktop { + application { + mainClass = "MainKt" + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "Minesweeper" + packageVersion = "1.0.0" + + windows { + menuGroup = "Compose Examples" + // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html + upgradeUuid = "2bf169f9-d851-49f0-b3a1-308966d473ca" + } + } + } +} diff --git a/experimental/examples/minesweeper/desktopApp/src/jvmMain/kotlin/Main.kt b/experimental/examples/minesweeper/desktopApp/src/jvmMain/kotlin/Main.kt new file mode 100644 index 0000000000..732c30af6a --- /dev/null +++ b/experimental/examples/minesweeper/desktopApp/src/jvmMain/kotlin/Main.kt @@ -0,0 +1,22 @@ +/* + * 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.ui.ExperimentalComposeUiApi +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.window.* + +fun main() = application { + val windowState = rememberWindowState() + + Window( + onCloseRequest = ::exitApplication, + resizable = false, + title = "Minesweeper", + icon = painterResource("assets/mine.png"), + state = windowState + ) { + MainView(windowState) + } +} \ No newline at end of file diff --git a/experimental/examples/minesweeper/gradle.properties b/experimental/examples/minesweeper/gradle.properties index e6fa271a3c..ea83c74d6a 100644 --- a/experimental/examples/minesweeper/gradle.properties +++ b/experimental/examples/minesweeper/gradle.properties @@ -1,17 +1,17 @@ -compose.version=1.3.0-alpha01-dev827 -kotlin.version=1.7.10 -agp.version=7.0.4 -org.gradle.jvmargs=-Xmx3g kotlin.code.style=official +xcodeproj=./iosApp +kotlin.native.cocoapods.generate.wrapper=true +android.useAndroidX=true +org.gradle.jvmargs=-Xmx3g +org.jetbrains.compose.experimental.jscanvas.enabled=true +org.jetbrains.compose.experimental.macos.enabled=true +org.jetbrains.compose.experimental.uikit.enabled=true kotlin.native.cacheKind=none kotlin.native.useEmbeddableCompilerJar=true kotlin.native.enableDependencyPropagation=false kotlin.mpp.enableGranularSourceSetsMetadata=true # Enable kotlin/native experimental memory model kotlin.native.binary.memoryModel=experimental -compose.desktop.verbose=true -android.useAndroidX=true -kotlin.js.webpack.major.version=4 -org.jetbrains.compose.experimental.jscanvas.enabled=true -org.jetbrains.compose.experimental.macos.enabled=true -org.jetbrains.compose.experimental.uikit.enabled=true +kotlin.version=1.7.20 +agp.version=7.1.3 +compose.version=1.3.0-beta04-dev885 diff --git a/experimental/examples/minesweeper/iosApp/Configuration/TeamId.xcconfig b/experimental/examples/minesweeper/iosApp/Configuration/TeamId.xcconfig new file mode 100644 index 0000000000..bf06eb27e9 --- /dev/null +++ b/experimental/examples/minesweeper/iosApp/Configuration/TeamId.xcconfig @@ -0,0 +1 @@ +TEAM_ID= diff --git a/experimental/examples/minesweeper/iosApp/Minesweeper.xcodeproj/project.pbxproj b/experimental/examples/minesweeper/iosApp/Minesweeper.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..af3f99d747 --- /dev/null +++ b/experimental/examples/minesweeper/iosApp/Minesweeper.xcodeproj/project.pbxproj @@ -0,0 +1,398 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 2152FB042600AC8F00CF470E /* iosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iosApp.swift */; }; + C1FC908188C4E8695729CB06 /* Pods_Minesweeper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DE96E47030356CE6AD9794A /* Pods_Minesweeper.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1EB65E27D2C0F884D0A1A133 /* Pods-Minesweeper.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minesweeper.debug.xcconfig"; path = "Target Support Files/Pods-Minesweeper/Pods-Minesweeper.debug.xcconfig"; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosApp.swift; sourceTree = ""; }; + 3D7A606AB0AD7636269BD9D0 /* Pods-Minesweeper.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Minesweeper.release.xcconfig"; path = "Target Support Files/Pods-Minesweeper/Pods-Minesweeper.release.xcconfig"; sourceTree = ""; }; + 7555FF7B242A565900829871 /* Minesweeper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Minesweeper.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8DE96E47030356CE6AD9794A /* Pods_Minesweeper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Minesweeper.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + AB3632DC29227652001CCB65 /* TeamId.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = TeamId.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9964867F0862B4D9FB6ABFC7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C1FC908188C4E8695729CB06 /* Pods_Minesweeper.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + E1DAFBE8E1CFC0878361EF0E /* Pods */, + B62309C7396AD7BF607A63B2 /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* Minesweeper.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iosApp.swift */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* TeamId.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; + B62309C7396AD7BF607A63B2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8DE96E47030356CE6AD9794A /* Pods_Minesweeper.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + E1DAFBE8E1CFC0878361EF0E /* Pods */ = { + isa = PBXGroup; + children = ( + 1EB65E27D2C0F884D0A1A133 /* Pods-Minesweeper.debug.xcconfig */, + 3D7A606AB0AD7636269BD9D0 /* Pods-Minesweeper.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* Minesweeper */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "Minesweeper" */; + buildPhases = ( + E8D673591E7196AEA2EA10E2 /* [CP] Check Pods Manifest.lock */, + 7555FF77242A565900829871 /* Sources */, + 7555FF79242A565900829871 /* Resources */, + 9964867F0862B4D9FB6ABFC7 /* Frameworks */, + AC9253D086E81B65FAE159E1 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Minesweeper; + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* Minesweeper.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1130; + ORGANIZATIONNAME = org.jetbrains; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "Minesweeper" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* Minesweeper */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + AC9253D086E81B65FAE159E1 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Minesweeper/Pods-Minesweeper-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Minesweeper/Pods-Minesweeper-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Minesweeper/Pods-Minesweeper-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + E8D673591E7196AEA2EA10E2 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Minesweeper-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iosApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* TeamId.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* TeamId.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1EB65E27D2C0F884D0A1A133 /* Pods-Minesweeper.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.Minesweeper${TEAM_ID}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3D7A606AB0AD7636269BD9D0 /* Pods-Minesweeper.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.jetbrains.Minesweeper${TEAM_ID}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "Minesweeper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "Minesweeper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} diff --git a/experimental/examples/minesweeper/iosApp/Podfile b/experimental/examples/minesweeper/iosApp/Podfile new file mode 100644 index 0000000000..43dcabd199 --- /dev/null +++ b/experimental/examples/minesweeper/iosApp/Podfile @@ -0,0 +1,5 @@ +target 'Minesweeper' do + use_frameworks! + platform :ios, '14.1' + pod 'shared', :path => '../shared' +end \ No newline at end of file diff --git a/experimental/examples/minesweeper/iosApp/iosApp/Info.plist b/experimental/examples/minesweeper/iosApp/iosApp/Info.plist new file mode 100644 index 0000000000..9a269f5eaa --- /dev/null +++ b/experimental/examples/minesweeper/iosApp/iosApp/Info.plist @@ -0,0 +1,48 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/experimental/examples/minesweeper/iosApp/iosApp/iosApp.swift b/experimental/examples/minesweeper/iosApp/iosApp/iosApp.swift new file mode 100644 index 0000000000..b42016a6fc --- /dev/null +++ b/experimental/examples/minesweeper/iosApp/iosApp/iosApp.swift @@ -0,0 +1,15 @@ +import UIKit +import shared + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + window = UIWindow(frame: UIScreen.main.bounds) + let mainViewController = Main_iosKt.MainViewController() + window?.rootViewController = mainViewController + window?.makeKeyAndVisible() + return true + } +} diff --git a/experimental/examples/minesweeper/jsApp/build.gradle.kts b/experimental/examples/minesweeper/jsApp/build.gradle.kts new file mode 100644 index 0000000000..99f2073a62 --- /dev/null +++ b/experimental/examples/minesweeper/jsApp/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + kotlin("multiplatform") + id("org.jetbrains.compose") +} + +kotlin { + js(IR) { + browser() + binaries.executable() + } + sourceSets { + val jsMain by getting { + dependencies { + implementation(project(":shared")) + } + } + } +} + +compose.experimental { + web.application {} +} + diff --git a/experimental/examples/minesweeper/jsApp/src/jsMain/kotlin/main.js.kt b/experimental/examples/minesweeper/jsApp/src/jsMain/kotlin/main.js.kt new file mode 100644 index 0000000000..b36b64e9ad --- /dev/null +++ b/experimental/examples/minesweeper/jsApp/src/jsMain/kotlin/main.js.kt @@ -0,0 +1,16 @@ + /* + * 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.ui.window.Window +import org.jetbrains.skiko.wasm.onWasmReady + + +fun main() { + onWasmReady { + Window("Minesweeper") { + MainView() + } + } +} diff --git a/experimental/examples/minesweeper/src/desktopMain/resources/assets/clock.png b/experimental/examples/minesweeper/jsApp/src/jsMain/resources/assets/clock.png similarity index 100% rename from experimental/examples/minesweeper/src/desktopMain/resources/assets/clock.png rename to experimental/examples/minesweeper/jsApp/src/jsMain/resources/assets/clock.png diff --git a/experimental/examples/minesweeper/src/desktopMain/resources/assets/flag.png b/experimental/examples/minesweeper/jsApp/src/jsMain/resources/assets/flag.png similarity index 100% rename from experimental/examples/minesweeper/src/desktopMain/resources/assets/flag.png rename to experimental/examples/minesweeper/jsApp/src/jsMain/resources/assets/flag.png diff --git a/experimental/examples/minesweeper/src/desktopMain/resources/assets/mine.png b/experimental/examples/minesweeper/jsApp/src/jsMain/resources/assets/mine.png similarity index 100% rename from experimental/examples/minesweeper/src/desktopMain/resources/assets/mine.png rename to experimental/examples/minesweeper/jsApp/src/jsMain/resources/assets/mine.png diff --git a/experimental/examples/minesweeper/src/jsMain/resources/index.html b/experimental/examples/minesweeper/jsApp/src/jsMain/resources/index.html similarity index 85% rename from experimental/examples/minesweeper/src/jsMain/resources/index.html rename to experimental/examples/minesweeper/jsApp/src/jsMain/resources/index.html index c0a74e6acf..9dd212786b 100644 --- a/experimental/examples/minesweeper/src/jsMain/resources/index.html +++ b/experimental/examples/minesweeper/jsApp/src/jsMain/resources/index.html @@ -11,6 +11,6 @@ - + \ No newline at end of file diff --git a/experimental/examples/minesweeper/run-configurations.png b/experimental/examples/minesweeper/run-configurations.png new file mode 100644 index 0000000000..3a904f61c3 Binary files /dev/null and b/experimental/examples/minesweeper/run-configurations.png differ diff --git a/experimental/examples/minesweeper/settings.gradle.kts b/experimental/examples/minesweeper/settings.gradle.kts index 037941996c..a5c3565b83 100644 --- a/experimental/examples/minesweeper/settings.gradle.kts +++ b/experimental/examples/minesweeper/settings.gradle.kts @@ -1,7 +1,5 @@ pluginManagement { repositories { - mavenLocal() - mavenCentral() gradlePluginPortal() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") google() @@ -9,14 +7,22 @@ pluginManagement { plugins { val kotlinVersion = extra["kotlin.version"] as String + val agpVersion = extra["agp.version"] as String + val composeVersion = extra["compose.version"] as String + + kotlin("jvm").version(kotlinVersion) kotlin("multiplatform").version(kotlinVersion) kotlin("android").version(kotlinVersion) - - val agpVersion = extra["agp.version"] as String + id("com.android.base").version(agpVersion) id("com.android.application").version(agpVersion) - - val composeVersion = extra["compose.version"] as String + id("com.android.library").version(agpVersion) id("org.jetbrains.compose").version(composeVersion) } } + rootProject.name = "minesweeper" + +include(":androidApp") +include(":shared") +include(":desktopApp") +include(":jsApp") diff --git a/experimental/examples/minesweeper/shared/build.gradle.kts b/experimental/examples/minesweeper/shared/build.gradle.kts new file mode 100644 index 0000000000..2477df7e4a --- /dev/null +++ b/experimental/examples/minesweeper/shared/build.gradle.kts @@ -0,0 +1,117 @@ +plugins { + kotlin("multiplatform") + kotlin("native.cocoapods") + id("com.android.library") + id("org.jetbrains.compose") +} + +version = "1.0-SNAPSHOT" + +kotlin { + android() + + jvm("desktop") + + ios() + iosSimulatorArm64() + + js(IR) { + browser() + } + + macosX64 { + binaries { + executable { + entryPoint = "main" + } + } + } + macosArm64 { + binaries { + executable { + entryPoint = "main" + } + } + } + + cocoapods { + summary = "Shared code for the sample" + homepage = "https://github.com/JetBrains/compose-jb" + ios.deploymentTarget = "14.1" + podfile = project.file("../iosApp/Podfile") + framework { + baseName = "shared" + isStatic = true + } + extraSpecAttributes["resources"] = "['src/commonMain/resources/**', 'src/iosMain/resources/**']" + } + + val composeVersion = extra["compose.version"] as String + + sourceSets { + val commonMain by getting { + dependencies { + implementation(compose.ui) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.runtime) + implementation("org.jetbrains.compose.components:components-resources:$composeVersion") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + implementation(kotlin("test-annotations-common")) } + } + + val androidMain by getting { + dependencies { + implementation("com.google.android.material:material:1.7.0") + } + } + val androidTest by getting { + dependencies { + implementation("junit:junit:4.13.2") + } + } + val iosMain by getting + val iosTest by getting + val iosSimulatorArm64Main by getting { + dependsOn(iosMain) + } + val iosSimulatorArm64Test by getting { + dependsOn(iosTest) + } + + val desktopMain by getting { + dependencies { + implementation(compose.desktop.common) + } + } + + val macosMain by creating { + dependsOn(commonMain) + } + val macosX64Main by getting { + dependsOn(macosMain) + } + val macosArm64Main by getting { + dependsOn(macosMain) + } + } +} + +android { + compileSdk = 33 + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/androidMain/res") + sourceSets["main"].resources.srcDirs("src/commonMain/resources") + defaultConfig { + minSdk = 24 + targetSdk = 33 + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} diff --git a/experimental/examples/minesweeper/shared/src/androidMain/AndroidManifest.xml b/experimental/examples/minesweeper/shared/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000000..77790de2eb --- /dev/null +++ b/experimental/examples/minesweeper/shared/src/androidMain/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/experimental/examples/minesweeper/shared/src/androidMain/kotlin/main.android.kt b/experimental/examples/minesweeper/shared/src/androidMain/kotlin/main.android.kt new file mode 100644 index 0000000000..460fdeb8f2 --- /dev/null +++ b/experimental/examples/minesweeper/shared/src/androidMain/kotlin/main.android.kt @@ -0,0 +1,5 @@ +import androidx.compose.runtime.Composable + +@Composable fun MainView() = Game() + +actual fun hasRightClick() = false diff --git a/experimental/examples/minesweeper/src/commonMain/kotlin/BoardView.kt b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/BoardView.kt similarity index 93% rename from experimental/examples/minesweeper/src/commonMain/kotlin/BoardView.kt rename to experimental/examples/minesweeper/shared/src/commonMain/kotlin/BoardView.kt index a72e93b88b..86c51edd8a 100644 --- a/experimental/examples/minesweeper/src/commonMain/kotlin/BoardView.kt +++ b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/BoardView.kt @@ -10,7 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @Composable -fun BoardView(game: GameController) = with(GameStyles) { +internal fun BoardView(game: GameController) = with(GameStyles) { Column { for (row in 0 until game.rows) { Row(verticalAlignment = Alignment.CenterVertically) { diff --git a/experimental/examples/minesweeper/src/commonMain/kotlin/GameController.kt b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/GameController.kt similarity index 100% rename from experimental/examples/minesweeper/src/commonMain/kotlin/GameController.kt rename to experimental/examples/minesweeper/shared/src/commonMain/kotlin/GameController.kt diff --git a/experimental/examples/minesweeper/src/commonMain/kotlin/game.kt b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/game.kt similarity index 81% rename from experimental/examples/minesweeper/src/commonMain/kotlin/game.kt rename to experimental/examples/minesweeper/shared/src/commonMain/kotlin/game.kt index 90768fa701..ea6893597b 100644 --- a/experimental/examples/minesweeper/src/commonMain/kotlin/game.kt +++ b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/game.kt @@ -5,36 +5,21 @@ import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.ui.* -import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.unit.* +import org.jetbrains.compose.resources.ExperimentalResourceApi +import org.jetbrains.compose.resources.orEmpty +import org.jetbrains.compose.resources.rememberImageBitmap +import org.jetbrains.compose.resources.resource import kotlin.math.max +@OptIn(ExperimentalResourceApi::class) @Composable -expect fun loadImage(src: String): Painter - -fun loadImageAsColoredRect(src: String): Painter { - // TODO Bundle pics and show images properly - val color = when (src) { - "assets/clock.png" -> Color.Blue - "assets/flag.png" -> Color.Green - "assets/mine.png" -> Color.Red - else -> Color.White - } - - return object : Painter() { - override val intrinsicSize: Size - get() = Size(16f, 16f) - - override fun DrawScope.onDraw() { - drawRect(color = color) - } - } -} +internal fun loadImage(res: String): Painter = BitmapPainter(resource(res).rememberImageBitmap().orEmpty()) -expect fun isMobileDevice(): Boolean +expect fun hasRightClick(): Boolean object Difficulty { val EASY = GameSettings(9, 9, 10) @@ -56,7 +41,7 @@ object GameStyles { } @Composable -fun Game(requestWindowSize: ((width: Dp, height: Dp) -> Unit)? = null) = MainLayout { +internal fun Game(requestWindowSize: ((width: Dp, height: Dp) -> Unit)? = null) = MainLayout { var message by remember { mutableStateOf(null) } val onWin = { message = "You win!" } diff --git a/experimental/examples/minesweeper/src/commonMain/kotlin/gameInteraction.kt b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/gameInteraction.kt similarity index 92% rename from experimental/examples/minesweeper/src/commonMain/kotlin/gameInteraction.kt rename to experimental/examples/minesweeper/shared/src/commonMain/kotlin/gameInteraction.kt index 32b0261038..e744152ec9 100644 --- a/experimental/examples/minesweeper/src/commonMain/kotlin/gameInteraction.kt +++ b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/gameInteraction.kt @@ -6,8 +6,8 @@ import androidx.compose.ui.input.pointer.* @OptIn(ExperimentalFoundationApi::class) @Composable -fun Modifier.gameInteraction(open: () -> Unit, flag: () -> Unit, seek: () -> Unit): Modifier = - if (isMobileDevice()) { +internal fun Modifier.gameInteraction(open: () -> Unit, flag: () -> Unit, seek: () -> Unit): Modifier = + if (!hasRightClick()) { combinedClickable( onClick = { open() diff --git a/experimental/examples/minesweeper/src/commonMain/kotlin/widgets.kt b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/widgets.kt similarity index 87% rename from experimental/examples/minesweeper/src/commonMain/kotlin/widgets.kt rename to experimental/examples/minesweeper/shared/src/commonMain/kotlin/widgets.kt index 3a81aca773..8b262ee713 100644 --- a/experimental/examples/minesweeper/src/commonMain/kotlin/widgets.kt +++ b/experimental/examples/minesweeper/shared/src/commonMain/kotlin/widgets.kt @@ -17,7 +17,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @Composable -fun OpenedCell(cell: Cell) { +internal fun OpenedCell(cell: Cell) { Text( text = cell.bombsNear.toString(), textAlign = TextAlign.Center, @@ -28,7 +28,7 @@ fun OpenedCell(cell: Cell) { } @Composable -fun CellWithIcon(src: String, alt: String) { +internal fun CellWithIcon(src: String, alt: String) { Image( painter = loadImage(src), contentDescription = alt, @@ -37,17 +37,17 @@ fun CellWithIcon(src: String, alt: String) { } @Composable -fun Mine() { +internal fun Mine() { CellWithIcon(src = "assets/mine.png", alt = "Bomb") } @Composable -fun Flag() { +internal fun Flag() { CellWithIcon(src = "assets/flag.png", alt = "Flag") } @Composable -fun IndicatorWithIcon(iconPath: String, alt: String, value: Int) { +internal fun IndicatorWithIcon(iconPath: String, alt: String, value: Int) { Box(modifier = Modifier.background(Color(0x8e, 0x6e, 0x0e))) { Row(verticalAlignment = Alignment.CenterVertically) { Box(modifier = Modifier.size(40.dp, 40.dp)) { @@ -66,7 +66,7 @@ fun IndicatorWithIcon(iconPath: String, alt: String, value: Int) { } @Composable -fun NewGameButton(text: String, onClick: () -> Unit) { +internal fun NewGameButton(text: String, onClick: () -> Unit) { Box( Modifier .background(color = Color(0x42, 0x8e, 0x04)) diff --git a/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/clock.png b/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/clock.png new file mode 100644 index 0000000000..d36975927b Binary files /dev/null and b/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/clock.png differ diff --git a/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/flag.png b/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/flag.png new file mode 100644 index 0000000000..92c5c3e0cf Binary files /dev/null and b/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/flag.png differ diff --git a/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/mine.png b/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/mine.png new file mode 100644 index 0000000000..d42eca2f38 Binary files /dev/null and b/experimental/examples/minesweeper/shared/src/commonMain/resources/assets/mine.png differ diff --git a/experimental/examples/minesweeper/src/commonTest/kotlin/GameControllerTest.kt b/experimental/examples/minesweeper/shared/src/commonTest/kotlin/GameControllerTest.kt similarity index 100% rename from experimental/examples/minesweeper/src/commonTest/kotlin/GameControllerTest.kt rename to experimental/examples/minesweeper/shared/src/commonTest/kotlin/GameControllerTest.kt diff --git a/experimental/examples/minesweeper/shared/src/desktopMain/kotlin/main.desktop.kt b/experimental/examples/minesweeper/shared/src/desktopMain/kotlin/main.desktop.kt new file mode 100644 index 0000000000..ed7f8f9d4b --- /dev/null +++ b/experimental/examples/minesweeper/shared/src/desktopMain/kotlin/main.desktop.kt @@ -0,0 +1,15 @@ +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.window.WindowState + +@Composable +fun MainView(windowState: WindowState) = + MaterialTheme { + Game( + requestWindowSize = { w, h -> + windowState.size = windowState.size.copy(width = w, height = h) + } + ) + } + +actual fun hasRightClick() = true diff --git a/experimental/examples/minesweeper/shared/src/iosMain/kotlin/main.ios.kt b/experimental/examples/minesweeper/shared/src/iosMain/kotlin/main.ios.kt new file mode 100644 index 0000000000..0ccba7c43a --- /dev/null +++ b/experimental/examples/minesweeper/shared/src/iosMain/kotlin/main.ios.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2020-2022 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.ui.window.Application +import platform.UIKit.UIViewController + +fun MainViewController() : UIViewController = + Application("Minesweeper") { + Game() + } + + +actual fun hasRightClick() = false \ No newline at end of file diff --git a/experimental/examples/minesweeper/shared/src/jsMain/kotlin/main.js.kt b/experimental/examples/minesweeper/shared/src/jsMain/kotlin/main.js.kt new file mode 100644 index 0000000000..5e07958636 --- /dev/null +++ b/experimental/examples/minesweeper/shared/src/jsMain/kotlin/main.js.kt @@ -0,0 +1,6 @@ +import androidx.compose.runtime.Composable + +@Composable +fun MainView() = Game() + +actual fun hasRightClick() = false diff --git a/experimental/examples/minesweeper/src/macosMain/kotlin/main.macos.kt b/experimental/examples/minesweeper/shared/src/macosMain/kotlin/main.macos.kt similarity index 88% rename from experimental/examples/minesweeper/src/macosMain/kotlin/main.macos.kt rename to experimental/examples/minesweeper/shared/src/macosMain/kotlin/main.macos.kt index 41ae920d49..88b034d366 100644 --- a/experimental/examples/minesweeper/src/macosMain/kotlin/main.macos.kt +++ b/experimental/examples/minesweeper/shared/src/macosMain/kotlin/main.macos.kt @@ -29,7 +29,4 @@ fun main() { NSApp?.run() } -@Composable -actual fun loadImage(src: String): Painter = loadImageAsColoredRect(src) - -actual fun isMobileDevice() = false \ No newline at end of file +actual fun hasRightClick() = false \ No newline at end of file diff --git a/experimental/examples/minesweeper/src/androidMain/kotlin/actualfuns.android.kt b/experimental/examples/minesweeper/src/androidMain/kotlin/actualfuns.android.kt deleted file mode 100644 index b8ac63873d..0000000000 --- a/experimental/examples/minesweeper/src/androidMain/kotlin/actualfuns.android.kt +++ /dev/null @@ -1,7 +0,0 @@ -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.painter.Painter - -@Composable -actual fun loadImage(src: String): Painter = loadImageAsColoredRect(src) - -actual fun isMobileDevice() = true \ No newline at end of file diff --git a/experimental/examples/minesweeper/src/desktopMain/kotlin/main.desktop.kt b/experimental/examples/minesweeper/src/desktopMain/kotlin/main.desktop.kt deleted file mode 100644 index c12de62591..0000000000 --- a/experimental/examples/minesweeper/src/desktopMain/kotlin/main.desktop.kt +++ /dev/null @@ -1,32 +0,0 @@ -import androidx.compose.material.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.window.Window -import androidx.compose.ui.window.application -import androidx.compose.ui.window.rememberWindowState - -fun main() = application { - val windowState = rememberWindowState() - - Window( - onCloseRequest = ::exitApplication, - resizable = false, - title = "Minesweeper", - icon = painterResource("assets/mine.png"), - state = windowState - ) { - MaterialTheme { - Game( - requestWindowSize = { w, h -> - windowState.size = windowState.size.copy(width = w, height = h) - } - ) - } - } -} - -@Composable -actual fun loadImage(src: String): Painter = painterResource(src) - -actual fun isMobileDevice() = false diff --git a/experimental/examples/minesweeper/src/jsMain/kotlin/main.js.kt b/experimental/examples/minesweeper/src/jsMain/kotlin/main.js.kt deleted file mode 100644 index 1d1dd2d69f..0000000000 --- a/experimental/examples/minesweeper/src/jsMain/kotlin/main.js.kt +++ /dev/null @@ -1,24 +0,0 @@ -import androidx.compose.runtime.Composable -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.window.Window -import org.jetbrains.skiko.wasm.onWasmReady - -fun main() { - onWasmReady { - Window("Minesweeper") { - Game( - requestWindowSize = { _, _ -> - // TODO(not implemented yet) - } - ) - } - } -} - -@Composable -actual fun loadImage(src: String): Painter = loadImageAsColoredRect(src) - -actual fun isMobileDevice() = false diff --git a/experimental/examples/minesweeper/src/uikitMain/kotlin/main.uikit.kt b/experimental/examples/minesweeper/src/uikitMain/kotlin/main.uikit.kt deleted file mode 100644 index 1cfea076b5..0000000000 --- a/experimental/examples/minesweeper/src/uikitMain/kotlin/main.uikit.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2020-2022 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.runtime.Composable -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.window.Application -import androidx.compose.ui.main.defaultUIKitMain - -fun main() { - defaultUIKitMain("Minesweeper", Application("Minesweeper") { - Game() - }) -} - -@Composable -actual fun loadImage(src: String): Painter = loadImageAsColoredRect(src) - -actual fun isMobileDevice() = true \ No newline at end of file