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.
172 lines
4.7 KiB
Rust
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) -> >k::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(())
|
|
}
|