milan jurkulák пре 2 година
родитељ
комит
bdae27f82c

+ 25 - 11
build.gradle.kts

@@ -1,3 +1,4 @@
+import org.jetbrains.compose.desktop.DesktopExtension
 import org.jetbrains.compose.desktop.application.dsl.TargetFormat
 
 plugins {
@@ -55,12 +56,9 @@ allprojects {
         implementation(libs.coil.compose)
         implementation(libs.coil.mp)
         implementation(libs.coil.svg)
-//        implementation(libs.coil.gif)
         implementation(libs.coil.network.okhttp)
         // ffmpeg
         implementation(libs.ffmpeg.platform)
-        // palette
-//        implementation(libs.material.kolor)
         // json
         implementation(libs.google.gson)
         // no log
@@ -72,20 +70,20 @@ allprojects {
         // gemini ai
         implementation(libs.google.generativeai)
         // jna
-//        implementation(libs.jna.platform)
+        implementation(libs.jna.platform)
         // qr code
         implementation(libs.qrose)
         // tts
         implementation(libs.tts)
+        // dbus
+        implementation(libs.dbus.java.core)
+        implementation(libs.dbus.java.transport.native.unixsocket)
         // paths
 //        implementation("me.sujanpoudel.multiplatform.utils:multiplatform-paths:0.2.2")
         // files
 //        implementation("io.github.vinceglb:filekit-core:0.8.2")
         // files with composable utilities
 //        implementation("io.github.vinceglb:filekit-compose:0.8.2")
-        // dbus
-//        implementation(libs.dbus.java.core)
-//        implementation(libs.dbus.java.transport.native.unixsocket)
         // testing yet
         // anim
 //        implementation("org.jetbrains.compose.animation:animation:1.7.0-alpha03")
@@ -100,6 +98,10 @@ allprojects {
         implementation("com.github.skydoves:flexible-bottomsheet-material3:0.1.5")
         // fs watcher
         implementation("io.github.irgaly.kfswatch:kfswatch:1.3.0")
+        // stt
+        implementation("com.alphacephei:vosk:0.3.32+")
+        // custom components
+        implementation("com.composables:core:1.12.0")
         // sikulix
 //        implementation("com.sikulix:sikulixapi:2.0.5")
         // blur
@@ -143,6 +145,10 @@ allprojects {
     }
 }
 
+inline fun <reified T> Project.configureIfExists(fn: T.() -> Unit) {
+    extensions.findByType(T::class.java)?.fn()
+}
+
 fun Project.createTask(
     name: String,
     group: String = "mjdev",
@@ -223,12 +229,12 @@ compose {
                     modules("jdk.security.auth")
                 }
                 targetFormats(
-                    TargetFormat.Dmg,
-                    TargetFormat.Msi,
+//                    TargetFormat.Dmg,
+//                    TargetFormat.Msi,
                     TargetFormat.Deb,
-                    TargetFormat.Exe,
+//                    TargetFormat.Exe,
                     TargetFormat.AppImage,
-                    TargetFormat.Pkg,
+//                    TargetFormat.Pkg,
                     TargetFormat.Rpm
                 )
 //                buildTypes.release.proguard {
@@ -240,6 +246,14 @@ compose {
     }
 }
 
+configureIfExists<DesktopExtension> {
+    application {
+        jvmArgs += "--enable-preview"
+    }
+    nativeApplication {
+    }
+}
+
 createTask(
     name = "cleanUp",
     group = "mjdev",

+ 2 - 2
gradle/libs.versions.toml

@@ -24,7 +24,7 @@ mozilla-rhino = "1.7.13"
 ini4j = "0.5.4"
 charts = "Beta-0.0.5"
 generativeai = "0.5.0-1.0.0-wasm"
-#jna-platform = "5.6.0"
+jna-platform = "5.6.0"
 qrose = "1.0.1"
 tts = "2.5.0"
 #korau = "4.0.10"
@@ -51,7 +51,7 @@ fuzzywuzzy = { module = "me.xdrop:fuzzywuzzy", version.ref = "fuzzywuzzy" }
 ffmpeg-platform = { module = "org.bytedeco:ffmpeg-platform", version.ref = "ffmpeg-platform" }
 charts = { module = "io.github.thechance101:chart", version.ref = "charts" }
 google-generativeai = { module = "dev.shreyaspatil.generativeai:generativeai-google-jvm", version.ref = "generativeai" }
-#jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna-platform" }
+jna-platform = { module = "net.java.dev.jna:jna-platform", version.ref = "jna-platform" }
 qrose = { module = "io.github.alexzhirkevich:qrose", version.ref = "qrose" }
 tts = { module = "nl.marc-apps:tts", version.ref = "tts" }
 dbus-java-core = { module = "com.github.hypfvieh:dbus-java-core", version = "5.0.0" }

+ 55 - 30
src/main/kotlin/eu/mjdev/desktop/components/file/FolderView.kt

@@ -1,23 +1,22 @@
 package eu.mjdev.desktop.components.file
 
 import androidx.compose.desktop.ui.tooling.preview.Preview
+import androidx.compose.foundation.*
 import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.FlowColumn
-import androidx.compose.foundation.layout.FlowColumnOverflow
-import androidx.compose.foundation.layout.FlowRow
-import androidx.compose.foundation.layout.FlowRowOverflow
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 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.unit.dp
+import com.composables.core.*
 import eu.mjdev.desktop.data.DesktopFolderItem
+import eu.mjdev.desktop.extensions.Compose.SuperDarkGray
+import eu.mjdev.desktop.extensions.Modifier.conditional
 import eu.mjdev.desktop.provider.DesktopProvider
 import eu.mjdev.desktop.provider.DesktopProvider.Companion.LocalDesktop
 import java.io.File
@@ -33,7 +32,7 @@ import java.io.File
 // ShellFolderManager
 // X11GraphicsEnvironment
 @Suppress("FunctionName", "SimplifyBooleanWithConstants")
-@OptIn(ExperimentalLayoutApi::class)
+@OptIn(ExperimentalLayoutApi::class, ExperimentalFoundationApi::class)
 @Composable
 fun FolderView(
     modifier: Modifier = Modifier,
@@ -68,15 +67,25 @@ fun FolderView(
             } ?: emptyList()
     },
     zIndexState: MutableState<Int> = remember { mutableStateOf(files.size) },
-    orientation: Orientation = Orientation.Vertical
+    orientation: Orientation = Orientation.Vertical,
+    scrollState: ScrollState = rememberScrollState(),
+    scrollbarsState: ScrollAreaState = rememberScrollAreaState(scrollState)
 ) = Box(
-    modifier = modifier.verticalScroll(rememberScrollState())
+    modifier = modifier.conditional(
+        condition = orientation == Orientation.Horizontal,
+        onTrue = {
+            verticalScroll(scrollState)
+        },
+        onFalse = {
+            horizontalScroll(scrollState)
+        }
+    )
 ) {
     val content: @Composable () -> Unit = {
         files.forEach { file ->
             FolderIcon(
                 path = file.path,
-                customName=file.customName,
+                customName = file.customName,
                 zIndex = { zIndexState.value },
                 onDragStart = {
                     zIndexState.value += 1
@@ -84,22 +93,38 @@ fun FolderView(
             )
         }
     }
-    if (orientation == Orientation.Horizontal) {
-        FlowRow(
-            modifier = Modifier.fillMaxSize(),
-            overflow = FlowRowOverflow.Visible,
-            horizontalArrangement = Arrangement.Start,
-            verticalArrangement = Arrangement.Top,
-            content = { content() }
-        )
-    } else {
-        FlowColumn(
-            modifier = Modifier.fillMaxSize(),
-            overflow = FlowColumnOverflow.Visible,
-            horizontalArrangement = Arrangement.Start,
-            verticalArrangement = Arrangement.Top,
-            content = { content() }
-        )
+    ScrollArea(state = scrollbarsState) {
+        if (orientation == Orientation.Horizontal) {
+            FlowRow(
+                modifier = Modifier.fillMaxSize(),
+                overflow = FlowRowOverflow.Visible,
+                horizontalArrangement = Arrangement.Start,
+                verticalArrangement = Arrangement.Top,
+                content = { content() }
+            )
+            HorizontalScrollbar(
+                modifier = Modifier.align(Alignment.TopEnd)
+                    .fillMaxHeight()
+                    .width(4.dp)
+            ) {
+                Thumb(Modifier.background(Color.SuperDarkGray))
+            }
+        } else {
+            FlowColumn(
+                modifier = Modifier.fillMaxSize(),
+                overflow = FlowColumnOverflow.Visible,
+                horizontalArrangement = Arrangement.Start,
+                verticalArrangement = Arrangement.Top,
+                content = { content() }
+            )
+            VerticalScrollbar(
+                modifier = Modifier.align(Alignment.TopEnd)
+                    .fillMaxHeight()
+                    .width(4.dp)
+            ) {
+                Thumb(Modifier.background(Color.SuperDarkGray))
+            }
+        }
     }
 }
 

+ 0 - 10
src/main/kotlin/eu/mjdev/desktop/data/App.kt

@@ -3,16 +3,6 @@ package eu.mjdev.desktop.data
 import androidx.compose.runtime.MutableState
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.ui.graphics.Color
-import eu.mjdev.desktop.data.DesktopFile.Companion.Categories
-import eu.mjdev.desktop.data.DesktopFile.Companion.Comment
-import eu.mjdev.desktop.data.DesktopFile.Companion.Exec
-import eu.mjdev.desktop.data.DesktopFile.Companion.Icon
-import eu.mjdev.desktop.data.DesktopFile.Companion.Name
-import eu.mjdev.desktop.data.DesktopFile.Companion.NotifyOnStart
-import eu.mjdev.desktop.data.DesktopFile.Companion.Path
-import eu.mjdev.desktop.data.DesktopFile.Companion.RunInTerminal
-import eu.mjdev.desktop.data.DesktopFile.Companion.Type
-import eu.mjdev.desktop.data.DesktopFile.Companion.Version
 import eu.mjdev.desktop.helpers.exception.EmptyException.Companion.EmptyException
 import eu.mjdev.desktop.helpers.system.Shell
 import eu.mjdev.desktop.provider.DesktopProvider

+ 149 - 99
src/main/kotlin/eu/mjdev/desktop/data/DesktopFile.kt

@@ -8,7 +8,7 @@ import org.ini4j.Profile.Section
 import org.ini4j.Wini
 import java.io.File
 
-@Suppress("unused", "MemberVisibilityCanBePrivate", "ConstPropertyName")
+@Suppress("unused", "MemberVisibilityCanBePrivate", "ConstPropertyName", "PropertyName")
 class DesktopFile(
     val file: File,
     val content: Ini = Wini(file),
@@ -20,167 +20,217 @@ class DesktopFile(
         get() = content.map { if (it is Section) it else null }.filterNotNull().associateBy { it.value.toString() }
 
     val desktopSection
-        get() = content[DesktopFileType.DesktopEntry.text]
+        get() = content[DesktopFileType.DesktopEntry.text]?.let { DesktopSectionScope(it) }
 
     val themeSection
-        get() = content[DesktopFileType.Theme.text]
+        get() = content[DesktopFileType.Theme.text]?.let { ThemeSectionScope(it) }
 
     fun section(
         type: DesktopFileType,
         block: Section.() -> Unit
     ) = (content[type.text] ?: content.add(type.text))?.apply(block)
 
-    fun write() {
-        if (file.exists()) file.delete()
-        file.createNewFile()
-        content.store(file)
+    fun desktopSection(
+        block: DesktopSectionScope.() -> Unit
+    ) = section(DesktopFile.DesktopFileType.DesktopEntry) {
+        DesktopSectionScope(this).apply(block)
     }
 
-    enum class DesktopFileType(val text: String) {
-        Unknown(""),
-        DesktopEntry("Desktop Entry"),
-        Application("Application"),
-        Theme("X-GNOME-Metatheme");
+    fun themeSection(
+        block: ThemeSectionScope.() -> Unit
+    ) = section(DesktopFile.DesktopFileType.Theme) {
+        ThemeSectionScope(this).apply(block)
+    }
 
-        companion object {
-            operator fun invoke(
-                value: String?
-            ): DesktopFileType = value.orEmpty().trim().let { v ->
-                entries.firstOrNull { e -> e.text.contentEquals(v, true) } ?: Unknown
-            }
+    fun write() {
+        if (file.exists()) {
+            file.delete()
         }
+        file.createNewFile()
+        content.store(file)
     }
 
-    companion object {
-        const val Prop_Type = "Type"
-        const val Prop_Version = "Version"
-        const val Prop_Path = "Path"
-        const val Prop_Name = "Name"
-        const val Prop_Comment = "Comment"
-        const val Prop_Exec = "Exec"
-        const val Prop_StartupNotify = "StartupNotify"
-        const val Prop_Terminal = "Terminal"
-        const val Prop_Icon = "Icon"
-        const val Prop_Categories = "Categories"
-        const val Prop_GenericName = "GenericName"
-        const val Prop_MimeType = "MimeType"
-        const val Prop_Actions = "Actions"
-        const val Prop_DBusActivatable = "DBusActivatable"
-        const val Prop_Encoding = "Encoding"
-
-        const val Prop_X_GNOME_UsesNotifications = "X-GNOME-UsesNotifications"
-        const val Prop_X_Purism_FormFactor = "X-Purism-FormFactor"
-        const val Prop_X_Unity_IconBackgroundColor = "X-Unity-IconBackgroundColor"
-        const val Prop_X_Ubuntu_Gettext_Domain = "X-Ubuntu-Gettext-Domain"
-
-        const val Prop_GtkTheme = "GtkTheme"
-        const val Prop_MetacityTheme = "MetacityTheme"
-        const val Prop_IconTheme = "IconTheme"
-        const val Prop_CursorTheme = "CursorTheme"
-        const val Prop_ButtonLayout = "ButtonLayout"
-        const val Prop_X_Ubuntu_UseOverlayScrollbars = "X-Ubuntu-UseOverlayScrollbars"
+    interface ISectionScope {
+        val section: Section
+    }
 
-        var Section.Type: DesktopFileType
-            get() = DesktopFileType(this[Prop_Type])
+    class DesktopSectionScope(
+        override val section: Section
+    ) : ISectionScope {
+        var Type: DesktopFileType
+            get() = DesktopFileType(section[Prop_Type])
             set(value) {
-                this[Prop_Type] = value.text
+                section[Prop_Type] = value.text
             }
 
-        var Section.Version: String
-            get() = ParsedString(this[Prop_Version])
+        var Version: String
+            get() = ParsedString(section[Prop_Version])
             set(value) {
-                this[Prop_Version] = value
+                section[Prop_Version] = value
             }
 
-        var Section.Name: String
-            get() = ParsedString(this[Prop_Name])
+        var Name: String
+            get() = ParsedString(section[Prop_Name])
             set(value) {
-                this[Prop_Name] = value
+                section[Prop_Name] = value
             }
 
-        var Section.Comment: String
-            get() = ParsedString(this[Prop_Comment])
+        var Comment: String
+            get() = ParsedString(section[Prop_Comment])
             set(value) {
-                this[Prop_Comment] = value
+                section[Prop_Comment] = value
             }
 
-        var Section.Path: String
-            get() = ParsedString(this[Prop_Path])
+        var Path: String
+            get() = ParsedString(section[Prop_Path])
             set(value) {
-                this[Prop_Path] = value
+                section[Prop_Path] = value
             }
 
-        var Section.Exec: String
-            get() = ParsedString(this[Prop_Exec])
+        var Exec: String
+            get() = ParsedString(section[Prop_Exec])
             set(value) {
-                this[Prop_Exec] = value
+                section[Prop_Exec] = value
             }
 
-        var Section.Icon: String
-            get() = ParsedString(this[Prop_Icon])
+        var Icon: String
+            get() = ParsedString(section[Prop_Icon])
             set(value) {
-                this[Prop_Icon] = value
+                section[Prop_Icon] = value
             }
-
-        var Section.Encoding: String
-            get() = ParsedString(this[Prop_Encoding])
+        var Encoding: String
+            get() = ParsedString(section[Prop_Encoding])
             set(value) {
-                this[Prop_Encoding] = value
+                section[Prop_Encoding] = value
             }
-
-        var Section.NotifyOnStart: Boolean
-            get() = ParsedBoolean(this[Prop_StartupNotify])
+        var NotifyOnStart: Boolean
+            get() = ParsedBoolean(section[Prop_StartupNotify])
             set(value) {
-                this[Prop_StartupNotify] = value.toString()
+                section[Prop_StartupNotify] = value.toString()
             }
 
-        var Section.RunInTerminal: Boolean
-            get() = ParsedBoolean(this[Prop_Terminal])
+        var RunInTerminal: Boolean
+            get() = ParsedBoolean(section[Prop_Terminal])
             set(value) {
-                this[Prop_Terminal] = value.toString()
+                section[Prop_Terminal] = value.toString()
             }
 
-        var Section.Categories: MutableList<String>
-            get() = ParsedList(this[Prop_Categories])
+        var Categories: MutableList<String>
+            get() = ParsedList(section[Prop_Categories])
             set(value) {
-                this[Prop_Categories] = value.joinToString { "$it;" }
+                section[Prop_Categories] = value.joinToString { "$it;" }
             }
+    }
 
-        var Section.GtkTheme: String
-            get() = ParsedString(this[Prop_GtkTheme])
+    class ThemeSectionScope(
+        override val section: Section
+    ) : ISectionScope {
+        var GtkTheme: String
+            get() = ParsedString(section[Prop_GtkTheme])
             set(value) {
-                this[Prop_GtkTheme] = value
+                section[Prop_GtkTheme] = value
             }
 
-        var Section.MetacityTheme: String
-            get() = ParsedString(this[Prop_MetacityTheme])
+        var MetacityTheme: String
+            get() = ParsedString(section[Prop_MetacityTheme])
             set(value) {
-                this[Prop_MetacityTheme] = value
+                section[Prop_MetacityTheme] = value
             }
 
-        var Section.IconTheme: String
-            get() = ParsedString(this[Prop_IconTheme])
+        var IconTheme: String
+            get() = ParsedString(section[Prop_IconTheme])
             set(value) {
-                this[Prop_IconTheme] = value
+                section[Prop_IconTheme] = value
             }
 
-        var Section.CursorTheme: String
-            get() = ParsedString(this[Prop_CursorTheme])
+        var CursorTheme: String
+            get() = ParsedString(section[Prop_CursorTheme])
             set(value) {
-                this[Prop_CursorTheme] = value
+                section[Prop_CursorTheme] = value
             }
 
-        var Section.ButtonLayout: String
-            get() = ParsedString(this[Prop_ButtonLayout])
+        var ButtonLayout: String
+            get() = ParsedString(section[Prop_ButtonLayout])
             set(value) {
-                this[Prop_ButtonLayout] = value
+                section[Prop_ButtonLayout] = value
             }
 
-        var Section.UseOverlayScrollbars: Boolean
-            get() = ParsedBoolean(this[Prop_X_Ubuntu_UseOverlayScrollbars])
+        var UseOverlayScrollbars: Boolean
+            get() = ParsedBoolean(section[Prop_X_Ubuntu_UseOverlayScrollbars])
             set(value) {
-                this[Prop_X_Ubuntu_UseOverlayScrollbars] = value.toString()
+                section[Prop_X_Ubuntu_UseOverlayScrollbars] = value.toString()
+            }
+    }
+
+    enum class DesktopFileType(val text: String) {
+        Unknown(""),
+        DesktopEntry("Desktop Entry"),
+        Application("Application"),
+        Theme("X-GNOME-Metatheme");
+
+        companion object {
+            operator fun invoke(
+                value: String?
+            ): DesktopFileType = value.orEmpty().trim().let { v ->
+                entries.firstOrNull { e -> e.text.contentEquals(v, true) } ?: Unknown
             }
+        }
+    }
+
+    fun mkDirs(): DesktopFile {
+        if (!file.parentFile.exists()) file.parentFile.mkdirs()
+        return this
+    }
+
+    fun deleteFile(): DesktopFile {
+        if (file.exists()) file.delete()
+        return this
+    }
+
+    fun createNewFile(): DesktopFile {
+        file.createNewFile()
+        return this
+    }
+
+    fun newFile(): DesktopFile {
+        mkDirs()
+        deleteFile()
+        createNewFile()
+        return this
+    }
+
+    companion object {
+        const val Prop_Type = "Type"
+        const val Prop_Version = "Version"
+        const val Prop_Path = "Path"
+        const val Prop_Name = "Name"
+        const val Prop_Comment = "Comment"
+        const val Prop_Exec = "Exec"
+        const val Prop_StartupNotify = "StartupNotify"
+        const val Prop_Terminal = "Terminal"
+        const val Prop_Icon = "Icon"
+        const val Prop_Categories = "Categories"
+        const val Prop_GenericName = "GenericName"
+        const val Prop_MimeType = "MimeType"
+        const val Prop_Actions = "Actions"
+        const val Prop_DBusActivatable = "DBusActivatable"
+        const val Prop_Encoding = "Encoding"
+
+        const val Prop_X_GNOME_UsesNotifications = "X-GNOME-UsesNotifications"
+        const val Prop_X_Purism_FormFactor = "X-Purism-FormFactor"
+        const val Prop_X_Unity_IconBackgroundColor = "X-Unity-IconBackgroundColor"
+        const val Prop_X_Ubuntu_Gettext_Domain = "X-Ubuntu-Gettext-Domain"
+
+        const val Prop_GtkTheme = "GtkTheme"
+        const val Prop_MetacityTheme = "MetacityTheme"
+        const val Prop_IconTheme = "IconTheme"
+        const val Prop_CursorTheme = "CursorTheme"
+        const val Prop_ButtonLayout = "ButtonLayout"
+        const val Prop_X_Ubuntu_UseOverlayScrollbars = "X-Ubuntu-UseOverlayScrollbars"
 
+        fun desktopFile(
+            file: File,
+            block: DesktopFile.() -> Unit
+        ) = DesktopFile(file).apply(block)
     }
 }

+ 8 - 6
src/main/kotlin/eu/mjdev/desktop/helpers/internal/Palette.kt

@@ -10,6 +10,8 @@ import eu.mjdev.desktop.extensions.ColorUtils.lighter
 import eu.mjdev.desktop.extensions.ColorUtils.nonAlphaValue
 import eu.mjdev.desktop.extensions.Compose.SuperDarkGray
 import eu.mjdev.desktop.extensions.Image.loadPicture
+import eu.mjdev.desktop.helpers.managers.GnomeManager.Companion.THEME_MJDEV
+import eu.mjdev.desktop.helpers.managers.GnomeManager.Companion.THEME_YARU
 import eu.mjdev.desktop.provider.DesktopProvider
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
@@ -79,15 +81,15 @@ class Palette(
             backgroundColorState.value = background
             textColorState.value = text
         }
-//        createFromPalette()
-//        setGtkTheme(THEME_MJDEV)
+        createFromPalette()
+        setGtkTheme(THEME_MJDEV)
     }
 
-//    fun createFromPalette() =
-//        api.gtkTheme.createFromPalette()
+    fun createFromPalette() =
+        api.gtkTheme.createFromPalette()
 
-//    fun setGtkTheme(themeName: String = THEME_YARU) =
-//        api.gnome.setGTKTheme(themeName)
+    fun setGtkTheme(themeName: String = THEME_YARU) =
+        api.gnome.setGTKTheme(themeName)
 
     companion object {
         @Composable

+ 9 - 9
src/main/kotlin/eu/mjdev/desktop/helpers/managers/GnomeManager.kt

@@ -33,15 +33,15 @@ class GnomeManager {
         schemeName
     )
 
-//    fun setGTKTheme(
-//        themeName: String = THEME_YARU
-//    ) = Shell.executeAndRead(
-//        "gsettings",
-//        "set",
-//        "org.gnome.desktop.interface",
-//        "gtk-theme",
-//        themeName
-//    )
+    fun setGTKTheme(
+        themeName: String = THEME_YARU
+    ) = Shell.executeAndRead(
+        "gsettings",
+        "set",
+        "org.gnome.desktop.interface",
+        "gtk-theme",
+        themeName
+    )
 
     fun getGTKTheme(): String = Shell.executeAndRead(
         "gsettings",

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

@@ -127,7 +127,7 @@ class AppsProvider(
     val categoriesAndApps
         get() = mutableMapOf<String, List<App>>().let { map ->
             allApps.forEach { app ->
-                app.categories.forEach { category ->
+                app.categories?.forEach { category ->
                     if (!map.containsKey(category)) {
                         map[category] = mutableListOf()
                     }

+ 31 - 38
src/main/kotlin/eu/mjdev/desktop/theme/gtk/GtkThemeHelper.kt

@@ -2,23 +2,12 @@ package eu.mjdev.desktop.theme.gtk
 
 import androidx.compose.ui.graphics.Color
 import eu.mjdev.desktop.data.DesktopFile
-import eu.mjdev.desktop.data.DesktopFile.Companion.Comment
-import eu.mjdev.desktop.data.DesktopFile.Companion.Encoding
-import eu.mjdev.desktop.data.DesktopFile.Companion.Name
-import eu.mjdev.desktop.data.DesktopFile.Companion.Type
-import eu.mjdev.desktop.data.DesktopFile.Companion.MetacityTheme
-import eu.mjdev.desktop.data.DesktopFile.Companion.GtkTheme
-import eu.mjdev.desktop.data.DesktopFile.Companion.IconTheme
-import eu.mjdev.desktop.data.DesktopFile.Companion.CursorTheme
-import eu.mjdev.desktop.data.DesktopFile.Companion.ButtonLayout
-import eu.mjdev.desktop.data.DesktopFile.Companion.UseOverlayScrollbars
+import eu.mjdev.desktop.data.DesktopFile.Companion.desktopFile
 import eu.mjdev.desktop.extensions.Compose.SuperDarkGray
 import eu.mjdev.desktop.extensions.Compose.rgbToHex
 import eu.mjdev.desktop.helpers.internal.Palette
-import eu.mjdev.desktop.helpers.managers.GnomeManager.Companion.THEME_ADWAITA
 import eu.mjdev.desktop.helpers.managers.GnomeManager.Companion.THEME_ADWAITA_DARK
 import eu.mjdev.desktop.helpers.managers.GnomeManager.Companion.THEME_MJDEV
-import eu.mjdev.desktop.helpers.managers.GnomeManager.Companion.THEME_YARU
 import eu.mjdev.desktop.provider.DesktopProvider
 import java.io.File
 
@@ -43,17 +32,16 @@ class GtkThemeHelper(
     }
 
     private fun createDesktopFile() {
-        if (!themeDir.exists()) themeDir.mkdirs()
-        if (!themeDesktopFile.exists()) themeDesktopFile.delete()
-        themeDesktopFile.createNewFile()
-        with(DesktopFile(themeDesktopFile)) {
-            section(DesktopFile.DesktopFileType.DesktopEntry) {
+        desktopFile(themeDesktopFile) {
+            mkDirs()
+            deleteFile()
+            desktopSection {
                 Type = DesktopFile.DesktopFileType.Theme
                 Name = THEME_MJDEV
                 Comment = "dynamic system theme"
                 Encoding = "UTF-8" // todo
             }
-            section(DesktopFile.DesktopFileType.Theme) {
+            themeSection {
                 GtkTheme = THEME_MJDEV
                 MetacityTheme = THEME_ADWAITA_DARK
                 IconTheme = THEME_ADWAITA_DARK
@@ -65,24 +53,19 @@ class GtkThemeHelper(
         }
     }
 
-    private fun createCssFile(file: File) {
-        theme {
-            bgColor = palette.backgroundColor
-            fgColor = palette.textColor
-            baseColor = palette.baseColor
-            textColor = palette.textColor
-            selectedBgColor = palette.selectedBgColor
-            selectedFgColor = palette.selectedFgColor
-            tooltipBgColor = palette.tooltipBgColor
-            tooltipFgColor = palette.tooltipFgColor
-        }.toString().also { data ->
-            file.apply { parentFile.mkdirs() }.writeText(data)
-        }
+    private fun createCssFile(file: File) = theme(file) {
+        bgColor = palette.backgroundColor
+        fgColor = palette.textColor
+        baseColor = palette.baseColor
+        textColor = palette.textColor
+        selectedBgColor = palette.selectedBgColor
+        selectedFgColor = palette.selectedFgColor
+        tooltipBgColor = palette.tooltipBgColor
+        tooltipFgColor = palette.tooltipFgColor
+        write()
     }
 
-    class Theme(
-        val name: String = THEME_ADWAITA,
-    ) {
+    class Theme(val file: File) {
         var bgColor: Color = Color.SuperDarkGray
         var fgColor: Color = Color.SuperDarkGray
         var baseColor: Color = Color.SuperDarkGray
@@ -102,8 +85,8 @@ class GtkThemeHelper(
         var warningBgColor: Color = Color.Magenta
         var infoFgColor: Color = Color.Black
         var infoBgColor: Color = Color.White
-        var osdFgColor : Color = Color.Black
-        var osdBgColor : Color = Color.SuperDarkGray
+        var osdFgColor: Color = Color.Black
+        var osdBgColor: Color = Color.SuperDarkGray
 
         override fun toString() = """
             @define-color bg_color ${bgColor.rgbToHex()};
@@ -175,8 +158,18 @@ class GtkThemeHelper(
 
     companion object {
         fun theme(
-            name: String = THEME_YARU,
+            file: File,
             block: Theme.() -> Unit
-        ) = Theme(name).apply(block)
+        ) = Theme(file).apply(block)
+
+        fun Theme.write() {
+            toString().also { data ->
+                file.apply {
+                    parentFile.mkdirs()
+                    delete()
+                    createNewFile()
+                }.writeText(data)
+            }
+        }
     }
 }

+ 0 - 0
src/main/resources/dependencies/linux/bin/mjdev-desktop → src/main/resources/dependencies/linux/sbin/mjdev-desktop