From 3795ff25501db626e0ddd2a735be197085129277 Mon Sep 17 00:00:00 2001 From: Frank Hansen Date: Tue, 29 Oct 2024 23:22:49 +0100 Subject: [PATCH] Add overlay panel to show orders and components --- .../MasteringMixologyConfig.java | 190 +++++++++++++--- .../MasteringMixologyPanel.java | 207 ++++++++++++++++++ .../MasteringMixologyPlugin.java | 41 +++- .../masteringmixology/PotionComponent.java | 14 +- .../masteringmixology/PotionModifier.java | 19 +- .../ui/RichTextComponent.java | 87 ++++++++ .../masteringmixology/ui/TextElement.java | 21 ++ 7 files changed, 532 insertions(+), 47 deletions(-) create mode 100644 src/main/java/work/fking/masteringmixology/MasteringMixologyPanel.java create mode 100644 src/main/java/work/fking/masteringmixology/ui/RichTextComponent.java create mode 100644 src/main/java/work/fking/masteringmixology/ui/TextElement.java diff --git a/src/main/java/work/fking/masteringmixology/MasteringMixologyConfig.java b/src/main/java/work/fking/masteringmixology/MasteringMixologyConfig.java index e45a8f1..25c6c45 100644 --- a/src/main/java/work/fking/masteringmixology/MasteringMixologyConfig.java +++ b/src/main/java/work/fking/masteringmixology/MasteringMixologyConfig.java @@ -1,10 +1,12 @@ package work.fking.masteringmixology; +import net.runelite.client.config.Alpha; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; import net.runelite.client.config.Notification; +import net.runelite.client.ui.overlay.components.ComponentConstants; import java.awt.Color; @@ -15,13 +17,7 @@ public interface MasteringMixologyConfig extends Config { String CONFIG_GROUP = "masteringmixology"; - @ConfigSection( - name = "Highlights", - description = "Highlighting related configuration", - position = 10 - ) - String HIGHLIGHTS = "Highlights"; - + // General Configurations @ConfigItem( keyName = "potionOrderSorting", name = "Order sorting", @@ -33,16 +29,37 @@ default PotionOrderSorting potionOrderSorting() { } @ConfigItem( + keyName = "displayResin", + name = "Display resin amount", + description = "Display total resin amounts", + position = 2 + ) + default boolean displayResin() { + return false; + } + + + // Highlights Section + @ConfigSection( + name = "Highlights", + description = "Highlighting related configuration", + position = 10 + ) + String HIGHLIGHTS = "Highlights"; + + @ConfigItem( + section = HIGHLIGHTS, keyName = "highlightLevers", name = "Highlight levers", description = "Highlight levers", - position = 2 + position = 1 ) default boolean highlightLevers() { return true; } @ConfigItem( + section = HIGHLIGHTS, keyName = "highlightStations", name = "Highlight stations", description = "Toggles alchemical station highlighting on or off", @@ -53,80 +70,77 @@ default boolean highlightStations() { } @ConfigItem( + section = HIGHLIGHTS, keyName = "highlightQuickActionEvents", name = "Highlight quick-action events", description = "Toggles station quick-action events highlighting on or off", - position = 2 + position = 3 ) default boolean highlightQuickActionEvents() { return true; } @ConfigItem( + section = HIGHLIGHTS, keyName = "identifyPotions", name = "Identify potions", description = "Identify potions in your inventory", - position = 2 + position = 4 ) default boolean identifyPotions() { return true; } @ConfigItem( - keyName = "displayResin", - name = "Display resin amount", - description = "Display total resin amounts", - position = 2 - ) - default boolean displayResin() { - return false; - } - - @ConfigItem( + section = HIGHLIGHTS, keyName = "stationHighlightColor", name = "Station color", description = "Configures the default station highlight color", - position = 3 + position = 5 ) default Color stationHighlightColor() { return Color.MAGENTA; } @ConfigItem( + section = HIGHLIGHTS, keyName = "stationQuickActionHighlightColor", name = "Quick-action color", description = "Configures the station quick-action highlight color", - position = 4 + position = 6 ) default Color stationQuickActionHighlightColor() { return Color.GREEN; } @ConfigItem( + section = HIGHLIGHTS, keyName = "notifyDigweed", name = "Notify DigWeed", description = "Toggles digweed notifications on or off", - position = 5 + position = 7 ) default Notification notifyDigWeed() { return Notification.ON; } @ConfigItem( + section = HIGHLIGHTS, keyName = "highlightDigweed", name = "Highlight DigWeed", description = "Toggles digweed highlighting on or off", - position = 6 + position = 8 ) default boolean highlightDigWeed() { return true; } @ConfigItem( + section = HIGHLIGHTS, keyName = "digweedHighlightColor", name = "DigWeed color", description = "Configures the digweed highlight color", - position = 7 + position = 9 ) default Color digweedHighlightColor() { return Color.GREEN; @@ -136,7 +150,8 @@ default Color digweedHighlightColor() { section = HIGHLIGHTS, keyName = "highlightBorderWidth", name = "Border width", - description = "Configures the border width of the object highlights" + description = "Configures the border width of the object highlights", + position = 10 ) default int highlightBorderWidth() { return 2; @@ -146,9 +161,130 @@ default int highlightBorderWidth() { section = HIGHLIGHTS, keyName = "highlightFeather", name = "Feather", - description = "Configures the amount of 'feathering' to be applied to the object highlights" + description = "Configures the amount of 'feathering' to be applied to the object highlights", + position = 11 ) default int highlightFeather() { return 1; } + + + // Panel Section + @ConfigSection( + name = "Panel", + description = "Panel related configuration", + position = 20 + ) + String PANEL_SECTION = "Panel"; + + @ConfigItem( + section = PANEL_SECTION, + keyName = "displayPanel", + name = "Display Overlay Panel", + description = "Toggles the overlay panel visibility", + position = 1 + ) + default boolean displayPanel() { + return false; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "displayOrdersInPanel", + name = "Display Potion Orders", + description = "Show potion orders in the panel", + position = 2 + ) + default boolean displayOrdersInPanel() { + return true; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "displayPasteInPanel", + name = "Display Paste Amounts", + description = "Show paste amounts in the panel", + position = 3 + ) + default boolean displayPasteInPanel() { + return true; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "displayResinInPanel", + name = "Display Resin Amounts", + description = "Show resin amounts in the panel", + position = 4 + ) + default boolean displayResinInPanel() { + return true; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "showPanelInLab", + name = "Show Panel in Lab", + description = "Show the panel when inside the lab UI", + position = 5 + ) + default boolean showPanelInLab() { + return false; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "orderIconSize", + name = "Order Icon Size", + description = "Set the icon size for potion orders", + position = 6 + ) + default int orderIconSize() { + return 20; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "orderFontSize", + name = "Order Font Size", + description = "Set the font size for potion orders", + position = 7 + ) + default int orderFontSize() { + return 16; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "resinIconSize", + name = "Resin Icon Size", + description = "Set the icon size for resin amounts", + position = 8 + ) + default int resinIconSize() { + return 20; + } + + @ConfigItem( + section = PANEL_SECTION, + keyName = "resinFontSize", + name = "Resin Font Size", + description = "Set the font size for resin amounts", + position = 9 + ) + default int resinFontSize() { + return 16; + } + + @Alpha + @ConfigItem( + section = PANEL_SECTION, + keyName = "panelBackgroundColor", + name = "Panel Background Color", + description = "Set the background color for the panel", + position = 10 + ) + default Color panelBackgroundColor() { + return ComponentConstants.STANDARD_BACKGROUND_COLOR; + } } diff --git a/src/main/java/work/fking/masteringmixology/MasteringMixologyPanel.java b/src/main/java/work/fking/masteringmixology/MasteringMixologyPanel.java new file mode 100644 index 0000000..ff9da9a --- /dev/null +++ b/src/main/java/work/fking/masteringmixology/MasteringMixologyPanel.java @@ -0,0 +1,207 @@ +package work.fking.masteringmixology; + +import net.runelite.api.Client; +import net.runelite.client.game.ItemManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.overlay.OverlayPanel; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.components.ComponentOrientation; +import net.runelite.client.ui.overlay.components.ImageComponent; +import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; +import net.runelite.client.ui.overlay.components.SplitComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; +import net.runelite.client.util.ImageUtil; +import work.fking.masteringmixology.ui.RichTextComponent; +import work.fking.masteringmixology.ui.TextElement; + +import javax.inject.Inject; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MasteringMixologyPanel extends OverlayPanel { + + private final Client client; + private final MasteringMixologyPlugin plugin; + private final MasteringMixologyConfig config; + private final SpriteManager spriteManager; + private final ItemManager itemManager; + private final Map spriteCache = new HashMap<>(); + + @Inject + public MasteringMixologyPanel(Client client, MasteringMixologyPlugin plugin, MasteringMixologyConfig config, SpriteManager spriteManager, ItemManager itemManager) { + this.client = client; + this.plugin = plugin; + this.config = config; + this.spriteManager = spriteManager; + this.itemManager = itemManager; + + setPosition(OverlayPosition.BOTTOM_LEFT); + setMovable(true); + + panelComponent.setOrientation(ComponentOrientation.VERTICAL); + panelComponent.setGap(new Point(0, 5)); + panelComponent.setBackgroundColor(config.panelBackgroundColor()); + } + + @Override + public Dimension render(Graphics2D graphics) { + // Hide the overlay if conditions are not met + if (!plugin.isInLabRegion() || !config.displayPanel() || (!config.showPanelInLab() && plugin.isInLab())) { + return null; + } + + // Display Potion Orders if enabled + if (config.displayOrdersInPanel()) { + displayOrders(); + } + + // Display Resin and Paste amounts + if (config.displayResinInPanel() || config.displayPasteInPanel()) { + displayResinAndPaste(); + } + + return super.render(graphics); + } + + public void updateBackgroundColor() { + panelComponent.setBackgroundColor(config.panelBackgroundColor()); + } + + private void displayOrders() { + // Title + panelComponent.getChildren().add(TitleComponent.builder() + .text("Potion Orders") + .color(Color.ORANGE) + .build()); + + for (PotionOrder order : plugin.getPotionOrders()) { + addPotionOrderComponent(order); + } + } + + private void addPotionOrderComponent(PotionOrder order) { + BufferedImage modifierIcon = getScaledSprite(order.potionModifier().spriteId(), config.orderIconSize()); + String potionName = itemManager.getItemComposition(order.potionType().itemId()).getName(); + Font font = getFont(config.orderFontSize()); + + // Build the text elements + List textElements = new ArrayList<>(); + + // potionName in white + textElements.add(new TextElement(potionName + " (", Color.WHITE)); + + // For each component, add the character with its color + for (PotionComponent component : order.potionType().components()) { + textElements.add(new TextElement(String.valueOf(component.character()), Color.decode("#" + component.color()))); + } + + // Add closing parenthesis in white + textElements.add(new TextElement(")", Color.WHITE)); + + // Create RichTextComponent with drop shadow + RichTextComponent richTextComponent = new RichTextComponent(textElements, font, true); + + // Create ImageComponent for the icon + ImageComponent iconComponent = new ImageComponent(modifierIcon); + + // Combine icon and text horizontally using SplitComponent + SplitComponent orderComponent = SplitComponent.builder() + .first(iconComponent) + .second(richTextComponent) + .orientation(ComponentOrientation.HORIZONTAL) + .gap(new Point(5, 0)) + .build(); + + // Add to the panel + panelComponent.getChildren().add(orderComponent); + } + + private void displayResinAndPaste() { + // Create components for each resin and paste + LayoutableRenderableEntity moxComponent = createResinAndPasteComponent(PotionComponent.MOX, MasteringMixologyPlugin.MOX_PASTE_VARBIT, MasteringMixologyPlugin.VARP_MOX_RESIN); + LayoutableRenderableEntity agaComponent = createResinAndPasteComponent(PotionComponent.AGA, MasteringMixologyPlugin.AGA_PASTE_VARBIT, MasteringMixologyPlugin.VARP_AGA_RESIN); + LayoutableRenderableEntity lyeComponent = createResinAndPasteComponent(PotionComponent.LYE, MasteringMixologyPlugin.LYE_PASTE_VARBIT, MasteringMixologyPlugin.VARP_LYE_RESIN); + + // Combine them horizontally + SplitComponent resinSplit = SplitComponent.builder() + .first(moxComponent) + .second(agaComponent) + .orientation(ComponentOrientation.HORIZONTAL) + .gap(new Point(10, 0)) + .build(); + + resinSplit = SplitComponent.builder() + .first(resinSplit) + .second(lyeComponent) + .orientation(ComponentOrientation.HORIZONTAL) + .gap(new Point(10, 0)) + .build(); + + panelComponent.getChildren().add(resinSplit); + } + + private LayoutableRenderableEntity createResinAndPasteComponent(PotionComponent component, int pasteVarbitId, int resinVarpId) { + List amountComponents = new ArrayList<>(); + + Font font = getFont(config.resinFontSize()); + + if (config.displayPasteInPanel()) { + int pasteAmount = client.getVarbitValue(pasteVarbitId); + RichTextComponent pasteTextComponent = new RichTextComponent(String.valueOf(pasteAmount), Color.WHITE, font, true); + amountComponents.add(pasteTextComponent); + } + + if (config.displayResinInPanel()) { + int resinAmount = client.getVarpValue(resinVarpId); + RichTextComponent resinTextComponent = new RichTextComponent(String.valueOf(resinAmount), Color.decode("#" + component.color()), font, true); + amountComponents.add(resinTextComponent); + } + + // Combine amounts vertically + LayoutableRenderableEntity amountsSplit = amountComponents.get(0); + for (int i = 1; i < amountComponents.size(); i++) { + amountsSplit = SplitComponent.builder() + .first(amountsSplit) + .second(amountComponents.get(i)) + .orientation(ComponentOrientation.VERTICAL) + .gap(new Point(0, 2)) + .build(); + } + + // Create ImageComponent for the icon + BufferedImage componentIcon = getScaledSprite(component.spriteId(), config.resinIconSize()); + ImageComponent iconComponent = new ImageComponent(componentIcon); + + // Combine icon and amounts horizontally + return SplitComponent.builder() + .first(iconComponent) + .second(amountsSplit) + .orientation(ComponentOrientation.HORIZONTAL) + .gap(new Point(5, 0)) + .build(); + } + + private BufferedImage getScaledSprite(int spriteId, int size) { + String key = spriteId + "-" + size; + return spriteCache.computeIfAbsent(key, k -> { + BufferedImage sprite = spriteManager.getSprite(spriteId, 0); + if (sprite != null) { + return ImageUtil.resizeImage(sprite, size, size, true); + } + return null; + }); + } + + private Font getFont(int size) { + return FontManager.getRunescapeFont().deriveFont((float) size); + } +} diff --git a/src/main/java/work/fking/masteringmixology/MasteringMixologyPlugin.java b/src/main/java/work/fking/masteringmixology/MasteringMixologyPlugin.java index 4b8172f..08a7b00 100644 --- a/src/main/java/work/fking/masteringmixology/MasteringMixologyPlugin.java +++ b/src/main/java/work/fking/masteringmixology/MasteringMixologyPlugin.java @@ -5,6 +5,7 @@ import net.runelite.api.FontID; import net.runelite.api.GameState; import net.runelite.api.InventoryID; +import net.runelite.api.Player; import net.runelite.api.TileObject; import net.runelite.api.coords.LocalPoint; import net.runelite.api.events.GameStateChanged; @@ -60,9 +61,13 @@ public class MasteringMixologyPlugin extends Plugin { private static final int VARBIT_POTION_ORDER_3 = 11319; private static final int VARBIT_POTION_MODIFIER_3 = 11320; - private static final int VARP_LYE_RESIN = 4414; - private static final int VARP_AGA_RESIN = 4415; - private static final int VARP_MOX_RESIN = 4416; + public static final int MOX_PASTE_VARBIT = 11431; + public static final int AGA_PASTE_VARBIT = 11432; + public static final int LYE_PASTE_VARBIT = 11433; + + static final int VARP_LYE_RESIN = 4414; + static final int VARP_AGA_RESIN = 4415; + static final int VARP_MOX_RESIN = 4416; private static final int VARBIT_ALEMBIC_PROGRESS = 11328; private static final int VARBIT_AGITATOR_PROGRESS = 11329; @@ -86,6 +91,9 @@ public class MasteringMixologyPlugin extends Plugin { private static final int COMPONENT_POTION_ORDERS_GROUP_ID = 882; private static final int COMPONENT_POTION_ORDERS = COMPONENT_POTION_ORDERS_GROUP_ID << 16 | 2; + private static final int LABS_REGION_ID = 5521; + private static final int LABS_REGION_PLANE = 0; + @Inject private Client client; @@ -107,6 +115,9 @@ public class MasteringMixologyPlugin extends Plugin { @Inject private InventoryPotionOverlay potionOverlay; + @Inject + private MasteringMixologyPanel masteringMixologyPanel; + private final Map highlightedObjects = new LinkedHashMap<>(); private List potionOrders = Collections.emptyList(); private boolean inLab = false; @@ -129,6 +140,16 @@ public boolean isInLab() { return inLab; } + /** + * @return true if the player is in the labs region (the area where the minigame takes place) + * the isInlab method only checks if they are inside the actual lab room where the UI is active + */ + public boolean isInLabRegion() { + Player player = client.getLocalPlayer(); + return player != null && player.getWorldLocation().getRegionID() == LABS_REGION_ID + && player.getWorldLocation().getPlane() == LABS_REGION_PLANE; + } + @Provides MasteringMixologyConfig provideConfig(ConfigManager configManager) { return configManager.getConfig(MasteringMixologyConfig.class); @@ -138,6 +159,7 @@ MasteringMixologyConfig provideConfig(ConfigManager configManager) { protected void startUp() { overlayManager.add(overlay); overlayManager.add(potionOverlay); + overlayManager.add(masteringMixologyPanel); if (client.getGameState() == GameState.LOGGED_IN) { clientThread.invokeLater(this::initialize); @@ -148,6 +170,7 @@ protected void startUp() { protected void shutDown() { overlayManager.remove(overlay); overlayManager.remove(potionOverlay); + overlayManager.remove(masteringMixologyPanel); inLab = false; } @@ -186,6 +209,10 @@ public void onConfigChanged(ConfigChanged event) { clientThread.invokeLater(this::updatePotionOrders); } + if (event.getKey().equals("panelBackgroundColor")) { + masteringMixologyPanel.updateBackgroundColor(); + } + if (!config.highlightStations()) { unHighlightAllStations(); } @@ -523,7 +550,7 @@ private void unHighlightLevers() { private void updatePotionOrders() { LOGGER.debug("Updating potion orders"); - potionOrders = getPotionOrders(); + potionOrders = getPotionOrdersFromVarbit(); var potionOrderSorting = config.potionOrderSorting(); @@ -590,7 +617,11 @@ private void tryHighlightNextStation() { } } - private List getPotionOrders() { + public List getPotionOrders() { + return potionOrders; + } + + private List getPotionOrdersFromVarbit() { var potionOrders = new ArrayList(3); for (int orderIdx = 0; orderIdx < 3; orderIdx++) { diff --git a/src/main/java/work/fking/masteringmixology/PotionComponent.java b/src/main/java/work/fking/masteringmixology/PotionComponent.java index b2890b9..7030089 100644 --- a/src/main/java/work/fking/masteringmixology/PotionComponent.java +++ b/src/main/java/work/fking/masteringmixology/PotionComponent.java @@ -1,16 +1,18 @@ package work.fking.masteringmixology; public enum PotionComponent { - AGA('A', "00e676"), - LYE('L', "e91e63"), - MOX('M', "03a9f4"); + AGA('A', "00e676", 5667), + LYE('L', "e91e63", 5668), + MOX('M', "03a9f4", 5666); private final char character; private final String color; + private final int spriteId; - PotionComponent(char character, String color) { + PotionComponent(char character, String color, int spriteId) { this.character = character; this.color = color; + this.spriteId = spriteId; } public char character() { @@ -20,4 +22,8 @@ public char character() { public String color() { return color; } + + public int spriteId() { + return spriteId; + } } diff --git a/src/main/java/work/fking/masteringmixology/PotionModifier.java b/src/main/java/work/fking/masteringmixology/PotionModifier.java index 5216c42..d31e08e 100644 --- a/src/main/java/work/fking/masteringmixology/PotionModifier.java +++ b/src/main/java/work/fking/masteringmixology/PotionModifier.java @@ -1,21 +1,18 @@ package work.fking.masteringmixology; public enum PotionModifier { - // Clicking the quick-time event on the Agitator gives 14 experience, this event can happen 1-2 times - HOMOGENOUS(AlchemyObject.AGITATOR, 21), - // Each click on the Retort gives 2 experience for a max of 10 clicks - CONCENTRATED(AlchemyObject.RETORT, 20), - // Clicking the quick-time event on the Alembic gives 14 experience - CRYSTALISED(AlchemyObject.ALEMBIC, 14); + HOMOGENOUS(AlchemyObject.AGITATOR, 5674), + CONCENTRATED(AlchemyObject.RETORT, 5672), + CRYSTALISED(AlchemyObject.ALEMBIC, 5673); private static final PotionModifier[] TYPES = PotionModifier.values(); private final AlchemyObject alchemyObject; - private final int quickActionExperience; + private final int spriteId; - PotionModifier(AlchemyObject alchemyObject, int quickActionExperience) { + PotionModifier(AlchemyObject alchemyObject, int spriteId) { this.alchemyObject = alchemyObject; - this.quickActionExperience = quickActionExperience; + this.spriteId = spriteId; } public static PotionModifier from(int potionModifierId) { @@ -29,7 +26,7 @@ public AlchemyObject alchemyObject() { return alchemyObject; } - public int quickActionExperience() { - return quickActionExperience; + public int spriteId() { + return spriteId; } } diff --git a/src/main/java/work/fking/masteringmixology/ui/RichTextComponent.java b/src/main/java/work/fking/masteringmixology/ui/RichTextComponent.java new file mode 100644 index 0000000..774cb9b --- /dev/null +++ b/src/main/java/work/fking/masteringmixology/ui/RichTextComponent.java @@ -0,0 +1,87 @@ +package work.fking.masteringmixology.ui; + +import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.List; + +public class RichTextComponent implements LayoutableRenderableEntity { + private final List textElements; + private final Font font; + private final boolean drawShadow; + private Point preferredLocation = new Point(); + private final Rectangle bounds = new Rectangle(); + + public RichTextComponent(List textElements, Font font, boolean drawShadow) { + this.textElements = textElements; + this.font = font; + this.drawShadow = drawShadow; + } + + public RichTextComponent(String text, Color color, Font font, boolean drawShadow) { + this.textElements = List.of(new TextElement(text, color)); + this.font = font; + this.drawShadow = drawShadow; + } + + @Override + public Dimension render(Graphics2D graphics) { + Font originalFont = graphics.getFont(); + if (font != null) { + graphics.setFont(font); + } + + FontMetrics metrics = graphics.getFontMetrics(); + + int x = preferredLocation.x; + int y = preferredLocation.y + metrics.getAscent(); + + for (TextElement element : textElements) { + // Draw shadow if enabled + if (drawShadow) { + graphics.setColor(Color.BLACK); + graphics.drawString(element.getText(), x + 1, y + 1); + } + + // Draw text + graphics.setColor(element.getColor()); + graphics.drawString(element.getText(), x, y); + + x += metrics.stringWidth(element.getText()); + } + + int width = x - preferredLocation.x; + int height = metrics.getHeight(); + + bounds.setLocation(preferredLocation); + bounds.setSize(width, height); + + // Restore the original font + if (font != null) { + graphics.setFont(originalFont); + } + + return new Dimension(width, height); + } + + @Override + public Rectangle getBounds() { + return bounds; + } + + @Override + public void setPreferredSize(Dimension dimension) { + // Not used + } + + @Override + public void setPreferredLocation(Point preferredLocation) { + this.preferredLocation = preferredLocation; + } +} diff --git a/src/main/java/work/fking/masteringmixology/ui/TextElement.java b/src/main/java/work/fking/masteringmixology/ui/TextElement.java new file mode 100644 index 0000000..94d9d2f --- /dev/null +++ b/src/main/java/work/fking/masteringmixology/ui/TextElement.java @@ -0,0 +1,21 @@ +package work.fking.masteringmixology.ui; + +import java.awt.Color; + +public class TextElement { + private final String text; + private final Color color; + + public TextElement(String text, Color color) { + this.text = text; + this.color = color; + } + + public String getText() { + return text; + } + + public Color getColor() { + return color; + } +}