diff --git a/resources/de.swsnr.turnon.metainfo.xml.in b/resources/de.swsnr.turnon.metainfo.xml.in index c3c4609..04d4aed 100644 --- a/resources/de.swsnr.turnon.metainfo.xml.in +++ b/resources/de.swsnr.turnon.metainfo.xml.in @@ -31,6 +31,25 @@ https://github.com/swsnr/turnon de.swsnr.turnon.desktop + + + +

Reorder devices with Alt+Up and Alt+Down

+
+ + GH-2 + + https://github.com/swsnr/turnon/releases/tag/next +

Refine application icon to make it sharper at smaller sizes, and redesign symbolic icon.

diff --git a/resources/gtk/help-overlay.blp b/resources/gtk/help-overlay.blp index 2a94ca7..3938e74 100644 --- a/resources/gtk/help-overlay.blp +++ b/resources/gtk/help-overlay.blp @@ -18,40 +18,30 @@ Gtk.ShortcutsWindow help_overlay { section-name: "shortcuts"; Gtk.ShortcutsGroup { - title: C_("shortcuts group", "Devices"); + title: C_("shortcuts group", "General"); Gtk.ShortcutsShortcut { - title: C_("shortcut description", "Add a new device"); - accelerator: "N"; + title: C_("shortcut description", "Show shortcuts"); + action-name: "win.show-help-overlay"; } Gtk.ShortcutsShortcut { - title: C_("shortcut description", "Toggle network scanning"); - action-name: "app.scan-network"; + title: C_("shortcut description", "Quit"); + action-name: "app.quit"; } } 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"; - } + title: C_("shortcuts group", "Devices"); Gtk.ShortcutsShortcut { - title: C_("shortcut description", "Ask to delete device"); - accelerator: "Delete"; + title: C_("shortcut description", "Add a new device"); + accelerator: "N"; } Gtk.ShortcutsShortcut { - title: C_("shortcut description", "Immediately delete device without confirmation"); - accelerator: "Delete"; + title: C_("shortcut description", "Toggle network scanning"); + action-name: "app.scan-network"; } } @@ -74,16 +64,36 @@ Gtk.ShortcutsWindow help_overlay { } Gtk.ShortcutsGroup { - title: C_("shortcuts group", "General"); + title: C_("shortcuts group", "Single device"); Gtk.ShortcutsShortcut { - title: C_("shortcut description", "Show shortcuts"); - action-name: "win.show-help-overlay"; + title: C_("shortcut description", "Turn on device"); + accelerator: "Return"; } Gtk.ShortcutsShortcut { - title: C_("shortcut description", "Quit"); - action-name: "app.quit"; + 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.ShortcutsShortcut { + title: C_("shortcut description", "Move device upwards"); + accelerator: "Up"; + } + + Gtk.ShortcutsShortcut { + title: C_("shortcut description", "Move device downwards"); + accelerator: "Down"; } } } diff --git a/resources/gtk/help-overlay.ui b/resources/gtk/help-overlay.ui index a257707..479d088 100644 --- a/resources/gtk/help-overlay.ui +++ b/resources/gtk/help-overlay.ui @@ -13,46 +13,34 @@ corresponding .blp file and regenerate this file with blueprint-compiler. shortcuts - Devices + General - Add a new device - <Ctrl>N + Show shortcuts + win.show-help-overlay - Toggle network scanning - app.scan-network + Quit + app.quit - Single device - - - Turn on device - Return - - - - - Edit device - <Alt>Return - - + Devices - Ask to delete device - Delete + Add a new device + <Ctrl>N - Immediately delete device without confirmation - <Ctrl>Delete + Toggle network scanning + app.scan-network @@ -81,17 +69,41 @@ corresponding .blp file and regenerate this file with blueprint-compiler. - General + Single device - Show shortcuts - win.show-help-overlay + Turn on device + Return - Quit - app.quit + Edit device + <Alt>Return + + + + + Ask to delete device + Delete + + + + + Immediately delete device without confirmation + <Ctrl>Delete + + + + + Move device upwards + <Alt>Up + + + + + Move device downwards + <Alt>Down diff --git a/src/app/widgets/application_window.rs b/src/app/widgets/application_window.rs index ed7abdf..3a7a7c4 100644 --- a/src/app/widgets/application_window.rs +++ b/src/app/widgets/application_window.rs @@ -194,12 +194,35 @@ mod imp { devices.registered_devices().append(device); } )); + row.connect_moved(glib::clone!( + #[strong] + devices, + move |_, device, direction| { + println!("Would swap {} with {direction}", device.label()); + let devices = devices.registered_devices(); + if let Some(current_index) = devices.find(device) { + let swap_index = current_index as i64 + direction as i64; + if 0 <= swap_index && swap_index < devices.n_items() as i64 { + if let Some(device_swapped) = devices.item(swap_index as u32) { + // We remove the other device, not the device being moved; this + // retains the widget for the device being moved in views consuming + // the model, meaning it remains focused, and we can repeatedly + // move the same device to rearrange it. + devices.remove(swap_index as u32); + devices.insert(current_index, &device_swapped); + } + } + } + } + )); 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); + row.action_set_enabled("row.move-up", is_registered); + row.action_set_enabled("row.move-down", is_registered); if !is_registered { row.add_css_class("discovered"); } diff --git a/src/app/widgets/device_row.rs b/src/app/widgets/device_row.rs index 9a463b1..ae844f2 100644 --- a/src/app/widgets/device_row.rs +++ b/src/app/widgets/device_row.rs @@ -62,6 +62,29 @@ impl DeviceRow { ), ) } + + pub fn connect_moved(&self, callback: F) -> glib::SignalHandlerId + where + F: Fn(&Self, &Device, i32) + 'static, + { + self.connect_local( + "moved", + false, + glib::clone!( + #[weak(rename_to=row)] + &self, + #[upgrade_or_default] + move |args| { + let device = &args[1].get().expect("No device passed as signal argument?"); + let direction = args[2] + .get() + .expect("No direction passed as signal argument?"); + callback(&row, device, direction); + None + } + ), + ) + } } impl Default for DeviceRow { @@ -131,6 +154,14 @@ mod imp { klass.bind_template(); klass.bind_template_callbacks(); + klass.install_action("row.move-up", None, |row, _, _| { + let direction: i32 = -1; + row.emit_by_name::<()>("moved", &[&row.device(), &direction]); + }); + klass.install_action("row.move-down", None, |row, _, _| { + let direction: i32 = 1; + row.emit_by_name::<()>("moved", &[&row.device(), &direction]); + }); klass.install_action("row.ask-delete", None, |row, _, _| { row.imp().set_suffix_mode("confirm-delete"); }); @@ -161,6 +192,8 @@ mod imp { dialog.present(Some(row)); }); + klass.add_binding_action(Key::Up, ModifierType::ALT_MASK, "row.move-up"); + klass.add_binding_action(Key::Down, ModifierType::ALT_MASK, "row.move-down"); 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( @@ -197,6 +230,10 @@ mod imp { .action() .param_types([Device::static_type()]) .build(), + Signal::builder("moved") + .action() + .param_types([Device::static_type(), i32::static_type()]) + .build(), ] }); SIGNALS.as_ref()