rustydialogs 0.4.2

Provides a simple and cross-platform way to display native dialog boxes.
Documentation
use super::*;

unsafe fn style_response_button(dialog: *mut gtk4_sys::GtkDialog, response: i32) {
	let button = gtk4_sys::gtk_dialog_get_widget_for_response(dialog, response);
	if button.is_null() {
		return;
	}

	gtk4_sys::gtk_widget_set_size_request(button, 110, -1);
	gtk4_sys::gtk_widget_set_margin_start(button, 6);
	gtk4_sys::gtk_widget_set_margin_end(button, 6);
	gtk4_sys::gtk_widget_set_margin_top(button, 8);
	gtk4_sys::gtk_widget_set_margin_bottom(button, 8);
}

pub fn text_input(p: &TextInput<'_>) -> Option<String> {
	match p.mode {
		TextInputMode::SingleLine => text_input_entry(p, false),
		TextInputMode::MultiLine => text_input_multiline(p),
		TextInputMode::Password => text_input_entry(p, true),
	}
}

fn text_input_entry(p: &TextInput<'_>, password: bool) -> Option<String> {
	ensure_gtk_initialized();

	let dialog = unsafe { gtk4_sys::gtk_dialog_new() };
	let title = cstring(p.title);
	let message = cstring(p.message);
	let value = cstring(p.value);
	let ok = c"OK";
	let cancel = c"Cancel";

	unsafe {
		gtk4_sys::gtk_window_set_title(dialog as *mut gtk4_sys::GtkWindow, title.as_ptr());
		gtk4_sys::gtk_window_set_default_size(dialog as *mut gtk4_sys::GtkWindow, 520, -1);
		gtk4_sys::gtk_dialog_add_button(dialog as *mut gtk4_sys::GtkDialog, cancel.as_ptr(), gtk4_sys::GTK_RESPONSE_CANCEL);
		gtk4_sys::gtk_dialog_add_button(dialog as *mut gtk4_sys::GtkDialog, ok.as_ptr(), gtk4_sys::GTK_RESPONSE_OK);
		gtk4_sys::gtk_dialog_set_default_response(dialog as *mut gtk4_sys::GtkDialog, gtk4_sys::GTK_RESPONSE_OK);
		style_response_button(dialog as *mut gtk4_sys::GtkDialog, gtk4_sys::GTK_RESPONSE_CANCEL);
		style_response_button(dialog as *mut gtk4_sys::GtkDialog, gtk4_sys::GTK_RESPONSE_OK);

		let content = gtk4_sys::gtk_dialog_get_content_area(dialog as *mut gtk4_sys::GtkDialog);
		gtk4_sys::gtk_box_set_spacing(content as *mut gtk4_sys::GtkBox, 10);
		gtk4_sys::gtk_widget_set_vexpand(content as *mut gtk4_sys::GtkWidget, 1);
		gtk4_sys::gtk_widget_set_hexpand(content as *mut gtk4_sys::GtkWidget, 1);
		gtk4_sys::gtk_widget_set_margin_start(content as *mut gtk4_sys::GtkWidget, 14);
		gtk4_sys::gtk_widget_set_margin_end(content as *mut gtk4_sys::GtkWidget, 14);
		gtk4_sys::gtk_widget_set_margin_top(content as *mut gtk4_sys::GtkWidget, 14);
		gtk4_sys::gtk_widget_set_margin_bottom(content as *mut gtk4_sys::GtkWidget, 14);

		let label = gtk4_sys::gtk_label_new(message.as_ptr());
		gtk4_sys::gtk_label_set_xalign(label as *mut gtk4_sys::GtkLabel, 0.0);
		let entry = gtk4_sys::gtk_entry_new();
		gtk4_sys::gtk_editable_set_text(entry as *mut gtk4_sys::GtkEditable, value.as_ptr());
		gtk4_sys::gtk_entry_set_activates_default(entry as *mut gtk4_sys::GtkEntry, 1);
		if password {
			gtk4_sys::gtk_entry_set_visibility(entry as *mut gtk4_sys::GtkEntry, 0);
		}

		gtk4_sys::gtk_box_append(content as *mut gtk4_sys::GtkBox, label);
		gtk4_sys::gtk_box_append(content as *mut gtk4_sys::GtkBox, entry);

		run_dialog_f(dialog as *mut gtk4_sys::GtkDialog, |response| {
			if response == gtk4_sys::GTK_RESPONSE_OK {
				let text_ptr = gtk4_sys::gtk_editable_get_text(entry as *mut gtk4_sys::GtkEditable);
				if text_ptr.is_null() {
					None
				} else {
					Some(CStr::from_ptr(text_ptr).to_string_lossy().to_string())
				}
			} else {
				None
			}
		})
	}
}

fn text_input_multiline(p: &TextInput<'_>) -> Option<String> {
	ensure_gtk_initialized();

	let dialog = unsafe { gtk4_sys::gtk_dialog_new() };
	let title = cstring(p.title);
	let message = cstring(p.message);
	let value = cstring(p.value);
	let ok = c"OK";
	let cancel = c"Cancel";

	unsafe {
		gtk4_sys::gtk_window_set_title(dialog as *mut gtk4_sys::GtkWindow, title.as_ptr());
		gtk4_sys::gtk_window_set_default_size(dialog as *mut gtk4_sys::GtkWindow, 500, 380);
		gtk4_sys::gtk_dialog_add_button(dialog as *mut gtk4_sys::GtkDialog, cancel.as_ptr(), gtk4_sys::GTK_RESPONSE_CANCEL);
		gtk4_sys::gtk_dialog_add_button(dialog as *mut gtk4_sys::GtkDialog, ok.as_ptr(), gtk4_sys::GTK_RESPONSE_OK);
		style_response_button(dialog as *mut gtk4_sys::GtkDialog, gtk4_sys::GTK_RESPONSE_CANCEL);
		style_response_button(dialog as *mut gtk4_sys::GtkDialog, gtk4_sys::GTK_RESPONSE_OK);

		let content = gtk4_sys::gtk_dialog_get_content_area(dialog as *mut gtk4_sys::GtkDialog);
		gtk4_sys::gtk_box_set_spacing(content as *mut gtk4_sys::GtkBox, 10);
		gtk4_sys::gtk_widget_set_margin_start(content as *mut gtk4_sys::GtkWidget, 14);
		gtk4_sys::gtk_widget_set_margin_end(content as *mut gtk4_sys::GtkWidget, 14);
		gtk4_sys::gtk_widget_set_margin_top(content as *mut gtk4_sys::GtkWidget, 14);
		gtk4_sys::gtk_widget_set_margin_bottom(content as *mut gtk4_sys::GtkWidget, 14);

		let label = gtk4_sys::gtk_label_new(message.as_ptr());
		gtk4_sys::gtk_label_set_xalign(label as *mut gtk4_sys::GtkLabel, 0.0);
		let scrolled = gtk4_sys::gtk_scrolled_window_new();
		let text_view = gtk4_sys::gtk_text_view_new();
		let buffer = gtk4_sys::gtk_text_view_get_buffer(text_view as *mut gtk4_sys::GtkTextView);
		gtk4_sys::gtk_text_buffer_set_text(buffer, value.as_ptr(), -1);

		gtk4_sys::gtk_widget_set_size_request(scrolled, 480, 280);
		gtk4_sys::gtk_widget_set_vexpand(scrolled, 1);
		gtk4_sys::gtk_widget_set_hexpand(scrolled, 1);
		gtk4_sys::gtk_widget_set_vexpand(text_view, 1);
		gtk4_sys::gtk_widget_set_hexpand(text_view, 1);
		gtk4_sys::gtk_scrolled_window_set_child(scrolled as *mut gtk4_sys::GtkScrolledWindow, text_view);

		gtk4_sys::gtk_box_append(content as *mut gtk4_sys::GtkBox, label);
		gtk4_sys::gtk_box_append(content as *mut gtk4_sys::GtkBox, scrolled);

		run_dialog_f(dialog as *mut gtk4_sys::GtkDialog, |response| {
			if response == gtk4_sys::GTK_RESPONSE_OK {
				let mut start = std::mem::zeroed();
				let mut end = std::mem::zeroed();
				gtk4_sys::gtk_text_buffer_get_bounds(buffer, &mut start, &mut end);
				let text_ptr = gtk4_sys::gtk_text_buffer_get_text(buffer, &mut start, &mut end, 0);
				if text_ptr.is_null() {
					None
				} else {
					let text = CStr::from_ptr(text_ptr).to_string_lossy().to_string();
					gtk4_glib_sys::g_free(text_ptr as *mut _);
					Some(text)
				}
			} else {
				None
			}
		})
	}
}