diff --git a/.gitignore b/.gitignore index 9dd94c143d..40d378f67e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ /desktop/mindustry-maps/ /desktop/gifexport/ /core/lib/ +/annotations/build/ /kryonet/build/ /server/build/ /annotations/build/ diff --git a/core/assets/sprites/icon.icns b/core/assets/sprites/icon.icns index 93771cc76c..a71732bda5 100644 Binary files a/core/assets/sprites/icon.icns and b/core/assets/sprites/icon.icns differ diff --git a/core/assets/sprites/icon@2x.icns b/core/assets/sprites/icon@2x.icns new file mode 100644 index 0000000000..7d3530cefa Binary files /dev/null and b/core/assets/sprites/icon@2x.icns differ diff --git a/core/src/io/anuke/mindustry/core/Platform.java b/core/src/io/anuke/mindustry/core/Platform.java index 847da450da..6e7556f78d 100644 --- a/core/src/io/anuke/mindustry/core/Platform.java +++ b/core/src/io/anuke/mindustry/core/Platform.java @@ -69,6 +69,16 @@ public abstract class Platform { public void downloadFile(String name, byte[] bytes){} /**Open a file chooser. Only used on GWT backend.*/ public void openFile(Consumer cons){} + + /**Show a file chooser. Desktop only. + * + * @param text File chooser title text + * @param content Type of files to be loaded + * @param cons Selection listener + * @param open Whether to open or save files. + * @param filetype File extensions to filter. + */ + public void showFileChooser(String text, String content, Consumer cons, boolean open, String filetype){} /**Use the default thread provider from the kryonet module for this.*/ public ThreadProvider getThreadProvider(){ return new ThreadProvider() { diff --git a/core/src/io/anuke/mindustry/ui/dialogs/AboutDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/AboutDialog.java index 109f2c6f12..fc1fa929df 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/AboutDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/AboutDialog.java @@ -2,17 +2,20 @@ package io.anuke.mindustry.ui.dialogs; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.utils.ObjectSet; import io.anuke.mindustry.ui.Links; import io.anuke.mindustry.ui.Links.LinkEntry; import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Timers; import io.anuke.ucore.scene.ui.ScrollPane; import io.anuke.ucore.scene.ui.layout.Table; +import io.anuke.ucore.util.OS; import static io.anuke.mindustry.Vars.ios; import static io.anuke.mindustry.Vars.ui; public class AboutDialog extends FloatingDialog { + private static ObjectSet bannedItems = ObjectSet.with("google-play", "itch.io", "dev-builds", "trello"); public AboutDialog(){ super("$text.about.button"); @@ -26,7 +29,7 @@ public class AboutDialog extends FloatingDialog { ScrollPane pane = new ScrollPane(in, "clear"); for(LinkEntry link : Links.getLinks()){ - if(ios && link.name.equals("google-play")){ //because Apple doesn't like me mentioning android + if((ios || OS.isMac) && bannedItems.contains(link.name)){ //because Apple doesn't like me mentioning things continue; } @@ -64,13 +67,14 @@ public class AboutDialog extends FloatingDialog { content().add(pane).growX(); buttons().addButton("$text.credits", this::showCredits).size(200f, 64f); - if(!ios){ + + if(!ios && !OS.isMac){ buttons().addButton("$text.changelog.title", ui.changelog::show).size(200f, 64f); } } - private void showCredits(){ + public void showCredits(){ FloatingDialog dialog = new FloatingDialog("$text.credits"); dialog.addCloseButton(); dialog.content().add("$text.about"); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/ChangelogDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/ChangelogDialog.java index 6da2480aa3..b525117aa9 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/ChangelogDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/ChangelogDialog.java @@ -10,6 +10,7 @@ import io.anuke.ucore.core.Settings; import io.anuke.ucore.scene.ui.ScrollPane; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.util.Log; +import io.anuke.ucore.util.OS; import static io.anuke.mindustry.Vars.ios; @@ -24,7 +25,7 @@ public class ChangelogDialog extends FloatingDialog{ content().add("$text.changelog.loading"); - if(!ios) { + if(!ios && !OS.isMac) { Changelogs.getChangelog(result -> { versions = result; Gdx.app.postRunnable(this::setup); @@ -55,6 +56,10 @@ public class ChangelogDialog extends FloatingDialog{ } }else{ for(VersionInfo info : versions){ + String desc = info.description; + + desc = desc.replace("Android", "Mobile"); + Table in = new Table("clear"); in.top().left().margin(10); @@ -67,7 +72,7 @@ public class ChangelogDialog extends FloatingDialog{ in.add("$text.changelog.latest"); } in.row(); - in.labelWrap("[lightgray]" + info.description).width(vw - 20).padTop(12); + in.labelWrap("[lightgray]" + desc).width(vw - 20).padTop(12); table.add(in).width(vw).pad(8).row(); } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java b/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java index 4e4d3f4353..32d019f226 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java @@ -8,6 +8,7 @@ import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Pools; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.Platform; +import io.anuke.ucore.UCore; import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Timers; import io.anuke.ucore.function.Consumer; @@ -16,6 +17,7 @@ import io.anuke.ucore.scene.event.Touchable; import io.anuke.ucore.scene.ui.*; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Unit; +import io.anuke.ucore.util.OS; import java.util.Arrays; @@ -23,7 +25,8 @@ import static io.anuke.mindustry.Vars.gwt; public class FileChooser extends FloatingDialog { private Table files; - private FileHandle homeDirectory = gwt ? Gdx.files.internal("") : Gdx.files.absolute(Gdx.files.getExternalStoragePath()); + private FileHandle homeDirectory = gwt? Gdx.files.internal("") : Gdx.files.absolute(OS.isMac ? UCore.getProperty("user.home") + "/Downloads/" : + Gdx.files.getExternalStoragePath()); private FileHandle directory = homeDirectory; private ScrollPane pane; private TextField navigation, filefield; @@ -99,6 +102,11 @@ public class FileChooser extends FloatingDialog { updateFiles(true); }); + //Macs are confined to the Downloads/ directory + if(OS.isMac){ + up.setDisabled(true); + } + ImageButton back = new ImageButton("icon-arrow-left"); back.resizeImage(isize); @@ -169,7 +177,8 @@ public class FileChooser extends FloatingDialog { private void updateFiles(boolean push){ if(push) stack.push(directory); - navigation.setText(directory.toString()); + //if is mac, don't display extra info since you can only ever go to downloads + navigation.setText(OS.isMac ? directory.name() : directory.toString()); GlyphLayout layout = Pools.obtain(GlyphLayout.class); @@ -184,23 +193,25 @@ public class FileChooser extends FloatingDialog { Pools.free(layout); files.clearChildren(); + files.top().left(); FileHandle[] names = getFileNames(); - Image upimage = new Image("icon-folder-parent"); + //macs are confined to the Downloads/ directory + if(!OS.isMac) { + Image upimage = new Image("icon-folder-parent"); + TextButton upbutton = new TextButton(".." + directory.toString()); + upbutton.clicked(() -> { + directory = directory.parent(); + updateFiles(true); + }); - TextButton upbutton = new TextButton(".." + directory.toString()); - upbutton.clicked(()->{ - directory = directory.parent(); - updateFiles(true); - }); - - upbutton.left().add(upimage).padRight(4f).size(14*2); - upbutton.getCells().reverse(); - - files.top().left().add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2); - upbutton.getLabel().setAlignment(Align.left); + upbutton.left().add(upimage).padRight(4f).size(14 * 2); + upbutton.getLabel().setAlignment(Align.left); + upbutton.getCells().reverse(); - files.row(); + files.add(upbutton).align(Align.topLeft).fillX().expandX().height(50).pad(2).colspan(2); + files.row(); + } ButtonGroup group = new ButtonGroup(); group.setMinCheckCount(0); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java index f301b09641..e6ddc6f613 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/LoadDialog.java @@ -87,14 +87,14 @@ public class LoadDialog extends FloatingDialog{ if(!gwt) { t.addImageButton("icon-save", "empty", 14 * 3, () -> { if(!ios) { - new FileChooser("$text.save.export", false, file -> { - try { - slot.exportFile(file); - setup(); - } catch (IOException e) { - ui.showError(Bundles.format("text.save.export.fail", Strings.parseException(e, false))); - } - }).show(); + Platform.instance.showFileChooser(Bundles.get("text.save.export"), "Mindustry Save", file -> { + try { + slot.exportFile(file); + setup(); + } catch (IOException e) { + ui.showError(Bundles.format("text.save.export.fail", Strings.parseException(e, false))); + } + }, false, "mins"); }else{ try { FileHandle file = Gdx.files.local("save-" + slot.getName() + ".mins"); @@ -150,7 +150,7 @@ public class LoadDialog extends FloatingDialog{ if(gwt || ios) return; slots.addImageTextButton("$text.save.import", "icon-add", "clear", 14*3, () -> { - new FileChooser("$text.save.import", f -> f.extension().equals("mins"), true, file -> { + Platform.instance.showFileChooser(Bundles.get("text.save.import"), "Mindustry Save", file -> { if(SaveIO.isSaveValid(file)){ try{ control.getSaves().importSave(file); @@ -161,7 +161,7 @@ public class LoadDialog extends FloatingDialog{ }else{ ui.showError("$text.save.import.invalid"); } - }).show(); + }, true, "mins"); }).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4); } diff --git a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java index e38c2b4bd8..4dc92354b8 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java @@ -10,6 +10,7 @@ import io.anuke.ucore.scene.Group; import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.table; +import io.anuke.ucore.util.OS; import static io.anuke.mindustry.Vars.*; @@ -39,7 +40,13 @@ public class MenuFragment implements Fragment{ add(new MenuButton("icon-info", "$text.about.button", ui.about::show)); - add(new MenuButton("icon-tools", "$text.settings", ui.settings::show)); + add(new MenuButton("icon-menu", OS.isMac ? "$text.credits" : "$text.changelog.title", () -> { + if(OS.isMac){ + ui.about.showCredits(); + }else { + ui.changelog.show(); + } + })); row(); diff --git a/desktop/build.gradle b/desktop/build.gradle index 268eb20c03..99775d3be4 100644 --- a/desktop/build.gradle +++ b/desktop/build.gradle @@ -86,7 +86,7 @@ task packrCmd() { commandLine("java", "-jar", PACKR_DIR + "packr.jar", "--verbose", - "--bundle", getPackage(), + "--bundle", getPackage() + ".mac", "--platform", getPlatform(), "--executable", appName, "--output", "packr-out/", diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index d87332c76a..4c007bd4ad 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -1,5 +1,6 @@ package io.anuke.mindustry.desktop; +import com.badlogic.gdx.Files.FileType; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; import io.anuke.kryonet.KryoClient; @@ -7,6 +8,8 @@ import io.anuke.kryonet.KryoServer; import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.net.Net; +import io.anuke.ucore.UCore; +import io.anuke.ucore.util.OS; public class DesktopLauncher { @@ -18,6 +21,10 @@ public class DesktopLauncher { config.setWindowedMode(960, 540); config.setWindowIcon("sprites/icon.png"); + if(OS.isMac) { + config.setPreferencesConfig(UCore.getProperty("user.home") + "/Library/Application Support/Mindustry", FileType.Absolute); + } + Platform.instance = new DesktopPlatform(arg); Net.setClientProvider(new KryoClient()); diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java index d25c590a4d..3dfb230c18 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java @@ -3,12 +3,14 @@ package io.anuke.mindustry.desktop; import club.minnced.discord.rpc.DiscordEventHandlers; import club.minnced.discord.rpc.DiscordRPC; import club.minnced.discord.rpc.DiscordRichPresence; +import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Base64Coder; import io.anuke.kryonet.DefaultThreadImpl; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.ui.dialogs.FileChooser; import io.anuke.ucore.core.Settings; import io.anuke.ucore.util.OS; import io.anuke.ucore.util.Strings; @@ -41,6 +43,11 @@ public class DesktopPlatform extends Platform { } } + @Override + public void showFileChooser(String text, String content, Consumer cons, boolean open, String filter) { + new FileChooser(text, file -> file.extension().equalsIgnoreCase(filter), open, cons).show(); + } + @Override public String format(Date date){ return format.format(date); diff --git a/ios/robovm.properties b/ios/robovm.properties index 9847b37ee0..a53bf9ac88 100644 --- a/ios/robovm.properties +++ b/ios/robovm.properties @@ -2,5 +2,5 @@ app.version=3.5 app.id=io.anuke.mindustry app.mainclass=io.anuke.mindustry.IOSLauncher app.executable=IOSLauncher -app.build=10 +app.build=11 app.name=Mindustry