Improve stable/static analysis
Change-Id: Id201bd133d3fc0628b827f1118f9bf29e0ed7505
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt
index ad83f29..9c2778c 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/DefaultParamTransformTests.kt
@@ -93,20 +93,17 @@
fun Example(foo: Foo, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) {
%composer.startRestartGroup(%key)
val %dirty = %changed
- val foo = foo
- if (%changed and 0b0110 === 0) {
- %dirty = %dirty or if (%default and 0b0001 === 0 && %composer.changed(foo)) 0b0100 else 0b0010
+ val foo = if (%default and 0b0001 !== 0) {
+ Foo(0)
+ } else {
+ foo
+ }
+ if (%default and 0b0001 !== 0) {
+ %dirty = %dirty or 0b0110
+ } else if (%changed and 0b0110 === 0) {
+ %dirty = %dirty or if (%composer.changed(foo)) 0b0100 else 0b0010
}
if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
- if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) {
- %composer.startDefaults()
- if (%default and 0b0001 !== 0) {
- foo = Foo(0)
- }
- %composer.endDefaults()
- } else {
- %composer.skipCurrentGroup()
- }
print(foo)
} else {
%composer.skipToGroupEnd()
diff --git a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt
index acc6bdb..1003027 100644
--- a/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt
+++ b/compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/FunctionBodySkippingTransformTests.kt
@@ -1260,12 +1260,24 @@
interface Modifier {
companion object : Modifier { }
}
+ inline class Dp(val value: Int)
+ @Stable
+ fun stableFun(x: Int): Int = x * x
+ @Stable
+ operator fun Dp.plus(other: Dp): Dp = Dp(this.value + other.value)
+ @Stable
+ val Int.dp: Dp get() = Dp(this)
""",
"""
// all of these should result in 0b0110
@Composable fun A() {
val x = 123
+ C(stableFun(123))
+ C(16.dp + 10.dp)
+ C(Dp(16))
+ C(16.dp)
C(normInt)
+ C(Int.MAX_VALUE)
C(stableTopLevelProp)
C(Modifier)
C(Foo.Bar)
@@ -1287,7 +1299,12 @@
%composer.startRestartGroup(%key)
if (%changed !== 0 || !%composer.skipping) {
val x = 123
+ C(stableFun(123), %composer, <>, 0b0110)
+ C(16.dp + 10.dp, %composer, <>, 0b0110)
+ C(Dp(16), %composer, <>, 0b0110)
+ C(16.dp, %composer, <>, 0b0110)
C(normInt, %composer, <>, 0b0110)
+ C(Companion.MAX_VALUE, %composer, <>, 0b0110)
C(stableTopLevelProp, %composer, <>, 0b0110)
C(Companion, %composer, <>, 0b0110)
C(Foo.Bar, %composer, <>, 0b0110)
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
index 38ba7a6..ab94fe0 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/ComposeFqNames.kt
@@ -38,6 +38,7 @@
val Direct = ComposeUtils.composeFqName("Direct")
val key = ComposeUtils.composeFqName("key")
val StableMarker = ComposeUtils.composeFqName("StableMarker")
+ val Stable = ComposeUtils.composeFqName("Stable")
val Composer = ComposeUtils.composeFqName("Composer")
val Untracked = ComposeUtils.composeFqName("Untracked")
val UiComposer = FqName.fromSegments(listOf("androidx", "ui", "node", "UiComposer"))
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt
index 5529993..36c352b 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/AbstractComposeLowering.kt
@@ -26,6 +26,7 @@
import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
+import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.extractParameterNameFromFunctionTypeArgument
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
@@ -96,12 +97,16 @@
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
+import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.defaultType
+import org.jetbrains.kotlin.ir.types.isNullable
+import org.jetbrains.kotlin.ir.types.isPrimitiveType
+import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.ConstantValueGenerator
import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
@@ -125,6 +130,7 @@
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
+import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
abstract class AbstractComposeLowering(
val context: IrPluginContext,
@@ -197,10 +203,38 @@
fun KotlinType.toIrType(): IrType = typeTranslator.translateType(this)
+ fun IrType.unboxInlineClass() = unboxType() ?: this
+
+ fun <T : IrSymbol> T.bindIfNecessary(): T {
+ if (!isBound) {
+ context.irProviders.firstNotNullResult { it.getDeclaration(this) }
+ }
+ return this
+ }
+
+ // NOTE(lmr): This implementation mimics the kotlin-provided unboxInlineClass method, except
+ // this one makes sure to bind the symbol if it is unbound, so is a bit safer to use.
+ fun IrType.unboxType(): IrType? {
+ val classSymbol = classOrNull ?: return null
+ val klass = classSymbol.bindIfNecessary().owner
+ if (!klass.isInline) return null
+
+ // TODO: Apply type substitutions
+ val underlyingType = InlineClassAbi.getUnderlyingType(klass).unboxInlineClass()
+ if (!isNullable()) return underlyingType
+ if (underlyingType.isNullable() || underlyingType.isPrimitiveType())
+ return null
+ return underlyingType.makeNullable()
+ }
+
fun IrAnnotationContainer.hasComposableAnnotation(): Boolean {
return annotations.hasAnnotation(ComposeFqNames.Composable)
}
+ fun IrAnnotationContainer.hasStableAnnotation(): Boolean {
+ return annotations.hasAnnotation(ComposeFqNames.Stable)
+ }
+
fun List<IrConstructorCall>.hasAnnotation(fqName: FqName): Boolean =
any { it.symbol.descriptor.constructedClass.fqNameSafe == fqName }
@@ -434,7 +468,7 @@
extensionReceiver: IrExpression? = null,
vararg args: IrExpression
): IrCallImpl {
- context.irProviders.getDeclaration(symbol)
+ symbol.bindIfNecessary()
return IrCallImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt
index 343aa5d..0573b49 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposableFunctionBodyTransformer.kt
@@ -74,6 +74,7 @@
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
+import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrDoWhileLoop
import org.jetbrains.kotlin.ir.expressions.IrElseBranch
import org.jetbrains.kotlin.ir.expressions.IrExpression
@@ -114,8 +115,10 @@
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
+import org.jetbrains.kotlin.ir.util.getArguments
import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.util.hasDefaultValue
+import org.jetbrains.kotlin.ir.util.isInlined
import org.jetbrains.kotlin.ir.util.isVararg
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.util.statements
@@ -2312,6 +2315,18 @@
// Getting a companion object or top level object can be considered static if the
// type of that object is Stable. (`Modifier` for instance is a common example)
is IrGetObjectValue -> symbol.owner.superTypes.any { it.toKotlinType().isStable() }
+ is IrConstructorCall -> {
+ // special case constructors of inline classes as static if their underlying
+ // value is static.
+ if (
+ type.isInlined() &&
+ type.unboxInlineClass().toKotlinType().isStable() &&
+ getValueArgument(0)?.isStatic() == true
+ ) {
+ return true
+ }
+ false
+ }
is IrCall -> when (origin) {
is IrStatementOrigin.GET_PROPERTY -> {
// If we are in a GET_PROPERTY call, then this should usually resolve to
@@ -2322,13 +2337,34 @@
// if the property is a top level constant, then it is static.
if (prop.isConst) return true
+ val typeIsStable = type.toKotlinType().isStable()
+ val dispatchReceiverIsStatic = dispatchReceiver?.isStatic() != false
+ val extensionReceiverIsStatic = extensionReceiver?.isStatic() != false
+
// if we see that the property is read-only with a default getter and a
// stable return type , then reading the property can also be considered
// static if this is a top level property or the subject is also static.
- !prop.isVar &&
+ if (!prop.isVar &&
prop.getter?.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR &&
- type.toKotlinType().isStable() &&
- (dispatchReceiver == null || dispatchReceiver?.isStatic() == true)
+ typeIsStable &&
+ dispatchReceiverIsStatic && extensionReceiverIsStatic
+ ) {
+ return true
+ }
+
+ val getterIsStable = prop.hasStableAnnotation() ||
+ symbol.owner.hasStableAnnotation()
+
+ if (
+ getterIsStable &&
+ typeIsStable &&
+ dispatchReceiverIsStatic &&
+ extensionReceiverIsStatic
+ ) {
+ return true
+ }
+
+ false
}
is IrStatementOrigin.PLUS,
is IrStatementOrigin.MUL,
@@ -2346,9 +2382,32 @@
// special case mathematical operators that are in the stdlib. These are
// immutable operations so the overall result is static if the operands are
// also static
- symbol.descriptor.fqNameSafe.topLevelName() == "kotlin" &&
- dispatchReceiver?.isStatic() == true &&
- getValueArgument(0)?.isStatic() == true
+ val isStableOperator = symbol
+ .descriptor
+ .fqNameSafe
+ .topLevelName() == "kotlin" ||
+ symbol.owner.hasStableAnnotation()
+
+ val typeIsStable = type.toKotlinType().isStable()
+ if (!typeIsStable) return false
+
+ if (!isStableOperator) {
+ return false
+ }
+
+ getArguments().all { it.second.isStatic() }
+ }
+ null -> {
+ // normal function call. If the function is marked as Stable and the result
+ // is Stable, then the static-ness of it is the static-ness of its arguments
+ val isStable = symbol.owner.hasStableAnnotation()
+ if (!isStable) return false
+
+ val typeIsStable = type.toKotlinType().isStable()
+ if (!typeIsStable) return false
+
+ // getArguments includes the receivers!
+ getArguments().all { it.second.isStatic() }
}
else -> false
}
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
index 6d9778da..778f493 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/ComposerParamTransformer.kt
@@ -247,10 +247,7 @@
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET
): IrExpression {
- val classSymbol = classOrNull
- if (classSymbol?.isBound == false) {
- context.irProviders.getDeclaration(classSymbol)
- }
+ val classSymbol = classOrNull?.bindIfNecessary()
if (this !is IrSimpleType || hasQuestionMark || classSymbol?.owner?.isInline != true)
return IrConstImpl.defaultValueForType(startOffset, endOffset, this)
diff --git a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt
index eab1be1..788188f 100644
--- a/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt
+++ b/compose/compose-compiler-hosted/src/main/java/androidx/compose/plugins/kotlin/compiler/lower/IrSourcePrinter.kt
@@ -331,16 +331,16 @@
// unary prefx
"unaryPlus", "unaryMinus", "not" -> {
print(opSymbol)
- expression.dispatchReceiver?.print()
+ (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
}
// unary postfix
"inc", "dec" -> {
- expression.dispatchReceiver?.print()
+ (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
print(opSymbol)
}
// invoke
"invoke" -> {
- expression.dispatchReceiver?.print()
+ (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
expression.printArgumentList()
}
// get indexer
@@ -355,7 +355,7 @@
}
// set indexer
"set" -> {
- expression.dispatchReceiver?.print()
+ (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
print("[")
expression.getValueArgument(0)?.print()
print("] = ")
@@ -368,14 +368,14 @@
expression.getValueArgument(1)?.print()
}
"iterator", "hasNext", "next" -> {
- expression.dispatchReceiver?.print()
+ (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
print(".")
print(opSymbol)
print("()")
}
// else binary
else -> {
- expression.dispatchReceiver?.print()
+ (expression.dispatchReceiver ?: expression.extensionReceiver)?.print()
print(" $opSymbol ")
expression.getValueArgument(0)?.print()
}
diff --git a/compose/compose-runtime/api/0.1.0-dev14.txt b/compose/compose-runtime/api/0.1.0-dev14.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/0.1.0-dev14.txt
+++ b/compose/compose-runtime/api/0.1.0-dev14.txt
@@ -80,7 +80,7 @@
}
public final class ComposeKt {
- method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
@@ -482,7 +482,7 @@
property public final int parentNodes;
}
- @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+ @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
}
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/current.txt b/compose/compose-runtime/api/current.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/current.txt
+++ b/compose/compose-runtime/api/current.txt
@@ -80,7 +80,7 @@
}
public final class ComposeKt {
- method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
@@ -482,7 +482,7 @@
property public final int parentNodes;
}
- @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+ @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
}
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_0.1.0-dev14.txt
@@ -80,7 +80,7 @@
}
public final class ComposeKt {
- method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
@@ -482,7 +482,7 @@
property public final int parentNodes;
}
- @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+ @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
}
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/public_plus_experimental_current.txt b/compose/compose-runtime/api/public_plus_experimental_current.txt
index cfe1f23..4302b1e 100644
--- a/compose/compose-runtime/api/public_plus_experimental_current.txt
+++ b/compose/compose-runtime/api/public_plus_experimental_current.txt
@@ -80,7 +80,7 @@
}
public final class ComposeKt {
- method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
@@ -482,7 +482,7 @@
property public final int parentNodes;
}
- @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+ @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
}
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/restricted_0.1.0-dev14.txt b/compose/compose-runtime/api/restricted_0.1.0-dev14.txt
index fd7a436..9ccc3fe 100644
--- a/compose/compose-runtime/api/restricted_0.1.0-dev14.txt
+++ b/compose/compose-runtime/api/restricted_0.1.0-dev14.txt
@@ -80,7 +80,7 @@
}
public final class ComposeKt {
- method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
@@ -511,7 +511,7 @@
property public final int parentNodes;
}
- @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+ @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
}
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/api/restricted_current.txt b/compose/compose-runtime/api/restricted_current.txt
index fd7a436..9ccc3fe 100644
--- a/compose/compose-runtime/api/restricted_current.txt
+++ b/compose/compose-runtime/api/restricted_current.txt
@@ -80,7 +80,7 @@
}
public final class ComposeKt {
- method public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
+ method @androidx.compose.Stable public static kotlin.jvm.functions.Function0<kotlin.Unit> emptyContent();
method public static inline kotlin.jvm.functions.Function0<kotlin.Unit> orEmpty(kotlin.jvm.functions.Function0<kotlin.Unit>?);
}
@@ -511,7 +511,7 @@
property public final int parentNodes;
}
- @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=AnnotationTarget.CLASS) public @interface Stable {
+ @androidx.compose.StableMarker @kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY}) public @interface Stable {
}
@kotlin.annotation.MustBeDocumented @kotlin.annotation.Retention(AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS}) public @interface StableMarker {
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
index a1a6942..cf01af8 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Compose.kt
@@ -23,6 +23,7 @@
*
* See [orEmpty] for handling nullable Composable lambdas using empty content.
*/
+@Stable
fun emptyContent() = EmptyComposable
/**
diff --git a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt
index 1ea88be..3247a11 100644
--- a/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt
+++ b/compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Stable.kt
@@ -16,8 +16,34 @@
package androidx.compose
+/**
+ * Stable is used to communicate some guarantees to the compose compiler about how a certain type
+ * or function will behave.
+ *
+ * When applied to a class or an interface, [Stable] indicates that the following must be true:
+ *
+ * 1) The result of [equals] will always return the same result for the same two instances.
+ * 2) When a public property of the type changes, composition will be notified.
+ * 3) All public property types are stable.
+ *
+ * When applied to a function or a property, the [Stable] annotation indicates that the function
+ * will return the same result if the same parameters are passed in. This is only meaningful if
+ * the parameters and results are themselves [Stable], [Immutable], or primitive.
+ *
+ * The invariants that this annotation implies are used for optimizations by the compose compiler,
+ * and have undefined behavior if the above assumptions are not met. As a result, one should not
+ * use this annotation unless they are certain that these conditions are satisfied.
+ *
+ * @see Immutable
+ * @see StableMarker
+ */
@MustBeDocumented
-@Target(AnnotationTarget.CLASS)
+@Target(
+ AnnotationTarget.CLASS,
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY
+)
@Retention(AnnotationRetention.BINARY)
@StableMarker
annotation class Stable
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt b/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt
index d455b19..ecb3d4f 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Alignment.kt
@@ -17,6 +17,7 @@
package androidx.ui.core
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.IntPx
import androidx.ui.unit.IntPxPosition
import androidx.ui.unit.IntPxSize
@@ -68,24 +69,39 @@
companion object {
// 2D Alignments.
+ @Stable
val TopStart: Alignment = DirectionalAlignment(-1f, -1f)
+ @Stable
val TopCenter: Alignment = DirectionalAlignment(-1f, 0f)
+ @Stable
val TopEnd: Alignment = DirectionalAlignment(-1f, 1f)
+ @Stable
val CenterStart: Alignment = DirectionalAlignment(0f, -1f)
+ @Stable
val Center: Alignment = DirectionalAlignment(0f, 0f)
+ @Stable
val CenterEnd: Alignment = DirectionalAlignment(0f, 1f)
+ @Stable
val BottomStart: Alignment = DirectionalAlignment(1f, -1f)
+ @Stable
val BottomCenter: Alignment = DirectionalAlignment(1f, 0f)
+ @Stable
val BottomEnd: Alignment = DirectionalAlignment(1f, 1f)
// 1D Alignment.Verticals.
+ @Stable
val Top: Vertical = DirectionalAlignment.Vertical(-1f)
+ @Stable
val CenterVertically: Vertical = DirectionalAlignment.Vertical(0f)
+ @Stable
val Bottom: Vertical = DirectionalAlignment.Vertical(1f)
// 1D Alignment.Horizontals.
+ @Stable
val Start: Horizontal = DirectionalAlignment.Horizontal(-1f)
+ @Stable
val CenterHorizontally: Horizontal = DirectionalAlignment.Horizontal(0f)
+ @Stable
val End: Horizontal = DirectionalAlignment.Horizontal(1f)
}
}
@@ -188,15 +204,23 @@
companion object {
// 2D AbsoluteAlignments.
+ @Stable
val TopLeft = AbsoluteAlignment(-1f, -1f)
+ @Stable
val TopRight = AbsoluteAlignment(-1f, 1f)
+ @Stable
val CenterLeft = AbsoluteAlignment(0f, -1f)
+ @Stable
val CenterRight = AbsoluteAlignment(0f, 1f)
+ @Stable
val BottomLeft = AbsoluteAlignment(1f, -1f)
+ @Stable
val BottomRight = AbsoluteAlignment(1f, 1f)
// 1D AbsoluteAlignment.Horizontals.
+ @Stable
val Left: Horizontal = Horizontal(-1f)
+ @Stable
val Right: Horizontal = Horizontal(1f)
}
}
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt b/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt
index a4f038a..b1005b5 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/AlignmentLine.kt
@@ -16,6 +16,7 @@
package androidx.ui.core
+import androidx.compose.Immutable
import androidx.ui.unit.IntPx
/**
@@ -46,6 +47,7 @@
* @param merger Defines the position of an alignment line inherited from more than one child.
* @param horizontal Whether the alignment line is horizontal or vertical.
*/
+@Immutable
sealed class AlignmentLine(
internal val merger: (IntPx, IntPx) -> IntPx
)
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt b/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt
index ce25f98..5d032b7 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Clip.kt
@@ -16,11 +16,13 @@
package androidx.ui.core
+import androidx.compose.Stable
import androidx.ui.graphics.Shape
/**
* Clip the content to the bounds of a layer defined at this modifier.
*/
+@Stable
fun Modifier.clipToBounds() = drawLayer(clip = true)
/**
@@ -28,4 +30,5 @@
*
* @param shape the content will be clipped to this [Shape].
*/
+@Stable
fun Modifier.clip(shape: Shape) = drawLayer(clip = true, shape = shape)
\ No newline at end of file
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt b/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt
index f9163dd..4e6b22a 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/ContentScale.kt
@@ -16,6 +16,8 @@
package androidx.ui.core
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.geometry.Size
import kotlin.math.max
import kotlin.math.min
@@ -25,6 +27,7 @@
/**
* Represents a rule to apply to scale a source rectangle to be inscribed into a destination
*/
+@Stable
interface ContentScale {
/**
@@ -46,6 +49,7 @@
* This [ContentScale] implementation in combination with usage of [Alignment.Center]
* provides similar behavior to [android.widget.ImageView.ScaleType.CENTER_CROP]
*/
+ @Stable
val Crop = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillMaxDimension(srcSize, dstSize)
@@ -59,6 +63,7 @@
* This [ContentScale] implementation in combination with usage of [Alignment.Center]
* provides similar behavior to [android.widget.ImageView.ScaleType.FIT_CENTER]
*/
+ @Stable
val Fit = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillMinDimension(srcSize, dstSize)
@@ -69,6 +74,7 @@
* height. This can cover a larger area than the destination if the height is larger than
* the width.
*/
+ @Stable
val FillHeight = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillHeight(srcSize, dstSize)
@@ -79,6 +85,7 @@
* destination width. This can cover a larger area than the destination if the width is
* larger than the height.
*/
+ @Stable
val FillWidth = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillWidth(srcSize, dstSize)
@@ -93,6 +100,7 @@
* This [ContentScale] implementation in combination with usage of [Alignment.Center]
* provides similar behavior to [android.widget.ImageView.ScaleType.CENTER_INSIDE]
*/
+ @Stable
val Inside = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
if (srcSize.width <= dstSize.width && srcSize.height <= dstSize.height) {
@@ -105,6 +113,7 @@
/**
* Do not apply any scaling to the source
*/
+ @Stable
val None = FixedScale(OriginalScale)
}
}
@@ -113,6 +122,7 @@
* [ContentScale] implementation that always scales the dimension by the provided
* fixed floating point value
*/
+@Immutable
data class FixedScale(val value: Float) : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float = value
}
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt b/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt
index 38f42dc..40a349e 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/DrawLayerModifier.kt
@@ -18,6 +18,7 @@
import androidx.annotation.FloatRange
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.graphics.RectangleShape
import androidx.ui.graphics.Shape
import androidx.ui.util.packFloats
@@ -176,6 +177,7 @@
* @param shape [DrawLayerModifier.shape]
* @param clip [DrawLayerModifier.clip]
*/
+@Stable
fun Modifier.drawLayer(
scaleX: Float = 1f,
scaleY: Float = 1f,
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt b/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt
index 0dfb726..6c29b8a 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/DrawShadow.kt
@@ -17,6 +17,7 @@
package androidx.ui.core
import androidx.annotation.FloatRange
+import androidx.compose.Stable
import androidx.ui.graphics.RectangleShape
import androidx.ui.graphics.Shape
import androidx.ui.unit.Dp
@@ -39,6 +40,7 @@
* @param clip When active, the content drawing clips to the shape.
* @param opacity The opacity of the layer, including the shadow.
*/
+@Stable
fun Modifier.drawShadow(
elevation: Dp,
shape: Shape = RectangleShape,
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt b/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt
index 5b01bc4..f3eafe6 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/LayoutTag.kt
@@ -17,11 +17,13 @@
package androidx.ui.core
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.Density
/**
* Tag the element with [tag] to identify the element within its parent.
*/
+@Stable
fun Modifier.tag(tag: Any) = this + LayoutTag(tag)
/**
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt b/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt
index 4e4d5a5..e5db024 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/Opacity.kt
@@ -17,6 +17,7 @@
package androidx.ui.core
import androidx.annotation.FloatRange
+import androidx.compose.Stable
/**
* Draw content with modified opacity (alpha) that may be less than 1.
@@ -26,6 +27,7 @@
*
* @param opacity the fraction of children's alpha value.
*/
+@Stable
fun Modifier.drawOpacity(
@FloatRange(from = 0.0, to = 1.0) opacity: Float
) = drawLayer(alpha = opacity, clip = true)
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt b/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt
index d324768..deec36c 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/PointerInput.kt
@@ -16,6 +16,8 @@
package androidx.ui.core
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.IntPxSize
import androidx.ui.unit.Px
import androidx.ui.unit.PxPosition
@@ -45,6 +47,7 @@
* @param previous The [PointerInputData] that represents the previous state of this pointer.
* @param consumed Which aspects of this change have been consumed.
*/
+@Immutable
data class PointerInputChange(
val id: PointerId,
val current: PointerInputData,
@@ -68,9 +71,13 @@
* the owning [PointerInputChange] is being dispatched to.
* @param down True if the at [uptime] the pointer was contacting the screen.
*/
+@Immutable
data class PointerInputData(
+ @Stable
val uptime: Uptime? = null,
+ @Stable
val position: PxPosition? = null,
+ @Stable
val down: Boolean = false
)
@@ -80,6 +87,7 @@
* @param positionChange The amount of change to the position that has been consumed.
* @param downChange True if a change to down or up has been consumed.
*/
+@Immutable
data class ConsumedData(
val positionChange: PxPosition = PxPosition.Origin,
val downChange: Boolean = false
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt b/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt
index 1abf7b6..f48f467 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/TestTag.kt
@@ -17,6 +17,7 @@
package androidx.ui.core
import androidx.compose.Composable
+import androidx.compose.Stable
import androidx.ui.core.semantics.semantics
import androidx.ui.semantics.Semantics
import androidx.ui.semantics.SemanticsPropertyReceiver
@@ -43,6 +44,7 @@
* Note that this does not, by itself, create a semantics boundary. If the element you are
* setting this on does not already create a boundary, you will need to create one.
*/
+@Stable
fun Modifier.testTag(tag: String) = semantics(
properties = {
testTag = tag
diff --git a/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt b/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt
index ff88370..91658d0 100644
--- a/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt
+++ b/ui/ui-core/src/main/java/androidx/ui/core/ZIndexModifier.kt
@@ -16,6 +16,8 @@
package androidx.ui.core
+import androidx.compose.Stable
+
/**
* A [Modifier.Element] that controls the drawing order for the children of the same layout
* parent. A child with larger [zIndex] will be drawn after all the children with smaller [zIndex].
@@ -42,6 +44,7 @@
*
* @sample androidx.ui.core.samples.ZIndexModifierSample
*/
+@Stable
fun Modifier.zIndex(zIndex: Float): Modifier = this + SimpleZIndexModifier(zIndex)
private data class SimpleZIndexModifier(override val zIndex: Float) : ZIndexModifier
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt
index 010c508..abfaa25 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/Border.kt
@@ -17,6 +17,7 @@
package androidx.ui.foundation
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.graphics.Brush
import androidx.ui.graphics.Color
import androidx.ui.graphics.SolidColor
@@ -37,4 +38,5 @@
* @param size size of the border in [Dp]. Use [Dp.Hairline] for one-pixel border.
* @param color color to paint the border with
*/
+@Stable
fun Border(size: Dp, color: Color) = Border(size, SolidColor(color))
\ No newline at end of file
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt
index 45e1c65..6526b56 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/TextField.kt
@@ -17,6 +17,8 @@
package androidx.ui.foundation
import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.compose.getValue
import androidx.compose.mutableStateOf
import androidx.compose.remember
@@ -57,8 +59,11 @@
* @param selection the selection range. If the selection is collapsed, it represents cursor
* location. Do not specify outside of the text buffer.
*/
+@Immutable
data class TextFieldValue(
+ @Stable
val text: String = "",
+ @Stable
val selection: TextRange = TextRange(0, 0)
) {
companion object {
diff --git a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt
index c04b17a..b770082 100644
--- a/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt
+++ b/ui/ui-foundation/src/main/java/androidx/ui/foundation/shape/corner/CornerSize.kt
@@ -19,12 +19,15 @@
import androidx.annotation.FloatRange
import androidx.annotation.IntRange
import androidx.ui.geometry.Size
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.Density
import androidx.ui.unit.Dp
/**
* Defines size of a corner in pixels. For example for rounded shape it can be a corner radius.
*/
+@Immutable
interface CornerSize {
/**
* Converts the [CornerSize] to pixels.
@@ -41,6 +44,7 @@
* Creates [CornerSize] with provided size.
* @param size the corner size defined in [Dp].
*/
+@Stable
fun CornerSize(size: Dp): CornerSize = DpCornerSize(size)
private data class DpCornerSize(private val size: Dp) : CornerSize {
@@ -52,6 +56,7 @@
* Creates [CornerSize] with provided size.
* @param size the corner size defined in pixels.
*/
+@Stable
fun CornerSize(size: Float): CornerSize = PxCornerSize(size)
private data class PxCornerSize(private val size: Float) : CornerSize {
@@ -63,6 +68,7 @@
* @param percent the corner size defined in percents of the shape's smaller side.
* Can't be negative or larger then 50 percents.
*/
+@Stable
fun CornerSize(@IntRange(from = 0, to = 50) percent: Int): CornerSize =
PercentCornerSize(percent.toFloat())
@@ -87,6 +93,7 @@
/**
* [CornerSize] always equals to zero.
*/
+@Stable
val ZeroCornerSize: CornerSize = object : CornerSize {
override fun toPx(shapeSize: Size, density: Density) = 0.0f
}
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt
index 2780b7e..af6c248 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Offset.kt
@@ -17,6 +17,7 @@
package androidx.ui.geometry
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
import androidx.ui.util.packFloats
import androidx.ui.util.toStringAsFixed
@@ -60,9 +61,11 @@
@Immutable
inline class Offset(@PublishedApi internal val packedValue: Long) {
+ @Stable
val dx: Float
get() = unpackFloat1(packedValue)
+ @Stable
val dy: Float
get() = unpackFloat2(packedValue)
@@ -72,6 +75,7 @@
*
* This can be used to represent the origin of a coordinate space.
*/
+ @Stable
val zero = Offset(0.0f, 0.0f)
/**
@@ -83,6 +87,7 @@
* * [isFinite], which checks whether both components are finite.
*/
// This is included for completeness, because [Size.infinite] exists.
+ @Stable
val infinite = Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
/**
@@ -100,6 +105,7 @@
* Values for [fraction] are usually obtained from an [Animation<Float>], such as
* an `AnimationController`.
*/
+ @Stable
fun lerp(start: Offset, stop: Offset, fraction: Float): Offset {
return Offset(
lerp(start.dx, stop.dx, fraction),
@@ -107,6 +113,7 @@
)
}
+ @Stable
fun isValid(offset: Offset): Boolean {
check(!offset.dx.isNaN() && !offset.dy.isNaN()) {
"Offset argument contained a NaN value."
@@ -121,6 +128,7 @@
* If you need this value to compare it to another [Offset]'s distance,
* consider using [getDistanceSquared] instead, since it is cheaper to compute.
*/
+ @Stable
fun getDistance() = sqrt(dx * dx + dy * dy)
/**
@@ -128,6 +136,7 @@
*
* This is cheaper than computing the [getDistance] itself.
*/
+ @Stable
fun getDistanceSquared() = dx * dx + dy * dy
/**
@@ -181,6 +190,7 @@
* Offset b = -a; // same as: a.scale(-1.0, -1.0)
* ```
*/
+ @Stable
fun scale(scaleX: Float, scaleY: Float): Offset = Offset(dx * scaleX, dy * scaleY)
/**
@@ -197,6 +207,7 @@
* Offset d = a - b; // same as: a.translate(-b.dx, -b.dy)
* ```
*/
+ @Stable
fun translate(translateX: Float, translateY: Float): Offset =
Offset(dx + translateX, dy + translateY)
@@ -208,6 +219,7 @@
* If the [Offset] represents an arrow on a plane, this operator returns the
* same arrow but pointing in the reverse direction.
*/
+ @Stable
operator fun unaryMinus(): Offset = Offset(-dx, -dy)
/**
@@ -219,6 +231,7 @@
*
* See also [translate].
*/
+ @Stable
operator fun minus(other: Offset): Offset = Offset(dx - other.dx, dy - other.dy)
/**
@@ -230,6 +243,7 @@
*
* See also [translate].
*/
+ @Stable
operator fun plus(other: Offset): Offset = Offset(dx + other.dx, dy + other.dy)
/**
@@ -241,6 +255,7 @@
*
* See also [scale].
*/
+ @Stable
operator fun times(operand: Float): Offset = Offset(dx * operand, dy * operand)
/**
@@ -252,6 +267,7 @@
*
* See also [scale].
*/
+ @Stable
operator fun div(operand: Float): Offset = Offset(dx / operand, dy / operand)
/**
@@ -272,6 +288,7 @@
* coordinates of the left-hand-side operand (an Offset) by the scalar
* right-hand-side operand (a Float).
*/
+ @Stable
operator fun rem(operand: Float) = Offset(dx % operand, dy % operand)
/**
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt
index 273b101..2648f74 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Radius.kt
@@ -16,25 +16,32 @@
package androidx.ui.geometry
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
import androidx.ui.util.toStringAsFixed
import kotlin.math.truncate
/** A radius for either circular or elliptical shapes. */
+@Immutable
data class Radius(
/** The radius value on the horizontal axis. */
+ @Stable
val x: Float,
/** The radius value on the vertical axis. */
+ @Stable
val y: Float
) {
companion object {
/** Constructs a circular radius. [x] and [y] will have the same radius value. */
+ @Stable
fun circular(radius: Float): Radius {
return Radius(radius, radius)
}
/** Constructs an elliptical radius with the given radii. */
+ @Stable
fun elliptical(x: Float, y: Float): Radius {
return Radius(x, y)
}
@@ -44,6 +51,7 @@
*
* You can use [Radius.zero] with [RRect] to have right-angle corners.
*/
+ @Stable
val zero: Radius = circular(0.0f)
}
@@ -57,6 +65,7 @@
* and then adding the result to another radius is equivalent to subtracting
* a radius of one pixel from the other.
*/
+ @Stable
operator fun unaryMinus() = elliptical(-x, -y)
/**
@@ -66,6 +75,7 @@
* minus the right-hand-side operand's [x] and whose [y] value is the
* left-hand-side operand's [y] minus the right-hand-side operand's [y].
*/
+ @Stable
operator fun minus(other: Radius) = elliptical(x - other.x, y - other.y)
/**
@@ -75,6 +85,7 @@
* two operands, and whose [y] value is the sum of the [y] values of the
* two operands.
*/
+ @Stable
operator fun plus(other: Radius) = elliptical(x + other.x, y + other.y)
/**
@@ -84,6 +95,7 @@
* left-hand-side operand (a radius) multiplied by the scalar
* right-hand-side operand (a Float).
*/
+ @Stable
operator fun times(operand: Float) = elliptical(x * operand, y * operand)
/**
@@ -93,6 +105,7 @@
* left-hand-side operand (a radius) divided by the scalar right-hand-side
* operand (a Float).
*/
+ @Stable
operator fun div(operand: Float) = elliptical(x / operand, y / operand)
/**
@@ -112,6 +125,7 @@
* coordinates of the left-hand-side operand (a radius) by the scalar
* right-hand-side operand (a Float).
*/
+ @Stable
operator fun rem(operand: Float) = elliptical(x % operand, y % operand)
override fun toString(): String {
@@ -152,6 +166,7 @@
* Values for [fraction] are usually obtained from an [Animation<Float>], such as
* an `AnimationController`.
*/
+@Stable
fun lerp(start: Radius, stop: Radius, fraction: Float): Radius {
return Radius.elliptical(
lerp(start.x, stop.x, fraction),
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt
index dbf06c5..9ebaf29 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Rect.kt
@@ -16,6 +16,8 @@
package androidx.ui.geometry
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
import androidx.ui.util.toStringAsFixed
import kotlin.math.absoluteValue
@@ -34,19 +36,25 @@
* Rect myRect = const Offset(1.0, 2.0) & const Size(3.0, 4.0);
* ```
*/
+@Immutable
data class Rect(
// The offset of the left edge of this rectangle from the x axis.
+ @Stable
val left: Float,
// The offset of the top edge of this rectangle from the y axis.
+ @Stable
val top: Float,
// The offset of the right edge of this rectangle from the x axis.
+ @Stable
val right: Float,
// The offset of the bottom edge of this rectangle from the y axis.
+ @Stable
val bottom: Float
) {
companion object {
/** Construct a rectangle from its left, top, right, and bottom edges. */
+ @Stable
fun fromLTRB(left: Float, top: Float, right: Float, bottom: Float): Rect {
return Rect(left, top, right, bottom)
}
@@ -58,6 +66,7 @@
* To construct a [Rect] from an [Offset] and a [Size], you can use the
* rectangle constructor operator `&`. See [Offset.&].
*/
+ @Stable
fun fromLTWH(left: Float, top: Float, width: Float, height: Float): Rect {
return Rect(left, top, left + width, top + height)
}
@@ -67,6 +76,7 @@
*
* The `center` argument is assumed to be an offset from the origin.
*/
+ @Stable
fun fromCircle(center: Offset, radius: Float): Rect {
return Rect(
center.dx - radius,
@@ -80,6 +90,7 @@
* Construct the smallest rectangle that encloses the given offsets, treating
* them as vectors from the origin.
*/
+ @Stable
fun fromPoints(a: Offset, b: Offset): Rect {
return Rect(
min(a.dx, b.dx),
@@ -90,6 +101,7 @@
}
/** A rectangle with left, top, right, and bottom edges all at zero. */
+ @Stable
val zero: Rect = Rect(0.0f, 0.0f, 0.0f, 0.0f)
val _giantScalar: Float = 1e7f // matches kGiantRect from default_layer_builder.cc
@@ -110,9 +122,11 @@
}
/** The distance between the left and right edges of this rectangle. */
+ @Stable
val width = right - left
/** The distance between the top and bottom edges of this rectangle. */
+ @Stable
val height = bottom - top
// static const int _kDataSize = 4;
@@ -132,6 +146,7 @@
/** Whether any of the coordinates of this rectangle are equal to positive infinity. */
// included for consistency with Offset and Size
+ @Stable
fun isInfinite(): Boolean {
return left >= Float.POSITIVE_INFINITY ||
top >= Float.POSITIVE_INFINITY ||
@@ -140,6 +155,7 @@
}
/** Whether all coordinates of this rectangle are finite. */
+ @Stable
fun isFinite(): Boolean =
left.isFinite() &&
top.isFinite() &&
@@ -150,6 +166,7 @@
* Whether this rectangle encloses a non-zero area. Negative areas are
* considered empty.
*/
+ @Stable
fun isEmpty(): Boolean = left >= right || top >= bottom
/**
@@ -158,6 +175,7 @@
* To translate a rectangle by separate x and y components rather than by an
* [Offset], consider [translate].
*/
+ @Stable
fun shift(offset: Offset): Rect {
return fromLTRB(left + offset.dx, top + offset.dy, right + offset.dx, bottom + offset.dy)
}
@@ -169,6 +187,7 @@
* To translate a rectangle by an [Offset] rather than by separate x and y
* components, consider [shift].
*/
+ @Stable
fun translate(translateX: Float, translateY: Float): Rect {
return fromLTRB(
left + translateX,
@@ -179,11 +198,13 @@
}
/** Returns a new rectangle with edges moved outwards by the given delta. */
+ @Stable
fun inflate(delta: Float): Rect {
return fromLTRB(left - delta, top - delta, right + delta, bottom + delta)
}
/** Returns a new rectangle with edges moved inwards by the given delta. */
+ @Stable
fun deflate(delta: Float): Rect = inflate(-delta)
/**
@@ -192,6 +213,7 @@
* for this to be meaningful. If the two rectangles do not overlap,
* then the resulting Rect will have a negative width or height.
*/
+ @Stable
fun intersect(other: Rect): Rect {
return fromLTRB(
max(left, other.left),
@@ -347,6 +369,7 @@
* Values for [fraction] are usually obtained from an [Animation<Float>], such as
* an `AnimationController`.
*/
+@Stable
fun lerp(start: Rect, stop: Rect, fraction: Float): Rect {
return Rect.fromLTRB(
lerp(start.left, stop.left, fraction),
diff --git a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt
index 2001dbf..10ca029 100644
--- a/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt
+++ b/ui/ui-geometry/src/commonMain/kotlin/androidx/ui/geometry/Size.kt
@@ -17,6 +17,7 @@
package androidx.ui.geometry
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
import androidx.ui.util.packFloats
import androidx.ui.util.toStringAsFixed
@@ -30,6 +31,7 @@
/**
* Constructs a [Size] from the given width and height
*/
+@Stable
fun Size(width: Float, height: Float) = Size(packFloats(width, height))
/**
@@ -40,9 +42,11 @@
@Immutable
inline class Size(@PublishedApi internal val value: Long) {
+ @Stable
val width: Float
get() = unpackFloat1(value)
+ @Stable
val height: Float
get() = unpackFloat2(value)
@@ -98,6 +102,7 @@
/**
* An empty size, one with a zero width and a zero height.
*/
+ @Stable
val zero = Size(0.0f, 0.0f)
/**
@@ -108,6 +113,7 @@
* * [isInfinite], which checks whether either dimension is infinite.
* * [isFinite], which checks whether both dimensions are finite.
*/
+ @Stable
val UnspecifiedSize = Size(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
/**
@@ -125,6 +131,7 @@
* Values for [fraction] are usually obtained from an [Animation<Float>], such as
* an `AnimationController`.
*/
+ @Stable
fun lerp(start: Size, stop: Size, fraction: Float): Size? {
return Size(
lerp(start.width, stop.width, fraction),
@@ -138,6 +145,7 @@
*
* Negative areas are considered empty.
*/
+ @Stable
fun isEmpty() = width <= 0.0f || height <= 0.0f
/**
@@ -158,10 +166,12 @@
* left-hand-side operand minus the [Offset.dy] dimension of the
* right-hand-side operand.
*/
+ @Stable
operator fun minus(other: Offset): Size {
return Size(width - other.dx, height - other.dy)
}
+ @Stable
operator fun minus(other: Size): Offset {
return Offset(width - other.width, height - other.height)
}
@@ -175,6 +185,7 @@
* [height] of the left-hand-side operand and the [Offset.dy] dimension of
* the right-hand-side operand.
*/
+ @Stable
operator fun plus(other: Offset) = Size(width + other.dx, height + other.dy)
/**
@@ -184,6 +195,7 @@
* operand (a [Size]) multiplied by the scalar right-hand-side operand (a
* [Float]).
*/
+ @Stable
operator fun times(operand: Float) = Size(width * operand, height * operand)
/**
@@ -193,6 +205,7 @@
* operand (a [Size]) divided by the scalar right-hand-side operand (a
* [Float]).
*/
+ @Stable
operator fun div(operand: Float) = Size(width / operand, height / operand)
/**
@@ -202,6 +215,7 @@
* operand (a [Size]) divided by the scalar right-hand-side operand (a
* [Float]), rounded towards zero.
*/
+ @Stable
fun truncDiv(operand: Float) =
Size(truncate(width / operand), truncate(height / operand))
@@ -212,17 +226,20 @@
* left-hand-side operand (a [Size]) by the scalar right-hand-side operand (a
* [Float]).
*/
+ @Stable
operator fun rem(operand: Float) = Size(width % operand, height % operand)
/**
* The lesser of the magnitudes of the [width] and the [height].
*/
+ @Stable
val minDimension: Float
get() = min(width.absoluteValue, height.absoluteValue)
/**
* The greater of the magnitudes of the [width] and the [height].
*/
+ @Stable
val maxDimension: Float
get() = max(width.absoluteValue, height.absoluteValue)
@@ -236,6 +253,7 @@
*
* See also [Rect.topLeft].
*/
+ @Stable
fun topLeft(origin: Offset): Offset = origin
/**
@@ -244,6 +262,7 @@
*
* See also [Rect.topCenter].
*/
+ @Stable
fun topCenter(origin: Offset): Offset = Offset(origin.dx + width / 2.0f, origin.dy)
/**
@@ -253,6 +272,7 @@
*
* See also [Rect.topRight].
*/
+ @Stable
fun topRight(origin: Offset): Offset = Offset(origin.dx + width, origin.dy)
/**
@@ -261,6 +281,7 @@
*
* See also [Rect.centerLeft].
*/
+ @Stable
fun centerLeft(origin: Offset): Offset = Offset(origin.dx, origin.dy + height / 2.0f)
/**
@@ -270,6 +291,7 @@
*
* See also [Rect.center].
*/
+ @Stable
fun center(origin: Offset = Offset.zero): Offset = Offset(origin.dx + width / 2.0f, origin.dy +
height / 2.0f)
@@ -279,6 +301,7 @@
*
* See also [Rect.centerLeft].
*/
+ @Stable
fun centerRight(origin: Offset): Offset = Offset(origin.dx + width, origin.dy + height / 2.0f)
/**
@@ -288,6 +311,7 @@
*
* See also [Rect.bottomLeft].
*/
+ @Stable
fun bottomLeft(origin: Offset): Offset = Offset(origin.dx, origin.dy + height)
/**
@@ -297,6 +321,7 @@
*
* See also [Rect.bottomLeft].
*/
+ @Stable
fun bottomCenter(origin: Offset): Offset = Offset(origin.dx + width / 2.0f, origin.dy + height)
/**
@@ -306,6 +331,7 @@
*
* See also [Rect.bottomRight].
*/
+ @Stable
fun bottomRight(origin: Offset): Offset = Offset(origin.dx + width, origin.dy + height)
/**
@@ -316,6 +342,7 @@
* Rectangles include their top and left edges but exclude their bottom and
* right edges.
*/
+ @Stable
fun contains(offset: Offset): Boolean {
return offset.dx >= 0.0f && offset.dx < width && offset.dy >= 0.0f && offset.dy < height
}
@@ -323,6 +350,7 @@
/**
* A [Size] with the [width] and [height] swapped.
*/
+ @Stable
fun getFlipped() = Size(height, width)
override fun toString() = "Size(${width.toStringAsFixed(1)}, ${height.toStringAsFixed(1)})"
@@ -331,6 +359,7 @@
/**
* Convert a [Size] to a [Rect].
*/
+@Stable
fun Size.toRect(): Rect {
return Rect(0f, 0f, width, height)
}
\ No newline at end of file
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt
index c48ed74..cd839a1 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Color.kt
@@ -22,6 +22,7 @@
import androidx.ui.util.annotation.IntRange
import androidx.ui.util.annotation.Size
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.graphics.colorspace.ColorModel
import androidx.ui.graphics.colorspace.ColorSpace
import androidx.ui.graphics.colorspace.ColorSpaces
@@ -121,6 +122,7 @@
*
* @return A non-null instance of [ColorSpace]
*/
+ @Stable
val colorSpace: ColorSpace
get() = ColorSpaces.getColorSpace((value and 0x3fUL).toInt())
@@ -161,6 +163,7 @@
* @see blue
* @see green
*/
+ @Stable
val red: Float
get() {
return if ((value and 0x3fUL) == 0UL) {
@@ -183,6 +186,7 @@
* @see red
* @see blue
*/
+ @Stable
val green: Float
get() {
return if ((value and 0x3fUL) == 0UL) {
@@ -205,6 +209,7 @@
* @see red
* @see green
*/
+ @Stable
val blue: Float
get() {
return if ((value and 0x3fUL) == 0UL) {
@@ -222,6 +227,7 @@
* @see green
* @see blue
*/
+ @Stable
val alpha: Float
get() {
return if ((value and 0x3fUL) == 0UL) {
@@ -231,20 +237,26 @@
}
}
+ @Stable
operator fun component1(): Float = red
+ @Stable
operator fun component2(): Float = green
+ @Stable
operator fun component3(): Float = blue
+ @Stable
operator fun component4(): Float = alpha
+ @Stable
operator fun component5(): ColorSpace = colorSpace
/**
* Copies the existing color, changing only the provided values. The [ColorSpace][colorSpace]
* of the returned [Color] is the same as this [colorSpace].
*/
+ @Stable
fun copy(
alpha: Float = this.alpha,
red: Float = this.red,
@@ -276,17 +288,29 @@
}
companion object {
+ @Stable
val Black = Color(0xFF000000)
+ @Stable
val DarkGray = Color(0xFF444444)
+ @Stable
val Gray = Color(0xFF888888)
+ @Stable
val LightGray = Color(0xFFCCCCCC)
+ @Stable
val White = Color(0xFFFFFFFF)
+ @Stable
val Red = Color(0xFFFF0000)
+ @Stable
val Green = Color(0xFF00FF00)
+ @Stable
val Blue = Color(0xFF0000FF)
+ @Stable
val Yellow = Color(0xFFFFFF00)
+ @Stable
val Cyan = Color(0xFF00FFFF)
+ @Stable
val Magenta = Color(0xFFFF00FF)
+ @Stable
val Transparent = Color(0x00000000)
/**
* Because Color is an inline class, this represents an unset value
@@ -295,6 +319,7 @@
* [isUnset] to check for the unset value or [isSet] for any color that isn't
* [Unset].
*/
+ @Stable
val Unset = Color(0f, 0f, 0f, 0f, ColorSpaces.Unset)
}
}
@@ -305,6 +330,7 @@
* the default [alpha] is `1.0` (opaque). [colorSpace] must have a [ColorSpace.componentCount] of
* 3.
*/
+@Stable
fun Color(
red: Float,
green: Float,
@@ -359,6 +385,7 @@
* @param color The ARGB color int to create a <code>Color</code> from.
* @return A non-null instance of {@link Color}
*/
+@Stable
fun Color(@ColorInt color: Int): Color {
return Color(value = color.toULong() shl 32)
}
@@ -375,6 +402,7 @@
* from
* @return A non-null instance of {@link Color}
*/
+@Stable
fun Color(color: Long): Color {
return Color(value = (color.toULong() and 0xffffffffUL) shl 32)
}
@@ -386,6 +414,7 @@
*
* @return A non-null instance of {@link Color}
*/
+@Stable
fun Color(
@IntRange(from = 0, to = 0xFF) red: Int,
@IntRange(from = 0, to = 0xFF) green: Int,
@@ -404,6 +433,7 @@
* between the two. The [ColorSpace] of the result is always the [ColorSpace][Color.colorSpace]
* of [stop].
*/
+@Stable
fun lerp(start: Color, stop: Color, @FloatRange(from = 0.0, to = 1.0) fraction: Float): Color {
val linearColorSpace = ColorSpaces.LinearExtendedSrgb
val startColor = start.convert(linearColorSpace)
@@ -440,6 +470,7 @@
* @return the [Color] representing [this] composited on top of [background], converted to the
* color space of [background].
*/
+@Stable
fun Color.compositeOver(background: Color): Color {
val fg = this.convert(background.colorSpace)
@@ -489,6 +520,7 @@
* @throws IllegalArgumentException If the this color's color space
* does not use the [RGB][ColorModel.Rgb] color model
*/
+@Stable
fun Color.luminance(): Float {
val colorSpace = colorSpace
require(colorSpace.model === ColorModel.Rgb) {
@@ -515,6 +547,7 @@
*
* @return An ARGB color in the sRGB color space
*/
+@Stable
@ColorInt
fun Color.toArgb(): Int {
val colorSpace = colorSpace
@@ -535,11 +568,13 @@
/**
* `false` when this is [Color.Unset].
*/
+@Stable
inline val Color.isSet: Boolean get() = value != Color.Unset.value
/**
* `true` when this is [Color.Unset].
*/
+@Stable
inline val Color.isUnset: Boolean get() = value == Color.Unset.value
/**
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt
index 83e14f8..c6e2e64 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/ColorFilter.kt
@@ -16,6 +16,9 @@
package androidx.ui.graphics
+import androidx.compose.Immutable
+import androidx.compose.Stable
+
/**
* Creates a color filter that applies the blend mode given as the second
* argument. The source color is the one given as the first argument, and the
@@ -25,14 +28,18 @@
* to the [Paint.blendMode], using the output of this filter as the source
* and the background as the destination.
*/
+@Immutable
data class ColorFilter(
+ @Stable
val color: Color,
+ @Stable
val blendMode: BlendMode
) {
companion object {
/**
* Helper method to create a [ColorFilter] that tints contents to the specified color
*/
+ @Stable
fun tint(color: Color): ColorFilter = ColorFilter(color, BlendMode.srcIn)
}
}
\ No newline at end of file
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt
index 070c1a2..2a51609 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/RectangleShape.kt
@@ -18,11 +18,13 @@
import androidx.ui.geometry.Size
import androidx.ui.geometry.toRect
+import androidx.compose.Stable
import androidx.ui.unit.Density
/**
* A shape describing the rectangle.
*/
+@Stable
val RectangleShape: Shape = object : Shape {
override fun createOutline(size: Size, density: Density) =
Outline.Rectangle(size.toRect())
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt
index 5e30cb7..ce60cdd 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shadow.kt
@@ -17,6 +17,7 @@
package androidx.ui.graphics
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.geometry.Offset
import androidx.ui.util.lerp
@@ -25,14 +26,18 @@
*/
@Immutable
data class Shadow(
+ @Stable
val color: Color = Color(0xFF000000),
+ @Stable
val offset: Offset = Offset.zero,
+ @Stable
val blurRadius: Float = 0.0f
) {
companion object {
/**
* Constant for no shadow.
*/
+ @Stable
val None = Shadow()
}
}
@@ -40,6 +45,7 @@
/**
* Linearly interpolate two [Shadow]s.
*/
+@Stable
fun lerp(start: Shadow, stop: Shadow, fraction: Float): Shadow {
return Shadow(
lerp(start.color, stop.color, fraction),
diff --git a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt
index 5681095..5394551 100644
--- a/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt
+++ b/ui/ui-graphics/src/commonMain/kotlin/androidx/ui/graphics/Shape.kt
@@ -17,11 +17,13 @@
package androidx.ui.graphics
import androidx.ui.geometry.Size
+import androidx.compose.Immutable
import androidx.ui.unit.Density
/**
* Defines a generic shape.
*/
+@Immutable
interface Shape {
/**
* Creates [Outline] of this shape for the given [size].
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt
index 602ff48..a2c0497 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/AlignmentLine.kt
@@ -17,6 +17,7 @@
package androidx.ui.layout
import androidx.compose.Composable
+import androidx.compose.Stable
import androidx.ui.core.AlignmentLine
import androidx.ui.core.Constraints
@@ -112,6 +113,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.RelativePaddingFromSample
*/
+@Stable
fun Modifier.relativePaddingFrom(
alignmentLine: AlignmentLine,
before: Dp = 0.dp,
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt
index bf55656..47db576 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Column.kt
@@ -18,6 +18,8 @@
import androidx.annotation.FloatRange
import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.core.Alignment
import androidx.ui.core.Measured
import androidx.ui.core.Modifier
@@ -79,6 +81,7 @@
* Scope for the children of [Column].
*/
@LayoutScopeMarker
+@Immutable
object ColumnScope {
/**
* Position the element horizontally within the [Column] according to [align].
@@ -86,6 +89,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleGravityInColumn
*/
+ @Stable
fun Modifier.gravity(align: Alignment.Horizontal) = this + GravityModifier(align)
/**
@@ -104,6 +108,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleRelativeToSiblingsInColumn
*/
+ @Stable
fun Modifier.alignWithSiblings(alignmentLine: VerticalAlignmentLine) =
this + SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
@@ -117,6 +122,7 @@
*
* @sample androidx.ui.layout.samples.SimpleColumn
*/
+ @Stable
fun Modifier.weight(
@FloatRange(from = 0.0, fromInclusive = false) weight: Float,
fill: Boolean = true
@@ -142,6 +148,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleRelativeToSiblings
*/
+ @Stable
fun Modifier.alignWithSiblings(
alignmentLineBlock: (Measured) -> IntPx
) = this + SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt
index 76e6aa5..fcdbe26 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/DpConstraints.kt
@@ -17,6 +17,7 @@
package androidx.ui.layout
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.core.Constraints
import androidx.ui.unit.Density
import androidx.ui.unit.Dp
@@ -30,9 +31,13 @@
*/
@Immutable
data class DpConstraints(
+ @Stable
val minWidth: Dp = 0.dp,
+ @Stable
val maxWidth: Dp = Dp.Infinity,
+ @Stable
val minHeight: Dp = 0.dp,
+ @Stable
val maxHeight: Dp = Dp.Infinity
) {
init {
@@ -58,11 +63,13 @@
/**
* Creates constraints tight in both dimensions.
*/
+ @Stable
fun fixed(width: Dp, height: Dp) = DpConstraints(width, width, height, height)
/**
* Creates constraints with tight width and loose height.
*/
+ @Stable
fun fixedWidth(width: Dp) = DpConstraints(
minWidth = width,
maxWidth = width,
@@ -73,6 +80,7 @@
/**
* Creates constraints with tight height and loose width.
*/
+ @Stable
fun fixedHeight(height: Dp) = DpConstraints(
minWidth = 0.dp,
maxWidth = Dp.Infinity,
@@ -86,38 +94,45 @@
* Whether or not the upper bound on the maximum height.
* @see hasBoundedWidth
*/
+@Stable
val DpConstraints.hasBoundedHeight get() = maxHeight.isFinite()
/**
* Whether or not the upper bound on the maximum width.
* @see hasBoundedHeight
*/
+@Stable
val DpConstraints.hasBoundedWidth get() = maxWidth.isFinite()
/**
* Whether there is exactly one width value that satisfies the constraints.
*/
+@Stable
val DpConstraints.hasFixedWidth get() = maxWidth == minWidth
/**
* Whether there is exactly one height value that satisfies the constraints.
*/
+@Stable
val DpConstraints.hasFixedHeight get() = maxHeight == minHeight
/**
* Whether the area of a component respecting these constraints will definitely be 0.
* This is true when at least one of maxWidth and maxHeight are 0.
*/
+@Stable
val DpConstraints.isZero get() = maxWidth == 0.dp || maxHeight == 0.dp
/**
* Whether there is any size that satisfies the current constraints.
*/
+@Stable
val DpConstraints.satisfiable get() = minWidth <= maxWidth && minHeight <= maxHeight
/**
* Returns the result of coercing the current constraints in a different set of constraints.
*/
+@Stable
fun DpConstraints.enforce(otherConstraints: DpConstraints) = DpConstraints(
minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
@@ -128,6 +143,7 @@
/**
* Returns the DpConstraints obtained by offsetting the current instance with the given values.
*/
+@Stable
fun DpConstraints.offset(horizontal: Dp = 0.dp, vertical: Dp = 0.dp) = DpConstraints(
(minWidth + horizontal).coerceAtLeast(0.dp),
(maxWidth + horizontal).coerceAtLeast(0.dp),
@@ -138,6 +154,7 @@
/**
* Creates the [Constraints] corresponding to the current [DpConstraints].
*/
+@Stable
fun Density.Constraints(dpConstraints: DpConstraints) = Constraints(
minWidth = dpConstraints.minWidth.toIntPx(),
maxWidth = dpConstraints.maxWidth.toIntPx(),
@@ -148,6 +165,7 @@
/**
* Creates the [DpConstraints] corresponding to the current [Constraints].
*/
+@Stable
fun Density.DpConstraints(constraints: Constraints) = DpConstraints(
minWidth = constraints.minWidth.toDp(),
maxWidth = constraints.maxWidth.toDp(),
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
index 2c6843e..4826d78 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Intrinsic.kt
@@ -17,6 +17,7 @@
package androidx.ui.layout
import androidx.compose.Composable
+import androidx.compose.Stable
import androidx.ui.core.Constraints
import androidx.ui.core.IntrinsicMeasurable
import androidx.ui.core.IntrinsicMeasureScope
@@ -45,6 +46,7 @@
* Example usage for max intrinsic:
* @sample androidx.ui.layout.samples.SameWidthTextBoxes
*/
+@Stable
fun Modifier.preferredWidth(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {
IntrinsicSize.Min -> this + PreferredMinIntrinsicWidthModifier
IntrinsicSize.Max -> this + PreferredMaxIntrinsicWidthModifier
@@ -64,6 +66,7 @@
* Example usage for max intrinsic:
* @sample androidx.ui.layout.samples.MatchParentDividerForAspectRatio
*/
+@Stable
fun Modifier.preferredHeight(intrinsicSize: IntrinsicSize) = when (intrinsicSize) {
IntrinsicSize.Min -> this + PreferredMinIntrinsicHeightModifier
IntrinsicSize.Max -> this + PreferredMaxIntrinsicHeightModifier
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt
index c687157..3704381 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutAspectRatio.kt
@@ -17,6 +17,7 @@
package androidx.ui.layout
import androidx.annotation.FloatRange
+import androidx.compose.Stable
import androidx.ui.core.Constraints
import androidx.ui.core.IntrinsicMeasurable
import androidx.ui.core.IntrinsicMeasureScope
@@ -42,6 +43,7 @@
*
* @param ratio the desired width/height positive ratio
*/
+@Stable
fun Modifier.aspectRatio(
@FloatRange(from = 0.0, fromInclusive = false)
ratio: Float
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt
index 9870f58..b135c8e 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutOffset.kt
@@ -16,6 +16,7 @@
package androidx.ui.layout
+import androidx.compose.Stable
import androidx.compose.State
import androidx.compose.mutableStateOf
import androidx.ui.core.Constraints
@@ -34,6 +35,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.LayoutOffsetModifier
*/
+@Stable
fun Modifier.offset(x: Dp = 0.dp, y: Dp = 0.dp) = this + OffsetModifier(x, y)
/**
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt
index 72431f8..b6a7f67 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutPadding.kt
@@ -17,6 +17,7 @@
package androidx.ui.layout
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.core.Constraints
import androidx.ui.core.LayoutDirection
import androidx.ui.core.Modifier
@@ -38,6 +39,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.PaddingModifier
*/
+@Stable
fun Modifier.padding(
start: Dp = 0.dp,
top: Dp = 0.dp,
@@ -62,6 +64,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SymmetricPaddingModifier
*/
+@Stable
fun Modifier.padding(
horizontal: Dp = 0.dp,
vertical: Dp = 0.dp
@@ -83,6 +86,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.PaddingAllModifier
*/
+@Stable
fun Modifier.padding(all: Dp) =
this + PaddingModifier(start = all, top = all, end = all, bottom = all, rtlAware = true)
@@ -116,6 +120,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.AbsolutePaddingModifier
*/
+@Stable
fun Modifier.absolutePadding(
left: Dp = 0.dp,
top: Dp = 0.dp,
@@ -171,9 +176,13 @@
*/
@Immutable
data class InnerPadding(
+ @Stable
val start: Dp = 0.dp,
+ @Stable
val top: Dp = 0.dp,
+ @Stable
val end: Dp = 0.dp,
+ @Stable
val bottom: Dp = 0.dp
) {
constructor(all: Dp) : this(all, all, all, all)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt
index 9439791..026a18c 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/LayoutSize.kt
@@ -16,6 +16,7 @@
package androidx.ui.layout
+import androidx.compose.Stable
import androidx.ui.core.Alignment
import androidx.ui.core.Constraints
import androidx.ui.core.IntrinsicMeasurable
@@ -48,6 +49,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimplePreferredWidthModifier
*/
+@Stable
fun Modifier.preferredWidth(width: Dp) = preferredSizeIn(minWidth = width, maxWidth = width)
/**
@@ -61,6 +63,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimplePreferredHeightModifier
*/
+@Stable
fun Modifier.preferredHeight(height: Dp) = preferredSizeIn(minHeight = height, maxHeight = height)
/**
@@ -74,6 +77,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimplePreferredSizeModifier
*/
+@Stable
fun Modifier.preferredSize(size: Dp) = preferredSizeIn(size, size, size, size)
/**
@@ -88,6 +92,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimplePreferredSizeModifier
*/
+@Stable
fun Modifier.preferredSize(width: Dp, height: Dp) = preferredSizeIn(
minWidth = width,
maxWidth = width,
@@ -101,6 +106,7 @@
* the requested size will obey the incoming constraints and attempt to be as close as possible
* to the preferred size.
*/
+@Stable
fun Modifier.preferredWidthIn(
minWidth: Dp = Dp.Unspecified,
maxWidth: Dp = Dp.Unspecified
@@ -112,6 +118,7 @@
* the requested size will obey the incoming constraints and attempt to be as close as possible
* to the preferred size.
*/
+@Stable
fun Modifier.preferredHeightIn(
minHeight: Dp = Dp.Unspecified,
maxHeight: Dp = Dp.Unspecified
@@ -123,6 +130,7 @@
* requested size will obey the incoming constraints and attempt to be as close as possible to
* the preferred size.
*/
+@Stable
fun Modifier.preferredSizeIn(constraints: DpConstraints) = preferredSizeIn(
constraints.minWidth,
constraints.minHeight,
@@ -136,6 +144,7 @@
* measurement [Constraints]. If the incoming constraints are more restrictive the requested size
* will obey the incoming constraints and attempt to be as close as possible to the preferred size.
*/
+@Stable
fun Modifier.preferredSizeIn(
minWidth: Dp = Dp.Unspecified,
minHeight: Dp = Dp.Unspecified,
@@ -158,6 +167,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleWidthModifier
*/
+@Stable
fun Modifier.width(width: Dp) = sizeIn(minWidth = width, maxWidth = width)
/**
@@ -175,6 +185,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleHeightModifier
*/
+@Stable
fun Modifier.height(height: Dp) = sizeIn(minHeight = height, maxHeight = height)
/**
@@ -192,6 +203,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleSizeModifier
*/
+@Stable
fun Modifier.size(size: Dp) = sizeIn(size, size, size, size)
/**
@@ -209,6 +221,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleWidthModifier
*/
+@Stable
fun Modifier.size(width: Dp, height: Dp) = sizeIn(
minWidth = width,
maxWidth = width,
@@ -223,6 +236,7 @@
* of the content will be automatically offset to be centered on the space assigned to
* the child by the parent layout under the assumption that [Constraints] were respected.
*/
+@Stable
fun Modifier.widthIn(
minWidth: Dp = Dp.Unspecified,
maxWidth: Dp = Dp.Unspecified
@@ -235,6 +249,7 @@
* of the content will be automatically offset to be centered on the space assigned to
* the child by the parent layout under the assumption that [Constraints] were respected.
*/
+@Stable
fun Modifier.heightIn(
minHeight: Dp = Dp.Unspecified,
maxHeight: Dp = Dp.Unspecified
@@ -247,6 +262,7 @@
* of the content will be automatically offset to be centered on the space assigned to
* the child by the parent layout under the assumption that [Constraints] were respected.
*/
+@Stable
fun Modifier.sizeIn(constraints: DpConstraints) = sizeIn(
constraints.minWidth,
constraints.minHeight,
@@ -262,6 +278,7 @@
* of the content will be automatically offset to be centered on the space assigned to
* the child by the parent layout under the assumption that [Constraints] were respected.
*/
+@Stable
fun Modifier.sizeIn(
minWidth: Dp = Dp.Unspecified,
minHeight: Dp = Dp.Unspecified,
@@ -278,6 +295,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleFillWidthModifier
*/
+@Stable
fun Modifier.fillMaxWidth() = this + FillModifier(Direction.Horizontal)
/**
@@ -289,6 +307,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleFillHeightModifier
*/
+@Stable
fun Modifier.fillMaxHeight() = this + FillModifier(Direction.Vertical)
/**
@@ -301,6 +320,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleFillModifier
*/
+@Stable
fun Modifier.fillMaxSize() = this + FillModifier(Direction.Both)
/**
@@ -311,6 +331,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleWrapContentHorizontallyAlignedModifier
*/
+@Stable
// TODO(popam): avoid recreating modifier for common align
fun Modifier.wrapContentWidth(align: Alignment.Horizontal = Alignment.CenterHorizontally) = this +
AlignmentModifier(Direction.Horizontal) { size, layoutDirection ->
@@ -326,6 +347,7 @@
* @sample androidx.ui.layout.samples.SimpleWrapContentVerticallyAlignedModifier
*/
// TODO(popam): avoid recreating modifier for common align
+@Stable
fun Modifier.wrapContentHeight(align: Alignment.Vertical = Alignment.CenterVertically) =
this + AlignmentModifier(Direction.Vertical) { size, _ ->
IntPxPosition(0.ipx, align.align(size.height))
@@ -340,6 +362,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleWrapContentAlignedModifier
*/
+@Stable
fun Modifier.wrapContentSize(align: Alignment = Alignment.Center) =
this + AlignmentModifier(Direction.Both) { size, layoutDirection ->
align.align(size, layoutDirection)
@@ -355,6 +378,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.DefaultMinSizeConstraintsSample
*/
+@Stable
fun Modifier.defaultMinSizeConstraints(
minWidth: Dp = Dp.Unspecified,
minHeight: Dp = Dp.Unspecified
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt
index b58ef9d..c140688 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Row.kt
@@ -18,6 +18,8 @@
import androidx.annotation.FloatRange
import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.core.Alignment
import androidx.ui.core.HorizontalAlignmentLine
import androidx.ui.core.Measured
@@ -77,6 +79,7 @@
* Scope for the children of [Row].
*/
@LayoutScopeMarker
+@Immutable
object RowScope {
/**
* Position the element vertically within the [Row] according to [align].
@@ -84,6 +87,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleGravityInRow
*/
+ @Stable
fun Modifier.gravity(align: Alignment.Vertical) = this + GravityModifier(align)
/**
@@ -104,6 +108,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleRelativeToSiblingsInRow
*/
+ @Stable
fun Modifier.alignWithSiblings(alignmentLine: HorizontalAlignmentLine) =
this + SiblingsAlignedModifier.WithAlignmentLine(alignmentLine)
@@ -115,6 +120,7 @@
* Otherwise, the element is allowed to be smaller - this will result in [Row] being smaller,
* as the unused allocated width will not be redistributed to other siblings.
*/
+ @Stable
fun Modifier.weight(
@FloatRange(from = 0.0, fromInclusive = false) weight: Float,
fill: Boolean = true
@@ -140,6 +146,7 @@
* Example usage:
* @sample androidx.ui.layout.samples.SimpleRelativeToSiblings
*/
+ @Stable
fun Modifier.alignWithSiblings(
alignmentLineBlock: (Measured) -> IntPx
) = this + SiblingsAlignedModifier.WithAlignmentLineBlock(alignmentLineBlock)
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt
index 3e0beef..153fa40 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/RowColumnImpl.kt
@@ -18,6 +18,7 @@
import androidx.compose.Composable
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.core.Alignment
import androidx.ui.core.AlignmentLine
import androidx.ui.core.Constraints
@@ -558,6 +559,7 @@
* Used to specify the alignment of a layout's children, in cross axis direction.
*/
// TODO(popam): refine this API surface with modifiers - add type safety for alignment orientation.
+@Immutable
class CrossAxisAlignment private constructor(
internal val alignmentLineProvider: AlignmentLineProvider? = null
) {
@@ -565,16 +567,19 @@
/**
* Place children such that their center is in the middle of the cross axis.
*/
+ @Stable
val Center = CrossAxisAlignment(null)
/**
* Place children such that their start edge is aligned to the start edge of the cross
* axis. TODO(popam): Consider rtl directionality.
*/
+ @Stable
val Start = CrossAxisAlignment(null)
/**
* Place children such that their end edge is aligned to the end edge of the cross
* axis. TODO(popam): Consider rtl directionality.
*/
+ @Stable
val End = CrossAxisAlignment(null)
/**
* Align children by their baseline.
diff --git a/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt b/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
index 8420164..b20a19e 100644
--- a/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
+++ b/ui/ui-layout/src/main/java/androidx/ui/layout/Stack.kt
@@ -17,6 +17,8 @@
package androidx.ui.layout
import androidx.compose.Composable
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.core.Alignment
import androidx.ui.core.Constraints
import androidx.ui.core.Layout
@@ -97,10 +99,12 @@
* A StackScope provides a scope for the children of a [Stack].
*/
@LayoutScopeMarker
+@Immutable
class StackScope {
/**
* Pull the content element to a specific [Alignment] within the [Stack].
*/
+ @Stable
fun Modifier.gravity(align: Alignment) = this + StackGravityModifier(align)
/**
@@ -115,9 +119,11 @@
* using it for an element inside a [Stack] will make the [Stack] itself always fill the
* available space.
*/
+ @Stable
fun Modifier.matchParentSize() = this + StretchGravityModifier
internal companion object {
+ @Stable
val StretchGravityModifier: ParentDataModifier =
StackGravityModifier(Alignment.Center, true)
}
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt b/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt
index 3b87230..4bf2a4d 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/input/EditorValue.kt
@@ -16,6 +16,7 @@
package androidx.ui.input
+import androidx.compose.Immutable
import androidx.ui.text.TextRange
import androidx.ui.text.substring
import kotlin.math.max
@@ -28,6 +29,7 @@
* This class stores a snapshot of the input state of the edit buffer and provide utility functions
* for answering these information retrieval requests.
*/
+@Immutable
data class EditorValue(
/**
* A text visible to IME
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt b/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt
index 683e4a32..69455f2 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/input/VisualTransformation.kt
@@ -16,6 +16,8 @@
package androidx.ui.input
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.text.AnnotatedString
/**
@@ -80,6 +82,7 @@
* For example, you can mask characters in password filed with asterisk with
* PasswordVisualTransformation.
*/
+@Immutable
interface VisualTransformation {
/**
* Change the visual output of given text.
@@ -109,6 +112,7 @@
/**
* A special visual transformation object indicating that no transformation is applied.
*/
+ @Stable
val None: VisualTransformation = object : VisualTransformation {
override fun filter(text: AnnotatedString) =
TransformedText(text, OffsetMap.identityOffsetMap)
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt
index abc9758..b58d03f 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/AnnotatedString.kt
@@ -16,6 +16,8 @@
package androidx.ui.text
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.text.AnnotatedString.Builder
import androidx.ui.text.AnnotatedString.Range
import androidx.ui.util.fastForEach
@@ -39,6 +41,7 @@
* The basic data structure of text with multiple styles. To construct an [AnnotatedString] you
* can use [Builder].
*/
+@Immutable
data class AnnotatedString internal constructor(
val text: String,
val spanStyles: List<SpanStyleRange> = listOf(),
@@ -83,6 +86,7 @@
}
}
+ @Stable
operator fun plus(other: AnnotatedString): AnnotatedString {
return with(Builder(this)) {
append(other)
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt
index f1556c5..acf74b7 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/ParagraphStyle.kt
@@ -17,6 +17,7 @@
package androidx.ui.text
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.text.style.TextAlign
import androidx.ui.text.style.TextDirectionAlgorithm
import androidx.ui.text.style.TextIndent
@@ -66,6 +67,7 @@
*
* If the given paragraph style is null, returns this paragraph style.
*/
+ @Stable
fun merge(other: ParagraphStyle? = null): ParagraphStyle {
if (other == null) return this
@@ -84,6 +86,7 @@
/**
* Plus operator overload that applies a [merge].
*/
+ @Stable
operator fun plus(other: ParagraphStyle): ParagraphStyle = this.merge(other)
}
@@ -100,6 +103,7 @@
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid.
*/
+@Stable
fun lerp(start: ParagraphStyle, stop: ParagraphStyle, fraction: Float): ParagraphStyle {
return ParagraphStyle(
textAlign = lerpDiscrete(start.textAlign, stop.textAlign, fraction),
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt
index d922d70..d778c0b 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/Placeholder.kt
@@ -16,6 +16,7 @@
package androidx.ui.text
+import androidx.compose.Immutable
import androidx.ui.unit.TextUnit
/**
@@ -31,6 +32,7 @@
* Check [PlaceholderVerticalAlign] for more information.
* @throws IllegalArgumentException if [TextUnit.Inherit] is passed to [width] or [height].
*/
+@Immutable
data class Placeholder(
val width: TextUnit,
val height: TextUnit,
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt
index 0737a49..7f6bc55 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/SpanStyle.kt
@@ -17,6 +17,7 @@
package androidx.ui.text
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.graphics.Color
import androidx.ui.graphics.Shadow
import androidx.ui.graphics.lerp
@@ -91,6 +92,7 @@
*
* If the given span style is null, returns this span style.
*/
+ @Stable
fun merge(other: SpanStyle? = null): SpanStyle {
if (other == null) return this
@@ -119,6 +121,7 @@
/**
* Plus operator overload that applies a [merge].
*/
+ @Stable
operator fun plus(other: SpanStyle): SpanStyle = this.merge(other)
}
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt
index c5035ed..4fa69a7 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/TextRange.kt
@@ -16,6 +16,8 @@
package androidx.ui.text
+import androidx.compose.Immutable
+
fun CharSequence.substring(range: TextRange): String = this.substring(range.min, range.max)
/**
@@ -26,6 +28,7 @@
* @param start the inclusive start offset of the range.
* @param end the exclusive end offset of the range
*/
+@Immutable
data class TextRange(val start: Int, val end: Int) {
/** The minimum offset of the range. */
val min: Int get() = if (start > end) end else start
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt
index 4fb7c2e..7bd0d66 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/TextStyle.kt
@@ -18,6 +18,7 @@
import androidx.annotation.VisibleForTesting
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.core.LayoutDirection
import androidx.ui.graphics.Color
import androidx.ui.graphics.Shadow
@@ -128,6 +129,7 @@
}
}
+ @Stable
fun toSpanStyle(): SpanStyle = SpanStyle(
color = color,
fontSize = fontSize,
@@ -145,6 +147,7 @@
shadow = shadow
)
+ @Stable
fun toParagraphStyle(): ParagraphStyle = ParagraphStyle(
textAlign = textAlign,
textDirectionAlgorithm = textDirectionAlgorithm,
@@ -161,6 +164,7 @@
*
* If the given text style is null, returns this text style.
*/
+ @Stable
fun merge(other: TextStyle? = null): TextStyle {
if (other == null || other == Default) return this
return TextStyle(
@@ -174,6 +178,7 @@
*
* @see merge
*/
+ @Stable
fun merge(other: SpanStyle): TextStyle {
return TextStyle(
spanStyle = toSpanStyle().merge(other),
@@ -186,6 +191,7 @@
*
* @see merge
*/
+ @Stable
fun merge(other: ParagraphStyle): TextStyle {
return TextStyle(
spanStyle = toSpanStyle(),
@@ -196,22 +202,26 @@
/**
* Plus operator overload that applies a [merge].
*/
+ @Stable
operator fun plus(other: TextStyle): TextStyle = this.merge(other)
/**
* Plus operator overload that applies a [merge].
*/
+ @Stable
operator fun plus(other: ParagraphStyle): TextStyle = this.merge(other)
/**
* Plus operator overload that applies a [merge].
*/
+ @Stable
operator fun plus(other: SpanStyle): TextStyle = this.merge(other)
companion object {
/**
* Constant for default text style.
*/
+ @Stable
val Default = TextStyle()
}
}
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt
index ce6dc80..dbb7568 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/font/Font.kt
@@ -16,11 +16,15 @@
package androidx.ui.text.font
+import androidx.compose.Immutable
+import androidx.compose.Stable
+
/**
* The interface of the font resource.
*
* @see ResourceFont
*/
+@Immutable
interface Font {
/**
* The weight of the font. The system uses this to match a font to a font request
@@ -78,6 +82,7 @@
*
* @see FontFamily
*/
+@Stable
fun font(
resId: Int,
weight: FontWeight = FontWeight.Normal,
@@ -87,4 +92,5 @@
/**
* Create a [FontFamily] from this single [font].
*/
+@Stable
fun Font.asFontFamily() = fontFamily(this)
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt
index eadbbf5..1a9a765 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontFamily.kt
@@ -17,6 +17,7 @@
package androidx.ui.text.font
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.text.Typeface
/**
@@ -106,6 +107,7 @@
*
* @param fonts list of font files
*/
+@Stable
fun fontFamily(fonts: List<Font>) = FontListFontFamily(fonts)
/**
@@ -113,6 +115,7 @@
*
* @param fonts list of font files
*/
+@Stable
fun fontFamily(vararg fonts: Font) = FontListFontFamily(fonts.asList())
/**
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt
index f3c6e12..72bce00 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/font/FontWeight.kt
@@ -16,6 +16,7 @@
package androidx.ui.text.font
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
/**
@@ -37,43 +38,61 @@
companion object {
/** [Thin] */
+ @Stable
val W100 = FontWeight(100)
/** [ExtraLight] */
+ @Stable
val W200 = FontWeight(200)
/** [Light] */
+ @Stable
val W300 = FontWeight(300)
/** [Normal] / regular / plain */
+ @Stable
val W400 = FontWeight(400)
/** [Medium] */
+ @Stable
val W500 = FontWeight(500)
/** [SemiBold] */
+ @Stable
val W600 = FontWeight(600)
/** [Bold] */
+ @Stable
val W700 = FontWeight(700)
/** [ExtraBold] */
+ @Stable
val W800 = FontWeight(800)
/** [Black] */
+ @Stable
val W900 = FontWeight(900)
/** Alias for [W100] */
+ @Stable
val Thin = W100
/** Alias for [W200] */
+ @Stable
val ExtraLight = W200
/** Alias for [W300] */
+ @Stable
val Light = W300
/** The default font weight - alias for [W400] */
+ @Stable
val Normal = W400
/** Alias for [W500] */
+ @Stable
val Medium = W500
/** Alias for [W600] */
+ @Stable
val SemiBold = W600
/**
* A commonly used font weight that is heavier than normal - alias for [W700]
*/
+ @Stable
val Bold = W700
/** Alias for [W800] */
+ @Stable
val ExtraBold = W800
/** Alias for [W900] */
+ @Stable
val Black = W900
/** A list of all the font weights. */
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt
index b36cda5..d6d4cb3 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/BaselineShift.kt
@@ -17,6 +17,7 @@
package androidx.ui.text.style
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
/**
@@ -34,16 +35,19 @@
/**
* Default baseline shift for superscript.
*/
+ @Stable
val Superscript = BaselineShift(0.5f)
/**
* Default baseline shift for subscript
*/
+ @Stable
val Subscript = BaselineShift(-0.5f)
/**
* Constant for no baseline shift.
*/
+ @Stable
val None = BaselineShift(0.0f)
}
}
@@ -51,6 +55,7 @@
/**
* Linearly interpolate two [BaselineShift]s.
*/
+@Stable
fun lerp(start: BaselineShift, stop: BaselineShift, fraction: Float): BaselineShift {
return BaselineShift(
lerp(
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt
index 7a5a2ba..536945d 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextDecoration.kt
@@ -16,6 +16,7 @@
package androidx.ui.text.style
import androidx.compose.Immutable
+import androidx.compose.Stable
/**
* Defines a horizontal line to be drawn on the text.
@@ -24,6 +25,7 @@
data class TextDecoration internal constructor(val mask: Int) {
companion object {
+ @Stable
val None: TextDecoration = TextDecoration(0x0)
/**
@@ -31,6 +33,7 @@
*
* @sample androidx.ui.text.samples.TextDecorationUnderlineSample
*/
+ @Stable
val Underline: TextDecoration = TextDecoration(0x1)
/**
@@ -38,6 +41,7 @@
*
* @sample androidx.ui.text.samples.TextDecorationLineThroughSample
*/
+ @Stable
val LineThrough: TextDecoration = TextDecoration(0x2)
/**
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt
index 1c35eb9..2acfb4c 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextGeometricTransform.kt
@@ -17,6 +17,7 @@
package androidx.ui.text.style
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
/**
@@ -34,6 +35,7 @@
val skewX: Float = 0f
) {
companion object {
+ @Stable
internal val None = TextGeometricTransform(1.0f, 0.0f)
}
}
diff --git a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt
index c5c3e73..d54b399 100644
--- a/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt
+++ b/ui/ui-text-core/src/main/java/androidx/ui/text/style/TextIndent.kt
@@ -17,6 +17,7 @@
package androidx.ui.text.style
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.TextUnit
import androidx.ui.unit.sp
import androidx.ui.text.lerpTextUnitInheritable
@@ -36,6 +37,7 @@
/**
* Constant fot no text indent.
*/
+ @Stable
val None = TextIndent()
}
}
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt
index 5eeb2da..d7439d3 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints.kt
@@ -17,6 +17,7 @@
package androidx.ui.core
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.IntPx
import androidx.ui.unit.IntPxSize
import androidx.ui.unit.coerceAtLeast
@@ -58,9 +59,13 @@
*/
@Immutable
data class Constraints(
+ @Stable
val minWidth: IntPx = IntPx.Zero,
+ @Stable
val maxWidth: IntPx = IntPx.Infinity,
+ @Stable
val minHeight: IntPx = IntPx.Zero,
+ @Stable
val maxHeight: IntPx = IntPx.Infinity
) {
init {
@@ -83,11 +88,13 @@
/**
* Creates constraints for fixed size in both dimensions.
*/
+ @Stable
fun fixed(width: IntPx, height: IntPx) = Constraints(width, width, height, height)
/**
* Creates constraints for fixed width and unspecified height.
*/
+ @Stable
fun fixedWidth(width: IntPx) = Constraints(
minWidth = width,
maxWidth = width,
@@ -98,6 +105,7 @@
/**
* Creates constraints for fixed height and unspecified width.
*/
+ @Stable
fun fixedHeight(height: IntPx) = Constraints(
minWidth = IntPx.Zero,
maxWidth = IntPx.Infinity,
@@ -111,33 +119,39 @@
* Whether or not the upper bound on the maximum height.
* @see hasBoundedWidth
*/
+@Stable
val Constraints.hasBoundedHeight get() = maxHeight.isFinite()
/**
* Whether or not the upper bound on the maximum width.
* @see hasBoundedHeight
*/
+@Stable
val Constraints.hasBoundedWidth get() = maxWidth.isFinite()
/**
* Whether there is exactly one width value that satisfies the constraints.
*/
+@Stable
val Constraints.hasFixedWidth get() = maxWidth == minWidth
/**
* Whether there is exactly one height value that satisfies the constraints.
*/
+@Stable
val Constraints.hasFixedHeight get() = maxHeight == minHeight
/**
* Whether the area of a component respecting these constraints will definitely be 0.
* This is true when at least one of maxWidth and maxHeight are 0.
*/
+@Stable
val Constraints.isZero get() = maxWidth == IntPx.Zero || maxHeight == IntPx.Zero
/**
* Returns the result of coercing the current constraints in a different set of constraints.
*/
+@Stable
fun Constraints.enforce(otherConstraints: Constraints) = Constraints(
minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
@@ -148,6 +162,7 @@
/**
* Takes a size and returns the closest size to it that satisfies the constraints.
*/
+@Stable
fun Constraints.constrain(size: IntPxSize) = IntPxSize(
size.width.coerceIn(minWidth, maxWidth),
size.height.coerceIn(minHeight, maxHeight)
@@ -156,6 +171,7 @@
/**
* Takes a size and returns whether it satisfies the current constraints.
*/
+@Stable
fun Constraints.satisfiedBy(size: IntPxSize) =
minWidth <= size.width && size.width <= maxWidth &&
minHeight <= size.height && size.height <= maxHeight
@@ -163,6 +179,7 @@
/**
* Returns the Constraints obtained by offsetting the current instance with the given values.
*/
+@Stable
fun Constraints.offset(horizontal: IntPx = 0.ipx, vertical: IntPx = 0.ipx) = Constraints(
(minWidth + horizontal).coerceAtLeast(0.ipx),
(maxWidth + horizontal).coerceAtLeast(0.ipx),
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt
index e767779..c979e1b 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/core/Constraints2.kt
@@ -18,6 +18,7 @@
package androidx.ui.core
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.IntSize
import androidx.ui.util.annotation.IntRange
import kotlin.math.max
@@ -70,12 +71,14 @@
* * MinFocusHeight
* * MaxFocusHeight
*/
+ @Stable
private val focusIndex
get() = (value and FocusMask).toInt()
/**
* The minimum width that the measurement can take.
*/
+ @Stable
val minWidth: Int
@IntRange(from = 0)
get() {
@@ -87,6 +90,7 @@
* The maximum width that the measurement can take. This will either be
* a positive value greater than or equal to [minWidth] or [Constraints2.Infinity].
*/
+ @Stable
val maxWidth: Int
get() {
val mask = WidthMask[focusIndex]
@@ -97,6 +101,7 @@
/**
* The minimum height that the measurement can take.
*/
+ @Stable
val minHeight: Int
@IntRange(from = 0)
get() {
@@ -110,6 +115,7 @@
* The maximum height that the measurement can take. This will either be
* a positive value greater than or equal to [minHeight] or [Constraints2.Infinity].
*/
+ @Stable
val maxHeight: Int
get() {
val focus = focusIndex
@@ -123,6 +129,7 @@
* Whether or not the upper bound on the maximum width.
* @see hasBoundedHeight
*/
+ @Stable
val hasBoundedWidth: Boolean
get() {
val mask = WidthMask[focusIndex]
@@ -133,6 +140,7 @@
* Whether or not the upper bound on the maximum height.
* @see hasBoundedWidth
*/
+ @Stable
val hasBoundedHeight: Boolean
get() {
val focus = focusIndex
@@ -147,6 +155,7 @@
* [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],
* respectively, or [Infinity].
*/
+ @Stable
fun copy(
minWidth: Int = this.minWidth,
maxWidth: Int = this.maxWidth,
@@ -174,6 +183,7 @@
* be considered infinite. [hasBoundedHeight] or [hasBoundedWidth] will be
* `true` when [maxHeight] or [maxWidth] is [Infinity], respectively.
*/
+ @Stable
const val Infinity = Int.MIN_VALUE / 2
/**
@@ -289,6 +299,7 @@
/**
* Creates constraints for fixed size in both dimensions.
*/
+ @Stable
fun fixed(width: Int, height: Int): Constraints2 {
require(width >= 0 && height >= 0) {
"width($width) and height($height) must be >= 0"
@@ -299,6 +310,7 @@
/**
* Creates constraints for fixed width and unspecified height.
*/
+ @Stable
fun fixedWidth(width: Int): Constraints2 {
require(width >= 0) {
"width($width) must be >= 0"
@@ -314,6 +326,7 @@
/**
* Creates constraints for fixed height and unspecified width.
*/
+ @Stable
fun fixedHeight(height: Int): Constraints2 {
require(height >= 0) {
"height($height) must be >= 0"
@@ -390,6 +403,7 @@
* [maxWidth] and [maxHeight] must be greater than or equal to [minWidth] and [minHeight],
* respectively, or [Infinity][Constraints2.Infinity].
*/
+@Stable
fun Constraints2(
@IntRange(from = 0)
minWidth: Int = 0,
@@ -413,22 +427,26 @@
/**
* Whether there is exactly one width value that satisfies the constraints.
*/
+@Stable
val Constraints2.hasFixedWidth get() = maxWidth == minWidth
/**
* Whether there is exactly one height value that satisfies the constraints.
*/
+@Stable
val Constraints2.hasFixedHeight get() = maxHeight == minHeight
/**
* Whether the area of a component respecting these constraints will definitely be 0.
* This is true when at least one of maxWidth and maxHeight are 0.
*/
+@Stable
val Constraints2.isZero get() = maxWidth == 0 || maxHeight == 0
/**
* Returns the result of coercing the current constraints in a different set of constraints.
*/
+@Stable
fun Constraints2.enforce(otherConstraints: Constraints2) = Constraints2(
minWidth = minWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
maxWidth = maxWidth.coerceIn(otherConstraints.minWidth, otherConstraints.maxWidth),
@@ -439,6 +457,7 @@
/**
* Takes a size and returns the closest size to it that satisfies the constraints.
*/
+@Stable
fun Constraints2.constrain(size: IntSize) = IntSize(
width = size.width.coerceIn(minWidth, maxWidth),
height = size.height.coerceIn(minHeight, maxHeight)
@@ -447,12 +466,14 @@
/**
* Takes a size and returns whether it satisfies the current constraints.
*/
+@Stable
fun Constraints2.satisfiedBy(size: IntSize) =
size.width in minWidth..maxWidth && size.height in minHeight..maxHeight
/**
* Returns the Constraints obtained by offsetting the current instance with the given values.
*/
+@Stable
fun Constraints2.offset(horizontal: Int = 0, vertical: Int = 0) = Constraints2(
(minWidth + horizontal).coerceAtLeast(0),
(maxWidth + horizontal).coerceAtLeast(0),
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt
index 8ecebb7..47c8601 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Density.kt
@@ -16,6 +16,8 @@
package androidx.ui.unit
+import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.geometry.Rect
import kotlin.math.roundToInt
@@ -25,6 +27,7 @@
* @param density The logical density of the display. This is a scaling factor for the [Dp] unit.
* @param fontScale Current user preference for the scaling factor for fonts.
*/
+@Stable
fun Density(density: Float, fontScale: Float = 1f): Density =
DensityImpl(density, fontScale)
@@ -38,37 +41,44 @@
*
* @sample androidx.ui.unit.samples.WithDensitySample
*/
+@Immutable
interface Density {
/**
* The logical density of the display. This is a scaling factor for the [Dp] unit.
*/
+ @Stable
val density: Float
/**
* Current user preference for the scaling factor for fonts.
*/
+ @Stable
val fontScale: Float
/**
* Convert [Dp] to pixels. Pixels are used to paint to Canvas.
*/
+ @Stable
fun Dp.toPx(): Float = value * density
/**
* Convert [Dp] to [IntPx] by rounding
*/
+ @Stable
fun Dp.toIntPx(): IntPx = toPx().roundToInt().ipx
/**
* Convert [Dp] to Sp. Sp is used for font size, etc.
*/
+ @Stable
fun Dp.toSp(): TextUnit = TextUnit.Sp(value / fontScale)
/**
* Convert Sp to pixels. Pixels are used to paint to Canvas.
* @throws IllegalStateException if TextUnit other than SP unit is specified.
*/
+ @Stable
fun TextUnit.toPx(): Float {
check(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
return value * fontScale * density
@@ -77,12 +87,14 @@
/**
* Convert Sp to [IntPx] by rounding
*/
+ @Stable
fun TextUnit.toIntPx(): IntPx = toPx().roundToInt().ipx
/**
* Convert Sp to [Dp].
* @throws IllegalStateException if TextUnit other than SP unit is specified.
*/
+ @Stable
fun TextUnit.toDp(): Dp {
check(type == TextUnitType.Sp) { "Only Sp can convert to Px" }
return Dp(value * fontScale)
@@ -91,38 +103,47 @@
/**
* Convert [Px] to [Dp].
*/
+ @Stable
fun Px.toDp(): Dp = (value / density).dp
/**
* Convert [Px] to Sp.
*/
+ @Stable
fun Px.toSp(): TextUnit = (value / (fontScale * density)).sp
/**
* Convert [IntPx] to [Dp].
*/
+ @Stable
fun IntPx.toDp(): Dp = (value / density).dp
/**
* Convert [IntPx] to Sp.
*/
+ @Stable
fun IntPx.toSp(): TextUnit = (value / (fontScale * density)).sp
/** Convert a [Float] pixel value to a Dp */
+ @Stable
fun Float.toDp(): Dp = (this / density).dp
/** Convert a [Float] pixel value to a Sp */
+ @Stable
fun Float.toSp(): TextUnit = (this / (fontScale * density)).sp
/** Convert a [Int] pixel value to a Dp */
+ @Stable
fun Int.toDp(): Dp = toFloat().toDp()
/** Convert a [Int] pixel value to a Sp */
+ @Stable
fun Int.toSp(): TextUnit = toFloat().toSp()
/**
* Convert a [Bounds] to a [Rect].
*/
+ @Stable
fun Bounds.toRect(): Rect {
return Rect(
left.toPx(),
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt
index 0184f2b..67bb906 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Dp.kt
@@ -18,6 +18,7 @@
package androidx.ui.unit
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.Dp.Companion.Hairline
import androidx.ui.util.lerp
import androidx.ui.util.packFloats
@@ -46,66 +47,79 @@
/**
* Add two [Dp]s together.
*/
+ @Stable
inline operator fun plus(other: Dp) =
Dp(value = this.value + other.value)
/**
* Subtract a Dp from another one.
*/
+ @Stable
inline operator fun minus(other: Dp) =
Dp(value = this.value - other.value)
/**
* This is the same as multiplying the Dp by -1.0.
*/
+ @Stable
inline operator fun unaryMinus() = Dp(-value)
/**
* Divide a Dp by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): Dp =
Dp(value = value / other)
+ @Stable
inline operator fun div(other: Int): Dp =
Dp(value = value / other)
/**
* Divide by another Dp to get a scalar.
*/
+ @Stable
inline operator fun div(other: Dp): Float = value / other.value
/**
* Divide by [DpSquared] to get a [DpInverse].
*/
+ @Stable
inline operator fun div(other: DpSquared): DpInverse =
DpInverse(value = value / other.value)
/**
* Multiply a Dp by a scalar.
*/
+ @Stable
inline operator fun times(other: Float): Dp =
Dp(value = value * other)
+ @Stable
inline operator fun times(other: Int): Dp =
Dp(value = value * other)
/**
* Multiply by a Dp to get a [DpSquared] result.
*/
+ @Stable
inline operator fun times(other: Dp): DpSquared =
DpSquared(value = value * other.value)
/**
* Multiply by a Dp to get a [DpSquared] result.
*/
+ @Stable
inline operator fun times(other: DpSquared): DpCubed =
DpCubed(value = value * other.value)
/**
* Support comparing Dimensions with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: Dp) = value.compareTo(other.value)
+ @Stable
override fun toString() = "$value.dp"
companion object {
@@ -113,16 +127,19 @@
* A dimension used to represent a hairline drawing element. Hairline elements take up no
* space, but will draw a single pixel, independent of the device's resolution and density.
*/
+ @Stable
val Hairline = Dp(value = 0f)
/**
* Infinite dp dimension.
*/
+ @Stable
val Infinity = Dp(value = Float.POSITIVE_INFINITY)
/**
* Constant that means unspecified Dp
*/
+ @Stable
val Unspecified = Dp(value = Float.NaN)
}
}
@@ -134,7 +151,8 @@
* // -- or --
* val y = 10.dp
*/
-inline val Int.dp: Dp get() = if (this == 0) Hairline else Dp(value = this.toFloat())
+@Stable
+inline val Int.dp: Dp get() = Dp(value = this.toFloat())
/**
* Create a [Dp] using a [Double]:
@@ -143,7 +161,8 @@
* // -- or --
* val y = 10.0.dp
*/
-inline val Double.dp: Dp get() = if (this == 0.0) Hairline else Dp(value = this.toFloat())
+@Stable
+inline val Double.dp: Dp get() = Dp(value = this.toFloat())
/**
* Create a [Dp] using a [Float]:
@@ -152,28 +171,37 @@
* // -- or --
* val y = 10f.dp
*/
-inline val Float.dp: Dp get() = if (this == 0f) Hairline else Dp(value = this)
+@Stable
+inline val Float.dp: Dp get() = Dp(value = this)
+@Stable
inline operator fun Float.div(other: Dp) =
DpInverse(this / other.value)
+@Stable
inline operator fun Double.div(other: Dp) =
DpInverse(this.toFloat() / other.value)
+@Stable
inline operator fun Int.div(other: Dp) =
DpInverse(this / other.value)
+@Stable
inline operator fun Float.times(other: Dp) =
Dp(this * other.value)
+@Stable
inline operator fun Double.times(other: Dp) =
Dp(this.toFloat() * other.value)
+@Stable
inline operator fun Int.times(other: Dp) =
Dp(this * other.value)
+@Stable
inline fun min(a: Dp, b: Dp): Dp = Dp(value = min(a.value, b.value))
+@Stable
inline fun max(a: Dp, b: Dp): Dp = Dp(value = max(a.value, b.value))
/**
@@ -182,6 +210,7 @@
* @return this value if it's in the range, or [minimumValue] if this value is less than
* [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
*/
+@Stable
inline fun Dp.coerceIn(minimumValue: Dp, maximumValue: Dp): Dp =
Dp(value = value.coerceIn(minimumValue.value, maximumValue.value))
@@ -190,6 +219,7 @@
* @return this value if it's greater than or equal to the [minimumValue] or the
* [minimumValue] otherwise.
*/
+@Stable
inline fun Dp.coerceAtLeast(minimumValue: Dp): Dp =
Dp(value = value.coerceAtLeast(minimumValue.value))
@@ -199,6 +229,7 @@
* @return this value if it's less than or equal to the [maximumValue] or the
* [maximumValue] otherwise.
*/
+@Stable
inline fun Dp.coerceAtMost(maximumValue: Dp): Dp =
Dp(value = value.coerceAtMost(maximumValue.value))
@@ -206,6 +237,7 @@
*
* Return `true` when it is finite or `false` when it is [Dp.Infinity]
*/
+@Stable
inline fun Dp.isFinite(): Boolean = value != Float.POSITIVE_INFINITY
/**
@@ -219,6 +251,7 @@
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid.
*/
+@Stable
fun lerp(start: Dp, stop: Dp, fraction: Float): Dp {
return Dp(lerp(start.value, stop.value, fraction))
}
@@ -238,56 +271,66 @@
/**
* Add two DimensionSquares together.
*/
+ @Stable
inline operator fun plus(other: DpSquared) =
DpSquared(value = value + other.value)
/**
* Subtract a DimensionSquare from another one.
*/
+ @Stable
inline operator fun minus(other: DpSquared) =
DpSquared(value = value - other.value)
/**
* Divide a DimensionSquare by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): DpSquared =
DpSquared(value = value / other)
/**
* Divide by a [Dp] to get a [Dp] result.
*/
+ @Stable
inline operator fun div(other: Dp): Dp =
Dp(value = value / other.value)
/**
* Divide by a DpSquared to get a scalar result.
*/
+ @Stable
inline operator fun div(other: DpSquared): Float = value / other.value
/**
* Divide by a [DpCubed] to get a [DpInverse] result.
*/
+ @Stable
inline operator fun div(other: DpCubed): DpInverse =
DpInverse(value / other.value)
/**
* Multiply by a scalar to get a DpSquared result.
*/
+ @Stable
inline operator fun times(other: Float): DpSquared =
DpSquared(value = value * other)
/**
* Multiply by a scalar to get a DpSquared result.
*/
+ @Stable
inline operator fun times(other: Dp): DpCubed =
DpCubed(value = value * other.value)
/**
* Support comparing DpSquared with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: DpSquared) =
value.compareTo(other.value)
+ @Stable
override fun toString(): String = "$value.dp^2"
}
@@ -307,50 +350,59 @@
/**
* Add two DpCubed together.
*/
+ @Stable
inline operator fun plus(dimension: DpCubed) =
DpCubed(value = value + dimension.value)
/**
* Subtract a DpCubed from another one.
*/
+ @Stable
inline operator fun minus(dimension: DpCubed) =
DpCubed(value = value - dimension.value)
/**
* Divide a DpCubed by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): DpCubed =
DpCubed(value = value / other)
/**
* Divide by a [Dp] to get a [DpSquared] result.
*/
+ @Stable
inline operator fun div(other: Dp): DpSquared =
DpSquared(value = value / other.value)
/**
* Divide by a [DpSquared] to get a [Dp] result.
*/
+ @Stable
inline operator fun div(other: DpSquared): Dp =
Dp(value = value / other.value)
/**
* Divide by a DpCubed to get a scalar result.
*/
+ @Stable
inline operator fun div(other: DpCubed): Float = value / other.value
/**
* Multiply by a scalar to get a DpCubed result.
*/
+ @Stable
inline operator fun times(other: Float): DpCubed =
DpCubed(value = value * other)
/**
* Support comparing DpCubed with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: DpCubed) =
value.compareTo(other.value)
+ @Stable
override fun toString(): String = "$value.dp^3"
}
/**
@@ -368,50 +420,59 @@
/**
* Add two DpInverse together.
*/
+ @Stable
inline operator fun plus(dimension: DpInverse) =
DpInverse(value = value + dimension.value)
/**
* Subtract a DpInverse from another one.
*/
+ @Stable
inline operator fun minus(dimension: DpInverse) =
DpInverse(value = value - dimension.value)
/**
* Divide a DpInverse by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): DpInverse =
DpInverse(value = value / other)
/**
* Multiply by a scalar to get a DpInverse result.
*/
+ @Stable
inline operator fun times(other: Float): DpInverse =
DpInverse(value = value * other)
/**
* Multiply by a [Dp] to get a scalar result.
*/
+ @Stable
inline operator fun times(other: Dp): Float = value * other.value
/**
* Multiply by a [DpSquared] to get a [Dp] result.
*/
+ @Stable
inline operator fun times(other: DpSquared): Dp =
Dp(value = value * other.value)
/**
* Multiply by a [DpCubed] to get a [DpSquared] result.
*/
+ @Stable
inline operator fun times(other: DpCubed): DpSquared =
DpSquared(value = value * other.value)
/**
* Support comparing DpInverse with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: DpInverse) =
value.compareTo(other.value)
+ @Stable
override fun toString(): String = "$value.dp^-1"
}
@@ -429,27 +490,32 @@
/**
* The horizontal aspect of the position in [Dp]
*/
+ @Stable
/*inline*/ val x: Dp
get() = unpackFloat1(value).dp
/**
* The vertical aspect of the position in [Dp]
*/
+ @Stable
/*inline*/ val y: Dp
get() = unpackFloat2(value).dp
/**
* Subtract a [Position] from another one.
*/
+ @Stable
inline operator fun minus(other: Position) =
Position(x - other.x, y - other.y)
/**
* Add a [Position] to another one.
*/
+ @Stable
inline operator fun plus(other: Position) =
Position(x + other.x, y + other.y)
+ @Stable
override fun toString(): String = "($x, $y)"
}
@@ -457,11 +523,13 @@
* Constructs a [Position] from [x] and [y] position [Dp] values.
*/
@OptIn(ExperimentalUnsignedTypes::class)
+@Stable
inline fun Position(x: Dp, y: Dp): Position = Position(packFloats(x.value, y.value))
/**
* The magnitude of the offset represented by this [Position].
*/
+@Stable
fun Position.getDistance(): Dp {
return Dp(sqrt(x.value * x.value + y.value * y.value))
}
@@ -477,6 +545,7 @@
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid.
*/
+@Stable
fun lerp(start: Position, stop: Position, fraction: Float): Position =
Position(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction))
@@ -485,18 +554,24 @@
*/
@Immutable
data class Bounds(
+ @Stable
val left: Dp,
+ @Stable
val top: Dp,
+ @Stable
val right: Dp,
+ @Stable
val bottom: Dp
)
/**
* A width of this Bounds in [Dp].
*/
+@Stable
inline val Bounds.width: Dp get() = right - left
/**
* A height of this Bounds in [Dp].
*/
+@Stable
inline val Bounds.height: Dp get() = bottom - top
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt
index 5db5baa..ee02176 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Duration.kt
@@ -19,6 +19,9 @@
package androidx.ui.unit
+import androidx.compose.Immutable
+import androidx.compose.Stable
+
/*
* The following unit conversion factors are required to be public due to their use
* from inline functions.
@@ -160,38 +163,45 @@
* val aLongWeekend = 86.hours
* assertEquals(3, aLongWeekend.inDays())
*/
+@Immutable
data class Duration(val nanoseconds: Long) : Comparable<Duration> {
/**
* Adds this Duration and [other] and returns the sum as a Duration.
*/
+ @Stable
operator fun plus(other: Duration) = Duration(nanoseconds + other.nanoseconds)
/**
* Subtracts [other] from this Duration and returns the difference.
*/
+ @Stable
operator fun minus(other: Duration) = Duration(nanoseconds - other.nanoseconds)
/**
* Multiplies this Duration by the given [factor] and returns the result.
*/
+ @Stable
operator fun times(factor: Int) = Duration(nanoseconds * factor)
/**
* Multiplies this Duration by the given [factor] and returns the result.
*/
+ @Stable
operator fun times(factor: Double) = Duration((nanoseconds * factor).toLong())
/**
* Divides this Duration by the given [quotient] and returns the truncated
* result as a Duration.
*/
+ @Stable
operator fun div(quotient: Int) = Duration(nanoseconds / quotient)
/**
* Divides this Duration by the given [quotient] and returns the truncated
* result as a Duration.
*/
+ @Stable
operator fun div(quotient: Double) = Duration((nanoseconds / quotient).toLong())
/**
@@ -205,6 +215,7 @@
* It is always the case that `duration1.compareTo(duration2) < 0` if
* `(someDate + duration1).compareTo(someDate + duration2) < 0`.
*/
+ @Stable
override fun compareTo(other: Duration): Int = when {
nanoseconds < other.nanoseconds -> -1
nanoseconds == other.nanoseconds -> 0
@@ -220,6 +231,7 @@
* val d = Duration(days = 1, hours = 1, minutes = 33, microseconds = 500)
* d.toString() // "25:33:00.000500"
*/
+ @Stable
override fun toString(): String {
if (inMicroseconds() < 0) {
return "-${Duration(-nanoseconds)}"
@@ -232,6 +244,7 @@
companion object {
/** An empty Duration. No delay. Instant. */
+ @Stable
val Zero = Duration(0)
}
}
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt
index 8e773cb0..1f90884 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntPx.kt
@@ -18,6 +18,7 @@
package androidx.ui.unit
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.unit.IntPx.Companion.Infinity
import androidx.ui.util.lerp
import androidx.ui.util.packInts
@@ -38,6 +39,7 @@
* Add two [IntPx]s together. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+ @Stable
operator fun plus(other: IntPx) =
keepInfinity(other, IntPx(value = this.value + other.value))
@@ -45,6 +47,7 @@
* Subtract a IntPx from another one. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+ @Stable
operator fun minus(other: IntPx) =
keepInfinity(other, IntPx(value = this.value - other.value))
@@ -52,12 +55,14 @@
* This is the same as multiplying the IntPx by -1. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+ @Stable
operator fun unaryMinus() = keepInfinity(IntPx(-value))
/**
* Divide a IntPx by a scalar and return the rounded result as an IntPx. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+ @Stable
operator fun div(other: Float): IntPx =
keepInfinity(IntPx(value = (value.toFloat() / other).roundToInt()))
@@ -65,6 +70,7 @@
* Divide a IntPx by a scalar and return the rounded result as an IntPx. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+ @Stable
operator fun div(other: Double): IntPx =
keepInfinity(IntPx(value = (value.toDouble() / other).roundToInt()))
@@ -72,6 +78,7 @@
* Divide a IntPx by a scalar and return the rounded result as an IntPx. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+ @Stable
operator fun div(other: Int): IntPx =
keepInfinity(IntPx(value = (value.toFloat() / other).roundToInt()))
@@ -79,37 +86,44 @@
* Multiply a IntPx by a scalar and round the result to an IntPx. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+ @Stable
operator fun times(other: Float): IntPx =
keepInfinity(IntPx(value = (value.toFloat() * other).roundToInt()))
/**
* Multiply a IntPx by a scalar and round the result to an IntPx
*/
+ @Stable
operator fun times(other: Double): IntPx =
keepInfinity(IntPx(value = (value.toDouble() * other).roundToInt()))
/**
* Multiply a IntPx by a scalar and result in an IntPx
*/
+ @Stable
operator fun times(other: Int): IntPx =
keepInfinity(IntPx(value = value * other))
/**
* Returns the remainder of the IntPx when dividing by an integer.
*/
+ @Stable
inline operator fun rem(other: Int): IntPx =
IntPx(value = value % other)
/**
* Support comparing Dimensions with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: IntPx) = value.compareTo(other.value)
/**
* Compares this [IntPx] to another [Px]
*/
+ @Stable
inline operator fun compareTo(other: Px) = value.compareTo(other.value)
+ @Stable
override fun toString() = "$value.ipx"
companion object {
@@ -119,11 +133,13 @@
* that the particular dimension is not regulated and measurement should choose
* the best option without any constraint.
*/
+ @Stable
val Infinity = IntPx(value = Int.MAX_VALUE)
/**
* Zero IntPx dimension. Same as `0.ipx`.
*/
+ @Stable
val Zero = IntPx(value = 0)
}
}
@@ -131,14 +147,17 @@
/**
* Return whether `true` when it is finite or `false` when it is [IntPx.Infinity]
*/
+@Stable
inline fun IntPx.isFinite(): Boolean = value != Int.MAX_VALUE
@PublishedApi
+@Stable
internal inline fun IntPx.keepInfinity(other: IntPx, noInfinityValue: IntPx): IntPx {
return if (!isFinite() || !other.isFinite()) Infinity else noInfinityValue
}
@PublishedApi
+@Stable
internal inline fun IntPx.keepInfinity(noInfinityValue: IntPx): IntPx {
return if (!isFinite()) this else noInfinityValue
}
@@ -150,12 +169,14 @@
* // -- or --
* val y = 10.ipx
*/
+@Stable
inline val Int.ipx: IntPx get() = IntPx(value = this)
/**
* Multiply an IntPx by a Float and round the result to an IntPx. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+@Stable
inline operator fun Float.times(other: IntPx): IntPx =
other.keepInfinity(IntPx(value = (other.value.toFloat() * this).roundToInt()))
@@ -163,6 +184,7 @@
* Multiply an IntPx by a Double and round the result to an IntPx. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+@Stable
inline operator fun Double.times(other: IntPx): IntPx =
other.keepInfinity(IntPx(value = (other.value.toDouble() * this).roundToInt()))
@@ -170,18 +192,21 @@
* Multiply an IntPx by a Double to result in an IntPx. Any operation on an
* [IntPx.Infinity] results in [IntPx.Infinity]
*/
+@Stable
inline operator fun Int.times(other: IntPx): IntPx =
other.keepInfinity(IntPx(value = other.value * this))
/**
* Return the minimum of two [IntPx]s. Any value is considered less than [IntPx.Infinity].
*/
+@Stable
inline fun min(a: IntPx, b: IntPx): IntPx =
IntPx(value = min(a.value, b.value))
/**
* Return the maximum of two [IntPx]s. An [IntPx.Infinity] is considered the maximum value.
*/
+@Stable
inline fun max(a: IntPx, b: IntPx): IntPx =
IntPx(value = max(a.value, b.value))
@@ -191,6 +216,7 @@
* @return this value if it's in the range, or [minimumValue] if this value is less than
* [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
*/
+@Stable
inline fun IntPx.coerceIn(minimumValue: IntPx, maximumValue: IntPx): IntPx =
IntPx(value = value.coerceIn(minimumValue.value, maximumValue.value))
@@ -200,6 +226,7 @@
* @return this value if it's greater than or equal to the [minimumValue] or the
* [minimumValue] otherwise.
*/
+@Stable
inline fun IntPx.coerceAtLeast(minimumValue: IntPx): IntPx =
IntPx(value = value.coerceAtLeast(minimumValue.value))
@@ -210,6 +237,7 @@
* [maximumValue] otherwise. Passing [IntPx.Infinity] as [maximumValue] will
* always return this.
*/
+@Stable
inline fun IntPx.coerceAtMost(maximumValue: IntPx): IntPx =
IntPx(value = value.coerceAtMost(maximumValue.value))
@@ -226,6 +254,7 @@
*
* If [start] or [stop] is [IntPx.Infinity], then [IntPx.Infinity] is returned.
*/
+@Stable
fun lerp(start: IntPx, stop: IntPx, fraction: Float): IntPx {
return start.keepInfinity(stop, IntPx(lerp(start.value, stop.value, fraction)))
}
@@ -233,12 +262,14 @@
/**
* Rounds a [Px] size to the nearest Int pixel value.
*/
+@Stable
inline fun Px.round(): IntPx =
if (value.isInfinite()) Infinity else IntPx(value.roundToInt())
/**
* Rounds up a [Px] to the smallest integer value that is not less than the original value.
*/
+@Stable
inline fun Px.ceil(): IntPx =
if (value.isInfinite()) Infinity else IntPx(kotlin.math.ceil(value).toInt())
@@ -257,33 +288,39 @@
/**
* The horizontal aspect of the size in [IntPx].
*/
+ @Stable
inline val width: IntPx
get() = unpackInt1(value).ipx
/**
* The vertical aspect of the size in [IntPx].
*/
+ @Stable
inline val height: IntPx
get() = unpackInt2(value).ipx
/**
* Returns an IntPxSize scaled by multiplying [width] and [height] by [other]
*/
+ @Stable
inline operator fun times(other: Int): IntPxSize =
IntPxSize(width = width * other, height = height * other)
/**
* Returns an IntPxSize scaled by dividing [width] and [height] by [other]
*/
+ @Stable
inline operator fun div(other: Int): IntPxSize =
IntPxSize(width = width / other, height = height / other)
+ @Stable
override fun toString(): String = "$width x $height"
companion object {
/**
* [IntPxSize] with zero values.
*/
+ @Stable
val Zero = IntPxSize(0.ipx, 0.ipx)
}
}
@@ -292,6 +329,7 @@
* Constructs an [IntPxSize] from width and height [IntPx] values.
*/
@OptIn(ExperimentalUnsignedTypes::class)
+@Stable
inline fun IntPxSize(width: IntPx, height: IntPx): IntPxSize =
IntPxSize(packInts(width.value, height.value))
@@ -299,12 +337,14 @@
* Returns an [IntPxSize] with [size]'s [IntPxSize.width] and [IntPxSize.height]
* multiplied by [this]
*/
+@Stable
inline operator fun Int.times(size: IntPxSize) = size * this
/**
* Returns the [IntPxPosition] of the center of the rect from the point of [0, 0]
* with this [IntPxSize].
*/
+@Stable
fun IntPxSize.center(): IntPxPosition {
return IntPxPosition(width / 2f, height / 2f)
}
@@ -320,35 +360,41 @@
/**
* The horizontal aspect of the position in [IntPx]
*/
+ @Stable
inline val x: IntPx
get() = unpackInt1(value).ipx
/**
* The vertical aspect of the position in [IntPx]
*/
+ @Stable
inline val y: IntPx
get() = unpackInt2(value).ipx
/**
* Subtract a [IntPxPosition] from another one.
*/
+ @Stable
inline operator fun minus(other: IntPxPosition) =
IntPxPosition(x - other.x, y - other.y)
/**
* Add a [IntPxPosition] to another one.
*/
+ @Stable
inline operator fun plus(other: IntPxPosition) =
IntPxPosition(x + other.x, y + other.y)
/**
* Returns a new PxPosition representing the negation of this point.
*/
+ @Stable
inline operator fun unaryMinus() = IntPxPosition(-x, -y)
override fun toString(): String = "($x, $y)"
companion object {
+ @Stable
val Origin = IntPxPosition(0.ipx, 0.ipx)
}
}
@@ -371,12 +417,14 @@
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid.
*/
+@Stable
fun lerp(start: IntPxPosition, stop: IntPxPosition, fraction: Float): IntPxPosition =
IntPxPosition(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction))
/**
* A four dimensional bounds using [IntPx] for units
*/
+@Immutable
data class IntPxBounds(
val left: IntPx,
val top: IntPx,
@@ -387,16 +435,19 @@
/**
* A width of this IntPxBounds in [IntPx].
*/
+@Stable
inline val IntPxBounds.width: IntPx get() = right - left
/**
* A height of this IntPxBounds in [IntPx].
*/
+@Stable
inline val IntPxBounds.height: IntPx get() = bottom - top
/**
* Returns the [IntPxPosition] of the center of the [IntPxBounds].
*/
+@Stable
inline fun IntPxBounds.center(): IntPxPosition {
return IntPxPosition(left + width / 2f, top + height / 2f)
}
@@ -404,6 +455,7 @@
/**
* Convert a [IntPxBounds] to a [IntPxSize].
*/
+@Stable
inline fun IntPxBounds.toSize(): IntPxSize {
return IntPxSize(width, height)
}
@@ -411,20 +463,24 @@
/**
* Create a [PxSize] from [IntPx] values.
*/
+@Stable
inline fun PxSize(width: IntPx, height: IntPx): PxSize =
PxSize(width = width.toPx(), height = height.toPx())
/**
* Create a [PxPosition] from [IntPx] values.
*/
+@Stable
inline fun PxPosition(x: IntPx, y: IntPx): PxPosition = PxPosition(x = x.toPx(), y = y.toPx())
/**
* Convert a [IntPxPosition] to a [PxPosition]
*/
+@Stable
inline fun IntPxPosition.toPxPosition(): PxPosition = PxPosition(this.x, this.y)
/**
* Convert a [IntPxSize] to a [PxSize]
*/
+@Stable
inline fun IntPxSize.toPxSize(): PxSize = PxSize(this.width, this.height)
\ No newline at end of file
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt
index 81386d2..e30c25e 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/IntSize.kt
@@ -19,6 +19,7 @@
package androidx.ui.unit
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.packInts
import androidx.ui.util.unpackInt1
import androidx.ui.util.unpackInt2
@@ -32,28 +33,32 @@
/**
* The horizontal aspect of the size in [Int] pixels.
*/
+ @Stable
val width: Int
get() = unpackInt1(value)
/**
* The vertical aspect of the size in [Int] pixels.
*/
+ @Stable
val height: Int
get() = unpackInt2(value)
/**
* Returns an IntSize scaled by multiplying [width] and [height] by [other]
*/
-
+ @Stable
operator fun times(other: Int): IntSize =
IntSize(width = width * other, height = height * other)
/**
* Returns an IntSize scaled by dividing [width] and [height] by [other]
*/
+ @Stable
operator fun div(other: Int): IntSize =
IntSize(width = width / other, height = height / other)
+ @Stable
override fun toString(): String = "$width x $height"
}
@@ -61,10 +66,12 @@
* Returns an [IntSize] with [size]'s [IntSize.width] and [IntSize.height]
* multiplied by [this].
*/
+@Stable
operator fun Int.times(size: IntSize) = size * this
/**
* Constructs an [IntPxSize] from width and height [IntPx] values.
*/
+@Stable
fun IntSize(width: Int, height: Int): IntSize =
IntSize(packInts(width, height))
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
index d4af92c..753049d 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Px.kt
@@ -18,6 +18,7 @@
package androidx.ui.unit
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.geometry.Offset
import androidx.ui.geometry.Rect
import androidx.ui.util.lerp
@@ -46,94 +47,112 @@
/**
* Add two [Px]s together.
*/
+ @Stable
inline operator fun plus(other: Px) =
Px(value = this.value + other.value)
/**
* Subtract a Px from another one.
*/
+ @Stable
inline operator fun minus(other: Px) =
Px(value = this.value - other.value)
/**
* This is the same as multiplying the Px by -1.0.
*/
+ @Stable
inline operator fun unaryMinus() = Px(-value)
/**
* Divide a Px by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): Px =
Px(value = value / other)
+ @Stable
inline operator fun div(other: Int): Px =
Px(value = value / other)
/**
* Divide by another Px to get a scalar.
*/
+ @Stable
inline operator fun div(other: Px): Float = value / other.value
/**
* Divide by [PxSquared] to get a [PxInverse].
*/
+ @Stable
inline operator fun div(other: PxSquared): PxInverse =
PxInverse(value = value / other.value)
/**
* Multiply a Px by a scalar.
*/
+ @Stable
inline operator fun times(other: Float): Px =
Px(value = value * other)
+ @Stable
inline operator fun times(other: Int): Px =
Px(value = value * other)
/**
* Multiply by a Px to get a [PxSquared] result.
*/
+ @Stable
inline operator fun times(other: Px): PxSquared =
PxSquared(value = value * other.value)
/**
* Multiply by a Px to get a [PxSquared] result.
*/
+ @Stable
inline operator fun times(other: PxSquared): PxCubed =
PxCubed(value = value * other.value)
/**
* Compare [Px] with another [Px].
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: Px) = value.compareTo(other.value)
/**
* Compares this [Px] to another [IntPx]
*/
+ @Stable
inline operator fun compareTo(other: IntPx): Int = value.compareTo(other.value)
/**
* Add an [IntPx] to this [Px].
*/
+ @Stable
inline operator fun plus(other: IntPx) =
Px(value = this.value + other.value)
/**
* Subtract an [IntPx] from this [Px].
*/
+ @Stable
inline operator fun minus(other: IntPx) =
Px(value = this.value - other.value)
+ @Stable
override fun toString() = "$value.px"
companion object {
/**
* Infinite px dimension.
*/
+ @Stable
val Infinity = Px(value = Float.POSITIVE_INFINITY)
/**
* Zero px dimension
*/
+ @Stable
val Zero = Px(0.0f)
}
}
@@ -145,6 +164,7 @@
* // -- or --
* val y = 10.px
*/
+@Stable
inline val Int.px: Px get() = Px(value = this.toFloat())
/**
@@ -154,6 +174,7 @@
* // -- or --
* val y = 10.0.px
*/
+@Stable
inline val Double.px: Px get() = Px(value = this.toFloat())
/**
@@ -163,30 +184,40 @@
* // -- or --
* val y = 10f.px
*/
+@Stable
inline val Float.px: Px get() = Px(value = this)
+@Stable
inline operator fun Float.div(other: Px) =
PxInverse(this / other.value)
+@Stable
inline operator fun Double.div(other: Px) =
PxInverse(this.toFloat() / other.value)
+@Stable
inline operator fun Int.div(other: Px) =
PxInverse(this / other.value)
+@Stable
inline operator fun Float.times(other: Px) =
Px(this * other.value)
+@Stable
inline operator fun Double.times(other: Px) =
Px(this.toFloat() * other.value)
+@Stable
inline operator fun Int.times(other: Px) =
Px(this * other.value)
+@Stable
inline fun min(a: Px, b: Px): Px = Px(value = min(a.value, b.value))
+@Stable
inline fun max(a: Px, b: Px): Px = Px(value = max(a.value, b.value))
+@Stable
inline fun abs(x: Px): Px = Px(abs(x.value))
/**
@@ -195,6 +226,7 @@
* @return this value if it's in the range, or [minimumValue] if this value is less than
* [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
*/
+@Stable
inline fun Px.coerceIn(minimumValue: Px, maximumValue: Px): Px =
Px(value = value.coerceIn(minimumValue.value, maximumValue.value))
@@ -204,6 +236,7 @@
* @return this value if it's greater than or equal to the [minimumValue] or the
* [minimumValue] otherwise.
*/
+@Stable
inline fun Px.coerceAtLeast(minimumValue: Px): Px =
Px(value = value.coerceAtLeast(minimumValue.value))
@@ -213,6 +246,7 @@
* @return this value if it's less than or equal to the [maximumValue] or the
* [maximumValue] otherwise.
*/
+@Stable
inline fun Px.coerceAtMost(maximumValue: Px): Px =
Px(value = value.coerceAtMost(maximumValue.value))
@@ -227,6 +261,7 @@
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid.
*/
+@Stable
fun lerp(start: Px, stop: Px, fraction: Float): Px {
return Px(lerp(start.value, stop.value, fraction))
}
@@ -246,56 +281,66 @@
/**
* Add two DimensionSquares together.
*/
+ @Stable
inline operator fun plus(other: PxSquared) =
PxSquared(value = value + other.value)
/**
* Subtract a DimensionSquare from another one.
*/
+ @Stable
inline operator fun minus(other: PxSquared) =
PxSquared(value = value - other.value)
/**
* Divide a DimensionSquare by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): PxSquared =
PxSquared(value = value / other)
/**
* Divide by a [Px] to get a [Px] result.
*/
+ @Stable
inline operator fun div(other: Px): Px =
Px(value = value / other.value)
/**
* Divide by a PxSquared to get a scalar result.
*/
+ @Stable
inline operator fun div(other: PxSquared): Float = value / other.value
/**
* Divide by a [PxCubed] to get a [PxInverse] result.
*/
+ @Stable
inline operator fun div(other: PxCubed): PxInverse =
PxInverse(value / other.value)
/**
* Multiply by a scalar to get a PxSquared result.
*/
+ @Stable
inline operator fun times(other: Float): PxSquared =
PxSquared(value = value * other)
/**
* Multiply by a scalar to get a PxSquared result.
*/
+ @Stable
inline operator fun times(other: Px): PxCubed =
PxCubed(value = value * other.value)
/**
* Support comparing PxSquared with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: PxSquared) =
value.compareTo(other.value)
+ @Stable
override fun toString(): String = "$value.px^2"
}
@@ -314,50 +359,59 @@
/**
* Add two PxCubed together.
*/
+ @Stable
inline operator fun plus(dimension: PxCubed) =
PxCubed(value = value + dimension.value)
/**
* Subtract a PxCubed from another one.
*/
+ @Stable
inline operator fun minus(dimension: PxCubed) =
PxCubed(value = value - dimension.value)
/**
* Divide a PxCubed by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): PxCubed =
PxCubed(value = value / other)
/**
* Divide by a [Px] to get a [PxSquared] result.
*/
+ @Stable
inline operator fun div(other: Px): PxSquared =
PxSquared(value = value / other.value)
/**
* Divide by a [PxSquared] to get a [Px] result.
*/
+ @Stable
inline operator fun div(other: PxSquared): Px =
Px(value = value / other.value)
/**
* Divide by a PxCubed to get a scalar result.
*/
+ @Stable
inline operator fun div(other: PxCubed): Float = value / other.value
/**
* Multiply by a scalar to get a PxCubed result.
*/
+ @Stable
inline operator fun times(other: Float): PxCubed =
PxCubed(value = value * other)
/**
* Support comparing PxCubed with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: PxCubed) =
value.compareTo(other.value)
+ @Stable
override fun toString(): String = "$value.px^3"
}
@@ -376,50 +430,59 @@
/**
* Add two PxInverse together.
*/
+ @Stable
inline operator fun plus(dimension: PxInverse) =
PxInverse(value = value + dimension.value)
/**
* Subtract a PxInverse from another one.
*/
+ @Stable
inline operator fun minus(dimension: PxInverse) =
PxInverse(value = value - dimension.value)
/**
* Divide a PxInverse by a scalar.
*/
+ @Stable
inline operator fun div(other: Float): PxInverse =
PxInverse(value = value / other)
/**
* Multiply by a scalar to get a PxInverse result.
*/
+ @Stable
inline operator fun times(other: Float): PxInverse =
PxInverse(value = value * other)
/**
* Multiply by a [Px] to get a scalar result.
*/
+ @Stable
inline operator fun times(other: Px): Float = value * other.value
/**
* Multiply by a [PxSquared] to get a [Px] result.
*/
+ @Stable
inline operator fun times(other: PxSquared): Px =
Px(value = value * other.value)
/**
* Multiply by a [PxCubed] to get a [PxSquared] result.
*/
+ @Stable
inline operator fun times(other: PxCubed): PxSquared =
PxSquared(value = value * other.value)
/**
* Support comparing PxInverse with comparison operators.
*/
+ @Stable
override /* TODO: inline */ operator fun compareTo(other: PxInverse) =
value.compareTo(other.value)
+ @Stable
override fun toString(): String = "$value.px^-1"
}
@@ -436,60 +499,71 @@
/**
* The horizontal aspect of the size in [Px].
*/
+ @Stable
inline val width: Px
get() = unpackFloat1(value).px
/**
* The vertical aspect of the size in [Px].
*/
+ @Stable
inline val height: Px
get() = unpackFloat2(value).px
/**
* Returns a PxSize scaled by multiplying [width] and [height] by [other]
*/
+ @Stable
inline operator fun times(other: Int): PxSize =
PxSize(width = width * other, height = height * other)
/**
* Returns a PxSize scaled by multiplying [width] and [height] by [other]
*/
+ @Stable
inline operator fun times(other: Float): PxSize =
PxSize(width = width * other, height = height * other)
/**
* Returns a PxSize scaled by multiplying [width] and [height] by [other]
*/
+ @Stable
inline operator fun times(other: Double): PxSize = times(other.toFloat())
/**
* Returns a PxSize scaled by dividing [width] and [height] by [other]
*/
+ @Stable
inline operator fun div(other: Int): PxSize =
PxSize(width = width / other, height = height / other)
/**
* Returns a PxSize scaled by dividing [width] and [height] by [other]
*/
+ @Stable
inline operator fun div(other: Float): PxSize =
PxSize(width = width / other, height = height / other)
/**
* Returns a PxSize scaled by dividing [width] and [height] by [other]
*/
+ @Stable
inline operator fun div(other: Double): PxSize = div(other.toFloat())
+ @Stable
override fun toString(): String = "$width x $height"
companion object {
/**
* [PxSize] with zero values.
*/
+ @Stable
val Zero = PxSize(0.px, 0.px)
/**
* Default value indicating no specified size
*/
+ @Stable
val UnspecifiedSize = PxSize(Px.Infinity, Px.Infinity)
}
}
@@ -497,32 +571,38 @@
/**
* Constructs a [PxSize] from width and height Float values.
*/
+@Stable
inline fun PxSize(width: Float, height: Float): PxSize = PxSize(packFloats(width, height))
/**
* Constructs a [PxSize] from width and height [Px] values.
*/
+@Stable
inline fun PxSize(width: Px, height: Px): PxSize = PxSize(packFloats(width.value, height.value))
/**
* Returns a [PxSize] with [size]'s [PxSize.width] and [PxSize.height] multiplied by [this]
*/
+@Stable
inline operator fun Int.times(size: PxSize) = size * this
/**
* Returns a [PxSize] with [size]'s [PxSize.width] and [PxSize.height] multiplied by [this]
*/
+@Stable
inline operator fun Float.times(size: PxSize) = size * this
/**
* Returns a [PxSize] with [size]'s [PxSize.width] and [PxSize.height] multiplied by [this]
*/
+@Stable
inline operator fun Double.times(size: PxSize) = size * this
/**
* Returns the [PxPosition] of the center of the rect from the point of [0, 0]
* with this [PxSize].
*/
+@Stable
fun PxSize.center(): PxPosition {
return PxPosition(width / 2f, height / 2f)
}
@@ -530,6 +610,7 @@
/**
* Returns the smallest dimension size.
*/
+@Stable
val PxSize.minDimension get() = min(width, height)
/**
@@ -540,47 +621,56 @@
/**
* The horizontal aspect of the position in [Px]
*/
+ @Stable
inline val x: Px
get() = unpackFloat1(value).px
/**
* The vertical aspect of the position in [Px]
*/
+ @Stable
inline val y: Px
get() = unpackFloat2(value).px
/**
* Subtract a [PxPosition] from another one.
*/
+ @Stable
inline operator fun minus(other: PxPosition) =
PxPosition(x - other.x, y - other.y)
/**
* Add a [PxPosition] to another one.
*/
+ @Stable
inline operator fun plus(other: PxPosition) =
PxPosition(x + other.x, y + other.y)
/**
* Subtract a [IntPxPosition] from this [PxPosition].
*/
+ @Stable
inline operator fun minus(other: IntPxPosition) =
PxPosition(x - other.x, y - other.y)
/**
* Add a [IntPxPosition] to this [PxPosition].
*/
+ @Stable
inline operator fun plus(other: IntPxPosition) =
PxPosition(x + other.x, y + other.y)
/**
* Returns a new PxPosition representing the negation of this point.
*/
+ @Stable
inline operator fun unaryMinus() = PxPosition(-x, -y)
+ @Stable
override fun toString(): String = "($x, $y)"
companion object {
+ @Stable
val Origin = PxPosition(0.px, 0.px)
}
}
@@ -588,26 +678,31 @@
/**
* Constructs a [PxPosition] from [x] and [y] position float values.
*/
+@Stable
inline fun PxPosition(x: Float, y: Float): PxPosition = PxPosition(packFloats(x, y))
/**
* Constructs a [PxPosition] from [x] and [y] position [Px] values.
*/
+@Stable
inline fun PxPosition(x: Px, y: Px): PxPosition = PxPosition(packFloats(x.value, y.value))
/**
* The magnitude of the offset represented by this [PxPosition].
*/
+@Stable
fun PxPosition.getDistance(): Px = Px(sqrt(x.value * x.value + y.value * y.value))
/**
* Convert a [PxPosition] to a [Offset].
*/
+@Stable
inline fun PxPosition.toOffset(): Offset = Offset(x.value, y.value)
/**
* Round a [PxPosition] down to the nearest [Int] coordinates.
*/
+@Stable
inline fun PxPosition.round(): IntPxPosition = IntPxPosition(x.round(), y.round())
/**
@@ -621,6 +716,7 @@
* between [start] and [stop]. The interpolation can be extrapolated beyond 0.0 and
* 1.0, so negative values and values greater than 1.0 are valid.
*/
+@Stable
fun lerp(start: PxPosition, stop: PxPosition, fraction: Float): PxPosition =
PxPosition(lerp(start.x, stop.x, fraction), lerp(start.y, stop.y, fraction))
@@ -635,6 +731,7 @@
val bottom: Px
)
+@Stable
inline fun PxBounds(topLeft: PxPosition, size: PxSize) =
PxBounds(
left = topLeft.x,
@@ -646,16 +743,19 @@
/**
* A width of this PxBounds in [Px].
*/
+@Stable
inline val PxBounds.width: Px get() = right - left
/**
* A height of this PxBounds in [Px].
*/
+@Stable
inline val PxBounds.height: Px get() = bottom - top
/**
* Returns the [PxPosition] of the center of the [PxBounds].
*/
+@Stable
inline fun PxBounds.center(): PxPosition {
return PxPosition(left + width / 2f, top + height / 2f)
}
@@ -663,6 +763,7 @@
/**
* Convert a [PxBounds] to a [PxSize].
*/
+@Stable
fun PxBounds.toSize(): PxSize {
return PxSize(width, height)
}
@@ -671,6 +772,7 @@
* Convert a [PxSize] to a [PxBounds]. The left and top are 0.px and the right and bottom
* are the width and height, respectively.
*/
+@Stable
fun PxSize.toBounds(): PxBounds {
return PxBounds(0.px, 0.px, width, height)
}
@@ -678,6 +780,7 @@
/**
* Convert a [PxBounds] to a [Rect].
*/
+@Stable
fun PxBounds.toRect(): Rect {
return Rect(
left.value,
@@ -690,6 +793,7 @@
/**
* Convert a [PxSize] to a [Rect].
*/
+@Stable
fun PxSize.toRect(): Rect {
return Rect(0f, 0f, width.value, height.value)
}
\ No newline at end of file
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
index d90d823..deda1b1 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/TextUnit.kt
@@ -18,6 +18,7 @@
package androidx.ui.unit
import androidx.compose.Immutable
+import androidx.compose.Stable
import androidx.ui.util.lerp
/**
@@ -207,6 +208,7 @@
/**
* A special [TextUnit] instance for representing inheriting from parent value.
*/
+ @Stable
val Inherit = pack(UNIT_TYPE_INHERIT, 0f)
}
@@ -249,31 +251,37 @@
/**
* Creates a SP unit [TextUnit]
*/
+@Stable
val Float.sp: TextUnit get() = pack(UNIT_TYPE_SP, this)
/**
* Creates an EM unit [TextUnit]
*/
+@Stable
val Float.em: TextUnit get() = pack(UNIT_TYPE_EM, this)
/**
* Creates a SP unit [TextUnit]
*/
+@Stable
val Double.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
/**
* Creates an EM unit [TextUnit]
*/
+@Stable
val Double.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
/**
* Creates a SP unit [TextUnit]
*/
+@Stable
val Int.sp: TextUnit get() = pack(UNIT_TYPE_SP, this.toFloat())
/**
* Creates an EM unit [TextUnit]
*/
+@Stable
val Int.em: TextUnit get() = pack(UNIT_TYPE_EM, this.toFloat())
/**
@@ -282,6 +290,7 @@
* This operation works only if the right operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
+@Stable
inline operator fun Float.times(other: TextUnit) = checkArithmetic(other) {
pack(other.rawType, this * other.value)
}
@@ -292,6 +301,7 @@
* This operation works only if the right operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
+@Stable
inline operator fun Double.times(other: TextUnit) = checkArithmetic(other) {
pack(other.rawType, this.toFloat() * other.value)
}
@@ -302,6 +312,7 @@
* This operation works only if the right operand is not equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
+@Stable
inline operator fun Int.times(other: TextUnit) = checkArithmetic(other) {
pack(other.rawType, this * other.value)
}
@@ -313,6 +324,7 @@
* equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
+@Stable
inline fun min(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
if (a.value < b.value) a else b
}
@@ -324,6 +336,7 @@
* equal to [TextUnit.Inherit].
* The result of this operation is the same unit type of the given one.
*/
+@Stable
inline fun max(a: TextUnit, b: TextUnit): TextUnit = checkArithmetic(a, b) {
if (a.value < b.value) b else a
}
@@ -339,6 +352,7 @@
* @return this value if it's in the range, or [minimumValue] if this value is less than
* [minimumValue], or [maximumValue] if this value is greater than [maximumValue].
*/
+@Stable
inline fun TextUnit.coerceIn(minimumValue: TextUnit, maximumValue: TextUnit): TextUnit =
checkArithmetic(this, minimumValue, maximumValue) {
pack(rawType, value.coerceIn(minimumValue.value, maximumValue.value))
@@ -350,6 +364,7 @@
* @return this value if it's greater than or equal to the [minimumValue] or the
* [minimumValue] otherwise.
*/
+@Stable
inline fun TextUnit.coerceAtLeast(minimumValue: TextUnit): TextUnit =
checkArithmetic(this, minimumValue) {
pack(rawType, value.coerceAtLeast(minimumValue.value))
@@ -361,6 +376,7 @@
* @return this value if it's less than or equal to the [maximumValue] or the
* [maximumValue] otherwise.
*/
+@Stable
inline fun TextUnit.coerceAtMost(maximumValue: TextUnit): TextUnit =
checkArithmetic(this, maximumValue) {
pack(rawType, value.coerceAtMost(maximumValue.value))
@@ -401,6 +417,7 @@
return block()
}
+@Stable
fun lerp(a: TextUnit, b: TextUnit, t: Float): TextUnit = checkArithmetic(a, b) {
return pack(a.rawType, lerp(a.value, b.value, t))
}
diff --git a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt
index 2348562..b870169 100644
--- a/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt
+++ b/ui/ui-unit/src/commonMain/kotlin/androidx/ui/unit/Velocity.kt
@@ -16,7 +16,10 @@
package androidx.ui.unit
+import androidx.compose.Immutable
+
/** A velocity in two dimensions. */
+@Immutable
data class Velocity(
/** The number of pixels per second of velocity in the x and y directions. */
val pixelsPerSecond: PxPosition