From 3d018ac9bb31d008257ea7e3f248f091ac00c75b Mon Sep 17 00:00:00 2001 From: Alex Gladkov Date: Thu, 13 Oct 2022 22:27:32 +0600 Subject: [PATCH] Done --- android/build.gradle.kts | 7 +- .../alexgladkov/odyssey_demo/MainActivity.kt | 22 +- build.gradle.kts | 13 +- .../buildSrc/src/main/kotlin/Dependencies.kt | 25 +- .../multiplatform-compose-setup.gradle.kts | 38 ++- .../kotlin/multiplatform-setup.gradle.kts | 7 - .../common/compose/NavigationTree.kt | 27 -- .../compose/navigation/NavigationGraph.kt | 68 ----- common/common-root/build.gradle.kts | 49 ---- .../src/androidMain/AndroidManifest.xml | 2 - .../kotlin/ru/alexgladkov/common/root/Root.kt | 4 - .../build.gradle.kts | 0 .../src/androidMain/AndroidManifest.xml | 0 .../common/compose/NavigationTree.kt | 5 + .../ru/alexgladkov/common/compose/Temp.kt | 0 .../common/compose/helpers/ModelsGenerator.kt | 0 .../compose/navigation/NavigationGraph.kt | 90 ++++++ .../common/compose/screens/ActionsScreen.kt | 13 +- .../compose/screens/AlertDialogScreen.kt | 0 .../common/compose/screens/DrawerScreen.kt | 0 .../compose/screens/ModalSheetScreen.kt | 0 .../compose/screens/PresentedActionsScreen.kt | 9 +- .../common/compose/screens/TabScreen.kt | 5 +- .../compose/tabs/BottomConfiguration.kt | 12 - .../common/compose/tabs/CartTab.kt | 0 .../common/compose/tabs/FeedTab.kt | 0 .../common/compose/tabs/SearchTab.kt | 0 .../common/compose/theme/OdysseyTheme.kt | 0 .../compose/theme/colors/OdysseyColors.kt | 0 .../common/compose/theme/colors/Palettes.kt | 0 desktop/build.gradle.kts | 8 +- .../odyssey-demo/extensions/JFrame + Setup.kt | 4 +- .../alexgladkov/odyssey-demo/main.desktop.kt | 15 + .../ru/alexgladkov/odyssey-demo/main.kt | 38 --- gradle.properties | 4 +- gradle/wrapper/gradle-wrapper.jar | Bin 58910 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 257 +++++++++++------- gradlew.bat | 193 ++++++------- .../odyssey/compose/RootController.kt | 11 +- .../compose/base/BottomBarNavigator.kt | 2 +- .../odyssey/compose/base/Navigator.kt | 2 +- .../extensions/RootComposeBuilder + DSL.kt | 18 ++ .../views/BottomSheetModalView.kt | 1 + .../odyssey/compose/DesktopScreenHost.kt | 35 --- .../odyssey/compose/extensions/RandomKey.kt | 5 + .../odyssey/compose/helpers/WindowSize.kt | 11 + odyssey/odyssey-core/build.gradle.kts | 2 +- run-uikit.sh | 5 + settings.gradle.kts | 7 +- uikit/build.gradle.kts | 92 +++++++ .../src/commonMain/kotlin/UikitApplication.kt | 10 + uikit/src/uikitMain/kotlin/main.uikit.kt | 73 +++++ 53 files changed, 660 insertions(+), 531 deletions(-) delete mode 100644 common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt delete mode 100644 common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt delete mode 100644 common/common-root/build.gradle.kts delete mode 100644 common/common-root/src/androidMain/AndroidManifest.xml delete mode 100644 common/common-root/src/commonMain/kotlin/ru/alexgladkov/common/root/Root.kt rename common/{common-compose => common-sample}/build.gradle.kts (100%) rename common/{common-compose => common-sample}/src/androidMain/AndroidManifest.xml (100%) create mode 100644 common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/Temp.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/helpers/ModelsGenerator.kt (100%) create mode 100644 common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ActionsScreen.kt (90%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/AlertDialogScreen.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/DrawerScreen.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ModalSheetScreen.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/PresentedActionsScreen.kt (92%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/TabScreen.kt (89%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/BottomConfiguration.kt (80%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/CartTab.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/FeedTab.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/SearchTab.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/OdysseyTheme.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/OdysseyColors.kt (100%) rename common/{common-compose => common-sample}/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/Palettes.kt (100%) create mode 100644 desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.desktop.kt delete mode 100644 desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.kt delete mode 100644 odyssey/odyssey-compose/src/desktopMain/kotlin/ru/alexgladkov/odyssey/compose/DesktopScreenHost.kt create mode 100644 odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RandomKey.kt create mode 100644 odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/helpers/WindowSize.kt create mode 100755 run-uikit.sh create mode 100644 uikit/build.gradle.kts create mode 100644 uikit/src/commonMain/kotlin/UikitApplication.kt create mode 100644 uikit/src/uikitMain/kotlin/main.uikit.kt diff --git a/android/build.gradle.kts b/android/build.gradle.kts index ecbed535..f1696e57 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -6,11 +6,11 @@ plugins { } android { - compileSdkVersion(31) + compileSdkVersion(Dependencies.compileSdk) defaultConfig { minSdkVersion(21) - targetSdkVersion(31) + targetSdkVersion(Dependencies.targetSdk) versionCode = 1 versionName = "1.0" @@ -28,10 +28,9 @@ android { } dependencies { - implementation(project(":common:common-root")) + implementation(project(":common:common-sample")) implementation(project(":odyssey:odyssey-compose")) implementation(project(":odyssey:odyssey-core")) - implementation(project(":common:common-compose")) implementation(compose.material) implementation(Dependencies.AndroidX.AppCompat.appCompat) diff --git a/android/src/main/java/ru/alexgladkov/odyssey_demo/MainActivity.kt b/android/src/main/java/ru/alexgladkov/odyssey_demo/MainActivity.kt index 6fef1676..6de3d62a 100644 --- a/android/src/main/java/ru/alexgladkov/odyssey_demo/MainActivity.kt +++ b/android/src/main/java/ru/alexgladkov/odyssey_demo/MainActivity.kt @@ -2,8 +2,10 @@ package ru.alexgladkov.odyssey_demo import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import ru.alexgladkov.common.compose.NavigationTree import ru.alexgladkov.common.compose.navigation.customNavScreen import ru.alexgladkov.common.compose.navigation.mainScreen +import ru.alexgladkov.common.compose.navigation.navigationGraph import ru.alexgladkov.common.compose.navigation.topNavScreen import ru.alexgladkov.common.compose.screens.ActionsScreen import ru.alexgladkov.common.compose.screens.PresentedActionsScreen @@ -16,24 +18,6 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setupThemedNavigation("actions") { - screen("actions") { - ActionsScreen(count = 0) - } - - screen("push") { - ActionsScreen(count = it as? Int) - } - - flow("present") { - screen("present_screen") { - PresentedActionsScreen(count = (it as? Int) ?: 0) - } - } - - mainScreen() - topNavScreen() - customNavScreen() - } + setupThemedNavigation(NavigationTree.Actions.name) { navigationGraph() } } } diff --git a/build.gradle.kts b/build.gradle.kts index d2c90d9a..b9b281ef 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,10 +4,6 @@ plugins { `kotlin-dsl` } -val compileSdkVersion by extra(31) -val targetSdkVersion by extra(30) -val minSdkVersion by extra(21) - allprojects { repositories { google() @@ -19,8 +15,13 @@ allprojects { } buildscript { - val kotlin_version by extra("1.5.31") + repositories { + mavenLocal() + mavenCentral() + google() + maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + } dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.20") } } diff --git a/buildSrc/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/buildSrc/src/main/kotlin/Dependencies.kt index 8cc4e701..e655d43b 100644 --- a/buildSrc/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/buildSrc/src/main/kotlin/Dependencies.kt @@ -1,8 +1,11 @@ object Dependencies { - val odyssey = "1.0.0-beta15" + val odyssey = "1.0.0" val odysseyPackage = "io.github.alexgladkov" + val compileSdk = 33 + val targetSdk = 33 + object DI { const val kodein = "org.kodein.di:kodein-di:7.1.0" } @@ -14,7 +17,7 @@ object Dependencies { object JetBrains { object Kotlin { // __KOTLIN_COMPOSE_VERSION__ - private const val VERSION = "1.6.10" + private const val VERSION = "1.7.20" const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$VERSION" const val testCommon = "org.jetbrains.kotlin:kotlin-test-common:$VERSION" const val testJunit = "org.jetbrains.kotlin:kotlin-test-junit:$VERSION" @@ -23,10 +26,10 @@ object Dependencies { const val dateTime = "org.jetbrains.kotlinx:kotlinx-datetime:0.2.1" - const val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.1" + const val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-core:1.4.0" const val serializationPlugin = "org.jetbrains.kotlin:kotlin-serialization:1.5.31" - private const val coroutinesVersion = "1.6.0-native-mt" + private const val coroutinesVersion = "1.6.4" const val coroutines = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion" const val coroutinesAndroid = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion" const val coroutinesSwing = "org.jetbrains.kotlinx:kotlinx-coroutines-swing:$coroutinesVersion" @@ -36,21 +39,9 @@ object Dependencies { object Compose { // __LATEST_COMPOSE_RELEASE_VERSION__ - private const val VERSION = "1.1.1" + private const val VERSION = "1.2.0" const val gradlePlugin = "org.jetbrains.compose:compose-gradle-plugin:$VERSION" } - - object Ktor { - private const val ktorVersion = "1.6.2" - const val core = "io.ktor:ktor-client-core:${ktorVersion}" - const val json = "io.ktor:ktor-client-json:${ktorVersion}" - const val iOS = "io.ktor:ktor-client-ios:${ktorVersion}" - const val iOSCore = "io.ktor:ktor-client-core-native:$ktorVersion" - const val serialization = "io.ktor:ktor-client-serialization:${ktorVersion}" - const val logging = "io.ktor:ktor-client-logging:$ktorVersion" - const val android = "io.ktor:ktor-client-android:$ktorVersion" - const val okHttp = "io.ktor:ktor-client-okhttp:$ktorVersion" - } } object Android { diff --git a/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts b/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts index b7536e4b..be74b03a 100644 --- a/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts +++ b/buildSrc/src/main/kotlin/multiplatform-compose-setup.gradle.kts @@ -9,9 +9,35 @@ plugins { kotlin { jvm("desktop") android() + 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" + ) + } + } + } + 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" + ) + // TODO: the current compose binary surprises LLVM, so disable checks for now. + freeCompilerArgs += "-Xdisable-phases=VerifyBitcode" + } + } + } sourceSets { - named("commonMain") { + val commonMain by getting { dependencies { implementation(compose.runtime) implementation(compose.foundation) @@ -31,6 +57,16 @@ kotlin { implementation(compose.desktop.common) } } + + val uikitMain by creating { + dependsOn(commonMain) + } + val uikitX64Main by getting { + dependsOn(uikitMain) + } + val uikitArm64Main by getting { + dependsOn(uikitMain) + } } tasks.withType { diff --git a/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts b/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts index eb0243e6..ee6267ff 100644 --- a/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts +++ b/buildSrc/src/main/kotlin/multiplatform-setup.gradle.kts @@ -10,13 +10,6 @@ kotlin { jvm("desktop") android() -// val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget = when { -// System.getenv("SDK_NAME")?.startsWith("iphoneos") == true -> ::iosArm64 -// else -> ::iosX64 -// } -// -// iosTarget("ios") { } - ios() sourceSets { diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt b/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt deleted file mode 100644 index f65971f3..00000000 --- a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt +++ /dev/null @@ -1,27 +0,0 @@ -package ru.alexgladkov.common.compose - -object NavigationTree { - enum class Root { - Splash, Auth, Main, Dialog - } - - enum class Auth { - Onboarding, Login, TwoFactor - } - - enum class Main { - Feed, Detail - } - - enum class Favorite { - Flow - } - - enum class Settings { - Profile - } - - enum class Tabs { - Main, Favorite, Settings - } -} \ No newline at end of file diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt b/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt deleted file mode 100644 index 9c826757..00000000 --- a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt +++ /dev/null @@ -1,68 +0,0 @@ -package ru.alexgladkov.common.compose.navigation - -import ru.alexgladkov.common.compose.screens.* -import ru.alexgladkov.common.compose.tabs.* -import ru.alexgladkov.odyssey.compose.extensions.* -import ru.alexgladkov.odyssey.compose.navigation.RootComposeBuilder - -fun RootComposeBuilder.mainScreen() { - bottomNavigation(name = "main", tabsNavModel = BottomConfiguration()) { - tab(FeedTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - tab(SearchTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - tab(CartTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - } -} - -fun RootComposeBuilder.topNavScreen() { - topNavigation(name = "top", tabsNavModel = TopConfiguration()) { - tab(FeedTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - tab(SearchTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - tab(CartTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - } -} - -fun RootComposeBuilder.customNavScreen() { - customNavigation(name = "drawer", tabsNavModel = CustomConfiguration( - content = { params -> DrawerScreen(params) } - )) { - tab(FeedTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - tab(SearchTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - tab(CartTab()) { - screen(name = "tab") { - TabScreen(it as? Int) - } - } - } -} diff --git a/common/common-root/build.gradle.kts b/common/common-root/build.gradle.kts deleted file mode 100644 index 7b9cdbcc..00000000 --- a/common/common-root/build.gradle.kts +++ /dev/null @@ -1,49 +0,0 @@ -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget - -plugins { - id("multiplatform-setup") - id("kotlin-parcelize") - id("android-setup") - kotlin("plugin.serialization") -} - -kotlin { - ios { - binaries { - framework { - transitiveExport = true - baseName = "OdysseyShared" - } - } - } - - sourceSets { - named("commonMain") { - dependencies { - implementation(Dependencies.DI.kodein) - } - } - - val iosMain by getting - -// val iosX64Main by getting { dependsOn(iosMain) } -// val iosArm64Main by getting { dependsOn(iosMain) } - } -} - -fun getIosTarget(): String { - val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator" - return if (sdkName.startsWith("iphoneos")) "iosArm64" else "iosX64" -} - -val packForXcode by tasks.creating(Sync::class) { - group = "build" - val mode = System.getenv("CONFIGURATION") ?: "DEBUG" - val targetName = getIosTarget() - val framework = kotlin.targets.getByName(targetName).binaries.getFramework(mode) - inputs.property("mode", mode) - dependsOn(framework.linkTask) - val targetDir = File(buildDir, "xcode-frameworks") - from(framework.outputDirectory) - into(targetDir) -} \ No newline at end of file diff --git a/common/common-root/src/androidMain/AndroidManifest.xml b/common/common-root/src/androidMain/AndroidManifest.xml deleted file mode 100644 index 9f354042..00000000 --- a/common/common-root/src/androidMain/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/common/common-root/src/commonMain/kotlin/ru/alexgladkov/common/root/Root.kt b/common/common-root/src/commonMain/kotlin/ru/alexgladkov/common/root/Root.kt deleted file mode 100644 index a19f79dc..00000000 --- a/common/common-root/src/commonMain/kotlin/ru/alexgladkov/common/root/Root.kt +++ /dev/null @@ -1,4 +0,0 @@ -package ru.alexgladkov.common.root - -class Root { -} \ No newline at end of file diff --git a/common/common-compose/build.gradle.kts b/common/common-sample/build.gradle.kts similarity index 100% rename from common/common-compose/build.gradle.kts rename to common/common-sample/build.gradle.kts diff --git a/common/common-compose/src/androidMain/AndroidManifest.xml b/common/common-sample/src/androidMain/AndroidManifest.xml similarity index 100% rename from common/common-compose/src/androidMain/AndroidManifest.xml rename to common/common-sample/src/androidMain/AndroidManifest.xml diff --git a/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt new file mode 100644 index 00000000..75bcba8c --- /dev/null +++ b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/NavigationTree.kt @@ -0,0 +1,5 @@ +package ru.alexgladkov.common.compose + +enum class NavigationTree { + Actions, Push, Present, PresentScreen, Main, Tab, Top, Drawer +} \ No newline at end of file diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/Temp.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/Temp.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/Temp.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/Temp.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/helpers/ModelsGenerator.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/helpers/ModelsGenerator.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/helpers/ModelsGenerator.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/helpers/ModelsGenerator.kt diff --git a/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt new file mode 100644 index 00000000..105c7783 --- /dev/null +++ b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/navigation/NavigationGraph.kt @@ -0,0 +1,90 @@ +package ru.alexgladkov.common.compose.navigation + +import ru.alexgladkov.common.compose.NavigationTree +import ru.alexgladkov.common.compose.screens.* +import ru.alexgladkov.common.compose.tabs.* +import ru.alexgladkov.odyssey.compose.base.Navigator +import ru.alexgladkov.odyssey.compose.extensions.* +import ru.alexgladkov.odyssey.compose.navigation.RootComposeBuilder + +fun RootComposeBuilder.navigationGraph() { + screen(NavigationTree.Actions.name) { + ActionsScreen(count = 0) + } + + screen(NavigationTree.Push.name) { + ActionsScreen(count = it as? Int) + } + + flow(NavigationTree.Present.name) { + screen(NavigationTree.PresentScreen.name) { + PresentedActionsScreen(count = (it as? Int) ?: 0) + } + } + + mainScreen() + topNavScreen() + customNavScreen() +} + +fun RootComposeBuilder.mainScreen() { + bottomNavigation(name = NavigationTree.Main.name, tabsNavModel = BottomConfiguration()) { + tab(FeedTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + tab(SearchTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + tab(CartTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + } +} + +fun RootComposeBuilder.topNavScreen() { + topNavigation(name = NavigationTree.Top.name, tabsNavModel = TopConfiguration()) { + tab(FeedTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + tab(SearchTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + tab(CartTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + } +} + +fun RootComposeBuilder.customNavScreen() { + customNavigation(name = NavigationTree.Drawer.name, tabsNavModel = CustomConfiguration( + content = { params -> DrawerScreen(params) } + )) { + tab(FeedTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + tab(SearchTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + tab(CartTab()) { + screen(name = NavigationTree.Tab.name) { + TabScreen(it as? Int) + } + } + } +} diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ActionsScreen.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ActionsScreen.kt similarity index 90% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ActionsScreen.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ActionsScreen.kt index fe60268f..6769d2f6 100644 --- a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ActionsScreen.kt +++ b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ActionsScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import ru.alexgladkov.common.compose.NavigationTree import ru.alexgladkov.common.compose.theme.Odyssey import ru.alexgladkov.odyssey.compose.extensions.present import ru.alexgladkov.odyssey.compose.extensions.push @@ -35,13 +36,13 @@ fun ActionsScreen(count: Int?) { LazyColumn { item { ActionCell(text = "Push Screen", icon = Icons.Filled.ArrowForward) { - rootController.push("push", (count ?: 0) + 1) + rootController.push(NavigationTree.Push.name, (count ?: 0) + 1) } } item { ActionCell("Present Flow", icon = Icons.Filled.ArrowUpward) { - rootController.present("present") + rootController.present(NavigationTree.Present.name) } } @@ -72,7 +73,7 @@ fun ActionsScreen(count: Int?) { "Show Bottom Navigation", icon = Icons.Filled.Dashboard ) { - rootController.present("main") + rootController.present(NavigationTree.Main.name) } } @@ -81,7 +82,7 @@ fun ActionsScreen(count: Int?) { "Show Top Navigation", icon = Icons.Filled.SpaceDashboard ) { - rootController.present("top") + rootController.present(NavigationTree.Top.name) } } @@ -90,7 +91,7 @@ fun ActionsScreen(count: Int?) { "Show Drawer Navigation", icon = Icons.Filled.DashboardCustomize ) { - rootController.present("drawer", params = "Custom Title from params") + rootController.present(NavigationTree.Drawer.name, params = "Custom Title from params") } } @@ -99,7 +100,7 @@ fun ActionsScreen(count: Int?) { "Start New Chain", icon = Icons.Filled.OpenInNew ) { - rootController.present(screen = "present", launchFlag = LaunchFlag.SingleNewTask) + rootController.present(screen = NavigationTree.Present.name, launchFlag = LaunchFlag.SingleNewTask) } } diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/AlertDialogScreen.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/AlertDialogScreen.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/AlertDialogScreen.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/AlertDialogScreen.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/DrawerScreen.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/DrawerScreen.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/DrawerScreen.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/DrawerScreen.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ModalSheetScreen.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ModalSheetScreen.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ModalSheetScreen.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/ModalSheetScreen.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/PresentedActionsScreen.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/PresentedActionsScreen.kt similarity index 92% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/PresentedActionsScreen.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/PresentedActionsScreen.kt index 5c9f1b69..2653cae5 100644 --- a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/PresentedActionsScreen.kt +++ b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/PresentedActionsScreen.kt @@ -9,6 +9,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import ru.alexgladkov.common.compose.NavigationTree import ru.alexgladkov.common.compose.theme.Odyssey import ru.alexgladkov.odyssey.compose.extensions.present import ru.alexgladkov.odyssey.compose.extensions.push @@ -31,13 +32,13 @@ fun PresentedActionsScreen(count: Int?) { LazyColumn { item { ActionCell(text = "Push Screen", icon = Icons.Filled.ArrowForward) { - rootController.push("present_screen", (count ?: 0) + 1) + rootController.push(NavigationTree.PresentScreen.name, (count ?: 0) + 1) } } item { ActionCell("Present Flow", icon = Icons.Filled.ArrowUpward) { - rootController.findRootController().present("present") + rootController.findRootController().present(NavigationTree.Present.name) } } @@ -68,7 +69,7 @@ fun PresentedActionsScreen(count: Int?) { "Show Bottom Navigation", icon = Icons.Filled.Dashboard ) { - rootController.findRootController().present("main") + rootController.findRootController().present(NavigationTree.Main.name) } } @@ -77,7 +78,7 @@ fun PresentedActionsScreen(count: Int?) { "Start New Chain", icon = Icons.Filled.OpenInNew ) { - rootController.findRootController().present(screen = "present", launchFlag = LaunchFlag.SingleNewTask) + rootController.findRootController().present(screen = NavigationTree.Present.name, launchFlag = LaunchFlag.SingleNewTask) } } diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/TabScreen.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/TabScreen.kt similarity index 89% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/TabScreen.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/TabScreen.kt index 2e85f289..34404856 100644 --- a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/TabScreen.kt +++ b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/screens/TabScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.material.icons.filled.ArrowForward import androidx.compose.material.icons.filled.ArrowUpward import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import ru.alexgladkov.common.compose.NavigationTree import ru.alexgladkov.common.compose.theme.Odyssey import ru.alexgladkov.odyssey.compose.extensions.present import ru.alexgladkov.odyssey.compose.extensions.push @@ -28,13 +29,13 @@ fun TabScreen(count: Int?) { LazyColumn { item { ActionCell(text = "Push Screen", icon = Icons.Filled.ArrowForward) { - rootController.push("tab", (count ?: 0) + 1) + rootController.push(NavigationTree.Tab.name, (count ?: 0) + 1) } } item { ActionCell("Present Flow", icon = Icons.Filled.ArrowUpward) { - rootController.findRootController().present("present") + rootController.findRootController().present(NavigationTree.Present.name) } } } diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/BottomConfiguration.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/BottomConfiguration.kt similarity index 80% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/BottomConfiguration.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/BottomConfiguration.kt index a844bbaa..e39375bc 100644 --- a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/BottomConfiguration.kt +++ b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/BottomConfiguration.kt @@ -41,15 +41,3 @@ class CustomConfiguration(private val content: @Composable (params: Any?) -> Uni ) } } - -fun RootComposeBuilder.customNavigation( - name: String, - tabsNavModel: TabsNavModel, - builder: MultiStackBuilder.() -> Unit -) { - addMultiStack( - key = name, - tabsNavModel = tabsNavModel, - multiStackBuilderModel = MultiStackBuilder(name).apply(builder).build() - ) -} \ No newline at end of file diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/CartTab.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/CartTab.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/CartTab.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/CartTab.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/FeedTab.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/FeedTab.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/FeedTab.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/FeedTab.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/SearchTab.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/SearchTab.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/SearchTab.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/tabs/SearchTab.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/OdysseyTheme.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/OdysseyTheme.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/OdysseyTheme.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/OdysseyTheme.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/OdysseyColors.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/OdysseyColors.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/OdysseyColors.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/OdysseyColors.kt diff --git a/common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/Palettes.kt b/common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/Palettes.kt similarity index 100% rename from common/common-compose/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/Palettes.kt rename to common/common-sample/src/commonMain/kotlin/ru/alexgladkov/common/compose/theme/colors/Palettes.kt diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 49700f14..e7cf45fd 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -13,7 +13,7 @@ kotlin { sourceSets { named("commonMain") { dependencies { - implementation(project(":common:common-compose")) + implementation(project(":common:common-sample")) implementation(project(":odyssey:odyssey-core")) implementation(project(":odyssey:odyssey-compose")) } @@ -22,10 +22,6 @@ kotlin { named("jvmMain") { dependencies { implementation(compose.desktop.currentOs) - implementation(project(":common:common-root")) - implementation(project(":common:common-compose")) - - implementation(Dependencies.JetBrains.Ktor.okHttp) } } @@ -39,7 +35,7 @@ kotlin { compose.desktop { application { - mainClass = "ru.alexgladkov.odyssey-demo.MainKt" + mainClass = "ru.alexgladkov.odyssey-demo.Main_DesktopKt" nativeDistributions { targetFormats( diff --git a/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/extensions/JFrame + Setup.kt b/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/extensions/JFrame + Setup.kt index 33205f25..62448880 100644 --- a/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/extensions/JFrame + Setup.kt +++ b/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/extensions/JFrame + Setup.kt @@ -25,8 +25,8 @@ fun JFrame.setupThemedNavigation( // by adding CompositionLocalProviders or something else composePanel.setContent { OdysseyTheme { - val rootController = RootComposeBuilder(backgroundColor = Odyssey.color.primaryBackground) - .apply(navigationGraph).build() + val rootController = RootComposeBuilder().apply(navigationGraph).build() + rootController.backgroundColor = Odyssey.color.primaryBackground CompositionLocalProvider( *providers, diff --git a/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.desktop.kt b/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.desktop.kt new file mode 100644 index 00000000..7c831889 --- /dev/null +++ b/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.desktop.kt @@ -0,0 +1,15 @@ +package ru.alexgladkov.`odyssey-demo` + +import ru.alexgladkov.`odyssey-demo`.extensions.setupThemedNavigation +import ru.alexgladkov.common.compose.NavigationTree +import ru.alexgladkov.common.compose.navigation.navigationGraph +import javax.swing.JFrame +import javax.swing.SwingUtilities + +fun main() = SwingUtilities.invokeLater { + val window = JFrame() + window.title = "Odyssey Demo" + window.setSize(800, 600) + + window.setupThemedNavigation(NavigationTree.Actions.name) { navigationGraph() } +} \ No newline at end of file diff --git a/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.kt b/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.kt deleted file mode 100644 index 9087c6f5..00000000 --- a/desktop/src/jvmMain/kotlin/ru/alexgladkov/odyssey-demo/main.kt +++ /dev/null @@ -1,38 +0,0 @@ -package ru.alexgladkov.`odyssey-demo` - -import ru.alexgladkov.`odyssey-demo`.extensions.setupThemedNavigation -import ru.alexgladkov.common.compose.navigation.customNavScreen -import ru.alexgladkov.common.compose.navigation.mainScreen -import ru.alexgladkov.common.compose.navigation.topNavScreen -import ru.alexgladkov.common.compose.screens.ActionsScreen -import ru.alexgladkov.common.compose.screens.PresentedActionsScreen -import ru.alexgladkov.odyssey.compose.extensions.flow -import ru.alexgladkov.odyssey.compose.extensions.screen -import javax.swing.JFrame -import javax.swing.SwingUtilities - -fun main() = SwingUtilities.invokeLater { - val window = JFrame() - window.title = "Odyssey Demo" - window.setSize(800, 600) - - window.setupThemedNavigation("actions") { - screen("actions") { - ActionsScreen(count = 0) - } - - screen("push") { - ActionsScreen(count = it as? Int) - } - - flow("present") { - screen("present_screen") { - PresentedActionsScreen(count = (it as? Int) ?: 0) - } - } - - mainScreen() - topNavScreen() - customNavScreen() - } -} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 2f8f9fbf..2d37e36d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,6 @@ kotlin.code.style=official org.gradle.parallel=false org.gradle.caching=true kotlin.mpp.enableGranularSourceSetsMetadata = true -kotlin.native.disableCompilerDaemon=true \ No newline at end of file +kotlin.native.disableCompilerDaemon=true +org.jetbrains.compose.experimental.macos.enabled=true +org.jetbrains.compose.experimental.uikit.enabled=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 62d4c053550b91381bbd28b1afc82d634bf73a8a..7454180f2ae8848c63b8b4dea2cb829da983f2fa 100644 GIT binary patch delta 18328 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 Y(TzS~Xdo8@nTUZrK}a{fL*HNQe=4o%4FCWD delta 17568 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 5093609d..ac1b06f9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,104 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/RootController.kt b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/RootController.kt index 71fe6b0c..aeb774cd 100644 --- a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/RootController.kt +++ b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/RootController.kt @@ -48,8 +48,8 @@ open class RootController( ) { private val _allowedDestinations: MutableList = mutableListOf() private val _backstack = mutableListOf() - private val _currentScreen: MutableStateFlow = - MutableStateFlow(Screen().wrap()) + private val _currentScreen: MutableStateFlow = + MutableStateFlow(null) private var _childrenRootController: MutableList = mutableListOf() private val _screenMap = mutableMapOf>() private var _onBackPressedDispatcher: OnBackPressedDispatcher? = null @@ -62,7 +62,7 @@ open class RootController( var onScreenRemove: (ScreenBundle) -> Unit = { parentRootController?.onScreenRemove?.invoke(it) } - var currentScreen: StateFlow = _currentScreen.asStateFlow() + var currentScreen: StateFlow = _currentScreen.asStateFlow() /** * Debug name need to debug :) if you like console debugging @@ -113,7 +113,6 @@ open class RootController( _onBackPressedDispatcher = onBackPressedDispatcher _onBackPressedDispatcher?.backPressedCallback = object : BackPressedCallback() { override fun onBackPressed() { - println("DEBUG: On Back Pressed") popBackStack() } } @@ -338,7 +337,6 @@ open class RootController( } private fun removeTopScreen(rootController: RootController?) { - println("RC ${rootController?.debugName}") rootController?.let { val isLastScreen = it._backstack.size <= 1 if (isLastScreen) { @@ -378,6 +376,7 @@ open class RootController( when (launchFlag) { LaunchFlag.SingleNewTask -> _backstack.clear() + null -> {} } _backstack.add(screen) @@ -396,6 +395,7 @@ open class RootController( when (launchFlag) { LaunchFlag.SingleNewTask -> _backstack.clear() + null -> {} } val rootController = RootController(RootControllerType.Flow) @@ -446,6 +446,7 @@ open class RootController( when (launchFlag) { LaunchFlag.SingleNewTask -> _backstack.clear() + null -> {} } val parentRootController = this diff --git a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/BottomBarNavigator.kt b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/BottomBarNavigator.kt index fd960a12..a06c3e62 100644 --- a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/BottomBarNavigator.kt +++ b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/BottomBarNavigator.kt @@ -26,7 +26,7 @@ fun TabNavigator( CompositionLocalProvider( LocalRootController provides currentTab.rootController ) { - configuration.value.let { navConfig -> + configuration.value?.let { navConfig -> AnimatedHost( currentScreen = navConfig.screen.toScreenBundle(), animationType = navConfig.screen.animationType, diff --git a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/Navigator.kt b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/Navigator.kt index 2ad31f46..7feadd6f 100644 --- a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/Navigator.kt +++ b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/base/Navigator.kt @@ -21,7 +21,7 @@ fun Navigator( val backgroundColor = rootController.backgroundColor val navConfiguration = rootController.currentScreen.collectAsState() - navConfiguration.value.let { config -> + navConfiguration.value?.let { config -> NavigatorAnimated(config.screen, config, rootController, backgroundColor) } diff --git a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RootComposeBuilder + DSL.kt b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RootComposeBuilder + DSL.kt index 84b8f677..0d5f62e2 100644 --- a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RootComposeBuilder + DSL.kt +++ b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RootComposeBuilder + DSL.kt @@ -81,6 +81,24 @@ fun RootComposeBuilder.topNavigation( ) } +/** + * Adds custom bar navigation to navigation graph + * @param name - name in graph + * @param tabsNavModel - UI configuration for custom navigation + * @param builder - dsl for custom nav bar building + */ +fun RootComposeBuilder.customNavigation( + name: String, + tabsNavModel: TabsNavModel, + builder: MultiStackBuilder.() -> Unit +) { + addMultiStack( + key = name, + tabsNavModel = tabsNavModel, + multiStackBuilderModel = MultiStackBuilder(name).apply(builder).build() + ) +} + /** * Adds tab to bottom nav bar building * @param tabItem - tab UI configuration diff --git a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/navigation/modal_navigation/views/BottomSheetModalView.kt b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/navigation/modal_navigation/views/BottomSheetModalView.kt index 000d68ff..085b9718 100644 --- a/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/navigation/modal_navigation/views/BottomSheetModalView.kt +++ b/odyssey/odyssey-compose/src/commonMain/kotlin/ru/alexgladkov/odyssey/compose/navigation/modal_navigation/views/BottomSheetModalView.kt @@ -55,6 +55,7 @@ internal fun BoxScope.BottomModalSheet( when (bundle.dialogState) { ModalDialogState.Idle -> modalController.setTopDialogState(ModalDialogState.Open, bundle.key) is ModalDialogState.Close -> modalController.finishCloseAction(bundle.key) + ModalDialogState.Open -> {} } } ) diff --git a/odyssey/odyssey-compose/src/desktopMain/kotlin/ru/alexgladkov/odyssey/compose/DesktopScreenHost.kt b/odyssey/odyssey-compose/src/desktopMain/kotlin/ru/alexgladkov/odyssey/compose/DesktopScreenHost.kt deleted file mode 100644 index 4955d1b8..00000000 --- a/odyssey/odyssey-compose/src/desktopMain/kotlin/ru/alexgladkov/odyssey/compose/DesktopScreenHost.kt +++ /dev/null @@ -1,35 +0,0 @@ -package ru.alexgladkov.odyssey.compose - -import androidx.compose.ui.awt.ComposePanel -import ru.alexgladkov.odyssey.compose.extensions.observeAsState -import ru.alexgladkov.odyssey.core.extensions.wrap -import java.awt.BorderLayout -import javax.swing.JFrame -import javax.swing.WindowConstants - -/** - * Class provider to set canvas to draw navigation - * @param window - root window of main function in Swing Application - */ -//class DesktopScreenHost constructor( -// private val window: JFrame, -//) : ComposableScreenHost() { -// -// override fun prepareFowDrawing() { -// val composePanel = ComposePanel() -// -// // Below function setup drawing, you can extend it -// // by adding CompositionLocalProviders or something else -// composePanel.setContent { -// val destinationState = destinationObserver.wrap().observeAsState() -// destinationState.value?.let { -// launchScreen(it) -// } -// } -// -// window.defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE -// window.contentPane.add(composePanel, BorderLayout.CENTER) -// window.setLocationRelativeTo(null) -// window.isVisible = true -// } -//} \ No newline at end of file diff --git a/odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RandomKey.kt b/odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RandomKey.kt new file mode 100644 index 00000000..0d764a7b --- /dev/null +++ b/odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/extensions/RandomKey.kt @@ -0,0 +1,5 @@ +package ru.alexgladkov.odyssey.compose.extensions + +import platform.Foundation.NSUUID + +actual fun createUniqueKey(key: String): String = "$key${NSUUID.UUID().UUIDString}" \ No newline at end of file diff --git a/odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/helpers/WindowSize.kt b/odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/helpers/WindowSize.kt new file mode 100644 index 00000000..e6d6325e --- /dev/null +++ b/odyssey/odyssey-compose/src/uikitMain/kotlin/ru/alexgladkov/odyssey/compose/helpers/WindowSize.kt @@ -0,0 +1,11 @@ +package ru.alexgladkov.odyssey.compose.helpers + +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalDensity +import platform.UIKit.UIScreen + +@Composable +actual fun extractWindowHeight(): Int { + val density = LocalDensity.current + return with(density) { UIScreen.mainScreen.bounds.size } +} \ No newline at end of file diff --git a/odyssey/odyssey-core/build.gradle.kts b/odyssey/odyssey-core/build.gradle.kts index 44b3526e..5845f182 100644 --- a/odyssey/odyssey-core/build.gradle.kts +++ b/odyssey/odyssey-core/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.compose.compose plugins { - id("multiplatform-setup") + id("multiplatform-compose-setup") id("android-setup") id("maven-publish") id("convention.publication") diff --git a/run-uikit.sh b/run-uikit.sh new file mode 100755 index 00000000..c1ee1afa --- /dev/null +++ b/run-uikit.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +cd "$(dirname "$0")" +#./gradlew uikit:linkDebugExecutableUikitX64 -i +./gradlew uikit:iosDeployIPhone13Debug -i \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 8795bf6b..b9a02b8b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,13 +1,12 @@ include( - ":common:common-root", - - ":common:common-compose", + ":common:common-sample", ":odyssey:odyssey-core", ":odyssey:odyssey-compose", ":android", - ":desktop" + ":desktop", + ":uikit" ) //includeBuild("convention-plugins") diff --git a/uikit/build.gradle.kts b/uikit/build.gradle.kts new file mode 100644 index 00000000..32330c10 --- /dev/null +++ b/uikit/build.gradle.kts @@ -0,0 +1,92 @@ +import org.gradle.internal.impldep.org.codehaus.plexus.util.Os +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 { + kotlin("multiplatform")// version "1.6.21" + id("org.jetbrains.compose")// version "1.2.0-alpha01-dev686" +} + +kotlin { + 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" + ) + } + } + } + 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" + ) + // TODO: the current compose binary surprises LLVM, so disable checks for now. + freeCompilerArgs += "-Xdisable-phases=VerifyBitcode" + } + } + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":common:common-sample")) + implementation(project(":odyssey:odyssey-compose")) + implementation(project(":odyssey:odyssey-core")) + implementation(compose.ui) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.runtime) + } + } + + val uikitMain by creating { + dependsOn(commonMain) + } + val uikitX64Main by getting { + dependsOn(uikitMain) + } + val uikitArm64Main by getting { + dependsOn(uikitMain) + } + } +} + +compose.experimental { + uikit.application { + bundleIdPrefix = "ru.alexgladkov" + projectName = "OdysseyiOS" + deployConfigurations { + // Usage ./gradlew uikit:iosDeployIPhone13Debug + simulator("IPhone13") { + device = IOSDevices.IPHONE_13_PRO + } + } + } +} + +tasks.withType { + kotlinOptions.jvmTarget = "11" +} + +kotlin { + targets.withType { + binaries.all { + // TODO: the current compose binary surprises LLVM, so disable checks for now. + freeCompilerArgs += "-Xdisable-phases=VerifyBitcode" + } + } +} \ No newline at end of file diff --git a/uikit/src/commonMain/kotlin/UikitApplication.kt b/uikit/src/commonMain/kotlin/UikitApplication.kt new file mode 100644 index 00000000..42604cfa --- /dev/null +++ b/uikit/src/commonMain/kotlin/UikitApplication.kt @@ -0,0 +1,10 @@ +import androidx.compose.material.Text +import androidx.compose.runtime.Composable + +class UikitApplication { + + @Composable + fun hello() { + Text("Hello") + } +} \ No newline at end of file diff --git a/uikit/src/uikitMain/kotlin/main.uikit.kt b/uikit/src/uikitMain/kotlin/main.uikit.kt new file mode 100644 index 00000000..780f1fc0 --- /dev/null +++ b/uikit/src/uikitMain/kotlin/main.uikit.kt @@ -0,0 +1,73 @@ +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Application +import kotlinx.cinterop.* +import platform.Foundation.NSStringFromClass +import platform.UIKit.* +import ru.alexgladkov.common.compose.NavigationTree +import ru.alexgladkov.common.compose.navigation.navigationGraph +import ru.alexgladkov.common.compose.theme.Odyssey +import ru.alexgladkov.common.compose.theme.OdysseyTheme +import ru.alexgladkov.odyssey.compose.base.Navigator +import ru.alexgladkov.odyssey.compose.local.LocalRootController +import ru.alexgladkov.odyssey.compose.navigation.RootComposeBuilder +import ru.alexgladkov.odyssey.compose.navigation.modal_navigation.ModalNavigator + +fun main() { + val args = emptyArray() + memScoped { + val argc = args.size + 1 + val argv = (arrayOf("skikoApp") + args).map { it.cstr.ptr }.toCValues() + autoreleasepool { + UIApplicationMain(argc, argv, null, NSStringFromClass(SkikoAppDelegate)) + } + } +} + +class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol { + companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta + + @ObjCObjectBase.OverrideInit + constructor() : super() + + private var _window: UIWindow? = null + override fun window() = _window + override fun setWindow(window: UIWindow?) { + _window = window + } + + override fun application( + application: UIApplication, + didFinishLaunchingWithOptions: Map? + ): Boolean { + window = UIWindow(frame = UIScreen.mainScreen.bounds) + window!!.rootViewController = Application("Odyssey Demo") { + OdysseyTheme { + Column(modifier = Modifier.background(Odyssey.color.primaryBackground)) { + // To skip upper part of screen. + Box( + modifier = Modifier.height(56.dp) + ) + + val rootController = RootComposeBuilder().apply { navigationGraph() }.build() + rootController.backgroundColor = Odyssey.color.primaryBackground + + CompositionLocalProvider( + LocalRootController provides rootController + ) { + ModalNavigator { + Navigator(NavigationTree.Actions.name) + } + } + } + } + } + window!!.makeKeyAndVisible() + return true + } +}