Skip to content

Commit 27f09da

Browse files
author
Sunny Chung
committed
Merge branch 'feature/windows-os' into 'main'
Windows OS See merge request products/hello-http!1
2 parents 3c4b7cc + 45a77e6 commit 27f09da

File tree

7 files changed

+87
-31
lines changed

7 files changed

+87
-31
lines changed

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/CodeEditorView.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import androidx.compose.ui.text.input.TextFieldValue
2828
import androidx.compose.ui.text.input.VisualTransformation
2929
import com.sunnychung.application.multiplatform.hellohttp.extension.insert
3030
import com.sunnychung.application.multiplatform.hellohttp.util.log
31+
import com.sunnychung.application.multiplatform.hellohttp.ux.compose.TextField
3132
import com.sunnychung.application.multiplatform.hellohttp.ux.compose.TextFieldColors
3233
import com.sunnychung.application.multiplatform.hellohttp.ux.compose.TextFieldDefaults
3334
import com.sunnychung.application.multiplatform.hellohttp.ux.local.LocalColor
@@ -55,17 +56,18 @@ fun CodeEditorView(
5556

5657
val themeColours = LocalColor.current
5758

58-
var textValue by remember { mutableStateOf(TextFieldValue(text = text)) }
59-
if (textValue.text != text) {
60-
textValue = textValue.copy(text = text)
61-
}
59+
// Replace "\r\n" by "\n" because to workaround the issue:
60+
// https://github.com/JetBrains/compose-multiplatform/issues/3877
61+
var textValue by remember(text) { mutableStateOf(TextFieldValue(text = text.replace("\r\n", "\n"))) }
62+
log.d { "CodeEditorView recompose" }
6263

6364
fun onPressEnterAddIndent() {
6465
val cursorPos = textValue.selection.min
6566
assert(textValue.selection.length == 0)
6667

6768
log.d { "onPressEnterAddIndent" }
6869

70+
val text = textValue.text
6971
var lastLineStart = getLineStart(text, cursorPos)
7072
var spacesMatch = "^(\\s+)".toRegex().matchAt(text.substring(lastLineStart, cursorPos), 0)
7173
val newSpaces = "\n" + (spacesMatch?.groups?.get(1)?.value ?: "")
@@ -77,6 +79,7 @@ fun CodeEditorView(
7779
log.v { "cursor at ${textValue.selection}" }
7880
fun onPressTab(isShiftPressed: Boolean) {
7981
val selection = textValue.selection
82+
val text = textValue.text
8083
if (selection.length == 0) {
8184
val cursorPos = selection.min
8285
val newSpaces = " ".repeat(4)

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/FileDialog.kt

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,56 @@ package com.sunnychung.application.multiplatform.hellohttp.ux
22

33
import androidx.compose.runtime.Composable
44
import androidx.compose.ui.window.AwtWindow
5+
import com.sunnychung.application.multiplatform.hellohttp.util.log
6+
import com.sunnychung.application.multiplatform.hellohttp.ux.viewmodel.FileDialogState
7+
import com.sunnychung.lib.multiplatform.kdatetime.KDuration
8+
import com.sunnychung.lib.multiplatform.kdatetime.KFixedTimeUnit
9+
import com.sunnychung.lib.multiplatform.kdatetime.KInstant
510
import java.awt.FileDialog
611
import java.awt.Frame
712
import java.io.File
813

14+
/**
15+
* Due to the bug stated in {@link FileDialogState}, result of onCloseRequest has 3 cases:
16+
* 1. non-empty list -> user selected a file
17+
* 2. empty list -> user clicked cancel
18+
* 3. null -> internally cancel
19+
*/
920
@Composable
1021
fun FileDialog(
1122
parent: Frame? = null,
23+
state: FileDialogState,
1224
mode: Int = FileDialog.LOAD,
1325
filename: String? = null,
14-
onCloseRequest: (result: List<File>) -> Unit
15-
) = AwtWindow(
16-
create = {
17-
object : FileDialog(parent, "Choose a file", mode) {
18-
init {
19-
if (filename != null) {
20-
file = filename
26+
onCloseRequest: (result: List<File>?) -> Unit
27+
) {
28+
log.d { "FileDialog 1" }
29+
val lastCloseTime = state.lastCloseTime.value
30+
if (lastCloseTime != null && KInstant.now() - lastCloseTime < KDuration.Companion.of(1, KFixedTimeUnit.Second)) {
31+
onCloseRequest(null)
32+
return
33+
}
34+
log.d { "FileDialog 2 $lastCloseTime" }
35+
AwtWindow(
36+
create = {
37+
log.d { "FileDialog create" }
38+
object : FileDialog(parent, "Choose a file", mode) {
39+
init {
40+
if (filename != null) {
41+
file = filename
42+
}
2143
}
22-
}
2344

24-
override fun setVisible(b: Boolean) {
25-
super.setVisible(b)
26-
if (b) {
27-
onCloseRequest(this.files.toList())
45+
override fun setVisible(b: Boolean) {
46+
super.setVisible(b)
47+
log.d { "FileDialog setVisible $b" }
48+
if (b) {
49+
onCloseRequest(this.files.toList())
50+
state.lastCloseTime.value = KInstant.now()
51+
}
2852
}
2953
}
30-
}
31-
},
32-
dispose = FileDialog::dispose
33-
)
54+
},
55+
dispose = FileDialog::dispose
56+
)
57+
}

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/KeyValueEditorView.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ import androidx.compose.ui.text.input.VisualTransformation
2727
import androidx.compose.ui.unit.dp
2828
import com.sunnychung.application.multiplatform.hellohttp.model.FieldValueType
2929
import com.sunnychung.application.multiplatform.hellohttp.model.UserKeyValuePair
30+
import com.sunnychung.application.multiplatform.hellohttp.util.log
3031
import com.sunnychung.application.multiplatform.hellohttp.util.uuidString
3132
import com.sunnychung.application.multiplatform.hellohttp.ux.local.LocalColor
3233
import com.sunnychung.application.multiplatform.hellohttp.ux.transformation.EnvironmentVariableTransformation
3334
import com.sunnychung.application.multiplatform.hellohttp.ux.transformation.FunctionTransformation
3435
import com.sunnychung.application.multiplatform.hellohttp.ux.transformation.MultipleVisualTransformation
36+
import com.sunnychung.application.multiplatform.hellohttp.ux.viewmodel.rememberFileDialogState
3537
import java.io.File
3638

3739
@Composable
@@ -54,12 +56,15 @@ fun KeyValueEditorView(
5456

5557
var isShowFileDialog by remember { mutableStateOf(false) }
5658
var fileDialogRequest by remember { mutableStateOf<Int?>(null) }
59+
val fileDialogState = rememberFileDialogState()
5760

5861
if (isShowFileDialog) {
59-
FileDialog {
62+
FileDialog(state = fileDialogState) {
6063
println("File Dialog result = $it")
61-
val index = fileDialogRequest!!
62-
onItemChange(index, keyValues[index].copy(value = it.firstOrNull()?.absolutePath ?: ""))
64+
if (it != null) {
65+
val index = fileDialogRequest!!
66+
onItemChange(index, keyValues[index].copy(value = it.firstOrNull()?.absolutePath ?: ""))
67+
}
6368
isShowFileDialog = false
6469
}
6570
}
@@ -175,7 +180,7 @@ fun KeyValueEditorView(
175180
text = file?.name ?: "Choose a File",
176181
color = if (isEnabled) colors.primary else colors.disabled,
177182
onClick = if (!isInheritedView) {
178-
{ fileDialogRequest = index; isShowFileDialog = true }
183+
{ log.d {"onClick file"}; fileDialogRequest = index; isShowFileDialog = true }
179184
} else null,
180185
modifier = Modifier.weight(0.6f).border(width = 1.dp, color = colors.placeholder)
181186
)

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/RequestEditorView.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import com.sunnychung.application.multiplatform.hellohttp.ux.local.LocalFont
5858
import com.sunnychung.application.multiplatform.hellohttp.ux.transformation.EnvironmentVariableTransformation
5959
import com.sunnychung.application.multiplatform.hellohttp.ux.transformation.JsonSyntaxHighlightTransformation
6060
import com.sunnychung.application.multiplatform.hellohttp.ux.viewmodel.EditNameViewModel
61+
import com.sunnychung.application.multiplatform.hellohttp.ux.viewmodel.rememberFileDialogState
6162
import java.io.File
6263

6364
@Composable
@@ -696,10 +697,11 @@ fun BinaryFileInputView(modifier: Modifier = Modifier, filePath: String?, onFile
696697
val colours = LocalColor.current
697698

698699
var isShowFileDialog by remember { mutableStateOf(false) }
700+
val fileDialogState = rememberFileDialogState()
699701

700702
if (isShowFileDialog) {
701-
FileDialog {
702-
if (it.isNotEmpty()) {
703+
FileDialog(state = fileDialogState) {
704+
if (it != null && it.isNotEmpty()) {
703705
onFilePathUpdate(it.first().absolutePath)
704706
}
705707
isShowFileDialog = false

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/SettingDialogView.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import com.sunnychung.application.multiplatform.hellohttp.model.ColourTheme
4040
import com.sunnychung.application.multiplatform.hellohttp.model.DEFAULT_BACKUP_RETENTION_DAYS
4141
import com.sunnychung.application.multiplatform.hellohttp.util.log
4242
import com.sunnychung.application.multiplatform.hellohttp.ux.local.LocalColor
43+
import com.sunnychung.application.multiplatform.hellohttp.ux.viewmodel.rememberFileDialogState
4344
import com.sunnychung.lib.multiplatform.kdatetime.KZonedInstant
4445
import kotlinx.coroutines.runBlocking
4546
import java.awt.Desktop
@@ -116,10 +117,13 @@ private fun DataTab(modifier: Modifier = Modifier, closeDialog: () -> Unit) {
116117

117118
var isShowFileDialog by remember { mutableStateOf(false) }
118119
var file by remember { mutableStateOf<File?>(null) }
120+
val fileDialogState = rememberFileDialogState()
119121
if (isShowFileDialog) {
120-
FileDialog {
122+
FileDialog(state = fileDialogState) {
121123
println("File Dialog result = $it")
122-
file = it.firstOrNull()
124+
if (it != null) {
125+
file = it.firstOrNull()
126+
}
123127
isShowFileDialog = false
124128
}
125129
}
@@ -184,6 +188,7 @@ private fun DataTab(modifier: Modifier = Modifier, closeDialog: () -> Unit) {
184188
var exportFileFormat by remember { mutableStateOf(ExportFormat.values().first()) }
185189
var isShowDirectoryPicker by remember { mutableStateOf(false) }
186190
var isShowFileDialog by remember { mutableStateOf(false) }
191+
val fileDialogState = rememberFileDialogState()
187192

188193
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
189194
AppText(text = "Choose project(s) to export")
@@ -241,9 +246,9 @@ private fun DataTab(modifier: Modifier = Modifier, closeDialog: () -> Unit) {
241246
}
242247
if (isShowFileDialog) {
243248
val dateTimeString = KZonedInstant.nowAtLocalZoneOffset().format("yyyy-MM-dd--HH-mm-ss")
244-
FileDialog(mode = java.awt.FileDialog.SAVE, filename = "Hello-HTTP_dump_$dateTimeString.dump") {
249+
FileDialog(state = fileDialogState, mode = java.awt.FileDialog.SAVE, filename = "Hello-HTTP_dump_$dateTimeString.dump") {
245250
isShowFileDialog = false
246-
val file = it.firstOrNull()
251+
val file = it?.firstOrNull()
247252

248253
file?.takeIf { exportFileFormat == ExportFormat.`Hello HTTP Data Dump` }?.let { file ->
249254
// TODO suspend instead of runBlocking

src/jvmMain/kotlin/com/sunnychung/application/multiplatform/hellohttp/ux/StatusBarView.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fun StatusBarView(modifier: Modifier = Modifier) {
2626

2727
var isShowSettingDialog by remember { mutableStateOf(false) }
2828

29-
Box(modifier = modifier.fillMaxWidth().height(22.dp).background(colors.backgroundLight).padding(4.dp)) {
29+
Box(modifier = modifier.fillMaxWidth().height(22.dp).background(colors.backgroundLight).padding(horizontal = 4.dp)) {
3030
AppText(
3131
text = "v${metadataManager.version} (${metadataManager.gitCommitHash})",
3232
fontSize = 12.sp,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.sunnychung.application.multiplatform.hellohttp.ux.viewmodel
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.remember
5+
import com.sunnychung.lib.multiplatform.kdatetime.KInstant
6+
import kotlinx.coroutines.flow.MutableStateFlow
7+
8+
/**
9+
* This class exists as a workaround to the bug that
10+
* "Modifier.clickable" is invoked twice in the Windows OS.
11+
*/
12+
class FileDialogState {
13+
val lastCloseTime = MutableStateFlow<KInstant?>(null)
14+
}
15+
16+
@Composable
17+
fun rememberFileDialogState() = remember { FileDialogState() }

0 commit comments

Comments
 (0)