PlayerWidget: Wire the play and pause buttons and add style classes to the Info Labels.

This also includes the yak shaving of a ::new and ::inti methods.
This commit is contained in:
Jordan Petridis 2018-06-14 02:17:43 +03:00
parent d462264ab7
commit d671c07afb
5 changed files with 103 additions and 33 deletions

View File

@ -39,7 +39,6 @@
<object class="GtkBox" id="buttons"> <object class="GtkBox" id="buttons">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">center</property>
<child> <child>
<object class="GtkButton" id="rewind_button"> <object class="GtkButton" id="rewind_button">
<property name="width_request">42</property> <property name="width_request">42</property>
@ -60,7 +59,6 @@
<child> <child>
<object class="GtkButton" id="play_button"> <object class="GtkButton" id="play_button">
<property name="width_request">60</property> <property name="width_request">60</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Play</property> <property name="tooltip_text" translatable="yes">Play</property>
@ -77,7 +75,6 @@
<object class="GtkButton" id="pause_button"> <object class="GtkButton" id="pause_button">
<property name="width_request">60</property> <property name="width_request">60</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Play</property> <property name="tooltip_text" translatable="yes">Play</property>
@ -124,7 +121,7 @@
<object class="GtkImage" id="show_cover"> <object class="GtkImage" id="show_cover">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="pixel_size">42</property> <property name="pixel_size">24</property>
<property name="icon_name">image-x-generic-symbolic</property> <property name="icon_name">image-x-generic-symbolic</property>
</object> </object>
<packing> <packing>
@ -137,12 +134,18 @@
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkLabel" id="show_label"> <object class="GtkLabel" id="show_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Show Title</property> <property name="label" translatable="yes">Show Title</property>
<style>
<class name="player-show-label"/>
</style>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -155,6 +158,9 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Episode Title</property> <property name="label" translatable="yes">Episode Title</property>
<style>
<class name="player-episode-label"/>
</style>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -178,6 +184,7 @@
<object class="GtkScale" id="seek"> <object class="GtkScale" id="seek">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="valign">center</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="show_fill_level">True</property> <property name="show_fill_level">True</property>
<property name="restrict_to_fill_level">False</property> <property name="restrict_to_fill_level">False</property>

View File

@ -10,6 +10,11 @@ list, border {
border-radius: 4px; border-radius: 4px;
} }
.playback { .player-show-label {
border-top: 1px solid @borders; font-weight: bold;
font-size: smaller;
}
.player-episode-label {
font-size: smaller;
} }

View File

@ -53,7 +53,7 @@ pub enum Action {
MarkAllPlayerNotification(Arc<Podcast>), MarkAllPlayerNotification(Arc<Podcast>),
RemoveShow(Arc<Podcast>), RemoveShow(Arc<Podcast>),
ErrorNotification(String), ErrorNotification(String),
PlayEpisode(String), InitEpisode(i32),
} }
#[derive(Debug)] #[derive(Debug)]
@ -119,8 +119,7 @@ impl App {
// Add the overlay to the main Box // Add the overlay to the main Box
wrap.add(&overlay); wrap.add(&overlay);
// FIXME: this should have a ::new() method instead. let player = PlayerWidget::new();
let player = PlayerWidget::default();
// Add the player to the main Box // Add the player to the main Box
wrap.add(&player.action_bar); wrap.add(&player.action_bar);
// player.reveal(); // player.reveal();
@ -208,7 +207,7 @@ impl App {
|| {}, UndoState::Hidden); || {}, UndoState::Hidden);
notif.show(&overlay); notif.show(&overlay);
}, },
Ok(Action::PlayEpisode(_uri)) => (), Ok(Action::InitEpisode(rowid)) => player.initialize_episode(rowid).unwrap(),
Err(_) => (), Err(_) => (),
} }

View File

@ -1,4 +1,3 @@
use gio::{File, FileExt};
use glib; use glib;
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
@ -18,7 +17,6 @@ use hammond_data::EpisodeWidgetQuery;
use app::Action; use app::Action;
use manager; use manager;
use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex, TryLockError}; use std::sync::{Arc, Mutex, TryLockError};
@ -444,26 +442,18 @@ fn on_download_clicked(ep: &EpisodeWidgetQuery, sender: &Sender<Action>) -> Resu
} }
fn on_play_bttn_clicked( fn on_play_bttn_clicked(
widget: &Rc<EpisodeWidget>, _widget: &Rc<EpisodeWidget>,
episode: &mut EpisodeWidgetQuery, episode: &mut EpisodeWidgetQuery,
sender: &Sender<Action>, sender: &Sender<Action>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let uri = dbqueries::get_episode_local_uri_from_id(episode.rowid())?
.ok_or_else(|| format_err!("Expected Some found None."))?;
let p = Path::new(&uri);
if p.exists() {
info!("Opening {}", uri);
// uri is actually a path, convert it (hacky)
let uri = File::new_for_path(p).get_uri().expect("Bad file path");
sender.send(Action::PlayEpisode(uri)).ok();
} else {
bail!("File \"{}\" does not exist.", uri);
}
widget.info.set_title(&episode);
sender sender
.send(Action::RefreshEpisodesViewBGR) .send(Action::InitEpisode(episode.rowid()))
.map_err(From::from) .map_err(From::from)
// widget.info.set_title(&episode);
// sender
// .send(Action::RefreshEpisodesViewBGR)
// .map_err(From::from)
} }
// fn open_uri(rowid: i32) -> Result<(), Error> { // fn open_uri(rowid: i32) -> Result<(), Error> {

View File

@ -1,5 +1,7 @@
#![allow(warnings)] #![allow(warnings)]
use gio::{File, FileExt};
use gstreamer::ClockTime; use gstreamer::ClockTime;
use gstreamer_player as gst; use gstreamer_player as gst;
use gtk; use gtk;
@ -7,6 +9,14 @@ use gtk::prelude::*;
use failure::Error; use failure::Error;
use hammond_data::dbqueries;
use hammond_data::{EpisodeWidgetQuery, PodcastCoverQuery};
use utils::set_image_from_path;
use std::path::Path;
use std::rc::Rc;
pub trait PlayerExt { pub trait PlayerExt {
fn play(&self); fn play(&self);
fn pause(&self); fn pause(&self);
@ -26,8 +36,25 @@ struct PlayerInfo {
} }
impl PlayerInfo { impl PlayerInfo {
fn init(&self) -> Result<(), Error> { // FIXME: create a Diesel Model of the joined episode and podcast query instead
unimplemented!() fn init(&self, episode: &EpisodeWidgetQuery, podcast: &PodcastCoverQuery) {
self.set_cover_image(podcast);
self.set_show_title(podcast);
self.set_episode_title(episode);
}
fn set_episode_title(&self, episode: &EpisodeWidgetQuery) {
self.episode.set_text(&episode.title());
}
fn set_show_title(&self, show: &PodcastCoverQuery) {
self.show.set_text(&show.title());
}
fn set_cover_image(&self, show: &PodcastCoverQuery) {
set_image_from_path(&self.cover, show.id(), 24)
.map_err(|err| error!("Player Cover: {}", err))
.ok();
} }
} }
@ -123,9 +150,51 @@ impl Default for PlayerWidget {
} }
impl PlayerWidget { impl PlayerWidget {
pub fn new() -> Rc<Self> {
let w = Rc::new(Self::default());
Self::init(&w);
w
}
fn init(s: &Rc<Self>) {
// Connect the play button to the gst Player.
s.controls.play.connect_clicked(clone!(s => move |_| s.play()));
// Connect the pause button to the gst Player.
s.controls.pause.connect_clicked(clone!(s => move |_| s.pause()));
}
fn reveal(&self) { fn reveal(&self) {
self.action_bar.show(); self.action_bar.show();
} }
pub fn initialize_episode(&self, rowid: i32) -> Result<(), Error> {
let ep = dbqueries::get_episode_widget_from_rowid(rowid)?;
let pd = dbqueries::get_podcast_cover_from_id(ep.podcast_id())?;
self.info.init(&ep, &pd);
// Currently that will always be the case since the play button is
// only shown if the file is downloaded
if let Some(ref path) = ep.local_uri() {
if Path::new(path).exists() {
// path is an absolute fs path ex. "foo/bar/baz".
// Convert it so it will have a "file:///"
// FIXME: convert it properly
let uri = File::new_for_path(path).get_uri().expect("Bad file path");
// FIXME: Should also reset/flush the pipeline and then add the file
// play the file
self.player.set_uri(&uri);
self.play();
return Ok(());
}
// TODO: log an error
}
// Stream stuff
unimplemented!()
}
} }
impl PlayerExt for PlayerWidget { impl PlayerExt for PlayerWidget {
@ -135,8 +204,8 @@ impl PlayerExt for PlayerWidget {
self.reveal(); self.reveal();
self.controls.pause.hide(); self.controls.pause.show();
self.controls.play.show(); self.controls.play.hide();
self.player.play(); self.player.play();
} }
@ -145,8 +214,8 @@ impl PlayerExt for PlayerWidget {
// assert the state is paused // assert the state is paused
// TODO: assert!() // TODO: assert!()
self.controls.pause.show(); self.controls.pause.hide();
self.controls.play.hide(); self.controls.play.show();
self.player.pause(); self.player.pause();
} }