From a1ad3c5f87dc5ef1d22cf3cacf00866b15cf1ea6 Mon Sep 17 00:00:00 2001 From: Dominic Gerhauser Date: Thu, 11 Apr 2024 11:24:58 +0200 Subject: [PATCH] feat: add input sources applet --- Cargo.lock | 110 ++++--- Cargo.toml | 1 + cosmic-applet-input-sources/Cargo.toml | 20 ++ ....system76.CosmicAppletInputSources.desktop | 13 + cosmic-applet-input-sources/i18n.toml | 4 + .../i18n/en/cosmic_applet_input_sources.ftl | 2 + cosmic-applet-input-sources/src/config.rs | 15 + cosmic-applet-input-sources/src/lib.rs | 53 ++++ cosmic-applet-input-sources/src/localize.rs | 36 +++ cosmic-applet-input-sources/src/main.rs | 3 + cosmic-applet-input-sources/src/window.rs | 277 ++++++++++++++++++ cosmic-applet-tiling/Cargo.toml | 1 + cosmic-applets/Cargo.toml | 3 +- cosmic-applets/src/main.rs | 1 + debian/links | 3 +- justfile | 2 +- 16 files changed, 508 insertions(+), 36 deletions(-) create mode 100644 cosmic-applet-input-sources/Cargo.toml create mode 100644 cosmic-applet-input-sources/data/com.system76.CosmicAppletInputSources.desktop create mode 100644 cosmic-applet-input-sources/i18n.toml create mode 100644 cosmic-applet-input-sources/i18n/en/cosmic_applet_input_sources.ftl create mode 100644 cosmic-applet-input-sources/src/config.rs create mode 100644 cosmic-applet-input-sources/src/lib.rs create mode 100644 cosmic-applet-input-sources/src/localize.rs create mode 100644 cosmic-applet-input-sources/src/main.rs create mode 100644 cosmic-applet-input-sources/src/window.rs diff --git a/Cargo.lock b/Cargo.lock index 157fe375..a295cc3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -442,9 +442,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -643,9 +643,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.96" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg-expr" @@ -962,6 +962,26 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "cosmic-applet-input-sources" +version = "0.1.0" +dependencies = [ + "cosmic-comp-config", + "cosmic-time", + "i18n-embed", + "i18n-embed-fl", + "libcosmic", + "libpulse-binding", + "once_cell", + "rust-embed", + "serde", + "tokio", + "tracing", + "tracing-log", + "tracing-subscriber", + "xkb-data", +] + [[package]] name = "cosmic-applet-minimize" version = "0.1.1" @@ -1071,6 +1091,7 @@ dependencies = [ "tracing", "tracing-log", "tracing-subscriber", + "xkb-data", ] [[package]] @@ -1116,6 +1137,7 @@ dependencies = [ "cosmic-applet-audio", "cosmic-applet-battery", "cosmic-applet-bluetooth", + "cosmic-applet-input-sources", "cosmic-applet-minimize", "cosmic-applet-network", "cosmic-applet-notifications", @@ -1227,7 +1249,7 @@ dependencies = [ [[package]] name = "cosmic-panel-config" version = "0.1.0" -source = "git+https://github.com/pop-os/cosmic-panel#d1d656d6b2a4faaee34d72ed86bc0073dcd4da89" +source = "git+https://github.com/pop-os/cosmic-panel#683a204ef81bd17c2b35264521e5145df3518ee5" dependencies = [ "anyhow", "cosmic-config", @@ -1272,7 +1294,7 @@ dependencies = [ "rangemap", "rustc-hash", "rustybuzz", - "self_cell 1.0.3", + "self_cell 1.0.4", "swash", "sys-locale", "ttf-parser", @@ -2011,9 +2033,9 @@ checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" [[package]] name = "fluent" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" +checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a" dependencies = [ "fluent-bundle", "unic-langid", @@ -2021,9 +2043,9 @@ dependencies = [ [[package]] name = "fluent-bundle" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" dependencies = [ "fluent-langneg", "fluent-syntax", @@ -2046,9 +2068,9 @@ dependencies = [ [[package]] name = "fluent-syntax" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ "thiserror", ] @@ -2351,9 +2373,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -3046,9 +3068,9 @@ dependencies = [ [[package]] name = "intl-memoizer" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" dependencies = [ "type-map", "unic-langid", @@ -3720,9 +3742,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -3743,9 +3765,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -4617,14 +4639,14 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" dependencies = [ - "self_cell 1.0.3", + "self_cell 1.0.4", ] [[package]] name = "self_cell" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "serde" @@ -4635,6 +4657,18 @@ dependencies = [ "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]] name = "serde_derive" version = "1.0.200" @@ -4798,7 +4832,7 @@ dependencies = [ [[package]] name = "smithay-clipboard" version = "0.8.0" -source = "git+https://github.com/pop-os/smithay-clipboard?tag=pop-dnd-4#eefa50c3df5135d98df7f4192e2e9b07eeafe56b" +source = "git+https://github.com/pop-os/smithay-clipboard?tag=pop-dnd-4#9b995a33a88c496a90259dd207367a998c95ac98" dependencies = [ "libc", "raw-window-handle 0.6.1", @@ -5295,7 +5329,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.7", + "winnow 0.6.8", ] [[package]] @@ -5367,9 +5401,9 @@ checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "type-map" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ "rustc-hash", ] @@ -6167,9 +6201,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ "memchr", ] @@ -6226,6 +6260,16 @@ dependencies = [ "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]] name = "xkbcommon" version = "0.7.0" @@ -6358,18 +6402,18 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index d67c9e03..7c8b6fb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ members = [ "cosmic-applet-time", "cosmic-applet-workspaces", "cosmic-panel-button", + "cosmic-applet-input-sources", ] resolver = "2" diff --git a/cosmic-applet-input-sources/Cargo.toml b/cosmic-applet-input-sources/Cargo.toml new file mode 100644 index 00000000..6dd90553 --- /dev/null +++ b/cosmic-applet-input-sources/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "cosmic-applet-input-sources" +version = "0.1.0" +edition = "2021" + +[dependencies] +cosmic-time.workspace = true +cosmic-comp-config = { git = "https://github.com/pop-os/cosmic-comp.git", rev = "5eb5af4" } +i18n-embed-fl.workspace = true +i18n-embed.workspace = true +libcosmic.workspace = true +libpulse-binding = "2.28.1" +rust-embed.workspace = true +tokio = { version = "1.36.0", features=["full"] } +tracing-log.workspace = true +tracing-subscriber.workspace = true +tracing.workspace = true +serde = { version = "1.0.197", features = ["derive"] } +once_cell = "1.19.0" +xkb-data = "0.1.0" diff --git a/cosmic-applet-input-sources/data/com.system76.CosmicAppletInputSources.desktop b/cosmic-applet-input-sources/data/com.system76.CosmicAppletInputSources.desktop new file mode 100644 index 00000000..f1cbbfda --- /dev/null +++ b/cosmic-applet-input-sources/data/com.system76.CosmicAppletInputSources.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Name=Cosmic Applet Input Sources +Comment=Applet for Cosmic Panel +Type=Application +Exec=cosmic-applet-input-sources +Terminal=false +Categories=Cosmic;Iced; +Keywords=Cosmic;Iced; +# Translators: Do NOT translate or transliterate this text (this is an icon file name)! +Icon=com.system76.CosmicAppletInputSources +StartupNotify=true +NoDisplay=true +X-CosmicApplet=true \ No newline at end of file diff --git a/cosmic-applet-input-sources/i18n.toml b/cosmic-applet-input-sources/i18n.toml new file mode 100644 index 00000000..05c50ba2 --- /dev/null +++ b/cosmic-applet-input-sources/i18n.toml @@ -0,0 +1,4 @@ +fallback_language = "en" + +[fluent] +assets_dir = "i18n" \ No newline at end of file diff --git a/cosmic-applet-input-sources/i18n/en/cosmic_applet_input_sources.ftl b/cosmic-applet-input-sources/i18n/en/cosmic_applet_input_sources.ftl new file mode 100644 index 00000000..2c5c8eab --- /dev/null +++ b/cosmic-applet-input-sources/i18n/en/cosmic_applet_input_sources.ftl @@ -0,0 +1,2 @@ +show-keyboard-layout = Show Keyboard Layout... +keyboard-settings = Keyboard Settings... diff --git a/cosmic-applet-input-sources/src/config.rs b/cosmic-applet-input-sources/src/config.rs new file mode 100644 index 00000000..c2a9b1f1 --- /dev/null +++ b/cosmic-applet-input-sources/src/config.rs @@ -0,0 +1,15 @@ +use cosmic::cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry}; +use cosmic_comp_config::XkbConfig; +use serde::{Deserialize, Serialize}; +pub const CONFIG_VERSION: u64 = 1; +#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)] +pub struct Config {} +impl Default for Config { + fn default() -> Self { + Self {} + } +} +#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, PartialEq, Serialize, Default)] +pub struct CosmicCompConfig { + pub xkb_config: XkbConfig, +} diff --git a/cosmic-applet-input-sources/src/lib.rs b/cosmic-applet-input-sources/src/lib.rs new file mode 100644 index 00000000..f7c81f47 --- /dev/null +++ b/cosmic-applet-input-sources/src/lib.rs @@ -0,0 +1,53 @@ +use crate::window::Window; +use config::{Config, CONFIG_VERSION}; +use cosmic::cosmic_config; +use cosmic::cosmic_config::CosmicConfigEntry; +mod config; +use cosmic_comp_config::CosmicCompConfig; +use window::Flags; +mod localize; +mod window; +pub fn run() -> cosmic::iced::Result { + localize::localize(); + let (config_handler, config) = match cosmic_config::Config::new(window::ID, CONFIG_VERSION) { + Ok(config_handler) => { + let config = match Config::get_entry(&config_handler) { + Ok(ok) => ok, + Err((errs, config)) => { + eprintln!("errors loading config: {:?}", errs); + config + } + }; + (Some(config_handler), config) + } + Err(err) => { + eprintln!("failed to create config handler: {}", err); + (None, Config::default()) + } + }; + let (comp_config_handler, comp_config) = + match cosmic_config::Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION) { + Ok(config_handler) => { + let config = match CosmicCompConfig::get_entry(&config_handler) { + Ok(ok) => ok, + Err((errs, config)) => { + eprintln!("errors loading config: {:?}", errs); + config + } + }; + (Some(config_handler), config) + } + Err(err) => { + eprintln!("failed to create config handler: {}", err); + (None, CosmicCompConfig::default()) + } + }; + + let flags = Flags { + comp_config, + comp_config_handler, + config_handler: config_handler, + config: config, + }; + cosmic::applet::run::(true, flags) +} diff --git a/cosmic-applet-input-sources/src/localize.rs b/cosmic-applet-input-sources/src/localize.rs new file mode 100644 index 00000000..8ff0c87b --- /dev/null +++ b/cosmic-applet-input-sources/src/localize.rs @@ -0,0 +1,36 @@ +use i18n_embed::{ + fluent::{fluent_language_loader, FluentLanguageLoader}, + DefaultLocalizer, LanguageLoader, Localizer, +}; +use once_cell::sync::Lazy; +use rust_embed::RustEmbed; +#[derive(RustEmbed)] +#[folder = "i18n/"] +struct Localizations; +pub static LANGUAGE_LOADER: Lazy = Lazy::new(|| { + let loader: FluentLanguageLoader = fluent_language_loader!(); + loader + .load_fallback_language(&Localizations) + .expect("Error while loading fallback language"); + loader +}); +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id) + }}; + ($message_id:literal, $($args:expr),*) => {{ + i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} +// Get the `Localizer` to be used for localizing this library. +pub fn localizer() -> Box { + Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations)) +} +pub fn localize() { + let localizer = localizer(); + let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages(); + if let Err(error) = localizer.select(&requested_languages) { + eprintln!("Error while loading language for App List {}", error); + } +} diff --git a/cosmic-applet-input-sources/src/main.rs b/cosmic-applet-input-sources/src/main.rs new file mode 100644 index 00000000..860fa906 --- /dev/null +++ b/cosmic-applet-input-sources/src/main.rs @@ -0,0 +1,3 @@ +fn main() -> cosmic::iced::Result { + cosmic_applet_input_sources::run() +} diff --git a/cosmic-applet-input-sources/src/window.rs b/cosmic-applet-input-sources/src/window.rs new file mode 100644 index 00000000..7e94073c --- /dev/null +++ b/cosmic-applet-input-sources/src/window.rs @@ -0,0 +1,277 @@ +use crate::config::{Config, CONFIG_VERSION}; +#[allow(unused_imports)] +use crate::fl; +use cosmic::app::Core; +use cosmic::applet::{self}; +use cosmic::cosmic_config::{self, ConfigSet}; +use cosmic::iced::wayland::popup::{destroy_popup, get_popup}; +use cosmic::iced::window::Id; +#[allow(unused_imports)] +use cosmic::iced::{alignment, Alignment, Length}; +use cosmic::iced::{Command, Limits}; +use cosmic::iced_futures::Subscription; +use cosmic::iced_runtime::core::window; +use cosmic::iced_style::application; +use cosmic::prelude::*; +use cosmic::widget; +use cosmic_comp_config::CosmicCompConfig; +use xkb_data::KeyboardLayouts; +pub const ID: &str = "com.system76.CosmicAppletInputSources"; + +pub struct Window { + core: Core, + popup: Option, + config: Config, + #[allow(dead_code)] + config_handler: Option, + comp_config: CosmicCompConfig, + comp_config_handler: Option, + layouts: KeyboardLayouts, + active_layouts: Vec, +} +#[derive(Clone, Debug)] +pub enum Message { + Config(Config), + TogglePopup, + PopupClosed(Id), + CompConfig(CosmicCompConfig), + SetActiveLayout(ActiveLayout), + KeyboardSettings, +} +#[derive(Clone, Debug)] +pub struct Flags { + pub config_handler: Option, + pub config: Config, + pub comp_config: CosmicCompConfig, + pub comp_config_handler: Option, +} +impl cosmic::Application for Window { + type Executor = cosmic::SingleThreadExecutor; + type Flags = Flags; + type Message = Message; + const APP_ID: &'static str = ID; + fn core(&self) -> &Core { + &self.core + } + fn core_mut(&mut self) -> &mut Core { + &mut self.core + } + fn init( + core: Core, + flags: Self::Flags, + ) -> (Self, Command>) { + let layouts = xkb_data::keyboard_layouts().unwrap(); + let window = Window { + comp_config_handler: flags.comp_config_handler, + layouts, + core, + config: flags.config, + config_handler: flags.config_handler, + popup: None, + comp_config: flags.comp_config, + active_layouts: Vec::new(), + }; + (window, Command::none()) + } + fn on_close_requested(&self, id: window::Id) -> Option { + Some(Message::PopupClosed(id)) + } + fn update(&mut self, message: Self::Message) -> Command> { + match message { + Message::Config(config) => self.config = config, + Message::TogglePopup => { + return if let Some(p) = self.popup.take() { + destroy_popup(p) + } else { + let new_id = Id::unique(); + self.popup.replace(new_id); + let mut popup_settings = + self.core + .applet + .get_popup_settings(Id::MAIN, new_id, None, None, None); + popup_settings.positioner.size_limits = Limits::NONE + .max_width(372.0) + .min_width(300.0) + .min_height(200.0) + .max_height(1080.0); + get_popup(popup_settings) + } + } + Message::PopupClosed(id) => { + if self.popup.as_ref() == Some(&id) { + self.popup = None; + } + } + Message::CompConfig(config) => { + self.comp_config = config; + self.active_layouts = self.update_xkb(); + } + Message::KeyboardSettings => { + let mut cmd = std::process::Command::new("cosmic-settings"); + cmd.arg("keyboard"); + cosmic::process::spawn(cmd); + } + Message::SetActiveLayout(active_layout) => { + let Some(i) = self + .active_layouts + .iter() + .position(|layout| layout == &active_layout) + else { + return Command::none(); + }; + + self.active_layouts.swap(0, i); + let mut new_layout = String::new(); + let mut new_variant = String::new(); + + for layout in &self.active_layouts { + new_layout.push_str(&layout.layout); + new_layout.push(','); + new_variant.push_str(&layout.variant); + new_variant.push(','); + } + let _excess_comma = new_layout.pop(); + let _excess_comma = new_variant.pop(); + + self.comp_config.xkb_config.layout = new_layout; + self.comp_config.xkb_config.variant = new_variant; + if let Some(comp_config_handler) = &self.comp_config_handler { + if let Err(err) = + comp_config_handler.set("xkb_config", &self.comp_config.xkb_config) + { + eprint!("Failed to set config 'xkb_config' {err}"); + } + } + } + } + Command::none() + } + fn view(&self) -> Element { + let suggested = self.core.applet.suggested_padding(); + widget::button( + widget::text( + self.active_layouts + .first() + .map_or(String::new(), |l| l.layout.clone()), + ) + .size(14), + ) + .style(cosmic::theme::Button::AppletIcon) + .padding([suggested / 2, suggested]) + .on_press(Message::TogglePopup) + .into() + } + fn view_window(&self, _id: Id) -> Element { + let mut content_list = + widget::column::with_capacity(4 + self.active_layouts.len()).padding([8, 0]); + for layout in &self.active_layouts { + let group = widget::column::with_capacity(2) + .push(widget::text(layout.description.clone()).size(16)) + .push(widget::text(layout.layout.clone()).size(14)); + content_list = content_list.push( + applet::menu_button(group).on_press(Message::SetActiveLayout(layout.clone())), + ); + } + content_list = content_list.extend( + [ + applet::padded_control(widget::divider::horizontal::default()).apply(Element::from), + applet::menu_button(widget::text(fl!("show-keyboard-layout"))).into(), + applet::padded_control(widget::divider::horizontal::default()).into(), + applet::menu_button(widget::text(fl!("keyboard-settings"))) + .on_press(Message::KeyboardSettings) + .into(), + ] + .into_iter(), + ); + + self.core.applet.popup_container(content_list).into() + } + fn subscription(&self) -> Subscription { + struct ConfigSubscription; + let config = cosmic_config::config_subscription( + std::any::TypeId::of::(), + Self::APP_ID.into(), + CONFIG_VERSION, + ) + .map(|update| { + if !update.errors.is_empty() { + eprintln!( + "errors loading config {:?}: {:?}", + update.keys, update.errors + ); + } + Message::Config(update.config) + }); + let xbg_config = self + .core + .watch_config("com.system76.CosmicComp") + .map(|update| { + if !update.errors.is_empty() { + eprintln!( + "errors loading config {:?}: {:?}", + update.keys, update.errors + ); + } + Message::CompConfig(update.config) + }); + Subscription::batch(vec![config, xbg_config]) + } + + fn style(&self) -> Option<::Style> { + Some(cosmic::applet::style()) + } +} +impl Window { + fn update_xkb(&self) -> Vec { + let mut active_layouts = Vec::new(); + let xkb = &self.comp_config.xkb_config; + + let layouts = xkb.layout.split_terminator(','); + + let variants = xkb + .variant + .split_terminator(',') + .chain(std::iter::repeat("")); + + for (layout, variant) in layouts.zip(variants) { + println!("{} : {}", layout, variant); + for xkb_layout in self.layouts.layouts() { + if layout != xkb_layout.name() { + continue; + } + if variant.is_empty() { + let active_layout = ActiveLayout { + description: xkb_layout.description().to_owned(), + layout: layout.to_owned(), + variant: variant.to_owned(), + }; + active_layouts.push(active_layout); + + continue; + } + + let Some(xkb_variants) = xkb_layout.variants() else { + continue; + }; + for xkb_variant in xkb_variants { + if variant != xkb_variant.name() { + continue; + } + let active_layout = ActiveLayout { + description: xkb_variant.description().to_owned(), + layout: layout.to_owned(), + variant: variant.to_owned(), + }; + active_layouts.push(active_layout); + } + } + } + active_layouts + } +} +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ActiveLayout { + layout: String, + description: String, + variant: String, +} diff --git a/cosmic-applet-tiling/Cargo.toml b/cosmic-applet-tiling/Cargo.toml index dafc4d57..bd2a9cb9 100644 --- a/cosmic-applet-tiling/Cargo.toml +++ b/cosmic-applet-tiling/Cargo.toml @@ -21,3 +21,4 @@ tokio = { version = "1.36.0", features = ["sync", "rt"] } tracing-log.workspace = true tracing-subscriber.workspace = true tracing.workspace = true +xkb-data = "0.1.0" diff --git a/cosmic-applets/Cargo.toml b/cosmic-applets/Cargo.toml index 70965bf2..a5712b89 100644 --- a/cosmic-applets/Cargo.toml +++ b/cosmic-applets/Cargo.toml @@ -16,7 +16,8 @@ cosmic-applet-status-area = { path = "../cosmic-applet-status-area" } cosmic-applet-tiling = { path = "../cosmic-applet-tiling" } cosmic-applet-time = { path = "../cosmic-applet-time" } cosmic-applet-workspaces = { path = "../cosmic-applet-workspaces" } +cosmic-applet-input-sources = { path = "../cosmic-applet-input-sources"} libcosmic.workspace = true tracing.workspace = true tracing-subscriber.workspace = true -tracing-log.workspace = true \ No newline at end of file +tracing-log.workspace = true diff --git a/cosmic-applets/src/main.rs b/cosmic-applets/src/main.rs index 389ce2d3..e2f041b7 100644 --- a/cosmic-applets/src/main.rs +++ b/cosmic-applets/src/main.rs @@ -26,6 +26,7 @@ fn main() -> cosmic::iced::Result { "cosmic-applet-tiling" => cosmic_applet_tiling::run(), "cosmic-applet-time" => cosmic_applet_time::run(), "cosmic-applet-workspaces" => cosmic_applet_workspaces::run(), + "cosmic-applet-input-sources" => cosmic_applet_input_sources::run(), _ => return Ok(()), } } diff --git a/debian/links b/debian/links index 3131e067..e7fcab0a 100644 --- a/debian/links +++ b/debian/links @@ -9,4 +9,5 @@ /usr/bin/cosmic-applets /usr/bin/cosmic-applet-status-area /usr/bin/cosmic-applets /usr/bin/cosmic-applet-tiling /usr/bin/cosmic-applets /usr/bin/cosmic-applet-time -/usr/bin/cosmic-applets /usr/bin/cosmic-applet-workspaces \ No newline at end of file +/usr/bin/cosmic-applets /usr/bin/cosmic-applet-workspaces +/usr/bin/cosmic-applets /usr/bin/cosmic-applet-input-sources diff --git a/justfile b/justfile index a7d196fc..3840ff72 100644 --- a/justfile +++ b/justfile @@ -51,7 +51,7 @@ _install_applet id name: (_install_icons name) \ _install_button id name: (_install_icons name) (_install_desktop name + '/data/' + id + '.desktop') # Installs files into the system -install: (_install_bin 'cosmic-applets') (_install_applet 'com.system76.CosmicAppList' 'cosmic-app-list') (_install_default_schema 'cosmic-app-list') (_install_applet 'com.system76.CosmicAppletAudio' 'cosmic-applet-audio') (_install_applet 'com.system76.CosmicAppletBattery' 'cosmic-applet-battery') (_install_applet 'com.system76.CosmicAppletBluetooth' 'cosmic-applet-bluetooth') (_install_applet 'com.system76.CosmicAppletMinimize' 'cosmic-applet-minimize') (_install_applet 'com.system76.CosmicAppletNetwork' 'cosmic-applet-network') (_install_applet 'com.system76.CosmicAppletNotifications' 'cosmic-applet-notifications') (_install_applet 'com.system76.CosmicAppletPower' 'cosmic-applet-power') (_install_applet 'com.system76.CosmicAppletStatusArea' 'cosmic-applet-status-area') (_install_applet 'com.system76.CosmicAppletTiling' 'cosmic-applet-tiling') (_install_applet 'com.system76.CosmicAppletTime' 'cosmic-applet-time') (_install_applet 'com.system76.CosmicAppletWorkspaces' 'cosmic-applet-workspaces') (_install_bin 'cosmic-panel-button') (_install_button 'com.system76.CosmicPanelAppButton' 'cosmic-panel-app-button') (_install_button 'com.system76.CosmicPanelLauncherButton' 'cosmic-panel-launcher-button') (_install_button 'com.system76.CosmicPanelWorkspacesButton' 'cosmic-panel-workspaces-button') +install: (_install_bin 'cosmic-applets') (_install_applet 'com.system76.CosmicAppList' 'cosmic-app-list') (_install_default_schema 'cosmic-app-list') (_install_applet 'com.system76.CosmicAppletAudio' 'cosmic-applet-audio') (_install_applet 'com.system76.CosmicAppletInputSources' 'cosmic-applet-input-sources') (_install_applet 'com.system76.CosmicAppletBattery' 'cosmic-applet-battery') (_install_applet 'com.system76.CosmicAppletBluetooth' 'cosmic-applet-bluetooth') (_install_applet 'com.system76.CosmicAppletMinimize' 'cosmic-applet-minimize') (_install_applet 'com.system76.CosmicAppletNetwork' 'cosmic-applet-network') (_install_applet 'com.system76.CosmicAppletNotifications' 'cosmic-applet-notifications') (_install_applet 'com.system76.CosmicAppletPower' 'cosmic-applet-power') (_install_applet 'com.system76.CosmicAppletStatusArea' 'cosmic-applet-status-area') (_install_applet 'com.system76.CosmicAppletTiling' 'cosmic-applet-tiling') (_install_applet 'com.system76.CosmicAppletTime' 'cosmic-applet-time') (_install_applet 'com.system76.CosmicAppletWorkspaces' 'cosmic-applet-workspaces') (_install_bin 'cosmic-panel-button') (_install_button 'com.system76.CosmicPanelAppButton' 'cosmic-panel-app-button') (_install_button 'com.system76.CosmicPanelLauncherButton' 'cosmic-panel-launcher-button') (_install_button 'com.system76.CosmicPanelWorkspacesButton' 'cosmic-panel-workspaces-button') # Vendor Cargo dependencies locally vendor: