Tweak main window
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Johannes Frohnmeyer 2023-01-13 19:34:29 +01:00
parent d4a016771f
commit c52f1f3350
Signed by: Johannes
GPG Key ID: E76429612C2929F4
14 changed files with 256 additions and 121 deletions

View File

@ -3,14 +3,14 @@
pipeline:
export_metadata:
image: gradle:jammy
image: gradle:jdk19-jammy
pull: true
commands:
- mkdir public
- gradle --build-cache :exportMetadata -Ppublic -Ptimestamp=${CI_PIPELINE_STARTED}
- mv version.json public/
build_platform_jars:
image: gradle:jammy
image: gradle:jdk19-jammy
commands:
- gradle --build-cache :launcher-dist:build -Pflavor=fat -Ppublic -Ptimestamp=${CI_PIPELINE_STARTED}
- gradle --build-cache :launcher-dist:build -Pflavor=windows -Ppublic -Ptimestamp=${CI_PIPELINE_STARTED}
@ -19,20 +19,20 @@ pipeline:
- for f in launcher-dist/build/libs/Inceptum-*-*-*.jar; do mv "$f" "public/Inceptum-$${f##*-}"; done
- mv public/Inceptum-fat.jar public/Inceptum.jar
build_wrapper:
image: gradle:jammy
image: gradle:jdk19-jammy
commands:
- gradle --build-cache :wrapper:build -Pflavor=windows -Ppublic -Ptimestamp=${CI_PIPELINE_STARTED}
- cp wrapper/build/libs/*.exe public/wrapper.exe
- cp wrapper/build/libs/*-all.jar public/wrapper.jar
publish_debug:
image: gradle:jammy
image: gradle:jdk19-jammy
commands:
- gradle --build-cache build publish -Pflavor=maven -Ppublic -Ptimestamp=${CI_PIPELINE_STARTED}
secrets: [ maven_token, maven_name ]
when:
- branch: master
publish_release:
image: gradle:jammy
image: gradle:jdk19-jammy
commands:
- gradle --build-cache build publish -Pflavor=maven -Ppublic -Prelease
secrets: [ maven_token, maven_name ]
@ -40,7 +40,7 @@ pipeline:
- event: tag
branch: master
portable:
image: gradle:jammy
image: gradle:jdk19-jammy
commands:
- apt update
- apt install -y p7zip-full curl jq

View File

@ -0,0 +1,23 @@
package extensions.org.gtk.gtk.Widget;
import manifold.ext.rt.api.Extension;
import manifold.ext.rt.api.This;
import org.gtk.gtk.Widget;
@Extension
public class WidgetExt {
public static void setMargin(@This Widget thiz, int margin) {
thiz.marginVertical = margin;
thiz.marginHorizontal = margin;
}
public static void setMarginVertical(@This Widget thiz, int margin) {
thiz.marginTop = margin;
thiz.marginBottom = margin;
}
public static void setMarginHorizontal(@This Widget thiz, int margin) {
thiz.marginStart = margin;
thiz.marginEnd = margin;
}
}

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.inceptum.gtk;
import io.gitlab.jfronny.inceptum.launcher.system.launch.*;
import org.gtk.gtk.Application;
import io.gitlab.jfronny.commons.ref.R;
import io.gitlab.jfronny.inceptum.common.Utils;
@ -12,7 +13,6 @@ import io.gitlab.jfronny.inceptum.launcher.api.account.AccountManager;
import io.gitlab.jfronny.inceptum.launcher.api.account.MicrosoftAccount;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.launcher.system.instance.InstanceList;
import io.gitlab.jfronny.inceptum.launcher.system.launch.InstanceLauncher;
import io.gitlab.jfronny.inceptum.launcher.system.setup.Steps;
import java.io.IOException;
@ -46,14 +46,14 @@ public class GtkMenubar {
launchMenu.clear();
try {
InstanceList.forEach(entry -> {
launchMenu.literalButton(entry.id(), entry.toString(), () -> launch(entry));
launchMenu.literalButton(entry.id() + ".launch", entry.toString(), () -> launch(entry, LaunchType.Client));
});
} catch (IOException e) {
Utils.LOGGER.error("Could not generate launch menu", e);
}
}
public static void launch(Instance instance) {
public static void launch(Instance instance, LaunchType launchType) {
//TODO show popup during launch w/ cancel option (lock main UI)
Runnable launch = () -> {
try {
@ -61,7 +61,14 @@ public class GtkMenubar {
} catch (IOException e) {
Utils.LOGGER.error("Could not redownload instance, trying to start anyways", e);
}
InstanceLauncher.launchClient(instance);
if (launchType == LaunchType.Client) InstanceLauncher.launchClient(instance);
else {
try {
InstanceLauncher.launch(instance, launchType, false, AccountManager.NULL_AUTH);
} catch (LaunchException | IOException e) {
LauncherEnv.showError("Could not start instance", e);
}
}
};
if (instance.isSetupLocked) {
LauncherEnv.showError(I18n.get("instance.launch.locked.setup"), I18n.get("instance.launch.locked"));

View File

@ -1,80 +1,136 @@
package io.gitlab.jfronny.inceptum.gtk.control;
import io.gitlab.jfronny.inceptum.gtk.util.ListIndexItem;
import org.gnome.adw.ActionRow;
import org.gtk.gtk.*;
import io.github.jwharm.javagi.Signal;
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.GtkMenubar;
import io.gitlab.jfronny.inceptum.gtk.menu.MenuBuilder;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
import io.gitlab.jfronny.inceptum.launcher.system.instance.Instance;
import io.gitlab.jfronny.inceptum.gtk.util.ListIndexItem;
import io.gitlab.jfronny.inceptum.launcher.LauncherEnv;
import io.gitlab.jfronny.inceptum.launcher.system.instance.*;
import io.gitlab.jfronny.inceptum.launcher.system.launch.LaunchType;
import org.gnome.adw.ActionRow;
import org.gtk.gio.ActionMap;
import org.gtk.gio.Menu;
import org.gtk.gtk.Stack;
import org.gtk.gtk.*;
import java.util.List;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
import java.util.function.Consumer;
public class InstanceListEntryFactory extends SignalListItemFactory {
public InstanceListEntryFactory(List<Instance> instanceList) {
public InstanceListEntryFactory(List<Instance> instanceList, ActionMap actionMap) {
super();
onSetup(item -> {
var thumbnail = new InstanceThumbnail();
thumbnail.name = "inceptum-thumbnail";
var launch = new Button();
launch.addCssClass("flat");
launch.name = "inceptum-launch";
launch.iconName = "computer-symbolic";
launch.iconName = "media-playback-start-symbolic";
launch.tooltipText = I18n.get("instance.launch");
launch.hasTooltip = true;
var openDir = new Button();
openDir.name = "inceptum-open-dir";
openDir.iconName = "folder-symbolic";
openDir.tooltipText = I18n.get("instance.directory");
openDir.hasTooltip = true;
var menu = new MenuButton();
menu.addCssClass("flat");
menu.iconName = "view-more-symbolic";
menu.menuModel = new Menu();
var row = new ActionRow();
row.margin = 8;
row.name = "inceptum-row";
row.activatableWidget = launch;
row.addPrefix(thumbnail);
row.addSuffix(launch);
row.addSuffix(openDir);
row.addSuffix(menu);
var clicked = new GestureClick();
clicked.onPressed((nPress, x, y) -> {
if (nPress == 2) launch.emitClicked(launch);
});
row.addController(clicked);
var rightClicked = new GestureClick();
rightClicked.button = 3;
rightClicked.onPressed((nPress, x, y) -> {
if (nPress == 1) menu.emitActivate(menu);
});
row.addController(rightClicked);
((ListItem) item).setChild(row);
//TODO server launch with network-server-symbolic
//TODO kill current instance
});
Map<String, Set<Signal<?>>> toDisconnect = new HashMap<>();
onBind(item -> {
ListItem li = (ListItem) item;
Decomposed li = Decomposed.of((ListItem) item, instanceList);
li.row.title = li.instance.toString();
Instance instance = instanceList.get(((ListIndexItem) li.getItem()).getIntValue());
ActionRow row = (ActionRow) li.child;
Box prefixes = (Box) row.firstChild.firstChild;
Box suffixes = (Box) row.firstChild.lastChild;
li.thumbnail.bind(li.instance);
InstanceThumbnail thumbnail = InstanceThumbnail.castFrom((Stack) prefixes.firstChild);
Button launch = (Button) suffixes.firstChild;
Button openDir = (Button) launch.nextSibling;
row.title = instance.toString();
//TODO fix menu not interactable
var menuBuilder = new MenuBuilder(actionMap, li.menu, li.instance.id);
var launchSection = menuBuilder.literalSection("launch", null);
launchSection.literalButton("launch.client", I18n.get("instance.launch.client"),
() -> GtkMenubar.launch(li.instance, LaunchType.Client))
.iconName = "media-playback-start-symbolic";
launchSection.literalButton("launch.server", I18n.get("instance.launch.server"),
() -> GtkMenubar.launch(li.instance, LaunchType.Server))
.iconName = "network-server-symbolic";
var settingsSection = menuBuilder.literalSection("settings", null);
settingsSection.literalButton("settings", I18n.get("instance.settings"), () -> {
//TODO open settings
}).iconName = "document-edit-symbolic";
settingsSection.literalButton("directory", I18n.get("instance.directory"),
() -> Utils.openFile(li.instance.path.toFile()))
.iconName = "folder-symbolic";
settingsSection.literalButton("copy", I18n.get("instance.copy"), () -> {
LauncherEnv.getInput(I18n.get("instance.copy.prompt"), I18n.get("instance.copy.details"), InstanceNameTool.getNextValid(li.instance.name), s -> {
try {
JFiles.copyRecursive(li.instance.path, MetaHolder.INSTANCE_DIR.resolve(InstanceNameTool.getNextValid(s)));
} catch (IOException e) {
LauncherEnv.showError("Could not copy instance", e);
}
}, R::nop);
}).iconName = "edit-copy-symbolic";
settingsSection.literalButton("delete", I18n.get("instance.delete"), () -> {
LauncherEnv.showOkCancel("This instance will be removed forever (a long time)", "Are you sure?", () -> {
try {
JFiles.deleteRecursive(li.instance.path);
} catch (IOException e) {
LauncherEnv.showError("Could not delete the instance", e);
}
}, R::nop);
}).iconName = "edit-delete-symbolic";
// InstanceThumbnail thumbnail = new InstanceThumbnail(row.getFirstChild().cast());
// Label label = new Label(thumbnail.getNextSibling().cast());
// Button launch = new Button(label.getNextSibling().cast());
// Button openDir = new Button(launch.getNextSibling().cast());
//TODO kill current instance
thumbnail.bind(instance);
// label.setText(new Str(instance.toString()));
launch.onClicked(() -> GtkMenubar.launch(instance));
openDir.onClicked(() -> Utils.openFile(instance.path().toFile()));
Consumer<Signal<?>> dc = s -> toDisconnect.computeIfAbsent(li.instance.id, $ -> new HashSet<>()).add(s);
//TODO why the hell does this crash the VM?
//TODO GestureClick.setButton(GDK_BUTTON_SECONDARY)
// var controller = new EventControllerLegacy();
// controller.onEvent(event -> {
// if (event.getEventType() == EventType.BUTTON_RELEASE) {
// Utils.LOGGER.info("Button " + new ButtonEvent(event.cast()).getButton());
// }
// return GTK.FALSE;
// });
// row.addController(controller);
//TODO edit button document-edit-symbolic -> edit-delete-symbolic, edit-copy-symbolic
dc.accept(li.launch.onClicked(() -> GtkMenubar.launch(li.instance, LaunchType.Client)));
});
onUnbind(item -> {
Decomposed li = Decomposed.of((ListItem) item, instanceList);
li.menu.removeAll();
toDisconnect.get(li.instance.id).forEach(Signal::disconnect);
});
}
private record Decomposed(Instance instance, ActionRow row, InstanceThumbnail thumbnail, Button launch, Menu menu) {
public static Decomposed of(ListItem item, List<Instance> instanceList) {
Instance instance = instanceList.get(((ListIndexItem) item.getItem()).getIntValue());
ActionRow row = (ActionRow) item.child;
Box prefixes = (Box) row.firstChild.firstChild;
Box suffixes = (Box) row.firstChild.lastChild;
InstanceThumbnail thumbnail = InstanceThumbnail.castFrom((Stack) prefixes.firstChild);
Button launch = (Button) suffixes.firstChild;
MenuButton menuButton = (MenuButton) launch.nextSibling;
return new Decomposed(instance, row, thumbnail, launch, (Menu) menuButton.menuModel);
}
}
}

View File

@ -0,0 +1,10 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import org.gtk.gio.MenuItem;
import org.gtk.gio.SimpleAction;
public class BuiltButtonItem extends BuiltMenuItem {
public BuiltButtonItem(SimpleAction action, MenuItem menuItem) {
super(action, menuItem);
}
}

View File

@ -0,0 +1,29 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import org.gtk.gio.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public abstract class BuiltMenuItem {
protected final SimpleAction action;
protected final MenuItem menuItem;
protected BuiltMenuItem(@NotNull SimpleAction action, @Nullable MenuItem menuItem) {
this.action = Objects.requireNonNull(action);
this.menuItem = menuItem;
}
public boolean getEnabled() {
return action.getEnabled();
}
public void setEnabled(boolean enabled) {
action.enabled = enabled;
}
public void setIconName(String iconName) {
Objects.requireNonNull(menuItem).icon = new ThemedIcon(iconName);
}
}

View File

@ -5,11 +5,11 @@ import org.gtk.glib.Variant;
import java.util.List;
public class RadioItem<T> extends MenuItem {
public class BuiltRadioItem<T> extends BuiltMenuItem {
private final List<T> options;
public RadioItem(SimpleAction action, List<T> options) {
super(action);
public BuiltRadioItem(SimpleAction action, List<T> options) {
super(action, null);
this.options = options;
}

View File

@ -1,11 +1,12 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import org.gtk.gio.MenuItem;
import org.gtk.gio.SimpleAction;
import org.gtk.glib.Variant;
public class ToggleItem extends MenuItem {
public ToggleItem(SimpleAction action) {
super(action);
public class BuiltToggleItem extends BuiltMenuItem {
public BuiltToggleItem(SimpleAction action, MenuItem menuItem) {
super(action, menuItem);
}
public boolean getState() {

View File

@ -1,9 +0,0 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import org.gtk.gio.SimpleAction;
public class ButtonItem extends MenuItem {
public ButtonItem(SimpleAction action) {
super(action);
}
}

View File

@ -9,6 +9,7 @@ import org.gtk.glib.Variant;
import org.gtk.glib.VariantType;
import org.gtk.gtk.Application;
import org.gtk.gtk.PopoverMenu;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
import java.util.List;
@ -49,66 +50,84 @@ public class MenuBuilder {
this.prefix = Objects.requireNonNull(prefix);
}
public ButtonItem button(String name, ThrowingRunnable<?> onClick) {
public BuiltButtonItem button(String name, ThrowingRunnable<?> onClick) {
return literalButton(name, I18n.get("menu." + prefix + name), onClick);
}
public ButtonItem literalButton(String internalName, String label, ThrowingRunnable<?> onClick) {
public BuiltButtonItem literalButton(String internalName, String label, ThrowingRunnable<?> onClick) {
internalName = prefix + internalName;
SimpleAction sAct = new SimpleAction(internalName, null);
addAction(internalName, sAct);
sAct.onActivate(variant -> {
SimpleAction action = new SimpleAction(internalName, null);
addAction(internalName, action);
action.onActivate(variant -> {
try {
onClick.run();
} catch (Throwable e) {
Utils.LOGGER.error("Could not execute action", e);
}
});
menu.appendItem(new MenuItem(label, "app." + internalName));
return new ButtonItem(sAct);
MenuItem menuItem = new MenuItem(label, "app." + internalName);
menu.appendItem(menuItem);
action.enabled = true;
return new BuiltButtonItem(action, menuItem);
}
public ToggleItem toggle(String name, boolean initial, Consumer<Boolean> action) {
public BuiltToggleItem toggle(String name, boolean initial, Consumer<Boolean> onToggle) {
name = prefix + name;
SimpleAction sAct = SimpleAction.newStateful(name, null, Variant.newBoolean(initial));
addAction(name, sAct);
sAct.onActivate(variant -> {
boolean state = !sAct.getState().getBoolean();
sAct.state = Variant.newBoolean(state);
action.accept(state);
SimpleAction action = SimpleAction.newStateful(name, null, Variant.newBoolean(initial));
addAction(name, action);
action.onActivate(variant -> {
boolean state = !action.getState().getBoolean();
action.state = Variant.newBoolean(state);
onToggle.accept(state);
});
menu.appendItem(new MenuItem(I18n.get("menu." + name), "app." + name));
return new ToggleItem(sAct);
MenuItem menuItem = new MenuItem(I18n.get("menu." + name), "app." + name);
menu.appendItem(menuItem);
return new BuiltToggleItem(action, menuItem);
}
public <T> RadioItem<T> radio(String name, T initial, List<T> options, Consumer<T> action) {
return literalRadio(name, initial, options, (i, t) -> I18n.get("menu." + name, i), action);
public <T> BuiltRadioItem<T> radio(String name, T initial, List<T> options, Consumer<T> onCheck) {
return literalRadio(name, initial, options, (i, t) -> I18n.get("menu." + prefix + name, i), onCheck);
}
public <T> RadioItem<T> literalRadio(String name, T initial, List<T> options, BiFunction<Integer, T, String> stringifier, Consumer<T> action) {
public <T> BuiltRadioItem<T> literalRadio(String name, T initial, List<T> options, BiFunction<Integer, T, String> stringifier, Consumer<T> onCheck) {
Objects.requireNonNull(options);
name = prefix + name;
SimpleAction sAct = SimpleAction.newStateful(name, new VariantType("i"), Variant.newInt32(options.indexOf(initial)));
addAction(name, sAct);
sAct.onActivate(variant -> {
sAct.state = variant;
action.accept(options.get(variant.getInt32()));
SimpleAction action = SimpleAction.newStateful(name, new VariantType("i"), Variant.newInt32(options.indexOf(initial)));
addAction(name, action);
action.onActivate(variant -> {
action.state = variant;
onCheck.accept(options.get(variant.getInt32()));
});
int i = 0;
for (T option : options) {
menu.appendItem(new MenuItem(stringifier.apply(i, option), "app." + name + "(" + i + ")"));
i++;
}
return new RadioItem<>(sAct, options);
return new BuiltRadioItem<>(action, options);
}
public MenuBuilder submenu(String name) {
return literalSubmenu(name, I18n.get("menu." + prefix + name));
}
public MenuBuilder literalSubmenu(String name, String label) {
name = prefix + name;
Menu submenu = new Menu();
menu.appendSubmenu(I18n.get("menu." + name), submenu);
menu.appendSubmenu(label, submenu);
return new MenuBuilder(map, submenu, name);
}
public MenuBuilder section(String name) {
return literalSection(name, I18n.get("section." + prefix + name));
}
public MenuBuilder literalSection(String name, @Nullable String label) {
name = prefix + name;
Menu section = new Menu();
menu.appendSection(label, section);
return new MenuBuilder(map, section, name);
}
public void clear() {
menu.removeAll();
refs.forEach((name, action) -> {

View File

@ -1,19 +0,0 @@
package io.gitlab.jfronny.inceptum.gtk.menu;
import org.gtk.gio.SimpleAction;
public abstract class MenuItem {
protected final SimpleAction action;
public MenuItem(SimpleAction action) {
this.action = action;
}
public boolean getEnabled() {
return action.getEnabled();
}
public void setEnabled(boolean enabled) {
action.enabled = enabled;
}
}

View File

@ -38,15 +38,15 @@ public class MainWindow extends ApplicationWindow {
HeaderBar header = new HeaderBar();
Button newButton = new Button();
newButton.setIconName("list-add-symbolic");
newButton.iconName = "list-add-symbolic";
newButton.onClicked(NewInstanceWindow::createAndShow);
MenuButton accountsButton = new MenuButton();
accountsButton.setIconName("avatar-default-symbolic");
accountsButton.setPopover(GtkMenubar.accountsMenu.asPopover());
accountsButton.iconName = "avatar-default-symbolic";
accountsButton.menuModel = GtkMenubar.accountsMenu.menu;
listButton = new Button();
listButton.setIconName("view-list-symbolic");
listButton.iconName = "view-list-symbolic";
listButton.onClicked(() -> {
InceptumConfig.listView = true;
InceptumConfig.saveConfig();
@ -54,7 +54,7 @@ public class MainWindow extends ApplicationWindow {
});
gridButton = new Button();
gridButton.setIconName("view-grid-symbolic");
gridButton.iconName = "view-grid-symbolic";
gridButton.onClicked(() -> {
InceptumConfig.listView = false;
InceptumConfig.saveConfig();
@ -68,8 +68,8 @@ public class MainWindow extends ApplicationWindow {
uiMenu.button("preferences", () -> {}); //TODO preferences UI inspired by boxes
uiMenu.button("about", AboutWindow::createAndShow);
MenuButton menuButton = new MenuButton();
menuButton.setIconName("open-menu-symbolic");
menuButton.setPopover(uiMenu.asPopover());
menuButton.iconName = "open-menu-symbolic";
menuButton.menuModel = uiMenu.menu;
header.packStart(newButton);
@ -84,7 +84,11 @@ public class MainWindow extends ApplicationWindow {
listView = new Clamp();
listView.maximumSize = 900;
listView.child = new ListView(singleSelection, new InstanceListEntryFactory(instanceList));
listView.child = new ListView(singleSelection, new InstanceListEntryFactory(instanceList, this));
listView.child.marginHorizontal = 24;
listView.child.marginVertical = 12;
listView.child.addCssClass("content");
listView.child.valign = Align.START;
gridView = new GridView(singleSelection, new InstanceGridEntryFactory(instanceList));
empty = new StatusPage();
empty.title = I18n.get("main.empty.title");

View File

@ -31,3 +31,10 @@ instance.launch=Launch
main.empty.title=Welcome to Inceptum
main.empty.description=To get started, create (or import) a new instance using the + button
instance.directory=Open Directory
instance.launch.client=Launch Client
instance.launch.server=Launch Server
instance.settings=Settings
instance.copy=Duplicate
instance.delete=Delete
instance.copy.prompt=New Name
instance.copy.details=Please enter a name for the new instance

View File

@ -31,3 +31,10 @@ instance.launch=Starten
main.empty.title=Willkommen bei Inceptum
main.empty.description=Importiere oder erstelle um anzufangen eine Instanz mit dem +
instance.directory=Verzeichnis öffnen
instance.launch.client=Starte Client
instance.launch.server=Starte Server
instance.settings=Einstellungen
instance.copy=Klonen
instance.delete=Löschen
instance.copy.prompt=Neuer Name
instance.copy.details=Gib den Namen für die neue Instanz ein