diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt index d4a7068..0d652de 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt @@ -6,7 +6,6 @@ import KondoKit.Helpers.formatHtmlLabelText import KondoKit.Helpers.formatNumber import KondoKit.Helpers.getProgressBarColor import KondoKit.Helpers.getSpriteId -import KondoKit.ImageCanvas import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite import KondoKit.XPTable import KondoKit.components.PopupMenuComponent @@ -26,9 +25,11 @@ import KondoKit.plugin.Companion.secondaryColor import KondoKit.plugin.StateManager.focusedView import plugin.api.API import java.awt.* +import java.awt.image.BufferedImage import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.* +import javax.swing.SwingConstants object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { private val COMBAT_SKILLS = intArrayOf(0,1,2,3,4) @@ -39,7 +40,7 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { const val VIEW_NAME = "XP_TRACKER_VIEW" override val name: String = VIEW_NAME override val iconSpriteId: Int = LVL_ICON - private val skillIconCache: MutableMap = HashMap() + private val skillIconCache: MutableMap = HashMap() val npcHitpointsMap: Map = try { @@ -63,6 +64,84 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { emptyMap() } + private val widgetFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + + private fun createPopupListener(popupMenu: JPopupMenu) = object : MouseAdapter() { + override fun mousePressed(e: MouseEvent) { + if (e.isPopupTrigger) popupMenu.show(e.component, e.x, e.y) + } + + override fun mouseReleased(e: MouseEvent) { + if (e.isPopupTrigger) popupMenu.show(e.component, e.x, e.y) + } + } + + private fun attachPopup(component: Container, popupMenu: JPopupMenu) { + val listener = createPopupListener(popupMenu) + addMouseListenerToAll(component, listener) + component.addMouseListener(listener) + } + + private fun BufferedImage.ensureOpaque(): BufferedImage { + for (y in 0 until height) { + for (x in 0 until width) { + val color = getRGB(x, y) + if (color != 0) { + setRGB(x, y, color or (0xFF shl 24)) + } + } + } + return this + } + + private fun createIconContainer(image: BufferedImage): JPanel { + val processed = image.ensureOpaque() + val icon = ImageIcon(processed) + val label = JLabel(icon).apply { + horizontalAlignment = SwingConstants.CENTER + verticalAlignment = SwingConstants.CENTER + isOpaque = false + } + + return JPanel(BorderLayout()).apply { + background = WIDGET_COLOR + val wrapperSize = Dimension(IMAGE_SIZE) + preferredSize = wrapperSize + minimumSize = wrapperSize + maximumSize = wrapperSize + add(label, BorderLayout.CENTER) + } + } + + private fun createMetricLabel(title: String, initialValue: String = "0", topPadding: Int = 0): JLabel { + return JLabel(formatHtmlLabelText("$title ", primaryColor, initialValue, secondaryColor)).apply { + horizontalAlignment = JLabel.LEFT + font = widgetFont + if (topPadding > 0) { + border = BorderFactory.createEmptyBorder(topPadding, 0, 0, 0) + } else { + border = BorderFactory.createEmptyBorder(0, 0, 0, 0) + } + } + } + + private fun createMenuItem(text: String): JMenuItem = JMenuItem(text).apply { + font = widgetFont + background = POPUP_BACKGROUND + foreground = POPUP_FOREGROUND + } + + private fun getSkillIcon(skillId: Int): BufferedImage { + return skillIconCache[skillId] ?: getBufferedImageFromSprite(API.GetSprite(getSpriteId(skillId))).also { + skillIconCache[skillId] = it + } + } + + private fun createTotalWidgetContainer(popupMenu: JPopupMenu): Container { + totalXPWidget = createTotalXPWidget() + return wrappedWidget(totalXPWidget!!.container).also { attachPopup(it, popupMenu) } + } + override val panel: JPanel get() = xpTrackerView ?: JPanel() @@ -116,18 +195,8 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { xpWidgets[skillId] = xpWidget val wrapped = wrappedWidget(xpWidget.container) - // Attach per-widget remove menu val popupMenu = removeXPWidgetMenu(wrapped, skillId) - val rightClickListener = object : MouseAdapter() { - override fun mousePressed(e: MouseEvent) { - if (e.isPopupTrigger) popupMenu.show(e.component, e.x, e.y) - } - override fun mouseReleased(e: MouseEvent) { - if (e.isPopupTrigger) popupMenu.show(e.component, e.x, e.y) - } - } - addMouseListenerToAll(wrapped, rightClickListener) - wrapped.addMouseListener(rightClickListener) + attachPopup(wrapped, popupMenu) xpTrackerView?.add(wrapped) xpTrackerView?.add(Box.createVerticalStrut(5)) @@ -206,146 +275,63 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { } - fun resetXPTracker(xpTrackerView : JPanel){ - - // Redo logic here + fun resetXPTracker(xpTrackerView: JPanel) { xpTrackerView.removeAll() val popupMenu = createResetMenu() - // Create a custom MouseListener - val rightClickListener = object : MouseAdapter() { - override fun mousePressed(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - - override fun mouseReleased(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - } - - // Create the XP widget - totalXPWidget = createTotalXPWidget() - val wrapped = wrappedWidget(totalXPWidget!!.container) - addMouseListenerToAll(wrapped,rightClickListener) - wrapped.addMouseListener(rightClickListener) xpTrackerView.add(Box.createVerticalStrut(5)) - xpTrackerView.add(wrapped) + xpTrackerView.add(createTotalWidgetContainer(popupMenu)) xpTrackerView.add(Box.createVerticalStrut(5)) initialXP.clear() xpWidgets.clear() xpTrackerView.revalidate() - if (focusedView == VIEW_NAME) + if (focusedView == VIEW_NAME) { xpTrackerView.repaint() + } } - fun createTotalXPWidget(): XPWidget { - val widgetPanel = WidgetPanel(TOTAL_XP_WIDGET_SIZE.width, TOTAL_XP_WIDGET_SIZE.height).apply { - border = BorderFactory.createEmptyBorder(5, 5, 5, 5) + fun createTotalXPWidget(): XPWidget { + val widgetPanel = WidgetPanel(TOTAL_XP_WIDGET_SIZE.width, TOTAL_XP_WIDGET_SIZE.height, addDefaultPadding = false) + + val iconContainer = createIconContainer(getBufferedImageFromSprite(API.GetSprite(LVL_ICON))) + + val textPanel = JPanel(GridLayout(2, 1, 5, 0)).apply { + background = WIDGET_COLOR } - val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(LVL_ICON)) - - val imageContainer = Panel(FlowLayout()).apply { - preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) - maximumSize = preferredSize - minimumSize = preferredSize - size = preferredSize - } - - bufferedImageSprite.let { image -> - val imageCanvas = ImageCanvas(image).apply { - preferredSize = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) - maximumSize = preferredSize - minimumSize = preferredSize - size = preferredSize - } - - imageContainer.add(imageCanvas) - imageContainer.size = Dimension(bufferedImageSprite.width, bufferedImageSprite.height) - - imageContainer.revalidate() - if(focusedView == VIEW_NAME) - imageContainer.repaint() - } - - val textPanel = Panel().apply { - layout = GridLayout(2, 1, 5, 0) - } - - val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - - val xpGainedLabel = JLabel( - formatHtmlLabelText("Gained: ", primaryColor, "0", secondaryColor) - ).apply { - this.horizontalAlignment = JLabel.LEFT - this.font = font - } - - val xpPerHourLabel = JLabel( - formatHtmlLabelText("XP /hr: ", primaryColor, "0", secondaryColor) - ).apply { - this.horizontalAlignment = JLabel.LEFT - this.font = font - } + val xpGainedLabel = createMetricLabel("Gained:") + val xpPerHourLabel = createMetricLabel("XP /hr:") textPanel.add(xpGainedLabel) textPanel.add(xpPerHourLabel) - widgetPanel.add(imageContainer, BorderLayout.WEST) + widgetPanel.add(iconContainer, BorderLayout.WEST) widgetPanel.add(textPanel, BorderLayout.CENTER) return XPWidget( skillId = -1, container = widgetPanel, xpGainedLabel = xpGainedLabel, - xpLeftLabel = JLabel(formatHtmlLabelText("XP Left: ", primaryColor, "0", secondaryColor)).apply { - this.horizontalAlignment = JLabel.LEFT - this.font = font - }, + xpLeftLabel = createMetricLabel("XP Left:"), xpPerHourLabel = xpPerHourLabel, progressBar = ProgressBar(0.0, Color.BLACK), // Unused totalXpGained = 0, startTime = System.currentTimeMillis(), previousXp = 0, - actionsRemainingLabel = JLabel(), + actionsRemainingLabel = JLabel().apply { font = widgetFont }, ) } - fun createXPTrackerView(){ + fun createXPTrackerView() { val widgetViewPanel = BaseView(VIEW_NAME, addDefaultSpacing = false).apply { add(Box.createVerticalStrut(5)) } val popupMenu = createResetMenu() - - // Create a custom MouseListener - val rightClickListener = object : MouseAdapter() { - override fun mousePressed(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - - override fun mouseReleased(e: MouseEvent) { - if (e.isPopupTrigger) { - popupMenu.show(e.component, e.x, e.y) - } - } - } - - // Create the XP widget - totalXPWidget = createTotalXPWidget() - val wrapped = wrappedWidget(totalXPWidget!!.container) - addMouseListenerToAll(wrapped,rightClickListener) - wrapped.addMouseListener(rightClickListener) - widgetViewPanel.add(wrapped) + widgetViewPanel.add(createTotalWidgetContainer(popupMenu)) widgetViewPanel.add(Box.createVerticalStrut(5)) xpTrackerView = widgetViewPanel @@ -353,10 +339,7 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { // Preload skill icons to avoid first-drop lag try { for (i in 0 until 24) { - if (!skillIconCache.containsKey(i)) { - val img = getBufferedImageFromSprite(API.GetSprite(getSpriteId(i))) - skillIconCache[i] = img - } + getSkillIcon(i) } } catch (_: Exception) { // Ignore preload errors; fallback at use time @@ -365,39 +348,22 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { fun createResetMenu(): JPopupMenu { - // Create a popup menu val popupMenu = PopupMenuComponent() - // Create menu items with custom font and colors - val menuItem1 = JMenuItem("Reset Tracker").apply { - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - background = POPUP_BACKGROUND - foreground = POPUP_FOREGROUND - } + val resetItem = createMenuItem("Reset Tracker") + popupMenu.add(resetItem) - // Add menu items to the popup menu - popupMenu.add(menuItem1) - - // Add action listeners to each menu item (optional) - menuItem1.addActionListener { plugin.registerDrawAction { resetXPTracker(xpTrackerView!!) } } + resetItem.addActionListener { plugin.registerDrawAction { resetXPTracker(xpTrackerView!!) } } return popupMenu } fun removeXPWidgetMenu(toRemove: Container, skillId: Int): JPopupMenu { val popupMenu = PopupMenuComponent() - val resetItem = JMenuItem("Reset").apply { - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - background = POPUP_BACKGROUND - foreground = POPUP_FOREGROUND - } + val resetItem = createMenuItem("Reset") popupMenu.add(resetItem) - val removeItem = JMenuItem("Remove").apply { - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) - background = POPUP_BACKGROUND - foreground = POPUP_FOREGROUND - } + val removeItem = createMenuItem("Remove") popupMenu.add(removeItem) resetItem.addActionListener { @@ -438,95 +404,39 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { fun createXPWidget(skillId: Int, previousXp: Int): XPWidget { - val widgetPanel = WidgetPanel(WIDGET_SIZE.width, WIDGET_SIZE.height).apply { - border = BorderFactory.createEmptyBorder(5, 5, 5, 5) - } + val widgetPanel = WidgetPanel(WIDGET_SIZE.width, WIDGET_SIZE.height, addDefaultPadding = false) - val bufferedImageSprite = skillIconCache[skillId] - ?: run { - val img = getBufferedImageFromSprite(API.GetSprite(getSpriteId(skillId))) - skillIconCache[skillId] = img - img - } - val imageContainer = Panel(FlowLayout()).apply { - background = WIDGET_COLOR - preferredSize = IMAGE_SIZE - maximumSize = IMAGE_SIZE - minimumSize = IMAGE_SIZE - size = IMAGE_SIZE - } + val iconContainer = createIconContainer(getSkillIcon(skillId)) - bufferedImageSprite.let { image -> - val imageCanvas = ImageCanvas(image).apply { - background = WIDGET_COLOR - preferredSize = Dimension(image.width, image.height) - maximumSize = Dimension(image.width, image.height) - minimumSize = Dimension(image.width, image.height) - size = Dimension(image.width, image.height) // Explicitly set the size - } - - imageContainer.add(imageCanvas) - imageContainer.size = Dimension(image.width, image.height) // Ensure container respects the image size - - imageContainer.revalidate() - if(focusedView == VIEW_NAME) - imageContainer.repaint() - } - - val textPanel = Panel().apply { - layout = GridLayout(2, 2, 5, 0) + val textPanel = JPanel(GridLayout(2, 2, 5, 0)).apply { background = WIDGET_COLOR } - val font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + val xpGainedLabel = createMetricLabel("XP Gained:") + val xpLeftLabel = createMetricLabel("XP Left:", "0K") + val xpPerHourLabel = createMetricLabel("XP /hr:") + val actionsTitle = if (COMBAT_SKILLS.contains(skillId)) "Kills:" else "Actions:" + val actionsLabel = createMetricLabel(actionsTitle) - val xpGainedLabel = JLabel( - formatHtmlLabelText("XP Gained: ", primaryColor, "0", secondaryColor) - ).apply { - this.horizontalAlignment = JLabel.LEFT - this.font = font - } - - val xpLeftLabel = JLabel( - formatHtmlLabelText("XP Left: ", primaryColor, "0K", secondaryColor) - ).apply { - this.horizontalAlignment = JLabel.LEFT - this.font = font - } - - val xpPerHourLabel = JLabel( - formatHtmlLabelText("XP /hr: ", primaryColor, "0", secondaryColor) - ).apply { - this.horizontalAlignment = JLabel.LEFT - this.font = font - } - - val killsLabel = JLabel( - formatHtmlLabelText("Kills: ", primaryColor, "0", secondaryColor) - ).apply { - this.horizontalAlignment = JLabel.LEFT - this.font = font - } - - val levelPanel = Panel().apply { - layout = BorderLayout(5, 0) - background = WIDGET_COLOR - } - - val progressBarPanel = ProgressBar(0.0, getProgressBarColor(skillId)).apply { + val progressBar = ProgressBar(0.0, getProgressBarColor(skillId)).apply { preferredSize = Dimension(160, 22) + minimumSize = preferredSize + maximumSize = preferredSize } - levelPanel.add(progressBarPanel, BorderLayout.CENTER) + val progressPanel = JPanel(BorderLayout()).apply { + background = WIDGET_COLOR + add(progressBar, BorderLayout.CENTER) + } textPanel.add(xpGainedLabel) textPanel.add(xpLeftLabel) textPanel.add(xpPerHourLabel) - textPanel.add(killsLabel) + textPanel.add(actionsLabel) - widgetPanel.add(imageContainer, BorderLayout.WEST) + widgetPanel.add(iconContainer, BorderLayout.WEST) widgetPanel.add(textPanel, BorderLayout.CENTER) - widgetPanel.add(levelPanel, BorderLayout.SOUTH) + widgetPanel.add(progressPanel, BorderLayout.SOUTH) widgetPanel.revalidate() if(focusedView == VIEW_NAME) @@ -538,9 +448,9 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { xpGainedLabel = xpGainedLabel, xpLeftLabel = xpLeftLabel, xpPerHourLabel = xpPerHourLabel, - progressBar = progressBarPanel, + progressBar = progressBar, totalXpGained = 0, - actionsRemainingLabel = killsLabel, + actionsRemainingLabel = actionsLabel, startTime = System.currentTimeMillis(), previousXp = previousXp ) @@ -584,4 +494,4 @@ data class XPWidget( var totalXpGained: Int = 0, var startTime: Long = System.currentTimeMillis(), var previousXp: Int = 0 -) \ No newline at end of file +)