| /* |
| * Copyright 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.ui.core |
| |
| import androidx.compose.runtime.Stable |
| |
| /** |
| * An ordered, immutable collection of [modifier elements][Modifier.Element] that decorate or add |
| * behavior to Compose UI elements. For example, backgrounds, padding and click event listeners |
| * decorate or add behavior to rows, text or buttons. |
| * |
| * @sample androidx.ui.core.samples.ModifierUsageSample |
| * |
| * Modifier implementations should offer a fluent factory extension function on [Modifier] for |
| * creating combined modifiers by starting from existing modifiers: |
| * |
| * @sample androidx.ui.core.samples.ModifierFactorySample |
| * |
| * Modifier elements may be combined using [then]. Order is significant; modifier elements that |
| * appear first will be applied first. |
| * |
| * Composables that accept a [Modifier] as a parameter to be applied to the whole component |
| * represented by the composable function should name the parameter `modifier` and |
| * assign the parameter a default value of [Modifier]. It should appear as the first |
| * optional parameter in the parameter list; after all required parameters (except for trailing |
| * lambda parameters) but before any other parameters with default values. Any default modifiers |
| * desired by a composable function should come after the `modifier` parameter's value in the |
| * composable function's implementation, keeping [Modifier] as the default parameter value. |
| * For example: |
| * |
| * @sample androidx.ui.core.samples.ModifierParameterSample |
| * |
| * The pattern above allows default modifiers to still be applied as part of the chain |
| * if a caller also supplies unrelated modifiers. |
| * |
| * Composables that accept modifiers to be applied to a specific subcomponent `foo` |
| * should name the parameter `fooModifier` and follow the same guidelines above for default values |
| * and behavior. Subcomponent modifiers should be grouped together and follow the parent |
| * composable's modifier. For example: |
| * |
| * @sample androidx.ui.core.samples.SubcomponentModifierSample |
| */ |
| @Stable |
| interface Modifier { |
| |
| /** |
| * Accumulates a value starting with [initial] and applying [operation] to the current value |
| * and each element from outside in. |
| * |
| * Elements wrap one another in a chain from left to right; an [Element] that appears to the |
| * left of another in a `+` expression or in [operation]'s parameter order affects all |
| * of the elements that appear after it. [foldIn] may be used to accumulate a value starting |
| * from the parent or head of the modifier chain to the final wrapped child. |
| */ |
| fun <R> foldIn(initial: R, operation: (R, Element) -> R): R |
| |
| /** |
| * Accumulates a value starting with [initial] and applying [operation] to the current value |
| * and each element from inside out. |
| * |
| * Elements wrap one another in a chain from left to right; an [Element] that appears to the |
| * left of another in a `+` expression or in [operation]'s parameter order affects all |
| * of the elements that appear after it. [foldOut] may be used to accumulate a value starting |
| * from the child or tail of the modifier chain up to the parent or head of the chain. |
| */ |
| fun <R> foldOut(initial: R, operation: (Element, R) -> R): R |
| |
| /** |
| * Returns `true` if [predicate] returns true for any [Element] in this [Modifier]. |
| */ |
| fun any(predicate: (Element) -> Boolean): Boolean |
| |
| /** |
| * Returns `true` if [predicate] returns true for all [Element]s in this [Modifier] or if |
| * this [Modifier] contains no [Element]s. |
| */ |
| fun all(predicate: (Element) -> Boolean): Boolean |
| |
| /** |
| * Concatenates this modifier with another. |
| * |
| * Returns a [Modifier] representing this modifier followed by [other] in sequence. |
| */ |
| @Deprecated( |
| "Use \"then\" instead.", |
| replaceWith = ReplaceWith("this.then(other)") |
| ) |
| operator fun plus(other: Modifier): Modifier = then(other) |
| |
| /** |
| * Concatenates this modifier with another. |
| * |
| * Returns a [Modifier] representing this modifier followed by [other] in sequence. |
| */ |
| infix fun then(other: Modifier): Modifier = |
| if (other === Modifier) this else CombinedModifier(this, other) |
| |
| /** |
| * A single element contained within a [Modifier] chain. |
| */ |
| interface Element : Modifier { |
| override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = |
| operation(initial, this) |
| |
| override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = |
| operation(this, initial) |
| |
| override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this) |
| |
| override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this) |
| } |
| |
| /** |
| * The companion object `Modifier` is the empty, default, or starter [Modifier] |
| * that contains no [elements][Element]. Use it to create a new [Modifier] using |
| * modifier extension factory functions: |
| * |
| * @sample androidx.ui.core.samples.ModifierUsageSample |
| * |
| * or as the default value for [Modifier] parameters: |
| * |
| * @sample androidx.ui.core.samples.ModifierParameterSample |
| */ |
| // The companion object implements `Modifier` so that it may be used as the start of a |
| // modifier extension factory expression. |
| companion object : Modifier { |
| override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial |
| override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial |
| override fun any(predicate: (Element) -> Boolean): Boolean = false |
| override fun all(predicate: (Element) -> Boolean): Boolean = true |
| @Deprecated( |
| "Use \"then\" instead.", |
| replaceWith = ReplaceWith("this.then(other)") |
| ) |
| override operator fun plus(other: Modifier): Modifier = then(other) |
| override infix fun then(other: Modifier): Modifier = other |
| override fun toString() = "Modifier" |
| |
| /** |
| * An empty [Modifier] that contains no [elements][Element]. |
| * Suitable for use as a sentinel or default parameter. |
| * |
| * @deprecated Use the [Modifier] companion object instead |
| */ |
| @Deprecated( |
| "use the Modifier companion object instead", |
| replaceWith = ReplaceWith( |
| "Modifier", |
| "androidx.ui.core.Modifier" |
| ) |
| ) |
| val None: Modifier |
| get() = this |
| } |
| } |
| |
| /** |
| * A node in a [Modifier] chain. A CombinedModifier always contains at least two elements; |
| * a Modifier [outer] that wraps around the Modifier [inner]. |
| */ |
| class CombinedModifier( |
| private val outer: Modifier, |
| private val inner: Modifier |
| ) : Modifier { |
| override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R = |
| inner.foldIn(outer.foldIn(initial, operation), operation) |
| |
| override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R = |
| outer.foldOut(inner.foldOut(initial, operation), operation) |
| |
| override fun any(predicate: (Modifier.Element) -> Boolean): Boolean = |
| outer.any(predicate) || inner.any(predicate) |
| |
| override fun all(predicate: (Modifier.Element) -> Boolean): Boolean = |
| outer.all(predicate) && inner.all(predicate) |
| |
| override fun equals(other: Any?): Boolean = |
| other is CombinedModifier && outer == other.outer && inner == other.inner |
| |
| override fun hashCode(): Int = outer.hashCode() + 31 * inner.hashCode() |
| |
| override fun toString() = "[" + foldIn("") { acc, element -> |
| if (acc.isEmpty()) element.toString() else "$acc, $element" |
| } + "]" |
| } |