diff --git a/data/resources/style.css b/data/resources/style.css index 689379b0..4802eaf8 100644 --- a/data/resources/style.css +++ b/data/resources/style.css @@ -665,6 +665,40 @@ roomtitle .subtitle { padding-right: 12px; } +sender-avatar { + padding: 3px; + border-radius: 100%; + transition-property: outline, outline-width, outline-offset, outline-color; + transition-duration: 300ms; + animation-timing-function: ease-in-out; + outline: 0 solid transparent; + outline-offset: 2px; +} + +sender-avatar:focus { + outline-color: alpha(@accent_color, 0.5); + outline-width: 2px; + outline-offset: -2px; +} + +sender-avatar:hover { + background-color: alpha(currentColor, .07); +} + +sender-avatar:active { + background-color: alpha(currentColor, .16); +} + +sender-avatar:checked { + background-color: alpha(currentColor, .1); +} + +sender-avatar popover button.text-button { + padding-left: 10px; + padding-right: 10px; + font-weight: 400; +} + /* Event Source Dialog */ diff --git a/po/POTFILES.in b/po/POTFILES.in index b9d12704..2b60fa04 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -109,6 +109,8 @@ src/session/view/content/room_history/member_timestamp/row.rs src/session/view/content/room_history/mod.rs src/session/view/content/room_history/mod.ui src/session/view/content/room_history/read_receipts_list/mod.rs +src/session/view/content/room_history/sender_avatar/mod.rs +src/session/view/content/room_history/sender_avatar/mod.ui src/session/view/content/room_history/state_row/creation.rs src/session/view/content/room_history/state_row/creation.ui src/session/view/content/room_history/state_row/mod.rs diff --git a/src/prelude.rs b/src/prelude.rs index 1c1725b3..885479f9 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -3,7 +3,6 @@ pub use crate::{ contrib::CameraExt, session::model::{TimelineItemExt, UserExt}, session_list::SessionInfoExt, - system_settings::SystemSettingsExt, user_facing_error::UserFacingError, utils::LocationExt, }; diff --git a/src/session/view/content/room_details/mod.rs b/src/session/view/content/room_details/mod.rs index 655305f3..37577e20 100644 --- a/src/session/view/content/room_details/mod.rs +++ b/src/session/view/content/room_details/mod.rs @@ -120,7 +120,7 @@ glib::wrapper! { } impl RoomDetails { - pub fn new(parent_window: &Option, room: &Room) -> Self { + pub fn new(parent_window: Option<>k::Window>, room: &Room) -> Self { glib::Object::builder() .property("transient-for", parent_window) .property("room", room) diff --git a/src/session/view/content/room_history/item_row.rs b/src/session/view/content/room_history/item_row.rs index d9ed7aee..f93c669f 100644 --- a/src/session/view/content/room_history/item_row.rs +++ b/src/session/view/content/room_history/item_row.rs @@ -408,19 +408,22 @@ impl ItemRow { } fn show_emoji_chooser(&self, popover: >k::PopoverMenu) { - let emoji_chooser = gtk::EmojiChooser::builder().has_arrow(false).build(); + let (_, rectangle) = popover.pointing_to(); + + let emoji_chooser = gtk::EmojiChooser::builder() + .has_arrow(false) + .pointing_to(&rectangle) + .build(); + emoji_chooser.connect_emoji_picked(clone!(@weak self as obj => move |_, emoji| { obj .activate_action("event.toggle-reaction", Some(&emoji.to_variant())) .unwrap(); })); - emoji_chooser.set_parent(self); emoji_chooser.connect_closed(|emoji_chooser| { emoji_chooser.unparent(); }); - - let (_, rectangle) = popover.pointing_to(); - emoji_chooser.set_pointing_to(Some(&rectangle)); + emoji_chooser.set_parent(self); popover.popdown(); emoji_chooser.popup(); diff --git a/src/session/view/content/room_history/message_row/mod.rs b/src/session/view/content/room_history/message_row/mod.rs index ce7dd4c7..af6460e8 100644 --- a/src/session/view/content/room_history/message_row/mod.rs +++ b/src/session/view/content/room_history/message_row/mod.rs @@ -18,10 +18,10 @@ pub use self::content::{ContentFormat, MessageContent}; use self::{ media::MessageMedia, message_state_stack::MessageStateStack, reaction_list::MessageReactionList, }; -use super::ReadReceiptsList; +use super::{ReadReceiptsList, SenderAvatar}; use crate::{ - components::Avatar, gettext_f, prelude::*, session::model::Event, system_settings::ClockFormat, - utils::BoundObject, Application, Window, + gettext_f, session::model::Event, system_settings::ClockFormat, utils::BoundObject, + Application, Window, }; mod imp { @@ -38,7 +38,7 @@ mod imp { #[properties(wrapper_type = super::MessageRow)] pub struct MessageRow { #[template_child] - pub avatar: TemplateChild, + pub avatar: TemplateChild, #[template_child] pub header: TemplateChild, #[template_child] @@ -157,8 +157,8 @@ mod imp { binding.unbind(); } - self.avatar - .set_data(Some(event.sender().avatar_data().clone())); + self.avatar.set_room(Some(event.room())); + self.avatar.set_sender(Some(event.sender())); let display_name_binding = event .sender() diff --git a/src/session/view/content/room_history/message_row/mod.ui b/src/session/view/content/room_history/message_row/mod.ui index 2a2773b5..9027c5e0 100644 --- a/src/session/view/content/room_history/message_row/mod.ui +++ b/src/session/view/content/room_history/message_row/mod.ui @@ -5,10 +5,7 @@ 10 - - 36 - start - presentation + 0 0 diff --git a/src/session/view/content/room_history/message_toolbar/mod.rs b/src/session/view/content/room_history/message_toolbar/mod.rs index 3c8d9ea8..d0764c68 100644 --- a/src/session/view/content/room_history/message_toolbar/mod.rs +++ b/src/session/view/content/room_history/message_toolbar/mod.rs @@ -36,7 +36,7 @@ use crate::{ components::{CustomEntry, LabelWithWidgets, Pill}, gettext_f, prelude::*, - session::model::{Event, EventKey, Room}, + session::model::{Event, EventKey, Member, Room}, spawn, toast, utils::{ matrix::extract_mentions, @@ -330,6 +330,23 @@ impl MessageToolbar { glib::Object::new() } + /// Add a mention of the given member to the message composer. + pub fn mention_member(&self, member: &Member) { + let view = &*self.imp().message_entry; + let buffer = view.buffer(); + + let mut insert = buffer.iter_at_mark(&buffer.get_insert()); + let anchor = match insert.child_anchor() { + Some(anchor) => anchor, + None => buffer.create_child_anchor(&mut insert), + }; + + let pill = member.to_pill(); + view.add_child_at_anchor(&pill, &anchor); + + view.grab_focus(); + } + /// Set the type of related event of the composer. fn set_related_event_type(&self, related_type: RelatedEventType) { if self.related_event_type() == related_type { diff --git a/src/session/view/content/room_history/mod.rs b/src/session/view/content/room_history/mod.rs index 445a4fc6..aa0b7860 100644 --- a/src/session/view/content/room_history/mod.rs +++ b/src/session/view/content/room_history/mod.rs @@ -4,6 +4,7 @@ mod member_timestamp; mod message_row; mod message_toolbar; mod read_receipts_list; +mod sender_avatar; mod state_row; mod typing_row; mod verification_info_bar; @@ -26,8 +27,9 @@ use tracing::{error, warn}; use self::{ divider_row::DividerRow, item_row::ItemRow, message_row::MessageRow, - message_toolbar::MessageToolbar, read_receipts_list::ReadReceiptsList, state_row::StateRow, - typing_row::TypingRow, verification_info_bar::VerificationInfoBar, + message_toolbar::MessageToolbar, read_receipts_list::ReadReceiptsList, + sender_avatar::SenderAvatar, state_row::StateRow, typing_row::TypingRow, + verification_info_bar::VerificationInfoBar, }; use super::{room_details, RoomDetails}; use crate::{ @@ -80,6 +82,9 @@ mod imp { pub sticky: Cell, pub item_context_menu: OnceCell, pub item_reaction_chooser: ReactionChooser, + pub sender_context_menu: OnceCell, + #[template_child] + pub sender_menu_model: TemplateChild, #[template_child] pub room_title: TemplateChild, #[template_child] @@ -609,13 +614,15 @@ impl RoomHistory { /// If `subpage_name` is set, the room details will be opened on the given /// subpage. pub fn open_room_details(&self, subpage_name: Option) { - if let Some(room) = self.room() { - let window = RoomDetails::new(&self.parent_window(), &room); - if let Some(subpage_name) = subpage_name { - window.show_initial_subpage(subpage_name); - } - window.present(); + let Some(room) = self.room() else { + return; + }; + + let window = RoomDetails::new(self.root().and_downcast_ref(), &room); + if let Some(subpage_name) = subpage_name { + window.show_initial_subpage(subpage_name); } + window.present(); } fn update_room_menu(&self) { @@ -707,11 +714,6 @@ impl RoomHistory { }); } - /// Returns the parent GtkWindow containing this widget. - fn parent_window(&self) -> Option { - self.root().and_downcast() - } - /// Scroll to the newest message in the timeline pub fn scroll_down(&self) { let imp = self.imp(); @@ -738,6 +740,7 @@ impl RoomHistory { self.message_toolbar().handle_paste_action(); } + /// The context menu for the item rows. pub fn item_context_menu(&self) -> >k::PopoverMenu { self.imp().item_context_menu.get_or_init(|| { let popover = gtk::PopoverMenu::builder() @@ -749,10 +752,27 @@ impl RoomHistory { }) } + /// The reaction chooser for the item rows. pub fn item_reaction_chooser(&self) -> &ReactionChooser { &self.imp().item_reaction_chooser } + /// The context menu for the sender avatars. + pub fn sender_context_menu(&self) -> >k::PopoverMenu { + let imp = self.imp(); + imp.sender_context_menu.get_or_init(|| { + let popover = gtk::PopoverMenu::builder() + .has_arrow(false) + .halign(gtk::Align::Start) + .menu_model(&*imp.sender_menu_model) + .build(); + popover.update_property(&[gtk::accessible::Property::Label(&gettext( + "Sender Context Menu", + ))]); + popover + }) + } + fn scroll_to_event(&self, key: &EventKey) { let room = match self.room() { Some(room) => room, diff --git a/src/session/view/content/room_history/mod.ui b/src/session/view/content/room_history/mod.ui index 8553e3f6..ca5f89db 100644 --- a/src/session/view/content/room_history/mod.ui +++ b/src/session/view/content/room_history/mod.ui @@ -35,6 +35,82 @@ + +
+ + user-id + +
+
+ + + _Mention + sender-avatar.mention + action-disabled + + + + _Open Direct Chat + sender-avatar.open-direct-chat + action-disabled + + + _Permalink + sender-avatar.permalink + +
+
+ + + _Invite + sender-avatar.invite + action-disabled + + + Revoke _Invite + sender-avatar.revoke-invite + action-disabled + + + + _Kick + sender-avatar.kick + action-disabled + + + _Deny Access + sender-avatar.deny-access + action-disabled + + + + _Ban + sender-avatar.ban + action-disabled + + + Un_ban + sender-avatar.unban + action-disabled + + + I_gnore + sender-avatar.ignore + action-disabled + + + Stop I_gnoring + sender-avatar.stop-ignoring + action-disabled + +
+
+ + _View Details + sender-avatar.view-details + +
+