Skip to content

Commit

Permalink
Support QuickSearch previews
Browse files Browse the repository at this point in the history
  • Loading branch information
garyttierney committed Apr 13, 2024
1 parent 7128521 commit 3c88258
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 20 deletions.
30 changes: 22 additions & 8 deletions src/main/kotlin/ui/root/WorkspaceView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,23 @@ package io.github.garyttierney.ghidralite.ui.root

import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.SwingPanel
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.*
import androidx.compose.ui.unit.dp
import io.github.garyttierney.ghidralite.framework.search.SearchResult
import io.github.garyttierney.ghidralite.ui.search.QuickSearch
import io.github.garyttierney.ghidralite.ui.search.QuickSearchWindow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
import org.jetbrains.compose.splitpane.HorizontalSplitPane
import org.jetbrains.compose.splitpane.rememberSplitPaneState
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.ui.component.*
import java.awt.BorderLayout
import javax.swing.JPanel


@OptIn(ExperimentalSplitPaneApi::class)
Expand Down Expand Up @@ -61,6 +58,23 @@ fun WorkspaceView(model: Workspace) = key(model) {
QuickSearch(
items = searchResults,
query = searchQuery,
itemPreview = { item ->
Box(modifier = Modifier.background(Color.Red)) {
SwingPanel(
modifier = Modifier.fillMaxSize().background(Color.Red),
factory = {

val panel = JPanel(BorderLayout())
panel.add(model.listing, BorderLayout.CENTER)
panel
},
update = {
val sym = model.program.symbolTable.getSymbol(item.element.key as Long)
model.listing.goTo(sym.address)
}
)
}
},
onQueryChanged = {
searchQuery = it
searchResults.clear()
Expand Down
57 changes: 49 additions & 8 deletions src/main/kotlin/ui/search/QuickSearch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,25 @@ import androidx.compose.foundation.rememberScrollbarAdapter
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.platform.LocalWindowInfo
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.MenuBar
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.rememberWindowState
import io.github.garyttierney.ghidralite.LocalWindowPosition
import io.github.garyttierney.ghidralite.framework.search.SearchResult
import kotlinx.coroutines.launch
import org.jetbrains.jewel.foundation.lazy.*
Expand All @@ -31,17 +38,20 @@ import org.jetbrains.jewel.ui.component.*
import org.jetbrains.jewel.ui.theme.colorPalette
import org.jetbrains.jewel.ui.theme.menuStyle

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun QuickSearch(
items: List<SearchResult>,
query: String,
onQueryChanged: (String) -> Unit,
onResultSelected: (SearchResult) -> Unit,
focusRequester: FocusRequester = FocusRequester(),
listActions: KeyActions = remember { DefaultSelectableLazyColumnKeyActions(DefaultSelectableColumnKeybindings) }
listActions: KeyActions = remember { DefaultSelectableLazyColumnKeyActions(DefaultSelectableColumnKeybindings) },
itemPreview: @Composable (SearchResult) -> Unit = {}
) {
val itemListState = rememberSelectableLazyListState()
val itemKeys by derivedStateOf { items.map { SelectableLazyListKey.Selectable(it.element.key) }.toList() }
val itemSelected by derivedStateOf { items.find { it.element.key == itemListState.selectedKeys.firstOrNull() } }

val handleListAction = { event: KeyEvent ->
val itemListHandler = listActions.handleOnKeyEvent(event, itemKeys, itemListState, SelectionMode.Single)
Expand All @@ -53,14 +63,44 @@ fun QuickSearch(
}
}

Column(modifier = Modifier.onPreviewKeyEvent(handleListAction).focusRequester(focusRequester).focusGroup()) {
QuickSearchInput(query = query, onQueryChanged = onQueryChanged)
Row {
Column(modifier = Modifier.onPreviewKeyEvent(handleListAction).focusRequester(focusRequester).focusGroup()) {
QuickSearchInput(query = query, onQueryChanged = onQueryChanged)

QuickSearchResultList(
items = items,
state = itemListState,
onItemSelected = onResultSelected,
)
QuickSearchResultList(
items = items,
itemPreview = itemPreview,
state = itemListState,
onItemSelected = onResultSelected,
)
}

itemSelected?.let {
val containerSize = LocalWindowInfo.current.containerSize
val containerPos = LocalWindowPosition.current
val popupOffset = containerSize.width + 10
val popupPos = WindowPosition.Absolute(containerPos.x + popupOffset.dp, containerPos.y)
val popupState = rememberWindowState(
position = popupPos,
width = containerSize.width.dp,
height = containerSize.height.dp
)

Window(
visible = true,
onCloseRequest = {},
state = popupState,
undecorated = true,
alwaysOnTop = true,
focusable = false,
) {
// TODO HACK: A window with a single SwingPanel it doesn't render without the presence of another heavyweight
// component.
MenuBar {}

itemPreview(it)
}
}
}
}

Expand All @@ -84,6 +124,7 @@ fun QuickSearchResultList(
items: List<SearchResult>,
onItemSelected: (SearchResult) -> Unit,
state: SelectableLazyListState = rememberSelectableLazyListState(),
itemPreview: @Composable (SearchResult) -> Unit,
) {
val listTheme = Modifier.fillMaxSize().background(JewelTheme.menuStyle.colors.background)
val scope = rememberCoroutineScope()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,14 @@ import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPosition
import androidx.compose.ui.window.WindowState
import androidx.compose.ui.window.rememberWindowState
import io.github.garyttierney.ghidralite.LocalWindowPosition
import io.github.garyttierney.ghidralite.framework.search.SearchResult
import java.util.concurrent.PriorityBlockingQueue

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun QuickSearchWindow(
fun QuickSearchPopup(
visible: Boolean = true,
state: WindowState = rememberWindowState(),
results: List<SearchResult>,
onResultSelected: (SearchResult) -> Unit,
query: String = "",
Expand Down

0 comments on commit 3c88258

Please sign in to comment.