Repository: flxzt/rnote (language: rust)

Issue:
file dialog filters not working as expected
**Describe the bug**  

After #1075, filters still don't work as expected.

On windows, applying the filters make gtk not even use the windows native file picker https://gitlab.gnome.org/GNOME/gtk/-/issues/6717.

On linux, the filter appears but you can still save a file without the file extension. The filename portion is the only part selected so it's harder to achieve but is still possible.
![image](https://github.com/flxzt/rnote/assets/115779707/7efe6b56-7371-4659-8aad-5b94c20076b9)

**Expected behavior**  
Have the filter work in a way that makes it impossible to have a `filename` saved without any extensions and using the native file picker on all platforms.

Libreoffice works around this by adding themselves the `.odt` if the extension is not there in the file picker
![Screenshot 2024-06-08 105938](https://github.com/flxzt/rnote/assets/115779707/1815156b-6a3a-43fc-a375-0d3aa7747966)

One issue on the gtk repo talks about this issue as well https://gitlab.gnome.org/GNOME/gtk/-/issues/5367

To have something that uses the native file picker everywhere, no mime type nor custom filters can be used. Now the question is whether using [add_pattern](https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileFilter.html#method.add_pattern) over [add_mime_type](https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileFilter.html#method.add_mime_type) will work

**Desktop (please complete the following information):**  
 - OS: Windows 11 23H2, fedora 40
 - App Version: rnote main (post #1075)
 - Installation Source: from source
 - Desktop Environment: KDE 6 on linux
 - Display Server: wayland on linux



Discussion / hints:

Fix patch:
diff --git a/crates/rnote-ui/src/dialogs/export.rs b/crates/rnote-ui/src/dialogs/export.rs
index 80f4270b9c..a1c0ce5fdd 100644
--- a/crates/rnote-ui/src/dialogs/export.rs
+++ b/crates/rnote-ui/src/dialogs/export.rs
@@ -21,9 +21,18 @@ use std::cell::RefCell;
 use std::rc::Rc;
 
 pub(crate) async fn dialog_save_doc_as(appwindow: &RnAppWindow, canvas: &RnCanvas) {
+    // note : mimetypes are not supported with the native file picker on windows
+    // See the limitations on FileChooserNative
+    // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileChooserNative.html#win32-details--gtkfilechooserdialognative-win32
     let filter = FileFilter::new();
-    filter.add_mime_type("application/rnote");
-    filter.add_suffix("rnote");
+    if cfg!(target_os = "windows") {
+        filter.add_pattern("*.rnote");
+    } else {
+        filter.add_mime_type("application/rnote");
+    }
+    if cfg!(target_os = "macos") {
+        filter.add_suffix("rnote");
+    }
     filter.set_name(Some(&gettext(".rnote")));
 
     // create the list of filters
@@ -276,20 +285,41 @@ fn create_filedialog_export_doc(
         .build();
 
     let filter = FileFilter::new();
+    // note : mimetypes are not supported with the native file picker on windows
+    // See the limitations on FileChooserNative
+    // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileChooserNative.html#win32-details--gtkfilechooserdialognative-win32
     match doc_export_prefs.export_format {
         DocExportFormat::Svg => {
-            filter.add_mime_type("image/svg+xml");
-            filter.add_suffix("svg");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.svg");
+            } else {
+                filter.add_mime_type("image/svg+xml");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("svg");
+            }
             filter.set_name(Some(&gettext("Svg")));
         }
         DocExportFormat::Pdf => {
-            filter.add_mime_type("application/pdf");
-            filter.add_suffix("pdf");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.pdf");
+            } else {
+                filter.add_mime_type("application/pdf");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("pdf");
+            }
             filter.set_name(Some(&gettext("Pdf")));
         }
         DocExportFormat::Xopp => {
-            filter.add_mime_type("application/x-xopp");
-            filter.add_suffix("xopp");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.xopp");
+            } else {
+                filter.add_mime_type("application/x-xopp");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("xopp");
+            }
             filter.set_name(Some(&gettext("Xopp")));
         }
     }
@@ -583,27 +613,42 @@ fn create_filedialog_export_doc_pages(
     filter.add_mime_type("inode/directory");
     match doc_pages_export_prefs.export_format {
         DocPagesExportFormat::Svg => {
-            filter.add_mime_type("image/svg+xml");
-            filter.add_suffix("svg");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.svg");
+            } else {
+                filter.add_mime_type("image/svg+xml");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("svg");
+            }
             filter.set_name(Some(&gettext("Svg")));
         }
         DocPagesExportFormat::Png => {
-            filter.add_mime_type("image/png");
-            filter.add_suffix("png");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.png");
+            } else {
+                filter.add_mime_type("image/png");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("png");
+            }
             filter.set_name(Some(&gettext("Png")));
         }
         DocPagesExportFormat::Jpeg => {
-            filter.add_mime_type("image/jpeg");
-            filter.add_suffix("jpg");
-            filter.add_suffix("jpeg");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.jpg");
+                filter.add_pattern("*.jpeg");
+            } else {
+                filter.add_mime_type("image/jpeg");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("jpg");
+                filter.add_suffix("jpeg");
+            }
             filter.set_name(Some(&gettext("Jpeg")));
         }
     }
 
-    let filter_list = gio::ListStore::new::<FileFilter>();
-    filter_list.append(&filter);
-    filedialog.set_filters(Some(&filter_list));
-
     filedialog.set_default_filter(Some(&filter));
 
     filedialog
@@ -872,21 +917,43 @@ fn create_filedialog_export_selection(
     filedialog.set_initial_folder(get_initial_folder_for_export(appwindow, canvas).as_ref());
 
     let filter = FileFilter::new();
+    // note : mimetypes are not supported with the native file picker on windows
+    // See the limitations on FileChooserNative
+    // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileChooserNative.html#win32-details--gtkfilechooserdialognative-win32
     match selection_export_prefs.export_format {
         SelectionExportFormat::Svg => {
-            filter.add_mime_type("image/svg+xml");
-            filter.add_suffix("svg");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.svg");
+            } else {
+                filter.add_mime_type("image/svg+xml");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("svg");
+            }
             filter.set_name(Some(&gettext("Svg")));
         }
         SelectionExportFormat::Png => {
-            filter.add_mime_type("image/png");
-            filter.add_suffix("png");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.png");
+            } else {
+                filter.add_mime_type("image/png");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("png");
+            }
             filter.set_name(Some(&gettext("Png")));
         }
         SelectionExportFormat::Jpeg => {
-            filter.add_mime_type("image/jpeg");
-            filter.add_suffix("jpg");
-            filter.add_suffix("jpeg");
+            if cfg!(target_os = "windows") {
+                filter.add_pattern("*.jpg");
+                filter.add_pattern("*.jpeg");
+            } else {
+                filter.add_mime_type("image/jpeg");
+            }
+            if cfg!(target_os = "macos") {
+                filter.add_suffix("jpg");
+                filter.add_suffix("jpeg");
+            }
             filter.set_name(Some(&gettext("Jpeg")));
         }
     }
@@ -909,8 +976,17 @@ fn create_filedialog_export_selection(
 
 pub(crate) async fn filechooser_export_engine_state(appwindow: &RnAppWindow, canvas: &RnCanvas) {
     let filter = FileFilter::new();
-    filter.add_mime_type("application/json");
-    filter.add_suffix("json");
+    // note : mimetypes are not supported with the native file picker on windows
+    // See the limitations on FileChooserNative
+    // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileChooserNative.html#win32-details--gtkfilechooserdialognative-win32
+    if cfg!(target_os = "windows") {
+        filter.add_pattern("*.json");
+    } else {
+        filter.add_mime_type("application/json");
+    }
+    if cfg!(target_os = "macos") {
+        filter.add_suffix("json");
+    }
     filter.set_name(Some(&gettext("Json")));
 
     let filter_list = gio::ListStore::new::<FileFilter>();
@@ -962,8 +1038,18 @@ pub(crate) async fn filechooser_export_engine_state(appwindow: &RnAppWindow, can
 
 pub(crate) async fn filechooser_export_engine_config(appwindow: &RnAppWindow, canvas: &RnCanvas) {
     let filter = FileFilter::new();
-    filter.add_mime_type("application/json");
-    filter.add_suffix("json");
+
+    // note : mimetypes are not supported with the native file picker on windows
+    // See the limitations on FileChooserNative
+    // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileChooserNative.html#win32-details--gtkfilechooserdialognative-win32
+    if cfg!(target_os = "windows") {
+        filter.add_pattern("*.json");
+    } else {
+        filter.add_mime_type("application/json");
+    }
+    if cfg!(target_os = "macos") {
+        filter.add_suffix("json");
+    }
     filter.set_name(Some(&gettext("Json")));
 
     let filter_list = gio::ListStore::new::<FileFilter>();
diff --git a/crates/rnote-ui/src/dialogs/import.rs b/crates/rnote-ui/src/dialogs/import.rs
index bd2f5c2e50..8a5cdb75b8 100644
--- a/crates/rnote-ui/src/dialogs/import.rs
+++ b/crates/rnote-ui/src/dialogs/import.rs
@@ -16,7 +16,14 @@ use rnote_engine::engine::import::{PdfImportPageSpacing, PdfImportPagesType};
 /// Opens a new rnote save file in a new tab
 pub(crate) async fn filedialog_open_doc(appwindow: &RnAppWindow) {
     let filter = FileFilter::new();
-    filter.add_mime_type("application/rnote");
+    // note : mimetypes are not supported with the native file picker on windows
+    // See the limitations on FileChooserNative
+    // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileChooserNative.html#win32-details--gtkfilechooserdialognative-win32
+    if cfg!(target_os = "windows") {
+        filter.add_pattern("*.rnote");
+    } else {
+        filter.add_mime_type("application/rnote");
+    }
     filter.add_suffix("rnote");
     filter.set_name(Some(&gettext(".rnote")));
 
@@ -51,12 +58,24 @@ pub(crate) async fn filedialog_open_doc(appwindow: &RnAppWindow) {
 
 pub(crate) async fn filedialog_import_file(appwindow: &RnAppWindow) {
     let filter = FileFilter::new();
-    filter.add_mime_type("application/x-xopp");
-    filter.add_mime_type("application/pdf");
-    filter.add_mime_type("image/svg+xml");
-    filter.add_mime_type("image/png");
-    filter.add_mime_type("image/jpeg");
-    filter.add_mime_type("text/plain");
+    // note : mimetypes are not supported with the native file picker on windows
+    // See the limitations on FileChooserNative
+    // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk/struct.FileChooserNative.html#win32-details--gtkfilechooserdialognative-win32
+    if cfg!(target_os = "windows") {
+        filter.add_pattern("*.xopp");
+        filter.add_pattern("*.pdf");
+        filter.add_pattern("*.svg");
+        filter.add_pattern("*.png");
+        filter.add_pattern("*.jpeg");
+        filter.add_pattern("*.txt");
+    } else {
+        filter.add_mime_type("application/x-xopp");
+        filter.add_mime_type("application/pdf");
+        filter.add_mime_type("image/svg+xml");
+        filter.add_mime_type("image/png");
+        filter.add_mime_type("image/jpeg");
+        filter.add_mime_type("text/plain");
+    }
     filter.add_suffix("xopp");
     filter.add_suffix("pdf");
     filter.add_suffix("svg");


Test patch: