[fractal] Improve video player in media viewer



commit 46ef6934bb0a492934046b0d3c176750d568900a
Author: sonjita <sonjaleaheinze gmail com>
Date:   Tue Jan 21 14:57:54 2020 +0100

    Improve video player in media viewer
    
    This commit improves the video player in the following 3 ways:
    - a mute button is added to the control box
    - the control box gets hidden away unless the video widget was created
    during the last second or the mouse was moved during the last second
    - both hitting the space key and clicking anywhere on the video widget
    switches between play and pause

 fractal-gtk/src/widgets/inline_player.rs | 39 +++++++++++++++
 fractal-gtk/src/widgets/media_viewer.rs  | 84 ++++++++++++++++++++++++++++++--
 2 files changed, 120 insertions(+), 3 deletions(-)
---
diff --git a/fractal-gtk/src/widgets/inline_player.rs b/fractal-gtk/src/widgets/inline_player.rs
index 99a058e8..9f4c9018 100644
--- a/fractal-gtk/src/widgets/inline_player.rs
+++ b/fractal-gtk/src/widgets/inline_player.rs
@@ -25,6 +25,7 @@ use log::{error, info, warn};
 
 use gtk;
 use gtk::prelude::*;
+use gtk::ButtonExt;
 
 // use gio::{File, FileExt};
 use glib::SignalHandlerId;
@@ -60,6 +61,7 @@ pub trait PlayerExt {
     );
     fn get_controls_container(player: &Rc<Self>) -> Option<gtk::Box>;
     fn get_player(player: &Rc<Self>) -> gst_player::Player;
+    fn switch_mute_state(player: &Rc<Self>, button: &gtk::Button);
 }
 
 #[derive(Debug, Clone)]
@@ -236,6 +238,7 @@ pub struct VideoPlayerWidget {
     controls: Option<PlayerControls>,
     local_path: Rc<RefCell<Option<String>>>,
     dimensions: Rc<RefCell<Option<(i32, i32)>>>,
+    state: Rc<RefCell<Option<gst_player::PlayerState>>>,
 }
 
 impl Default for VideoPlayerWidget {
@@ -265,6 +268,7 @@ impl Default for VideoPlayerWidget {
             controls: None,
             local_path: Rc::new(RefCell::new(None)),
             dimensions: Rc::new(RefCell::new(None)),
+            state: Rc::new(RefCell::new(None)),
         }
     }
 }
@@ -299,6 +303,15 @@ impl VideoPlayerWidget {
                 foo.borrow_mut().take();
             });
         }
+
+        /* The followign callback requires `Send` but is handled by the gtk main loop */
+        let player_weak = Fragile::new(Rc::downgrade(&w));
+        w.player.connect_state_changed(move |_, state| {
+            player_weak.get().upgrade().map(|player| {
+                *player.state.borrow_mut() = Some(state);
+            });
+        });
+
         w
     }
 
@@ -416,6 +429,17 @@ impl VideoPlayerWidget {
         self.player.stop();
         self.player.disconnect(id);
     }
+
+    pub fn switch_play_pause_state(player: &Rc<Self>) {
+        match *player.state.borrow() {
+            Some(gst_player::PlayerState::Paused) => {
+                player.play();
+            }
+            _ => {
+                player.pause();
+            }
+        }
+    }
 }
 
 impl PartialEq for VideoPlayerWidget {
@@ -522,6 +546,21 @@ impl<T: MediaPlayer + 'static> PlayerExt for T {
     fn get_player(player: &Rc<Self>) -> gst_player::Player {
         player.get_player()
     }
+
+    fn switch_mute_state(player_widget: &Rc<Self>, button: &gtk::Button) {
+        let player = player_widget.get_player();
+        if player.get_mute() {
+            player.set_mute(false);
+            let image =
+                gtk::Image::new_from_icon_name(Some("audio-volume-high"), gtk::IconSize::Button);
+            button.set_image(Some(&image));
+        } else {
+            player.set_mute(true);
+            let image =
+                gtk::Image::new_from_icon_name(Some("audio-volume-muted"), gtk::IconSize::Button);
+            button.set_image(Some(&image));
+        }
+    }
 }
 
 impl<T: MediaPlayer + 'static> ControlsConnection for T {
diff --git a/fractal-gtk/src/widgets/media_viewer.rs b/fractal-gtk/src/widgets/media_viewer.rs
index 95e17476..5cfde95b 100644
--- a/fractal-gtk/src/widgets/media_viewer.rs
+++ b/fractal-gtk/src/widgets/media_viewer.rs
@@ -303,10 +303,61 @@ impl Data {
 
         let overlay = Overlay::new();
         overlay.add(&player.get_video_widget());
+
+        let full_control_box = gtk::Box::new(gtk::Orientation::Horizontal, 6);
         let control_box = PlayerExt::get_controls_container(&player).unwrap();
-        control_box.set_valign(gtk::Align::End);
-        control_box.get_style_context().add_class("osd");
-        overlay.add_overlay(&control_box);
+        full_control_box.pack_start(&control_box, false, true, 0);
+
+        let mute_button = gtk::Button::new_from_icon_name(
+            Some("audio-volume-high"),
+            gtk::IconSize::Button.into(),
+        );
+        let player_weak = Rc::downgrade(&player);
+        mute_button.connect_clicked(move |button| {
+            player_weak.upgrade().map(|player| {
+                VideoPlayerWidget::switch_mute_state(&player, &button);
+            });
+        });
+        full_control_box.pack_start(&mute_button, false, false, 3);
+
+        let control_revealer = gtk::Revealer::new();
+        control_revealer.add(&full_control_box);
+        control_revealer.set_reveal_child(true);
+        let source_id: Rc<RefCell<Option<glib::source::SourceId>>> = Rc::new(RefCell::new(None));
+        let first_sid = gtk::timeout_add_seconds(
+            1,
+            clone!(source_id, control_revealer => move || {
+                control_revealer.set_reveal_child(false);
+                *source_id.borrow_mut() = None;
+                Continue(false)
+            }),
+        );
+        *source_id.borrow_mut() = Some(first_sid);
+        let media_viewer_box = self
+            .builder
+            .clone()
+            .get_object::<gtk::Box>("media_viewer_box")
+            .expect("Cant find media_viewer_box in ui file.");
+        media_viewer_box.connect_motion_notify_event(clone!( control_revealer => move |_, _| {
+            control_revealer.set_reveal_child(true);
+            if let Some(sid) = source_id.borrow_mut().take() {
+                glib::source::source_remove(sid);
+            }
+            let new_sid = gtk::timeout_add_seconds(
+                1,
+                clone!(source_id, control_revealer => move || {
+                    control_revealer.set_reveal_child(false);
+                    *source_id.borrow_mut() = None;
+                    Continue(false)
+                }),
+            );
+            *source_id.borrow_mut() = Some(new_sid);
+            Inhibit(false)
+        }));
+
+        control_revealer.set_valign(gtk::Align::End);
+        control_revealer.get_style_context().add_class("osd");
+        overlay.add_overlay(&control_revealer);
 
         bx.pack_start(&overlay, false, false, 0);
 
@@ -329,6 +380,33 @@ impl Data {
                 }
             });
         });
+
+        let player_weak = Rc::downgrade(&player);
+        self.main_window.connect_key_press_event(move |_, k| {
+            player_weak.upgrade().map(|player| {
+                if player.get_video_widget().get_mapped() {
+                    match k.get_keyval() {
+                        gdk::enums::key::space => {
+                            VideoPlayerWidget::switch_play_pause_state(&player);
+                        }
+                        _ => {}
+                    }
+                }
+            });
+            Inhibit(false)
+        });
+        let player_weak = Rc::downgrade(&player);
+        player
+            .get_video_widget()
+            .connect_button_press_event(move |_, e| {
+                if e.get_button() == 1 {
+                    player_weak.upgrade().map(|player| {
+                        VideoPlayerWidget::switch_play_pause_state(&player);
+                    });
+                }
+                Inhibit(false)
+            });
+
         self.widget = Widget::Video(player);
         bx
     }


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