Files
Client/src/main/java/de/fullrisk/citrusDev/UI/MyScreen.java
T

435 lines
17 KiB
Java

package de.fullrisk.citrusDev.UI;
import de.fullrisk.citrusDev.Config;
import de.fullrisk.citrusDev.UIHelper.*;
import io.wispforest.owo.ui.base.BaseOwoScreen;
import io.wispforest.owo.ui.component.UIComponents;
import io.wispforest.owo.ui.container.FlowLayout;
import io.wispforest.owo.ui.container.UIContainers;
import io.wispforest.owo.ui.core.*;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FontDescription;
import net.minecraft.resources.Identifier;
import net.minecraft.sounds.SoundEvents;
import de.fullrisk.citrusDev.client.PlaytimeTracker;
import java.util.List;
import java.util.concurrent.Flow;
import static de.fullrisk.citrusDev.UI.Module_FPS.fpsContainer;
import static de.fullrisk.citrusDev.UI.Module_FPS.toggle;
public class MyScreen extends BaseOwoScreen<FlowLayout> {
static final Identifier MC_FIVE = Identifier.fromNamespaceAndPath("citrus-dev", "minecraft_five");
static final Surface GLASS_PANEL = Surface.blur(5f, 15f)
.and(Surface.flat(0x40FFFFFF))
.and(Surface.outline(0x64de4b));
private static final Surface DISABLED = Surface.blur(5f, 15f)
.and(Surface.flat(0x401c1c1c));
private static final Surface FLAT = Surface.blur(5f, 15f)
.and(Surface.flat(0x40FFFFFF));
private static final int PANEL_WIDTH = 250;
private static final int PANEL_HEIGHT = 150;
private static final int INNER_WIDTH = PANEL_WIDTH - 4;
private static final int INNER_HEIGHT = PANEL_HEIGHT - 4;
private static final int NAVBAR_HEIGHT = 24;
private static final int DIVIDER_SIZE = 1;
private static final int SIDEBAR_WIDTH = 34;
private static final int ROW_HEIGHT = INNER_HEIGHT - NAVBAR_HEIGHT - DIVIDER_SIZE;
private static final int CONTENT_WIDTH = INNER_WIDTH - SIDEBAR_WIDTH - DIVIDER_SIZE;
private record TabDef(String label, Runnable onClick) {}
private record SettingRow(String label, FlowLayout control) {}
private record QuestData(String title, String description, int requiredPlaytime) {
boolean isUnlocked() {
return PlaytimeTracker.getCurrentPlaytimeSeconds() >= requiredPlaytime;
}
}
private FlowLayout content;
private final String initialModule;
public MyScreen() {
this(null);
}
public MyScreen(String initialModule) {
super(Component.literal("Citrus Dev"));
this.initialModule = initialModule;
Minecraft.getInstance().getSoundManager().play(
SimpleSoundInstance.forUI(SoundEvents.UI_TOAST_IN, 1.5F, 2)
);
}
@Override
protected OwoUIAdapter<FlowLayout> createAdapter() {
return OwoUIAdapter.create(this, UIContainers::verticalFlow);
}
@Override
protected void build(FlowLayout rootComponent) {
rootComponent.gap(0);
rootComponent
.horizontalAlignment(HorizontalAlignment.CENTER)
.verticalAlignment(VerticalAlignment.CENTER);
FlowLayout panel = UIContainers.verticalFlow(Sizing.content(), Sizing.content());
panel.gap(0);
panel.padding(Insets.of(2));
panel.horizontalAlignment(HorizontalAlignment.CENTER);
panel.verticalAlignment(VerticalAlignment.CENTER);
panel.horizontalSizing(Sizing.fixed(0));
panel.verticalSizing(Sizing.fixed(0));
panel.horizontalSizing().animate(300, Easing.CUBIC, Sizing.fixed(PANEL_WIDTH)).forwards();
panel.verticalSizing().animate(300, Easing.CUBIC, Sizing.fixed(PANEL_HEIGHT)).forwards();
panel.surface(GLASS_PANEL);
FlowLayout navbar = UIContainers.horizontalFlow(Sizing.fixed(INNER_WIDTH), Sizing.fixed(NAVBAR_HEIGHT));
buildNavbar(navbar);
panel.child(navbar);
FlowLayout navDivider = UIContainers.horizontalFlow(Sizing.fixed(INNER_WIDTH), Sizing.fixed(DIVIDER_SIZE));
navDivider.surface(Surface.flat(0x80FFFFFF));
panel.child(navDivider);
FlowLayout row = UIContainers.horizontalFlow(Sizing.fixed(INNER_WIDTH), Sizing.fixed(ROW_HEIGHT));
row.gap(0);
FlowLayout sidebar = buildSidebar();
row.child(sidebar);
content = UIContainers.verticalFlow(Sizing.fixed(CONTENT_WIDTH), Sizing.fixed(ROW_HEIGHT));
content.gap(2);
content.padding(Insets.of(4));
content.horizontalAlignment(HorizontalAlignment.CENTER);
content.verticalAlignment(VerticalAlignment.TOP);
row.child(content);
panel.child(row);
FlowLayout sidebarDivider = UIContainers.verticalFlow(Sizing.fixed(DIVIDER_SIZE), Sizing.fixed(INNER_HEIGHT));
sidebarDivider.surface(Surface.flat(0x80FFFFFF));
sidebarDivider.positioning(Positioning.absolute(SIDEBAR_WIDTH, 0));
panel.child(sidebarDivider);
var version = new ScalableLabel(Component.literal("Citrus-Dev v2.0"));
version.horizontalTextAlignment(HorizontalAlignment.RIGHT);
version.verticalTextAlignment(VerticalAlignment.BOTTOM);
version.horizontalSizing(Sizing.fixed(202));
version.verticalSizing(Sizing.fixed(12));
version.positioning(Positioning.relative(125, 100));
version.scale().set(new ScaleValue(0.7f));
panel.child(version);
rootComponent.child(panel);
if (initialModule != null) {
openModuleByKey(initialModule);
} else {
buildGeneralPanel(content);
}
}
private void buildNavbar(FlowLayout navbar) {
navbar.horizontalAlignment(HorizontalAlignment.RIGHT);
navbar.verticalAlignment(VerticalAlignment.CENTER);
navbar.padding(Insets.of(0, 0, 0, 24));
navbar.gap(16);
navbar.child(UIComponents.texture(
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/citrus-logo.png"),
0, 0, 30, 30, 30, 30
)).horizontalAlignment(HorizontalAlignment.LEFT);
List<TabDef> tabs = List.of(
new TabDef("general", () -> {
content.clearChildren();
content.horizontalAlignment(HorizontalAlignment.CENTER);
content.verticalAlignment(VerticalAlignment.TOP);
buildGeneralPanel(content);
}),
new TabDef("quests", () -> {
content.clearChildren();
content.horizontalAlignment(HorizontalAlignment.CENTER);
content.verticalAlignment(VerticalAlignment.TOP);
QuestsPanel.build(content, CONTENT_WIDTH, ROW_HEIGHT);
}),
new TabDef("settings", () -> {
content.clearChildren();
content.horizontalAlignment(HorizontalAlignment.CENTER);
content.verticalAlignment(VerticalAlignment.TOP);
buildSettingsPanel(content);
})
);
for (TabDef tab : tabs) {
navbar.child(makeTab(tab.label(), tab.onClick()));
}
}
private FlowLayout makeTab(String text, Runnable onClick) {
FlowLayout tab = UIContainers.verticalFlow(Sizing.content(), Sizing.content());
tab.verticalAlignment(VerticalAlignment.CENTER);
tab.horizontalAlignment(HorizontalAlignment.CENTER);
tab.padding(Insets.of(2, 0, 2, 2));
tab.gap(2);
var label = UIComponents.label(Component.literal(text)
.withStyle(style -> style.withFont(new FontDescription.Resource(MC_FIVE))))
.color(Color.ofRgb(0xCCCCCC))
.shadow(true);;
tab.child(label);
int underlineWidth = Math.max(text.length() * 6, 10);
FlowLayout underline = UIContainers.horizontalFlow(Sizing.fixed(underlineWidth), Sizing.fixed(1));
underline.surface(Surface.flat(0x00FFFFFF));
tab.child(underline);
tab.mouseEnter().subscribe(() -> {
label.color(Color.ofRgb(0xFFFFFF));
underline.surface(Surface.flat(0xFFFFFFFF));
});
tab.mouseLeave().subscribe(() -> {
label.color(Color.ofRgb(0xCCCCCC));
underline.surface(Surface.flat(0x00FFFFFF));
});
tab.mouseDown().subscribe((click, doubled) -> {
Minecraft.getInstance().getSoundManager().play(
SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 2.0F, 2)
);
onClick.run();
return true;
});
return tab;
}
private FlowLayout buildSidebar() {
FlowLayout sidebar = UIContainers.verticalFlow(Sizing.fixed(SIDEBAR_WIDTH), Sizing.fixed(ROW_HEIGHT));
sidebar.horizontalAlignment(HorizontalAlignment.CENTER);
sidebar.verticalAlignment(VerticalAlignment.TOP);
sidebar.padding(Insets.of(4));
sidebar.gap(2);
sidebar.child(makeSidebarButton("-", 430));
sidebar.child(makeSidebarButton("+", 400));
return sidebar;
}
private ScalableButton makeSidebarButton(String symbol, int animateDelay) {
var button = new ScalableButton(Component.literal(symbol), btn -> {
Minecraft.getInstance().getSoundManager().play(
SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK.value(), 2.0F, 2)
);
},
0x80FFFFFF,
0xA0FFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
1
);
button.horizontalSizing(Sizing.fixed(20));
button.verticalSizing(Sizing.fixed(20));
button.scale().set(new ScaleValue(0f));
button.scale().animate(animateDelay, Easing.CUBIC, new ScaleValue(1f)).forwards();
button.mouseEnter().subscribe(() -> button.scale().animate(120, Easing.CUBIC, new ScaleValue(1.1f)).forwards());
button.mouseLeave().subscribe(() -> button.scale().animate(120, Easing.CUBIC, new ScaleValue(1.0f)).forwards());
return button;
}
private void buildGeneralPanel(FlowLayout content) {
FlowLayout fpsRow = ModuleRow.build(
GLASS_PANEL, DISABLED, FLAT,
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/block_culling_file.png"),
18, 12,
MC_FIVE,
"FPS",
"Displays your FPS",
new ModuleRow.ToggleState() {
public boolean get() { return toggle; }
public void set(boolean value) { toggle = value; }
},
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/settings.png"),
() -> {
Config.get().lastModule = "FPS";
Config.get().save();
openModuleSettings("FPS", fpsModuleSettings());
},
"Module Enabled", "FPS counter is now active",
"Module Disabled", "FPS counter is now inactive"
);
FlowLayout keyStrokesRow = ModuleRow.build(
GLASS_PANEL, DISABLED, FLAT,
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/block_culling_file.png"),
18, 12,
MC_FIVE,
"Keystrokes",
"Current Key Presses",
new ModuleRow.ToggleState() {
public boolean get() { return Module_KeyStrokes.isEnabled(); }
public void set(boolean value) { Module_KeyStrokes.setEnabled(value); }
},
Identifier.fromNamespaceAndPath("citrus-dev", "textures/icons/settings.png"),
() -> {
Config.get().lastModule = "Key";
Config.get().save();
openModuleSettings("Key", keyStrokesSettings());
},
"Module Enabled", "Keystrokes is now active",
"Module Disabled", "KeyStrokes is now inactive"
);
content.child(keyStrokesRow);
content.child(fpsRow);
}
private void buildSettingsPanel(FlowLayout content) {
content.child(UIComponents.label(Component.literal("Settings")));
}
private void openModuleSettings(String title, List<SettingRow> rows) {
content.clearChildren();
content.horizontalAlignment(HorizontalAlignment.LEFT);
content.verticalAlignment(VerticalAlignment.TOP);
FlowLayout header = UIContainers.horizontalFlow(Sizing.fixed(CONTENT_WIDTH - 8), Sizing.content());
header.verticalAlignment(VerticalAlignment.CENTER);
header.horizontalAlignment(HorizontalAlignment.LEFT);
header.gap(4);
header.child(SettingsControls.makeTextButton(FLAT, "<", () -> {
content.clearChildren();
content.horizontalAlignment(HorizontalAlignment.CENTER);
content.verticalAlignment(VerticalAlignment.TOP);
buildGeneralPanel(content);
}));
header.child(UIComponents.label(Component.literal(title)
.withStyle(style -> style.withFont(new FontDescription.Resource(MC_FIVE))))
.color(Color.ofRgb(0xCCCCCC)));
content.child(header);
FlowLayout rowsContainer = UIContainers.verticalFlow(Sizing.fixed(CONTENT_WIDTH - 8), Sizing.content());
rowsContainer.gap(1);
rowsContainer.verticalAlignment(VerticalAlignment.TOP);
rowsContainer.horizontalAlignment(HorizontalAlignment.LEFT);
rowsContainer.padding(Insets.of(0));
for (SettingRow row : rows) {
FlowLayout settingRow = makeSettingRow(row);
settingRow.verticalSizing(Sizing.fixed(16)); // pin each row to a compact fixed height
rowsContainer.child(settingRow);
}
var scrollArea = UIContainers.verticalScroll(
Sizing.fixed(CONTENT_WIDTH - 8),
Sizing.fixed(ROW_HEIGHT - 24),
rowsContainer
);
content.child(scrollArea);
}
private void openModuleByKey(String key) {
switch (key) {
case "FPS" -> openModuleSettings("FPS", fpsModuleSettings());
case "Key" -> openModuleSettings("Keystrokes", keyStrokesSettings());
default -> buildGeneralPanel(content);
}
}
private FlowLayout makeSettingRow(SettingRow setting) {
FlowLayout row = UIContainers.horizontalFlow(Sizing.fixed(CONTENT_WIDTH - 8), Sizing.fixed(16));
row.verticalAlignment(VerticalAlignment.CENTER);
row.horizontalAlignment(HorizontalAlignment.LEFT);
row.padding(Insets.of(0));
row.gap(4);
row.child(UIComponents.label(Component.literal(setting.label()))
.color(Color.ofRgb(0xa3a3ac)));
row.child(UIComponents.spacer());
row.child(setting.control());
return row;
}
private List<SettingRow> fpsModuleSettings() {
List<SettingsControls.ButtonState> positionStates = List.of(
new SettingsControls.ButtonState("L-TOP", () -> Module_FPS.setPosition(1, 1)),
new SettingsControls.ButtonState("R-TOP", () -> Module_FPS.setPosition(99, 1)),
new SettingsControls.ButtonState("R-BOTTOM", () -> Module_FPS.setPosition(99, 99)),
new SettingsControls.ButtonState("L-BOTTOM", () -> Module_FPS.setPosition(1, 99))
);
FlowLayout sizeSlider = SettingsControls.makeSlider(
FLAT,
80,
0.0, 20.0,
4.0,
value -> (int) value + "px",
value -> Module_FPS.fpsContainer.gap((int) value)
);
int currentIndex = positionIndex(Config.get().fpsX, Config.get().fpsY);
return List.of(
new SettingRow("Position", SettingsControls.makeCyclingButton(GLASS_PANEL, positionStates, currentIndex)),
new SettingRow("Size", sizeSlider)
);
}
private List<SettingRow> keyStrokesSettings() {
List<SettingsControls.ButtonState> positionStates = List.of(
new SettingsControls.ButtonState("L-TOP", () -> Module_KeyStrokes.setPosition(1, 1)),
new SettingsControls.ButtonState("R-TOP", () -> Module_KeyStrokes.setPosition(99, 1)),
new SettingsControls.ButtonState("R-BOTTOM", () -> Module_KeyStrokes.setPosition(99, 99)),
new SettingsControls.ButtonState("L-BOTTOM", () -> Module_KeyStrokes.setPosition(1, 99))
);
FlowLayout sizeSlider = SettingsControls.makeSlider(
FLAT,
80,
0.0, 20.0,
4.0,
value -> (int) value + "px",
value -> {} // fixed: was Module_FPS.fpsContainer.gap(...)
);
int currentIndex = positionIndex(Config.get().keyX, Config.get().keyY);
return List.of(
new SettingRow("Position", SettingsControls.makeCyclingButton(GLASS_PANEL, positionStates, currentIndex)),
new SettingRow("Size", sizeSlider)
);
}
private int positionIndex(int x, int y) {
boolean right = x > 50;
boolean bottom = y > 50;
if (!right && !bottom) return 0; // L-TOP
if (right && !bottom) return 1; // R-TOP
if (right && bottom) return 2; // R-BOTTOM
return 3; // L-BOTTOM
}
@Override
public boolean isPauseScreen() {
return false;
}
}