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

Layout customisation #241

Merged
merged 41 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
9f91bf6
Begin layout customisation
toasterofbread Feb 24, 2024
f341afc
Layout customisation progress
toasterofbread Feb 25, 2024
7198d61
Layout customisation progress
toasterofbread Feb 26, 2024
5f0889c
Add hex input field to theme colour picker
toasterofbread Feb 27, 2024
8702fe9
Layout customisation progress
toasterofbread Mar 2, 2024
34b3477
Layout customisation progress
toasterofbread Mar 6, 2024
8c4a461
Remove magic values from search page vertical primary bar
toasterofbread Mar 7, 2024
000e6a5
Layout customisation progress
toasterofbread Mar 7, 2024
c3fcfc1
Layout customisation progress
toasterofbread Mar 8, 2024
ae66de4
Layout customisation progress
toasterofbread Mar 9, 2024
77d8b1b
Re-implement lyrics display
toasterofbread Mar 10, 2024
3cadb6f
Layout customisation progress
toasterofbread Mar 10, 2024
edc366b
Move content bar element size properties to parent class
toasterofbread Mar 11, 2024
794988f
Add pinned items element
toasterofbread Mar 12, 2024
fa9c08e
Layout customisation progress
toasterofbread Mar 12, 2024
d589af8
Continue layout customisation search bars
toasterofbread Mar 19, 2024
cc6b25b
Continue layout customisation, begin shortcuts
toasterofbread Mar 20, 2024
83c84af
Complete basic shortcut functionality
toasterofbread Mar 21, 2024
3c0be63
Implement remaining shortcut actions
toasterofbread Mar 22, 2024
a15e81c
Complete shortcuts, add defaults, add NAVIGATE_SONG_WITH_NUMBERS
toasterofbread Mar 22, 2024
bc45d5c
Add home shortcut
toasterofbread Mar 22, 2024
b845567
Serialise ContentBarElement as sealed class
toasterofbread Mar 22, 2024
f80f81a
Use AppAction for ContentBarElementButton behaviour
toasterofbread Mar 22, 2024
515b389
Add missing content bars warning
toasterofbread Mar 23, 2024
e0d5cf4
Add/implement player bars, add slot-specific options
toasterofbread Mar 23, 2024
0e0210c
Complete below player bar options
toasterofbread Mar 23, 2024
73fe519
Add landscape player bottom bar layout slots
toasterofbread Mar 23, 2024
e3eac32
Merge branch 'main' into layout-customisation
toasterofbread Mar 29, 2024
26fdbeb
Player fixes, localise strings
toasterofbread Mar 29, 2024
6b11e63
Fix bottom padding, fix incorrect layout warning logic
toasterofbread Mar 29, 2024
ead5427
Misc layout fixes
toasterofbread Mar 29, 2024
df5be65
Merge main, make like and pin song actions interactive
toasterofbread Mar 29, 2024
6786515
Add VerticalLyricsLineDisplay, mostly implement lyrics element
toasterofbread Mar 30, 2024
a2a24f8
Fix Android build
toasterofbread Mar 30, 2024
df04a83
Implement visualiser element
toasterofbread Mar 30, 2024
e87f184
Fix content bar list not updating properly
toasterofbread Mar 30, 2024
8e82a35
Apply top bar background colour to status bar
toasterofbread Mar 30, 2024
6a59306
Add option to hide portrait feed artists row
toasterofbread Mar 30, 2024
02ee25e
Top bar finishing touches, add multiselect display
toasterofbread Mar 30, 2024
51b5927
Merge branch 'main' into layout-customisation
toasterofbread Mar 30, 2024
26da7e7
Set portrait layout slot defaults
toasterofbread Mar 30, 2024
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
Prev Previous commit
Next Next commit
Complete basic shortcut functionality
  • Loading branch information
toasterofbread committed Mar 21, 2024
commit 83c84afcdb2f1f6b998f500066a5303218f47a3b
6 changes: 3 additions & 3 deletions desktopApp/src/jvmMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ import com.toasterofbread.composekit.platform.composable.onWindowBackPressed
import com.toasterofbread.spmp.model.settings.category.DesktopSettings
import com.toasterofbread.spmp.model.settings.category.ThemeSettings
import com.toasterofbread.spmp.platform.AppContext
import com.toasterofbread.spmp.ui.component.shortcut.trigger.KeyboardShortcutTrigger
import com.toasterofbread.spmp.ui.layout.apppage.mainpage.getTextFieldFocusState
import com.toasterofbread.spmp.ui.shortcut.ShortcutState
import com.toasterofbread.spmp.ui.shortcut.trigger.KeyboardShortcutTrigger
import com.toasterofbread.spmp.ui.shortcut.processKeyEventShortcuts
import com.toasterofbread.spmp.model.appaction.shortcut.ShortcutState
import com.toasterofbread.spmp.model.appaction.shortcut.processKeyEventShortcuts
import kotlinx.coroutines.*
import org.jetbrains.skiko.OS
import org.jetbrains.skiko.hostOs
Expand Down
4 changes: 2 additions & 2 deletions shared/src/commonMain/kotlin/SpMp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ import com.toasterofbread.spmp.ui.layout.apppage.mainpage.LoadingSplashView
import com.toasterofbread.spmp.ui.layout.apppage.mainpage.RootView
import com.toasterofbread.spmp.ui.layout.apppage.mainpage.SplashMode
import com.toasterofbread.spmp.ui.layout.nowplaying.NowPlayingExpansionState
import com.toasterofbread.spmp.ui.shortcut.LocalShortcutState
import com.toasterofbread.spmp.ui.shortcut.ShortcutState
import com.toasterofbread.spmp.model.appaction.shortcut.LocalShortcutState
import com.toasterofbread.spmp.model.appaction.shortcut.ShortcutState
import com.toasterofbread.spmp.ui.theme.ApplicationTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.toasterofbread.spmp.model.appaction

import kotlinx.serialization.Serializable
import androidx.compose.runtime.Composable
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import com.toasterofbread.spmp.service.playercontroller.PlayerState
import com.toasterofbread.spmp.resources.getString

@Serializable
sealed interface AppAction {
fun getType(): Type
suspend fun executeAction(player: PlayerState)

@Composable
fun Preview(modifier: Modifier)

@Composable
fun ConfigurationItems(item_modifier: Modifier, onModification: (AppAction) -> Unit)

enum class Type {
OPEN_PAGE,
SONG,
PLAYBACK,
SYSTEM,
MODIFY_SETTING;

fun getName(): String =
when (this) {
OPEN_PAGE -> getString("appaction_open_page")
SONG -> getString("appaction_song")
PLAYBACK -> getString("appaction_playback")
SYSTEM -> getString("appaction_system")
MODIFY_SETTING -> getString("appaction_modify_setting")
}

fun getIcon(): ImageVector =
when (this) {
OPEN_PAGE -> Icons.Default.OpenInBrowser
SONG -> Icons.Default.MusicNote
PLAYBACK -> Icons.Default.PlayArrow
SYSTEM -> Icons.Default.Settings
MODIFY_SETTING -> Icons.Default.ToggleOn
}

fun createAction(): AppAction =
when (this) {
OPEN_PAGE -> OpenPageAppAction()
SONG -> SongAppAction()
PLAYBACK -> TODO()
SYSTEM -> TODO()
MODIFY_SETTING -> TODO()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.toasterofbread.spmp.model.appaction

import kotlinx.serialization.Serializable
import com.toasterofbread.spmp.ui.layout.apppage.AppPage
import com.toasterofbread.spmp.resources.getString
import com.toasterofbread.spmp.service.playercontroller.PlayerState
import com.toasterofbread.composekit.utils.composable.LargeDropdownMenu
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.ui.Modifier
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import androidx.compose.runtime.*
import androidx.compose.animation.AnimatedVisibility

@Serializable
data class OpenPageAppAction(
val page: AppPage.Type = AppPage.Type.DEFAULT,
val settings_category: String? = null
): AppAction {
override fun getType(): AppAction.Type = AppAction.Type.OPEN_PAGE
override suspend fun executeAction(player: PlayerState) {
val page: AppPage = page.getPage(player, player.app_page_state) ?: return
player.openAppPage(page)
}

@Composable
override fun Preview(modifier: Modifier) {
Row(
modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
Icon(page.getIcon(), null)
Text(page.getName(), softWrap = false)
}
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
override fun ConfigurationItems(item_modifier: Modifier, onModification: (AppAction) -> Unit) {
var show_page_selector: Boolean by remember { mutableStateOf(false) }

LargeDropdownMenu(
expanded = show_page_selector,
onDismissRequest = { show_page_selector = false },
item_count = AppPage.Type.entries.size,
selected = page.ordinal,
itemContent = {
Text(AppPage.Type.entries[it].getName())
},
onSelected = {
onModification(copy(page = AppPage.Type.entries[it]))
show_page_selector = false
}
)


FlowRow(
item_modifier,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
getString("appaction_config_open_page_page"),
Modifier.align(Alignment.CenterVertically),
softWrap = false
)

Button({ show_page_selector = !show_page_selector }) {
Preview(Modifier)
}
}

AnimatedVisibility(page == AppPage.Type.SETTINGS) {

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package com.toasterofbread.spmp.model.appaction

import kotlinx.serialization.Serializable
import com.toasterofbread.spmp.service.playercontroller.PlayerState
import com.toasterofbread.spmp.resources.getString
import com.toasterofbread.spmp.model.mediaitem.song.Song
import com.toasterofbread.spmp.model.mediaitem.song.SongLikedStatus
import com.toasterofbread.spmp.model.mediaitem.song.updateLiked
import com.toasterofbread.spmp.model.mediaitem.playlist.Playlist
import com.toasterofbread.spmp.platform.download.DownloadStatus
import com.toasterofbread.composekit.utils.composable.LargeDropdownMenu
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.Button
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import com.toasterofbread.spmp.model.mediaitem.db.togglePinned
import com.toasterofbread.spmp.model.mediaitem.loader.SongLikedLoader
import com.toasterofbread.spmp.youtubeapi.endpoint.SetSongLikedEndpoint
import com.toasterofbread.spmp.youtubeapi.endpoint.SongLikedEndpoint

@Serializable
data class SongAppAction(
val action: Action = Action.DEFAULT
): AppAction {
override fun getType(): AppAction.Type = AppAction.Type.SONG
override suspend fun executeAction(player: PlayerState) {
val song: Song = player.status.song ?: return
val index: Int = player.status.index
if (index < 0) {
return
}
action.execute(song, index, player)
}

@Composable
override fun Preview(modifier: Modifier) {
Row(
modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
Icon(action.getIcon(), null)
Text(action.getName(), softWrap = false)
}
}

@OptIn(ExperimentalLayoutApi::class)
@Composable
override fun ConfigurationItems(item_modifier: Modifier, onModification: (AppAction) -> Unit) {
var show_action_selector: Boolean by remember { mutableStateOf(false) }

LargeDropdownMenu(
expanded = show_action_selector,
onDismissRequest = { show_action_selector = false },
item_count = Action.entries.size,
selected = action.ordinal,
itemContent = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
val action: Action = Action.entries[it]
Icon(action.getIcon(), null)
Text(action.getName())
}
},
onSelected = {
onModification(copy(action = Action.entries[it]))
show_action_selector = false
}
)

FlowRow(
item_modifier,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
getString("appaction_config_song_action"),
Modifier.align(Alignment.CenterVertically),
softWrap = false
)

Button({ show_action_selector = !show_action_selector }) {
Preview(Modifier)
}
}
}

enum class Action {
TOGGLE_LIKE,
TOGGLE_PIN,
HIDE,
START_RADIO,
DOWNLOAD,
OPEN_ALBUM,
OPEN_RELATED,
REMOVE_FROM_QUEUE;

companion object {
val DEFAULT: Action = TOGGLE_LIKE
}

fun getName(): String =
when (this) {
TOGGLE_LIKE -> getString("appaction_song_action_toggle_like")
TOGGLE_PIN -> getString("appaction_song_action_toggle_pin")
HIDE -> getString("appaction_song_action_hide")
START_RADIO -> getString("appaction_song_action_start_radio")
DOWNLOAD -> getString("appaction_song_action_download")
OPEN_ALBUM -> getString("appaction_song_action_open_album")
OPEN_RELATED -> getString("appaction_song_action_open_related")
REMOVE_FROM_QUEUE -> getString("appaction_song_action_remove_from_queue")
}

fun getIcon(): ImageVector =
when (this) {
TOGGLE_LIKE -> Icons.Default.Favorite
TOGGLE_PIN -> Icons.Default.PushPin
HIDE -> Icons.Default.VisibilityOff
START_RADIO -> Icons.Default.Radio
DOWNLOAD -> Icons.Default.Download
OPEN_ALBUM -> Icons.Default.Album
OPEN_RELATED -> Icons.Default.GridView
REMOVE_FROM_QUEUE -> Icons.Default.Close
}

suspend fun execute(song: Song, queue_index: Int, player: PlayerState) {
when (this) {
TOGGLE_LIKE -> {
val set_liked_endpoint: SetSongLikedEndpoint? = player.context.ytapi.user_auth_state?.SetSongLiked
val get_liked_endpoint: SongLikedEndpoint? = player.context.ytapi.user_auth_state?.SongLiked
val liked: SongLikedStatus? =
SongLikedLoader.loadSongLiked(song.id, player.context, get_liked_endpoint).getOrNull()
?: song.Liked.get(player.database)

song.updateLiked(
when (liked) {
SongLikedStatus.LIKED, SongLikedStatus.DISLIKED -> SongLikedStatus.NEUTRAL
SongLikedStatus.NEUTRAL, null -> SongLikedStatus.LIKED
},
set_liked_endpoint,
player.context
)
}
TOGGLE_PIN -> {
song.togglePinned(player.context)
}
HIDE -> {
song.Hidden.set(true, player.database)
player.withPlayer {
removeFromQueue(queue_index)
}
}
START_RADIO -> {
player.withPlayer {
playSong(song)
}
}
DOWNLOAD -> {
player.onSongDownloadRequested(song) { status ->
when (status?.status) {
null -> {}
DownloadStatus.Status.FINISHED -> player.context.sendToast(getString("notif_download_finished"))
DownloadStatus.Status.ALREADY_FINISHED -> player.context.sendToast(getString("notif_download_already_finished"))
DownloadStatus.Status.CANCELLED -> player.context.sendToast(getString("notif_download_cancelled"))

// IDLE, DOWNLOADING, PAUSED
else -> {
player.context.sendToast(getString("notif_download_already_downloading"))
}
}
}
}
OPEN_ALBUM -> {
val album: Playlist = song.Album.get(player.database) ?: return
player.openMediaItem(album)
}
OPEN_RELATED -> {
player.openMediaItem(song)
}
REMOVE_FROM_QUEUE -> {
player.withPlayer {
removeFromQueue(queue_index)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package com.toasterofbread.spmp.ui.shortcut
package com.toasterofbread.spmp.model.appaction.shortcut

import androidx.compose.foundation.shape.CircleShape
import kotlinx.serialization.Serializable
import com.toasterofbread.spmp.ui.shortcut.action.ShortcutAction
import com.toasterofbread.spmp.ui.shortcut.trigger.ShortcutTrigger
import com.toasterofbread.spmp.model.appaction.AppAction
import com.toasterofbread.spmp.ui.component.shortcut.trigger.ShortcutTrigger

val SHORTCUT_INDICATOR_SHAPE get() = CircleShape
const val SHORTCUT_INDICATOR_GROUP_ANIM_DURATION_MS: Long = 30
const val SHORTCUT_INDICATOR_SHOW_DELAY_MS: Long = 200

@Serializable
data class Shortcut(val trigger: ShortcutTrigger, val action: ShortcutAction)

data class Shortcut(val trigger: ShortcutTrigger?, val action: AppAction)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.toasterofbread.spmp.ui.shortcut
package com.toasterofbread.spmp.model.appaction.shortcut

import androidx.compose.runtime.Composable

Expand Down
Loading