-
Notifications
You must be signed in to change notification settings - Fork 23
/
SpiGUI.java
309 lines (282 loc) · 12.4 KB
/
SpiGUI.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
package com.samjakob.spigui;
import com.samjakob.spigui.buttons.SGButton;
import com.samjakob.spigui.menu.SGMenu;
import com.samjakob.spigui.menu.SGMenuListener;
import com.samjakob.spigui.menu.SGOpenMenu;
import com.samjakob.spigui.item.ItemBuilder;
import com.samjakob.spigui.toolbar.SGToolbarBuilder;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.ArrayList;
import java.util.List;
/**
* The core class for the SpiGUI library.<br>
* One instance of the SpiGUI class is registered for each plugin using it.
* <p>
* The expected usage of SpiGUI is that you register a SpiGUI instance for your
* plugin with <code>new SpiGUI(this);</code> in your class that extends
* <code>JavaPlugin</code>. You can then use the instance you've created throughout
* your project to create GUIs that use SpiGUI.
*/
public class SpiGUI {
private final JavaPlugin plugin;
/**
* Whether or not the inventory click actions should be
* cancelled by default.
* <p>
* This is typically set to true so events needn't be manually cancelled
* every time an item is clicked in the inventory as that is the behavior
* most typically used with an inventory GUI.
* <p>
* With this set to true, you can of course use <code>event.setCancelled(false);</code> to
* allow the default interaction.
*/
private boolean blockDefaultInteractions = true;
/**
* Whether or not automatic pagination should be enabled.
* <p>
* This is set to true by default and it means if you set an inventory slot
* greater than the highest slot on the inventory, a row will automatically
* be added containing pagination items that allow a user to scroll between
* different 'pages' to access all of the assigned slots in the inventory.
* <p>
* This concept is based on an improved version of the approach taken with
* my SpigotPaginatedGUI library.
*/
private boolean enableAutomaticPagination = true;
/**
* The defaultToolbarBuilder is the plugin-wide {@link SGToolbarBuilder}
* called when building pagination buttons for inventory GUIs.
* <p>
* This can be overridden per-inventory, as well as per-plugin using the appropriate methods
* on either the inventory class ({@link SGMenu}) or your plugin's instance of
* {@link SpiGUI}.
*/
private SGToolbarBuilder defaultToolbarBuilder = (slot, page, type, menu) -> {
switch (type) {
case PREV_BUTTON:
if (menu.getCurrentPage() > 0) return new SGButton(new ItemBuilder(Material.ARROW)
.name("&a&l\u2190 Previous Page")
.lore(
"&aClick to move back to",
"&apage " + menu.getCurrentPage() + ".")
.build()
).withListener(event -> {
event.setCancelled(true);
menu.previousPage(event.getWhoClicked());
});
else return null;
case CURRENT_BUTTON:
return new SGButton(new ItemBuilder(Material.NAME_TAG)
.name("&7&lPage " + (menu.getCurrentPage() + 1) + " of " + menu.getMaxPage())
.lore(
"&7You are currently viewing",
"&7page " + (menu.getCurrentPage() + 1) + "."
).build()
).withListener(event -> event.setCancelled(true));
case NEXT_BUTTON:
if (menu.getCurrentPage() < menu.getMaxPage() - 1) return new SGButton(new ItemBuilder(Material.ARROW)
.name("&a&lNext Page \u2192")
.lore(
"&aClick to move forward to",
"&apage " + (menu.getCurrentPage() + 2) + "."
).build()
).withListener(event -> {
event.setCancelled(true);
menu.nextPage(event.getWhoClicked());
});
else return null;
case UNASSIGNED:
default:
return null;
}
};
/**
* Creates an instance of the SpiGUI library associated with a given plugin.
* <br><br>
* This is intended to be stored as a static field in your plugin with a public static
* getter (or a public static field - dealer's choice) and you create inventories through
* this class by calling {@link #create(String, int)} on the static {@link SpiGUI} field.
* <p>
* A lengthy justification of this is provided below, should you care to read it.
*
* <br><br>
*
* <p><b>Note:</b></p>
* The association with a plugin is an important design decision that was overlooked
* in this library's predecessor, SpigotPaginatedGUI.
* <br><br>
* This library is not designed to act as a standalone plugin because that is inconvenient
* for both developers and server administrators for such a relatively insignificant task - the
* library is more just a small convenience measure. However, this library still needs to register
* a listener under a given plugin, which is where the issue arises; which plugin should the
* library use to register events with. Previously, it was whichever plugin made the call to
* <code>PaginatedGUI.prepare</code> first, however this obviously causes problems if that
* particular plugin is unloaded - as any other plugins using the library no longer have the
* listener that was registered.
* <br><br>
* This approach was therefore considered a viable compromise - each plugin registers its own
* listener, however the downside of this is that each inventory and the listener must now
* also be registered with the plugin too.
* <br><br>
* Thus, the design whereby this class is registered as a static field on a {@link JavaPlugin}
* instance and serves as a proxy for creating ({@link SGMenu}) inventories and an instance
* of the {@link SGMenuListener} registered with that plugin seemed like a good way to try
* and minimize the inconvenience of the approach.
*
* @param plugin The plugin using SpiGUI.
*/
public SpiGUI(JavaPlugin plugin) {
this.plugin = plugin;
plugin.getServer().getPluginManager().registerEvents(
new SGMenuListener(plugin, this), plugin
);
}
/**
* An alias for {@link #create(String, int, String)} with the tag set to null.
* Use this method if you don't need the tag, or you don't know what it's for.
* <p>
* The rows parameter is used in place of the size parameter of the
* Bukkit/Spigot inventory API. So, if you wanted an inventory of size
* 27, you would supply 3 as the value of the rows parameter.
*
* <br><br>
*
* The <code>name</code> parameter supports the following 'placeholders':
* <ul>
* <li><code>{currentPage}</code>: the current page the inventory is on.</li>
* <li><code>{maxPage}</code>: the final page of the inventory.</li>
* </ul>
*
* @param name The display name of the inventory.
* @param rows The number of rows the inventory should have per page.
* @return The created inventory.
*/
public SGMenu create(String name, int rows) {
return create(name, rows, null);
}
/**
* Creates an inventory with a given name, tag and number of rows.
* The display name is color code translated.
*
* <br><br>
*
* The <code>name</code> parameter supports the following 'placeholders':
* <ul>
* <li><code>{currentPage}</code>: the current page the inventory is on.</li>
* <li><code>{maxPage}</code>: the final page of the inventory.</li>
* </ul>
*
* <br><br>
*
* The rows parameter is used in place of the size parameter of the
* Bukkit/Spigot inventory API. So, if you wanted an inventory of size
* 27, you would supply 3 as the value of the rows parameter.
*
* <br><br>
*
* The tag is used when getting all open inventories ({@link #findOpenWithTag(String)}) with your chosen tag.
* An example of where this might be useful is with a permission GUI - when
* the permissions are updated by one user in the GUI, it would be desirable to
* refresh the state of the permissions GUI for all users observing the GUI.
*
* <br><br>
*
* You might give the permissions GUI a tag of 'myPermissionsGUI', then refreshing
* all the open instances of the GUI would be as simple as getting all open inventories
* with the aforementioned tag using {@link #findOpenWithTag(String)} and calling refresh
* on each GUI in the list.
*
* <br><br>
*
* @param name The display name of the inventory.
* @param rows The number of rows the inventory should have per page.
* @param tag The inventory's tag.
* @return The created inventory.
*/
public SGMenu create(String name, int rows, String tag) {
return new SGMenu(plugin, this, name, rows, tag);
}
/**
* @see SpiGUI#blockDefaultInteractions
*
* @param blockDefaultInteractions Whether or not default inventory interactions should be cancelled.
*/
public void setBlockDefaultInteractions(boolean blockDefaultInteractions) {
this.blockDefaultInteractions = blockDefaultInteractions;
}
/**
* Returns the value of {@link SpiGUI#blockDefaultInteractions} for this plugin.
*
* @return Whether or not default inventory interactions should be cancelled.
*/
public boolean areDefaultInteractionsBlocked() {
return blockDefaultInteractions;
}
/**
* @see SpiGUI#enableAutomaticPagination
*
* @param enableAutomaticPagination Whether or not automatic pagination should be enabled.
*/
public void setEnableAutomaticPagination(boolean enableAutomaticPagination) {
this.enableAutomaticPagination = enableAutomaticPagination;
}
/**
* Returns the value of {@link SpiGUI#enableAutomaticPagination} for this plugin.
*
* @return Whether or not automatic pagination is enabled.
*/
public boolean isAutomaticPaginationEnabled() {
return enableAutomaticPagination;
}
/**
* @see SpiGUI#defaultToolbarBuilder
*
* @param defaultToolbarBuilder The default toolbar builder used for GUIs.
*/
public void setDefaultToolbarBuilder(SGToolbarBuilder defaultToolbarBuilder) {
this.defaultToolbarBuilder = defaultToolbarBuilder;
}
/**
* @see SpiGUI#defaultToolbarBuilder
*
* @return The default toolbar builder used for GUIs.
*/
public SGToolbarBuilder getDefaultToolbarBuilder() {
return defaultToolbarBuilder;
}
/**
* Finds a list of all open inventories with a given tag along with the
* player who has that inventory open.
* <p>
* This returns a list of {@link SGOpenMenu} which simply stores the
* opened inventory along with the player viewing the open inventory.
* <p>
* Supplying null as the tag value will get all untagged inventories.
*
* @param tag The tag to search for.
* @return A list of {@link SGOpenMenu} whose inventories have the specified tag.
*/
public List<SGOpenMenu> findOpenWithTag(String tag) {
List<SGOpenMenu> foundInventories = new ArrayList<>();
// Loop through every online player...
for (Player player : plugin.getServer().getOnlinePlayers()) {
// ...if that player has an open inventory with a top inventory...
if (player.getOpenInventory().getTopInventory() != null) {
// ...get that top inventory.
Inventory topInventory = player.getOpenInventory().getTopInventory();
// If the top inventory is an SGMenu,
if (topInventory.getHolder() != null && topInventory.getHolder() instanceof SGMenu) {
// and the SGMenu has the tag matching the one we're checking for,
SGMenu inventory = (SGMenu) topInventory.getHolder();
if (inventory.getTag().equals(tag))
// add the SGMenu to our list of found inventories.
foundInventories.add(new SGOpenMenu(inventory, player));
}
}
}
return foundInventories;
}
}