Inceptum/launcher-gtk/src/main/java/io/gitlab/jfronny/inceptum/gtk/menu/MenuBuilder.java

176 lines
6.0 KiB
Java

package io.gitlab.jfronny.inceptum.gtk.menu;
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable;
import io.gitlab.jfronny.inceptum.common.Utils;
import io.gitlab.jfronny.inceptum.gtk.util.I18n;
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.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public class MenuBuilder {
private static final Object LOCK = new Object();
private static Menu getRootMenu(Application app) {
synchronized (LOCK) {
var currentMenu = app.menubar;
if (currentMenu == null) {
var menu = new Menu();
app.menubar = menu;
return menu;
} else {
return (Menu) currentMenu;
}
}
}
private final ActionMap map;
private final Map<String, Action> refs = new LinkedHashMap<>();
private final Menu menu;
private final String prefix;
private final String groupName;
public MenuBuilder(Application app) {
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);
}
private static ActionMap insertMap(PopoverMenu menu, String groupName) {
SimpleActionGroup ag = new SimpleActionGroup();
menu.insertActionGroup(groupName, ag);
return ag;
}
public MenuBuilder(Application app, Menu menu, String prefix) {
this(app, menu, prefix, "app.");
}
private MenuBuilder(ActionMap map, Menu menu, String prefix, String groupName) {
if (!Objects.requireNonNull(prefix).isEmpty && !prefix.endsWith(".")) prefix += ".";
if (!Objects.requireNonNull(groupName).isEmpty && !groupName.endsWith(".")) groupName += ".";
this.map = Objects.requireNonNull(map);
this.menu = Objects.requireNonNull(menu);
this.prefix = prefix;
this.groupName = groupName;
}
public BuiltButtonItem button(String name, ThrowingRunnable<?> onClick) {
return literalButton(name, I18n.get("menu." + prefix + name), onClick);
}
public BuiltButtonItem literalButton(String internalName, String label, ThrowingRunnable<?> onClick) {
internalName = prefix + internalName;
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);
}
});
MenuItem menuItem = new MenuItem(label, groupName + internalName);
menu.appendItem(menuItem);
action.enabled = true;
return new BuiltButtonItem(action, menuItem);
}
public BuiltToggleItem toggle(String name, boolean initial, Consumer<Boolean> onToggle) {
name = prefix + name;
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);
});
MenuItem menuItem = new MenuItem(I18n.get("menu." + name), groupName + name);
menu.appendItem(menuItem);
return new BuiltToggleItem(action, menuItem);
}
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> 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 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), groupName + name + "(" + i + ")"));
i++;
}
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(label, submenu);
return new MenuBuilder(map, submenu, name, groupName);
}
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, groupName);
}
public void clear() {
menu.removeAll();
refs.forEach((name, action) -> {
map.removeAction(name);
});
refs.clear();
}
private void addAction(String name, SimpleAction action) {
map.addAction(action);
refs.put(name, action);
}
public Menu getMenu() {
return menu;
}
public PopoverMenu asPopover() {
return PopoverMenu.newFromModel(menu);
}
}