[go: nahoru, domu]

blob: 2a356d9f8150d3a6b43e5b5a8a5db729d36485ec [file] [log] [blame]
/*
* 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.
*/
@file:Suppress("Deprecation")
package androidx.ui.material
import androidx.compose.Composable
import androidx.ui.core.Constraints
import androidx.ui.core.IntrinsicMeasurable
import androidx.ui.core.IntrinsicMeasureScope
import androidx.ui.core.LayoutDirection
import androidx.ui.core.LayoutModifier
import androidx.ui.core.Measurable
import androidx.ui.core.MeasureScope
import androidx.ui.core.Modifier
import androidx.ui.core.enforce
import androidx.ui.foundation.Box
import androidx.ui.foundation.Clickable
import androidx.ui.foundation.ContentGravity
import androidx.ui.foundation.ProvideTextStyle
import androidx.ui.foundation.shape.corner.CornerSize
import androidx.ui.graphics.Color
import androidx.ui.graphics.Shape
import androidx.ui.layout.Row
import androidx.ui.layout.Spacer
import androidx.ui.layout.padding
import androidx.ui.layout.preferredSizeIn
import androidx.ui.layout.preferredWidth
import androidx.ui.material.ripple.ripple
import androidx.ui.semantics.Semantics
import androidx.ui.unit.Dp
import androidx.ui.unit.IntPx
import androidx.ui.unit.dp
import androidx.ui.unit.ipx
import androidx.ui.unit.max
/**
* A floating action button (FAB) is a button that represents the primary action of a screen.
*
* This FAB is typically used with an [androidx.ui.foundation.Icon]:
*
* @sample androidx.ui.material.samples.SimpleFab
*
* See [ExtendedFloatingActionButton] for an extended FAB that contains text and an optional icon.
*
* @param modifier [Modifier] to be applied to this FAB.
* @param onClick will be called when user clicked on this FAB. The FAB will be disabled
* when it is null.
* @param shape The [Shape] of this FAB
* @param backgroundColor The background color. Use [Color.Transparent] to have no color
* @param contentColor The preferred content color for content inside this FAB
* @param elevation The z-coordinate at which to place this FAB. This controls the size
* of the shadow below the button.
* @param icon the content of this FAB
*/
@Composable
fun FloatingActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
backgroundColor: Color = MaterialTheme.colors.secondary,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = 6.dp,
icon: @Composable() () -> Unit
) {
// Since we're adding layouts in between the clickable layer and the content, we need to
// merge all descendants, or we'll get multiple nodes
Semantics(container = true, mergeAllDescendants = true) {
Surface(
modifier = modifier,
shape = shape,
color = backgroundColor,
contentColor = contentColor,
elevation = elevation
) {
Clickable(onClick, modifier = Modifier.ripple()) {
ProvideTextStyle(MaterialTheme.typography.button) {
Box(
modifier = MinimumFabSizeModifier,
gravity = ContentGravity.Center,
children = icon
)
}
}
}
}
}
/**
* A floating action button (FAB) is a button that represents the primary action of a screen.
*
* This extended FAB contains text and an optional icon that will be placed at the start. See
* [FloatingActionButton] for a FAB that just contains some content, typically an icon.
*
* @sample androidx.ui.material.samples.SimpleExtendedFabWithIcon
*
* @param text Text label displayed inside this FAB
* @param icon Optional icon for this FAB, typically this will be a [androidx.ui.foundation.Icon]
* @param modifier [Modifier] to be applied to this FAB
* @param onClick will be called when user clicked on this FAB. The FAB will be disabled
* when it is null.
* @param shape The [Shape] of this FAB
* @param onClick will be called when user clicked on the button. The button will be disabled
* when it is null.
* @param backgroundColor The background color. Use [Color.Transparent] to have no color
* @param contentColor The preferred content color. Will be used by text and iconography
* @param elevation The z-coordinate at which to place this button. This controls the size
* of the shadow below the button.
*/
@Composable
fun ExtendedFloatingActionButton(
text: @Composable() () -> Unit,
onClick: () -> Unit,
modifier: Modifier = Modifier,
icon: @Composable() (() -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
backgroundColor: Color = MaterialTheme.colors.secondary,
contentColor: Color = contentColorFor(backgroundColor),
elevation: Dp = 6.dp
) {
FloatingActionButton(
modifier = modifier.preferredSizeIn(
minWidth = ExtendedFabSize,
minHeight = ExtendedFabSize
),
onClick = onClick,
shape = shape,
backgroundColor = backgroundColor,
contentColor = contentColor,
elevation = elevation
) {
Box(
modifier = Modifier.padding(
start = ExtendedFabTextPadding,
end = ExtendedFabTextPadding
),
gravity = ContentGravity.Center
) {
if (icon == null) {
text()
} else {
Row(verticalGravity = ContentGravity.CenterVertically) {
icon()
Spacer(Modifier.preferredWidth(ExtendedFabIconPadding))
text()
}
}
}
}
}
/**
* [LayoutModifier] that will set minimum constraints in each dimension to be [FabSize] unless
* there is an incoming, non-zero minimum already. This allows us to define a default minimum in
* [FloatingActionButton], but let [ExtendedFloatingActionButton] override it with a smaller
* minimum just by settings a normal [preferredSizeIn] modifier.
*
* TODO: b/150460257 remove after support for this is added as a SizeModifier / similar
*/
private object MinimumFabSizeModifier : LayoutModifier {
override fun MeasureScope.measure(
measurable: Measurable,
constraints: Constraints,
layoutDirection: LayoutDirection
): MeasureScope.MeasureResult {
val minWidth = constraints.minWidth.takeIf { it != IntPx.Zero }
val minHeight = constraints.minHeight.takeIf { it != IntPx.Zero }
val wrappedConstraints = when {
minWidth != null && minHeight != null -> constraints
else -> {
Constraints(
minWidth = minWidth ?: FabSize.toIntPx(),
minHeight = minHeight ?: FabSize.toIntPx()
).enforce(constraints)
}
}
val placeable = measurable.measure(wrappedConstraints)
return layout(placeable.width, placeable.height) {
placeable.place(0.ipx, 0.ipx)
}
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: IntPx,
layoutDirection: LayoutDirection
) = max(measurable.minIntrinsicWidth(height), FabSize.toIntPx())
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: IntPx,
layoutDirection: LayoutDirection
) = max(measurable.minIntrinsicHeight(width), FabSize.toIntPx())
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurable: IntrinsicMeasurable,
height: IntPx,
layoutDirection: LayoutDirection
) = max(measurable.maxIntrinsicWidth(height), FabSize.toIntPx())
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurable: IntrinsicMeasurable,
width: IntPx,
layoutDirection: LayoutDirection
) = max(measurable.maxIntrinsicHeight(width), FabSize.toIntPx())
}
private val FabSize = 56.dp
private val ExtendedFabSize = 48.dp
private val ExtendedFabIconPadding = 12.dp
private val ExtendedFabTextPadding = 20.dp