Define an app-menu with About & Quit actions

Rename some paths for auto resource magic
This commit is contained in:
Zander Brown 2018-05-19 20:38:36 +01:00
parent c4e6fcc451
commit c6ce888cc7
12 changed files with 99 additions and 59 deletions

View File

@ -364,6 +364,7 @@ Tobias Bernard
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="text" translatable="yes">About</property> <property name="text" translatable="yes">About</property>
<property name="action-name">app.about</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

View File

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<menu id="app-menu">
<section>
<item>
<attribute name="label" translatable="yes">_Preferences</attribute>
<attribute name="action">app.preferences</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
<attribute name="action">win.show-help-overlay</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Help</attribute>
<attribute name="action">app.help</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_About</attribute>
<attribute name="action">app.about</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Quit</attribute>
<attribute name="action">app.quit</attribute>
</item>
</section>
</menu>
</interface>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<gresources> <gresources>
<gresource prefix="/org/gnome/hammond/"> <gresource prefix="/org/gnome/Hammond/">
<file compressed="true" preprocess="xml-stripblanks">gtk/episode_widget.ui</file> <file compressed="true" preprocess="xml-stripblanks">gtk/episode_widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/show_widget.ui</file> <file compressed="true" preprocess="xml-stripblanks">gtk/show_widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/empty_view.ui</file> <file compressed="true" preprocess="xml-stripblanks">gtk/empty_view.ui</file>
@ -11,6 +11,7 @@
<file compressed="true" preprocess="xml-stripblanks">gtk/shows_child.ui</file> <file compressed="true" preprocess="xml-stripblanks">gtk/shows_child.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/headerbar.ui</file> <file compressed="true" preprocess="xml-stripblanks">gtk/headerbar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/inapp_notif.ui</file> <file compressed="true" preprocess="xml-stripblanks">gtk/inapp_notif.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/menus.ui</file>
<file compressed="true">gtk/style.css</file> <file compressed="true">gtk/style.css</file>
</gresource> </gresource>
</gresources> </gresources>

View File

@ -1,6 +1,6 @@
#![allow(new_without_default)] #![allow(new_without_default)]
use gio::{ApplicationExt, ApplicationExtManual, ApplicationFlags, Settings, SettingsExt}; use gio::{ApplicationExt, ApplicationExtManual, ApplicationFlags, Settings, SettingsExt, SimpleAction, SimpleActionExt, ActionMapExt};
use glib; use glib;
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
@ -67,6 +67,19 @@ impl App {
let window = gtk::Window::new(gtk::WindowType::Toplevel); let window = gtk::Window::new(gtk::WindowType::Toplevel);
window.set_title("Hammond"); window.set_title("Hammond");
// Ideally a lot more than actions would happen in startup & window
// creation would be in activate
application.connect_startup(clone!(window => move |app| {
let about = SimpleAction::new("about", None);
// Should investigate use of active_window here
about.connect_activate(clone!(window => move |_, _| about_dialog(&window)));
app.add_action(&about);
let quit = SimpleAction::new("quit", None);
quit.connect_activate(clone!(app => move |_, _| app.quit()));
app.add_action(&quit);
}));
window.connect_delete_event(clone!(application, settings, window => move |_, _| { window.connect_delete_event(clone!(application, settings, window => move |_, _| {
WindowGeometry::from_window(&window).write(&settings); WindowGeometry::from_window(&window).write(&settings);
application.quit(); application.quit();
@ -224,3 +237,40 @@ fn build_ui(window: &gtk::Window, app: &gtk::Application) {
window.activate(); window.activate();
app.connect_activate(move |_| ()); app.connect_activate(move |_| ());
} }
// Totally copied it from fractal.
// https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912
fn about_dialog(window: &gtk::Window) {
// Feel free to add yourself if you contribured.
let authors = &[
"Constantin Nickel",
"Gabriele Musco",
"James Wykeham-Martin",
"Jordan Petridis",
"Julian Sparber",
"Rowan Lewis",
"Zander Brown"
];
let dialog = gtk::AboutDialog::new();
// Waiting for a logo.
// dialog.set_logo_icon_name("org.gnome.Hammond");
dialog.set_logo_icon_name("multimedia-player");
dialog.set_comments("Podcast Client for the GNOME Desktop.");
dialog.set_copyright("© 2017, 2018 Jordan Petridis");
dialog.set_license_type(gtk::License::Gpl30);
dialog.set_modal(true);
// TODO: make it show it fetches the commit hash from which it was built
// and the version number is kept in sync automaticly
dialog.set_version("0.3.3");
dialog.set_program_name("Hammond");
// TODO: Need a wiki page first.
// dialog.set_website("https://wiki.gnome.org/Design/Apps/Potential/Podcasts");
// dialog.set_website_label("Learn more about Hammond");
dialog.set_transient_for(window);
dialog.set_artists(&["Tobias Bernard"]);
dialog.set_authors(authors);
dialog.show();
}

View File

@ -21,7 +21,7 @@ pub struct InAppNotification {
impl Default for InAppNotification { impl Default for InAppNotification {
fn default() -> Self { fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/inapp_notif.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/inapp_notif.ui");
let revealer: gtk::Revealer = builder.get_object("revealer").unwrap(); let revealer: gtk::Revealer = builder.get_object("revealer").unwrap();
let text: gtk::Label = builder.get_object("text").unwrap(); let text: gtk::Label = builder.get_object("text").unwrap();

View File

@ -23,7 +23,6 @@ pub struct Header {
switch: gtk::StackSwitcher, switch: gtk::StackSwitcher,
back: gtk::Button, back: gtk::Button,
show_title: gtk::Label, show_title: gtk::Label,
about: gtk::ModelButton,
import: gtk::ModelButton, import: gtk::ModelButton,
export: gtk::ModelButton, export: gtk::ModelButton,
update_button: gtk::ModelButton, update_button: gtk::ModelButton,
@ -34,7 +33,7 @@ pub struct Header {
impl Default for Header { impl Default for Header {
fn default() -> 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 = builder.get_object("headerbar").unwrap(); let header = builder.get_object("headerbar").unwrap();
let add_toggle = builder.get_object("add_toggle").unwrap(); let add_toggle = builder.get_object("add_toggle").unwrap();
@ -47,7 +46,6 @@ impl Default for Header {
let update_box = builder.get_object("update_notification").unwrap(); let update_box = builder.get_object("update_notification").unwrap();
let update_label = builder.get_object("update_label").unwrap(); let update_label = builder.get_object("update_label").unwrap();
let update_spinner = builder.get_object("update_spinner").unwrap(); let update_spinner = builder.get_object("update_spinner").unwrap();
let about = builder.get_object("about").unwrap();
Header { Header {
container: header, container: header,
@ -55,7 +53,6 @@ impl Default for Header {
switch, switch,
back, back,
show_title, show_title,
about,
import, import,
export, export,
update_button, update_button,
@ -75,7 +72,7 @@ impl Header {
} }
pub fn init(&self, content: &Content, window: &gtk::Window, sender: &Sender<Action>) { pub fn init(&self, content: &Content, window: &gtk::Window, sender: &Sender<Action>) {
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 add_popover: gtk::Popover = builder.get_object("add_popover").unwrap(); let add_popover: gtk::Popover = builder.get_object("add_popover").unwrap();
let new_url: gtk::Entry = builder.get_object("new_url").unwrap(); let new_url: gtk::Entry = builder.get_object("new_url").unwrap();
@ -107,9 +104,6 @@ impl Header {
})); }));
})); }));
self.about
.connect_clicked(clone!(window => move |_| about_dialog(&window)));
self.import.connect_clicked( self.import.connect_clicked(
clone!(window, sender => move |_| on_import_clicked(&window, &sender)), clone!(window, sender => move |_| on_import_clicked(&window, &sender)),
); );
@ -288,39 +282,3 @@ fn on_import_clicked(window: &gtk::Window, sender: &Sender<Action>) {
dialog.run(); dialog.run();
} }
// Totally copied it from fractal.
// https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912
fn about_dialog(window: &gtk::Window) {
// Feel free to add yourself if you contribured.
let authors = &[
"Constantin Nickel",
"Gabriele Musco",
"James Wykeham-Martin",
"Jordan Petridis",
"Julian Sparber",
"Rowan Lewis",
];
let dialog = gtk::AboutDialog::new();
// Waiting for a logo.
// dialog.set_logo_icon_name("org.gnome.Hammond");
dialog.set_logo_icon_name("multimedia-player");
dialog.set_comments("Podcast Client for the GNOME Desktop.");
dialog.set_copyright("© 2017, 2018 Jordan Petridis");
dialog.set_license_type(gtk::License::Gpl30);
dialog.set_modal(true);
// TODO: make it show it fetches the commit hash from which it was built
// and the version number is kept in sync automaticly
dialog.set_version("0.3.3");
dialog.set_program_name("Hammond");
// TODO: Need a wiki page first.
// dialog.set_website("https://wiki.gnome.org/Design/Apps/Potential/Podcasts");
// dialog.set_website_label("Learn more about Hammond");
dialog.set_transient_for(window);
dialog.set_artists(&["Tobias Bernard"]);
dialog.set_authors(authors);
dialog.show();
}

View File

@ -86,7 +86,7 @@ fn main() {
// Add custom style // Add custom style
let provider = gtk::CssProvider::new(); let provider = gtk::CssProvider::new();
gtk::CssProvider::load_from_resource(&provider, "/org/gnome/hammond/gtk/style.css"); gtk::CssProvider::load_from_resource(&provider, "/org/gnome/Hammond/gtk/style.css");
gtk::StyleContext::add_provider_for_screen( gtk::StyleContext::add_provider_for_screen(
&gdk::Screen::get_default().expect("Error initializing gtk css provider."), &gdk::Screen::get_default().expect("Error initializing gtk css provider."),
&provider, &provider,

View File

@ -7,7 +7,7 @@ pub struct EmptyView {
impl Default for EmptyView { impl Default for EmptyView {
fn default() -> Self { 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 }

View File

@ -33,7 +33,7 @@ pub struct EpisodeWidget {
impl Default for EpisodeWidget { impl Default for EpisodeWidget {
fn default() -> Self { 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();
let progress: gtk::ProgressBar = builder.get_object("progress_bar").unwrap(); let progress: gtk::ProgressBar = builder.get_object("progress_bar").unwrap();

View File

@ -49,7 +49,7 @@ pub struct HomeView {
impl Default for HomeView { impl Default for HomeView {
fn default() -> Self { fn default() -> Self {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episodes_view.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/episodes_view.ui");
let container: gtk::Box = builder.get_object("container").unwrap(); let container: gtk::Box = builder.get_object("container").unwrap();
let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap(); let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap();
let frame_parent: gtk::Box = builder.get_object("frame_parent").unwrap(); let frame_parent: gtk::Box = builder.get_object("frame_parent").unwrap();
@ -180,7 +180,7 @@ struct HomeEpisode {
impl Default for HomeEpisode { impl Default for HomeEpisode {
fn default() -> Self { fn default() -> Self {
let builder = let builder =
gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episodes_view_widget.ui"); gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/episodes_view_widget.ui");
let container: gtk::Box = builder.get_object("container").unwrap(); let container: gtk::Box = builder.get_object("container").unwrap();
let image: gtk::Image = builder.get_object("cover").unwrap(); let image: gtk::Image = builder.get_object("cover").unwrap();
let ep = EpisodeWidget::default(); let ep = EpisodeWidget::default();
@ -197,7 +197,7 @@ impl Default for HomeEpisode {
impl HomeEpisode { impl HomeEpisode {
fn new(episode: EpisodeWidgetQuery, sender: &Sender<Action>) -> HomeEpisode { fn new(episode: EpisodeWidgetQuery, sender: &Sender<Action>) -> HomeEpisode {
let builder = let builder =
gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/episodes_view_widget.ui"); gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/episodes_view_widget.ui");
let container: gtk::Box = builder.get_object("container").unwrap(); let container: gtk::Box = builder.get_object("container").unwrap();
let image: gtk::Image = builder.get_object("cover").unwrap(); let image: gtk::Image = builder.get_object("cover").unwrap();
let pid = episode.podcast_id(); let pid = episode.podcast_id();
@ -224,4 +224,4 @@ impl HomeEpisode {
fn set_cover(&self, podcast_id: i32) -> Result<(), Error> { fn set_cover(&self, podcast_id: i32) -> Result<(), Error> {
utils::set_image_from_path(&self.image, podcast_id, 64) utils::set_image_from_path(&self.image, podcast_id, 64)
} }
} }

View File

@ -41,7 +41,7 @@ pub struct ShowWidget {
impl Default for ShowWidget { impl Default for ShowWidget {
fn default() -> Self { fn default() -> Self {
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("container").unwrap(); let container: gtk::Box = builder.get_object("container").unwrap();
let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap(); let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap();
let episodes = builder.get_object("episodes").unwrap(); let episodes = builder.get_object("episodes").unwrap();
@ -79,7 +79,7 @@ impl ShowWidget {
} }
pub fn init(&mut self, pd: &Arc<Podcast>, sender: &Sender<Action>) { pub fn init(&mut self, pd: &Arc<Podcast>, sender: &Sender<Action>) {
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");
self.unsub self.unsub
.connect_clicked(clone!(pd, sender => move |bttn| { .connect_clicked(clone!(pd, sender => move |bttn| {
@ -189,7 +189,7 @@ fn populate_listbox(
})); }));
if count == 0 { if count == 0 {
let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/empty_show.ui"); let builder = gtk::Builder::new_from_resource("/org/gnome/Hammond/gtk/empty_show.ui");
let container: gtk::Box = builder.get_object("empty_show").unwrap(); let container: gtk::Box = builder.get_object("empty_show").unwrap();
show.episodes.add(&container); show.episodes.add(&container);
return Ok(()); return Ok(());

View File

@ -28,7 +28,7 @@ pub struct ShowsView {
impl Default for ShowsView { impl Default for ShowsView {
fn default() -> Self { fn default() -> Self {
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 scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap(); let scrolled_window: gtk::ScrolledWindow = builder.get_object("scrolled_window").unwrap();
let flowbox: gtk::FlowBox = builder.get_object("flowbox").unwrap(); let flowbox: gtk::FlowBox = builder.get_object("flowbox").unwrap();
@ -129,7 +129,7 @@ struct ShowsChild {
impl Default for ShowsChild { impl Default for ShowsChild {
fn default() -> Self { 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");
let container: gtk::Box = builder.get_object("fb_child").unwrap(); let container: gtk::Box = builder.get_object("fb_child").unwrap();
let cover: gtk::Image = builder.get_object("pd_cover").unwrap(); let cover: gtk::Image = builder.get_object("pd_cover").unwrap();