Support for custom menu buttons (#7595)

* added support for custom main menu buttons

* layout fix

* ...and yet another fix

* removed another unneccesary table, which was causing issues

* actually, it looks better with even and odd buttons swapped

* made the container a scroll pane, needs some testing...

* this seems to work correctly

* render the logo under the menu buttons

* rename Buttoni to MenuButton
This commit is contained in:
Mnemotechnican 2022-09-20 18:25:56 +03:00 committed by GitHub
parent 057f1ee446
commit 10d989fbc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -12,7 +12,9 @@ import arc.scene.ui.*;
import arc.scene.ui.ImageButton.*;
import arc.scene.ui.TextButton.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.core.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
@ -26,6 +28,7 @@ public class MenuFragment{
private Table container, submenu;
private Button currentMenu;
private MenuRenderer renderer;
private Seq<MenuButton> customButtons = new Seq<>();
public void build(Group parent){
renderer = new MenuRenderer();
@ -39,46 +42,6 @@ public class MenuFragment{
parent.fill((x, y, w, h) -> renderer.render());
parent.fill(c -> {
container = c;
c.name = "menu container";
if(!mobile){
buildDesktop();
Events.on(ResizeEvent.class, event -> buildDesktop());
}else{
buildMobile();
Events.on(ResizeEvent.class, event -> buildMobile());
}
});
parent.fill(c -> c.bottom().right().button(Icon.discord, new ImageButtonStyle(){{
up = discordBanner;
}}, ui.discord::show).marginTop(9f).marginLeft(10f).tooltip("@discord").size(84, 45).name("discord"));
//info icon
if(mobile){
parent.fill(c -> c.bottom().left().button("", new TextButtonStyle(){{
font = Fonts.def;
fontColor = Color.white;
up = infoBanner;
}}, ui.about::show).size(84, 45).name("info"));
}else if(becontrol.active()){
parent.fill(c -> c.bottom().right().button("@be.check", Icon.refresh, () -> {
ui.loadfrag.show();
becontrol.checkUpdate(result -> {
ui.loadfrag.hide();
if(!result){
ui.showInfo("@be.noupdates");
}
});
}).size(200, 60).name("becheck").update(t -> {
t.getLabel().setColor(becontrol.isUpdateAvailable() ? Tmp.c1.set(Color.white).lerp(Pal.accent, Mathf.absin(5f, 1f)) : Color.white);
}));
}
String versionText = ((Version.build == -1) ? "[#fc8140aa]" : "[#ffffffba]") + Version.combined();
parent.fill((x, y, w, h) -> {
TextureRegion logo = Core.atlas.find("logo");
@ -96,6 +59,49 @@ public class MenuFragment{
Fonts.outline.setColor(Color.white);
Fonts.outline.draw(versionText, fx, fy - logoh/2f - Scl.scl(2f), Align.center);
}).touchable = Touchable.disabled;
parent.fill(c -> {
c.pane(Styles.noBarPane, cont -> {
container = cont;
cont.name = "menu container";
if(!mobile){
c.left();
buildDesktop();
Events.on(ResizeEvent.class, event -> buildDesktop());
}else{
buildMobile();
Events.on(ResizeEvent.class, event -> buildMobile());
}
}).with(pane -> {
pane.setOverscroll(false, false);
}).grow();
});
parent.fill(c -> c.bottom().right().button(Icon.discord, new ImageButtonStyle(){{
up = discordBanner;
}}, ui.discord::show).marginTop(9f).marginLeft(10f).tooltip("@discord").size(84, 45).name("discord"));
//info icon
if(mobile){
parent.fill(c -> c.bottom().left().button("", new TextButtonStyle(){{
font = Fonts.def;
fontColor = Color.white;
up = infoBanner;
}}, ui.about::show).size(84, 45).name("info"));
}else if(becontrol.active()){
parent.fill(c -> c.bottom().right().button("@be.check", Icon.refresh, () -> {
ui.loadfrag.show();
becontrol.checkUpdate(result -> {
ui.loadfrag.hide();
if(!result){
ui.showInfo("@be.noupdates");
}
});
}).size(200, 60).name("becheck").update(t -> {
t.getLabel().setColor(becontrol.isUpdateAvailable() ? Tmp.c1.set(Color.white).lerp(Pal.accent, Mathf.absin(5f, 1f)) : Color.white);
}));
}
}
private void buildMobile(){
@ -116,23 +122,28 @@ public class MenuFragment{
mods = new MobileButton(Icon.book, "@mods", ui.mods::show),
exit = new MobileButton(Icon.exit, "@quit", () -> Core.app.exit());
Seq<MobileButton> customs = customButtons.map(b -> new MobileButton(b.icon, b.text, b.runnable == null ? () -> {} : b.runnable));
if(!Core.graphics.isPortrait()){
container.marginTop(60f);
container.add(play);
container.add(join);
container.add(custom);
container.add(maps);
// add odd custom buttons
for(int i = 1; i < customs.size; i += 2){
container.add(customs.get(i));
}
container.row();
container.table(table -> {
table.defaults().set(container.defaults());
table.add(editor);
table.add(tools);
table.add(mods);
if(!ios) table.add(exit);
}).colspan(4);
container.add(editor);
container.add(tools);
container.add(mods);
// add even custom buttons (before the exit button)
for(int i = 0; i < customs.size; i += 2){
container.add(customs.get(i));
}
if(!ios) container.add(exit);
}else{
container.marginTop(0f);
container.add(play);
@ -144,13 +155,13 @@ public class MenuFragment{
container.add(editor);
container.add(tools);
container.row();
container.table(table -> {
table.defaults().set(container.defaults());
table.add(mods);
if(!ios) table.add(exit);
}).colspan(2);
container.add(mods);
// add custom buttons
for(int i = 0; i < customs.size; i++){
container.add(customs.get(i));
if(i % 2 == 0) container.row();
}
if(!ios) container.add(exit);
}
}
@ -168,23 +179,23 @@ public class MenuFragment{
t.name = "buttons";
buttons(t,
new Buttoni("@play", Icon.play,
new Buttoni("@campaign", Icon.play, () -> checkPlay(ui.planet::show)),
new Buttoni("@joingame", Icon.add, () -> checkPlay(ui.join::show)),
new Buttoni("@customgame", Icon.terrain, () -> checkPlay(ui.custom::show)),
new Buttoni("@loadgame", Icon.download, () -> checkPlay(ui.load::show))
new MenuButton("@play", Icon.play,
new MenuButton("@campaign", Icon.play, () -> checkPlay(ui.planet::show)),
new MenuButton("@joingame", Icon.add, () -> checkPlay(ui.join::show)),
new MenuButton("@customgame", Icon.terrain, () -> checkPlay(ui.custom::show)),
new MenuButton("@loadgame", Icon.download, () -> checkPlay(ui.load::show))
),
new Buttoni("@database.button", Icon.menu,
new Buttoni("@schematics", Icon.paste, ui.schematics::show),
new Buttoni("@database", Icon.book, ui.database::show),
new Buttoni("@about.button", Icon.info, ui.about::show)
new MenuButton("@database.button", Icon.menu,
new MenuButton("@schematics", Icon.paste, ui.schematics::show),
new MenuButton("@database", Icon.book, ui.database::show),
new MenuButton("@about.button", Icon.info, ui.about::show)
),
new Buttoni("@editor", Icon.terrain, () -> checkPlay(ui.maps::show)), steam ? new Buttoni("@workshop", Icon.steam, platform::openWorkshop) : null,
new Buttoni("@mods", Icon.book, ui.mods::show),
new Buttoni("@settings", Icon.settings, ui.settings::show),
new Buttoni("@quit", Icon.exit, Core.app::exit)
new MenuButton("@editor", Icon.terrain, () -> checkPlay(ui.maps::show)), steam ? new MenuButton("@workshop", Icon.steam, platform::openWorkshop) : null,
new MenuButton("@mods", Icon.book, ui.mods::show),
new MenuButton("@settings", Icon.settings, ui.settings::show)
);
buttons(t, customButtons.toArray(MenuButton.class));
buttons(t, new MenuButton("@quit", Icon.exit, Core.app::exit));
}).width(width).growY();
container.table(background, t -> {
@ -199,7 +210,6 @@ public class MenuFragment{
}
private void checkPlay(Runnable run){
if(!mods.hasContentErrors()){
run.run();
}else{
@ -222,8 +232,8 @@ public class MenuFragment{
submenu.actions(Actions.alpha(1f), Actions.alpha(0f, 0.2f, Interp.fade), Actions.run(() -> submenu.clearChildren()));
}
private void buttons(Table t, Buttoni... buttons){
for(Buttoni b : buttons){
private void buttons(Table t, MenuButton... buttons){
for(MenuButton b : buttons){
if(b == null) continue;
Button[] out = {null};
out[0] = t.button(b.text, b.icon, Styles.flatToggleMenut, () -> {
@ -251,24 +261,56 @@ public class MenuFragment{
}
}
private static class Buttoni{
final Drawable icon;
final String text;
final Runnable runnable;
final Buttoni[] submenu;
/** Adds a custom button to the menu. */
public void addButton(String text, Drawable icon, Runnable callback){
addButton(new MenuButton(text, icon, callback));
}
public Buttoni(String text, Drawable icon, Runnable runnable){
/** Adds a custom button to the menu. */
public void addButton(String text, Runnable callback){
addButton(text, Styles.none, callback);
}
/**
* Adds a custom button to the menu.
* If {@link MenuButton#submenu} is null or the player is on mobile, {@link MenuButton#runnable} is invoked on click.
* Otherwise, {@link MenuButton#submenu} is shown.
*/
public void addButton(MenuButton button){
customButtons.add(button);
}
/** Represents a menu button definition. */
public static class MenuButton{
public final Drawable icon;
public final String text;
/** Runnable ran when the button is clicked. Ignored on desktop if {@link #submenu} is not null. */
public final Runnable runnable;
/** Submenu shown when this button is clicked. Used instead of {@link #runnable} on desktop. */
public final @Nullable MenuButton[] submenu;
/** Constructs a simple menu button, which behaves the same way on desktop and mobile. */
public MenuButton(String text, Drawable icon, Runnable runnable){
this.icon = icon;
this.text = text;
this.runnable = runnable;
this.submenu = null;
}
public Buttoni(String text, Drawable icon, Buttoni... buttons){
/** Constructs a button that runs the runnable when clicked on mobile or shows the submenu on desktop. */
public MenuButton(String text, Drawable icon, Runnable runnable, MenuButton... submenu){
this.icon = icon;
this.text = text;
this.runnable = runnable;
this.submenu = submenu;
}
/** Comstructs a desktop-only button; used internally. */
MenuButton(String text, Drawable icon, MenuButton... submenu){
this.icon = icon;
this.text = text;
this.runnable = () -> {};
this.submenu = buttons;
this.submenu = submenu;
}
}
}