[niepce] rust+ui: port ThumbStripView to Rust
- From: Hubert Figuière <hub src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [niepce] rust+ui: port ThumbStripView to Rust
- Date: Wed, 29 Jan 2020 01:13:19 +0000 (UTC)
commit ee09702a3bab0f1929966974c6fa199b5786ef2d
Author: Hubert Figuière <hub figuiere net>
Date: Tue Jan 28 01:15:47 2020 -0500
rust+ui: port ThumbStripView to Rust
build.rs | 1 +
examples/widget-test.rs | 11 +-
src/Makefile.am | 1 +
src/niepce/ui/Makefile.am | 1 -
src/niepce/ui/filmstripcontroller.cpp | 7 +-
src/niepce/ui/library_cell_renderer.rs | 7 -
src/niepce/ui/mod.rs | 1 +
src/niepce/ui/niepcewindow.cpp | 1 -
src/niepce/ui/thumb_strip_view.rs | 307 +++++++++++++++++++++++++++++++++
src/niepce/ui/thumbstripview.cpp | 194 ---------------------
src/niepce/ui/thumbstripview.hpp | 72 --------
11 files changed, 324 insertions(+), 279 deletions(-)
---
diff --git a/build.rs b/build.rs
index a6f2fd7..046f2a4 100644
--- a/build.rs
+++ b/build.rs
@@ -21,6 +21,7 @@ fn main() {
.exclude_item("GtkWindow")
.exclude_item("GtkToolbar")
.exclude_item("GtkTreeIter")
+ .exclude_item("GtkTreeModel")
.exclude_item("GtkIconView")
.exclude_item("GtkListStore")
.exclude_item("GtkTreePath")
diff --git a/examples/widget-test.rs b/examples/widget-test.rs
index 86aba4f..a96f7b9 100644
--- a/examples/widget-test.rs
+++ b/examples/widget-test.rs
@@ -17,11 +17,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+extern crate gdk_pixbuf;
extern crate gtk;
extern crate niepce_rust;
use gtk::prelude::*;
use niepce_rust::niepce::ui::thumb_nav::{ThumbNav, ThumbNavMode};
+use niepce_rust::niepce::ui::thumb_strip_view::ThumbStripView;
pub fn main() {
if let Err(err) = gtk::init() {
@@ -29,8 +31,13 @@ pub fn main() {
panic!();
}
- let thumbview = gtk::IconView::new();
- let thn = ThumbNav::new(&thumbview, ThumbNavMode::OneRow, true);
+ let model = gtk::ListStore::new(&[gdk_pixbuf::Pixbuf::static_type()]);
+ let thumbview = ThumbStripView::new(&model.upcast::<gtk::TreeModel>());
+ let thn = NpcThumbNav::new(
+ &thumbview.upcast::<gtk::IconView>(),
+ NpcThumbNavMode::OneRow,
+ true,
+ );
thn.set_size_request(-1, 134);
let box_ = gtk::Box::new(gtk::Orientation::Vertical, 0);
diff --git a/src/Makefile.am b/src/Makefile.am
index 64009cb..9c0f801 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -58,6 +58,7 @@ RUST_SOURCES = \
@top_srcdir@/src/niepce/ui/dialogs/requestnewfolder.rs \
@top_srcdir@/src/niepce/ui/thumb_nav.rs \
@top_srcdir@/src/niepce/ui/library_cell_renderer.rs \
+ @top_srcdir@/src/niepce/ui/thumb_strip_view.rs \
$(NULL)
EXTRA_DIST = \
diff --git a/src/niepce/ui/Makefile.am b/src/niepce/ui/Makefile.am
index 80975c0..a8a003c 100644
--- a/src/niepce/ui/Makefile.am
+++ b/src/niepce/ui/Makefile.am
@@ -40,6 +40,5 @@ libniepceui_a_SOURCES = \
dialogs/importers/cameraimporterui.cpp \
selectioncontroller.hpp selectioncontroller.cpp \
filmstripcontroller.hpp filmstripcontroller.cpp \
- thumbstripview.cpp thumbstripview.hpp \
$(PUBLICHEADERS) \
$(NULL)
diff --git a/src/niepce/ui/filmstripcontroller.cpp b/src/niepce/ui/filmstripcontroller.cpp
index 118c420..1d63895 100644
--- a/src/niepce/ui/filmstripcontroller.cpp
+++ b/src/niepce/ui/filmstripcontroller.cpp
@@ -24,7 +24,6 @@
#include "engine/library/thumbnailnotification.hpp"
#include "fwk/base/debug.hpp"
-#include "thumbstripview.hpp"
#include "filmstripcontroller.hpp"
namespace ui {
@@ -42,7 +41,11 @@ Gtk::Widget * FilmStripController::buildWidget()
return m_widget;
}
DBG_ASSERT(static_cast<bool>(m_store), "m_store NULL");
- m_thumbview = manage(new ThumbStripView(m_store));
+ // We need to ref m_store since it's held by the RefPtr<>
+ // and the ThumbStripView in Rust gets full ownership.
+ m_thumbview = manage(
+ Glib::wrap(GTK_ICON_VIEW(ffi::npc_thumb_strip_view_new(
+ GTK_TREE_MODEL(g_object_ref(m_store->gobjmm()->gobj()))))));
GtkWidget *thn = ffi::npc_thumb_nav_new(m_thumbview->gobj(),
ffi::ThumbNavMode::OneRow, true);
m_thumbview->set_selection_mode(Gtk::SELECTION_SINGLE);
diff --git a/src/niepce/ui/library_cell_renderer.rs b/src/niepce/ui/library_cell_renderer.rs
index e4b3471..909b702 100644
--- a/src/niepce/ui/library_cell_renderer.rs
+++ b/src/niepce/ui/library_cell_renderer.rs
@@ -646,10 +646,3 @@ pub unsafe extern "C" fn npc_library_cell_renderer_hit(
.expect("Expected a LibraryCellRenderer");
renderer.hit(x, y);
}
-
-#[no_mangle]
-pub unsafe extern "C" fn npc_library_thumb_cell_renderer_new() -> *mut gtk_sys::GtkCellRenderer {
- LibraryCellRenderer::new_thumb_renderer()
- .upcast::<gtk::CellRenderer>()
- .to_glib_full()
-}
diff --git a/src/niepce/ui/mod.rs b/src/niepce/ui/mod.rs
index 9e19cbb..d40ecdc 100644
--- a/src/niepce/ui/mod.rs
+++ b/src/niepce/ui/mod.rs
@@ -22,3 +22,4 @@ pub mod image_list_store;
pub mod imagetoolbar;
pub mod library_cell_renderer;
pub mod thumb_nav;
+pub mod thumb_strip_view;
diff --git a/src/niepce/ui/niepcewindow.cpp b/src/niepce/ui/niepcewindow.cpp
index d8973cc..7de3b77 100644
--- a/src/niepce/ui/niepcewindow.cpp
+++ b/src/niepce/ui/niepcewindow.cpp
@@ -41,7 +41,6 @@
#include "fwk/toolkit/gtkutils.hpp"
#include "libraryclient/uidataprovider.hpp"
-#include "thumbstripview.hpp"
#include "niepcewindow.hpp"
#include "dialogs/editlabels.hpp"
#include "selectioncontroller.hpp"
diff --git a/src/niepce/ui/thumb_strip_view.rs b/src/niepce/ui/thumb_strip_view.rs
new file mode 100644
index 0000000..c9428bd
--- /dev/null
+++ b/src/niepce/ui/thumb_strip_view.rs
@@ -0,0 +1,307 @@
+/*
+ * niepce - niepce/ui/thumbstripview.rs
+ *
+ * Copyright (C) 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use std::cell::{Cell, RefCell};
+use std::rc::Rc;
+
+use once_cell::unsync::OnceCell;
+
+use glib::subclass;
+use glib::subclass::prelude::*;
+use glib::translate::*;
+use gtk;
+use gtk::prelude::*;
+use gtk::subclass::prelude::*;
+
+use crate::niepce::ui::library_cell_renderer::LibraryCellRenderer;
+
+const THUMB_STRIP_VIEW_DEFAULT_ITEM_HEIGHT: i32 = 0;
+const THUMB_STRIP_VIEW_SPACING: i32 = 0;
+
+#[repr(i32)]
+pub enum ImageListStoreColIndex {
+ ThumbIndex = 0,
+ FileIndex = 1,
+ StripThumbIndex = 2,
+ FileStatusIndex = 3,
+}
+
+glib_wrapper! {
+ pub struct ThumbStripView(
+ Object<subclass::simple::InstanceStruct<ThumbStripViewPriv>,
+ subclass::simple::ClassStruct<ThumbStripViewPriv>,
+ ThumbStripViewClass>)
+ @extends gtk::Widget, gtk::Container, gtk::IconView;
+
+ match fn {
+ get_type => || ThumbStripViewPriv::get_type().to_glib(),
+ }
+}
+
+impl ThumbStripView {
+ pub fn new(store: >k::TreeModel) -> Self {
+ glib::Object::new(
+ Self::static_type(),
+ &[
+ ("model", store),
+ ("item-height", &THUMB_STRIP_VIEW_DEFAULT_ITEM_HEIGHT),
+ ("selection-mode", >k::SelectionMode::Multiple),
+ ("column-spacing", &THUMB_STRIP_VIEW_SPACING),
+ ("row-spacing", &THUMB_STRIP_VIEW_SPACING),
+ ("margin", &0),
+ ],
+ )
+ .expect("Failed to create ThumbStripView")
+ .downcast()
+ .expect("Created ThumbStripView is of the wrong type")
+ }
+}
+
+#[derive(Default)]
+struct Signals {
+ model_add: Option<glib::SignalHandlerId>,
+ model_remove: Option<glib::SignalHandlerId>,
+}
+
+struct ItemCount {
+ count: Cell<i32>,
+}
+
+impl ItemCount {
+ fn set(&self, count: i32) {
+ self.count.set(count);
+ }
+
+ fn row_added(&self, view: >k::IconView) {
+ self.count.replace(self.count.get() + 1);
+ self.update(view);
+ }
+
+ fn row_deleted(&self, view: >k::IconView) {
+ let count = self.count.get();
+ if count > 0 {
+ self.count.replace(count + 1);
+ }
+ self.update(view);
+ }
+
+ fn update(&self, view: >k::IconView) {
+ view.set_columns(self.count.get());
+ }
+}
+
+pub struct ThumbStripViewPriv {
+ item_height: Cell<i32>,
+ item_count: Rc<ItemCount>,
+ renderer: OnceCell<LibraryCellRenderer>,
+ store: RefCell<Option<gtk::TreeModel>>,
+ signals: RefCell<Signals>,
+}
+
+pub trait ThumbStripViewExt {
+ fn set_item_height(&self, height: i32);
+ fn set_model(&self, model: Option<gtk::TreeModel>);
+}
+
+impl ThumbStripViewExt for ThumbStripView {
+ fn set_item_height(&self, height: i32) {
+ let priv_ = ThumbStripViewPriv::from_instance(self);
+ priv_.set_item_height(height);
+ }
+
+ fn set_model(&self, model: Option<gtk::TreeModel>) {
+ let priv_ = ThumbStripViewPriv::from_instance(self);
+ priv_.set_model(model);
+ }
+}
+
+impl ThumbStripViewPriv {
+ fn set_item_height(&self, height: i32) {
+ self.item_height.set(height);
+ if let Some(renderer) = self.renderer.get() {
+ renderer.set_property_height(height);
+ }
+ }
+
+ fn set_model(&self, model: Option<gtk::TreeModel>) {
+ if let Some(store) = &*self.store.borrow() {
+ let mut signals = self.signals.borrow_mut();
+ if signals.model_add.is_some() {
+ glib::signal_handler_disconnect(store, signals.model_add.take().unwrap());
+ }
+ if signals.model_remove.is_some() {
+ glib::signal_handler_disconnect(store, signals.model_remove.take().unwrap());
+ }
+ }
+
+ self.store.replace(model.clone());
+ self.setup_model();
+ self.get_instance()
+ .upcast::<gtk::IconView>()
+ .set_model(model.as_ref());
+ }
+
+ fn setup_model(&self) {
+ if let Some(store) = &*self.store.borrow() {
+ // model item count
+ let iter = store.get_iter_first();
+ let count = if let Some(ref iter) = iter {
+ let mut c = 0;
+ while store.iter_next(iter) {
+ c += 1;
+ }
+ c
+ } else {
+ 0
+ };
+ self.item_count.set(count);
+
+ let view = self.get_instance().upcast::<gtk::IconView>();
+ // update item count
+ self.item_count.update(&view);
+
+ let mut signals = self.signals.borrow_mut();
+ signals.model_add = Some(store.connect_row_inserted(
+ clone!(@strong self.item_count as item_count, @weak view => move |_,_,_| {
+ item_count.row_added(&view);
+ }),
+ ));
+ signals.model_remove = Some(store.connect_row_deleted(
+ clone!(@strong self.item_count as item_count, @weak view => move |_,_| {
+ item_count.row_deleted(&view);
+ }),
+ ));
+ }
+ }
+}
+
+static PROPERTIES: [subclass::Property; 1] = [subclass::Property("item-height", |item_height| {
+ glib::ParamSpec::int(
+ item_height,
+ "Item Height",
+ "The Item Height",
+ -1,
+ 100,
+ THUMB_STRIP_VIEW_DEFAULT_ITEM_HEIGHT, // Default value
+ glib::ParamFlags::READWRITE,
+ )
+})];
+
+impl ObjectSubclass for ThumbStripViewPriv {
+ const NAME: &'static str = "ThumbStripView";
+ type ParentType = gtk::IconView;
+ 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);
+ }
+
+ fn new() -> Self {
+ Self {
+ item_height: Cell::new(THUMB_STRIP_VIEW_DEFAULT_ITEM_HEIGHT),
+ item_count: Rc::new(ItemCount {
+ count: Cell::new(0),
+ }),
+ renderer: OnceCell::new(),
+ store: RefCell::new(None),
+ signals: RefCell::new(Signals::default()),
+ }
+ }
+}
+
+impl ObjectImpl for ThumbStripViewPriv {
+ glib_object_impl!();
+
+ fn constructed(&self, obj: &glib::Object) {
+ self.parent_constructed(obj);
+
+ let self_ = obj.downcast_ref::<ThumbStripView>().unwrap();
+
+ let cell_renderer = LibraryCellRenderer::new_thumb_renderer();
+
+ let icon_view_self = self_.clone().upcast::<gtk::IconView>();
+ icon_view_self.pack_start(&cell_renderer, false);
+ cell_renderer.set_property_height(100);
+ cell_renderer.set_property_yalign(0.5);
+ cell_renderer.set_property_xalign(0.5);
+
+ icon_view_self.add_attribute(
+ &cell_renderer,
+ "pixbuf",
+ ImageListStoreColIndex::StripThumbIndex as i32,
+ );
+ icon_view_self.add_attribute(
+ &cell_renderer,
+ "libfile",
+ ImageListStoreColIndex::FileIndex as i32,
+ );
+ icon_view_self.add_attribute(
+ &cell_renderer,
+ "status",
+ ImageListStoreColIndex::FileStatusIndex as i32,
+ );
+ self.renderer
+ .set(cell_renderer)
+ .expect("ThumbStripView::constructed set cell render failed.");
+ let model = icon_view_self.get_model();
+
+ self.setup_model();
+ self.store.replace(model);
+ }
+
+ fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
+ let prop = &PROPERTIES[id];
+ match *prop {
+ subclass::Property("item-height", ..) => {
+ let height: i32 = value
+ .get_some()
+ .expect("type conformity checked by `Object::set_property`");
+ self.set_item_height(height);
+ }
+ _ => unimplemented!(),
+ }
+ }
+
+ fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
+ let prop = &PROPERTIES[id];
+
+ match *prop {
+ subclass::Property("item-height", ..) => Ok(self.item_height.get().to_value()),
+ _ => unimplemented!(),
+ }
+ }
+}
+
+impl WidgetImpl for ThumbStripViewPriv {}
+
+impl ContainerImpl for ThumbStripViewPriv {}
+
+impl IconViewImpl for ThumbStripViewPriv {}
+
+#[no_mangle]
+pub unsafe extern "C" fn npc_thumb_strip_view_new(
+ store: *mut gtk_sys::GtkTreeModel,
+) -> *mut gtk_sys::GtkWidget {
+ ThumbStripView::new(>k::TreeModel::from_glib_full(store))
+ .upcast::<gtk::Widget>()
+ .to_glib_full()
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]