Merge branch 'show_widget_redesign' into 'master'

Show widget redesign

See merge request alatiera/Hammond!6
This commit is contained in:
Jordan Petridis 2017-12-17 13:05:25 +00:00
commit 3cbda5979d
14 changed files with 371 additions and 313 deletions

View File

@ -50,7 +50,7 @@ pub fn get_played_episodes() -> Result<Vec<Episode>> {
Ok(episode.filter(played.is_not_null()).load::<Episode>(&*con)?) Ok(episode.filter(played.is_not_null()).load::<Episode>(&*con)?)
} }
pub fn get_episode_from_id(ep_id: i32) -> Result<Episode> { pub fn get_episode_from_rowid(ep_id: i32) -> Result<Episode> {
use schema::episode::dsl::*; use schema::episode::dsl::*;
let db = connection(); let db = connection();

View File

@ -8,7 +8,7 @@ use std::io::{BufWriter, Read, Write};
use std::path::Path; use std::path::Path;
use errors::*; use errors::*;
use hammond_data::{Episode, EpisodeWidgetQuery, Podcast}; use hammond_data::{EpisodeWidgetQuery, Podcast};
use hammond_data::xdg_dirs::{DL_DIR, HAMMOND_CACHE}; use hammond_data::xdg_dirs::{DL_DIR, HAMMOND_CACHE};
// TODO: Replace path that are of type &str with std::path. // TODO: Replace path that are of type &str with std::path.

View File

@ -6,7 +6,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="spacing">5</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
@ -28,8 +27,9 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="label" translatable="yes">Episode Title</property> <property name="label" translatable="yes">Episode Title</property>
<property name="wrap">True</property>
<property name="ellipsize">end</property> <property name="ellipsize">end</property>
<property name="single_line_mode">True</property>
<property name="max_width_chars">60</property>
<property name="track_visited_links">False</property> <property name="track_visited_links">False</property>
<property name="lines">1</property> <property name="lines">1</property>
</object> </object>
@ -41,7 +41,7 @@
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
@ -135,14 +135,14 @@
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="padding">5</property> <property name="padding">5</property>
<property name="position">0</property> <property name="position">0</property>
@ -162,7 +162,7 @@
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">False</property>
<property name="pack_type">end</property> <property name="pack_type">end</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
@ -237,13 +237,15 @@
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="padding">5</property> <property name="padding">5</property>
<property name="pack_type">end</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="padding">5</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>

View File

@ -190,27 +190,6 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="GtkButton" id="ref_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="valign">center</property>
<property name="use_underline">True</property>
<property name="always_show_image">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">view-refresh-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child> <child>
<object class="GtkMenuButton" id="menu"> <object class="GtkMenuButton" id="menu">
<property name="visible">True</property> <property name="visible">True</property>

View File

@ -2,191 +2,239 @@
<!-- Generated with glade 3.20.2 --> <!-- Generated with glade 3.20.2 -->
<interface domain="gnome-music"> <interface domain="gnome-music">
<requires lib="gtk+" version="3.12"/> <requires lib="gtk+" version="3.12"/>
<object class="GtkBox" id="podcast_widget"> <object class="GtkBox" id="container">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkBox"> <object class="GtkScrolledWindow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="halign">center</property> <property name="shadow_type">in</property>
<property name="valign">center</property>
<property name="margin_left">32</property>
<property name="margin_right">32</property>
<property name="margin_start">32</property>
<property name="margin_end">32</property>
<property name="margin_top">64</property>
<property name="margin_bottom">32</property>
<child> <child>
<object class="GtkBox"> <object class="GtkViewport">
<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>
<property name="valign">center</property>
<property name="orientation">vertical</property>
<property name="spacing">15</property>
<child>
<object class="GtkImage" id="cover">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">start</property>
<property name="margin_left">1</property>
<property name="margin_right">1</property>
<property name="margin_start">1</property>
<property name="margin_end">1</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<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="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property>
<child> <child>
<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="halign">center</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkLabel" id="title_label"> <placeholder/>
<property name="width_request">50</property> </child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="width_request">600</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkFrame">
<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> <property name="label_xalign">0</property>
<property name="label" translatable="yes">Foobar</property> <property name="shadow_type">none</property>
<property name="use_markup">True</property> <child>
<property name="justify">center</property> <object class="GtkBox">
<property name="wrap">True</property> <property name="visible">True</property>
<property name="max_width_chars">28</property> <property name="can_focus">False</property>
<property name="track_visited_links">False</property> <property name="orientation">vertical</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">center</property>
<property name="spacing">10</property>
<child>
<object class="GtkImage" id="cover">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="pixel_size">128</property>
<property name="icon_name">image-x-generic-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkMenuButton" id="settings_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon_name">emblem-system-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="link_button">
<property name="label" translatable="yes">Website</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="unsub_button">
<property name="label" translatable="yes">Unsubscribe</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="description">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="label" translatable="yes">Show description</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="max_width_chars">55</property>
<attributes>
<attribute name="weight" value="medium"/>
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="padding">25</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkFrame" id="episodes">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="unsub_button"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">False</property>
<property name="receives_default">True</property> <property name="orientation">vertical</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child> <child>
<object class="GtkImage"> <placeholder/>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Unsubrscribe from this Podcast.
Warn: This will delete downloaded content associated with this Podcast.</property>
<property name="icon_name">user-trash-symbolic</property>
</object>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">True</property>
<property name="fill">False</property>
<property name="padding">5</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="mark_all_played_button">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Mark all episodes as Played.</property>
<property name="halign">center</property>
<property name="valign">center</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">object-select-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="position">2</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="min_content_width">200</property>
<property name="max_content_width">200</property>
<property name="propagate_natural_width">True</property>
<property name="propagate_natural_height">True</property>
<child>
<object class="GtkTextView" id="desc_text_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="wrap_mode">word-char</property>
<property name="cursor_visible">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="hscrollbar_policy">never</property>
<child>
<object class="GtkViewport" id="view">
<property name="width_request">400</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="shadow_type">none</property>
<child>
<placeholder/>
</child> </child>
</object> </object>
</child> </child>
@ -194,7 +242,7 @@ Warn: This will delete downloaded content associated with this Podcast.</propert
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">2</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
</object> </object>

View File

@ -60,8 +60,8 @@ impl ShowStack {
header: header.clone(), header: header.clone(),
}); });
let pop = ShowsPopulated::new_initialized(show.clone(), header); let pop = ShowsPopulated::new(show.clone(), header);
let widget = ShowWidget::new(); let widget = ShowWidget::default();
let empty = EmptyView::new(); let empty = EmptyView::new();
show.stack.add_named(&pop.container, "podcasts"); show.stack.add_named(&pop.container, "podcasts");
@ -90,7 +90,7 @@ impl ShowStack {
let vis = self.stack.get_visible_child_name().unwrap(); let vis = self.stack.get_visible_child_name().unwrap();
let old = self.stack.get_child_by_name("podcasts").unwrap(); let old = self.stack.get_child_by_name("podcasts").unwrap();
let pop = ShowsPopulated::new(); let pop = ShowsPopulated::default();
pop.init(Rc::new(self.clone()), self.header.clone()); pop.init(Rc::new(self.clone()), self.header.clone());
self.stack.remove(&old); self.stack.remove(&old);
@ -109,7 +109,7 @@ impl ShowStack {
pub fn replace_widget(&self, pd: &Podcast) { pub fn replace_widget(&self, pd: &Podcast) {
let old = self.stack.get_child_by_name("widget").unwrap(); let old = self.stack.get_child_by_name("widget").unwrap();
let new = ShowWidget::new_initialized(Rc::new(self.clone()), self.header.clone(), pd); let new = ShowWidget::new(Rc::new(self.clone()), self.header.clone(), pd);
self.stack.remove(&old); self.stack.remove(&old);
self.stack.add_named(&new.container, "widget"); self.stack.add_named(&new.container, "widget");

View File

@ -12,19 +12,17 @@ use content::Content;
#[derive(Debug)] #[derive(Debug)]
pub struct Header { pub struct Header {
pub container: gtk::HeaderBar, pub container: gtk::HeaderBar,
refresh: gtk::Button,
add_toggle: gtk::MenuButton, add_toggle: gtk::MenuButton,
switch: gtk::StackSwitcher, switch: gtk::StackSwitcher,
back_button: gtk::Button, back_button: gtk::Button,
show_title: gtk::Label, show_title: gtk::Label,
} }
impl Header { impl Default for Header {
pub fn new() -> Rc<Header> { fn default() -> Header {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/headerbar.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/headerbar.ui");
let header: gtk::HeaderBar = builder.get_object("headerbar").unwrap(); let header: gtk::HeaderBar = builder.get_object("headerbar").unwrap();
let refresh: gtk::Button = builder.get_object("ref_button").unwrap();
let add_toggle: gtk::MenuButton = builder.get_object("add_toggle_button").unwrap(); let add_toggle: gtk::MenuButton = builder.get_object("add_toggle_button").unwrap();
let switch: gtk::StackSwitcher = builder.get_object("switch").unwrap(); let switch: gtk::StackSwitcher = builder.get_object("switch").unwrap();
let back_button: gtk::Button = builder.get_object("back_button").unwrap(); let back_button: gtk::Button = builder.get_object("back_button").unwrap();
@ -33,14 +31,22 @@ impl Header {
switch.set_halign(gtk::Align::Center); switch.set_halign(gtk::Align::Center);
switch.show(); switch.show();
Rc::new(Header { Header {
container: header, container: header,
refresh,
add_toggle, add_toggle,
switch, switch,
back_button, back_button,
show_title, show_title,
}) }
}
}
impl Header {
#[allow(dead_code)]
pub fn new(content: Rc<Content>) -> Rc<Header> {
let h = Header::default();
h.init(content);
Rc::new(h)
} }
pub fn init(&self, content: Rc<Content>) { pub fn init(&self, content: Rc<Content>) {
@ -64,11 +70,6 @@ impl Header {
})); }));
self.add_toggle.set_popover(&add_popover); self.add_toggle.set_popover(&add_popover);
// FIXME: There appears to be a memmory leak here.
self.refresh.connect_clicked(clone!(content => move |_| {
utils::refresh_feed(content.clone(), None, None);
}));
let switch = &self.switch; let switch = &self.switch;
let add_toggle = &self.add_toggle; let add_toggle = &self.add_toggle;
let show_title = &self.show_title; let show_title = &self.show_title;
@ -87,7 +88,7 @@ impl Header {
self.switch.hide(); self.switch.hide();
self.add_toggle.hide(); self.add_toggle.hide();
self.back_button.show(); self.back_button.show();
self.show_title.set_text(title); self.set_show_title(title);
self.show_title.show(); self.show_title.show();
} }
@ -98,9 +99,9 @@ impl Header {
self.show_title.hide(); self.show_title.hide();
} }
// pub fn set_show_title(&self, title: &str) { pub fn set_show_title(&self, title: &str) {
// self.show_title.set_text(title) self.show_title.set_text(title)
// } }
} }
fn on_add_bttn_clicked(content: Rc<Content>, entry: &gtk::Entry) { fn on_add_bttn_clicked(content: Rc<Content>, entry: &gtk::Entry) {
@ -111,7 +112,7 @@ fn on_add_bttn_clicked(content: Rc<Content>, entry: &gtk::Entry) {
if let Ok(s) = source { if let Ok(s) = source {
info!("{:?} feed added", url); info!("{:?} feed added", url);
// update the db // update the db
utils::refresh_feed(content, Some(vec![s]), None); utils::refresh_feed(content, Some(vec![s]));
} else { } else {
error!("Feed probably already exists."); error!("Feed probably already exists.");
error!("Error: {:?}", source.unwrap_err()); error!("Error: {:?}", source.unwrap_err());

View File

@ -24,6 +24,7 @@ use hammond_data::utils::checkup;
use gtk::prelude::*; use gtk::prelude::*;
use gio::{ActionMapExt, ApplicationExt, MenuExt, SimpleActionExt}; use gio::{ActionMapExt, ApplicationExt, MenuExt, SimpleActionExt};
use std::rc::Rc;
// http://gtk-rs.org/tuto/closures // http://gtk-rs.org/tuto/closures
#[macro_export] #[macro_export]
@ -52,28 +53,19 @@ mod content;
mod utils; mod utils;
mod static_resource; mod static_resource;
/*
THIS IS STILL A PROTOTYPE.
*/
fn build_ui(app: &gtk::Application) { fn build_ui(app: &gtk::Application) {
let menu = gio::Menu::new(); let menu = gio::Menu::new();
menu.append("Quit", "app.quit"); menu.append("Quit", "app.quit");
menu.append("Checkup", "app.check"); menu.append("Checkup", "app.check");
menu.append("Update feeds", "app.update");
app.set_app_menu(&menu); app.set_app_menu(&menu);
// Get the main window // Get the main window
let window = gtk::ApplicationWindow::new(app); let window = gtk::ApplicationWindow::new(app);
window.set_default_size(1150, 650); window.set_default_size(1150, 650);
// TODO: this will blow horribly
// let ct = content::ContentState::new().unwrap();
// let stack = ct.get_stack();
// let ct = content::Content::new_initialized();
// Get the headerbar // Get the headerbar
let header = headerbar::Header::new(); let header = Rc::new(headerbar::Header::default());
let ct = content::Content::new(header.clone()); let ct = content::Content::new(header.clone());
header.init(ct.clone()); header.init(ct.clone());
window.set_titlebar(&header.container); window.set_titlebar(&header.container);
@ -99,11 +91,32 @@ fn build_ui(app: &gtk::Application) {
}); });
app.add_action(&check); app.add_action(&check);
// queue a db update 1 minute after the startup. let update = gio::SimpleAction::new("update", None);
gtk::idle_add(clone!(ct => move || { let ct_clone = ct.clone();
utils::refresh_feed(ct.clone(), None, Some(60)); update.connect_activate(move |_, _| {
utils::refresh_feed(ct_clone.clone(), None);
});
app.add_action(&update);
// Update on startup
gtk::timeout_add_seconds(
30,
clone!(ct => move || {
utils::refresh_feed(ct.clone(), None);
glib::Continue(false) glib::Continue(false)
})); }),
);
// Auto-updater, runs every hour.
// TODO: expose the interval in which it run to a user setting.
// TODO: show notifications.
gtk::timeout_add_seconds(
3600,
clone!(ct => move || {
utils::refresh_feed(ct.clone(), None);
glib::Continue(true)
}),
);
gtk::idle_add(move || { gtk::idle_add(move || {
let _ = checkup(); let _ = checkup();

View File

@ -5,14 +5,13 @@ use hammond_data::feed;
use hammond_data::{Podcast, Source}; use hammond_data::{Podcast, Source};
use hammond_downloader::downloader; use hammond_downloader::downloader;
use std::{thread, time}; use std::thread;
use std::cell::RefCell; use std::cell::RefCell;
use std::sync::mpsc::{channel, Receiver}; use std::sync::mpsc::{channel, Receiver};
use std::rc::Rc;
use content::Content; use content::Content;
use std::rc::Rc;
type Foo = RefCell<Option<(Rc<Content>, Receiver<bool>)>>; type Foo = RefCell<Option<(Rc<Content>, Receiver<bool>)>>;
// Create a thread local storage that will store the arguments to be transfered. // Create a thread local storage that will store the arguments to be transfered.
@ -22,7 +21,7 @@ thread_local!(static GLOBAL: Foo = RefCell::new(None));
/// If `source` is None, Fetches all the `Source` entries in the database and updates them. /// If `source` is None, Fetches all the `Source` entries in the database and updates them.
/// `delay` represents the desired time in seconds for the thread to sleep before executing. /// `delay` represents the desired time in seconds for the thread to sleep before executing.
/// When It's done,it queues up a `podcast_view` refresh. /// When It's done,it queues up a `podcast_view` refresh.
pub fn refresh_feed(content: Rc<Content>, source: Option<Vec<Source>>, delay: Option<u64>) { pub fn refresh_feed(content: Rc<Content>, source: Option<Vec<Source>>) {
// Create a async channel. // Create a async channel.
let (sender, receiver) = channel(); let (sender, receiver) = channel();
@ -32,11 +31,6 @@ pub fn refresh_feed(content: Rc<Content>, source: Option<Vec<Source>>, delay: Op
})); }));
thread::spawn(move || { thread::spawn(move || {
if let Some(s) = delay {
let t = time::Duration::from_secs(s);
thread::sleep(t);
}
let feeds = { let feeds = {
if let Some(vec) = source { if let Some(vec) = source {
Ok(feed::fetch(vec)) Ok(feed::fetch(vec))
@ -70,6 +64,11 @@ pub fn get_pixbuf_from_path(pd: &Podcast) -> Option<Pixbuf> {
Pixbuf::new_from_file_at_scale(&img_path, 256, 256, true).ok() Pixbuf::new_from_file_at_scale(&img_path, 256, 256, true).ok()
} }
pub fn get_pixbuf_from_path_128(pd: &Podcast) -> Option<Pixbuf> {
let img_path = downloader::cache_image(pd)?;
Pixbuf::new_from_file_at_scale(&img_path, 128, 128, true).ok()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hammond_data::Source; use hammond_data::Source;

View File

@ -5,11 +5,17 @@ pub struct EmptyView {
pub container: gtk::Box, pub container: gtk::Box,
} }
impl EmptyView { impl Default for EmptyView {
pub fn new() -> EmptyView { fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/empty_view.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/empty_view.ui");
let view: gtk::Box = builder.get_object("empty_view").unwrap(); let view: gtk::Box = builder.get_object("empty_view").unwrap();
EmptyView { container: view } EmptyView { container: view }
} }
} }
impl EmptyView {
pub fn new() -> EmptyView {
EmptyView::default()
}
}

View File

@ -19,18 +19,8 @@ pub struct ShowsPopulated {
viewport: gtk::Viewport, viewport: gtk::Viewport,
} }
#[derive(Debug)] impl Default for ShowsPopulated {
struct ShowsChild { fn default() -> Self {
container: gtk::Box,
title: gtk::Label,
cover: gtk::Image,
banner: gtk::Image,
number: gtk::Label,
child: gtk::FlowBoxChild,
}
impl ShowsPopulated {
pub fn new() -> ShowsPopulated {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/shows_view.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/shows_view.ui");
let container: gtk::Box = builder.get_object("fb_parent").unwrap(); let container: gtk::Box = builder.get_object("fb_parent").unwrap();
let flowbox: gtk::FlowBox = builder.get_object("flowbox").unwrap(); let flowbox: gtk::FlowBox = builder.get_object("flowbox").unwrap();
@ -42,10 +32,11 @@ impl ShowsPopulated {
viewport, viewport,
} }
} }
}
#[allow(dead_code)] impl ShowsPopulated {
pub fn new_initialized(show: Rc<ShowStack>, header: Rc<Header>) -> ShowsPopulated { pub fn new(show: Rc<ShowStack>, header: Rc<Header>) -> ShowsPopulated {
let pop = ShowsPopulated::new(); let pop = ShowsPopulated::default();
pop.init(show, header); pop.init(show, header);
pop pop
} }
@ -74,7 +65,7 @@ impl ShowsPopulated {
if let Ok(pds) = podcasts { if let Ok(pds) = podcasts {
pds.iter().for_each(|parent| { pds.iter().for_each(|parent| {
let flowbox_child = ShowsChild::new_initialized(parent); let flowbox_child = ShowsChild::new(parent);
self.flowbox.add(&flowbox_child.child); self.flowbox.add(&flowbox_child.child);
}); });
self.flowbox.show_all(); self.flowbox.show_all();
@ -86,11 +77,20 @@ impl ShowsPopulated {
} }
} }
impl ShowsChild { #[derive(Debug)]
fn new() -> ShowsChild { struct ShowsChild {
container: gtk::Box,
title: gtk::Label,
cover: gtk::Image,
banner: gtk::Image,
number: gtk::Label,
child: gtk::FlowBoxChild,
}
impl Default for ShowsChild {
fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/shows_child.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/shows_child.ui");
// Copy of gnome-music AlbumWidget
let container: gtk::Box = builder.get_object("fb_child").unwrap(); let container: gtk::Box = builder.get_object("fb_child").unwrap();
let title: gtk::Label = builder.get_object("pd_title").unwrap(); let title: gtk::Label = builder.get_object("pd_title").unwrap();
let cover: gtk::Image = builder.get_object("pd_cover").unwrap(); let cover: gtk::Image = builder.get_object("pd_cover").unwrap();
@ -109,6 +109,15 @@ impl ShowsChild {
child, child,
} }
} }
}
impl ShowsChild {
pub fn new(pd: &Podcast) -> ShowsChild {
let child = ShowsChild::default();
child.init(pd);
child
}
fn init(&self, pd: &Podcast) { fn init(&self, pd: &Podcast) {
self.title.set_text(pd.title()); self.title.set_text(pd.title());
@ -122,13 +131,6 @@ impl ShowsChild {
self.configure_banner(pd); self.configure_banner(pd);
} }
pub fn new_initialized(pd: &Podcast) -> ShowsChild {
let child = ShowsChild::new();
child.init(pd);
child
}
fn configure_banner(&self, pd: &Podcast) { fn configure_banner(&self, pd: &Podcast) {
let bann = let bann =
Pixbuf::new_from_resource_at_scale("/org/gnome/hammond/banner.png", 256, 256, true); Pixbuf::new_from_resource_at_scale("/org/gnome/hammond/banner.png", 256, 256, true);

View File

@ -47,8 +47,8 @@ struct EpisodeWidget {
progress_label: gtk::Label, progress_label: gtk::Label,
} }
impl EpisodeWidget { impl Default for EpisodeWidget {
fn new() -> EpisodeWidget { fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episode_widget.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episode_widget.ui");
let container: gtk::Box = builder.get_object("episode_container").unwrap(); let container: gtk::Box = builder.get_object("episode_container").unwrap();
@ -95,9 +95,11 @@ impl EpisodeWidget {
progress_label, progress_label,
} }
} }
}
pub fn new_initialized(episode: &mut EpisodeWidgetQuery, pd: &Podcast) -> EpisodeWidget { impl EpisodeWidget {
let widget = EpisodeWidget::new(); pub fn new(episode: &mut EpisodeWidgetQuery, pd: &Podcast) -> EpisodeWidget {
let widget = EpisodeWidget::default();
widget.init(episode, pd); widget.init(episode, pd);
widget widget
} }
@ -109,6 +111,12 @@ impl EpisodeWidget {
self.title.set_xalign(0.0); self.title.set_xalign(0.0);
self.title.set_text(episode.title()); self.title.set_text(episode.title());
if episode.played().is_some() {
self.title
.get_style_context()
.map(|c| c.add_class("dim-label"));
}
let progress = self.progress.clone(); let progress = self.progress.clone();
timeout_add(200, move || { timeout_add(200, move || {
progress.pulse(); progress.pulse();
@ -133,10 +141,16 @@ impl EpisodeWidget {
self.delete.show(); self.delete.show();
} }
self.play.connect_clicked(clone!(episode => move |_| { let title = &self.title;
self.play
.connect_clicked(clone!(episode, title => move |_| {
let mut episode = episode.clone(); let mut episode = episode.clone();
on_play_bttn_clicked(episode.rowid()); on_play_bttn_clicked(episode.rowid());
let _ = episode.set_played_now(); if episode.set_played_now().is_ok() {
title
.get_style_context()
.map(|c| c.add_class("dim-label"));
};
})); }));
let play = &self.play; let play = &self.play;
@ -234,7 +248,7 @@ fn on_play_bttn_clicked(episode_id: i32) {
} }
fn on_delete_bttn_clicked(episode_id: i32) { fn on_delete_bttn_clicked(episode_id: i32) {
let mut ep = dbqueries::get_episode_from_id(episode_id).unwrap(); let mut ep = dbqueries::get_episode_from_rowid(episode_id).unwrap();
let e = delete_local_content(&mut ep); let e = delete_local_content(&mut ep);
if let Err(err) = e { if let Err(err) = e {
@ -269,11 +283,18 @@ fn receive() -> glib::Continue {
pub fn episodes_listbox(pd: &Podcast) -> Result<gtk::ListBox> { pub fn episodes_listbox(pd: &Podcast) -> Result<gtk::ListBox> {
let episodes = dbqueries::get_pd_episodeswidgets(pd)?; let episodes = dbqueries::get_pd_episodeswidgets(pd)?;
// TODO: add a separator
let list = gtk::ListBox::new(); let list = gtk::ListBox::new();
episodes.into_iter().for_each(|mut ep| { episodes.into_iter().for_each(|mut ep| {
let widget = EpisodeWidget::new_initialized(&mut ep, pd); let widget = EpisodeWidget::new(&mut ep, pd);
list.add(&widget.container) list.add(&widget.container);
let sep = gtk::Separator::new(gtk::Orientation::Vertical);
sep.set_sensitive(false);
sep.set_can_focus(false);
list.add(&sep);
sep.show()
}); });
list.set_vexpand(false); list.set_vexpand(false);

View File

@ -1,57 +1,64 @@
use gtk::prelude::*; use gtk::prelude::*;
use gtk; use gtk;
use diesel::Identifiable; use diesel::Identifiable;
use open;
use std::fs; use dissolve;
use hammond_data::dbqueries; use hammond_data::dbqueries;
use hammond_data::Podcast; use hammond_data::Podcast;
use hammond_data::utils::replace_extra_spaces;
use hammond_downloader::downloader; use hammond_downloader::downloader;
use widgets::episode::episodes_listbox; use widgets::episode::episodes_listbox;
use utils::get_pixbuf_from_path; use utils::get_pixbuf_from_path_128;
use content::ShowStack; use content::ShowStack;
use headerbar::Header; use headerbar::Header;
use std::rc::Rc; use std::rc::Rc;
use std::fs;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ShowWidget { pub struct ShowWidget {
pub container: gtk::Box, pub container: gtk::Box,
cover: gtk::Image, cover: gtk::Image,
title: gtk::Label, description: gtk::Label,
description: gtk::TextView, link: gtk::Button,
view: gtk::Viewport, settings: gtk::MenuButton,
unsub: gtk::Button, unsub: gtk::Button,
played: gtk::Button, episodes: gtk::Frame,
} }
impl ShowWidget { impl Default for ShowWidget {
pub fn new() -> ShowWidget { fn default() -> Self {
// Adapted from gnome-music AlbumWidget
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/show_widget.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/show_widget.ui");
let container: gtk::Box = builder.get_object("podcast_widget").unwrap(); let container: gtk::Box = builder.get_object("container").unwrap();
let episodes: gtk::Frame = builder.get_object("episodes").unwrap();
let cover: gtk::Image = builder.get_object("cover").unwrap(); let cover: gtk::Image = builder.get_object("cover").unwrap();
let title: gtk::Label = builder.get_object("title_label").unwrap(); let description: gtk::Label = builder.get_object("description").unwrap();
let description: gtk::TextView = builder.get_object("desc_text_view").unwrap();
let view: gtk::Viewport = builder.get_object("view").unwrap();
let unsub: gtk::Button = builder.get_object("unsub_button").unwrap(); let unsub: gtk::Button = builder.get_object("unsub_button").unwrap();
let played: gtk::Button = builder.get_object("mark_all_played_button").unwrap(); let link: gtk::Button = builder.get_object("link_button").unwrap();
let settings: gtk::MenuButton = builder.get_object("settings_button").unwrap();
unsub
.get_style_context()
.map(|c| c.add_class("destructive-action"));
ShowWidget { ShowWidget {
container, container,
cover, cover,
title,
description, description,
view,
unsub, unsub,
played, link,
settings,
episodes,
} }
} }
}
pub fn new_initialized(shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) -> ShowWidget { impl ShowWidget {
let pdw = ShowWidget::new(); pub fn new(shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) -> ShowWidget {
let pdw = ShowWidget::default();
pdw.init(shows, header, pd); pdw.init(shows, header, pd);
pdw pdw
} }
@ -65,37 +72,29 @@ impl ShowWidget {
header.switch_to_normal(); header.switch_to_normal();
})); }));
self.title.set_text(pd.title());
let listbox = episodes_listbox(pd); let listbox = episodes_listbox(pd);
if let Ok(l) = listbox { if let Ok(l) = listbox {
self.view.add(&l); self.episodes.add(&l);
} }
{ // TODO: Temporary solution until we render html urls/bold/italic probably with markup.
let buff = self.description.get_buffer().unwrap(); let desc = dissolve::strip_html_tags(pd.description()).join(" ");
buff.set_text(pd.description()); self.description.set_text(&replace_extra_spaces(&desc));
}
let img = get_pixbuf_from_path(pd); let img = get_pixbuf_from_path_128(pd);
if let Some(i) = img { if let Some(i) = img {
self.cover.set_from_pixbuf(&i); self.cover.set_from_pixbuf(&i);
} }
self.played.connect_clicked(clone!(shows, pd => move |_| { let link = pd.link().to_owned();
on_played_button_clicked(shows.clone(), &pd); self.link.connect_clicked(move |_| {
})); info!("Opening link: {}", &link);
let _ = open::that(&link);
});
self.show_played_button(pd); // self.played.connect_clicked(clone!(shows, pd => move |_| {
} // on_played_button_clicked(shows.clone(), &pd);
// }));
fn show_played_button(&self, pd: &Podcast) {
let new_episodes = dbqueries::get_pd_unplayed_episodes(pd);
if let Ok(n) = new_episodes {
if !n.is_empty() {
self.played.show()
}
}
} }
} }
@ -119,6 +118,7 @@ fn on_unsub_button_clicked(shows: Rc<ShowStack>, pd: &Podcast, unsub_button: &gt
shows.update_podcasts(); shows.update_podcasts();
} }
#[allow(dead_code)]
fn on_played_button_clicked(shows: Rc<ShowStack>, pd: &Podcast) { fn on_played_button_clicked(shows: Rc<ShowStack>, pd: &Podcast) {
let _ = dbqueries::update_none_to_played_now(pd); let _ = dbqueries::update_none_to_played_now(pd);

View File

@ -1,26 +1,13 @@
unstable_features = true unstable_features = true
verbose = false verbose = false
disable_all_formatting = false
skip_children = false
max_width = 100 max_width = 100
comment_width = 100 comment_width = 100
wrap_comments = true wrap_comments = true
error_on_line_overflow = true
error_on_line_overflow_comments = false
tab_spaces = 4 tab_spaces = 4
newline_style = "Unix"
fn_call_style = "Block"
report_todo = "Never"
report_fixme = "Never"
reorder_extern_crates = true
reorder_extern_crates_in_group = true
reorder_imports = false
hard_tabs = false hard_tabs = false
spaces_within_parens = false newline_style = "Unix"
reorder_imports = false
write_mode = "Overwrite" write_mode = "Overwrite"
merge_derives = true
condense_wildcard_suffixes = false condense_wildcard_suffixes = false
format_strings = true format_strings = true
multiline_closure_forces_block = true normalize_comments = true
attributes_on_same_line_as_field = true
attributes_on_same_line_as_variant = true