Browse Source

Merge branch 'wip/scroll-to-original-message' into 'main'

Working jump to original

See merge request World/fractal!2137
merge-requests/2137/merge
Pavel Shirshov 7 days ago
parent
commit
ada4de674c
  1. 14
      data/resources/stylesheet/_room_history.scss
  2. 1
      src/session_view/room_history/message_row/content.rs
  3. 4
      src/session_view/room_history/message_row/reply.blp
  4. 35
      src/session_view/room_history/message_row/reply.rs
  5. 83
      src/session_view/room_history/mod.rs

14
data/resources/stylesheet/_room_history.scss

@ -106,6 +106,11 @@ room-title {
}
}
&.jump-highlight {
animation: jump-highlight 1200ms ease-out;
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent-bg-color) 45%, transparent);
}
button.sender-avatar {
padding: 5px;
@ -131,6 +136,15 @@ room-title {
}
}
@keyframes jump-highlight {
0% {
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent-bg-color) 55%, transparent);
}
100% {
box-shadow: inset 0 0 0 0px transparent;
}
}
message-sender {
@include vendor.focus-ring($offset: -1px, $focus-state: ':focus-within');

1
src/session_view/room_history/message_row/content.rs

@ -198,6 +198,7 @@ impl MessageContent {
replied_to_event.content.can_show_header(),
);
reply.set_related_content_sender(replied_to_sender.upcast_ref());
reply.set_related_event_id(event.reply_to_id());
reply.related_content().build_content(
replied_to_event.content,
ContentFormat::Compact,

4
src/session_view/room_history/message_row/reply.blp

@ -12,6 +12,10 @@ template $ContentMessageReply: Gtk.Grid {
"quote",
]
Gtk.GestureClick {
released => $handle_related_content_click() swapped;
}
accessibility {
label: _("In Reply To");
}

35
src/session_view/room_history/message_row/reply.rs

@ -1,7 +1,11 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::glib;
use matrix_sdk_ui::timeline::TimelineEventItemId;
use ruma::OwnedEventId;
use tracing::error;
use crate::session::User;
use crate::utils::matrix::ext_traits::TimelineEventItemIdExt;
mod imp {
use std::cell::{Cell, RefCell};
@ -20,6 +24,7 @@ mod imp {
related_content: TemplateChild<adw::Bin>,
#[template_child]
content: TemplateChild<adw::Bin>,
related_event_id: RefCell<Option<OwnedEventId>>,
/// Whether to show the header of the related content.
#[property(get, set = Self::set_show_related_content_header, explicit_notify)]
show_related_content_header: Cell<bool>,
@ -34,6 +39,7 @@ mod imp {
fn class_init(klass: &mut Self::Class) {
Self::bind_template(klass);
Self::bind_template_callbacks(klass);
}
fn instance_init(obj: &InitializingObject<Self>) {
@ -53,7 +59,13 @@ mod imp {
impl WidgetImpl for MessageReply {}
impl GridImpl for MessageReply {}
#[gtk::template_callbacks]
impl MessageReply {
/// Set the ID of the related event.
pub(super) fn set_related_event_id(&self, event_id: Option<OwnedEventId>) {
self.related_event_id.replace(event_id);
}
/// Set whether to show the header of the related content.
fn set_show_related_content_header(&self, show: bool) {
if self.show_related_content_header.get() == show {
@ -87,6 +99,24 @@ mod imp {
pub(super) fn content(&self) -> &adw::Bin {
self.content.as_ref()
}
/// Handle a click on the replied-to content.
#[template_callback]
fn handle_related_content_click(&self) {
let Some(event_id) = self.related_event_id.borrow().clone() else {
return;
};
if self
.obj()
.activate_action(
"room-history.scroll-to-event",
Some(&TimelineEventItemId::EventId(event_id).to_variant()),
)
.is_err()
{
error!("Could not activate `room-history.scroll-to-event` action");
}
}
}
}
@ -107,6 +137,11 @@ impl MessageReply {
self.imp().set_related_content_sender(user);
}
/// Set the ID of the related event.
pub(crate) fn set_related_event_id(&self, event_id: Option<OwnedEventId>) {
self.imp().set_related_event_id(event_id);
}
/// The widget containing the replied-to content.
pub(crate) fn related_content(&self) -> &adw::Bin {
self.imp().related_content()

83
src/session_view/room_history/mod.rs

@ -54,6 +54,10 @@ use crate::{
const SCROLL_TIMEOUT: Duration = Duration::from_millis(500);
/// The time to wait before considering that messages on a screen where read.
const READ_TIMEOUT: Duration = Duration::from_secs(5);
/// The time to keep the jump highlight on a message.
const JUMP_HIGHLIGHT_DURATION: Duration = Duration::from_millis(1200);
/// The CSS class used for jump highlighting.
const JUMP_HIGHLIGHT_CLASS: &str = "jump-highlight";
mod imp {
use std::{
@ -120,6 +124,8 @@ mod imp {
grouping_model: OnceCell<GroupingListModel>,
scroll_timeout: RefCell<Option<glib::SourceId>>,
read_timeout: RefCell<Option<glib::SourceId>>,
jump_highlight_timeout: RefCell<Option<glib::SourceId>>,
jump_highlight_key: RefCell<Option<TimelineEventItemId>>,
room_handler: RefCell<Option<glib::SignalHandlerId>>,
permissions_handlers: RefCell<Vec<glib::SignalHandlerId>>,
membership_handler: RefCell<Option<glib::SignalHandlerId>>,
@ -611,6 +617,16 @@ mod imp {
if let Some(event) = item.downcast_ref::<Event>() {
let child = list_item.child_or_else::<EventRow>(|| EventRow::new(&self.obj()));
child.set_event(Some(event.clone()));
if self
.jump_highlight_key
.borrow()
.as_ref()
.is_some_and(|key| event.matches_identifier(key))
{
child.add_css_class(JUMP_HIGHLIGHT_CLASS);
} else {
child.remove_css_class(JUMP_HIGHLIGHT_CLASS);
}
} else if let Some(virtual_item) = item.downcast_ref::<VirtualItem>() {
set_virtual_item_child(list_item, virtual_item);
} else if let Some(group) = item.downcast_ref::<GroupingListGroup>() {
@ -836,11 +852,78 @@ mod imp {
if let Some(pos) = timeline.find_event_position(key) {
let pos = pos as u32;
self.set_is_auto_scrolling(false);
self.set_sticky(false);
self.listview
.scroll_to(pos, gtk::ListScrollFlags::FOCUS, None);
self.schedule_jump_highlight(key.clone());
}
}
fn schedule_jump_highlight(&self, key: TimelineEventItemId) {
glib::idle_add_local_once(clone!(
#[weak(rename_to = imp)]
self,
move || {
imp.highlight_event_row(&key);
}
));
}
fn highlight_event_row(&self, key: &TimelineEventItemId) {
self.clear_jump_highlight();
let Some(row) = self.find_event_row(key) else {
return;
};
row.add_css_class(JUMP_HIGHLIGHT_CLASS);
self.jump_highlight_key.replace(Some(key.clone()));
let timeout_id = glib::timeout_add_local_once(JUMP_HIGHLIGHT_DURATION, clone!(
#[weak(rename_to = imp)]
self,
move || {
imp.clear_jump_highlight();
}
));
self.jump_highlight_timeout.replace(Some(timeout_id));
}
fn clear_jump_highlight(&self) {
if let Some(timeout_id) = self.jump_highlight_timeout.take() {
timeout_id.remove();
}
let Some(key) = self.jump_highlight_key.take() else {
return;
};
if let Some(row) = self.find_event_row(&key) {
row.remove_css_class(JUMP_HIGHLIGHT_CLASS);
}
}
fn find_event_row(&self, key: &TimelineEventItemId) -> Option<EventRow> {
let listview = &*self.listview;
let mut child = listview.first_child();
while let Some(item) = child {
if let Some(event_row) = item
.first_child()
.and_downcast::<EventRow>()
&& event_row
.event()
.is_some_and(|event| event.matches_identifier(key))
{
return Some(event_row);
}
child = item.next_sibling();
}
None
}
/// The ancestor window of the room history.
fn parent_window(&self) -> Option<Window> {
self.obj().root().and_downcast()

Loading…
Cancel
Save