Skip to content

Commit aba1ca2

Browse files
committed
Added pager utility for commands
1 parent ff4f3a7 commit aba1ca2

File tree

2 files changed

+226
-3
lines changed

2 files changed

+226
-3
lines changed

build.gradle

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66

77

88
group = 'com.datdeveloper'
9-
version = "${minecraftVersion}-1.3.0"
9+
version = "${minecraftVersion}-1.4.0"
1010

1111
java {
1212
archivesBaseName = 'datmoddingapi'
@@ -153,8 +153,8 @@ publishing {
153153
}
154154
}
155155
scm {
156-
connection = 'scm:git:git://github.com.com/jtljac/datmoddingapi.git'
157-
developerConnection = 'scm:git:ssh://github.com/jtljac/datmoddingapi.git'
156+
connection = 'scm:git:git://github.com.com/dat-developer-mods/datmoddingapi.git'
157+
developerConnection = 'scm:git:ssh://github.com/dat-developer-mods/datmoddingapi.git'
158158
url = 'https://datdeveloper.com/datmoddingapi/'
159159
}
160160

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package com.datdeveloper.datmoddingapi.command.util;
2+
3+
import com.datdeveloper.datmoddingapi.util.DatChatFormatting;
4+
import net.minecraft.ChatFormatting;
5+
import net.minecraft.commands.CommandSource;
6+
import net.minecraft.network.chat.*;
7+
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
9+
10+
import java.util.Collection;
11+
import java.util.List;
12+
13+
/**
14+
* A class to split the results of a command into pages
15+
* Commands that implement this should take an integer as the last argument that is passed to the {@link #sendPage(int, CommandSource)} method
16+
* @param <PagedElement> The type of the item being paged
17+
*/
18+
public class Pager<PagedElement> {
19+
/**
20+
* The command used to create the pager
21+
*/
22+
final String command;
23+
24+
/**
25+
* The heading of the pager
26+
*/
27+
final String headerText;
28+
29+
/**
30+
* The amount of elements per page
31+
*/
32+
final int elementsPerPage;
33+
34+
/**
35+
* The elements
36+
*/
37+
final Collection<? extends PagedElement> elements;
38+
39+
/**
40+
* A function that converts the elements into chat components
41+
*/
42+
final ElementTransformer<PagedElement> transformer;
43+
44+
/**
45+
* Construct a pager with the default amount of items per page (10)
46+
* @param command The command used
47+
* @param headerText The heading of the pager
48+
* @param elements A list of the elements being paged
49+
* @param transformer The function that converts the elements into a chat component
50+
*/
51+
public Pager(final String command, @Nullable final String headerText, final Collection<? extends PagedElement> elements, final ElementTransformer<PagedElement> transformer) {
52+
this(command, headerText, 10, elements, transformer);
53+
}
54+
55+
/**
56+
* Construct a pager
57+
* @param command The command used
58+
* @param headerText The heading of the pager
59+
* @param elementsPerPage The amount of elements per page
60+
* @param elements A list of the elements being paged
61+
* @param transformer The function that converts the elements into a chat component
62+
*/
63+
public Pager(@NotNull final String command, @Nullable final String headerText, final int elementsPerPage, @NotNull final Collection<? extends PagedElement> elements, @NotNull final ElementTransformer<PagedElement> transformer) {
64+
this.command = command;
65+
this.headerText = headerText;
66+
this.elementsPerPage = elementsPerPage;
67+
this.elements = elements;
68+
this.transformer = transformer;
69+
}
70+
71+
private int getTotalPageCount() {
72+
return (int) Math.ceil((float) elements.size() / (float) elementsPerPage);
73+
}
74+
75+
@SuppressWarnings("ConstantConditions")
76+
protected Component getHeader() {
77+
String header = headerText;
78+
int headerLength = 2 + headerText.length();
79+
if (headerText.length() % 2 == 1) {
80+
header += " ";
81+
headerLength += 1;
82+
}
83+
84+
final int footerLength = 37 + (String.valueOf(getTotalPageCount()).length() * 2);
85+
final int paddingLength = (footerLength - headerLength) / 2;
86+
final String pad;
87+
if (paddingLength > 2) {
88+
pad = "=".repeat(paddingLength);
89+
} else {
90+
pad = "";
91+
}
92+
93+
return Component.literal(
94+
DatChatFormatting.TextColour.INFO + pad + "["
95+
+ DatChatFormatting.TextColour.HEADER + header
96+
+ DatChatFormatting.TextColour.INFO + "]" + pad
97+
);
98+
}
99+
100+
protected Component getPage(final int page) {
101+
final List<Component> components = elements.stream()
102+
.skip((long) (page - 1) * elementsPerPage)
103+
.limit(elementsPerPage)
104+
.map(transformer::transform)
105+
.toList();
106+
107+
return ComponentUtils.formatList(components, Component.literal("\n"));
108+
}
109+
110+
protected Component getFooter(final int page) {
111+
final int totalPages = getTotalPageCount();
112+
final int prevPage = page - 1;
113+
final int nextPage = page + 1;
114+
// First and Previous buttons
115+
final MutableComponent firstButton = Component.literal(" «");
116+
final MutableComponent prevButton = Component.literal(" < ");
117+
if (page == 1) {
118+
firstButton.withStyle(ChatFormatting.DARK_GRAY);
119+
prevButton.withStyle(ChatFormatting.DARK_GRAY);
120+
} else {
121+
firstButton
122+
.withStyle(Style.EMPTY
123+
.withHoverEvent(new HoverEvent(
124+
HoverEvent.Action.SHOW_TEXT, Component.literal(DatChatFormatting.TextColour.INFO + "First Page")
125+
))
126+
.withClickEvent(new ClickEvent(
127+
ClickEvent.Action.RUN_COMMAND,
128+
command + " 1"
129+
))
130+
.applyFormats(DatChatFormatting.TextColour.COMMAND)
131+
);
132+
prevButton
133+
.withStyle(Style.EMPTY.withHoverEvent(new HoverEvent(
134+
HoverEvent.Action.SHOW_TEXT,
135+
Component.literal(DatChatFormatting.TextColour.INFO + "Previous Page")
136+
))
137+
.withClickEvent(new ClickEvent(
138+
ClickEvent.Action.RUN_COMMAND,
139+
command + " " + prevPage
140+
))
141+
.applyFormats(DatChatFormatting.TextColour.COMMAND)
142+
);
143+
}
144+
145+
146+
// Current page and total pages text
147+
String pageString = String.valueOf(page);
148+
final String totalPagesString = String.valueOf(totalPages);
149+
{
150+
final int lengthDiff = totalPagesString.length() - pageString.length();
151+
pageString = " ".repeat(lengthDiff) + pageString;
152+
}
153+
final MutableComponent pageText = Component.literal("(%s/%s)".formatted(pageString, totalPagesString))
154+
.withStyle(DatChatFormatting.TextColour.HEADER);
155+
156+
// Next and Last Buttons
157+
final MutableComponent nextButton = Component.literal(" > ");
158+
final MutableComponent lastButton = Component.literal("» ");
159+
if (page == totalPages) {
160+
nextButton.withStyle(ChatFormatting.DARK_GRAY);
161+
lastButton.withStyle(ChatFormatting.DARK_GRAY);
162+
} else {
163+
nextButton.withStyle(Style.EMPTY
164+
.withHoverEvent(new HoverEvent(
165+
HoverEvent.Action.SHOW_TEXT,
166+
Component.literal(DatChatFormatting.TextColour.INFO + "Next Page")
167+
))
168+
.withClickEvent(new ClickEvent(
169+
ClickEvent.Action.RUN_COMMAND,
170+
command + " " + nextPage
171+
))
172+
.applyFormats(DatChatFormatting.TextColour.COMMAND)
173+
);
174+
lastButton.withStyle(Style.EMPTY
175+
.withHoverEvent(new HoverEvent(
176+
HoverEvent.Action.SHOW_TEXT,
177+
Component.literal(DatChatFormatting.TextColour.INFO + "Last Page")
178+
))
179+
.withClickEvent(new ClickEvent(
180+
ClickEvent.Action.RUN_COMMAND,
181+
command + " " + totalPages
182+
))
183+
.applyFormats(DatChatFormatting.TextColour.COMMAND)
184+
);
185+
}
186+
187+
return Component.literal(DatChatFormatting.TextColour.INFO + "============[")
188+
.append(firstButton).append(prevButton)
189+
.append(pageText)
190+
.append(nextButton).append(lastButton)
191+
.append(DatChatFormatting.TextColour.INFO + "]============");
192+
}
193+
194+
/**
195+
* Send the given page to the given {@link CommandSource}
196+
* @param page The page to show to the CommandSource
197+
* @param source The command source to send the page to
198+
*/
199+
public void sendPage(final int page, final CommandSource source) {
200+
if (page > getTotalPageCount()) return;
201+
202+
final MutableComponent component = MutableComponent.create(ComponentContents.EMPTY);
203+
if (headerText != null) {
204+
component.append(getHeader())
205+
.append("\n");
206+
}
207+
component.append(getPage(page))
208+
.append("\n");
209+
210+
component.append(getFooter(page));
211+
212+
source.sendSystemMessage(component);
213+
}
214+
215+
/**
216+
* A Functional interface to transform the given PagedElement into a Chat Component
217+
* @param <PagedElement> The class to transform into a {@link Component}
218+
*/
219+
@FunctionalInterface
220+
public interface ElementTransformer<PagedElement> {
221+
Component transform(final PagedElement element);
222+
}
223+
}

0 commit comments

Comments
 (0)