mirror of
https://gitlab.com/2009scape/rt4-client.git
synced 2025-12-09 16:45:46 -07:00
Table editor
This commit is contained in:
parent
50e03baea7
commit
5e4ee2a5da
4 changed files with 307 additions and 85 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
package FooTextPlugin
|
package FooTextPlugin
|
||||||
|
|
||||||
|
import KondoKit.Exposed
|
||||||
import plugin.Plugin
|
import plugin.Plugin
|
||||||
|
import plugin.api.API
|
||||||
import rt4.Component
|
import rt4.Component
|
||||||
import rt4.JagString
|
import rt4.JagString
|
||||||
import rt4.Player
|
import rt4.Player
|
||||||
|
|
@ -32,8 +34,15 @@ class plugin : Plugin() {
|
||||||
3 to "<img=6>", // UIM
|
3 to "<img=6>", // UIM
|
||||||
)
|
)
|
||||||
|
|
||||||
private var ACCOUNT_TYPE = 0
|
// Local storage for username matches to avoid API calls
|
||||||
|
@Exposed(description = "Username to account type mappings (0=Normal, 1=IM, 2=HCIM, 3=UIM)")
|
||||||
|
private var usernameMatches = HashMap<String, Int>()
|
||||||
|
|
||||||
|
// Not used anymore - keeping for reference
|
||||||
|
// @Exposed(description = "Manual username entries (format: username:accountType, e.g., 'example:1')")
|
||||||
|
// private var manualEntries = listOf<String>()
|
||||||
|
|
||||||
|
private var ACCOUNT_TYPE = 0
|
||||||
|
|
||||||
private var component: Component? = null
|
private var component: Component? = null
|
||||||
|
|
||||||
|
|
@ -234,6 +243,19 @@ class plugin : Plugin() {
|
||||||
'\u00FF' to "-X"
|
'\u00FF' to "-X"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override fun Init() {
|
||||||
|
// Load username matches from local storage
|
||||||
|
usernameMatches = API.GetData("foo-text-username-matches") as? HashMap<String, Int> ?: HashMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun OnKondoValueUpdated() {
|
||||||
|
StoreData()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StoreData() {
|
||||||
|
API.StoreData("foo-text-username-matches", usernameMatches)
|
||||||
|
}
|
||||||
|
|
||||||
override fun Draw(timeDelta: Long) {
|
override fun Draw(timeDelta: Long) {
|
||||||
if (component != null && component?.id == TARGET_COMPONENT_ID) {
|
if (component != null && component?.id == TARGET_COMPONENT_ID) {
|
||||||
val username = Player.usernameInput.toString().toLowerCase()
|
val username = Player.usernameInput.toString().toLowerCase()
|
||||||
|
|
@ -267,19 +289,31 @@ class plugin : Plugin() {
|
||||||
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
val reader = BufferedReader(InputStreamReader(connection.inputStream))
|
||||||
val response = reader.use { it.readText() }
|
val response = reader.use { it.readText() }
|
||||||
reader.close()
|
reader.close()
|
||||||
updatePlayerData(response)
|
updatePlayerData(response, cleanUsername)
|
||||||
}
|
}
|
||||||
} catch (_: Exception) { }
|
} catch (_: Exception) { }
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePlayerData(jsonResponse: String) {
|
private fun updatePlayerData(jsonResponse: String, username: String) {
|
||||||
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java)
|
||||||
ACCOUNT_TYPE = hiscoresResponse.info.iron_mode.toInt()
|
ACCOUNT_TYPE = hiscoresResponse.info.iron_mode.toInt()
|
||||||
println("Resolved you are account type: $ACCOUNT_TYPE")
|
println("Resolved you are account type: $ACCOUNT_TYPE")
|
||||||
|
|
||||||
|
// Store the result in our local cache
|
||||||
|
usernameMatches[username] = ACCOUNT_TYPE
|
||||||
|
StoreData()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun OnLogin() {
|
override fun OnLogin() {
|
||||||
|
// Check if we already have the account type for this user
|
||||||
|
val cleanUsername = Player.usernameInput.toString().toLowerCase().replace(" ", "_")
|
||||||
|
if (usernameMatches.containsKey(cleanUsername)) {
|
||||||
|
ACCOUNT_TYPE = usernameMatches[cleanUsername]!!
|
||||||
|
println("Using cached account type: $ACCOUNT_TYPE for $cleanUsername")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// The server doesn't tell us what account type we are.
|
// The server doesn't tell us what account type we are.
|
||||||
// Requesting from API is the easier way to tell.
|
// Requesting from API is the easier way to tell.
|
||||||
fetchAccountTypeFromAPI()
|
fetchAccountTypeFromAPI()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR=YourName
|
AUTHOR=YourName
|
||||||
DESCRIPTION=Replaces the username part of Component Index: 51 ID: 8978483 with "foo"
|
DESCRIPTION=Displays account type icons next to usernames in chat. Uses local storage to cache results and avoid API calls. Supports manual entries for players not on live server.
|
||||||
VERSION=1.0
|
VERSION=1.0
|
||||||
|
|
@ -12,6 +12,7 @@ import java.awt.event.MouseEvent
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
import javax.swing.table.DefaultTableModel
|
||||||
|
|
||||||
class SettingsPanel(private val plugin: Plugin) : JPanel() {
|
class SettingsPanel(private val plugin: Plugin) : JPanel() {
|
||||||
|
|
||||||
|
|
@ -70,103 +71,131 @@ class SettingsPanel(private val plugin: Plugin) : JPanel() {
|
||||||
fieldPanel.add(label, gbc)
|
fieldPanel.add(label, gbc)
|
||||||
|
|
||||||
// Create appropriate input component based on field type
|
// Create appropriate input component based on field type
|
||||||
val inputComponent: JComponent = when {
|
val inputComponent: JComponent
|
||||||
field.type == Boolean::class.javaPrimitiveType || field.type == java.lang.Boolean::class.java ->
|
val isHashMapEditor: Boolean
|
||||||
JCheckBox().apply {
|
when {
|
||||||
|
field.type == Boolean::class.javaPrimitiveType || field.type == java.lang.Boolean::class.java -> {
|
||||||
|
inputComponent = JCheckBox().apply {
|
||||||
isSelected = field.get(plugin) as Boolean
|
isSelected = field.get(plugin) as Boolean
|
||||||
}
|
}
|
||||||
|
isHashMapEditor = false
|
||||||
|
}
|
||||||
|
|
||||||
field.type.isEnum ->
|
field.type.isEnum -> {
|
||||||
JComboBox((field.type.enumConstants as Array<Enum<*>>)).apply {
|
val enumConstants = field.type.enumConstants
|
||||||
|
inputComponent = JComboBox(enumConstants as Array<out Enum<*>>).apply {
|
||||||
selectedItem = field.get(plugin)
|
selectedItem = field.get(plugin)
|
||||||
}
|
}
|
||||||
|
isHashMapEditor = false
|
||||||
|
}
|
||||||
|
|
||||||
field.type == Int::class.javaPrimitiveType || field.type == Integer::class.java ->
|
field.type == Int::class.javaPrimitiveType || field.type == Integer::class.java -> {
|
||||||
JSpinner(SpinnerNumberModel(field.get(plugin) as Int, Int.MIN_VALUE, Int.MAX_VALUE, 1))
|
inputComponent = JSpinner(SpinnerNumberModel(field.get(plugin) as Int, Int.MIN_VALUE, Int.MAX_VALUE, 1))
|
||||||
|
isHashMapEditor = false
|
||||||
|
}
|
||||||
|
|
||||||
field.type == Float::class.javaPrimitiveType ||
|
field.type == Float::class.javaPrimitiveType ||
|
||||||
field.type == Double::class.javaPrimitiveType ||
|
field.type == Double::class.javaPrimitiveType ||
|
||||||
field.type == java.lang.Float::class.java ||
|
field.type == java.lang.Float::class.java ||
|
||||||
field.type == java.lang.Double::class.java ->
|
field.type == java.lang.Double::class.java -> {
|
||||||
JSpinner(SpinnerNumberModel((field.get(plugin) as Number).toDouble(), -Double.MAX_VALUE, Double.MAX_VALUE, 0.1))
|
inputComponent = JSpinner(SpinnerNumberModel((field.get(plugin) as Number).toDouble(), -Double.MAX_VALUE, Double.MAX_VALUE, 0.1))
|
||||||
|
isHashMapEditor = false
|
||||||
|
}
|
||||||
|
|
||||||
else ->
|
// Check if the field is a HashMap
|
||||||
JTextField(field.get(plugin)?.toString() ?: "")
|
field.type == HashMap::class.java || field.type.simpleName == "HashMap" -> {
|
||||||
}
|
inputComponent = createHashMapEditor(field, plugin, fieldNotifier)
|
||||||
|
isHashMapEditor = true
|
||||||
|
}
|
||||||
|
|
||||||
// Add mouse listener to the label only if a description is available
|
else -> {
|
||||||
if (description.isNotBlank()) {
|
inputComponent = JTextField(field.get(plugin)?.toString() ?: "")
|
||||||
label.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
|
isHashMapEditor = false
|
||||||
label.addMouseListener(object : MouseAdapter() {
|
|
||||||
override fun mouseEntered(e: MouseEvent) {
|
|
||||||
showCustomToolTip(description, label)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun mouseExited(e: MouseEvent) {
|
|
||||||
customToolTipWindow?.isVisible = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
gbc.gridx = 1
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 1.0
|
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL
|
|
||||||
fieldPanel.add(inputComponent, gbc)
|
|
||||||
|
|
||||||
val applyButton = JButton("\u2714").apply {
|
|
||||||
maximumSize = Dimension(Int.MAX_VALUE, 8)
|
|
||||||
}
|
|
||||||
gbc.gridx = 2
|
|
||||||
gbc.gridy = 0
|
|
||||||
gbc.weightx = 0.0
|
|
||||||
gbc.fill = GridBagConstraints.NONE
|
|
||||||
applyButton.addActionListener {
|
|
||||||
try {
|
|
||||||
val newValue = when (inputComponent) {
|
|
||||||
is JCheckBox -> inputComponent.isSelected
|
|
||||||
is JComboBox<*> -> inputComponent.selectedItem
|
|
||||||
is JSpinner -> inputComponent.value
|
|
||||||
is JTextField -> Helpers.convertValue(field.type, field.genericType, inputComponent.text)
|
|
||||||
else -> throw IllegalArgumentException("Unsupported input component type")
|
|
||||||
}
|
|
||||||
fieldNotifier.setFieldValue(field, newValue)
|
|
||||||
Helpers.showToast(
|
|
||||||
this@SettingsPanel,
|
|
||||||
"${field.name} updated successfully!"
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Helpers.showToast(
|
|
||||||
this@SettingsPanel,
|
|
||||||
"Failed to update ${field.name}: ${e.message}",
|
|
||||||
JOptionPane.ERROR_MESSAGE
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldPanel.add(applyButton, gbc)
|
// Handle HashMap editors differently - they don't use the fieldPanel layout
|
||||||
add(fieldPanel)
|
if (isHashMapEditor) {
|
||||||
|
// Add the HashMap editor directly to the settings panel
|
||||||
|
add(inputComponent)
|
||||||
|
} else {
|
||||||
|
// Add mouse listener to the label only if a description is available
|
||||||
|
if (description.isNotBlank()) {
|
||||||
|
label.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
|
||||||
|
label.addMouseListener(object : MouseAdapter() {
|
||||||
|
override fun mouseEntered(e: MouseEvent) {
|
||||||
|
showCustomToolTip(description, label)
|
||||||
|
}
|
||||||
|
|
||||||
// Track field changes in real-time and update UI
|
override fun mouseExited(e: MouseEvent) {
|
||||||
var previousValue = field.get(plugin)?.toString()
|
customToolTipWindow?.isVisible = false
|
||||||
val timer = Timer()
|
}
|
||||||
timer.schedule(object : TimerTask() {
|
})
|
||||||
override fun run() {
|
}
|
||||||
val currentValue = field.get(plugin)?.toString()
|
|
||||||
if (currentValue != previousValue) {
|
gbc.gridx = 1
|
||||||
previousValue = currentValue
|
gbc.gridy = 0
|
||||||
SwingUtilities.invokeLater {
|
gbc.weightx = 1.0
|
||||||
// Update the inputComponent based on the new value
|
gbc.fill = GridBagConstraints.HORIZONTAL
|
||||||
when (inputComponent) {
|
fieldPanel.add(inputComponent, gbc)
|
||||||
is JCheckBox -> inputComponent.isSelected = field.get(plugin) as Boolean
|
|
||||||
is JComboBox<*> -> inputComponent.selectedItem = field.get(plugin)
|
// Add apply button for non-HashMap editors
|
||||||
is JSpinner -> inputComponent.value = field.get(plugin)
|
val applyButton = JButton("\u2714").apply {
|
||||||
is JTextField -> inputComponent.text = field.get(plugin)?.toString() ?: ""
|
maximumSize = Dimension(Int.MAX_VALUE, 8)
|
||||||
|
}
|
||||||
|
gbc.gridx = 2
|
||||||
|
gbc.gridy = 0
|
||||||
|
gbc.weightx = 0.0
|
||||||
|
gbc.fill = GridBagConstraints.NONE
|
||||||
|
applyButton.addActionListener {
|
||||||
|
try {
|
||||||
|
val newValue = when (inputComponent) {
|
||||||
|
is JCheckBox -> inputComponent.isSelected
|
||||||
|
is JComboBox<*> -> inputComponent.selectedItem
|
||||||
|
is JSpinner -> inputComponent.value
|
||||||
|
is JTextField -> Helpers.convertValue(field.type, field.genericType, inputComponent.text)
|
||||||
|
else -> throw IllegalArgumentException("Unsupported input component type")
|
||||||
|
}
|
||||||
|
fieldNotifier.setFieldValue(field, newValue)
|
||||||
|
Helpers.showToast(
|
||||||
|
this@SettingsPanel,
|
||||||
|
"${field.name} updated successfully!"
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Helpers.showToast(
|
||||||
|
this@SettingsPanel,
|
||||||
|
"Failed to update ${field.name}: ${e.message}",
|
||||||
|
JOptionPane.ERROR_MESSAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldPanel.add(applyButton, gbc)
|
||||||
|
add(fieldPanel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track field changes in real-time and update UI (skip HashMap fields)
|
||||||
|
if (field.type != HashMap::class.java && field.type.simpleName != "HashMap") {
|
||||||
|
var previousValue = field.get(plugin)?.toString()
|
||||||
|
val timer = Timer()
|
||||||
|
timer.schedule(object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
val currentValue = field.get(plugin)?.toString()
|
||||||
|
if (currentValue != previousValue) {
|
||||||
|
previousValue = currentValue
|
||||||
|
SwingUtilities.invokeLater {
|
||||||
|
// Update the inputComponent based on the new value
|
||||||
|
when (inputComponent) {
|
||||||
|
is JCheckBox -> inputComponent.isSelected = field.get(plugin) as Boolean
|
||||||
|
is JComboBox<*> -> inputComponent.selectedItem = field.get(plugin)
|
||||||
|
is JSpinner -> inputComponent.value = field.get(plugin)
|
||||||
|
is JTextField -> inputComponent.text = field.get(plugin)?.toString() ?: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, 0, 1000) // Poll every 1000 milliseconds (1 second)
|
||||||
}, 0, 1000) // Poll every 1000 milliseconds (1 second)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exposedFields.isNotEmpty()) {
|
if (exposedFields.isNotEmpty()) {
|
||||||
|
|
@ -175,6 +204,165 @@ class SettingsPanel(private val plugin: Plugin) : JPanel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createHashMapEditor(field: java.lang.reflect.Field, plugin: Plugin, fieldNotifier: FieldNotifier): JComponent {
|
||||||
|
// Create a panel to hold the table and buttons
|
||||||
|
val editorPanel = JPanel()
|
||||||
|
editorPanel.layout = BoxLayout(editorPanel, BoxLayout.Y_AXIS)
|
||||||
|
editorPanel.background = WIDGET_COLOR
|
||||||
|
editorPanel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
||||||
|
editorPanel.maximumSize = Dimension(Int.MAX_VALUE, 250)
|
||||||
|
|
||||||
|
// Create title label
|
||||||
|
val titleLabel = JLabel("${field.name} (Key-Value Pairs)").apply {
|
||||||
|
foreground = secondaryColor
|
||||||
|
font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16)
|
||||||
|
alignmentX = Component.LEFT_ALIGNMENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current HashMap value
|
||||||
|
val hashMap = field.get(plugin) as? HashMap<*, *> ?: HashMap<Any, Any>()
|
||||||
|
|
||||||
|
// Create a table model for the HashMap
|
||||||
|
val tableModel = object : DefaultTableModel() {
|
||||||
|
init {
|
||||||
|
val columnVector = java.util.Vector<String>()
|
||||||
|
columnVector.add("Key")
|
||||||
|
columnVector.add("Value")
|
||||||
|
columnIdentifiers = columnVector
|
||||||
|
hashMap.forEach { (key, value) ->
|
||||||
|
val vector = java.util.Vector<Any>()
|
||||||
|
vector.add(key.toString())
|
||||||
|
vector.add(value.toString())
|
||||||
|
addRow(vector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCellEditable(row: Int, column: Int): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getColumnClass(columnIndex: Int): Class<*> {
|
||||||
|
return String::class.java
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the table
|
||||||
|
val table = JTable(tableModel).apply {
|
||||||
|
background = WIDGET_COLOR
|
||||||
|
foreground = secondaryColor
|
||||||
|
gridColor = primaryColor
|
||||||
|
tableHeader.background = WIDGET_COLOR
|
||||||
|
tableHeader.foreground = secondaryColor
|
||||||
|
preferredScrollableViewportSize = Dimension(Int.MAX_VALUE, 150)
|
||||||
|
setFillsViewportHeight(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a scroll pane for the table with fixed size
|
||||||
|
val scrollPane = JScrollPane(table).apply {
|
||||||
|
preferredSize = Dimension(Int.MAX_VALUE, 150)
|
||||||
|
maximumSize = preferredSize
|
||||||
|
minimumSize = preferredSize
|
||||||
|
background = WIDGET_COLOR
|
||||||
|
verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED
|
||||||
|
horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
|
||||||
|
alignmentX = Component.LEFT_ALIGNMENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create buttons panel
|
||||||
|
val buttonsPanel = JPanel(FlowLayout(FlowLayout.LEFT, 5, 5)).apply {
|
||||||
|
background = WIDGET_COLOR
|
||||||
|
alignmentX = Component.LEFT_ALIGNMENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add button
|
||||||
|
val addButton = JButton("+").apply {
|
||||||
|
maximumSize = Dimension(40, 30)
|
||||||
|
addActionListener {
|
||||||
|
val vector = java.util.Vector<Any>()
|
||||||
|
vector.add("")
|
||||||
|
vector.add("")
|
||||||
|
tableModel.addRow(vector)
|
||||||
|
// Select the new row for editing
|
||||||
|
val newRow = tableModel.rowCount - 1
|
||||||
|
table.setRowSelectionInterval(newRow, newRow)
|
||||||
|
table.editCellAt(newRow, 0)
|
||||||
|
table.editorComponent?.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove button
|
||||||
|
val removeButton = JButton("-").apply {
|
||||||
|
maximumSize = Dimension(40, 30)
|
||||||
|
addActionListener {
|
||||||
|
val selectedRow = table.selectedRow
|
||||||
|
if (selectedRow >= 0) {
|
||||||
|
tableModel.removeRow(selectedRow)
|
||||||
|
} else {
|
||||||
|
Helpers.showToast(
|
||||||
|
this@SettingsPanel,
|
||||||
|
"Please select a row to remove",
|
||||||
|
JOptionPane.WARNING_MESSAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply button
|
||||||
|
val applyButton = JButton("Apply Changes").apply {
|
||||||
|
maximumSize = Dimension(220, 30)
|
||||||
|
addActionListener {
|
||||||
|
try {
|
||||||
|
// Create a new HashMap from the table data
|
||||||
|
// We need to determine the key and value types from the field's generic type
|
||||||
|
val newHashMap = HashMap<String, Any>()
|
||||||
|
for (i in 0 until tableModel.rowCount) {
|
||||||
|
val key = tableModel.getValueAt(i, 0).toString()
|
||||||
|
val value = tableModel.getValueAt(i, 1).toString()
|
||||||
|
// Only add non-empty keys
|
||||||
|
if (key.isNotBlank()) {
|
||||||
|
// Try to convert the value to the appropriate type
|
||||||
|
val convertedValue = try {
|
||||||
|
// For now, we'll keep everything as strings
|
||||||
|
// In the future, we could parse based on the generic type information
|
||||||
|
value
|
||||||
|
} catch (e: Exception) {
|
||||||
|
value // fallback to string
|
||||||
|
}
|
||||||
|
newHashMap[key] = convertedValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the field with the new HashMap
|
||||||
|
fieldNotifier.setFieldValue(field, newHashMap)
|
||||||
|
Helpers.showToast(
|
||||||
|
this@SettingsPanel,
|
||||||
|
"${field.name} updated successfully!"
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Helpers.showToast(
|
||||||
|
this@SettingsPanel,
|
||||||
|
"Failed to update ${field.name}: ${e.message}",
|
||||||
|
JOptionPane.ERROR_MESSAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonsPanel.add(addButton)
|
||||||
|
buttonsPanel.add(removeButton)
|
||||||
|
buttonsPanel.add(Box.createHorizontalStrut(20))
|
||||||
|
buttonsPanel.add(applyButton)
|
||||||
|
|
||||||
|
// Add components to the editor panel in vertical stack
|
||||||
|
editorPanel.add(titleLabel)
|
||||||
|
editorPanel.add(Box.createVerticalStrut(5))
|
||||||
|
editorPanel.add(scrollPane)
|
||||||
|
editorPanel.add(Box.createVerticalStrut(5))
|
||||||
|
editorPanel.add(buttonsPanel)
|
||||||
|
|
||||||
|
return editorPanel
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var customToolTipWindow: JWindow? = null
|
var customToolTipWindow: JWindow? = null
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
AUTHOR='downthecrop'
|
AUTHOR='downthecrop'
|
||||||
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation.'
|
DESCRIPTION='A plugin that adds a right-side panel with custom widgets and navigation. Now with enhanced HashMap editor for easier configuration management.'
|
||||||
VERSION=2.0
|
VERSION=2.1
|
||||||
Loading…
Add table
Add a link
Reference in a new issue