#[REVIEW] Just a simple GTK-RS App

5 messages · Page 1 of 1 (latest)

burnt tartan
#
// main.rs
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Box, glib};
mod count_button;
use count_button::{CountButton, CountRole};
const APP_ID: &str = "org.gtk_rs.HelloWorld2";

fn main() -> glib::ExitCode {
    let app = Application::builder().application_id(APP_ID).build();

    app.connect_activate(build_ui);

    app.run()
}

fn build_ui(app: &Application) {
    let button_increase = CountButton::new(CountRole::Add);
    button_increase.set_margin_top(12);
    button_increase.set_margin_bottom(12);
    button_increase.set_margin_start(12);
    button_increase.set_margin_end(12);

    let button_decrease = CountButton::new(CountRole::Sub);
    button_decrease.set_margin_top(12);
    button_decrease.set_margin_bottom(12);
    button_decrease.set_margin_start(12);
    button_decrease.set_margin_end(12);

    button_increase
        .bind_property("count", &button_decrease, "count")
        .sync_create()
        .bidirectional()
        .build();

    let view_box = Box::builder()
        .orientation(gtk::Orientation::Vertical)
        .halign(gtk::Align::Center)
        .spacing(4)
        .valign(gtk::Align::Center)
        .build();
    view_box.append(&button_increase);

    view_box.append(&button_decrease);

    let window = ApplicationWindow::builder()
        .application(app)
        .title("GTK App")
        .child(&view_box)
        .build();

    window.present();
}

#
// count_button/mod.rs

use glib::Object;
use gtk::glib;

glib::wrapper! {
    pub struct CountButton(ObjectSubclass<imp::CountButton>)
        @extends gtk::Button, gtk::Widget,
        @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, glib::Enum)]
#[repr(i32)]
#[enum_type(name = "CountRole")]
#[derive(Default)]
pub enum CountRole {
    #[default]
    Add,
    Sub,
}
impl CountButton {
    pub fn new(role: CountRole) -> Self {
        Object::builder().property("role", role).build()
    }

    pub fn with_label(label: &str) -> Self {
        Object::builder().property("label", label).build()
    }
}

impl Default for CountButton {
    fn default() -> Self {
        Self::new(CountRole::Add)
    }
}
#
 imp {
    use glib::prelude::*;
    use gtk::glib::subclass::Signal;
    use gtk::glib::{
        self, ParamFlags, ParamSpec, ParamSpecBuilderExt, ParamSpecEnum, ParamSpecInt, Properties,
    };
    use gtk::prelude::ButtonExt;
    use gtk::subclass::prelude::*;
    use once_cell::sync::Lazy;
    use std::cell::RefCell;

    use super::CountRole;
    macro_rules! update_field {
        ($field:ident, $self: expr, $value: expr) => {{
            let val = $value.get().expect("Type conformity checked by `glib`");
            $self.$field.replace(val);
        }};
    }

    #[derive(Default, Properties)]
    #[properties(wrapper_type = super::CountButton)]
    pub struct CountButton {
        #[property(get, set)]
        count: RefCell<i32>,
        role: RefCell<CountRole>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for CountButton {
        const NAME: &'static str = "GtkCountButton";
        type Type = super::CountButton;
        type ParentType = gtk::Button;
    }

    impl ObjectImpl for CountButton {
        fn signals() -> &'static [Signal] {
            static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
                let count_updated = Signal::builder("count-updated")
                    .param_types([i32::static_type()])
                    .build();
                vec![count_updated]
            });
            &SIGNALS
        }
        fn constructed(&self) {
            self.parent_constructed();
        }

#

        fn properties() -> &'static [glib::ParamSpec] {
            static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
                let count = ParamSpecInt::builder("count")
                    .default_value(0)
                    .blurb("The Count of the Button")
                    .flags(ParamFlags::READWRITE)
                    .build();
                let role = ParamSpecEnum::builder::<CountRole>("role")
                    .default_value(CountRole::Add)
                    .blurb("The Role of the Button")
                    .flags(ParamFlags::READWRITE)
                    .build();
                vec![count, role]
            });
            &PROPERTIES
        }
        fn property(&self, _id: usize, pspec: &ParamSpec) -> glib::Value {
            match pspec.name() {
                "count" => self.count.borrow().to_value(),
                "role" => self.role.borrow().to_value(),
                _ => unimplemented!(),
            }
        }
        fn set_property(&self, _id: usize, value: &glib::Value, pspec: &ParamSpec) {
            match pspec.name() {
                "count" => {
                    update_field!(count, self, value);
                    self.obj()
                        .set_label(self.count.borrow().clone().to_string().as_str());
                }
                "role" => update_field!(role, self, value),
                _ => unimplemented!(),
            }
        }
    }
#
    impl WidgetImpl for CountButton {
        fn map(&self) {
            self.parent_map();
            let obj = self.obj();

            obj.set_label(obj.count().to_string().as_str());
        }
    }

    impl ButtonImpl for CountButton {
        fn clicked(&self) {
            let mut count = *self.count.borrow();
            match *self.role.borrow() {
                CountRole::Add => count += 1,
                _ => count -= 1,
            }
            self.obj().set_count(count);
        }
    }
}

use glib::Object;
use gtk::glib;

glib::wrapper! {
    pub struct CountButton(ObjectSubclass<imp::CountButton>)
        @extends gtk::Button, gtk::Widget,
        @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, glib::Enum)]
#[repr(i32)]
#[enum_type(name = "CountRole")]
#[derive(Default)]
pub enum CountRole {
    #[default]
    Add,
    Sub,
}
impl CountButton {
    pub fn new(role: CountRole) -> Self {
        Object::builder().property("role", role).build()
    }

    pub fn with_label(label: &str) -> Self {
        Object::builder().property("label", label).build()
    }
}

impl Default for CountButton {
    fn default() -> Self {
        Self::new(CountRole::Add)
    }
}