Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 82 additions & 69 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions cosmic-settings/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ upower_dbus = { git = "https://github.com/pop-os/dbus-settings-bindings", option
bluez-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
url = "2.5.7"
xkb-data = "0.2.1"
xkeysym = { version = "0.2.0", optional = true }
zbus = { version = "5.11.0", default-features = false, features = [
"tokio",
], optional = true }
Expand Down Expand Up @@ -147,6 +148,8 @@ page-input = [
"dep:cosmic-comp-config",
"dep:cosmic-settings-config",
"dep:udev",
"dep:xkeysym",
"wayland",
]
page-legacy-applications = ["dep:cosmic-comp-config"]
page-networking = [
Expand Down
226 changes: 198 additions & 28 deletions cosmic-settings/src/pages/input/keyboard/shortcuts/common.rs

Large diffs are not rendered by default.

242 changes: 226 additions & 16 deletions cosmic-settings/src/pages/input/keyboard/shortcuts/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ use std::str::FromStr;
use super::{ShortcutBinding, ShortcutMessage, ShortcutModel};

use cosmic::app::ContextDrawer;
use cosmic::iced::keyboard::key::Named;
use cosmic::iced::keyboard::{Key, Location, Modifiers};
use cosmic::iced::{Alignment, Length};
use cosmic::iced_winit;
use cosmic::widget::{self, button, icon};
use cosmic::{Apply, Element, Task};
use cosmic_settings_config::Binding;
use cosmic_settings_config::shortcuts::{Action, Shortcuts};
use cosmic_settings_page::{self as page, Section, section};
use slab::Slab;
use slotmap::{Key, SlotMap};
use slotmap::{Key as SlotKey, SlotMap};

pub struct Page {
entity: page::Entity,
Expand Down Expand Up @@ -63,6 +66,9 @@ pub enum Message {
Shortcut(ShortcutMessage),
/// Open the add shortcut context drawer
ShortcutContext,
ModifiersChanged(Modifiers),
KeyReleased(u32, Key, Location),
KeyPressed(u32, Key, Location, Modifiers),
}

#[derive(Default)]
Expand All @@ -72,6 +78,7 @@ struct AddShortcut {
pub name: String,
pub task: String,
pub keys: Slab<(String, widget::Id)>,
pub binding: Binding,
}

impl AddShortcut {
Expand Down Expand Up @@ -99,17 +106,19 @@ impl Page {
self.add_shortcut.task = text;
}

Message::KeyInput(id, text) => {
self.add_shortcut.keys[id].0 = text;
}
Message::KeyInput(..) => {}

Message::KeyEditing(id, enable) => {
if enable {
self.add_shortcut.editing = Some(id)
self.add_shortcut.editing = Some(id);
return iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(true).discard();
} else if self.add_shortcut.editing == Some(id) {
let task = self.add_keybinding();
self.add_shortcut.editing = None;
return task;

return Task::batch(vec![
widget::text_input::focus(widget::Id::unique()),
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(false).discard(),
]);
}
}

Expand Down Expand Up @@ -151,11 +160,13 @@ impl Page {
addable_bindings.push(binding);
}

for binding in addable_bindings {
for mut binding in addable_bindings {
binding.keycode = None;
self.add_shortcut(binding);
}

self.model.on_enter();
self.add_shortcut.binding = Default::default();
_ = self.model.on_enter();
}

Message::EditCombination => {
Expand All @@ -164,6 +175,10 @@ impl Page {
return Task::batch(vec![
widget::text_input::focus(id.clone()),
widget::text_input::select_all(id.clone()),
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(
true,
)
.discard()
]);
}
}
Expand All @@ -175,20 +190,23 @@ impl Page {
}

Message::ReplaceApply => {
if let Some((binding, ..)) = self.replace_dialog.pop() {
if let Some((mut binding, ..)) = self.replace_dialog.pop() {
self.model.config_remove(&binding);
binding.keycode = None;
self.add_shortcut(binding);

if self.replace_dialog.is_empty() {
self.model.on_enter();
self.add_shortcut = Default::default();
_ = self.model.on_enter();
}
}
}

Message::ReplaceCancel => {
_ = self.replace_dialog.pop();
if self.replace_dialog.is_empty() {
self.model.on_enter();
self.add_shortcut = Default::default();
_ = self.model.on_enter();
}
}

Expand All @@ -207,6 +225,112 @@ impl Page {
widget::text_input::focus(self.name_id.clone()),
]);
}

Message::ModifiersChanged(modifiers) => {
if self.add_shortcut.active {
let mut cfg_modifiers = cosmic_settings_config::shortcuts::Modifiers::new();
if modifiers.alt() {
cfg_modifiers = cfg_modifiers.alt()
}
if modifiers.control() {
cfg_modifiers = cfg_modifiers.ctrl()
}
if modifiers.shift() {
cfg_modifiers = cfg_modifiers.shift()
}
if modifiers.logo() {
cfg_modifiers = cfg_modifiers.logo()
}
self.add_shortcut.binding.modifiers = cfg_modifiers;

if self.add_shortcut.binding.keycode.is_none() && modifiers.is_empty() {
self.add_shortcut = Default::default();
self.add_shortcut = Default::default();
_ = self.model.on_enter();

return Task::batch(vec![
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(false).discard()
]);
}
if let Some(k) = self
.add_shortcut
.keys
.get_mut(self.add_shortcut.editing.unwrap())
{
k.0 = self.add_shortcut.binding.to_string();
}
}
}
Message::KeyReleased(keycode, _, _) => {
// if the currently selected shortcut matches, finish selecting shortcut
if self.add_shortcut.editing.is_some()
&& self.add_shortcut.active
&& self.add_shortcut.binding.key.is_some()
&& self
.add_shortcut
.binding
.keycode
.is_some_and(|k| k == keycode)
&& self.add_shortcut.binding.modifiers
!= cosmic_settings_config::shortcuts::Modifiers::new()
{
// XX for now avoid applying the keycode
let binding = Binding {
modifiers: self.add_shortcut.binding.modifiers.clone(),
key: self.add_shortcut.binding.key,
keycode: None,
description: None,
};
let Some(k) = self
.add_shortcut
.keys
.get_mut(self.add_shortcut.editing.unwrap())
else {
return iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(false).discard();
};
k.0 = binding.to_string();

if self.add_shortcut.name.trim().is_empty()
|| self.add_shortcut.task.trim().is_empty()
{
return Task::batch(vec![
widget::text_input::focus(widget::Id::unique()),
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(false).discard(),
]);
}
self.add_shortcut(binding);
self.add_shortcut = Default::default();
_ = self.model.on_enter();

return Task::batch(vec![
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(false).discard(),
]);
}
}
Message::KeyPressed(keycode, unmodified_keysym, location, modifiers) => {
if unmodified_keysym == Key::Named(Named::Escape) && modifiers.is_empty() {
self.add_shortcut.editing = None;
return Task::batch(vec![
widget::text_input::focus(widget::Id::unique()),
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(false).discard(),
]);
}
if self.add_shortcut.active {
self.add_shortcut.binding.keycode = Some(keycode);
self.add_shortcut.binding.key =
iced_winit::platform_specific::wayland::keymap::key_to_keysym(
unmodified_keysym,
location,
);
if let Some(k) = self
.add_shortcut
.keys
.get_mut(self.add_shortcut.editing.unwrap())
{
k.0 = self.add_shortcut.binding.to_string();
}
}
}
}

Task::none()
Expand All @@ -221,7 +345,13 @@ impl Page {

binding.clear();

return widget::text_input::focus(id.clone());
return Task::batch(vec![
widget::text_input::focus(id.clone()),
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(
true,
)
.discard(),
]);
}

let new_id = widget::Id::unique();
Expand All @@ -234,6 +364,10 @@ impl Page {
Task::batch(vec![
widget::text_input::focus(new_id.clone()),
widget::text_input::select_all(new_id),
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(
true,
)
.discard(),
])
}

Expand Down Expand Up @@ -275,6 +409,7 @@ impl Page {
self.add_shortcut.editing == Some(id),
move |enable| Message::KeyEditing(id, enable),
)
.on_focus(Message::KeyEditing(id, true))
.select_on_focus(true)
.padding([0, 12])
.on_input(move |input| Message::KeyInput(id, input))
Expand Down Expand Up @@ -381,13 +516,87 @@ impl page::Page<crate::pages::Message> for Page {
}

fn on_enter(&mut self) -> Task<crate::pages::Message> {
self.model.on_enter();
self.add_shortcut = Default::default();
_ = self.model.on_enter();
Task::none()
}

fn on_leave(&mut self) -> Task<crate::pages::Message> {
self.model.on_clear();
Task::none()
_ = self.model.on_clear();
iced_winit::platform_specific::commands::keyboard_shortcuts_inhibit::inhibit_shortcuts(
false,
)
.discard()
}

#[cfg(feature = "wayland")]
fn subscription(
&self,
core: &cosmic::Core,
) -> cosmic::iced::Subscription<crate::pages::Message> {
use cosmic::iced::{self, event::listen_with};

cosmic::iced::Subscription::batch(vec![
if self.add_shortcut.active && self.add_shortcut.editing.is_some() {
listen_with(|event, _, _| match event {
iced::event::Event::Keyboard(iced::keyboard::Event::KeyPressed {
key,
physical_key,
location,
modifiers,
..
}) => {
use cosmic::iced::keyboard::{Key, key::Named};
if matches!(
key,
Key::Named(Named::Super | Named::Alt | Named::Control | Named::Shift)
) {
return None;
}
cosmic::iced_winit::conversion::physical_to_scancode(physical_key).map(
|code| {
crate::pages::Message::CustomShortcuts(Message::KeyPressed(
code, key, location, modifiers,
))
},
)
}
iced::event::Event::Keyboard(iced::keyboard::Event::KeyReleased {
key,
physical_key,
location,
..
}) => {
use cosmic::iced::keyboard::{Key, key::Named};
if matches!(
key,
Key::Named(Named::Super | Named::Alt | Named::Control | Named::Shift)
) {
return None;
}
cosmic::iced_winit::conversion::physical_to_scancode(physical_key).map(
|code| {
crate::pages::Message::CustomShortcuts(Message::KeyReleased(
code, key, location,
))
},
)
}
iced::event::Event::Keyboard(iced::keyboard::Event::ModifiersChanged(e)) => {
Some(crate::pages::Message::CustomShortcuts(
Message::ModifiersChanged(e),
))
}

_ => None,
})
} else {
cosmic::iced::Subscription::none()
},
self.model
.subscription(core)
.map(|m| crate::pages::Message::CustomShortcuts(Message::Shortcut(m))),
])
}
}

Expand All @@ -406,6 +615,7 @@ fn bindings(_defaults: &Shortcuts, keybindings: &Shortcuts) -> Slab<ShortcutMode
let new_binding = ShortcutBinding {
id: widget::Id::unique(),
binding: binding.clone(),
pending: binding.clone(),
input: String::new(),
is_default: false,
is_saved: true,
Expand Down
Loading