<NME> saveDlg.ts
<BEF> // Copies a file to user-defined destination.
//
// Usage:
//   saveDlg.js SRCFILE DSTDIR DSTNAME
//

import * as Gio from '@imports/Gio-2.0';
import * as Gtk from '@imports/Gtk-3.0';

import { _, init as initTranslations } from '../gselib/gettext';

const copy = (srcPath, dstDir, dstName) => {
  if (!srcPath) {
    print('no srcPath');
    return;
  }
  const srcFile = Gio.File.new_for_path(srcPath);

  Gtk.init(null);

  const dlg = new Gtk.FileChooserDialog({ action: Gtk.FileChooserAction.SAVE });

  dlg.add_button(_('_Cancel'), Gtk.ResponseType.CANCEL);
  dlg.add_button(_('_Save'), Gtk.ResponseType.OK);

  dlg.set_do_overwrite_confirmation(true);

  if (dstDir) {
    const dstDirFile = Gio.File.new_for_path(dstDir);
    dlg.set_current_folder_file(dstDirFile);
  }

  if (dstName) {
    dlg.set_current_name(dstName);
  }

  if (dlg.run() !== Gtk.ResponseType.OK) {
    return;
  }

  const filename = dlg.get_filename();

  if (!filename) {
    throw new Error();
  }

  const dstFile = Gio.File.new_for_path(filename);
  srcFile.copy(dstFile, Gio.FileCopyFlags.OVERWRITE, null, null);
};

if (window['ARGV']) {
  initTranslations(ARGV[3]);

  const [srcPath, dstDir, dstName] = [ARGV[0], ARGV[1], ARGV[2]].map(decodeURIComponent);
  copy(srcPath, dstDir, dstName);
}

<MSG> fix(saveDlg): support Gtk4.0

<DFF> @@ -5,52 +5,67 @@
 //
 
 import * as Gio from '@imports/Gio-2.0';
-import * as Gtk from '@imports/Gtk-3.0';
+import * as Gtk3 from '@imports/Gtk-3.0';
+import * as Gtk4 from '@imports/Gtk-4.0';
 
 import { _, init as initTranslations } from '../gselib/gettext';
 
-const copy = (srcPath, dstDir, dstName) => {
-  if (!srcPath) {
-    print('no srcPath');
-    return;
+function wrapCompatGFileArgument(str: string): Gio.File | string {
+  switch (Gtk4.get_major_version()) {
+    case 3:
+      return str;
+    case 4:
+      return Gio.File.new_for_path(str);
   }
-  const srcFile = Gio.File.new_for_path(srcPath);
-
-  Gtk.init(null);
+  throw new Error('unsupported version');
+}
 
-  const dlg = new Gtk.FileChooserDialog({ action: Gtk.FileChooserAction.SAVE });
+function getCopyDialog(app: Gtk4.Application, srcPath: string, dstDir?: string, dstName?: string): Gtk4.Dialog {
+  const srcFile = Gio.File.new_for_path(srcPath);
 
-  dlg.add_button(_('_Cancel'), Gtk.ResponseType.CANCEL);
-  dlg.add_button(_('_Save'), Gtk.ResponseType.OK);
+  const dlg = new Gtk4.FileChooserDialog({
+    application: app,
+    action: Gtk4.FileChooserAction.SAVE,
+  });
 
-  dlg.set_do_overwrite_confirmation(true);
+  dlg.add_button(_('_Cancel'), Gtk4.ResponseType.CANCEL);
+  dlg.add_button(_('_Save'), Gtk4.ResponseType.OK);
 
   if (dstDir) {
-    const dstDirFile = Gio.File.new_for_path(dstDir);
-    dlg.set_current_folder_file(dstDirFile);
+    dlg.set_current_folder(wrapCompatGFileArgument(dstDir) as any);
   }
 
   if (dstName) {
     dlg.set_current_name(dstName);
   }
 
-  if (dlg.run() !== Gtk.ResponseType.OK) {
-    return;
-  }
-
-  const filename = dlg.get_filename();
-
-  if (!filename) {
-    throw new Error();
-  }
+  dlg.connect('response', (_dialog, response) => {
+    if (response === Gtk4.ResponseType.OK) {
+      srcFile.copy(dlg.get_file(), Gio.FileCopyFlags.OVERWRITE, null, null);
+    }
+    dlg.close();
+  });
 
-  const dstFile = Gio.File.new_for_path(filename);
-  srcFile.copy(dstFile, Gio.FileCopyFlags.OVERWRITE, null, null);
-};
+  return dlg;
+}
 
 if (window['ARGV']) {
   initTranslations(ARGV[3]);
 
   const [srcPath, dstDir, dstName] = [ARGV[0], ARGV[1], ARGV[2]].map(decodeURIComponent);
-  copy(srcPath, dstDir, dstName);
+
+  if (!srcPath) {
+    throw new Error('no srcPath');
+  }
+
+  const app = new Gtk4.Application({
+    application_id: 'org.gnome.GnomeShellScreenshot.SaveDialog',
+    flags: Gio.ApplicationFlags.FLAGS_NONE,
+  });
+
+  app.connect('activate', () => {
+    getCopyDialog(app, srcPath, dstDir, dstName).show();
+  });
+
+  app.run([]);
 }

fix(saveDlg): support Gtk4.0