GTK: More work on settings screens
This commit is contained in:
parent
18c4953b36
commit
cd3c8a1852
|
@ -13,7 +13,7 @@ import java.nio.file.Path;
|
|||
public class InceptumConfig {
|
||||
@GComment("Whether to show snapshots in the version selector for new instances")
|
||||
public static boolean snapshots = false;
|
||||
@GComment("Whether to launch the GUI in dark mode\nConfigurable in Settings->Dark Theme")
|
||||
@GComment("Whether to launch the ImGUI in dark mode\nConfigurable in Settings->Dark Theme")
|
||||
public static boolean darkTheme = false;
|
||||
@GComment("Whether the GTK UI should default to a list view instead of a grid")
|
||||
public static boolean listView = false;
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.gtk.control.Dropdown;
|
||||
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
|
||||
import org.gtk.gio.ListStore;
|
||||
import org.gtk.gobject.GObject;
|
||||
import org.gtk.gtk.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class GtkTest extends ApplicationWindow {
|
||||
private final String searchTextWidget;
|
||||
private final String searchTextMethod;
|
||||
private final ListStore modelWidget;
|
||||
private final SortListModel sortModelWidget;
|
||||
private final FilterListModel filterModelWidget;
|
||||
private final CustomFilter filterWidget;
|
||||
|
||||
public GtkTest(Application application) {
|
||||
super(application);
|
||||
this.searchTextWidget = "";
|
||||
this.searchTextMethod = "";
|
||||
|
||||
this.modelWidget = new ListStore(Widget.type);
|
||||
this.sortModelWidget = new SortListModel(modelWidget, null);
|
||||
this.filterModelWidget = new FilterListModel(sortModelWidget, null);
|
||||
this.filterWidget = new CustomFilter(this::doFilterWidgetView, null);
|
||||
this.filterModelWidget.setFilter(this.filterWidget);
|
||||
|
||||
// modelWidget.append();
|
||||
}
|
||||
|
||||
private boolean doFilterWidgetView(GObject item) {
|
||||
var arg = filterModelWidget;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
LauncherEnv.initialize(GtkEnvBackend.INSTANCE);
|
||||
int statusCode = -1;
|
||||
try {
|
||||
statusCode = GtkMain.setupApplication(args, app -> {
|
||||
// var wnd = new GtkTest(app);
|
||||
var wnd = new ApplicationWindow(app);
|
||||
var row = new Box(Orientation.VERTICAL, 0);
|
||||
var btn = DropDown.newFromStrings(new String[]{"Ae", "Io", "U"});
|
||||
var innerBox = (Box) btn.lastChild.firstChild.firstChild;
|
||||
var chkbx = CheckButton.newWithLabel("Joe Biden");
|
||||
chkbx.insertBefore(innerBox, innerBox.firstChild);
|
||||
btn.enableSearch = true;
|
||||
btn.selected = 1;
|
||||
btn.onNotify("selected", pspec -> System.out.println(btn.selected));
|
||||
row.append(btn);
|
||||
wnd.child = row;
|
||||
wnd.show();
|
||||
GtkEnvBackend.INSTANCE.dialogParent = wnd;
|
||||
wnd.onCloseRequest(() -> {
|
||||
GtkEnvBackend.INSTANCE.dialogParent = null;
|
||||
app.quit();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
LauncherEnv.terminate();
|
||||
System.exit(statusCode);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.control;
|
||||
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable;
|
||||
import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder;
|
||||
import org.gtk.gtk.MenuButton;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.*;
|
||||
|
||||
public class Dropdown<T> extends MenuButton {
|
||||
private final T[] source;
|
||||
private final List<Consumer<T>> onChange = new LinkedList<>();
|
||||
private int selected;
|
||||
|
||||
public Dropdown(T[] source, int def, Function<T, String> stringify) {
|
||||
this.source = source;
|
||||
MenuBuilder builder = MenuBuilder.create(this, "nil");
|
||||
this.selected = def;
|
||||
for (int i = 0; i < source.length; i++) {
|
||||
String n = stringify.apply(source[i]);
|
||||
if (def == i) label = n;
|
||||
builder.literalButton("" + i, n, new LB(i, n));
|
||||
}
|
||||
}
|
||||
|
||||
public void onChanged(Consumer<T> action) {
|
||||
onChange.add(action);
|
||||
}
|
||||
|
||||
private class LB implements ThrowingRunnable<RuntimeException> {
|
||||
private final int i;
|
||||
private final String n;
|
||||
|
||||
public LB(int i, String n) {
|
||||
this.i = i;
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() throws RuntimeException {
|
||||
if (selected != i) {
|
||||
selected = i;
|
||||
for (Consumer<T> action : onChange) action.accept(source[i]);
|
||||
Dropdown.this.label = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@ import org.gtk.gtk.*;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
public class IRow extends Box {
|
||||
public IRow(@PropertyKey(resourceBundle = I18n.BUNDLE) String title, @PropertyKey(resourceBundle = I18n.BUNDLE) @Nullable String subtitle, Object... args) {
|
||||
super(Orientation.HORIZONTAL, 40);
|
||||
|
@ -35,4 +38,37 @@ public class IRow extends Box {
|
|||
btn.onClicked(action);
|
||||
append(btn);
|
||||
}
|
||||
|
||||
public void setDropdown(String[] options, int defaultIndex, IntConsumer changed) {
|
||||
firstChild.hexpand = true;
|
||||
DropDown btn = DropDown.newFromStrings(options);
|
||||
btn.valign = Align.CENTER;
|
||||
btn.halign = Align.END;
|
||||
btn.selected = defaultIndex;
|
||||
btn.onNotify("selected", pspec -> {
|
||||
changed.accept(btn.selected);
|
||||
});
|
||||
append(btn);
|
||||
}
|
||||
|
||||
public void setCheckbox(@PropertyKey(resourceBundle = I18n.BUNDLE) String text, boolean value, Consumer<Boolean> changed) {
|
||||
firstChild.hexpand = true;
|
||||
CheckButton btn = CheckButton.newWithLabel(I18n.get(text));
|
||||
btn.valign = Align.CENTER;
|
||||
btn.halign = Align.END;
|
||||
btn.active = value;
|
||||
btn.onToggled(() -> changed.accept(btn.active));
|
||||
append(btn);
|
||||
}
|
||||
|
||||
public Entry setEntry(String value, Consumer<String> onChanged) {
|
||||
Entry entry = new Entry();
|
||||
entry.text = value;
|
||||
entry.hexpand = true;
|
||||
entry.valign = Align.CENTER;
|
||||
entry.halign = Align.FILL;
|
||||
entry.onChanged(() -> onChanged.accept(entry.text));
|
||||
append(entry);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.control;
|
||||
|
||||
import org.gtk.gtk.*;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ISEntry extends Entry {
|
||||
public ISEntry(String content) {
|
||||
this.text = content;
|
||||
hexpand = true;
|
||||
halign = Align.FILL;
|
||||
valign = Align.CENTER;
|
||||
}
|
||||
|
||||
public void onChanged(Consumer<String> s) {
|
||||
super.onChanged(() -> s.accept(text));
|
||||
}
|
||||
}
|
|
@ -7,8 +7,8 @@ import org.gtk.gio.MenuItem;
|
|||
import org.gtk.gio.*;
|
||||
import org.gtk.glib.Variant;
|
||||
import org.gtk.glib.VariantType;
|
||||
import org.gtk.gtk.*;
|
||||
import org.gtk.gtk.Application;
|
||||
import org.gtk.gtk.PopoverMenu;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
@ -44,6 +44,13 @@ public class MenuBuilder {
|
|||
this(app, getRootMenu(app), "");
|
||||
}
|
||||
|
||||
public static MenuBuilder create(MenuButton target, String groupName) {
|
||||
Menu menu = new Menu();
|
||||
PopoverMenu pm = PopoverMenu.newFromModel(menu);
|
||||
target.popover = pm;
|
||||
return new MenuBuilder(pm, groupName);
|
||||
}
|
||||
|
||||
public MenuBuilder(PopoverMenu menu, String groupName) {
|
||||
this(insertMap(menu, groupName), (Menu) menu.menuModel, "", groupName);
|
||||
}
|
||||
|
|
|
@ -1,41 +1,18 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window;
|
||||
|
||||
import io.gitlab.jfronny.commons.ArgumentsTokenizer;
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.commons.ref.R;
|
||||
import io.gitlab.jfronny.inceptum.common.MetaHolder;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkEnvBackend;
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkMain;
|
||||
import io.gitlab.jfronny.inceptum.gtk.control.*;
|
||||
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporter;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporters;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.instance.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import manifold.ext.props.rt.api.var;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.edit.*;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
|
||||
import org.gnome.adw.HeaderBar;
|
||||
import org.gnome.adw.*;
|
||||
import org.gtk.gobject.BindingFlags;
|
||||
import org.gtk.gtk.Application;
|
||||
import org.gtk.gtk.MessageDialog;
|
||||
import org.gtk.gtk.Window;
|
||||
import org.gtk.gtk.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class InstanceSettingsWindow extends Window {
|
||||
private final Instance instance;
|
||||
|
||||
public InstanceSettingsWindow(Application app, Instance instance) {
|
||||
this.application = app;
|
||||
this.instance = instance;
|
||||
|
||||
ViewStack stack = new ViewStack();
|
||||
|
||||
|
@ -59,245 +36,8 @@ public class InstanceSettingsWindow extends Window {
|
|||
|
||||
child = view;
|
||||
|
||||
// General settings
|
||||
{
|
||||
var box = new Box(Orientation.VERTICAL, 8);
|
||||
box.marginHorizontal = 24;
|
||||
box.marginTop = 12;
|
||||
{
|
||||
Frame frame = new Frame(null);
|
||||
ListBox lb = new ListBox();
|
||||
lb.selectionMode = SelectionMode.NONE;
|
||||
frame.child = lb;
|
||||
box.append(frame);
|
||||
{
|
||||
var row = new IRow("instance.settings.general.name", "instance.settings.general.name.placeholder");
|
||||
ISEntry entry = new ISEntry(instance.name);
|
||||
entry.maxLength = 64;
|
||||
entry.placeholderText = I18n.get("instance.settings.general.name.placeholder");
|
||||
row.append(entry);
|
||||
Button apply = Button.newWithLabel(I18n.get("instance.settings.apply"));
|
||||
apply.valign = Align.CENTER;
|
||||
apply.onClicked(() -> {
|
||||
try {
|
||||
Path newPath = MetaHolder.INSTANCE_DIR.resolve(InstanceNameTool.getNextValid(entry.text));
|
||||
Files.move(instance.path, newPath);
|
||||
close();
|
||||
new InstanceSettingsWindow(application, InstanceList.read(newPath)).show();
|
||||
} catch (IOException e) {
|
||||
showError("Could not rename", e);
|
||||
}
|
||||
});
|
||||
entry.onChanged(s -> {
|
||||
apply.sensitive = !s.equals(instance.name);
|
||||
});
|
||||
apply.sensitive = false;
|
||||
row.append(apply);
|
||||
lb.append(row);
|
||||
}
|
||||
}
|
||||
//TODO Version (dropdown) + checkbox: "show snapshots"
|
||||
//TODO Fabric support (checkbox) + dropdown: Loader version
|
||||
//TODO Custom Java (checkbox) + String: path
|
||||
{
|
||||
box.append(new ILabel("instance.settings.general.args", ILabel.Mode.HEADING));
|
||||
Frame frame = new Frame(null);
|
||||
ListBox lb = new ListBox();
|
||||
lb.selectionMode = SelectionMode.NONE;
|
||||
frame.child = lb;
|
||||
box.append(frame);
|
||||
if (instance.meta.arguments == null) instance.meta.arguments = new InstanceMeta.Arguments(List.of(), List.of(), List.of());
|
||||
if (instance.meta.arguments.jvm == null) instance.meta.arguments = instance.meta.arguments.withJvm(List.of());
|
||||
if (instance.meta.arguments.client == null) instance.meta.arguments = instance.meta.arguments.withClient(List.of());
|
||||
if (instance.meta.arguments.server == null) instance.meta.arguments = instance.meta.arguments.withServer(List.of());
|
||||
{
|
||||
var row = new IRow("instance.settings.general.args.jvm", "instance.settings.general.args.jvm.subtitle");
|
||||
ISEntry entry = new ISEntry(ArgumentsTokenizer.join(instance.meta.arguments.jvm.toArray(String[]::new)));
|
||||
entry.onChanged(s -> {
|
||||
instance.meta.arguments = instance.meta.arguments.withJvm(List.of(ArgumentsTokenizer.tokenize(s)));
|
||||
instance.writeMeta();
|
||||
});
|
||||
row.append(entry);
|
||||
lb.append(row);
|
||||
}
|
||||
{
|
||||
var row = new IRow("instance.settings.general.args.client", "instance.settings.general.args.client.subtitle");
|
||||
ISEntry entry = new ISEntry(ArgumentsTokenizer.join(instance.meta.arguments.client.toArray(String[]::new)));
|
||||
entry.onChanged(s -> {
|
||||
instance.meta.arguments = instance.meta.arguments.withClient(List.of(ArgumentsTokenizer.tokenize(s)));
|
||||
instance.writeMeta();
|
||||
});
|
||||
row.append(entry);
|
||||
lb.append(row);
|
||||
}
|
||||
{
|
||||
var row = new IRow("instance.settings.general.args.server", "instance.settings.general.args.server.subtitle");
|
||||
ISEntry entry = new ISEntry(ArgumentsTokenizer.join(instance.meta.arguments.server.toArray(String[]::new)));
|
||||
entry.onChanged(s -> {
|
||||
instance.meta.arguments = instance.meta.arguments.withServer(List.of(ArgumentsTokenizer.tokenize(s)));
|
||||
instance.writeMeta();
|
||||
});
|
||||
row.append(entry);
|
||||
lb.append(row);
|
||||
}
|
||||
}
|
||||
{
|
||||
box.append(new ILabel("instance.settings.general.manage", ILabel.Mode.HEADING));
|
||||
Frame frame = new Frame(null);
|
||||
ListBox lb = new ListBox();
|
||||
lb.selectionMode = SelectionMode.NONE;
|
||||
frame.child = lb;
|
||||
box.append(frame);
|
||||
{
|
||||
var row = new IRow("instance.delete", "instance.delete.subtitle");
|
||||
row.setButton("instance.delete", () -> {
|
||||
MessageDialog dialog = new MessageDialog(this, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), MessageType.WARNING, ButtonsType.OK_CANCEL, null);
|
||||
dialog.markup = I18n.get("instance.delete.confirm");
|
||||
dialog.title = I18n.get("instance.delete.confirm.title");
|
||||
dialog.onResponse(responseId -> {
|
||||
switch (ResponseType.of(responseId)) {
|
||||
case OK -> {
|
||||
try {
|
||||
JFiles.deleteRecursive(instance.path);
|
||||
dialog.close();
|
||||
close();
|
||||
} catch (IOException e) {
|
||||
showError("Could not delete the instance", e);
|
||||
}
|
||||
dialog.close();
|
||||
}
|
||||
case CLOSE, CANCEL -> dialog.close();
|
||||
case DELETE_EVENT -> dialog.destroy();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
lb.append(row);
|
||||
}
|
||||
{
|
||||
var row = new IRow("instance.directory", "instance.directory.subtitle");
|
||||
row.setButton("instance.directory", () -> Utils.openFile(instance.path.toFile()));
|
||||
lb.append(row);
|
||||
}
|
||||
}
|
||||
stack.addTitledWithIcon(box, null, I18n.get("instance.settings.general"), "preferences-other-symbolic");
|
||||
}
|
||||
|
||||
// Mods
|
||||
{
|
||||
var box = new Box(Orientation.VERTICAL, 0);
|
||||
box.marginHorizontal = 24;
|
||||
box.marginTop = 12;
|
||||
box.append(new ILabel("instance.settings.mods.unsupported"));
|
||||
//TODO implement this, somehow
|
||||
stack.addTitledWithIcon(box, null, I18n.get("instance.settings.mods"), "package-x-generic-symbolic");
|
||||
}
|
||||
|
||||
// Export
|
||||
{
|
||||
var box = new Box(Orientation.VERTICAL, 0);
|
||||
box.marginHorizontal = 24;
|
||||
box.marginTop = 12;
|
||||
{
|
||||
Frame frame = new Frame(null);
|
||||
ListBox lb = new ListBox();
|
||||
lb.selectionMode = SelectionMode.NONE;
|
||||
frame.child = lb;
|
||||
box.append(frame);
|
||||
{
|
||||
var row = new IRow("instance.settings.export.version", "instance.settings.export.version.subtitle");
|
||||
ISEntry entry = new ISEntry(instance.meta.instanceVersion);
|
||||
entry.onChanged(s -> {
|
||||
instance.meta.instanceVersion = s;
|
||||
instance.writeMeta();
|
||||
});
|
||||
row.append(entry);
|
||||
lb.append(row);
|
||||
}
|
||||
for (Exporter<?> exporter : Exporters.EXPORTERS) {
|
||||
var row = new IRow("instance.settings.export.title", "instance.settings.export.subtitle", exporter.name, exporter.fileExtension);
|
||||
row.setButton("instance.settings.export", () -> {
|
||||
FileChooserNative dialog = new FileChooserNative(
|
||||
I18n.get("instance.settings.export.dialog.title", exporter.name),
|
||||
this,
|
||||
FileChooserAction.SAVE,
|
||||
"_" + I18n.get("save"),
|
||||
"_" + I18n.get("cancel")
|
||||
);
|
||||
dialog.currentName = exporter.getDefaultFileName(instance);
|
||||
dialog.onResponse(responseId -> {
|
||||
if (responseId == ResponseType.ACCEPT.value) {
|
||||
var file = dialog.getFile().path;
|
||||
if (file == null) {
|
||||
GtkEnvBackend.simpleDialog(
|
||||
this,
|
||||
"The path returned by the file dialog is null",
|
||||
"Could not export",
|
||||
MessageType.ERROR,
|
||||
ButtonsType.CLOSE,
|
||||
null,
|
||||
null
|
||||
);
|
||||
return;
|
||||
}
|
||||
export(exporter, Path.of(file));
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
lb.append(row);
|
||||
}
|
||||
}
|
||||
stack.addTitledWithIcon(box, null, I18n.get("instance.settings.export"), "send-to-symbolic");
|
||||
}
|
||||
}
|
||||
|
||||
private void export(Exporter<?> exporter, Path path) {
|
||||
ProcessState state = new ProcessState(Exporters.STEP_COUNT, "Initializing...");
|
||||
ProcessStateWatcherDialog.show(
|
||||
this,
|
||||
I18n.get("instance.settings.export.dialog.title", exporter.name),
|
||||
I18n.get("instance.settings.export.dialog.error", instance.name),
|
||||
state,
|
||||
cancel -> {
|
||||
exporter.generate(state, instance, path);
|
||||
GtkMain.schedule(() -> {
|
||||
MessageDialog success = new MessageDialog(
|
||||
this,
|
||||
DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT),
|
||||
MessageType.INFO,
|
||||
ButtonsType.NONE,
|
||||
I18n.get("instance.settings.export.dialog.success", instance.name, path.toString())
|
||||
);
|
||||
success.title = I18n.get("instance.settings.export.dialog.success.title");
|
||||
success.addButton(I18n.get("show"), ResponseType.OK.value);
|
||||
success.addButton(I18n.get("ok"), ResponseType.CANCEL.value);
|
||||
success.onResponse(responseId1 -> {
|
||||
switch (ResponseType.of(responseId1)) {
|
||||
case OK -> {
|
||||
success.close();
|
||||
Utils.openFile(path.toFile());
|
||||
}
|
||||
case CLOSE, CANCEL -> success.close();
|
||||
case DELETE_EVENT -> success.destroy();
|
||||
}
|
||||
});
|
||||
success.show();
|
||||
});
|
||||
},
|
||||
R::nop
|
||||
);
|
||||
}
|
||||
|
||||
private void showError(String message, Throwable t) {
|
||||
GtkEnvBackend.simpleDialog(
|
||||
this,
|
||||
StringFormatter.toString(t),
|
||||
message,
|
||||
MessageType.ERROR,
|
||||
ButtonsType.CLOSE,
|
||||
null,
|
||||
null
|
||||
);
|
||||
stack.addTitledWithIcon(new GeneralTab(instance, this), null, I18n.get("instance.settings.general"), "preferences-other-symbolic");
|
||||
stack.addTitledWithIcon(new ModsTab(instance, this), null, I18n.get("instance.settings.mods"), "package-x-generic-symbolic");
|
||||
stack.addTitledWithIcon(new ExportTab(instance, this), null, I18n.get("instance.settings.export"), "send-to-symbolic");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.common.InceptumConfig;
|
||||
import io.gitlab.jfronny.inceptum.common.model.inceptum.UpdateChannel;
|
||||
import io.gitlab.jfronny.inceptum.gtk.control.IRow;
|
||||
import org.gtk.gtk.*;
|
||||
|
||||
public class LauncherSettingsWindow extends Window {
|
||||
public LauncherSettingsWindow(Application app) {
|
||||
this.application = app;
|
||||
|
||||
var box = new Box(Orientation.VERTICAL, 8);
|
||||
box.marginHorizontal = 24;
|
||||
box.marginTop = 12;
|
||||
this.child = box;
|
||||
|
||||
{
|
||||
Frame frame = new Frame(null);
|
||||
ListBox listBox = new ListBox();
|
||||
listBox.selectionMode = SelectionMode.NONE;
|
||||
frame.child = listBox;
|
||||
box.append(frame);
|
||||
{
|
||||
IRow row = new IRow("settings.snapshots", "settings.snapshots.subtitle");
|
||||
listBox.append(row);
|
||||
row.setCheckbox("settings.snapshots", InceptumConfig.snapshots, b -> {
|
||||
InceptumConfig.snapshots = b;
|
||||
InceptumConfig.saveConfig();
|
||||
});
|
||||
}
|
||||
{
|
||||
IRow row = new IRow("settings.update-channel", "settings.update-channel.subtitle");
|
||||
listBox.append(row);
|
||||
row.setDropdown(new String[] {"Stable", "CI"}, InceptumConfig.channel == UpdateChannel.CI ? 1 : 0, state -> {
|
||||
InceptumConfig.channel = state == 1 ? UpdateChannel.CI : UpdateChannel.Stable;
|
||||
InceptumConfig.saveConfig();
|
||||
});
|
||||
}
|
||||
{
|
||||
IRow row = new IRow("settings.author-name", "settings.author-name.subtitle");
|
||||
listBox.append(row);
|
||||
row.setEntry(InceptumConfig.authorName, s -> {
|
||||
InceptumConfig.authorName = s;
|
||||
InceptumConfig.saveConfig();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,16 +43,14 @@ public class MainWindow extends ApplicationWindow {
|
|||
accountsButton.iconName = "avatar-default-symbolic";
|
||||
accountsButton.menuModel = GtkMenubar.accountsMenu.menu;
|
||||
|
||||
listButton = new Button();
|
||||
listButton.iconName = "view-list-symbolic";
|
||||
listButton = Button.newFromIconName("view-list-symbolic");
|
||||
listButton.onClicked(() -> {
|
||||
InceptumConfig.listView = true;
|
||||
InceptumConfig.saveConfig();
|
||||
generateWindowBody();
|
||||
});
|
||||
|
||||
gridButton = new Button();
|
||||
gridButton.iconName = "view-grid-symbolic";
|
||||
gridButton = Button.newFromIconName("view-grid-symbolic");
|
||||
gridButton.onClicked(() -> {
|
||||
InceptumConfig.listView = false;
|
||||
InceptumConfig.saveConfig();
|
||||
|
@ -63,7 +61,7 @@ public class MainWindow extends ApplicationWindow {
|
|||
|
||||
MenuBuilder uiMenu = new MenuBuilder(app, new Menu(), "hamburger");
|
||||
uiMenu.button("support", () -> Utils.openWebBrowser(new URI("https://git.frohnmeyer-wds.de/JfMods/Inceptum/issues")));
|
||||
uiMenu.button("preferences", () -> {}); //TODO preferences UI inspired by boxes
|
||||
uiMenu.button("preferences", () -> new LauncherSettingsWindow(app).show());
|
||||
uiMenu.button("about", AboutWindow::createAndShow);
|
||||
MenuButton menuButton = new MenuButton();
|
||||
menuButton.iconName = "open-menu-symbolic";
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window.edit;
|
||||
|
||||
import io.gitlab.jfronny.commons.ref.R;
|
||||
import io.gitlab.jfronny.inceptum.common.Utils;
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkEnvBackend;
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkMain;
|
||||
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.InstanceSettingsWindow;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.dialog.ProcessStateWatcherDialog;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporter;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.exporter.Exporters;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
|
||||
import io.gitlab.jfronny.inceptum.launcher.util.ProcessState;
|
||||
import org.gtk.gtk.*;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ExportTab extends SettingsTab {
|
||||
public ExportTab(Instance instance, InstanceSettingsWindow window) {
|
||||
super(instance, window);
|
||||
section(null, section -> {
|
||||
{
|
||||
var row = section.row("instance.settings.export.version", "instance.settings.export.version.subtitle");
|
||||
row.setEntry(instance.meta.instanceVersion, s -> {
|
||||
instance.meta.instanceVersion = s;
|
||||
instance.writeMeta();
|
||||
});
|
||||
}
|
||||
for (Exporter<?> exporter : Exporters.EXPORTERS) {
|
||||
var row = section.row("instance.settings.export.title", "instance.settings.export.subtitle", exporter.name, exporter.fileExtension);
|
||||
row.setButton("instance.settings.export", () -> {
|
||||
FileChooserNative dialog = new FileChooserNative(
|
||||
I18n.get("instance.settings.export.dialog.title", exporter.name),
|
||||
window,
|
||||
FileChooserAction.SAVE,
|
||||
"_" + I18n.get("save"),
|
||||
"_" + I18n.get("cancel")
|
||||
);
|
||||
dialog.currentName = exporter.getDefaultFileName(instance);
|
||||
dialog.onResponse(responseId -> {
|
||||
if (responseId == ResponseType.ACCEPT.value) {
|
||||
var file = dialog.getFile().path;
|
||||
if (file == null) {
|
||||
GtkEnvBackend.simpleDialog(
|
||||
window,
|
||||
"The path returned by the file dialog is null",
|
||||
"Could not export",
|
||||
MessageType.ERROR,
|
||||
ButtonsType.CLOSE,
|
||||
null,
|
||||
null
|
||||
);
|
||||
return;
|
||||
}
|
||||
export(exporter, Path.of(file));
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void export(Exporter<?> exporter, Path path) {
|
||||
ProcessState state = new ProcessState(Exporters.STEP_COUNT, "Initializing...");
|
||||
ProcessStateWatcherDialog.show(
|
||||
window,
|
||||
I18n.get("instance.settings.export.dialog.title", exporter.name),
|
||||
I18n.get("instance.settings.export.dialog.error", instance.name),
|
||||
state,
|
||||
cancel -> {
|
||||
exporter.generate(state, instance, path);
|
||||
GtkMain.schedule(() -> {
|
||||
MessageDialog success = new MessageDialog(
|
||||
window,
|
||||
DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT),
|
||||
MessageType.INFO,
|
||||
ButtonsType.NONE,
|
||||
I18n.get("instance.settings.export.dialog.success", instance.name, path.toString())
|
||||
);
|
||||
success.title = I18n.get("instance.settings.export.dialog.success.title");
|
||||
success.addButton(I18n.get("show"), ResponseType.OK.value);
|
||||
success.addButton(I18n.get("ok"), ResponseType.CANCEL.value);
|
||||
success.onResponse(responseId1 -> {
|
||||
switch (ResponseType.of(responseId1)) {
|
||||
case OK -> {
|
||||
success.close();
|
||||
Utils.openFile(path.toFile());
|
||||
}
|
||||
case CLOSE, CANCEL -> success.close();
|
||||
case DELETE_EVENT -> success.destroy();
|
||||
}
|
||||
});
|
||||
success.show();
|
||||
});
|
||||
},
|
||||
R::nop
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window.edit;
|
||||
|
||||
import io.gitlab.jfronny.commons.ArgumentsTokenizer;
|
||||
import io.gitlab.jfronny.commons.io.JFiles;
|
||||
import io.gitlab.jfronny.inceptum.common.*;
|
||||
import io.gitlab.jfronny.inceptum.gtk.control.*;
|
||||
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.InstanceSettingsWindow;
|
||||
import io.gitlab.jfronny.inceptum.launcher.api.McApi;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.inceptum.InstanceMeta;
|
||||
import io.gitlab.jfronny.inceptum.launcher.model.mojang.VersionsList;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.instance.*;
|
||||
import org.gtk.gtk.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class GeneralTab extends SettingsTab {
|
||||
private static final VersionsList VERSIONS = McApi.getVersions();
|
||||
|
||||
public GeneralTab(Instance instance, InstanceSettingsWindow window) {
|
||||
super(instance, window);
|
||||
section(null, section -> {
|
||||
var row = section.row("instance.settings.general.name", "instance.settings.general.name.placeholder");
|
||||
Button apply = Button.newWithLabel(I18n.get("instance.settings.apply"));
|
||||
Entry entry = row.setEntry(instance.name, s -> apply.sensitive = !s.equals(instance.name));
|
||||
entry.placeholderText = I18n.get("instance.settings.general.name.placeholder");
|
||||
apply.valign = Align.CENTER;
|
||||
apply.onClicked(() -> {
|
||||
try {
|
||||
Path newPath = MetaHolder.INSTANCE_DIR.resolve(InstanceNameTool.getNextValid(entry.text));
|
||||
Files.move(instance.path, newPath);
|
||||
window.close();
|
||||
new InstanceSettingsWindow(window.application, InstanceList.read(newPath)).show();
|
||||
} catch (IOException e) {
|
||||
showError("Could not rename", e);
|
||||
}
|
||||
});
|
||||
apply.sensitive = false;
|
||||
row.append(apply);
|
||||
});
|
||||
section("instance.settings.general.game", section -> {
|
||||
var row = section.row("instance.settings.general.game.version", "instance.settings.general.game.version.subtitle");
|
||||
String[] versions = VERSIONS.versions.stream()
|
||||
.filter(s -> InceptumConfig.snapshots || s.type.equals("release"))
|
||||
.map(s -> s.id)
|
||||
.toArray(String[]::new);
|
||||
int def = 0;
|
||||
for (int i = 0; i < versions.length; i++) if (versions[i].equals(VERSIONS.latest.release)) def = i;
|
||||
row.setDropdown(
|
||||
versions,
|
||||
def,
|
||||
i -> {
|
||||
instance.meta.gameVersion = versions[i];
|
||||
instance.writeMeta();
|
||||
});
|
||||
//TODO Fabric support (checkbox) + dropdown: Loader version
|
||||
//TODO Custom Java (checkbox) + String: path
|
||||
//TODO minMem/maxMem (slider?)
|
||||
});
|
||||
section("instance.settings.general.args", section -> {
|
||||
if (instance.meta.arguments == null) instance.meta.arguments = new InstanceMeta.Arguments(List.of(), List.of(), List.of());
|
||||
if (instance.meta.arguments.jvm == null) instance.meta.arguments = instance.meta.arguments.withJvm(List.of());
|
||||
if (instance.meta.arguments.client == null) instance.meta.arguments = instance.meta.arguments.withClient(List.of());
|
||||
if (instance.meta.arguments.server == null) instance.meta.arguments = instance.meta.arguments.withServer(List.of());
|
||||
{
|
||||
var row = section.row("instance.settings.general.args.jvm", "instance.settings.general.args.jvm.subtitle");
|
||||
row.setEntry(ArgumentsTokenizer.join(instance.meta.arguments.jvm.toArray(String[]::new)), s -> {
|
||||
instance.meta.arguments = instance.meta.arguments.withJvm(List.of(ArgumentsTokenizer.tokenize(s)));
|
||||
instance.writeMeta();
|
||||
});
|
||||
}
|
||||
{
|
||||
var row = section.row("instance.settings.general.args.client", "instance.settings.general.args.client.subtitle");
|
||||
row.setEntry(ArgumentsTokenizer.join(instance.meta.arguments.client.toArray(String[]::new)), s -> {
|
||||
instance.meta.arguments = instance.meta.arguments.withClient(List.of(ArgumentsTokenizer.tokenize(s)));
|
||||
instance.writeMeta();
|
||||
});
|
||||
}
|
||||
{
|
||||
var row = section.row("instance.settings.general.args.server", "instance.settings.general.args.server.subtitle");
|
||||
row.setEntry(ArgumentsTokenizer.join(instance.meta.arguments.server.toArray(String[]::new)), s -> {
|
||||
instance.meta.arguments = instance.meta.arguments.withServer(List.of(ArgumentsTokenizer.tokenize(s)));
|
||||
instance.writeMeta();
|
||||
});
|
||||
}
|
||||
});
|
||||
section("instance.settings.general.manage", section -> {
|
||||
{
|
||||
var row = section.row("instance.delete", "instance.delete.subtitle");
|
||||
row.setButton("instance.delete", () -> {
|
||||
MessageDialog dialog = new MessageDialog(window, DialogFlags.MODAL.or(DialogFlags.DESTROY_WITH_PARENT), MessageType.WARNING, ButtonsType.OK_CANCEL, null);
|
||||
dialog.markup = I18n.get("instance.delete.confirm");
|
||||
dialog.title = I18n.get("instance.delete.confirm.title");
|
||||
dialog.onResponse(responseId -> {
|
||||
switch (ResponseType.of(responseId)) {
|
||||
case OK -> {
|
||||
try {
|
||||
JFiles.deleteRecursive(instance.path);
|
||||
dialog.close();
|
||||
window.close();
|
||||
} catch (IOException e) {
|
||||
showError("Could not delete the instance", e);
|
||||
}
|
||||
dialog.close();
|
||||
}
|
||||
case CLOSE, CANCEL -> dialog.close();
|
||||
case DELETE_EVENT -> dialog.destroy();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
}
|
||||
{
|
||||
var row = section.row("instance.directory", "instance.directory.subtitle");
|
||||
row.setButton("instance.directory", () -> Utils.openFile(instance.path.toFile()));
|
||||
}
|
||||
});
|
||||
long timestamp = instance.meta.lastLaunched == null ? 0 : instance.meta.lastLaunched;
|
||||
append(new ILabel("instance.settings.general.last-launched", ILabel.Mode.SUBTITLE, new Date(timestamp * 1000).toString()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window.edit;
|
||||
|
||||
import io.gitlab.jfronny.inceptum.gtk.control.ILabel;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.InstanceSettingsWindow;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
|
||||
|
||||
public class ModsTab extends SettingsTab {
|
||||
public ModsTab(Instance instance, InstanceSettingsWindow window) {
|
||||
super(instance, window);
|
||||
append(new ILabel("instance.settings.mods.unsupported"));
|
||||
//TODO implement this, somehow
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package io.gitlab.jfronny.inceptum.gtk.window.edit;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.inceptum.gtk.GtkEnvBackend;
|
||||
import io.gitlab.jfronny.inceptum.gtk.control.ILabel;
|
||||
import io.gitlab.jfronny.inceptum.gtk.control.IRow;
|
||||
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
|
||||
import io.gitlab.jfronny.inceptum.gtk.window.InstanceSettingsWindow;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
|
||||
import org.gtk.gtk.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
public class SettingsTab extends Box {
|
||||
protected final Instance instance;
|
||||
protected final InstanceSettingsWindow window;
|
||||
|
||||
public SettingsTab(Instance instance, InstanceSettingsWindow window) {
|
||||
super(Orientation.VERTICAL, 8);
|
||||
this.instance = instance;
|
||||
this.marginHorizontal = 24;
|
||||
this.marginTop = 12;
|
||||
this.window = window;
|
||||
}
|
||||
|
||||
protected void section(@Nullable @PropertyKey(resourceBundle = I18n.BUNDLE) String title, SectionBuilder builder) {
|
||||
if (title != null) append(new ILabel(title, ILabel.Mode.HEADING));
|
||||
Frame frame = new Frame(null);
|
||||
ListBox listBox = new ListBox();
|
||||
listBox.selectionMode = SelectionMode.NONE;
|
||||
frame.child = listBox;
|
||||
builder.build(new SectionBuilder.Section() {
|
||||
@Override
|
||||
public IRow row(String title, @Nullable String subtitle, Object... args) {
|
||||
IRow row = new IRow(title, subtitle, args);
|
||||
listBox.append(row);
|
||||
return row;
|
||||
}
|
||||
});
|
||||
append(frame);
|
||||
}
|
||||
|
||||
protected interface SectionBuilder {
|
||||
void build(Section section);
|
||||
|
||||
interface Section {
|
||||
IRow row(@PropertyKey(resourceBundle = I18n.BUNDLE) String title, @PropertyKey(resourceBundle = I18n.BUNDLE) @Nullable String subtitle, Object... args);
|
||||
}
|
||||
}
|
||||
|
||||
protected void showError(String message, Throwable t) {
|
||||
GtkEnvBackend.simpleDialog(
|
||||
window,
|
||||
StringFormatter.toString(t),
|
||||
message,
|
||||
MessageType.ERROR,
|
||||
ButtonsType.CLOSE,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
|
@ -73,4 +73,14 @@ auth.title=Microsoft Login
|
|||
auth.description=This feature uses modified ATLauncher code, so the login prompt will ask you to log in to ATLauncher.\
|
||||
Click the button below to begin!
|
||||
auth.open-browser=Open in Browser
|
||||
instance.settings.general.manage=Manage
|
||||
instance.settings.general.manage=Manage
|
||||
instance.settings.general.last-launched=Last launched: %1$s
|
||||
instance.settings.general.game=Game
|
||||
instance.settings.general.game.version=Version
|
||||
instance.settings.general.game.version.subtitle=Minecraft version of this instance
|
||||
settings.author-name=Author Name
|
||||
settings.snapshots=Snapshots
|
||||
settings.update-channel=Update Channel
|
||||
settings.snapshots.subtitle=Whether to show snapshots in the version selector for new instances
|
||||
settings.update-channel.subtitle=The update channel. I personnaly recommend the CI channel as it gest the latest features and fixes more quickly, but it might be more unstable
|
||||
settings.author-name.subtitle=The author name to add to packs where the metadata format requires specifying one
|
|
@ -73,4 +73,14 @@ auth.title=Microsoft-Anmeldung
|
|||
auth.description=Diese Funktion nutzt Code des ATLauncher, weshalb der Anmeldebildschirm nach einer Anmeldung für ATLauncher fragen wird.\
|
||||
Klicken Sie auf die Schaltfläche unten, um zu beginnen!<
|
||||
auth.open-browser=Im Browser öffnen
|
||||
instance.settings.general.manage=Manage
|
||||
instance.settings.general.manage=Manage
|
||||
instance.settings.general.last-launched=Zuletzt gestartet: %1$s
|
||||
instance.settings.general.game=Game
|
||||
instance.settings.general.game.version=Version
|
||||
instance.settings.general.game.version.subtitle=Minecraft-Version dieser Instanz
|
||||
settings.author-name=Name des Authors
|
||||
settings.snapshots=Vorschauversionen
|
||||
settings.update-channel=Updatekanal
|
||||
settings.snapshots.subtitle=Ob Vorschauversionen im Versions-Auswahlmenü gezeigt werden sollen
|
||||
settings.update-channel.subtitle=Der Update-Kanal. Ich empfehle den etwas instabileren, aber häufiger aktualisierten CI-Kanal
|
||||
settings.author-name.subtitle=Der Name, der bei Modpack-Exporten, deren Metadaten einen Autor angeben, aufgelistet werden soll
|
Loading…
Reference in New Issue