From edcf29df9e38fdffc39bd67ee3aed0bbf648c7a6 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Wed, 8 Jan 2025 12:17:35 +0100 Subject: [PATCH] fix(input-sources): duplicating input sources --- cosmic-applet-input-sources/src/config.rs | 21 -- cosmic-applet-input-sources/src/lib.rs | 342 ++++++++++++++++++++-- cosmic-applet-input-sources/src/window.rs | 334 --------------------- 3 files changed, 312 insertions(+), 385 deletions(-) delete mode 100644 cosmic-applet-input-sources/src/config.rs delete mode 100644 cosmic-applet-input-sources/src/window.rs diff --git a/cosmic-applet-input-sources/src/config.rs b/cosmic-applet-input-sources/src/config.rs deleted file mode 100644 index a2516f27..00000000 --- a/cosmic-applet-input-sources/src/config.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2024 System76 -// SPDX-License-Identifier: GPL-3.0-only - -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 index 7f7f90b6..5af38c26 100644 --- a/cosmic-applet-input-sources/src/lib.rs +++ b/cosmic-applet-input-sources/src/lib.rs @@ -1,14 +1,31 @@ // Copyright 2024 System76 // SPDX-License-Identifier: GPL-3.0-only -use crate::window::Window; -use config::{Config, CONFIG_VERSION}; -use cosmic::{cosmic_config, cosmic_config::CosmicConfigEntry}; -mod config; -use cosmic_comp_config::CosmicCompConfig; -use window::Flags; mod localize; -mod window; + +use cosmic::iced::{Alignment, Length}; +use cosmic::{ + app::Core, + applet::{self}, + cosmic_config::{self, ConfigSet, CosmicConfigEntry}, + cosmic_theme::Spacing, + iced::{ + platform_specific::shell::commands::popup::{destroy_popup, get_popup}, + widget::{column, row}, + window::Id, + Limits, Task, + }, + iced_futures::Subscription, + iced_runtime::{core::window, Appearance}, + prelude::*, + theme, + widget::{self, horizontal_space, vertical_space}, +}; +use cosmic_comp_config::CosmicCompConfig; +use xkb_data::KeyboardLayout; + +pub const ID: &str = "com.system76.CosmicAppletInputSources"; + pub fn run() -> cosmic::iced::Result { localize::localize(); @@ -20,23 +37,6 @@ pub fn run() -> cosmic::iced::Result { } }; - 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)) => { - tracing::error!("errors loading config: {:?}", errs); - config - } - }; - (Some(config_handler), config) - } - Err(err) => { - tracing::error!("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) => { @@ -55,12 +55,294 @@ pub fn run() -> cosmic::iced::Result { } }; - let flags = Flags { + cosmic::applet::run::(Flags { comp_config, comp_config_handler, - config_handler, - config, - layouts, - }; - cosmic::applet::run::(flags) + layouts: layouts.layout_list.layout, + }) +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ActiveLayout { + layout: String, + description: String, + variant: String, +} + +pub struct Window { + core: Core, + popup: Option, + comp_config: CosmicCompConfig, + comp_config_handler: Option, + layouts: Vec, + active_layouts: Vec, +} + +#[derive(Clone, Debug)] +pub enum Message { + TogglePopup, + PopupClosed(Id), + CompConfig(Box), + SetActiveLayout(usize), + KeyboardSettings, +} + +#[derive(Debug)] +pub struct Flags { + pub comp_config: CosmicCompConfig, + pub comp_config_handler: Option, + pub layouts: Vec, +} + +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, Task>) { + let window = Window { + comp_config_handler: flags.comp_config_handler, + layouts: flags.layouts, + core, + popup: None, + comp_config: flags.comp_config, + active_layouts: Vec::new(), + }; + (window, Task::none()) + } + + fn on_close_requested(&self, id: window::Id) -> Option { + Some(Message::PopupClosed(id)) + } + + fn update(&mut self, message: Self::Message) -> Task> { + match message { + 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( + self.core.main_window_id().unwrap(), + new_id, + None, + None, + None, + ); + popup_settings.positioner.size_limits = Limits::NONE + .max_width(372.0) + .min_width(300.0) + .min_height(1.) + .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"); + tokio::spawn(cosmic::process::spawn(cmd)); + } + + Message::SetActiveLayout(pos) => { + if pos == 0 { + return Task::none(); + } + + self.active_layouts.swap(0, pos); + 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) + { + tracing::error!("Failed to set config 'xkb_config' {err}"); + } + } + } + } + + Task::none() + } + + fn view(&self) -> Element { + let input_source_text = self.core.applet.text( + self.active_layouts + .first() + .map_or("", |l| l.layout.as_str()), + ); + + cosmic::widget::button::custom( + row!( + column!( + input_source_text, + horizontal_space().width(Length::Fixed( + (self.core.applet.suggested_size(true).0 + + 2 * self.core.applet.suggested_padding(true)) + as f32 + )) + ) + .width(Length::Shrink) + .height(Length::Shrink) + .align_x(Alignment::Center), + vertical_space().height(Length::Fixed( + (self.core.applet.suggested_size(true).1 + + 2 * self.core.applet.suggested_padding(true)) as f32 + )) + ) + .align_y(Alignment::Center) + .width(Length::Shrink) + .height(Length::Shrink), + ) + .on_press_down(Message::TogglePopup) + .class(cosmic::theme::Button::AppletIcon) + .into() + } + + fn view_window(&self, _id: Id) -> Element { + let Spacing { + space_xxs, space_s, .. + } = theme::active().cosmic().spacing; + + let mut content_list = + widget::column::with_capacity(4 + self.active_layouts.len()).padding([8, 0]); + for (id, layout) in self.active_layouts.iter().enumerate() { + let group = widget::column::with_capacity(2) + .push(widget::text::body(layout.description.clone())) + .push(widget::text::caption(layout.layout.clone())); + content_list = content_list + .push(applet::menu_button(group).on_press(Message::SetActiveLayout(id))); + } + if !self.active_layouts.is_empty() { + content_list = content_list.push( + applet::padded_control(widget::divider::horizontal::default()) + .padding([space_xxs, space_s]) + .apply(Element::from), + ); + } + content_list = content_list.push( + applet::menu_button(widget::text::body(fl!("keyboard-settings"))) + .on_press(Message::KeyboardSettings), + ); + + self.core + .applet + .popup_container(content_list) + .limits( + Limits::NONE + .min_height(1.) + .max_height(1080.) + .min_width(1.) + .max_width(372.), + ) + .into() + } + + fn subscription(&self) -> Subscription { + self.core + .watch_config("com.system76.CosmicComp") + .map(|update| { + if !update.errors.is_empty() { + tracing::error!( + "errors loading config {:?}: {:?}", + update.keys, + update.errors + ); + } + Message::CompConfig(Box::new(update.config)) + }) + } + + fn style(&self) -> Option { + 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("")); + + 'outer: for (layout, variant) in layouts.zip(variants) { + println!("{} : {}", layout, variant); + for xkb_layout in &self.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 'outer; + } + + 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); + continue 'outer; + } + } + } + + active_layouts + } } diff --git a/cosmic-applet-input-sources/src/window.rs b/cosmic-applet-input-sources/src/window.rs deleted file mode 100644 index 955936d9..00000000 --- a/cosmic-applet-input-sources/src/window.rs +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2024 System76 -// SPDX-License-Identifier: GPL-3.0-only - -use crate::config::{Config, CONFIG_VERSION}; -#[allow(unused_imports)] -use crate::fl; -#[allow(unused_imports)] -use cosmic::iced::{Alignment, Length}; -use cosmic::{ - app::Core, - applet::{self}, - cosmic_config::{self, ConfigSet}, - cosmic_theme::Spacing, - iced::{ - platform_specific::shell::commands::popup::{destroy_popup, get_popup}, - widget::{column, row}, - window::Id, - Limits, Task, - }, - iced_futures::Subscription, - iced_runtime::{core::window, Appearance}, - prelude::*, - theme, - widget::{self, horizontal_space, vertical_space}, -}; -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(Debug)] -pub struct Flags { - pub config_handler: Option, - pub config: Config, - pub comp_config: CosmicCompConfig, - pub comp_config_handler: Option, - pub layouts: KeyboardLayouts, -} - -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, Task>) { - let window = Window { - comp_config_handler: flags.comp_config_handler, - layouts: flags.layouts, - core, - config: flags.config, - config_handler: flags.config_handler, - popup: None, - comp_config: flags.comp_config, - active_layouts: Vec::new(), - }; - (window, Task::none()) - } - - fn on_close_requested(&self, id: window::Id) -> Option { - Some(Message::PopupClosed(id)) - } - - fn update(&mut self, message: Self::Message) -> Task> { - 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( - self.core.main_window_id().unwrap(), - new_id, - None, - None, - None, - ); - popup_settings.positioner.size_limits = Limits::NONE - .max_width(372.0) - .min_width(300.0) - .min_height(1.) - .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"); - tokio::spawn(cosmic::process::spawn(cmd)); - } - Message::SetActiveLayout(active_layout) => { - let Some(i) = self - .active_layouts - .iter() - .position(|layout| layout == &active_layout) - else { - return Task::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) - { - tracing::error!("Failed to set config 'xkb_config' {err}"); - } - } - } - } - Task::none() - } - - fn view(&self) -> Element { - let input_source_text = self.core.applet.text( - self.active_layouts - .first() - .map_or(String::new(), |l| l.layout.clone()), - ); - - cosmic::widget::button::custom( - row!( - column!( - input_source_text, - horizontal_space().width(Length::Fixed( - (self.core.applet.suggested_size(true).0 - + 2 * self.core.applet.suggested_padding(true)) - as f32 - )) - ) - .width(Length::Shrink) - .height(Length::Shrink) - .align_x(Alignment::Center), - vertical_space().height(Length::Fixed( - (self.core.applet.suggested_size(true).1 - + 2 * self.core.applet.suggested_padding(true)) as f32 - )) - ) - .align_y(Alignment::Center) - .width(Length::Shrink) - .height(Length::Shrink), - ) - .on_press_down(Message::TogglePopup) - .class(cosmic::theme::Button::AppletIcon) - .into() - } - - fn view_window(&self, _id: Id) -> Element { - let Spacing { - space_xxs, space_s, .. - } = theme::active().cosmic().spacing; - - 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::body(layout.description.clone())) - .push(widget::text::caption(layout.layout.clone())); - content_list = content_list.push( - applet::menu_button(group).on_press(Message::SetActiveLayout(layout.clone())), - ); - } - if !self.active_layouts.is_empty() { - content_list = content_list.push( - applet::padded_control(widget::divider::horizontal::default()) - .padding([space_xxs, space_s]) - .apply(Element::from), - ); - } - content_list = content_list.push( - applet::menu_button(widget::text::body(fl!("keyboard-settings"))) - .on_press(Message::KeyboardSettings), - ); - - self.core - .applet - .popup_container(content_list) - .limits( - Limits::NONE - .min_height(1.) - .max_height(1080.) - .min_width(1.) - .max_width(372.), - ) - .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() { - tracing::error!( - "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() { - tracing::error!( - "errors loading config {:?}: {:?}", - update.keys, - update.errors - ); - } - Message::CompConfig(update.config) - }); - Subscription::batch(vec![config, xbg_config]) - } - - fn style(&self) -> Option { - 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, -}