Как добавить кнопку «Сохранить» в диалог выбора файлов gtk? - PullRequest
0 голосов
/ 01 февраля 2019

У меня есть приложение Gjs, которое нужно будет сохранять файлы.Я могу просто открыть диалоговое окно выбора файла из моего меню, и я добавил кнопки «сохранить» и «отменить», но я не могу получить кнопку «сохранить», чтобы вызвать что-либо.

Я знаю, что должен передать его 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 и запустить соответствующее небольшое действие регистрации, когда я закрываю диалоговое окно, но кнопки «Сохранить» и «Отмена» просто закрываютсядиалоговое окно, ничего не делая и не произнося.

Мой вопрос такой: каковы должны быть GTK_RESPONSE_ACCEPT и GTK_RESPONSE_CANCEL (похожи) в GJS и как их использовать?

1 Ответ

0 голосов
/ 02 февраля 2019

В 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);
}

Здесь немного больше информации здесь об этом.

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();
...