Skip to content

Commit

Permalink
页面 window insets 考虑 macos 标题栏
Browse files Browse the repository at this point in the history
  • Loading branch information
Him188 committed Nov 10, 2024
1 parent 415a156 commit a6535ed
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ fun CacheManagementPage(
}
},
colors = appBarColors,
windowInsets = AniWindowInsets.forTopAppBarWithoutDesktopTitle(),
)
},
containerColor = Color.Unspecified,
Expand Down
1 change: 0 additions & 1 deletion app/shared/src/commonMain/kotlin/ui/main/AniAppContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,6 @@ private fun AniAppContentImpl(
PeerFilterSettingsPage(
viewModel.state,
Modifier.desktopTitleBarPadding(),
windowInsets = ScaffoldDefaults.contentWindowInsets,
)
}
}
Expand Down
3 changes: 3 additions & 0 deletions app/shared/src/commonMain/kotlin/ui/main/MainScene.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import androidx.compose.animation.core.snap
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Search
Expand Down Expand Up @@ -231,6 +233,7 @@ private fun MainSceneContent(
}
},
navigator = listDetailNavigator,
contentWindowInsets = WindowInsets.safeDrawing, // 不包含 macos 标题栏, 因为左侧有 navigation rail
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ private fun CollectionPageLayout(
}
},
avatar = avatar,
windowInsets = AniWindowInsets.forTopAppBarWithoutDesktopTitle(),
)

filters(CollectionPageFilters)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,26 @@ import me.him188.ani.app.ui.foundation.navigation.BackHandler
* 总之, 你总是需要在 [listPaneContent] 和 [detailPane] 中使用 [PaneScope.paneContentPadding] 和 [PaneScope.paneWindowInsetsPadding] 来处理 window insets.
* [listPaneContent] 内会自动帮你处理 TopAppBar 的 insets.
*
* 如果要在 [detailPane] 内也增加 [AniTopAppBar], 则需要自行处理 insets.
* 如果要在 [detailPane] 内也增加 [AniTopAppBar], 则需要自行处理 insets:
* ```
* detailPane = {
* AniTopAppBar(windowInsets = paneContentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal))
* Box(Modifier.consumeWindowInsets(paneContentWindowInsets.only(WindowInsetsSides.Top))) {
* Column(Modifier.paneContentPadding().paneWindowInsetsPadding()) {
* // ...
* }
* }
* }
* ```
* TODO: 为 [detailPane] 内使用 [AniTopAppBar] 做一个 scaffold
*
* @param listPaneTopAppBar 通常可以放 [AniTopAppBar]. 可以为 `null`, 届时不占额外空间 (也不会造成 insets 消耗).
* @param listPaneTopAppBar 通常可以放 [AniTopAppBar]. 可以为 `null`, 届时不占额外空间 (也不会造成 insets 消耗). 你需要指定 [AniTopAppBar] 的 windowInsets 为 `contentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)`.
* @param listPaneContent 列表内容, 可以是 [Column] 或者 Grid. 需要自行实现 vertical scroll.
* @param detailPane 详情页内容.
* @param listPanePreferredWidth See also [androidx.compose.material3.adaptive.layout.PaneScaffoldScope.preferredWidth]
* @param useSharedTransition 是否在[单页模式][ListDetailLayoutParameters.isSinglePane]时使用 Container Transform 等 [SharedTransitionLayout] 的动画.
* 启用后将会调整切换 pane 时的 fade 动画逻辑来支持 Container Transform.
* @param contentWindowInsets 内容的 [WindowInsets]. 这会影响 [PaneScope.contentWindowInsets].
* @param contentWindowInsets 内容的 [WindowInsets]. 这会影响 [PaneScope.paneContentWindowInsets].
*
* @sample me.him188.ani.app.ui.exploration.search.SearchPageLayout
*/
Expand Down Expand Up @@ -114,7 +125,7 @@ fun <T> AniListDetailPaneScaffold(
override val role: ThreePaneScaffoldRole
get() = threePaneScaffoldScope.role

override val contentWindowInsets: WindowInsets
override val paneContentWindowInsets: WindowInsets
get() = when {
isSinglePane -> contentWindowInsets
else -> contentWindowInsets.only(WindowInsetsSides.Start + WindowInsetsSides.Vertical)
Expand All @@ -132,7 +143,7 @@ fun <T> AniListDetailPaneScaffold(
listPaneTopAppBar(scope)
Column(
Modifier.consumeWindowInsets(
AniWindowInsets.forTopAppBar().only(WindowInsetsSides.Top),
contentWindowInsets.only(WindowInsetsSides.Top),
),
) {
listPaneContent(scope)
Expand All @@ -158,7 +169,7 @@ fun <T> AniListDetailPaneScaffold(
override val role: ThreePaneScaffoldRole
get() = threePaneScaffoldScope.role

override val contentWindowInsets: WindowInsets
override val paneContentWindowInsets: WindowInsets
get() = when {
isSinglePane -> contentWindowInsets
else -> contentWindowInsets.only(WindowInsetsSides.End + WindowInsetsSides.Vertical)
Expand Down Expand Up @@ -230,13 +241,13 @@ interface PaneScope : SharedTransitionScope {
* ```
*/
@Stable
val contentWindowInsets: WindowInsets
val paneContentWindowInsets: WindowInsets

/**
* @see contentWindowInsets
* @see paneContentWindowInsets
*/
@Stable
fun Modifier.paneWindowInsetsPadding(): Modifier = windowInsetsPadding(contentWindowInsets)
fun Modifier.paneWindowInsetsPadding(): Modifier = windowInsetsPadding(paneContentWindowInsets)

/**
* 为 pane 增加自动的 content padding 并 consume 等量的 [WindowInsets]. 通常应用于 pane 的最外层容器:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ fun ExplorationPage(
Icon(Icons.Rounded.Search, "搜索")
}
},
windowInsets = AniWindowInsets.forTopAppBarWithoutDesktopTitle(),
)
},
contentWindowInsets = windowInsets.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -84,6 +84,7 @@ fun SearchPage(
onSelect: (index: Int, item: SubjectPreviewItemInfo) -> Unit = { _, _ -> },
focusSearchBarByDefault: Boolean = true,
navigator: ThreePaneScaffoldNavigator<*> = rememberListDetailPaneScaffoldNavigator(),
contentWindowInsets: WindowInsets = AniWindowInsets.forPageContent(),
) {
BackHandler(navigator.canNavigateBack()) {
navigator.navigateBack()
Expand Down Expand Up @@ -143,6 +144,7 @@ fun SearchPage(
}
},
modifier,
contentWindowInsets = contentWindowInsets,
)

// 不能挪到 searchBar 里面, 否则从详情页回来的时候会重复 focus
Expand Down Expand Up @@ -304,6 +306,7 @@ internal fun SearchPageLayout(
searchResultList: @Composable (PaneScope.() -> Unit),
detailContent: @Composable (PaneScope.() -> Unit),
modifier: Modifier = Modifier,
contentWindowInsets: WindowInsets = AniWindowInsets.forPageContent(),
searchBarHeight: Dp = 64.dp,
) {
AniListDetailPaneScaffold(
Expand All @@ -315,15 +318,11 @@ internal fun SearchPageLayout(
navigationIcon = {
TopAppBarGoBackButton()
},
windowInsets = paneContentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
},
listPaneContent = {
Box(
Modifier
.consumeWindowInsets(
AniWindowInsets.forTopAppBar().only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
),
) {
Box {
Column(
Modifier
.paneContentPadding()
Expand All @@ -336,8 +335,8 @@ internal fun SearchPageLayout(
Row(Modifier.fillMaxWidth()) {
searchBar(
Modifier
// no window insets padding. 让 search bar 自己 consume
.paneContentPadding(),
// no window insets padding. 让 search bar 自己 consume
)
}
}
Expand All @@ -348,5 +347,6 @@ internal fun SearchPageLayout(
modifier,
useSharedTransition = true,
listPanePreferredWidth = 480.dp,
contentWindowInsets = contentWindowInsets,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.material3.DrawerDefaults
import androidx.compose.material3.NavigationBarDefaults
import androidx.compose.material3.NavigationRailDefaults
Expand Down Expand Up @@ -116,9 +117,23 @@ object AniWindowInsets {
@Composable
get() = WindowInsets.safeDrawing + WindowInsets.desktopTitleBar()

/**
* 如果 TopAppBar 会接触窗口左上角, 就使用这个. 因为 macOS 的标题栏是透明且悬浮的.
*/
@Composable
inline fun forTopAppBar() = (systemBars.union(WindowInsets.displayCutout))
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) // 要加上 displayCutout 因为刘海可能会挡住横屏状态下的状态栏返回键

/**
* 如果 TopAppBar 不会接触窗口左上角, 就使用这个.
*/
@Composable
inline fun forTopAppBar() = systemBars + WindowInsets.displayCutout // 刘海可能会挡住横屏状态下的状态栏返回键
inline fun forTopAppBarWithoutDesktopTitle() = (WindowInsets.systemBars.union(WindowInsets.displayCutout))
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) // 要加上 displayCutout 因为刘海可能会挡住横屏状态下的状态栏返回键

/**
* 包含 macOS 标题栏
*/
@Composable
inline fun forPageContent() = safeDrawing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package me.him188.ani.app.ui.settings

import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
Expand Down Expand Up @@ -70,7 +71,6 @@ import me.him188.ani.app.ui.foundation.LocalPlatform
import me.him188.ani.app.ui.foundation.ifThen
import me.him188.ani.app.ui.foundation.layout.AniWindowInsets
import me.him188.ani.app.ui.foundation.layout.cardVerticalPadding
import me.him188.ani.app.ui.foundation.layout.paneHorizontalPadding
import me.him188.ani.app.ui.foundation.layout.paneVerticalPadding
import me.him188.ani.app.ui.foundation.theme.AniThemeDefaults
import me.him188.ani.app.ui.foundation.widgets.TopAppBarGoBackButton
Expand Down Expand Up @@ -143,8 +143,7 @@ fun SettingsPage(
},
tabContent = { currentTab ->
val tabModifier = Modifier
.padding(horizontal = currentWindowAdaptiveInfo().windowSizeClass.paneHorizontalPadding - 8.dp)
Column(Modifier.verticalScroll(rememberScrollState())) {
Column(Modifier.verticalScroll(rememberScrollState()).padding(horizontal = 8.dp)) {
when (currentTab) {
SettingsTab.ABOUT -> AboutTab({ vm.debugTriggerState.triggerDebugMode() }, tabModifier)
SettingsTab.DEBUG -> DebugTab(
Expand Down Expand Up @@ -249,6 +248,7 @@ internal fun SettingsPageLayout(
},
colors = AniThemeDefaults.transparentAppBarColors(),
scrollBehavior = topAppBarScrollBehavior,
windowInsets = paneContentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
},
listPaneContent = {
Expand Down Expand Up @@ -308,16 +308,18 @@ internal fun SettingsPageLayout(
}
},
colors = AniThemeDefaults.transparentAppBarColors(),
windowInsets = paneContentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
}

Column(
Modifier
.paneContentPadding()
.consumeWindowInsets(AniWindowInsets.forTopAppBar().only(WindowInsetsSides.Top))
.paneWindowInsetsPadding(),
) {
tabContent(tab)
Box(Modifier.consumeWindowInsets(paneContentWindowInsets.only(WindowInsetsSides.Top))) {
Column(
Modifier
.paneContentPadding()
.paneWindowInsetsPadding(),
) {
tabContent(tab)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@

package me.him188.ani.app.ui.settings.tabs.media.torrent.peer

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
Expand Down Expand Up @@ -53,13 +52,8 @@ import me.him188.ani.app.ui.settings.tabs.media.torrent.peer.blocklist.BlockList
fun PeerFilterSettingsPage(
state: PeerFilterSettingsState,
modifier: Modifier = Modifier,
windowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
navigator: ThreePaneScaffoldNavigator<Nothing> = rememberListDetailPaneScaffoldNavigator()
) {
val paneModifier = Modifier
.consumeWindowInsets(windowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal))
.fillMaxWidth()

Surface(color = AniThemeDefaults.pageContentBackgroundColor) {
AniListDetailPaneScaffold(
navigator = navigator,
Expand All @@ -68,15 +62,17 @@ fun PeerFilterSettingsPage(
enableSearch = !listDetailLayoutParameters.isSinglePane,
title = { AniTopAppBarDefaults.Title("Peer 过滤和屏蔽设置") },
state = state,
windowInsets = windowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
windowInsets = paneContentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
},
listPaneContent = {
PeerFilterEditPane(
state = state,
showIpBlockingItem = !listDetailLayoutParameters.isSinglePane,
onClickIpBlockSettings = { navigator.navigateTo(ThreePaneScaffoldRole.Primary) },
modifier = paneModifier.paneContentPadding(),
modifier = Modifier
.paneContentPadding()
.paneWindowInsetsPadding(),
)
},
detailPane = {
Expand All @@ -87,21 +83,23 @@ fun PeerFilterSettingsPage(
enableSearch = true,
title = { AniTopAppBarDefaults.Title("管理 IP 黑名单") },
state = state,
windowInsets = windowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
windowInsets = paneContentWindowInsets.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
)
}

BlockListEditPane(
blockedIpList = filteredList,
showTitle = !listDetailLayoutParameters.isSinglePane,
modifier = paneModifier.paneContentPadding(),
onAdd = { state.addBlockedIp(it) },
onRemove = { state.removeBlockedIp(it) },
)
Box(Modifier.consumeWindowInsets(paneContentWindowInsets.only(WindowInsetsSides.Top))) {
BlockListEditPane(
blockedIpList = filteredList,
showTitle = !listDetailLayoutParameters.isSinglePane,
modifier = Modifier
.paneContentPadding()
.paneWindowInsetsPadding(),
onAdd = { state.addBlockedIp(it) },
onRemove = { state.removeBlockedIp(it) },
)
}
},
modifier = modifier
.windowInsetsPadding(windowInsets.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom))
.consumeWindowInsets(windowInsets.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom)),
modifier = modifier,
listPanePreferredWidth = 420.dp,
)
}
Expand Down

0 comments on commit a6535ed

Please sign in to comment.