[config-ui-tiny] reflow config screen entries on size change
This commit is contained in:
parent
aafc14e3fc
commit
0821ce57c2
|
@ -1,12 +1,17 @@
|
|||
package io.gitlab.jfronny.libjf.config.api.v1.ui.tiny;
|
||||
|
||||
import io.gitlab.jfronny.libjf.config.impl.ui.tiny.TinyConfigScreen;
|
||||
import io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry.Reflowable;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.widget.ClickableWidget;
|
||||
|
||||
public interface WidgetFactory {
|
||||
Widget build(TinyConfigScreen screen, TextRenderer textRenderer);
|
||||
|
||||
record Widget(Runnable updateControls, ClickableWidget control) {
|
||||
record Widget(Runnable updateControls, ClickableWidget control, Reflowable reflow) implements Reflowable {
|
||||
@Override
|
||||
public void reflow(int width, int height) {
|
||||
reflow.reflow(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.gitlab.jfronny.libjf.config.impl.ui.tiny.presets.PresetsScreen;
|
|||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.ScreenRect;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.tab.TabManager;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
|
@ -63,18 +64,17 @@ public class TinyConfigScreen extends Screen {
|
|||
protected void init() {
|
||||
super.init();
|
||||
|
||||
this.done = ButtonWidget.builder(ScreenTexts.DONE, button -> {
|
||||
for (WidgetState<?> state : widgets) {
|
||||
Try.orElse(state::writeToEntry, e -> LibJf.LOGGER.error("Could not write config data to class", e));
|
||||
}
|
||||
config.getRoot().write();
|
||||
Objects.requireNonNull(client).setScreen(parent);
|
||||
})
|
||||
.position(this.width / 2 + 4, this.height - 28)
|
||||
.size(150, 20)
|
||||
.build();
|
||||
|
||||
if (!reload) {
|
||||
this.done = ButtonWidget.builder(ScreenTexts.DONE, button -> {
|
||||
for (WidgetState<?> state : widgets) {
|
||||
Try.orElse(state::writeToEntry, e -> LibJf.LOGGER.error("Could not write config data to class", e));
|
||||
}
|
||||
config.getRoot().write();
|
||||
Objects.requireNonNull(client).setScreen(parent);
|
||||
})
|
||||
.dimensions(this.width / 2 + 4, this.height - 28, 150, 20)
|
||||
.build();
|
||||
|
||||
if (config.getEntries().isEmpty()
|
||||
&& config.getPresets().keySet().stream().allMatch(s -> s.equals(CategoryBuilder.CONFIG_PRESET_DEFAULT))
|
||||
&& config.getReferencedConfigs().isEmpty()
|
||||
|
@ -88,7 +88,7 @@ public class TinyConfigScreen extends Screen {
|
|||
this.tabs = List.of();
|
||||
}
|
||||
if (this.tabs.isEmpty()) this.tabs = List.of(new TinyConfigTab(this, config, textRenderer, true));
|
||||
}
|
||||
} else done.setPosition(width / 2 + 4, height - 28);
|
||||
|
||||
TabNavigationWidget tabNavigation = TabNavigationWidget.builder(tabManager, this.width)
|
||||
.tabs(tabs.toArray(TinyConfigTab[]::new))
|
||||
|
@ -98,23 +98,24 @@ public class TinyConfigScreen extends Screen {
|
|||
tabNavigation.selectTab(0, false);
|
||||
tabNavigation.init();
|
||||
|
||||
this.addDrawableChild(ButtonWidget.builder(ScreenTexts.CANCEL,
|
||||
button -> Objects.requireNonNull(client).setScreen(parent))
|
||||
.dimensions(this.width / 2 - 154, this.height - 28, 150, 20)
|
||||
.build());
|
||||
|
||||
this.addDrawableChild(done);
|
||||
|
||||
if (tabs.size() == 1 && !config.getPresets().isEmpty()) {
|
||||
this.addDrawableChild(ButtonWidget.builder(Text.translatable("libjf-config-v1.presets"),
|
||||
button -> Objects.requireNonNull(client).setScreen(new PresetsScreen(this, config)))
|
||||
.position(4, 6)
|
||||
.size(80, 20)
|
||||
.dimensions(4, 6, 80, 20)
|
||||
.build());
|
||||
}
|
||||
|
||||
this.addDrawableChild(ButtonWidget.builder(ScreenTexts.CANCEL,
|
||||
button -> Objects.requireNonNull(client).setScreen(parent))
|
||||
.position(this.width / 2 - 154, this.height - 28)
|
||||
.size(150, 20)
|
||||
.build());
|
||||
|
||||
|
||||
this.addSelectableChild(this.placeholder);
|
||||
this.addDrawableChild(done);
|
||||
|
||||
// Sizing is also done in TinyConfigTab. Keep these in sync!
|
||||
tabManager.setTabArea(new ScreenRect(0, 32, width, height));
|
||||
|
||||
reload = true;
|
||||
}
|
||||
|
|
|
@ -30,18 +30,17 @@ public class TinyConfigTab implements Tab {
|
|||
widget.updateCache();
|
||||
}
|
||||
|
||||
// Sizing is also done in TinyConfigScreen. Keep these in sync!
|
||||
this.list = new EntryListWidget(screen.getClient(), textRenderer, screen.width, screen.height, 32, screen.height - 36, 25);
|
||||
|
||||
if (isRoot) {
|
||||
for (Map.Entry<String, ConfigCategory> entry : config.getCategories().entrySet()) {
|
||||
this.list.addReference(screen.width / 2,
|
||||
TinyConfigScreen.getTitle(entry.getValue().getTranslationPrefix()),
|
||||
this.list.addReference(TinyConfigScreen.getTitle(entry.getValue().getTranslationPrefix()),
|
||||
() -> new TinyConfigScreen(entry.getValue(), screen));
|
||||
}
|
||||
} else {
|
||||
if (!config.getPresets().isEmpty()) {
|
||||
this.list.addReference(screen.width / 2,
|
||||
Text.translatable("libjf-config-v1.presets"),
|
||||
this.list.addReference(Text.translatable("libjf-config-v1.presets"),
|
||||
() -> new PresetsScreen(screen, config));
|
||||
}
|
||||
}
|
||||
|
@ -52,21 +51,23 @@ public class TinyConfigTab implements Tab {
|
|||
info.reset();
|
||||
if (control != null) control.updateControls().run();
|
||||
}))
|
||||
.position(screen.width - 155, 0)
|
||||
.size(40, 20)
|
||||
.dimensions(screen.width - 155, 0, 40, 20)
|
||||
.build();
|
||||
BooleanSupplier resetVisible = () -> {
|
||||
boolean visible = !Objects.equals(info.entry.getDefault(), info.cachedValue);
|
||||
resetButton.active = visible;
|
||||
return visible;
|
||||
};
|
||||
if (control == null) this.list.addUnknown(resetButton, resetVisible, name);
|
||||
else this.list.addButton(control.control(), resetButton, resetVisible, name);
|
||||
Reflowable reflow = (width, height) -> {
|
||||
resetButton.setX(width - 155);
|
||||
if (control != null) control.reflow(width, height);
|
||||
};
|
||||
if (control == null) this.list.addUnknown(resetButton, resetVisible, name, reflow);
|
||||
else this.list.addButton(control.control(), resetButton, resetVisible, name, reflow);
|
||||
}
|
||||
for (ConfigInstance ci : config.getReferencedConfigs()) {
|
||||
if (ci != null) {
|
||||
this.list.addReference(screen.width / 2,
|
||||
Text.translatable("libjf-config-v1.see-also", TinyConfigScreen.getTitle(ci.getTranslationPrefix())),
|
||||
this.list.addReference(Text.translatable("libjf-config-v1.see-also", TinyConfigScreen.getTitle(ci.getTranslationPrefix())),
|
||||
() -> new TinyConfigScreen(ci, screen));
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +85,7 @@ public class TinyConfigTab implements Tab {
|
|||
|
||||
@Override
|
||||
public void refreshGrid(ScreenRect tabArea) {
|
||||
list.refreshGrid(tabArea);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -100,8 +100,10 @@ public class EntryInfoWidgetBuilder {
|
|||
final ButtonWidget button = ButtonWidget.builder(valueToText.apply(state.cachedValue), btn -> {
|
||||
state.updateCache(increment.apply(state.cachedValue));
|
||||
btn.setMessage(valueToText.apply(state.cachedValue));
|
||||
}).position(screen.width - 110, 0).size(info.getWidth(), 20).build();
|
||||
return new WidgetFactory.Widget(() -> button.setMessage(valueToText.apply(state.cachedValue)), button);
|
||||
})
|
||||
.dimensions(screen.width - 110, 0, info.getWidth(), 20)
|
||||
.build();
|
||||
return new WidgetFactory.Widget(() -> button.setMessage(valueToText.apply(state.cachedValue)), button, (width, height) -> button.setX(width - 110));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -148,59 +150,63 @@ public class EntryInfoWidgetBuilder {
|
|||
return true;
|
||||
});
|
||||
|
||||
return new WidgetFactory.Widget(() -> widget.setText(state.cachedValue == null ? "" : state.cachedValue.toString()), widget);
|
||||
return new WidgetFactory.Widget(() -> widget.setText(state.cachedValue == null ? "" : state.cachedValue.toString()), widget, (width, height) -> widget.setX(width - 110));
|
||||
};
|
||||
}
|
||||
|
||||
private static <T> WidgetFactory jsonScreen(ConfigCategory config, EntryInfo<T> info, WidgetState<T> state) {
|
||||
state.managedTemp = false;
|
||||
state.tempValue = null;
|
||||
return (screen, textRenderer) -> new WidgetFactory.Widget(
|
||||
R::nop,
|
||||
ButtonWidget.builder(Text.translatable("libjf-config-core-v1.edit"), button -> {
|
||||
final String jsonified;
|
||||
if (state.tempValue == null) {
|
||||
try {
|
||||
jsonified = GsonHolders.CONFIG.getGson().toJson(state.cachedValue);
|
||||
} catch (Throwable e) {
|
||||
LibJf.LOGGER.error("Could not stringify element", e);
|
||||
SystemToast.add(
|
||||
screen.getClient().getToastManager(),
|
||||
SystemToast.Type.PACK_LOAD_FAILURE,
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.read.fail.title"),
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.read.fail.description")
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
jsonified = state.tempValue;
|
||||
}
|
||||
String key = config.getTranslationPrefix() + info.getName();
|
||||
screen.getClient().setScreen(new EditorScreen(
|
||||
Text.translatable(key),
|
||||
I18n.hasTranslation(key + ".tooltip") ? Text.translatable(key + ".tooltip") : null,
|
||||
screen,
|
||||
jsonified,
|
||||
json -> {
|
||||
try {
|
||||
state.updateCache(GsonHolders.CONFIG.getGson().fromJson(json, info.getValueType().asClass()));
|
||||
state.tempValue = null;
|
||||
} catch (Throwable e) {
|
||||
LibJf.LOGGER.error("Could not write element", e);
|
||||
SystemToast.add(
|
||||
screen.getClient().getToastManager(),
|
||||
SystemToast.Type.PACK_LOAD_FAILURE,
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.write.fail.title"),
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.write.fail.description")
|
||||
);
|
||||
state.tempValue = json;
|
||||
}
|
||||
return (screen, textRenderer) -> {
|
||||
final ButtonWidget button = ButtonWidget.builder(Text.translatable("libjf-config-core-v1.edit"), $ -> {
|
||||
final String jsonified;
|
||||
if (state.tempValue == null) {
|
||||
try {
|
||||
jsonified = GsonHolders.CONFIG.getGson().toJson(state.cachedValue);
|
||||
} catch (Throwable e) {
|
||||
LibJf.LOGGER.error("Could not stringify element", e);
|
||||
SystemToast.add(
|
||||
screen.getClient().getToastManager(),
|
||||
SystemToast.Type.PACK_LOAD_FAILURE,
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.read.fail.title"),
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.read.fail.description")
|
||||
);
|
||||
return;
|
||||
}
|
||||
));
|
||||
})
|
||||
.dimensions(screen.width - 110, 0, info.getWidth(), 20)
|
||||
.build()
|
||||
);
|
||||
} else {
|
||||
jsonified = state.tempValue;
|
||||
}
|
||||
String key = config.getTranslationPrefix() + info.getName();
|
||||
screen.getClient().setScreen(new EditorScreen(
|
||||
Text.translatable(key),
|
||||
I18n.hasTranslation(key + ".tooltip") ? Text.translatable(key + ".tooltip") : null,
|
||||
screen,
|
||||
jsonified,
|
||||
json -> {
|
||||
try {
|
||||
state.updateCache(GsonHolders.CONFIG.getGson().fromJson(json, info.getValueType().asClass()));
|
||||
state.tempValue = null;
|
||||
} catch (Throwable e) {
|
||||
LibJf.LOGGER.error("Could not write element", e);
|
||||
SystemToast.add(
|
||||
screen.getClient().getToastManager(),
|
||||
SystemToast.Type.PACK_LOAD_FAILURE,
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.write.fail.title"),
|
||||
Text.translatable("libjf-config-ui-tiny-v1.entry.json.write.fail.description")
|
||||
);
|
||||
state.tempValue = json;
|
||||
}
|
||||
}
|
||||
));
|
||||
})
|
||||
.dimensions(screen.width - 110, 0, info.getWidth(), 20)
|
||||
.build();
|
||||
return new WidgetFactory.Widget(
|
||||
R::nop,
|
||||
button,
|
||||
(width, height) -> button.setX(width - 110)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
private static <T extends Number> WidgetFactory slider(EntryInfo info, WidgetState state, Function<T, Double> t2d, Function<Double, T> d2t, boolean wholeNumber) {
|
||||
|
@ -213,7 +219,7 @@ public class EntryInfoWidgetBuilder {
|
|||
state.updateCache(d2t.apply(v));
|
||||
}, wholeNumber);
|
||||
|
||||
return new WidgetFactory.Widget(() -> slider.setValue(t2d.apply((T) state.cachedValue)), slider);
|
||||
return new WidgetFactory.Widget(() -> slider.setValue(t2d.apply((T) state.cachedValue)), slider, (width, height) -> slider.setX(width - 110));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ import net.fabricmc.api.EnvType;
|
|||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.Element;
|
||||
import net.minecraft.client.gui.Selectable;
|
||||
import net.minecraft.client.gui.*;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.*;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.sql.Ref;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
@ -21,8 +21,8 @@ import java.util.function.Supplier;
|
|||
public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEntry> {
|
||||
TextRenderer textRenderer;
|
||||
|
||||
public EntryListWidget(MinecraftClient client, TextRenderer tr, int i, int j, int k, int l, int m) {
|
||||
super(client, i, j, k, l, m);
|
||||
public EntryListWidget(MinecraftClient client, TextRenderer tr, int width, int height, int top, int bottom, int itemHeight) {
|
||||
super(client, width, height, top, bottom, itemHeight);
|
||||
this.centerListVertically = false;
|
||||
textRenderer = tr;
|
||||
setRenderBackground(client.world == null);
|
||||
|
@ -33,16 +33,16 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
return this.width -7;
|
||||
}
|
||||
|
||||
public void addUnknown(ClickableWidget resetButton, BooleanSupplier resetVisible, Text text) {
|
||||
this.addEntry(new ConfigUnknownEntry(text, resetVisible, resetButton));
|
||||
public void addUnknown(ClickableWidget resetButton, BooleanSupplier resetVisible, Text text, Reflowable reflow) {
|
||||
this.addEntry(new ConfigUnknownEntry(text, resetVisible, resetButton, reflow));
|
||||
}
|
||||
|
||||
public void addButton(@Nullable ClickableWidget button, ClickableWidget resetButton, BooleanSupplier resetVisible, Text text) {
|
||||
this.addEntry(new ConfigScreenEntry(button, text, resetVisible, resetButton));
|
||||
public void addButton(@Nullable ClickableWidget button, ClickableWidget resetButton, BooleanSupplier resetVisible, Text text, Reflowable reflow) {
|
||||
this.addEntry(new ConfigScreenEntry(button, text, resetVisible, resetButton, reflow));
|
||||
}
|
||||
|
||||
public void addReference(int centerX, Text text, Supplier<Screen> targetScreen) {
|
||||
this.addEntry(new ConfigReferenceEntry(centerX, text, targetScreen));
|
||||
public void addReference(Text text, Supplier<Screen> targetScreen) {
|
||||
this.addEntry(new ConfigReferenceEntry(width, text, targetScreen));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,8 +64,16 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void refreshGrid(ScreenRect tabArea) {
|
||||
updateSize(tabArea.width(), tabArea.height(), tabArea.getTop(), tabArea.getBottom());
|
||||
setLeftPos(tabArea.getLeft());
|
||||
for (int i = 0, len = getEntryCount(); i < len; i++) {
|
||||
getEntry(i).reflow(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public abstract static class ConfigEntry extends Entry<ConfigEntry> {
|
||||
public abstract static class ConfigEntry extends Entry<ConfigEntry> implements Reflowable {
|
||||
@Override
|
||||
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
||||
if (hovered) {
|
||||
|
@ -78,10 +86,9 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
public static class ConfigReferenceEntry extends ConfigEntry {
|
||||
private final ClickableWidget button;
|
||||
|
||||
public ConfigReferenceEntry(int centerX, Text text, Supplier<Screen> targetScreen) {
|
||||
public ConfigReferenceEntry(int width, Text text, Supplier<Screen> targetScreen) {
|
||||
this.button = ButtonWidget.builder(text, btn -> MinecraftClient.getInstance().setScreen(targetScreen.get()))
|
||||
.position(centerX - 154, 0)
|
||||
.size(308, 20)
|
||||
.dimensions(width / 2 - 154, 0, 308, 20)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -101,6 +108,11 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
button.setY(y);
|
||||
button.render(matrices, mouseX, mouseY, tickDelta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reflow(int width, int height) {
|
||||
button.setX(width / 2 - 154);
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
|
@ -110,12 +122,14 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
private final BooleanSupplier resetVisible;
|
||||
private final ClickableWidget resetButton;
|
||||
private final Text text;
|
||||
private final Reflowable reflow;
|
||||
|
||||
public ConfigScreenEntry(ClickableWidget button, Text text, BooleanSupplier resetVisible, ClickableWidget resetButton) {
|
||||
public ConfigScreenEntry(ClickableWidget button, Text text, BooleanSupplier resetVisible, ClickableWidget resetButton, Reflowable reflow) {
|
||||
this.button = button;
|
||||
this.resetVisible = resetVisible;
|
||||
this.resetButton = resetButton;
|
||||
this.text = text;
|
||||
this.reflow = reflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -130,6 +144,11 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reflow(int width, int height) {
|
||||
reflow.reflow(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Element> children() {
|
||||
return List.of(button, resetButton);
|
||||
|
@ -151,11 +170,13 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
private final BooleanSupplier resetVisible;
|
||||
private final ClickableWidget resetButton;
|
||||
private final Text text;
|
||||
private final Reflowable reflow;
|
||||
|
||||
public ConfigUnknownEntry(Text text, BooleanSupplier resetVisible, ClickableWidget resetButton) {
|
||||
public ConfigUnknownEntry(Text text, BooleanSupplier resetVisible, ClickableWidget resetButton, Reflowable reflow) {
|
||||
this.resetVisible = resetVisible;
|
||||
this.resetButton = resetButton;
|
||||
this.text = text;
|
||||
this.reflow = reflow;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -168,6 +189,11 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reflow(int width, int height) {
|
||||
reflow.reflow(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends Element> children() {
|
||||
return List.of(resetButton);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry;
|
||||
|
||||
public interface Reflowable {
|
||||
void reflow(int width, int height);
|
||||
}
|
|
@ -36,8 +36,7 @@ public class PresetsScreen extends Screen {
|
|||
config.fix();
|
||||
Objects.requireNonNull(client).setScreen(parent);
|
||||
})
|
||||
.position(width / 2 - 100, 0)
|
||||
.size(200, 20)
|
||||
.dimensions(width / 2 - 100, 0, 200, 20)
|
||||
.build());
|
||||
}
|
||||
this.addSelectableChild(this.list);
|
||||
|
|
Loading…
Reference in New Issue