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]] [[package]]
name = "build_helpers" name = "build_helpers"
version = "0.14.0" version = "0.14.0"
source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8"
dependencies = [ dependencies = [
"cfg_aliases 0.2.2", "cfg_aliases 0.2.2",
] ]
@ -1396,7 +1396,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-config" name = "cosmic-config"
version = "1.0.0" version = "1.0.0"
source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8"
dependencies = [ dependencies = [
"atomicwrites", "atomicwrites",
"cosmic-config-derive", "cosmic-config-derive",
@ -1417,7 +1417,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-config-derive" name = "cosmic-config-derive"
version = "1.0.0" version = "1.0.0"
source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.119", "syn 2.0.119",
@ -1467,6 +1467,16 @@ dependencies = [
"xdg", "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]] [[package]]
name = "cosmic-panel-config" name = "cosmic-panel-config"
version = "0.1.0" version = "0.1.0"
@ -1535,6 +1545,7 @@ dependencies = [
"cosmic-config", "cosmic-config",
"cosmic-idle-config", "cosmic-idle-config",
"cosmic-mime-apps", "cosmic-mime-apps",
"cosmic-osk-config",
"cosmic-panel-config", "cosmic-panel-config",
"cosmic-protocols", "cosmic-protocols",
"cosmic-randr", "cosmic-randr",
@ -2347,7 +2358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3278,7 +3289,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_accessibility" name = "iced_accessibility"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8"
dependencies = [ dependencies = [
"accesskit", "accesskit",
"accesskit_winit", "accesskit_winit",
@ -3287,7 +3298,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_core" name = "iced_core"
version = "0.14.0" version = "0.14.0"
source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"build_helpers", "build_helpers",
@ -3324,7 +3335,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_futures" name = "iced_futures"
version = "0.14.0" version = "0.14.0"
source = "git+https://github.com/pop-os/libcosmic#d77e99fb1555e2728297677de61e23ada4d9d09b" source = "git+https://github.com/pop-os/libcosmic#87ab8179e1bd9880239c340855ae8862034bd0e8"
dependencies = [ dependencies = [
"futures", "futures",
"iced_core", "iced_core",
@ -6496,7 +6507,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -6509,7 +6520,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.12.1", "linux-raw-sys 0.12.1",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -7222,7 +7233,7 @@ dependencies = [
"getrandom 0.4.3", "getrandom 0.4.3",
"once_cell", "once_cell",
"rustix 1.1.4", "rustix 1.1.4",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -8302,7 +8313,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]

View file

@ -22,6 +22,7 @@ cosmic-bg-config.workspace = true
cosmic-comp-config = { workspace = true, optional = true } cosmic-comp-config = { workspace = true, optional = true }
cosmic-config.workspace = true cosmic-config.workspace = true
cosmic-idle-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-panel-config = { workspace = true, optional = true }
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", optional = true } cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", optional = true }
cosmic-randr-shell.workspace = true cosmic-randr-shell.workspace = true

View file

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

View file

@ -68,6 +68,9 @@ pub enum PageCommands {
/// Accessibility Magnifier settings page /// Accessibility Magnifier settings page
#[cfg(feature = "page-accessibility")] #[cfg(feature = "page-accessibility")]
AccessibilityMagnifier, AccessibilityMagnifier,
/// Accessibility OSK settings page
#[cfg(feature = "page-accessibility")]
AccessibilityOsk,
/// About settings page /// About settings page
#[cfg(feature = "page-about")] #[cfg(feature = "page-about")]
About, About,

View file

@ -17,6 +17,8 @@ use num_traits::FromPrimitive;
use slotmap::SlotMap; use slotmap::SlotMap;
pub mod magnifier; pub mod magnifier;
pub mod osk;
pub use cosmic_a11y_manager::{AccessibilityEvent, AccessibilityRequest, ColorFilter}; pub use cosmic_a11y_manager::{AccessibilityEvent, AccessibilityRequest, ColorFilter};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
@ -128,7 +130,11 @@ impl page::Page<crate::pages::Message> for Page {
&self, &self,
sections: &mut SlotMap<section::Entity, page::Section<crate::pages::Message>>, sections: &mut SlotMap<section::Entity, page::Section<crate::pages::Message>>,
) -> Option<page::Content> { ) -> 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> { 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 { impl page::AutoBind<crate::pages::Message> for Page {
fn sub_pages(page: Insert<crate::pages::Message>) -> Insert<crate::pages::Message> { 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 { impl Page {
pub fn update(&mut self, message: Message) -> cosmic::iced::Task<crate::app::Message> { pub fn update(&mut self, message: Message) -> cosmic::iced::Task<crate::app::Message> {
match 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), Accessibility(accessibility::Message),
#[cfg(feature = "page-accessibility")] #[cfg(feature = "page-accessibility")]
AccessibilityMagnifier(accessibility::magnifier::Message), AccessibilityMagnifier(accessibility::magnifier::Message),
#[cfg(feature = "page-accessibility")]
AccessibilityOsk(accessibility::osk::Message),
#[cfg(feature = "page-input")] #[cfg(feature = "page-input")]
AccessibilityShortcuts(input::keyboard::shortcuts::ShortcutMessage), AccessibilityShortcuts(input::keyboard::shortcuts::ShortcutMessage),
#[cfg(feature = "page-about")] #[cfg(feature = "page-about")]

View file

@ -312,6 +312,14 @@ color-filter = Color filter type
.protanopia = Red/Green (red weakness, Protanopia) .protanopia = Red/Green (red weakness, Protanopia)
.tritanopia = Blue/Yellow (blue weakness, Tritanopia) .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 = Desktop desktop = Desktop