diff --git a/src/main/java/world/bentobox/level/panels/DetailsPanel.java b/src/main/java/world/bentobox/level/panels/DetailsPanel.java index f253505..deac18b 100644 --- a/src/main/java/world/bentobox/level/panels/DetailsPanel.java +++ b/src/main/java/world/bentobox/level/panels/DetailsPanel.java @@ -63,6 +63,7 @@ public class DetailsPanel // By default no-filters are active. this.activeTab = Tab.ALL_BLOCKS; + this.activeFilter = Filter.NAME; this.materialCountList = new ArrayList<>(Material.values().length); this.updateFilters(); @@ -101,6 +102,8 @@ public class DetailsPanel panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); panelBuilder.registerTypeBuilder("BLOCK", this::createMaterialButton); + panelBuilder.registerTypeBuilder("FILTER", this::createFilterButton); + // Register tabs panelBuilder.registerTypeBuilder("TAB", this::createTabButton); @@ -152,6 +155,61 @@ public class DetailsPanel } } + Comparator> sorter; + + switch (this.activeFilter) + { + case COUNT -> + { + sorter = (o1, o2) -> + { + if (o1.getValue().equals(o2.getValue())) + { + String o1Name = DetailsPanel.prettifyObject(o1.getKey(), this.user); + String o2Name = DetailsPanel.prettifyObject(o2.getKey(), this.user); + + return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); + } + else + { + return Integer.compare(o2.getValue(), o1.getValue()); + } + }; + } + case VALUE -> + { + sorter = (o1, o2) -> + { + int o1Value = this.addon.getBlockConfig().getBlockValues().getOrDefault(o1.getKey(), 0); + int o2Value = this.addon.getBlockConfig().getBlockValues().getOrDefault(o2.getKey(), 0); + + if (o1Value == o2Value) + { + String o1Name = DetailsPanel.prettifyObject(o1.getKey(), this.user); + String o2Name = DetailsPanel.prettifyObject(o2.getKey(), this.user); + + return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); + } + else + { + return Integer.compare(o2Value, o1Value); + } + }; + } + default -> + { + sorter = (o1, o2) -> + { + String o1Name = DetailsPanel.prettifyObject(o1.getKey(), this.user); + String o2Name = DetailsPanel.prettifyObject(o2.getKey(), this.user); + + return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); + }; + } + } + + this.materialCountList.sort(sorter); + this.pageIndex = 0; } @@ -240,6 +298,117 @@ public class DetailsPanel } + /** + * Create next button panel item. + * + * @param template the template + * @param slot the slot + * @return the panel item + */ + private PanelItem createFilterButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) + { + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) + { + // Set icon + builder.icon(template.icon().clone()); + } + + Filter filter; + + if (slot.amountMap().getOrDefault("FILTER", 0) > 1) + { + filter = Enums.getIfPresent(Filter.class, String.valueOf(template.dataMap().get("filter"))).or(Filter.NAME); + } + else + { + filter = this.activeFilter; + } + + final String reference = "level.gui.buttons.filters."; + + if (template.title() != null) + { + // Set title + builder.name(this.user.getTranslation(this.world, template.title().replace("[filter]", filter.name().toLowerCase()))); + } + else + { + builder.name(this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".name")); + } + + if (template.description() != null) + { + // Set description + builder.description(this.user.getTranslation(this.world, template.description().replace("[filter]", filter.name().toLowerCase()))); + } + else + { + builder.name(this.user.getTranslation(this.world, reference + filter.name().toLowerCase() + ".description")); + } + + // Get only possible actions, by removing all inactive ones. + List activeActions = new ArrayList<>(template.actions()); + + // Add Click handler + builder.clickHandler((panel, user, clickType, i) -> + { + for (ItemTemplateRecord.ActionRecords action : activeActions) + { + if (clickType == action.clickType() || ClickType.UNKNOWN.equals(action.clickType())) + { + if ("UP".equalsIgnoreCase(action.actionType())) + { + this.activeFilter = Utils.getNextValue(Filter.values(), filter); + + // Update filters. + this.updateFilters(); + this.build(); + } + else if ("DOWN".equalsIgnoreCase(action.actionType())) + { + this.activeFilter = Utils.getPreviousValue(Filter.values(), filter); + + // Update filters. + this.updateFilters(); + this.build(); + } + else if ("SELECT".equalsIgnoreCase(action.actionType())) + { + this.activeFilter = filter; + + // Update filters. + this.updateFilters(); + this.build(); + } + } + } + + return true; + }); + + // Collect tooltips. + List tooltips = activeActions.stream(). + filter(action -> action.tooltip() != null). + map(action -> this.user.getTranslation(this.world, action.tooltip())). + filter(text -> !text.isBlank()). + collect(Collectors.toCollection(() -> new ArrayList<>(template.actions().size()))); + + // Add tooltips. + if (!tooltips.isEmpty()) + { + // Empty line and tooltips. + builder.description(""); + builder.description(tooltips); + } + + builder.glow(this.activeFilter == filter); + + return builder.build(); + } + + // --------------------------------------------------------------------- // Section: Create common buttons // --------------------------------------------------------------------- @@ -646,6 +815,26 @@ public class DetailsPanel } + /** + * Sorting order of blocks. + */ + private enum Filter + { + /** + * By name + */ + NAME, + /** + * By value + */ + VALUE, + /** + * By number + */ + COUNT + } + + // --------------------------------------------------------------------- // Section: Variables // --------------------------------------------------------------------- @@ -689,4 +878,9 @@ public class DetailsPanel * This variable stores which tab currently is active. */ private Tab activeTab; + + /** + * This variable stores active filter for items. + */ + private Filter activeFilter; } diff --git a/src/main/java/world/bentobox/level/util/Utils.java b/src/main/java/world/bentobox/level/util/Utils.java index 2c6cd71..45bc002 100644 --- a/src/main/java/world/bentobox/level/util/Utils.java +++ b/src/main/java/world/bentobox/level/util/Utils.java @@ -73,4 +73,62 @@ public class Utils return defaultValue; } + + + /** + * This method allows to get next value from array list after given value. + * + * @param values Array that should be searched for given value. + * @param currentValue Value which next element should be found. + * @param Instance of given object. + * @return Next value after currentValue in values array. + */ + public static T getNextValue(T[] values, T currentValue) + { + for (int i = 0; i < values.length; i++) + { + if (values[i].equals(currentValue)) + { + if (i + 1 == values.length) + { + return values[0]; + } + else + { + return values[i + 1]; + } + } + } + + return currentValue; + } + + + /** + * This method allows to get previous value from array list after given value. + * + * @param values Array that should be searched for given value. + * @param currentValue Value which previous element should be found. + * @param Instance of given object. + * @return Previous value before currentValue in values array. + */ + public static T getPreviousValue(T[] values, T currentValue) + { + for (int i = 0; i < values.length; i++) + { + if (values[i].equals(currentValue)) + { + if (i > 0) + { + return values[i - 1]; + } + else + { + return values[values.length - 1]; + } + } + } + + return currentValue; + } } diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 255e481..f7eeb7a 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -123,6 +123,19 @@ level: name: "&f&l Spawners" description: |- &7 Display only spawners. + filters: + name: + name: "&f&l Sort by Name" + description: |- + &7 Sort all blocks by name. + value: + name: "&f&l Sort by Value" + description: |- + &7 Sort all blocks by their value. + count: + name: "&f&l Sort by Count" + description: |- + &7 Sort all blocks by their amount. # Button that is used in multi-page GUIs which allows to return to previous page. previous: name: "&f&l Previous Page" @@ -137,6 +150,9 @@ level: click-to-view: "&e Click &7 to view." click-to-previous: "&e Click &7 to view previous page." click-to-next: "&e Click &7 to view next page." + click-to-select: "&e Click &7 to select." + left-click-to-cycle-up: "&e Left Click &7 to cycle up." + right-click-to-cycle-down: "&e Right Click &7 to cycle down." conversations: # Prefix for messages that are send from server. prefix: "&l&6 [BentoBox]: &r" diff --git a/src/main/resources/panels/detail_panel.yml b/src/main/resources/panels/detail_panel.yml index 61daf9c..927a788 100644 --- a/src/main/resources/panels/detail_panel.yml +++ b/src/main/resources/panels/detail_panel.yml @@ -19,7 +19,7 @@ detail_panel: tab: ALL_BLOCKS actions: view: - click-type: unknwon + click-type: unknown tooltip: level.gui.tips.click-to-view 3: icon: GRASS_BLOCK @@ -30,7 +30,7 @@ detail_panel: tab: ABOVE_SEA_LEVEL actions: view: - click-type: unknwon + click-type: unknown tooltip: level.gui.tips.click-to-view 4: icon: WATER_BUCKET @@ -41,7 +41,7 @@ detail_panel: tab: UNDERWATER actions: view: - click-type: unknwon + click-type: unknown tooltip: level.gui.tips.click-to-view 5: icon: SPAWNER @@ -52,8 +52,29 @@ detail_panel: tab: SPAWNER actions: view: - click-type: unknwon + click-type: unknown tooltip: level.gui.tips.click-to-view + 9: + # You can create multiple buttons. By default it is one. + icon: IRON_TRAPDOOR + # [filter] is placeholder for different filter types. It will be replaced with name, value, count. + title: level.gui.buttons.filters.[filter].name + description: level.gui.buttons.filters.[filter].description + data: + type: FILTER + # the value of filter button. Suggestion is to leave fist value to name if you use single button. + filter: NAME + actions: + up: + click-type: left + tooltip: level.gui.tips.left-click-to-cycle-up + down: + click-type: right + tooltip: level.gui.tips.right-click-to-cycle-down + # There is also select action. With it you can create multiple filter buttons. + # select: + # click-type: unknown + # tooltip: level.gui.tips.click-to-select 2: 2: material_button 3: material_button diff --git a/src/main/resources/panels/top_panel.yml b/src/main/resources/panels/top_panel.yml index 83d49ce..2260c3a 100644 --- a/src/main/resources/panels/top_panel.yml +++ b/src/main/resources/panels/top_panel.yml @@ -11,7 +11,7 @@ top_panel: content: 2: 5: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -22,7 +22,7 @@ top_panel: title: level.gui.buttons.island.empty 3: 4: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -32,7 +32,7 @@ top_panel: icon: LIME_STAINED_GLASS_PANE title: level.gui.buttons.island.empty 6: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -43,7 +43,7 @@ top_panel: title: level.gui.buttons.island.empty 4: 2: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -53,7 +53,7 @@ top_panel: icon: LIME_STAINED_GLASS_PANE title: level.gui.buttons.island.empty 3: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -63,7 +63,7 @@ top_panel: icon: LIME_STAINED_GLASS_PANE title: level.gui.buttons.island.empty 4: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -73,7 +73,7 @@ top_panel: icon: LIME_STAINED_GLASS_PANE title: level.gui.buttons.island.empty 5: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -83,7 +83,7 @@ top_panel: icon: LIME_STAINED_GLASS_PANE title: level.gui.buttons.island.empty 6: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -93,7 +93,7 @@ top_panel: icon: LIME_STAINED_GLASS_PANE title: level.gui.buttons.island.empty 7: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -103,7 +103,7 @@ top_panel: icon: LIME_STAINED_GLASS_PANE title: level.gui.buttons.island.empty 8: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: @@ -114,7 +114,7 @@ top_panel: title: level.gui.buttons.island.empty 6: 5: - icon: PLAYER_HEAD + #icon: PLAYER_HEAD title: level.gui.buttons.island.name description: level.gui.buttons.island.description data: