feat: add OSK settings (#2201)

This commit is contained in:
Jeremy Soller 2026-09-21 16:28:16 -06:00 • committed by GitHub
parent 0f787deaec
commit a366fc7bce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 226 additions and 13 deletions

33
Cargo.lock generated
View file

@ -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]]

View file

@ -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

View file

@ -78,6 +78,8 @@ impl SettingsApp {
PageCommands::AccessibilityMagnifier => {
self.pages.page_id::<accessibility::magnifier::Page>()
}
#[cfg(feature = "page-accessibility")]
PageCommands::AccessibilityOsk => self.pages.page_id::<accessibility::osk::Page>(),
#[cfg(feature = "page-about")]
PageCommands::About => self.pages.page_id::<system::about::Page>(),
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::<accessibility::osk::Page>() {
return page.update(self.active_page, message).map(Into::into);
}
}
#[cfg(feature = "page-input")]
crate::pages::Message::AccessibilityShortcuts(message) => {
if let Some(page) = self

View file

@ -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,

View file

@ -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<crate::pages::Message> for Page {
&self,
sections: &mut SlotMap<section::Entity, page::Section<crate::pages::Message>>,
) -> Option<page::Content> {
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<crate::pages::Message> {
@ -185,7 +191,7 @@ impl page::Page<crate::pages::Message> for Page {
impl page::AutoBind<crate::pages::Message> for Page {
fn sub_pages(page: Insert<crate::pages::Message>) -> Insert<crate::pages::Message> {
page.sub_page::<magnifier::Page>()
page.sub_page::<magnifier::Page>().sub_page::<osk::Page>()
}
}
@ -326,6 +332,34 @@ pub fn hearing() -> section::Section<crate::pages::Message> {
})
}
pub fn mobility() -> section::Section<crate::pages::Message> {
crate::slab!(descriptions {
mobility = fl!("mobility");
on_screen_keyboard = fl!("on-screen-keyboard");
});
Section::default()
.title(&descriptions[mobility])
.descriptions(descriptions)
.view::<Page>(move |binder, _, section| {
let descriptions = &section.descriptions;
let (osk_entity, _osk_info) = binder
.info
.iter()
.find(|(_, v)| v.id == "accessibility_osk")
.expect("osk page not found");
settings::section()
.title(&section.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<crate::app::Message> {
match message {

View file

@ -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<crate::pages::Message> 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<section::Entity, page::Section<crate::pages::Message>>,
) -> Option<page::Content> {
Some(vec![sections.insert(osk())])
}
fn subscription(
&self,
_core: &cosmic::Core,
) -> cosmic::iced::Subscription<crate::pages::Message> {
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<crate::pages::Message> for Page {}
pub fn osk() -> section::Section<crate::pages::Message> {
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::<Page>(move |_binder, page, section| {
let descriptions = &section.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<crate::app::Message> {
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()
}
}

View file

@ -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")]

View file

@ -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