Desktop app: shell and library screens

This commit is contained in:
2026-10-26 23:46:29 +03:00
parent 69048452b8
commit 01c1b304ef
46 changed files with 4700 additions and 5 deletions

View File

@@ -0,0 +1,5 @@
package ru.stenfredd.sleepyowl.ui.menu
import androidx.compose.ui.Modifier
internal actual fun Modifier.secondaryClick(onClick: () -> Unit): Modifier = this

View File

@@ -128,6 +128,12 @@ class LibraryViewModel(
}
}
/** A tile clicked (the desktop): that library now. */
fun selectLibrary(id: UUID) {
selectJob?.cancel()
if (id != _state.value.selected) select(id)
}
fun setSort(sort: ItemSort) {
val content = _state.value.content ?: return
if (content.sort != sort) restartGrid(content.libraryId, sort, content.unwatched)

View File

@@ -68,8 +68,9 @@ class WatchMenu {
val LocalWatchMenu = staticCompositionLocalOf<WatchMenu?> { null }
/**
* Click of a card or chip that also opens the watched menu for [target]: OK held down (a long press), or the remote's
* Menu key where it has one. Without a target it's a plain click. Put it where `clickable` would go.
* Click of a card or chip that also opens the watched menu for [target]: OK held down (a long press), the remote's
* Menu key where it has one, or the right mouse button on the desktop. Without a target it's a plain click. Put it
* where `clickable` would go.
*/
@Composable
fun Modifier.clickableWithMenu(target: WatchTarget?, onClick: () -> Unit): Modifier {
@@ -79,12 +80,16 @@ fun Modifier.clickableWithMenu(target: WatchTarget?, onClick: () -> Unit): Modif
val open = remember(menu, target) { { menu.open(target, self) } }
return this
.focusRequester(self)
.secondaryClick(open)
.onKeyEvent { e ->
(e.key == Key.Menu).also { if (it && e.type == KeyEventType.KeyUp) open() }
}
.combinedClickable(interactionSource = null, indication = null, onLongClick = open, onClick = onClick)
}
/** The right mouse button runs [onClick]; nothing on the TV. */
internal expect fun Modifier.secondaryClick(onClick: () -> Unit): Modifier
class WatchMenuViewModel(private val watch: WatchRepository) : ViewModel() {
/** Screens reload on their own when it's done (the cache tells them the user data changed). */
suspend fun setPlayed(id: UUID, played: Boolean) = watch.setPlayed(id, played)

View File

@@ -97,7 +97,8 @@ class SearchViewModel(
fun toggleLayout() = _state.update { it.copy(latin = !it.latin) }
private fun setQuery(query: String) {
/** The whole query at once: the desktop's text field. */
fun setQuery(query: String) {
val q = query.take(MAX_LENGTH)
if (q == _state.value.query) return
searchJob?.cancel()

View File

@@ -0,0 +1,11 @@
package ru.stenfredd.sleepyowl.ui.menu
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.isSecondaryPressed
import androidx.compose.ui.input.pointer.onPointerEvent
@OptIn(ExperimentalComposeUiApi::class)
internal actual fun Modifier.secondaryClick(onClick: () -> Unit): Modifier =
onPointerEvent(PointerEventType.Press) { if (it.buttons.isSecondaryPressed) onClick() }