Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples: Dialog's response signal handling #1514

Merged
merged 13 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -187,4 +191,3 @@ path = "video_player/main.rs"
[[bin]]
name = "virtual_methods"
path = "virtual_methods/main.rs"

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.
jobale marked this conversation as resolved.
Show resolved Hide resolved
/// 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.
Loading