diff --git a/plugin-playground/src/main/kotlin/KondoKit/ViewConstants.kt b/plugin-playground/src/main/kotlin/KondoKit/ViewConstants.kt index 9643657..8237358 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/ViewConstants.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/ViewConstants.kt @@ -14,21 +14,24 @@ object ViewConstants { val FONT_RUNESCAPE_SMALL_16 = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) val FONT_RUNESCAPE_SMALL_14 = Font("RuneScape Small", Font.PLAIN, 14) val FONT_RUNESCAPE_SMALL_PLAIN_16 = Font("RuneScape Small", Font.PLAIN, 16) + val FONT_RUNESCAPE_SMALL_BOLD_16 = Font("RuneScape Small", Font.BOLD, 16) val FONT_ARIAL_PLAIN_14 = Font("Arial", Font.PLAIN, 14) val FONT_ARIAL_BOLD_12 = Font("Arial", Font.BOLD, 12) // Common Dimensions val DIMENSION_SMALL_ICON = Dimension(12, 12) val DIMENSION_LARGE_ICON = Dimension(30, 30) - val DEFAULT_WIDGET_SIZE = Dimension(220, 50) + val DEFAULT_WIDGET_SIZE = Dimension(234, 50) + val PLUGIN_LIST_ITEM_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 60) + val TOGGLE_PLACEHOLDER_SIZE = Dimension(60, 24) val TOTAL_XP_WIDGET_SIZE = Dimension(220, 30) val IMAGE_SIZE = Dimension(25, 23) - val SEARCH_FIELD_SIZE = Dimension(230, 30) - val DEFAULT_PANEL_SIZE = Dimension(230, 500) - val FILTER_PANEL_SIZE = Dimension(230, 30) - val SKILLS_PANEL_SIZE = Dimension(230, 290) - val TOTAL_COMBAT_PANEL_SIZE = Dimension(230, 30) - val SKILL_PANEL_SIZE = Dimension(76, 35) + val SEARCH_FIELD_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 30) + val DEFAULT_PANEL_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 500) + val FILTER_PANEL_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 30) + val SKILLS_PANEL_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 290) + val TOTAL_COMBAT_PANEL_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width, 30) + val SKILL_PANEL_SIZE = Dimension(DEFAULT_WIDGET_SIZE.width / 3, 35) val IMAGE_CANVAS_SIZE = Dimension(20, 20) val SKILL_SPRITE_SIZE = Dimension(14, 14) val NUMBER_LABEL_SIZE = Dimension(20, 20) @@ -42,19 +45,6 @@ object ViewConstants { val COLOR_RED = Color.RED val COLOR_GRAY = Color.GRAY - // UI Colors (already defined in plugin.kt companion object, but referenced here for consistency) - // These should match the ones in plugin.kt Companion object - val COLOR_WIDGET = Color(30, 30, 30) - val COLOR_TITLE_BAR = Color(21, 21, 21) - val COLOR_VIEW_BACKGROUND = Color(40, 40, 40) - val COLOR_PRIMARY = Color(165, 165, 165) - val COLOR_SECONDARY = Color(255, 255, 255) - val COLOR_POPUP_BACKGROUND = Color(45, 45, 45) - val COLOR_POPUP_FOREGROUND = Color(220, 220, 220) - val COLOR_TOOLTIP_BACKGROUND = Color(50, 50, 50) - val COLOR_SCROLL_BAR = Color(64, 64, 64) - val COLOR_PROGRESS_BAR_FILL = Color(61, 56, 49) - // Skill display order 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) @@ -65,4 +55,4 @@ object ViewConstants { const val LVL_BAR_SPRITE = 898 const val WRENCH_ICON = 907 const val LOOT_ICON = 777 -} \ No newline at end of file +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/SearchField.kt b/plugin-playground/src/main/kotlin/KondoKit/components/SearchField.kt index 85aeff1..4f7ddda 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/SearchField.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/components/SearchField.kt @@ -16,8 +16,8 @@ class SearchField( private val parentPanel: JPanel, private val onSearch: (String) -> Unit, private val placeholderText: String = "Search...", - private val fieldWidth: Int = 230, - private val fieldHeight: Int = 30, + private val fieldWidth: Int = ViewConstants.SEARCH_FIELD_SIZE.width, + private val fieldHeight: Int = ViewConstants.SEARCH_FIELD_SIZE.height, private val viewName: String? = "" ) : Canvas() { @@ -40,9 +40,24 @@ class SearchField( preferredSize = dimension background = WIDGET_COLOR foreground = secondaryColor - font = Font("Arial", Font.PLAIN, 14) + font = ViewConstants.FONT_ARIAL_PLAIN_14 minimumSize = dimension maximumSize = dimension + + isFocusable = true + focusTraversalKeysEnabled = false + + addFocusListener(object : FocusAdapter() { + override fun focusGained(e: FocusEvent) { + cursorVisible = true + repaintAsync() + } + + override fun focusLost(e: FocusEvent) { + cursorVisible = false + repaintAsync() + } + }) addKeyListener(object : KeyAdapter() { override fun keyTyped(e: KeyEvent) { @@ -56,33 +71,33 @@ class SearchField( text = text.dropLast(1) } } else if (e.keyChar == '\n') { - triggerSearch() + // Handled in keyPressed to avoid duplicate searches } else { text += e.keyChar } - SwingUtilities.invokeLater { - repaint() - } + repaintAsync() } override fun keyPressed(e: KeyEvent) { + if (e.keyCode == KeyEvent.VK_ENTER) { + triggerSearch() + e.consume() + return + } + if (e.isControlDown) { when (e.keyCode) { KeyEvent.VK_A -> { // They probably want to clear the search box text = "" - SwingUtilities.invokeLater { - repaint() - } + repaintAsync() } KeyEvent.VK_V -> { try { val clipboard = Toolkit.getDefaultToolkit().systemClipboard val pasteText = clipboard.getData(DataFlavor.stringFlavor) as String text += pasteText - SwingUtilities.invokeLater { - repaint() - } + repaintAsync() } catch (_: Exception) { } } } @@ -93,23 +108,24 @@ class SearchField( addMouseListener(object : MouseAdapter() { // Clicked the search field 'x' button override fun mouseClicked(e: MouseEvent) { + requestFocusInWindow() if (e.x > width - 20 && e.y < 20) { text = "" triggerSearch() - SwingUtilities.invokeLater { - repaint() - } + repaintAsync() } } + + override fun mousePressed(e: MouseEvent) { + requestFocusInWindow() + } }) Timer(500) { _ -> cursorVisible = !cursorVisible // Only repaint if the view is active or if viewName is not specified if (viewName == null || focusedView == viewName) { - SwingUtilities.invokeLater { - repaint() - } + repaintAsync() } }.start() } @@ -155,7 +171,7 @@ class SearchField( fun setText(newText: String) { text = newText - repaint() + repaintAsync() } fun getText(): String = text @@ -164,10 +180,14 @@ class SearchField( val query = text.trim() if (query.isNotEmpty()) { text = query - repaint() + repaintAsync() onSearch(query) } else { onSearch(query) // Call with empty string for clearing filters } } -} \ No newline at end of file + + private fun repaintAsync() { + SwingUtilities.invokeLater { repaint() } + } +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/SettingsPanel.kt b/plugin-playground/src/main/kotlin/KondoKit/components/SettingsPanel.kt index 9f6a5d2..d803b92 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/SettingsPanel.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/components/SettingsPanel.kt @@ -2,6 +2,7 @@ package KondoKit.components import KondoKit.Helpers import KondoKit.Helpers.FieldNotifier +import KondoKit.ViewConstants import KondoKit.plugin.Companion.WIDGET_COLOR import KondoKit.plugin.Companion.primaryColor import KondoKit.plugin.Companion.secondaryColor @@ -62,7 +63,7 @@ class SettingsPanel(private val plugin: Plugin) : JPanel() { val label = JLabel(field.name.capitalize()).apply { foreground = secondaryColor - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = ViewConstants.FONT_RUNESCAPE_SMALL_16 } gbc.gridx = 0 gbc.gridy = 0 @@ -229,7 +230,7 @@ class SettingsPanel(private val plugin: Plugin) : JPanel() { // Create title label val titleLabel = JLabel("${field.name} (Key-Value Pairs)").apply { foreground = secondaryColor - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = ViewConstants.FONT_RUNESCAPE_SMALL_16 alignmentX = Component.LEFT_ALIGNMENT // Add mouse listener to the label only if a description is available @@ -451,14 +452,14 @@ class SettingsPanel(private val plugin: Plugin) : JPanel() { var customToolTipWindow: JWindow? = null fun showCustomToolTip(text: String, component: JComponent) { - val _font = Font("RuneScape Small", Font.PLAIN, 16) + val tooltipFont = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16 val maxWidth = 150 - val lineHeight = 16 - + // Create a dummy JLabel to get FontMetrics for the font used in the tooltip val dummyLabel = JLabel() - dummyLabel.font = _font - val fontMetrics = dummyLabel.getFontMetrics(_font) + dummyLabel.font = tooltipFont + val fontMetrics = dummyLabel.getFontMetrics(tooltipFont) + val lineHeight = fontMetrics.height // Calculate the approximate width of the text val textWidth = fontMetrics.stringWidth(text) @@ -478,7 +479,7 @@ class SettingsPanel(private val plugin: Plugin) : JPanel() { isOpaque = true background = KondoKit.plugin.Companion.TOOLTIP_BACKGROUND foreground = Color.WHITE - font = _font + font = tooltipFont maximumSize = Dimension(maxWidth, Int.MAX_VALUE) preferredSize = Dimension(maxWidth, requiredHeight) } @@ -500,4 +501,4 @@ class SettingsPanel(private val plugin: Plugin) : JPanel() { customToolTipWindow!!.isVisible = true } } -} \ No newline at end of file +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ViewHeader.kt b/plugin-playground/src/main/kotlin/KondoKit/components/ViewHeader.kt index e9e0b89..fd0e505 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/ViewHeader.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/components/ViewHeader.kt @@ -1,5 +1,6 @@ package KondoKit.components +import KondoKit.ViewConstants import KondoKit.plugin.Companion.TITLE_BAR_COLOR import KondoKit.plugin.Companion.secondaryColor import java.awt.* @@ -19,7 +20,7 @@ class ViewHeader( layout = BorderLayout() titleLabel.foreground = secondaryColor - titleLabel.font = Font("RuneScape Small", Font.PLAIN, 16) + titleLabel.font = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16 titleLabel.horizontalAlignment = SwingConstants.CENTER add(titleLabel, BorderLayout.CENTER) @@ -28,4 +29,4 @@ class ViewHeader( fun setTitle(title: String) { titleLabel.text = title } -} \ No newline at end of file +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/WidgetPanel.kt b/plugin-playground/src/main/kotlin/KondoKit/components/WidgetPanel.kt index 65d6018..7084ed9 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/WidgetPanel.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/components/WidgetPanel.kt @@ -1,5 +1,6 @@ package KondoKit.components +import KondoKit.ViewConstants import KondoKit.plugin.Companion.WIDGET_COLOR import java.awt.BorderLayout import java.awt.Dimension @@ -7,22 +8,27 @@ import javax.swing.BorderFactory import javax.swing.JPanel class WidgetPanel( - private val widgetWidth: Int = 220, - private val widgetHeight: Int = 50, + private val widgetWidth: Int = ViewConstants.DEFAULT_WIDGET_SIZE.width, + private val widgetHeight: Int = ViewConstants.DEFAULT_WIDGET_SIZE.height, private val addDefaultPadding: Boolean = true ) : JPanel() { init { - layout = BorderLayout(5, 5) + layout = BorderLayout( + if (addDefaultPadding) 5 else 0, + if (addDefaultPadding) 5 else 0 + ) background = WIDGET_COLOR - + val size = Dimension(widgetWidth, widgetHeight) preferredSize = size maximumSize = size minimumSize = size - + if (addDefaultPadding) { border = BorderFactory.createEmptyBorder(5, 5, 5, 5) + } else { + border = BorderFactory.createEmptyBorder(0, 0, 0, 0) } } @@ -32,4 +38,4 @@ class WidgetPanel( maximumSize = size minimumSize = size } -} \ No newline at end of file +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/BaseView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/BaseView.kt index 353a789..9e2aaf1 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/BaseView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/BaseView.kt @@ -1,5 +1,6 @@ package KondoKit.views +import KondoKit.ViewConstants import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR import java.awt.Dimension import javax.swing.BorderFactory @@ -9,7 +10,7 @@ import javax.swing.JPanel open class BaseView( private val viewName: String, - private val preferredWidth: Int = 242, + private val preferredWidth: Int = ViewConstants.DEFAULT_PANEL_SIZE.width, private val addDefaultSpacing: Boolean = true ) : JPanel() { @@ -30,4 +31,4 @@ open class BaseView( maximumSize = dimension minimumSize = dimension } -} \ No newline at end of file +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt index 0bea313..3ffdb0f 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/HiscoresView.kt @@ -5,8 +5,10 @@ import KondoKit.Helpers.showToast import KondoKit.ImageCanvas import KondoKit.ViewConstants import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite -import KondoKit.components.SearchField import KondoKit.components.LabelComponent +import KondoKit.components.WidgetPanel +import KondoKit.components.SearchField +import KondoKit.views.ViewLayoutHelpers.createSearchFieldSection import KondoKit.plugin.Companion.WIDGET_COLOR import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR import KondoKit.plugin.Companion.POPUP_FOREGROUND @@ -51,65 +53,50 @@ object HiscoresView : View { setViewSize(ViewConstants.DEFAULT_PANEL_SIZE.height) } - customSearchField = SearchField( - hiscorePanel, - { username -> - searchPlayerForHiscores(username, hiscorePanel) - }, - "Search player...", - 242, - 30, - VIEW_NAME - ) - - val searchField = customSearchField!! - - val searchFieldWrapper = JPanel().apply { - layout = BoxLayout(this, BoxLayout.X_AXIS) - background = VIEW_BACKGROUND_COLOR - preferredSize = ViewConstants.SEARCH_FIELD_SIZE - maximumSize = preferredSize - minimumSize = preferredSize - alignmentX = Component.CENTER_ALIGNMENT - add(searchField) + val searchSection = createSearchFieldSection( + parent = hiscorePanel, + placeholderText = "Search player...", + viewName = VIEW_NAME + ) { username -> + searchPlayerForHiscores(username, hiscorePanel) } - val searchPanel = JPanel().apply { - layout = BoxLayout(this, BoxLayout.Y_AXIS) - background = VIEW_BACKGROUND_COLOR - add(searchFieldWrapper) - } + customSearchField = searchSection.searchField hiscorePanel.add(Box.createVerticalStrut(5)) - hiscorePanel.add(searchPanel) + hiscorePanel.add(searchSection.wrapper) hiscorePanel.add(Box.createVerticalStrut(5)) - val playerNamePanel = JPanel().apply { + val playerNamePanel = WidgetPanel( + widgetWidth = ViewConstants.FILTER_PANEL_SIZE.width, + widgetHeight = ViewConstants.FILTER_PANEL_SIZE.height, + addDefaultPadding = false + ).apply { layout = GridBagLayout() // This will center the JLabel both vertically and horizontally background = TOOLTIP_BACKGROUND.darker() - preferredSize = ViewConstants.FILTER_PANEL_SIZE - maximumSize = preferredSize - minimumSize = preferredSize name = "playerNameLabel" } hiscorePanel.add(playerNamePanel) hiscorePanel.add(Box.createVerticalStrut(5)) - val skillsPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply { + val skillsPanel = WidgetPanel( + widgetWidth = ViewConstants.SKILLS_PANEL_SIZE.width, + widgetHeight = ViewConstants.SKILLS_PANEL_SIZE.height, + addDefaultPadding = false + ).apply { + layout = FlowLayout(FlowLayout.CENTER, 0, 0) background = VIEW_BACKGROUND_COLOR - preferredSize = ViewConstants.SKILLS_PANEL_SIZE - maximumSize = preferredSize - minimumSize = preferredSize } for (i in ViewConstants.SKILL_DISPLAY_ORDER) { - val skillPanel = JPanel().apply { + val skillPanel = WidgetPanel( + widgetWidth = ViewConstants.SKILL_PANEL_SIZE.width, + widgetHeight = ViewConstants.SKILL_PANEL_SIZE.height, + addDefaultPadding = false + ).apply { layout = BorderLayout() background = WIDGET_COLOR - preferredSize = ViewConstants.SKILL_PANEL_SIZE - maximumSize = preferredSize - minimumSize = preferredSize border = MatteBorder(5, 0, 0, 0, WIDGET_COLOR) } @@ -143,11 +130,13 @@ object HiscoresView : View { hiscorePanel.add(skillsPanel) - val totalCombatPanel = JPanel(FlowLayout(FlowLayout.CENTER, 0, 0)).apply { + val totalCombatPanel = WidgetPanel( + widgetWidth = ViewConstants.TOTAL_COMBAT_PANEL_SIZE.width, + widgetHeight = ViewConstants.TOTAL_COMBAT_PANEL_SIZE.height, + addDefaultPadding = false + ).apply { + layout = FlowLayout(FlowLayout.LEFT, 0, 0) background = WIDGET_COLOR - preferredSize = ViewConstants.TOTAL_COMBAT_PANEL_SIZE - maximumSize = preferredSize - minimumSize = preferredSize } val bufferedImageSprite = getBufferedImageFromSprite(API.GetSprite(ViewConstants.LVL_BAR_SPRITE)) @@ -166,7 +155,12 @@ object HiscoresView : View { iconTextGap = 10 } - val totalLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply { + val totalLevelPanel = WidgetPanel( + widgetWidth = ViewConstants.TOTAL_COMBAT_PANEL_SIZE.width / 2, + widgetHeight = ViewConstants.TOTAL_COMBAT_PANEL_SIZE.height, + addDefaultPadding = false + ).apply { + layout = FlowLayout(FlowLayout.LEFT, 5, 0) background = WIDGET_COLOR add(totalLevelIcon) add(totalLevelLabel) @@ -188,7 +182,12 @@ object HiscoresView : View { iconTextGap = 10 } - val combatLevelPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply { + val combatLevelPanel = WidgetPanel( + widgetWidth = ViewConstants.TOTAL_COMBAT_PANEL_SIZE.width / 2, + widgetHeight = ViewConstants.TOTAL_COMBAT_PANEL_SIZE.height, + addDefaultPadding = false + ).apply { + layout = FlowLayout(FlowLayout.LEFT, 5, 0) background = WIDGET_COLOR add(combatLevelIcon) add(combatLevelLabel) @@ -401,4 +400,3 @@ object HiscoresView : View { return Math.round((base + maxCombatType + summoningFactor) * 1000.0) / 1000.0 } } - diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/LootTrackerView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/LootTrackerView.kt index fd06e9e..980c8ec 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/LootTrackerView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/LootTrackerView.kt @@ -5,6 +5,7 @@ import KondoKit.Helpers.addMouseListenerToAll import KondoKit.Helpers.formatHtmlLabelText import KondoKit.ImageCanvas import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite +import KondoKit.ViewConstants import KondoKit.views.XPTrackerView.wrappedWidget import KondoKit.components.PopupMenuComponent import KondoKit.components.ProgressBar @@ -228,7 +229,7 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac private fun createLabel(text: String): JLabel { return JLabel(text).apply { - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = ViewConstants.FONT_RUNESCAPE_SMALL_16 horizontalAlignment = JLabel.LEFT } } @@ -320,7 +321,7 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac "GE: ${formatValue(totalGePrice)} ${geText}
" + "HA: ${formatValue(totalHaPrice)} ${haText}" - val _font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + val tooltipFont = ViewConstants.FONT_RUNESCAPE_SMALL_16 if (customToolTipWindow == null) { customToolTipWindow = JWindow().apply { contentPane = JLabel(text).apply { @@ -328,7 +329,7 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac isOpaque = true background = TOOLTIP_BACKGROUND foreground = Color.WHITE - font = _font + font = tooltipFont } pack() } @@ -463,15 +464,15 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac val childFramePanel = JPanel().apply { layout = BoxLayout(this, BoxLayout.Y_AXIS) background = WIDGET_COLOR - minimumSize = Dimension(230, 0) - maximumSize = Dimension(230, 700) + minimumSize = Dimension(ViewConstants.DEFAULT_WIDGET_SIZE.width, 0) + maximumSize = Dimension(ViewConstants.DEFAULT_WIDGET_SIZE.width, 700) name = "HELLO_WORLD" } val labelPanel = JPanel(BorderLayout()).apply { background = TITLE_BAR_COLOR border = BorderFactory.createEmptyBorder(5, 5, 5, 5) - maximumSize = Dimension(230, 24) + maximumSize = Dimension(ViewConstants.DEFAULT_WIDGET_SIZE.width, 24) minimumSize = maximumSize preferredSize = maximumSize } @@ -479,14 +480,14 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac val killCount = npcKillCounts.getOrPut(npcName) { 0 } val countLabel = JLabel(formatHtmlLabelText(npcName, secondaryColor, " x $killCount", primaryColor)).apply { foreground = secondaryColor - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = ViewConstants.FONT_RUNESCAPE_SMALL_16 horizontalAlignment = JLabel.LEFT name = "killCountLabel_$npcName" } val valueLabel = JLabel("0 gp").apply { foreground = secondaryColor - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = ViewConstants.FONT_RUNESCAPE_SMALL_16 horizontalAlignment = JLabel.RIGHT name = "valueLabel_$npcName" } @@ -531,7 +532,7 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac private fun removeLootFrameMenu(toRemove: JPanel, npcName: String): JPopupMenu { // Create a popup menu val popupMenu = PopupMenuComponent() - val rFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + val rFont = ViewConstants.FONT_RUNESCAPE_SMALL_16 popupMenu.background = POPUP_BACKGROUND @@ -571,7 +572,7 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac // Create menu items with custom font and colors val menuItem1 = JMenuItem("Reset Loot Tracker").apply { - font = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + font = ViewConstants.FONT_RUNESCAPE_SMALL_16 background = POPUP_BACKGROUND foreground = POPUP_FOREGROUND } @@ -646,4 +647,4 @@ object LootTrackerView : View, OnPostClientTickCallback, OnKillingBlowNPCCallbac var startTime: Long = System.currentTimeMillis(), var previousXp: Int = 0 ) -} \ No newline at end of file +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt index c2977d6..1e4ca9f 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt @@ -2,7 +2,8 @@ package KondoKit.views import KondoKit.Helpers import KondoKit.components.* -import KondoKit.components.SearchField +import KondoKit.ViewConstants +import KondoKit.views.ViewLayoutHelpers.createSearchFieldSection import KondoKit.pluginmanager.GitLabPlugin import KondoKit.pluginmanager.GitLabPluginFetcher import KondoKit.pluginmanager.PluginDownloadManager @@ -22,11 +23,8 @@ import rt4.GlobalJsonConfig import java.awt.* import java.awt.event.MouseAdapter import java.awt.event.MouseEvent -import java.awt.image.BufferedImage import java.io.File import java.util.* -import java.util.concurrent.atomic.AtomicInteger -import javax.imageio.ImageIO import javax.swing.* import kotlin.math.ceil @@ -49,8 +47,6 @@ object ReflectiveEditorView : View { private var pluginStatuses: List = listOf() - private var cogIcon: Icon? = null - private var searchField: SearchField? = null // Flag for scheduled plugin reload to avoid crashes @@ -61,19 +57,6 @@ object ReflectiveEditorView : View { return pluginName == "KondoKit" } - private fun loadCogIcon() { - try { - val imageStream = this::class.java.getResourceAsStream("res/cog.png") - if (imageStream != null) { - val image: BufferedImage = ImageIO.read(imageStream) - val scaledImage = image.getScaledInstance(12, 12, Image.SCALE_SMOOTH) - cogIcon = ImageIcon(scaledImage) - } - } catch (e: Exception) { - e.printStackTrace() - } - } - // Card layout for switching between views within the reflective editor view private lateinit var cardLayout: CardLayout private lateinit var mainPanel: JPanel @@ -99,8 +82,6 @@ object ReflectiveEditorView : View { } fun createReflectiveEditorView() { - loadCogIcon() - cardLayout = CardLayout() mainPanel = JPanel(cardLayout) mainPanel.background = VIEW_BACKGROUND_COLOR @@ -122,7 +103,6 @@ object ReflectiveEditorView : View { cardLayout.show(mainPanel, PLUGIN_LIST_VIEW) GitLabPluginFetcher.fetchGitLabPlugins { plugins -> - System.out.println("GitLab plugins fetched: ${plugins.size}") gitLabPlugins = plugins // Update plugin statuses with comparison between installed and remote plugins updatePluginStatuses() @@ -131,41 +111,26 @@ object ReflectiveEditorView : View { } private fun createPluginListView(): JPanel { - val panel = JPanel() - panel.layout = BoxLayout(panel, BoxLayout.Y_AXIS) - panel.background = VIEW_BACKGROUND_COLOR - - val searchField = SearchField( - 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) - } - - val searchFieldWrapperPanel = JPanel().apply { - layout = BoxLayout(this, BoxLayout.X_AXIS) + val panel = BaseView(PLUGIN_LIST_VIEW, addDefaultSpacing = false).apply { background = VIEW_BACKGROUND_COLOR - preferredSize = Dimension(230, 30) - maximumSize = preferredSize - minimumSize = preferredSize - alignmentX = Component.CENTER_ALIGNMENT - add(searchField) } - searchFieldWrapper = searchFieldWrapperPanel + + val searchSection = createSearchFieldSection( + parent = panel, + placeholderText = "Search plugins...", + viewName = VIEW_NAME, + initialText = pluginSearchText + ) { searchText -> + pluginSearchText = searchText + SwingUtilities.invokeLater { + addPlugins(reflectiveEditorView!!) + } + } + this.searchField = searchSection.searchField + searchFieldWrapper = searchSection.wrapper panel.add(Box.createVerticalStrut(10)) - panel.add(searchFieldWrapperPanel) + panel.add(searchSection.wrapper) panel.add(Box.createVerticalStrut(10)) pluginListContentPanel = JPanel().apply { @@ -276,59 +241,34 @@ object ReflectiveEditorView : View { } if (pluginSearchText.isNotBlank()) { - System.out.println("Filtering plugins for search term: '$pluginSearchText'") - System.out.println("Total plugin statuses: ${pluginStatuses.size}") - val matchingPluginStatuses = pluginStatuses.filter { pluginStatus -> - // Only show plugins that are not currently loaded and not KondoKit - val shouldShow = !pluginStatus.isInstalled && + !pluginStatus.isInstalled && !isKondoKit(pluginStatus.name) && (pluginStatus.name.contains(pluginSearchText, ignoreCase = true) || - (pluginStatus.description?.contains(pluginSearchText, ignoreCase = true) ?: false)) - - System.out.println("Plugin: ${pluginStatus.name}, isInstalled: ${pluginStatus.isInstalled}, shouldShow: $shouldShow") - shouldShow + (pluginStatus.description?.contains(pluginSearchText, ignoreCase = true) ?: false)) } - System.out.println("Matching plugin statuses: ${matchingPluginStatuses.size}") - - // Add a separator - val separator = JPanel() - separator.background = VIEW_BACKGROUND_COLOR - separator.preferredSize = Dimension(Int.MAX_VALUE, 1) - separator.maximumSize = Dimension(Int.MAX_VALUE, 1) - contentPanel.add(Box.createVerticalStrut(10)) - contentPanel.add(separator) - contentPanel.add(Box.createVerticalStrut(5)) - - val headerPanel = JPanel(FlowLayout(FlowLayout.LEFT)) - headerPanel.background = VIEW_BACKGROUND_COLOR - val headerLabel = JLabel(if (matchingPluginStatuses.isNotEmpty()) "Available Plugins" else "") - headerLabel.foreground = secondaryColor - headerLabel.font = Font("RuneScape Small", Font.BOLD, 16) - headerPanel.add(headerLabel) - contentPanel.add(headerPanel) - contentPanel.add(Box.createVerticalStrut(5)) - if (matchingPluginStatuses.isNotEmpty()) { - if (matchingPluginStatuses.size > 1) { - val downloadAllPanel = JPanel(FlowLayout(FlowLayout.LEFT)) - downloadAllPanel.background = VIEW_BACKGROUND_COLOR - val downloadAllButton = JButton("Download All") - downloadAllButton.background = TITLE_BAR_COLOR - downloadAllButton.foreground = secondaryColor - downloadAllButton.font = Font("RuneScape Small", Font.PLAIN, 14) - downloadAllButton.addActionListener { - startMultiplePluginDownloads(matchingPluginStatuses) - } - downloadAllPanel.add(downloadAllButton) - contentPanel.add(downloadAllPanel) - contentPanel.add(Box.createVerticalStrut(5)) + val separator = JPanel().apply { + background = VIEW_BACKGROUND_COLOR + preferredSize = Dimension(Int.MAX_VALUE, 1) + maximumSize = preferredSize } + contentPanel.add(Box.createVerticalStrut(10)) + contentPanel.add(separator) + contentPanel.add(Box.createVerticalStrut(5)) - // Add matching plugin statuses - for (pluginStatus in matchingPluginStatuses) { - System.out.println("Adding plugin to UI: ${pluginStatus.name}") + val headerPanel = JPanel(FlowLayout(FlowLayout.LEFT)).apply { + background = VIEW_BACKGROUND_COLOR + add(JLabel("Available Plugins").apply { + foreground = secondaryColor + font = ViewConstants.FONT_RUNESCAPE_SMALL_BOLD_16 + }) + } + contentPanel.add(headerPanel) + contentPanel.add(Box.createVerticalStrut(5)) + + matchingPluginStatuses.forEach { pluginStatus -> val pluginPanel = createPluginStatusItemPanel(pluginStatus) contentPanel.add(pluginPanel) contentPanel.add(Box.createVerticalStrut(5)) @@ -342,16 +282,19 @@ object ReflectiveEditorView : View { } private fun createPluginItemPanel(pluginInfo: PluginInfo, plugin: Plugin): JPanel { - val panel = JPanel(BorderLayout()) - panel.background = WIDGET_COLOR - panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) - panel.maximumSize = Dimension(220, 60) + val panel = WidgetPanel( + widgetWidth = ViewConstants.PLUGIN_LIST_ITEM_SIZE.width, + widgetHeight = ViewConstants.PLUGIN_LIST_ITEM_SIZE.height, + addDefaultPadding = false + ).apply { + 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 = Font("RuneScape Small", Font.PLAIN, 16) + nameLabel.font = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16 // Check if plugin has exposed attributes val exposedFields = plugin.javaClass.declaredFields.filter { field -> @@ -362,15 +305,10 @@ object ReflectiveEditorView : View { // Edit button (only show if plugin has exposed attributes) val editButton = if (exposedFields.isNotEmpty()) { - val button = JButton() - if (cogIcon != null) { - button.icon = cogIcon - } else { - button.text = "Edit" - } + val button = JButton("Edit") button.background = TITLE_BAR_COLOR button.foreground = secondaryColor - button.font = Font("RuneScape Small", Font.PLAIN, 14) + button.font = ViewConstants.FONT_RUNESCAPE_SMALL_14 button.addActionListener { showPluginDetails(pluginInfo, plugin) } @@ -386,8 +324,8 @@ object ReflectiveEditorView : View { // Create a placeholder component that takes the same space but is invisible val placeholder = JPanel() placeholder.background = WIDGET_COLOR - placeholder.preferredSize = Dimension(60, 24) // Same size as toggle switch - placeholder.maximumSize = Dimension(60, 24) + placeholder.preferredSize = ViewConstants.TOGGLE_PLACEHOLDER_SIZE + placeholder.maximumSize = ViewConstants.TOGGLE_PLACEHOLDER_SIZE placeholder.isVisible = false placeholder } @@ -417,15 +355,18 @@ object ReflectiveEditorView : View { } private fun createDisabledPluginItemPanel(pluginName: String): JPanel { - val panel = JPanel(BorderLayout()) - panel.background = WIDGET_COLOR - panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) - panel.maximumSize = Dimension(220, 60) + val panel = WidgetPanel( + widgetWidth = ViewConstants.PLUGIN_LIST_ITEM_SIZE.width, + widgetHeight = ViewConstants.PLUGIN_LIST_ITEM_SIZE.height, + addDefaultPadding = false + ).apply { + layout = BorderLayout() + } // Plugin name val nameLabel = JLabel(pluginName) nameLabel.foreground = secondaryColor - nameLabel.font = Font("RuneScape Small", Font.PLAIN, 16) + nameLabel.font = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16 // Plugin toggle switch (iOS style) - initially off for disabled plugins val toggleSwitch = ToggleSwitch() @@ -462,21 +403,24 @@ object ReflectiveEditorView : View { } private fun createPluginStatusItemPanel(pluginStatus: PluginInfoWithStatus): JPanel { - val panel = JPanel(BorderLayout()) - panel.background = WIDGET_COLOR - panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) - panel.maximumSize = Dimension(220, 60) + val panel = WidgetPanel( + widgetWidth = ViewConstants.PLUGIN_LIST_ITEM_SIZE.width, + widgetHeight = ViewConstants.PLUGIN_LIST_ITEM_SIZE.height, + addDefaultPadding = false + ).apply { + layout = BorderLayout() + } // Plugin name val nameLabel = JLabel(pluginStatus.name) nameLabel.foreground = secondaryColor - nameLabel.font = Font("RuneScape Small", Font.PLAIN, 16) + nameLabel.font = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16 // Action button based on plugin status val actionButton = JButton() actionButton.background = TITLE_BAR_COLOR actionButton.foreground = secondaryColor - actionButton.font = Font("RuneScape Small", Font.PLAIN, 14) + actionButton.font = ViewConstants.FONT_RUNESCAPE_SMALL_14 // Progress bar for downloads val progressBar = JProgressBar(0, 100) @@ -543,6 +487,17 @@ object ReflectiveEditorView : View { controlsPanel.add(toggleSwitch) } + if (!pluginStatus.isInstalled) { + val tooltipText = buildInstallableTooltip(pluginStatus) + panel.toolTipText = tooltipText + actionButton.toolTipText = tooltipText + progressBar.toolTipText = tooltipText + } else { + panel.toolTipText = null + actionButton.toolTipText = null + progressBar.toolTipText = null + } + panel.add(infoPanel, BorderLayout.CENTER) panel.add(controlsPanel, BorderLayout.EAST) @@ -553,6 +508,32 @@ object ReflectiveEditorView : View { return panel } + + private fun buildInstallableTooltip(pluginStatus: PluginInfoWithStatus): String { + val lines = mutableListOf() + pluginStatus.remoteVersion?.takeIf { it.isNotBlank() }?.let { + lines += "Version: ${escapeHtml(it)}" + } + pluginStatus.author?.takeIf { it.isNotBlank() }?.let { + lines += "Author: ${escapeHtml(it)}" + } + pluginStatus.description?.takeIf { it.isNotBlank() }?.let { + lines += escapeHtml(it).replace("\n", "
") + } + + if (lines.isEmpty()) { + lines += "Click Download to install this plugin." + } + + return "${lines.joinToString("
")}" + } + + private fun escapeHtml(value: String): String { + return value + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + } private fun enablePlugin(pluginName: String) { try { @@ -733,13 +714,13 @@ object ReflectiveEditorView : View { """.trimIndent() + val infoFont = ViewConstants.FONT_RUNESCAPE_SMALL_14 val infoLabel = LabelComponent(infoText, isHtml = true).apply { - font = Font("RuneScape Small", Font.PLAIN, 14) + font = infoFont } infoPanel.add(infoLabel, BorderLayout.CENTER) - val font = Font("RuneScape Small", Font.PLAIN, 14) - val fm = infoLabel.getFontMetrics(font) + val fm = infoLabel.getFontMetrics(infoFont) val avgCharWidth = fm.stringWidth("x") val availableWidth = 200 @@ -759,7 +740,7 @@ object ReflectiveEditorView : View { val finalHeight = kotlin.math.max(60, kotlin.math.min(estimatedHeight, 150)) infoPanel.maximumSize = Dimension(Int.MAX_VALUE, finalHeight) - infoPanel.preferredSize = Dimension(220, finalHeight) + infoPanel.preferredSize = Dimension(ViewConstants.DEFAULT_WIDGET_SIZE.width, finalHeight) @@ -798,7 +779,6 @@ object ReflectiveEditorView : View { } fun addPlugins(reflectiveEditorView: JPanel) { - System.out.println("addPlugins called") // Ensure we run on the EDT; if not, reschedule and return if (!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater { addPlugins(reflectiveEditorView) } @@ -850,14 +830,14 @@ object ReflectiveEditorView : View { var customToolTipWindow: JWindow? = null fun showCustomToolTip(text: String, component: JComponent) { - val _font = Font("RuneScape Small", Font.PLAIN, 16) + val tooltipFont = ViewConstants.FONT_RUNESCAPE_SMALL_PLAIN_16 val maxWidth = 150 - val lineHeight = 16 // Create a dummy JLabel to get FontMetrics for the font used in the tooltip val dummyLabel = JLabel() - dummyLabel.font = _font - val fontMetrics = dummyLabel.getFontMetrics(_font) + dummyLabel.font = tooltipFont + val fontMetrics = dummyLabel.getFontMetrics(tooltipFont) + val lineHeight = fontMetrics.height // Calculate the approximate width of the text val textWidth = fontMetrics.stringWidth(text) @@ -877,7 +857,7 @@ object ReflectiveEditorView : View { isOpaque = true background = TOOLTIP_BACKGROUND foreground = Color.WHITE - font = _font + font = tooltipFont maximumSize = Dimension(maxWidth, Int.MAX_VALUE) preferredSize = Dimension(maxWidth, requiredHeight) } @@ -911,7 +891,6 @@ object ReflectiveEditorView : View { return parsePluginProperties(content) } } catch (e: Exception) { - System.out.println("Error reading properties for disabled plugin $pluginName: ${e.message}") } return null } @@ -967,18 +946,15 @@ object ReflectiveEditorView : View { loadedPluginsField.isAccessible = true val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *> - System.out.println("Found ${loadedPlugins.size} loaded plugins") for ((_, plugin) in loadedPlugins) { val pluginName = getPluginDirName(plugin as Plugin) loadedPluginNames.add(pluginName) - //System.out.println("Loaded plugin: $pluginName") } } catch (e: Exception) { e.printStackTrace() } - System.out.println("Returning loaded plugin names: ${loadedPluginNames.joinToString(", ")}") return loadedPluginNames } @@ -992,7 +968,6 @@ object ReflectiveEditorView : View { // Log the download URL for debugging val downloadUrl = PluginDownloadManager.getDownloadUrlForLogging(gitLabPlugin) - System.out.println("Download URL for plugin ${gitLabPlugin.path}: $downloadUrl") // Update plugin status to show downloading val updatedStatuses = pluginStatuses.map { @@ -1056,80 +1031,11 @@ object ReflectiveEditorView : View { }) } - // Method to start downloading multiple plugins - private fun startMultiplePluginDownloads(pluginStatuses: List) { - val gitLabPlugins = pluginStatuses.mapNotNull { it.gitLabPlugin } - - if (gitLabPlugins.isEmpty()) { - showToast(mainPanel, "No valid plugins to download", JOptionPane.ERROR_MESSAGE) - return - } - - // Update all plugin statuses to show downloading - val pluginNames = pluginStatuses.map { it.name }.toSet() - val updatedStatuses = pluginStatuses.map { - if (pluginNames.contains(it.name)) { - it.copy(isDownloading = true, downloadProgress = 0) - } else { - it - } - } - this.pluginStatuses = updatedStatuses - - // Refresh UI to show progress bars - SwingUtilities.invokeLater { - addPlugins(reflectiveEditorView!!) - } - - // Counter to track completed downloads - val completedCount = AtomicInteger(0) - val totalCount = gitLabPlugins.size - val failedPlugins = mutableListOf() - - // Start the downloads - PluginDownloadManager.downloadPlugins(gitLabPlugins) { pluginName, success, errorMessage -> - // Update progress in plugin statuses - val updatedStatuses = this.pluginStatuses.map { - if (it.name == pluginName) { - it.copy(isDownloading = false, downloadProgress = if (success) 100 else 0) - } else { - it - } - } - this.pluginStatuses = updatedStatuses - - // Track completion - if (!success) { - failedPlugins.add(pluginName) - } - - val completed = completedCount.incrementAndGet() - - // If all downloads are complete, show final message - if (completed == totalCount) { - SwingUtilities.invokeLater { - if (failedPlugins.isEmpty()) { - showToast(mainPanel, "All plugins downloaded successfully!", JOptionPane.INFORMATION_MESSAGE) - // Reload plugins to make the newly downloaded plugins available - PluginRepository.reloadPlugins() - } else { - val failedList = failedPlugins.joinToString(", ") - showToast(mainPanel, "Completed with errors. Failed plugins: $failedList", JOptionPane.WARNING_MESSAGE) - } - - // Refresh UI - addPlugins(reflectiveEditorView!!) - } - } - } - } - // Update plugin statuses by comparing installed and remote plugins private fun updatePluginStatuses() { val loadedPluginNames = getLoadedPluginNames() val statuses = mutableListOf() - System.out.println("Updating plugin statuses. Loaded plugins: ${loadedPluginNames.joinToString(", ")}") // Get disabled plugin names and their versions val disabledPluginInfo = getDisabledPluginInfo() @@ -1152,7 +1058,6 @@ object ReflectiveEditorView : View { val isDisabled = disabledPluginInfo.containsKey(pluginName) val disabledVersion = disabledPluginInfo[pluginName] - //System.out.println("Processing plugin: $pluginName, isLoaded: $isLoaded, isDisabled: $isDisabled, disabledVersion: $disabledVersion") // Check if this plugin is currently being downloaded val existingStatus = pluginStatuses.find { it.name == pluginName } @@ -1209,7 +1114,6 @@ object ReflectiveEditorView : View { } } - System.out.println("Updated plugin statuses. Total statuses: ${statuses.size}") pluginStatuses = statuses } @@ -1218,20 +1122,16 @@ object ReflectiveEditorView : View { var needsReload = false for (pluginName in loadedPluginNames) { if (disabledPluginInfo.containsKey(pluginName)) { - System.out.println("Found duplicate plugin: $pluginName. Deleting disabled version.") try { val disabledDir = File(pluginsDirectory, "disabled") val pluginDir = File(disabledDir, pluginName) if (pluginDir.exists() && pluginDir.isDirectory) { if (deleteRecursively(pluginDir)) { - System.out.println("Successfully deleted disabled version of $pluginName") needsReload = true } else { - System.out.println("Failed to delete disabled version of $pluginName") } } } catch (e: Exception) { - System.out.println("Error deleting disabled version of $pluginName: ${e.message}") } } } @@ -1257,10 +1157,8 @@ object ReflectiveEditorView : View { val content = propertiesFile.readText() val properties = parsePluginProperties(content) disabledPluginInfo[pluginDir.name] = properties.version - System.out.println("Found disabled plugin: ${pluginDir.name}, version: ${properties.version}") } } catch (e: Exception) { - System.out.println("Error reading properties for disabled plugin ${pluginDir.name}: ${e.message}") } } } diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/ViewLayoutHelpers.kt b/plugin-playground/src/main/kotlin/KondoKit/views/ViewLayoutHelpers.kt new file mode 100644 index 0000000..3a1f375 --- /dev/null +++ b/plugin-playground/src/main/kotlin/KondoKit/views/ViewLayoutHelpers.kt @@ -0,0 +1,90 @@ +package KondoKit.views + +import KondoKit.ViewConstants +import KondoKit.components.SearchField +import KondoKit.components.WidgetPanel +import KondoKit.plugin.Companion.VIEW_BACKGROUND_COLOR +import java.awt.Color +import java.awt.Component +import java.awt.Container +import java.awt.Dimension +import javax.swing.Box +import javax.swing.BoxLayout +import javax.swing.JPanel + +/** + * Utility helpers shared across view implementations to avoid duplicating + * common UI wiring such as search rows and vertical panel setup. + */ +object ViewLayoutHelpers { + + data class SearchFieldSection( + val wrapper: JPanel, + val searchField: SearchField + ) + + /** + * Creates a standard search field row with consistent sizing and styling. + */ + fun createSearchFieldSection( + parent: JPanel, + placeholderText: String, + viewName: String, + initialText: String = "", + fieldWidth: Int = ViewConstants.SEARCH_FIELD_SIZE.width, + fieldHeight: Int = ViewConstants.SEARCH_FIELD_SIZE.height, + onSearch: (String) -> Unit + ): SearchFieldSection { + val searchField = SearchField( + parentPanel = parent, + onSearch = onSearch, + placeholderText = placeholderText, + fieldWidth = fieldWidth, + fieldHeight = fieldHeight, + viewName = viewName + ) + + if (initialText.isNotBlank()) { + searchField.setText(initialText) + } + + val preferredSize = Dimension(fieldWidth, fieldHeight) + val wrapper = WidgetPanel( + widgetWidth = preferredSize.width, + widgetHeight = preferredSize.height, + addDefaultPadding = false + ).apply { + layout = BoxLayout(this, BoxLayout.X_AXIS) + background = VIEW_BACKGROUND_COLOR + alignmentX = Component.CENTER_ALIGNMENT + add(searchField) + } + + return SearchFieldSection(wrapper = wrapper, searchField = searchField) + } + + /** + * Provides a BoxLayout.Y_AXIS panel with the default view background. + */ + fun createVerticalPanel(background: Color = VIEW_BACKGROUND_COLOR): JPanel { + return JPanel().apply { + layout = BoxLayout(this, BoxLayout.Y_AXIS) + this.background = background + } + } +} + +fun Container.addVerticalSpacing(pixels: Int) { + add(Box.createVerticalStrut(pixels)) +} + +fun T.setFixedSize(width: Int, height: Int): T { + return setFixedSize(Dimension(width, height)) +} + +fun T.setFixedSize(size: Dimension): T { + preferredSize = size + minimumSize = size + maximumSize = size + return this +} diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt index 1d35880..ba2f875 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/XPTrackerView.kt @@ -7,6 +7,7 @@ import KondoKit.Helpers.formatNumber import KondoKit.Helpers.getProgressBarColor import KondoKit.Helpers.getSpriteId import KondoKit.SpriteToBufferedImage.getBufferedImageFromSprite +import KondoKit.ViewConstants import KondoKit.XPTable import KondoKit.components.PopupMenuComponent import KondoKit.components.ProgressBar @@ -64,7 +65,7 @@ object XPTrackerView : View, OnUpdateCallback, OnXPUpdateCallback { emptyMap() } - private val widgetFont = Font("RuneScape Small", Font.TRUETYPE_FONT, 16) + private val widgetFont = ViewConstants.FONT_RUNESCAPE_SMALL_16 private fun createPopupListener(popupMenu: JPopupMenu) = object : MouseAdapter() { override fun mousePressed(e: MouseEvent) {