Skip to content
Open
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
109 changes: 67 additions & 42 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use cosmic::iced::runtime::core::window::Id as SurfaceId;
use cosmic::iced::{self, Rectangle, Size, Subscription};
use cosmic::widget;
use cosmic_config::{ConfigSet, CosmicConfigEntry};
use cosmic_greeter_daemon::{BgSource, CosmicCompConfig, UserData};
use cosmic_greeter_daemon::{BgSource, CosmicCompConfig, UserData, XkbConfig};
use std::collections::HashMap;
use std::sync::Arc;
use wayland_client::protocol::wl_output::WlOutput;
Expand Down Expand Up @@ -60,6 +60,7 @@ pub enum Message {
SessionLockEvent(SessionLockEvent),
Tick,
Tz(jiff::tz::TimeZone),
XkbConfigChanged(XkbConfig),
}

impl<M: From<Message> + Send + 'static> Common<M> {
Expand Down Expand Up @@ -195,52 +196,55 @@ impl<M: From<Message> + Send + 'static> Common<M> {
}
}

pub fn update_user_data(&mut self, user_data: &UserData) {
self.update_wallpapers(user_data);
pub fn refresh_active_layouts(&mut self, xkb_config: &XkbConfig) {
let Some(keyboard_layouts) = self.layouts_opt.as_ref() else {
return;
};

// From cosmic-applet-input-sources
if let Some(keyboard_layouts) = &self.layouts_opt
&& let Some(xkb_config) = &user_data.xkb_config_opt
{
self.active_layouts.clear();
let config_layouts = xkb_config.layout.split_terminator(',');
let config_variants = xkb_config
.variant
.split_terminator(',')
.chain(std::iter::repeat(""));
'outer: for (config_layout, config_variant) in config_layouts.zip(config_variants) {
for xkb_layout in keyboard_layouts.layouts() {
if config_layout != xkb_layout.name() {
continue;
}
if config_variant.is_empty() {
let active_layout = ActiveLayout {
description: xkb_layout.description().to_owned(),
layout: config_layout.to_owned(),
variant: config_variant.to_owned(),
};
self.active_layouts.push(active_layout);
continue 'outer;
}
self.active_layouts.clear();
let config_layouts = xkb_config.layout.split_terminator(',');
let config_variants = xkb_config
.variant
.split_terminator(',')
.chain(std::iter::repeat(""));
'outer: for (config_layout, config_variant) in config_layouts.zip(config_variants) {
for xkb_layout in keyboard_layouts.layouts() {
if config_layout != xkb_layout.name() {
continue;
}
if config_variant.is_empty() {
self.active_layouts.push(ActiveLayout {
description: xkb_layout.description().to_owned(),
layout: config_layout.to_owned(),
variant: config_variant.to_owned(),
});
continue 'outer;
}

let Some(xkb_variants) = xkb_layout.variants() else {
let Some(xkb_variants) = xkb_layout.variants() else {
continue;
};
for xkb_variant in xkb_variants {
if config_variant != xkb_variant.name() {
continue;
};
for xkb_variant in xkb_variants {
if config_variant != xkb_variant.name() {
continue;
}
let active_layout = ActiveLayout {
description: xkb_variant.description().to_owned(),
layout: config_layout.to_owned(),
variant: config_variant.to_owned(),
};
self.active_layouts.push(active_layout);
continue 'outer;
}
self.active_layouts.push(ActiveLayout {
description: xkb_variant.description().to_owned(),
layout: config_layout.to_owned(),
variant: config_variant.to_owned(),
});
continue 'outer;
}
}
tracing::info!("{:?}", self.active_layouts);
}
}

pub fn update_user_data(&mut self, user_data: &UserData) {
self.update_wallpapers(user_data);

// From cosmic-applet-input-sources
if let Some(xkb_config) = &user_data.xkb_config_opt {
self.refresh_active_layouts(xkb_config);
}
}

Expand Down Expand Up @@ -322,12 +326,33 @@ impl<M: From<Message> + Send + 'static> Common<M> {
Message::Tz(tz) => {
self.time.set_tz(tz);
}
Message::XkbConfigChanged(xkb_config) => {
self.refresh_active_layouts(&xkb_config);
}
}
Task::none()
}

pub fn subscription(&self) -> Subscription<Message> {
let mut subscriptions = Vec::with_capacity(3);
let mut subscriptions = Vec::with_capacity(4);

struct XkbConfigSubscription;
subscriptions.push(
cosmic_config::config_subscription(
std::any::TypeId::of::<XkbConfigSubscription>(),
"com.system76.CosmicComp".into(),
CosmicCompConfig::VERSION,
)
.map(|update: cosmic_config::Update<CosmicCompConfig>| {
if !update.errors.is_empty() {
tracing::warn!(
errors = ?update.errors,
"errors loading com.system76.CosmicComp"
);
}
Message::XkbConfigChanged(update.config.xkb_config)
}),
);

subscriptions.push(event::listen_with(|event, status, id| match event {
iced::Event::Keyboard(KeyEvent::KeyPressed {
Expand Down
30 changes: 29 additions & 1 deletion src/locker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,36 @@ impl App {
.width(Length::Fixed(240.0))
};

let layout_label = self.common.active_layouts.first().map(|l| {
// Layout codes like "us", "ru", "de" become "US"/"RU"/"DE".
// Cap at 2 chars so exotic codes like "latam"/"epo" don't
// make the button jump width between switches.
l.layout.chars().take(2).collect::<String>().to_uppercase()
});
// Match sibling bare-icon buttons in height (libcosmic icon default).
const ICON_SIZE: u16 = 16;
// Fixed width so "RU"/"EN" don't resize the button between switches.
const LABEL_WIDTH: f32 = 20.0;
let keyboard_button_content: Element<Message> = match layout_label {
Some(label) => widget::container(
iced::widget::row![
widget::icon::from_name("input-keyboard-symbolic").size(ICON_SIZE),
widget::container(widget::text(label).size(12))
.width(iced::Length::Fixed(LABEL_WIDTH))
.align_x(iced::Alignment::Center),
]
.spacing(4.0)
.align_y(iced::Alignment::Center),
)
.height(iced::Length::Fixed(f32::from(ICON_SIZE)))
.align_y(iced::Alignment::Center)
.into(),
None => widget::icon::from_name("input-keyboard-symbolic")
.size(ICON_SIZE)
.into(),
};
let mut input_button = widget::popover(
widget::button::custom(widget::icon::from_name("input-keyboard-symbolic"))
widget::button::custom(keyboard_button_content)
.padding(12.0)
.on_press(Message::DropdownToggle(Dropdown::Keyboard)),
)
Expand Down