From a366fc7bcecdf576e07bc609e58d30e45b69e2a0 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Mon, 21 Sep 2026 16:28:16 -0600 Subject: [PATCH] feat: add OSK settings (#2201) --- Cargo.lock | 33 ++-- cosmic-settings/Cargo.toml | 1 + cosmic-settings/src/app.rs | 8 + cosmic-settings/src/main.rs | 3 + .../src/pages/accessibility/mod.rs | 38 ++++- .../src/pages/accessibility/osk.rs | 146 ++++++++++++++++++ cosmic-settings/src/pages/mod.rs | 2 + i18n/en/cosmic_settings.ftl | 8 + 8 files changed, 226 insertions(+), 13 deletions(-) create mode 100644 cosmic-settings/src/pages/accessibility/osk.rs diff --git a/Cargo.lock b/Cargo.lock index 675791e..3c2c09c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -896,7 +896,7 @@ dependencies = [ [[package]] name = "build_helpers" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" +source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8" dependencies = [ "cfg_aliases 0.2.2", ] @@ -1396,7 +1396,7 @@ dependencies = [ [[package]] name = "cosmic-config" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" +source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8" dependencies = [ "atomicwrites", "cosmic-config-derive", @@ -1417,7 +1417,7 @@ dependencies = [ [[package]] name = "cosmic-config-derive" version = "1.0.0" -source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" +source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8" dependencies = [ "quote", "syn 2.0.119", @@ -1467,6 +1467,16 @@ dependencies = [ "xdg", ] +[[package]] +name = "cosmic-osk-config" +version = "0.1.0" +source = "git+https://github.com/pop-os/cosmic-osk#d7b66a24890d03e38dd8962d956bc802d4a41067" +dependencies = [ + "cosmic-config", + "iced_futures", + "serde", +] + [[package]] name = "cosmic-panel-config" version = "0.1.0" @@ -1535,6 +1545,7 @@ dependencies = [ "cosmic-config", "cosmic-idle-config", "cosmic-mime-apps", + "cosmic-osk-config", "cosmic-panel-config", "cosmic-protocols", "cosmic-randr", @@ -2347,7 +2358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -3278,7 +3289,7 @@ dependencies = [ [[package]] name = "iced_accessibility" version = "0.1.0" -source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" +source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8" dependencies = [ "accesskit", "accesskit_winit", @@ -3287,7 +3298,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" +source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8" dependencies = [ "bitflags 2.13.2", "build_helpers", @@ -3324,7 +3335,7 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.14.0" -source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" +source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8" dependencies = [ "futures", "iced_core", @@ -6496,7 +6507,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6509,7 +6520,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7222,7 +7233,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -8302,7 +8313,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/cosmic-settings/Cargo.toml b/cosmic-settings/Cargo.toml index d1a4e17..959eca2 100644 --- a/cosmic-settings/Cargo.toml +++ b/cosmic-settings/Cargo.toml @@ -22,6 +22,7 @@ cosmic-bg-config.workspace = true cosmic-comp-config = { workspace = true, optional = true } cosmic-config.workspace = true cosmic-idle-config.workspace = true +cosmic-osk-config = { git = "https://github.com/pop-os/cosmic-osk", features = ["subscription"] } cosmic-panel-config = { workspace = true, optional = true } cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", optional = true } cosmic-randr-shell.workspace = true diff --git a/cosmic-settings/src/app.rs b/cosmic-settings/src/app.rs index 15459c3..e002ec9 100644 --- a/cosmic-settings/src/app.rs +++ b/cosmic-settings/src/app.rs @@ -78,6 +78,8 @@ impl SettingsApp { PageCommands::AccessibilityMagnifier => { self.pages.page_id::() } + #[cfg(feature = "page-accessibility")] + PageCommands::AccessibilityOsk => self.pages.page_id::(), #[cfg(feature = "page-about")] PageCommands::About => self.pages.page_id::(), PageCommands::Appearance { command: _ } => { @@ -419,6 +421,12 @@ impl cosmic::Application for SettingsApp { return page.update(self.active_page, message).map(Into::into); } } + #[cfg(feature = "page-accessibility")] + crate::pages::Message::AccessibilityOsk(message) => { + if let Some(page) = self.pages.page_mut::() { + return page.update(self.active_page, message).map(Into::into); + } + } #[cfg(feature = "page-input")] crate::pages::Message::AccessibilityShortcuts(message) => { if let Some(page) = self diff --git a/cosmic-settings/src/main.rs b/cosmic-settings/src/main.rs index 7a39baf..faf1a8f 100644 --- a/cosmic-settings/src/main.rs +++ b/cosmic-settings/src/main.rs @@ -68,6 +68,9 @@ pub enum PageCommands { /// Accessibility Magnifier settings page #[cfg(feature = "page-accessibility")] AccessibilityMagnifier, + /// Accessibility OSK settings page + #[cfg(feature = "page-accessibility")] + AccessibilityOsk, /// About settings page #[cfg(feature = "page-about")] About, diff --git a/cosmic-settings/src/pages/accessibility/mod.rs b/cosmic-settings/src/pages/accessibility/mod.rs index 77c65cb..4345473 100644 --- a/cosmic-settings/src/pages/accessibility/mod.rs +++ b/cosmic-settings/src/pages/accessibility/mod.rs @@ -17,6 +17,8 @@ use num_traits::FromPrimitive; use slotmap::SlotMap; pub mod magnifier; +pub mod osk; + pub use cosmic_a11y_manager::{AccessibilityEvent, AccessibilityRequest, ColorFilter}; use tokio::sync::mpsc::UnboundedSender; @@ -128,7 +130,11 @@ impl page::Page for Page { &self, sections: &mut SlotMap>, ) -> Option { - Some(vec![sections.insert(vision()), sections.insert(hearing())]) + Some(vec![ + sections.insert(vision()), + sections.insert(hearing()), + sections.insert(mobility()), + ]) } fn on_enter(&mut self) -> cosmic::Task { @@ -185,7 +191,7 @@ impl page::Page for Page { impl page::AutoBind for Page { fn sub_pages(page: Insert) -> Insert { - page.sub_page::() + page.sub_page::().sub_page::() } } @@ -326,6 +332,34 @@ pub fn hearing() -> section::Section { }) } +pub fn mobility() -> section::Section { + crate::slab!(descriptions { + mobility = fl!("mobility"); + on_screen_keyboard = fl!("on-screen-keyboard"); + }); + + Section::default() + .title(&descriptions[mobility]) + .descriptions(descriptions) + .view::(move |binder, _, section| { + let descriptions = §ion.descriptions; + + let (osk_entity, _osk_info) = binder + .info + .iter() + .find(|(_, v)| v.id == "accessibility_osk") + .expect("osk page not found"); + + settings::section() + .title(§ion.title) + .add(crate::widget::go_next_item( + &descriptions[on_screen_keyboard], + crate::pages::Message::Page(osk_entity), + )) + .into() + }) +} + impl Page { pub fn update(&mut self, message: Message) -> cosmic::iced::Task { match message { diff --git a/cosmic-settings/src/pages/accessibility/osk.rs b/cosmic-settings/src/pages/accessibility/osk.rs new file mode 100644 index 0000000..dfbc186 --- /dev/null +++ b/cosmic-settings/src/pages/accessibility/osk.rs @@ -0,0 +1,146 @@ +use cosmic::Apply; +use cosmic::iced::Element; +use cosmic::widget::settings; +use cosmic_config::CosmicConfigEntry; +use cosmic_osk_config::Config; +use cosmic_settings_page::section::{self, Section}; +use cosmic_settings_page::{self as page, Entity}; +use slotmap::SlotMap; + +#[derive(Debug)] +pub struct Page { + entity: Entity, + + config: Config, + config_handler: cosmic_config::Config, +} + +#[derive(Debug, Clone)] +pub enum Message { + AlwaysShow(bool), + Config(Config), + GamepadShortcut(bool), + ImeActivation(bool), +} + +impl Default for Page { + fn default() -> Self { + let (config_handler, config) = match Config::handler() { + Ok(config_handler) => { + let config = Config::get_entry(&config_handler).unwrap_or_else(|(errs, config)| { + tracing::warn!("errors loading OSK config: {:?}", errs); + config + }); + (config_handler, config) + } + Err(err) => { + panic!("failed to create OSK config handler: {}", err); + } + }; + + Page { + entity: Entity::default(), + config, + config_handler, + } + } +} + +impl page::Page for Page { + fn set_id(&mut self, entity: Entity) { + self.entity = entity; + } + + fn info(&self) -> page::Info { + page::Info::new("accessibility_osk", "preferences-desktop-accessibility") + .title(fl!("on-screen-keyboard")) + } + + fn content( + &self, + sections: &mut SlotMap>, + ) -> Option { + Some(vec![sections.insert(osk())]) + } + + fn subscription( + &self, + _core: &cosmic::Core, + ) -> cosmic::iced::Subscription { + Config::subscription().map(|update| { + if !update.errors.is_empty() { + tracing::warn!("failed to parse OSK config update: {:?}", update.errors); + } + crate::pages::Message::AccessibilityOsk(Message::Config(update.config)) + }) + } +} + +impl page::AutoBind for Page {} + +pub fn osk() -> section::Section { + crate::slab!(descriptions { + on_screen_keyboard = fl!("on-screen-keyboard"); + always_show = fl!("on-screen-keyboard", "always-show"); + //TODO: icon_on_panel = fl!("on-screen-keyboard", "icon-on-panel"); + show_on_gamepad_shortcut = fl!("on-screen-keyboard", "show-on-gamepad-shortcut"); + show_on_text_input = fl!("on-screen-keyboard", "show-on-text-input"); + }); + + Section::default() + .title(&descriptions[on_screen_keyboard]) + .descriptions(descriptions) + .view::(move |_binder, page, section| { + let descriptions = §ion.descriptions; + + settings::section() + .add( + settings::item::builder(&descriptions[always_show]) + .toggler(page.config.always_shown, Message::AlwaysShow), + ) + .add( + settings::item::builder(&descriptions[show_on_gamepad_shortcut]) + .toggler(page.config.gamepad_shortcut, Message::GamepadShortcut), + ) + .add( + settings::item::builder(&descriptions[show_on_text_input]) + .toggler(page.config.ime_activation, Message::ImeActivation), + ) + .apply(Element::from) + .map(crate::pages::Message::AccessibilityOsk) + }) +} + +impl Page { + pub fn update( + &mut self, + _: page::Entity, + message: Message, + ) -> cosmic::iced::Task { + match message { + Message::AlwaysShow(value) => { + if let Err(err) = self.config.set_always_shown(&self.config_handler, value) { + tracing::error!("failed to set OSK config: {}", err); + } + } + Message::Config(config) => { + self.config = config; + } + Message::GamepadShortcut(value) => { + if let Err(err) = self + .config + .set_gamepad_shortcut(&self.config_handler, value) + { + tracing::error!("failed to set OSK config: {}", err); + } + } + Message::ImeActivation(value) => { + if let Err(err) = self.config.set_ime_activation(&self.config_handler, value) { + tracing::error!("failed to set OSK config: {}", err); + } + } + } + + cosmic::iced::Task::none() + } +} diff --git a/cosmic-settings/src/pages/mod.rs b/cosmic-settings/src/pages/mod.rs index 84f82c4..01170b1 100644 --- a/cosmic-settings/src/pages/mod.rs +++ b/cosmic-settings/src/pages/mod.rs @@ -31,6 +31,8 @@ pub enum Message { Accessibility(accessibility::Message), #[cfg(feature = "page-accessibility")] AccessibilityMagnifier(accessibility::magnifier::Message), + #[cfg(feature = "page-accessibility")] + AccessibilityOsk(accessibility::osk::Message), #[cfg(feature = "page-input")] AccessibilityShortcuts(input::keyboard::shortcuts::ShortcutMessage), #[cfg(feature = "page-about")] diff --git a/i18n/en/cosmic_settings.ftl b/i18n/en/cosmic_settings.ftl index 1aae514..1a8c7ed 100644 --- a/i18n/en/cosmic_settings.ftl +++ b/i18n/en/cosmic_settings.ftl @@ -312,6 +312,14 @@ color-filter = Color filter type .protanopia = Red/Green (red weakness, Protanopia) .tritanopia = Blue/Yellow (blue weakness, Tritanopia) +mobility = Mobility + +on-screen-keyboard = On-screen keyboard + .always-show = Always show on-screen-keyboard + .icon-on-panel = Icon on panel + .show-on-gamepad-shortcut = Show on-screen keyboard with a gamepad by pressing the Start and Select/Back buttons at the same time + .show-on-text-input = Show on-screen keyboard when selecting a text field + ## Desktop desktop = Desktop