Prechádzať zdrojové kódy

memory chart

memory chart
Milan Jurkulak 2 rokov pred
rodič
commit
b63e885214

+ 11 - 0
build.gradle.kts

@@ -4,6 +4,7 @@ plugins {
     alias(libs.plugins.kotlin.jvm)
     alias(libs.plugins.compose.desktop)
     alias(libs.plugins.kotlin.serialization)
+//    id("dev.datlag.sekret") version "2.0.0-alpha-07"
 }
 
 group = libs.versions.packageName.get()
@@ -62,6 +63,16 @@ allprojects {
         implementation(libs.slf4j.nop)
         // fuzzy search
         implementation(libs.fuzzywuzzy)
+        // graphs
+        implementation(libs.charts)
+        // flow extensions
+//        implementation("io.github.hoc081098:FlowExt:1.0.0-RC")
+        // drag & drop
+//        implementation("com.mohamedrejeb.dnd:compose-dnd:0.2.0")
+        // metadata
+//        implementation("com.ashampoo:kim:0.18.4")
+        // html
+//        implementation("com.github.Hamamas:Kotlin-Wasm-Html-Interop:1.0.1")
         // sockets
 //        implementation("com.kohlschutter.junixsocket:junixsocket-core:2.3.2")
         // x11 client

+ 2 - 0
gradle/libs.versions.toml

@@ -23,6 +23,7 @@ ffmpeg-platform = "6.1.1-1.5.10"
 google-gson = "2.8.9"
 mozilla-rhino = "1.7.13"
 ini4j = "0.5.4"
+charts = "Beta-0.0.5"
 #korau = "4.0.10"
 #lanscapist = "2.3.8"
 #generativeai = "0.5.0-1.0.0-wasm"
@@ -46,6 +47,7 @@ coil-svg = { module = "io.coil-kt.coil3:coil-svg", version.ref = "coil3" }
 slf4j-nop = { module = "org.slf4j:slf4j-nop", version.ref = "slf4j-nop" }
 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" }
 
 #coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coil3" }
 #korau = { module = "com.soywiz.korlibs.korau:korau", version.ref = "korau" }

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

@@ -1,10 +1,15 @@
 package eu.mjdev.desktop
 
+import androidx.compose.foundation.layout.size
 import androidx.compose.material.MaterialTheme
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.application
 import eu.mjdev.desktop.components.appsmenu.AppsMenu
+import eu.mjdev.desktop.components.charts.MemoryChart
 import eu.mjdev.desktop.components.controlcenter.ControlCenter
 import eu.mjdev.desktop.components.desktoppanel.DesktopPanel
 import eu.mjdev.desktop.components.sliding.VisibilityState.Companion.rememberVisibilityState
@@ -52,6 +57,9 @@ fun main() = application(
                 controlCenterState = controlCenterState,
                 menuState = menuState
             ) {
+                MemoryChart(
+                    modifier = Modifier.size(350.dp, 300.dp).align(Alignment.BottomEnd)
+                )
                 DesktopPanel(
                     panelState = panelState,
                     onMenuIconClicked = {

+ 54 - 0
src/main/kotlin/eu/mjdev/desktop/components/DraggableView.kt

@@ -0,0 +1,54 @@
+package eu.mjdev.desktop.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.gestures.detectDragGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.pointerInput
+
+@Composable
+fun DraggableView(
+    modifier: Modifier = Modifier,
+    dragEnabled: Boolean = true,
+    backgroundColor: Color = Color.Transparent,
+    dragBackgroundColor: Color = Color.White.copy(alpha = 0.3f),
+    content: @Composable BoxScope.() -> Unit = {}
+) {
+    var position by remember { mutableStateOf(Offset.Zero) }
+    var isDragging by remember { mutableStateOf(false) }
+    Box(
+        modifier = modifier
+            .graphicsLayer(
+                translationX = position.x,
+                translationY = position.y
+            )
+            .pointerInput(Unit) {
+                detectDragGestures(
+                    onDrag = { change, dragAmount ->
+                        if (dragEnabled) {
+                            change.consume()
+                            position += dragAmount
+                        }
+                    },
+                    onDragStart = {
+                        isDragging = true
+                    },
+                    onDragEnd = {
+                        isDragging = false
+                    }
+                )
+            },
+        content = {
+            Box(
+                modifier = Modifier.background(if (isDragging) dragBackgroundColor else backgroundColor)
+            ) {
+                content()
+            }
+        }
+    )
+}

+ 61 - 0
src/main/kotlin/eu/mjdev/desktop/components/charts/DualDonutChart.kt

@@ -0,0 +1,61 @@
+package eu.mjdev.desktop.components.charts
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import com.aay.compose.baseComponents.model.LegendPosition
+import com.aay.compose.donutChart.DonutChart
+import com.aay.compose.donutChart.model.PieChartData
+import eu.mjdev.desktop.extensions.Compose.DarkDarkGray
+
+@Composable
+fun DualDonutChart(
+    modifier: Modifier = Modifier,
+    title: String = "",
+    textColor: Color = Color.White,
+    outerCircularColor: Color = Color.White,
+    innerCircularColor: Color = Color.White,
+    ratioLineColor: Color = Color.White,
+    firstValue: Double = 0.0,
+    secondValue: Double = 0.0,
+    firstColor: Color = Color.DarkDarkGray,
+    secondColor: Color = Color.White,
+    firstTitle: String = "",
+    secondTitle: String = "",
+    animation: AnimationSpec<Float> = TweenSpec(durationMillis = 3000),
+    legendPosition: LegendPosition = LegendPosition.BOTTOM,
+) {
+    val data = remember(
+        firstValue,
+        firstColor,
+        firstTitle,
+        secondValue,
+        secondColor,
+        secondTitle
+    ) {
+        derivedStateOf {
+            listOf(
+                PieChartData(firstValue, firstColor, firstTitle),
+                PieChartData(secondValue, secondColor, secondTitle)
+            )
+        }
+    }
+    DonutChart(
+        modifier = modifier,
+        pieChartData = data.value,
+        centerTitle = title,
+        centerTitleStyle = TextStyle(color = textColor),
+        descriptionStyle = TextStyle(color = textColor),
+        textRatioStyle = TextStyle(color = textColor),
+        outerCircularColor = outerCircularColor,
+        innerCircularColor = innerCircularColor,
+        ratioLineColor = ratioLineColor,
+        animation = animation,
+        legendPosition = legendPosition
+    )
+}

+ 66 - 0
src/main/kotlin/eu/mjdev/desktop/components/charts/MemoryChart.kt

@@ -0,0 +1,66 @@
+package eu.mjdev.desktop.components.charts
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.TweenSpec
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.aay.compose.baseComponents.model.LegendPosition
+import eu.mjdev.desktop.components.DraggableView
+import eu.mjdev.desktop.extensions.Compose.DarkDarkGray
+import eu.mjdev.desktop.helpers.MemInfo
+import eu.mjdev.desktop.helpers.MemInfo.Companion.toReadable
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.isActive
+
+@Composable
+fun MemoryChart(
+    modifier: Modifier = Modifier,
+    textColor: Color = Color.White,
+    outerCircularColor: Color = Color.White,
+    innerCircularColor: Color = Color.White,
+    totalColor: Color = Color.DarkDarkGray,
+    currentColor: Color = Color.White,
+    ratioLineColor: Color = Color.Transparent,
+    mainTitle: String = "Memory",
+    usedTitle: String = "Used",
+    freeTitle: String = "Free",
+    animation: AnimationSpec<Float> = TweenSpec(durationMillis = 3000),
+    legendPosition: LegendPosition = LegendPosition.BOTTOM,
+    updateTimeOut: Long = 10000,
+    dragEnabled: Boolean = true
+) = DraggableView(
+    modifier = modifier,
+    dragEnabled = dragEnabled
+) {
+    val memData = remember { mutableStateOf(MemInfo()) }
+    DualDonutChart(
+        modifier = modifier,
+        title = mainTitle,
+        textColor = textColor,
+        outerCircularColor = outerCircularColor,
+        innerCircularColor = innerCircularColor,
+        ratioLineColor = ratioLineColor,
+        secondColor = totalColor,
+        firstColor = currentColor,
+        secondValue = memData.value.free,
+        firstValue = memData.value.used,
+        secondTitle = freeTitle.plus(": ").plus(memData.value.free.toReadable()),
+        firstTitle = usedTitle.plus(": ").plus(memData.value.used.toReadable()),
+        animation = animation,
+        legendPosition = legendPosition
+    )
+    LaunchedEffect(Unit) {
+        while (isActive) {
+            MemInfo().let { n ->
+                if (memData.value.percent != n.percent) {
+                    memData.value = n
+                }
+            }
+            delay(updateTimeOut)
+        }
+    }
+}

+ 36 - 0
src/main/kotlin/eu/mjdev/desktop/helpers/MemInfo.kt

@@ -0,0 +1,36 @@
+package eu.mjdev.desktop.helpers
+
+import java.io.File
+import kotlin.math.roundToInt
+
+@Suppress("MemberVisibilityCanBePrivate")
+class MemInfo(
+    private val info: List<String> = File("/proc/meminfo").readLines(),
+    val free: Double = info.parseLine("MemAvailable").times(KB),
+    val total: Double = info.parseLine("MemTotal").times(KB),
+    val used: Double = total - free,
+    val percent: Int = (((free.div(MB) / total.div(MB)) * 100).roundToInt())
+) {
+    companion object {
+        const val KB = 1024
+        const val MB = KB * KB
+
+        fun List<String>.parseLine(
+            filter: String
+        ): Double = firstOrNull { l -> l.startsWith(filter) }?.filter { it.isDigit() }?.toDouble() ?: 0.0
+
+        fun Double.toReadable(): String {
+            val bytes = toLong()
+            return when {
+                bytes == Long.MIN_VALUE || bytes < 0 -> "-"
+                bytes < 1024L -> "$bytes B"
+                bytes <= 0xfffccccccccccccL shr 40 -> "%.1f KB".format(bytes.toDouble() / (0x1 shl 10))
+                bytes <= 0xfffccccccccccccL shr 30 -> "%.1f MB".format(bytes.toDouble() / (0x1 shl 20))
+                bytes <= 0xfffccccccccccccL shr 20 -> "%.1f GB".format(bytes.toDouble() / (0x1 shl 30))
+                bytes <= 0xfffccccccccccccL shr 10 -> "%.1f TB".format(bytes.toDouble() / (0x1 shl 40))
+                bytes <= 0xfffccccccccccccL -> "%.1f PB".format((bytes shr 10).toDouble() / (0x1 shl 40))
+                else -> "%.1f EB".format((bytes shr 20).toDouble() / (0x1 shl 40))
+            }
+        }
+    }
+}