mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-02 11:10:50 +03:00
File chooser system refactor
This commit is contained in:
@@ -5,20 +5,22 @@ import android.app.*;
|
|||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.content.pm.*;
|
import android.content.pm.*;
|
||||||
import android.net.*;
|
import android.net.*;
|
||||||
import android.os.Build.*;
|
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
|
import android.os.Build.*;
|
||||||
import android.telephony.*;
|
import android.telephony.*;
|
||||||
import arc.*;
|
import arc.*;
|
||||||
import arc.backend.android.*;
|
import arc.backend.android.*;
|
||||||
import arc.files.*;
|
import arc.files.*;
|
||||||
import arc.func.*;
|
|
||||||
import arc.scene.ui.layout.*;
|
import arc.scene.ui.layout.*;
|
||||||
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import dalvik.system.*;
|
import dalvik.system.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.game.Saves.*;
|
import mindustry.game.Saves.*;
|
||||||
import mindustry.io.*;
|
import mindustry.io.*;
|
||||||
import mindustry.net.*;
|
import mindustry.net.*;
|
||||||
|
import mindustry.ui.*;
|
||||||
|
import mindustry.ui.FileChooser.*;
|
||||||
import mindustry.ui.dialogs.*;
|
import mindustry.ui.dialogs.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -30,7 +32,7 @@ import static mindustry.Vars.*;
|
|||||||
public class AndroidLauncher extends AndroidApplication{
|
public class AndroidLauncher extends AndroidApplication{
|
||||||
public static final int PERMISSION_REQUEST_CODE = 1;
|
public static final int PERMISSION_REQUEST_CODE = 1;
|
||||||
boolean doubleScaleTablets = true;
|
boolean doubleScaleTablets = true;
|
||||||
FileChooser chooser;
|
FileChooserDialog chooser;
|
||||||
Runnable permCallback;
|
Runnable permCallback;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -97,8 +99,7 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}catch(SecurityException e){
|
}catch(SecurityException e){
|
||||||
//`setReadOnly` to jar file in `/sdcard/Android/data/...` does not work on some Android 14 device
|
//`setReadOnly` to jar file in `/sdcard/Android/data/...` does not work on some Android 14 devices, but in `/data/...`, it does
|
||||||
//But in `/data/...` it works
|
|
||||||
|
|
||||||
if(Build.VERSION.SDK_INT < VERSION_CODES.O_MR1){
|
if(Build.VERSION.SDK_INT < VERSION_CODES.O_MR1){
|
||||||
throw e;
|
throw e;
|
||||||
@@ -127,27 +128,34 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showFileChooser(boolean open, String title, String extension, Cons<Fi> cons){
|
public void showFileChooser(FileChooserParams params){
|
||||||
showFileChooser(open, title, cons, extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showFileChooser(boolean open, String title, Cons<Fi> cons, String... extensions){
|
|
||||||
try{
|
try{
|
||||||
String extension = extensions[0];
|
String extension = params.extensions[0];
|
||||||
|
|
||||||
if(VERSION.SDK_INT >= VERSION_CODES.Q){
|
if(VERSION.SDK_INT >= VERSION_CODES.Q){
|
||||||
Intent intent = new Intent(open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT);
|
Intent intent = new Intent(params.open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT);
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
intent.setType(extension.equals("zip") && !open && extensions.length == 1 ? "application/zip" : "*/*");
|
intent.setType(extension.equals("zip") && !params.open && params.extensions.length == 1 ? "application/zip" : "*/*");
|
||||||
intent.putExtra(Intent.EXTRA_TITLE, "export." + extension);
|
intent.putExtra(Intent.EXTRA_TITLE, params.fileName);
|
||||||
|
if(params.allowMultiple){
|
||||||
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||||
|
}
|
||||||
|
|
||||||
addResultListener(i -> startActivityForResult(intent, i), (code, in) -> {
|
addResultListener(i -> startActivityForResult(intent, i), (code, in) -> {
|
||||||
if(code == Activity.RESULT_OK && in != null && in.getData() != null){
|
if(code == Activity.RESULT_OK && in != null && in.getData() != null){
|
||||||
Uri uri = in.getData();
|
Uri[] uris;
|
||||||
|
if(in.getClipData() != null){
|
||||||
|
uris = new Uri[in.getClipData().getItemCount()];
|
||||||
|
for(int i = 0; i < uris.length; i++){
|
||||||
|
uris[i] = in.getClipData().getItemAt(i).getUri();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
uris = new Uri[]{in.getData()};
|
||||||
|
}
|
||||||
|
|
||||||
if(uri.getPath().contains("(invalid)")) return;
|
if(uris.length == 0 || uris[0].getPath().contains("(invalid)")) return;
|
||||||
|
|
||||||
Core.app.post(() -> Core.app.post(() -> cons.get(new Fi(uri.getPath()){
|
Fi[] files = Seq.with(uris).map(uri -> new Fi(uri.getPath()){
|
||||||
@Override
|
@Override
|
||||||
public InputStream read(){
|
public InputStream read(){
|
||||||
try{
|
try{
|
||||||
@@ -165,18 +173,14 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
throw new ArcRuntimeException(e);
|
throw new ArcRuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})));
|
}).toArray(Fi.class);
|
||||||
|
|
||||||
|
Core.app.post(() -> Core.app.post(() -> params.handleChooseResult(files)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}else if(VERSION.SDK_INT >= VERSION_CODES.M && !(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
|
}else if(VERSION.SDK_INT >= VERSION_CODES.M && !(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
|
||||||
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){
|
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){
|
||||||
chooser = new FileChooser(title, file -> Structs.contains(extensions, file.extension().toLowerCase()), open, file -> {
|
chooser = FileChooser.createFallbackFileChooser(params);
|
||||||
if(!open){
|
|
||||||
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
|
||||||
}else{
|
|
||||||
cons.get(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ArrayList<String> perms = new ArrayList<>();
|
ArrayList<String> perms = new ArrayList<>();
|
||||||
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
|
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
|
||||||
@@ -187,22 +191,13 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
}
|
}
|
||||||
requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE);
|
requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE);
|
||||||
}else{
|
}else{
|
||||||
if(open){
|
FileChooser.showFallbackFileChooser(params);
|
||||||
new FileChooser(title, file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show();
|
|
||||||
}else{
|
|
||||||
super.showFileChooser(open, "@open", extension, cons);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}catch(Throwable error){
|
}catch(Throwable error){
|
||||||
Core.app.post(() -> Vars.ui.showException(error));
|
Core.app.post(() -> Vars.ui.showException(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showMultiFileChooser(Cons<Fi> cons, String... extensions){
|
|
||||||
showFileChooser(true, "@open", cons, extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beginForceLandscape(){
|
public void beginForceLandscape(){
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||||
@@ -235,7 +230,6 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
Log.err("Failed to delete cached folder", t);
|
Log.err("Failed to delete cached folder", t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//move to internal storage if there's no file indicating that it moved
|
//move to internal storage if there's no file indicating that it moved
|
||||||
if(!Core.files.local("files_moved").exists()){
|
if(!Core.files.local("files_moved").exists()){
|
||||||
Log.info("Moving files to external storage...");
|
Log.info("Moving files to external storage...");
|
||||||
|
|||||||
@@ -308,12 +308,6 @@
|
|||||||
"discord-logo-white"
|
"discord-logo-white"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"uid": "dd6df3b754e8d0dbf09d9b24088f0442",
|
|
||||||
"css": "box",
|
|
||||||
"code": 59422,
|
|
||||||
"src": "iconic"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"uid": "f08ba9f0e8f9b426a92efcaa59c1e932",
|
"uid": "f08ba9f0e8f9b426a92efcaa59c1e932",
|
||||||
"css": "reddit-alien",
|
"css": "reddit-alien",
|
||||||
@@ -1203,6 +1197,66 @@
|
|||||||
"css": "sitemap",
|
"css": "sitemap",
|
||||||
"code": 61672,
|
"code": 61672,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "2c413e78faf1d6631fd7b094d14c2253",
|
||||||
|
"css": "cloud",
|
||||||
|
"code": 59441,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "de2fc7a5c986ab8c622f63455d7cf814",
|
||||||
|
"css": "upload-cloud",
|
||||||
|
"code": 61678,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "f5999a012fc3752386635ec02a858447",
|
||||||
|
"css": "download-cloud",
|
||||||
|
"code": 61677,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "e7a4824e79002c2d8ce3047b2da8b5a2",
|
||||||
|
"css": "rain",
|
||||||
|
"code": 59442,
|
||||||
|
"src": "typicons"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "ac10a3a3077eab1b9d66509eb25fb9b4",
|
||||||
|
"css": "cloud-sun",
|
||||||
|
"code": 59448,
|
||||||
|
"src": "typicons"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "6f200a5a449259c51cd0ccf56b4c1e93",
|
||||||
|
"css": "drizzle",
|
||||||
|
"code": 59452,
|
||||||
|
"src": "typicons"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3cac7625657b10cf706e7f332749c912",
|
||||||
|
"css": "cloud-flash",
|
||||||
|
"code": 59456,
|
||||||
|
"src": "typicons"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "364cc69fb3edb8fb21caa6a2c0489589",
|
||||||
|
"css": "cloud-wind",
|
||||||
|
"code": 59457,
|
||||||
|
"src": "typicons"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "cc05df515bebe11df3ada0a5910a8f6d",
|
||||||
|
"css": "box",
|
||||||
|
"code": 59422,
|
||||||
|
"src": "entypo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "d5f287ad8e3a2db8f808bc09980ec3dc",
|
||||||
|
"css": "chart-alt",
|
||||||
|
"code": 59459,
|
||||||
|
"src": "typicons"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -3,13 +3,11 @@ package mindustry.core;
|
|||||||
import arc.*;
|
import arc.*;
|
||||||
import arc.assets.*;
|
import arc.assets.*;
|
||||||
import arc.audio.*;
|
import arc.audio.*;
|
||||||
import arc.graphics.*;
|
|
||||||
import arc.graphics.g2d.*;
|
import arc.graphics.g2d.*;
|
||||||
import arc.input.*;
|
import arc.input.*;
|
||||||
import arc.math.*;
|
import arc.math.*;
|
||||||
import arc.scene.style.*;
|
import arc.scene.style.*;
|
||||||
import arc.scene.ui.*;
|
import arc.scene.ui.*;
|
||||||
import arc.scene.ui.layout.*;
|
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
@@ -24,7 +22,6 @@ import mindustry.game.Objectives.*;
|
|||||||
import mindustry.game.Saves.*;
|
import mindustry.game.Saves.*;
|
||||||
import mindustry.game.Teams.*;
|
import mindustry.game.Teams.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.graphics.*;
|
|
||||||
import mindustry.input.*;
|
import mindustry.input.*;
|
||||||
import mindustry.io.*;
|
import mindustry.io.*;
|
||||||
import mindustry.io.SaveIO.*;
|
import mindustry.io.SaveIO.*;
|
||||||
@@ -113,39 +110,6 @@ public class Control implements ApplicationListener, Loadable{
|
|||||||
input.checkUnit();
|
input.checkUnit();
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.on(WorldLoadEvent.class, e -> app.post(() -> {
|
|
||||||
var errored = state.data.getContent().select(c -> c.errored);
|
|
||||||
if(!errored.isEmpty()){
|
|
||||||
new BaseDialog(bundle.get("mod.erroredcontent").replace("[red]", "")){{
|
|
||||||
setFillParent(true);
|
|
||||||
setTitleColor(Color.scarlet);
|
|
||||||
cont.margin(15);
|
|
||||||
cont.row();
|
|
||||||
cont.add("@mod.errors.map").wrap().growX().center().labelAlign(Align.center);
|
|
||||||
cont.row();
|
|
||||||
cont.pane(d -> {
|
|
||||||
d.left().marginLeft(15f);
|
|
||||||
for(var asset : errored){
|
|
||||||
d.add(asset.path).color(Pal.accent).left().padRight(10).growX().row();
|
|
||||||
|
|
||||||
d.table(t -> {
|
|
||||||
t.left();
|
|
||||||
t.image(Tex.whiteui).width(3f).growY().color(Color.lightGray).padRight(8f).padTop(2f).padBottom(2f);
|
|
||||||
|
|
||||||
t.add(asset.warnings.toString("\n\n")).color(Color.lightGray).labelAlign(Align.left, Align.left).left().growX().wrap().padTop(5f).row();
|
|
||||||
}).padBottom(10f).grow().row();
|
|
||||||
}
|
|
||||||
}).width(Math.min(graphics.getWidth() / Scl.scl(1f) * 0.9f, 700f));
|
|
||||||
|
|
||||||
buttons.button("@quit", Icon.exit, () -> {
|
|
||||||
hide();
|
|
||||||
ui.paused.runExitSave(false);
|
|
||||||
}).size(210, 64);
|
|
||||||
buttons.button("@continue", Icon.warning, this::hide).size(210, 64);
|
|
||||||
}}.show();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
Events.on(ResetEvent.class, event -> {
|
Events.on(ResetEvent.class, event -> {
|
||||||
player.reset();
|
player.reset();
|
||||||
toBePlaced.clear();
|
toBePlaced.clear();
|
||||||
|
|||||||
@@ -2,22 +2,18 @@ package mindustry.core;
|
|||||||
|
|
||||||
import arc.*;
|
import arc.*;
|
||||||
import arc.files.*;
|
import arc.files.*;
|
||||||
import arc.func.*;
|
|
||||||
import arc.math.*;
|
import arc.math.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
|
||||||
import arc.util.serialization.*;
|
import arc.util.serialization.*;
|
||||||
import mindustry.mod.*;
|
import mindustry.mod.*;
|
||||||
import mindustry.net.*;
|
import mindustry.net.*;
|
||||||
import mindustry.net.Net.*;
|
import mindustry.net.Net.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.ui.dialogs.*;
|
import mindustry.ui.FileChooser.*;
|
||||||
import rhino.*;
|
import rhino.*;
|
||||||
|
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
|
|
||||||
import static mindustry.Vars.*;
|
|
||||||
|
|
||||||
public interface Platform{
|
public interface Platform{
|
||||||
|
|
||||||
/** Dynamically creates a class loader for a jar file. This loader must be child-first. */
|
/** Dynamically creates a class loader for a jar file. This loader must be child-first. */
|
||||||
@@ -106,68 +102,9 @@ public interface Platform{
|
|||||||
default void shareFile(Fi file){
|
default void shareFile(Fi file){
|
||||||
}
|
}
|
||||||
|
|
||||||
default void export(String name, String extension, FileWriter writer){
|
/** Do not call directly; use the builder pattern in {@link mindustry.ui.FileChooser}. */
|
||||||
if(!ios){
|
default void showFileChooser(FileChooserParams params){
|
||||||
platform.showFileChooser(false, extension, file -> {
|
throw new IllegalArgumentException("Not implemented on this platform!");
|
||||||
ui.loadAnd(() -> {
|
|
||||||
try{
|
|
||||||
writer.write(file);
|
|
||||||
}catch(Throwable e){
|
|
||||||
ui.showException(e);
|
|
||||||
Log.err(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
ui.loadAnd(() -> {
|
|
||||||
try{
|
|
||||||
Fi result = Core.files.local(name + "." + extension);
|
|
||||||
writer.write(result);
|
|
||||||
platform.shareFile(result);
|
|
||||||
}catch(Throwable e){
|
|
||||||
ui.showException(e);
|
|
||||||
Log.err(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a file chooser.
|
|
||||||
* @param cons Selection listener
|
|
||||||
* @param open Whether to open or save files
|
|
||||||
* @param extension File extension to filter
|
|
||||||
* @param title The title of the native dialog
|
|
||||||
*/
|
|
||||||
default void showFileChooser(boolean open, String title, String extension, Cons<Fi> cons){
|
|
||||||
defaultFileDialog(open, title, extension, cons);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show a file chooser for multiple file types.
|
|
||||||
* @param cons Selection listener
|
|
||||||
* @param extensions File extensions to filter
|
|
||||||
*/
|
|
||||||
default void showMultiFileChooser(Cons<Fi> cons, String... extensions){
|
|
||||||
defaultMultiFileChooser(cons, extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void showFileChooser(boolean open, String extension, Cons<Fi> cons){
|
|
||||||
showFileChooser(open, open ? "@open": "@save", extension, cons);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void defaultFileDialog(boolean open, String title, String extension, Cons<Fi> cons){
|
|
||||||
new FileChooser(title, file -> file.extEquals(extension), open, file -> {
|
|
||||||
if(!open){
|
|
||||||
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
|
|
||||||
}else{
|
|
||||||
cons.get(file);
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void defaultMultiFileChooser(Cons<Fi> cons, String... extensions){
|
|
||||||
new FileChooser("@open", file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Hide the app. Android only. */
|
/** Hide the app. Android only. */
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
t.button("@editor.import", Icon.download, () -> createDialog("@editor.import",
|
t.button("@editor.import", Icon.download, () -> createDialog("@editor.import",
|
||||||
"@editor.importmap", "@editor.importmap.description", Icon.download, (Runnable)loadDialog::show,
|
"@editor.importmap", "@editor.importmap.description", Icon.download, (Runnable)loadDialog::show,
|
||||||
"@editor.importfile", "@editor.importfile.description", Icon.file, (Runnable)() ->
|
"@editor.importfile", "@editor.importfile.description", Icon.file, (Runnable)() ->
|
||||||
platform.showFileChooser(true, mapExtension, file -> ui.loadAnd(() -> {
|
FileChooser.open(mapExtension).submit(file -> ui.loadAnd(() -> {
|
||||||
maps.tryCatchMapError(() -> {
|
maps.tryCatchMapError(() -> {
|
||||||
if(MapIO.isImage(file)){
|
if(MapIO.isImage(file)){
|
||||||
ui.showInfo("@editor.errorimage");
|
ui.showInfo("@editor.errorimage");
|
||||||
@@ -105,7 +105,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
})),
|
})),
|
||||||
|
|
||||||
"@editor.importimage", "@editor.importimage.description", Icon.fileImage, (Runnable)() ->
|
"@editor.importimage", "@editor.importimage.description", Icon.fileImage, (Runnable)() ->
|
||||||
platform.showFileChooser(true, "png", file ->
|
FileChooser.open("png").submit(file ->
|
||||||
ui.loadAnd(() -> {
|
ui.loadAnd(() -> {
|
||||||
try{
|
try{
|
||||||
Pixmap pixmap = new Pixmap(file);
|
Pixmap pixmap = new Pixmap(file);
|
||||||
@@ -124,9 +124,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
|
|
||||||
t.button("@editor.export", Icon.upload, () -> createDialog("@editor.export",
|
t.button("@editor.export", Icon.upload, () -> createDialog("@editor.export",
|
||||||
"@editor.exportfile", "@editor.exportfile.description", Icon.file,
|
"@editor.exportfile", "@editor.exportfile.description", Icon.file,
|
||||||
(Runnable)() -> platform.export(editor.tags.get("name", "unknown"), mapExtension, file -> MapIO.writeMap(file, editor.createMap(file))),
|
(Runnable)() -> FileChooser.export(editor.tags.get("name", "unknown"), mapExtension, file -> MapIO.writeMap(file, editor.createMap(file))),
|
||||||
"@editor.exportimage", "@editor.exportimage.description", Icon.fileImage,
|
"@editor.exportimage", "@editor.exportimage.description", Icon.fileImage,
|
||||||
(Runnable)() -> platform.export(editor.tags.get("name", "unknown"), "png", file -> {
|
(Runnable)() -> FileChooser.export(editor.tags.get("name", "unknown"), "png", file -> {
|
||||||
Pixmap out = MapIO.writeImage(editor.tiles());
|
Pixmap out = MapIO.writeImage(editor.tiles());
|
||||||
file.writePng(out);
|
file.writePng(out);
|
||||||
out.dispose();
|
out.dispose();
|
||||||
|
|||||||
@@ -16,12 +16,8 @@ import static mindustry.Vars.*;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
TODO:
|
TODO:
|
||||||
• warn about critical errors for content that prevent content loading
|
|
||||||
• dividers for content types
|
• dividers for content types
|
||||||
• weather icon in font?
|
|
||||||
• remove planets from list of content that can be parsed to prevent confusion?
|
|
||||||
• drop down for import/export zip, clear all
|
• drop down for import/export zip, clear all
|
||||||
• allow multi file selection when importing
|
|
||||||
*/
|
*/
|
||||||
public class MapAssetsDialog extends BaseDialog{
|
public class MapAssetsDialog extends BaseDialog{
|
||||||
private static final TextureRegionDrawable[] typeIcons = {
|
private static final TextureRegionDrawable[] typeIcons = {
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ public class MapAudioView implements AssetView{
|
|||||||
m.setPosition(value);
|
m.setPosition(value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
//only ogg can seek
|
||||||
|
slider.touchable = asset.path.endsWith(".ogg") ? Touchable.enabled : Touchable.disabled;
|
||||||
slider.visible(() -> {
|
slider.visible(() -> {
|
||||||
boolean valid = lastPlaying == m && m.isPlaying();
|
boolean valid = lastPlaying == m && m.isPlaying();
|
||||||
if(valid){
|
if(valid){
|
||||||
@@ -88,25 +90,7 @@ public class MapAudioView implements AssetView{
|
|||||||
}
|
}
|
||||||
}).size(mobile ? 390f : 450f, h).margin(10f);
|
}).size(mobile ? 390f : 450f, h).margin(10f);
|
||||||
|
|
||||||
list.button(Icon.export, Styles.graySquarei, Vars.iconMed, () -> {
|
list.button(Icon.export, Styles.graySquarei, Vars.iconMed, () -> FileChooser.export(asset.name, Strings.getFileExtension(asset.path), file::copyTo)).size(h).disabled(file == null);
|
||||||
if(ios){
|
|
||||||
try{
|
|
||||||
Fi out = tmpDirectory.child(Strings.getFileName(asset.path));
|
|
||||||
file.copyTo(out);
|
|
||||||
platform.shareFile(out);
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
platform.showFileChooser(false, Strings.getFileExtension(asset.path), result -> {
|
|
||||||
try{
|
|
||||||
file.copyTo(result);
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).size(h).disabled(file == null);
|
|
||||||
|
|
||||||
list.button(Icon.trash, Styles.graySquarei, iconMed, () -> {
|
list.button(Icon.trash, Styles.graySquarei, iconMed, () -> {
|
||||||
ui.showConfirm("@asset.delete.confirm", () -> {
|
ui.showConfirm("@asset.delete.confirm", () -> {
|
||||||
@@ -140,32 +124,35 @@ public class MapAudioView implements AssetView{
|
|||||||
public void buildButtons(MapAssetsDialog diag, Table buttons){
|
public void buildButtons(MapAssetsDialog diag, Table buttons){
|
||||||
|
|
||||||
buttons.button("@add", Icon.add, () -> {
|
buttons.button("@add", Icon.add, () -> {
|
||||||
platform.showMultiFileChooser(result -> {
|
FileChooser.open("ogg", "mp3").submitMulti(result -> {
|
||||||
try{
|
var assets = state.data.getAssets(type);
|
||||||
//path and name are the same here; there's no path context.
|
|
||||||
String name = result.nameWithoutExtension();
|
for(Fi file : result){
|
||||||
String path = name;
|
try{
|
||||||
var other = state.data.getAssets(type).find(p -> (p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name)));
|
//path and name are the same here; there's no path context.
|
||||||
if(other != null){
|
String name = file.nameWithoutExtension();
|
||||||
ui.showErrorMessage(Core.bundle.format("asset.exists", other.name + " (" + other.path + ")"));
|
String path = name;
|
||||||
return;
|
var other = state.data.getAssets(type).find(p -> (p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name)));
|
||||||
|
if(other != null){
|
||||||
|
ui.showErrorMessage(Core.bundle.format("asset.exists", other.name + " (" + other.path + ")"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataAsset asset = (type == DataAssetType.music ? new MusicAsset() : new SoundAsset());
|
||||||
|
asset.setPath(path);
|
||||||
|
asset.updateData(file.readBytes());
|
||||||
|
|
||||||
|
assets.add(asset);
|
||||||
|
}catch(Exception e){
|
||||||
|
ui.showException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
var assets = state.data.getAssets(type);
|
|
||||||
|
|
||||||
DataAsset asset = (type == DataAssetType.music ? new MusicAsset() : new SoundAsset());
|
|
||||||
asset.setPath(path);
|
|
||||||
asset.updateData(result.readBytes());
|
|
||||||
|
|
||||||
assets.add(asset);
|
|
||||||
assets.sort();
|
|
||||||
state.data.reloadAudio();
|
|
||||||
diag.rebuild();
|
|
||||||
lastPlaying = null;
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
}
|
||||||
}, "ogg", "mp3");
|
|
||||||
|
assets.sort();
|
||||||
|
state.data.reloadAudio();
|
||||||
|
diag.rebuild();
|
||||||
|
lastPlaying = null;
|
||||||
|
});
|
||||||
}).size(190f, 64f);
|
}).size(190f, 64f);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,23 +69,7 @@ public class MapBundlesView implements AssetView{
|
|||||||
}).size(h).disabled(file == null);
|
}).size(h).disabled(file == null);
|
||||||
|
|
||||||
list.button(Icon.export, Styles.graySquarei, Vars.iconMed, () -> {
|
list.button(Icon.export, Styles.graySquarei, Vars.iconMed, () -> {
|
||||||
if(ios){
|
FileChooser.export(bundle.name, "properties", file::copyTo);
|
||||||
try{
|
|
||||||
Fi out = tmpDirectory.child(Strings.getFileName(bundle.path));
|
|
||||||
file.copyTo(out);
|
|
||||||
platform.shareFile(out);
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
platform.showFileChooser(false, "properties", out -> {
|
|
||||||
try{
|
|
||||||
file.copyTo(out);
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).size(h).disabled(file == null);
|
}).size(h).disabled(file == null);
|
||||||
|
|
||||||
list.button(Icon.trash, Styles.graySquarei, iconMed, () -> {
|
list.button(Icon.trash, Styles.graySquarei, iconMed, () -> {
|
||||||
@@ -140,10 +124,12 @@ public class MapBundlesView implements AssetView{
|
|||||||
handler.get("bundle.properties", Core.app.getClipboardText());
|
handler.get("bundle.properties", Core.app.getClipboardText());
|
||||||
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null);
|
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null);
|
||||||
t.row();
|
t.row();
|
||||||
t.button("@schematic.importfile", Icon.download, style, () -> platform.showMultiFileChooser(file -> {
|
t.button("@schematic.importfile", Icon.download, style, () -> FileChooser.open("properties", "txt").submitMulti(files -> {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
handler.get(file.name(), file.readString());
|
for(var file : files){
|
||||||
}, "txt", "bundle")).marginLeft(12f);
|
handler.get(file.name(), file.readString());
|
||||||
|
}
|
||||||
|
})).marginLeft(12f);
|
||||||
t.row();
|
t.row();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package mindustry.editor.data;
|
package mindustry.editor.data;
|
||||||
|
|
||||||
import arc.*;
|
import arc.*;
|
||||||
import arc.files.*;
|
|
||||||
import arc.scene.event.*;
|
import arc.scene.event.*;
|
||||||
import arc.scene.style.*;
|
import arc.scene.style.*;
|
||||||
import arc.scene.ui.layout.*;
|
import arc.scene.ui.layout.*;
|
||||||
@@ -24,7 +23,7 @@ public class MapContentView implements AssetView{
|
|||||||
ContentType.unit, Icon.units,
|
ContentType.unit, Icon.units,
|
||||||
ContentType.block, Icon.distribution,
|
ContentType.block, Icon.distribution,
|
||||||
ContentType.planet, Icon.planet,
|
ContentType.planet, Icon.planet,
|
||||||
ContentType.weather, Icon.power, //TODO cloud icon
|
ContentType.weather, Icon.drizzle,
|
||||||
ContentType.status, Icon.power
|
ContentType.status, Icon.power
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -44,7 +43,14 @@ public class MapContentView implements AssetView{
|
|||||||
dialog.cont.top().pane(p -> {
|
dialog.cont.top().pane(p -> {
|
||||||
p.top();
|
p.top();
|
||||||
|
|
||||||
for(var warning : content.warnings){
|
var warns = content.warnings;
|
||||||
|
if(content.errored){
|
||||||
|
warns = new Seq<>();
|
||||||
|
warns.add(Iconc.warning + " This content has critical errors, and cannot be used until the errors are fixed.");
|
||||||
|
warns.addAll(content.warnings);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var warning : warns){
|
||||||
p.table(Styles.grayPanel, in -> {
|
p.table(Styles.grayPanel, in -> {
|
||||||
in.add(warning.replaceAll("\t", " "), Styles.monoLabel).grow().wrap();
|
in.add(warning.replaceAll("\t", " "), Styles.monoLabel).grow().wrap();
|
||||||
}).margin(6f).growX().pad(3f).row();
|
}).margin(6f).growX().pad(3f).row();
|
||||||
@@ -80,23 +86,7 @@ public class MapContentView implements AssetView{
|
|||||||
}).size(h);
|
}).size(h);
|
||||||
|
|
||||||
list.button(Icon.export, Styles.graySquarei, Vars.iconMed, () -> {
|
list.button(Icon.export, Styles.graySquarei, Vars.iconMed, () -> {
|
||||||
if(ios){
|
FileChooser.export(content.name, "json", file -> file.writeString(content.data));
|
||||||
try{
|
|
||||||
Fi out = tmpDirectory.child(Strings.getFileName(content.path));
|
|
||||||
out.writeString(content.data);
|
|
||||||
platform.shareFile(out);
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
platform.showFileChooser(false, "properties", out -> {
|
|
||||||
try{
|
|
||||||
out.writeString(content.data);
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).size(h);
|
}).size(h);
|
||||||
|
|
||||||
list.button(Icon.trash, Styles.graySquarei, iconMed, () -> {
|
list.button(Icon.trash, Styles.graySquarei, iconMed, () -> {
|
||||||
@@ -158,11 +148,11 @@ public class MapContentView implements AssetView{
|
|||||||
|
|
||||||
choose.buttons.button("@cancel", Icon.cancel, choose::hide).size(170f, 64f);
|
choose.buttons.button("@cancel", Icon.cancel, choose::hide).size(170f, 64f);
|
||||||
|
|
||||||
choose.buttons.button("@asset.content.import.file", Icon.fileText, () -> platform.showMultiFileChooser(file -> {
|
choose.buttons.button("@asset.content.import.file", Icon.fileText, () -> FileChooser.open("json", "json5", "hjson").submit(file -> {
|
||||||
choose.hide();
|
choose.hide();
|
||||||
addContent(selected[0], nameField.getText(), file.readString());
|
addContent(selected[0], nameField.getText(), file.readString());
|
||||||
diag.rebuild();
|
diag.rebuild();
|
||||||
}, "json", "hjson", "json5")).size(200f, 64f).disabled(b -> nameField.getText().isEmpty() || invalid[0]);
|
})).size(200f, 64f).disabled(b -> nameField.getText().isEmpty() || invalid[0]);
|
||||||
|
|
||||||
choose.buttons.button("@asset.content.import.clipboard", Icon.copy, () -> {
|
choose.buttons.button("@asset.content.import.clipboard", Icon.copy, () -> {
|
||||||
String text = Core.app.getClipboardText();
|
String text = Core.app.getClipboardText();
|
||||||
|
|||||||
@@ -30,40 +30,43 @@ public class MapImagesView implements AssetView{
|
|||||||
public void buildButtons(MapAssetsDialog diag, Table buttons){
|
public void buildButtons(MapAssetsDialog diag, Table buttons){
|
||||||
|
|
||||||
buttons.button("@add", Icon.add, () -> {
|
buttons.button("@add", Icon.add, () -> {
|
||||||
platform.showFileChooser(true, "png", result -> {
|
FileChooser.open("png", "jpeg", "jpg").submitMulti(results -> {
|
||||||
try{
|
var images = getImages();
|
||||||
Pixmap pix = new Pixmap(result);
|
|
||||||
int width = pix.width;
|
|
||||||
int height = pix.height;
|
|
||||||
Pixmaps.bleed(pix);
|
|
||||||
byte[] bytes = PixmapIO.writePngBytes(pix);
|
|
||||||
pix.dispose();
|
|
||||||
|
|
||||||
if(width > DataPatcher.maxImageSize || height > DataPatcher.maxImageSize){
|
for(var result : results){
|
||||||
ui.showErrorMessage(Core.bundle.format("asset.image.toolarge", width, height, DataPatcher.maxImageSize, DataPatcher.maxImageSize));
|
try{
|
||||||
return;
|
Pixmap pix = new Pixmap(result);
|
||||||
|
int width = pix.width;
|
||||||
|
int height = pix.height;
|
||||||
|
Pixmaps.bleed(pix);
|
||||||
|
byte[] bytes = PixmapIO.writePngBytes(pix);
|
||||||
|
pix.dispose();
|
||||||
|
|
||||||
|
if(width > DataPatcher.maxImageSize || height > DataPatcher.maxImageSize){
|
||||||
|
ui.showErrorMessage(Core.bundle.format("asset.image.toolarge", width, height, DataPatcher.maxImageSize, DataPatcher.maxImageSize));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//path and name are the same here; there's no path context.
|
||||||
|
String name = result.nameWithoutExtension();
|
||||||
|
String path = name;
|
||||||
|
var other = images.find(p -> (p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name)));
|
||||||
|
if(other != null){
|
||||||
|
ui.showErrorMessage(Core.bundle.format("asset.image.exists", other.name + " (" + other.path + ")"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] hash = assetCache.add(bytes);
|
||||||
|
|
||||||
|
images.add(new ImageAsset(path, hash));
|
||||||
|
}catch(Exception e){
|
||||||
|
ui.showException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
var images = getImages();
|
|
||||||
|
|
||||||
//path and name are the same here; there's no path context.
|
|
||||||
String name = result.nameWithoutExtension();
|
|
||||||
String path = name;
|
|
||||||
var other = images.find(p -> (p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name)));
|
|
||||||
if(other != null){
|
|
||||||
ui.showErrorMessage(Core.bundle.format("asset.image.exists", other.name + " (" + other.path + ")"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] hash = assetCache.add(bytes);
|
|
||||||
|
|
||||||
images.add(new ImageAsset(path, hash));
|
|
||||||
images.sort();
|
|
||||||
state.data.reloadImages(images);
|
|
||||||
diag.rebuild();
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
images.sort();
|
||||||
|
state.data.reloadImages(images);
|
||||||
|
diag.rebuild();
|
||||||
});
|
});
|
||||||
}).size(190f, 64f);
|
}).size(190f, 64f);
|
||||||
|
|
||||||
@@ -74,7 +77,7 @@ public class MapImagesView implements AssetView{
|
|||||||
p.table(Tex.button, t -> {
|
p.table(Tex.button, t -> {
|
||||||
TextButtonStyle style = Styles.flatt;
|
TextButtonStyle style = Styles.flatt;
|
||||||
t.defaults().size(280f, 60f).left();
|
t.defaults().size(280f, 60f).left();
|
||||||
t.button("@asset.image.importzip", Icon.download, style, () -> platform.showFileChooser(true, "zip", file -> {
|
t.button("@asset.image.importzip", Icon.download, style, () -> FileChooser.open("zip").submit(file -> {
|
||||||
|
|
||||||
//Android doesn't allow accessing the file contents outside the callback; other platforms either copy it already, or don't have dumb permission issues
|
//Android doesn't allow accessing the file contents outside the callback; other platforms either copy it already, or don't have dumb permission issues
|
||||||
Fi targetFile;
|
Fi targetFile;
|
||||||
@@ -171,21 +174,17 @@ public class MapImagesView implements AssetView{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
})).marginLeft(12f).row();
|
})).marginLeft(12f).row();
|
||||||
t.button("@asset.image.exportzip", Icon.upload, style, () -> platform.showFileChooser(false, "zip", file -> {
|
t.button("@asset.image.exportzip", Icon.upload, style, () -> FileChooser.export("images", "zip", file -> {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
try{
|
try(OutputStream fos = file.write(false, 4096); ZipOutputStream zos = new ZipOutputStream(fos)){
|
||||||
try(OutputStream fos = file.write(false, 4096); ZipOutputStream zos = new ZipOutputStream(fos)){
|
for(var image : getImages()){
|
||||||
for(var image : getImages()){
|
Fi cacheFile = image.getCacheFile();
|
||||||
Fi cacheFile = image.getCacheFile();
|
if(cacheFile == null) continue;
|
||||||
if(cacheFile == null) continue;
|
|
||||||
|
|
||||||
zos.putNextEntry(new ZipEntry(image.path + ".png"));
|
zos.putNextEntry(new ZipEntry(image.path + ".png"));
|
||||||
zos.write(cacheFile.readBytes());
|
zos.write(cacheFile.readBytes());
|
||||||
zos.closeEntry();
|
zos.closeEntry();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}catch(Throwable e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
}
|
||||||
})).marginLeft(12f).row();
|
})).marginLeft(12f).row();
|
||||||
t.button("@asset.image.clearall", Icon.trash, style, () -> {
|
t.button("@asset.image.clearall", Icon.trash, style, () -> {
|
||||||
@@ -279,13 +278,7 @@ public class MapImagesView implements AssetView{
|
|||||||
});
|
});
|
||||||
b.button(Icon.info, istyle, showInfo);
|
b.button(Icon.info, istyle, showInfo);
|
||||||
b.button(Icon.export, istyle, () -> {
|
b.button(Icon.export, istyle, () -> {
|
||||||
platform.showFileChooser(false, "png", out -> {
|
FileChooser.export(image.name, "png", out -> image.getCacheFileNoNull().copyTo(out));
|
||||||
try{
|
|
||||||
image.getCacheFileNoNull().copyTo(out);
|
|
||||||
}catch(Throwable e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
b.button(Icon.trash, istyle, () -> {
|
b.button(Icon.trash, istyle, () -> {
|
||||||
ui.showConfirm("@asset.image.delete.confirm", () -> {
|
ui.showConfirm("@asset.image.delete.confirm", () -> {
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ public class MapPatchesView implements AssetView{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void buildButtons(MapAssetsDialog diag, Table buttons){
|
public void buildButtons(MapAssetsDialog diag, Table buttons){
|
||||||
buttons.button("@add", Icon.add, () -> showImport(this::addPatch)).size(190f, 64f);
|
buttons.button("@add", Icon.add, () -> showImport(s -> {
|
||||||
|
addPatch(s);
|
||||||
|
diag.rebuild();
|
||||||
|
})).size(190f, 64f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -106,10 +109,11 @@ public class MapPatchesView implements AssetView{
|
|||||||
handler.get(Core.app.getClipboardText());
|
handler.get(Core.app.getClipboardText());
|
||||||
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null);
|
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null);
|
||||||
t.row();
|
t.row();
|
||||||
t.button("@schematic.importfile", Icon.download, style, () -> platform.showMultiFileChooser(file -> {
|
//currently doesn't allow multi import because it could be replacing exactly 1 patch
|
||||||
|
t.button("@schematic.importfile", Icon.download, style, () -> FileChooser.open("json", "hjson", "json5").submit(file -> {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
handler.get(file.readString());
|
handler.get(file.readString());
|
||||||
}, "json", "hjson", "json5")).marginLeft(12f);
|
})).marginLeft(12f);
|
||||||
t.row();
|
t.row();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import java.util.*;
|
|||||||
/** The current implementation is awful. Consider it a proof of concept. */
|
/** The current implementation is awful. Consider it a proof of concept. */
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class DataPatcher{
|
public class DataPatcher{
|
||||||
public static final int maxImageSize = 1024;
|
public static final int maxImageSize = 2000;
|
||||||
public static final int patchFormatVersion = 2;
|
public static final int patchFormatVersion = 2;
|
||||||
|
|
||||||
private static boolean needsArrayFix = false;
|
private static boolean needsArrayFix = false;
|
||||||
@@ -109,7 +109,9 @@ public class DataPatcher{
|
|||||||
set.error = false;
|
set.error = false;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
JsonValue value = parser.getJson().fromJson(null, Jval.read(set.patch).toString(Jformat.plain));
|
Object someValue = parser.getJson().fromJson(null, Jval.read(set.patch).toString(Jformat.plain));
|
||||||
|
if(!(someValue instanceof JsonValue value)) throw new SerializationException("Patch must be a JSON object.");
|
||||||
|
|
||||||
if(Vars.state.rules.planet != null && value.has("requiredPlanets")){
|
if(Vars.state.rules.planet != null && value.has("requiredPlanets")){
|
||||||
JsonValue req = value.get("requiredPlanets");
|
JsonValue req = value.get("requiredPlanets");
|
||||||
value.remove("requiredPlanets");
|
value.remove("requiredPlanets");
|
||||||
@@ -136,6 +138,7 @@ public class DataPatcher{
|
|||||||
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
set.error = true;
|
set.error = true;
|
||||||
|
set.name = "";
|
||||||
set.warnings.add(Strings.getSimpleMessage(e));
|
set.warnings.add(Strings.getSimpleMessage(e));
|
||||||
currentlyApplyingPatch = null;
|
currentlyApplyingPatch = null;
|
||||||
|
|
||||||
@@ -146,7 +149,6 @@ public class DataPatcher{
|
|||||||
if(!content.isEmpty()){
|
if(!content.isEmpty()){
|
||||||
content.sort();
|
content.sort();
|
||||||
|
|
||||||
ContentLoader preLoad = Vars.content.copy();
|
|
||||||
dpMod.erroredContent.clear();
|
dpMod.erroredContent.clear();
|
||||||
|
|
||||||
for(var asset : content){
|
for(var asset : content){
|
||||||
@@ -169,7 +171,6 @@ public class DataPatcher{
|
|||||||
needsArrayFix = true;
|
needsArrayFix = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: what to do when errors happen in general?
|
|
||||||
try{
|
try{
|
||||||
//this binds the content but does not load it entirely
|
//this binds the content but does not load it entirely
|
||||||
asset.content = parser.parse(dpMod, asset.name, asset.data, file, asset.type);
|
asset.content = parser.parse(dpMod, asset.name, asset.data, file, asset.type);
|
||||||
@@ -178,10 +179,11 @@ public class DataPatcher{
|
|||||||
asset.warnings.add(Strings.getFinalMessage(e));
|
asset.warnings.add(Strings.getFinalMessage(e));
|
||||||
asset.errored = true;
|
asset.errored = true;
|
||||||
|
|
||||||
//TODO: this warning is not very useful, nor is markError in general. What should be done here?
|
var lastAdded = Vars.content.getLastAdded();
|
||||||
if(current != Vars.content.getLastAdded() && Vars.content.getLastAdded() != null){
|
if(current != lastAdded && lastAdded != null){
|
||||||
|
Vars.content.remove(lastAdded);
|
||||||
//markError should log it already
|
//markError should log it already
|
||||||
parser.markError(Vars.content.getLastAdded(), dpMod, file, e);
|
parser.markError(lastAdded, dpMod, file, e);
|
||||||
}else{
|
}else{
|
||||||
Log.err("Error loading content: " + asset.path, e);
|
Log.err("Error loading content: " + asset.path, e);
|
||||||
}
|
}
|
||||||
@@ -207,6 +209,7 @@ public class DataPatcher{
|
|||||||
try{
|
try{
|
||||||
cont.init();
|
cont.init();
|
||||||
}catch(Throwable t){
|
}catch(Throwable t){
|
||||||
|
Vars.content.remove(cont);
|
||||||
if(cont.minfo.asset != null) cont.minfo.asset.errored = true;
|
if(cont.minfo.asset != null) cont.minfo.asset.errored = true;
|
||||||
parser.markError(cont, dpMod, cont.minfo.sourceFile, t);
|
parser.markError(cont, dpMod, cont.minfo.sourceFile, t);
|
||||||
}
|
}
|
||||||
@@ -216,6 +219,7 @@ public class DataPatcher{
|
|||||||
try{
|
try{
|
||||||
cont.postInit();
|
cont.postInit();
|
||||||
}catch(Throwable t){
|
}catch(Throwable t){
|
||||||
|
Vars.content.remove(cont);
|
||||||
if(cont.minfo.asset != null) cont.minfo.asset.errored = true;
|
if(cont.minfo.asset != null) cont.minfo.asset.errored = true;
|
||||||
parser.markError(cont, dpMod, cont.minfo.sourceFile, t);
|
parser.markError(cont, dpMod, cont.minfo.sourceFile, t);
|
||||||
}
|
}
|
||||||
@@ -227,24 +231,18 @@ public class DataPatcher{
|
|||||||
cont.loadIcon();
|
cont.loadIcon();
|
||||||
cont.load();
|
cont.load();
|
||||||
if(cont.minfo.asset != null && cont instanceof UnlockableContent u){
|
if(cont.minfo.asset != null && cont instanceof UnlockableContent u){
|
||||||
if(!u.uiIcon.found()){
|
if(!u.uiIcon.found() && u.getContentType() != ContentType.planet && u.getContentType() != ContentType.weather){
|
||||||
cont.minfo.asset.warnings.add("[" + u.name.substring(u.minfo.mod.name.length() + 1) + "] Could not find an icon. Ensure that you have an image named '" + u.name + "' loaded. Remember that imported images always have the 'dp-' prefix automatically applied.");
|
cont.minfo.asset.warnings.add("[" + u.name.substring(u.minfo.mod.name.length() + 1) + "] Could not find an icon. Ensure that you have an image named '" + u.name + "' loaded. Remember that imported images always have the 'dp-' prefix automatically applied.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}catch(Throwable t){
|
}catch(Throwable t){
|
||||||
|
Vars.content.remove(cont);
|
||||||
if(cont.minfo.asset != null) cont.minfo.asset.errored = true;
|
if(cont.minfo.asset != null) cont.minfo.asset.errored = true;
|
||||||
parser.markError(cont, dpMod, cont.minfo.sourceFile, t);
|
parser.markError(cont, dpMod, cont.minfo.sourceFile, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(all.contains(Content::hasErrored)){
|
|
||||||
Log.err("Errors were encountered loading content. Content will be unloaded.");
|
|
||||||
callContentRemove();
|
|
||||||
Vars.content = preLoad;
|
|
||||||
needsArrayFix = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(reloadContentWorld) fixContentArrays();
|
if(reloadContentWorld) fixContentArrays();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import mindustry.ctype.*;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
public class ContentAsset extends DataAsset{
|
public class ContentAsset extends DataAsset{
|
||||||
//Note: sectors can't be loaded at the moment
|
//Note: sectors and planets can't be loaded at the moment, as custom campaigns aren't functional to begin with, and adding them would cause confusion
|
||||||
public static final ContentType[] loadableContent = {ContentType.item, ContentType.block, ContentType.liquid, ContentType.status, ContentType.unit, ContentType.weather, ContentType.planet};
|
public static final ContentType[] loadableContent = {ContentType.item, ContentType.block, ContentType.liquid, ContentType.status, ContentType.unit, ContentType.weather};
|
||||||
|
|
||||||
/** Content type to be parsed as. */
|
/** Content type to be parsed as. */
|
||||||
public ContentType type = ContentType.unit;
|
public ContentType type = ContentType.unit;
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package mindustry.ui;
|
||||||
|
|
||||||
|
import arc.*;
|
||||||
|
import arc.files.*;
|
||||||
|
import arc.func.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import mindustry.*;
|
||||||
|
import mindustry.core.Platform.*;
|
||||||
|
import mindustry.ui.dialogs.*;
|
||||||
|
|
||||||
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
|
public class FileChooser{
|
||||||
|
|
||||||
|
public static void export(String name, String extension, FileWriter writer){
|
||||||
|
if(!ios){
|
||||||
|
FileChooser.save(extension).name(name).submit(file -> {
|
||||||
|
ui.loadAnd(() -> {
|
||||||
|
try{
|
||||||
|
writer.write(file);
|
||||||
|
}catch(Throwable e){
|
||||||
|
ui.showException(e);
|
||||||
|
Log.err(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
ui.loadAnd(() -> {
|
||||||
|
try{
|
||||||
|
Fi result = Core.files.local(name + "." + extension);
|
||||||
|
writer.write(result);
|
||||||
|
platform.shareFile(result);
|
||||||
|
}catch(Throwable e){
|
||||||
|
ui.showException(e);
|
||||||
|
Log.err(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileChooserParams open(String... extensions){
|
||||||
|
return new FileChooserParams().open(true).extensions(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileChooserParams save(String... extensions){
|
||||||
|
return new FileChooserParams().open(false).extensions(extensions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FileChooserDialog createFallbackFileChooser(FileChooserParams params){
|
||||||
|
return new FileChooserDialog(params.title, file -> Structs.contains(params.extensions, file::extEquals), params.open, file -> {
|
||||||
|
if(!params.open){
|
||||||
|
params.handleChooseResult(file.parent().child(file.nameWithoutExtension() + "." + params.extensions[0]));
|
||||||
|
}else{
|
||||||
|
params.handleChooseResult(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showFallbackFileChooser(FileChooserParams params){
|
||||||
|
createFallbackFileChooser(params).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FileChooserParams{
|
||||||
|
public boolean open;
|
||||||
|
public boolean allowMultiple;
|
||||||
|
public String title;
|
||||||
|
public String fileName;
|
||||||
|
public String[] extensions;
|
||||||
|
public @Nullable Cons<Fi> handler;
|
||||||
|
public @Nullable Cons<Fi[]> multipleHandler;
|
||||||
|
|
||||||
|
private void checkParams(){
|
||||||
|
if(extensions == null || extensions.length == 0) throw new IllegalArgumentException("Extension types must be defined.");
|
||||||
|
|
||||||
|
if(title == null){
|
||||||
|
title = open ? Core.bundle.get("open") : Core.bundle.get("save");
|
||||||
|
}else if(title.startsWith("@")){
|
||||||
|
title = Core.bundle.get(title.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fileName == null){
|
||||||
|
fileName = "file." + extensions[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Submits the request to handle a single file. */
|
||||||
|
public void submit(Cons<Fi> handler){
|
||||||
|
checkParams();
|
||||||
|
this.handler = handler;
|
||||||
|
Vars.platform.showFileChooser(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Submits the request to handle multiple files. */
|
||||||
|
public void submitMulti(Cons<Fi[]> multipleHandler){
|
||||||
|
checkParams();
|
||||||
|
if(!open) throw new IllegalArgumentException("Saving in a file chooser with multiple choices does not make sense.");
|
||||||
|
|
||||||
|
this.multipleHandler = multipleHandler;
|
||||||
|
this.allowMultiple = true;
|
||||||
|
Vars.platform.showFileChooser(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Submits the request to handle multiple files as a loop that iterates through the resulting files and invokes the callback repeatedly. */
|
||||||
|
public void submitMultiLoop(Cons<Fi> multipleHandler){
|
||||||
|
submitMulti(files -> {
|
||||||
|
for(var f : files){
|
||||||
|
multipleHandler.get(f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileChooserParams name(String fileName){
|
||||||
|
this.fileName = fileName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileChooserParams extensions(String... extensions){
|
||||||
|
this.extensions = extensions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileChooserParams open(boolean open){
|
||||||
|
this.open = open;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileChooserParams title(String title){
|
||||||
|
this.title = title;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleChooseResult(Fi... files){
|
||||||
|
if(files.length == 0) return;
|
||||||
|
|
||||||
|
if(handler != null){
|
||||||
|
handler.get(files[0]);
|
||||||
|
}else if(multipleHandler != null){
|
||||||
|
multipleHandler.get(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,9 +43,9 @@ public class CanvasEditDialog extends BaseDialog{
|
|||||||
|
|
||||||
addCloseButton(160f);
|
addCloseButton(160f);
|
||||||
|
|
||||||
buttons.button("@import", Icon.image, () -> platform.showFileChooser(true, "png", this::importFrom));
|
buttons.button("@import", Icon.image, () -> FileChooser.open("png", "jpeg").submit(this::importFrom));
|
||||||
|
|
||||||
buttons.button("@export", Icon.export, () -> platform.showFileChooser(false, "png", this::exportTo));
|
buttons.button("@export", Icon.export, () -> FileChooser.export("canvas", "png", this::exportTo));
|
||||||
|
|
||||||
hidden(() -> {
|
hidden(() -> {
|
||||||
save();
|
save();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mindustry.ui.dialogs;
|
package mindustry.ui.dialogs;
|
||||||
|
|
||||||
import arc.*;
|
import arc.*;
|
||||||
|
import arc.files.*;
|
||||||
import arc.graphics.*;
|
import arc.graphics.*;
|
||||||
import arc.scene.ui.*;
|
import arc.scene.ui.*;
|
||||||
import arc.scene.ui.layout.*;
|
import arc.scene.ui.layout.*;
|
||||||
@@ -40,48 +41,49 @@ public class EditorMapsDialog extends MapListDialog{
|
|||||||
}).size(210f, 64f);
|
}).size(210f, 64f);
|
||||||
|
|
||||||
buttons.button("@editor.importmap", Icon.upload, () -> {
|
buttons.button("@editor.importmap", Icon.upload, () -> {
|
||||||
platform.showFileChooser(true, mapExtension, file -> {
|
FileChooser.open(mapExtension).submitMulti(files -> {
|
||||||
ui.loadAnd(() -> {
|
ui.loadAnd(() -> {
|
||||||
maps.tryCatchMapError(() -> {
|
for(Fi file : files){
|
||||||
if(MapIO.isImage(file)){
|
maps.tryCatchMapError(() -> {
|
||||||
ui.showErrorMessage("@editor.errorimage");
|
if(MapIO.isImage(file)){
|
||||||
return;
|
ui.showErrorMessage("@editor.errorimage");
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Map map = MapIO.createMap(file, true);
|
Map map = MapIO.createMap(file, true);
|
||||||
|
|
||||||
//when you attempt to import a save, it will have no name, so generate one
|
//when you attempt to import a save, it will have no name, so generate one
|
||||||
String name = map.tags.get("name", () -> {
|
String name = map.tags.get("name", () -> {
|
||||||
String result = "unknown";
|
String result = "unknown";
|
||||||
int number = 0;
|
int number = 0;
|
||||||
while(maps.byName(result + number++) != null) ;
|
while(maps.byName(result + number++) != null) ;
|
||||||
return result + number;
|
return result + number;
|
||||||
});
|
|
||||||
|
|
||||||
//this will never actually get called, but it remains just in case
|
|
||||||
if(name == null){
|
|
||||||
ui.showErrorMessage("@editor.errorname");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map conflict = maps.all().find(m -> m.name().equalsIgnoreCase(name));
|
|
||||||
|
|
||||||
if(conflict != null && !conflict.custom){
|
|
||||||
ui.showInfo(Core.bundle.format("editor.import.exists", name));
|
|
||||||
}else if(conflict != null){
|
|
||||||
ui.showConfirm("@confirm", Core.bundle.format("editor.overwrite.confirm", map.name()), () -> {
|
|
||||||
maps.tryCatchMapError(() -> {
|
|
||||||
maps.removeMap(conflict);
|
|
||||||
maps.importMap(map.file);
|
|
||||||
setup();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}else{
|
|
||||||
maps.importMap(map.file);
|
|
||||||
setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
//this will never actually get called, but it remains just in case
|
||||||
|
if(name == null){
|
||||||
|
ui.showErrorMessage("@editor.errorname");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map conflict = maps.all().find(m -> m.name().equalsIgnoreCase(name));
|
||||||
|
|
||||||
|
if(conflict != null && !conflict.custom){
|
||||||
|
ui.showInfo(Core.bundle.format("editor.import.exists", name));
|
||||||
|
}else if(conflict != null){
|
||||||
|
ui.showConfirm("@confirm", Core.bundle.format("editor.overwrite.confirm", map.name()), () -> {
|
||||||
|
maps.tryCatchMapError(() -> {
|
||||||
|
maps.removeMap(conflict);
|
||||||
|
maps.importMap(map.file);
|
||||||
|
setup();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
maps.importMap(map.file);
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).size(210f, 64f);
|
}).size(210f, 64f);
|
||||||
|
|||||||
+2
-2
@@ -16,7 +16,7 @@ import mindustry.ui.*;
|
|||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class FileChooser extends BaseDialog{
|
public class FileChooserDialog extends BaseDialog{
|
||||||
private static final Fi homeDirectory = Core.files.absolute(Core.files.getExternalStoragePath());
|
private static final Fi homeDirectory = Core.files.absolute(Core.files.getExternalStoragePath());
|
||||||
static Fi lastDirectory = Core.files.absolute(Core.settings.getString("lastDirectory", homeDirectory.absolutePath()));
|
static Fi lastDirectory = Core.files.absolute(Core.settings.getString("lastDirectory", homeDirectory.absolutePath()));
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ public class FileChooser extends BaseDialog{
|
|||||||
private Cons<Fi> selectListener;
|
private Cons<Fi> selectListener;
|
||||||
private boolean open;
|
private boolean open;
|
||||||
|
|
||||||
public FileChooser(String title, Boolf<Fi> filter, boolean open, Cons<Fi> result){
|
public FileChooserDialog(String title, Boolf<Fi> filter, boolean open, Cons<Fi> result){
|
||||||
super(title);
|
super(title);
|
||||||
setFillParent(true);
|
setFillParent(true);
|
||||||
this.open = open;
|
this.open = open;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package mindustry.ui.dialogs;
|
package mindustry.ui.dialogs;
|
||||||
|
|
||||||
import arc.*;
|
import arc.*;
|
||||||
|
import arc.files.*;
|
||||||
import arc.graphics.*;
|
import arc.graphics.*;
|
||||||
import arc.graphics.g2d.*;
|
import arc.graphics.g2d.*;
|
||||||
import arc.scene.style.*;
|
import arc.scene.style.*;
|
||||||
@@ -139,7 +140,7 @@ public class LoadDialog extends BaseDialog{
|
|||||||
if(slot.hasExternalAssets() && !slot.isBeingPlayed()){
|
if(slot.hasExternalAssets() && !slot.isBeingPlayed()){
|
||||||
ui.showInfo("@save.export.needsload");
|
ui.showInfo("@save.export.needsload");
|
||||||
}else{
|
}else{
|
||||||
platform.export("save-" + slot.getName(), saveExtension, slot::exportFile);
|
FileChooser.export(slot.getName(), saveExtension, slot::exportFile);
|
||||||
}
|
}
|
||||||
}).right();
|
}).right();
|
||||||
|
|
||||||
@@ -196,24 +197,27 @@ public class LoadDialog extends BaseDialog{
|
|||||||
public void addSetup(){
|
public void addSetup(){
|
||||||
|
|
||||||
buttons.button("@save.import", Icon.add, () -> {
|
buttons.button("@save.import", Icon.add, () -> {
|
||||||
platform.showFileChooser(true, saveExtension, file -> {
|
FileChooser.open("msav").submitMulti(files -> {
|
||||||
if(SaveIO.isSaveValid(file)){
|
for(Fi file : files){
|
||||||
var meta = SaveIO.getMeta(file);
|
if(SaveIO.isSaveValid(file)){
|
||||||
|
var meta = SaveIO.getMeta(file);
|
||||||
|
|
||||||
if(meta.rules.sector != null){
|
if(meta.rules.sector != null){
|
||||||
ui.showErrorMessage("@save.nocampaign");
|
ui.showErrorMessage("@save.nocampaign");
|
||||||
}else{
|
}else{
|
||||||
try{
|
try{
|
||||||
control.saves.importSave(file);
|
control.saves.importSave(file);
|
||||||
rebuild();
|
}catch(Exception e){
|
||||||
}catch(IOException e){
|
Log.err(e);
|
||||||
e.printStackTrace();
|
ui.showException("@save.import.fail", e);
|
||||||
ui.showException("@save.import.fail", e);
|
}
|
||||||
}
|
}
|
||||||
|
}else{
|
||||||
|
ui.showErrorMessage("@save.import.invalid");
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
ui.showErrorMessage("@save.import.invalid");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rebuild();
|
||||||
});
|
});
|
||||||
}).fillX().margin(10f);
|
}).fillX().margin(10f);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,15 +191,18 @@ public class ModsDialog extends BaseDialog{
|
|||||||
t.button("@mod.import.file", Icon.file, bstyle, () -> {
|
t.button("@mod.import.file", Icon.file, bstyle, () -> {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
|
|
||||||
platform.showMultiFileChooser(file -> {
|
FileChooser.open("zip", "jar").submitMulti(files -> {
|
||||||
try{
|
for(var file : files){
|
||||||
mods.importMod(file);
|
try{
|
||||||
setup();
|
mods.importMod(file);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
ui.showException(e.getMessage() != null && e.getMessage().toLowerCase(Locale.ROOT).contains("writable dex") ? "@error.moddex" : "", e);
|
ui.showException(e.getMessage() != null && e.getMessage().toLowerCase(Locale.ROOT).contains("writable dex") ? "@error.moddex" : "", e);
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, "zip", "jar");
|
|
||||||
|
setup();
|
||||||
|
});
|
||||||
}).margin(12f);
|
}).margin(12f);
|
||||||
|
|
||||||
t.row();
|
t.row();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mindustry.ui.dialogs;
|
package mindustry.ui.dialogs;
|
||||||
|
|
||||||
import arc.*;
|
import arc.*;
|
||||||
|
import arc.files.*;
|
||||||
import arc.func.*;
|
import arc.func.*;
|
||||||
import arc.graphics.*;
|
import arc.graphics.*;
|
||||||
import arc.graphics.Texture.*;
|
import arc.graphics.Texture.*;
|
||||||
@@ -241,19 +242,28 @@ public class SchematicsDialog extends BaseDialog{
|
|||||||
}
|
}
|
||||||
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || !Core.app.getClipboardText().startsWith(schematicBaseStart));
|
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || !Core.app.getClipboardText().startsWith(schematicBaseStart));
|
||||||
t.row();
|
t.row();
|
||||||
t.button("@schematic.importfile", Icon.download, style, () -> platform.showFileChooser(true, schematicExtension, file -> {
|
t.button("@schematic.importfile", Icon.download, style, () -> FileChooser.open(schematicExtension).submitMulti(files -> {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
|
|
||||||
try{
|
Schematic last = null;
|
||||||
Schematic s = Schematics.read(file);
|
|
||||||
s.removeSteamID();
|
for(Fi file : files){
|
||||||
schematics.add(s);
|
try{
|
||||||
setup();
|
Schematic s = Schematics.read(file);
|
||||||
showInfo(s);
|
s.removeSteamID();
|
||||||
checkTags(s);
|
schematics.add(s);
|
||||||
}catch(Exception e){
|
checkTags(s);
|
||||||
ui.showException(e);
|
last = s;
|
||||||
|
}catch(Exception e){
|
||||||
|
ui.showException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(last != null){
|
||||||
|
showInfo(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
setup();
|
||||||
})).marginLeft(12f);
|
})).marginLeft(12f);
|
||||||
t.row();
|
t.row();
|
||||||
if(steam){
|
if(steam){
|
||||||
@@ -290,7 +300,7 @@ public class SchematicsDialog extends BaseDialog{
|
|||||||
t.row();
|
t.row();
|
||||||
t.button("@schematic.exportfile", Icon.export, style, () -> {
|
t.button("@schematic.exportfile", Icon.export, style, () -> {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
platform.export(s.name(), schematicExtension, file -> Schematics.write(s, file));
|
FileChooser.export(s.name(), schematicExtension, file -> Schematics.write(s, file));
|
||||||
}).marginLeft(12f);
|
}).marginLeft(12f);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -249,30 +249,12 @@ public class SettingsMenuDialog extends BaseDialog{
|
|||||||
t.row();
|
t.row();
|
||||||
|
|
||||||
t.button("@data.export", Icon.upload, style, () -> {
|
t.button("@data.export", Icon.upload, style, () -> {
|
||||||
if(ios){
|
FileChooser.export("mindustry-data-export", "zip", this::exportData);
|
||||||
Fi file = Core.files.local("mindustry-data-export.zip");
|
|
||||||
try{
|
|
||||||
exportData(file);
|
|
||||||
}catch(Exception e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
platform.shareFile(file);
|
|
||||||
}else{
|
|
||||||
platform.showFileChooser(false, "zip", file -> {
|
|
||||||
try{
|
|
||||||
exportData(file);
|
|
||||||
ui.showInfo("@data.exported");
|
|
||||||
}catch(Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}).marginLeft(4);
|
}).marginLeft(4);
|
||||||
|
|
||||||
t.row();
|
t.row();
|
||||||
|
|
||||||
t.button("@data.import", Icon.download, style, () -> ui.showConfirm("@confirm", "@data.import.confirm", () -> platform.showFileChooser(true, "zip", file -> {
|
t.button("@data.import", Icon.download, style, () -> ui.showConfirm("@confirm", "@data.import.confirm", () -> FileChooser.open("zip").submit(file -> {
|
||||||
try{
|
try{
|
||||||
importData(file);
|
importData(file);
|
||||||
control.saves.resetSave();
|
control.saves.resetSave();
|
||||||
@@ -281,7 +263,7 @@ public class SettingsMenuDialog extends BaseDialog{
|
|||||||
}catch(IllegalArgumentException e){
|
}catch(IllegalArgumentException e){
|
||||||
ui.showErrorMessage("@data.invalid");
|
ui.showErrorMessage("@data.invalid");
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
e.printStackTrace();
|
Log.err(e);
|
||||||
if(e.getMessage() == null || !e.getMessage().contains("too short")){
|
if(e.getMessage() == null || !e.getMessage().contains("too short")){
|
||||||
ui.showException(e);
|
ui.showException(e);
|
||||||
}else{
|
}else{
|
||||||
@@ -301,20 +283,7 @@ public class SettingsMenuDialog extends BaseDialog{
|
|||||||
if(settings.getDataDirectory().child("crashes").list().length == 0 && !settings.getDataDirectory().child("last_log.txt").exists()){
|
if(settings.getDataDirectory().child("crashes").list().length == 0 && !settings.getDataDirectory().child("last_log.txt").exists()){
|
||||||
ui.showInfo("@crash.none");
|
ui.showInfo("@crash.none");
|
||||||
}else{
|
}else{
|
||||||
if(ios){
|
FileChooser.export("logs", "txt", file -> file.writeString(getLogs()));
|
||||||
Fi logs = tmpDirectory.child("logs.txt");
|
|
||||||
logs.writeString(getLogs());
|
|
||||||
platform.shareFile(logs);
|
|
||||||
}else{
|
|
||||||
platform.showFileChooser(false, "txt", file -> {
|
|
||||||
try{
|
|
||||||
file.writeBytes(getLogs().getBytes(Strings.utf8));
|
|
||||||
app.post(() -> ui.showInfo("@crash.exported"));
|
|
||||||
}catch(Throwable e){
|
|
||||||
ui.showException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}).marginLeft(4);
|
}).marginLeft(4);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ public class ConsoleFragment extends Table{
|
|||||||
|
|
||||||
button(Icon.downOpen, Styles.cleari, () -> scrollPos = Mathf.clamp(scrollPos - 1, 0, Math.max(0, messages.size))).disabled(b -> scrollPos <= 0).size(s).padLeft(4f);
|
button(Icon.downOpen, Styles.cleari, () -> scrollPos = Mathf.clamp(scrollPos - 1, 0, Math.max(0, messages.size))).disabled(b -> scrollPos <= 0).size(s).padLeft(4f);
|
||||||
|
|
||||||
button(Icon.fileText, Styles.cleari, () -> platform.showFileChooser(true, "js", file -> {
|
button(Icon.fileText, Styles.cleari, () -> FileChooser.open("js").submit(file -> {
|
||||||
try{
|
try{
|
||||||
mods.getScripts().runConsole(file.readString());
|
mods.getScripts().runConsole(file.readString());
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import arc.discord.*;
|
|||||||
import arc.discord.DiscordRPC.*;
|
import arc.discord.DiscordRPC.*;
|
||||||
import arc.filedialogs.*;
|
import arc.filedialogs.*;
|
||||||
import arc.files.*;
|
import arc.files.*;
|
||||||
import arc.func.*;
|
|
||||||
import arc.math.*;
|
import arc.math.*;
|
||||||
import arc.profiling.*;
|
import arc.profiling.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
@@ -27,6 +26,8 @@ import mindustry.net.*;
|
|||||||
import mindustry.net.Net.*;
|
import mindustry.net.Net.*;
|
||||||
import mindustry.service.*;
|
import mindustry.service.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
|
import mindustry.ui.*;
|
||||||
|
import mindustry.ui.FileChooser.*;
|
||||||
import mindustry.ui.dialogs.*;
|
import mindustry.ui.dialogs.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@@ -327,30 +328,11 @@ public class DesktopLauncher extends ClientLauncher{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showFileChooser(boolean open, String title, String extension, Cons<Fi> cons){
|
public void showFileChooser(FileChooserParams params){
|
||||||
showNativeFileChooser(title, open, cons, extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showMultiFileChooser(Cons<Fi> cons, String... extensions){
|
|
||||||
showNativeFileChooser("@open", true, cons, extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void showNativeFileChooser(String title, boolean open, Cons<Fi> cons, String... shownExtensions){
|
|
||||||
String formatted = (title.startsWith("@") ? Core.bundle.get(title.substring(1)) : title).replaceAll("\"", "'");
|
|
||||||
|
|
||||||
//this should never happen unless someone is being dumb with the parameters
|
|
||||||
String[] ext = shownExtensions == null || shownExtensions.length == 0 ? new String[]{""} : shownExtensions;
|
|
||||||
|
|
||||||
if(OS.isLinux){
|
|
||||||
showZenity(open, formatted, shownExtensions, cons, () -> Platform.defaultFileDialog(open, title, ext[0], cons));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//native file dialog
|
|
||||||
Threads.daemon(() -> {
|
Threads.daemon(() -> {
|
||||||
try{
|
try{
|
||||||
FileDialogs.loadNatives();
|
FileDialogs.loadNatives();
|
||||||
|
var ext = params.extensions;
|
||||||
|
|
||||||
String result;
|
String result;
|
||||||
String[] patterns = new String[ext.length];
|
String[] patterns = new String[ext.length];
|
||||||
@@ -359,14 +341,14 @@ public class DesktopLauncher extends ClientLauncher{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//on MacOS, .msav is not properly recognized until I put garbage into the array?
|
//on MacOS, .msav is not properly recognized until I put garbage into the array?
|
||||||
if(patterns.length == 1 && OS.isMac && open){
|
if(patterns.length == 1 && OS.isMac && params.open){
|
||||||
patterns = new String[]{"", "*." + ext[0]};
|
patterns = new String[]{"", "*." + ext[0]};
|
||||||
}
|
}
|
||||||
|
|
||||||
if(open){
|
if(params.open){
|
||||||
result = FileDialogs.openFileDialog(formatted, FileChooser.getLastDirectory().absolutePath(), patterns, "." + ext[0] + " files", false);
|
result = FileDialogs.openFileDialog(params.title, FileChooserDialog.getLastDirectory().absolutePath() + "/", patterns, "." + ext[0] + " files", params.allowMultiple);
|
||||||
}else{
|
}else{
|
||||||
result = FileDialogs.saveFileDialog(formatted, FileChooser.getLastDirectory().child("file." + ext[0]).absolutePath(), patterns, "." + ext[0] + " files");
|
result = FileDialogs.saveFileDialog(params.title, FileChooserDialog.getLastDirectory().child(params.fileName).absolutePath(), patterns, "." + ext[0] + " files");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result == null) return;
|
if(result == null) return;
|
||||||
@@ -378,86 +360,32 @@ public class DesktopLauncher extends ClientLauncher{
|
|||||||
//cancelled selection, ignore result
|
//cancelled selection, ignore result
|
||||||
if(result.isEmpty() || result.equals("\n")) return;
|
if(result.isEmpty() || result.equals("\n")) return;
|
||||||
if(result.endsWith("\n")) result = result.substring(0, result.length() - 1);
|
if(result.endsWith("\n")) result = result.substring(0, result.length() - 1);
|
||||||
if(result.contains("\n")) throw new IOException("invalid input: \"" + result + "\"");
|
|
||||||
|
|
||||||
Fi file = Core.files.absolute(result);
|
Fi[] resultFiles = Seq.with(result.split("\\|")).map(Core.files::absolute).toArray(Fi.class);
|
||||||
|
|
||||||
|
if(result.isEmpty()) return;
|
||||||
|
|
||||||
Core.app.post(() -> {
|
Core.app.post(() -> {
|
||||||
FileChooser.setLastDirectory(file.isDirectory() ? file : file.parent());
|
FileChooserDialog.setLastDirectory(resultFiles[0].isDirectory() ? resultFiles[0] : resultFiles[0].parent());
|
||||||
|
|
||||||
if(!open){
|
if(!params.open){
|
||||||
cons.get(file.parent().child(file.nameWithoutExtension() + "." + ext[0]));
|
Fi single = resultFiles[0];
|
||||||
|
//fix extension to match filters
|
||||||
|
if(!Structs.contains(params.extensions, single::extEquals)){
|
||||||
|
single = single.parent().child(single.nameWithoutExtension() + "." + ext[0]);
|
||||||
|
}
|
||||||
|
params.handleChooseResult(single);
|
||||||
}else{
|
}else{
|
||||||
cons.get(file);
|
params.handleChooseResult(resultFiles);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}catch(Throwable error){
|
}catch(Throwable error){
|
||||||
Log.err("Failure to execute native file chooser", error);
|
Log.err("Failed to execute native file chooser", error);
|
||||||
Core.app.post(() -> {
|
Core.app.post(() -> FileChooser.showFallbackFileChooser(params));
|
||||||
if(ext.length > 1){
|
|
||||||
showMultiFileChooser(cons, ext);
|
|
||||||
}else{
|
|
||||||
Platform.defaultFileDialog(open, formatted, ext[0], cons);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** attempt to use the native file picker with zenity, or runs the fallback Runnable if the operation fails */
|
|
||||||
static void showZenity(boolean open, String title, String[] extensions, Cons<Fi> cons, Runnable fallback){
|
|
||||||
Threads.daemon(() -> {
|
|
||||||
try{
|
|
||||||
String formatted = (title.startsWith("@") ? Core.bundle.get(title.substring(1)) : title).replaceAll("\"", "'");
|
|
||||||
|
|
||||||
String last = FileChooser.getLastDirectory().absolutePath();
|
|
||||||
if(!last.endsWith("/")) last += "/";
|
|
||||||
|
|
||||||
//zenity doesn't support filtering by extension
|
|
||||||
Seq<String> args = Seq.with("zenity",
|
|
||||||
"--file-selection",
|
|
||||||
"--title=" + formatted,
|
|
||||||
"--filename=" + last,
|
|
||||||
"--confirm-overwrite",
|
|
||||||
"--file-filter=" + Seq.with(extensions).toString(" ", s -> "*." + s),
|
|
||||||
"--file-filter=All files | *" //allow anything if the user wants
|
|
||||||
);
|
|
||||||
|
|
||||||
if(!open){
|
|
||||||
args.add("--save");
|
|
||||||
}
|
|
||||||
|
|
||||||
String result = OS.exec(args.toArray(String.class));
|
|
||||||
//first line.
|
|
||||||
if(result.length() > 1 && result.contains("\n")){
|
|
||||||
result = result.split("\n")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
//cancelled selection, ignore result
|
|
||||||
if(result.isEmpty() || result.equals("\n")) return;
|
|
||||||
|
|
||||||
if(result.endsWith("\n")) result = result.substring(0, result.length() - 1);
|
|
||||||
if(result.contains("\n")) throw new IOException("invalid input: \"" + result + "\"");
|
|
||||||
|
|
||||||
Fi file = Core.files.absolute(result);
|
|
||||||
Core.app.post(() -> {
|
|
||||||
FileChooser.setLastDirectory(file.isDirectory() ? file : file.parent());
|
|
||||||
|
|
||||||
if(!open){
|
|
||||||
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extensions[0]));
|
|
||||||
}else{
|
|
||||||
cons.get(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}catch(Exception e){
|
|
||||||
Log.err(e);
|
|
||||||
Log.warn("zenity not found, using non-native file dialog. Consider installing `zenity` for native file dialogs.");
|
|
||||||
Core.app.post(fallback);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Seq<Fi> getWorkshopContent(Class<? extends Publishable> type){
|
public Seq<Fi> getWorkshopContent(Class<? extends Publishable> type){
|
||||||
return !steam ? super.getWorkshopContent(type) : SVars.workshop.getWorkshopFiles(type);
|
return !steam ? super.getWorkshopContent(type) : SVars.workshop.getWorkshopFiles(type);
|
||||||
|
|||||||
+1
-1
@@ -26,4 +26,4 @@ org.gradle.caching=true
|
|||||||
org.gradle.internal.http.socketTimeout=100000
|
org.gradle.internal.http.socketTimeout=100000
|
||||||
org.gradle.internal.http.connectionTimeout=100000
|
org.gradle.internal.http.connectionTimeout=100000
|
||||||
android.enableR8.fullMode=false
|
android.enableR8.fullMode=false
|
||||||
archash=aaa6d755e7
|
archash=62887bdcae
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import arc.*;
|
|||||||
import arc.Input.*;
|
import arc.Input.*;
|
||||||
import arc.backend.robovm.*;
|
import arc.backend.robovm.*;
|
||||||
import arc.files.*;
|
import arc.files.*;
|
||||||
import arc.func.*;
|
|
||||||
import arc.scene.ui.layout.*;
|
import arc.scene.ui.layout.*;
|
||||||
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import arc.util.io.*;
|
import arc.util.io.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
@@ -13,6 +13,7 @@ import mindustry.game.EventType.*;
|
|||||||
import mindustry.game.Saves.*;
|
import mindustry.game.Saves.*;
|
||||||
import mindustry.io.*;
|
import mindustry.io.*;
|
||||||
import mindustry.net.*;
|
import mindustry.net.*;
|
||||||
|
import mindustry.ui.FileChooser.*;
|
||||||
import mindustry.ui.*;
|
import mindustry.ui.*;
|
||||||
import org.robovm.apple.coregraphics.*;
|
import org.robovm.apple.coregraphics.*;
|
||||||
import org.robovm.apple.foundation.*;
|
import org.robovm.apple.foundation.*;
|
||||||
@@ -44,16 +45,16 @@ public class IOSLauncher extends IOSApplication.Delegate{
|
|||||||
return new IOSApplication(new ClientLauncher(){
|
return new IOSApplication(new ClientLauncher(){
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showFileChooser(boolean open, String titleIgn, String extension, Cons<Fi> cons){
|
public void showFileChooser(FileChooserParams params){
|
||||||
if(!open){ //when exporting, just share it.
|
if(!params.open){ //when exporting, just share it.
|
||||||
//ask for export name
|
//ask for export name
|
||||||
Core.input.getTextInput(new TextInput(){{
|
Core.input.getTextInput(new TextInput(){{
|
||||||
title = Core.bundle.get("filename");
|
title = Core.bundle.get("filename");
|
||||||
accepted = name -> {
|
accepted = name -> {
|
||||||
try{
|
try{
|
||||||
//write result
|
//write result
|
||||||
Fi result = tmpDirectory.child(name + "." + extension);
|
Fi result = tmpDirectory.child(name + "." + params.extensions[0]);
|
||||||
cons.get(result);
|
params.handleChooseResult(result);
|
||||||
|
|
||||||
//import the document
|
//import the document
|
||||||
shareFile(result);
|
shareFile(result);
|
||||||
@@ -80,33 +81,35 @@ public class IOSLauncher extends IOSApplication.Delegate{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void didPickDocumentsAtURLs(UIDocumentBrowserViewController controller, NSArray<NSURL> documentURLs){
|
public void didPickDocumentsAtURLs(UIDocumentBrowserViewController controller, NSArray<NSURL> documentURLs){
|
||||||
if(documentURLs.size() < 1) return;
|
if(documentURLs.isEmpty()) return;
|
||||||
|
|
||||||
NSURL url = documentURLs.first();
|
Seq<Fi> results = new Seq<>();
|
||||||
NSFileCoordinator coord = new NSFileCoordinator(null);
|
NSFileCoordinator coord = new NSFileCoordinator(null);
|
||||||
url.startAccessingSecurityScopedResource();
|
for(NSURL url : documentURLs){
|
||||||
try{
|
url.startAccessingSecurityScopedResource();
|
||||||
coord.coordinateReadingItem(url, NSFileCoordinatorReadingOptions.ForUploading, result -> {
|
try{
|
||||||
|
coord.coordinateReadingItem(url, NSFileCoordinatorReadingOptions.ForUploading, result -> {
|
||||||
Fi src = Core.files.absolute(result.getAbsoluteURL().getPath());
|
Fi src = Core.files.absolute(result.getAbsoluteURL().getPath());
|
||||||
Fi dst = Core.files.absolute(getDocumentsDirectory()).child(src.name());
|
Fi dst = Core.files.absolute(getDocumentsDirectory()).child(src.name());
|
||||||
src.copyTo(dst);
|
src.copyTo(dst);
|
||||||
|
results.add(dst);
|
||||||
Core.app.post(() -> {
|
|
||||||
try{
|
|
||||||
cons.get(dst);
|
|
||||||
}catch(Throwable t){
|
|
||||||
ui.showException(t);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
}catch(Throwable e){
|
||||||
}catch(Throwable e){
|
ui.showException(e);
|
||||||
ui.showException(e);
|
}
|
||||||
|
|
||||||
|
url.stopAccessingSecurityScopedResource();
|
||||||
|
|
||||||
|
cont.dismissViewController(true, () -> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
url.stopAccessingSecurityScopedResource();
|
Core.app.post(() -> {
|
||||||
|
try{
|
||||||
cont.dismissViewController(true, () -> {});
|
params.handleChooseResult(results.toArray(Fi.class));
|
||||||
|
}catch(Throwable t){
|
||||||
|
ui.showException(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -137,12 +140,6 @@ public class IOSLauncher extends IOSApplication.Delegate{
|
|||||||
|
|
||||||
UIApplication.getSharedApplication().getKeyWindow().getRootViewController().presentViewController(cont, true, () -> {});
|
UIApplication.getSharedApplication().getKeyWindow().getRootViewController().presentViewController(cont, true, () -> {});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showMultiFileChooser(Cons<Fi> cons, String... extensions){
|
|
||||||
showFileChooser(true, extensions[0], cons);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Context getScriptContext(){
|
public Context getScriptContext(){
|
||||||
Context context = Context.getCurrentContext();
|
Context context = Context.getCurrentContext();
|
||||||
|
|||||||
Reference in New Issue
Block a user