feat(desktop): unify macOS window chrome

This commit is contained in:
2026-07-13 08:34:44 +02:00
parent b90148f6b8
commit 4059bce3e4
6 changed files with 246 additions and 67 deletions

View File

@@ -1,6 +1,26 @@
package com.vnidrop.app
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.window.WindowDraggableArea
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.awtEventOrNull
import androidx.compose.ui.input.pointer.PointerEventType
import androidx.compose.ui.input.pointer.onPointerEvent
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowScope
import androidx.compose.ui.window.application
import com.vnidrop.app.platform.DesktopAppearanceBridge
import com.vnidrop.app.feature.send.DesktopShareBridge
@@ -8,11 +28,13 @@ import com.vnidrop.app.feature.receive.ExternalInvitationController
import com.vnidrop.app.feature.receive.MaxVniDropInvitationBytes
import com.vnidrop.app.feature.receive.VniDropInvitationExtension
import com.vnidrop.app.feature.receive.decodeInvitationBytes
import com.vnidrop.app.ui.theme.LocalVniDropColors
import java.awt.Desktop
import java.io.File
fun main(args: Array<String>) {
val externalInvitations = ExternalInvitationController()
val macOs = DesktopAppearanceBridge.isMacOs()
configureMacOsNativeAppearance()
configureInvitationOpenHandler(externalInvitations)
args.asSequence()
@@ -20,7 +42,7 @@ fun main(args: Array<String>) {
.filter { it.extension.equals(VniDropInvitationExtension, ignoreCase = true) }
.forEach { externalInvitations.openFile(it) }
DesktopAppearanceBridge.applyNativeAppearance = MacOsAppKitAppearance::apply
if (System.getProperty("os.name").startsWith("Mac", ignoreCase = true)) {
if (macOs) {
DesktopShareBridge.shareFile = MacOsShareSheet::share
}
application {
@@ -28,7 +50,16 @@ fun main(args: Array<String>) {
onCloseRequest = ::exitApplication,
title = "vnidrop",
) {
App(rememberJvmAppDependencies(externalInvitations))
App(
dependencies = rememberJvmAppDependencies(externalInvitations),
windowChromeTopInset = if (macOs) MacOsTitleBarHeight else 0.dp,
windowContentTopStartRadius = if (macOs) MacOsContentCornerRadius else 0.dp,
windowChrome = if (macOs) {
{ MacOsTitleBar() }
} else {
null
},
)
}
}
}
@@ -52,8 +83,47 @@ private fun ExternalInvitationController.openFile(file: File) {
}
private fun configureMacOsNativeAppearance() {
if (!System.getProperty("os.name").startsWith("Mac", ignoreCase = true)) return
if (!DesktopAppearanceBridge.isMacOs()) return
// AWT reads this before creating the first native window. Runtime theme
// changes are handled in the JVM platform appearance hook.
System.setProperty("apple.awt.application.appearance", "system")
}
private val MacOsTitleBarHeight = 28.dp
private val MacOsTrafficLightsWidth = 76.dp
private val MacOsContentCornerRadius = 20.dp
@Composable
@OptIn(ExperimentalComposeUiApi::class)
private fun WindowScope.MacOsTitleBar() {
val colors = LocalVniDropColors.current
Box(
modifier = Modifier
.fillMaxWidth()
.height(MacOsTitleBarHeight)
.background(colors.backgroundSurface200),
) {
WindowDraggableArea(
modifier = Modifier
.fillMaxSize()
.padding(start = MacOsTrafficLightsWidth)
.onPointerEvent(PointerEventType.Press) { event ->
if (event.awtEventOrNull?.clickCount == 2) {
DesktopAppearanceBridge.toggleMaximized(window)
}
},
) {
Box(modifier = Modifier.fillMaxSize().padding(end = MacOsTrafficLightsWidth)) {
BasicText(
text = "vnidrop",
modifier = Modifier.align(Alignment.Center),
style = TextStyle(
color = colors.foregroundDefault,
fontSize = 13.sp,
fontWeight = FontWeight.SemiBold,
),
)
}
}
}
}