Make pane expansion public
Relnote: Introduce pane expansion APIs to public
Test: existing test
Bug: 327637983
Change-Id: I301d6a9efc34f352707aaabe45dca05ed40a9f97
diff --git a/compose/material3/adaptive/adaptive-layout/api/current.txt b/compose/material3/adaptive/adaptive-layout/api/current.txt
index 1305c09..c69428b 100644
--- a/compose/material3/adaptive/adaptive-layout/api/current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/current.txt
@@ -36,7 +36,7 @@
public final class ListDetailPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
}
public final class ListDetailPaneScaffoldRole {
@@ -60,6 +60,39 @@
property public final String Hidden;
}
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public abstract sealed class PaneExpansionAnchor {
+ }
+
+ public static final class PaneExpansionAnchor.Offset extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ ctor public PaneExpansionAnchor.Offset(float offset);
+ method public float getOffset();
+ property public final float offset;
+ }
+
+ public static final class PaneExpansionAnchor.Proportion extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ ctor public PaneExpansionAnchor.Proportion(@FloatRange(from=0.0, to=1.0) float proportion);
+ method public float getProportion();
+ property public final float proportion;
+ }
+
+ public final class PaneExpansionDragHandleKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void PaneExpansionDragHandle(androidx.compose.material3.adaptive.layout.PaneExpansionState state, long color, optional androidx.compose.ui.Modifier modifier);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public final class PaneExpansionState implements androidx.compose.foundation.gestures.DraggableState {
+ method public void clear();
+ method public void dispatchRawDelta(float delta);
+ method public suspend Object? drag(androidx.compose.foundation.MutatePriority dragPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.DragScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public boolean isUnspecified();
+ method public void setFirstPaneProportion(@FloatRange(from=0.0, to=1.0) float firstPaneProportion);
+ method public void setFirstPaneWidth(int firstPaneWidth);
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionState.Companion Companion;
+ field public static final int Unspecified = -1; // 0xffffffff
+ }
+
+ public static final class PaneExpansionState.Companion {
+ }
+
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public sealed interface PaneExpansionStateKey {
field public static final androidx.compose.material3.adaptive.layout.PaneExpansionStateKey.Companion Companion;
}
@@ -74,6 +107,11 @@
property public abstract androidx.compose.material3.adaptive.layout.PaneExpansionStateKey paneExpansionStateKey;
}
+ public final class PaneExpansionStateKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(optional androidx.compose.material3.adaptive.layout.PaneExpansionStateKey key, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider keyProvider, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors);
+ }
+
public final class PaneKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void AnimatedPane(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content);
}
@@ -117,7 +155,7 @@
public final class SupportingPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldRole {
diff --git a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
index 1305c09..c69428b 100644
--- a/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive-layout/api/restricted_current.txt
@@ -36,7 +36,7 @@
public final class ListDetailPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
}
public final class ListDetailPaneScaffoldRole {
@@ -60,6 +60,39 @@
property public final String Hidden;
}
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public abstract sealed class PaneExpansionAnchor {
+ }
+
+ public static final class PaneExpansionAnchor.Offset extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ ctor public PaneExpansionAnchor.Offset(float offset);
+ method public float getOffset();
+ property public final float offset;
+ }
+
+ public static final class PaneExpansionAnchor.Proportion extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor {
+ ctor public PaneExpansionAnchor.Proportion(@FloatRange(from=0.0, to=1.0) float proportion);
+ method public float getProportion();
+ property public final float proportion;
+ }
+
+ public final class PaneExpansionDragHandleKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void PaneExpansionDragHandle(androidx.compose.material3.adaptive.layout.PaneExpansionState state, long color, optional androidx.compose.ui.Modifier modifier);
+ }
+
+ @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Stable public final class PaneExpansionState implements androidx.compose.foundation.gestures.DraggableState {
+ method public void clear();
+ method public void dispatchRawDelta(float delta);
+ method public suspend Object? drag(androidx.compose.foundation.MutatePriority dragPriority, kotlin.jvm.functions.Function2<? super androidx.compose.foundation.gestures.DragScope,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object?> block, kotlin.coroutines.Continuation<? super kotlin.Unit>);
+ method public boolean isUnspecified();
+ method public void setFirstPaneProportion(@FloatRange(from=0.0, to=1.0) float firstPaneProportion);
+ method public void setFirstPaneWidth(int firstPaneWidth);
+ field public static final androidx.compose.material3.adaptive.layout.PaneExpansionState.Companion Companion;
+ field public static final int Unspecified = -1; // 0xffffffff
+ }
+
+ public static final class PaneExpansionState.Companion {
+ }
+
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Immutable public sealed interface PaneExpansionStateKey {
field public static final androidx.compose.material3.adaptive.layout.PaneExpansionStateKey.Companion Companion;
}
@@ -74,6 +107,11 @@
property public abstract androidx.compose.material3.adaptive.layout.PaneExpansionStateKey paneExpansionStateKey;
}
+ public final class PaneExpansionStateKt {
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(optional androidx.compose.material3.adaptive.layout.PaneExpansionStateKey key, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static androidx.compose.material3.adaptive.layout.PaneExpansionState rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider keyProvider, optional java.util.List<? extends androidx.compose.material3.adaptive.layout.PaneExpansionAnchor> anchors);
+ }
+
public final class PaneKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void AnimatedPane(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.AnimatedPaneScope,kotlin.Unit> content);
}
@@ -117,7 +155,7 @@
public final class SupportingPaneScaffoldKt {
method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState scaffoldState, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective directive, androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue value, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi public final class SupportingPaneScaffoldRole {
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldScreenshotTest.kt b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldScreenshotTest.kt
index d8bfa84..36723c0 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldScreenshotTest.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldScreenshotTest.kt
@@ -116,8 +116,9 @@
fun threePaneScaffold_paneExpansion_fixedFirstPaneWidth() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPaneWidth =
+ mockPaneExpansionState.setFirstPaneWidth(
with(LocalDensity.current) { 412.dp.roundToPx() }
+ )
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -134,7 +135,7 @@
fun threePaneScaffold_paneExpansion_zeroFirstPaneWidth() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPaneWidth = 0
+ mockPaneExpansionState.setFirstPaneWidth(0)
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -151,8 +152,9 @@
fun threePaneScaffold_paneExpansion_overflowFirstPaneWidth() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPaneWidth =
+ mockPaneExpansionState.setFirstPaneWidth(
with(LocalDensity.current) { 1024.dp.roundToPx() }
+ )
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -169,7 +171,7 @@
fun threePaneScaffold_paneExpansion_fixedFirstPanePercentage() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPanePercentage = 0.5f
+ mockPaneExpansionState.setFirstPaneProportion(0.5f)
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -186,7 +188,7 @@
fun threePaneScaffold_paneExpansion_zeroFirstPanePercentage() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPanePercentage = 0f
+ mockPaneExpansionState.setFirstPaneProportion(0f)
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -203,7 +205,7 @@
fun threePaneScaffold_paneExpansion_smallFirstPanePercentage() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPanePercentage = 0.05f
+ mockPaneExpansionState.setFirstPaneProportion(0.05f)
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -220,7 +222,7 @@
fun threePaneScaffold_paneExpansion_largeFirstPanePercentage() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPanePercentage = 0.95f
+ mockPaneExpansionState.setFirstPaneProportion(0.95f)
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -237,7 +239,7 @@
fun threePaneScaffold_paneExpansion_fullFirstPanePercentage() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPanePercentage = 1.0f
+ mockPaneExpansionState.setFirstPaneProportion(1f)
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState)
}
@@ -254,8 +256,9 @@
fun threePaneScaffold_paneExpansionWithDragHandle_fixedFirstPaneWidth() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPaneWidth =
+ mockPaneExpansionState.setFirstPaneWidth(
with(LocalDensity.current) { 412.dp.roundToPx() }
+ )
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState) { MockDragHandle(it) }
}
@@ -272,7 +275,7 @@
fun threePaneScaffold_paneExpansionWithDragHandle_zeroFirstPaneWidth() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPaneWidth = 0
+ mockPaneExpansionState.setFirstPaneWidth(0)
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState) { MockDragHandle(it) }
}
@@ -289,8 +292,9 @@
fun threePaneScaffold_paneExpansionWithDragHandle_overflowFirstPaneWidth() {
rule.setContentWithSimulatedSize(simulatedWidth = 1024.dp, simulatedHeight = 800.dp) {
val mockPaneExpansionState = PaneExpansionState()
- mockPaneExpansionState.firstPaneWidth =
+ mockPaneExpansionState.setFirstPaneWidth(
with(LocalDensity.current) { 1024.dp.roundToPx() }
+ )
SampleThreePaneScaffoldWithPaneExpansion(mockPaneExpansionState) { MockDragHandle(it) }
}
diff --git a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt
index 860b4dc..957275d 100644
--- a/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/androidInstrumentedTest/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldTest.kt
@@ -270,9 +270,9 @@
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private val MockPaneExpansionAnchors =
listOf(
- PaneExpansionAnchor(percentage = 0),
- PaneExpansionAnchor(startOffset = MockPaneExpansionMiddleAnchor),
- PaneExpansionAnchor(percentage = 100),
+ PaneExpansionAnchor.Proportion(0f),
+ PaneExpansionAnchor.Offset(MockPaneExpansionMiddleAnchor),
+ PaneExpansionAnchor.Proportion(1f),
)
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
index 2ad02fe..0403a04 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
@@ -47,6 +47,13 @@
* @param extraPane the extra pane of the scaffold, which is supposed to hold any supplementary info
* besides the list and the detail panes, for example, a task list or a mini-calendar view of a
* mail app. See [ListDetailPaneScaffoldRole.Extra].
+ * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
+ * change pane expansion state. Note that by default this argument will be `null`, and there won't
+ * be a drag handle rendered and users won't be able to drag to change the pane split. You can
+ * provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
+ * there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
+ * expansion.
+ * @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
@@ -57,6 +64,8 @@
detailPane: @Composable ThreePaneScaffoldScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null,
+ paneExpansionDragHandle: (@Composable (PaneExpansionState) -> Unit)? = null,
+ paneExpansionState: PaneExpansionState = rememberPaneExpansionState(value),
) {
ThreePaneScaffold(
modifier = modifier.fillMaxSize(),
@@ -65,6 +74,8 @@
paneOrder = ListDetailPaneScaffoldDefaults.PaneOrder,
secondaryPane = listPane,
tertiaryPane = extraPane,
+ paneExpansionDragHandle = paneExpansionDragHandle,
+ paneExpansionState = paneExpansionState,
primaryPane = detailPane
)
}
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDragHandle.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDragHandle.kt
index 0a83188..8398174 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDragHandle.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDragHandle.kt
@@ -30,14 +30,20 @@
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
+/**
+ * A default, basic non-customizable implementation of pane expansion drag handle. Note that this
+ * implementation will be deprecated in favor of the corresponding Material3 implementation when
+ * it's available.
+ */
@ExperimentalMaterial3AdaptiveApi
@Composable
-// TODO(b/327637983): Implement this as a customizable component.
-internal fun PaneExpansionDragHandle(
+// TODO(b/327637983): Implement this as a customizable component as a Material3 component.
+fun PaneExpansionDragHandle(
state: PaneExpansionState,
color: Color,
modifier: Modifier = Modifier,
) {
+ // TODO (conradchen): support drag handle motion during scaffold and expansion state change
Box(
modifier = modifier.paneExpansionDragHandle(state).size(24.dp, 48.dp),
contentAlignment = Alignment.Center
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionState.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionState.kt
index 7a0d154..df95da6 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionState.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionState.kt
@@ -16,18 +16,17 @@
package androidx.compose.material3.adaptive.layout
-import androidx.annotation.IntRange
+import androidx.annotation.FloatRange
import androidx.annotation.VisibleForTesting
import androidx.collection.IntList
import androidx.collection.MutableIntList
-import androidx.collection.emptyIntList
import androidx.compose.animation.core.animate
import androidx.compose.foundation.MutatePriority
import androidx.compose.foundation.MutatorMutex
import androidx.compose.foundation.gestures.DragScope
import androidx.compose.foundation.gestures.DraggableState
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
-import androidx.compose.material3.adaptive.layout.PaneExpansionState.Companion.UnspecifiedWidth
+import androidx.compose.material3.adaptive.layout.PaneExpansionState.Companion.Unspecified
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
@@ -40,8 +39,8 @@
import androidx.compose.runtime.setValue
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.isSpecified
import kotlin.math.abs
+import kotlin.math.roundToInt
import kotlinx.coroutines.coroutineScope
/**
@@ -96,7 +95,7 @@
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
-internal fun rememberPaneExpansionState(
+fun rememberPaneExpansionState(
keyProvider: PaneExpansionStateKeyProvider,
anchors: List<PaneExpansionAnchor> = emptyList()
): PaneExpansionState = rememberPaneExpansionState(keyProvider.paneExpansionStateKey, anchors)
@@ -112,7 +111,7 @@
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
-internal fun rememberPaneExpansionState(
+fun rememberPaneExpansionState(
key: PaneExpansionStateKey = PaneExpansionStateKey.Default,
anchors: List<PaneExpansionAnchor> = emptyList()
): PaneExpansionState {
@@ -129,32 +128,35 @@
}
}
+/**
+ * This class manages the pane expansion state for pane scaffolds. By providing and modifying an
+ * instance of this class, you can specify the expanded panes' expansion width or percentage when
+ * pane scaffold is displaying a dual-pane layout.
+ *
+ * This class also serves as the [DraggableState] of pane expansion handle. When a handle
+ * implementation is provided to the associated pane scaffold, the scaffold will use
+ * [PaneExpansionState] to store and manage dragging and anchoring of the handle, and thus the pane
+ * expansion state.
+ */
@ExperimentalMaterial3AdaptiveApi
@Stable
-internal class PaneExpansionState
+class PaneExpansionState
internal constructor(
// TODO(conradchen): Handle state change during dragging and settling
data: PaneExpansionStateData = PaneExpansionStateData(),
- internal var anchors: List<PaneExpansionAnchor> = emptyList()
+ anchors: List<PaneExpansionAnchor> = emptyList()
) : DraggableState {
- var firstPaneWidth: Int
- set(value) {
- data.firstPanePercentageState = Float.NaN
- data.currentDraggingOffsetState = UnspecifiedWidth
- val coercedValue = value.coerceIn(0, maxExpansionWidth)
- data.firstPaneWidthState = coercedValue
- }
- get() = data.firstPaneWidthState
+ internal val firstPaneWidth
+ get() =
+ if (maxExpansionWidth == Unspecified || data.firstPaneWidthState == Unspecified) {
+ Unspecified
+ } else {
+ data.firstPaneWidthState.coerceIn(0, maxExpansionWidth)
+ }
- var firstPanePercentage: Float
- set(value) {
- require(value in 0f..1f) { "Percentage value needs to be in [0, 1]" }
- data.firstPaneWidthState = UnspecifiedWidth
- data.currentDraggingOffsetState = UnspecifiedWidth
- data.firstPanePercentageState = value
- }
- get() = data.firstPanePercentageState
+ internal val firstPaneProportion: Float
+ get() = data.firstPaneProportionState
internal var currentDraggingOffset
get() = data.currentDraggingOffsetState
@@ -179,16 +181,18 @@
get() = isDragging || isSettling
@VisibleForTesting
- internal var maxExpansionWidth = 0
+ internal var maxExpansionWidth by mutableIntStateOf(Unspecified)
private set
// Use this field to store the dragging offset decided by measuring instead of dragging to
// prevent redundant re-composition.
@VisibleForTesting
- internal var currentMeasuredDraggingOffset = UnspecifiedWidth
+ internal var currentMeasuredDraggingOffset = Unspecified
private set
- private var anchorPositions: IntList = emptyIntList()
+ internal var anchors: List<PaneExpansionAnchor> by mutableStateOf(anchors)
+
+ private lateinit var measuredDensity: Density
private val dragScope: DragScope =
object : DragScope {
@@ -197,13 +201,14 @@
private val dragMutex = MutatorMutex()
+ /** Returns `true` if none of [firstPaneWidth] or [firstPaneProportion] has been set. */
fun isUnspecified(): Boolean =
- firstPaneWidth == UnspecifiedWidth &&
- firstPanePercentage.isNaN() &&
- currentDraggingOffset == UnspecifiedWidth
+ firstPaneWidth == Unspecified &&
+ firstPaneProportion.isNaN() &&
+ currentDraggingOffset == Unspecified
override fun dispatchRawDelta(delta: Float) {
- if (currentMeasuredDraggingOffset == UnspecifiedWidth) {
+ if (currentMeasuredDraggingOffset == Unspecified) {
return
}
currentDraggingOffset = (currentMeasuredDraggingOffset + delta).toInt()
@@ -216,11 +221,45 @@
isDragging = false
}
- /** Clears any existing expansion state. */
+ /**
+ * Set the width of the first expanded pane in the layout. When the set value gets applied, it
+ * will be coerced within the range of `[0, the full displayable width of the layout]`.
+ *
+ * Note that setting this value will reset the first pane proportion previously set via
+ * [setFirstPaneProportion] or the current dragging result if there's any. Also if user drags
+ * the pane after setting the first pane width, the user dragging result will take the priority
+ * over this set value when rendering panes, but the set value will be saved.
+ */
+ fun setFirstPaneWidth(firstPaneWidth: Int) {
+ data.firstPaneProportionState = Float.NaN
+ data.currentDraggingOffsetState = Unspecified
+ data.firstPaneWidthState = firstPaneWidth
+ }
+
+ /**
+ * Set the proportion of the first expanded pane in the layout. The set value needs to be within
+ * the range of `[0f, 1f]`, otherwise the setter throws.
+ *
+ * Note that setting this value will reset the first pane width previously set via
+ * [setFirstPaneWidth] or the current dragging result if there's any. Also if user drags the
+ * pane after setting the first pane proportion, the user dragging result will take the priority
+ * over this set value when rendering panes, but the set value will be saved.
+ */
+ fun setFirstPaneProportion(@FloatRange(0.0, 1.0) firstPaneProportion: Float) {
+ require(firstPaneProportion in 0f..1f) { "Proportion value needs to be in [0f, 1f]" }
+ data.firstPaneWidthState = Unspecified
+ data.currentDraggingOffsetState = Unspecified
+ data.firstPaneProportionState = firstPaneProportion
+ }
+
+ /**
+ * Clears any previously set [firstPaneWidth] or [firstPaneProportion], as well as the user
+ * dragging result.
+ */
fun clear() {
- data.firstPaneWidthState = UnspecifiedWidth
- data.firstPanePercentageState = Float.NaN
- data.currentDraggingOffsetState = UnspecifiedWidth
+ data.firstPaneWidthState = Unspecified
+ data.firstPaneProportionState = Float.NaN
+ data.currentDraggingOffsetState = Unspecified
}
internal fun onMeasured(measuredWidth: Int, density: Density) {
@@ -228,10 +267,11 @@
return
}
maxExpansionWidth = measuredWidth
- if (firstPaneWidth != UnspecifiedWidth) {
- firstPaneWidth = firstPaneWidth
+ measuredDensity = density
+ // To re-coerce the value
+ if (currentDraggingOffset != Unspecified) {
+ currentDraggingOffset = currentDraggingOffset
}
- anchorPositions = anchors.toPositions(measuredWidth, density)
}
internal fun onExpansionOffsetMeasured(measuredOffset: Int) {
@@ -239,7 +279,7 @@
}
internal suspend fun settleToAnchorIfNeeded(velocity: Float) {
- val currentAnchorPositions = anchorPositions
+ val currentAnchorPositions = anchors.toPositions(maxExpansionWidth, measuredDensity)
if (currentAnchorPositions.isEmpty()) {
return
}
@@ -283,41 +323,72 @@
)
companion object {
- const val UnspecifiedWidth = -1
+ /** The constant value used to denote the pane expansion is not specified. */
+ const val Unspecified = -1
+
private const val AnchoringVelocityThreshold = 200F
}
}
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
internal class PaneExpansionStateData {
- var firstPaneWidthState by mutableIntStateOf(UnspecifiedWidth)
- var firstPanePercentageState by mutableFloatStateOf(Float.NaN)
- var currentDraggingOffsetState by mutableIntStateOf(UnspecifiedWidth)
+ var firstPaneWidthState by mutableIntStateOf(Unspecified)
+ var firstPaneProportionState by mutableFloatStateOf(Float.NaN)
+ var currentDraggingOffsetState by mutableIntStateOf(Unspecified)
}
+/**
+ * The implementations of this interface represent different types of anchors of pane expansion
+ * dragging. Setting up anchors when create [PaneExpansionState] will force user dragging to snap to
+ * the set anchors after user releases the drag.
+ */
@ExperimentalMaterial3AdaptiveApi
-@Immutable
-internal class PaneExpansionAnchor
-private constructor(
- val percentage: Int,
- val startOffset: Dp // TODO(conradchen): confirm RTL support
-) {
- constructor(@IntRange(0, 100) percentage: Int) : this(percentage, Dp.Unspecified)
+sealed class PaneExpansionAnchor private constructor() {
+ internal abstract fun positionIn(totalSizePx: Int, density: Density): Int
- constructor(startOffset: Dp) : this(Int.MIN_VALUE, startOffset)
+ /**
+ * [PaneExpansionAnchor] implementation that specifies the anchor position in the proportion of
+ * the total size of the layout at the start side of the anchor.
+ *
+ * @property proportion the proportion of the layout at the start side of the anchor. layout.
+ */
+ class Proportion(@FloatRange(0.0, 1.0) val proportion: Float) : PaneExpansionAnchor() {
+ override fun positionIn(totalSizePx: Int, density: Density) =
+ (totalSizePx * proportion).roundToInt().coerceIn(0, totalSizePx)
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is PaneExpansionAnchor) return false
- if (percentage != other.percentage) return false
- if (startOffset != other.startOffset) return false
- return true
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Proportion) return false
+ return proportion == other.proportion
+ }
+
+ override fun hashCode(): Int {
+ return proportion.hashCode()
+ }
}
- override fun hashCode(): Int {
- var result = percentage
- result = 31 * result + startOffset.hashCode()
- return result
+ /**
+ * [PaneExpansionAnchor] implementation that specifies the anchor position in the offset in
+ * [Dp]. If a positive value is provided, the offset will be treated as a start offset, on the
+ * other hand, if a negative value is provided, the absolute value of the provided offset will
+ * be used as an end offset. For example, if -150.dp is provided, the resulted anchor will be at
+ * the position that is 150dp away from the end side of the associated layout.
+ *
+ * @property offset the offset of the anchor in [Dp].
+ */
+ class Offset(val offset: Dp) : PaneExpansionAnchor() {
+ override fun positionIn(totalSizePx: Int, density: Density) =
+ with(density) { offset.toPx() }.toInt().let { if (it < 0) totalSizePx + it else it }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is Offset) return false
+ return offset == other.offset
+ }
+
+ override fun hashCode(): Int {
+ return offset.hashCode()
+ }
}
}
@@ -328,19 +399,7 @@
): IntList {
val anchors = MutableIntList(size)
@Suppress("ListIterator") // Not necessarily a random-accessible list
- forEach { anchor ->
- if (anchor.startOffset.isSpecified) {
- val position =
- with(density) { anchor.startOffset.toPx() }
- .toInt()
- .let { if (it < 0) maxExpansionWidth + it else it }
- if (position in 0..maxExpansionWidth) {
- anchors.add(position)
- }
- } else {
- anchors.add(maxExpansionWidth * anchor.percentage / 100)
- }
- }
+ forEach { anchor -> anchors.add(anchor.positionIn(maxExpansionWidth, density)) }
anchors.sort()
return anchors
}
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
index 700e186..40d8c68 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
@@ -41,6 +41,13 @@
* @param extraPane the extra pane of the scaffold, which is supposed to hold any additional content
* besides the main and the supporting panes, for example, a styling panel in a doc app. See
* [SupportingPaneScaffoldRole.Extra].
+ * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
+ * change pane expansion state. Note that by default this argument will be `null`, and there won't
+ * be a drag handle rendered and users won't be able to drag to change the pane split. You can
+ * provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
+ * there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
+ * expansion.
+ * @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
@@ -51,6 +58,8 @@
supportingPane: @Composable ThreePaneScaffoldScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null,
+ paneExpansionDragHandle: (@Composable (PaneExpansionState) -> Unit)? = null,
+ paneExpansionState: PaneExpansionState = rememberPaneExpansionState(value),
) {
ThreePaneScaffold(
modifier = modifier.fillMaxSize(),
@@ -59,6 +68,8 @@
paneOrder = SupportingPaneScaffoldDefaults.PaneOrder,
secondaryPane = supportingPane,
tertiaryPane = extraPane,
+ paneExpansionDragHandle = paneExpansionDragHandle,
+ paneExpansionState = paneExpansionState,
primaryPane = mainPane
)
}
diff --git a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
index b99b9f5..d85c569 100644
--- a/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
+++ b/compose/material3/adaptive/adaptive-layout/src/commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
@@ -314,9 +314,7 @@
if (!paneExpansionState.isUnspecified() && visiblePanes.size == 2) {
// Pane expansion should override everything
- if (
- paneExpansionState.currentDraggingOffset != PaneExpansionState.UnspecifiedWidth
- ) {
+ if (paneExpansionState.currentDraggingOffset != PaneExpansionState.Unspecified) {
// Respect the user dragging result if there's any
val halfSpacerSize = verticalSpacerSize / 2
if (paneExpansionState.currentDraggingOffset <= halfSpacerSize) {
@@ -366,7 +364,7 @@
val availableWidth = constraints.maxWidth
if (
paneExpansionState.firstPaneWidth == 0 ||
- paneExpansionState.firstPanePercentage == 0f
+ paneExpansionState.firstPaneProportion == 0f
) {
measureAndPlacePaneWithLocalBounds(
outerBounds,
@@ -375,7 +373,7 @@
)
} else if (
paneExpansionState.firstPaneWidth >= availableWidth - verticalSpacerSize ||
- paneExpansionState.firstPanePercentage >= 1f
+ paneExpansionState.firstPaneProportion >= 1f
) {
measureAndPlacePaneWithLocalBounds(
outerBounds,
@@ -385,12 +383,11 @@
} else {
val firstPaneWidth =
if (
- paneExpansionState.firstPaneWidth !=
- PaneExpansionState.UnspecifiedWidth
+ paneExpansionState.firstPaneWidth != PaneExpansionState.Unspecified
) {
paneExpansionState.firstPaneWidth
} else {
- (paneExpansionState.firstPanePercentage *
+ (paneExpansionState.firstPaneProportion *
(availableWidth - verticalSpacerSize))
.toInt()
}
@@ -505,7 +502,7 @@
if (
!paneExpansionState.isDraggingOrSettling ||
paneExpansionState.currentDraggingOffset ==
- PaneExpansionState.UnspecifiedWidth
+ PaneExpansionState.Unspecified
) {
val spacerMiddleOffset =
getSpacerMiddleOffsetX(visiblePanes[0], visiblePanes[1])
@@ -524,7 +521,7 @@
handleOffsetX
)
} else if (!isLookingAhead) {
- paneExpansionState.onExpansionOffsetMeasured(PaneExpansionState.UnspecifiedWidth)
+ paneExpansionState.onExpansionOffsetMeasured(PaneExpansionState.Unspecified)
}
// Place the hidden panes to ensure a proper motion at the AnimatedVisibility,
@@ -703,7 +700,7 @@
maxHandleWidth: Int,
offsetX: Int
) {
- if (offsetX == PaneExpansionState.UnspecifiedWidth) {
+ if (offsetX == PaneExpansionState.Unspecified) {
return
}
val placeables =
@@ -724,7 +721,7 @@
(paneLeft.placedPositionX + paneLeft.measuredWidth + paneRight.placedPositionX) / 2
paneLeft.measuredAndPlaced -> paneLeft.placedPositionX + paneLeft.measuredWidth
paneRight.measuredAndPlaced -> 0
- else -> PaneExpansionState.UnspecifiedWidth
+ else -> PaneExpansionState.Unspecified
}
}
}
diff --git a/compose/material3/adaptive/adaptive-navigation/api/current.txt b/compose/material3/adaptive/adaptive-navigation/api/current.txt
index c1f74e3..962ebd8 100644
--- a/compose/material3/adaptive/adaptive-navigation/api/current.txt
+++ b/compose/material3/adaptive/adaptive-navigation/api/current.txt
@@ -2,8 +2,8 @@
package androidx.compose.material3.adaptive.navigation {
public final class AndroidThreePaneScaffold_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class BackNavigationBehavior {
diff --git a/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt b/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
index c1f74e3..962ebd8 100644
--- a/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
+++ b/compose/material3/adaptive/adaptive-navigation/api/restricted_current.txt
@@ -2,8 +2,8 @@
package androidx.compose.material3.adaptive.navigation {
public final class AndroidThreePaneScaffold_androidKt {
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior);
- method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableListDetailPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> listPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> detailPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
+ method @SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @androidx.compose.runtime.Composable public static void NavigableSupportingPaneScaffold(androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator<java.lang.Object> navigator, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> mainPane, kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit> supportingPane, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,kotlin.Unit>? extraPane, optional String defaultBackBehavior, optional kotlin.jvm.functions.Function1<? super androidx.compose.material3.adaptive.layout.PaneExpansionState,kotlin.Unit>? paneExpansionDragHandle, optional androidx.compose.material3.adaptive.layout.PaneExpansionState paneExpansionState);
}
@SuppressCompatibility @androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi @kotlin.jvm.JvmInline public final value class BackNavigationBehavior {
diff --git a/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt b/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt
index 1efa9f4..e55f244 100644
--- a/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt
+++ b/compose/material3/adaptive/adaptive-navigation/src/androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt
@@ -19,8 +19,11 @@
import androidx.activity.compose.BackHandler
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold as BaseListDetailPaneScaffold
+import androidx.compose.material3.adaptive.layout.PaneExpansionDragHandle
+import androidx.compose.material3.adaptive.layout.PaneExpansionState
import androidx.compose.material3.adaptive.layout.SupportingPaneScaffold as BaseSupportingPaneScaffold
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope
+import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -41,6 +44,13 @@
* mail app. See [ListDetailPaneScaffoldRole.Extra].
* @param defaultBackBehavior the default back navigation behavior when the system back event
* happens. See [BackNavigationBehavior] for the use cases of each behavior.
+ * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
+ * change pane expansion state. Note that by default this argument will be `null`, and there won't
+ * be a drag handle rendered and users won't be able to drag to change the pane split. You can
+ * provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
+ * there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
+ * expansion.
+ * @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
@@ -50,7 +60,9 @@
detailPane: @Composable ThreePaneScaffoldScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null,
- defaultBackBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilContentChange
+ defaultBackBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilContentChange,
+ paneExpansionDragHandle: (@Composable (PaneExpansionState) -> Unit)? = null,
+ paneExpansionState: PaneExpansionState = rememberPaneExpansionState(navigator.scaffoldValue),
) {
// TODO(b/330584029): support predictive back
BackHandler(enabled = navigator.canNavigateBack(defaultBackBehavior)) {
@@ -62,7 +74,9 @@
value = navigator.scaffoldValue,
detailPane = detailPane,
listPane = listPane,
- extraPane = extraPane
+ extraPane = extraPane,
+ paneExpansionDragHandle = paneExpansionDragHandle,
+ paneExpansionState = paneExpansionState,
)
}
@@ -82,6 +96,13 @@
* [SupportingPaneScaffoldRole.Extra].
* @param defaultBackBehavior the default back navigation behavior when the system back event
* happens. See [BackNavigationBehavior] for the use cases of each behavior.
+ * @param paneExpansionDragHandle the pane expansion drag handle to let users be able to drag to
+ * change pane expansion state. Note that by default this argument will be `null`, and there won't
+ * be a drag handle rendered and users won't be able to drag to change the pane split. You can
+ * provide a [PaneExpansionDragHandle] here as our sample suggests. On the other hand, even if
+ * there's no drag handle, you can still modify [paneExpansionState] directly to apply pane
+ * expansion.
+ * @param paneExpansionState the state object of pane expansion.
*/
@ExperimentalMaterial3AdaptiveApi
@Composable
@@ -91,7 +112,9 @@
supportingPane: @Composable ThreePaneScaffoldScope.() -> Unit,
modifier: Modifier = Modifier,
extraPane: (@Composable ThreePaneScaffoldScope.() -> Unit)? = null,
- defaultBackBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilContentChange
+ defaultBackBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilContentChange,
+ paneExpansionDragHandle: (@Composable (PaneExpansionState) -> Unit)? = null,
+ paneExpansionState: PaneExpansionState = rememberPaneExpansionState(navigator.scaffoldValue),
) {
// TODO(b/330584029): support predictive back
BackHandler(enabled = navigator.canNavigateBack(defaultBackBehavior)) {
@@ -103,6 +126,8 @@
value = navigator.scaffoldValue,
mainPane = mainPane,
supportingPane = supportingPane,
- extraPane = extraPane
+ extraPane = extraPane,
+ paneExpansionDragHandle = paneExpansionDragHandle,
+ paneExpansionState = paneExpansionState,
)
}
diff --git a/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt b/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt
index 3a44336..77e13de 100644
--- a/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt
+++ b/compose/material3/adaptive/samples/src/main/java/androidx/compose/material3/adaptive/samples/ThreePaneScaffoldSample.kt
@@ -54,6 +54,9 @@
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
+import androidx.compose.material3.adaptive.layout.PaneExpansionAnchor
+import androidx.compose.material3.adaptive.layout.PaneExpansionDragHandle
+import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
import androidx.compose.material3.adaptive.navigation.BackNavigationBehavior
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
@@ -174,6 +177,14 @@
Text("Extra")
}
}
+ },
+ paneExpansionState =
+ rememberPaneExpansionState(
+ keyProvider = scaffoldNavigator.scaffoldValue,
+ anchors = PaneExpansionAnchors
+ ),
+ paneExpansionDragHandle = { state ->
+ PaneExpansionDragHandle(state = state, color = MaterialTheme.colorScheme.outline)
}
)
}
@@ -362,3 +373,11 @@
}
}
}
+
+@OptIn(ExperimentalMaterial3AdaptiveApi::class)
+private val PaneExpansionAnchors =
+ listOf(
+ PaneExpansionAnchor.Proportion(0f),
+ PaneExpansionAnchor.Proportion(0.5f),
+ PaneExpansionAnchor.Proportion(1f),
+ )