如何将 "save" 按钮添加到 gtk 文件选择器对话框?

How do I add a "save" button to the gtk filechooser dialog?

我有一个需要保存文件的 Gjs 应用程序。我可以从我的菜单中打开文件选择器对话框,我添加了 "save" 和 "cancel" 按钮,但我无法让 "save" 按钮触发任何东西。

我知道我应该给它传递一个 response_id,但我不确定它应该是什么样子,也不确定之后我应该用它做什么。 我在这里读了那部分: https://www.roojs.com/seed/gir-1.2-gtk-3.0/gjs/Gtk.FileChooserDialog.html#expand

let actionSaveAs = new Gio.SimpleAction ({ name: 'saveAs' });
    actionSaveAs.connect('activate', () => {
            const saver = new Gtk.FileChooserDialog({title:'Select a destination'});
            saver.set_action(Gtk.FileChooserAction.SAVE);
            saver.add_button('save', 'GTK_RESPONSE_ACCEPT');
            saver.add_button('cancel', 'GTK_RESPONSE_CANCEL');
            const res = saver.run();
            if (res) {
              print(res);
              const filename = saver.get_filename();
              print(filename);
            }
            saver.destroy();
          });
    APP.add_action(actionSaveAs);

当我关闭对话框时,我可以捕获 res 并触发相关的小日志记录操作,但是 "save" 和 "cancel" 按钮都只是关闭对话框而不做或说任何事情.

我的问题是,GTK_RESPONSE_ACCEPT 和 GTK_RESPONSE_CANCEL 在 GJS 中应该(看起来)是什么,我该如何使用它们?

在 GJS 枚举中,如 GTK_RESPONSE_* 是数字,实际上看起来像这样:

// imagine this is the Gtk import
const Gtk = {
    ResponseType: {
        NONE: -1,
        REJECT: -2,
        ACCEPT: -3,
        DELETE_EVENT: -4,
        ...
    }
};

// access like so
let response_id = -3;

if (response_id === Gtk.ResponseType.ACCEPT) {
    log(true);
}

有更多关于此的信息here

let saver = new Gtk.FileChooserDialog({
    title:'Select a destination',
    // you had the enum usage correct here
    action: Gtk.FileChooserAction.SAVE
});

// Really the response code doesn't matter much, since you're
// deciding what to do with it. You could pass number literals
// like 1, 2 or 3. Probably this was not working because you were
// passing a string as a response id.
saver.add_button('Cancel', Gtk.ResponseType.CANCEL);
saver.add_button('Save', Gtk.ResponseType.OK);

// run() is handy, but be aware that it will block the current (only)
// thread until it returns, so I usually prefer to connect to the
// GtkDialog::response signal and use GtkWidget.show()
saver.connect('response', (dialog, response_id) => {
    if (response_id === Gtk.ResponseType.OK) {
        // outputs "-5"
        print(response_id);

        // NOTE: we're using @dialog instead of 'saver' in the callback to
        // avoid a possible cyclic reference which could prevent the dialog
        // from being garbage collected.
        let filename = dialog.get_filename();

        // here's where you do your stuff with the filename. You might consider
        // wrapping this whole thing in a re-usable Promise. Then you could call
        // `resolve(filename)` or maybe `resolve(null)` if the response_id
        // was not Gtk.ResponseType.OK. You could then `await` the result to get
        // the same functionality as run() but allow other code to execute while
        // you wait for the user.
        print(filename);

        // Also note, you actually have to do the writing yourself, such as
        // with a GFile. GtkFileChooserDialog is really just for getting a
        // file path from the user
        let file = Gio.File.new_for_path(filename);

        file.replace_contents_bytes_async(
            // of course you actually need bytes to write, since GActions
            // have no way to return a value, unless you're passing all the
            // data through as a parameter, it might not be the best option
            new GLib.Bytes('file contents to write to disk'),
            null,
            false,
            Gio.FileCreateFlags.REPLACE_DESTINATION,
            null,
            // "shadowing" variable with the same name is another way
            // to prevent cyclic references in callbacks.
            (file, res) => {
                try {
                    file.replace_contents_finish(res);
                } catch (e) {
                    logError(e);
                }
            }
        );
    }

    // destroy the dialog regardless of the response when we're done.
    dialog.destroy();
});

// for bonus points, here's how you'd implement a simple preview widget ;)
saver.preview_widget = new Gtk.Image();
saver.preview_widget_active = false;
this.connect('update-preview', (dialog) => {
    try {
        // you'll have to import GdkPixbuf to use this
        let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
            dialog.get_preview_filename(),
            dialog.get_scale_factor() * 128,
            -1
        );
        dialog.preview_widget.pixbuf = pixbuf;
        dialog.preview_widget.visible = true;
        dialog.preview_widget_active = true;

    // if there's some kind of error or the file isn't an image
    // we'll just hide the preview widget
    } catch (e) {
        dialog.preview_widget.visible = false;
        dialog.preview_widget_active = false;
    }
});

// this is how we'll show the dialog to the user
saver.show();