Skip to content

Instantly share code, notes, and snippets.

@DrBrad
Last active March 5, 2025 00:08
Show Gist options
  • Save DrBrad/5a48899bcfac8b31be9b0d66a4174d53 to your computer and use it in GitHub Desktop.
Save DrBrad/5a48899bcfac8b31be9b0d66a4174d53 to your computer and use it in GitHub Desktop.
Rust GTK-3 Custom widget
use std::cell::RefCell;
use glib::{ParamFlags, ParamSpec, ParamSpecString, Propagation, Value};
use glib::once_cell::sync::Lazy;
use glib::Propagation::Proceed;
use glib::subclass::prelude::ObjectSubclassExt;
use gtk::prelude::*;
use gtk::{gdk, Allocation, Application, ApplicationWindow, Widget};
use gtk::cairo::Context;
use gtk::gdk::{EventMask, WindowAttr, WindowType};
use gtk::subclass::prelude::{ObjectImpl, ObjectSubclass, WidgetImpl, WidgetImplExt};
use gtk::glib;
#[derive(Default)]
pub struct SimpleWidgetImpl {
label: RefCell<Option<String>>
}
#[glib::object_subclass]
impl ObjectSubclass for SimpleWidgetImpl {
const NAME: &'static str = "SimpleWidget";
type ParentType = Widget;
type Type = SimpleWidget;
}
impl ObjectImpl for SimpleWidgetImpl {
fn properties() -> &'static [ParamSpec] {
static PROPERTIES: Lazy<[ParamSpec; 1]> = Lazy::new(|| [
ParamSpecString::builder("label").nick("Label").blurb("The text of the label").flags(ParamFlags::READWRITE).build()
]);
&*PROPERTIES
}
fn set_property(&self, id: usize, value: &Value, pspec: &ParamSpec) {
match pspec.name() {
"label" => {
let label: Option<String> = value.get().expect("Type mismatch");
self.label.replace(label);
}
_ => unimplemented!(),
}
}
fn property(&self, id: usize, pspec: &ParamSpec) -> Value {
match pspec.name() {
"label" => self.label.borrow().clone().to_value(),
_ => unimplemented!(),
}
}
}
impl WidgetImpl for SimpleWidgetImpl {
fn realize(&self) {
let widget = self.obj();
let allocation = widget.allocation();
let mut attr = WindowAttr::default();
attr.window_type = WindowType::Child;
attr.x = Some(allocation.x());
attr.y = Some(allocation.y());
attr.width = allocation.width();
attr.height = allocation.height();
let parent_window = widget.parent_window().unwrap();
let window = gdk::Window::new(Some(&parent_window), &attr);
widget.register_window(&window);
widget.set_window(window);
widget.set_realized(true);
}
fn draw(&self, cr: &Context) -> Propagation {
cr.set_source_rgb(0.5, 0.5, 0.5);
cr.paint();
cr.set_source_rgb(1.0, 0.0, 0.0);
cr.rectangle(0.0, 0.0, 100.0, 100.0);
cr.fill();
cr.set_source_rgb(0.0, 0.0, 0.0);
cr.move_to(200.0, 20.0);
cr.show_text(self.label.borrow().as_ref().unwrap());
Proceed
}
}
glib::wrapper! {
pub struct SimpleWidget(ObjectSubclass<SimpleWidgetImpl>)
@extends Widget;
}
impl SimpleWidget {
pub fn new(label: &str) -> Self {
let _self = glib::Object::new::<SimpleWidget>();
_self.set_properties(
&[("label", &label)]
);
_self
}
pub fn set_label(&self, label: &str) {
self.set_property("label", &label);
}
pub fn get_label(&self) -> Option<String> {
Some(self.property("label"))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment