Skip to content
Open
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
17 changes: 8 additions & 9 deletions shared/src/commonMain/kotlin/dev/bilby/ui/live/LiveRoomScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import dev.bilby.ui.player.PlayerTooltip
import dev.bilby.ui.video.LiveSettingsContent
import dev.bilby.ui.video.PlayerSettingsButton
import dev.bilby.ui.video.DanmakuSettingsButton
import dev.bilby.ui.player.ControlBarDanmaku
import dev.bilby.ui.player.ControlBarDanmakuField
import dev.bilby.ui.video.PlayerSettingsHost
Expand Down Expand Up @@ -337,6 +336,10 @@ fun LiveRoomScreen(
)
}
}
PlayerSettingsButton {
settingsOnly = null
settingsOpen = true
}
},
embeddedTopActions = {
OnlineRankLabel(state.onlineRank)
Expand Down Expand Up @@ -435,6 +438,7 @@ fun LiveRoomScreen(
},
// 外壳与内容的排法同播放页,见 PlayerSettingsHost。
panel = {
if (settingsOpen) setMenuOpen(true)
val close = {
settingsOpen = false
setMenuOpen(false)
Expand Down Expand Up @@ -754,8 +758,8 @@ private fun LiveControlBar(
error = danmakuField.error,
onSend = danmakuField.onSend,
onFocusChange = { focused ->
composing = focused
onDanmakuFieldFocusChange(focused)
if (!focused) composing = false
},
autoFocus = !roomy,
focusRequestNonce = danmakuFocusNonce,
Expand Down Expand Up @@ -788,13 +792,8 @@ private fun LiveControlBar(
}
}
if (roomy || !composing) LiveAudioOnlyButton(onlyAudio, onOnlyAudioChange, isFullscreen)
if (wide) {
DanmakuButton(danmakuEnabled, onDanmakuEnabledChange, isFullscreen)
DanmakuSettingsButton { onOpenSettings(PlayerSettingsSection.Danmaku) }
} else {
if (isFullscreen) DanmakuButton(danmakuEnabled, onDanmakuEnabledChange, isFullscreen)
PlayerSettingsButton { onOpenSettings(null) }
}
if (wide || isFullscreen) DanmakuButton(danmakuEnabled, onDanmakuEnabledChange, isFullscreen)
PlayerSettingsButton { onOpenSettings(null) }
FullscreenButton(isFullscreen, onFullscreenToggle)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.ErrorOutline
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
Expand All @@ -26,6 +27,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -38,10 +40,12 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import dev.bilby.resources.Res
import dev.bilby.resources.action_close
import dev.bilby.resources.action_send
import dev.bilby.resources.danmaku_input_hint
import dev.bilby.stringResource
import dev.bilby.ui.components.LoadingSpinner
import dev.bilby.ui.components.ReleaseFocusWhenKeyboardHides
import dev.bilby.ui.components.trackEditableFocus
import dev.bilby.ui.theme.Dimens
import dev.bilby.ui.theme.Spacing
Expand Down Expand Up @@ -91,15 +95,41 @@ internal fun ControlBarDanmakuField(
val hint = stringResource(Res.string.danmaku_input_hint)
val canSend = draft.isNotBlank() && !sending
val focusRequester = remember { FocusRequester() }
if (autoFocus) LaunchedEffect(Unit) { focusRequester.requestFocus() }
val keyboard = LocalSoftwareKeyboardController.current
val focusManager = LocalFocusManager.current
var focused by remember { mutableStateOf(false) }
var wasFocused by remember { mutableStateOf(false) }
var sendPending by remember { mutableStateOf(false) }

if (autoFocus) {
LaunchedEffect(Unit) {
focusRequester.requestFocus()
keyboard?.show()
}
}
LaunchedEffect(focusRequestNonce) {
if (focusRequestNonce > 0) runCatching { focusRequester.requestFocus() }
if (focusRequestNonce > 0) {
runCatching { focusRequester.requestFocus() }
keyboard?.show()
}
}
val focusManager = LocalFocusManager.current
var hadDraft by remember { mutableStateOf(false) }
LaunchedEffect(draft) {
if (draft.isEmpty() && hadDraft) focusManager.clearFocus()
hadDraft = draft.isNotEmpty()
ReleaseFocusWhenKeyboardHides(focused)

val submit: () -> Unit = {
if (canSend) {
sendPending = true
onSend()
}
}
LaunchedEffect(sendPending, sending, draft, error) {
if (!sendPending) return@LaunchedEffect
if (!sending && draft.isEmpty()) {
sendPending = false
keyboard?.hide()
focusManager.clearFocus()
} else if (!sending && error != null) {
sendPending = false
}
}

// 外层撑到触摸下限,画出来的胶囊与旁边的 chip 同高。
Expand Down Expand Up @@ -129,12 +159,22 @@ internal fun ControlBarDanmakuField(
textStyle = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onSurface),
cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send),
keyboardActions = KeyboardActions(onSend = { if (canSend) onSend() }),
keyboardActions = KeyboardActions(onSend = { submit() }),
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
.trackEditableFocus()
.onFocusChanged { onFocusChange(it.isFocused) }
// 刚进组合的那一帧焦点还没到,初始 false 不能当成"放掉焦点",否则点「发弹幕」
// 展开的那一帧就会立刻把 composing 重置回 false。
.onFocusChanged { state ->
focused = state.isFocused
if (state.isFocused) {
wasFocused = true
onFocusChange(true)
} else if (wasFocused) {
onFocusChange(false)
}
}
.semantics { contentDescription = hint },
)
}
Expand All @@ -148,16 +188,33 @@ internal fun ControlBarDanmakuField(
)
}
}
IconButton(onClick = onSend, enabled = canSend, modifier = Modifier.size(ControlChipHeight)) {
if (sending) {
LoadingSpinner()
} else {
if (autoFocus && draft.isEmpty() && !sending) {
IconButton(
onClick = {
keyboard?.hide()
focusManager.clearFocus()
onFocusChange(false)
},
modifier = Modifier.size(ControlChipHeight),
) {
Icon(
Icons.AutoMirrored.Filled.Send,
contentDescription = stringResource(Res.string.action_send),
Icons.Filled.Close,
contentDescription = stringResource(Res.string.action_close),
modifier = Modifier.size(Dimens.IconInline),
)
}
} else {
IconButton(onClick = submit, enabled = canSend, modifier = Modifier.size(ControlChipHeight)) {
if (sending) {
LoadingSpinner()
} else {
Icon(
Icons.AutoMirrored.Filled.Send,
contentDescription = stringResource(Res.string.action_send),
modifier = Modifier.size(Dimens.IconInline),
)
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import dev.bilby.formatDurationMillis
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.ime
import androidx.compose.foundation.layout.union
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
Expand Down Expand Up @@ -103,7 +105,9 @@ internal fun Modifier.playerControlBar(isFullscreen: Boolean): Modifier =
listOf(Color.Transparent, FixedColors.PlayerControlScrim, ControlScrimBottom),
),
)
.windowInsetsPadding(WindowInsets.barsAndCutout)
.windowInsetsPadding(
if (isFullscreen) WindowInsets.barsAndCutout.union(WindowInsets.ime) else WindowInsets.barsAndCutout,
)
.padding(
start = if (isFullscreen) Spacing.Comfortable else Spacing.Tight,
end = if (isFullscreen) Spacing.Comfortable else Spacing.Tight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
Expand Down Expand Up @@ -381,6 +382,7 @@ fun PlayerShell(
// 手势的反馈只有画面正中那个浮层,而横屏时手指往往正压在它上面。触感补的是"这一下认了"
// 这件事:长按真的进了加速、锁真的合上了、拖到取消区了。
val haptics = LocalHapticFeedback.current
val focusManager = LocalFocusManager.current
val playback = LocalPlaybackHost.current
val brightness = rememberWindowBrightness()
val volume = rememberMediaVolume()
Expand Down Expand Up @@ -779,6 +781,11 @@ fun PlayerShell(
}
detectTapGestures(
onTap = {
if (menuOpen) {
focusManager.clearFocus()
keepAwake()
return@detectTapGestures
}
if (pointerSource.isTouchLike) {
controlsVisible = !controlsVisible
} else {
Expand Down
Loading
Loading