From d35c0564b3f5dd10f6e23bf462bbfabd3084e486 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Wed, 9 Feb 2022 22:47:38 +0100 Subject: Add a /sticker plugin This plugin currently uploads the selected sticker every time, to the HTTP File Upload service of the server (see XEP-0363), a future optimisation would be to use XEP-0231 instead, for better caching on the recipient side. It relies on a helper tool to select the wanted sticker inside the pack, a sample one is provided in tools/sticker-picker/, but it is not built by default. --- tools/sticker-picker/src/main.rs | 93 +++++++++++++++++++++++++++++++ tools/sticker-picker/src/sticker.rs | 106 ++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 tools/sticker-picker/src/main.rs create mode 100644 tools/sticker-picker/src/sticker.rs (limited to 'tools/sticker-picker/src') diff --git a/tools/sticker-picker/src/main.rs b/tools/sticker-picker/src/main.rs new file mode 100644 index 00000000..49795f4d --- /dev/null +++ b/tools/sticker-picker/src/main.rs @@ -0,0 +1,93 @@ +// This file is part of Poezio. +// +// Poezio is free software: you can redistribute it and/or modify +// it under the terms of the zlib license. See the COPYING file. + +mod sticker; + +use gtk::prelude::*; +use sticker::StickerType as Sticker; + +fn main() { + let app = gtk::Application::builder() + .application_id("io.poez.StickerPicker") + .flags(gio::ApplicationFlags::HANDLES_OPEN) + .build(); + + let quit = gio::SimpleAction::new("quit", None); + app.set_accels_for_action("app.quit", &["q"]); + app.add_action(&quit); + quit.connect_activate(glib::clone!(@weak app => move |_, _| app.quit())); + + app.connect_open(move |app, directories, _| { + let path = match directories { + [directory] => directory.path().unwrap(), + _ => { + eprintln!("Only a single directory is allowed!"); + std::process::exit(1); + } + }; + + let window = gtk::ApplicationWindow::builder() + .application(app) + .default_width(1280) + .default_height(720) + .title("Poezio Sticker Picker") + .build(); + + let sw = gtk::ScrolledWindow::builder() + .has_frame(true) + .hscrollbar_policy(gtk::PolicyType::Always) + .vscrollbar_policy(gtk::PolicyType::Always) + .vexpand(true) + .build(); + window.set_child(Some(&sw)); + + let store = gio::ListStore::new(Sticker::static_type()); + + for dir_entry in std::fs::read_dir(path).unwrap() { + let dir_entry = dir_entry.unwrap(); + let file_name = dir_entry.file_name().into_string().unwrap(); + let sticker = Sticker::new(file_name, &dir_entry.path()); + store.append(&sticker); + } + + let factory = gtk::SignalListItemFactory::new(); + factory.connect_setup(|_, item| { + let picture = gtk::Picture::builder() + .alternative_text("Sticker") + .can_shrink(false) + .build(); + item.set_child(Some(&picture)); + }); + factory.connect_bind(|_, list_item| { + if let Some(child) = list_item.child() { + if let Some(item) = list_item.item() { + let picture: gtk::Picture = child.downcast().unwrap(); + let sticker: Sticker = item.downcast().unwrap(); + picture.set_paintable(sticker.texture().as_ref()); + } + } + }); + + let selection = gtk::SingleSelection::new(Some(&store)); + let grid_view = gtk::GridView::builder() + .single_click_activate(true) + .model(&selection) + .factory(&factory) + .build(); + grid_view.connect_activate(move |_, position| { + let item = store.item(position).unwrap(); + let sticker: Sticker = item.downcast().unwrap(); + if let Some(filename) = sticker.filename() { + println!("{}", filename); + std::process::exit(0); + } + }); + sw.set_child(Some(&grid_view)); + + window.show(); + }); + + app.run(); +} diff --git a/tools/sticker-picker/src/sticker.rs b/tools/sticker-picker/src/sticker.rs new file mode 100644 index 00000000..7fb44e8e --- /dev/null +++ b/tools/sticker-picker/src/sticker.rs @@ -0,0 +1,106 @@ +// This file is part of Poezio. +// +// Poezio is free software: you can redistribute it and/or modify +// it under the terms of the zlib license. See the COPYING file. + +use gtk::prelude::*; +use gtk::subclass::prelude::*; +use std::cell::RefCell; +use std::path::Path; + +#[derive(Debug, Default)] +pub struct Sticker { + filename: RefCell>, + texture: RefCell>, +} + +#[glib::object_subclass] +impl ObjectSubclass for Sticker { + const NAME: &'static str = "Sticker"; + type Type = StickerType; +} + +impl ObjectImpl for Sticker { + fn properties() -> &'static [glib::ParamSpec] { + use once_cell::sync::Lazy; + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ + glib::ParamSpecString::new( + "filename", + "Filename", + "Filename", + None, + glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY, + ), + glib::ParamSpecObject::new( + "texture", + "Texture", + "Texture", + gdk::Texture::static_type(), + glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY, + ), + ] + }); + PROPERTIES.as_ref() + } + + fn set_property( + &self, + _obj: &StickerType, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.name() { + "filename" => { + let filename = value.get().unwrap(); + self.filename.replace(filename); + } + "texture" => { + let texture = value.get().unwrap(); + self.texture.replace(texture); + } + _ => unimplemented!(), + } + } + + fn property(&self, _obj: &StickerType, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.name() { + "filename" => self.filename.borrow().to_value(), + "texture" => self.texture.borrow().to_value(), + _ => unimplemented!(), + } + } +} + +glib::wrapper! { + pub struct StickerType(ObjectSubclass); +} + +impl StickerType { + pub fn new(filename: String, path: &Path) -> StickerType { + let texture = gdk::Texture::from_filename(path).unwrap(); + glib::Object::new(&[("filename", &filename), ("texture", &texture)]) + .expect("Failed to create Sticker") + } + + pub fn filename(&self) -> Option { + let imp = self.imp(); + let filename = imp.filename.borrow(); + if let Some(filename) = filename.as_ref() { + Some(filename.clone()) + } else { + None + } + } + + pub fn texture(&self) -> Option { + let imp = self.imp(); + let texture = imp.texture.borrow(); + if let Some(texture) = texture.as_ref() { + Some(texture.clone()) + } else { + None + } + } +} -- cgit v1.2.3