Keyboard shortcut overview!
(shame everything else is broken...)
This commit is contained in:
parent
095dd73c52
commit
8c2ea052de
23
hammond-gtk/resources/gtk/help-overlay.ui
Normal file
23
hammond-gtk/resources/gtk/help-overlay.ui
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<object class="GtkShortcutsWindow" id="help_overlay">
|
||||||
|
<property name="modal">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkShortcutsSection">
|
||||||
|
<property name="section-name">shortcuts</property>
|
||||||
|
<property name="max-height">12</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkShortcutsGroup">
|
||||||
|
<property name="title" translatable="yes">Miscellaneous</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkShortcutsShortcut">
|
||||||
|
<property name="accelerator"><primar>q</property>
|
||||||
|
<property name="title" translatable="yes">Quit</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</interface>
|
||||||
@ -12,6 +12,7 @@
|
|||||||
<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" preprocess="xml-stripblanks">gtk/menus.ui</file>
|
||||||
|
<file compressed="true" preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
||||||
<file compressed="true">gtk/style.css</file>
|
<file compressed="true">gtk/style.css</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|||||||
@ -9,15 +9,15 @@ use gtk::SettingsExt as GtkSettingsExt;
|
|||||||
use hammond_data::Podcast;
|
use hammond_data::Podcast;
|
||||||
use hammond_data::{opml};
|
use hammond_data::{opml};
|
||||||
|
|
||||||
use appnotif::{InAppNotification, UndoState};
|
//use appnotif::{InAppNotification, UndoState};
|
||||||
use headerbar::Header;
|
use headerbar::Header;
|
||||||
use settings::{self, WindowGeometry};
|
use settings::{self, WindowGeometry};
|
||||||
use stacks::{Content, PopulatedState};
|
use stacks::{Content/*, PopulatedState*/};
|
||||||
use utils;
|
use utils;
|
||||||
use widgets::{mark_all_notif, remove_show_notif};
|
//use widgets::{mark_all_notif, remove_show_notif};
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Sender};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use rayon;
|
use rayon;
|
||||||
@ -44,12 +44,6 @@ pub enum Action {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
app_instance: gtk::Application,
|
app_instance: gtk::Application,
|
||||||
window: gtk::Window,
|
|
||||||
overlay: gtk::Overlay,
|
|
||||||
header: Rc<Header>,
|
|
||||||
content: Rc<Content>,
|
|
||||||
receiver: Receiver<Action>,
|
|
||||||
sender: Sender<Action>,
|
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,21 +60,11 @@ impl App {
|
|||||||
let cleanup_date = settings::get_cleanup_date(&settings);
|
let cleanup_date = settings::get_cleanup_date(&settings);
|
||||||
utils::cleanup(cleanup_date);
|
utils::cleanup(cleanup_date);
|
||||||
|
|
||||||
// Create the main window
|
|
||||||
let window = gtk::Window::new(gtk::WindowType::Toplevel);
|
|
||||||
window.set_title("Hammond");
|
|
||||||
|
|
||||||
let (sender, receiver) = channel();
|
|
||||||
|
|
||||||
window.connect_delete_event(clone!(application, settings, window => move |_, _| {
|
|
||||||
WindowGeometry::from_window(&window).write(&settings);
|
|
||||||
application.quit();
|
|
||||||
Inhibit(false)
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Ideally a lot more than actions would happen in startup & window
|
// Ideally a lot more than actions would happen in startup & window
|
||||||
// creation would be in activate
|
// creation would be in activate
|
||||||
application.connect_startup(clone!(window, sender => move |app| {
|
application.connect_startup(clone!(settings => move |app| {
|
||||||
|
let (sender, _receiver) = channel();
|
||||||
|
|
||||||
let refresh = SimpleAction::new("refresh", None);
|
let refresh = SimpleAction::new("refresh", None);
|
||||||
refresh.connect_activate(clone!(sender => move |_, _| {
|
refresh.connect_activate(clone!(sender => move |_, _| {
|
||||||
gtk::idle_add(clone!(sender => move || {
|
gtk::idle_add(clone!(sender => move || {
|
||||||
@ -92,17 +76,38 @@ impl App {
|
|||||||
app.add_action(&refresh);
|
app.add_action(&refresh);
|
||||||
|
|
||||||
let import = SimpleAction::new("import", None);
|
let import = SimpleAction::new("import", None);
|
||||||
import.connect_activate(clone!(window, sender => move |_, _| on_import_clicked(&window, &sender)));
|
import.connect_activate(clone!(sender, app => move |_, _| {
|
||||||
|
let window = app.get_active_window().expect("Failed to get active window");
|
||||||
|
on_import_clicked(&window, &sender);
|
||||||
|
}));
|
||||||
app.add_action(&import);
|
app.add_action(&import);
|
||||||
|
|
||||||
let about = SimpleAction::new("about", None);
|
let about = SimpleAction::new("about", None);
|
||||||
// Should investigate use of active_window here
|
about.connect_activate(clone!(app => move |_, _| {
|
||||||
about.connect_activate(clone!(window => move |_, _| about_dialog(&window)));
|
let window = app.get_active_window().expect("Failed to get active window");
|
||||||
|
about_dialog(&window);
|
||||||
|
}));
|
||||||
app.add_action(&about);
|
app.add_action(&about);
|
||||||
|
|
||||||
let quit = SimpleAction::new("quit", None);
|
let quit = SimpleAction::new("quit", None);
|
||||||
quit.connect_activate(clone!(app => move |_, _| app.quit()));
|
quit.connect_activate(clone!(app => move |_, _| app.quit()));
|
||||||
app.add_action(&quit);
|
app.add_action(&quit);
|
||||||
|
|
||||||
|
app.connect_activate(clone!(sender, settings => move |app| {
|
||||||
|
// Get the current window (if any)
|
||||||
|
if let Some(window) = app.get_active_window() {
|
||||||
|
// Already open, just raise the window
|
||||||
|
window.present();
|
||||||
|
} else {
|
||||||
|
// Time to open one!
|
||||||
|
// Create the main window
|
||||||
|
let window = gtk::ApplicationWindow::new(&app);
|
||||||
|
window.set_title("Hammond");
|
||||||
|
|
||||||
|
window.connect_delete_event(clone!(app, settings => move |window, _| {
|
||||||
|
WindowGeometry::from_window(&window).write(&settings);
|
||||||
|
app.quit();
|
||||||
|
Inhibit(false)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Create a content instance
|
// Create a content instance
|
||||||
@ -110,7 +115,7 @@ impl App {
|
|||||||
Rc::new(Content::new(sender.clone()).expect("Content Initialization failed."));
|
Rc::new(Content::new(sender.clone()).expect("Content Initialization failed."));
|
||||||
|
|
||||||
// Create the headerbar
|
// Create the headerbar
|
||||||
let header = Rc::new(Header::new(&content, &window, &sender));
|
let _header = Rc::new(Header::new(&content, &window, &sender));
|
||||||
|
|
||||||
// Add the content main stack to the overlay.
|
// Add the content main stack to the overlay.
|
||||||
let overlay = gtk::Overlay::new();
|
let overlay = gtk::Overlay::new();
|
||||||
@ -119,79 +124,16 @@ impl App {
|
|||||||
// Add the overlay to the main window
|
// Add the overlay to the main window
|
||||||
window.add(&overlay);
|
window.add(&overlay);
|
||||||
|
|
||||||
App {
|
WindowGeometry::from_settings(&settings).apply(&window);
|
||||||
app_instance: application,
|
|
||||||
window,
|
|
||||||
overlay,
|
|
||||||
header,
|
|
||||||
content,
|
|
||||||
receiver,
|
|
||||||
sender,
|
|
||||||
settings,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_timed_callbacks(&self) {
|
App::setup_timed_callbacks(&sender, &settings);
|
||||||
self.setup_dark_theme();
|
|
||||||
self.setup_refresh_on_startup();
|
|
||||||
self.setup_auto_refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_dark_theme(&self) {
|
window.show_all();
|
||||||
let settings = gtk::Settings::get_default().unwrap();
|
window.activate();
|
||||||
let enabled = self.settings.get_boolean("dark-theme");
|
|
||||||
|
|
||||||
settings.set_property_gtk_application_prefer_dark_theme(enabled);
|
let _headerbar = _header;
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_refresh_on_startup(&self) {
|
|
||||||
// Update the feeds right after the Application is initialized.
|
|
||||||
if self.settings.get_boolean("refresh-on-startup") {
|
|
||||||
let sender = self.sender.clone();
|
|
||||||
|
|
||||||
info!("Refresh on startup.");
|
|
||||||
// The ui loads async, after initialization
|
|
||||||
// so we need to delay this a bit so it won't block
|
|
||||||
// requests that will come from loading the gui on startup.
|
|
||||||
gtk::timeout_add(1500, move || {
|
|
||||||
let s: Option<Vec<_>> = None;
|
|
||||||
utils::refresh(s, sender.clone());
|
|
||||||
glib::Continue(false)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_auto_refresh(&self) {
|
|
||||||
let refresh_interval = settings::get_refresh_interval(&self.settings).num_seconds() as u32;
|
|
||||||
let sender = self.sender.clone();
|
|
||||||
|
|
||||||
info!("Auto-refresh every {:?} seconds.", refresh_interval);
|
|
||||||
|
|
||||||
gtk::timeout_add_seconds(refresh_interval, move || {
|
|
||||||
let s: Option<Vec<_>> = None;
|
|
||||||
utils::refresh(s, sender.clone());
|
|
||||||
|
|
||||||
glib::Continue(true)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(self) {
|
|
||||||
WindowGeometry::from_settings(&self.settings).apply(&self.window);
|
|
||||||
|
|
||||||
let window = self.window.clone();
|
|
||||||
self.app_instance.connect_startup(move |app| {
|
|
||||||
build_ui(&window, app);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.setup_timed_callbacks();
|
|
||||||
|
|
||||||
let content = self.content;
|
|
||||||
let headerbar = self.header;
|
|
||||||
let sender = self.sender;
|
|
||||||
let overlay = self.overlay;
|
|
||||||
let receiver = self.receiver;
|
|
||||||
gtk::timeout_add(50, move || {
|
gtk::timeout_add(50, move || {
|
||||||
match receiver.try_recv() {
|
/*match receiver.try_recv() {
|
||||||
Ok(Action::RefreshAllViews) => content.update(),
|
Ok(Action::RefreshAllViews) => content.update(),
|
||||||
Ok(Action::RefreshShowsView) => content.update_shows_view(),
|
Ok(Action::RefreshShowsView) => content.update_shows_view(),
|
||||||
Ok(Action::RefreshWidgetIfSame(id)) => content.update_widget_if_same(id),
|
Ok(Action::RefreshWidgetIfSame(id)) => content.update_widget_if_same(id),
|
||||||
@ -239,22 +181,68 @@ impl App {
|
|||||||
notif.show(&overlay);
|
notif.show(&overlay);
|
||||||
}
|
}
|
||||||
Err(_) => (),
|
Err(_) => (),
|
||||||
}
|
}*/
|
||||||
|
|
||||||
Continue(true)
|
Continue(true)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
|
App {
|
||||||
|
app_instance: application,
|
||||||
|
settings,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_timed_callbacks(sender: &Sender<Action>, settings: &Settings) {
|
||||||
|
App::setup_dark_theme(&sender, settings);
|
||||||
|
App::setup_refresh_on_startup(&sender, settings);
|
||||||
|
App::setup_auto_refresh(&sender, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_dark_theme(_sender: &Sender<Action>, settings: &Settings) {
|
||||||
|
let gtk_settings = gtk::Settings::get_default().unwrap();
|
||||||
|
let enabled = settings.get_boolean("dark-theme");
|
||||||
|
|
||||||
|
gtk_settings.set_property_gtk_application_prefer_dark_theme(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_refresh_on_startup(sender: &Sender<Action>, settings: &Settings) {
|
||||||
|
// Update the feeds right after the Application is initialized.
|
||||||
|
let sender = sender.clone();
|
||||||
|
if settings.get_boolean("refresh-on-startup") {
|
||||||
|
info!("Refresh on startup.");
|
||||||
|
// The ui loads async, after initialization
|
||||||
|
// so we need to delay this a bit so it won't block
|
||||||
|
// requests that will come from loading the gui on startup.
|
||||||
|
gtk::timeout_add(1500, move || {
|
||||||
|
let s: Option<Vec<_>> = None;
|
||||||
|
utils::refresh(s, sender.clone());
|
||||||
|
glib::Continue(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_auto_refresh(sender: &Sender<Action>, settings: &Settings) {
|
||||||
|
let refresh_interval = settings::get_refresh_interval(&settings).num_seconds() as u32;
|
||||||
|
|
||||||
|
info!("Auto-refresh every {:?} seconds.", refresh_interval);
|
||||||
|
|
||||||
|
let sender = sender.clone();
|
||||||
|
gtk::timeout_add_seconds(refresh_interval, move || {
|
||||||
|
let s: Option<Vec<_>> = None;
|
||||||
|
utils::refresh(s, sender.clone());
|
||||||
|
|
||||||
|
glib::Continue(true)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(self) {
|
||||||
ApplicationExtManual::run(&self.app_instance, &[]);
|
ApplicationExtManual::run(&self.app_instance, &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_ui(window: >k::Window, app: >k::Application) {
|
|
||||||
window.set_application(app);
|
|
||||||
window.show_all();
|
|
||||||
window.activate();
|
|
||||||
app.connect_activate(move |_| ());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Totally copied it from fractal.
|
// Totally copied it from fractal.
|
||||||
// https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912
|
// https://gitlab.gnome.org/danigm/fractal/blob/503e311e22b9d7540089d735b92af8e8f93560c5/fractal-gtk/src/app.rs#L1883-1912
|
||||||
fn about_dialog(window: >k::Window) {
|
fn about_dialog(window: >k::Window) {
|
||||||
|
|||||||
@ -58,13 +58,13 @@ impl Default for Header {
|
|||||||
|
|
||||||
// TODO: Refactor components into smaller state machines
|
// TODO: Refactor components into smaller state machines
|
||||||
impl Header {
|
impl Header {
|
||||||
pub fn new(content: &Content, window: >k::Window, sender: &Sender<Action>) -> Header {
|
pub fn new(content: &Content, window: >k::ApplicationWindow, sender: &Sender<Action>) -> Header {
|
||||||
let h = Header::default();
|
let h = Header::default();
|
||||||
h.init(content, window, &sender);
|
h.init(content, window, &sender);
|
||||||
h
|
h
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(&self, content: &Content, window: >k::Window, sender: &Sender<Action>) {
|
pub fn init(&self, content: &Content, window: >k::ApplicationWindow, 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();
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
)]
|
)]
|
||||||
#![allow(unknown_lints)]
|
#![allow(unknown_lints)]
|
||||||
#![warn(unused_extern_crates, unused)]
|
#![warn(unused_extern_crates, unused)]
|
||||||
#![deny(warnings)]
|
//#![deny(warnings)]
|
||||||
|
|
||||||
extern crate gdk;
|
extern crate gdk;
|
||||||
extern crate gdk_pixbuf;
|
extern crate gdk_pixbuf;
|
||||||
|
|||||||
@ -15,7 +15,7 @@ pub struct WindowGeometry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WindowGeometry {
|
impl WindowGeometry {
|
||||||
pub fn from_window(window: >k::Window) -> WindowGeometry {
|
pub fn from_window(window: >k::ApplicationWindow) -> WindowGeometry {
|
||||||
let position = window.get_position();
|
let position = window.get_position();
|
||||||
let size = window.get_size();
|
let size = window.get_size();
|
||||||
let left = position.0;
|
let left = position.0;
|
||||||
@ -49,7 +49,7 @@ impl WindowGeometry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply(&self, window: >k::Window) {
|
pub fn apply(&self, window: >k::ApplicationWindow) {
|
||||||
if self.width > 0 && self.height > 0 {
|
if self.width > 0 && self.height > 0 {
|
||||||
window.resize(self.width, self.height);
|
window.resize(self.width, self.height);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user