rt4-client/plugin-playground/src/main/kotlin/KondoKit/components/SearchField.kt
2025-10-21 21:51:40 -07:00

173 lines
No EOL
5.6 KiB
Kotlin

package KondoKit.components
import KondoKit.ImageCanvas
import KondoKit.ViewConstants
import KondoKit.SpriteToBufferedImage
import KondoKit.plugin.Companion.WIDGET_COLOR
import KondoKit.plugin.Companion.secondaryColor
import KondoKit.plugin.StateManager.focusedView
import plugin.api.API
import java.awt.*
import java.awt.datatransfer.DataFlavor
import java.awt.event.*
import javax.swing.*
class SearchField(
private val parentPanel: JPanel,
private val onSearch: (String) -> Unit,
private val placeholderText: String = "Search...",
private val fieldWidth: Int = 230,
private val fieldHeight: Int = 30,
private val viewName: String? = ""
) : Canvas() {
private var cursorVisible: Boolean = true
private var text: String = ""
private val bufferedImageSprite = SpriteToBufferedImage.getBufferedImageFromSprite(API.GetSprite(1423)) // MAG_SPRITE
private val imageCanvas = bufferedImageSprite.let {
ImageCanvas(it).apply {
preferredSize = ViewConstants.DIMENSION_SMALL_ICON
size = preferredSize
minimumSize = preferredSize
maximumSize = preferredSize
fillColor = WIDGET_COLOR
}
}
init {
val dimension = Dimension(fieldWidth, fieldHeight)
preferredSize = dimension
background = WIDGET_COLOR
foreground = secondaryColor
font = Font("Arial", Font.PLAIN, 14)
minimumSize = dimension
maximumSize = dimension
addKeyListener(object : KeyAdapter() {
override fun keyTyped(e: KeyEvent) {
// Prevent null character from being typed on Ctrl+A & Ctrl+V
if (e.isControlDown && (e.keyChar == '\u0001' || e.keyChar == '\u0016')) {
e.consume()
return
}
if (e.keyChar == '\b') {
if (text.isNotEmpty()) {
text = text.dropLast(1)
}
} else if (e.keyChar == '\n') {
triggerSearch()
} else {
text += e.keyChar
}
SwingUtilities.invokeLater {
repaint()
}
}
override fun keyPressed(e: KeyEvent) {
if (e.isControlDown) {
when (e.keyCode) {
KeyEvent.VK_A -> {
// They probably want to clear the search box
text = ""
SwingUtilities.invokeLater {
repaint()
}
}
KeyEvent.VK_V -> {
try {
val clipboard = Toolkit.getDefaultToolkit().systemClipboard
val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String
text += pasteText
SwingUtilities.invokeLater {
repaint()
}
} catch (_: Exception) { }
}
}
}
}
})
addMouseListener(object : MouseAdapter() {
// Clicked the search field 'x' button
override fun mouseClicked(e: MouseEvent) {
if (e.x > width - 20 && e.y < 20) {
text = ""
triggerSearch()
SwingUtilities.invokeLater {
repaint()
}
}
}
})
Timer(500) { _ ->
cursorVisible = !cursorVisible
// Only repaint if the view is active or if viewName is not specified
if (viewName == null || focusedView == viewName) {
SwingUtilities.invokeLater {
repaint()
}
}
}.start()
}
override fun paint(g: Graphics) {
super.paint(g)
g.color = foreground
g.font = font
val fm = g.fontMetrics
val cursorX = fm.stringWidth(text) + 30
// Draw magnifying glass icon
imageCanvas.let { canvas ->
val imgG = g.create(5, 5, canvas.width, canvas.height)
canvas.paint(imgG)
imgG.dispose()
}
// Use a local copy of the text to avoid threading issues
val currentText = text
// Draw placeholder text if field is empty, otherwise draw actual text
if (currentText.isEmpty()) {
g.color = Color.GRAY // lighter color for placeholder
g.drawString(placeholderText, 30, 20)
} else {
g.color = foreground // normal color for user entered text
g.drawString(currentText, 30, 20)
}
if (cursorVisible && hasFocus()) {
g.color = foreground
g.drawLine(cursorX, 5, cursorX, 25)
}
// Only draw the "x" button if there's text
if (currentText.isNotEmpty()) {
g.color = Color.RED
g.drawString("x", width - 20, 20)
}
}
fun setText(newText: String) {
text = newText
repaint()
}
fun getText(): String = text
private fun triggerSearch() {
val query = text.trim()
if (query.isNotEmpty()) {
text = query
repaint()
onSearch(query)
} else {
onSearch(query) // Call with empty string for clearing filters
}
}
}