Skip to content

Commit c8f2a28

Browse files
Add KeyEvents for search results
1 parent e4bb0e6 commit c8f2a28

File tree

4 files changed

+75
-24
lines changed

4 files changed

+75
-24
lines changed

composeApp/src/desktopMain/kotlin/com/sebastianneubauer/jsontreeviewer/App.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import androidx.compose.material.icons.filled.KeyboardArrowUp
3333
import androidx.compose.material.icons.outlined.Info
3434
import androidx.compose.material.icons.outlined.KeyboardArrowDown
3535
import androidx.compose.runtime.Composable
36+
import androidx.compose.runtime.LaunchedEffect
3637
import androidx.compose.runtime.getValue
3738
import androidx.compose.runtime.mutableStateOf
3839
import androidx.compose.runtime.remember
@@ -106,7 +107,7 @@ private fun AppUi(
106107
is Contract.State.InitialLoading -> InitialLoading(isHovering = isHovering)
107108
is Contract.State.Loading -> Loading()
108109
is Contract.State.Error -> Error(error = state.error, isHovering = isHovering)
109-
is Contract.State.Content -> Content(json = state.json, stats = state.stats, onJsonParsingError = onJsonParsingError)
110+
is Contract.State.Content -> Content(json = state.json, searchDirection = state.searchDirection, stats = state.stats, onJsonParsingError = onJsonParsingError)
110111
}
111112
}
112113
}
@@ -115,6 +116,7 @@ private fun AppUi(
115116
@Composable
116117
private fun Content(
117118
json: String,
119+
searchDirection: Contract.SearchDirection?,
118120
stats: Contract.Stats,
119121
onJsonParsingError: (Throwable) -> Unit
120122
) {
@@ -123,6 +125,14 @@ private fun Content(
123125
val searchQuery by remember(searchState.query) { mutableStateOf(searchState.query.orEmpty()) }
124126
var showSidebar by remember { mutableStateOf(false) }
125127

128+
LaunchedEffect(searchDirection) {
129+
when(searchDirection) {
130+
is Contract.SearchDirection.Next -> searchState.selectNext()
131+
is Contract.SearchDirection.Previous -> searchState.selectPrevious()
132+
null -> Unit
133+
}
134+
}
135+
126136
Column(
127137
modifier = Modifier
128138
.fillMaxSize()

composeApp/src/desktopMain/kotlin/com/sebastianneubauer/jsontreeviewer/State.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ object Contract {
77
data object InitialLoading: State
88
data object Loading: State
99
data class Error(val error: ErrorType): State
10-
data class Content(val json: String, val stats: Stats): State
10+
data class Content(val json: String, val stats: Stats, val searchDirection: SearchDirection?): State
1111
}
1212

1313
sealed class ErrorType {
@@ -17,6 +17,13 @@ object Contract {
1717
data class JsonParserError(val message: String): ErrorType()
1818
}
1919

20+
sealed interface SearchDirection {
21+
val increment: Int
22+
23+
data class Next(override val increment: Int): SearchDirection
24+
data class Previous(override val increment: Int): SearchDirection
25+
}
26+
2027
data class Stats(
2128
val filePath: String,
2229
val fileName: String,

composeApp/src/desktopMain/kotlin/com/sebastianneubauer/jsontreeviewer/ViewModel.kt

Lines changed: 55 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class ViewModel(
4343

4444
State.Content(
4545
json = json,
46+
searchDirection = null,
4647
stats = Contract.Stats(
4748
filePath = validFile.path,
4849
fileName = validFile.name,
@@ -63,34 +64,66 @@ class ViewModel(
6364

6465
fun onKeyEvent(event: KeyEvent): Boolean {
6566
return if ((event.isCtrlPressed || event.isMetaPressed) && event.key == Key.V) {
66-
val clipboardString = try {
67-
Toolkit.getDefaultToolkit()
68-
.systemClipboard
69-
.getData(DataFlavor.stringFlavor) as String
70-
} catch (e: Exception) {
71-
null
72-
}
73-
74-
viewModelState.value = if(clipboardString != null) {
75-
State.Content(
76-
json = clipboardString,
77-
stats = Contract.Stats(
78-
filePath = "from clipboard",
79-
fileName = "n/a",
80-
fileSize = "n/a",
81-
fileReadTime = "n/a",
82-
fileLines = clipboardString.lines().count().toString()
83-
)
84-
)
85-
} else {
86-
State.Error(error = Contract.ErrorType.CopyPasteError)
87-
}
67+
viewModelState.value = getStateFromClipboardData()
8868
true
69+
} else if(event.key == Key.DirectionDown || event.key == Key.Enter) {
70+
val newState = getStateFromSearchDirectionEvent(isDownEvent = true)
71+
viewModelState.value = newState
72+
newState is State.Content
73+
} else if(event.key == Key.DirectionUp) {
74+
val newState = getStateFromSearchDirectionEvent(isDownEvent = false)
75+
viewModelState.value = newState
76+
newState is State.Content
8977
} else {
9078
false
9179
}
9280
}
9381

82+
private fun getStateFromClipboardData(): State {
83+
val clipboardString = try {
84+
Toolkit.getDefaultToolkit()
85+
.systemClipboard
86+
.getData(DataFlavor.stringFlavor) as String
87+
} catch (e: Exception) {
88+
null
89+
}
90+
91+
return if(clipboardString != null) {
92+
State.Content(
93+
json = clipboardString,
94+
searchDirection = null,
95+
stats = Contract.Stats(
96+
filePath = "from clipboard",
97+
fileName = "n/a",
98+
fileSize = "n/a",
99+
fileReadTime = "n/a",
100+
fileLines = clipboardString.lines().count().toString()
101+
)
102+
)
103+
} else {
104+
State.Error(error = Contract.ErrorType.CopyPasteError)
105+
}
106+
}
107+
108+
private fun getStateFromSearchDirectionEvent(isDownEvent: Boolean): State {
109+
val currentState = viewModelState.value
110+
return if(currentState is State.Content) {
111+
val currentSearchDirection = currentState.searchDirection
112+
val newSearchDirection = if(isDownEvent) {
113+
when(currentSearchDirection) {
114+
is Contract.SearchDirection.Next -> currentSearchDirection.copy(increment = currentSearchDirection.increment + 1)
115+
else -> Contract.SearchDirection.Next(increment = 0)
116+
}
117+
} else {
118+
when(currentSearchDirection) {
119+
is Contract.SearchDirection.Previous -> currentSearchDirection.copy(increment = currentSearchDirection.increment + 1)
120+
else -> Contract.SearchDirection.Previous(increment = 0)
121+
}
122+
}
123+
currentState.copy(searchDirection = newSearchDirection)
124+
} else currentState
125+
}
126+
94127
fun showJsonParsingError(throwable: Throwable) {
95128
viewModelState.value = State.Error(
96129
error = Contract.ErrorType.JsonParserError(message = throwable.localizedMessage)

composeApp/src/desktopTest/kotlin/com/sebastianneubauer/jsontreeviewer/ViewModelTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ViewModelTest {
4444
assertEquals(
4545
expected = State.Content(
4646
json = json,
47+
searchDirection = null,
4748
stats = Contract.Stats(
4849
filePath = file.path,
4950
fileName = file.name,

0 commit comments

Comments
 (0)