Native (Zenity) file dialogs for Linux

This commit is contained in:
Anuken
2023-05-15 11:44:08 -04:00
parent 3593803ad9
commit 1289e20990
2 changed files with 73 additions and 7 deletions

View File

@@ -14,6 +14,7 @@ import mindustry.type.*;
import mindustry.ui.dialogs.*; import mindustry.ui.dialogs.*;
import rhino.*; import rhino.*;
import java.io.*;
import java.net.*; import java.net.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -140,6 +141,61 @@ public interface Platform{
* @param title The title of the native dialog * @param title The title of the native dialog
*/ */
default void showFileChooser(boolean open, String title, String extension, Cons<Fi> cons){ default void showFileChooser(boolean open, String title, String extension, Cons<Fi> cons){
if(OS.isLinux && !OS.isAndroid){
showZenity(open, title, new String[]{extension}, cons, () -> defaultFileDialog(open, title, extension, cons));
}else{
defaultFileDialog(open, title, extension, 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));
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");
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.warn("zenity not found, using non-native file dialog. Consider installing `zenity` for native file dialogs.");
Core.app.post(fallback);
}
});
}
static void defaultFileDialog(boolean open, String title, String extension, Cons<Fi> cons){
new FileChooser(title, file -> file.extEquals(extension), open, file -> { new FileChooser(title, file -> file.extEquals(extension), open, file -> {
if(!open){ if(!open){
cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension)); cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension));
@@ -161,11 +217,17 @@ public interface Platform{
default void showMultiFileChooser(Cons<Fi> cons, String... extensions){ default void showMultiFileChooser(Cons<Fi> cons, String... extensions){
if(mobile){ if(mobile){
showFileChooser(true, extensions[0], cons); showFileChooser(true, extensions[0], cons);
}else if(OS.isLinux && !OS.isAndroid){
showZenity(true, "@open", extensions, cons, () -> defaultMultiFileChooser(cons, extensions));
}else{ }else{
new FileChooser("@open", file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show(); defaultMultiFileChooser(cons, extensions);
} }
} }
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. */
default void hide(){ default void hide(){
} }

View File

@@ -20,7 +20,7 @@ public class FileChooser 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()));
Fi directory = lastDirectory; Fi directory;
private Table files; private Table files;
private ScrollPane pane; private ScrollPane pane;
private TextField navigation, filefield; private TextField navigation, filefield;
@@ -37,10 +37,7 @@ public class FileChooser extends BaseDialog{
this.filter = filter; this.filter = filter;
this.selectListener = result; this.selectListener = result;
if(!lastDirectory.exists()){ directory = getLastDirectory();
lastDirectory = homeDirectory;
directory = lastDirectory;
}
onResize(() -> { onResize(() -> {
cont.clear(); cont.clear();
@@ -254,11 +251,18 @@ public class FileChooser extends BaseDialog{
if(open) filefield.clearText(); if(open) filefield.clearText();
} }
public static void setLastDirectory(Fi directory){ public static synchronized void setLastDirectory(Fi directory){
lastDirectory = directory; lastDirectory = directory;
Core.settings.put("lastDirectory", directory.absolutePath()); Core.settings.put("lastDirectory", directory.absolutePath());
} }
public static synchronized Fi getLastDirectory(){
if(!lastDirectory.exists()){
lastDirectory = homeDirectory;
}
return lastDirectory;
}
public class FileHistory{ public class FileHistory{
private Seq<Fi> history = new Seq<>(); private Seq<Fi> history = new Seq<>();
private int index; private int index;