From 5477c4beee84968074e61630cd29f97df62e840f Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Wed, 25 Mar 2026 23:55:40 -0600 Subject: [PATCH] use swing picker on wayland --- android/.gitignore | 1 + android/app/.gitignore | 1 + backend/.gitignore | 1 + desktop/.gitignore | 1 + desktop/src/main/java/module-info.java | 1 + .../controllers/MainLayoutController.java | 9 +-- .../util/FilePickerSupport.java | 77 +++++++++++++++++++ web/.gitignore | 1 + 8 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 desktop/src/main/java/org/example/petshopdesktop/util/FilePickerSupport.java diff --git a/android/.gitignore b/android/.gitignore index 5cfb3b8d..edc74b8b 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,4 +1,5 @@ *.iml +nohup.out .gradle /local.properties /.idea/* diff --git a/android/app/.gitignore b/android/app/.gitignore index 86de5a5e..fdcdf404 100644 --- a/android/app/.gitignore +++ b/android/app/.gitignore @@ -1,4 +1,5 @@ /build +/nohup.out /.classpath /.project /.settings/ diff --git a/backend/.gitignore b/backend/.gitignore index 4ade3c30..9aece802 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,4 +1,5 @@ target/ +nohup.out !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ diff --git a/desktop/.gitignore b/desktop/.gitignore index c5df67b2..2dc38ee8 100644 --- a/desktop/.gitignore +++ b/desktop/.gitignore @@ -1,4 +1,5 @@ target/ +nohup.out !.mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ diff --git a/desktop/src/main/java/module-info.java b/desktop/src/main/java/module-info.java index 910be91c..d6cefd63 100644 --- a/desktop/src/main/java/module-info.java +++ b/desktop/src/main/java/module-info.java @@ -2,6 +2,7 @@ module org.example.petshopdesktop { requires javafx.controls; requires javafx.fxml; requires javafx.web; + requires java.desktop; requires java.sql; requires java.net.http; requires com.fasterxml.jackson.databind; diff --git a/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java b/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java index cb283dd2..d87e181d 100644 --- a/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java +++ b/desktop/src/main/java/org/example/petshopdesktop/controllers/MainLayoutController.java @@ -16,13 +16,13 @@ import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.paint.ImagePattern; import javafx.scene.shape.Circle; -import javafx.stage.FileChooser; import javafx.stage.Stage; import org.example.petshopdesktop.api.ChatRealtimeClient; import org.example.petshopdesktop.api.dto.auth.AvatarUploadResponse; import org.example.petshopdesktop.api.dto.auth.UserInfoResponse; import org.example.petshopdesktop.api.endpoints.AuthApi; import org.example.petshopdesktop.auth.UserSession; +import org.example.petshopdesktop.util.FilePickerSupport; import org.example.petshopdesktop.ui.SvgWebViewFactory; import org.example.petshopdesktop.util.ActivityLogger; @@ -206,12 +206,7 @@ public class MainLayoutController { @FXML void btnChangeAvatarClicked(ActionEvent event) { - FileChooser chooser = new FileChooser(); - chooser.setTitle("Choose Profile Picture"); - chooser.getExtensionFilters().addAll( - new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg", "*.jpeg", "*.gif") - ); - java.io.File file = chooser.showOpenDialog(btnChangeAvatar.getScene().getWindow()); + java.io.File file = FilePickerSupport.pickImageFile(btnChangeAvatar.getScene().getWindow()); if (file == null) { return; } diff --git a/desktop/src/main/java/org/example/petshopdesktop/util/FilePickerSupport.java b/desktop/src/main/java/org/example/petshopdesktop/util/FilePickerSupport.java new file mode 100644 index 00000000..313c78e9 --- /dev/null +++ b/desktop/src/main/java/org/example/petshopdesktop/util/FilePickerSupport.java @@ -0,0 +1,77 @@ +package org.example.petshopdesktop.util; + +import javafx.stage.FileChooser; +import javafx.stage.Window; + +import javax.swing.JFileChooser; +import javax.swing.UIManager; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.awt.Component; +import java.awt.GraphicsEnvironment; +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.atomic.AtomicReference; + +public final class FilePickerSupport { + + private FilePickerSupport() { + } + + public static File pickImageFile(Window ownerWindow) { + if (shouldUseAwtPicker()) { + return pickImageFileWithSwing(); + } + return pickImageFileWithJavaFx(ownerWindow); + } + + private static boolean shouldUseAwtPicker() { + if (GraphicsEnvironment.isHeadless()) { + return false; + } + String sessionType = System.getenv("XDG_SESSION_TYPE"); + String waylandDisplay = System.getenv("WAYLAND_DISPLAY"); + return "wayland".equalsIgnoreCase(sessionType) || (waylandDisplay != null && !waylandDisplay.isBlank()); + } + + private static File pickImageFileWithJavaFx(Window ownerWindow) { + FileChooser chooser = new FileChooser(); + chooser.setTitle("Choose Profile Picture"); + chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg", "*.jpeg", "*.gif")); + return chooser.showOpenDialog(ownerWindow); + } + + private static File pickImageFileWithSwing() { + AtomicReference selectedFile = new AtomicReference<>(); + Runnable dialogTask = () -> { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception ignored) { + } + + JFileChooser chooser = new JFileChooser(); + chooser.setDialogTitle("Choose Profile Picture"); + chooser.setAcceptAllFileFilterUsed(false); + chooser.setFileFilter(new FileNameExtensionFilter("Image Files", "png", "jpg", "jpeg", "gif")); + + int result = chooser.showOpenDialog((Component) null); + if (result == JFileChooser.APPROVE_OPTION) { + selectedFile.set(chooser.getSelectedFile()); + } + }; + + try { + if (java.awt.EventQueue.isDispatchThread()) { + dialogTask.run(); + } else { + java.awt.EventQueue.invokeAndWait(dialogTask); + } + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + return null; + } catch (InvocationTargetException ex) { + throw new IllegalStateException("Failed to open Swing file picker", ex.getCause()); + } + + return selectedFile.get(); + } +} diff --git a/web/.gitignore b/web/.gitignore index 5ef6a520..d957bcc5 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -25,6 +25,7 @@ *.pem # debug +nohup.out npm-debug.log* yarn-debug.log* yarn-error.log*