ソースを参照

icons improvement

icons improvement

icons improvement

icons improvement

icons improvement

icons improvement
milan jurkulák 2 年 前
親
コミット
f65853d664

+ 6 - 4
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsBottomBar.kt

@@ -14,10 +14,12 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
 import eu.mjdev.desktop.components.icon.ShapedIcon
-import kotlin.system.exitProcess
+import eu.mjdev.desktop.provider.DesktopProvider
+import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 
 @Composable
 fun AppsBottomBar(
+    api: DesktopProvider = LocalDesktop.current,
     modifier: Modifier = Modifier,
     iconBackgroundColor: Color = Color.White,
     iconColor: Color = Color.Black,
@@ -51,21 +53,21 @@ fun AppsBottomBar(
             iconColor = iconColor,
             iconBackgroundColor = iconBackgroundColor,
         ) {
-            exitProcess(0)
+            api.restart()
         }
         ShapedIcon(
             imageVector = Icons.AutoMirrored.Filled.Logout,
             iconColor = iconColor,
             iconBackgroundColor = iconBackgroundColor,
         ) {
-            exitProcess(0)
+            api.logOut()
         }
         ShapedIcon(
             imageVector = Icons.Filled.PowerOff,
             iconColor = iconColor,
             iconBackgroundColor = iconBackgroundColor,
         ) {
-            exitProcess(0)
+            api.shutdown()
         }
     }
 }

+ 4 - 2
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenuApp.kt

@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 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
@@ -35,7 +36,8 @@ fun AppsMenuApp(
     modifier = modifier.clickable { onClick(app) },
     verticalAlignment = Alignment.CenterVertically,
 ) {
-    val materialIcon = api.currentUser.theme.iconSet.iconForName(app?.name ?: icon) ?: "?".toInt()
+    val appIconName = remember(app, icon) { app?.name ?: icon }
+    val materialIcon = remember(appIconName) { api.currentUser.theme.iconSet.iconForName(appIconName) ?: "?".toInt() }
     FontIcon(
         iconId = materialIcon,
         iconSize = iconSize,
@@ -52,4 +54,4 @@ fun AppsMenuApp(
         color = textColor,
         fontWeight = FontWeight.Bold
     )
-}
+}

+ 4 - 2
src/main/kotlin/eu/mjdev/desktop/components/appsmenu/AppsMenuCategory.kt

@@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 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
@@ -34,7 +35,8 @@ fun AppsMenuCategory(
     modifier = modifier.clickable { onClick(category) },
     verticalAlignment = Alignment.CenterVertically
 ) {
-    val materialIcon = api.currentUser.theme.iconSet.iconForName(category.name) ?: "?".toInt()
+    val categoryName = remember(category) { category.name }
+    val materialIcon = remember(categoryName) { api.currentUser.theme.iconSet.iconForName(categoryName) ?: "?".toInt() }
     FontIcon(
         iconId = materialIcon,
         iconSize = iconSize,
@@ -51,4 +53,4 @@ fun AppsMenuCategory(
         color = textColor,
         fontWeight = FontWeight.Bold
     )
-}
+}

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

@@ -51,8 +51,8 @@ fun DesktopPanelIcon(
     onToolTip: (item: Any?) -> Unit = {},
     onClick: () -> Unit,
 ) {
-    val iconName = app?.name ?: icon
-    val materialIcon = api.currentUser.theme.iconSet.iconForName(iconName) ?: "?".toInt()
+    val iconName = remember(app, icon) { app?.name ?: icon }
+    val materialIcon = remember(iconName) { api.currentUser.theme.iconSet.iconForName(iconName) ?: "?".toInt() }
     val background = when {
         (iconState.value || isRunning) -> iconBackgroundHover
         else -> Color.Transparent
@@ -101,4 +101,4 @@ fun DesktopPanelIcon(
             )
         }
     }
-}
+}

+ 5 - 2
src/main/kotlin/eu/mjdev/desktop/components/fonticon/FontFile.kt

@@ -11,5 +11,8 @@ class FontFile(
     private val ttfResource: ResourceStream = ResourceStream(ttfFileName),
     private val fontWeight: FontWeight = FontWeight.Normal,
     private val font: Font = Font(ttfFileName, ttfResource.bytes, fontWeight),
-    val fontFamily: FontFamily = FontFamily(font)
-)
+) {
+    val fontFamily by lazy {
+        FontFamily(font)
+    }
+}

+ 9 - 5
src/main/kotlin/eu/mjdev/desktop/components/fonticon/FontIcon.kt

@@ -5,6 +5,7 @@ package eu.mjdev.desktop.components.fonticon
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.shape.CircleShape
 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
@@ -22,8 +23,8 @@ import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 
 @Composable
 fun FontIcon(
-    iconicFont: MaterialIconFont = LocalDesktop.current.currentUser.theme.iconSet,
     iconId: Int,
+    iconicFont: MaterialIconFont = LocalDesktop.current.currentUser.theme.iconSet,
     iconSize: DpSize = DpSize(32.dp, 32.dp),
     iconColor: Color = Color.Black,
     iconBackgroundColor: Color = Color.White,
@@ -41,14 +42,17 @@ fun FontIcon(
     innerPadding = innerPadding,
     outerPadding = outerPadding,
     customContent = {
+        val rememberSize = remember(iconicFont, iconSize, innerPadding, outerPadding, iconShape) {
+            FontSizeRange(
+                min = 24.sp,
+                max = (iconSize.width + innerPadding.width + outerPadding.width).sp,
+            )
+        }
         AutoResizeText(
             text = iconId.toChar().toString(),
             modifier = Modifier.align(Alignment.Center),
             color = iconColor,
-            fontSizeRange = FontSizeRange(
-                min = 24.sp,
-                max = (iconSize.width + innerPadding.width + outerPadding.width).sp,
-            ),
+            fontSizeRange = rememberSize,
             fontFamily = iconicFont.fontFamily,
             textAlign = TextAlign.Center
         )

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

@@ -52,4 +52,4 @@ val MaterialSymbolsSharp by lazy {
         "icons/MaterialSymbolsSharp.ttf",
         "icons/MaterialSymbolsSharp.codepoints"
     )
-}
+}

+ 3 - 3
src/main/kotlin/eu/mjdev/desktop/components/text/AutoResizeText.kt

@@ -34,7 +34,7 @@ fun AutoResizeText(
     maxLines: Int = Int.MAX_VALUE,
     style: TextStyle = LocalTextStyle.current,
 ) {
-    var fontSizeValue by remember { mutableStateOf(fontSizeRange.max.value) }
+    var fontSizeValue by remember(text, fontSizeRange) { mutableStateOf(fontSizeRange.max.value) }
     var readyToDraw by remember { mutableStateOf(false) }
     Text(
         text = text,
@@ -51,8 +51,8 @@ fun AutoResizeText(
         softWrap = softWrap,
         style = style,
         fontSize = fontSizeValue.sp,
-        onTextLayout = {
-            if (it.didOverflowHeight && !readyToDraw) {
+        onTextLayout = { textLayout ->
+            if (textLayout.didOverflowHeight && !readyToDraw) {
                 val nextFontSizeValue = fontSizeValue - fontSizeRange.step.value
                 if (nextFontSizeValue <= fontSizeRange.min.value) {
                     fontSizeValue = fontSizeRange.min.value

+ 9 - 11
src/main/kotlin/eu/mjdev/desktop/data/App.kt

@@ -4,9 +4,10 @@ import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.graphics.Color
 import eu.mjdev.desktop.helpers.EmptyException.Companion.EmptyException
+import eu.mjdev.desktop.windows.WindowsTracker
 import java.io.File
 
-@Suppress("unused", "MemberVisibilityCanBePrivate")
+@Suppress("unused", "MemberVisibilityCanBePrivate", "HasPlatformType")
 class App(
     val file: File = File("nonexistent_file_path"),
     val desktopFile: DesktopFile = DesktopFile(file),
@@ -49,6 +50,9 @@ class App(
     val cleanExec
         get() = exec.replace("%u", "").replace("%U", "")
 
+    val runningProcesses
+        get() = ProcessHandle.allProcesses()
+
     fun start() = runCatching {
         triggerStart()
         println("Starting app: $name [$cleanExec]")
@@ -96,26 +100,20 @@ class App(
         return this
     }
 
-//    private fun isRunning() =
-//        ProcessHandle.allProcesses().filter {
-//            it.pid() == process?.pid()
-//        }.count() > 0
-
     fun requestWindowFocus() {
-        // todo
+        WindowsTracker().getWindowByPid(process?.pid())?.toFront() ?: closeWindow()
     }
 
     fun minimizeWindow() {
-        // todo
+        WindowsTracker().getWindowByPid(process?.pid())?.minimize()
     }
 
     fun maximizeWindow() {
-        // todo
+        WindowsTracker().getWindowByPid(process?.pid())?.maximize()
     }
 
     fun closeWindow() {
-        // todo
-        exit()
+        WindowsTracker().getWindowByPid(process?.pid())?.close() ?: exit()
     }
 
     fun exit() {

+ 10 - 0
src/main/kotlin/eu/mjdev/desktop/extensions/Compose.kt

@@ -113,4 +113,14 @@ object Compose {
         .memoryCache { imageLoaderMemoryCache }
         .build()
 
+    //    Toolkit.getDefaultToolkit().addAWTEventListener({ event ->
+//        }, AWTEvent.MOUSE_EVENT_MASK or AWTEvent.FOCUS_EVENT_MASK
+//    )
+
+//    Toolkit.getDefaultToolkit().addAWTEventListener(object:AWTEventListener {
+//        override fun eventDispatched(event: AWTEvent?) {
+//            println(event)
+//        }
+//    }, AWTEvent.WINDOW_EVENT_MASK)
+
 }

+ 142 - 0
src/main/kotlin/eu/mjdev/desktop/helpers/PdfDecoder.kt

@@ -0,0 +1,142 @@
+package eu.mjdev.desktop.helpers
+
+import coil3.DrawableImage
+import coil3.Image
+import coil3.ImageLoader
+import coil3.annotation.ExperimentalCoilApi
+import coil3.decode.DecodeResult
+import coil3.decode.DecodeUtils
+import coil3.decode.Decoder
+import coil3.decode.ImageSource
+import coil3.fetch.SourceFetchResult
+import coil3.request.Options
+import coil3.size.Dimension
+import coil3.size.Scale
+import coil3.size.isOriginal
+import coil3.size.pxOrElse
+import org.jetbrains.skia.Canvas
+import org.jetbrains.skia.Data
+import org.jetbrains.skia.Rect
+import org.jetbrains.skia.svg.SVGDOM
+import org.jetbrains.skia.svg.SVGLength
+import org.jetbrains.skia.svg.SVGLengthUnit
+import kotlin.math.roundToInt
+
+// todo from svg
+class PdfDecoder(
+    private val source: ImageSource,
+    private val options: Options,
+    val useViewBoundsAsIntrinsicSize: Boolean,
+) : Decoder {
+    @OptIn(ExperimentalCoilApi::class)
+    override suspend fun decode(): DecodeResult {
+//        val context = options.context
+        val bytes = source.source().readByteArray()
+        val pdf = SVGDOM(Data.makeFromBytes(bytes))
+        val pdfWidth: Float
+        val pdfHeight: Float
+        val viewBox: Rect? = pdf.root?.viewBox
+        if (useViewBoundsAsIntrinsicSize && viewBox != null) {
+            pdfWidth = viewBox.width
+            pdfHeight = viewBox.height
+        } else {
+            pdfWidth = pdf.root?.width?.value ?: 0f
+            pdfHeight = pdf.root?.height?.value ?: 0f
+        }
+        val bitmapWidth: Int
+        val bitmapHeight: Int
+        val (dstWidth, dstHeight) = getDstSize(pdfWidth, pdfHeight, options.scale)
+        if (pdfWidth > 0f && pdfHeight > 0f) {
+            val multiplier = DecodeUtils.computeSizeMultiplier(
+                srcWidth = pdfWidth,
+                srcHeight = pdfHeight,
+                dstWidth = dstWidth.toFloat(),
+                dstHeight = dstHeight.toFloat(),
+                scale = options.scale,
+            )
+            bitmapWidth = (multiplier * pdfWidth).toInt()
+            bitmapHeight = (multiplier * pdfHeight).toInt()
+        } else {
+            bitmapWidth = dstWidth
+            bitmapHeight = dstHeight
+        }
+        if (viewBox == null && pdfWidth > 0f && pdfHeight > 0f) {
+            pdf.root?.viewBox = Rect.makeWH(pdfWidth, pdfHeight)
+        }
+        pdf.root?.width = SVGLength(
+            value = 100f,
+            unit = SVGLengthUnit.PERCENTAGE,
+        )
+        pdf.root?.height = SVGLength(
+            value = 100f,
+            unit = SVGLengthUnit.PERCENTAGE,
+        )
+        pdf.setContainerSize(bitmapWidth.toFloat(), bitmapHeight.toFloat())
+        return DecodeResult(
+            image = pdf.asCoilImage(bitmapWidth, bitmapHeight),
+            isSampled = false,
+        )
+    }
+
+    private fun getDstSize(srcWidth: Float, srcHeight: Float, scale: Scale): Pair<Int, Int> {
+        if (options.size.isOriginal) {
+            val dstWidth = if (srcWidth > 0) srcWidth.roundToInt() else PDF_DEFAULT_SIZE
+            val dstHeight = if (srcHeight > 0) srcHeight.roundToInt() else PDF_DEFAULT_SIZE
+            return dstWidth to dstHeight
+        } else {
+            val (dstWidth, dstHeight) = options.size
+            return dstWidth.toPx(scale) to dstHeight.toPx(scale)
+        }
+    }
+
+    class Factory(
+        val useViewBoundsAsIntrinsicSize: Boolean,
+    ) : Decoder.Factory {
+        override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? {
+            if (!isApplicable(result)) return null
+            return PdfDecoder(result.source, options, useViewBoundsAsIntrinsicSize)
+        }
+
+        private fun isApplicable(result: SourceFetchResult): Boolean =
+            result.mimeType == MIME_TYPE_PDF
+    }
+
+    @OptIn(ExperimentalCoilApi::class)
+    private class PdfImage(
+        val pdf: SVGDOM,
+        override val width: Int,
+        override val height: Int,
+    ) : DrawableImage() {
+        override val size: Long
+            get() = 4L * width * height
+
+        override val shareable: Boolean = true
+
+        @ExperimentalCoilApi
+        override fun Canvas.onDraw() {
+            pdf.render(this)
+        }
+    }
+
+    companion object {
+        private const val PDF_DEFAULT_SIZE = 512
+        private const val MIME_TYPE_PDF = "application/pdf"
+
+        private fun Dimension.toPx(scale: Scale): Int = pxOrElse {
+            when (scale) {
+                Scale.FILL -> Int.MIN_VALUE
+                Scale.FIT -> Int.MAX_VALUE
+            }
+        }
+
+        @OptIn(ExperimentalCoilApi::class)
+        internal fun SVGDOM.asCoilImage(
+            width: Int,
+            height: Int,
+        ): Image = PdfImage(
+            pdf = this,
+            width = width,
+            height = height,
+        )
+    }
+}

+ 24 - 0
src/main/kotlin/eu/mjdev/desktop/provider/DesktopProvider.kt

@@ -19,6 +19,7 @@ import java.io.File
 import java.io.FileReader
 import javax.script.ScriptEngine
 import javax.script.ScriptEngineManager
+import kotlin.system.exitProcess
 
 @Suppress("unused", "MemberVisibilityCanBePrivate", "PrivatePropertyName", "SameParameterValue")
 class DesktopProvider(
@@ -104,6 +105,29 @@ class DesktopProvider(
         }.start()
     }
 
+    @Suppress("UNUSED_PARAMETER")
+    fun login(
+        user: String,
+        password: String
+    ) {
+        // todo
+    }
+
+    fun logOut() {
+        // todo
+        exitProcess(0)
+    }
+
+    fun shutdown() {
+        // todo
+        exitProcess(0)
+    }
+
+    fun restart() {
+        // todo
+        exitProcess(0)
+    }
+
     companion object {
         private val CONTROL_CENTER_PAGES = listOf(
             MainSettingsPage(),

+ 4 - 6
src/main/kotlin/eu/mjdev/desktop/provider/providers/AppsProvider.kt

@@ -13,7 +13,7 @@ import eu.mjdev.desktop.provider.DesktopProvider
 import java.io.File
 import java.util.*
 
-@Suppress("unused", "MemberVisibilityCanBePrivate")
+@Suppress("unused", "MemberVisibilityCanBePrivate", "UNNECESSARY_SAFE_CALL")
 class AppsProvider(
     val desktopProvider: DesktopProvider
 ) {
@@ -108,7 +108,7 @@ class AppsProvider(
         get() = allAppsDesktopFiles.map { file -> App(desktopFile = file, file = file.file) }
     val backgrounds
         get() = runCatching {
-            backgroundFilesDir?.listFiles()?.toList()?.sortedBy { it.name }
+            backgroundFilesDir?.listFiles()?.toList()?.sortedBy { it.name }?.filter { it.isFile }
         }.getOrNull() ?: emptyList<File>()
     val appCategories
         get() = mutableListOf<String>().apply {
@@ -133,8 +133,7 @@ class AppsProvider(
 
     fun startApp(app: App) {
         if (app.isRunning) {
-            // todo : focus window
-            app.closeWindow()
+            app.requestWindowFocus()
         } else {
             app.onStarting {
                 favoriteApps.invalidate()
@@ -159,8 +158,7 @@ class AppsProvider(
             ?.toList<String>()
             ?.mapNotNull { deskFileName ->
                 allAppsDesktopFiles.filter { deskFile ->
-                    @Suppress("PlatformExtensionReceiverOfInline")
-                    deskFile.file.name.contentEquals(deskFileName)
+                    deskFile.file.name?.contentEquals(deskFileName) == true
                 }.map { deskFile ->
                     App(
                         desktopFile = deskFile,

+ 20 - 0
src/main/kotlin/eu/mjdev/desktop/windows/WindowsTracker.kt

@@ -0,0 +1,20 @@
+package eu.mjdev.desktop.windows
+
+@Suppress("unused")
+class WindowsTracker {
+
+    @Suppress("UNUSED_PARAMETER")
+    fun getWindowByPid(
+        pid: Long?
+    ): IWindow? {
+        return null // todo
+    }
+
+    interface IWindow {
+        fun toBack() {}
+        fun toFront() {}
+        fun minimize() {}
+        fun maximize() {}
+        fun close() {}
+    }
+}