diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotTopLevelConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotTopLevelConfigPanel.java index effb74108b6..323683d5b8d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotTopLevelConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/ui/MicrobotTopLevelConfigPanel.java @@ -6,6 +6,7 @@ import net.runelite.client.ui.components.materialtabs.MaterialTab; import net.runelite.client.ui.components.materialtabs.MaterialTabGroup; import net.runelite.client.util.ImageUtil; +import net.runelite.client.ui.FontManager; import javax.inject.Inject; import javax.inject.Provider; @@ -13,6 +14,7 @@ import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; +import java.awt.image.BufferedImage; @Singleton public class MicrobotTopLevelConfigPanel extends PluginPanel { @@ -28,6 +30,174 @@ public class MicrobotTopLevelConfigPanel extends PluginPanel { private PluginPanel current; private boolean removeOnTabChange; + // -- BEGIN: NEW badge ( to be removed once we migrate all plugins to Hub) -- + private final MaterialTab hubTab; + private JPanel glassPane; + private JLabel newBadgeOverlay; + private Timer newBadgeTimer; + private Component previousGlassPane; + + private void createNewBadgeOverlay() { + if (hubTab == null) return; + + glassPane = new JPanel() { + @Override + public boolean contains(int x, int y) { + return false; + } + @Override + protected void paintChildren(Graphics g) { + super.paintChildren(g); + } + }; + glassPane.setOpaque(false); + glassPane.setLayout(null); + + newBadgeOverlay = new JLabel() { + private final BufferedImage newBadge = ImageUtil.loadImageResource(MicrobotTopLevelConfigPanel.class, "NEW.png"); + private final long startTime = System.currentTimeMillis(); + + @Override + protected void paintComponent(Graphics g) { + if (newBadge == null) { + System.out.println("DEBUG: newBadge image is null, cannot paint"); + SwingUtilities.invokeLater(MicrobotTopLevelConfigPanel.this::cleanupNewBadge); + return; + } + + Graphics2D g2d = (Graphics2D) g.create(); + try { + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + + double time = (System.currentTimeMillis() - startTime) % 3000; + double scale; + + if (time < 300) { + double pulsePhase = (time / 300.0) * Math.PI; + double pulseAmount = Math.sin(pulsePhase) * 0.06; + scale = 0.7 + pulseAmount; + } else { + scale = 0.7; + } + + double scaledWidth = newBadge.getWidth() * scale; + double scaledHeight = newBadge.getHeight() * scale; + + double x = (getWidth() - scaledWidth) / 2.0; + double y = (getHeight() - scaledHeight) / 2.0; + + g2d.rotate(Math.toRadians(20), getWidth() / 2.0, getHeight() / 2.0); + g2d.drawImage(newBadge, (int)x, (int)y, (int)(x + scaledWidth), (int)(y + scaledHeight), + 0, 0, newBadge.getWidth(), newBadge.getHeight(), null); + + } finally { + g2d.dispose(); + } + } + }; + + newBadgeOverlay.setOpaque(false); + newBadgeOverlay.setSize(32, 32); + newBadgeOverlay.setVisible(false); + glassPane.add(newBadgeOverlay); + + SwingUtilities.invokeLater(() -> { + JRootPane rootPane = SwingUtilities.getRootPane(this); + if (rootPane != null) { + if (previousGlassPane == null) { + previousGlassPane = rootPane.getGlassPane(); + } + rootPane.setGlassPane(glassPane); + glassPane.setVisible(true); + updateBadgePosition(); + } + }); + + newBadgeTimer = new Timer(50, e -> { + if (hubTab != null && hubTab.isShowing() && glassPane.isVisible()) { + updateBadgePosition(); + newBadgeOverlay.setVisible(true); + newBadgeOverlay.repaint(); + } else { + newBadgeOverlay.setVisible(false); + } + }); + newBadgeTimer.start(); + } + + private void updateBadgePosition() { + if (newBadgeOverlay == null || hubTab == null || glassPane == null) return; + + try { + Point topRight = SwingUtilities.convertPoint(hubTab, hubTab.getWidth(), 0, glassPane); + int x = topRight.x - 25; + int y = topRight.y - 12; + newBadgeOverlay.setLocation(x, y); + } catch (Exception ex) { + System.out.println("DEBUG: Exception in updateBadgePosition: " + ex); + } + } + + private void cleanupNewBadge() { + if (newBadgeTimer != null) { + newBadgeTimer.stop(); + newBadgeTimer = null; + } + if (glassPane != null) { + glassPane.setVisible(false); + JRootPane rootPane = SwingUtilities.getRootPane(this); + if (rootPane != null && previousGlassPane != null) { + rootPane.setGlassPane(previousGlassPane); + previousGlassPane = null; + } + glassPane = null; + } + newBadgeOverlay = null; + } + // -- END: NEW badge ( to be removed once we migrate all plugins to Hub) -- + + /** + * Creates a simple text-based icon for tabs. + * @param text {@link String} Text to display + * @param textColor {@link Color} Color of the text + * @param backgroundColor {@link Color} Background color (can be null for transparent) + * @param width Width of the icon + * @param height Height of the icon + * @return ImageIcon {@link ImageIcon} with rendered text + */ + private ImageIcon createTextIcon(String text, Color textColor, Color backgroundColor, int width, int height) { + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = image.createGraphics(); + + // Enable antialiasing for smoother text + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + // Set background (optional, can be transparent) + if (backgroundColor != null) { + g2d.setColor(backgroundColor); + g2d.fillRect(0, 0, width, height); + } + + // Set font and text color + g2d.setFont(FontManager.getRunescapeBoldFont()); + g2d.setColor(textColor); + + // Calculate text position to center it + FontMetrics fm = g2d.getFontMetrics(); + int textWidth = fm.stringWidth(text); + int textHeight = fm.getAscent(); + + int x = (width - textWidth) / 2; + int y = (height - textHeight) / 2 + textHeight; + + g2d.drawString(text, x, y); + g2d.dispose(); + + return new ImageIcon(image); + } + @Inject MicrobotTopLevelConfigPanel( EventBus eventBus, @@ -51,46 +221,48 @@ public class MicrobotTopLevelConfigPanel extends PluginPanel { add(content, BorderLayout.CENTER); this.pluginListPanel = pluginListPanel; - pluginListPanelTab = addTab(pluginListPanel.getMuxer(), "microbot_config_icon_lg.png", "Microbot Plugins"); - addTab(microbotPluginHubPanelProvider, "plugin_hub_icon.png", "Microbot Hub"); + // Create text-based icons instead of using image files for better clarity + ImageIcon installedIcon = createTextIcon("Installed", Color.YELLOW, null, 80, 32); + ImageIcon hubIcon = createTextIcon("Plugin Hub", Color.YELLOW, null, 80, 32); + + pluginListPanelTab = addTab(pluginListPanel.getMuxer(), installedIcon, "Installed Microbot Plugins"); + hubTab = addTab(microbotPluginHubPanelProvider, hubIcon, "Microbot Hub"); tabGroup.select(pluginListPanelTab); + // Create NEW badge overlay after UI is initialized (remove after migrating all plugins to hub) + SwingUtilities.invokeLater(this::createNewBadgeOverlay); } - private MaterialTab addTab(PluginPanel panel, String image, String tooltip) { - MaterialTab mt = new MaterialTab( - new ImageIcon(ImageUtil.loadImageResource(MicrobotTopLevelConfigPanel.class, image)), - tabGroup, null); + private MaterialTab addTab(PluginPanel panel, ImageIcon icon, String tooltip) { + MaterialTab mt = new MaterialTab(icon, tabGroup, null); mt.setToolTipText(tooltip); tabGroup.addTab(mt); - content.add(image, panel.getWrappedPanel()); + content.add(tooltip, panel.getWrappedPanel()); // Use tooltip as unique key instead of image name eventBus.register(panel); mt.setOnSelectEvent(() -> { - switchTo(image, panel, false); + switchTo(tooltip, panel, false); return true; }); return mt; } - private MaterialTab addTab(Provider panelProvider, String image, String tooltip) { - MaterialTab mt = new MaterialTab( - new ImageIcon(ImageUtil.loadImageResource(MicrobotTopLevelConfigPanel.class, image)), - tabGroup, null); + private MaterialTab addTab(Provider panelProvider, ImageIcon icon, String tooltip) { + MaterialTab mt = new MaterialTab(icon, tabGroup, null); mt.setToolTipText(tooltip); tabGroup.addTab(mt); mt.setOnSelectEvent(() -> { PluginPanel panel = panelProvider.get(); - content.add(image, panel.getWrappedPanel()); + content.add(tooltip, panel.getWrappedPanel()); eventBus.register(panel); - switchTo(image, panel, true); + switchTo(tooltip, panel, true); return true; }); return mt; @@ -121,12 +293,20 @@ private void switchTo(String cardName, PluginPanel panel, boolean removeOnTabCha public void onActivate() { active = true; current.onActivate(); + // BEGIN: NEW badge readd code (remove once we migrate all plugins to hub) + if (newBadgeTimer == null || glassPane == null) { + SwingUtilities.invokeLater(this::createNewBadgeOverlay); + } + // END: NEW badge readd code (remove once we migrate all plugins to hub) } @Override public void onDeactivate() { active = false; current.onDeactivate(); + // BEGIN: NEW badge clean up code (remove once we migrate all plugins to hub) + cleanupNewBadge(); + // END: NEW badge clean up code (remove once we migrate all plugins to hub) } public void openConfigurationPanel(String name) { @@ -143,4 +323,4 @@ public void openWithFilter(String filter) { tabGroup.select(pluginListPanelTab); pluginListPanel.openWithFilter(filter); } -} \ No newline at end of file +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/ui/NEW.png b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/ui/NEW.png new file mode 100644 index 00000000000..0ba54454803 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/microbot/ui/NEW.png differ