diff --git a/cosmic-settings/src/app.rs b/cosmic-settings/src/app.rs index dd96440..20676e9 100644 --- a/cosmic-settings/src/app.rs +++ b/cosmic-settings/src/app.rs @@ -60,6 +60,7 @@ impl SettingsApp { PageCommands::DesktopPanel => self.pages.page_id::(), PageCommands::Displays => self.pages.page_id::(), PageCommands::Firmware => self.pages.page_id::(), + PageCommands::Keyboard => self.pages.page_id::(), PageCommands::Mouse => self.pages.page_id::(), PageCommands::Network => None, PageCommands::Notifications => self.pages.page_id::(), @@ -316,6 +317,12 @@ impl cosmic::Application for SettingsApp { } } + crate::pages::Message::Keyboard(message) => { + if let Some(page) = self.pages.page_mut::() { + return page.update(message).map(cosmic::app::Message::App); + } + } + crate::pages::Message::Input(message) => { if let Some(page) = self.pages.page_mut::() { return page.update(message).map(cosmic::app::Message::App); @@ -498,17 +505,17 @@ impl cosmic::Application for SettingsApp { }); } - if let Some(Some(page)) = (id == *keyboard::ADD_INPUT_SOURCE_DIALOGUE_ID) - .then(|| self.pages.page::()) - { - return page.add_input_source_view(); - } + // if let Some(Some(page)) = (id == *keyboard::ADD_INPUT_SOURCE_DIALOGUE_ID) + // .then(|| self.pages.page::()) + // { + // return page.add_input_source_view(); + // } - if let Some(Some(page)) = (id == *keyboard::SPECIAL_CHARACTER_DIALOGUE_ID) - .then(|| self.pages.page::()) - { - return page.special_character_key_view(); - } + // if let Some(Some(page)) = (id == *keyboard::SPECIAL_CHARACTER_DIALOGUE_ID) + // .then(|| self.pages.page::()) + // { + // return page.special_character_key_view(); + // } if let Some(page) = self.pages.page::() { if id == page.color_dialog { diff --git a/cosmic-settings/src/pages/input/keyboard/mod.rs b/cosmic-settings/src/pages/input/keyboard/mod.rs index 072e7c1..c061b58 100644 --- a/cosmic-settings/src/pages/input/keyboard/mod.rs +++ b/cosmic-settings/src/pages/input/keyboard/mod.rs @@ -1,23 +1,20 @@ use cosmic::{ + cosmic_config::{self, ConfigSet}, iced::{ self, widget::{self, horizontal_space}, - window, Length, + Length, }, iced_core::Border, iced_style, theme, widget::{button, container, icon, radio, settings}, - Apply, + Apply, Command, Element, }; +use cosmic_comp_config::XkbConfig; use cosmic_settings_page::{self as page, section, Section}; -use once_cell::sync::Lazy; +use itertools::Itertools; use slotmap::SlotMap; -use super::Message; - -pub static ADD_INPUT_SOURCE_DIALOGUE_ID: Lazy = Lazy::new(window::Id::unique); -pub static SPECIAL_CHARACTER_DIALOGUE_ID: Lazy = Lazy::new(window::Id::unique); - static COMPOSE_OPTIONS: &[(&str, &str)] = &[ // ("Left Alt", "compose:lalt"), XXX? ("Right Alt", "compose:ralt"), @@ -42,6 +39,39 @@ static ALTERNATE_CHARACTER_OPTIONS: &[(&str, &str)] = &[ // ("Print Screen", "lv3"), XXX ]; +#[derive(Clone, Debug)] +pub enum Message { + ExpandInputSourcePopover(Option), + OpenSpecialCharacterContext(SpecialKey), + SpecialCharacterSelect(Option<&'static str>), +} + +pub struct Page { + config: cosmic_config::Config, + context: Option, + expanded_source_popover: Option, + sources: Vec, + xkb: XkbConfig, +} + +impl Default for Page { + fn default() -> Self { + let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap(); + + Self { + context: None, + expanded_source_popover: None, + sources: default_input_sources(), + xkb: super::get_config(&config, "xkb_config"), + config, + } + } +} + +enum Context { + SpecialCharacter(SpecialKey), +} + #[derive(Copy, Clone, Debug)] pub enum SpecialKey { AlternateCharacters, @@ -144,54 +174,6 @@ pub struct InputSource { label: String, } -impl super::Page { - pub fn add_input_source_view(&self) -> cosmic::Element<'static, crate::app::Message> { - widget::column![].into() - } - - pub fn special_character_key_view(&self) -> cosmic::Element<'_, crate::app::Message> { - let Some(special_key) = self.special_character_dialog else { - return widget::text("").into(); - }; - - let options = match special_key { - SpecialKey::Compose => COMPOSE_OPTIONS, - SpecialKey::AlternateCharacters => ALTERNATE_CHARACTER_OPTIONS, - }; - let prefix = special_key.prefix(); - let current = self - .xkb - .options - .iter() - .flat_map(|x| x.split(',')) - .find(|x| x.starts_with(prefix)); - - // TODO description, layout default - - let mut list = cosmic::widget::list_column(); - list = list.add(special_char_radio_row("None", None, current)); - for (desc, id) in options { - list = list.add(special_char_radio_row(desc, Some(id), current)); - } - widget::column![ - cosmic::widget::header_bar() - .title(special_key.title()) - .on_close(Message::CloseSpecialCharacterDialog), - cosmic::widget::container( - cosmic::widget::scrollable(cosmic::widget::container(list).padding(24)) - .width(Length::Fill) - .height(Length::Fill) - ) - .style(theme::Container::Background) - .width(Length::Fill) - .height(Length::Fill) - ] - .apply(cosmic::Element::from) - .map(crate::pages::Message::Input) - .map(crate::app::Message::PageMessage) - } -} - fn special_char_radio_row<'a>( desc: &'a str, value: Option<&'static str>, @@ -204,9 +186,6 @@ fn special_char_radio_row<'a>( .into() } -#[derive(Default)] -pub struct Page; - // XXX pub fn default_input_sources() -> Vec { vec![InputSource { @@ -232,6 +211,85 @@ impl page::Page for Page { .title(fl!("keyboard")) .description(fl!("keyboard", "desc")) } + + fn context_drawer(&self) -> Option> { + match self.context { + Some(Context::SpecialCharacter(special_key)) => self + .special_character_key_view(special_key) + .map(crate::pages::Message::Keyboard) + .apply(Some), + + None => None, + } + } +} + +impl Page { + pub fn update(&mut self, message: Message) -> Command { + match message { + Message::ExpandInputSourcePopover(value) => { + self.expanded_source_popover = value; + } + + Message::OpenSpecialCharacterContext(key) => { + self.context = Some(Context::SpecialCharacter(key)); + return cosmic::command::message(crate::app::Message::OpenContextDrawer( + key.title().into(), + )); + } + + Message::SpecialCharacterSelect(id) => { + if let Some(Context::SpecialCharacter(special_key)) = self.context { + let options = self.xkb.options.as_deref().unwrap_or(""); + let prefix = special_key.prefix(); + let new_options = options + .split(',') + .filter(|x| !x.starts_with(prefix)) + .chain(id) + .join(","); + + self.xkb.options = Some(new_options).filter(|x| !x.is_empty()); + + if let Err(err) = self.config.set("xkb_config", &self.xkb) { + tracing::error!(?err, "Failed to set config 'xkb_config'"); + } + } + } + } + + Command::none() + } + + pub fn add_input_source_view(&self) -> cosmic::Element<'static, crate::app::Message> { + widget::column![].into() + } + + fn special_character_key_view(&self, special_key: SpecialKey) -> cosmic::Element<'_, Message> { + let options = match special_key { + SpecialKey::Compose => COMPOSE_OPTIONS, + SpecialKey::AlternateCharacters => ALTERNATE_CHARACTER_OPTIONS, + }; + let prefix = special_key.prefix(); + let current = self + .xkb + .options + .iter() + .flat_map(|x| x.split(',')) + .find(|x| x.starts_with(prefix)); + + // TODO description, layout default + + let mut list = cosmic::widget::list_column(); + list = list.add(special_char_radio_row("None", None, current)); + for (desc, id) in options { + list = list.add(special_char_radio_row(desc, Some(id), current)); + } + + cosmic::widget::scrollable(cosmic::widget::container(list).padding(24)) + .width(Length::Fill) + .height(Length::Fill) + .into() + } } impl page::AutoBind for Page { @@ -244,20 +302,18 @@ fn input_sources() -> Section { // TODO desc Section::default() .title(fl!("keyboard-sources")) - .view::(|binder, _page, section| { - let input = binder.page::().expect("input page not found"); - + .view::(|_binder, page, section| { // TODO Need something more custom, with drag and drop let mut section = settings::view_section(§ion.title); - let expanded_source = input.expanded_source_popover.as_deref(); - for source in &input.sources { + let expanded_source = page.expanded_source_popover.as_deref(); + for source in &page.sources { section = section.add(input_source(source, expanded_source)); } section .apply(cosmic::Element::from) - .map(crate::pages::Message::Input) + .map(crate::pages::Message::Keyboard) }) } @@ -271,18 +327,17 @@ fn special_character_entry() -> Section { .view::(|_binder, _page, section| { let descriptions = §ion.descriptions; - // TODO dialogs settings::view_section(§ion.title) .add(go_next_item( &*descriptions[0], - Message::OpenSpecialCharacterDialog(SpecialKey::AlternateCharacters), + Message::OpenSpecialCharacterContext(SpecialKey::AlternateCharacters), )) .add(go_next_item( &*descriptions[1], - Message::OpenSpecialCharacterDialog(SpecialKey::Compose), + Message::OpenSpecialCharacterContext(SpecialKey::Compose), )) .apply(cosmic::Element::from) - .map(crate::pages::Message::Input) + .map(crate::pages::Message::Keyboard) }) } diff --git a/cosmic-settings/src/pages/input/mod.rs b/cosmic-settings/src/pages/input/mod.rs index c206f4e..b67d3b3 100644 --- a/cosmic-settings/src/pages/input/mod.rs +++ b/cosmic-settings/src/pages/input/mod.rs @@ -33,11 +33,8 @@ crate::cache_dynamic_lazy! { #[derive(Clone, Debug)] pub enum Message { - CloseSpecialCharacterDialog, // seperate close message, to make sure another isn't closed? DisableWhileTyping(bool, bool), - ExpandInputSourcePopover(Option), - OpenSpecialCharacterDialog(keyboard::SpecialKey), PrimaryButtonSelected(cosmic::widget::segmented_button::Entity, bool), SetAcceleration(bool, bool), SetMouseSpeed(f64, bool), @@ -45,7 +42,6 @@ pub enum Message { SetSecondaryClickBehavior(Option, bool), SetScrollFactor(f64, bool), SetScrollMethod(Option, bool), - SpecialCharacterSelect(Option<&'static str>), TapToClick(bool), } @@ -60,12 +56,6 @@ pub struct Page { // Touchpad touchpad_primary_button: cosmic::widget::segmented_button::SingleSelectModel, - - // Keyboard - expanded_source_popover: Option, - sources: Vec, - special_character_dialog: Option, - xkb: XkbConfig, } fn get_config( @@ -83,7 +73,6 @@ impl Default for Page { let config = cosmic_config::Config::new("com.system76.CosmicComp", 1).unwrap(); let input_default: InputConfig = get_config(&config, "input_default"); let input_touchpad: InputConfig = get_config(&config, "input_touchpad"); - let xkb = get_config(&config, "xkb_config"); let mut primary_button = mouse::default_primary_button(); let idx = input_default.left_handed.unwrap_or(false) as u16; @@ -103,12 +92,6 @@ impl Default for Page { // Touchpad touchpad_primary_button, - - // Keyboard - expanded_source_popover: None, - sources: keyboard::default_input_sources(), - special_character_dialog: None, - xkb, } } } @@ -160,7 +143,7 @@ impl Page { Message::SetSecondaryClickBehavior(click_method, touchpad) => { self.update_input(touchpad, |x| { x.click_method = click_method; - }) + }); } Message::SetScrollFactor(value, touchpad) => self.update_input(touchpad, |x| { @@ -193,53 +176,6 @@ impl Page { self.update_input(touchpad, |x| x.left_handed = Some(left_handed)); } - Message::ExpandInputSourcePopover(value) => { - self.expanded_source_popover = value; - } - - Message::OpenSpecialCharacterDialog(special_key) => { - self.special_character_dialog = Some(special_key); - let window_settings = SctkWindowSettings { - window_id: *keyboard::SPECIAL_CHARACTER_DIALOGUE_ID, - app_id: Some("com.system76.CosmicSettings".to_string()), - title: Some(special_key.title()), - parent: Some(window::Id::MAIN), - autosize: false, - size_limits: layout::Limits::NONE - .min_width(300.0) - .max_width(800.0) - .min_height(200.0) - .max_height(1080.0), - size: (512, 420), - resizable: None, - client_decorations: true, - transparent: true, - ..Default::default() - }; - return commands::window::get_window(window_settings); - } - - Message::CloseSpecialCharacterDialog => { - self.special_character_dialog = None; - return commands::window::close_window(*keyboard::SPECIAL_CHARACTER_DIALOGUE_ID); - } - - Message::SpecialCharacterSelect(id) => { - if let Some(special_key) = self.special_character_dialog { - let options = self.xkb.options.as_deref().unwrap_or(""); - let prefix = special_key.prefix(); - let new_options = options - .split(',') - .filter(|x| !x.starts_with(prefix)) - .chain(id) - .join(","); - self.xkb.options = Some(new_options).filter(|x| !x.is_empty()); - if let Err(err) = self.config.set("xkb_config", &self.xkb) { - error!(?err, "Failed to set config 'xkb_config'"); - } - } - } - Message::TapToClick(enabled) => { self.update_input(true, |conf| { conf.tap_config diff --git a/cosmic-settings/src/pages/mod.rs b/cosmic-settings/src/pages/mod.rs index 224434a..8ea18f3 100644 --- a/cosmic-settings/src/pages/mod.rs +++ b/cosmic-settings/src/pages/mod.rs @@ -23,6 +23,7 @@ pub enum Message { Dock(desktop::dock::Message), DockApplet(desktop::dock::applets::Message), External { id: String, message: Vec }, + Keyboard(input::keyboard::Message), Input(input::Message), Page(Entity), Panel(desktop::panel::Message),