Buttons
Overview
Apollo provides a shared button system in the com.lunarclient.apollo.common.button package. The abstract
ApolloButton carries everything a clickable GUI button needs: content, tooltip, colors, shape, size and a
click action; independently of where the button is displayed. Each surface then defines the placement.
Two surfaces are available today: the inventory module's inventory buttons, placed in two fixed-size boxes on each side of the player inventory, and the chat module's chat buttons, placed in a strip between the chat input and the chat log.
Buttons are always built through a surface type (e.g. InventoryButton.builder()), which adds the surface's
placement properties and defines the container the button position is measured against. There is no
standalone ApolloButton.builder().
ApolloButton Properties
Every surface builder inherits the following properties from ApolloButton.
public abstract class ApolloButton {
/**
* Returns the button {@link String} id, unique within a single
* display batch.
*
* <p>Displaying another button with the same id replaces the
* previous one.</p>
*
* @since 1.2.9
*/
@NotNull String id;
/**
* Returns the {@link HudPosition} of this button, relative to the
* top-left corner of the container it is placed in.
*
* <p>The button must fit inside the container: {@code 0 <= x},
* {@code 0 <= y}, {@code x + width <= container width} and
* {@code y + height <= container height}. See the surface type for
* its container dimensions (e.g. {@code InventoryButton#BOX_WIDTH}).</p>
*
* @since 1.2.9
*/
@NotNull HudPosition position;
/**
* Returns the {@link ApolloButtonSize} of this button.
*
* <p>Use one of the surface's suggested sizes (e.g.
* {@code InventoryButton.SIZE_MEDIUM}) or a fully custom
* {@link ApolloButtonSize#of(float, float)}.</p>
*
* @return the button size
* @since 1.2.9
*/
@NotNull ApolloButtonSize size;
/**
* Returns the {@link ApolloButtonShape} of this button.
*
* @since 1.2.9
*/
@NotNull ApolloButtonShape shape;
/**
* Returns the {@link ApolloButtonContent} rendered inside this button.
*
* <p>Built via {@code ApolloButtonContent.builder()}, appending static
* components, icons or live per-player parts that are re-resolved
* whenever the content is sent (see the owning module's live button
* broadcast option, e.g. {@code InventoryModule#BROADCAST_LIVE_BUTTONS}).</p>
*
* @return the button content
* @since 1.2.9
*/
@NotNull ApolloButtonContent content;
/**
* Returns the {@link ApolloButtonTooltip} shown while this button is hovered.
*
* <p>Built via {@code ApolloButtonTooltip.of(...)} for static lines,
* or {@code ApolloButtonTooltip.live(...)} for per-player lines that
* are re-resolved whenever the button content is sent (see the owning
* module's live button broadcast option, e.g.
* {@code InventoryModule#BROADCAST_LIVE_BUTTONS}).</p>
*
* @return the tooltip, or {@code null} for no tooltip
* @since 1.2.9
*/
@Builder.Default
@Nullable ApolloButtonTooltip tooltip = null;
/**
* Returns the {@link ApolloButtonAction} executed when this button
* is clicked.
*
* <p>Built via {@link ApolloButtonAction#runCommand(String)},
* {@link ApolloButtonAction#openUrl(String)} or
* {@link ApolloButtonAction#clientAction(ApolloButtonClientAction)}.</p>
*
* @return the click action, or {@code null}
* @since 1.2.9
*/
@Builder.Default
@Nullable ApolloButtonAction onClick = null;
/**
* Returns the background {@link Color} used while this button is hovered.
*
* @return the hovered background color, or {@code null}
* @since 1.2.9
*/
@Builder.Default
@Nullable Color hoveredBackgroundColor = null;
/**
* Returns the border {@link Color} used while this button is hovered.
*
* @return the hovered border color, or {@code null}
* @since 1.2.9
*/
@Builder.Default
@Nullable Color hoveredBorderColor = null;
/**
* Returns the background {@link Color} of this button.
*
* @return the background color
* @since 1.2.9
*/
public abstract Color getBackgroundColor();
/**
* Returns the border {@link Color} of this button.
*
* @return the border color
* @since 1.2.9
*/
public abstract Color getBorderColor();
}Sample Code
The chain below builds an inventory button; .box(...) is the inventory surface's placement property, while
everything else is inherited from ApolloButton.
public void displayButtonExample(Player viewer) {
Optional<ApolloPlayer> apolloPlayerOpt = Apollo.getPlayerManager().getPlayer(viewer.getUniqueId());
apolloPlayerOpt.ifPresent(apolloPlayer -> {
InventoryButton vote = InventoryButton.builder()
.id("vote")
.inventoryType(InventoryType.PLAYER)
.box(InventoryButtonBox.RIGHT)
.position(HudPosition.of(6, 52))
.size(InventoryButton.SIZE_WIDE)
.shape(ApolloButtonShape.ROUNDED_SQUARE)
.backgroundColor(InventoryButton.DEFAULT_BACKGROUND_COLOR)
.borderColor(InventoryButton.DEFAULT_BORDER_COLOR)
.content(ApolloButtonContent.builder()
.append(Component.text("Vote"))
.scale(1.0F)
.build())
.tooltip(ApolloButtonTooltip.of(
Component.text("Vote", NamedTextColor.GREEN),
Component.text("Vote daily for rewards", NamedTextColor.GRAY)))
.onClick(ApolloButtonAction.openUrl("https://example.com/vote"))
.build();
this.inventoryModule.displayInventoryButton(apolloPlayer, vote);
});
}ApolloButtonContent Builder
Button content is an ordered sequence of adventure components and icons, rendered as a single centered row.
At least one part is required, and content supports at most 30 parts (ApolloButtonContent.MAX_PARTS)
.append(Component) appends an adventure component to the content row.
.append(Component.text("Vote for us!")).append(Icon) appends an icon to the content row. Read the icons utilities page to learn more about icons.
.append(ItemStackIcon.builder().itemName("NETHER_STAR").build()).append(Function<ApolloPlayer, Component>, Duration) appends a live part, resolved into a component for each viewing
player whenever the content is sent: at the owning module's live button broadcast (e.g. the inventory module's live button broadcast)
and refreshed at the given interval. The interval must be positive and is quantized to server ticks (50ms).
Shorthand for append(ApolloButtonContentPart.live(resolver, updateInterval)).
.append(apolloViewer -> Component.text(Bukkit.getOnlinePlayers().size(), NamedTextColor.GREEN), Duration.ofMillis(2500L)).append(ApolloButtonContentPart) appends a pre-built content part; see the ApolloButtonContentPart factories (component(...), icon(...), live(...)).
.append(ApolloButtonContentPart.live(
apolloViewer -> Component.text(getBalance(apolloViewer.getUniqueId()), NamedTextColor.GOLD),
Duration.ofMillis(2500L))).scale(float) sets the scale factor applied to the content row. Defaults to 1.0 and must be between 0.25 and 4.
.scale(1.0F)Sample Code
public static ApolloButtonContent buttonContentExample() {
return ApolloButtonContent.builder()
.append(ItemStackIcon.builder().itemName("GOLD_INGOT").build())
.append(Component.text("Balance: ", NamedTextColor.GRAY))
.append(ApolloButtonContentPart.live(
apolloViewer -> Component.text(getBalance(apolloViewer.getUniqueId()), NamedTextColor.GOLD),
Duration.ofMillis(2500L)))
.scale(1.0F)
.build();
}ApolloButtonTooltip
The tooltip shown while a button is hovered, rendered like a vanilla item tooltip at the mouse. Built via
ApolloButtonTooltip.of(...) for static lines (accepting either varargs or a List<Component>), or
ApolloButtonTooltip.live(resolver, Duration) for a per-player resolver that is re-resolved whenever the
button content is sent, refreshed at the given interval. The interval must be positive and is quantized to
server ticks (50ms). Tooltips support at most 100 lines (ApolloButtonTooltip.MAX_LINES).
ApolloButtonTooltip.of(
Component.text("Shop", NamedTextColor.GREEN),
Component.text("Browse the server shop", NamedTextColor.GRAY));
ApolloButtonTooltip.live(apolloViewer -> Arrays.asList(
Component.text("Updated " + LocalTime.now(), NamedTextColor.DARK_GRAY)),
Duration.ofMillis(2500L));ApolloButtonAction
The action executed when a button is clicked. A button without an action is purely decorative. Built via one
of three factories, each returning a dedicated ApolloButtonAction subtype (RunCommandAction, OpenUrlAction, ClientAction):
ApolloButtonAction.runCommand(String)runs the command as the player. Must start with/. The client shows a confirmation prompt before running the command.ApolloButtonAction.openUrl(String)opens the URL. Respects the player's chat links setting and Apollo link-prompt settings.ApolloButtonAction.clientAction(ApolloButtonClientAction)executes a built-in client action:OPEN_MINIMAP_VIEWopens Lunar's fullscreen minimap view (does nothing when the player has the MiniMap mod disabled) orOPEN_WAYPOINTS_MENUopens the waypoints menu.
.onClick(ApolloButtonAction.runCommand("/shop"))
.onClick(ApolloButtonAction.openUrl("https://example.com/vote"))
.onClick(ApolloButtonAction.clientAction(ApolloButtonClientAction.OPEN_MINIMAP_VIEW))ApolloButtonShape
The button shape, either ROUNDED_SQUARE or CIRCLE. Rounded squares render with slightly rounded corners;
circles use the smaller of the button's width and height as their diameter, centered within the button
bounds.
.shape(ApolloButtonShape.ROUNDED_SQUARE)ApolloButtonSize
The button size, in GUI-scaled pixels, created via ApolloButtonSize.of(width, height).
.size(ApolloButtonSize.of(56, 24))
.size(ApolloButtonSize.of(40))Suggested size presets are per-surface, tuned to the surface's container dimensions; for inventory buttons they live
on InventoryButton (SIZE_SMALL, SIZE_MEDIUM, SIZE_LARGE, SIZE_WIDE) and for chat buttons on ChatButton (SIZE_SMALL, SIZE_MEDIUM, SIZE_ICON);
see the inventory module page and the chat module page.
Updating Buttons
Modules expose update methods that replace the content and/or tooltip of a previously displayed button,
leaving all other button properties unchanged (e.g. InventoryModule#updateInventoryButton, which takes an
ApolloButtonContent and an ApolloButtonTooltip). Buttons are matched by id.
The partial-update semantics are the same for every surface:
- An unset
contentkeeps the previous content; its parts and its scale; a present content replaces both. - An unset
tooltipkeeps the previous tooltip; a present tooltip fully replaces it, and a present tooltip with zero lines clears it.
Live content parts and live tooltips are resolved per recipient before sending, both on manual updates and by the owning module's automatic live button broadcast. The broadcast refreshes content and tooltip independently, each on its own update interval.