DevNatan / inventory-framework

Minecraft Inventory API framework
MIT License
117 stars 21 forks source link

Erro nos itens de navegação #177

Closed thelipe7 closed 2 years ago

thelipe7 commented 2 years ago

Is there an existing issue for this?

🐛 Describe the bug

Mesmo sem outras páginas para exibir ele continua aparecendo os itens de navegação

✔️ Expected behavior

O item de navegação aparecer apenas quando necessário.

👣 Steps to Reproduce

Ao me deparar com este problema fui em busca de uma solução, o que resultou no seguinte código:

image

Porém quando tento abrir o menu resulta no seguinte erro:

image

💻 Platform

⭐ Versions

MC 1.8.8, IF 2.5.2

✍️ Additional context

Meu setSource está da seguinte forma:

image

Dentro de:

image

luiz-otavio commented 2 years ago

E ai meu amigão, beleza? Provavelmente durante o setSource, a instância de Main.getInstance() pode estar retornando nullo. Você já tentou debuggar o code?

luiz-otavio commented 2 years ago

Aliás, sugiro que você veja o tópico de paginação da wiki aqui. Haverá alguns métodos mais simples que pode o auxiliar na hora de criar um menu paginado!

thelipe7 commented 2 years ago

Este erro acontece apenas quando tento usar o hasNextPage ou hasPreviousPage, isso em qualquer parte do código que já tentei o utilizar, quando removo o mesmo o menu funciona normalmente, mas o problema é os itens de navegação que quero esconder, afinal não tem sentido eles aparecerem se não existem outras páginas. O menu fica da seguinte forma: image

Utilizando o mesmo código em tudo apenas removendo o hasNextPage e hasPreviousPage:

    Main.getInstance().getViewFrame().setDefaultNextPageItem((context) -> {
        ViewItem viewItem = new ViewItem();
        viewItem.withItem(BukkitUtils.loadItemStack("262:0 : 1 : nome>&aPágina {page} : esconder>tudo"
                .replace("{page}", String.valueOf(context.getPage() + 2))));
        viewItem.onClick(event -> {
            if (event.isLeftClick()) {
                event.paginated().switchToNextPage();
                BukkitUtils.playSound(event.getPlayer(), Sound.CLICK);
            }
        });
        return viewItem;
    });
    Main.getInstance().getViewFrame().setDefaultPreviousPageItem((context) -> {
        ViewItem viewItem = new ViewItem();
        viewItem.withItem(BukkitUtils.loadItemStack("262:0 : 1 : nome>&aPágina {page} : esconder>tudo"
                .replace("{page}", String.valueOf(context.getPage()))));
        viewItem.onClick(event -> {
            if (event.isLeftClick()) {
                event.paginated().switchToPreviousPage();
                BukkitUtils.playSound(event.getPlayer(), Sound.CLICK);
            }
        });
        return viewItem;
    });
luiz-otavio commented 2 years ago

Aparentemente, o método setDefaultPreviousPageItem não possui efeitos nenhum. Você já tentou usar os métodos setNavigateNextItemFactory e setNavigateBackItemFactory?

luiz-otavio commented 2 years ago

Aparentemente, o método setDefaultPreviousPageItem não possui efeitos nenhum. Você já tentou usar os métodos setNavigateNextItemFactory e setNavigateBackItemFactory?

O principio é o mesmo, entretanto, você não precisa aplicar nenhum item ao ViewItem caso não haja nenhuma pagina disponivel.

thelipe7 commented 2 years ago

@luiz-otavio Testei a sua sugestão e o erro continou, então fui testando e testando diversas formas e possíveis motivos para acontecer este problema. Por fim cheguei a seguinte conclusão:

Procurando por formas de fazer o que foi discutido acima, eu encontrei este exemplo deixado em https://github.com/DevNatan/inventory-framework/blob/main/examples/src/main/java/me/saiintbrisson/minecraft/examples/PersistentLayeredNavigablePaginatedView.java onde dentro do mesmo tem uma forma de definir os itens de navegação, modifiquei o mesmo e finalmente consegui o resultado esperado, chegando no seguinte código final:

public final class TopLossesView extends PaginatedView<Integer> {

    public TopLossesView() {
        super(6, "Paginated view");

        setSource(IntStream.rangeClosed(0, 100).boxed().collect(Collectors.toList()));

        setLayout("XXXXXXXXX", "XOOOOOOOX", "XOOOOOOOX", "XOOOOOOOX", "XOOOOOOOX", "XXX<X>XXX");
    }

    @Override
    protected ViewItem getPreviousPageItem(@NotNull PaginatedViewContext<Integer> context) {
        ViewItem viewItem = new ViewItem();
        if (!context.isFirstPage()) {
            viewItem
                    .withItem(BukkitUtils.loadItemStack("262:0 : 1 : nome>&aPágina {page} : esconder>tudo"
                            .replace("{page}", String.valueOf(context.getPage()))))
                    .onClick(event -> {
                        if (event.isLeftClick()) {
                            context.switchToPreviousPage();
                            BukkitUtils.playSound(event.getPlayer(), Sound.CLICK);
                        }
                    });
        } else {
            viewItem.withItem(new ItemStack(Material.AIR));
        }
        return viewItem;
    }

    @Override
    protected ViewItem getNextPageItem(@NotNull PaginatedViewContext<Integer> context) {
        ViewItem viewItem = new ViewItem();
        if (context.getPage() + 1 != context.getPagesCount()) {
            viewItem
                    .withItem(BukkitUtils.loadItemStack("262:0 : 1 : nome>&aPágina {page} : esconder>tudo"
                            .replace("{page}", String.valueOf(context.getPage() + 2))))
                    .onClick(event -> {
                        if (event.isLeftClick()) {
                            context.switchToNextPage();
                            BukkitUtils.playSound(event.getPlayer(), Sound.CLICK);
                        }
                    });
        } else {
            viewItem.withItem(new ItemStack(Material.AIR));
        }
        return viewItem;
    }

    @Override
    protected void onItemRender(PaginatedViewSlotContext<Integer> render, ViewItem item, Integer value) {
        item.withItem(createPaginationItemStack(value));
    }

    private ItemStack createPaginationItemStack(int value) {
        ItemStack stack = new ItemStack(Material.PAPER);
        ItemMeta meta = requireNonNull(stack.getItemMeta());
        meta.setDisplayName("Item " + value);
        stack.setItemMeta(meta);
        return stack;
    }
}

Porém quando passei a parte de navegação para meus menus, o mesmo erro de antes voltou a aparecer, então eu pensei que poderia ser erro meu em fazer os menus ou algo parecido, mas resolvi testar uma coisa, ao alterar o set source do código acima de: setSource(IntStream.rangeClosed(0, 100).boxed().collect(Collectors.toList())); para:

setSource(context -> {
                return IntStream.rangeClosed(0, 100).boxed().collect(Collectors.toList());
});

O erro começou a aparecer e o menu parou de funcionar. Por fim resumindo ao tornar o setSource como dinâmico algumas funções ficam retornando o erro acima. Minha explicação é bem bagunçada mas espero ter conseguido explicar de uma forma boa.

luiz-otavio commented 2 years ago

Excelente! Por fim, isso resolve a issue?

thelipe7 commented 2 years ago

Na verdade não. Eu preciso usar o setSource dinâmico mas por causa dele da o erro aí ,-,

luiz-otavio commented 2 years ago

Você tem certeza que precisa disso dinamicamente? O que acha de tentar:

    public TopWinsView() {
        super(3, "Top Views"); // Example

        List<User> user = Main.getInstance().getMainDataManager().USERS.getCached()
            .clone();

        user.sort(Comparing.comparing(User::getWins).reversed()); // Coloca para atualizar isso a cada 5m, pois o cache é variavel;

        setSource(user); // Pronto, clonou a lista de usuários e ordenou por vitórias.

        setNextPageItem((context, item) -> {
            if (!context.hasNextPage()) {
                return;
            }

            ItemStack arrow = new ItemBuilder(Material.ARROW)
                .name(
                    translate("&aNext page")
                ).build();

            item.withItem(arrow).onClick(handler -> context.switchToNextPage());
        });

        setPreviousPageItem((context, item) -> {
            if (!context.hasPreviousPage()) {
                return;
            }

            ItemStack arrow = new ItemBuilder(Material.ARROW)
                .name(
                    translate("&aPrevious page")
                ).build();

            item.withItem(arrow).onClick(handler -> context.switchToPreviousPage());
        });
    }

     @Override
    protected void onItemRender(@NotNull PaginatedViewSlotContext<Match> context, @NotNull ViewItem viewItem, @NotNull User value) {
        String name = value.getName(); // Salva o nome do user, porque o método Bukkit#getOfflinePlayer é lento.

        ItemStack icon = BukkitUtils.loadItemStack("397:3 : 1 : nome>&e{pos}° - {player} : lore>&fVitórias: &7{wins} : donc
            .replace(target:"{player}", name)
            .replace(target:"{pos}", String.valueOf(context.getIndex() + 1))
            .replace(target:"{wins}", String.valueOf(value.getWins())));

        return viewItem.withItem(icon);
    }

    @Override
    protected void onRender(@NotNull ViewContext context) {
        if (context.paginated().getSource().isEmpty()) {
            // Fico vazio? então bota um item para falar que ta vazio.
            ItemStack empty = new ItemBuilder(Material.BARRIER)
                .name(
                    translate("&cSem vencedores.")
                ).build();

            context.slot(
                13,
                empty
            );
        }
    }

Ai fica igualzinho o exemplo, e é claro, o melhor seria tornar essa lista global e usar métodos como scheduleUpdate para atualizar frequentemente a lista.

thelipe7 commented 2 years ago

Adaptei um pouco e apliquei em todos os meus menus e finalmente funcionou da forma esperada :). Agora apenas uma dúvida, 3 menus onde são listados alguns elementos, vou usar de exemplo um que é uma lista de itens, dentro do menu eu coloquei para ao clicar com o botão direito ele remove o item da lista. Porém eu quero que ele atualize na hora isso no menu e não que eu delete e tenha que reabrir o menu para atualizar, se for possívela atualizar para todos que estão vendo o menu também seria bem top.

luiz-otavio commented 2 years ago

Você pode invocar o método ViewContext#update, na qual, vai atualizar o layout atual da pagina conforme a source!