[fractal/fractal-next] room-history: Add automatic scrolling and scroll down button



commit 19d71dd96a9094e08c865c2379bd7fd2e4b1c9ec
Author: Julian Sparber <julian sparber net>
Date:   Mon Sep 6 17:07:34 2021 +0200

    room-history: Add automatic scrolling and scroll down button

 data/resources/ui/content-room-history.ui | 70 ++++++++++++++++++++---------
 src/session/content/room_history.rs       | 73 ++++++++++++++++++++++++++++++-
 2 files changed, 120 insertions(+), 23 deletions(-)
---
diff --git a/data/resources/ui/content-room-history.ui b/data/resources/ui/content-room-history.ui
index 397d2bd0..947064fc 100644
--- a/data/resources/ui/content-room-history.ui
+++ b/data/resources/ui/content-room-history.ui
@@ -90,33 +90,60 @@
               </object>
             </child>
             <child>
-              <object class="GtkScrolledWindow" id="scrolled_window">
-                <property name="vexpand">True</property>
-                <property name="hscrollbar-policy">never</property>
-                <style>
-                  <class name="room-history"/>
-                </style>
-                <property name="child">
-                  <object class="AdwClampScrollable">
-                    <property name="vexpand">True</property>
-                    <property name="hexpand">True</property>
-                    <property name="child">
-                      <object class="GtkListView" id="listview">
+              <object class="GtkOverlay" id="content">
+                <child type="overlay">
+                  <object class="GtkRevealer" id="scroll_btn_revealer">
+                    <property name="transition_type">crossfade</property>
+                    <property name="reveal_child" bind-source="ContentRoomHistory" bind-property="sticky" 
bind-flags="sync-create | invert-boolean"/>
+                    <property name="valign">end</property>
+                    <property name="halign">end</property>
+                    <property name="margin-end">24</property>
+                    <property name="margin-bottom">24</property>
+                    <child>
+                      <object class="GtkButton" id="scroll_btn">
+                        <property name="icon-name">go-bottom-symbolic</property>
+                        <property name="action-name">room-history.scroll-down</property>
+                        <accessibility>
+                          <property name="label" translatable="yes">Scroll to bottom</property>
+                        </accessibility>
                         <style>
-                          <class name="navigation-sidebar"/>
+                          <class name="osd"/>
+                          <class name="circular"/>
                         </style>
-                        <property name="factory">
-                          <object class="GtkBuilderListItemFactory">
-                            <property name="resource">/org/gnome/FractalNext/content-item.ui</property>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkScrolledWindow" id="scrolled_window">
+                    <property name="vexpand">True</property>
+                    <property name="hscrollbar-policy">never</property>
+                    <style>
+                      <class name="room-history"/>
+                    </style>
+                    <property name="child">
+                      <object class="AdwClampScrollable">
+                        <property name="vexpand">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="child">
+                          <object class="GtkListView" id="listview">
+                            <style>
+                              <class name="navigation-sidebar"/>
+                            </style>
+                            <property name="factory">
+                              <object class="GtkBuilderListItemFactory">
+                                <property name="resource">/org/gnome/FractalNext/content-item.ui</property>
+                              </object>
+                            </property>
+                            <accessibility>
+                              <property name="label" translatable="yes">Room History</property>
+                            </accessibility>
                           </object>
                         </property>
-                        <accessibility>
-                          <property name="label" translatable="yes">Room History</property>
-                        </accessibility>
                       </object>
                     </property>
                   </object>
-                </property>
+                </child>
               </object>
             </child>
           </object>
@@ -155,7 +182,7 @@
                 <child>
                   <object class="CustomEntry">
                     <style>
-                      <class name="message-entry" />
+                      <class name="message-entry"/>
                     </style>
                     <child>
                       <object class="GtkScrolledWindow">
@@ -193,3 +220,4 @@
     </child>
   </template>
 </interface>
+
diff --git a/src/session/content/room_history.rs b/src/session/content/room_history.rs
index 3830de98..af972145 100644
--- a/src/session/content/room_history.rs
+++ b/src/session/content/room_history.rs
@@ -23,6 +23,8 @@ mod imp {
         pub category_handler: RefCell<Option<SignalHandlerId>>,
         pub empty_timeline_handler: RefCell<Option<SignalHandlerId>>,
         pub md_enabled: Cell<bool>,
+        pub is_auto_scrolling: Cell<bool>,
+        pub sticky: Cell<bool>,
         #[template_child]
         pub headerbar: TemplateChild<adw::HeaderBar>,
         #[template_child]
@@ -32,8 +34,14 @@ mod imp {
         #[template_child]
         pub listview: TemplateChild<gtk::ListView>,
         #[template_child]
+        pub content: TemplateChild<gtk::Widget>,
+        #[template_child]
         pub scrolled_window: TemplateChild<gtk::ScrolledWindow>,
         #[template_child]
+        pub scroll_btn: TemplateChild<gtk::Button>,
+        #[template_child]
+        pub scroll_btn_revealer: TemplateChild<gtk::Revealer>,
+        #[template_child]
         pub message_entry: TemplateChild<sourceview::View>,
         #[template_child]
         pub markdown_button: TemplateChild<gtk::MenuButton>,
@@ -65,9 +73,14 @@ mod imp {
             klass.install_action("room-history.leave", None, move |widget, _, _| {
                 widget.leave();
             });
+
             klass.install_action("room-history.details", None, move |widget, _, _| {
                 widget.open_room_details();
             });
+
+            klass.install_action("room-history.scroll-down", None, move |widget, _, _| {
+                widget.scroll_down();
+            });
         }
 
         fn instance_init(obj: &InitializingObject<Self>) {
@@ -108,6 +121,13 @@ mod imp {
                         false,
                         glib::ParamFlags::READWRITE,
                     ),
+                    glib::ParamSpec::new_boolean(
+                        "sticky",
+                        "Sticky",
+                        "Whether the room history should stick to the newest message in the timeline",
+                        true,
+                        glib::ParamFlags::READWRITE,
+                    ),
                 ]
             });
 
@@ -139,6 +159,7 @@ mod imp {
                         "format-justify-left-symbolic"
                     });
                 }
+                "sticky" => obj.set_sticky(value.get().unwrap()),
                 _ => unimplemented!(),
             }
         }
@@ -149,15 +170,32 @@ mod imp {
                 "room" => obj.room().to_value(),
                 "empty" => obj.room().is_none().to_value(),
                 "markdown-enabled" => self.md_enabled.get().to_value(),
+                "sticky" => obj.sticky().to_value(),
                 _ => unimplemented!(),
             }
         }
 
         fn constructed(&self, obj: &Self::Type) {
+            obj.set_sticky(true);
             let adj = self.listview.vadjustment().unwrap();
 
             adj.connect_value_changed(clone!(@weak obj => move |adj| {
-                obj.load_more_messages(adj);
+                let priv_ = imp::RoomHistory::from_instance(&obj);
+
+                if priv_.is_auto_scrolling.get() {
+                    if adj.value() + adj.page_size() == adj.upper() {
+                        priv_.is_auto_scrolling.set(false);
+                        obj.set_sticky(true);
+                    }
+                } else {
+                    obj.set_sticky(adj.value() + adj.page_size() == adj.upper());
+                    obj.load_more_messages(adj);
+                }
+            }));
+            adj.connect_upper_notify(clone!(@weak obj => move |_| {
+                if obj.sticky() {
+                    obj.scroll_down();
+                }
             }));
 
             let key_events = gtk::EventControllerKey::new();
@@ -326,7 +364,7 @@ impl RoomHistory {
             if room.timeline().empty() {
                 priv_.stack.set_visible_child(&*priv_.loading);
             } else {
-                priv_.stack.set_visible_child(&*priv_.scrolled_window);
+                priv_.stack.set_visible_child(&*priv_.content);
             }
         }
     }
@@ -345,6 +383,37 @@ impl RoomHistory {
     fn parent_window(&self) -> Option<gtk::Window> {
         self.root()?.downcast().ok()
     }
+
+    pub fn sticky(&self) -> bool {
+        let priv_ = imp::RoomHistory::from_instance(self);
+
+        priv_.sticky.get()
+    }
+
+    pub fn set_sticky(&self, sticky: bool) {
+        let priv_ = imp::RoomHistory::from_instance(self);
+
+        if self.sticky() == sticky {
+            return;
+        }
+
+        priv_.scroll_btn_revealer.set_reveal_child(!sticky);
+
+        priv_.sticky.set(sticky);
+        self.notify("sticky");
+    }
+
+    /// Scroll to the newest message in the timeline
+    pub fn scroll_down(&self) {
+        let priv_ = imp::RoomHistory::from_instance(self);
+
+        priv_.is_auto_scrolling.set(true);
+
+        priv_
+            .scrolled_window
+            .emit_by_name("scroll-child", &[&gtk::ScrollType::End, &false])
+            .unwrap();
+    }
 }
 
 impl Default for RoomHistory {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]