Skip to content

Commit

Permalink
Examples: Dialog's response signal handling (#1514)
Browse files Browse the repository at this point in the history
* example of a dialog's response signal handling

* Apply suggestions Remove comment and one-time use statements

Co-authored-by: Bilal Elmoussaoui <[email protected]>

* Remove one-time use statement

* Remove useless part of ObjectImpl implement statement.

* Add gtk::Dialog in ui xml file. Remove old instance from code.
Allow deprecated use of gtk::Dialog.

* Apply suggestions group import, remove unused comment and blank lines.

Co-authored-by: Bilal Elmoussaoui <[email protected]>

* Replace redundant statements as suggested.

Co-authored-by: Bilal Elmoussaoui <[email protected]>

* Rename gtk::Dialog. Fix multiple identical call in match statement.

* Comment details about types used in callback handler for signal response.

* Rename callback handler popup_dialog in on_counter_notify

* Update examples/composite_dialog/my_app_window/mod.rs

Co-authored-by: jobale <[email protected]>

* Update examples/composite_dialog/my_app_window/mod.rs

---------

Co-authored-by: Bilal Elmoussaoui <[email protected]>
  • Loading branch information
jobale and bilelmoussaoui authored Jan 29, 2024
1 parent 234b1db commit d48338f
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 0 deletions.
4 changes: 4 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ name = "composite_template"
path = "composite_template/main.rs"
required-features = ["v4_10"]

[[bin]]
name = "composite_dialog"
path = "composite_dialog/main.rs"

[[bin]]
name = "confetti_snapshot_animation"
path = "confetti_snapshot_animation/main.rs"
Expand Down
6 changes: 6 additions & 0 deletions examples/composite_dialog/README.md
Original file line number Diff line number Diff line change
@@ -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)
16 changes: 16 additions & 0 deletions examples/composite_dialog/main.rs
Original file line number Diff line number Diff line change
@@ -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()
}
41 changes: 41 additions & 0 deletions examples/composite_dialog/my_app_window/imp.rs
Original file line number Diff line number Diff line change
@@ -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<i32>,
#[template_child]
pub count_label: TemplateChild<gtk::Label>,
#[template_child]
pub plus: TemplateChild<gtk::Button>,
#[template_child]
pub minus: TemplateChild<gtk::Button>,
#[template_child]
pub dialog: TemplateChild<gtk::Dialog>,
}

#[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<Self>) {
obj.init_template();
}
}

#[glib::derived_properties]
impl ObjectImpl for MyAppWindow {}
impl WidgetImpl for MyAppWindow {}
impl WindowImpl for MyAppWindow {}
impl ApplicationWindowImpl for MyAppWindow {}
69 changes: 69 additions & 0 deletions examples/composite_dialog/my_app_window/mod.rs
Original file line number Diff line number Diff line change
@@ -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<imp::MyAppWindow>)
@extends gtk::Widget, gtk::Window, gtk::ApplicationWindow;
}

#[gtk::template_callbacks]
impl MyAppWindow {
pub fn new<P: IsA<gtk::Application>>(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: &gtk::Button) {
let n = self.counter() + 1;
self.set_counter(n);
}

/// Callback handler for gtk::Button minus.
#[template_callback]
fn sub_to_counter(&self, _button: &gtk::Button) {
let n = self.counter() - 1;
self.set_counter(n);
}
}
84 changes: 84 additions & 0 deletions examples/composite_dialog/my_app_window/window.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?xml version='1.0' encoding='UTF-8'?>
<interface>
<template class="MyAppWindow" parent="GtkApplicationWindow">
<property name="default-height">300</property>
<property name="default-width">400</property>
<property name="hexpand">True</property>
<property name="show-menubar">True</property>
<property name="title">Composite Template Dialog</property>
<property name="vexpand">True</property>
<signal name="notify::counter" handler="on_counter_notify" swapped="true"/>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="margin-top">10</property>
<property name="spacing">5</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkLabel">
<property name="label">Click until you reach 3</property>
<style>
<class name="large-title"/>
</style>
</object>
</child>
<child>
<object class="GtkBox">
<property name="halign">center</property>
<property name="valign">center</property>
<property name="spacing">10</property>
<child>
<object class="GtkButton" id="plus">
<signal name="clicked" handler="add_to_counter" swapped="true"/>
<property name="label">+</property>
<property name="focus-on-click">True</property>
</object>
</child>
<child>
<object class="GtkButton" id="minus">
<signal name="clicked" handler="sub_to_counter" swapped="true"/>
<property name="label">-</property>
<property name="focus-on-click">True</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="count_label">
<property name="label"
bind-source="MyAppWindow"
bind-property="counter"
bind-flags="sync-create"/>
<property name="justify">center</property>
<property name="margin-end">5</property>
<property name="margin-start">5</property>
<property name="margin-top">20</property>
<property name="margin-bottom">5</property>
</object>
</child>
</object>
</child>
</template>
<object class="GtkDialog" id="dialog">
<property name="transient-for">MyAppWindow</property>
<property name="title">Counter reach 3</property>
<property name="modal">True</property>
<signal name="response" handler="counter_choice" swapped="true"/>
<child type="action">
<object class="GtkButton" id="button_six">
<property name="visible">True</property>
<property name="label">Set counter to 6</property>
</object>
</child>
<child type="action">
<object class="GtkButton" id="button_reset">
<property name="visible">True</property>
<property name="label">Reset counter</property>
</object>
</child>
<action-widgets>
<action-widget response="35">button_six</action-widget>
<action-widget response="ok">button_reset</action-widget>
</action-widgets>
</object>
</interface>
Binary file added examples/composite_dialog/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit d48338f

Please sign in to comment.