diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 99a7e256f0af..9d0b1f509afd 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -57,6 +57,10 @@ path = "column_view_datagrid/main.rs" name = "composite_template" path = "composite_template/main.rs" +[[bin]] +name = "composite_dialog" +path = "composite_dialog/main.rs" + [[bin]] name = "confetti_snapshot_animation" path = "confetti_snapshot_animation/main.rs" @@ -187,4 +191,3 @@ path = "video_player/main.rs" [[bin]] name = "virtual_methods" path = "virtual_methods/main.rs" - diff --git a/examples/composite_dialog/README.md b/examples/composite_dialog/README.md new file mode 100644 index 000000000000..52f5b92e4f92 --- /dev/null +++ b/examples/composite_dialog/README.md @@ -0,0 +1,6 @@ +# Composite Template Dialog + +This example shows the use of callbacks, custom and bind property, notify and response signal in a composite +template situation. + +![Screenshot](screenshot.png) \ No newline at end of file diff --git a/examples/composite_dialog/main.rs b/examples/composite_dialog/main.rs new file mode 100644 index 000000000000..7fcd319ed4f3 --- /dev/null +++ b/examples/composite_dialog/main.rs @@ -0,0 +1,16 @@ +mod my_app_window; + +use gtk::{glib, prelude::*}; +use my_app_window::MyAppWindow; + +fn main() -> glib::ExitCode { + let application = gtk::Application::builder() + .application_id("com.github.gtk-rs.examples.composite_dialog") + .build(); + + application.connect_activate(|app| { + let win = MyAppWindow::new(app); + win.present(); + }); + application.run() +} diff --git a/examples/composite_dialog/my_app_window/imp.rs b/examples/composite_dialog/my_app_window/imp.rs new file mode 100644 index 000000000000..f760dd423422 --- /dev/null +++ b/examples/composite_dialog/my_app_window/imp.rs @@ -0,0 +1,41 @@ +use std::cell::Cell; + +use gtk::{glib, prelude::*, subclass::prelude::*}; + +#[derive(Default, gtk::CompositeTemplate, glib::Properties)] +#[template(file = "window.ui")] +#[properties(wrapper_type = super::MyAppWindow)] +pub struct MyAppWindow { + #[property(get, set)] + counter: Cell, + #[template_child] + pub count_label: TemplateChild, + #[template_child] + pub plus: TemplateChild, + #[template_child] + pub minus: TemplateChild, + #[template_child] + pub dialog: TemplateChild, +} + +#[glib::object_subclass] +impl ObjectSubclass for MyAppWindow { + const NAME: &'static str = "MyAppWindow"; + type Type = super::MyAppWindow; + type ParentType = gtk::ApplicationWindow; + + fn class_init(klass: &mut Self::Class) { + klass.bind_template(); + klass.bind_template_instance_callbacks(); + } + + fn instance_init(obj: &glib::subclass::InitializingObject) { + obj.init_template(); + } +} + +#[glib::derived_properties] +impl ObjectImpl for MyAppWindow {} +impl WidgetImpl for MyAppWindow {} +impl WindowImpl for MyAppWindow {} +impl ApplicationWindowImpl for MyAppWindow {} diff --git a/examples/composite_dialog/my_app_window/mod.rs b/examples/composite_dialog/my_app_window/mod.rs new file mode 100644 index 000000000000..36c2dce3dc6c --- /dev/null +++ b/examples/composite_dialog/my_app_window/mod.rs @@ -0,0 +1,69 @@ +// Since Gtk4 v4.10 gtk4::Dialog is deprecated and gtk4-rs's examples minimum version is v4.10 +#[allow(deprecated)] +mod imp; + +use gtk::{glib, prelude::*, subclass::prelude::*}; + +glib::wrapper! { + pub struct MyAppWindow(ObjectSubclass) + @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow; +} + +#[gtk::template_callbacks] +impl MyAppWindow { + pub fn new>(app: &P) -> Self { + glib::Object::builder().property("application", app).build() + } + + /// Callback handler for `notify::counter` signal. + /// + /// When counter property reach 3, a dialog pops up and present the user + /// with 2 choices: Set the counter to 6 or reset the counter to 0. + #[template_callback] + fn on_counter_notify(&self, _p: &glib::ParamSpec) { + // Check counter property and create a Dialog. + if self.counter() == 3 { + self.imp().dialog.present(); + } + } + + /// Handler for dialog's response. + /// + /// In the callback handler, response type is i32 instead of gtk::ResponseType. + /// As the bindings replaces the original i32 to an enum that makes more sense in + /// Rust. Although, the template callback can't be changed. + /// + /// To illustrate this, we can replace `i32` with `gtk::ResponseType` in this handler's signature + /// and compile. + /// However, at runtime, a crash would occur when the dialog emits a signal response (when clicking + /// a dialog's button): This handler signature ask for a `gtk::ResponseType` but receive a `i32`. + /// The error message details this discrepancy: + /// + /// *Wrong type for argument 1 in template callback \`counter_choice\`: + /// ValueTypeMismatchError { actual: gint, requested: GtkResponseType }* + #[template_callback] + fn counter_choice(&self, response: i32) { + self.imp().dialog.set_visible(false); + + match gtk::ResponseType::from(response) { + gtk::ResponseType::Ok => self.set_counter(0), + gtk::ResponseType::Other(35) => self.set_counter(6), + gtk::ResponseType::DeleteEvent => (), + _ => unimplemented!(), + } + } + + /// Callback handler for gtk::Button plus. + #[template_callback] + fn add_to_counter(&self, _button: >k::Button) { + let n = self.counter() + 1; + self.set_counter(n); + } + + /// Callback handler for gtk::Button minus. + #[template_callback] + fn sub_to_counter(&self, _button: >k::Button) { + let n = self.counter() - 1; + self.set_counter(n); + } +} diff --git a/examples/composite_dialog/my_app_window/window.ui b/examples/composite_dialog/my_app_window/window.ui new file mode 100755 index 000000000000..b40046ab237c --- /dev/null +++ b/examples/composite_dialog/my_app_window/window.ui @@ -0,0 +1,84 @@ + + + + + MyAppWindow + Counter reach 3 + True + + + + True + Set counter to 6 + + + + + True + Reset counter + + + + button_six + button_reset + + + diff --git a/examples/composite_dialog/screenshot.png b/examples/composite_dialog/screenshot.png new file mode 100644 index 000000000000..b2671ff8cbc6 Binary files /dev/null and b/examples/composite_dialog/screenshot.png differ