diff --git a/resources/de.swsnr.turnon.metainfo.xml.in b/resources/de.swsnr.turnon.metainfo.xml.in index 3835ee3..ea02097 100644 --- a/resources/de.swsnr.turnon.metainfo.xml.in +++ b/resources/de.swsnr.turnon.metainfo.xml.in @@ -34,6 +34,7 @@

Save and restore main window state.

+

Define additional shortcuts.

Update translations.

diff --git a/resources/gtk/help-overlay.blp b/resources/gtk/help-overlay.blp index 0f0f063..313e9a5 100644 --- a/resources/gtk/help-overlay.blp +++ b/resources/gtk/help-overlay.blp @@ -18,13 +18,64 @@ Gtk.ShortcutsWindow help_overlay { section-name: "shortcuts"; Gtk.ShortcutsGroup { - title: C_("shortcuts group", "General"); + title: C_("shortcuts group", "Devices"); Gtk.ShortcutsShortcut { title: C_("shortcut description", "Add a new device"); - action-name: "win.add-device"; + accelerator: "N"; + } + + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Toggle network scanning"); + accelerator: "F5"; + } + } + + Gtk.ShortcutsGroup { + title: C_("shortcuts group", "Single device"); + + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Turn on device"); + accelerator: "Return"; } + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Edit device"); + accelerator: "Return"; + } + + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Ask to delete device"); + accelerator: "Delete"; + } + + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Immediately delete device without confirmation"); + accelerator: "Delete"; + } + } + + Gtk.ShortcutsGroup { + title: C_("shortcuts group", "Discovered device"); + + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Add as a new device"); + accelerator: "N"; + } + } + + Gtk.ShortcutsGroup { + title: C_("shortcuts group", "Edit device"); + + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Save device"); + accelerator: "S"; + } + } + + Gtk.ShortcutsGroup { + title: C_("shortcuts group", "General"); + Gtk.ShortcutsShortcut { title: C_("shortcut description", "Show shortcuts"); action-name: "win.show-help-overlay"; diff --git a/resources/gtk/help-overlay.ui b/resources/gtk/help-overlay.ui index c2c19c2..58bd643 100644 --- a/resources/gtk/help-overlay.ui +++ b/resources/gtk/help-overlay.ui @@ -13,13 +13,75 @@ corresponding .blp file and regenerate this file with blueprint-compiler. shortcuts - General + Devices Add a new device - win.add-device + <Ctrl>N + + + + + Toggle network scanning + F5 + + + + + + + Single device + + + Turn on device + Return + + + + + Edit device + <Alt>Return + + + + + Ask to delete device + Delete + + + + + Immediately delete device without confirmation + <Ctrl>Delete + + + + + + + Discovered device + + + Add as a new device + <Ctrl>N + + + + + Edit device + + + Save device + <Ctrl>S + + + + + + + General Show shortcuts diff --git a/resources/ui/device-row.blp b/resources/ui/device-row.blp index d342c5d..048a162 100644 --- a/resources/ui/device-row.blp +++ b/resources/ui/device-row.blp @@ -64,37 +64,37 @@ template $DeviceRow: Adw.ActionRow { child: Gtk.Box { orientation: horizontal; - Gtk.Button { + Gtk.Button add { icon-name: "list-add-symbolic"; tooltip-text: C_("device-row.action.row.add.tooltip", "Add this device"); action-name: "row.add"; valign: center; - visible: bind template.can-add; + visible: bind add.sensitive; styles [ "flat" ] } - Gtk.Button { + Gtk.Button edit { icon-name: "document-edit-symbolic"; tooltip-text: C_("device-row.action.row.edit.tooltip", "Edit this device"); action-name: "row.edit"; valign: center; - visible: bind template.can-edit; + visible: bind edit.sensitive; styles [ "flat" ] } - Gtk.Button { + Gtk.Button delete { icon-name: "user-trash-symbolic"; tooltip-text: C_("device-row.action.row.ask-delete.tooltip", "Delete this device?"); - action-name: "row.ask_delete"; + action-name: "row.ask-delete"; margin-start: 6; valign: center; - visible: bind template.can-delete; + visible: bind delete.sensitive; styles [ "flat" diff --git a/resources/ui/device-row.ui b/resources/ui/device-row.ui index fb64cb6..09d1453 100644 --- a/resources/ui/device-row.ui +++ b/resources/ui/device-row.ui @@ -90,37 +90,37 @@ corresponding .blp file and regenerate this file with blueprint-compiler. 0 - + list-add-symbolic Add this device row.add 3 - + - + document-edit-symbolic Edit this device row.edit 3 - + - + user-trash-symbolic Delete this device? - row.ask_delete + row.ask-delete 6 3 - + diff --git a/src/app.rs b/src/app.rs index 31e52ef..197649b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -107,7 +107,6 @@ impl TurnOnApplication { ]; self.add_action_entries(actions); - self.set_accels_for_action("win.add-device", &["n"]); self.set_accels_for_action("window.close", &["w"]); self.set_accels_for_action("app.quit", &["q"]); } @@ -339,7 +338,7 @@ mod imp { fn activate(&self) { glib::debug!("Activating application"); self.parent_activate(); - let app: &super::TurnOnApplication = &self.obj(); + let app = &*self.obj(); match app.active_window() { Some(window) => { glib::debug!("Representing existing application window"); @@ -352,6 +351,15 @@ mod imp { window.add_css_class("devel"); } window.bind_model(&self.devices); + window.connect_scan_network_notify(glib::clone!( + #[strong] + app, + move |window| { + app.devices() + .discovered_devices() + .set_discovery_enabled(window.scan_network()); + } + )); window.present(); } } diff --git a/src/app/widgets/application_window.rs b/src/app/widgets/application_window.rs index 945511e..3009044 100644 --- a/src/app/widgets/application_window.rs +++ b/src/app/widgets/application_window.rs @@ -7,15 +7,11 @@ use adw::prelude::*; use adw::subclass::prelude::*; use glib::object::IsA; -use gtk::gio::ActionEntry; -use gtk::gio::{self, PropertyAction}; +use gtk::gio; use gtk::glib; use crate::app::model::Devices; use crate::app::TurnOnApplication; -use crate::config::G_LOG_DOMAIN; - -use super::EditDeviceDialog; glib::wrapper! { pub struct TurnOnApplicationWindow(ObjectSubclass) @@ -42,39 +38,12 @@ impl TurnOnApplicationWindow { } pub fn bind_model(&self, devices: &Devices) { - self.setup_actions(); self.imp().bind_model(devices); } - - fn setup_actions(&self) { - let add_device = ActionEntry::builder("add-device") - .activate(move |window: &TurnOnApplicationWindow, _, _| { - let dialog = EditDeviceDialog::new(); - dialog.connect_saved(glib::clone!( - #[weak(rename_to = devices)] - window.application().devices(), - move |_, device| { - glib::debug!("Adding new device: {:?}", device.imp()); - devices.registered_devices().append(device); - } - )); - dialog.present(Some(window)); - }) - .build(); - - self.add_action_entries([add_device]); - - let scan_network = PropertyAction::new( - "toggle-scan-network", - &self.application().devices().discovered_devices(), - "discovery-enabled", - ); - self.add_action(&scan_network); - } } mod imp { - use std::cell::RefCell; + use std::cell::{Cell, RefCell}; use std::rc::Rc; use std::time::Duration; @@ -82,10 +51,12 @@ mod imp { use adw::{prelude::*, ToastOverlay}; use futures_util::{stream, StreamExt, TryStreamExt}; use glib::dpgettext2; + use gtk::gdk::{Key, ModifierType}; use gtk::glib::subclass::InitializingObject; use gtk::{gio, glib, CompositeTemplate}; use crate::app::model::{Device, Devices}; + use crate::app::widgets::EditDeviceDialog; use crate::config::G_LOG_DOMAIN; use crate::net; @@ -97,6 +68,8 @@ mod imp { pub struct TurnOnApplicationWindow { settings: gio::Settings, #[property(get, set)] + scan_network: Cell, + #[property(get, set)] startpage_icon_name: RefCell, #[template_child] devices_list: TemplateChild, @@ -235,11 +208,12 @@ mod imp { } )); - if devices.registered_devices().find(device).is_some() { - row.set_can_delete(true); - row.set_can_edit(true); - } else { - row.set_can_add(true); + let is_registered = devices.registered_devices().find(device).is_some(); + row.action_set_enabled("row.ask-delete", is_registered); + row.action_set_enabled("row.delete", is_registered); + row.action_set_enabled("row.edit", is_registered); + row.action_set_enabled("row.add", !is_registered); + if !is_registered { row.add_css_class("discovered"); } @@ -264,6 +238,7 @@ mod imp { gio::SettingsBackend::NONE, None, ), + scan_network: Default::default(), startpage_icon_name: Default::default(), devices_list: Default::default(), feedback: Default::default(), @@ -272,6 +247,27 @@ mod imp { fn class_init(klass: &mut Self::Class) { klass.bind_template(); + + klass.install_action("win.add-device", None, move |window, _, _| { + let dialog = EditDeviceDialog::new(); + dialog.connect_saved(glib::clone!( + #[weak(rename_to = devices)] + window.application().devices(), + move |_, device| { + glib::debug!("Adding new device: {:?}", device.imp()); + devices.registered_devices().append(device); + } + )); + dialog.present(Some(window)); + }); + klass.install_property_action("win.toggle-scan-network", "scan-network"); + + klass.add_binding_action(Key::N, ModifierType::CONTROL_MASK, "win.add-device"); + klass.add_binding_action( + Key::F5, + ModifierType::NO_MODIFIER_MASK, + "win.toggle-scan-network", + ); } fn instance_init(obj: &InitializingObject) { diff --git a/src/app/widgets/device_row.rs b/src/app/widgets/device_row.rs index deb9ff4..9a463b1 100644 --- a/src/app/widgets/device_row.rs +++ b/src/app/widgets/device_row.rs @@ -20,8 +20,6 @@ impl DeviceRow { glib::Object::builder() .property("device", device) .property("is-device-online", false) - .property("can-delete", false) - .property("can-edit", false) .build() } @@ -80,6 +78,7 @@ mod imp { use adw::subclass::prelude::*; use glib::subclass::{InitializingObject, Signal}; use glib::Properties; + use gtk::gdk::{Key, ModifierType}; use gtk::{template_callbacks, CompositeTemplate}; use crate::app::model::Device; @@ -96,12 +95,6 @@ mod imp { is_device_online: Cell, #[property(get)] suffix_mode: RefCell, - #[property(get, set, default = false)] - can_delete: Cell, - #[property(get, set, default = false)] - can_edit: Cell, - #[property(get, set, default = false)] - can_add: Cell, } #[template_callbacks] @@ -138,7 +131,7 @@ mod imp { klass.bind_template(); klass.bind_template_callbacks(); - klass.install_action("row.ask_delete", None, |row, _, _| { + klass.install_action("row.ask-delete", None, |row, _, _| { row.imp().set_suffix_mode("confirm-delete"); }); klass.install_action("row.cancel-delete", None, |row, _, _| { @@ -166,7 +159,16 @@ mod imp { move |_, device| row.emit_by_name::<()>("added", &[device]) )); dialog.present(Some(row)); - }) + }); + + klass.add_binding_action(Key::Return, ModifierType::ALT_MASK, "row.edit"); + klass.add_binding_action(Key::N, ModifierType::CONTROL_MASK, "row.add"); + klass.add_binding_action( + Key::Delete, + ModifierType::NO_MODIFIER_MASK, + "row.ask-delete", + ); + klass.add_binding_action(Key::Delete, ModifierType::CONTROL_MASK, "row.delete"); } fn instance_init(obj: &InitializingObject) { @@ -178,9 +180,6 @@ mod imp { device: Default::default(), is_device_online: Default::default(), suffix_mode: RefCell::new("buttons".into()), - can_edit: Default::default(), - can_delete: Default::default(), - can_add: Default::default(), } } } diff --git a/src/app/widgets/edit_device_dialog.rs b/src/app/widgets/edit_device_dialog.rs index eb9747e..ac58559 100644 --- a/src/app/widgets/edit_device_dialog.rs +++ b/src/app/widgets/edit_device_dialog.rs @@ -64,6 +64,7 @@ mod imp { use adw::prelude::*; use adw::subclass::prelude::*; + use gtk::gdk::{Key, ModifierType}; use gtk::glib::subclass::{InitializingObject, Signal}; use gtk::glib::Properties; use gtk::CompositeTemplate; @@ -221,6 +222,8 @@ mod imp { dialog.close(); } }); + + klass.add_binding_action(Key::S, ModifierType::CONTROL_MASK, "device.save"); } fn instance_init(obj: &InitializingObject) {