comment pruning

This commit is contained in:
downthecrop 2025-11-21 17:55:13 -08:00
parent 0c1e445a95
commit f61852660a
15 changed files with 32 additions and 278 deletions

View file

@ -43,8 +43,6 @@ annotation class Exposed(val description: String = "")
class plugin : Plugin() {
companion object {
val WIDGET_SIZE = ViewConstants.DEFAULT_WIDGET_SIZE
val TOTAL_XP_WIDGET_SIZE = ViewConstants.TOTAL_XP_WIDGET_SIZE
val IMAGE_SIZE = Dimension(25, 23)
// Default Theme Colors
@ -142,7 +140,6 @@ class plugin : Plugin() {
}
override fun Init() {
// Disable Font AA
System.setProperty("sun.java2d.opengl", "false")
System.setProperty("awt.useSystemAAFontSettings", "off")
System.setProperty("swing.aatext", "false")
@ -150,8 +147,6 @@ class plugin : Plugin() {
override fun OnLogin() {
if (lastLogin != "" && lastLogin != Player.usernameInput.toString()) {
// if we logged in with a new character
// we need to reset the trackers
XPTrackerView.xpTrackerView?.let { XPTrackerView.resetXPTracker(it) }
}
lastLogin = Player.usernameInput.toString()
@ -162,15 +157,12 @@ class plugin : Plugin() {
for ((index, entry) in currentEntries.withIndex()) {
if (entry.type == MiniMenuType.PLAYER && index == currentEntries.size - 1) {
val input = entry.subject
// Trim spaces, clean up tags, and remove the level info
val cleanedInput = input
.trim() // Remove any leading/trailing spaces
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "") // Remove color tags
.replace(Regex("<img=\\d+>"), "") // Remove image tags
.replace(Regex("\\(level: \\d+\\)"), "") // Remove level text e.g. (level: 44)
.trim() // Trim again to remove extra spaces after removing level text
// Proceed with the full cleaned username
.trim()
.replace(Regex("<col=[0-9a-fA-F]{6}>"), "")
.replace(Regex("<img=\\d+>"), "")
.replace(Regex("\\(level: \\d+\\)"), "")
.trim()
InsertMiniMenuEntry("Lookup", entry.subject, searchHiscore(cleanedInput))
}
}
@ -201,7 +193,6 @@ class plugin : Plugin() {
frame.revalidate()
frame.repaint()
// Rebuild the reflective editor UI on the EDT and in one batch
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
pluginsReloaded = true
@ -210,7 +201,6 @@ class plugin : Plugin() {
}
override fun OnXPUpdate(skillId: Int, xp: Int) {
// Call registered XP update callbacks
xpUpdateCallbacks.forEach { callback ->
callback.onXPUpdate(skillId, xp)
}
@ -223,7 +213,6 @@ class plugin : Plugin() {
}
if (pluginsReloaded) {
// Rebuild the reflective editor UI on the EDT and in one batch
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
@ -237,14 +226,12 @@ class plugin : Plugin() {
accumulatedTime += timeDelta
if (accumulatedTime >= TICK_INTERVAL) {
// Call registered post client tick callbacks
postClientTickCallbacks.forEach { callback ->
callback.onPostClientTick()
}
accumulatedTime = 0L
}
// Call registered draw callbacks
drawCallbacks.forEach { callback ->
callback.onDraw(timeDelta)
}
@ -282,25 +269,22 @@ class plugin : Plugin() {
} else {
moveCanvasToFront()
}
altCanvas?.updateGameImage() // Update the game image as needed
altCanvas?.updateGameImage()
}
override fun Update() {
// Call registered update callbacks
updateCallbacks.forEach { callback ->
callback.onUpdate()
}
}
override fun OnKillingBlowNPC(npcID: Int, x: Int, z: Int) {
// Call registered killing blow NPC callbacks
killingBlowNPCCallbacks.forEach { callback ->
callback.onKillingBlowNPC(npcID, x, z)
}
}
private fun allSpritesLoaded() : Boolean {
// Check all skill sprites
try{
for (i in 0 until 24) {
if(!js5Archive8.isFileReady(getSpriteId(i))){
@ -481,31 +465,27 @@ class plugin : Plugin() {
cardLayout = CardLayout()
mainContentPanel = JPanel(cardLayout).apply {
border = BorderFactory.createEmptyBorder(0, 0, 0, 0) // Removes any default border or padding
border = BorderFactory.createEmptyBorder(0, 0, 0, 0)
background = VIEW_BACKGROUND_COLOR
preferredSize = Dimension(MAIN_CONTENT_WIDTH, frame.height)
isOpaque = true
}
// Register Views
val xpTrackerView = XPTrackerView
val hiscoresView = HiscoresView
val lootTrackerView = LootTrackerView
val reflectiveEditorView = ReflectiveEditorView
// Create views
xpTrackerView.createView()
hiscoresView.createView()
lootTrackerView.createView()
reflectiveEditorView.createView()
// Register views
views.add(xpTrackerView)
views.add(hiscoresView)
views.add(lootTrackerView)
views.add(reflectiveEditorView)
// Register view functions
xpTrackerView.registerFunctions()
hiscoresView.registerFunctions()
lootTrackerView.registerFunctions()
@ -636,7 +616,6 @@ class plugin : Plugin() {
}
}
// ImageCanvas with forced size
val imageCanvas = ImageCanvas(bufferedImageSprite).apply {
background = WIDGET_COLOR
setFixedSize(imageSize)
@ -663,7 +642,6 @@ class plugin : Plugin() {
add(imageCanvasWrapper, gbc)
// Hover and click behavior
val hoverListener = object : MouseAdapter() {
override fun mouseEntered(e: MouseEvent?) {
background = WIDGET_COLOR.darker()

View file

@ -5,15 +5,12 @@ package KondoKit.pluginmanager
* GitLabPluginFetcher and PluginDownloadManager
*/
object GitLabConfig {
// GitLab project information - using the ID from PluginFetcher
const val GITLAB_PROJECT_ID = "38297322"
const val GITLAB_PROJECT_PATH = "2009scape/tools/client-plugins" // Using path from DownloadManager
const val GITLAB_BRANCH = "master"
// Debug settings
const val DEBUG = true // Set to false to disable debug logging
// API endpoints and URL patterns
fun getGitLabApiBaseUrl(): String = "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID"
fun getRepositoryTreeUrl(): String =

View file

@ -13,13 +13,10 @@ import javax.swing.SwingUtilities
object GitLabPluginFetcher {
private const val TAG = "GitLabPluginFetcher"
// Thread pool for concurrent plugin property fetching
private val executorService = Executors.newFixedThreadPool(5)
// Debug logging function
private fun debugLog(message: String) = PluginLogger.debug(TAG, message)
// Function to fetch plugins from GitLab
fun fetchGitLabPlugins(onComplete: (List<GitLabPlugin>) -> Unit) {
Thread {
val plugins = mutableListOf<GitLabPlugin>()
@ -32,12 +29,11 @@ object GitLabPluginFetcher {
debugLog("Response length: ${response.length} characters")
debugLog("Response preview: ${response.take(200)}...")
// Parse JSON response
val treeItems = JsonParser.fromJson(response, Array<JsonObject>::class.java)
debugLog("Parsed ${treeItems.size} items from JSON")
// Filter for directories (trees) with mode "040000"
val pluginDirectories = mutableListOf<Pair<String, String>>()
// Filter for directories (trees) by mode 040000 so we only consider plugin folders
for (jsonObject in treeItems) {
if (jsonObject["type"].asString == "tree" && jsonObject["mode"].asString == "040000") {
val folderId = jsonObject["id"].asString
@ -48,23 +44,21 @@ object GitLabPluginFetcher {
}
debugLog("Found ${pluginDirectories.size} plugin directories")
// Fetch plugin properties in parallel
val pluginFutures = mutableListOf<Future<GitLabPlugin>>()
for ((folderId, folderPath) in pluginDirectories) {
val future = executorService.submit(Callable<GitLabPlugin> {
try {
val pluginProperties = fetchPluginProperties(folderPath)
debugLog("Successfully fetched properties for: $folderPath")
GitLabPlugin(folderId, folderPath, pluginProperties, null)
GitLabPlugin(folderId, folderPath, pluginProperties)
} catch (e: Exception) {
debugLog("Error fetching plugin.properties for $folderPath: ${e.message}")
GitLabPlugin(folderId, folderPath, null, "Error fetching plugin.properties")
GitLabPlugin(folderId, folderPath, null)
}
})
pluginFutures.add(future)
}
// Collect results
for (future in pluginFutures) {
try {
plugins.add(future.get())
@ -80,14 +74,12 @@ object GitLabPluginFetcher {
}
debugLog("Completed fetching plugins. Total plugins: ${plugins.size}")
// Update on UI thread
SwingUtilities.invokeLater {
onComplete(plugins)
}
}.start()
}
// Function to fetch plugin.properties from a specific folder
private fun fetchPluginProperties(folderPath: String): PluginProperties {
val pluginFilePath = "$folderPath/plugin.properties"
val pluginUrl = GitLabConfig.getRawFileUrl(pluginFilePath)
@ -106,7 +98,6 @@ object GitLabPluginFetcher {
}
}
// Function to parse plugin.properties content
private fun parseProperties(content: String): PluginProperties {
debugLog("Parsing plugin.properties content")
val lines = content.split("\n")

View file

@ -1,11 +1,7 @@
package KondoKit.pluginmanager
import KondoKit.pluginmanager.GitLabPlugin
import KondoKit.util.HttpFetcher
import KondoKit.pluginmanager.GitLabConfig
import KondoKit.views.ReflectiveEditorView
import plugin.PluginRepository
import java.awt.EventQueue
import java.io.*
import java.net.HttpURLConnection
import java.net.URL
@ -21,19 +17,15 @@ object PluginDownloadManager {
private const val TAG = "PluginDownloadManager"
private const val MAX_CONCURRENT_DOWNLOADS = 3
// Thread pool for concurrent downloads
private val downloadExecutor = Executors.newFixedThreadPool(MAX_CONCURRENT_DOWNLOADS)
// Callback for download progress updates
interface DownloadProgressCallback {
fun onProgress(pluginName: String, progress: Int)
fun onComplete(pluginName: String, success: Boolean, errorMessage: String? = null)
}
// Debug logging function
private fun debugLog(message: String) = PluginLogger.debug(TAG, message)
// Get download URL for debugging/logging purposes
fun getDownloadUrlForLogging(plugin: GitLabPlugin): String {
return GitLabConfig.getArchiveUrl(plugin.path)
}
@ -47,7 +39,6 @@ object PluginDownloadManager {
debugLog("Starting download for plugin: ${plugin.path}")
callback.onProgress(plugin.path, 0)
// Download the plugin as a ZIP archive
val success = downloadAndExtractPlugin(plugin, callback)
if (success) {
@ -77,11 +68,9 @@ object PluginDownloadManager {
fun downloadPlugins(plugins: List<GitLabPlugin>, callback: (String, Boolean, String?) -> Unit) {
debugLog("Starting concurrent download of ${plugins.size} plugins")
// Submit all downloads to the executor
for (plugin in plugins) {
downloadPlugin(plugin, object : DownloadProgressCallback {
override fun onProgress(pluginName: String, progress: Int) {
// We don't need to do anything here for the simple callback
}
override fun onComplete(pluginName: String, success: Boolean, errorMessage: String?) {
@ -96,7 +85,6 @@ object PluginDownloadManager {
*/
private fun downloadAndExtractPlugin(plugin: GitLabPlugin, callback: DownloadProgressCallback): Boolean {
try {
// Validate plugin path
if (plugin.path.isBlank()) {
debugLog("Plugin path is blank for plugin: ${plugin.path}")
return false
@ -117,7 +105,6 @@ object PluginDownloadManager {
for (downloadUrl in downloadUrls) {
debugLog("Trying download URL: $downloadUrl")
// Validate URL
val url = try {
URL(downloadUrl)
} catch (e: Exception) {
@ -125,7 +112,6 @@ object PluginDownloadManager {
continue
}
// Create URL connection
val connection = try {
HttpFetcher.openGetConnection(url.toString())
} catch (e: Exception) {
@ -137,20 +123,16 @@ object PluginDownloadManager {
debugLog("Request headers - User-Agent: ${connection.getRequestProperty("User-Agent")}")
debugLog("Request headers - Accept: ${connection.getRequestProperty("Accept")}")
// Get content length for progress tracking
val contentLength = connection.contentLength
debugLog("Content length: $contentLength bytes for plugin: ${plugin.path}")
// Read response
val responseCode = connection.responseCode
debugLog("Response code: $responseCode for plugin: ${plugin.path}")
// Log response message
val responseMessage = connection.responseMessage
debugLog("Response message: $responseMessage for plugin: ${plugin.path}")
if (responseCode == HttpURLConnection.HTTP_OK) {
// Check if input stream is available
val inputStream = connection.inputStream
if (inputStream == null) {
debugLog("Input stream is null for plugin: ${plugin.path}")
@ -159,11 +141,9 @@ object PluginDownloadManager {
debugLog("Input stream available for plugin: ${plugin.path}")
// Create output directory
val pluginsDir = File(rt4.GlobalJsonConfig.instance.pluginsFolder)
debugLog("Plugins directory: ${pluginsDir.absolutePath}")
// Validate plugins directory
if (!pluginsDir.exists()) {
debugLog("Plugins directory does not exist: ${pluginsDir.absolutePath}")
if (!pluginsDir.mkdirs()) {
@ -181,13 +161,11 @@ object PluginDownloadManager {
val pluginDir = File(pluginsDir, plugin.path)
debugLog("Plugin directory: ${pluginDir.absolutePath}")
// Create directory if it doesn't exist
if (!pluginDir.exists()) {
pluginDir.mkdirs()
debugLog("Created plugin directory: ${pluginDir.absolutePath}")
}
// Download the ZIP file
val tempZipFile = File.createTempFile("plugin_", ".zip")
tempZipFile.deleteOnExit()
debugLog("Created temp file: ${tempZipFile.absolutePath}")
@ -222,10 +200,8 @@ object PluginDownloadManager {
debugLog("Downloaded ${totalBytesRead} bytes to ${tempZipFile.absolutePath}")
// Extract the ZIP file
if (extractZipFile(tempZipFile, pluginDir, plugin.path)) {
debugLog("Successfully extracted plugin to ${pluginDir.absolutePath}")
// Clean up temp file
tempZipFile.delete()
return true
} else {
@ -270,7 +246,6 @@ object PluginDownloadManager {
try {
debugLog("Extracting ZIP file: ${zipFile.absolutePath} to ${targetDir.absolutePath}")
// Validate inputs
if (!zipFile.exists()) {
debugLog("ZIP file does not exist: ${zipFile.absolutePath}")
return false
@ -291,8 +266,7 @@ object PluginDownloadManager {
debugLog("Processing ZIP entry $entryCount: $entryName")
// Skip the top-level directory in the ZIP (GitLab adds a project-branch-hash directory)
// We want to extract the contents of the plugin directory directly
// Strip the top-level GitLab archive directory so we extract only the plugin contents
val relativePath = if (entryName.contains("/")) {
entryName.substring(entryName.indexOf("/") + 1)
} else {
@ -301,7 +275,6 @@ object PluginDownloadManager {
debugLog("Relative path for entry: $relativePath")
// Only extract files that are part of this specific plugin
if (relativePath.startsWith(pluginPath) && relativePath != pluginPath) {
val fileName = relativePath.substring(pluginPath.length + 1) // +1 for the trailing slash
@ -312,7 +285,6 @@ object PluginDownloadManager {
debugLog("Target file path: ${file.absolutePath}")
// Create parent directories if needed
val parent = file.parentFile
if (parent != null && !parent.exists()) {
if (parent.mkdirs()) {
@ -324,7 +296,6 @@ object PluginDownloadManager {
}
}
// Extract file or directory
if (entry!!.isDirectory) {
if (!file.exists()) {
if (file.mkdirs()) {
@ -336,7 +307,6 @@ object PluginDownloadManager {
debugLog("Directory already exists: ${file.absolutePath}")
}
} else {
// Create file
try {
FileOutputStream(file).use { fos ->
zis.copyTo(fos)

View file

@ -3,8 +3,7 @@ package KondoKit.pluginmanager
data class GitLabPlugin(
val id: String,
val path: String,
val pluginProperties: PluginProperties?,
val pluginError: String?
val pluginProperties: PluginProperties?
)
data class PluginProperties(

View file

@ -1,8 +1,5 @@
package KondoKit.pluginmanager
import KondoKit.pluginmanager.GitLabPlugin
import KondoKit.pluginmanager.PluginInfoWithStatus
import KondoKit.pluginmanager.PluginProperties
import KondoKit.util.FileUtils
import KondoKit.util.Helpers
import KondoKit.views.ReflectiveEditorView
@ -17,22 +14,18 @@ import javax.swing.*
class ReflectiveEditorPlugin : Plugin() {
// Fields that were previously in ReflectiveEditorView
private var gitLabPlugins: List<GitLabPlugin> = listOf()
private var pluginStatuses: List<PluginInfoWithStatus> = listOf()
private var reloadPlugins = false // Flag for scheduled plugin reload to avoid crashes
private val pluginsDirectory: File = File(GlobalJsonConfig.instance.pluginsFolder)
override fun Init() {
// Initialize the plugin, fetch remote plugins
GitLabPluginFetcher.fetchGitLabPlugins { plugins ->
gitLabPlugins = plugins
// Update plugin statuses with comparison between installed and remote plugins
updatePluginStatuses()
}
}
// Plugin management functionality that was in ReflectiveEditorView
fun getGitLabPlugins(): List<GitLabPlugin> = gitLabPlugins
fun getPluginStatuses(): List<PluginInfoWithStatus> = pluginStatuses
@ -41,13 +34,10 @@ class ReflectiveEditorPlugin : Plugin() {
val loadedPluginNames = getLoadedPluginNames()
val statuses = mutableListOf<PluginInfoWithStatus>()
// Get disabled plugin names and their versions
val disabledPluginInfo = getDisabledPluginInfo()
// Handle duplicate plugins (loaded and disabled) - delete the disabled version
handleDuplicatePlugins(loadedPluginNames, disabledPluginInfo)
// Process remote plugins
for (gitLabPlugin in gitLabPlugins) {
val pluginName = gitLabPlugin.path
// Skip KondoKit since it should always be loaded
@ -62,13 +52,11 @@ class ReflectiveEditorPlugin : Plugin() {
val isDisabled = disabledPluginInfo.containsKey(pluginName)
val disabledVersion = disabledPluginInfo[pluginName]
// Check if this plugin is currently being downloaded
val existingStatus = pluginStatuses.find { it.name == pluginName }
val isDownloading = existingStatus?.isDownloading ?: false
val downloadProgress = existingStatus?.downloadProgress ?: 0
if (isLoaded) {
// Plugin is currently loaded
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = remoteVersion, // We don't have the actual installed version, but we know it's loaded
@ -82,10 +70,8 @@ class ReflectiveEditorPlugin : Plugin() {
downloadProgress = downloadProgress
))
} else if (isDisabled) {
// Plugin is disabled, check if versions match
val versionsMatch = disabledVersion != null && disabledVersion == remoteVersion
if (!versionsMatch) {
// Versions don't match, show update option
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = disabledVersion,
@ -99,9 +85,7 @@ class ReflectiveEditorPlugin : Plugin() {
downloadProgress = downloadProgress
))
}
// If versions match, we don't add it to the list since there's no point showing it
} else {
// Plugin is not installed at all
statuses.add(PluginInfoWithStatus(
name = pluginName,
installedVersion = null,
@ -120,12 +104,10 @@ class ReflectiveEditorPlugin : Plugin() {
pluginStatuses = statuses
}
// Helper method to get currently loaded plugin names
private fun getLoadedPluginNames(): Set<String> {
val loadedPluginNames = mutableSetOf<String>()
try {
// Get loaded plugins
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
loadedPluginsField.isAccessible = true
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
@ -141,7 +123,6 @@ class ReflectiveEditorPlugin : Plugin() {
return loadedPluginNames
}
// Helper method to parse plugin.properties content
private fun parsePluginProperties(content: String): PluginProperties {
var author = "Unknown"
var version = "Unknown"
@ -171,7 +152,6 @@ class ReflectiveEditorPlugin : Plugin() {
return PluginProperties(author, version, description)
}
// Helper method to get disabled plugin names and their versions
private fun getDisabledPluginInfo(): Map<String, String> {
val disabledPluginInfo = mutableMapOf<String, String>()
val disabledDir = File(pluginsDirectory, "disabled")
@ -196,7 +176,6 @@ class ReflectiveEditorPlugin : Plugin() {
return disabledPluginInfo
}
// Helper method to handle duplicate plugins (loaded and disabled)
private fun handleDuplicatePlugins(loadedPluginNames: Set<String>, disabledPluginInfo: Map<String, String>) {
var needsReload = false
for (pluginName in loadedPluginNames) {
@ -221,7 +200,6 @@ class ReflectiveEditorPlugin : Plugin() {
}
}
// Helper method to delete a plugin
fun deletePlugin(pluginName: String, isDisabled: Boolean, mainPanel: Component) {
try {
val pluginDir = if (isDisabled) {
@ -231,16 +209,13 @@ class ReflectiveEditorPlugin : Plugin() {
}
if (pluginDir.exists() && pluginDir.isDirectory) {
// Recursively delete the directory
if (FileUtils.deleteRecursively(pluginDir)) {
Helpers.showToast(mainPanel, "Plugin deleted successfully", JOptionPane.INFORMATION_MESSAGE)
// If we deleted a loaded plugin, schedule plugin reload
if (!isDisabled) {
reloadPlugins = true
}
// Refresh the plugin list view
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
@ -256,33 +231,26 @@ class ReflectiveEditorPlugin : Plugin() {
}
}
// Helper function to check if a plugin is the KondoKit plugin
private fun isKondoKit(pluginName: String): Boolean {
return pluginName == "KondoKit"
}
// Enable a plugin by moving it from disabled to main directory
fun enablePlugin(pluginName: String, mainPanel: Component) {
try {
// Source and destination directories
val disabledDir = File(pluginsDirectory, "disabled")
val sourceDir = File(disabledDir, pluginName)
val destDir = File(pluginsDirectory, pluginName)
// Check if source directory exists
if (!sourceDir.exists()) {
Helpers.showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
return
}
// Move the directory
if (sourceDir.renameTo(destDir)) {
Helpers.showToast(mainPanel, "Plugin enabled")
// Schedule plugin reload to avoid crashes
reloadPlugins = true
// Refresh the plugin list view
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
@ -295,26 +263,19 @@ class ReflectiveEditorPlugin : Plugin() {
}
}
// Toggle a plugin's enable/disable state by moving it between directories
fun togglePlugin(plugin: Plugin, pluginInfo: PluginInfo, toggleSwitch: Component, activated: Boolean, mainPanel: Component) {
try {
// Get the plugin directory name from the plugin's class package
val pluginDirName = getPluginDirName(plugin)
// Source and destination directories
val sourceDir = if (activated) {
// Moving from disabled to enabled
File(File(pluginsDirectory, "disabled"), pluginDirName)
} else {
// Moving from enabled to disabled
File(pluginsDirectory, pluginDirName)
}
val destDir = if (activated) {
// Moving to main plugins directory
File(pluginsDirectory, pluginDirName)
} else {
// Moving to disabled directory
val disabledDir = File(pluginsDirectory, "disabled")
if (!disabledDir.exists()) {
disabledDir.mkdirs()
@ -322,10 +283,8 @@ class ReflectiveEditorPlugin : Plugin() {
File(disabledDir, pluginDirName)
}
// Check if source directory exists
if (!sourceDir.exists()) {
Helpers.showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
SwingUtilities.invokeLater {
if (toggleSwitch is JComponent) {
toggleSwitch.repaint()
@ -334,20 +293,17 @@ class ReflectiveEditorPlugin : Plugin() {
return
}
// Move the directory
if (sourceDir.renameTo(destDir)) {
Helpers.showToast(mainPanel, if (activated) "Plugin enabled" else "Plugin disabled")
// Schedule plugin reload to avoid crashes
reloadPlugins = true
// Refresh the plugin list view
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
} else {
Helpers.showToast(mainPanel, "Failed to ${if (activated) "enable" else "disable"} plugin", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
SwingUtilities.invokeLater {
if (toggleSwitch is JComponent) {
toggleSwitch.repaint()
@ -357,7 +313,6 @@ class ReflectiveEditorPlugin : Plugin() {
} catch (e: Exception) {
e.printStackTrace()
Helpers.showToast(mainPanel, "Error toggling plugin: ${e.message}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
SwingUtilities.invokeLater {
if (toggleSwitch is JComponent) {
toggleSwitch.repaint()
@ -366,7 +321,6 @@ class ReflectiveEditorPlugin : Plugin() {
}
}
// Helper function to get plugin directory name from plugin instance
private fun getPluginDirName(plugin: Plugin): String {
// Extract the directory name from the plugin's package
// The package name is typically like "GroundItems.plugin" so we take the first part
@ -374,15 +328,12 @@ class ReflectiveEditorPlugin : Plugin() {
return packageName.substringBefore(".")
}
// Check if a plugin is currently enabled
fun isPluginEnabled(plugin: Plugin, pluginInfo: PluginInfo): Boolean {
// Get the plugin directory name from the plugin's class package
val pluginDirName = getPluginDirName(plugin)
val pluginDir = File(pluginsDirectory, pluginDirName)
return pluginDir.exists() && pluginDir.isDirectory
}
// Method to start downloading a plugin
fun startPluginDownload(pluginStatus: PluginInfoWithStatus, mainPanel: Component) {
val gitLabPlugin = pluginStatus.gitLabPlugin
if (gitLabPlugin == null) {
@ -390,7 +341,6 @@ class ReflectiveEditorPlugin : Plugin() {
return
}
// Update plugin status to show downloading
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginStatus.name) {
it.copy(isDownloading = true, downloadProgress = 0)
@ -400,15 +350,12 @@ class ReflectiveEditorPlugin : Plugin() {
}
pluginStatuses = updatedStatuses
// Refresh UI to show progress bar
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
// Start the download
PluginDownloadManager.downloadPlugin(gitLabPlugin, object : PluginDownloadManager.DownloadProgressCallback {
override fun onProgress(pluginName: String, progress: Int) {
// Update progress in plugin statuses
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginName) {
it.copy(downloadProgress = progress)
@ -418,14 +365,12 @@ class ReflectiveEditorPlugin : Plugin() {
}
pluginStatuses = updatedStatuses
// Refresh UI to show progress
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
}
override fun onComplete(pluginName: String, success: Boolean, errorMessage: String?) {
// Update plugin status
val updatedStatuses = pluginStatuses.map {
if (it.name == pluginName) {
it.copy(isDownloading = false, downloadProgress = if (success) 100 else 0)
@ -435,7 +380,6 @@ class ReflectiveEditorPlugin : Plugin() {
}
pluginStatuses = updatedStatuses
// Show result to user
if (success) {
Helpers.showToast(mainPanel, "Plugin downloaded successfully!", JOptionPane.INFORMATION_MESSAGE)
// Reload plugins to make the newly downloaded plugin available
@ -444,7 +388,6 @@ class ReflectiveEditorPlugin : Plugin() {
Helpers.showToast(mainPanel, "Failed to download plugin: ${errorMessage ?: "Unknown error"}", JOptionPane.ERROR_MESSAGE)
}
// Refresh UI
SwingUtilities.invokeLater {
ReflectiveEditorView.addPlugins(ReflectiveEditorView.panel)
}
@ -452,13 +395,10 @@ class ReflectiveEditorPlugin : Plugin() {
})
}
// Getter for plugin directory - needed for the view
fun getPluginsDirectory(): File = pluginsDirectory
// Getter for reload flag - needed for the view
fun shouldReloadPlugins(): Boolean = reloadPlugins
// Setter for reload flag - needed for the view
fun setReloadPlugins(reload: Boolean) {
reloadPlugins = reload
}

View file

@ -1,6 +1,5 @@
package KondoKit.ui
import java.awt.Color
import java.awt.Dimension
import java.awt.Font
@ -34,12 +33,6 @@ object ViewConstants {
val PROGRESS_BAR_SIZE = Dimension(220, 16)
val TOGGLE_SWITCH_SIZE = Dimension(32, 20)
val COLOR_WHITE = Color.WHITE
val COLOR_BLACK = Color.BLACK
val COLOR_DARK_GRAY = Color.DARK_GRAY
val COLOR_RED = Color.RED
val COLOR_GRAY = Color.GRAY
val SKILL_DISPLAY_ORDER = arrayOf(0, 3, 14, 2, 16, 13, 1, 15, 10, 4, 17, 7, 5, 12, 11, 6, 9, 8, 20, 18, 19, 22, 21, 23)
const val COMBAT_LVL_SPRITE = 168

View file

@ -4,8 +4,6 @@ import KondoKit.util.ImageCanvas
import KondoKit.util.SpriteToBufferedImage
import KondoKit.util.setFixedSize
import KondoKit.plugin.Companion.WIDGET_COLOR
import KondoKit.plugin.Companion.primaryColor
import KondoKit.plugin.Companion.secondaryColor
import plugin.api.API
import java.awt.BorderLayout
import java.awt.Color

View file

@ -22,38 +22,32 @@ class ProgressBar(
override fun paint(g: Graphics) {
super.paint(g)
// Draw the filled part of the progress bar
g.color = barColor
val width = (progress * this.width / 100).toInt()
g.fillRect(0, 0, width, this.height)
// Draw the unfilled part of the progress bar
g.color = PROGRESS_BAR_FILL
g.fillRect(width, 0, this.width - width, this.height)
// Variables for text position
val textY = this.height / 2 + 6
// Draw the current level on the far left
drawTextWithShadow(g, "Lvl. $currentLevel", 5, textY, secondaryColor)
// Draw the percentage in the middle
val percentageText = String.format("%.2f%%", progress)
val percentageWidth = g.fontMetrics.stringWidth(percentageText)
drawTextWithShadow(g, percentageText, (this.width - percentageWidth) / 2, textY, secondaryColor)
// Draw the next level on the far right
val nextLevelText = "Lvl. $nextLevel"
val nextLevelWidth = g.fontMetrics.stringWidth(nextLevelText)
drawTextWithShadow(g, nextLevelText, this.width - nextLevelWidth - 5, textY, secondaryColor)
}
override fun getPreferredSize(): Dimension {
return ViewConstants.PROGRESS_BAR_SIZE // Force the height to 16px, width can be anything appropriate
return ViewConstants.PROGRESS_BAR_SIZE
}
override fun getMinimumSize(): Dimension {
return ViewConstants.PROGRESS_BAR_SIZE // Force the minimum height to 16px, width can be smaller
return ViewConstants.PROGRESS_BAR_SIZE
}
fun updateProgress(newProgress: Double, currentLevel: Int, nextLevel: Int, isVisible : Boolean) {
@ -64,13 +58,10 @@ class ProgressBar(
repaint()
}
// Helper function to draw text with a shadow effect
private fun drawTextWithShadow(g: Graphics, text: String, x: Int, y: Int, textColor: Color) {
// Draw shadow (black text with -1 x and -1 y offset)
g.color = Color(0, 0, 0)
g.drawString(text, x + 1, y + 1)
// Draw actual text on top
g.color = textColor
g.drawString(text, x, y)
}

View file

@ -34,7 +34,7 @@ class ToggleSwitch : JPanel() {
})
cursor = Cursor(Cursor.HAND_CURSOR)
setFixedSize(ViewConstants.TOGGLE_SWITCH_SIZE)
isOpaque = false // Make the panel background transparent
isOpaque = false
}
override fun paint(gr: Graphics) {
@ -48,21 +48,18 @@ class ToggleSwitch : JPanel() {
g?.setRenderingHints(rh)
}
// Clear the buffer with transparent background
g?.color = Color(0, 0, 0, 0)
g?.fillRect(0, 0, width, height)
// Draw the track with circular ends (rounded rectangle)
val trackHeight = height - 2
val trackWidth = width - 2
val trackArc = trackHeight // Makes it fully rounded at the ends
val trackArc = trackHeight
g?.color = if (activated) activeSwitch else switchColor
g?.fillRoundRect(1, 1, trackWidth, trackHeight, trackArc, trackArc)
g?.color = borderColor
g?.drawRoundRect(1, 1, trackWidth, trackHeight, trackArc, trackArc)
// Draw the thumb (circular button)
val thumbDiameter = trackHeight - 4
val thumbX = if (activated) {
width - thumbDiameter - 3 // Right side when activated

View file

@ -1,7 +1,5 @@
package KondoKit.util
import KondoKit.util.HttpFetcher
/**
* Shared loader for GE price JSON from remote or bundled sources.
*/

View file

@ -44,7 +44,6 @@ object HiscoresView : View {
}
override fun registerFunctions() {
// Hiscores functions are handled within the view itself
}
fun createHiscoreSearchView() {
@ -72,7 +71,7 @@ object HiscoresView : View {
widgetHeight = ViewConstants.FILTER_PANEL_SIZE.height,
addDefaultPadding = false
).apply {
layout = GridBagLayout() // This will center the JLabel both vertically and horizontally
layout = GridBagLayout()
background = TOOLTIP_BACKGROUND.darker()
name = "playerNameLabel"
}
@ -214,17 +213,13 @@ object HiscoresView : View {
data class Skill(
val id: String,
val dynamic: String,
val experience: String,
val static: String
)
// Function to search for players in hiscores, extracted from the old inner class
fun searchPlayerForHiscores(username: String, hiscoresPanel: JPanel) {
if(username.isEmpty() || username.length < 3) {
// Skip on no value
return
}
@ -268,12 +263,12 @@ object HiscoresView : View {
private fun updateHiscoresViewStandalone(hiscoresPanel: JPanel, data: HiscoresResponse?, username: String) {
val playerNameLabel = findComponentByNameStandalone(hiscoresPanel, "playerNameLabel") as? JPanel
playerNameLabel?.removeAll() // Clear previous components
playerNameLabel?.removeAll()
var nameLabel = LabelComponent().apply {
updateHtmlText(username, secondaryColor, "", primaryColor)
font = ViewConstants.FONT_ARIAL_BOLD_12
foreground = POPUP_FOREGROUND
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
border = BorderFactory.createEmptyBorder(0, 6, 0, 0)
horizontalAlignment = JLabel.CENTER
}
playerNameLabel?.add(nameLabel)
@ -304,7 +299,7 @@ object HiscoresView : View {
updateHtmlText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor)
font = ViewConstants.FONT_ARIAL_BOLD_12
foreground = POPUP_FOREGROUND
border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding
border = BorderFactory.createEmptyBorder(0, 6, 0, 0)
horizontalAlignment = JLabel.CENTER
}
@ -314,7 +309,6 @@ object HiscoresView : View {
playerNameLabel?.revalidate()
playerNameLabel?.repaint()
// Update skill labels
data.skills.forEachIndexed { index, skill ->
val labelName = "skillLabel_$index"
val numberLabel = findComponentByNameStandalone(hiscoresPanel, labelName) as? JLabel

View file

@ -19,7 +19,6 @@ import KondoKit.ui.components.LabelComponent
import KondoKit.ui.components.WidgetPanel
import KondoKit.plugin.Companion.TITLE_BAR_COLOR
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
import KondoKit.plugin.Companion.WIDGET_COLOR
import KondoKit.plugin.Companion.primaryColor
import KondoKit.plugin.Companion.registerDrawAction
@ -30,7 +29,6 @@ import plugin.api.API
import rt4.*
import java.awt.*
import java.awt.Component
import java.awt.Font
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import java.awt.image.BufferedImage
@ -63,7 +61,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
}
override fun registerFunctions() {
// Register callbacks with the plugin
KondoKit.plugin.registerPostClientTickCallback(this)
KondoKit.plugin.registerKillingBlowNPCCallback(this)
}
@ -184,7 +181,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
?.let { updateItemPanelIcon(it, drop.id, newQuantity) }
?: addNewItemToPanel(lootPanel, drop, newQuantity)
// Recalculate lootPanel size based on the number of unique items.
val totalItems = lootItemPanels[npcName]?.size ?: 0
val rowsNeeded = ceil(totalItems / 6.0).toInt()
val lootPanelHeight = rowsNeeded * (40)
@ -208,7 +204,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
private fun createItemPanel(itemId: Int, quantity: Int): JPanel {
val bufferedImageSprite = getBufferedImageFromSprite(API.GetObjSprite(itemId, quantity, true, 1, 3153952))
// Create the panel for the item
val itemPanel = FixedSizePanel(Dimension(36, 32)).apply {
background = WIDGET_COLOR
@ -217,21 +212,16 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
background = WIDGET_COLOR
}
// Add the imageCanvas to the panel
add(imageCanvas, BorderLayout.CENTER)
// Put the itemId as a property for reference
putClientProperty("itemId", itemId)
// Add mouse listener for custom hover text
imageCanvas.addMouseListener(object : MouseAdapter() {
override fun mouseEntered(e: MouseEvent) {
// Show custom tooltip when the mouse enters the component
showCustomToolTip(e.point, itemId,quantity,imageCanvas)
}
override fun mouseExited(e: MouseEvent) {
// Hide tooltip when mouse exits
hideCustomToolTip()
}
})
@ -240,7 +230,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
return itemPanel
}
// Function to show the custom tooltip
fun showCustomToolTip(location: Point, itemId: Int, quantity: Int, parentComponent: ImageCanvas) {
val itemDef = ObjTypeList.get(itemId)
val gePricePerItem = gePriceMap[itemDef.id.toString()]?.toInt() ?: 0
@ -269,13 +258,11 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
}
}
// Calculate the tooltip location relative to the parent component
val screenLocation = parentComponent.locationOnScreen
customToolTipWindow!!.setLocation(screenLocation.x + location.x, screenLocation.y + location.y + 20)
customToolTipWindow!!.isVisible = true
}
// Function to hide the custom tooltip
fun hideCustomToolTip() {
customToolTipWindow?.isVisible = false
customToolTipWindow = null // Nullify the global instance
@ -427,7 +414,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
labelPanel.add(countLabel, BorderLayout.WEST)
labelPanel.add(valueLabel, BorderLayout.EAST)
// Panel to hold loot items, using GridLayout to manage rows and columns.
val lootPanel = JPanel().apply {
background = WIDGET_COLOR
border = BorderFactory.createLineBorder(WIDGET_COLOR, 5)
@ -446,7 +432,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
}
private fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu {
// Create a popup menu
val popupMenu = PopupMenuComponent()
popupMenu.addMenuItem("Remove") {
lootItemPanels[npcName]?.clear()
@ -457,8 +442,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
if (toRemoveIndex >= 0 && toRemoveIndex < components.size - 1) {
val nextComponent = components[toRemoveIndex + 1]
if (nextComponent is Box.Filler) {
// Nasty way to remove the Box.createVerticalStrut(8) after
// the lootpanel.
parent.remove(nextComponent)
}
}
@ -472,7 +455,6 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac
private fun resetLootTrackerMenu(): JPopupMenu {
// Create a popup menu
val popupMenu = PopupMenuComponent()
popupMenu.addMenuItem("Reset Loot Tracker") {

View file

@ -3,16 +3,11 @@ package KondoKit.views
import KondoKit.pluginmanager.ReflectiveEditorPlugin
import KondoKit.util.attachPopupMenu
import KondoKit.ui.components.*
import KondoKit.pluginmanager.GitLabPlugin
import KondoKit.pluginmanager.PluginInfoWithStatus
import KondoKit.pluginmanager.PluginProperties
import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND
import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR
import KondoKit.plugin.Companion.WIDGET_COLOR
import KondoKit.plugin.Companion.WRENCH_ICON
import KondoKit.plugin.Companion.secondaryColor
import KondoKit.pluginmanager.GitLabPluginFetcher
import KondoKit.pluginmanager.PluginDownloadManager
import KondoKit.util.setFixedSize
import KondoKit.ui.BaseView
import KondoKit.ui.View
@ -22,7 +17,6 @@ import KondoKit.views.ViewLayoutHelpers.createSearchFieldSection
import plugin.Plugin
import plugin.PluginInfo
import plugin.PluginRepository
import rt4.GlobalJsonConfig
import java.awt.*
import java.io.File
import java.util.*
@ -47,18 +41,15 @@ object ReflectiveEditorView : View {
private var searchField: SearchField? = null
// Helper function to check if a plugin is the KondoKit plugin
private fun isKondoKit(pluginName: String): Boolean {
return pluginName == "KondoKit"
}
// Card layout for switching between views within the reflective editor view
private lateinit var cardLayout: CardLayout
private lateinit var mainPanel: JPanel
private var currentPluginInfo: PluginInfo? = null
private var currentPlugin: Plugin? = null
// Search text for filtering plugins
private var pluginSearchText: String = ""
private var searchFieldWrapper: JPanel? = null
private var pluginListContentPanel: JPanel? = null
@ -70,7 +61,6 @@ object ReflectiveEditorView : View {
get() = reflectiveEditorView ?: JPanel()
override fun createView() {
// Initialize the plugin
reflectiveEditorPlugin.Init()
createReflectiveEditorView()
}
@ -98,8 +88,6 @@ object ReflectiveEditorView : View {
reflectiveEditorView = mainPanel
cardLayout.show(mainPanel, PLUGIN_LIST_VIEW)
// Plugin initialization is handled by the plugin class, gitLabPlugins will be fetched automatically
}
private fun createPluginListView(): JPanel {
@ -152,16 +140,14 @@ object ReflectiveEditorView : View {
val contentPanel = pluginListContentPanel ?: return
val treeLock = contentPanel.treeLock
synchronized(treeLock) {
synchronized(treeLock) {
contentPanel.removeAll()
try {
// Get loaded plugins
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
loadedPluginsField.isAccessible = true
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
// Separate plugins with and without exposed attributes
val pluginsWithExposed = mutableListOf<Pair<PluginInfo, Plugin>>()
val pluginsWithoutExposed = mutableListOf<Pair<PluginInfo, Plugin>>()
@ -172,7 +158,6 @@ object ReflectiveEditorView : View {
}
}
// Apply search filter
val pluginName = plugin.javaClass.`package`.name
if (pluginSearchText.isBlank() || pluginName.contains(pluginSearchText, ignoreCase = true)) {
if (exposedFields.isNotEmpty()) {
@ -278,31 +263,26 @@ object ReflectiveEditorView : View {
layout = BorderLayout()
}
// Plugin name and version (using package name as in original implementation)
val packageName = plugin.javaClass.`package`.name
val nameLabel = JLabel(packageName)
nameLabel.foreground = secondaryColor
nameLabel.font = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16
// Check if plugin has exposed attributes
val exposedFields = plugin.javaClass.declaredFields.filter { field ->
field.annotations.any { annotation ->
annotation.annotationClass.simpleName == "Exposed"
}
}
// Edit button (only show if plugin has exposed attributes)
val editButton = exposedFields.takeIf { it.isNotEmpty() }?.let {
UiStyler.button(text = "Edit") {
showPluginDetails(pluginInfo, plugin)
}
}
// Plugin toggle switch (iOS style) - skip for KondoKit since it should always be on
val toggleSwitch = if (!isKondoKit(packageName)) {
createToggleSwitch(plugin, pluginInfo)
} else {
// Create a placeholder component that takes the same space but is invisible
val placeholder = JPanel()
placeholder.background = WIDGET_COLOR
placeholder.setFixedSize(ViewConstants.TOGGLE_PLACEHOLDER_SIZE)
@ -339,25 +319,21 @@ object ReflectiveEditorView : View {
layout = BorderLayout()
}
// Plugin name
val nameLabel = JLabel(pluginName)
nameLabel.foreground = secondaryColor
nameLabel.font = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16
// Plugin toggle switch (iOS style) - initially off for disabled plugins
val toggleSwitch = ToggleSwitch()
toggleSwitch.setActivated(false)
toggleSwitch.onToggleListener = { activated ->
if (activated) {
reflectiveEditorPlugin.enablePlugin(pluginName, mainPanel ?: JPanel())
}
// If trying to disable an already disabled plugin, reset the toggle
else {
toggleSwitch.setActivated(false)
}
}
// Layout
val infoPanel = JPanel(BorderLayout())
infoPanel.background = WIDGET_COLOR
infoPanel.add(nameLabel, BorderLayout.WEST)
@ -369,7 +345,6 @@ object ReflectiveEditorView : View {
panel.add(infoPanel, BorderLayout.CENTER)
panel.add(controlsPanel, BorderLayout.EAST)
// Add right-click context menu (except for KondoKit itself)
if (!isKondoKit(pluginName)) {
val popupMenu = createPluginContextMenu(null, null, pluginName, true)
panel.attachPopupMenu(popupMenu)
@ -383,15 +358,11 @@ object ReflectiveEditorView : View {
layout = BorderLayout()
}
// Plugin name
val nameLabel = JLabel(pluginStatus.name)
nameLabel.foreground = secondaryColor
nameLabel.font = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16
// Action button based on plugin status
val actionButton = UiStyler.button()
// Progress bar for downloads
val progressBar = JProgressBar(0, 100)
progressBar.isStringPainted = true
progressBar.isVisible = false
@ -405,7 +376,6 @@ object ReflectiveEditorView : View {
if (pluginStatus.needsUpdate) {
actionButton.text = "Update"
actionButton.addActionListener {
// Start downloading the plugin to update it
startPluginDownload(pluginStatus)
}
} else {
@ -415,17 +385,14 @@ object ReflectiveEditorView : View {
} else {
actionButton.text = "Download"
actionButton.addActionListener {
// Start downloading the plugin
startPluginDownload(pluginStatus)
}
}
// Plugin toggle switch (iOS style)
val toggleSwitch = ToggleSwitch()
// Skip the toggle switch for KondoKit since it should always be enabled
if (isKondoKit(pluginStatus.name)) {
// Hide the toggle switch for KondoKit
toggleSwitch.isVisible = false
} else {
toggleSwitch.setActivated(pluginStatus.isInstalled && !pluginStatus.needsUpdate)
@ -433,7 +400,6 @@ object ReflectiveEditorView : View {
if (pluginStatus.isInstalled && !pluginStatus.needsUpdate) {
toggleSwitch.onToggleListener = { activated ->
// Find the corresponding loaded plugin to use toggle functionality
val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins")
loadedPluginsField.isAccessible = true
val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *>
@ -452,21 +418,16 @@ object ReflectiveEditorView : View {
if (foundPlugin != null && foundPluginInfo != null) {
reflectiveEditorPlugin.togglePlugin(foundPlugin, foundPluginInfo, toggleSwitch, activated, mainPanel ?: JPanel())
} else {
// Fallback for plugins that don't have loaded instances available
val pluginDirName = pluginStatus.name
val sourceDir = if (activated) {
// Moving from disabled to enabled
File(File(reflectiveEditorPlugin.getPluginsDirectory(), "disabled"), pluginDirName)
} else {
// Moving from enabled to disabled
File(reflectiveEditorPlugin.getPluginsDirectory(), pluginDirName)
}
val destDir = if (activated) {
// Moving to main plugins directory
File(reflectiveEditorPlugin.getPluginsDirectory(), pluginDirName)
} else {
// Moving to disabled directory
val disabledDir = File(reflectiveEditorPlugin.getPluginsDirectory(), "disabled")
if (!disabledDir.exists()) {
disabledDir.mkdirs()
@ -474,33 +435,26 @@ object ReflectiveEditorView : View {
File(disabledDir, pluginDirName)
}
// Check if source directory exists
if (!sourceDir.exists()) {
showToast(mainPanel, "Plugin directory not found: ${sourceDir.absolutePath}", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
toggleSwitch.setActivated(!activated)
} else {
// Move the directory
if (sourceDir.renameTo(destDir)) {
showToast(mainPanel, if (activated) "Plugin enabled" else "Plugin disabled")
// Schedule plugin reload to avoid crashes
reflectiveEditorPlugin.setReloadPlugins(true)
// Refresh the plugin list view
reflectiveEditorPlugin.setReloadPlugins(true) // Schedule plugin reload to avoid crashes
SwingUtilities.invokeLater {
addPlugins(reflectiveEditorView!!)
}
} else {
showToast(mainPanel, "Failed to ${if (activated) "enable" else "disable"} plugin", JOptionPane.ERROR_MESSAGE)
// Reset toggle switch to previous state
toggleSwitch.setActivated(!activated)
toggleSwitch.setActivated(!activated) // Reset toggle to previous state on failure
}
}
}
}
} else {
// Hide the toggle switch for non-installed plugins
toggleSwitch.isVisible = false
}
}
@ -581,11 +535,8 @@ object ReflectiveEditorView : View {
private fun createToggleSwitch(plugin: Plugin, pluginInfo: PluginInfo): ToggleSwitch {
val toggleSwitch = ToggleSwitch()
// Set initial state
toggleSwitch.setActivated(reflectiveEditorPlugin.isPluginEnabled(plugin, pluginInfo))
// Add toggle listener
toggleSwitch.onToggleListener = { activated ->
reflectiveEditorPlugin.togglePlugin(plugin, pluginInfo, toggleSwitch, activated, mainPanel ?: JPanel())
}
@ -594,7 +545,6 @@ object ReflectiveEditorView : View {
}
private fun getPluginDirName(plugin: Plugin): String {
// Extract the directory name from the plugin's package
// The package name is typically like "GroundItems.plugin" so we take the first part
val packageName = plugin.javaClass.`package`.name
return packageName.substringBefore(".")
@ -606,18 +556,15 @@ object ReflectiveEditorView : View {
searchFieldWrapper?.isVisible = false
// Remove the existing detail view if it exists
val existingDetailView = mainPanel.components.find { it.name == PLUGIN_DETAIL_VIEW }
if (existingDetailView != null) {
mainPanel.remove(existingDetailView)
}
// Create a new detail view
val detailView = BaseView(PLUGIN_DETAIL_VIEW)
detailView.layout = BorderLayout()
detailView.background = VIEW_BACKGROUND_COLOR
// Header with back button
val packageName = plugin.javaClass.`package`.name
val headerPanel = ViewHeader("$packageName v${pluginInfo.version}", 40).apply {
@ -625,7 +572,6 @@ object ReflectiveEditorView : View {
}
val backButton = ButtonPanel(FlowLayout.LEFT).addButton("Back") {
// Reset scroll position to top when returning to the list view
SwingUtilities.invokeLater {
// Find the ScrollablePanel in the plugin list view and reset its scroll position
val listView = mainPanel.components.find { it.name == PLUGIN_LIST_VIEW }
@ -640,7 +586,6 @@ object ReflectiveEditorView : View {
headerPanel.add(backButton, BorderLayout.WEST)
// Content panel for settings
val contentPanel = JPanel()
contentPanel.layout = BoxLayout(contentPanel, BoxLayout.Y_AXIS)
contentPanel.background = VIEW_BACKGROUND_COLOR
@ -693,28 +638,22 @@ object ReflectiveEditorView : View {
contentPanel.add(infoPanel)
contentPanel.add(Box.createVerticalStrut(10))
// Add exposed fields
addPluginSettings(contentPanel, plugin)
// Wrap the content panel in a ScrollablePanel for custom scrolling
val scrollablePanel = ScrollablePanel(contentPanel)
// Reset scroll position to top when entering the edit page
SwingUtilities.invokeLater {
resetScrollablePanel(scrollablePanel)
}
detailView.add(headerPanel, BorderLayout.NORTH)
detailView.add(scrollablePanel, BorderLayout.CENTER)
// Add the new detail view to the main panel
mainPanel.add(detailView, PLUGIN_DETAIL_VIEW)
// Revalidate and repaint the main panel
mainPanel.revalidate()
mainPanel.repaint()
// Show the detail view
cardLayout.show(mainPanel, PLUGIN_DETAIL_VIEW)
}
@ -773,7 +712,6 @@ object ReflectiveEditorView : View {
mainPanel.repaint()
}
// Helper method to find and reset ScrollablePanel components within a container
private fun findAndResetScrollablePanel(container: Component) {
if (container is ScrollablePanel) {
resetScrollablePanel(container)
@ -783,17 +721,14 @@ object ReflectiveEditorView : View {
}
}
}
// Method to start downloading a plugin
private fun startPluginDownload(pluginStatus: PluginInfoWithStatus) {
reflectiveEditorPlugin.startPluginDownload(pluginStatus, mainPanel ?: JPanel())
}
// Helper method to create context menu for plugins
private fun createPluginContextMenu(plugin: Plugin?, pluginInfo: PluginInfo?, pluginName: String, isDisabled: Boolean): JPopupMenu {
val popupMenu = PopupMenuComponent()
// Add "Delete" menu item
popupMenu.addMenuItem("Delete Plugin") {
deletePlugin(pluginName, isDisabled)
}
@ -801,15 +736,11 @@ object ReflectiveEditorView : View {
return popupMenu
}
// Helper method to delete a plugin
private fun deletePlugin(pluginName: String, isDisabled: Boolean) {
reflectiveEditorPlugin.deletePlugin(pluginName, isDisabled, mainPanel ?: JPanel())
}
// Helper method to reset a ScrollablePanel to the top
private fun resetScrollablePanel(scrollablePanel: ScrollablePanel) {
// Access the content panel and reset its position
// Since we can't directly access the private fields, we'll use reflection
try {
val contentField = ScrollablePanel::class.java.getDeclaredField("content")
contentField.isAccessible = true

View file

@ -21,9 +21,7 @@ import KondoKit.ui.components.WidgetPanel
import KondoKit.ui.components.XPWidget
import KondoKit.plugin.Companion.IMAGE_SIZE
import KondoKit.plugin.Companion.LVL_ICON
import KondoKit.plugin.Companion.TOTAL_XP_WIDGET_SIZE
import KondoKit.plugin.Companion.WIDGET_COLOR
import KondoKit.plugin.Companion.WIDGET_SIZE
import KondoKit.plugin
import KondoKit.plugin.Companion.playerXPMultiplier
import KondoKit.plugin.Companion.primaryColor
@ -121,7 +119,6 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback {
}
override fun registerFunctions() {
// Register callbacks with the plugin
plugin.registerUpdateCallback(this)
plugin.registerXPUpdateCallback(this)
}
@ -165,7 +162,6 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback {
xpWidget = createXPWidget(skillId, previousXpSnapshot)
xpWidgets[skillId] = xpWidget
// Create new widget
val wrapped = wrappedWidget(xpWidget.container, padding = 0, innerPadding = 6)
val popupMenu = removeXPWidgetMenu(wrapped, skillId)
wrapped.attachPopupMenu(popupMenu, includeChildren = true)
@ -228,7 +224,6 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback {
val formattedXp = formatNumber(xpWidget.totalXpGained)
xpWidget.xpGainedLabel.text = formatHtmlLabelText("XP Gained: ", primaryColor, formattedXp, secondaryColor)
// Update the progress bar with current level, progress, and next level
xpWidget.progressBar.updateProgress(progress, currentLevel, if (currentLevel < 99) currentLevel + 1 else 99, focusedView == VIEW_NAME)
xpWidget.previousXp = xp