Argument suggestions with tooltips
The CommandAPI can also display tooltips for specific argument suggestions. These are shown to the user when they hover over a given suggestion and can be used to provide more context to a user about the suggestions that are shown to them. In this section, we'll outline the two ways of creating suggestions with tooltips:
- Normal (String) suggestions with tooltips
- Safe suggestions with tooltips
Tooltips can have formatting to change how the text is displayed by using the ChatColor
class.
Tooltips with normal (String) suggestions
To use these features, the CommandAPI includes the overrideSuggestionsT
methods for arguments, that accept IStringTooltip
objects instead of String
objects:
Argument overrideSuggestionsT(IStringTooltip... suggestions);
Argument overrideSuggestionsT(Collection<IStringTooltip> suggestions);
Argument overrideSuggestionsT(Function<CommandSender, IStringTooltip[]> suggestions);
Argument overrideSuggestionsT(BiFunction<CommandSender, Object[], IStringTooltip[]> suggestions);
The StringTooltip
class is the CommandAPI's default implementation of IStringTooltip
, which has two static methods to construct it easily:
StringTooltip none(String suggestion);
StringTooltip of(String suggestion, String tooltip);
The first method, StringTooltip.none(String)
creates a normal suggestion entry with no tooltip, whereas the StringTooltip.of(String, String)
method creates a suggestion with the provided tooltip text.
Example - An emotes command with string suggestion tooltips
Say we want to create a simple command to provide ingame emotes between players. For example, if you did /emote wave Bob
, you'll "wave" to the player Bob. For this example, we'll use the following command syntax:
/emote <emote> <target>
First, we'll declare our arguments. Here, we'll use the overrideSuggestionsT
method, along with the StringTooltip.of(String, String)
method to create emote suggestions and include suitable descriptions:
List<Argument> arguments = new ArrayList<>();
arguments.add(new StringArgument("emote")
.overrideSuggestionsT(
StringTooltip.of("wave", "Waves at a player"),
StringTooltip.of("hug", "Gives a player a hug"),
StringTooltip.of("glare", "Gives a player the death glare")
)
);
arguments.add(new PlayerArgument("target"));
Finally, we declare our command as normal:
new CommandAPICommand("emote")
.withArguments(arguments)
.executesPlayer((player, args) -> {
String emote = (String) args[0];
Player target = (Player) args[1];
switch(emote) {
case "wave":
target.sendMessage(player.getName() + " waves at you!");
break;
case "hug":
target.sendMessage(player.getName() + " hugs you!");
break;
case "glare":
target.sendMessage(player.getName() + " gives you the death glare...");
break;
}
})
.register();
The IStringTooltip
interface can be implemented by any other class to provide tooltips for custom objects. The IStringTooltip
interface has the following methods:
public interface IStringTooltip {
public String getSuggestion();
public String getTooltip();
}
This is incredibly useful if you are using suggestions with custom objects, such as a plugin that has custom items.
Example - Using IStringTooltip
for custom items
Let's say we've created a simple plugin which has custom items. For a custom item, we'll have a super simple class CustomItem
that sets its name, lore and attached itemstack:
public class CustomItem implements IStringTooltip {
private ItemStack itemstack;
private String name;
public CustomItem(ItemStack itemstack, String name, String lore) {
ItemMeta meta = itemstack.getItemMeta();
meta.setDisplayName(name);
meta.setLore(Arrays.asList(lore));
itemstack.setItemMeta(meta);
this.itemstack = itemstack;
this.name = name;
}
public String getName() {
return this.name;
}
public ItemStack getItem() {
return this.itemstack;
}
@Override
public String getSuggestion() {
return this.itemstack.getItemMeta().getDisplayName();
}
@Override
public String getTooltip() {
return this.itemstack.getItemMeta().getLore().get(0);
}
}
Let's also say that our plugin has registered lots of CustomItem
s and has this stored in a CustomItem[]
in our plugin. We could then use this as our input for suggestions:
CustomItem[] customItems = new CustomItem[] {
new CustomItem(new ItemStack(Material.DIAMOND_SWORD), "God sword", "A sword from the heavens"),
new CustomItem(new ItemStack(Material.PUMPKIN_PIE), "Sweet pie", "Just like grandma used to make")
}; //
new CommandAPICommand("giveitem")
.withArguments(new StringArgument("item").overrideSuggestionsT(customItems)) // We use customItems[] as the input for our suggestions with tooltips
.executesPlayer((player, args) -> {
String itemName = (String) args[0];
//Give them the item
for(CustomItem item : customItems) {
if(item.getName().equals(itemName)) {
player.getInventory().addItem(item.getItem());
}
}
})
.register();
Tooltips with safe suggestions
Using tooltips with safe suggestions is almost identical to the method described above for normal suggestions, except for two things. Firstly, you must use safeOverrideSuggestionsT
method instead of the overrideSuggestionsT
method and secondly, instead of using StringTooltip
, you must use Tooltip<S>
. Let's look at these differences in more detail.
The safeOverrideSuggestionsT
methods are fairly similar to the overrideSuggestionsT
methods, except instead of using StringTooltip
, it simply uses Tooltip<S>
.
Argument safeOverrideSuggestionsT(Tooltip<S>... suggestions);
Argument safeOverrideSuggestionsT(Collection<Tooltip<S>> suggestions);
Argument safeOverrideSuggestionsT(Function<CommandSender, Tooltip<S>[]> suggestions);
Argument safeOverrideSuggestionsT(BiFunction<CommandSender, Object[], Tooltip<S>[]> suggestions);
The Tooltip<S>
class represents a tooltip for a given object S
. For example, a tooltip that is for a LocationArgument
would be a Tooltip<Location>
and a tooltip for an EnchantmentArgument
would be a Tooltip<Enchantment>
.
Just like the StringTooltip
class, the Tooltip<S>
class provides the following static methods, which operate exactly the same as the ones in the StringTooltip
class:
Tooltip<S> none(S object);
Tooltip<S> of(S object, String tooltip);
Tooltip<S>[] arrayOf(Tooltip<S>... tooltips);
The use of arrayOf
is heavily recommended as it provides the necessary type safety for Java code to ensure that the correct types are being passed to the safeOverrideSuggestionsT
method.
Example - Teleportation command with suggestion descriptions
Say we wanted to create a custom teleport command which suggestions a few key locations. In this example, we'll use the following command syntax:
/warp <location>
First, we'll declare our arguments. Here, we use a LocationArgument
and use the safeOverrideSuggestionsT
method, with a parameter for the command sender, so we can get information about the world. We populate the suggestions with tooltips using Tooltip.of(Location, String)
and collate them together with Tooltip.arrayOf(Tooltip<Location>...)
:
List<Argument> arguments = new ArrayList<>();
arguments.add(new LocationArgument("location")
.safeOverrideSuggestionsT((sender) -> {
return Tooltip.arrayOf(
Tooltip.of(((Player) sender).getWorld().getSpawnLocation(), "World spawn"),
Tooltip.of(((Player) sender).getBedSpawnLocation(), "Your bed"),
Tooltip.of(((Player) sender).getTargetBlockExact(256).getLocation(), "Target block")
);
}));
In the arguments declaration, we've casted the command sender to a player. To ensure that the command sender is definitely a player, we'll use the executesPlayer
command execution method in our command declaration:
new CommandAPICommand("warp")
.withArguments(arguments)
.executesPlayer((player, args) -> {
player.teleport((Location) args[0]);
})
.register();