[niepce] rust+ui: port rating label to Rust
- From: Hubert Figuière <hub src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [niepce] rust+ui: port rating label to Rust
- Date: Thu, 30 Jan 2020 05:33:52 +0000 (UTC)
commit f99b48117234468d11e6f654a75e86c4018d2b3c
Author: Hubert Figuière <hub figuiere net>
Date: Tue Jan 28 23:20:19 2020 -0500
rust+ui: port rating label to Rust
- Also remove the corresponding C++ code.
- Compile resources for pure Rust (examples)
Cargo.lock | 2 +
build.rs | 48 ++++-
crates/npc-fwk/Cargo.toml | 2 +
crates/npc-fwk/build.rs | 2 +
crates/npc-fwk/src/lib.rs | 3 +
crates/npc-fwk/src/toolkit/widgets/rating_label.rs | 211 ++++++++++++++++++++-
examples/widget-test.rs | 30 +++
src/fwk/toolkit/Makefile.am | 1 -
src/fwk/toolkit/metadatawidget.cpp | 34 ++--
src/fwk/toolkit/metadatawidget.hpp | 2 +
src/fwk/toolkit/widgets/ratinglabel.cpp | 174 -----------------
src/fwk/toolkit/widgets/ratinglabel.hpp | 67 -------
12 files changed, 318 insertions(+), 258 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 7fc03a0..00f69fc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -627,6 +627,8 @@ dependencies = [
"gio-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"glib 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gtk 0.8.0 (git+https://github.com/hfiguiere/gtk.git?branch=0.8.0-p1)",
+ "gtk-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"multimap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"once_cell 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/build.rs b/build.rs
index 046f2a4..0536573 100644
--- a/build.rs
+++ b/build.rs
@@ -4,9 +4,9 @@ use std::env;
use std::path::PathBuf;
fn main() {
+ let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
if env::var("SKIP_CBINDINGS").is_err() {
// Use cbindgen to generate C bindings.
- let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let target_dir = env::var("CARGO_TARGET_DIR").unwrap_or(String::from("./target"));
let mut target_file = PathBuf::from(target_dir);
target_file.push("bindings.h");
@@ -34,4 +34,50 @@ fn main() {
.expect("Couldn't generate bindings")
.write_to_file(&target_file);
}
+
+ #[cfg(examples)]
+ {
+ use std::fs::remove_file;
+ use std::path::Path;
+ use std::process::Command;
+ use std::str::from_utf8;
+
+ // Remove old versions of the gresource to make sure we're using the latest version
+ if Path::new("examples/gresource.gresource").exists() {
+ remove_file("examples/gresource.gresource").unwrap();
+ }
+
+ // Compile Gresource
+ let mut source_dir = String::from("--sourcedir=");
+ source_dir.push_str(&crate_dir);
+ let mut target_dir = String::from("--target=");
+ let mut target_path = PathBuf::from(crate_dir);
+ target_path.push("examples");
+ target_path.push("gresource.gresource");
+ target_dir.push_str(target_path.to_str().unwrap());
+ let output =
+ Command::new(option_env!("GRESOURCE_BINARY_PATH").unwrap_or("glib-compile-resources"))
+ .args(&[
+ "--generate",
+ "gresource.xml",
+ source_dir.as_str(),
+ target_dir.as_str(),
+ ])
+ .current_dir("src/niepce")
+ .output()
+ .unwrap();
+
+ if !output.status.success() {
+ println!("Failed to generate GResources!");
+ println!(
+ "glib-compile-resources stdout: {}",
+ from_utf8(&output.stdout).unwrap()
+ );
+ println!(
+ "glib-compile-resources stderr: {}",
+ from_utf8(&output.stderr).unwrap()
+ );
+ panic!("Can't continue build without GResources!");
+ }
+ }
}
diff --git a/crates/npc-fwk/Cargo.toml b/crates/npc-fwk/Cargo.toml
index d9a296a..33df4f6 100644
--- a/crates/npc-fwk/Cargo.toml
+++ b/crates/npc-fwk/Cargo.toml
@@ -15,8 +15,10 @@ gio-sys = "*"
gio = "^0.8.0"
glib-sys = "*"
glib = { version = "^0.9.0" }
+gtk-sys = "*"
gdk = "^0.12.0"
gdk-pixbuf = "0.8.0"
+gtk = { version = "^0.8.0", git = "https://github.com/hfiguiere/gtk.git", branch = "0.8.0-p1" }
libc = "0.2.39"
multimap = "0.4.0"
once_cell = "^0"
diff --git a/crates/npc-fwk/build.rs b/crates/npc-fwk/build.rs
index 0126961..1850b30 100644
--- a/crates/npc-fwk/build.rs
+++ b/crates/npc-fwk/build.rs
@@ -16,6 +16,8 @@ fn main() {
.with_language(cbindgen::Language::Cxx)
.with_parse_deps(true)
.with_parse_exclude(&["exempi", "chrono", "multimap"])
+ .exclude_item("GtkDrawingArea")
+ .exclude_item("GtkWidget")
.exclude_item("GtkWindow")
.exclude_item("GtkToolbar")
.exclude_item("GFileInfo")
diff --git a/crates/npc-fwk/src/lib.rs b/crates/npc-fwk/src/lib.rs
index b474b88..1c2899d 100644
--- a/crates/npc-fwk/src/lib.rs
+++ b/crates/npc-fwk/src/lib.rs
@@ -23,8 +23,11 @@ extern crate gdk;
extern crate gdk_pixbuf;
extern crate gio;
extern crate gio_sys;
+#[macro_use]
extern crate glib;
extern crate glib_sys;
+extern crate gtk;
+extern crate gtk_sys;
extern crate libc;
extern crate multimap;
extern crate once_cell;
diff --git a/crates/npc-fwk/src/toolkit/widgets/rating_label.rs
b/crates/npc-fwk/src/toolkit/widgets/rating_label.rs
index 937dd21..d50d67b 100644
--- a/crates/npc-fwk/src/toolkit/widgets/rating_label.rs
+++ b/crates/npc-fwk/src/toolkit/widgets/rating_label.rs
@@ -17,9 +17,21 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+use libc::c_int;
+use std::cell::Cell;
+
use cairo;
use gdk::prelude::*;
use gdk_pixbuf::Pixbuf;
+use glib::subclass;
+use glib::subclass::prelude::*;
+use glib::translate::*;
+use glib::Type;
+use gtk;
+use gtk::prelude::*;
+use gtk::subclass::prelude::*;
+use gtk_sys;
+
use once_cell::unsync::Lazy;
struct Pixbufs {
@@ -32,7 +44,125 @@ const PIXBUFS: Lazy<Pixbufs> = Lazy::new(|| Pixbufs {
unstar: Pixbuf::new_from_resource("/org/gnome/Niepce/pixmaps/niepce-unset-star.png").unwrap(),
});
-pub struct RatingLabel {}
+glib_wrapper! {
+ pub struct RatingLabel(
+ Object<subclass::simple::InstanceStruct<RatingLabelPriv>,
+ subclass::simple::ClassStruct<RatingLabelPriv>, RatingLabelClass>)
+ @extends gtk::DrawingArea, gtk::Widget;
+
+ match fn {
+ get_type => || RatingLabelPriv::get_type().to_glib(),
+ }
+}
+
+pub struct RatingLabelPriv {
+ editable: Cell<bool>,
+ rating: Cell<i32>,
+}
+
+impl RatingLabelPriv {
+ fn set_editable(&self, editable: bool) {
+ self.editable.set(editable);
+ }
+
+ fn set_rating(&self, rating: i32) {
+ self.rating.set(rating);
+ let w = self.get_instance();
+ w.queue_draw();
+ }
+}
+
+static PROPERTIES: [subclass::Property; 1] = [subclass::Property("rating", |rating| {
+ glib::ParamSpec::int(
+ rating,
+ "Rating",
+ "The rating value",
+ 0,
+ 5,
+ 0,
+ glib::ParamFlags::READWRITE,
+ )
+})];
+
+impl ObjectSubclass for RatingLabelPriv {
+ const NAME: &'static str = "RatingLabel";
+ type ParentType = gtk::DrawingArea;
+ type Instance = subclass::simple::InstanceStruct<Self>;
+ type Class = subclass::simple::ClassStruct<Self>;
+
+ glib_object_subclass!();
+
+ fn class_init(klass: &mut Self::Class) {
+ klass.install_properties(&PROPERTIES);
+ klass.add_signal(
+ "rating-changed",
+ glib::SignalFlags::RUN_LAST,
+ &[Type::I32],
+ Type::Unit,
+ );
+ }
+
+ fn new() -> Self {
+ Self {
+ editable: Cell::new(true),
+ rating: Cell::new(0),
+ }
+ }
+}
+
+impl ObjectImpl for RatingLabelPriv {
+ glib_object_impl!();
+
+ fn constructed(&self, obj: &glib::Object) {
+ self.parent_constructed(obj);
+
+ let widget = self.get_instance();
+ widget.connect_realize(|w| {
+ let priv_ = RatingLabelPriv::from_instance(w);
+ if priv_.editable.get() {
+ if let Some(win) = w.get_window() {
+ let mut mask = win.get_events();
+ mask |= gdk::EventMask::BUTTON_PRESS_MASK;
+ win.set_events(mask);
+ }
+ }
+ });
+ }
+
+ fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
+ let prop = &PROPERTIES[id];
+
+ match *prop {
+ subclass::Property("rating", ..) => {
+ let rating = value
+ .get_some()
+ .expect("type conformity checked by `Object::set_property`");
+ self.set_rating(rating);
+ }
+ _ => unimplemented!(),
+ }
+ }
+
+ fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
+ let prop = &PROPERTIES[id];
+
+ match *prop {
+ subclass::Property("rating", ..) => Ok(self.rating.get().to_value()),
+ _ => unimplemented!(),
+ }
+ }
+}
+
+pub trait RatingLabelExt {
+ fn set_rating(&self, rating: i32);
+}
+
+impl RatingLabelExt for RatingLabel {
+ fn set_rating(&self, rating: i32) {
+ let priv_ = RatingLabelPriv::from_instance(self);
+ priv_.set_rating(rating);
+ }
+}
impl RatingLabel {
pub fn get_star() -> Pixbuf {
@@ -78,4 +208,83 @@ impl RatingLabel {
let width: f64 = Self::get_star().get_width().into();
(x / width).round() as i32
}
+
+ pub fn new(rating: i32, editable: bool) -> Self {
+ let obj: Self = glib::Object::new(Self::static_type(), &[])
+ .expect("Failed to create RatingLabel")
+ .downcast()
+ .expect("Created RatingLabel is of the wrong type");
+
+ let priv_ = RatingLabelPriv::from_instance(&obj);
+ priv_.set_editable(editable);
+ priv_.set_rating(rating);
+ obj
+ }
+}
+
+impl DrawingAreaImpl for RatingLabelPriv {}
+impl WidgetImpl for RatingLabelPriv {
+ fn button_press_event(&self, _widget: >k::Widget, event: &gdk::EventButton) -> Inhibit {
+ if event.get_button() != 1 {
+ Inhibit(false)
+ } else {
+ if let Some((x, _)) = event.get_coords() {
+ let new_rating = RatingLabel::rating_value_from_hit_x(x);
+ if new_rating != self.rating.get() {
+ self.set_rating(new_rating);
+ if let Err(err) = self.get_instance().emit("rating-changed", &[&new_rating]) {
+ err_out!("Emit signal 'rating-changed' failed: {}", err);
+ }
+ }
+ }
+ Inhibit(true)
+ }
+ }
+
+ fn draw(&self, _widget: >k::Widget, cr: &cairo::Context) -> Inhibit {
+ let star = RatingLabel::get_star();
+ let x = 0_f64;
+ let y = star.get_height() as f64;
+ RatingLabel::draw_rating(
+ cr,
+ self.rating.get(),
+ &star,
+ &RatingLabel::get_unstar(),
+ x,
+ y,
+ );
+
+ Inhibit(true)
+ }
+
+ fn get_preferred_width(&self, _widget: >k::Widget) -> (i32, i32) {
+ let w = RatingLabel::get_star().get_width() * 5;
+ (w, w)
+ }
+
+ fn get_preferred_height(&self, _widget: >k::Widget) -> (i32, i32) {
+ let h = RatingLabel::get_star().get_height();
+ (h, h)
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fwk_rating_label_new(
+ rating: c_int,
+ editable: bool,
+) -> *mut gtk_sys::GtkWidget {
+ RatingLabel::new(rating, editable)
+ .upcast::<gtk::Widget>()
+ .to_glib_full()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn fwk_rating_label_set_rating(
+ widget: *mut gtk_sys::GtkDrawingArea,
+ rating: i32,
+) {
+ let rating_label = gtk::DrawingArea::from_glib_borrow(widget)
+ .downcast::<RatingLabel>()
+ .expect("Not a RatingLabel widget");
+ rating_label.set_rating(rating);
}
diff --git a/examples/widget-test.rs b/examples/widget-test.rs
index 289dfc6..1f17097 100644
--- a/examples/widget-test.rs
+++ b/examples/widget-test.rs
@@ -18,12 +18,35 @@
*/
extern crate gdk_pixbuf;
+extern crate gio;
+extern crate glib;
extern crate gtk;
extern crate niepce_rust;
+extern crate npc_fwk;
+use gio::{resources_register, Resource};
+use glib::{Bytes, Error};
use gtk::prelude::*;
+
use niepce_rust::niepce::ui::thumb_nav::{ThumbNav, ThumbNavMode};
use niepce_rust::niepce::ui::thumb_strip_view::ThumbStripView;
+use npc_fwk::toolkit::widgets::rating_label::RatingLabel;
+
+fn init() -> Result<(), Error> {
+ // load the gresource binary at build time and include/link it into the final
+ // binary.
+ let res_bytes = include_bytes!("gresource.gresource");
+
+ // Create Resource it will live as long the value lives.
+ let gbytes = Bytes::from_static(res_bytes.as_ref());
+ let resource = Resource::new_from_data(&gbytes)?;
+
+ // Register the resource so it won't be dropped and will continue to live in
+ // memory.
+ resources_register(&resource);
+
+ Ok(())
+}
pub fn main() {
if let Err(err) = gtk::init() {
@@ -31,6 +54,11 @@ pub fn main() {
panic!();
}
+ if let Err(err) = init() {
+ println!("main: init failed: {}", err);
+ panic!();
+ }
+
let model = gtk::ListStore::new(&[gdk_pixbuf::Pixbuf::static_type()]);
let thumbview = ThumbStripView::new(&model.upcast::<gtk::TreeModel>());
let thn = ThumbNav::new(
@@ -41,6 +69,8 @@ pub fn main() {
thn.set_size_request(-1, 134);
let box_ = gtk::Box::new(gtk::Orientation::Vertical, 0);
+ let rating = RatingLabel::new(3, true);
+ box_.pack_start(&rating, false, false, 0);
box_.pack_start(&thn, false, false, 0);
let window = gtk::Window::new(gtk::WindowType::Toplevel);
diff --git a/src/fwk/toolkit/Makefile.am b/src/fwk/toolkit/Makefile.am
index c67834a..65f03d5 100644
--- a/src/fwk/toolkit/Makefile.am
+++ b/src/fwk/toolkit/Makefile.am
@@ -49,7 +49,6 @@ libniepceframework_a_SOURCES = configuration.hpp configuration.cpp \
gtkutils.hpp gtkutils.cpp \
gphoto.hpp gphoto.cpp \
widgets/addinstreemodel.hpp widgets/addinstreemodel.cpp \
- widgets/ratinglabel.hpp widgets/ratinglabel.cpp \
widgets/toolboxitemwidget.hpp widgets/toolboxitemwidget.cpp \
widgets/editablehscale.hpp widgets/editablehscale.cpp \
widgets/dock.cpp widgets/dock.hpp \
diff --git a/src/fwk/toolkit/metadatawidget.cpp b/src/fwk/toolkit/metadatawidget.cpp
index 81383a1..c24eb12 100644
--- a/src/fwk/toolkit/metadatawidget.cpp
+++ b/src/fwk/toolkit/metadatawidget.cpp
@@ -1,7 +1,7 @@
/*
* niepce - fwk/toolkit/metadatawidget.cpp
*
- * Copyright (C) 2008-2019 Hubert Figuiere
+ * Copyright (C) 2008-2020 Hubert Figuière
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,12 +28,12 @@
#include <gtkmm/label.h>
#include <gtkmm/entry.h>
#include <gtkmm/textview.h>
+#include <gtkmm/drawingarea.h>
#include "fwk/base/debug.hpp"
#include "fwk/base/autoflag.hpp"
#include "fwk/utils/exempi.hpp"
#include "fwk/utils/stringutils.hpp"
-#include "fwk/toolkit/widgets/ratinglabel.hpp"
#include "fwk/toolkit/widgets/notabtextview.hpp"
#include "fwk/toolkit/widgets/tokentextview.hpp"
@@ -63,16 +63,22 @@ void MetaDataWidget::set_data_format(const MetaDataSectionFormat * fmt)
create_widgets_for_format(fmt);
}
-Gtk::Widget*
+void MetaDataWidget::rating_callback(GtkWidget* w, gint rating, gpointer user_data)
+{
+ auto self = static_cast<MetaDataWidget*>(user_data);
+ auto id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), "id"));
+ self->on_int_changed(rating, id);
+}
+
+Gtk::Widget*
MetaDataWidget::create_star_rating_widget(bool readonly, uint32_t id)
{
- fwk::RatingLabel* r = Gtk::manage(new fwk::RatingLabel(0, !readonly));
- if(!readonly) {
- r->signal_changed.connect(
- sigc::bind(
- sigc::mem_fun(*this,
- &MetaDataWidget::on_int_changed),
- id));
+ Gtk::DrawingArea* r =
+ Gtk::manage(Glib::wrap(
+ GTK_DRAWING_AREA(ffi::fwk_rating_label_new(0, !readonly))));
+ if (!readonly) {
+ r->set_data("id", GINT_TO_POINTER(id));
+ g_signal_connect(r->gobj(), "rating-changed", G_CALLBACK(rating_callback), this);
}
return r;
}
@@ -218,9 +224,9 @@ void MetaDataWidget::clear_widget(const std::pair<const PropertyIndex, Gtk::Widg
tv->get_buffer()->set_text("");
return;
}
- fwk::RatingLabel * rl = dynamic_cast<fwk::RatingLabel*>(p.second);
- if(rl) {
- rl->set_rating(0);
+ Gtk::DrawingArea* rl = dynamic_cast<Gtk::DrawingArea*>(p.second);
+ if (rl) {
+ ffi::fwk_rating_label_set_rating(rl->gobj(), 0);
return;
}
}
@@ -314,7 +320,7 @@ bool MetaDataWidget::set_star_rating_data(Gtk::Widget* w,
try {
int rating = fwk_property_value_get_integer(value.get());
AutoFlag flag(m_update);
- static_cast<fwk::RatingLabel*>(w)->set_rating(rating);
+ ffi::fwk_rating_label_set_rating(static_cast<Gtk::DrawingArea*>(w)->gobj(), rating);
}
catch(...) {
return false;
diff --git a/src/fwk/toolkit/metadatawidget.hpp b/src/fwk/toolkit/metadatawidget.hpp
index 24459bd..9222492 100644
--- a/src/fwk/toolkit/metadatawidget.hpp
+++ b/src/fwk/toolkit/metadatawidget.hpp
@@ -112,6 +112,8 @@ private:
fwk::PropertyBagPtr m_current_data;
const MetaDataSectionFormat * m_fmt;
bool m_update;
+
+ static void rating_callback(GtkWidget* w, gint rating, gpointer user_data);
};
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]