diff --git a/src/Avalonia.Themes.Default/TitleBar.xaml b/src/Avalonia.Themes.Default/TitleBar.xaml
index 45798d3fa1..4dba5b4ba4 100644
--- a/src/Avalonia.Themes.Default/TitleBar.xaml
+++ b/src/Avalonia.Themes.Default/TitleBar.xaml
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Avalonia.Themes.Fluent/TitleBar.xaml b/src/Avalonia.Themes.Fluent/TitleBar.xaml
index 45798d3fa1..4dba5b4ba4 100644
--- a/src/Avalonia.Themes.Fluent/TitleBar.xaml
+++ b/src/Avalonia.Themes.Fluent/TitleBar.xaml
@@ -15,7 +15,7 @@
-
+
diff --git a/src/Avalonia.X11/NativeDialogs/Gtk.cs b/src/Avalonia.X11/NativeDialogs/Gtk.cs
index 82cf3c934f..77e410162f 100644
--- a/src/Avalonia.X11/NativeDialogs/Gtk.cs
+++ b/src/Avalonia.X11/NativeDialogs/Gtk.cs
@@ -204,6 +204,9 @@ namespace Avalonia.X11.NativeDialogs
[DllImport(GtkName)]
public static extern IntPtr gtk_file_chooser_add_filter(IntPtr chooser, IntPtr filter);
+
+ [DllImport(GtkName)]
+ public static extern IntPtr gtk_file_chooser_get_filter(IntPtr chooser);
[DllImport(GtkName)]
public static extern void gtk_widget_realize(IntPtr gtkWidget);
diff --git a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs
index 287a541bc8..6cf6c6f35f 100644
--- a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs
+++ b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs
@@ -16,7 +16,7 @@ namespace Avalonia.X11.NativeDialogs
{
private Task _initialized;
private unsafe Task ShowDialog(string title, IWindowImpl parent, GtkFileChooserAction action,
- bool multiSelect, string initialFileName, IEnumerable filters)
+ bool multiSelect, string initialFileName, IEnumerable filters, string defaultExtension)
{
IntPtr dlg;
using (var name = new Utf8Buffer(title))
@@ -35,11 +35,13 @@ namespace Avalonia.X11.NativeDialogs
foreach (var d in disposables) d.Dispose();
disposables.Clear();
}
-
+
+ var filtersDic = new Dictionary();
if(filters != null)
foreach (var f in filters)
{
var filter = gtk_file_filter_new();
+ filtersDic[filter] = f;
using (var b = new Utf8Buffer(f.Name))
gtk_file_filter_set_name(filter, b);
@@ -74,6 +76,15 @@ namespace Avalonia.X11.NativeDialogs
}
g_slist_free(gs);
result = resultList.ToArray();
+
+ // GTK doesn't auto-append the extension, so we need to do that manually
+ if (action == GtkFileChooserAction.Save)
+ {
+ var currentFilter = gtk_file_chooser_get_filter(dlg);
+ filtersDic.TryGetValue(currentFilter, out var selectedFilter);
+ for (var c = 0; c < result.Length; c++)
+ result[c] = NameWithExtension(result[c], defaultExtension, selectedFilter);
+ }
}
gtk_widget_hide(dlg);
@@ -101,7 +112,30 @@ namespace Avalonia.X11.NativeDialogs
gtk_window_present(dlg);
return tcs.Task;
}
-
+
+ string NameWithExtension(string path, string defaultExtension, FileDialogFilter filter)
+ {
+ var name = Path.GetFileName(path);
+ if (name != null && !name.Contains("."))
+ {
+ if (filter?.Extensions?.Count > 0)
+ {
+ if (defaultExtension != null
+ && filter.Extensions.Contains(defaultExtension))
+ return path + "." + defaultExtension.TrimStart('.');
+
+ var ext = filter.Extensions.FirstOrDefault(x => x != "*");
+ if (ext != null)
+ return path + "." + ext.TrimStart('.');
+ }
+
+ if (defaultExtension != null)
+ path += "." + defaultExtension.TrimStart('.');
+ }
+
+ return path;
+ }
+
public async Task ShowFileDialogAsync(FileDialog dialog, Window parent)
{
await EnsureInitialized();
@@ -113,7 +147,8 @@ namespace Avalonia.X11.NativeDialogs
dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save,
(dialog as OpenFileDialog)?.AllowMultiple ?? false,
Path.Combine(string.IsNullOrEmpty(dialog.Directory) ? "" : dialog.Directory,
- string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters));
+ string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters,
+ (dialog as SaveFileDialog)?.DefaultExtension));
}
public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent)
@@ -125,7 +160,7 @@ namespace Avalonia.X11.NativeDialogs
return await await RunOnGlibThread(async () =>
{
var res = await ShowDialog(dialog.Title, platformImpl,
- GtkFileChooserAction.SelectFolder, false, dialog.Directory, null);
+ GtkFileChooserAction.SelectFolder, false, dialog.Directory, null, null);
return res?.FirstOrDefault();
});
}