[fractal] Let ScrollWidget handle scrolls



commit 2d8350ee341c806486cf028594ede5abcafb4526
Author: Kai A. Hiller <V02460 gmail com>
Date:   Tue Oct 27 10:56:50 2020 +0100

    Let ScrollWidget handle scrolls

 fractal-gtk/src/widgets/room_history.rs  | 23 ++++-----
 fractal-gtk/src/widgets/scroll_widget.rs | 80 ++++++++++++++++++++++++++++----
 2 files changed, 81 insertions(+), 22 deletions(-)
---
diff --git a/fractal-gtk/src/widgets/room_history.rs b/fractal-gtk/src/widgets/room_history.rs
index 2c097f0c..4b704691 100644
--- a/fractal-gtk/src/widgets/room_history.rs
+++ b/fractal-gtk/src/widgets/room_history.rs
@@ -60,16 +60,12 @@ impl List {
     /// ### Panics
     /// Panics if `index > len`.
     pub fn add_item(&mut self, index: usize, element: Element) {
-        /* Spinner is at position 0, so increment index by 1 */
-        self.listbox
-            .insert(element.get_listbox_row(), (index + 1) as i32);
+        self.view.insert(index, element.get_listbox_row());
         self.list.insert(self.list.len() - index, element);
     }
 
     pub fn add_top(&mut self, element: Element) {
-        self.view.set_balance_top();
         self.add_item(0, element);
-        self.view.set_kinetic_scrolling(true);
         /* TODO: update the previous message:
          * we need to update the previous row because it could be that we have to remove the header */
     }
@@ -81,16 +77,15 @@ impl List {
         self.add_item(self.list.len(), element);
     }
 
-    fn remove_item(&mut self, index: usize, row: &gtk::ListBoxRow) {
+    fn remove_item(&mut self, index: usize) {
         self.list.remove(index);
-        self.listbox.remove(row);
+        self.view.remove(self.list.len() - index - 1);
     }
 
-    fn replace_item(&mut self, index: usize, row: &gtk::ListBoxRow, element: Element) {
-        /* Spinner is at position 0, so increment index by 1 */
-        self.listbox
-            .insert(element.get_listbox_row(), (self.list.len() - index) as i32);
-        self.listbox.remove(row);
+    fn replace_item(&mut self, index: usize, element: Element) {
+        self.view.remove(self.list.len() - index - 1);
+        self.view
+            .insert(self.list.len() - index - 1, element.get_listbox_row());
         self.list[index] = element;
     }
 
@@ -636,7 +631,7 @@ impl RoomHistory {
             self.access_token.clone(),
             &self.rows,
         ));
-        rows.replace_item(i, msg_widget.get_listbox_row(), Element::Message(item));
+        rows.replace_item(i, Element::Message(item));
         None
     }
 
@@ -661,7 +656,7 @@ impl RoomHistory {
         let msg_widget = msg.widget.clone()?;
         let msg_sender = msg.sender.clone();
         msg.msg.redacted = true;
-        rows.remove_item(i, msg_widget.get_listbox_row());
+        rows.remove_item(i);
 
         // If the redacted message was a header message let's set
         // the header on the next non-redacted message instead.
diff --git a/fractal-gtk/src/widgets/scroll_widget.rs b/fractal-gtk/src/widgets/scroll_widget.rs
index bd8a224b..a3bc33b9 100644
--- a/fractal-gtk/src/widgets/scroll_widget.rs
+++ b/fractal-gtk/src/widgets/scroll_widget.rs
@@ -10,8 +10,6 @@ use gtk::prelude::*;
 
 use libhandy::prelude::*;
 
-// This really requires to opt-out of the lint
-#[allow(dead_code)]
 #[derive(Debug, Clone, PartialEq)]
 enum Position {
     Top,
@@ -217,14 +215,29 @@ impl ScrollWidget {
         None
     }
 
-    pub fn set_balance_top(&self) {
-        /* FIXME: Workaround: https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 */
-        self.widgets.view.set_kinetic_scrolling(false);
-        self.balance.set(Some(Position::Top));
+    /// Inserts an element to the list and adjusts the scoll position.
+    ///
+    /// ### Panics
+    /// Panics if `index > number of elements`.
+    pub fn insert(&self, index: usize, row: &gtk::ListBoxRow) {
+        // Spinner is at position 0, so increment index by 1.
+        self.set_balance(index + 1); // might panic
+        self.widgets.listbox.insert(row, (index + 1) as i32);
+        self.widgets.view.set_kinetic_scrolling(true);
     }
 
-    pub fn set_kinetic_scrolling(&self, enabled: bool) {
-        self.widgets.view.set_kinetic_scrolling(enabled);
+    /// Removes an element from the list and adjusts the scoll position.
+    ///
+    /// ### Panics
+    /// Panics if `index >= number of elements`.
+    pub fn remove(&self, index: usize) {
+        // Spinner is at position 0, so increment index by 1.
+        self.set_balance(index + 1); // might panic
+        match self.widgets.listbox.get_row_at_index((index + 1) as i32) {
+            Some(row) => self.widgets.listbox.remove(&row),
+            None => panic!("Could not remove element due to invalid index"),
+        }
+        self.widgets.view.set_kinetic_scrolling(true);
     }
 
     pub fn get_listbox(&self) -> gtk::ListBox {
@@ -251,6 +264,57 @@ impl ScrollWidget {
             self.widgets.typing_label.set_markup(typing_str);
         }
     }
+
+    /// Sets the balance for a change happening at the given index.
+    ///
+    /// ### Panics
+    /// Panics if `index > number of listbox children`.
+    fn set_balance(&self, index: usize) {
+        /* FIXME: Workaround: https://gitlab.gnome.org/GNOME/gtk/merge_requests/395 */
+        self.widgets.view.set_kinetic_scrolling(false);
+
+        // Calculate relative positions
+        let change_pos = self.get_relative_index_pos(index); // might panic
+        let scroll_pos = self.get_relative_scroll_pos();
+
+        if change_pos < scroll_pos {
+            // Insertion happens in or above the view, so it needs adjustment.
+            self.balance.set(Some(Position::Top));
+        } else {
+            self.balance.set(Some(Position::Bottom));
+        }
+    }
+
+    /// Returns the relative position of a given index for the listbox.
+    ///
+    /// - `index == 0` will return 0.0
+    /// - `index == number of listbox children` will return 1.0
+    ///
+    /// ### Panics
+    /// Panics if `index > number of listbox children`.
+    fn get_relative_index_pos(&self, index: usize) -> f64 {
+        if index == self.widgets.listbox.get_children().len() {
+            return 1.0;
+        }
+
+        let row_at_index = self
+            .widgets
+            .listbox
+            .get_row_at_index(index as i32)
+            .expect("Index out of bounds");
+        let y = row_at_index.get_allocation().y as f64;
+        let listbox_height = self.widgets.listbox.get_allocated_height() as f64;
+
+        y / listbox_height
+    }
+
+    /// Returns the relative position of the view's bottom.
+    fn get_relative_scroll_pos(&self) -> f64 {
+        match self.widgets.view.get_vadjustment() {
+            Some(adj) => (adj.get_value() + adj.get_page_size()) / adj.get_upper(),
+            None => 1.0,
+        }
+    }
 }
 
 pub fn page_up(sw: gtk::ScrolledWindow) {


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