feat(keyboard): add layouts and their variants to the xkb config
This commit is contained in:
parent
2b88275af8
commit
42989b68a7
6 changed files with 308 additions and 69 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
|
@ -1330,6 +1330,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
|
"xkb-data",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -4894,6 +4895,18 @@ dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde-xml-rs"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb3aa78ecda1ebc9ec9847d5d3aba7d618823446a049ba2491940506da6e2782"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"xml-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.197"
|
version = "1.0.197"
|
||||||
|
|
@ -6585,6 +6598,16 @@ dependencies = [
|
||||||
"wayland-protocols-wlr",
|
"wayland-protocols-wlr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xkb-data"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "294a599fc9e6a43c9f44f5d6c560b89fd751be413717442b31c17fa367d3c764"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde-xml-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xkbcommon"
|
name = "xkbcommon"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["cosmic-settings", "page", "pages/*"]
|
members = [ "cosmic-settings", "page", "pages/*"]
|
||||||
default-members = ["cosmic-settings"]
|
default-members = ["cosmic-settings"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
rust-version = "1.71.0"
|
rust-version = "1.71.0"
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ static_init = "1.0.3"
|
||||||
clap = { version = "4.4.18", features = ["derive"] }
|
clap = { version = "4.4.18", features = ["derive"] }
|
||||||
itoa = "1.0.10"
|
itoa = "1.0.10"
|
||||||
futures = { package = "futures-lite", version = "2.2.0" }
|
futures = { package = "futures-lite", version = "2.2.0" }
|
||||||
|
xkb-data = "0.1.0"
|
||||||
|
|
||||||
[dependencies.i18n-embed]
|
[dependencies.i18n-embed]
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,15 @@
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
cosmic_config::{self, ConfigSet},
|
cosmic_config::{self, ConfigSet},
|
||||||
iced::{
|
iced::{self, Length},
|
||||||
self,
|
|
||||||
widget::{self, horizontal_space},
|
|
||||||
Length,
|
|
||||||
},
|
|
||||||
iced_core::Border,
|
iced_core::Border,
|
||||||
iced_style, theme,
|
iced_style, theme,
|
||||||
widget::{button, container, icon, radio, settings},
|
widget::{self, button, container, icon, radio, settings},
|
||||||
Apply, Command, Element,
|
Apply, Command, Element,
|
||||||
};
|
};
|
||||||
use cosmic_comp_config::XkbConfig;
|
use cosmic_comp_config::XkbConfig;
|
||||||
use cosmic_settings_page::{self as page, section, Section};
|
use cosmic_settings_page::{self as page, section, Section};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use slotmap::SlotMap;
|
use slotmap::{DefaultKey, SlotMap};
|
||||||
|
|
||||||
static COMPOSE_OPTIONS: &[(&str, &str)] = &[
|
static COMPOSE_OPTIONS: &[(&str, &str)] = &[
|
||||||
// ("Left Alt", "compose:lalt"), XXX?
|
// ("Left Alt", "compose:lalt"), XXX?
|
||||||
|
|
@ -41,16 +37,33 @@ static ALTERNATE_CHARACTER_OPTIONS: &[(&str, &str)] = &[
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
ExpandInputSourcePopover(Option<String>),
|
ExpandInputSourcePopover(Option<DefaultKey>),
|
||||||
OpenSpecialCharacterContext(SpecialKey),
|
OpenSpecialCharacterContext(SpecialKey),
|
||||||
|
ShowInputSourcesContext,
|
||||||
|
SourceAdd(DefaultKey),
|
||||||
|
SourceContext(SourceContext),
|
||||||
SpecialCharacterSelect(Option<&'static str>),
|
SpecialCharacterSelect(Option<&'static str>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum SourceContext {
|
||||||
|
MoveDown(DefaultKey),
|
||||||
|
MoveUp(DefaultKey),
|
||||||
|
Remove(DefaultKey),
|
||||||
|
Settings(DefaultKey),
|
||||||
|
ViewLayout(DefaultKey),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Locale = String;
|
||||||
|
pub type Variant = String;
|
||||||
|
pub type Description = String;
|
||||||
|
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
config: cosmic_config::Config,
|
config: cosmic_config::Config,
|
||||||
context: Option<Context>,
|
context: Option<Context>,
|
||||||
expanded_source_popover: Option<String>,
|
expanded_source_popover: Option<DefaultKey>,
|
||||||
sources: Vec<InputSource>,
|
keyboard_layouts: SlotMap<DefaultKey, (Locale, Variant, Description)>,
|
||||||
|
active_layouts: Vec<DefaultKey>,
|
||||||
xkb: XkbConfig,
|
xkb: XkbConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,14 +74,16 @@ impl Default for Page {
|
||||||
Self {
|
Self {
|
||||||
context: None,
|
context: None,
|
||||||
expanded_source_popover: None,
|
expanded_source_popover: None,
|
||||||
sources: default_input_sources(),
|
keyboard_layouts: SlotMap::new(),
|
||||||
xkb: super::get_config(&config, "xkb_config"),
|
active_layouts: Vec::new(),
|
||||||
|
xkb: XkbConfig::default(),
|
||||||
config,
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Context {
|
enum Context {
|
||||||
|
ShowInputSourcesContext,
|
||||||
SpecialCharacter(SpecialKey),
|
SpecialCharacter(SpecialKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +109,11 @@ impl SpecialKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popover_menu_row(label: String) -> cosmic::Element<'static, Message> {
|
fn popover_menu_row(
|
||||||
|
id: DefaultKey,
|
||||||
|
label: String,
|
||||||
|
message: impl Fn(DefaultKey) -> SourceContext + 'static,
|
||||||
|
) -> cosmic::Element<'static, Message> {
|
||||||
widget::text(label)
|
widget::text(label)
|
||||||
.apply(widget::container)
|
.apply(widget::container)
|
||||||
.style(cosmic::theme::Container::custom(|theme| {
|
.style(cosmic::theme::Container::custom(|theme| {
|
||||||
|
|
@ -104,22 +123,41 @@ fn popover_menu_row(label: String) -> cosmic::Element<'static, Message> {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.apply(button)
|
.apply(button)
|
||||||
|
.on_press(())
|
||||||
.style(theme::Button::Transparent)
|
.style(theme::Button::Transparent)
|
||||||
.into()
|
.apply(Element::from)
|
||||||
|
.map(move |()| Message::SourceContext(message(id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO for on press, would need to clone ID for each row?
|
fn popover_menu(id: DefaultKey) -> cosmic::Element<'static, Message> {
|
||||||
fn popover_menu() -> cosmic::Element<'static, Message> {
|
widget::column::with_children(vec![
|
||||||
// XXX translate
|
popover_menu_row(
|
||||||
widget::column![
|
id,
|
||||||
popover_menu_row(fl!("keyboard-sources", "move-up")),
|
fl!("keyboard-sources", "move-up"),
|
||||||
popover_menu_row(fl!("keyboard-sources", "move-down")),
|
SourceContext::MoveUp,
|
||||||
//cosmic::widget::divider::horizontal::light(),
|
)
|
||||||
cosmic::widget::divider::horizontal::light(),
|
.into(),
|
||||||
popover_menu_row(fl!("keyboard-sources", "settings")),
|
popover_menu_row(
|
||||||
popover_menu_row(fl!("keyboard-sources", "view-layout")),
|
id,
|
||||||
popover_menu_row(fl!("keyboard-sources", "remove")),
|
fl!("keyboard-sources", "move-down"),
|
||||||
]
|
SourceContext::MoveDown,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
cosmic::widget::divider::horizontal::light().into(),
|
||||||
|
popover_menu_row(
|
||||||
|
id,
|
||||||
|
fl!("keyboard-sources", "settings"),
|
||||||
|
SourceContext::Settings,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
popover_menu_row(
|
||||||
|
id,
|
||||||
|
fl!("keyboard-sources", "view-layout"),
|
||||||
|
SourceContext::ViewLayout,
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
popover_menu_row(id, fl!("keyboard-sources", "remove"), SourceContext::Remove).into(),
|
||||||
|
])
|
||||||
.width(Length::Shrink)
|
.width(Length::Shrink)
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
.apply(cosmic::widget::container)
|
.apply(cosmic::widget::container)
|
||||||
|
|
@ -139,41 +177,34 @@ fn popover_menu() -> cosmic::Element<'static, Message> {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popover_button(input_source: &InputSource, expanded: bool) -> cosmic::Element<'static, Message> {
|
fn popover_button(id: DefaultKey, expanded: bool) -> cosmic::Element<'static, Message> {
|
||||||
let on_press = Message::ExpandInputSourcePopover(if expanded {
|
let on_press = Message::ExpandInputSourcePopover(if expanded { None } else { Some(id) });
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(input_source.id.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
let button = button::icon(icon::from_name("open-menu-symbolic"))
|
let button = button::icon(icon::from_name("open-menu-symbolic"))
|
||||||
.extra_small()
|
.extra_small()
|
||||||
.padding(0)
|
|
||||||
.on_press(on_press);
|
.on_press(on_press);
|
||||||
|
|
||||||
if expanded {
|
if expanded {
|
||||||
cosmic::widget::popover(button).popup(popover_menu()).into()
|
cosmic::widget::popover(button)
|
||||||
|
.popup(popover_menu(id))
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
button.into()
|
button.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_source<'a>(
|
fn input_source(
|
||||||
input_source: &'a InputSource,
|
id: DefaultKey,
|
||||||
expanded_source_popover: Option<&'a str>,
|
description: &str,
|
||||||
) -> cosmic::Element<'a, Message> {
|
expanded_source_popover: Option<DefaultKey>,
|
||||||
let expanded = expanded_source_popover == Some(input_source.id.as_str());
|
) -> cosmic::Element<Message> {
|
||||||
settings::item(&input_source.label, popover_button(input_source, expanded)).into()
|
let expanded = expanded_source_popover.is_some_and(|expanded_id| expanded_id == id);
|
||||||
|
|
||||||
|
settings::item(description, popover_button(id, expanded)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod shortcuts;
|
pub mod shortcuts;
|
||||||
|
|
||||||
pub struct InputSource {
|
|
||||||
id: String,
|
|
||||||
// TODO Translate?
|
|
||||||
label: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn special_char_radio_row<'a>(
|
fn special_char_radio_row<'a>(
|
||||||
desc: &'a str,
|
desc: &'a str,
|
||||||
value: Option<&'static str>,
|
value: Option<&'static str>,
|
||||||
|
|
@ -186,14 +217,6 @@ fn special_char_radio_row<'a>(
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX
|
|
||||||
pub fn default_input_sources() -> Vec<InputSource> {
|
|
||||||
vec![InputSource {
|
|
||||||
id: "us".to_string(),
|
|
||||||
label: "English (US)".to_string(),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
impl page::Page<crate::pages::Message> for Page {
|
impl page::Page<crate::pages::Message> for Page {
|
||||||
fn content(
|
fn content(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -214,6 +237,7 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
|
|
||||||
fn context_drawer(&self) -> Option<Element<'_, crate::pages::Message>> {
|
fn context_drawer(&self) -> Option<Element<'_, crate::pages::Message>> {
|
||||||
match self.context {
|
match self.context {
|
||||||
|
Some(Context::ShowInputSourcesContext) => Some(self.add_input_source_view()),
|
||||||
Some(Context::SpecialCharacter(special_key)) => self
|
Some(Context::SpecialCharacter(special_key)) => self
|
||||||
.special_character_key_view(special_key)
|
.special_character_key_view(special_key)
|
||||||
.map(crate::pages::Message::Keyboard)
|
.map(crate::pages::Message::Keyboard)
|
||||||
|
|
@ -222,11 +246,124 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reload(&mut self, _page: page::Entity) -> Command<crate::pages::Message> {
|
||||||
|
self.xkb = super::get_config(&self.config, "xkb_config");
|
||||||
|
match xkb_data::keyboard_layouts() {
|
||||||
|
Ok(keyboard_layouts) => {
|
||||||
|
self.active_layouts.clear();
|
||||||
|
self.keyboard_layouts.clear();
|
||||||
|
|
||||||
|
for layout in keyboard_layouts.layouts() {
|
||||||
|
self.keyboard_layouts.insert((
|
||||||
|
layout.name().to_owned(),
|
||||||
|
String::new(),
|
||||||
|
layout.description().to_owned(),
|
||||||
|
));
|
||||||
|
|
||||||
|
if let Some(variants) = layout.variants() {
|
||||||
|
for variant in variants {
|
||||||
|
self.keyboard_layouts.insert((
|
||||||
|
layout.name().to_owned(),
|
||||||
|
variant.name().to_owned(),
|
||||||
|
variant.description().to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Xkb layouts currently enabled.
|
||||||
|
let layouts = self.xkb.layout.split_terminator(',');
|
||||||
|
|
||||||
|
// Xkb variants for each layout. Repeat empty strings in case there's more layouts than variants.
|
||||||
|
let variants = self
|
||||||
|
.xkb
|
||||||
|
.variant
|
||||||
|
.split_terminator(',')
|
||||||
|
.chain(std::iter::repeat(""));
|
||||||
|
|
||||||
|
for (layout, variant) in layouts.zip(variants) {
|
||||||
|
for (id, (xkb_layout, xkb_variant, _desc)) in &self.keyboard_layouts {
|
||||||
|
if layout == xkb_layout && variant == xkb_variant {
|
||||||
|
self.active_layouts.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(why) => {
|
||||||
|
tracing::error!(?why, "failed to get keyboard layouts");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
||||||
match message {
|
match message {
|
||||||
|
Message::SourceAdd(id) => {
|
||||||
|
self.context = None;
|
||||||
|
|
||||||
|
if !self.active_layouts.contains(&id) {
|
||||||
|
self.active_layouts.push(id);
|
||||||
|
self.update_xkb_config();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::SourceContext(context_message) => {
|
||||||
|
self.expanded_source_popover = None;
|
||||||
|
|
||||||
|
match context_message {
|
||||||
|
SourceContext::MoveDown(id) => {
|
||||||
|
if let Some(pos) =
|
||||||
|
self.active_layouts.iter().position(|&active| active == id)
|
||||||
|
{
|
||||||
|
if pos + 1 < self.active_layouts.len() {
|
||||||
|
self.active_layouts.swap(pos, pos + 1);
|
||||||
|
self.update_xkb_config();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceContext::MoveUp(id) => {
|
||||||
|
if let Some(pos) =
|
||||||
|
self.active_layouts.iter().position(|&active| active == id)
|
||||||
|
{
|
||||||
|
if pos > 0 {
|
||||||
|
self.active_layouts.swap(pos, pos - 1);
|
||||||
|
self.update_xkb_config();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceContext::Remove(id) => {
|
||||||
|
if let Some(pos) =
|
||||||
|
self.active_layouts.iter().position(|&active| active == id)
|
||||||
|
{
|
||||||
|
let _removed = self.active_layouts.remove(pos);
|
||||||
|
self.update_xkb_config();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceContext::Settings(id) => {
|
||||||
|
eprintln!("settings not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceContext::ViewLayout(id) => {
|
||||||
|
eprintln!("view layout not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::ShowInputSourcesContext => {
|
||||||
|
self.context = Some(Context::ShowInputSourcesContext);
|
||||||
|
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||||
|
fl!("keyboard-sources", "add").into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Message::ExpandInputSourcePopover(value) => {
|
Message::ExpandInputSourcePopover(value) => {
|
||||||
self.expanded_source_popover = value;
|
self.expanded_source_popover = value;
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +377,7 @@ impl Page {
|
||||||
|
|
||||||
Message::SpecialCharacterSelect(id) => {
|
Message::SpecialCharacterSelect(id) => {
|
||||||
if let Some(Context::SpecialCharacter(special_key)) = self.context {
|
if let Some(Context::SpecialCharacter(special_key)) = self.context {
|
||||||
let options = self.xkb.options.as_deref().unwrap_or("");
|
let options = self.xkb.options.as_deref().unwrap_or_default();
|
||||||
let prefix = special_key.prefix();
|
let prefix = special_key.prefix();
|
||||||
let new_options = options
|
let new_options = options
|
||||||
.split(',')
|
.split(',')
|
||||||
|
|
@ -260,8 +397,41 @@ impl Page {
|
||||||
Command::none()
|
Command::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_input_source_view(&self) -> cosmic::Element<'static, crate::app::Message> {
|
pub fn add_input_source_view(&self) -> Element<'_, crate::pages::Message> {
|
||||||
widget::column![].into()
|
let mut list = widget::list_column();
|
||||||
|
|
||||||
|
for (id, (_locale, variant, description)) in &self.keyboard_layouts {
|
||||||
|
list = list.add(self.input_source_item(id, description, !variant.is_empty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
widget::column()
|
||||||
|
.push(list)
|
||||||
|
.apply(Element::from)
|
||||||
|
.map(crate::pages::Message::Keyboard)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input_source_item<'a>(
|
||||||
|
&self,
|
||||||
|
id: DefaultKey,
|
||||||
|
description: &'a str,
|
||||||
|
indent: bool,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
|
let is_added = self.active_layouts.contains(&id);
|
||||||
|
let button_text = if is_added { fl!("added") } else { fl!("add") };
|
||||||
|
|
||||||
|
let add_button = widget::button::text(button_text).on_press_maybe(if is_added {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Message::SourceAdd(id))
|
||||||
|
});
|
||||||
|
|
||||||
|
let button = widget::settings::item::builder(description).control(add_button);
|
||||||
|
|
||||||
|
if indent {
|
||||||
|
container(button).padding([0, 0, 0, 16]).into()
|
||||||
|
} else {
|
||||||
|
button.into()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn special_character_key_view(&self, special_key: SpecialKey) -> cosmic::Element<'_, Message> {
|
fn special_character_key_view(&self, special_key: SpecialKey) -> cosmic::Element<'_, Message> {
|
||||||
|
|
@ -290,6 +460,30 @@ impl Page {
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_xkb_config(&mut self) {
|
||||||
|
let mut new_layout = String::new();
|
||||||
|
let mut new_variant = String::new();
|
||||||
|
|
||||||
|
for id in &self.active_layouts {
|
||||||
|
if let Some((locale, variant, _description)) = self.keyboard_layouts.get(*id) {
|
||||||
|
new_layout.push_str(locale);
|
||||||
|
new_layout.push(',');
|
||||||
|
new_variant.push_str(variant);
|
||||||
|
new_variant.push(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _excess_comma = new_layout.pop();
|
||||||
|
let _excess_comma = new_variant.pop();
|
||||||
|
|
||||||
|
self.xkb.layout = new_layout;
|
||||||
|
self.xkb.variant = new_variant;
|
||||||
|
|
||||||
|
if let Err(err) = self.config.set("xkb_config", &self.xkb) {
|
||||||
|
tracing::error!(?err, "Failed to set config 'xkb_config'");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl page::AutoBind<crate::pages::Message> for Page {
|
impl page::AutoBind<crate::pages::Message> for Page {
|
||||||
|
|
@ -299,20 +493,31 @@ impl page::AutoBind<crate::pages::Message> for Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input_sources() -> Section<crate::pages::Message> {
|
fn input_sources() -> Section<crate::pages::Message> {
|
||||||
// TODO desc
|
|
||||||
Section::default()
|
Section::default()
|
||||||
.title(fl!("keyboard-sources"))
|
.title(fl!("keyboard-sources"))
|
||||||
.view::<Page>(|_binder, page, section| {
|
.view::<Page>(|_binder, page, section| {
|
||||||
// TODO Need something more custom, with drag and drop
|
// TODO Need something more custom, with drag and drop
|
||||||
let mut section = settings::view_section(§ion.title);
|
let mut section = settings::view_section(§ion.title);
|
||||||
|
|
||||||
let expanded_source = page.expanded_source_popover.as_deref();
|
for id in &page.active_layouts {
|
||||||
for source in &page.sources {
|
if let Some((_locale, _variant, description)) = page.keyboard_layouts.get(*id) {
|
||||||
section = section.add(input_source(source, expanded_source));
|
section =
|
||||||
|
section.add(input_source(*id, description, page.expanded_source_popover));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section
|
let add_input_source = widget::button::standard(fl!("keyboard-sources", "add"))
|
||||||
.apply(cosmic::Element::from)
|
.on_press(Message::ShowInputSourcesContext);
|
||||||
|
|
||||||
|
widget::column::with_capacity(2)
|
||||||
|
.spacing(cosmic::theme::active().cosmic().space_xxs())
|
||||||
|
.push(section)
|
||||||
|
.push(
|
||||||
|
widget::container(add_input_source)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.align_x(iced::alignment::Horizontal::Right),
|
||||||
|
)
|
||||||
|
.apply(Element::from)
|
||||||
.map(crate::pages::Message::Keyboard)
|
.map(crate::pages::Message::Keyboard)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -364,10 +569,10 @@ fn keyboard_shortcuts() -> Section<crate::pages::Message> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn go_next_control<Msg: Clone + 'static>() -> cosmic::Element<'static, Msg> {
|
fn go_next_control<Msg: Clone + 'static>() -> cosmic::Element<'static, Msg> {
|
||||||
widget::row!(
|
widget::row::with_children(vec![
|
||||||
horizontal_space(Length::Fill),
|
widget::horizontal_space(Length::Fill).into(),
|
||||||
icon::from_name("go-next-symbolic").size(16).icon(),
|
icon::from_name("go-next-symbolic").size(16).icon().into(),
|
||||||
)
|
])
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
9
debian/control
vendored
9
debian/control
vendored
|
|
@ -21,6 +21,13 @@ Homepage: https://github.com/pop-os/cosmic-settings
|
||||||
|
|
||||||
Package: cosmic-settings
|
Package: cosmic-settings
|
||||||
Architecture: amd64 arm64
|
Architecture: amd64 arm64
|
||||||
Depends: ${misc:Depends}, ${shlibs:Depends}, cosmic-randr
|
Depends:
|
||||||
|
${misc:Depends},
|
||||||
|
${shlibs:Depends},
|
||||||
|
accountsservice,
|
||||||
|
cosmic-randr,
|
||||||
|
gettext,
|
||||||
|
iso-codes,
|
||||||
|
xkb-data,
|
||||||
Description: Settings application for the COSMIC desktop environment
|
Description: Settings application for the COSMIC desktop environment
|
||||||
Settings application for the COSMIC desktop environment
|
Settings application for the COSMIC desktop environment
|
||||||
|
|
|
||||||
|
|
@ -399,11 +399,14 @@ keyboard-sources = Input Sources
|
||||||
.settings = Settings
|
.settings = Settings
|
||||||
.view-layout = View keyboard layout
|
.view-layout = View keyboard layout
|
||||||
.remove = Remove
|
.remove = Remove
|
||||||
|
.add = Add input source
|
||||||
|
|
||||||
keyboard-special-char = Special Character Entry
|
keyboard-special-char = Special Character Entry
|
||||||
.alternate = Alternate characters key
|
.alternate = Alternate characters key
|
||||||
.compose = Compose key
|
.compose = Compose key
|
||||||
|
|
||||||
|
added = Added
|
||||||
|
|
||||||
## Input: Keyboard: Shortcuts
|
## Input: Keyboard: Shortcuts
|
||||||
|
|
||||||
keyboard-shortcuts = Keyboard Shortcuts
|
keyboard-shortcuts = Keyboard Shortcuts
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue