// 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();
}
#[REVIEW] Just a simple GTK-RS App
5 messages · Page 1 of 1 (latest)
// 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)
}
}