Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer;
import de.hysky.skyblocker.utils.render.gui.ItemSelectionPopup;
import de.hysky.skyblocker.utils.render.gui.RangedSliderWidget;
import de.hysky.skyblocker.utils.render.gui.SoundSelectionPopup;
import de.hysky.skyblocker.utils.render.gui.ToggleableLayoutWidget;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.ChatFormatting;
Expand All @@ -28,11 +29,13 @@
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Util;
Expand All @@ -52,6 +55,7 @@
public class ChatRuleConfigScreen extends Screen {
private static final int COLUMN_WIDTH = 105;
private static final int GRID_SPACING = 2;
protected static final Identifier SEARCH_ICON_TEXTURE = Identifier.withDefaultNamespace("icon/search");
private static final ItemStack INVALID_ITEM = new ItemStack(Items.BARRIER);
private static final Component YES_TEXT = CommonComponents.GUI_YES.copy().withStyle(ChatFormatting.GREEN);
private static final Component NO_TEXT = CommonComponents.GUI_NO.copy().withStyle(ChatFormatting.RED);
Expand All @@ -78,6 +82,7 @@ public class ChatRuleConfigScreen extends Screen {

private final HeaderAndFooterLayout layout = new HeaderAndFooterLayout(this);
private final GridLayout content = new GridLayout().columnSpacing(GRID_SPACING);
private CycleButton soundButton;

public ChatRuleConfigScreen(Screen parent, int chatRuleIndex) {
super(Component.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen"));
Expand Down Expand Up @@ -156,13 +161,17 @@ protected void init() {
displayedValues.add(Optional.ofNullable(chatRule.getCustomSound()));
}
// using an optional since it doesn't allow null values.
buttons.addChild(CycleButton.builder(opt -> soundNames.get(opt.orElse(null)), Optional.ofNullable(chatRule.getCustomSound()))
soundButton = CycleButton.builder(opt -> soundNames.get(opt.orElse(null)), Optional.ofNullable(chatRule.getCustomSound()))
.withValues(() -> true, displayedValues, availableValues)
.create(0, 0, getWidth(1.5f), 20, Component.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds"), (button, value) -> {

.create(0, 0, getWidth(1.3f), 20, Component.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds"), (button, value) -> {
chatRule.setCustomSound(value.orElse(null));
value.ifPresent(soundEvent -> minecraft.getSoundManager().play(SimpleSoundInstance.forUI(soundEvent, 1.0F)));
})
).setTooltip(Tooltip.create(Component.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.@Tooltip")));
});
soundButton.setTooltip(Tooltip.create(Component.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.@Tooltip")));
buttons.addChild(soundButton);
//search button
buttons.addChild(new SoundSearchMenu(), LayoutSettings::alignHorizontallyRight);

// Chat message
EditBox chatMessageInput = new EditBox(font, getWidth(2), 20, Component.empty());
Expand Down Expand Up @@ -371,6 +380,39 @@ public void onClick(MouseButtonEvent click, boolean doubled) {
protected void updateWidgetNarration(NarrationElementOutput builder) {}
}

private class SoundSearchMenu extends AbstractWidget {

private SoundSearchMenu() {
super(0, 0, 16, 16, Component.empty());
setTooltip(Tooltip.create(Component.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.search.@Tooltip")));
}

@Override
protected void renderWidget(GuiGraphics context, int mouseX, int mouseY, float deltaTicks) {
context.blitSprite(RenderPipelines.GUI_TEXTURED, SEARCH_ICON_TEXTURE, this.getX(), this.getY(), this.getWidth(), this.getHeight());
}

@Override
public void setX(int x) {
super.setX(x);
}

@Override
public void onClick(MouseButtonEvent click, boolean doubled) {
super.onClick(click, doubled);
Objects.requireNonNull(minecraft);
minecraft.setScreen(new SoundSelectionPopup(ChatRuleConfigScreen.this, sound -> {
if (sound != null) {
chatRule.setCustomSound(sound);
soundButton.setMessage(Component.translatable("skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.custom").withStyle(ChatFormatting.YELLOW));
}
}));
}

@Override
protected void updateWidgetNarration(NarrationElementOutput builder) {}
}

private class ContentContainer extends AbstractContainerWidget implements Layout {
private static final int SIDE_PADDING = 10;
private final List<AbstractWidget> children = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package de.hysky.skyblocker.utils.render.gui;

import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractContainerWidget;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.CycleButton;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.components.StringWidget;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.layouts.GridLayout;
import net.minecraft.client.gui.layouts.LinearLayout;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.input.MouseButtonEvent;
import net.minecraft.client.renderer.RenderPipelines;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvent;
import org.jspecify.annotations.Nullable;

import java.util.List;
import java.util.Locale;
import java.util.function.Consumer;

public class SoundSelectionPopup extends AbstractPopupScreen {
private static final Component YES_TEXT = CommonComponents.GUI_YES.copy().withStyle(ChatFormatting.GREEN);
private static final Component NO_TEXT = CommonComponents.GUI_NO.copy().withStyle(ChatFormatting.RED);

private final List<AbstractWidget> filteredWidgets = new ObjectArrayList<>();
private final Consumer<@Nullable SoundEvent> onDone;
private @Nullable SoundEvent selectedSound = null;
private boolean advanced = false;

private final GridLayout gridWidget = new GridLayout();
private LinearLayout listLayout = new LinearLayout(0, 0, LinearLayout.Orientation.VERTICAL);
private @Nullable Button doneButton;
private @Nullable ListContainer widgetsContainer;

public SoundSelectionPopup(Screen backgroundScreen, Consumer<@Nullable SoundEvent> onDone) {
super(Component.literal("Select Sound"), backgroundScreen);
this.onDone = onDone;

}

@Override
protected void init() {
GridLayout.RowHelper adder = gridWidget.createRowHelper(2);
EditBox searchField = new EditBox(Minecraft.getInstance().font, 200, 20, Component.empty());
searchField.setHint(Component.translatable("gui.recipebook.search_hint").withStyle(ChatFormatting.ITALIC).withStyle(ChatFormatting.GRAY));
searchField.setResponder(this::filterSounds);
addRenderableWidget(adder.addChild(searchField));

CycleButton<Boolean> toggleAdvanced = CycleButton.booleanBuilder(YES_TEXT, NO_TEXT, advanced)
.withTooltip(b -> Tooltip.create(Component.translatable("skyblocker.utils.render.gui.soundSelectionPopup.advanced.@Tooltip")))
.create(0, 0, 100, 20, Component.translatable("skyblocker.utils.render.gui.soundSelectionPopup.advanced"), (button, value) -> {
advanced = !advanced;
filterSounds(searchField.getValue());
});

addRenderableWidget(adder.addChild(toggleAdvanced));

widgetsContainer = new ListContainer(0, 0, 400, this.height / 2);

addRenderableWidget(adder.addChild(widgetsContainer, 2));

addRenderableWidget(adder.addChild(Button.builder(CommonComponents.GUI_CANCEL, b -> {
onClose();
onDone.accept(null);
}).build()));
doneButton = Button.builder(CommonComponents.GUI_DONE, b -> {
onClose();
onDone.accept(selectedSound);
}).build();
doneButton.active = false;
addRenderableWidget(adder.addChild(doneButton));
gridWidget.arrangeElements();
repositionElements();
filterSounds("");
}

private @Nullable Component getSoundName(SoundEvent sound) {
String key = BuiltInRegistries.SOUND_EVENT.getKey(sound).toShortLanguageKey();
//first check for translation
Component translation = Component.translatableWithFallback("subtitles." + key, "null");
if (!translation.getString().equals("null")) {
return translation;
}
//convert note block sounds
if (key.contains("note_block")) {
String[] split = key.split("\\.");
return Component.literal("note block " + split[split.length - 1].replace("_", " "));
}
if (advanced) {
return Component.literal(key);
} else {
return null;
}


}

private void filterSounds(String input) {
filteredWidgets.clear();
for (SoundEvent soundEvent : BuiltInRegistries.SOUND_EVENT) {
Component translation = getSoundName(soundEvent);
//filter sounds
if (translation != null && translation.getString().toLowerCase(Locale.ENGLISH).contains(input.toLowerCase(Locale.ENGLISH))) {
AbstractWidget widget = new SoundWidget(translation, soundEvent);
filteredWidgets.add(widget);
}
}
recreateList();
}

private void recreateList() {
int listX = listLayout.getX();
int listY = listLayout.getY();
listLayout = new LinearLayout(0, 0, LinearLayout.Orientation.VERTICAL);
filteredWidgets.forEach(listLayout::addChild);
listLayout.arrangeElements();
listLayout.setPosition(listX, listY);
if (widgetsContainer != null) {
widgetsContainer.refreshScrollAmount();
}
}

@Override
protected void repositionElements() {
gridWidget.setPosition((width - gridWidget.getWidth()) / 2, (height - gridWidget.getHeight()) / 2);
}

@Override
public void renderBackground(GuiGraphics context, int mouseX, int mouseY, float delta) {
super.renderBackground(context, mouseX, mouseY, delta);
drawPopupBackground(context, gridWidget.getX(), gridWidget.getY(), gridWidget.getWidth(), gridWidget.getHeight());
}

private class ListContainer extends AbstractContainerWidget {

ListContainer(int i, int j, int k, int l) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could copy the parameter names from mcsrc.dev

super(i, j, k, l, Component.literal("List"));
}

@Override
protected int contentHeight() {
return listLayout.getHeight();
}

@Override
protected double scrollRate() {
return 15;
}

@Override
public void setScrollAmount(double scrollY) {
super.setScrollAmount(scrollY);
listLayout.setY(getY() - (int) scrollAmount());
}

private boolean isVisible(AbstractWidget widget) {
return widget.getBottom() >= getY() && widget.getY() < getBottom();
}

@Override
protected void renderWidget(GuiGraphics context, int mouseX, int mouseY, float deltaTicks) {
context.enableScissor(getX(), getY(), getRight(), getBottom());
for (AbstractWidget widget : filteredWidgets) {
if (isVisible(widget)) widget.render(context, mouseX, mouseY, deltaTicks);
}
renderScrollbar(context, mouseX, mouseY);
context.disableScissor();
}

@Override
public void setX(int x) {
super.setX(x);
listLayout.setX(x);
}

@Override
public void setY(int y) {
super.setY(y);
listLayout.setY(y);
}

@Override
protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {

}

@Override
public List<? extends GuiEventListener> children() {
return filteredWidgets;
}
}

private class SoundWidget extends StringWidget {
SoundEvent sound;
Component name;

SoundWidget(Component name, SoundEvent sound) {
super(name, font);
this.name = name;
this.sound = sound;
this.active = true;
}

@Override
public int getHeight() {
return super.getHeight() + 10;
}

@Override
public void renderWidget(GuiGraphics context, int mouseX, int mouseY, float delta) {
super.renderWidget(context, mouseX, mouseY, delta);
if (selectedSound == sound) {
context.fill(RenderPipelines.GUI, this.getX(), this.getY(), this.getRight(), this.getBottom(), 0x3000FF00);
} else if (this.isHovered) {
context.fill(RenderPipelines.GUI, this.getX(), this.getY(), this.getRight(), this.getBottom(), 0x20FFFFFF);
}
}

@Override
public void onClick(MouseButtonEvent click, boolean doubled) {
selectedSound = sound;
if (doneButton != null) doneButton.active = true;
minecraft.getSoundManager().stop();
minecraft.getSoundManager().play(SimpleSoundInstance.forUI(sound, 1.0F));
super.onClick(click, doubled);
}
}
}
3 changes: 3 additions & 0 deletions src/main/resources/assets/skyblocker/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.custom": "Custom",
"skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.none": "None",
"skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.pling": "Pling",
"skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.search.@Tooltip": "Search more sounds",
"skyblocker.config.chat.chatRules.screen.ruleScreen.sounds.zombie": "Zombie",
"skyblocker.config.chat.chatRules.screen.ruleScreen.toast": "Toast:",
"skyblocker.config.chat.chatRules.screen.ruleScreen.toast.duration": "Toast Duration",
Expand Down Expand Up @@ -1811,6 +1812,8 @@
"skyblocker.updateRepository.success": "§aUpdated the local repository successfully.",

"skyblocker.utils.locationUpdateError": "Failed to update your location! Some features of the mod may not work properly :(",
"skyblocker.utils.render.gui.soundSelectionPopup.advanced": "Advanced",
"skyblocker.utils.render.gui.soundSelectionPopup.advanced.@Tooltip": "Show sounds without a propper name",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should end with a period


"skyblocker.valueBreakdownPopup": "Skyblocker Estimated Value Breakdown",
"skyblocker.valueBreakdownPopup.baseItemPrice": "Base item price: %s",
Expand Down