podcasts/podcasts-gtk/src/widgets/show.rs
Jordan Petridis c0dca6ab9a
ShowWidget: handle vadjustment with BaseView
Instead of using lazy_static to save the adjustment,
pass it to the widget upon creation. If previous it doesn't
exists pass None instead.
2018-08-09 10:36:51 +03:00

172 lines
4.7 KiB
Rust

use glib;
use gtk::{self, prelude::*, Adjustment, SelectionMode};
use crossbeam_channel::Sender;
use failure::Error;
use fragile::Fragile;
use html2text;
use libhandy::{Column, ColumnExt};
use rayon;
use podcasts_data::dbqueries;
use podcasts_data::Show;
use app::Action;
use utils::{self, lazy_load};
use widgets::{BaseView, EpisodeWidget, ShowMenu};
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub(crate) struct ShowWidget {
view: BaseView,
cover: gtk::Image,
description: gtk::Label,
episodes: gtk::ListBox,
show_id: Option<i32>,
}
impl Default for ShowWidget {
fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/Podcasts/gtk/show_widget.ui");
let sub_cont: gtk::Box = builder.get_object("sub_container").unwrap();
let cover: gtk::Image = builder.get_object("cover").unwrap();
let description: gtk::Label = builder.get_object("description").unwrap();
let view = BaseView::default();
let frame = gtk::Frame::new(None);
let episodes = gtk::ListBox::new();
episodes.set_selection_mode(SelectionMode::None);
let column = Column::new();
column.set_maximum_width(700);
// For some reason the Column is not seen as a gtk::container
// and therefore we can't call add() without the cast
let column = column.upcast::<gtk::Widget>();
let column = column.downcast::<gtk::Container>().unwrap();
frame.add(&episodes);
sub_cont.add(&frame);
column.add(&sub_cont);
view.add(&column);
column.show_all();
ShowWidget {
view,
cover,
description,
episodes,
show_id: None,
}
}
}
impl ShowWidget {
pub(crate) fn new(
pd: Arc<Show>,
sender: Sender<Action>,
vadj: Option<Adjustment>,
) -> Rc<ShowWidget> {
let mut pdw = ShowWidget::default();
pdw.init(&pd);
let menu = ShowMenu::new(&pd, &pdw.episodes, &sender);
sender.send(Action::InitShowMenu(Fragile::new(menu)));
let pdw = Rc::new(pdw);
let res = populate_listbox(&pdw, pd.clone(), sender, vadj);
debug_assert!(res.is_ok());
pdw
}
pub(crate) fn init(&mut self, pd: &Arc<Show>) {
self.set_description(pd.description());
self.show_id = Some(pd.id());
let res = self.set_cover(&pd);
debug_assert!(res.is_ok());
}
pub(crate) fn container(&self) -> &gtk::Box {
self.view.container()
}
pub(crate) fn get_vadjustment(&self) -> Option<Adjustment> {
self.view.get_vadjustment()
}
/// Set the show cover.
fn set_cover(&self, pd: &Arc<Show>) -> Result<(), Error> {
utils::set_image_from_path(&self.cover, pd.id(), 256)
}
/// Set the descripton text.
fn set_description(&self, text: &str) {
self.description
.set_markup(html2text::from_read(text.as_bytes(), 70).trim());
}
pub(crate) fn show_id(&self) -> Option<i32> {
self.show_id
}
}
/// Populate the listbox with the shows episodes.
fn populate_listbox(
// FIXME: we are leaking strong refs here
show: &Rc<ShowWidget>,
pd: Arc<Show>,
sender: Sender<Action>,
vadj: Option<Adjustment>,
) -> Result<(), Error> {
use crossbeam_channel::bounded;
let count = dbqueries::get_pd_episodes_count(&pd)?;
let (sender_, receiver) = bounded(1);
rayon::spawn(clone!(pd => move || {
if let Ok(episodes) = dbqueries::get_pd_episodeswidgets(&pd) {
// The receiver can be dropped if there's an early return
// like on show without episodes for example.
sender_.send(episodes);
}
}));
if count == 0 {
let builder = gtk::Builder::new_from_resource("/org/gnome/Podcasts/gtk/empty_show.ui");
let container: gtk::Box = builder
.get_object("empty_show")
.ok_or_else(|| format_err!("FOO"))?;
show.episodes.add(&container);
return Ok(());
}
let show_ = show.clone();
gtk::idle_add(move || {
let episodes = match receiver.try_recv() {
Some(e) => e,
None => return glib::Continue(true),
};
debug_assert!(episodes.len() as i64 == count);
let list = show_.episodes.clone();
let constructor = clone!(sender => move |ep| {
EpisodeWidget::new(ep, &sender).container.clone()
});
let callback = clone!(show_, vadj => move || {
if let Some(ref v) = vadj {
show_.view.set_adjutments(None, Some(v))
};
});
lazy_load(episodes, list.clone(), constructor, callback);
glib::Continue(false)
});
Ok(())
}