Browse Source

theme autocreate

milan jurkulák 2 years ago
parent
commit
d4fa2ee167

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

@@ -191,7 +191,7 @@ fun AppsMenu(
                             .fillMaxWidth()
                             .wrapContentHeight()
                             .background(backgroundColor),
-                        backButtonVisible = items.first() is App,
+                        backButtonVisible = items.firstOrNull() is App,
                         onBackClick = {
                             items = appCategories
                         },

+ 25 - 16
src/main/kotlin/eu/mjdev/desktop/data/App.kt

@@ -3,6 +3,16 @@ 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
@@ -13,32 +23,32 @@ import java.io.File
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
 class App(
-    val file: File = File("nonexistent_file_path"),
-    val desktopFile: DesktopFile = DesktopFile(file),
+    val file: File? = null,
+    val desktopFile: DesktopFile? = runCatching { file?.let { f -> DesktopFile(f) } }.getOrNull(),
     val isFavorite: Boolean = false
 ) {
     val fileName
-        get() = desktopFile.fileName
+        get() = desktopFile?.fileName
     val type
-        get() = desktopFile.type
+        get() = desktopFile?.desktopSection?.Type ?: ""
     val version
-        get() = desktopFile.version
+        get() = desktopFile?.desktopSection?.Version ?: ""
     val name
-        get() = desktopFile.name
+        get() = desktopFile?.desktopSection?.Name ?: ""
     val comment
-        get() = desktopFile.comment
+        get() = desktopFile?.desktopSection?.Comment ?: ""
     val path
-        get() = desktopFile.path
+        get() = desktopFile?.desktopSection?.Path ?: ""
     val exec
-        get() = desktopFile.exec
+        get() = desktopFile?.desktopSection?.Exec ?: ""
     val iconName
-        get() = desktopFile.icon
+        get() = desktopFile?.desktopSection?.Icon ?: ""
     val categories
-        get() = desktopFile.categories.ifEmptyCategories { listOf(Category.UNCATEGORIZED) }
+        get() = desktopFile?.desktopSection?.Categories.ifEmptyCategories { listOf(Category.UNCATEGORIZED) }
     val notifyOnStart
-        get() = desktopFile.notifyOnStart
+        get() = desktopFile?.desktopSection?.NotifyOnStart ?: false
     val runInTerminal
-        get() = desktopFile.runInTerminal
+        get() = desktopFile?.desktopSection?.RunInTerminal ?: false
 
     var iconTint: Color? = null
     val iconBackground: Color = Color.White
@@ -80,7 +90,7 @@ class App(
             process = Shell(api)
                 .writeCommand("export DBUS_SESSION_BUS_ADDRESS=\"unix:path=\$XDG_RUNTIME_DIR/bus\"")
                 .writeCommand("$cmd  &")
-                .writeCommand( "exit")
+                .writeCommand("exit")
                 .apply {
                     triggerStarted()
                     println("app started.")
@@ -137,8 +147,7 @@ class App(
     override fun equals(other: Any?): Boolean {
         return when {
             other is App -> {
-                @Suppress("PlatformExtensionReceiverOfInline")
-                other.file.name.contentEquals(file.name)
+                other.file?.name?.contentEquals(file?.name) ?: false
             }
 
             else -> false

+ 129 - 121
src/main/kotlin/eu/mjdev/desktop/data/DesktopFile.kt

@@ -1,131 +1,42 @@
-@file:Suppress("DEPRECATION")
-
 package eu.mjdev.desktop.data
 
 import eu.mjdev.desktop.extensions.Custom.ParsedBoolean
 import eu.mjdev.desktop.extensions.Custom.ParsedList
 import eu.mjdev.desktop.extensions.Custom.ParsedString
 import org.ini4j.Ini
-import org.ini4j.Profile
+import org.ini4j.Profile.Section
 import org.ini4j.Wini
 import java.io.File
-import java.io.InputStream
-import java.io.StringBufferInputStream
 
-@Suppress("ConstPropertyName", "unused", "CanBeParameter", "MemberVisibilityCanBePrivate", "KotlinConstantConditions")
+@Suppress("unused", "MemberVisibilityCanBePrivate", "ConstPropertyName")
 class DesktopFile(
     val file: File,
-    private val content: Ini? = runCatching { if (file.exists()) Wini(file) else null }.getOrNull(),
-    private val props: Profile.Section? = content?.get(DesktopFileType.DesktopEntry.text)
+    val content: Ini = Wini(file),
 ) {
     var fileName: String = file.name
     val absolutePath: String = file.absolutePath
 
-    val actions: MutableList<Action> = parseActions()
+    val sections: Map<String, Section>
+        get() = content.map { if (it is Section) it else null }.filterNotNull().associateBy { it.value.toString() }
 
-    var type: DesktopFileType = DesktopFileType(props?.get(Prop_Type))
-    var version: String = ParsedString(props?.get(Prop_Version))
-    var name: String = ParsedString(props?.get(Prop_Name))
-    var comment: String = ParsedString(props?.get(Prop_Comment))
-    var path: String = ParsedString(props?.get(Prop_Path))
-    var exec: String = ParsedString(props?.get(Prop_Exec))
-    var icon: String = ParsedString(props?.get(Prop_Icon))
-    var categories: MutableList<String> = ParsedList(props?.get(Prop_Categories))
-    var notifyOnStart: Boolean = ParsedBoolean(props?.get(Prop_StartupNotify))
-    var runInTerminal: Boolean = ParsedBoolean(props?.get(Prop_Terminal))
-    var encoding: String = ParsedString(props?.get(Prop_Encoding))
+    val desktopSection
+        get() = content[DesktopFileType.DesktopEntry.text]
 
-    // todo
-    private fun parseActions(): MutableList<Action> = mutableListOf()
+    val themeSection
+        get() = content[DesktopFileType.Theme.text]
 
-    fun action(
-        header: DesktopFileType,
-        block: Action.() -> Unit
-    ) = actions.add(Action("[${header.text}]").apply(block))
+    fun section(
+        type: DesktopFileType,
+        block: Section.() -> Unit
+    ) = (content[type.text] ?: content.add(type.text))?.apply(block)
 
     fun write() {
-        Wini().also { ini ->
-            ini.add(DesktopFileType.DesktopEntry.text).also { section ->
-                section.add(Prop_Type, type.text)
-                if (name.isNotEmpty()) section.add(Prop_Name, name)
-                if (comment.isNotEmpty()) section.add(Prop_Comment, comment)
-                if (version.isNotEmpty()) section.add(Prop_Version, version)
-                if (encoding.isNotEmpty()) section.add(Prop_Encoding, encoding)
-                if (type == DesktopFileType.Application) {
-                    if (path.isNotEmpty()) section.add(Prop_Path, path)
-                    if (exec.isNotEmpty()) section.add(Prop_Exec, exec)
-                    if (icon.isNotEmpty()) section.add(Prop_Icon, icon)
-                    if (categories.isNotEmpty()) section.add(Prop_Categories, categories.joinToString { "$it;" })
-                    if (notifyOnStart) section.add(Prop_StartupNotify, notifyOnStart)
-                    if (runInTerminal) section.add(Prop_Terminal, runInTerminal)
-                }
-                actions.forEach { action ->
-                    section.addChild(action.type.text).also { subsection ->
-                        if (type == DesktopFileType.Application) {
-                            subsection.add(Prop_Type, type.text)
-                            if (version.isNotEmpty()) subsection.add(Prop_Type, version)
-                            if (name.isNotEmpty()) subsection.add(Prop_Name, name)
-                            if (comment.isNotEmpty()) subsection.add(Prop_Comment, comment)
-                            if (path.isNotEmpty()) subsection.add(Prop_Path, path)
-                            if (exec.isNotEmpty()) subsection.add(Prop_Exec, exec)
-                            if (icon.isNotEmpty()) subsection.add(Prop_Icon, icon)
-                            if (categories.isNotEmpty()) subsection.add(
-                                Prop_Categories,
-                                categories.joinToString { "$it;" })
-                            if (notifyOnStart) subsection.add(Prop_StartupNotify, notifyOnStart)
-                            if (runInTerminal) subsection.add(Prop_Terminal, runInTerminal)
-                        }
-                        if (type == DesktopFileType.Theme) {
-                            if (action.gtkTheme.isNotEmpty()) subsection.add(Prop_GtkTheme, action.gtkTheme)
-                            if (action.metacityTheme.isNotEmpty()) subsection.add(
-                                Prop_MetacityTheme,
-                                action.metacityTheme
-                            )
-                            if (action.iconTheme.isNotEmpty()) subsection.add(Prop_IconTheme, action.iconTheme)
-                            if (action.cursorTheme.isNotEmpty()) subsection.add(Prop_CursorTheme, action.cursorTheme)
-                            if (action.buttonLayout.isNotEmpty()) subsection.add(Prop_ButtonLayout, action.buttonLayout)
-//                            subsection.add(Prop_X_Ubuntu_UseOverlayScrollbars, action.useOverlayScrollbars)
-                        }
-                        if (encoding.isNotEmpty()) section.add(Prop_Encoding, encoding)
-                    }
-                }
-            }
-            if (file.exists()) {
-                file.delete()
-            }
-            file.createNewFile()
-            ini.store(file)
-        }
+        if (file.exists()) file.delete()
+        file.createNewFile()
+        content.store(file)
     }
 
-    class Action(
-        val text: String = "",
-        private val data: InputStream = StringBufferInputStream(text),
-        private val content: Ini? = runCatching { Ini(data) }.getOrNull(),
-        private val props: Profile.Section? = content?.get("Desktop Entry")
-    ) {
-        var type: DesktopFileType = DesktopFileType(props?.get(Prop_Type))
-        var version: String = ParsedString(props?.get(Prop_Version))
-        var name: String = ParsedString(props?.get(Prop_Name))
-        var comment: String = ParsedString(props?.get(Prop_Comment))
-        var path: String = ParsedString(props?.get(Prop_Path))
-        var exec: String = ParsedString(props?.get(Prop_Exec))
-        var icon: String = ParsedString(props?.get(Prop_Icon))
-        var categories: MutableList<String> = ParsedList(props?.get(Prop_Categories))
-        var notifyOnStart: Boolean = ParsedBoolean(props?.get(Prop_StartupNotify))
-        var runInTerminal: Boolean = ParsedBoolean(props?.get(Prop_Terminal))
-        var encoding: String = ParsedString(props?.get(Prop_Encoding))
-        var gtkTheme: String = ParsedString(props?.get(Prop_GtkTheme))
-        var metacityTheme: String = ParsedString(props?.get(Prop_MetacityTheme))
-        var iconTheme: String = ParsedString(props?.get(Prop_IconTheme))
-        var cursorTheme: String = ParsedString(props?.get(Prop_CursorTheme))
-        var buttonLayout: String = ParsedString(props?.get(Prop_ButtonLayout))
-//        var useOverlayScrollbars: Boolean = ParsedBoolean(props?.get(Prop_X_Ubuntu_UseOverlayScrollbars))
-    }
-
-    enum class DesktopFileType(
-        val text: String
-    ) {
+    enum class DesktopFileType(val text: String) {
         Unknown(""),
         DesktopEntry("Desktop Entry"),
         Application("Application"),
@@ -141,18 +52,18 @@ class DesktopFile(
     }
 
     companion object {
-        const val Prop_Type = "Type" // string "Application"
-        const val Prop_Version = "Version" // string
-        const val Prop_Path = "Path" // string
-        const val Prop_Name = "Name" // string
-        const val Prop_Comment = "Comment" // string
-        const val Prop_Exec = "Exec" // string
-        const val Prop_StartupNotify = "StartupNotify" // boolean
-        const val Prop_Terminal = "Terminal" // boolean
-        const val Prop_Icon = "Icon" // string
-        const val Prop_Categories = "Categories" // string delimited ;
+        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" // string delimited ;
+        const val Prop_MimeType = "MimeType"
         const val Prop_Actions = "Actions"
         const val Prop_DBusActivatable = "DBusActivatable"
         const val Prop_Encoding = "Encoding"
@@ -169,10 +80,107 @@ class DesktopFile(
         const val Prop_ButtonLayout = "ButtonLayout"
         const val Prop_X_Ubuntu_UseOverlayScrollbars = "X-Ubuntu-UseOverlayScrollbars"
 
-        fun create(path: String): DesktopFile = create(File(path).apply {
-            if (!exists()) createNewFile()
-        })
+        var Section.Type: DesktopFileType
+            get() = DesktopFileType(this[Prop_Type])
+            set(value) {
+                this[Prop_Type] = value.text
+            }
+
+        var Section.Version: String
+            get() = ParsedString(this[Prop_Version])
+            set(value) {
+                this[Prop_Version] = value
+            }
+
+        var Section.Name: String
+            get() = ParsedString(this[Prop_Name])
+            set(value) {
+                this[Prop_Name] = value
+            }
+
+        var Section.Comment: String
+            get() = ParsedString(this[Prop_Comment])
+            set(value) {
+                this[Prop_Comment] = value
+            }
+
+        var Section.Path: String
+            get() = ParsedString(this[Prop_Path])
+            set(value) {
+                this[Prop_Path] = value
+            }
+
+        var Section.Exec: String
+            get() = ParsedString(this[Prop_Exec])
+            set(value) {
+                this[Prop_Exec] = value
+            }
+
+        var Section.Icon: String
+            get() = ParsedString(this[Prop_Icon])
+            set(value) {
+                this[Prop_Icon] = value
+            }
+
+        var Section.Encoding: String
+            get() = ParsedString(this[Prop_Encoding])
+            set(value) {
+                this[Prop_Encoding] = value
+            }
+
+        var Section.NotifyOnStart: Boolean
+            get() = ParsedBoolean(this[Prop_StartupNotify])
+            set(value) {
+                this[Prop_StartupNotify] = value.toString()
+            }
+
+        var Section.RunInTerminal: Boolean
+            get() = ParsedBoolean(this[Prop_Terminal])
+            set(value) {
+                this[Prop_Terminal] = value.toString()
+            }
+
+        var Section.Categories: MutableList<String>
+            get() = ParsedList(this[Prop_Categories])
+            set(value) {
+                this[Prop_Categories] = value.joinToString { "$it;" }
+            }
+
+        var Section.GtkTheme: String
+            get() = ParsedString(this[Prop_GtkTheme])
+            set(value) {
+                this[Prop_GtkTheme] = value
+            }
+
+        var Section.MetacityTheme: String
+            get() = ParsedString(this[Prop_MetacityTheme])
+            set(value) {
+                this[Prop_MetacityTheme] = value
+            }
+
+        var Section.IconTheme: String
+            get() = ParsedString(this[Prop_IconTheme])
+            set(value) {
+                this[Prop_IconTheme] = value
+            }
+
+        var Section.CursorTheme: String
+            get() = ParsedString(this[Prop_CursorTheme])
+            set(value) {
+                this[Prop_CursorTheme] = value
+            }
+
+        var Section.ButtonLayout: String
+            get() = ParsedString(this[Prop_ButtonLayout])
+            set(value) {
+                this[Prop_ButtonLayout] = value
+            }
+
+        var Section.UseOverlayScrollbars: Boolean
+            get() = ParsedBoolean(this[Prop_X_Ubuntu_UseOverlayScrollbars])
+            set(value) {
+                this[Prop_X_Ubuntu_UseOverlayScrollbars] = value.toString()
+            }
 
-        fun create(file: File): DesktopFile = DesktopFile(file)
     }
 }

+ 0 - 2
src/main/kotlin/eu/mjdev/desktop/extensions/ColorUtils.kt

@@ -1,9 +1,7 @@
 package eu.mjdev.desktop.extensions
 
-import androidx.compose.material.MaterialTheme
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.luminance
-import androidx.compose.ui.graphics.toArgb
 import java.util.*
 
 @Suppress("unused", "UnusedReceiverParameter")

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

@@ -35,6 +35,14 @@ object Compose {
     val Color.Companion.DarkBlue
         get() = Color(0xff202060)
 
+    fun Color.toHex(): String {
+        val alpha : Int= (alpha * 255).toInt()
+        val red = (red * 255).toInt()
+        val green = (green * 255).toInt()
+        val blue = (blue * 255).toInt()
+        return String.format("#%02x%02x%02x%02x", alpha, red, green, blue)
+    }
+
     val PaddingValues.width: Dp
         get() = calculateLeftPadding(LayoutDirection.Ltr) + calculateRightPadding(LayoutDirection.Ltr)
 

+ 19 - 3
src/main/kotlin/eu/mjdev/desktop/helpers/internal/Palette.kt

@@ -16,13 +16,13 @@ import kotlinx.coroutines.launch
 
 @Suppress("CanBeParameter", "MemberVisibilityCanBePrivate")
 class Palette(
-    val api:DesktopProvider,
+    val api: DesktopProvider,
     val scope: CoroutineScope = api.scope,
-    val baseColor: Color = Color.SuperDarkGray,
+    val initialColor: Color = Color.SuperDarkGray,
     val borderFactor: Float = 0.1f,
     val textFactor: Float = 0.6f,
 ) {
-    val backgroundColorState: MutableState<Color> = mutableStateOf(baseColor)
+    val backgroundColorState: MutableState<Color> = mutableStateOf(initialColor)
     var backgroundColor
         get() = backgroundColorState.value
         set(value) {
@@ -45,6 +45,21 @@ class Palette(
     val iconsTintColor
         get() = if (textColor.isLightColor) textColor else backgroundColor.lighter(textFactor)
 
+    val baseColor
+        get() = backgroundColor
+
+    val selectedBgColor
+        get() = textColor
+
+    val selectedFgColor
+        get() = iconsTintColor
+
+    val tooltipBgColor
+        get() = backgroundColor
+
+    val tooltipFgColor
+        get() = textColor
+
     fun update(src: Any?) = scope.launch {
         loadPicture(src).getOrNull()?.let { image ->
             val width = image.width
@@ -65,6 +80,7 @@ class Palette(
             textColorState.value = text
         }
 //        api.gtkTheme.createFromPalette()
+//        api.gnome.setGTKTheme(THEME_MJDEV)
     }
 
     companion object {

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

@@ -90,6 +90,7 @@ class GnomeManager {
         const val COLOR_SCHEME_MJDEV = "mjdev"
 
         const val THEME_YARU = "Yaru"
+        const val THEME_ADWAITA = "Adwaita"
         const val THEME_ADWAITA_DARK = "Adwaita-dark"
         const val THEME_MJDEV = "mjdev"
     }

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

@@ -21,7 +21,7 @@ import eu.mjdev.desktop.helpers.system.DBus
 import eu.mjdev.desktop.helpers.system.OsRelease
 import eu.mjdev.desktop.helpers.system.Shell
 import eu.mjdev.desktop.provider.AIProvider.AiPluginGemini
-import eu.mjdev.desktop.theme.gtk.GtkTheme
+import eu.mjdev.desktop.theme.gtk.GtkThemeHelper
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import java.awt.*
@@ -61,7 +61,7 @@ class DesktopProvider(
             println("Mounted: ${this.targetDirectory}")
         }
     }
-    val gtkTheme: GtkTheme by lazy { GtkTheme(this) }
+    val gtkTheme: GtkThemeHelper by lazy { GtkThemeHelper(this) }
     val palette: Palette by lazy { Palette(this) }
     val homeDir: File
         get() = currentUser.homeDir
@@ -267,9 +267,9 @@ class DesktopProvider(
         }
 
         fun Desktop.startApp(
-            desktopFile: DesktopFile
+            desktopFile: DesktopFile?
         ) {
-            this.open(desktopFile.file)
+            open(desktopFile?.file)
         }
     }
 }

+ 0 - 124
src/main/kotlin/eu/mjdev/desktop/theme/gtk/GtkTheme.kt

@@ -1,124 +0,0 @@
-package eu.mjdev.desktop.theme.gtk
-
-import eu.mjdev.desktop.data.DesktopFile
-import eu.mjdev.desktop.provider.DesktopProvider
-import java.io.File
-
-@Suppress("CanBeParameter", "unused")
-class GtkTheme(
-    val api: DesktopProvider,
-    private val themeName: String = "mjdev",
-    private val themesDir: File = File(api.homeDir, ".themes"),
-    private val themeDir: File = File(themesDir, themeName),
-    private val themeDesktopFile: File = File(themeDir, "index.theme"),
-    private val themeCssFile: File = File(themeDir, "gtk.css"),
-    private val desktopFile: DesktopFile = DesktopFile(themeDesktopFile)
-) {
-    fun createFromPalette() {
-        themeDir.mkdirs()
-        with(desktopFile) {
-            type = DesktopFile.DesktopFileType.Theme
-            name = "mjdev"
-            comment = "dynamic mjdev theme"
-            encoding = "UTF-8" // todo
-            action(DesktopFile.DesktopFileType.Theme) {
-                gtkTheme = "mjdev"
-                metacityTheme = "mjdev"
-                iconTheme = "mjdev"
-                cursorTheme = "DMZ-White" // todo
-                buttonLayout = "close,minimize,maximize:"
-//                useOverlayScrollbars = true
-            }
-            write()
-        }
-    }
-
-    /*
-    [Desktop Entry]
-    Type=X-GNOME-Metatheme
-    Name=Ambiance
-    Comment=Ubuntu Ambiance theme
-    Encoding=UTF-8
-
-    [X-GNOME-Metatheme]
-    GtkTheme=Ambiance
-    MetacityTheme=Ambiance
-    IconTheme=ubuntu-mono-dark
-    CursorTheme=DMZ-White
-    ButtonLayout=close,minimize,maximize:
-    X-Ubuntu-UseOverlayScrollbars=true
-     */
-
-    val cssFile = themeCssFile
-
-    /*
-    /*default color scheme */
-    @define-color bg_color #f2f1f0;
-    @define-color fg_color #4c4c4c;
-    @define-color base_color #ffffff;
-    @define-color text_color #3C3C3C;
-    @define-color selected_bg_color #f07746;
-    @define-color selected_fg_color #ffffff;
-    @define-color tooltip_bg_color #000000;
-    @define-color tooltip_fg_color #ffffff;
-
-    /* misc colors used by gtk+
-     *
-     * Gtk doesn't currently expand color variables for style properties. Thus,
-     * gtk-widgets.css uses literal color names, but includes a comment containing
-     * the name of the variable. Please remember to change values there as well
-     * when changing one of the variables below.
-     */
-    @define-color info_fg_color rgb (181, 171, 156);
-    @define-color info_bg_color rgb (252, 252, 189);
-    @define-color warning_fg_color rgb (173, 120, 41);
-    @define-color warning_bg_color rgb (250, 173, 61);
-    @define-color question_fg_color rgb (97, 122, 214);
-    @define-color question_bg_color rgb (138, 173, 212);
-    @define-color error_fg_color rgb (235, 235, 235);
-    @define-color error_bg_color rgb (223, 56, 44);
-    @define-color link_color @selected_bg_color;
-    @define-color success_color #4e9a06;
-    @define-color error_color #df382c;
-
-    /* theme common colors */
-    @define-color button_bg_color shade (@bg_color, 1.02); /*shade (#cdcdcd, 1.08);*/
-    @define-color notebook_button_bg_color shade (@bg_color, 1.02);
-    @define-color button_insensitive_bg_color mix (@button_bg_color, @bg_color, 0.6);
-    @define-color dark_bg_color #3c3b37;
-    @define-color dark_fg_color #dfdbd2;
-
-    @define-color backdrop_fg_color mix (@bg_color, @fg_color, 0.8);
-    @define-color backdrop_text_color mix (@base_color, @text_color, 0.8);
-    @define-color backdrop_dark_fg_color mix (@dark_bg_color, @dark_fg_color, 0.75);
-    /*@define-color backdrop_dark_bg_color mix (@dark_bg_color, @dark_fg_color, 0.75);*/
-    @define-color backdrop_selected_bg_color shade (@bg_color, 0.92);
-    @define-color backdrop_selected_fg_color @fg_color;
-
-    @define-color focus_color alpha (@selected_bg_color, 0.5);
-    @define-color focus_bg_color alpha (@selected_bg_color, 0.1);
-
-    @define-color shadow_color alpha(black, 0.5);
-
-    @define-color osd_fg_color #eeeeec;
-    @define-color osd_bg_color alpha(#202526, 0.7);
-    @define-color osd_border_color alpha(black, 0.7);
-
-    @import url("gtk-widgets-borders.css");
-    @import url("gtk-widgets-assets.css");
-    @import url("gtk-widgets.css");
-    @import url("apps/geary.css");
-    @import url("apps/unity.css");
-    @import url("apps/baobab.css");
-    @import url("apps/gedit.css");
-    @import url("apps/nautilus.css");
-    @import url("apps/gnome-panel.css");
-    @import url("apps/gnome-terminal.css");
-    @import url("apps/gnome-system-log.css");
-    @import url("apps/unity-greeter.css");
-    @import url("apps/glade.css");
-    @import url("apps/california.css");
-    @import url("apps/software-center.css");
-    @import url("public-colors.css");
-     */
-}

+ 163 - 0
src/main/kotlin/eu/mjdev/desktop/theme/gtk/GtkThemeHelper.kt

@@ -0,0 +1,163 @@
+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.extensions.Compose.SuperDarkGray
+import eu.mjdev.desktop.extensions.Compose.toHex
+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
+
+@Suppress("unused")
+class GtkThemeHelper(
+    private val api: DesktopProvider,
+    private val palette: Palette = api.palette,
+    private val themeName: String = THEME_MJDEV,
+    private val themesDir: File = File(api.homeDir, ".themes"),
+    private val themeDir: File = File(themesDir, themeName),
+    private val themeDesktopFile: File = File(themeDir, "index.theme"),
+    private val gtk2ThemeDir: File = File(themeDir, "gtk-2.0"),
+    private val gtk3ThemeDir: File = File(themeDir, "gtk-3.0"),
+    private val gtk4ThemeDir: File = File(themeDir, "gtk-4.0"),
+    private val gtk3CssFile: File = File(gtk3ThemeDir, "gtk.css"),
+    private val gtk4CssFile: File = File(gtk4ThemeDir, "gtk.css"),
+) {
+    fun createFromPalette() {
+        createDesktopFile()
+        createCssFile(gtk3CssFile)
+        createCssFile(gtk4CssFile)
+    }
+
+    private fun createDesktopFile() {
+        if (!themeDir.exists()) themeDir.mkdirs()
+        if (!themeDesktopFile.exists()) themeDesktopFile.delete()
+        themeDesktopFile.createNewFile()
+        with(DesktopFile(themeDesktopFile)) {
+            section(DesktopFile.DesktopFileType.DesktopEntry) {
+                Type = DesktopFile.DesktopFileType.Theme
+                Name = THEME_MJDEV
+                Comment = "dynamic system theme"
+                Encoding = "UTF-8" // todo
+            }
+            section(DesktopFile.DesktopFileType.Theme) {
+                GtkTheme = THEME_MJDEV
+                MetacityTheme = THEME_ADWAITA_DARK
+                IconTheme = THEME_ADWAITA_DARK
+                CursorTheme = "DMZ-White" // todo
+                ButtonLayout = "close,minimize,maximize:"
+                UseOverlayScrollbars = true
+            }
+            write()
+        }
+    }
+
+    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)
+        }
+    }
+
+    class Theme(
+        val name: String = THEME_ADWAITA,
+    ) {
+        var bgColor: Color = Color.SuperDarkGray
+        var fgColor: Color = Color.SuperDarkGray
+        var baseColor: Color = Color.SuperDarkGray
+        var textColor: Color = Color.SuperDarkGray
+        var selectedBgColor: Color = Color.SuperDarkGray
+        var selectedFgColor: Color = Color.SuperDarkGray
+        var tooltipBgColor: Color = Color.SuperDarkGray
+        var tooltipFgColor: Color = Color.SuperDarkGray
+
+        override fun toString() = """
+            @define-color bg_color ${bgColor.toHex()};
+            @define-color fg_color ${fgColor.toHex()};
+            @define-color base_color ${baseColor.toHex()};
+            @define-color text_color ${textColor.toHex()};
+            @define-color selected_bg_color ${selectedBgColor.toHex()};
+            @define-color selected_fg_color ${selectedFgColor.toHex()};
+            @define-color tooltip_bg_color ${tooltipBgColor.toHex()};
+            @define-color tooltip_fg_color ${tooltipFgColor.toHex()};
+            @define-color info_fg_color ${fgColor.toHex()};
+            @define-color info_bg_color ${bgColor.toHex()};
+            @define-color warning_fg_color ${fgColor.toHex()};
+            @define-color warning_bg_color ${bgColor.toHex()};
+            @define-color question_fg_color ${fgColor.toHex()};
+            @define-color question_bg_color ${bgColor.toHex()};
+            @define-color error_fg_color rgb (235, 235, 235);
+            @define-color error_bg_color rgb (223, 56, 44);
+            
+            @define-color link_color @selected_bg_color;
+            
+            @define-color success_color #4e9a06;
+            @define-color error_color #df382c;
+            
+            @define-color button_bg_color shade (@bg_color, 1.02); /*shade (#cdcdcd, 1.08);*/
+            @define-color notebook_button_bg_color shade (@bg_color, 1.02);
+            @define-color button_insensitive_bg_color mix (@button_bg_color, @bg_color, 0.6);
+            @define-color dark_bg_color${bgColor.toHex()};
+            @define-color dark_fg_color #dfdbd2;
+            @define-color backdrop_fg_color mix (@bg_color, @fg_color, 0.8);
+            @define-color backdrop_text_color mix (@base_color, @text_color, 0.8);
+            @define-color backdrop_dark_fg_color mix (@dark_bg_color, @dark_fg_color, 0.75);
+            /*@define-color backdrop_dark_bg_color mix (@dark_bg_color, @dark_fg_color, 0.75);*/
+            @define-color backdrop_selected_bg_color shade (@bg_color, 0.92);
+            @define-color backdrop_selected_fg_color @fg_color;
+            @define-color focus_color alpha (@selected_bg_color, 0.5);
+            @define-color focus_bg_color alpha (@selected_bg_color, 0.1);
+            @define-color shadow_color alpha(black, 0.5);
+            
+            @define-color osd_fg_color #eeeeec;
+            @define-color osd_bg_color alpha(#202526, 0.7);
+            @define-color osd_border_color alpha(black, 0.7);
+            
+            decoration {
+            	background-color: ${bgColor.toHex()};
+            	border-radius: 8px;
+            	border-bottom-left-radius: ${textColor.toHex()};
+            	border-bottom-right-radius: ${textColor.toHex()};
+            	box-shadow: 0px 0px 0px 1px ${textColor.toHex()};
+            	border: 1px solid ${textColor.toHex()};
+            	border-top: none;
+            }
+            
+            headerbar {
+                background-color: ${bgColor.toHex()};
+            	border-bottom: 1px solid ${textColor.toHex()};
+            	box-shadow: 0px 1px 0px 0px ${textColor.toHex()};
+            	border-top: 1px solid ${textColor.toHex()};
+            }
+            
+        """.trimIndent()
+    }
+
+    companion object {
+        fun theme(
+            name: String = THEME_YARU,
+            block: Theme.() -> Unit
+        ) = Theme(name).apply(block)
+    }
+}