diff --git a/i18n/kmr/cosmic_greeter.ftl b/i18n/kmr/cosmic_greeter.ftl index 8f1faf5..e69de29 100644 --- a/i18n/kmr/cosmic_greeter.ftl +++ b/i18n/kmr/cosmic_greeter.ftl @@ -1,37 +0,0 @@ -cancel = Têk bibe -accessibility = Gihîştin - .screen-reader = Xwînera dîmenderê - .magnifier = Mezinker - .high-contrast = Dijberiya bilind - .invert-colors = Rengên berevajîkirî -authenticating = Tê rastandin... -type-username = Navê bikarhêner: -restart = Ji nû ve bide destpêkirin -user = Bikarhêner -caps-lock = Caps Lock çalak e. -enter-user = Navê bi destan têxîne... -keyboard-layout = Awayê kilîtdankê -restart-now = Niha ji nû ve bide destpêkirin? -restart-timeout = - Pergal wê xweber were jinûvedestpêkirin - { $seconds -> - [0] niha - [1] di 1 çirke de - *[other] di { $seconds } çirke de. - } -session = Danaşîn -shutdown = Vemrîne -shutdown-now = Niha vemrîne? -shutdown-timeout = - Pergal wê xweber were vemirandin - { $seconds -> - [0] niha. - [1] di 1 çirke de. - *[other] di { $seconds } çirke de. - } -suspend = Rawestîne -auth-error-default = Rastandinê têk çû. Tika ye dîsa hewl bide. -auth-error-credentials = Borînpeyv şaş e. Tika ye awayê kilîtdankê xwe kontrol bike û dîsa hewl bide. -auth-error-denied = Gihîştin hate astengkirin. -auth-error-maxtries = Gelek hewldanên rastandinê yên şaş. -auth-error-account = Jimarê bikarhêner tune ye yan jî neçalak e. diff --git a/i18n/ru/cosmic_greeter.ftl b/i18n/ru/cosmic_greeter.ftl index e36549b..7e6d299 100644 --- a/i18n/ru/cosmic_greeter.ftl +++ b/i18n/ru/cosmic_greeter.ftl @@ -27,7 +27,7 @@ accessibility = Специальные возможности .high-contrast = Высокая контрастность .invert-colors = Инверсия цветов caps-lock = Включён Caps Lock. -enter-user = Ввести имя вручную... +enter-user = Введите имя вручную... type-username = Имя пользователя: authenticating = Аутентификация… auth-error-default = Сбой аутентификации. Повторите попытку. diff --git a/justfile b/justfile index 6019b2c..76a22e3 100644 --- a/justfile +++ b/justfile @@ -92,15 +92,15 @@ vendor: echo 'directory = "vendor"' >> .cargo/config.toml echo >> .cargo/config.toml echo '[env]' >> .cargo/config.toml - if [ -z "${SOURCE_DATE_EPOCH}" ]; then - SOURCE_DATE_EPOCH=$(git log -1 --format='%ct') + if [ -n "${SOURCE_DATE_EPOCH}" ] + then + source_date="$(date -d "@${SOURCE_DATE_EPOCH}" "+%Y-%m-%d")" + echo "VERGEN_GIT_COMMIT_DATE = \"${source_date}\"" >> .cargo/config.toml fi - if [ -z "${SOURCE_GIT_HASH}" ]; then - SOURCE_GIT_HASH=$(git rev-parse HEAD) + if [ -n "${SOURCE_GIT_HASH}" ] + then + echo "VERGEN_GIT_SHA = \"${SOURCE_GIT_HASH}\"" >> .cargo/config.toml fi - source_date="$(date -d "@${SOURCE_DATE_EPOCH}" "+%Y-%m-%d")" - echo "VERGEN_GIT_COMMIT_DATE = \"${source_date}\"" >> .cargo/config.toml - echo "VERGEN_GIT_SHA = \"${SOURCE_GIT_HASH}\"" >> .cargo/config.toml tar pcf vendor.tar .cargo vendor rm -rf .cargo vendor diff --git a/src/greeter.rs b/src/greeter.rs index 8722d56..886bb3a 100644 --- a/src/greeter.rs +++ b/src/greeter.rs @@ -390,6 +390,7 @@ pub enum Message { HighContrast(bool), InvertColors(bool), WaylandUpdate(WaylandUpdate), + SpinnerTick, } impl From for Message { @@ -419,6 +420,8 @@ pub struct App { accessibility: Accessibility, authenticating: bool, + spinner_rotation: f32, + spinner_handle: Option, } #[derive(Default)] @@ -899,7 +902,14 @@ impl App { widget::row::with_capacity(2) .spacing(8.0) .align_y(Alignment::Center) - .push(widget::indeterminate_circular().size(16.0).bar_height(2.0)) + .push( + widget::icon::from_name("process-working-symbolic") + .size(16) + .icon() + .rotation(iced::Rotation::Floating(iced::Radians( + self.spinner_rotation.to_radians(), + ))), + ) .push(widget::text(fl!("authenticating"))), ) .width(Length::Fill) @@ -1163,6 +1173,8 @@ impl cosmic::Application for App { randr_list: None, surface_id_pairs: Vec::new(), authenticating: false, + spinner_rotation: 0.0, + spinner_handle: None, }; (app, Task::batch(tasks)) } @@ -1470,12 +1482,39 @@ impl cosmic::Application for App { self.common.error_opt = None; self.authenticating = true; self.send_request(Request::PostAuthMessageResponse { response }); + + // Start spinner animation if not already running + if self.spinner_handle.is_none() { + let (spinner_task, handle) = + cosmic::task::stream(cosmic::iced::stream::channel( + 1, + |mut msg_tx: iced::futures::channel::mpsc::Sender<_>| async move { + let mut interval = time::interval(Duration::from_millis(16)); // ~60fps + loop { + msg_tx + .send(cosmic::Action::App(Message::SpinnerTick)) + .await + .unwrap(); + interval.tick().await; + } + }, + )) + .abortable(); + self.spinner_handle = Some(handle); + return spinner_task; + } } Message::Login => { self.common.prompt_opt = None; self.common.error_opt = None; self.authenticating = false; + // Stop spinner animation + if let Some(handle) = self.spinner_handle.take() { + handle.abort(); + } + self.spinner_rotation = 0.0; + match self.flags.sessions.get(&self.selected_session).cloned() { Some((cmd, env)) => { self.send_request(Request::StartSession { cmd, env }); @@ -1488,6 +1527,12 @@ impl cosmic::Application for App { self.common.error_opt = Some(error); self.authenticating = false; + // Stop spinner animation + if let Some(handle) = self.spinner_handle.take() { + handle.abort(); + } + self.spinner_rotation = 0.0; + self.send_request(Request::CancelSession); } Message::Reconnect => { @@ -1818,6 +1863,10 @@ impl cosmic::Application for App { }; return reposition_subsurface(*subsurface_id, loc.x as i32, loc.y as i32); } + Message::SpinnerTick => { + // Update spinner rotation angle (360 degrees per second = 6 degrees per frame at 60fps) + self.spinner_rotation = (self.spinner_rotation + 6.0) % 360.0; + } } Task::none() } diff --git a/src/locker.rs b/src/locker.rs index 7f489df..de7f7dc 100644 --- a/src/locker.rs +++ b/src/locker.rs @@ -274,6 +274,7 @@ pub enum Message { Error(String), Lock, Unlock, + SpinnerTick, } impl From for Message { @@ -311,6 +312,8 @@ pub struct App { inhibit_opt: Option>, value_tx_opt: Option>, authenticating: bool, + spinner_rotation: f32, + spinner_handle: Option, } impl App { @@ -563,7 +566,14 @@ impl App { widget::row::with_capacity(2) .spacing(8.0) .align_y(Alignment::Center) - .push(widget::indeterminate_circular().size(16.0).bar_height(2.0)) + .push( + widget::icon::from_name("process-working-symbolic") + .size(16) + .icon() + .rotation(iced::Rotation::Floating(iced::Radians( + self.spinner_rotation.to_radians(), + ))), + ) .push(widget::text(fl!("authenticating"))), ) .width(Length::Fill) @@ -665,6 +675,8 @@ impl cosmic::Application for App { inhibit_opt: None, value_tx_opt: None, authenticating: false, + spinner_rotation: 0.0, + spinner_handle: None, }; let task = if cfg!(feature = "logind") { @@ -1035,10 +1047,39 @@ impl cosmic::Application for App { self.authenticating = true; match self.value_tx_opt.take() { Some(value_tx) => { - return cosmic::task::future(async move { - value_tx.send(value).await.unwrap(); - Message::Channel(value_tx) - }); + // Start spinner animation if not already running + if self.spinner_handle.is_none() { + let (spinner_task, handle) = + cosmic::task::stream(cosmic::iced::stream::channel( + 1, + |mut msg_tx: futures::channel::mpsc::Sender<_>| async move { + let mut interval = + tokio::time::interval(Duration::from_millis(16)); // ~60fps + loop { + msg_tx + .send(cosmic::Action::App(Message::SpinnerTick)) + .await + .unwrap(); + interval.tick().await; + } + }, + )) + .abortable(); + self.spinner_handle = Some(handle); + + return Task::batch([ + spinner_task, + cosmic::task::future(async move { + value_tx.send(value).await.unwrap(); + Message::Channel(value_tx) + }), + ]); + } else { + return cosmic::task::future(async move { + value_tx.send(value).await.unwrap(); + Message::Channel(value_tx) + }); + } } None => tracing::warn!("tried to submit when value_tx_opt not set"), } @@ -1057,6 +1098,16 @@ impl cosmic::Application for App { Message::Error(error) => { self.common.error_opt = Some(error); self.authenticating = false; + + // Stop spinner animation + if let Some(handle) = self.spinner_handle.take() { + handle.abort(); + } + self.spinner_rotation = 0.0; + } + Message::SpinnerTick => { + // Update spinner rotation angle (360 degrees per second = 6 degrees per frame at 60fps) + self.spinner_rotation = (self.spinner_rotation + 6.0) % 360.0; } Message::Lock => match self.state { State::Unlocked => { @@ -1068,6 +1119,10 @@ impl cosmic::Application for App { self.value_tx_opt = None; // Reset authenticating state self.authenticating = false; + if let Some(handle) = self.spinner_handle.take() { + handle.abort(); + } + self.spinner_rotation = 0.0; // Try to create lockfile when locking if let Some(ref lockfile) = self.flags.lockfile_opt && let Err(err) = fs::File::create(lockfile) @@ -1096,6 +1151,11 @@ impl cosmic::Application for App { // Stop authenticating self.authenticating = false; + // Stop spinner animation + if let Some(handle) = self.spinner_handle.take() { + handle.abort(); + } + self.spinner_rotation = 0.0; // Try to delete lockfile when unlocking if let Some(ref lockfile) = self.flags.lockfile_opt && let Err(err) = fs::remove_file(lockfile)