mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 18:39:55 +02:00
refactor(platform): remove Apple targets from KMP
This commit is contained in:
@@ -128,44 +128,11 @@ private fun File.systemIconPng(): ByteArray? = runCatching {
|
||||
}
|
||||
}.getOrNull()
|
||||
|
||||
private fun pickDirectory(title: String): File? =
|
||||
if (isMacOs()) {
|
||||
val dialog = withMacDirectoryDialog {
|
||||
nativeFileDialog(title).apply { isVisible = true }
|
||||
}
|
||||
try {
|
||||
val directory = dialog.directory ?: return null
|
||||
dialog.file
|
||||
?.let { File(directory, it) }
|
||||
?: File(directory)
|
||||
} finally {
|
||||
dialog.dispose()
|
||||
}
|
||||
} else {
|
||||
val chooser = JFileChooser().apply {
|
||||
dialogTitle = title
|
||||
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
|
||||
isAcceptAllFileFilterUsed = false
|
||||
}
|
||||
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) chooser.selectedFile else null
|
||||
}
|
||||
|
||||
private fun <T> withMacDirectoryDialog(block: () -> T): T {
|
||||
if (!isMacOs()) return block()
|
||||
|
||||
val key = "apple.awt.fileDialogForDirectories"
|
||||
val previous = System.getProperty(key)
|
||||
System.setProperty(key, "true")
|
||||
return try {
|
||||
block()
|
||||
} finally {
|
||||
if (previous == null) {
|
||||
System.clearProperty(key)
|
||||
} else {
|
||||
System.setProperty(key, previous)
|
||||
}
|
||||
private fun pickDirectory(title: String): File? {
|
||||
val chooser = JFileChooser().apply {
|
||||
dialogTitle = title
|
||||
fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
|
||||
isAcceptAllFileFilterUsed = false
|
||||
}
|
||||
return if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) chooser.selectedFile else null
|
||||
}
|
||||
|
||||
private fun isMacOs(): Boolean =
|
||||
System.getProperty("os.name").startsWith("Mac", ignoreCase = true)
|
||||
|
||||
@@ -11,7 +11,7 @@ import java.io.File
|
||||
@Composable
|
||||
actual fun rememberTransferShareActions(): TransferShareActions = remember {
|
||||
object : TransferShareActions {
|
||||
override val canUseNativeShare = DesktopShareBridge.shareFile != null
|
||||
override val canUseNativeShare = false
|
||||
override val nfcAvailability = NfcShareAvailability.Hidden
|
||||
|
||||
override fun exportInvitation(ticket: String, transferName: String, onResult: (Result<Unit>) -> Unit) {
|
||||
@@ -31,18 +31,7 @@ actual fun rememberTransferShareActions(): TransferShareActions = remember {
|
||||
}
|
||||
|
||||
override fun shareInvitation(ticket: String, transferName: String, onResult: (Result<Unit>) -> Unit) {
|
||||
EventQueue.invokeLater {
|
||||
val share = DesktopShareBridge.shareFile
|
||||
if (share == null) {
|
||||
onResult(Result.failure(UnsupportedOperationException("System sharing is unavailable on this desktop")))
|
||||
return@invokeLater
|
||||
}
|
||||
onResult(runCatching {
|
||||
val directory = File(System.getProperty("java.io.tmpdir"), "vnidrop-share").apply { mkdirs() }
|
||||
val file = File(directory, invitationFileName(transferName)).apply { writeText(ticket) }
|
||||
share(file).getOrThrow()
|
||||
})
|
||||
}
|
||||
onResult(Result.failure(UnsupportedOperationException("System sharing is unavailable on this desktop")))
|
||||
}
|
||||
|
||||
override fun writeInvitationToNfc(ticket: String, onResult: (Result<Unit>) -> Unit) {
|
||||
@@ -55,8 +44,3 @@ actual fun rememberTransferShareActions(): TransferShareActions = remember {
|
||||
private fun activeFrame(): Frame? =
|
||||
(KeyboardFocusManager.getCurrentKeyboardFocusManager().activeWindow as? Frame)
|
||||
?: Frame.getFrames().firstOrNull { it.isActive || it.isFocused }
|
||||
|
||||
object DesktopShareBridge {
|
||||
@Volatile
|
||||
var shareFile: ((File) -> Result<Unit>)? = null
|
||||
}
|
||||
|
||||
@@ -1,83 +1,14 @@
|
||||
package com.vnidrop.app.platform
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import java.awt.Color
|
||||
import java.awt.EventQueue
|
||||
import java.awt.Frame
|
||||
import java.awt.Window
|
||||
import javax.swing.JFrame
|
||||
import javax.swing.JRootPane
|
||||
|
||||
@Composable
|
||||
actual fun PlatformSystemAppearance(isDarkTheme: Boolean) {
|
||||
SideEffect {
|
||||
DesktopSystemAppearance.apply(isDarkTheme)
|
||||
}
|
||||
}
|
||||
|
||||
internal object DesktopSystemAppearance {
|
||||
private const val MAC_APPEARANCE_PROPERTY = "apple.awt.application.appearance"
|
||||
private const val FULL_WINDOW_CONTENT_PROPERTY = "apple.awt.fullWindowContent"
|
||||
private const val TRANSPARENT_TITLE_BAR_PROPERTY = "apple.awt.transparentTitleBar"
|
||||
private const val WINDOW_TITLE_VISIBLE_PROPERTY = "apple.awt.windowTitleVisible"
|
||||
|
||||
fun apply(isDarkTheme: Boolean) {
|
||||
if (!DesktopAppearanceBridge.isMacOs()) return
|
||||
System.setProperty(MAC_APPEARANCE_PROPERTY, macOsAppearanceName(isDarkTheme))
|
||||
EventQueue.invokeLater {
|
||||
DesktopAppearanceBridge.applyNativeAppearance?.invoke(isDarkTheme)
|
||||
Window.getWindows().forEach { window ->
|
||||
applyWindowChrome(window, isDarkTheme)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun macOsAppearanceName(isDarkTheme: Boolean): String =
|
||||
if (isDarkTheme) "NSAppearanceNameDarkAqua" else "NSAppearanceNameAqua"
|
||||
|
||||
internal fun usesTransparentTitlebar(): Boolean = true
|
||||
internal fun usesFullWindowContent(): Boolean = true
|
||||
internal fun showsNativeWindowTitle(): Boolean = false
|
||||
|
||||
internal fun titlebarBackground(isDarkTheme: Boolean): Color =
|
||||
if (isDarkTheme) Color(0x21, 0x21, 0x21) else Color(0xF3, 0xF3, 0xF3)
|
||||
|
||||
private fun applyWindowChrome(window: Window, isDarkTheme: Boolean) {
|
||||
val background = titlebarBackground(isDarkTheme)
|
||||
window.background = background
|
||||
(window as? JFrame)?.rootPane?.let { rootPane -> applyRootPaneChrome(rootPane, background) }
|
||||
}
|
||||
|
||||
internal fun applyRootPaneChrome(rootPane: JRootPane, background: Color) {
|
||||
// Extending the Compose surface beneath the native titlebar lets the
|
||||
// window chrome and sidebar share one uninterrupted background.
|
||||
rootPane.putClientProperty(FULL_WINDOW_CONTENT_PROPERTY, usesFullWindowContent())
|
||||
rootPane.putClientProperty(TRANSPARENT_TITLE_BAR_PROPERTY, usesTransparentTitlebar())
|
||||
rootPane.putClientProperty(WINDOW_TITLE_VISIBLE_PROPERTY, showsNativeWindowTitle())
|
||||
rootPane.background = background
|
||||
rootPane.contentPane.background = background
|
||||
}
|
||||
}
|
||||
actual fun PlatformSystemAppearance(isDarkTheme: Boolean) = Unit
|
||||
|
||||
object DesktopAppearanceBridge {
|
||||
@Volatile
|
||||
var applyNativeAppearance: ((Boolean) -> Unit)? = null
|
||||
|
||||
fun isMacOs(): Boolean = isMacOs(System.getProperty("os.name"))
|
||||
fun isLinux(): Boolean = isLinux(System.getProperty("os.name"))
|
||||
|
||||
fun toggleMaximized(window: Window) {
|
||||
if (!isMacOs()) return
|
||||
val frame = window as? Frame ?: return
|
||||
EventQueue.invokeLater {
|
||||
frame.extendedState = toggledWindowState(frame.extendedState)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun isMacOs(osName: String): Boolean =
|
||||
osName.startsWith("Mac", ignoreCase = true)
|
||||
|
||||
internal fun isLinux(osName: String): Boolean =
|
||||
osName.startsWith("Linux", ignoreCase = true)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user