[fractal/fractal-next] content: Show media messages in replies in a compact format



commit 245e35de1033d9d443290c45b5ef9b2b36f76f4b
Author: Kévin Commaille <zecakeh tedomum fr>
Date:   Thu Jan 13 14:32:09 2022 +0100

    content: Show media messages in replies in a compact format

 data/resources/ui/components-video-player.ui       |  1 +
 data/resources/ui/content-message-file.ui          |  5 +-
 src/components/video_player.rs                     | 57 ++++++++++++++++++++-
 .../content/room_history/message_row/file.rs       | 49 +++++++++++++-----
 .../content/room_history/message_row/media.rs      | 58 +++++++++++++++++-----
 .../content/room_history/message_row/mod.rs        | 20 +++++---
 6 files changed, 153 insertions(+), 37 deletions(-)
---
diff --git a/data/resources/ui/components-video-player.ui b/data/resources/ui/components-video-player.ui
index 08fe5ca4..a1d664dc 100644
--- a/data/resources/ui/components-video-player.ui
+++ b/data/resources/ui/components-video-player.ui
@@ -12,6 +12,7 @@
               <class name="osd"/>
               <class name="timestamp"/>
             </style>
+            <property name="visible" bind-source="ComponentsVideoPlayer" bind-property="compact" 
bind-flags="sync-create | invert-boolean"/>
             <property name="halign">GTK_ALIGN_START</property>
             <property name="valign">GTK_ALIGN_START</property>
             <property name="margin-start">5</property>
diff --git a/data/resources/ui/content-message-file.ui b/data/resources/ui/content-message-file.ui
index fc7dff38..afba9c9d 100644
--- a/data/resources/ui/content-message-file.ui
+++ b/data/resources/ui/content-message-file.ui
@@ -9,13 +9,12 @@
         <child>
           <object class="GtkLabel">
             <property name="ellipsize">end</property>
-            <binding name="label">
-              <lookup name="filename">ContentMessageFile</lookup>
-            </binding>
+            <property name="label" bind-source="ContentMessageFile" bind-property="filename" 
bind-flags="sync-create"/>
           </object>
         </child>
         <child>
           <object class="GtkBox">
+              <property name="visible" bind-source="ContentMessageFile" bind-property="compact" 
bind-flags="sync-create | invert-boolean"/>
               <child>
                 <object class="GtkButton" id="open">
                   <property name="icon-name">document-open-symbolic</property>
diff --git a/src/components/video_player.rs b/src/components/video_player.rs
index e417d8b7..4b57e293 100644
--- a/src/components/video_player.rs
+++ b/src/components/video_player.rs
@@ -4,11 +4,14 @@ use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate
 mod imp {
     use super::*;
     use glib::subclass::InitializingObject;
-    use std::cell::RefCell;
+    use once_cell::sync::Lazy;
+    use std::cell::{Cell, RefCell};
 
     #[derive(Debug, Default, CompositeTemplate)]
     #[template(resource = "/org/gnome/FractalNext/components-video-player.ui")]
     pub struct VideoPlayer {
+        /// Whether this player should be displayed in a compact format.
+        pub compact: Cell<bool>,
         pub duration_handler: RefCell<Option<glib::SignalHandlerId>>,
         #[template_child]
         pub video: TemplateChild<gtk::Picture>,
@@ -31,7 +34,41 @@ mod imp {
         }
     }
 
-    impl ObjectImpl for VideoPlayer {}
+    impl ObjectImpl for VideoPlayer {
+        fn properties() -> &'static [glib::ParamSpec] {
+            static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
+                vec![glib::ParamSpec::new_boolean(
+                    "compact",
+                    "Compact",
+                    "Whether this player should be displayed in a compact format",
+                    false,
+                    glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+                )]
+            });
+
+            PROPERTIES.as_ref()
+        }
+
+        fn set_property(
+            &self,
+            obj: &Self::Type,
+            _id: usize,
+            value: &glib::Value,
+            pspec: &glib::ParamSpec,
+        ) {
+            match pspec.name() {
+                "compact" => obj.set_compact(value.get().unwrap()),
+                _ => unimplemented!(),
+            }
+        }
+
+        fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
+            match pspec.name() {
+                "compact" => obj.compact().to_value(),
+                _ => unimplemented!(),
+            }
+        }
+    }
 
     impl WidgetImpl for VideoPlayer {}
 
@@ -50,6 +87,22 @@ impl VideoPlayer {
         glib::Object::new(&[]).expect("Failed to create VideoPlayer")
     }
 
+    pub fn compact(&self) -> bool {
+        let priv_ = imp::VideoPlayer::from_instance(self);
+        priv_.compact.get()
+    }
+
+    pub fn set_compact(&self, compact: bool) {
+        let priv_ = imp::VideoPlayer::from_instance(self);
+
+        if self.compact() == compact {
+            return;
+        }
+
+        priv_.compact.set(compact);
+        self.notify("compact");
+    }
+
     /// Set the media_file to display.
     pub fn set_media_file(&self, media_file: &gtk::MediaFile) {
         let priv_ = imp::VideoPlayer::from_instance(self);
diff --git a/src/session/content/room_history/message_row/file.rs 
b/src/session/content/room_history/message_row/file.rs
index 5466d4de..2ea65e8f 100644
--- a/src/session/content/room_history/message_row/file.rs
+++ b/src/session/content/room_history/message_row/file.rs
@@ -5,13 +5,15 @@ mod imp {
     use super::*;
     use glib::subclass::InitializingObject;
     use once_cell::sync::Lazy;
-    use std::cell::RefCell;
+    use std::cell::{Cell, RefCell};
 
     #[derive(Debug, Default, CompositeTemplate)]
     #[template(resource = "/org/gnome/FractalNext/content-message-file.ui")]
     pub struct MessageFile {
         /// The filename of the file
         pub filename: RefCell<Option<String>>,
+        /// Whether this file should be displayed in a compact format.
+        pub compact: Cell<bool>,
     }
 
     #[glib::object_subclass]
@@ -32,13 +34,22 @@ mod imp {
     impl ObjectImpl for MessageFile {
         fn properties() -> &'static [glib::ParamSpec] {
             static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
-                vec![glib::ParamSpec::new_string(
-                    "filename",
-                    "Filename",
-                    "The filename of the file",
-                    None,
-                    glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
-                )]
+                vec![
+                    glib::ParamSpec::new_string(
+                        "filename",
+                        "Filename",
+                        "The filename of the file",
+                        None,
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+                    ),
+                    glib::ParamSpec::new_boolean(
+                        "compact",
+                        "Compact",
+                        "Whether this file should be displayed in a compact format",
+                        false,
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+                    ),
+                ]
             });
 
             PROPERTIES.as_ref()
@@ -53,6 +64,7 @@ mod imp {
         ) {
             match pspec.name() {
                 "filename" => obj.set_filename(value.get().unwrap()),
+                "compact" => obj.set_compact(value.get().unwrap()),
                 _ => unimplemented!(),
             }
         }
@@ -60,13 +72,10 @@ mod imp {
         fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
             match pspec.name() {
                 "filename" => obj.filename().to_value(),
+                "compact" => obj.compact().to_value(),
                 _ => unimplemented!(),
             }
         }
-
-        fn constructed(&self, obj: &Self::Type) {
-            self.parent_constructed(obj);
-        }
     }
 
     impl WidgetImpl for MessageFile {}
@@ -102,6 +111,22 @@ impl MessageFile {
         let priv_ = imp::MessageFile::from_instance(self);
         priv_.filename.borrow().to_owned()
     }
+
+    pub fn set_compact(&self, compact: bool) {
+        let priv_ = imp::MessageFile::from_instance(self);
+
+        if self.compact() == compact {
+            return;
+        }
+
+        priv_.compact.set(compact);
+        self.notify("compact");
+    }
+
+    pub fn compact(&self) -> bool {
+        let priv_ = imp::MessageFile::from_instance(self);
+        priv_.compact.get()
+    }
 }
 
 impl Default for MessageFile {
diff --git a/src/session/content/room_history/message_row/media.rs 
b/src/session/content/room_history/message_row/media.rs
index b8cf64df..a167f88c 100644
--- a/src/session/content/room_history/message_row/media.rs
+++ b/src/session/content/room_history/message_row/media.rs
@@ -32,6 +32,8 @@ const MAX_THUMBNAIL_WIDTH: i32 = 600;
 const MAX_THUMBNAIL_HEIGHT: i32 = 400;
 const FALLBACK_WIDTH: i32 = 480;
 const FALLBACK_HEIGHT: i32 = 360;
+const MAX_COMPACT_THUMBNAIL_WIDTH: i32 = 75;
+const MAX_COMPACT_THUMBNAIL_HEIGHT: i32 = 50;
 
 #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy, glib::GEnum)]
 #[repr(u32)]
@@ -75,6 +77,8 @@ mod imp {
         pub height: Cell<i32>,
         /// The state of the media.
         pub state: Cell<MediaState>,
+        /// Whether to display this media in a compact format.
+        pub compact: Cell<bool>,
         #[template_child]
         pub media: TemplateChild<gtk::Overlay>,
         #[template_child]
@@ -128,6 +132,13 @@ mod imp {
                         MediaState::default() as i32,
                         glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
                     ),
+                    glib::ParamSpec::new_boolean(
+                        "compact",
+                        "Compact",
+                        "Whether to display this media in a compact format",
+                        false,
+                        glib::ParamFlags::READABLE,
+                    ),
                 ]
             });
 
@@ -160,6 +171,7 @@ mod imp {
                 "width" => obj.width().to_value(),
                 "height" => obj.height().to_value(),
                 "state" => obj.state().to_value(),
+                "compact" => obj.compact().to_value(),
                 _ => unimplemented!(),
             }
         }
@@ -172,29 +184,36 @@ mod imp {
     impl WidgetImpl for MessageMedia {
         fn measure(
             &self,
-            _obj: &Self::Type,
+            obj: &Self::Type,
             orientation: gtk::Orientation,
             for_size: i32,
         ) -> (i32, i32, i32, i32) {
             let original_width = self.width.get();
             let original_height = self.height.get();
 
+            let compact = obj.compact();
+            let (max_width, max_height) = if compact {
+                (MAX_COMPACT_THUMBNAIL_WIDTH, MAX_COMPACT_THUMBNAIL_HEIGHT)
+            } else {
+                (MAX_THUMBNAIL_WIDTH, MAX_THUMBNAIL_HEIGHT)
+            };
+
             let (original, max, fallback, original_other, max_other) =
                 if orientation == gtk::Orientation::Vertical {
                     (
                         original_height,
-                        MAX_THUMBNAIL_HEIGHT,
+                        max_height,
                         FALLBACK_HEIGHT,
                         original_width,
-                        MAX_THUMBNAIL_WIDTH,
+                        max_width,
                     )
                 } else {
                     (
                         original_width,
-                        MAX_THUMBNAIL_WIDTH,
+                        max_width,
                         FALLBACK_WIDTH,
                         original_height,
-                        MAX_THUMBNAIL_HEIGHT,
+                        max_height,
                     )
                 };
 
@@ -212,7 +231,7 @@ mod imp {
                 fallback
             };
 
-            // Limit this size to 400 pixels.
+            // Limit this side to max size.
             let size = nat.min(max);
             (0, size, -1, -1)
         }
@@ -316,19 +335,31 @@ impl MessageMedia {
         self.notify("state");
     }
 
-    /// Display the given `image`.
-    pub fn image(&self, image: ImageMessageEventContent, session: &Session) {
+    fn compact(&self) -> bool {
+        let priv_ = imp::MessageMedia::from_instance(self);
+        priv_.compact.get()
+    }
+
+    fn set_compact(&self, compact: bool) {
+        let priv_ = imp::MessageMedia::from_instance(self);
+        priv_.compact.set(compact);
+        self.notify("compact");
+    }
+
+    /// Display the given `image`, in a `compact` format or not.
+    pub fn image(&self, image: ImageMessageEventContent, session: &Session, compact: bool) {
         let info = image.info.as_deref();
         let width = uint_to_i32(info.and_then(|info| info.width));
         let height = uint_to_i32(info.and_then(|info| info.height));
 
         self.set_width(width);
         self.set_height(height);
+        self.set_compact(compact);
         self.build(image, None, MediaType::Image, session);
     }
 
-    /// Display the given `sticker`.
-    pub fn sticker(&self, sticker: StickerEventContent, session: &Session) {
+    /// Display the given `sticker`, in a `compact` format or not.
+    pub fn sticker(&self, sticker: StickerEventContent, session: &Session, compact: bool) {
         let info = &sticker.info;
         let width = uint_to_i32(info.width);
         let height = uint_to_i32(info.height);
@@ -336,11 +367,12 @@ impl MessageMedia {
 
         self.set_width(width);
         self.set_height(height);
+        self.set_compact(compact);
         self.build(sticker, body, MediaType::Sticker, session);
     }
 
-    /// Display the given `video`.
-    pub fn video(&self, video: VideoMessageEventContent, session: &Session) {
+    /// Display the given `video`, in a `compact` format or not.
+    pub fn video(&self, video: VideoMessageEventContent, session: &Session, compact: bool) {
         let info = &video.info.as_deref();
         let width = uint_to_i32(info.and_then(|info| info.width));
         let height = uint_to_i32(info.and_then(|info| info.height));
@@ -348,6 +380,7 @@ impl MessageMedia {
 
         self.set_width(width);
         self.set_height(height);
+        self.set_compact(compact);
         self.build(video, body, MediaType::Video, session);
     }
 
@@ -446,6 +479,7 @@ impl MessageMedia {
                                     priv_.media.set_child(Some(&child));
                                     child
                                 };
+                                child.set_compact(obj.compact());
                                 child.set_media_file(&media_file)
                             }
                         };
diff --git a/src/session/content/room_history/message_row/mod.rs 
b/src/session/content/room_history/message_row/mod.rs
index f5a9b57d..8aa07981 100644
--- a/src/session/content/room_history/message_row/mod.rs
+++ b/src/session/content/room_history/message_row/mod.rs
@@ -200,16 +200,16 @@ impl MessageRow {
                     if let Ok(Some(related_event)) = event.reply_to_event().await {
                         let reply = MessageReply::new();
                         reply.set_related_content_sender(related_event.sender().upcast());
-                        build_content(reply.related_content(), &related_event);
-                        build_content(reply.content(), &event);
+                        build_content(reply.related_content(), &related_event, true);
+                        build_content(reply.content(), &event, false);
                         priv_.content.set_child(Some(&reply));
                     } else {
-                        build_content(&*priv_.content, &event);
+                        build_content(&*priv_.content, &event, false);
                     }
                 })
             );
         } else {
-            build_content(&*priv_.content, event);
+            build_content(&*priv_.content, event, false);
         }
     }
 }
@@ -221,7 +221,10 @@ impl Default for MessageRow {
 }
 
 /// Build the content widget of `event` as a child of `parent`.
-fn build_content(parent: &adw::Bin, event: &Event) {
+///
+/// If `compact` is true, the content should appear in a smaller format without
+/// interactions, if possible.
+fn build_content(parent: &adw::Bin, event: &Event, compact: bool) {
     // TODO: create widgets for all event types
     // TODO: display reaction events from event.relates_to()
     // TODO: we should reuse the already present child widgets when possible
@@ -267,6 +270,7 @@ fn build_content(parent: &adw::Bin, event: &Event) {
                         child
                     };
                     child.set_filename(Some(filename));
+                    child.set_compact(compact);
                 }
                 MessageType::Image(message) => {
                     let child = if let Some(Ok(child)) =
@@ -278,7 +282,7 @@ fn build_content(parent: &adw::Bin, event: &Event) {
                         parent.set_child(Some(&child));
                         child
                     };
-                    child.image(message, &event.room().session());
+                    child.image(message, &event.room().session(), compact);
                 }
                 MessageType::Location(_message) => {}
                 MessageType::Notice(message) => {
@@ -327,7 +331,7 @@ fn build_content(parent: &adw::Bin, event: &Event) {
                         parent.set_child(Some(&child));
                         child
                     };
-                    child.video(message, &event.room().session());
+                    child.video(message, &event.room().session(), compact);
                 }
                 MessageType::VerificationRequest(_) => {
                     // TODO: show more information about the verification
@@ -366,7 +370,7 @@ fn build_content(parent: &adw::Bin, event: &Event) {
                     parent.set_child(Some(&child));
                     child
                 };
-            child.sticker(content, &event.room().session());
+            child.sticker(content, &event.room().session(), compact);
         }
         Some(AnyMessageEventContent::RoomEncrypted(content)) => {
             warn!("Couldn't decrypt event {:?}", content);


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