Native (Zenity) file dialogs for Linux
This commit is contained in:
@@ -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(){
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user