Skip to content

Instantly share code, notes, and snippets.

@lll9p
Last active December 14, 2022 16:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lll9p/dd465f1236f4db365c76984c9859f437 to your computer and use it in GitHub Desktop.
Save lll9p/dd465f1236f4db365c76984c9859f437 to your computer and use it in GitHub Desktop.
Custom widget
pub fn create_widgets_on_window(app: &Application) -> ApplicationWindow {
let container = gtk::Box::new(gtk::Orientation::Vertical, 5);
let shape_widget = shape::Shape::new(); // create custom shape widget.
let class = 0;
let mut shape_data = shapes::Shape::new_polygon(class);
shape_widget.init_shape(shapes::Shape::new_polygon(class));
use shapes::Point;
let path = [
Point::new(209., 393., class),
Point::new(339., 207., class),
Point::new(37., 77., class),
Point::new(37., 16., class),
Point::new(391., 103., class),
Point::new(421., 263., class),
];
path.into_iter().for_each(|point| {
shape_data.add_data(point);
shape_widget.update_shape(&shape_data);
});
container.append(&shape_widget);
let shape_widget = shape::Shape::new();// create second custom shape widget.
let class = 1;
let mut shape_data = shapes::Shape::new_polygon(class);
shape_widget.init_shape(shapes::Shape::new_polygon(class));
let path = [
Point::new(40., 100., class),
Point::new(120., 500., class),
Point::new(150., 15., class),
];
path.into_iter().for_each(|point| {
shape_data.add_data(point);
shape_widget.update_shape(&shape_data);
});
container.append(&shape_widget);
let window = cascade! {
ApplicationWindow::new(app);
..add_css_class(if PROFILE == "Devel" {
"devel"
} else {
""
});
..set_show_menubar(true);
..set_child(Some(&container));
};
window.present();
window
}
use crate::shapes::{self, DrawBehavier, ShapeBehavier};
use gtk::{
glib::{self, subclass::Signal},
prelude::{GestureExt, WidgetExt},
subclass::prelude::{
ObjectImpl, ObjectImplExt, ObjectSubclass, ObjectSubclassExt, ObjectSubclassIsExt,
WidgetClassSubclassExt, WidgetImpl,
},
traits::SnapshotExt,
};
use std::{cell::RefCell, rc::Rc};
glib::wrapper! {
pub struct Shape(ObjectSubclass<ShapePrivate>)
@extends gtk::Widget,
@implements gtk::Accessible;
}
impl Default for Shape {
fn default() -> Self {
Self::new()
}
}
impl Shape {
pub fn new() -> Self {
glib::Object::new(&[])
}
pub fn update_shape(&self, shape: &shapes::Shape) {
self.imp().shape.borrow_mut().translate_from(shape);
}
pub fn init_shape(&self, shape: shapes::Shape) {
*self.imp().shape.borrow_mut() = shape;
}
}
#[derive(Debug)]
pub struct ShapePrivate {
shape: Rc<RefCell<shapes::Shape>>,
}
impl ShapePrivate {
fn get_class(&self) -> usize {
self.shape.borrow().class()
}
}
impl Default for ShapePrivate {
fn default() -> Self {
Self {
shape: Rc::new(RefCell::new(shapes::Shape::default())),
}
}
}
// Trait shared by all GObjects.
#[glib::object_subclass]
impl ObjectSubclass for ShapePrivate {
const NAME: &'static str = "Shape";
type Type = Shape;
type ParentType = gtk::Widget;
fn class_init(klass: &mut Self::Class) {
klass.set_css_name("shape");
klass.set_accessible_role(gtk::AccessibleRole::Widget);
}
}
// Trait shared by all widgets.
impl ObjectImpl for ShapePrivate {
fn constructed(&self) {
let obj = self.obj();
self.parent_constructed();
obj.add_css_class("shape");
// Connect a gesture to handle clicks.
let gesture = gtk::GestureClick::new();
gesture.connect_released(|gesture, _n_pressed, x, y| {
gesture.set_state(gtk::EventSequenceState::Claimed);
println!("Pressed!");
});
obj.add_controller(&gesture);
let evt = gtk::EventControllerMotion::new();
let shape = self.shape.clone();
evt.connect_enter(move |_c, _x, _y| {
println!("enter shape {}", shape.borrow().class());
});
let shape = self.shape.clone();
evt.connect_leave(move |_c| {
println!("leave shape {}", shape.borrow().class());
});
obj.add_controller(&evt);
}
fn signals() -> &'static [glib::subclass::Signal] {
Box::leak(Box::new([Signal::builder("update").build()]))
}
fn properties() -> &'static [glib::ParamSpec] {
Box::leak(Box::new([glib::ParamSpecUInt::new(
"shape-class",
"Shape-class",
"class of shape",
0,
u32::MAX,
0,
glib::ParamFlags::READWRITE,
)]))
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"shape-class" => {
if let Ok(class) = value.get::<u32>() {
unimplemented!();
};
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, _pspec: &glib::ParamSpec) -> glib::Value {
match _pspec.name() {
"shape-class" => unimplemented!(),
_ => unimplemented!(),
}
}
}
impl WidgetImpl for ShapePrivate {
fn contains(&self, x: f64, y: f64) -> bool {
let ret = self
.shape
.borrow()
.contains(x, y)
.map_or(false, |contains| contains);
ret
}
fn measure(&self, orientation: gtk::Orientation, for_size: i32) -> (i32, i32, i32, i32) {
if let Ok((min_x, min_y, max_x, max_y)) = self.shape.borrow().bounding_box() {
let width = max_x - min_x;
let height = max_y - min_y;
match orientation {
gtk::Orientation::Horizontal => (width as i32, width as i32, -1, -1),
gtk::Orientation::Vertical => (height as i32, height as i32, -1, -1),
_ => unreachable!(),
}
} else {
(0, 0, -1, -1)
}
}
fn snapshot(&self, snapshot: &gtk::Snapshot) {
// Save the original coordinate space
snapshot.save();
self.shape.borrow().draw(snapshot).unwrap();
// Restore original coordinate space
snapshot.restore();
// End the clip of widget bounds
// snapshot.pop();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment