fractal/src/components/reaction_chooser.rs

205 lines
6.1 KiB
Rust

use adw::subclass::prelude::*;
use gtk::{glib, glib::clone, prelude::*, CompositeTemplate};
use crate::session::model::ReactionList;
struct ReactionGridItem<'a> {
key: &'a str,
column: i32,
row: i32,
}
static QUICK_REACTIONS: &[ReactionGridItem] = &[
ReactionGridItem {
key: "👍️",
column: 0,
row: 0,
},
ReactionGridItem {
key: "👎️",
column: 1,
row: 0,
},
ReactionGridItem {
key: "😄",
column: 2,
row: 0,
},
ReactionGridItem {
key: "🎉",
column: 3,
row: 0,
},
ReactionGridItem {
key: "😕",
column: 0,
row: 1,
},
ReactionGridItem {
key: "❤️",
column: 1,
row: 1,
},
ReactionGridItem {
key: "🚀",
column: 2,
row: 1,
},
];
mod imp {
use std::{cell::RefCell, collections::HashMap};
use glib::subclass::InitializingObject;
use super::*;
#[derive(Debug, Default, CompositeTemplate)]
#[template(resource = "/org/gnome/Fractal/ui/components/reaction_chooser.ui")]
pub struct ReactionChooser {
/// The `ReactionList` associated to this chooser
pub reactions: RefCell<Option<ReactionList>>,
pub reactions_handler: RefCell<Option<glib::SignalHandlerId>>,
pub reaction_bindings: RefCell<HashMap<String, glib::Binding>>,
#[template_child]
pub reaction_grid: TemplateChild<gtk::Grid>,
}
#[glib::object_subclass]
impl ObjectSubclass for ReactionChooser {
const NAME: &'static str = "ComponentsReactionChooser";
type Type = super::ReactionChooser;
type ParentType = adw::Bin;
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for ReactionChooser {
fn constructed(&self) {
self.parent_constructed();
let grid = &self.reaction_grid;
for reaction_item in QUICK_REACTIONS {
let button = gtk::ToggleButton::builder()
.label(reaction_item.key)
.action_name("event.toggle-reaction")
.action_target(&reaction_item.key.to_variant())
.css_classes(vec!["flat".to_string(), "circular".to_string()])
.build();
button.connect_clicked(|button| {
button.activate_action("context-menu.close", None).unwrap();
});
grid.attach(&button, reaction_item.column, reaction_item.row, 1, 1);
}
}
}
impl WidgetImpl for ReactionChooser {}
impl BinImpl for ReactionChooser {}
}
glib::wrapper! {
/// A widget displaying a `ReactionChooser` for a `ReactionList`.
pub struct ReactionChooser(ObjectSubclass<imp::ReactionChooser>)
@extends gtk::Widget, adw::Bin, @implements gtk::Accessible;
}
impl ReactionChooser {
pub fn new() -> Self {
glib::Object::new()
}
pub fn reactions(&self) -> Option<ReactionList> {
self.imp().reactions.borrow().clone()
}
pub fn set_reactions(&self, reactions: Option<ReactionList>) {
let imp = self.imp();
let prev_reactions = self.reactions();
if prev_reactions == reactions {
return;
}
if let Some(reactions) = prev_reactions.as_ref() {
if let Some(signal_handler) = imp.reactions_handler.take() {
reactions.disconnect(signal_handler);
}
let mut reaction_bindings = imp.reaction_bindings.borrow_mut();
for reaction_item in QUICK_REACTIONS {
if let Some(binding) = reaction_bindings.remove(reaction_item.key) {
if let Some(button) = imp
.reaction_grid
.child_at(reaction_item.column, reaction_item.row)
.and_then(|widget| widget.downcast::<gtk::ToggleButton>().ok())
{
button.set_active(false);
}
binding.unbind();
}
}
}
if let Some(reactions) = reactions.as_ref() {
let signal_handler =
reactions.connect_items_changed(clone!(@weak self as obj => move |_, _, _, _| {
obj.update_reactions();
}));
imp.reactions_handler.replace(Some(signal_handler));
}
imp.reactions.replace(reactions);
self.update_reactions();
}
fn update_reactions(&self) {
let imp = self.imp();
let mut reaction_bindings = imp.reaction_bindings.borrow_mut();
let reactions = self.reactions();
for reaction_item in QUICK_REACTIONS {
if let Some(reaction) = reactions
.as_ref()
.and_then(|reactions| reactions.reaction_group_by_key(reaction_item.key))
{
if reaction_bindings.get(reaction_item.key).is_none() {
let button = imp
.reaction_grid
.child_at(reaction_item.column, reaction_item.row)
.unwrap();
let binding = reaction
.bind_property("has-user", &button, "active")
.flags(glib::BindingFlags::SYNC_CREATE)
.build();
reaction_bindings.insert(reaction_item.key.to_string(), binding);
}
} else if let Some(binding) = reaction_bindings.remove(reaction_item.key) {
if let Some(button) = imp
.reaction_grid
.child_at(reaction_item.column, reaction_item.row)
.and_then(|widget| widget.downcast::<gtk::ToggleButton>().ok())
{
button.set_active(false);
}
binding.unbind();
}
}
}
}
impl Default for ReactionChooser {
fn default() -> Self {
Self::new()
}
}