Merge branch 'show_widget_redesign' into 'master'
Show widget redesign See merge request alatiera/Hammond!6
This commit is contained in:
commit
3cbda5979d
@ -50,7 +50,7 @@ pub fn get_played_episodes() -> Result<Vec<Episode>> {
|
||||
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::*;
|
||||
|
||||
let db = connection();
|
||||
|
||||
@ -8,7 +8,7 @@ use std::io::{BufWriter, Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use errors::*;
|
||||
use hammond_data::{Episode, EpisodeWidgetQuery, Podcast};
|
||||
use hammond_data::{EpisodeWidgetQuery, Podcast};
|
||||
use hammond_data::xdg_dirs::{DL_DIR, HAMMOND_CACHE};
|
||||
|
||||
// TODO: Replace path that are of type &str with std::path.
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
@ -28,8 +27,9 @@
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Episode Title</property>
|
||||
<property name="wrap">True</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="lines">1</property>
|
||||
</object>
|
||||
@ -41,7 +41,7 @@
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
@ -135,14 +135,14 @@
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">0</property>
|
||||
@ -162,7 +162,7 @@
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
@ -237,13 +237,15 @@
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">5</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
@ -190,27 +190,6 @@
|
||||
</child>
|
||||
</object>
|
||||
</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>
|
||||
<object class="GtkMenuButton" id="menu">
|
||||
<property name="visible">True</property>
|
||||
|
||||
@ -2,191 +2,239 @@
|
||||
<!-- Generated with glade 3.20.2 -->
|
||||
<interface domain="gnome-music">
|
||||
<requires lib="gtk+" version="3.12"/>
|
||||
<object class="GtkBox" id="podcast_widget">
|
||||
<object class="GtkBox" id="container">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</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>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<object class="GtkViewport">
|
||||
<property name="visible">True</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>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="title_label">
|
||||
<property name="width_request">50</property>
|
||||
<placeholder/>
|
||||
</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="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="label" translatable="yes">Foobar</property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
<property name="max_width_chars">28</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">none</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">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>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">25</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</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>
|
||||
<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">
|
||||
<object class="GtkBox">
|
||||
<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>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<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>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</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="expand">True</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">2</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="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>
|
||||
</object>
|
||||
</child>
|
||||
@ -194,7 +242,7 @@ Warn: This will delete downloaded content associated with this Podcast.</propert
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@ -60,8 +60,8 @@ impl ShowStack {
|
||||
header: header.clone(),
|
||||
});
|
||||
|
||||
let pop = ShowsPopulated::new_initialized(show.clone(), header);
|
||||
let widget = ShowWidget::new();
|
||||
let pop = ShowsPopulated::new(show.clone(), header);
|
||||
let widget = ShowWidget::default();
|
||||
let empty = EmptyView::new();
|
||||
|
||||
show.stack.add_named(&pop.container, "podcasts");
|
||||
@ -90,7 +90,7 @@ impl ShowStack {
|
||||
let vis = self.stack.get_visible_child_name().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());
|
||||
|
||||
self.stack.remove(&old);
|
||||
@ -109,7 +109,7 @@ impl ShowStack {
|
||||
|
||||
pub fn replace_widget(&self, pd: &Podcast) {
|
||||
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.add_named(&new.container, "widget");
|
||||
|
||||
@ -12,19 +12,17 @@ use content::Content;
|
||||
#[derive(Debug)]
|
||||
pub struct Header {
|
||||
pub container: gtk::HeaderBar,
|
||||
refresh: gtk::Button,
|
||||
add_toggle: gtk::MenuButton,
|
||||
switch: gtk::StackSwitcher,
|
||||
back_button: gtk::Button,
|
||||
show_title: gtk::Label,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new() -> Rc<Header> {
|
||||
impl Default for Header {
|
||||
fn default() -> Header {
|
||||
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/headerbar.ui");
|
||||
|
||||
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 switch: gtk::StackSwitcher = builder.get_object("switch").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.show();
|
||||
|
||||
Rc::new(Header {
|
||||
Header {
|
||||
container: header,
|
||||
refresh,
|
||||
add_toggle,
|
||||
switch,
|
||||
back_button,
|
||||
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>) {
|
||||
@ -64,11 +70,6 @@ impl Header {
|
||||
}));
|
||||
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 add_toggle = &self.add_toggle;
|
||||
let show_title = &self.show_title;
|
||||
@ -87,7 +88,7 @@ impl Header {
|
||||
self.switch.hide();
|
||||
self.add_toggle.hide();
|
||||
self.back_button.show();
|
||||
self.show_title.set_text(title);
|
||||
self.set_show_title(title);
|
||||
self.show_title.show();
|
||||
}
|
||||
|
||||
@ -98,9 +99,9 @@ impl Header {
|
||||
self.show_title.hide();
|
||||
}
|
||||
|
||||
// pub fn set_show_title(&self, title: &str) {
|
||||
// self.show_title.set_text(title)
|
||||
// }
|
||||
pub fn set_show_title(&self, title: &str) {
|
||||
self.show_title.set_text(title)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_add_bttn_clicked(content: Rc<Content>, entry: >k::Entry) {
|
||||
@ -111,7 +112,7 @@ fn on_add_bttn_clicked(content: Rc<Content>, entry: >k::Entry) {
|
||||
if let Ok(s) = source {
|
||||
info!("{:?} feed added", url);
|
||||
// update the db
|
||||
utils::refresh_feed(content, Some(vec![s]), None);
|
||||
utils::refresh_feed(content, Some(vec![s]));
|
||||
} else {
|
||||
error!("Feed probably already exists.");
|
||||
error!("Error: {:?}", source.unwrap_err());
|
||||
|
||||
@ -24,6 +24,7 @@ use hammond_data::utils::checkup;
|
||||
|
||||
use gtk::prelude::*;
|
||||
use gio::{ActionMapExt, ApplicationExt, MenuExt, SimpleActionExt};
|
||||
use std::rc::Rc;
|
||||
|
||||
// http://gtk-rs.org/tuto/closures
|
||||
#[macro_export]
|
||||
@ -52,28 +53,19 @@ mod content;
|
||||
mod utils;
|
||||
mod static_resource;
|
||||
|
||||
/*
|
||||
THIS IS STILL A PROTOTYPE.
|
||||
*/
|
||||
|
||||
fn build_ui(app: >k::Application) {
|
||||
let menu = gio::Menu::new();
|
||||
menu.append("Quit", "app.quit");
|
||||
menu.append("Checkup", "app.check");
|
||||
menu.append("Update feeds", "app.update");
|
||||
app.set_app_menu(&menu);
|
||||
|
||||
// Get the main window
|
||||
let window = gtk::ApplicationWindow::new(app);
|
||||
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
|
||||
let header = headerbar::Header::new();
|
||||
let header = Rc::new(headerbar::Header::default());
|
||||
let ct = content::Content::new(header.clone());
|
||||
header.init(ct.clone());
|
||||
window.set_titlebar(&header.container);
|
||||
@ -99,11 +91,32 @@ fn build_ui(app: >k::Application) {
|
||||
});
|
||||
app.add_action(&check);
|
||||
|
||||
// queue a db update 1 minute after the startup.
|
||||
gtk::idle_add(clone!(ct => move || {
|
||||
utils::refresh_feed(ct.clone(), None, Some(60));
|
||||
let update = gio::SimpleAction::new("update", None);
|
||||
let ct_clone = ct.clone();
|
||||
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)
|
||||
}));
|
||||
}),
|
||||
);
|
||||
|
||||
// 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 || {
|
||||
let _ = checkup();
|
||||
|
||||
@ -5,14 +5,13 @@ use hammond_data::feed;
|
||||
use hammond_data::{Podcast, Source};
|
||||
use hammond_downloader::downloader;
|
||||
|
||||
use std::{thread, time};
|
||||
use std::thread;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
use std::rc::Rc;
|
||||
|
||||
use content::Content;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
type Foo = RefCell<Option<(Rc<Content>, Receiver<bool>)>>;
|
||||
|
||||
// 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.
|
||||
/// `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.
|
||||
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.
|
||||
let (sender, receiver) = channel();
|
||||
|
||||
@ -32,11 +31,6 @@ pub fn refresh_feed(content: Rc<Content>, source: Option<Vec<Source>>, delay: Op
|
||||
}));
|
||||
|
||||
thread::spawn(move || {
|
||||
if let Some(s) = delay {
|
||||
let t = time::Duration::from_secs(s);
|
||||
thread::sleep(t);
|
||||
}
|
||||
|
||||
let feeds = {
|
||||
if let Some(vec) = source {
|
||||
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()
|
||||
}
|
||||
|
||||
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)]
|
||||
mod tests {
|
||||
use hammond_data::Source;
|
||||
|
||||
@ -5,11 +5,17 @@ pub struct EmptyView {
|
||||
pub container: gtk::Box,
|
||||
}
|
||||
|
||||
impl EmptyView {
|
||||
pub fn new() -> EmptyView {
|
||||
impl Default for EmptyView {
|
||||
fn default() -> Self {
|
||||
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/empty_view.ui");
|
||||
let view: gtk::Box = builder.get_object("empty_view").unwrap();
|
||||
|
||||
EmptyView { container: view }
|
||||
}
|
||||
}
|
||||
|
||||
impl EmptyView {
|
||||
pub fn new() -> EmptyView {
|
||||
EmptyView::default()
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,18 +19,8 @@ pub struct ShowsPopulated {
|
||||
viewport: gtk::Viewport,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ShowsChild {
|
||||
container: gtk::Box,
|
||||
title: gtk::Label,
|
||||
cover: gtk::Image,
|
||||
banner: gtk::Image,
|
||||
number: gtk::Label,
|
||||
child: gtk::FlowBoxChild,
|
||||
}
|
||||
|
||||
impl ShowsPopulated {
|
||||
pub fn new() -> ShowsPopulated {
|
||||
impl Default for ShowsPopulated {
|
||||
fn default() -> Self {
|
||||
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 flowbox: gtk::FlowBox = builder.get_object("flowbox").unwrap();
|
||||
@ -42,10 +32,11 @@ impl ShowsPopulated {
|
||||
viewport,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn new_initialized(show: Rc<ShowStack>, header: Rc<Header>) -> ShowsPopulated {
|
||||
let pop = ShowsPopulated::new();
|
||||
impl ShowsPopulated {
|
||||
pub fn new(show: Rc<ShowStack>, header: Rc<Header>) -> ShowsPopulated {
|
||||
let pop = ShowsPopulated::default();
|
||||
pop.init(show, header);
|
||||
pop
|
||||
}
|
||||
@ -74,7 +65,7 @@ impl ShowsPopulated {
|
||||
|
||||
if let Ok(pds) = podcasts {
|
||||
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.show_all();
|
||||
@ -86,11 +77,20 @@ impl ShowsPopulated {
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowsChild {
|
||||
fn new() -> ShowsChild {
|
||||
#[derive(Debug)]
|
||||
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");
|
||||
|
||||
// Copy of gnome-music AlbumWidget
|
||||
let container: gtk::Box = builder.get_object("fb_child").unwrap();
|
||||
let title: gtk::Label = builder.get_object("pd_title").unwrap();
|
||||
let cover: gtk::Image = builder.get_object("pd_cover").unwrap();
|
||||
@ -109,6 +109,15 @@ impl ShowsChild {
|
||||
child,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ShowsChild {
|
||||
pub fn new(pd: &Podcast) -> ShowsChild {
|
||||
let child = ShowsChild::default();
|
||||
child.init(pd);
|
||||
|
||||
child
|
||||
}
|
||||
|
||||
fn init(&self, pd: &Podcast) {
|
||||
self.title.set_text(pd.title());
|
||||
@ -122,13 +131,6 @@ impl ShowsChild {
|
||||
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) {
|
||||
let bann =
|
||||
Pixbuf::new_from_resource_at_scale("/org/gnome/hammond/banner.png", 256, 256, true);
|
||||
|
||||
@ -47,8 +47,8 @@ struct EpisodeWidget {
|
||||
progress_label: gtk::Label,
|
||||
}
|
||||
|
||||
impl EpisodeWidget {
|
||||
fn new() -> EpisodeWidget {
|
||||
impl Default for EpisodeWidget {
|
||||
fn default() -> Self {
|
||||
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episode_widget.ui");
|
||||
|
||||
let container: gtk::Box = builder.get_object("episode_container").unwrap();
|
||||
@ -95,9 +95,11 @@ impl EpisodeWidget {
|
||||
progress_label,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_initialized(episode: &mut EpisodeWidgetQuery, pd: &Podcast) -> EpisodeWidget {
|
||||
let widget = EpisodeWidget::new();
|
||||
impl EpisodeWidget {
|
||||
pub fn new(episode: &mut EpisodeWidgetQuery, pd: &Podcast) -> EpisodeWidget {
|
||||
let widget = EpisodeWidget::default();
|
||||
widget.init(episode, pd);
|
||||
widget
|
||||
}
|
||||
@ -109,6 +111,12 @@ impl EpisodeWidget {
|
||||
self.title.set_xalign(0.0);
|
||||
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();
|
||||
timeout_add(200, move || {
|
||||
progress.pulse();
|
||||
@ -133,10 +141,16 @@ impl EpisodeWidget {
|
||||
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();
|
||||
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;
|
||||
@ -234,7 +248,7 @@ fn on_play_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);
|
||||
if let Err(err) = e {
|
||||
@ -269,11 +283,18 @@ fn receive() -> glib::Continue {
|
||||
pub fn episodes_listbox(pd: &Podcast) -> Result<gtk::ListBox> {
|
||||
let episodes = dbqueries::get_pd_episodeswidgets(pd)?;
|
||||
|
||||
// TODO: add a separator
|
||||
let list = gtk::ListBox::new();
|
||||
|
||||
episodes.into_iter().for_each(|mut ep| {
|
||||
let widget = EpisodeWidget::new_initialized(&mut ep, pd);
|
||||
list.add(&widget.container)
|
||||
let widget = EpisodeWidget::new(&mut ep, pd);
|
||||
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);
|
||||
|
||||
@ -1,57 +1,64 @@
|
||||
use gtk::prelude::*;
|
||||
use gtk;
|
||||
use diesel::Identifiable;
|
||||
|
||||
use std::fs;
|
||||
use open;
|
||||
use dissolve;
|
||||
|
||||
use hammond_data::dbqueries;
|
||||
use hammond_data::Podcast;
|
||||
use hammond_data::utils::replace_extra_spaces;
|
||||
use hammond_downloader::downloader;
|
||||
|
||||
use widgets::episode::episodes_listbox;
|
||||
use utils::get_pixbuf_from_path;
|
||||
use utils::get_pixbuf_from_path_128;
|
||||
use content::ShowStack;
|
||||
use headerbar::Header;
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::fs;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShowWidget {
|
||||
pub container: gtk::Box,
|
||||
cover: gtk::Image,
|
||||
title: gtk::Label,
|
||||
description: gtk::TextView,
|
||||
view: gtk::Viewport,
|
||||
description: gtk::Label,
|
||||
link: gtk::Button,
|
||||
settings: gtk::MenuButton,
|
||||
unsub: gtk::Button,
|
||||
played: gtk::Button,
|
||||
episodes: gtk::Frame,
|
||||
}
|
||||
|
||||
impl ShowWidget {
|
||||
pub fn new() -> ShowWidget {
|
||||
// Adapted from gnome-music AlbumWidget
|
||||
impl Default for ShowWidget {
|
||||
fn default() -> Self {
|
||||
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 title: gtk::Label = builder.get_object("title_label").unwrap();
|
||||
let description: gtk::TextView = builder.get_object("desc_text_view").unwrap();
|
||||
let view: gtk::Viewport = builder.get_object("view").unwrap();
|
||||
let description: gtk::Label = builder.get_object("description").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 {
|
||||
container,
|
||||
cover,
|
||||
title,
|
||||
description,
|
||||
view,
|
||||
unsub,
|
||||
played,
|
||||
link,
|
||||
settings,
|
||||
episodes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_initialized(shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) -> ShowWidget {
|
||||
let pdw = ShowWidget::new();
|
||||
impl ShowWidget {
|
||||
pub fn new(shows: Rc<ShowStack>, header: Rc<Header>, pd: &Podcast) -> ShowWidget {
|
||||
let pdw = ShowWidget::default();
|
||||
pdw.init(shows, header, pd);
|
||||
pdw
|
||||
}
|
||||
@ -65,37 +72,29 @@ impl ShowWidget {
|
||||
header.switch_to_normal();
|
||||
}));
|
||||
|
||||
self.title.set_text(pd.title());
|
||||
let listbox = episodes_listbox(pd);
|
||||
if let Ok(l) = listbox {
|
||||
self.view.add(&l);
|
||||
self.episodes.add(&l);
|
||||
}
|
||||
|
||||
{
|
||||
let buff = self.description.get_buffer().unwrap();
|
||||
buff.set_text(pd.description());
|
||||
}
|
||||
// TODO: Temporary solution until we render html urls/bold/italic probably with markup.
|
||||
let desc = dissolve::strip_html_tags(pd.description()).join(" ");
|
||||
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 {
|
||||
self.cover.set_from_pixbuf(&i);
|
||||
}
|
||||
|
||||
self.played.connect_clicked(clone!(shows, pd => move |_| {
|
||||
on_played_button_clicked(shows.clone(), &pd);
|
||||
}));
|
||||
let link = pd.link().to_owned();
|
||||
self.link.connect_clicked(move |_| {
|
||||
info!("Opening link: {}", &link);
|
||||
let _ = open::that(&link);
|
||||
});
|
||||
|
||||
self.show_played_button(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()
|
||||
}
|
||||
}
|
||||
// self.played.connect_clicked(clone!(shows, pd => move |_| {
|
||||
// on_played_button_clicked(shows.clone(), &pd);
|
||||
// }));
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,6 +118,7 @@ fn on_unsub_button_clicked(shows: Rc<ShowStack>, pd: &Podcast, unsub_button: >
|
||||
shows.update_podcasts();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn on_played_button_clicked(shows: Rc<ShowStack>, pd: &Podcast) {
|
||||
let _ = dbqueries::update_none_to_played_now(pd);
|
||||
|
||||
|
||||
19
rustfmt.toml
19
rustfmt.toml
@ -1,26 +1,13 @@
|
||||
unstable_features = true
|
||||
verbose = false
|
||||
disable_all_formatting = false
|
||||
skip_children = false
|
||||
max_width = 100
|
||||
comment_width = 100
|
||||
wrap_comments = true
|
||||
error_on_line_overflow = true
|
||||
error_on_line_overflow_comments = false
|
||||
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
|
||||
spaces_within_parens = false
|
||||
newline_style = "Unix"
|
||||
reorder_imports = false
|
||||
write_mode = "Overwrite"
|
||||
merge_derives = true
|
||||
condense_wildcard_suffixes = false
|
||||
format_strings = true
|
||||
multiline_closure_forces_block = true
|
||||
attributes_on_same_line_as_field = true
|
||||
attributes_on_same_line_as_variant = true
|
||||
normalize_comments = true
|
||||
Loading…
Reference in New Issue
Block a user