From 06d9c4417d19a4c01ccf994f1341ae1b7e61da64 Mon Sep 17 00:00:00 2001 From: Charles Samborski Date: Sun, 3 Dec 2017 10:08:52 +0100 Subject: [PATCH] Add support for file chooser buttons This commit adds support for file chooser buttons. They are represented as a slice of tuples `(text, action)`. The original function uses varargs to support an arbitrary number of arguments. This implementation supports up to 3 buttons. **This is a breaking change.** See: https://developer.gnome.org/gtk3/stable/GtkFileChooserDialog.html#gtk-file-chooser-dialog-new Improve file chooser with buttons reliability Following the reviews in gtk-rs/gtk#602, this commit: - uses the `with_buttons` function instead of modifying `new` to keep backward compatibility. - saves the button texts in `Stash` objects to avoid invalid pointers - panics when the number of buttons is too high (instead of silently truncating the number of buttons) Remove doc comment for `FileChooserDialog::with_buttons` Remove local variables in `FileChooserDialog::with_buttons` This commit removes the Stash variables from `FileChooserDialog::with_buttons`, as requested in the PR #602 Fix formatting issue Keep `downcast_unchecked` method on the same line as the closing parenthesis of the chopped-down call to `Widget::from_glib_none`. --- src/file_chooser_dialog.rs | 74 ++++++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 6 deletions(-) diff --git a/src/file_chooser_dialog.rs b/src/file_chooser_dialog.rs index 271cbed118..6e1cf3b666 100644 --- a/src/file_chooser_dialog.rs +++ b/src/file_chooser_dialog.rs @@ -5,21 +5,83 @@ use ffi; use glib::translate::*; use glib::object::{Downcast, IsA}; +use libc::c_char; use std::ptr; use FileChooserAction; use FileChooserDialog; +use ResponseType; use Widget; use Window; impl FileChooserDialog { - pub fn new>(title: Option<&str>, parent: Option<&T>, action: FileChooserAction) - -> FileChooserDialog { + // TODO: Keep the other constructor with buttons support as the only constructor (this one was + // left for compatibility) and rename it to `new` for consistency. + pub fn new>(title: Option<&str>, parent: Option<&T>, action: FileChooserAction) -> FileChooserDialog { assert_initialized_main_thread!(); unsafe { - Widget::from_glib_none( - ffi::gtk_file_chooser_dialog_new(title.to_glib_none().0, parent.to_glib_none().0, - action.to_glib(), ptr::null_mut())) - .downcast_unchecked() + Widget::from_glib_none(ffi::gtk_file_chooser_dialog_new( + title.to_glib_none().0, + parent.to_glib_none().0, + action.to_glib(), + ptr::null::() + )).downcast_unchecked() + } + } + + pub fn with_buttons>(title: Option<&str>, parent: Option<&T>, action: FileChooserAction, buttons: &[(&str, ResponseType)]) -> FileChooserDialog { + assert_initialized_main_thread!(); + unsafe { + Widget::from_glib_none(match buttons.len() { + 0 => { + ffi::gtk_file_chooser_dialog_new( + title.to_glib_none().0, + parent.to_glib_none().0, + action.to_glib(), + ptr::null::() + ) + }, + 1 => { + ffi::gtk_file_chooser_dialog_new( + title.to_glib_none().0, + parent.to_glib_none().0, + action.to_glib(), + buttons[0].0.to_glib_none().0, + buttons[0].1.to_glib(), + ptr::null::(), + ) + }, + 2 => { + ffi::gtk_file_chooser_dialog_new( + title.to_glib_none().0, + parent.to_glib_none().0, + action.to_glib(), + buttons[0].0.to_glib_none().0, + buttons[0].1.to_glib(), + (buttons[1].0.to_glib_none() as Stash<*const c_char, str>).0, + buttons[1].1.to_glib(), + ptr::null::(), + ) + }, + 3 => { + ffi::gtk_file_chooser_dialog_new( + title.to_glib_none().0, + parent.to_glib_none().0, + action.to_glib(), + buttons[0].0.to_glib_none().0, + buttons[0].1.to_glib(), + (buttons[1].0.to_glib_none() as Stash<*const c_char, str>).0, + buttons[1].1.to_glib(), + (buttons[2].0.to_glib_none() as Stash<*const c_char, str>).0, + buttons[2].1.to_glib(), + ptr::null::(), + ) + }, + _ => { + // TODO: Support arbitrary number of buttons once variadic functions are supported. + // See: https://github.com/rust-lang/rust/issues/44930 + panic!(format!("`FileChooserDialog::with_buttons` does not support 4+ buttons, received {}", buttons.len())) + } + }).downcast_unchecked() } } }