From fcbdb5da1c17560ebd1797ee045d0fad25cc058d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Wed, 13 Nov 2024 15:51:13 +0100 Subject: [PATCH] components: Rename ReactionChooser to QuickReactionChooser and refactor --- po/POTFILES.in | 2 +- src/components/mod.rs | 4 +- src/components/quick_reaction_chooser.rs | 251 ++++++++++++++++++ ...n_chooser.ui => quick_reaction_chooser.ui} | 4 +- src/components/reaction_chooser.rs | 207 --------------- .../content/room_history/event_actions.ui | 2 +- .../view/content/room_history/item_row.rs | 65 +++-- src/session/view/content/room_history/mod.rs | 8 +- src/ui-resources.gresource.xml | 2 +- 9 files changed, 300 insertions(+), 245 deletions(-) create mode 100644 src/components/quick_reaction_chooser.rs rename src/components/{reaction_chooser.ui => quick_reaction_chooser.ui} (87%) delete mode 100644 src/components/reaction_chooser.rs diff --git a/po/POTFILES.in b/po/POTFILES.in index 1dd94e96..f33ca0d4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -25,7 +25,7 @@ src/components/dialogs/user_profile.ui src/components/offline_banner.rs src/components/media/content_viewer.rs src/components/media/location_viewer.rs -src/components/reaction_chooser.ui +src/components/quick_reaction_chooser.ui src/components/pill/at_room.rs src/components/power_level_selection/popover.ui src/components/rows/loading_row.ui diff --git a/src/components/mod.rs b/src/components/mod.rs index 94b1722c..c17a0af9 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -11,7 +11,7 @@ mod media; mod offline_banner; mod pill; mod power_level_selection; -mod reaction_chooser; +mod quick_reaction_chooser; mod role_badge; mod rows; mod scale_revealer; @@ -30,7 +30,7 @@ pub use self::{ offline_banner::OfflineBanner, pill::*, power_level_selection::*, - reaction_chooser::ReactionChooser, + quick_reaction_chooser::QuickReactionChooser, role_badge::RoleBadge, rows::*, scale_revealer::ScaleRevealer, diff --git a/src/components/quick_reaction_chooser.rs b/src/components/quick_reaction_chooser.rs new file mode 100644 index 00000000..8277b4f3 --- /dev/null +++ b/src/components/quick_reaction_chooser.rs @@ -0,0 +1,251 @@ +use adw::subclass::prelude::*; +use gtk::{ + glib, + glib::{clone, closure_local}, + prelude::*, + CompositeTemplate, +}; + +use crate::{session::model::ReactionList, utils::BoundObject}; + +/// A quick reaction. +#[derive(Debug, Clone, Copy)] +struct QuickReaction { + /// The emoji that is presented. + key: &'static str, + /// The number of the column where this reaction is presented. + /// + /// There are 4 columns in total. + column: i32, + /// The number of the row where this reaction is presented. + /// + /// There are 2 rows in total. + row: i32, +} + +/// The quick reactions to present. +static QUICK_REACTIONS: &[QuickReaction] = &[ + QuickReaction { + key: "👍️", + column: 0, + row: 0, + }, + QuickReaction { + key: "👎️", + column: 1, + row: 0, + }, + QuickReaction { + key: "😄", + column: 2, + row: 0, + }, + QuickReaction { + key: "🎉", + column: 3, + row: 0, + }, + QuickReaction { + key: "😕", + column: 0, + row: 1, + }, + QuickReaction { + key: "❤️", + column: 1, + row: 1, + }, + QuickReaction { + key: "🚀", + column: 2, + row: 1, + }, +]; + +mod imp { + + use std::{cell::RefCell, collections::HashMap, sync::LazyLock}; + + use glib::subclass::{InitializingObject, Signal}; + + use super::*; + + #[derive(Debug, Default, CompositeTemplate, glib::Properties)] + #[template(resource = "/org/gnome/Fractal/ui/components/quick_reaction_chooser.ui")] + #[properties(wrapper_type = super::QuickReactionChooser)] + pub struct QuickReactionChooser { + /// The list of reactions of the event for which this chooser is + /// presented. + #[property(get, set = Self::set_reactions, explicit_notify, nullable)] + reactions: BoundObject, + reaction_bindings: RefCell>, + #[template_child] + reaction_grid: TemplateChild, + } + + #[glib::object_subclass] + impl ObjectSubclass for QuickReactionChooser { + const NAME: &'static str = "QuickReactionChooser"; + type Type = super::QuickReactionChooser; + type ParentType = adw::Bin; + + fn class_init(klass: &mut Self::Class) { + Self::bind_template(klass); + Self::bind_template_callbacks(klass); + } + + fn instance_init(obj: &InitializingObject) { + obj.init_template(); + } + } + + #[glib::derived_properties] + impl ObjectImpl for QuickReactionChooser { + fn signals() -> &'static [Signal] { + static SIGNALS: LazyLock> = + LazyLock::new(|| vec![Signal::builder("more-reactions-activated").build()]); + SIGNALS.as_ref() + } + + fn constructed(&self) { + self.parent_constructed(); + + // Construct the quick reactions. + let grid = &self.reaction_grid; + for reaction in QUICK_REACTIONS { + let button = gtk::ToggleButton::builder() + .label(reaction.key) + .action_name("event.toggle-reaction") + .action_target(&reaction.key.to_variant()) + .css_classes(["flat", "circular"]) + .build(); + button.connect_clicked(|button| { + button.activate_action("context-menu.close", None).unwrap(); + }); + grid.attach(&button, reaction.column, reaction.row, 1, 1); + } + } + } + + impl WidgetImpl for QuickReactionChooser {} + impl BinImpl for QuickReactionChooser {} + + #[gtk::template_callbacks] + impl QuickReactionChooser { + /// Set the list of reactions of the event for which this chooser is + /// presented. + fn set_reactions(&self, reactions: Option) { + let prev_reactions = self.reactions.obj(); + + if prev_reactions == reactions { + return; + } + + self.reactions.disconnect_signals(); + for (_, binding) in self.reaction_bindings.borrow_mut().drain() { + binding.unbind(); + } + + // Reset the state of the buttons. + for row in 0..=1 { + for column in 0..=3 { + if let Some(button) = self + .reaction_grid + .child_at(column, row) + .and_downcast::() + { + button.set_active(false); + } + } + } + + if let Some(reactions) = reactions { + let signal_handler = reactions.connect_items_changed(clone!( + #[weak(rename_to = imp)] + self, + move |_, _, _, _| { + imp.update_reactions(); + } + )); + self.reactions.set(reactions, vec![signal_handler]); + } + + self.update_reactions(); + } + + /// Update the state of the quick reactions. + fn update_reactions(&self) { + let mut reaction_bindings = self.reaction_bindings.borrow_mut(); + let reactions = self.reactions.obj(); + + 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 = self + .reaction_grid + .child_at(reaction_item.column, reaction_item.row) + .unwrap(); + let binding = reaction + .bind_property("has-user", &button, "active") + .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) = self + .reaction_grid + .child_at(reaction_item.column, reaction_item.row) + .and_downcast::() + { + button.set_active(false); + } + + binding.unbind(); + } + } + } + + /// Handle when the "More reactions" button is activated. + #[template_callback] + fn more_reactions_activated(&self) { + self.obj() + .emit_by_name::<()>("more-reactions-activated", &[]); + } + } +} + +glib::wrapper! { + /// A widget displaying quick reactions and taking its state from a [`ReactionList`]. + pub struct QuickReactionChooser(ObjectSubclass) + @extends gtk::Widget, adw::Bin, @implements gtk::Accessible; +} + +impl QuickReactionChooser { + pub fn new() -> Self { + glib::Object::new() + } + + /// Connect to the signal emitted when the "More reactions" button is + /// activated. + pub(crate) fn connect_more_reactions_activated( + &self, + f: F, + ) -> glib::SignalHandlerId { + self.connect_closure( + "more-reactions-activated", + true, + closure_local!(move |obj: Self| { + f(&obj); + }), + ) + } +} + +impl Default for QuickReactionChooser { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/reaction_chooser.ui b/src/components/quick_reaction_chooser.ui similarity index 87% rename from src/components/reaction_chooser.ui rename to src/components/quick_reaction_chooser.ui index c8463d32..001c0504 100644 --- a/src/components/reaction_chooser.ui +++ b/src/components/quick_reaction_chooser.ui @@ -1,6 +1,6 @@ -