From 6c06909cf34c9cb31d1c3e279f122b50e0b540ea Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Mon, 26 Aug 2024 18:23:23 +0200 Subject: [PATCH 1/7] chore: update compose to 1.7.0-alpha03 --- gradle/libs.versions.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8d6c39c..2ce4f39 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,9 +2,9 @@ androidx-activity = "1.9.1" androidx-core = "1.13.1" compose = "1.7.0-alpha02" -composeDetektPlugin = "1.3.0" -coroutines = "1.9.0-RC" -datetime = "0.6.0" +composeDetektPlugin = "1.4.0" +coroutines = "1.9.0-RC.2" +datetime = "0.6.1" dependencyAnalysisPlugin = "1.32.0" detekt = "1.23.6" dokka = "1.9.20" @@ -13,7 +13,7 @@ gradleDoctorPlugin = "0.10.0" junit = "4.13.2" kotest = "5.9.1" # @pin -kotlin = "2.0.10" +kotlin = "2.0.20" kotlinx-atomicfu = "0.25.0" lifecycle = "2.8.0" maven-publish-plugin = "0.29.0" From fc943456416099892764a002533e4f6a02e86306 Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Mon, 26 Aug 2024 18:23:43 +0200 Subject: [PATCH 2/7] chore: bump version --- buildSrc/src/main/kotlin/Config.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index 83feaf5..ca4648f 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -18,7 +18,7 @@ object Config { const val majorRelease = 1 const val minorRelease = 4 - const val patch = 3 + const val patch = 4 const val postfix = "" const val versionName = "$majorRelease.$minorRelease.$patch$postfix" From b599d4e4d19e82dfe5ac1e51d686323569369f6f Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Mon, 26 Aug 2024 18:25:03 +0200 Subject: [PATCH 3/7] fix: trim string resources by default --- .../kmmutils/compose/resources/ResourcesExt.kt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/resources/ResourcesExt.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/resources/ResourcesExt.kt index ae568eb..48c3337 100644 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/resources/ResourcesExt.kt +++ b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/resources/ResourcesExt.kt @@ -1,3 +1,5 @@ +@file:Suppress("TooManyFunctions") + package pro.respawn.kmmutils.compose.resources import androidx.compose.runtime.Composable @@ -39,7 +41,10 @@ public fun StringArrayResource.strings(): List = stringArrayResource(thi @Composable @NonSkippableComposable -public fun StringResource.string(vararg args: Any): String = stringResource(this, formatArgs = args) +public fun StringResource.string( + vararg args: Any, + trim: Boolean = true +): String = stringResource(this, formatArgs = args).let { if (trim) it.trim() else it } @Composable @NonSkippableComposable @@ -64,3 +69,8 @@ public fun FontResource.font( weight: FontWeight = FontWeight.Normal, style: FontStyle = FontStyle.Normal ): Font = Font(this, weight, style) + +public suspend fun StringResource.getString( + vararg args: Any, + trim: Boolean = true +): String = org.jetbrains.compose.resources.getString(this, formatArgs = args).let { if (trim) it.trim() else it } From 561ecc1d483b09ab9745da6270aebdbc9982e0a3 Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Tue, 27 Aug 2024 12:14:28 +0200 Subject: [PATCH 4/7] fix: fix deprecations --- build.gradle.kts | 6 +++--- buildSrc/src/main/kotlin/Config.kt | 2 +- buildSrc/src/main/kotlin/ConfigureMultiplatform.kt | 8 ++++++-- compose/build.gradle.kts | 1 + .../pro/respawn/kmmutils/compose/ScreenModifiers.kt | 2 +- gradle/libs.versions.toml | 1 + 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8fa3f19..365aac2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ import nl.littlerobots.vcu.plugin.versionCatalogUpdate import nl.littlerobots.vcu.plugin.versionSelector import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradlePluginExtension import org.jetbrains.kotlin.compose.compiler.gradle.ComposeCompilerGradleSubplugin +import org.jetbrains.kotlin.compose.compiler.gradle.ComposeFeatureFlag.Companion.OptimizeNonSkippingGroups import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnLockMismatchReport import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension @@ -37,6 +38,7 @@ buildscript { allprojects { group = Config.artifactId version = Config.versionName + tasks.withType().configureEach { compilerOptions { jvmTarget.set(Config.jvmTarget) @@ -77,9 +79,7 @@ subprojects { } plugins.withType().configureEach { the().apply { - enableIntrinsicRemember = true - enableNonSkippingGroupOptimization = true - enableStrongSkippingMode = true + featureFlags.addAll(OptimizeNonSkippingGroups) stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_definitions.txt") if (properties["enableComposeCompilerReports"] == "true") { val metricsDir = layout.buildDirectory.dir("compose_metrics") diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index ca4648f..6455bc3 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -43,9 +43,9 @@ object Config { ) val compilerArgs = listOf( "-Xbackend-threads=0", // parallel IR compilation + "-Xconsistent-data-class-copy-visibility", ) val jvmCompilerArgs = buildList { - addAll(compilerArgs) add("-Xjvm-default=all") // enable all jvm optimizations add("-Xstring-concat=inline") addAll(optIns.map { "-opt-in=$it" }) diff --git a/buildSrc/src/main/kotlin/ConfigureMultiplatform.kt b/buildSrc/src/main/kotlin/ConfigureMultiplatform.kt index f4eb6ff..f7c01f8 100644 --- a/buildSrc/src/main/kotlin/ConfigureMultiplatform.kt +++ b/buildSrc/src/main/kotlin/ConfigureMultiplatform.kt @@ -4,9 +4,9 @@ import org.gradle.api.Project import org.gradle.kotlin.dsl.getValue import org.gradle.kotlin.dsl.getting import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyBuilder -import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl @OptIn(ExperimentalWasmDsl::class, ExperimentalKotlinGradlePluginApi::class) fun Project.configureMultiplatform( @@ -29,6 +29,11 @@ fun Project.configureMultiplatform( applyDefaultHierarchyTemplate(configure) withSourcesJar(true) + compilerOptions { + freeCompilerArgs.addAll(Config.compilerArgs) + optIn.addAll(Config.optIns) + } + if (linux) { linuxX64() linuxArm64() @@ -91,7 +96,6 @@ fun Project.configureMultiplatform( all { languageSettings { progressiveMode = true - Config.optIns.forEach { optIn(it) } } } } diff --git a/compose/build.gradle.kts b/compose/build.gradle.kts index 8229369..1a02359 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -44,6 +44,7 @@ kotlin { api(compose.components.resources) api(libs.lifecycle.runtime) + api(libs.lifecycle.compose) implementation(compose.runtime) implementation(compose.foundation) implementation(compose.animationGraphics) diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/ScreenModifiers.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/ScreenModifiers.kt index 946c8c0..676a55b 100644 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/ScreenModifiers.kt +++ b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/ScreenModifiers.kt @@ -4,9 +4,9 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.compose.LocalLifecycleOwner /** * Registers a new lifecycle observer for the lifetime of the composition of this function, then clears it. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ce4f39..1eb3e85 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -45,6 +45,7 @@ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-c kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } lifecycle-runtime = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime", version.ref = "lifecycle" } lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycle" } +lifecycle-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } [bundles] From 9bb9cce6b2a8ab451d266855b4047d58ce570018 Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Tue, 27 Aug 2024 12:32:47 +0200 Subject: [PATCH 5/7] chore: bump compose to 1.7 alpha 03 --- gradle/libs.versions.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1eb3e85..1869a3a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] androidx-activity = "1.9.1" androidx-core = "1.13.1" -compose = "1.7.0-alpha02" +compose = "1.7.0-alpha03" composeDetektPlugin = "1.4.0" coroutines = "1.9.0-RC.2" datetime = "0.6.1" @@ -46,6 +46,7 @@ kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version. lifecycle-runtime = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime", version.ref = "lifecycle" } lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycle" } lifecycle-compose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } +compose-window-size = { module = "org.jetbrains.compose.material3:material3-window-size-class", version.ref = "compose" } turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } [bundles] From 1bbb48aa1b57c15225f2cc0761340f5e373a59d8 Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Tue, 27 Aug 2024 12:33:43 +0200 Subject: [PATCH 6/7] feat: use official window size library --- .../kmmutils__dokkaHtml_.xml | 23 -- ...shAllPublicationsToSonatypeRepository_.xml | 24 -- buildSrc/src/main/kotlin/Config.kt | 2 +- compose/build.gradle.kts | 1 + .../respawn/kmmutils/compose/Annotations.kt | 17 +- .../pro/respawn/kmmutils/compose/Utils.kt | 1 - .../compose/windowsize/WindowSizeClass.kt | 297 ------------------ .../compose/windowsize/WindowSizeExt.kt | 16 + 8 files changed, 26 insertions(+), 355 deletions(-) delete mode 100644 .idea/runConfigurations/kmmutils__dokkaHtml_.xml delete mode 100644 .idea/runConfigurations/kmmutils__publishAllPublicationsToSonatypeRepository_.xml delete mode 100644 compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Utils.kt delete mode 100644 compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeClass.kt diff --git a/.idea/runConfigurations/kmmutils__dokkaHtml_.xml b/.idea/runConfigurations/kmmutils__dokkaHtml_.xml deleted file mode 100644 index cfb3025..0000000 --- a/.idea/runConfigurations/kmmutils__dokkaHtml_.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - true - true - false - - - \ No newline at end of file diff --git a/.idea/runConfigurations/kmmutils__publishAllPublicationsToSonatypeRepository_.xml b/.idea/runConfigurations/kmmutils__publishAllPublicationsToSonatypeRepository_.xml deleted file mode 100644 index 8f18692..0000000 --- a/.idea/runConfigurations/kmmutils__publishAllPublicationsToSonatypeRepository_.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - true - true - false - false - - - \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index 6455bc3..1e0ca07 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -42,12 +42,12 @@ object Config { "kotlin.contracts.ExperimentalContracts" ) val compilerArgs = listOf( - "-Xbackend-threads=0", // parallel IR compilation "-Xconsistent-data-class-copy-visibility", ) val jvmCompilerArgs = buildList { add("-Xjvm-default=all") // enable all jvm optimizations add("-Xstring-concat=inline") + add("-Xbackend-threads=0") // parallel IR compilation addAll(optIns.map { "-opt-in=$it" }) } diff --git a/compose/build.gradle.kts b/compose/build.gradle.kts index 1a02359..4d70443 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -45,6 +45,7 @@ kotlin { api(libs.lifecycle.runtime) api(libs.lifecycle.compose) + implementation(libs.compose.window.size) implementation(compose.runtime) implementation(compose.foundation) implementation(compose.animationGraphics) diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt index ac39d53..a76c57c 100644 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt +++ b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt @@ -4,6 +4,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontFamily @@ -129,12 +130,10 @@ public fun String.shadow( */ public fun String.font(fontFamily: FontFamily): AnnotatedString = annotate(SpanStyle(fontFamily = fontFamily)) -// TODO: Waiting for compose update - -// public fun String.clickable(onClick: () -> Unit): AnnotatedString = annotate { -// pushLink(LinkAnnotation.Clickable("clickable") { onClick() }) -// pushStyle(SpanStyle(textDecoration = TextDecoration.Underline)) -// append(this@clickable) -// pop() -// pop() -// } +public fun String.clickable(onClick: () -> Unit): AnnotatedString = annotate { + pushLink(LinkAnnotation.Clickable("clickable") { onClick() }) + pushStyle(SpanStyle(textDecoration = TextDecoration.Underline)) + append(this@clickable) + pop() + pop() +} diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Utils.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Utils.kt deleted file mode 100644 index 65a80e5..0000000 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Utils.kt +++ /dev/null @@ -1 +0,0 @@ -package pro.respawn.kmmutils.compose diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeClass.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeClass.kt deleted file mode 100644 index ad79a6b..0000000 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeClass.kt +++ /dev/null @@ -1,297 +0,0 @@ -package pro.respawn.kmmutils.compose.windowsize - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.runtime.ReadOnlyComposable -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpSize -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.toSize - -/** - * Calculates the window's [WindowSizeClass]. - * - * A new [WindowSizeClass] will be returned whenever a change causes the width or - * height of the window to cross a breakpoint, such as when the device is rotated or the window - * is resized. - */ -@Composable -@ReadOnlyComposable -public fun calculateWindowSizeClass(): WindowSizeClass = WindowSizeClass.calculateFromSize( - size = windowSizePx.toSize(), - density = LocalDensity.current -) - -/** - * Window size classes are a set of opinionated viewport breakpoints to design, develop, and test - * responsive application layouts against. - * For more details check Support different screen sizes documentation. - * - * WindowSizeClass contains a [WindowWidthSizeClass] and [WindowHeightSizeClass], representing the - * window size classes for this window's width and height respectively. - * - * See [calculateWindowSizeClass] to calculate the WindowSizeClass. - * - * @property widthSizeClass width-based window size class ([WindowWidthSizeClass]) - * @property heightSizeClass height-based window size class ([WindowHeightSizeClass]) - */ -@Immutable -public class WindowSizeClass private constructor( - public val widthSizeClass: WindowWidthSizeClass, - public val heightSizeClass: WindowHeightSizeClass, -) { - - public companion object { - - internal fun calculateFromSize(size: DpSize): WindowSizeClass { - val windowWidthSizeClass = WindowWidthSizeClass.fromWidth(size.width) - val windowHeightSizeClass = WindowHeightSizeClass.fromHeight(size.height) - return WindowSizeClass(windowWidthSizeClass, windowHeightSizeClass) - } - - /** - * Calculates the best matched [WindowSizeClass] for a given [size] and [Density] according - * to the provided [supportedWidthSizeClasses] and [supportedHeightSizeClasses]. - * - * @param size of the window - * @param density of the window - * @param supportedWidthSizeClasses the set of width size classes that are supported - * @param supportedHeightSizeClasses the set of height size classes that are supported - * @return [WindowSizeClass] corresponding to the given width and height - */ - public fun calculateFromSize( - size: Size, - density: Density, - supportedWidthSizeClasses: Set = - WindowWidthSizeClass.DefaultSizeClasses, - supportedHeightSizeClasses: Set = - WindowHeightSizeClass.DefaultSizeClasses, - ): WindowSizeClass { - val windowWidthSizeClass = - WindowWidthSizeClass.fromWidth(size.width, density, supportedWidthSizeClasses) - val windowHeightSizeClass = - WindowHeightSizeClass.fromHeight(size.height, density, supportedHeightSizeClasses) - return WindowSizeClass(windowWidthSizeClass, windowHeightSizeClass) - } - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as WindowSizeClass - - if (widthSizeClass != other.widthSizeClass) return false - if (heightSizeClass != other.heightSizeClass) return false - - return true - } - - override fun hashCode(): Int { - var result = widthSizeClass.hashCode() - result = 31 * result + heightSizeClass.hashCode() - return result - } - - override fun toString(): String = "WindowSizeClass($widthSizeClass, $heightSizeClass)" -} - -/** - * Width-based window size class. - * - * A window size class represents a breakpoint that can be used to build responsive layouts. Each - * window size class breakpoint represents a majority case for typical device scenarios so your - * layouts will work well on most devices and configurations. - * - * For more details see Window size classes documentation. - */ -@Immutable -@kotlin.jvm.JvmInline -public value class WindowWidthSizeClass private constructor(private val value: Int) : - Comparable { - - override operator fun compareTo(other: WindowWidthSizeClass): Int = - breakpoint().compareTo(other.breakpoint()) - - override fun toString(): String = "WindowWidthSizeClass." + when (this) { - Compact -> "Compact" - Medium -> "Medium" - Expanded -> "Expanded" - else -> "" - } - - public companion object { - - /** Represents the majority of phones in portrait. */ - public val Compact: WindowWidthSizeClass = WindowWidthSizeClass(0) - - /** - * Represents the majority of tablets in portrait and large unfolded inner displays in - * portrait. - */ - public val Medium: WindowWidthSizeClass = WindowWidthSizeClass(1) - - /** - * Represents the majority of tablets in landscape and large unfolded inner displays in - * landscape. - */ - public val Expanded: WindowWidthSizeClass = WindowWidthSizeClass(2) - - /** - * The default set of size classes that includes [Compact], [Medium], and [Expanded] size - * classes. Should never expand to ensure behavioral consistency. - */ - public val DefaultSizeClasses: Set = setOf(Compact, Medium, Expanded) - - /** - * The standard set of size classes. It's supposed to include all size classes and will be - * expanded whenever a new size class is defined. By default - * [WindowSizeClass.calculateFromSize] will only return size classes in [DefaultSizeClasses] - * in order to avoid behavioral changes when new size classes are added. You can opt in to - * support all available size classes by doing: - * ``` - * WindowSizeClass.calculateFromSize( - * size = size, - * density = density, - * supportedWidthSizeClasses = WindowWidthSizeClass.StandardSizeClasses, - * supportedHeightSizeClasses = WindowHeightSizeClass.StandardSizeClasses - * ) - * ``` - */ - public val StandardSizeClasses: Set get() = DefaultSizeClasses - - private fun WindowWidthSizeClass.breakpoint(): Dp = when { - this == Expanded -> 840.dp - this == Medium -> 600.dp - else -> 0.dp - } - - /** Calculates the [WindowWidthSizeClass] for a given [width] */ - internal fun fromWidth(width: Dp): WindowWidthSizeClass = fromWidth( - with(defaultDensity) { width.toPx() }, - defaultDensity, - DefaultSizeClasses, - ) - - /** - * Calculates the best matched [WindowWidthSizeClass] for a given [width] in Pixels and - * a given [Density] from [supportedSizeClasses]. - */ - internal fun fromWidth( - width: Float, - density: Density, - supportedSizeClasses: Set, - ): WindowWidthSizeClass { - require(width >= 0) { "Width must not be negative" } - require(supportedSizeClasses.isNotEmpty()) { "Must support at least one size class" } - val sortedSizeClasses = supportedSizeClasses.sortedDescending() - // Find the largest supported size class that matches the width - sortedSizeClasses.forEach { - if (width >= with(density) { it.breakpoint().toPx() }) { - return it - } - } - // If none of the size classes matches, return the smallest one. - return sortedSizeClasses.last() - } - } -} - -/** - * Height-based window size class. - * - * A window size class represents a breakpoint that can be used to build responsive layouts. Each - * window size class breakpoint represents a majority case for typical device scenarios so your - * layouts will work well on most devices and configurations. - * - * For more details see Window size classes documentation. - */ -@Immutable -@kotlin.jvm.JvmInline -public value class WindowHeightSizeClass private constructor(private val value: Int) : - Comparable { - - override operator fun compareTo(other: WindowHeightSizeClass): Int = breakpoint().compareTo(other.breakpoint()) - - override fun toString(): String = "WindowHeightSizeClass." + when (this) { - Compact -> "Compact" - Medium -> "Medium" - Expanded -> "Expanded" - else -> "" - } - - public companion object { - - /** Represents the majority of phones in landscape */ - public val Compact: WindowHeightSizeClass = WindowHeightSizeClass(0) - - /** Represents the majority of tablets in landscape and majority of phones in portrait */ - public val Medium: WindowHeightSizeClass = WindowHeightSizeClass(1) - - /** Represents the majority of tablets in portrait */ - public val Expanded: WindowHeightSizeClass = WindowHeightSizeClass(2) - - /** - * The default set of size classes that includes [Compact], [Medium], and [Expanded] size - * classes. Should never expand to ensure behavioral consistency. - */ - public val DefaultSizeClasses: Set = setOf(Compact, Medium, Expanded) - - /** - * The standard set of size classes. It's supposed to include all size classes and will be - * expanded whenever a new size class is defined. By default - * [WindowSizeClass.calculateFromSize] will only return size classes in [DefaultSizeClasses] - * in order to avoid behavioral changes when new size classes are added. You can opt in to - * support all available size classes by doing: - * ``` - * WindowSizeClass.calculateFromSize( - * size = size, - * density = density, - * supportedWidthSizeClasses = WindowWidthSizeClass.StandardSizeClasses, - * supportedHeightSizeClasses = WindowHeightSizeClass.StandardSizeClasses - * ) - * ``` - */ - public val StandardSizeClasses: Set get() = DefaultSizeClasses - - private fun WindowHeightSizeClass.breakpoint(): Dp = when { - this == Expanded -> 900.dp - this == Medium -> 480.dp - else -> 0.dp - } - - /** Calculates the [WindowHeightSizeClass] for a given [height] */ - internal fun fromHeight(height: Dp) = fromHeight( - with(defaultDensity) { height.toPx() }, - defaultDensity, - DefaultSizeClasses, - ) - - /** - * Calculates the best matched [WindowHeightSizeClass] for a given [height] in Pixels and - * a given [Density] from [supportedSizeClasses]. - */ - internal fun fromHeight( - height: Float, - density: Density, - supportedSizeClasses: Set, - ): WindowHeightSizeClass { - require(height >= 0) { "Width must not be negative" } - require(supportedSizeClasses.isNotEmpty()) { "Must support at least one size class" } - val sortedSizeClasses = supportedSizeClasses.sortedDescending() - // Find the largest supported size class that matches the width - sortedSizeClasses.forEach { - if (height >= with(density) { it.breakpoint().toPx() }) { - return it - } - } - // If none of the size classes matches, return the smallest one. - return sortedSizeClasses.last() - } - } -} - -private val defaultDensity = Density(1F, 1F) diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeExt.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeExt.kt index 56d561d..54d193a 100644 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeExt.kt +++ b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/windowsize/WindowSizeExt.kt @@ -1,5 +1,9 @@ package pro.respawn.kmmutils.compose.windowsize +import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi +import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.platform.LocalDensity @@ -8,6 +12,18 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.toSize +/** + * Calculates the window's [WindowSizeClass]. + * + * A new [WindowSizeClass] will be returned whenever a change causes the width or + * height of the window to cross a breakpoint, such as when the device is rotated or the window + * is resized. + */ +@OptIn(ExperimentalMaterial3WindowSizeClassApi::class) +@Composable +@ReadOnlyComposable +public fun calculateWindowSizeClass(): WindowSizeClass = WindowSizeClass.calculateFromSize(size = windowSize) + /** * Whether the window width is long (longer than most phones **in portrait**). * From 56e6827d6077b841c96fb2a74d134158bb3902bc Mon Sep 17 00:00:00 2001 From: Nek-12 Date: Wed, 28 Aug 2024 23:08:47 +0200 Subject: [PATCH 7/7] fix lint --- .../kotlin/pro/respawn/kmmutils/compose/Annotations.kt | 5 +++++ .../kotlin/pro/respawn/kmmutils/compose/TypeCrossfade.kt | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt index a76c57c..bb0cfd5 100644 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt +++ b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/Annotations.kt @@ -130,6 +130,11 @@ public fun String.shadow( */ public fun String.font(fontFamily: FontFamily): AnnotatedString = annotate(SpanStyle(fontFamily = fontFamily)) +/** + * Makes this string clickable. When clicked, the [onClick] lambda is invoked + * + * @return the [AnnotatedString] created + */ public fun String.clickable(onClick: () -> Unit): AnnotatedString = annotate { pushLink(LinkAnnotation.Clickable("clickable") { onClick() }) pushStyle(SpanStyle(textDecoration = TextDecoration.Underline)) diff --git a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/TypeCrossfade.kt b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/TypeCrossfade.kt index 55b80bb..7530cb0 100644 --- a/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/TypeCrossfade.kt +++ b/compose/src/commonMain/kotlin/pro/respawn/kmmutils/compose/TypeCrossfade.kt @@ -53,7 +53,7 @@ public inline fun TypeCrossfade( @PublishedApi internal fun Transition.Crossfade( modifier: Modifier = Modifier, - contentAlignment: Alignment, + contentAlignment: Alignment = Alignment.Center, animationSpec: FiniteAnimationSpec = tween(), contentKey: (targetState: T) -> Any? = { it }, content: @Composable BoxScope.(targetState: T) -> Unit