Skip to content

Commit

Permalink
Merge pull request #123 from FTBTeam/dev
Browse files Browse the repository at this point in the history
[1.21] Add Grouped Button & IntTextField (#121)
  • Loading branch information
desht authored Sep 11, 2024
2 parents 5f0ad8c + 5f2c9a7 commit ece8062
Show file tree
Hide file tree
Showing 2 changed files with 303 additions and 0 deletions.
67 changes: 67 additions & 0 deletions common/src/main/java/dev/ftb/mods/ftblibrary/ui/IntTextBox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package dev.ftb.mods.ftblibrary.ui;

import dev.ftb.mods.ftblibrary.ui.input.KeyModifiers;

import java.util.function.Predicate;

public class IntTextBox extends TextBox {

private static final Predicate<String> IS_NUMBER = s -> s.matches("^[0-9]+$");
private int min = Integer.MIN_VALUE;
private int max = Integer.MAX_VALUE;

public IntTextBox(Panel panel) {
super(panel);
setFilter(IS_NUMBER);
}

public int getIntValue() {
return Integer.parseInt(getText());
}

public void setMin(int min) {
this.min = min;
}

public void setMax(int max) {
this.max = max;
}

public void setMinMax(int min, int max) {
this.min = min;
this.max = max;
}

@Override
public boolean mouseScrolled(double scroll) {
setAmount(getIntValue() + (int) scroll);
return true;
}

public void setAmount(int amount) {
setText(String.valueOf(amount));
}

@Override
public void onTextChanged() {
ensureValue();
}

public void ensureValue() {
int amount = getIntValue();

if (amount < min) {
setAmount(min);
} else if (amount > max) {
setAmount(max);
}
}

@Override
public boolean charTyped(char c, KeyModifiers modifiers) {
if (Character.isDigit(c)) {
return super.charTyped(c, modifiers);
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
package dev.ftb.mods.ftblibrary.ui.misc;

import com.mojang.blaze3d.platform.InputConstants;
import dev.ftb.mods.ftblibrary.icon.Color4I;
import dev.ftb.mods.ftblibrary.icon.Icons;
import dev.ftb.mods.ftblibrary.ui.Button;
import dev.ftb.mods.ftblibrary.ui.Panel;
import dev.ftb.mods.ftblibrary.ui.SimpleButton;
import dev.ftb.mods.ftblibrary.ui.TextField;
import dev.ftb.mods.ftblibrary.ui.Theme;
import dev.ftb.mods.ftblibrary.ui.input.Key;
import dev.ftb.mods.ftblibrary.ui.input.MouseButton;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import org.lwjgl.glfw.GLFW;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static dev.ftb.mods.ftblibrary.util.TextComponentUtils.hotkeyTooltip;

public abstract class AbstractGroupedButtonListScreen<G, E> extends AbstractButtonListScreen {
protected final List<GroupData<G, E>> groups;
private final Set<G> collapsed = new HashSet<>();

private final Button buttonCollapseAll, buttonExpandAll;
private final Component title;

public AbstractGroupedButtonListScreen(Component title) {
showBottomPanel(false);
showCloseButton(true);
setHasSearchBox(true);
this.title = title;
this.groups = getGroups();

groups.stream()
.filter(GroupData::defaultedCollapsed)
.map(GroupData::group)
.forEach(collapsed::add);

buttonExpandAll = new SimpleButton(topPanel, List.of(Component.translatable("gui.expand_all"), hotkeyTooltip("="), hotkeyTooltip("+")), Icons.UP,
(widget, button) -> toggleAll(true));
buttonCollapseAll = new SimpleButton(topPanel, List.of(Component.translatable("gui.collapse_all"), hotkeyTooltip("-")), Icons.DOWN,
(widget, button) -> toggleAll(false));
}

protected abstract List<GroupData<G, E>> getGroups();

protected GroupButton createGroupButton(Panel panel, GroupData<G, E> group) {
return new GroupButton(panel, group.group(), group.groupName, group.values());
}

protected abstract RowPanel createRowPanel(Panel panel, E value);

private void toggleAll(boolean collapsed) {
boolean allOpen = this.groups.stream().noneMatch(g -> isCollapsed(g.group));
// Don't try and re-render if everything is already open
if (allOpen && !collapsed) {
return;
}
this.groups.forEach(group -> setCollapsed(group.group(), collapsed));
scrollBar.setValue(0);
getGui().refreshWidgets();
}

@Override
protected void doCancel() {

}

@Override
protected void doAccept() {

}

@Override
public boolean onInit() {
setWidth(220);
setHeight(getScreen().getGuiScaledHeight() * 4 / 5);
return true;
}

@Override
public boolean keyPressed(Key key) {
if (super.keyPressed(key)) {
return true;
} else if (key.is(InputConstants.KEY_ADD) || key.is(InputConstants.KEY_EQUALS)) {
toggleAll(false);
} else if (key.is(InputConstants.KEY_MINUS) || key.is(GLFW.GLFW_KEY_KP_SUBTRACT)) {
toggleAll(true);
}
return false;
}

@Override
protected int getTopPanelHeight() {
return 22;
}

@Override
protected Panel createTopPanel() {
return new CustomTopPanel();
}

@Override
public void addButtons(Panel panel) {
for (GroupData<G, E> group : groups) {
GroupButton groupButton = createGroupButton(panel, group);
panel.add(groupButton);
if (!groupButton.isCollapsed()) {
panel.addAll(groupButton.collectPanels());
}
}
}


public boolean isCollapsed(G group) {
return collapsed.contains(group);
}

public void setCollapsed(G group, boolean collapsed) {
if (collapsed) {
this.collapsed.add(group);
} else {
this.collapsed.remove(group);
}
}

public record GroupData<G, E>(G group, boolean defaultedCollapsed, Component groupName, List<E> values) {
}

protected class CustomTopPanel extends TopPanel {
private final TextField titleLabel = new TextField(this);

@Override
public void addWidgets() {
titleLabel.setText(title);
titleLabel.addFlags(Theme.CENTERED_V);
add(titleLabel);

if (groups.size() > 1) {
add(buttonExpandAll);
add(buttonCollapseAll);
}
}

@Override
public void alignWidgets() {
titleLabel.setPosAndSize(4, 0, titleLabel.width, height);
if (groups.size() > 1) {
buttonExpandAll.setPos(width - 18, 2);
buttonCollapseAll.setPos(width - 38, 2);
}
}
}

protected class GroupButton extends Button {
protected final G group;
private final Component titleText;
private final List<RowPanel> rowPanels;

public GroupButton(Panel panel, G group, Component titleText, List<E> values) {
super(panel);
this.group = group;
this.titleText = titleText;
setCollapsed(isCollapsed());
this.rowPanels = new ArrayList<>();
for (E value : values) {
rowPanels.add(createRowPanel(panel, value));
}
}

public List<RowPanel> collectPanels() {
return isCollapsed() ? List.of() : List.copyOf(rowPanels);
}

@Override
public void onClicked(MouseButton button) {
setCollapsed(!isCollapsed());
parent.refreshWidgets();
refreshWidgets();
playClickSound();
}

public boolean isCollapsed() {
return AbstractGroupedButtonListScreen.this.isCollapsed(group);
}

public void setCollapsed(boolean collapsed) {
AbstractGroupedButtonListScreen.this.setCollapsed(group, collapsed);
boolean isCollapsed = isCollapsed();
setTitle(Component.literal(isCollapsed ? "▶ " : "▼ ").withStyle(isCollapsed ? ChatFormatting.RED : ChatFormatting.GREEN).append(titleText));
}

@Override
public void draw(GuiGraphics graphics, Theme theme, int x, int y, int w, int h) {
theme.drawWidget(graphics, x, y, w, h, getWidgetType());
theme.drawString(graphics, getTitle(), x + 3, y + 3);
if (isMouseOver()) {
Color4I.WHITE.withAlpha(33).draw(graphics, x, y, w, h);
}
}
}

protected abstract class RowPanel extends Panel {
protected final E value;

public RowPanel(Panel panel, E value) {
super(panel);
this.value = value;
setHeight(18);
}

@Override
public void draw(GuiGraphics graphics, Theme theme, int x, int y, int w, int h) {
super.draw(graphics, theme, x, y, w, h);

var mouseOver = getMouseY() >= 20 && isMouseOver();

if (mouseOver) {
Color4I.WHITE.withAlpha(33).draw(graphics, x, y, w, h);
}
}

@Override
public boolean mousePressed(MouseButton button) {
if (isMouseOver() && button.isRight()) {
return true;
}
return super.mousePressed(button);
}
}
}

0 comments on commit ece8062

Please sign in to comment.