From 5fe8170e7cc80bb08d1feeede1e03d3be3b90178 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 6 Feb 2026 00:51:13 -0500 Subject: [PATCH] Testing SDL-based file chooser --- core/src/mindustry/core/Platform.java | 61 +------------------ .../mindustry/desktop/DesktopLauncher.java | 56 ++++++++++++++++- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/core/src/mindustry/core/Platform.java b/core/src/mindustry/core/Platform.java index e395d82412..81a3f35742 100644 --- a/core/src/mindustry/core/Platform.java +++ b/core/src/mindustry/core/Platform.java @@ -142,68 +142,13 @@ public interface Platform{ * @param title The title of the native dialog */ default void showFileChooser(boolean open, String title, String extension, Cons cons){ - if(OS.isWindows || OS.isMac){ + if(OS.isWindows || OS.isMac || (OS.isLinux && !OS.isAndroid)){ showNativeFileChooser(open, title, cons, extension); - }else 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 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 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); - } - }); - } - static void defaultFileDialog(boolean open, String title, String extension, Cons cons){ new FileChooser(title, file -> file.extEquals(extension), open, file -> { if(!open){ @@ -226,10 +171,8 @@ public interface Platform{ default void showMultiFileChooser(Cons cons, String... extensions){ if(mobile){ showFileChooser(true, extensions[0], cons); - }else if(OS.isWindows || OS.isMac){ + }else if(OS.isWindows || OS.isMac || (OS.isLinux && !OS.isAndroid)){ showNativeFileChooser(true, "@open", cons, extensions); - }else if(OS.isLinux && !OS.isAndroid){ - showZenity(true, "@open", extensions, cons, () -> defaultMultiFileChooser(cons, extensions)); }else{ defaultMultiFileChooser(cons, extensions); } diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java index 840a08d70f..656cc521aa 100644 --- a/desktop/src/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/mindustry/desktop/DesktopLauncher.java @@ -6,6 +6,7 @@ import arc.backend.sdl.*; import arc.discord.*; import arc.discord.DiscordRPC.*; import arc.files.*; +import arc.func.*; import arc.math.*; import arc.profiling.*; import arc.struct.*; @@ -14,7 +15,7 @@ import arc.util.Log.*; import arc.util.serialization.*; import com.codedisaster.steamworks.*; import mindustry.*; -import mindustry.core.*; +import mindustry.core.Version; import mindustry.desktop.steam.*; import mindustry.game.EventType.*; import mindustry.gen.*; @@ -24,7 +25,10 @@ import mindustry.net.*; import mindustry.net.Net.*; import mindustry.service.*; import mindustry.type.*; +import mindustry.ui.dialogs.*; +import org.lwjgl.*; import org.lwjgl.sdl.*; +import org.lwjgl.system.*; import java.io.*; @@ -292,6 +296,56 @@ public class DesktopLauncher extends ClientLauncher{ }); } + @Override + public void showNativeFileChooser(boolean open, String title, Cons cons, String... shownExtensions){ + String[] ext = shownExtensions == null || shownExtensions.length == 0 ? new String[]{""} : shownExtensions; + + SDL_DialogFileFilter.Buffer filters = SDL_DialogFileFilter.calloc(ext.length); + try(MemoryStack stack = MemoryStack.stackPush()){ + for(int i = 0; i < ext.length; i++){ + String extName = ext[i]; + + var filter = SDL_DialogFileFilter.calloc(stack) + .name(MemoryUtil.memUTF8(extName.isEmpty() ? "All Files" : "." + extName + " files")) + .pattern(MemoryUtil.memUTF8(extName.isEmpty() ? "*" : extName)); + + filters.put(i, filter); + } + } + SDL_DialogFileCallbackI callback = (userData, files, filter) -> { + if(files != 0){ + PointerBuffer pointerBuffer = MemoryUtil.memPointerBuffer(files, 1); + long firstFile = pointerBuffer.get(); + if(firstFile != 0){ + String result = MemoryUtil.memUTF8(firstFile); + + if(result.isEmpty() || result.equals("\n")) return; + if(result.endsWith("\n")) result = result.substring(0, result.length() - 1); + if(result.contains("\n")) return; + + 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() + "." + ext[0])); + }else{ + cons.get(file); + } + }); + } + } + }; + + if(open){ + SDLDialog.SDL_ShowOpenFileDialog(callback, 0, ((SdlApplication)Core.app).getWindow(), filters, FileChooser.getLastDirectory().absolutePath(), false); + }else{ + SDLDialog.SDL_ShowSaveFileDialog(callback, 0, ((SdlApplication)Core.app).getWindow(), filters, FileChooser.getLastDirectory().absolutePath()); + } + + filters.free(); + } + @Override public Seq getWorkshopContent(Class type){ return !steam ? super.getWorkshopContent(type) : SVars.workshop.getWorkshopFiles(type);