Add native macOS folder picker for staging directory
Uses AWT FileDialog with apple.awt.fileDialogForDirectories=true to open the native macOS folder chooser. Falls back gracefully (dialog cancelled) with no change to the text field.
This commit is contained in:
@@ -3,12 +3,16 @@ package com.bolenpad.photophetch.ui
|
|||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.FolderOpen
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import java.awt.FileDialog
|
||||||
|
import java.awt.Frame
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(state: AppState) {
|
fun SettingsScreen(state: AppState) {
|
||||||
@@ -33,14 +37,30 @@ fun SettingsScreen(state: AppState) {
|
|||||||
|
|
||||||
// Staging directory
|
// Staging directory
|
||||||
Text("Staging Directory", style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.SemiBold)
|
Text("Staging Directory", style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.SemiBold)
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = stagingRoot,
|
value = stagingRoot,
|
||||||
onValueChange = { stagingRoot = it },
|
onValueChange = { stagingRoot = it },
|
||||||
label = { Text("Path to _staging/ directory") },
|
label = { Text("Path to _staging/ directory") },
|
||||||
placeholder = { Text("/Volumes/delphi/_staging") },
|
placeholder = { Text("/Volumes/delphi/_staging") },
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.weight(1f),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
)
|
)
|
||||||
|
OutlinedIconButton(
|
||||||
|
onClick = {
|
||||||
|
val chosen = pickFolder(
|
||||||
|
title = "Select _staging directory",
|
||||||
|
initialDir = stagingRoot.ifBlank { "/Volumes" },
|
||||||
|
)
|
||||||
|
if (chosen != null) stagingRoot = chosen
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Icon(Icons.Default.FolderOpen, contentDescription = "Browse…")
|
||||||
|
}
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
"This should be the _staging directory on your NAS (must be mounted).",
|
"This should be the _staging directory on your NAS (must be mounted).",
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
@@ -78,7 +98,6 @@ fun SettingsScreen(state: AppState) {
|
|||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
state.updateStagingRoot(stagingRoot)
|
state.updateStagingRoot(stagingRoot)
|
||||||
// Also save template update
|
|
||||||
state.saveConfig()
|
state.saveConfig()
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth().height(52.dp),
|
modifier = Modifier.fillMaxWidth().height(52.dp),
|
||||||
@@ -87,3 +106,24 @@ fun SettingsScreen(state: AppState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the native macOS folder picker (System.setProperty trick to make
|
||||||
|
* AWT FileDialog show directories instead of files).
|
||||||
|
* Returns the chosen absolute path, or null if cancelled.
|
||||||
|
*/
|
||||||
|
private fun pickFolder(title: String, initialDir: String): String? {
|
||||||
|
// On macOS, this system property switches FileDialog to directory-selection mode
|
||||||
|
System.setProperty("apple.awt.fileDialogForDirectories", "true")
|
||||||
|
return try {
|
||||||
|
val dialog = FileDialog(null as Frame?, title, FileDialog.LOAD).apply {
|
||||||
|
directory = initialDir
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
val dir = dialog.directory
|
||||||
|
val file = dialog.file
|
||||||
|
if (file != null) File(dir, file).absolutePath else null
|
||||||
|
} finally {
|
||||||
|
System.setProperty("apple.awt.fileDialogForDirectories", "false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user