From 8a437abb0f17d00aeb0d96eb6ec4d3e671dc7f61 Mon Sep 17 00:00:00 2001 From: Rukira Date: Wed, 4 Mar 2026 16:18:25 +0000 Subject: [PATCH] Icon and app name --- composeApp/build.gradle.kts | 10 ++- .../com/rukira/wowbackup/ui/TrayIcon.kt | 86 ++++++++++++------- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 2b3eabd..b4dc905 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -69,8 +69,16 @@ compose.desktop { nativeDistributions { targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) - packageName = "com.rukira.wowbackup" + packageName = "WoW Backup" packageVersion = appVersion + + macOS { + bundleID = "com.rukira.wowbackup" + } + + windows { + upgradeUuid = "e5a8b2c4-1f3d-4a6e-9b7c-2d8f0e1a3b5c" + } } } } diff --git a/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/ui/TrayIcon.kt b/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/ui/TrayIcon.kt index 7ee515d..3cfc11d 100644 --- a/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/ui/TrayIcon.kt +++ b/composeApp/src/jvmMain/kotlin/com/rukira/wowbackup/ui/TrayIcon.kt @@ -1,45 +1,69 @@ package com.rukira.wowbackup.ui import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.drawscope.DrawScope -import androidx.compose.ui.graphics.Path -import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.toComposeImageBitmap +import java.awt.BasicStroke +import java.awt.Graphics2D +import java.awt.RenderingHints +import java.awt.geom.GeneralPath +import java.awt.image.BufferedImage /** - * A simple shield-shaped tray icon drawn in code. + * A simple shield-shaped tray icon with a "W" letter inside. + * Rendered via Java2D BufferedImage for reliable AWT system tray display. * Works with macOS template images (-Dapple.awt.enableTemplateImages=true) * which will automatically adapt to dark/light menu bar. */ -fun trayIconPainter(): Painter = object : Painter() { - override val intrinsicSize = Size(32f, 32f) +fun trayIconPainter(): Painter { + val size = 128 + val img = BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB) + val g: Graphics2D = img.createGraphics() + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY) - override fun DrawScope.onDraw() { - val w = size.width - val h = size.height + val w = size.toFloat() + val h = size.toFloat() - // Shield shape - val shield = Path().apply { - moveTo(w * 0.5f, h * 0.05f) - lineTo(w * 0.9f, h * 0.2f) - lineTo(w * 0.9f, h * 0.5f) - cubicTo(w * 0.9f, h * 0.75f, w * 0.5f, h * 0.95f, w * 0.5f, h * 0.95f) - cubicTo(w * 0.5f, h * 0.95f, w * 0.1f, h * 0.75f, w * 0.1f, h * 0.5f) - lineTo(w * 0.1f, h * 0.2f) - close() - } - drawPath(shield, Color.Black) - - // Checkmark inside the shield - val check = Path().apply { - moveTo(w * 0.25f, h * 0.48f) - lineTo(w * 0.42f, h * 0.65f) - lineTo(w * 0.72f, h * 0.32f) - } - drawPath( - check, - Color.White, - style = androidx.compose.ui.graphics.drawscope.Stroke(width = w * 0.08f), + // Shield shape (filled black) + val shield = GeneralPath().apply { + moveTo((w * 0.5f).toDouble(), (h * 0.05f).toDouble()) + lineTo((w * 0.9f).toDouble(), (h * 0.2f).toDouble()) + lineTo((w * 0.9f).toDouble(), (h * 0.5f).toDouble()) + curveTo( + (w * 0.9f).toDouble(), (h * 0.75f).toDouble(), + (w * 0.5f).toDouble(), (h * 0.95f).toDouble(), + (w * 0.5f).toDouble(), (h * 0.95f).toDouble(), ) + curveTo( + (w * 0.5f).toDouble(), (h * 0.95f).toDouble(), + (w * 0.1f).toDouble(), (h * 0.75f).toDouble(), + (w * 0.1f).toDouble(), (h * 0.5f).toDouble(), + ) + lineTo((w * 0.1f).toDouble(), (h * 0.2f).toDouble()) + closePath() } + // Create the "W" stroke shape to carve out of the shield + val letterW = GeneralPath().apply { + moveTo((w * 0.22f).toDouble(), (h * 0.30f).toDouble()) + lineTo((w * 0.35f).toDouble(), (h * 0.70f).toDouble()) + lineTo((w * 0.50f).toDouble(), (h * 0.45f).toDouble()) + lineTo((w * 0.65f).toDouble(), (h * 0.70f).toDouble()) + lineTo((w * 0.78f).toDouble(), (h * 0.30f).toDouble()) + } + val wStroke = BasicStroke(w * 0.06f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND) + val wShape = wStroke.createStrokedShape(letterW) + + // Subtract the W from the shield using Area + val shieldArea = java.awt.geom.Area(shield) + shieldArea.subtract(java.awt.geom.Area(wShape)) + + g.color = java.awt.Color.BLACK + g.fill(shieldArea) + + g.dispose() + + return BitmapPainter(img.toComposeImageBitmap()) }