From 2fff220392e50b6d2adaab33355da014c007894e Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 9 May 2025 18:47:52 -0600 Subject: [PATCH] Move layout and prompt handling to common code --- src/common.rs | 138 ++++++++++++++++++++++++++++++--- src/greeter.rs | 167 +++++++--------------------------------- src/greeter/ipc.rs | 33 +++++--- src/locker.rs | 185 +++++++++++++++++++++++++++++++-------------- 4 files changed, 308 insertions(+), 215 deletions(-) diff --git a/src/common.rs b/src/common.rs index 0aee09c..5ac3114 100644 --- a/src/common.rs +++ b/src/common.rs @@ -12,15 +12,26 @@ use cosmic::{ iced_runtime::core::window::Id as SurfaceId, widget, }; -use cosmic_greeter_daemon::{BgSource, UserData}; -use std::collections::HashMap; +use cosmic_config::{ConfigSet, CosmicConfigEntry}; +use cosmic_greeter_daemon::{BgSource, CosmicCompConfig, UserData}; +use std::{collections::HashMap, sync::Arc}; use wayland_client::protocol::wl_output::WlOutput; +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ActiveLayout { + pub layout: String, + pub description: String, + pub variant: String, +} + pub struct Common { + pub active_layouts: Vec, pub active_surface_id_opt: Option, + pub comp_config_handler: Option, pub core: Core, pub error_opt: Option, - pub input: String, + pub fallback_background: widget::image::Handle, + pub layouts_opt: Option>, pub network_icon_opt: Option<&'static str>, pub on_output_event: Option M>>, pub on_session_lock_event: Option M>>, @@ -39,11 +50,11 @@ pub struct Common { #[derive(Clone, Debug)] pub enum Message { Focus(SurfaceId), - Input(String), Key(Modifiers, Key, Option), NetworkIcon(Option<&'static str>), OutputEvent(OutputEvent, WlOutput), PowerInfo(Option<(String, f64)>), + Prompt(String, bool, Option), SessionLockEvent(SessionLockEvent), Tick, Tz(chrono_tz::Tz), @@ -59,11 +70,35 @@ impl + Send + 'static> Common { core.window.show_minimize = false; core.window.use_template = false; + let comp_config_handler = match cosmic_config::Config::new( + "com.system76.CosmicComp", + CosmicCompConfig::VERSION, + ) { + Ok(config_handler) => Some(config_handler), + Err(err) => { + log::error!("failed to create cosmic-comp config handler: {}", err); + None + } + }; + + let layouts_opt = match xkb_data::all_keyboard_layouts() { + Ok(ok) => Some(Arc::new(ok)), + Err(err) => { + log::warn!("failed to load keyboard layouts: {}", err); + None + } + }; + let app = Self { + active_layouts: Vec::new(), active_surface_id_opt: None, + comp_config_handler, core, error_opt: None, - input: String::new(), + fallback_background: widget::image::Handle::from_bytes( + include_bytes!("../res/background.jpg").as_slice(), + ), + layouts_opt, network_icon_opt: None, on_output_event: None, on_session_lock_event: None, @@ -87,6 +122,27 @@ impl + Send + 'static> Common { ) } + pub fn set_xkb_config(&self, user_data: &UserData) { + if let Some(mut xkb_config) = user_data.xkb_config_opt.clone() { + xkb_config.layout = String::new(); + xkb_config.variant = String::new(); + for (i, layout) in self.active_layouts.iter().enumerate() { + if i > 0 { + xkb_config.layout.push(','); + xkb_config.variant.push(','); + } + xkb_config.layout.push_str(&layout.layout); + xkb_config.variant.push_str(&layout.variant); + } + if let Some(comp_config_handler) = &self.comp_config_handler { + match comp_config_handler.set("xkb_config", xkb_config) { + Ok(()) => log::info!("updated cosmic-comp xkb_config"), + Err(err) => log::error!("failed to update cosmic-comp xkb_config: {}", err), + } + } + } + } + pub fn update_wallpapers(&mut self, user_data: &UserData) { for (_output, surface_id) in self.surface_ids.iter() { if self.surface_images.contains_key(surface_id) { @@ -129,6 +185,55 @@ impl + Send + 'static> Common { } } + pub fn update_user_data(&mut self, user_data: &UserData) { + self.update_wallpapers(user_data); + + // From cosmic-applet-input-sources + if let Some(keyboard_layouts) = &self.layouts_opt { + if 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; + } + + let Some(xkb_variants) = xkb_layout.variants() else { + 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; + } + } + } + log::info!("{:?}", self.active_layouts); + } + } + } + pub fn update(&mut self, message: Message) -> Task { match message { Message::Focus(surface_id) => { @@ -141,9 +246,6 @@ impl + Send + 'static> Common { return widget::text_input::focus(text_input_id.clone()); } } - Message::Input(input) => { - self.input = input; - } Message::Key(modifiers, key, text) => { // Uncaptured keys with only shift modifiers go to the password box if !modifiers.logo() @@ -152,7 +254,9 @@ impl + Send + 'static> Common { && matches!(key, Key::Character(_)) { if let Some(text) = text { - self.input.push_str(&text); + if let Some((_, _, Some(value))) = &mut self.prompt_opt { + value.push_str(&text); + } } if let Some(surface_id) = self.active_surface_id_opt { @@ -177,6 +281,22 @@ impl + Send + 'static> Common { Message::PowerInfo(power_info_opt) => { self.power_info_opt = power_info_opt; } + Message::Prompt(prompt, secret, value_opt) => { + let prompt_was_none = self.prompt_opt.is_none(); + self.prompt_opt = Some((prompt, secret, value_opt)); + if prompt_was_none { + if let Some(surface_id) = self.active_surface_id_opt { + if let Some(text_input_id) = self + .surface_names + .get(&surface_id) + .and_then(|id| self.text_input_ids.get(id)) + { + log::info!("focus surface found id {:?}", text_input_id); + return widget::text_input::focus(text_input_id.clone()); + } + } + } + } Message::SessionLockEvent(lock_event) => { if let Some(on_session_lock_event) = &self.on_session_lock_event { return Task::done(cosmic::Action::App(on_session_lock_event(lock_event))); diff --git a/src/greeter.rs b/src/greeter.rs index 9a1e2d0..dc06c73 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -11,7 +11,7 @@ use cosmic::surface; use cosmic::widget::text; use cosmic::{ Element, - cosmic_config::{self, ConfigSet, CosmicConfigEntry}, + cosmic_config::{self, ConfigSet}, executor, iced::{ self, Background, Border, Length, Subscription, alignment, @@ -27,7 +27,6 @@ use cosmic::{ iced_runtime::core::window::Id as SurfaceId, theme, widget, }; -use cosmic_comp_config::CosmicCompConfig; use cosmic_greeter_config::Config as CosmicGreeterConfig; use cosmic_greeter_daemon::UserData; use greetd_ipc::Request; @@ -260,34 +259,11 @@ pub fn main() -> Result<(), Box> { sessions }; - let layouts_opt = match xkb_data::all_keyboard_layouts() { - Ok(ok) => Some(Arc::new(ok)), - Err(err) => { - log::warn!("failed to load keyboard layouts: {}", err); - None - } - }; - - let comp_config_handler = - match cosmic_config::Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION) { - Ok(config_handler) => Some(config_handler), - Err(err) => { - log::error!("failed to create cosmic-comp config handler: {}", err); - None - } - }; - - let fallback_background = - widget::image::Handle::from_bytes(include_bytes!("../res/background.jpg").as_slice()); - let flags = Flags { user_datas, sessions, - layouts_opt, - comp_config_handler, greeter_config, greeter_config_handler, - fallback_background, }; let settings = Settings::default().no_main_window(true); @@ -301,11 +277,8 @@ pub fn main() -> Result<(), Box> { pub struct Flags { user_datas: Vec, sessions: HashMap, Vec)>, - layouts_opt: Option>, - comp_config_handler: Option, greeter_config: CosmicGreeterConfig, greeter_config_handler: Option, - fallback_background: widget::image::Handle, } #[derive(Clone, Debug)] @@ -320,13 +293,6 @@ pub enum SocketState { Error(Arc), } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ActiveLayout { - layout: String, - description: String, - variant: String, -} - #[derive(Clone, Copy, Debug)] pub enum DialogPage { Restart(Instant), @@ -377,7 +343,6 @@ pub enum Message { Heartbeat, KeyboardLayout(usize), Login, - Prompt(String, bool, Option), Reconnect, Restart, Session(String), @@ -404,7 +369,6 @@ pub struct App { selected_username: NameIndexPair, session_names: Vec, selected_session: String, - active_layouts: Vec, dialog_page_opt: Option, dropdown_opt: Option, heartbeat_handle: Option, @@ -495,8 +459,8 @@ impl App { ) .position(widget::popover::Position::Bottom); if matches!(self.dropdown_opt, Some(Dropdown::Keyboard)) { - let mut items = Vec::with_capacity(self.active_layouts.len()); - for (i, layout) in self.active_layouts.iter().enumerate() { + let mut items = Vec::with_capacity(self.common.active_layouts.len()); + for (i, layout) in self.common.active_layouts.iter().enumerate() { items.push(menu_checklist( &layout.description, i == 0, @@ -648,16 +612,22 @@ impl App { .unwrap_or_else(|| cosmic::widget::Id::new("text_input")); let mut text_input = widget::secure_input( prompt.clone(), - &self.common.input, - Some(Message::Prompt( - prompt.clone(), - !*secret, - Some(value.clone()), - )), + value.as_str(), + Some( + common::Message::Prompt( + prompt.clone(), + !*secret, + Some(value.clone()), + ) + .into(), + ), *secret, ) .id(text_input_id) - .on_input(|input| common::Message::Input(input).into()) + .on_input(|input| { + common::Message::Prompt(prompt.clone(), *secret, Some(input)) + .into() + }) .on_submit(|v| Message::Auth(Some(v))); if let Some(text_input_id) = self @@ -773,6 +743,7 @@ impl App { None => popover.into(), } } + /// Send a [`Request`] to the greetd IPC subscription. fn send_request(&self, request: Request) { if let Some(ref sender) = self.greetd_sender { @@ -793,27 +764,10 @@ impl App { None => return, }; - if let Some(mut xkb_config) = user_data.xkb_config_opt.clone() { - xkb_config.layout = String::new(); - xkb_config.variant = String::new(); - for (i, layout) in self.active_layouts.iter().enumerate() { - if i > 0 { - xkb_config.layout.push(','); - xkb_config.variant.push(','); - } - xkb_config.layout.push_str(&layout.layout); - xkb_config.variant.push_str(&layout.variant); - } - if let Some(comp_config_handler) = &self.flags.comp_config_handler { - match comp_config_handler.set("xkb_config", xkb_config) { - Ok(()) => log::info!("updated cosmic-comp xkb_config"), - Err(err) => log::error!("failed to update cosmic-comp xkb_config: {}", err), - } - } - } + self.common.set_xkb_config(&user_data); } - fn update_user_config(&mut self) -> Task { + fn update_user_data(&mut self) -> Task { let user_data = match self .selected_username .data_idx @@ -825,54 +779,10 @@ impl App { } }; - self.common.update_wallpapers(&user_data); + self.common.update_user_data(&user_data); - // From cosmic-applet-input-sources - if let Some(keyboard_layouts) = &self.flags.layouts_opt { - if 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("")); - 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; - } - - let Some(xkb_variants) = xkb_layout.variants() else { - 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); - } - } - } - log::info!("{:?}", self.active_layouts); - - // Ensure that user's xkb config is used - self.set_xkb_config(); - } - } + // Ensure that user's xkb config is used + self.common.set_xkb_config(&user_data); match &user_data.theme_opt { Some(theme) => { @@ -958,7 +868,6 @@ impl cosmic::Application for App { selected_username, session_names, selected_session, - active_layouts: Vec::new(), dialog_page_opt: None, dropdown_opt: None, heartbeat_handle: None, @@ -1059,7 +968,7 @@ impl cosmic::Application for App { })), ); return Task::batch([ - self.update_user_config(), + self.update_user_data(), get_layer_surface(SctkLayerSurfaceSettings { id: surface_id, layer: Layer::Overlay, @@ -1116,27 +1025,6 @@ impl cosmic::Application for App { _ => {} } } - Message::Prompt(prompt, secret, value_opt) => { - let value_was_some = self - .common - .prompt_opt - .as_ref() - .map_or(false, |(_, _, x)| x.is_some()); - let value_is_some = value_opt.is_some(); - self.common.prompt_opt = Some((prompt, secret, value_opt)); - if value_is_some && !value_was_some { - if let Some(surface_id) = self.common.active_surface_id_opt { - if let Some(text_input_id) = self - .common - .surface_names - .get(&surface_id) - .and_then(|id| self.common.text_input_ids.get(id)) - { - return widget::text_input::focus(text_input_id.clone()); - } - } - } - } Message::Session(selected_session) => { self.selected_session = selected_session; if self.dropdown_opt == Some(Dropdown::Session) { @@ -1241,7 +1129,6 @@ impl cosmic::Application for App { } } Message::Auth(response) => { - self.common.input.clear(); self.common.prompt_opt = None; self.common.error_opt = None; self.send_request(Request::PostAuthMessageResponse { response }); @@ -1262,7 +1149,7 @@ impl cosmic::Application for App { self.send_request(Request::CancelSession); } Message::Reconnect => { - return self.update_user_config(); + return self.update_user_data(); } Message::DialogCancel => { self.dialog_page_opt = None; @@ -1305,8 +1192,8 @@ impl cosmic::Application for App { } } Message::KeyboardLayout(layout_i) => { - if layout_i < self.active_layouts.len() { - self.active_layouts.swap(0, layout_i); + if layout_i < self.common.active_layouts.len() { + self.common.active_layouts.swap(0, layout_i); self.set_xkb_config(); } if self.dropdown_opt == Some(Dropdown::Keyboard) { @@ -1400,7 +1287,7 @@ impl cosmic::Application for App { .common .surface_images .get(&surface_id) - .unwrap_or(&self.flags.fallback_background); + .unwrap_or(&self.common.fallback_background); widget::image(img) .content_fit(iced::ContentFit::Cover) .width(Length::Fill) diff --git a/src/greeter/ipc.rs b/src/greeter/ipc.rs index b58e66d..17e498c 100644 --- a/src/greeter/ipc.rs +++ b/src/greeter/ipc.rs @@ -10,6 +10,8 @@ use std::time::Duration; use tokio::net::UnixStream; use tokio::sync::mpsc; +use crate::common; + pub fn subscription() -> Subscription { struct GreetdSubscription; Subscription::run_with_id( @@ -53,27 +55,36 @@ pub fn subscription() -> Subscription { } => match auth_message_type { greetd_ipc::AuthMessageType::Secret => { _ = sender - .send(Message::Prompt( - auth_message, - true, - Some(String::new()), - )) + .send( + common::Message::Prompt( + auth_message, + true, + Some(String::new()), + ) + .into(), + ) .await; } greetd_ipc::AuthMessageType::Visible => { _ = sender - .send(Message::Prompt( - auth_message, - false, - Some(String::new()), - )) + .send( + common::Message::Prompt( + auth_message, + false, + Some(String::new()), + ) + .into(), + ) .await; } //TODO: treat error type differently? greetd_ipc::AuthMessageType::Info | greetd_ipc::AuthMessageType::Error => { _ = sender - .send(Message::Prompt(auth_message, false, None)) + .send( + common::Message::Prompt(auth_message, false, None) + .into(), + ) .await; } }, diff --git a/src/locker.rs b/src/locker.rs index f88a757..a018c5a 100644 --- a/src/locker.rs +++ b/src/locker.rs @@ -9,7 +9,7 @@ use cosmic::surface; use cosmic::{ Element, executor, iced::{ - self, Length, Subscription, alignment, + self, Background, Border, Length, Subscription, alignment, event::wayland::{OutputEvent, SessionLockEvent}, futures::{self, SinkExt}, platform_specific::shell::wayland::commands::session_lock::{ @@ -17,7 +17,7 @@ use cosmic::{ }, }, iced_runtime::core::window::Id as SurfaceId, - widget, + theme, widget, }; use cosmic_config::CosmicConfigEntry; use cosmic_greeter_daemon::{TimeAppletConfig, UserData}; @@ -35,7 +35,10 @@ use std::{ use tokio::{sync::mpsc, task}; use wayland_client::{Proxy, protocol::wl_output::WlOutput}; -use crate::common::{self, Common}; +use crate::{ + common::{self, Common}, + fl, +}; fn lockfile_opt() -> Option { let runtime_dir = dirs::runtime_dir()?; @@ -52,13 +55,9 @@ pub fn main(user: pwd::Passwd) -> Result<(), Box> { // We are already the user at this point user_data.load_config_as_user(); - let fallback_background = - widget::image::Handle::from_bytes(include_bytes!("../res/background.jpg").as_slice()); - let flags = Flags { user_data, lockfile_opt: lockfile_opt(), - fallback_background, }; let settings = Settings::default().no_main_window(true); @@ -103,11 +102,9 @@ impl Conversation { futures::executor::block_on(async { self.msg_tx - .send(cosmic::Action::App(Message::Prompt( - prompt.to_string(), - secret, - Some(String::new()), - ))) + .send(cosmic::Action::App( + common::Message::Prompt(prompt.to_string(), secret, Some(String::new())).into(), + )) .await }) .map_err(|err| { @@ -134,11 +131,9 @@ impl Conversation { futures::executor::block_on(async { self.msg_tx - .send(cosmic::Action::App(Message::Prompt( - prompt.to_string(), - false, - None, - ))) + .send(cosmic::Action::App( + common::Message::Prompt(prompt.to_string(), false, None).into(), + )) .await }) .map_err(|err| { @@ -182,7 +177,12 @@ impl pam_client::ConversationHandler for Conversation { pub struct Flags { user_data: UserData, lockfile_opt: Option, - fallback_background: widget::image::Handle, +} + +///TODO: this is custom code that should be better handled by libcosmic +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Dropdown { + Keyboard, } /// Messages that are used specifically by our [`App`]. @@ -194,12 +194,13 @@ pub enum Message { SessionLockEvent(SessionLockEvent), Channel(mpsc::Sender), BackgroundState(cosmic_bg_config::state::State), - TimeAppletConfig(TimeAppletConfig), + DropdownToggle(Dropdown), + KeyboardLayout(usize), Inhibit(Arc), - Prompt(String, bool, Option), Submit(String), Surface(surface::Action), Suspend, + TimeAppletConfig(TimeAppletConfig), Error(String), Lock, Unlock, @@ -236,6 +237,7 @@ pub struct App { common: Common, flags: Flags, state: State, + dropdown_opt: Option, inhibit_opt: Option>, value_tx_opt: Option>, } @@ -270,22 +272,88 @@ impl App { ]); } + //TODO: move code for custom dropdowns to libcosmic + let menu_checklist = |label, value, message| { + Element::from( + widget::menu::menu_button(vec![ + if value { + widget::icon::from_name("object-select-symbolic") + .size(16) + .icon() + .width(Length::Fixed(16.0)) + .into() + } else { + widget::Space::with_width(Length::Fixed(17.0)).into() + }, + widget::Space::with_width(Length::Fixed(8.0)).into(), + widget::text(label) + .align_x(iced::alignment::Horizontal::Left) + .into(), + ]) + .on_press(message), + ) + }; + let dropdown_menu = |items| { + widget::container(widget::column::with_children(items)) + .padding(1) + //TODO: move style to libcosmic + .class(theme::Container::custom(|theme| { + let cosmic = theme.cosmic(); + let component = &cosmic.background.component; + widget::container::Style { + icon_color: Some(component.on.into()), + text_color: Some(component.on.into()), + background: Some(Background::Color(component.base.into())), + border: Border { + radius: 8.0.into(), + width: 1.0, + color: component.divider.into(), + }, + ..Default::default() + } + })) + .width(Length::Fixed(240.0)) + }; + + let mut input_button = widget::popover( + widget::button::custom(widget::icon::from_name("input-keyboard-symbolic")) + .padding(12.0) + .on_press(Message::DropdownToggle(Dropdown::Keyboard)), + ) + .position(widget::popover::Position::Bottom); + if matches!(self.dropdown_opt, Some(Dropdown::Keyboard)) { + let mut items = Vec::with_capacity(self.common.active_layouts.len()); + for (i, layout) in self.common.active_layouts.iter().enumerate() { + items.push(menu_checklist( + &layout.description, + i == 0, + Message::KeyboardLayout(i), + )); + } + input_button = input_button.popup(dropdown_menu(items)); + } + //TODO: implement these buttons let button_row = iced::widget::row![ + /*TODO: greeter accessibility options widget::button::custom(widget::icon::from_name( "applications-accessibility-symbolic" )) .padding(12.0) .on_press(Message::None), - widget::button::custom(widget::icon::from_name("input-keyboard-symbolic")) - .padding(12.0) - .on_press(Message::None), - widget::button::custom(widget::icon::from_name("system-users-symbolic")) - .padding(12.0) - .on_press(Message::None), - widget::button::custom(widget::icon::from_name("system-suspend-symbolic")) - .padding(12.0) - .on_press(Message::Suspend), + */ + widget::tooltip( + input_button, + widget::text(fl!("keyboard-layout")), + widget::tooltip::Position::Top + ), + widget::tooltip( + widget::button::custom(widget::icon::from_name("system-suspend-symbolic")) + .padding(12.0) + .on_press(Message::Suspend), + widget::text(fl!("suspend")), + widget::tooltip::Position::Top + ), ] .padding([16.0, 0.0, 0.0, 0.0]) .spacing(8.0); @@ -340,16 +408,21 @@ impl App { let mut text_input = widget::secure_input( prompt.clone(), - &self.common.input, - Some(Message::Prompt( - prompt.clone(), - !*secret, - Some(value.clone()), - )), + value.as_str(), + Some( + common::Message::Prompt( + prompt.clone(), + !*secret, + Some(value.clone()), + ) + .into(), + ), *secret, ) .id(text_input_id) - .on_input(|input| common::Message::Input(input).into()) + .on_input(|input| { + common::Message::Prompt(prompt.clone(), *secret, Some(input)).into() + }) .on_submit(Message::Submit); if *secret { @@ -434,6 +507,7 @@ impl cosmic::Application for App { Message::OutputEvent(output_event, output) })); common.on_session_lock_event = Some(Box::new(|evt| Message::SessionLockEvent(evt))); + common.update_user_data(&flags.user_data); let already_locked = match flags.lockfile_opt { Some(ref lockfile) => lockfile.exists(), @@ -444,6 +518,7 @@ impl cosmic::Application for App { common, flags, state: State::Unlocked, + dropdown_opt: None, inhibit_opt: None, value_tx_opt: None, }; @@ -783,31 +858,28 @@ impl cosmic::Application for App { self.common.surface_images.clear(); self.common.update_wallpapers(&self.flags.user_data); } - Message::TimeAppletConfig(config) => { - self.flags.user_data.time_applet_config = config; + Message::DropdownToggle(dropdown) => { + if self.dropdown_opt == Some(dropdown) { + self.dropdown_opt = None; + } else { + self.dropdown_opt = Some(dropdown); + } } Message::Inhibit(inhibit) => { self.inhibit_opt = Some(inhibit); } - Message::Prompt(prompt, secret, value_opt) => { - let prompt_was_none = self.common.prompt_opt.is_none(); - self.common.prompt_opt = Some((prompt, secret, value_opt)); - if prompt_was_none { - if let Some(surface_id) = self.common.active_surface_id_opt { - if let Some(text_input_id) = self - .common - .surface_names - .get(&surface_id) - .and_then(|id| self.common.text_input_ids.get(id)) - { - log::info!("focus surface found id {:?}", text_input_id); - return widget::text_input::focus(text_input_id.clone()); - } - } + Message::KeyboardLayout(layout_i) => { + if layout_i < self.common.active_layouts.len() { + self.common.active_layouts.swap(0, layout_i); + self.common.set_xkb_config(&self.flags.user_data); + } + if self.dropdown_opt == Some(Dropdown::Keyboard) { + self.dropdown_opt = None } } Message::Submit(value) => { - self.common.input.clear(); + self.common.prompt_opt = None; + self.common.error_opt = None; match self.value_tx_opt.take() { Some(value_tx) => { // Clear errors @@ -828,6 +900,9 @@ impl cosmic::Application for App { cosmic::task::message(cosmic::Action::App(Message::Error(err.to_string()))) }); } + Message::TimeAppletConfig(config) => { + self.flags.user_data.time_applet_config = config; + } Message::Error(error) => { self.common.error_opt = Some(error); } @@ -914,7 +989,7 @@ impl cosmic::Application for App { .common .surface_images .get(&surface_id) - .unwrap_or(&self.flags.fallback_background); + .unwrap_or(&self.common.fallback_background); widget::image(img) .content_fit(iced::ContentFit::Cover) .width(Length::Fill)