From 8ac1bad46f68cf8f9da9e1596b9424dd4a9354bc Mon Sep 17 00:00:00 2001 From: downthecrop Date: Sun, 12 Oct 2025 19:33:25 -0700 Subject: [PATCH] Merge old and new search panels --- .../CustomSearchField.kt | 108 ++--- .../GitLabDataModels.kt | 14 - .../src/main/kotlin/KondoKit/plugin.kt | 8 +- .../KondoKit/pluginmanager/GitLabConfig.kt | 37 ++ .../GitLabDataModels.kt} | 17 +- .../GitLabPluginFetcher.kt | 19 +- .../PluginDownloadManager.kt | 63 +-- .../kotlin/KondoKit/views/HiscoresView.kt | 373 +++++++++--------- .../KondoKit/views/ReflectiveEditorView.kt | 49 ++- 9 files changed, 352 insertions(+), 336 deletions(-) rename plugin-playground/src/main/kotlin/KondoKit/components/{ReflectiveEditorComponents => }/CustomSearchField.kt (61%) delete mode 100644 plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabDataModels.kt create mode 100644 plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabConfig.kt rename plugin-playground/src/main/kotlin/KondoKit/{components/ReflectiveEditorComponents/PluginStatus.kt => pluginmanager/GitLabDataModels.kt} (56%) rename plugin-playground/src/main/kotlin/KondoKit/{components/ReflectiveEditorComponents => pluginmanager}/GitLabPluginFetcher.kt (89%) rename plugin-playground/src/main/kotlin/KondoKit/{components/ReflectiveEditorComponents => pluginmanager}/PluginDownloadManager.kt (84%) diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/CustomSearchField.kt b/plugin-playground/src/main/kotlin/KondoKit/components/CustomSearchField.kt similarity index 61% rename from plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/CustomSearchField.kt rename to plugin-playground/src/main/kotlin/KondoKit/components/CustomSearchField.kt index 155f4c0..e49d18f 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/CustomSearchField.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/components/CustomSearchField.kt @@ -1,54 +1,47 @@ -package KondoKit.components.ReflectiveEditorComponents +package KondoKit.components import KondoKit.ImageCanvas -import KondoKit.plugin +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.ActionListener -import java.awt.event.KeyAdapter -import java.awt.event.KeyEvent -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent -import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite -import java.awt.image.BufferedImage +import java.awt.event.* import javax.swing.* -class CustomSearchField(private val parentPanel: JPanel, private val onSearch: (String) -> Unit) : Canvas() { +class CustomSearchField( + private val parentPanel: JPanel, + private val onSearch: (String) -> Unit, + private val placeholderText: String = "Search plugins...", + private val fieldWidth: Int = 230, + private val fieldHeight: Int = 30, + private val viewName: String? = "REFLECTIVE_EDITOR_VIEW" // Default to the original behavior +) : Canvas() { + private var cursorVisible: Boolean = true private var text: String = "" - private val bufferedImageSprite = getBufferedImageFromSpriteWithScale(API.GetSprite(1423)) // MAG_SPRITE + private val bufferedImageSprite = SpriteToBufferedImage.getBufferedImageFromSprite(API.GetSprite(1423)) // MAG_SPRITE private val imageCanvas = bufferedImageSprite.let { ImageCanvas(it).apply { - preferredSize = Dimension(12, 12) // ICON_DIMENSION_SMALL + preferredSize = Dimension(12, 12) size = preferredSize minimumSize = preferredSize maximumSize = preferredSize fillColor = WIDGET_COLOR } } - - fun setText(newText: String) { - text = newText - SwingUtilities.invokeLater { - repaint() - } - } - - fun getText(): String { - return text - } init { - preferredSize = Dimension(230, 30) // SEARCH_FIELD_DIMENSION - background = WIDGET_COLOR // COLOR_BACKGROUND_DARK - foreground = secondaryColor // COLOR_FOREGROUND_LIGHT - font = Font("Arial", Font.PLAIN, 14) // FONT_ARIAL_PLAIN_14 - minimumSize = preferredSize - maximumSize = preferredSize + 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) { @@ -62,7 +55,7 @@ class CustomSearchField(private val parentPanel: JPanel, private val onSearch: ( text = text.dropLast(1) } } else if (e.keyChar == '\n') { - onSearch(text) + triggerSearch() } else { text += e.keyChar } @@ -70,21 +63,27 @@ class CustomSearchField(private val parentPanel: JPanel, private val onSearch: ( 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 -> { - val clipboard = Toolkit.getDefaultToolkit().systemClipboard - val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String - text += pasteText - SwingUtilities.invokeLater { - repaint() + try { + val clipboard = Toolkit.getDefaultToolkit().systemClipboard + val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String + text += pasteText + SwingUtilities.invokeLater { + repaint() + } + } catch (ex: Exception) { + // Ignore clipboard errors } } } @@ -96,7 +95,6 @@ class CustomSearchField(private val parentPanel: JPanel, private val onSearch: ( override fun mouseClicked(e: MouseEvent) { if (e.x > width - 20 && e.y < 20) { text = "" - onSearch(text) SwingUtilities.invokeLater { repaint() } @@ -104,13 +102,15 @@ class CustomSearchField(private val parentPanel: JPanel, private val onSearch: ( } }) - Timer(500, ActionListener { _ -> + Timer(500) { _ -> cursorVisible = !cursorVisible - if (plugin.StateManager.focusedView == "REFLECTIVE_EDITOR_VIEW") + // Only repaint if the view is active or if viewName is not specified + if (viewName == null || focusedView == viewName) { SwingUtilities.invokeLater { repaint() } - }).start() + } + }.start() } override fun paint(g: Graphics) { @@ -132,9 +132,9 @@ class CustomSearchField(private val parentPanel: JPanel, private val onSearch: ( val currentText = text // Draw placeholder text if field is empty, otherwise draw actual text - if (currentText == "") { + if (currentText.isEmpty()) { g.color = Color.GRAY // Use a lighter color for placeholder text - g.drawString("Search plugins...", 30, 20) + g.drawString(placeholderText, 30, 20) } else { g.color = foreground // Use normal color for actual text g.drawString(currentText, 30, 20) @@ -146,19 +146,27 @@ class CustomSearchField(private val parentPanel: JPanel, private val onSearch: ( } // Only draw the "x" button if there's text - if (currentText != "") { + if (currentText.isNotEmpty()) { g.color = Color.RED g.drawString("x", width - 20, 20) } } - private fun getBufferedImageFromSpriteWithScale(sprite: Any?): BufferedImage { - val image = BufferedImage(12, 12, BufferedImage.TYPE_INT_ARGB) - val g2d = image.createGraphics() - g2d.color = Color.GRAY - g2d.fillOval(2, 2, 8, 8) - g2d.drawLine(8, 8, 11, 11) - g2d.dispose() - return image + 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 + } } } \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabDataModels.kt b/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabDataModels.kt deleted file mode 100644 index 780d6e5..0000000 --- a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabDataModels.kt +++ /dev/null @@ -1,14 +0,0 @@ -package KondoKit.components.ReflectiveEditorComponents - -data class GitLabPlugin( - val id: String, - val path: String, - val pluginProperties: PluginProperties?, - val pluginError: String? -) - -data class PluginProperties( - val author: String, - val version: String, - val description: String -) \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/plugin.kt b/plugin-playground/src/main/kotlin/KondoKit/plugin.kt index 068a7c9..02765ef 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/plugin.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/plugin.kt @@ -406,10 +406,10 @@ class plugin : Plugin() { private fun searchHiscore(username: String): Runnable { return Runnable { setActiveView(HiscoresView.VIEW_NAME) - val customSearchField = HiscoresView.hiScoreView?.let { HiscoresView.CustomSearchField(it) } - - customSearchField?.searchPlayer(username) ?: run { - println("searchView is null or CustomSearchField creation failed.") + HiscoresView.hiScoreView?.let { hiscoresPanel -> + HiscoresView.searchPlayerForHiscores(username, hiscoresPanel) + } ?: run { + println("hiscoresPanel is null") } } } diff --git a/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabConfig.kt b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabConfig.kt new file mode 100644 index 0000000..d1de83a --- /dev/null +++ b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabConfig.kt @@ -0,0 +1,37 @@ +package KondoKit.pluginmanager + +/** + * Configuration object for GitLab API settings that can be shared between + * 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" + + // HTTP headers and client settings + const val CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" + + // 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 = + "${getGitLabApiBaseUrl()}/repository/tree?ref=${GITLAB_BRANCH}&per_page=100" + + fun getRawFileUrl(filePath: String): String = + "${getGitLabApiBaseUrl()}/repository/files/${filePath.replace("/", "%2F")}/raw?ref=${GITLAB_BRANCH}" + + // Archive URL helper + fun getArchiveBaseUrl(): String = + "https://gitlab.com/$GITLAB_PROJECT_PATH/-/archive/$GITLAB_BRANCH/${GITLAB_PROJECT_PATH.replace("/", "-")}-$GITLAB_BRANCH.zip" + + fun getArchiveUrl(pluginPath: String): String = + "${getArchiveBaseUrl()}?path=$pluginPath" + + fun getArchiveUrlWithRefType(pluginPath: String): String = + "${getArchiveBaseUrl()}?path=$pluginPath&ref_type=heads" +} \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginStatus.kt b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabDataModels.kt similarity index 56% rename from plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginStatus.kt rename to plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabDataModels.kt index 2bfabea..22bbd4f 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginStatus.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabDataModels.kt @@ -1,9 +1,22 @@ -package KondoKit.components.ReflectiveEditorComponents +package KondoKit.pluginmanager + +data class GitLabPlugin( + val id: String, + val path: String, + val pluginProperties: PluginProperties?, + val pluginError: String? +) + +data class PluginProperties( + val author: String, + val version: String, + val description: String +) /** * Data class representing a plugin with its installation status and update information */ -data class PluginStatus( +data class PluginInfoWithStatus( val name: String, val installedVersion: String?, val remoteVersion: String?, diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcher.kt b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabPluginFetcher.kt similarity index 89% rename from plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcher.kt rename to plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabPluginFetcher.kt index f9e32b1..df25cd2 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcher.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/GitLabPluginFetcher.kt @@ -1,4 +1,4 @@ -package KondoKit.components.ReflectiveEditorComponents +package KondoKit.pluginmanager import KondoKit.views.ReflectiveEditorView import com.google.gson.Gson @@ -10,19 +10,16 @@ import java.net.HttpURLConnection import java.net.URL import java.util.concurrent.* import javax.swing.SwingUtilities +import KondoKit.pluginmanager.GitLabConfig object GitLabPluginFetcher { - private const val GITLAB_PROJECT_ID = "38297322" - private const val GITLAB_BRANCH = "master" - private const val CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" - private const val DEBUG = true // Set to false to disable debug logging // Thread pool for concurrent plugin property fetching private val executorService = Executors.newFixedThreadPool(5) // Debug logging function private fun debugLog(message: String) { - if (DEBUG) { + if (GitLabConfig.DEBUG) { println("[GitLabPluginFetcher] $message") } } @@ -33,15 +30,15 @@ object GitLabPluginFetcher { try { debugLog("Starting to fetch GitLab plugins...") val plugins = mutableListOf() - val apiUrl = "https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/repository/tree?ref=${GITLAB_BRANCH}&per_page=100" + val apiUrl = GitLabConfig.getRepositoryTreeUrl() debugLog("API URL: $apiUrl") // Create URL connection val url = URL(apiUrl) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" - connection.setRequestProperty("User-Agent", CHROME_USER_AGENT) - debugLog("Set User-Agent header to: $CHROME_USER_AGENT") + connection.setRequestProperty("User-Agent", GitLabConfig.CHROME_USER_AGENT) + debugLog("Set User-Agent header to: ${GitLabConfig.CHROME_USER_AGENT}") // Read response val responseCode = connection.responseCode @@ -119,14 +116,14 @@ object GitLabPluginFetcher { // Function to fetch plugin.properties from a specific folder private fun fetchPluginProperties(folderPath: String): PluginProperties { val pluginFilePath = "$folderPath/plugin.properties" - val pluginUrl = "https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/repository/files/${pluginFilePath.replace("/", "%2F")}/raw?ref=${GITLAB_BRANCH}" + val pluginUrl = GitLabConfig.getRawFileUrl(pluginFilePath) debugLog("Fetching plugin.properties from: $pluginUrl") // Create URL connection val url = URL(pluginUrl) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" - connection.setRequestProperty("User-Agent", CHROME_USER_AGENT) + connection.setRequestProperty("User-Agent", GitLabConfig.CHROME_USER_AGENT) // Read response val responseCode = connection.responseCode diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginDownloadManager.kt b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/PluginDownloadManager.kt similarity index 84% rename from plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginDownloadManager.kt rename to plugin-playground/src/main/kotlin/KondoKit/pluginmanager/PluginDownloadManager.kt index d5c5afb..1c0618f 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginDownloadManager.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/pluginmanager/PluginDownloadManager.kt @@ -1,4 +1,4 @@ -package KondoKit.components.ReflectiveEditorComponents +package KondoKit.pluginmanager import KondoKit.views.ReflectiveEditorView import plugin.PluginRepository @@ -10,16 +10,13 @@ import java.util.concurrent.* import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import javax.swing.SwingUtilities +import KondoKit.pluginmanager.GitLabConfig /** * Manages downloading and installing plugins from GitLab with concurrent support */ object PluginDownloadManager { - private const val GITLAB_PROJECT_PATH = "2009scape/tools/client-plugins" - private const val GITLAB_BRANCH = "master" - private const val CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" private const val MAX_CONCURRENT_DOWNLOADS = 3 - private const val DEBUG = true // Set to false to disable debug logging // Thread pool for concurrent downloads private val downloadExecutor = Executors.newFixedThreadPool(MAX_CONCURRENT_DOWNLOADS) @@ -32,54 +29,14 @@ object PluginDownloadManager { // Debug logging function private fun debugLog(message: String) { - if (DEBUG) { + if (GitLabConfig.DEBUG) { println("[PluginDownloadManager] $message") } } - // Test URL method for debugging - fun testDownloadUrl(plugin: GitLabPlugin): String { - return "https://gitlab.com/$GITLAB_PROJECT_PATH/-/archive/$GITLAB_BRANCH/${GITLAB_PROJECT_PATH.replace("/", "-")}-$GITLAB_BRANCH.zip?path=${plugin.path}" - } - - /** - * Download a single plugin - */ - fun downloadPlugin(plugin: GitLabPlugin, callback: (String, Boolean, String?) -> Unit) { - downloadExecutor.submit { - try { - debugLog("Starting download for plugin: ${plugin.path}") - - // Download the plugin as a ZIP archive - val success = downloadAndExtractPlugin(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?) { - callback(pluginName, success, errorMessage) - } - }) - - if (success) { - debugLog("Successfully downloaded and extracted plugin: ${plugin.path}") - SwingUtilities.invokeLater { - callback(plugin.path, true, null) - } - } else { - debugLog("Failed to download plugin: ${plugin.path}") - SwingUtilities.invokeLater { - callback(plugin.path, false, "Failed to download plugin") - } - } - } catch (e: Exception) { - debugLog("Exception during download of ${plugin.path}: ${e.message}") - e.printStackTrace() - SwingUtilities.invokeLater { - callback(plugin.path, false, e.message) - } - } - } + // Get download URL for debugging/logging purposes + fun getDownloadUrlForLogging(plugin: GitLabPlugin): String { + return GitLabConfig.getArchiveUrl(plugin.path) } /** @@ -151,11 +108,11 @@ object PluginDownloadManager { // Try multiple URL formats since GitLab has changed their API val downloadUrls = listOf( // Format 1: Using ref_type parameter - "https://gitlab.com/$GITLAB_PROJECT_PATH/-/archive/$GITLAB_BRANCH/${GITLAB_PROJECT_PATH.replace("/", "-")}-$GITLAB_BRANCH.zip?path=${plugin.path}&ref_type=heads", + GitLabConfig.getArchiveUrlWithRefType(plugin.path), // Format 2: Using ref parameter - "https://gitlab.com/$GITLAB_PROJECT_PATH/-/archive/$GITLAB_BRANCH/${GITLAB_PROJECT_PATH.replace("/", "-")}-$GITLAB_BRANCH.zip?path=${plugin.path}", + GitLabConfig.getArchiveUrl(plugin.path), // Format 3: Direct archive URL without path parameter (we'll filter later) - "https://gitlab.com/$GITLAB_PROJECT_PATH/-/archive/$GITLAB_BRANCH/${GITLAB_PROJECT_PATH.replace("/", "-")}-$GITLAB_BRANCH.zip" + GitLabConfig.getArchiveBaseUrl() ) for (downloadUrl in downloadUrls) { @@ -172,7 +129,7 @@ object PluginDownloadManager { // Create URL connection val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" - connection.setRequestProperty("User-Agent", CHROME_USER_AGENT) + connection.setRequestProperty("User-Agent", GitLabConfig.CHROME_USER_AGENT) // Add Accept header to avoid 406 errors connection.setRequestProperty("Accept", "*/*") diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt index a482900..c237446 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt @@ -4,6 +4,7 @@ import KondoKit.Helpers.getSpriteId import KondoKit.Helpers.showToast import KondoKit.ImageCanvas import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite +import KondoKit.components.CustomSearchField import KondoKit.components.LabelComponent import KondoKit.components.SearchField import KondoKit.plugin.Companion.WIDGET_COLOR @@ -12,6 +13,8 @@ import KondoKit.plugin.Companion.POPUP_FOREGROUND import KondoKit.plugin.Companion.secondaryColor import KondoKit.plugin.Companion.primaryColor import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND +import KondoKit.views.HiscoresView.VIEW_NAME +import KondoKit.views.HiscoresView.hiScoreView import com.google.gson.Gson import plugin.api.API import rt4.Sprites @@ -63,7 +66,7 @@ object HiscoresView : View { var hiScoreView: JPanel? = null override val name: String = VIEW_NAME override val iconSpriteId: Int = Constants.MAG_SPRITE - + override val panel: JPanel get() = hiScoreView ?: JPanel() @@ -75,191 +78,22 @@ object HiscoresView : View { // Hiscores functions are handled within the view itself } - class CustomSearchField(private val hiscoresPanel: JPanel) : SearchField( - onSearch = { _ -> }, // Placeholder, will be replaced - viewName = VIEW_NAME - ) { - private val gson = Gson() - - init { - // This is a workaround to set the onSearch callback after the class is fully initialized - val onSearchField = javaClass.superclass.getDeclaredField("onSearch") - onSearchField.isAccessible = true - onSearchField.set(this, { username: String -> searchPlayer(username) }) - } - - fun searchPlayer(username: String) { - val cleanUsername = username.replace(" ", "_") - setText(cleanUsername) - val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${cleanUsername.toLowerCase()}" - - updateHiscoresView(null, "Searching...") - - Thread { - try { - val url = URL(apiUrl) - val connection = url.openConnection() as HttpURLConnection - connection.requestMethod = "GET" - - // If a request take longer than 5 seconds timeout. - connection.connectTimeout = 5000 - connection.readTimeout = 5000 - - val responseCode = connection.responseCode - if (responseCode == HttpURLConnection.HTTP_OK) { - val reader = BufferedReader(InputStreamReader(connection.inputStream)) - val response = reader.use { it.readText() } - reader.close() - - SwingUtilities.invokeLater { - updatePlayerData(response, username) - } - } else { - SwingUtilities.invokeLater { - showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE) - } - } - } catch (e: SocketTimeoutException) { - SwingUtilities.invokeLater { - showToast(hiscoresPanel, "Request timed out", JOptionPane.ERROR_MESSAGE) - } - } catch (e: Exception) { - // Handle other errors - SwingUtilities.invokeLater { - showToast(hiscoresPanel, "Error fetching data!", JOptionPane.ERROR_MESSAGE) - } - } - }.start() - } - - private fun updatePlayerData(jsonResponse: String, username: String) { - val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java) - updateHiscoresView(hiscoresResponse, username) - } - - private fun updateHiscoresView(data: HiscoresResponse?, username: String) { - val playerNameLabel = findComponentByName(hiscoresPanel, "playerNameLabel") as? JPanel - playerNameLabel?.removeAll() // Clear previous components - var nameLabel = LabelComponent().apply { - updateHtmlText(username, secondaryColor, "", primaryColor) - font = Constants.FONT_ARIAL_BOLD_12 - foreground = Constants.COLOR_FOREGROUND_LIGHT - border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding - horizontalAlignment = JLabel.CENTER - } - playerNameLabel?.add(nameLabel) - playerNameLabel?.revalidate() - playerNameLabel?.repaint() - - if(data == null) return - - playerNameLabel?.removeAll() - - val ironMode = data.info.iron_mode - - if (ironMode != "0") { - val ironmanBufferedImage = getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1]) - val imageCanvas = ironmanBufferedImage.let { - ImageCanvas(it).apply { - preferredSize = Constants.IMAGE_CANVAS_DIMENSION - size = Constants.IMAGE_CANVAS_DIMENSION - } - } - - playerNameLabel?.add(imageCanvas) - } - - val exp_multiplier = data.info.exp_multiplier - nameLabel = LabelComponent().apply { - updateHtmlText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor) - font = Constants.FONT_ARIAL_BOLD_12 - foreground = Constants.COLOR_FOREGROUND_LIGHT - border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding - horizontalAlignment = JLabel.CENTER - } - - - playerNameLabel?.add(nameLabel) - - playerNameLabel?.revalidate() - playerNameLabel?.repaint() - - // Update skill labels - data.skills.forEachIndexed { index, skill -> - val labelName = "skillLabel_$index" - val numberLabel = findComponentByName(hiscoresPanel, labelName) as? JLabel - numberLabel?.text = skill.static - } - - updateTotalAndCombatLevel(data.skills, true) - - hiscoresPanel.revalidate() - hiscoresPanel.repaint() - } - - private fun updateTotalAndCombatLevel(skills: List, isMemberWorld: Boolean) { - val totalLevel = skills.sumBy { it.static.toInt() } - val totalLevelLabel = findComponentByName(hiscoresPanel, "totalLevelLabel") as? JLabel - totalLevelLabel?.text = totalLevel.toString() - - val attack = skills.find { it.id == "0" }?.static?.toInt() ?: 1 - val defence = skills.find { it.id == "1" }?.static?.toInt() ?: 1 - val strength = skills.find { it.id == "2" }?.static?.toInt() ?: 1 - val hitpoints = skills.find { it.id == "3" }?.static?.toInt() ?: 1 - val ranged = skills.find { it.id == "4" }?.static?.toInt() ?: 1 - val prayer = skills.find { it.id == "5" }?.static?.toInt() ?: 1 - val magic = skills.find { it.id == "6" }?.static?.toInt() ?: 1 - val summoning = skills.find { it.id == "23" }?.static?.toInt() ?: 1 - - val combatLevel = calculateCombatLevel(attack, defence, strength, hitpoints, prayer, ranged, magic, summoning, true) - val combatLevelLabel = findComponentByName(hiscoresPanel, "combatLevelLabel") as? JLabel - combatLevelLabel?.text = combatLevel.toString() - } - - private fun calculateCombatLevel( - attack: Int, - defence: Int, - strength: Int, - hitpoints: Int, - prayer: Int, - ranged: Int, - magic: Int, - summoning: Int, - isMemberWorld: Boolean - ): Double { - val base = (defence + hitpoints + floor(prayer.toDouble() / 2)) * 0.25 - val melee = (attack + strength) * 0.325 - val range = floor(ranged * 1.5) * 0.325 - val mage = floor(magic * 1.5) * 0.325 - val maxCombatType = maxOf(melee, range, mage) - - val summoningFactor = if (isMemberWorld) floor(summoning.toDouble() / 8) else 0.0 - return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0 - } - - private fun findComponentByName(container: Container, name: String): Component? { - for (component in container.components) { - if (name == component.name) { - return component - } - if (component is Container) { - val child = findComponentByName(component, name) - if (child != null) { - return child - } - } - } - return null - } - } - fun createHiscoreSearchView() { val hiscorePanel = BaseView(VIEW_NAME, Constants.HISCORE_PANEL_DIMENSION.width).apply { background = Constants.COLOR_BACKGROUND_MEDIUM setViewSize(Constants.HISCORE_PANEL_DIMENSION.height) } - val customSearchField = CustomSearchField(hiscorePanel) + val customSearchField = CustomSearchField( + hiscorePanel, + { username -> + searchPlayerForHiscores(username, hiscorePanel) + }, + "Search player...", + 230, + 30, + VIEW_NAME + ) val searchFieldWrapper = JPanel().apply { layout = BoxLayout(this, BoxLayout.X_AXIS) @@ -415,4 +249,181 @@ object HiscoresView : View { val experience: String, val static: String ) -} \ No newline at end of file + + + // Function to search for players in hiscores, extracted from the old inner class + fun searchPlayerForHiscores(username: String, hiscoresPanel: JPanel) { + val cleanUsername = username.replace(" ", "_") + // Note: We can't call setText on the CustomSearchField because it now doesn't have that method + // The text is already set when the user presses enter in the search field + val apiUrl = "http://api.2009scape.org:3000/hiscores/playerSkills/1/${cleanUsername.toLowerCase()}" + + // Find the updateHiscoresView function or update the display + // For now, we'll just call the main updateHiscoresView method if it's accessible + updateHiscoresViewStandalone(hiscoresPanel, null, "Searching...") + + Thread { + try { + val url = URL(apiUrl) + val connection = url.openConnection() as HttpURLConnection + connection.requestMethod = "GET" + + // If a request take longer than 5 seconds timeout. + connection.connectTimeout = 5000 + connection.readTimeout = 5000 + + val responseCode = connection.responseCode + if (responseCode == HttpURLConnection.HTTP_OK) { + val reader = BufferedReader(InputStreamReader(connection.inputStream)) + val response = reader.use { it.readText() } + reader.close() + + SwingUtilities.invokeLater { + updatePlayerDataStandalone(response, username, hiscoresPanel) + } + } else { + SwingUtilities.invokeLater { + showToast(hiscoresPanel, "Player not found!", JOptionPane.ERROR_MESSAGE) + } + } + } catch (e: SocketTimeoutException) { + SwingUtilities.invokeLater { + showToast(hiscoresPanel, "Request timed out", JOptionPane.ERROR_MESSAGE) + } + } catch (e: Exception) { + // Handle other errors + SwingUtilities.invokeLater { + showToast(hiscoresPanel, "Error fetching data!", JOptionPane.ERROR_MESSAGE) + } + } + }.start() + } + + private fun updatePlayerDataStandalone(jsonResponse: String, username: String, hiscoresPanel: JPanel) { + val gson = Gson() + val hiscoresResponse = gson.fromJson(jsonResponse, HiscoresResponse::class.java) + updateHiscoresViewStandalone(hiscoresPanel, hiscoresResponse, username) + } + + private fun updateHiscoresViewStandalone(hiscoresPanel: JPanel, data: HiscoresResponse?, username: String) { + val playerNameLabel = findComponentByNameStandalone(hiscoresPanel, "playerNameLabel") as? JPanel + playerNameLabel?.removeAll() // Clear previous components + var nameLabel = LabelComponent().apply { + updateHtmlText(username, secondaryColor, "", primaryColor) + font = Constants.FONT_ARIAL_BOLD_12 + foreground = Constants.COLOR_FOREGROUND_LIGHT + border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding + horizontalAlignment = JLabel.CENTER + } + playerNameLabel?.add(nameLabel) + playerNameLabel?.revalidate() + playerNameLabel?.repaint() + + if (data == null) return + + playerNameLabel?.removeAll() + + val ironMode = data.info.iron_mode + + if (ironMode != "0") { + val ironmanBufferedImage = + getBufferedImageFromSprite(Sprites.nameIcons[Constants.IRONMAN_SPRITE + ironMode.toInt() - 1]) + val imageCanvas = ironmanBufferedImage.let { + ImageCanvas(it).apply { + preferredSize = Constants.IMAGE_CANVAS_DIMENSION + size = Constants.IMAGE_CANVAS_DIMENSION + } + } + + playerNameLabel?.add(imageCanvas) + } + + val exp_multiplier = data.info.exp_multiplier + nameLabel = LabelComponent().apply { + updateHtmlText(username, secondaryColor, " (${exp_multiplier}x)", primaryColor) + font = Constants.FONT_ARIAL_BOLD_12 + foreground = Constants.COLOR_FOREGROUND_LIGHT + border = BorderFactory.createEmptyBorder(0, 6, 0, 0) // Top, Left, Bottom, Right padding + horizontalAlignment = JLabel.CENTER + } + + + playerNameLabel?.add(nameLabel) + + playerNameLabel?.revalidate() + playerNameLabel?.repaint() + + // Update skill labels + data.skills.forEachIndexed { index, skill -> + val labelName = "skillLabel_$index" + val numberLabel = findComponentByNameStandalone(hiscoresPanel, labelName) as? JLabel + numberLabel?.text = skill.static.toInt().toString() + } + + updateTotalAndCombatLevelStandalone(data.skills, hiscoresPanel, true) + + hiscoresPanel.revalidate() + hiscoresPanel.repaint() + } + + private fun updateTotalAndCombatLevelStandalone( + skills: List, + hiscoresPanel: JPanel, + isMemberWorld: Boolean + ) { + val totalLevel = skills.sumBy { it.static.toInt() } + val totalLevelLabel = findComponentByNameStandalone(hiscoresPanel, "totalLevelLabel") as? JLabel + totalLevelLabel?.text = totalLevel.toString() + + val attack = skills.find { it.id == "0" }?.static?.toInt() ?: 1 + val defence = skills.find { it.id == "1" }?.static?.toInt() ?: 1 + val strength = skills.find { it.id == "2" }?.static?.toInt() ?: 1 + val hitpoints = skills.find { it.id == "3" }?.static?.toInt() ?: 1 + val ranged = skills.find { it.id == "4" }?.static?.toInt() ?: 1 + val prayer = skills.find { it.id == "5" }?.static?.toInt() ?: 1 + val magic = skills.find { it.id == "6" }?.static?.toInt() ?: 1 + val summoning = skills.find { it.id == "23" }?.static?.toInt() ?: 1 + + val combatLevel = + calculateCombatLevel(attack, defence, strength, hitpoints, prayer, ranged, magic, summoning, true) + val combatLevelLabel = findComponentByNameStandalone(hiscoresPanel, "combatLevelLabel") as? JLabel + combatLevelLabel?.text = combatLevel.toString() + } + + private fun findComponentByNameStandalone(container: Container, name: String): Component? { + for (component in container.components) { + if (name == component.name) { + return component + } + if (component is Container) { + val child = findComponentByNameStandalone(component, name) + if (child != null) { + return child + } + } + } + return null + } + + private fun calculateCombatLevel( + attack: Int, + defence: Int, + strength: Int, + hitpoints: Int, + prayer: Int, + ranged: Int, + magic: Int, + summoning: Int, + isMemberWorld: Boolean + ): Double { + val base = (defence + hitpoints + floor(prayer.toDouble() / 2)) * 0.25 + val melee = (attack + strength) * 0.325 + val range = floor(ranged * 1.5) * 0.325 + val mage = floor(magic * 1.5) * 0.325 + val maxCombatType = maxOf(melee, range, mage) + + val summoningFactor = if (isMemberWorld) floor(summoning.toDouble() / 8) else 0.0 + return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0 + } +} + diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt index 92f9229..acd33c8 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt @@ -2,12 +2,12 @@ package KondoKit.views import KondoKit.Helpers import KondoKit.components.* -import KondoKit.components.ReflectiveEditorComponents.CustomSearchField -import KondoKit.components.ReflectiveEditorComponents.GitLabPlugin -import KondoKit.components.ReflectiveEditorComponents.GitLabPluginFetcher -import KondoKit.components.ReflectiveEditorComponents.PluginDownloadManager -import KondoKit.components.ReflectiveEditorComponents.PluginProperties -import KondoKit.components.ReflectiveEditorComponents.PluginStatus +import KondoKit.components.CustomSearchField +import KondoKit.pluginmanager.GitLabPlugin +import KondoKit.pluginmanager.GitLabPluginFetcher +import KondoKit.pluginmanager.PluginDownloadManager +import KondoKit.pluginmanager.PluginProperties +import KondoKit.pluginmanager.PluginInfoWithStatus import KondoKit.Helpers.showToast import KondoKit.plugin.Companion.TITLE_BAR_COLOR import KondoKit.plugin.Companion.TOOLTIP_BACKGROUND @@ -47,7 +47,7 @@ object ReflectiveEditorView : View { private var gitLabPlugins: List = listOf() - private var pluginStatuses: List = listOf() + private var pluginStatuses: List = listOf() private var cogIcon: Icon? = null @@ -133,12 +133,19 @@ object ReflectiveEditorView : View { panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS) panel.background = VIEW_BACKGROUND_COLOR - val searchField = CustomSearchField(panel) { searchText -> - pluginSearchText = searchText - SwingUtilities.invokeLater { - addPlugins(reflectiveEditorView!!) - } - } + val searchField = CustomSearchField( + parentPanel = panel, + onSearch = { searchText -> + pluginSearchText = searchText + SwingUtilities.invokeLater { + addPlugins(reflectiveEditorView!!) + } + }, + placeholderText = "Search plugins...", + fieldWidth = 230, + fieldHeight = 30, + viewName = "REFLECTIVE_EDITOR_VIEW" + ) this.searchField = searchField if (pluginSearchText.isNotBlank()) { searchField.setText(pluginSearchText) @@ -433,7 +440,7 @@ object ReflectiveEditorView : View { return panel } - private fun createPluginStatusItemPanel(pluginStatus: PluginStatus): JPanel { + private fun createPluginStatusItemPanel(pluginStatus: PluginInfoWithStatus): JPanel { val panel = JPanel(BorderLayout()) panel.background = WIDGET_COLOR panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) @@ -948,7 +955,7 @@ object ReflectiveEditorView : View { } // Method to start downloading a plugin - private fun startPluginDownload(pluginStatus: PluginStatus) { + private fun startPluginDownload(pluginStatus: PluginInfoWithStatus) { val gitLabPlugin = pluginStatus.gitLabPlugin if (gitLabPlugin == null) { showToast(mainPanel, "Plugin information not available", JOptionPane.ERROR_MESSAGE) @@ -956,7 +963,7 @@ object ReflectiveEditorView : View { } // Log the download URL for debugging - val downloadUrl = PluginDownloadManager.testDownloadUrl(gitLabPlugin) + val downloadUrl = PluginDownloadManager.getDownloadUrlForLogging(gitLabPlugin) System.out.println("Download URL for plugin ${gitLabPlugin.path}: $downloadUrl") // Update plugin status to show downloading @@ -1022,7 +1029,7 @@ object ReflectiveEditorView : View { } // Method to start downloading multiple plugins - private fun startMultiplePluginDownloads(pluginStatuses: List) { + private fun startMultiplePluginDownloads(pluginStatuses: List) { val gitLabPlugins = pluginStatuses.mapNotNull { it.gitLabPlugin } if (gitLabPlugins.isEmpty()) { @@ -1092,7 +1099,7 @@ object ReflectiveEditorView : View { // Update plugin statuses by comparing installed and remote plugins private fun updatePluginStatuses() { val loadedPluginNames = getLoadedPluginNames() - val statuses = mutableListOf() + val statuses = mutableListOf() System.out.println("Updating plugin statuses. Loaded plugins: ${loadedPluginNames.joinToString(", ")}") @@ -1126,7 +1133,7 @@ object ReflectiveEditorView : View { if (isLoaded) { // Plugin is currently loaded - statuses.add(PluginStatus( + statuses.add(PluginInfoWithStatus( name = pluginName, installedVersion = remoteVersion, // We don't have the actual installed version, but we know it's loaded remoteVersion = remoteVersion, @@ -1143,7 +1150,7 @@ object ReflectiveEditorView : View { val versionsMatch = disabledVersion != null && disabledVersion == remoteVersion if (!versionsMatch) { // Versions don't match, show update option - statuses.add(PluginStatus( + statuses.add(PluginInfoWithStatus( name = pluginName, installedVersion = disabledVersion, remoteVersion = remoteVersion, @@ -1159,7 +1166,7 @@ object ReflectiveEditorView : View { // 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(PluginStatus( + statuses.add(PluginInfoWithStatus( name = pluginName, installedVersion = null, remoteVersion = remoteVersion,