Bläddra i källkod

icons improvements

milan jurkulák 2 år sedan
förälder
incheckning
6af1063186

+ 2 - 1
src/main/kotlin/eu/mjdev/desktop/Main.kt

@@ -1,5 +1,6 @@
 package eu.mjdev.desktop
 
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.MaterialTheme
 import androidx.compose.runtime.CompositionLocalProvider
@@ -71,7 +72,7 @@ fun main() = application(
 //                    modifier = Modifier.size(640.dp, 480.dp).align(Alignment.Center)
 //                )
                 MemoryChart(
-                    modifier = Modifier.size(350.dp, 300.dp).align(Alignment.BottomEnd)
+                    modifier = Modifier.padding(bottom = 64.dp).size(350.dp, 300.dp).align(Alignment.BottomEnd)
                 )
                 DesktopPanel(
                     panelState = panelState,

+ 11 - 9
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsBottomBar.kt

@@ -24,6 +24,7 @@ fun AppsBottomBar(
     iconBackgroundColor: Color = Color.White,
     iconColor: Color = Color.Black,
     backButtonVisible: Boolean = false,
+    onContextMenuClick: () -> Unit,
     onBackClick: () -> Unit
 ) = Box(
     modifier = modifier.padding(8.dp)
@@ -36,6 +37,7 @@ fun AppsBottomBar(
                 imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                 iconColor = iconColor,
                 iconBackgroundColor = iconBackgroundColor,
+                onRightClick = onContextMenuClick,
                 onClick = onBackClick
             )
         }
@@ -52,22 +54,22 @@ fun AppsBottomBar(
             imageVector = Icons.Filled.RestartAlt,
             iconColor = iconColor,
             iconBackgroundColor = iconBackgroundColor,
-        ) {
-            api.restart()
-        }
+            onRightClick = onContextMenuClick,
+            onClick = { api.restart() }
+        )
         ShapedIcon(
             imageVector = Icons.AutoMirrored.Filled.Logout,
             iconColor = iconColor,
             iconBackgroundColor = iconBackgroundColor,
-        ) {
-            api.logOut()
-        }
+            onRightClick = onContextMenuClick,
+            onClick = { api.logOut() }
+        )
         ShapedIcon(
             imageVector = Icons.Filled.PowerOff,
             iconColor = iconColor,
             iconBackgroundColor = iconBackgroundColor,
-        ) {
-            api.shutdown()
-        }
+            onRightClick = onContextMenuClick,
+            onClick = { api.shutdown() }
+        )
     }
 }

+ 32 - 11
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenu.kt

@@ -44,6 +44,21 @@ fun AppsMenu(
     exitAnimation: ExitTransition = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
     items: MutableState<List<Any>> = remember(api.appsProvider) { mutableStateOf(api.appsProvider.appCategories) },
     onFocusChange: (Boolean) -> Unit = {},
+    onAppClick: (App) -> Unit = { app ->
+        api.appsProvider.startApp(app)
+    },
+    onCategoryClick: (Category) -> Unit = { category ->
+        items.value = api.appsProvider.categoriesAndApps[category.name]?.sortedBy { it.name } ?: emptyList()
+    },
+    onAppContextMenuClick: (App) -> Unit = {
+        // todo
+    },
+    onCategoryContextMenuClick: (Category) -> Unit = {
+        // todo
+    },
+    onUserAvatarClick: () -> Unit = {
+        // todo
+    }
 ) = ChromeWindow(
     visible = menuState.isVisible,
     enterAnimation = enterAnimation,
@@ -80,7 +95,8 @@ fun AppsMenu(
                 UserAvatar(
                     avatarSize = 64.dp,
                     backgroundColor = appMenuBackgroundColor,
-                    orientation = Orientation.Horizontal
+                    orientation = Orientation.Horizontal,
+                    onUserAvatarClick = onUserAvatarClick
                 )
                 Divider(
                     modifier = Modifier
@@ -105,11 +121,10 @@ fun AppsMenu(
                                 AppsMenuCategory(
                                     category = item as Category,
                                     backgroundColor = appMenuBackgroundColor,
-                                    iconTint = Color.White
-                                ) { category ->
-                                    items.value = api.appsProvider.categoriesAndApps[category.name]
-                                        ?.sortedBy { it.name } ?: emptyList()
-                                }
+                                    iconTint = Color.White,
+                                    onClick = { onCategoryClick(item) },
+                                    onContextMenuClick = { onCategoryContextMenuClick(item) }
+                                )
                             }
                         }
 
@@ -118,11 +133,14 @@ fun AppsMenu(
                                 AppsMenuApp(
                                     app = item as App,
                                     backgroundColor = appMenuBackgroundColor,
-                                    iconTint = Color.White
-                                ) { app ->
-                                    app?.let { api.appsProvider.startApp(it) }
-                                    menuState.hide()
-                                }
+                                    iconTint = Color.White,
+                                    onClick = {
+                                        onAppClick(item)
+                                    },
+                                    onContextMenuClick = {
+                                        onAppContextMenuClick(item)
+                                    }
+                                )
                             }
                         }
                     }
@@ -153,6 +171,9 @@ fun AppsMenu(
                     backButtonVisible = items.value.first() is App,// todo
                     onBackClick = {
                         items.value = api.appsProvider.appCategories
+                    },
+                    onContextMenuClick = {
+                        // todo : context menu
                     }
                 )
             }

+ 18 - 7
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenuApp.kt

@@ -1,16 +1,18 @@
 package eu.mjdev.desktop.components.appsmenu
 
 import androidx.compose.desktop.ui.tooling.preview.Preview
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.mouseClickable
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.isSecondaryPressed
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
@@ -20,6 +22,8 @@ import eu.mjdev.desktop.data.App
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 
+@Suppress("DEPRECATION")
+@OptIn(ExperimentalFoundationApi::class)
 @Preview
 @Composable
 fun AppsMenuApp(
@@ -31,9 +35,16 @@ fun AppsMenuApp(
     textColor: Color = Color.White,
     backgroundColor: Color = Color.White,
     api: DesktopProvider = LocalDesktop.current,
-    onClick: (app: App?) -> Unit = { app?.let { api.appsProvider.startApp(it) } }
+    onClick: () -> Unit,
+    onContextMenuClick: () -> Unit
 ) = Row(
-    modifier = modifier.clickable { onClick(app) },
+    modifier = modifier.mouseClickable {
+        if (buttons.isSecondaryPressed) {
+            onContextMenuClick()
+        } else {
+            onClick()
+        }
+    },
     verticalAlignment = Alignment.CenterVertically,
 ) {
     val appIconName = remember(app, icon) { app?.name ?: icon }
@@ -44,10 +55,10 @@ fun AppsMenuApp(
         iconColor = iconTint,
         iconBackgroundColor = backgroundColor,
         outerPadding = PaddingValues(2.dp),
-        innerPadding = PaddingValues(0.dp)
-    ) {
-        onClick(app)
-    }
+        innerPadding = PaddingValues(0.dp),
+        onClick = onClick,
+        onRightClick = onContextMenuClick
+    )
     TextAny(
         modifier = Modifier.padding(start = 4.dp).fillMaxWidth(),
         text = app?.name ?: "",

+ 18 - 7
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenuCategory.kt

@@ -1,16 +1,18 @@
 package eu.mjdev.desktop.components.appsmenu
 
 import androidx.compose.desktop.ui.tooling.preview.Preview
-import androidx.compose.foundation.clickable
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.mouseClickable
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.isSecondaryPressed
 import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
@@ -20,6 +22,8 @@ import eu.mjdev.desktop.data.Category
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 
+@Suppress("DEPRECATION")
+@OptIn(ExperimentalFoundationApi::class)
 @Preview
 @Composable
 fun AppsMenuCategory(
@@ -30,9 +34,16 @@ fun AppsMenuCategory(
     textColor: Color = Color.White,
     backgroundColor: Color = Color.White,
     api: DesktopProvider = LocalDesktop.current,
-    onClick: (category: Category) -> Unit = {}
+    onClick: (category: Category) -> Unit = {},
+    onContextMenuClick: (category: Category) -> Unit = {}
 ) = Row(
-    modifier = modifier.clickable { onClick(category) },
+    modifier = modifier.mouseClickable {
+        if (buttons.isSecondaryPressed) {
+            onContextMenuClick(category)
+        } else {
+            onClick(category)
+        }
+    },
     verticalAlignment = Alignment.CenterVertically
 ) {
     val categoryName = remember(category) { category.name }
@@ -43,10 +54,10 @@ fun AppsMenuCategory(
         iconColor = iconTint,
         iconBackgroundColor = backgroundColor,
         outerPadding = PaddingValues(2.dp),
-        innerPadding = PaddingValues(0.dp)
-    ) {
-        onClick(category)
-    }
+        innerPadding = PaddingValues(0.dp),
+        onClick = { onClick(category) },
+        onRightClick = { onContextMenuClick(category) }
+    )
     TextAny(
         modifier = Modifier.padding(start = 4.dp).fillMaxWidth(),
         text = category.name,

+ 13 - 5
src/main/kotlin/eu/mjdev/desktop/components/controlcenter/ControlCenter.kt

@@ -1,12 +1,13 @@
 package eu.mjdev.desktop.components.controlcenter
 
 import androidx.compose.animation.*
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
 import androidx.compose.foundation.gestures.Orientation.Horizontal
 import androidx.compose.foundation.layout.*
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.mouseClickable
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.Divider
 import androidx.compose.material.Icon
@@ -14,6 +15,7 @@ import androidx.compose.runtime.*
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.isSecondaryPressed
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
@@ -29,7 +31,8 @@ import eu.mjdev.desktop.windows.ChromeWindow
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
-@Suppress("FunctionName")
+@OptIn(ExperimentalFoundationApi::class)
+@Suppress("FunctionName", "DEPRECATION")
 @Composable
 fun ControlCenter(
     api: DesktopProvider = LocalDesktop.current,
@@ -47,6 +50,7 @@ fun ControlCenter(
     enterAnimation: EnterTransition = fadeIn() + slideInHorizontally(initialOffsetX = { it }),
     exitAnimation: ExitTransition = slideOutHorizontally(targetOffsetX = { it }) + fadeOut(),
     onFocusChange: (Boolean) -> Unit = {},
+    onContextMenuClick: () -> Unit = {}
 ) = ChromeWindow(
     visible = true,
     enterAnimation = enterAnimation,
@@ -110,9 +114,13 @@ fun ControlCenter(
                                             color = if (isSelected) Color.White else Color.Transparent,
                                             shape = RoundedCornerShape(8.dp)
                                         )
-                                        .clickable {
-                                            scope.launch {
-                                                pagerState.value = idx
+                                        .mouseClickable {
+                                            if (buttons.isSecondaryPressed) {
+                                                onContextMenuClick()
+                                            } else {
+                                                scope.launch {
+                                                    pagerState.value = idx
+                                                }
                                             }
                                         },
                                     imageVector = page.icon,

+ 4 - 1
src/main/kotlin/eu/mjdev/desktop/components/controlcenter/pages/MainSettingsPage.kt

@@ -32,7 +32,10 @@ fun MainSettingsPage() = ControlCenterPage(
             UserAvatar(
                 backgroundColor = backgroundColor,
                 avatarSize = 128.dp,
-                orientation = Orientation.Vertical
+                orientation = Orientation.Vertical,
+                onUserAvatarClick = {
+                    // todo
+                }
             )
             Divider(
                 modifier = Modifier.fillMaxWidth().height(2.dp),

+ 2 - 1
src/main/kotlin/eu/mjdev/desktop/components/custom/UserAvatar.kt

@@ -30,6 +30,7 @@ fun UserAvatar(
     avatarSize: Dp = 128.dp,
     orientation: Orientation = Orientation.Vertical,
     api: DesktopProvider = LocalDesktop.current,
+    onUserAvatarClick: () -> Unit
 ) = Box(
     modifier = Modifier
         .fillMaxWidth()
@@ -48,7 +49,7 @@ fun UserAvatar(
                     .size(avatarSize)
                     .clipCircle(),
                 contentPadding = PaddingValues(0.dp),
-                onClick = {},
+                onClick = onUserAvatarClick,
                 colors = ButtonDefaults.transparent(),
                 elevation = ButtonDefaults.noElevation()
             ) {

+ 22 - 10
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanel.kt

@@ -40,7 +40,6 @@ import eu.mjdev.desktop.windows.ChromeWindow
 fun DesktopPanel(
     api: DesktopProvider = LocalDesktop.current,
     backgroundColor: Color = api.currentUser.theme.backgroundColor,
-//    shadowColor: Color = Color.Black.copy(alpha = 0.3f),
     iconColor: Color = Color.Black,
     iconColorRunning: Color = Color.White.copy(0.8f),
     iconBackgroundColor: Color = Color.White.copy(0.6f),
@@ -51,8 +50,8 @@ fun DesktopPanel(
     showMenuIcon: Boolean = true,
     panelContentPadding: PaddingValues = PaddingValues(
         start = 4.dp,
-        bottom = 4.dp,
-        top = 4.dp
+        top = 4.dp,
+        end = 4.dp
     ),
     panelState: VisibilityState = rememberVisibilityState(),
     enterAnimation: EnterTransition = fadeIn() + slideInVertically(initialOffsetY = { it }),
@@ -66,13 +65,25 @@ fun DesktopPanel(
         else
             dividerWidth
     },
-    onMenuIconClicked: () -> Unit = {},
-    onFocusChange: (Boolean) -> Unit = {},
     tooltipConverter: (Any?) -> TooltipData = { item ->
         when (item) {
             is App -> TooltipData(title = item.name, description = item.comment)
             else -> TooltipData(title = item.toString())
         }
+    },
+    onMenuIconClicked: () -> Unit = {
+        // todo
+    },
+    onMenuIconContextMenuClicked: () -> Unit = {
+        // todo
+    },
+    onFocusChange: (Boolean) -> Unit = {},
+    onAppClick: (App) -> Unit = { app -> api.appsProvider.startApp(app) },
+    onAppContextMenuClick: (App) -> Unit = {
+        // todo
+    },
+    onLanguageClick: () -> Unit = {
+        // todo
     }
 ) = ChromeWindow(
     visible = true,
@@ -161,7 +172,8 @@ fun DesktopPanel(
                                     iconPadding = iconPadding,
                                     iconOuterPadding = iconOuterPadding,
                                     onTooltip = onTooltip,
-                                    onClick = onMenuIconClicked
+                                    onClick = { onMenuIconClicked() },
+                                    onContextMenuClick = onMenuIconContextMenuClicked
                                 )
                             }
                             DesktopPanelFavoriteApps(
@@ -172,14 +184,14 @@ fun DesktopPanel(
                                 iconSize = iconSize,
                                 iconPadding = iconPadding,
                                 iconOuterPadding = iconOuterPadding,
-                                onTooltip = onTooltip
+                                onTooltip = onTooltip,
+                                onAppClick = onAppClick,
+                                onContextMenuClick = onAppContextMenuClick
                             )
                             DesktopPanelLanguage(
                                 modifier = Modifier.align(Alignment.CenterEnd),
                                 onTooltip = onTooltip,
-                                onClick = {
-                                    // todo
-                                }
+                                onClick = onLanguageClick
                             )
                         }
                     }

+ 4 - 9
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/DesktopPanelIcon.kt

@@ -15,11 +15,8 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.hapticfeedback.HapticFeedback
-import androidx.compose.ui.hapticfeedback.HapticFeedbackType
 import androidx.compose.ui.input.pointer.PointerEventType
 import androidx.compose.ui.input.pointer.onPointerEvent
-import androidx.compose.ui.platform.LocalHapticFeedback
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.fonticon.FontIcon
@@ -35,7 +32,6 @@ import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 @Composable
 fun DesktopPanelIcon(
     api: DesktopProvider = LocalDesktop.current,
-    hapticFeedback: HapticFeedback = LocalHapticFeedback.current,
     app: App? = null,
     isRunning: Boolean = false,
     isStarting: Boolean = false,
@@ -49,8 +45,9 @@ fun DesktopPanelIcon(
     iconOuterPadding: PaddingValues = PaddingValues(2.dp),
     iconState: MutableState<Boolean> = remember { mutableStateOf(false) },
     contentDescription: String? = null,
-    onToolTip: (item: Any?) -> Unit = {},
+    onToolTip: (item: Any?) -> Unit,
     onClick: () -> Unit,
+    onContextMenuClick: () -> Unit
 ) {
     val iconName = remember(app, icon) { app?.name ?: icon }
     val materialIcon = remember(iconName) { api.currentUser.theme.iconSet.iconForName(iconName) ?: "?".toInt() }
@@ -87,10 +84,8 @@ fun DesktopPanelIcon(
                 iconSize = iconSize,
                 innerPadding = iconPadding,
                 outerPadding = iconOuterPadding,
-                onClick = {
-                    hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
-                    onClick()
-                }
+                onClick = onClick,
+                onRightClick = onContextMenuClick
             )
         }
         if (isStarting) {

+ 5 - 3
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopMenuIcon.kt

@@ -18,8 +18,9 @@ fun DesktopMenuIcon(
     iconSize: DpSize = DpSize(56.dp, 56.dp),
     iconPadding: PaddingValues = PaddingValues(4.dp),
     iconOuterPadding: PaddingValues = PaddingValues(2.dp),
-    onTooltip: (item: Any?) -> Unit = {},
-    onClick: () -> Unit = {},
+    onTooltip: (item: Any?) -> Unit,
+    onClick: () -> Unit,
+    onContextMenuClick: () -> Unit
 ) = Row(
     modifier = modifier
 ) {
@@ -33,7 +34,8 @@ fun DesktopMenuIcon(
         iconPadding = iconPadding,
         iconOuterPadding = iconOuterPadding,
         onToolTip = onTooltip,
-        onClick = onClick
+        onClick = onClick,
+        onContextMenuClick = onContextMenuClick
     )
     Divider(
         modifier = Modifier

+ 27 - 24
src/main/kotlin/eu/mjdev/desktop/components/desktoppanel/applets/DesktopPanelFavoriteApps.kt

@@ -8,7 +8,6 @@ import androidx.compose.foundation.lazy.LazyRow
 import androidx.compose.foundation.lazy.items
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.snapshots.SnapshotStateList
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -30,31 +29,35 @@ fun DesktopPanelFavoriteApps(
     iconPadding: PaddingValues = PaddingValues(4.dp),
     iconOuterPadding: PaddingValues = PaddingValues(2.dp),
     onTooltip: (item: Any?) -> Unit = {},
-    apps: SnapshotStateList<App> = remember(api.appsProvider.favoriteApps) { api.appsProvider.favoriteApps },
-    onClick: (app: App) -> Unit = { app -> api.appsProvider.startApp(app) },
-) = Box(
-    modifier = modifier
+    onAppClick: (app: App) -> Unit,
+    onContextMenuClick: (app: App) -> Unit
 ) {
-    LazyRow(
-        modifier = Modifier.wrapContentHeight(),
-        horizontalArrangement = Arrangement.Start,
-        verticalAlignment = Alignment.CenterVertically
+    val apps = remember(api.appsProvider.favoriteApps.size) { api.appsProvider.favoriteApps }
+    Box(
+        modifier = modifier
     ) {
-        items(apps) { app ->
-            DesktopPanelIcon(
-                app = app,
-                isRunning = app.isRunning,
-                isStarting = app.isStarted,
-                iconColor = iconColor,
-                iconBackgroundColor = iconBackgroundColor,
-                iconColorRunning = iconColorRunning,
-                iconBackgroundHover = Color.White.copy(alpha = 0.4f),
-                iconSize = iconSize,
-                iconPadding = iconPadding,
-                iconOuterPadding = iconOuterPadding,
-                onToolTip = onTooltip,
-                onClick = { onClick(app) }
-            )
+        LazyRow(
+            modifier = Modifier.wrapContentHeight(),
+            horizontalArrangement = Arrangement.Start,
+            verticalAlignment = Alignment.CenterVertically
+        ) {
+            items(apps) { app ->
+                DesktopPanelIcon(
+                    app = app,
+                    isRunning = app.isRunning,
+                    isStarting = app.isStarted,
+                    iconColor = iconColor,
+                    iconBackgroundColor = iconBackgroundColor,
+                    iconColorRunning = iconColorRunning,
+                    iconBackgroundHover = Color.White.copy(alpha = 0.4f),
+                    iconSize = iconSize,
+                    iconPadding = iconPadding,
+                    iconOuterPadding = iconOuterPadding,
+                    onToolTip = onTooltip,
+                    onClick = { onAppClick(app) },
+                    onContextMenuClick = { onContextMenuClick(app) }
+                )
+            }
         }
     }
 }

+ 3 - 1
src/main/kotlin/eu/mjdev/desktop/components/fonticon/FontIcon.kt

@@ -32,6 +32,7 @@ fun FontIcon(
     contentDescription: String = "",
     outerPadding: PaddingValues = PaddingValues(2.dp),
     iconShape: Shape = CircleShape,
+    onRightClick: () -> Unit,
     onClick: () -> Unit
 ) = ShapedIcon(
     iconSize = iconSize,
@@ -57,5 +58,6 @@ fun FontIcon(
             textAlign = TextAlign.Center
         )
     },
-    onClick = onClick
+    onClick = onClick,
+    onRightClick = onRightClick
 )

+ 19 - 5
src/main/kotlin/eu/mjdev/desktop/components/icon/ShapedIcon.kt

@@ -3,7 +3,7 @@ package eu.mjdev.desktop.components.icon
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.onClick
+import androidx.compose.foundation.mouseClickable
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.Icon
 import androidx.compose.material.icons.Icons
@@ -14,10 +14,12 @@ import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.input.pointer.isSecondaryPressed
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.extensions.Compose.size
 
+@Suppress("DEPRECATION")
 @OptIn(ExperimentalFoundationApi::class)
 @Composable
 fun ShapedIcon(
@@ -31,15 +33,20 @@ fun ShapedIcon(
     iconShape: Shape = CircleShape,
     innerPadding: PaddingValues = PaddingValues(4.dp),
     outerPadding: PaddingValues = PaddingValues(2.dp),
-    onClick: () -> Unit = {}
+    onRightClick: () -> Unit,
+    onClick: () -> Unit
 ) = Box(
     modifier = modifier
         .padding(outerPadding)
         .size(iconSize + innerPadding.size + outerPadding.size)
         .background(iconBackgroundColor, iconShape)
         .clip(iconShape)
-        .onClick {
-            onClick()
+        .mouseClickable {
+            if (buttons.isSecondaryPressed) {
+                onRightClick()
+            } else {
+                onClick()
+            }
         }
 ) {
     Box(
@@ -51,7 +58,14 @@ fun ShapedIcon(
             customContent()
         } else {
             Icon(
-                modifier = Modifier.fillMaxSize(),
+                modifier = Modifier.fillMaxSize()
+                    .mouseClickable {
+                        if (buttons.isSecondaryPressed) {
+                            onRightClick()
+                        } else {
+                            onClick()
+                        }
+                    },
                 imageVector = imageVector,
                 contentDescription = contentDescription,
                 tint = iconColor

+ 2 - 1
src/main/kotlin/eu/mjdev/desktop/data/App.kt

@@ -15,6 +15,7 @@ val Process.command: String?
 class App(
     val file: File = File("nonexistent_file_path"),
     val desktopFile: DesktopFile = DesktopFile(file),
+    val isFavorite: Boolean = false
 ) {
     val type
         get() = desktopFile.type
@@ -77,7 +78,7 @@ class App(
     fun start() = runCatching {
         triggerStart()
         println("Starting app: $name [$cmd]")
-        ProcessBuilder(cmd).start().also {
+        ProcessBuilder("/bin/bash", "-c", cmd).start().also {
             process = it
         }.apply {
             onExit().thenRun {

+ 12 - 3
src/main/kotlin/eu/mjdev/desktop/provider/AppsProvider.kt

@@ -152,11 +152,19 @@ class AppsProvider(
                 }
             } else {
                 app.onStarting {
-                    favoriteApps.invalidate()
+                    if (favoriteApps.contains(app)) {
+                        favoriteApps.invalidate()
+                    } else {
+                        favoriteApps.add(app)
+                    }
                 }.onStarted {
                     favoriteApps.invalidate()
                 }.onStop { result ->
-                    favoriteApps.invalidate()
+                    if (!app.isFavorite) {
+                        favoriteApps.remove(app)
+                    } else {
+                        favoriteApps.invalidate()
+                    }
                     if (result != EmptyException) {
                         result.printStackTrace()
                     }
@@ -180,7 +188,8 @@ class AppsProvider(
                 }.map { deskFile ->
                     App(
                         desktopFile = deskFile,
-                        file = deskFile.file
+                        file = deskFile.file,
+                        isFavorite = true
                     )
                 }.firstOrNull()
             }?.also { favorite ->