Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New API, RedwoodLayout.additionalInsets #2491

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Breaking:

New:
- `UIConfiguration.viewInsets` tracks the safe area of the specific `RedwoodView` being targeted. This is currently implemented for views on Android and UIViews on iOS.
- `RedwoodLayout.additionalInsets` configures additional insets for application controls like tab bars and floating action buttons.
- `ConsumeInsets {}` composable consumes insets. Most applications should call this in their root composable function.

Changed:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,42 @@ class TreehouseLayoutTest {
}
}

@Test fun viewInsetsSumsSystemBarsAndAdditionalInsets() = runTest {
val layout = TreehouseLayout(activity, throwingWidgetSystem, activity.onBackPressedDispatcher)
layout.uiConfiguration.test {
assertThat(awaitItem().viewInsets).isEqualTo(Margin.Zero)

layout.additionalInsets = Insets.of(5, 6, 7, 8)
assertThat(awaitItem().viewInsets).isEqualTo(
with(Density(activity.resources)) {
Margin(
start = 5.toDp(),
top = 6.toDp(),
end = 7.toDp(),
bottom = 8.toDp(),
)
},
)

ViewCompat.dispatchApplyWindowInsets(
layout,
WindowInsetsCompat.Builder()
.setInsets(WindowInsetsCompat.Type.systemBars(), Insets.of(10, 20, 30, 40))
.build(),
)
assertThat(awaitItem().viewInsets).isEqualTo(
with(Density(activity.resources)) {
Margin(
start = 15.toDp(),
top = 26.toDp(),
end = 37.toDp(),
bottom = 48.toDp(),
)
},
)
}
}

@Test fun uiConfigurationEmitsLayoutDirectionChanges() = runTest {
val layout = TreehouseLayout(activity, throwingWidgetSystem, activity.onBackPressedDispatcher)
layout.uiConfiguration.test {
Expand Down
2 changes: 2 additions & 0 deletions redwood-widget/api/android/redwood-widget.api
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public final class app/cash/redwood/widget/MutableListChildren : app/cash/redwoo

public class app/cash/redwood/widget/RedwoodLayout : android/view/ViewGroup, app/cash/redwood/widget/RedwoodView {
public fun <init> (Landroid/content/Context;Landroidx/activity/OnBackPressedDispatcher;)V
public final fun getAdditionalInsets ()Landroidx/core/graphics/Insets;
public final fun getChildren ()Lapp/cash/redwood/widget/Widget$Children;
public fun getOnBackPressedDispatcher ()Lapp/cash/redwood/ui/OnBackPressedDispatcher;
public fun getSavedStateRegistry ()Lapp/cash/redwood/widget/SavedStateRegistry;
Expand All @@ -59,6 +60,7 @@ public class app/cash/redwood/widget/RedwoodLayout : android/view/ViewGroup, app
protected fun onConfigurationChanged (Landroid/content/res/Configuration;)V
protected fun onLayout (ZIIII)V
protected fun onMeasure (II)V
public final fun setAdditionalInsets (Landroidx/core/graphics/Insets;)V
}

public abstract interface class app/cash/redwood/widget/RedwoodView {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.activity.OnBackPressedCallback as AndroidOnBackPressedCallback
import androidx.activity.OnBackPressedDispatcher as AndroidOnBackPressedDispatcher
import androidx.core.graphics.Insets
import androidx.core.view.children as viewGroupChildren
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import app.cash.redwood.ui.Cancellable
Expand All @@ -46,15 +47,27 @@ public open class RedwoodLayout(
final override val value: View
get() = this

init {
// The view needs to have an ID to participate in instance state saving.
id = R.id.redwood_layout
}
private var windowInsets: Insets = Insets.NONE
set(value) {
if (field == value) return
field = value
mutableUiConfiguration.value = computeUiConfiguration()
}

/**
* Additional insets that are summed with this view's window insets to produce
* [UiConfiguration.viewInsets]. Use this when an application-layer control like a floating action
* button or toolbar requires content to be inset.
*/
public var additionalInsets: Insets = Insets.NONE
set(value) {
if (field == value) return
field = value
mutableUiConfiguration.value = computeUiConfiguration()
}

private val mutableUiConfiguration = MutableStateFlow(
computeUiConfiguration(
viewInsets = Margin.Zero,
),
computeUiConfiguration(),
)

override val onBackPressedDispatcher: RedwoodOnBackPressedDispatcher =
Expand Down Expand Up @@ -85,10 +98,11 @@ public open class RedwoodLayout(
get() = mutableUiConfiguration

init {
// The view needs to have an ID to participate in instance state saving.
id = R.id.redwood_layout

setOnWindowInsetsChangeListener { insets ->
mutableUiConfiguration.value = computeUiConfiguration(
viewInsets = insets.safeDrawing.toMargin(Density(resources)),
)
windowInsets = insets.safeDrawing
}
}

Expand Down Expand Up @@ -118,13 +132,19 @@ public open class RedwoodLayout(

private fun computeUiConfiguration(
config: Configuration = context.resources.configuration,
viewInsets: Margin = uiConfiguration.value.viewInsets,
): UiConfiguration {
val viewportSize: Size
val density: Double
val viewInsets: Margin
with(Density(resources)) {
density = rawDensity
viewportSize = Size(width.toDp(), height.toDp())
viewInsets = Margin(
start = (windowInsets.left + additionalInsets.left).toDp(),
end = (windowInsets.right + additionalInsets.right).toDp(),
top = (windowInsets.top + additionalInsets.top).toDp(),
bottom = (windowInsets.bottom + additionalInsets.bottom).toDp(),
)
}
return UiConfiguration(
darkMode = (config.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES,
Expand Down
Loading