Skip to content

Commit

Permalink
Expose fold-aware entry points only
Browse files Browse the repository at this point in the history
  • Loading branch information
alexvanyo committed Aug 5, 2022
1 parent 26d1a55 commit dfd4bc3
Show file tree
Hide file tree
Showing 4 changed files with 343 additions and 197 deletions.
14 changes: 6 additions & 8 deletions adaptive/api/current.api
Expand Up @@ -10,17 +10,15 @@ package com.google.accompanist.adaptive {
}

public final class TwoPaneKt {
method public static com.google.accompanist.adaptive.TwoPaneStrategy FixedOffsetHorizontalTwoPaneStrategy(float splitOffset, boolean offsetFromStart, optional float gapWidth);
method public static com.google.accompanist.adaptive.TwoPaneStrategy FixedOffsetVerticalTwoPaneStrategy(float splitOffset, boolean offsetFromTop, optional float gapHeight);
method public static com.google.accompanist.adaptive.TwoPaneStrategy FractionHorizontalTwoPaneStrategy(float splitFraction, optional float gapWidth);
method public static com.google.accompanist.adaptive.TwoPaneStrategy FractionVerticalTwoPaneStrategy(float splitFraction, optional float gapHeight);
method public static com.google.accompanist.adaptive.TwoPaneStrategy HorizontalTwoPaneStrategy(com.google.accompanist.adaptive.TwoPaneStrategy fallbackStrategy, com.google.accompanist.adaptive.WindowGeometry windowGeometry);
method public static com.google.accompanist.adaptive.TwoPaneStrategy HorizontalTwoPaneStrategy(com.google.accompanist.adaptive.WindowGeometry windowGeometry, float splitFraction, optional float gapWidth);
method public static com.google.accompanist.adaptive.TwoPaneStrategy HorizontalTwoPaneStrategy(com.google.accompanist.adaptive.WindowGeometry windowGeometry, float splitOffset, boolean offsetFromStart, optional float gapWidth);
method @androidx.compose.runtime.Composable public static void TwoPane(kotlin.jvm.functions.Function0<kotlin.Unit> first, kotlin.jvm.functions.Function0<kotlin.Unit> second, com.google.accompanist.adaptive.TwoPaneStrategy strategy, optional androidx.compose.ui.Modifier modifier);
method public static com.google.accompanist.adaptive.TwoPaneStrategy TwoPaneStrategy(com.google.accompanist.adaptive.TwoPaneStrategy fallbackStrategy, com.google.accompanist.adaptive.WindowGeometry windowGeometry);
method public static com.google.accompanist.adaptive.TwoPaneStrategy VerticalTwoPaneStrategy(com.google.accompanist.adaptive.TwoPaneStrategy fallbackStrategy, com.google.accompanist.adaptive.WindowGeometry windowGeometry);
method public static com.google.accompanist.adaptive.TwoPaneStrategy TwoPaneStrategy(com.google.accompanist.adaptive.WindowGeometry windowGeometry, com.google.accompanist.adaptive.TwoPaneStrategy defaultStrategy);
method public static com.google.accompanist.adaptive.TwoPaneStrategy VerticalTwoPaneStrategy(com.google.accompanist.adaptive.WindowGeometry windowGeometry, float splitFraction, optional float gapHeight);
method public static com.google.accompanist.adaptive.TwoPaneStrategy VerticalTwoPaneStrategy(com.google.accompanist.adaptive.WindowGeometry windowGeometry, float splitOffset, boolean offsetFromTop, optional float gapHeight);
}

@androidx.compose.runtime.Stable public fun interface TwoPaneStrategy {
public fun interface TwoPaneStrategy {
method public com.google.accompanist.adaptive.SplitResult calculateSplitResult(androidx.compose.ui.unit.Density density, androidx.compose.ui.unit.LayoutDirection layoutDirection, androidx.compose.ui.layout.LayoutCoordinates layoutCoordinates);
}

Expand Down
203 changes: 163 additions & 40 deletions adaptive/src/main/java/com/google/accompanist/adaptive/TwoPane.kt
Expand Up @@ -19,7 +19,6 @@ package com.google.accompanist.adaptive
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
Expand Down Expand Up @@ -144,11 +143,31 @@ fun TwoPane(
}
}

/**
* Returns the specification for where to place a split in [TwoPane] as a result of
* [TwoPaneStrategy.calculateSplitResult]
*/
class SplitResult(

/**
* Whether the split is vertical or horizontal
*/
val isHorizontalSplit: Boolean,

/**
* The bounds that are nether a `start` pane or an `end` pane, but a separation between those
* two. In case width or height is 0 - it means that the gap itself is a 0 width/height, but the
* place within the layout is still defined.
*
* The [gapBounds] should be defined in local bounds to the [TwoPane].
*/
val gapBounds: Rect,
)

/**
* A strategy for configuring the [TwoPane] component, that is responsible for the meta-data
* corresponding to the arrangement of the two panes of the layout.
*/
@Stable
fun interface TwoPaneStrategy {
/**
* Calculates the split result in local bounds of the [TwoPane].
Expand All @@ -165,52 +184,158 @@ fun interface TwoPaneStrategy {
}

/**
* Returns the specification for where to place a split in [TwoPane] as a result of
* [TwoPaneStrategy.calculateSplitResult]
* A strategy for configuring the [TwoPane] component, that is responsible for the meta-data
* corresponding to the arrangement of the two panes of the layout.
*
* This strategy can be conditional: If `null` is returned from [calculateSplitResult], then this
* strategy did not produce a split result to use, and a different strategy should be used.
*/
class SplitResult(

internal fun interface ConditionalTwoPaneStrategy {
/**
* Whether the split is vertical or horizontal
*/
val isHorizontalSplit: Boolean,

/**
* The bounds that are nether a `start` pane or an `end` pane, but a separation between those
* two. In case width or height is 0 - it means that the gap itself is a 0 width/height, but the
* place within the layout is still defined.
* Calculates the split result in local bounds of the [TwoPane], or `null` if this strategy
* does not apply.
*
* The [gapBounds] should be defined in local bounds to the [TwoPane].
* @param density the [Density] for measuring and laying out
* @param layoutDirection the [LayoutDirection] for measuring and laying out
* @param layoutCoordinates the [LayoutCoordinates] of the [TwoPane]
*/
val gapBounds: Rect,
)
fun calculateSplitResult(
density: Density,
layoutDirection: LayoutDirection,
layoutCoordinates: LayoutCoordinates
): SplitResult?
}

/**
* Returns a [TwoPaneStrategy] that will place the slots vertically or horizontally if there is a
* horizontal or vertical fold respectively.
*
* If there is no fold, then the [fallbackStrategy] will be used instead.
* If there is no fold, then the [defaultStrategy] will be used instead.
*/
public fun TwoPaneStrategy(
fallbackStrategy: TwoPaneStrategy,
windowGeometry: WindowGeometry,
): TwoPaneStrategy = HorizontalTwoPaneStrategy(
fallbackStrategy = VerticalTwoPaneStrategy(
fallbackStrategy = fallbackStrategy,
windowGeometry = windowGeometry,
),
windowGeometry = windowGeometry
defaultStrategy: TwoPaneStrategy,
): TwoPaneStrategy = TwoPaneStrategy(
FoldAwareHorizontalTwoPaneStrategy(windowGeometry),
FoldAwareVerticalTwoPaneStrategy(windowGeometry),
defaultStrategy = defaultStrategy
)

/**
* Returns a [TwoPaneStrategy] that will place the slots horizontally if there is a vertical fold.
* Returns a [TwoPaneStrategy] that will place the slots horizontally.
*
* If there is no horizontal fold, then the [fallbackStrategy] will be used instead.
* If there is a vertical fold, then the gap will be placed along the fold.
*
* Otherwise, the gap will be placed at the given [splitFraction] from start, with the given
* [gapWidth].
*/
public fun HorizontalTwoPaneStrategy(
fallbackStrategy: TwoPaneStrategy,
windowGeometry: WindowGeometry,
splitFraction: Float,
gapWidth: Dp = 0.dp,
): TwoPaneStrategy = TwoPaneStrategy(
FoldAwareHorizontalTwoPaneStrategy(windowGeometry),
defaultStrategy = FractionHorizontalTwoPaneStrategy(
splitFraction = splitFraction,
gapWidth = gapWidth
)
)

/**
* Returns a [TwoPaneStrategy] that will place the slots horizontally.
*
* If there is a vertical fold, then the gap will be placed along the fold.
*
* Otherwise, the gap will be placed at [splitOffset] either from the start or end based on
* [offsetFromStart], with the given [gapWidth].
*/
public fun HorizontalTwoPaneStrategy(
windowGeometry: WindowGeometry,
splitOffset: Dp,
offsetFromStart: Boolean,
gapWidth: Dp = 0.dp,
): TwoPaneStrategy = TwoPaneStrategy(
FoldAwareHorizontalTwoPaneStrategy(windowGeometry),
defaultStrategy = FixedOffsetHorizontalTwoPaneStrategy(
splitOffset = splitOffset,
offsetFromStart = offsetFromStart,
gapWidth = gapWidth
)
)

/**
* Returns a [TwoPaneStrategy] that will place the slots horizontally.
*
* If there is a vertical fold, then the gap will be placed along the fold.
*
* Otherwise, the gap will be placed at the given [splitFraction] from top, with the given
* [gapHeight].
*/
public fun VerticalTwoPaneStrategy(
windowGeometry: WindowGeometry,
splitFraction: Float,
gapHeight: Dp = 0.dp,
): TwoPaneStrategy = TwoPaneStrategy(
FoldAwareHorizontalTwoPaneStrategy(windowGeometry),
defaultStrategy = FractionVerticalTwoPaneStrategy(
splitFraction = splitFraction,
gapHeight = gapHeight
)
)

/**
* Returns a [TwoPaneStrategy] that will place the slots horizontally.
*
* If there is a vertical fold, then the gap will be placed along the fold.
*
* Otherwise, the gap will be placed at [splitOffset] either from the top or bottom based on
* [offsetFromTop], with the given [gapHeight].
*/
public fun VerticalTwoPaneStrategy(
windowGeometry: WindowGeometry,
splitOffset: Dp,
offsetFromTop: Boolean,
gapHeight: Dp = 0.dp,
): TwoPaneStrategy = TwoPaneStrategy(
FoldAwareHorizontalTwoPaneStrategy(windowGeometry),
defaultStrategy = FixedOffsetVerticalTwoPaneStrategy(
splitOffset = splitOffset,
offsetFromTop = offsetFromTop,
gapHeight = gapHeight
)
)

/**
* Returns a composite [TwoPaneStrategy].
*
* The conditional strategies (if any) will be attempted in order, and their split result used
* if they return one. If none return a split result, then the [defaultStrategy] will be used,
* which guarantees returning a [SplitResult].
*/
private fun TwoPaneStrategy(
vararg conditionalStrategies: ConditionalTwoPaneStrategy,
defaultStrategy: TwoPaneStrategy
): TwoPaneStrategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates ->
conditionalStrategies.firstNotNullOfOrNull { conditionalTwoPaneStrategy ->
conditionalTwoPaneStrategy.calculateSplitResult(
density = density,
layoutDirection = layoutDirection,
layoutCoordinates = layoutCoordinates
)
} ?: defaultStrategy.calculateSplitResult(
density = density,
layoutDirection = layoutDirection,
layoutCoordinates = layoutCoordinates
)
}

/**
* Returns a [ConditionalTwoPaneStrategy] that will place the slots horizontally if there is a
* vertical fold, or `null` if there is no fold.
*/
private fun FoldAwareHorizontalTwoPaneStrategy(
windowGeometry: WindowGeometry,
): ConditionalTwoPaneStrategy = ConditionalTwoPaneStrategy { _, _, layoutCoordinates ->
val verticalFold = windowGeometry.displayFeatures.find {
it is FoldingFeature && it.orientation == FoldingFeature.Orientation.VERTICAL
} as FoldingFeature?
Expand All @@ -231,19 +356,17 @@ public fun HorizontalTwoPaneStrategy(
)
)
} else {
fallbackStrategy.calculateSplitResult(density, layoutDirection, layoutCoordinates)
null
}
}

/**
* Returns a [TwoPaneStrategy] that will place the slots vertically if there is a horizontal fold.
*
* If there is no vertical fold, then the [fallbackStrategy] will be used instead.
* Returns a [ConditionalTwoPaneStrategy] that will place the slots vertically if there is a
* horizontal fold, or `null` if there is no fold.
*/
public fun VerticalTwoPaneStrategy(
fallbackStrategy: TwoPaneStrategy,
internal fun FoldAwareVerticalTwoPaneStrategy(
windowGeometry: WindowGeometry,
): TwoPaneStrategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates ->
): ConditionalTwoPaneStrategy = ConditionalTwoPaneStrategy { _, _, layoutCoordinates ->
val horizontalFold = windowGeometry.displayFeatures.find {
it is FoldingFeature && it.orientation == FoldingFeature.Orientation.HORIZONTAL
} as FoldingFeature?
Expand All @@ -264,7 +387,7 @@ public fun VerticalTwoPaneStrategy(
)
)
} else {
fallbackStrategy.calculateSplitResult(density, layoutDirection, layoutCoordinates)
null
}
}

Expand All @@ -275,7 +398,7 @@ public fun VerticalTwoPaneStrategy(
*
* This strategy is _not_ fold aware.
*/
public fun FractionHorizontalTwoPaneStrategy(
internal fun FractionHorizontalTwoPaneStrategy(
splitFraction: Float,
gapWidth: Dp = 0.dp,
): TwoPaneStrategy = TwoPaneStrategy { density, layoutDirection, layoutCoordinates ->
Expand Down Expand Up @@ -304,7 +427,7 @@ public fun FractionHorizontalTwoPaneStrategy(
*
* This strategy is _not_ fold aware.
*/
public fun FixedOffsetHorizontalTwoPaneStrategy(
internal fun FixedOffsetHorizontalTwoPaneStrategy(
splitOffset: Dp,
offsetFromStart: Boolean,
gapWidth: Dp = 0.dp,
Expand Down Expand Up @@ -344,7 +467,7 @@ public fun FixedOffsetHorizontalTwoPaneStrategy(
*
* This strategy is _not_ fold aware.
*/
public fun FractionVerticalTwoPaneStrategy(
internal fun FractionVerticalTwoPaneStrategy(
splitFraction: Float,
gapHeight: Dp = 0.dp,
): TwoPaneStrategy = TwoPaneStrategy { density, _, layoutCoordinates ->
Expand All @@ -370,7 +493,7 @@ public fun FractionVerticalTwoPaneStrategy(
*
* This strategy is _not_ fold aware.
*/
public fun FixedOffsetVerticalTwoPaneStrategy(
internal fun FixedOffsetVerticalTwoPaneStrategy(
splitOffset: Dp,
offsetFromTop: Boolean,
gapHeight: Dp = 0.dp,
Expand Down

0 comments on commit dfd4bc3

Please sign in to comment.