diff --git a/data/com.github.pikaos-linux.pikafirstsetup.gschema.xml b/data/com.github.pikaos-linux.pikafirstsetup.gschema.xml index ca66877..1970bfa 100644 --- a/data/com.github.pikaos-linux.pikafirstsetup.gschema.xml +++ b/data/com.github.pikaos-linux.pikafirstsetup.gschema.xml @@ -2,11 +2,11 @@ - 1200 + 700 Default window width - 600 + 500 Default window height diff --git a/src/first_setup/codec_carousel.rs b/src/first_setup/codec_carousel.rs new file mode 100644 index 0000000..cca1503 --- /dev/null +++ b/src/first_setup/codec_carousel.rs @@ -0,0 +1,230 @@ +use std::cell::RefCell; +use std::rc::Rc; +// GTK crates +use adw::ffi::ADW_TOOLBAR_FLAT; +use adw::prelude::*; +use adw::*; +use gdk::Display; +use glib::*; +/// Use all gtk4 libraries (gtk4 -> gtk because cargo) +/// Use all libadwaita libraries (libadwaita -> adw because cargo) +use gtk::prelude::*; +use gtk::*; +use vte::prelude::*; +use vte::*; + +use std::{thread, time}; + +use std::{ + error::Error, + io::Error as ErrorIO, + io::{ErrorKind, Read, Write}, + process::{Command, Stdio}, +}; + +use duct::cmd; +use os_pipe::*; +use std::io::prelude::*; +use std::io::BufReader; + +const CODEC_INSTALL_PROG: &str = " +#! /bin/bash +set -e +sudo apt update -y && sudo apt install pika-codecs-meta -y +"; + +fn codec_install( + log_loop_sender: async_channel::Sender, +) -> Result<(), std::boxed::Box> { + let (pipe_reader, pipe_writer) = os_pipe::pipe()?; + let child = cmd!("bash", "-c", CODEC_INSTALL_PROG) + .stderr_to_stdout() + .stdout_file(pipe_writer) + .start()?; + for line in BufReader::new(pipe_reader).lines() { + log_loop_sender + .send_blocking(line?) + .expect("Channel needs to be opened.") + } + child.wait()?; + + Ok(()) +} + +pub fn codec_carousel( + first_setup_carousel: &adw::Carousel, + internet_connected: &Rc>, + window: &adw::ApplicationWindow, +) { + let internet_connected_status = internet_connected.clone(); + + let (internet_loop_sender, internet_loop_receiver) = async_channel::unbounded(); + let internet_loop_sender = internet_loop_sender.clone(); + // The long running operation runs now in a separate thread + gio::spawn_blocking(move || loop { + thread::sleep(time::Duration::from_secs(1)); + internet_loop_sender + .send_blocking(true) + .expect("The channel needs to be open."); + }); + + let (log_loop_sender, log_loop_receiver) = async_channel::unbounded(); + let log_loop_sender: async_channel::Sender = log_loop_sender.clone(); + + let (log_status_loop_sender, log_status_loop_receiver) = async_channel::unbounded(); + let log_status_loop_sender: async_channel::Sender = log_status_loop_sender.clone(); + + let first_setup_codec_box = gtk::Box::builder() + // that puts items vertically + .orientation(Orientation::Vertical) + .valign(gtk::Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + + let first_setup_codec_box_text = adw::StatusPage::builder() + .icon_name("media-tape") + .title("Multi-media Codecs") + .description("Would you like to install additional video playback and encoding/decoding packages?\n(strongly recommended)") + .build(); + first_setup_codec_box_text.add_css_class("compact"); + + let first_setup_codec_button = gtk::Button::builder() + .label("Install Codecs") + .sensitive(false) + .build(); + + first_setup_codec_button.add_css_class("suggested-action"); + first_setup_codec_button.add_css_class("pill"); + + let first_setup_codec_skip_button = gtk::Button::builder() + .label("Skip Codec Installation") + .sensitive(true) + .width_request(25) + .build(); + + let first_setup_codec_buttons_box = gtk::Box::builder() + .orientation(Orientation::Horizontal) + .halign(gtk::Align::Center) + .valign(gtk::Align::End) + .vexpand(true) + .hexpand(true) + .margin_end(15) + .margin_start(15) + .margin_bottom(15) + .margin_top(15) + .spacing(80) + .build(); + + first_setup_codec_skip_button.add_css_class("pill"); + + let codec_install_log_terminal_buffer = gtk::TextBuffer::builder().build(); + + let codec_install_log_terminal = gtk::TextView::builder() + .vexpand(true) + .hexpand(true) + .editable(false) + .buffer(&codec_install_log_terminal_buffer) + .build(); + + let codec_install_log_terminal_scroll = gtk::ScrolledWindow::builder() + .width_request(400) + .height_request(200) + .vexpand(true) + .hexpand(true) + .child(&codec_install_log_terminal) + .build(); + + let codec_install_dialog = adw::MessageDialog::builder() + .transient_for(window) + .hide_on_close(true) + .extra_child(&codec_install_log_terminal_scroll) + .width_request(400) + .height_request(200) + .heading("Codec installation Log") + .build(); + codec_install_dialog.add_response("codec_install_dialog_ok", "Ok"); + + first_setup_codec_buttons_box.append(&first_setup_codec_button); + first_setup_codec_buttons_box.append(&first_setup_codec_skip_button); + + first_setup_codec_box.append(&first_setup_codec_box_text); + first_setup_codec_box.append(&first_setup_codec_buttons_box); + + first_setup_carousel.append(&first_setup_codec_box); + + let internet_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + internet_loop_context.spawn_local( + clone!(@strong internet_connected_status, @weak first_setup_codec_button => async move { + while let Ok(_state) = internet_loop_receiver.recv().await { + if *internet_connected_status.borrow_mut() == true { + first_setup_codec_button.set_sensitive(true); + first_setup_codec_button.set_label("Install Codecs"); + } else { + first_setup_codec_button.set_sensitive(false); + first_setup_codec_button.set_label("Disabled.. Network setup was skipped"); + } + } + }), + ); + + let log_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + log_loop_context.spawn_local(clone!(@weak codec_install_log_terminal_buffer, @weak codec_install_dialog => async move { + while let Ok(state) = log_loop_receiver.recv().await { + codec_install_log_terminal_buffer.insert(&mut codec_install_log_terminal_buffer.end_iter(), &("\n".to_string() + &state)) + } + })); + + let log_status_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + log_status_loop_context.spawn_local(clone!(@weak codec_install_dialog, @weak first_setup_codec_button, @weak first_setup_codec_skip_button => async move { + while let Ok(state) = log_status_loop_receiver.recv().await { + if state == true { + codec_install_dialog.set_response_enabled("codec_install_dialog_ok", true); + codec_install_dialog.set_body("Codec installation Completed Successfully!"); + first_setup_codec_button.remove_css_class("suggested-action"); + first_setup_codec_skip_button.set_label("Next"); + first_setup_codec_skip_button.add_css_class("suggested-action"); + } else { + first_setup_codec_skip_button.remove_css_class("suggested-action"); + first_setup_codec_skip_button.set_label("Skip Codec Installation"); + first_setup_codec_button.add_css_class("suggested-action"); + codec_install_dialog.set_response_enabled("codec_install_dialog_ok", true); + codec_install_dialog.set_body("Codec installation Failed!\nPlease try again."); + } + } + })); + + codec_install_log_terminal_buffer.connect_changed(clone!(@weak codec_install_log_terminal, @weak codec_install_log_terminal_buffer,@weak codec_install_log_terminal_scroll => move |_|{ + if codec_install_log_terminal_scroll.vadjustment().upper() - codec_install_log_terminal_scroll.vadjustment().value() > 100.0 { + codec_install_log_terminal_scroll.vadjustment().set_value(codec_install_log_terminal_scroll.vadjustment().upper()) + } + })); + + first_setup_codec_button.connect_clicked(clone!(@strong internet_connected_status, @weak codec_install_log_terminal,@weak codec_install_log_terminal_buffer, @weak codec_install_dialog,@weak first_setup_carousel => move |_| { + codec_install_log_terminal_buffer.delete(&mut codec_install_log_terminal_buffer.bounds().0, &mut codec_install_log_terminal_buffer.bounds().1); + codec_install_dialog.set_response_enabled("codec_install_dialog_ok", false); + codec_install_dialog.set_body(""); + codec_install_dialog.present(); + // The long running operation runs now in a separate thread + gio::spawn_blocking(clone!(@strong log_loop_sender, @strong log_status_loop_sender => move || { + let command = codec_install(log_loop_sender); + match command { + Ok(_) => { + println!("Status: Codec install Successful"); + log_status_loop_sender.send_blocking(true).expect("The channel needs to be open."); + } + Err(_) => { + println!("Status: Codec install Failed"); + log_status_loop_sender.send_blocking(false).expect("The channel needs to be open."); + } + } + })); + })); + + first_setup_codec_skip_button.connect_clicked(clone!(@weak first_setup_carousel => move |_|{ + first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(6), true); + })); +} diff --git a/src/first_setup/driver_carousel.rs b/src/first_setup/driver_carousel.rs new file mode 100644 index 0000000..a12b344 --- /dev/null +++ b/src/first_setup/driver_carousel.rs @@ -0,0 +1,126 @@ +use std::cell::RefCell; +use std::rc::Rc; +// GTK crates +use adw::ffi::ADW_TOOLBAR_FLAT; +use adw::prelude::*; +use adw::*; +use gdk::Display; +use glib::*; +/// Use all gtk4 libraries (gtk4 -> gtk because cargo) +/// Use all libadwaita libraries (libadwaita -> adw because cargo) +use gtk::prelude::*; +use gtk::*; +use vte::prelude::*; +use vte::*; + +use std::{thread, time}; + +use std::{ + error::Error, + io::Error as ErrorIO, + io::{ErrorKind, Read, Write}, + process::{Command, Stdio}, +}; + +use duct::cmd; +use os_pipe::*; +use std::io::prelude::*; +use std::io::BufReader; + +pub fn driver_carousel( + first_setup_carousel: &adw::Carousel, + internet_connected: &Rc>, +) { + let internet_connected_status = internet_connected.clone(); + + let (internet_loop_sender, internet_loop_receiver) = async_channel::unbounded(); + let internet_loop_sender = internet_loop_sender.clone(); + // The long running operation runs now in a separate thread + gio::spawn_blocking(move || loop { + thread::sleep(time::Duration::from_secs(1)); + internet_loop_sender + .send_blocking(true) + .expect("The channel needs to be open."); + }); + + let first_setup_driver_box = gtk::Box::builder() + // that puts items vertically + .orientation(Orientation::Vertical) + .valign(gtk::Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + + let first_setup_driver_box_text = adw::StatusPage::builder() + .icon_name("audio-card") + .title("Hardware Drivers") + .description("You can install drivers such as the NVIDIA proprietary drivers and CPU microcode.") + .build(); + first_setup_driver_box_text.add_css_class("compact"); + + let first_setup_driver_button = gtk::Button::builder() + .label("Open Driver manager") + .sensitive(false) + .build(); + + first_setup_driver_button.add_css_class("suggested-action"); + first_setup_driver_button.add_css_class("pill"); + + let first_setup_driver_skip_button = gtk::Button::builder() + .label("Skip Driver installation") + .sensitive(true) + .width_request(25) + .build(); + + let first_setup_driver_buttons_box = gtk::Box::builder() + .orientation(Orientation::Horizontal) + .halign(gtk::Align::Center) + .valign(gtk::Align::End) + .vexpand(true) + .hexpand(true) + .margin_end(15) + .margin_start(15) + .margin_bottom(15) + .margin_top(15) + .spacing(80) + .build(); + + first_setup_driver_skip_button.add_css_class("pill"); + + first_setup_driver_buttons_box.append(&first_setup_driver_button); + first_setup_driver_buttons_box.append(&first_setup_driver_skip_button); + + first_setup_driver_box.append(&first_setup_driver_box_text); + first_setup_driver_box.append(&first_setup_driver_buttons_box); + + first_setup_carousel.append(&first_setup_driver_box); + + let internet_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + internet_loop_context.spawn_local( + clone!(@strong internet_connected_status, @weak first_setup_driver_button => async move { + while let Ok(_state) = internet_loop_receiver.recv().await { + if *internet_connected_status.borrow_mut() == true { + first_setup_driver_button.set_sensitive(true); + first_setup_driver_button.set_label("Open Driver manager"); + } else { + first_setup_driver_button.set_sensitive(false); + first_setup_driver_button.set_label("Disabled.. Network setup was skipped"); + } + } + }), + ); + + first_setup_driver_button.connect_clicked(clone!(@weak first_setup_carousel, @weak first_setup_driver_button, @weak first_setup_driver_skip_button => move |_| { + Command::new("pika-drivers") + .spawn() + .expect("pika-drivers failed to start"); + first_setup_driver_button.remove_css_class("suggested-action"); + first_setup_driver_skip_button.set_label("Next"); + first_setup_driver_skip_button.add_css_class("suggested-action"); + })); + + first_setup_driver_skip_button.connect_clicked(clone!(@weak first_setup_carousel => move |_|{ + first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(5), true); + })); +} diff --git a/src/first_setup/first_setup.rs b/src/first_setup/first_setup.rs index a269800..fe0e0f8 100644 --- a/src/first_setup/first_setup.rs +++ b/src/first_setup/first_setup.rs @@ -19,6 +19,9 @@ use crate::first_setup::initial_carousel::initial_carousel; use crate::first_setup::internet_carousel::internet_carousel; use crate::first_setup::update_carousel::update_carousel; use crate::first_setup::user_carousel::user_carousel; +use crate::first_setup::driver_carousel::driver_carousel; +use crate::first_setup::codec_carousel::codec_carousel; +use crate::first_setup::gameutils_carousel::gameutils_carousel; pub fn first_setup(window: &adw::ApplicationWindow) { let first_setup_carousel = adw::Carousel::builder() @@ -63,6 +66,9 @@ pub fn first_setup(window: &adw::ApplicationWindow) { internet_carousel(&first_setup_carousel, &internet_connected, &window); user_carousel(&first_setup_carousel); update_carousel(&first_setup_carousel, &internet_connected, &window); + driver_carousel(&first_setup_carousel, &internet_connected); + codec_carousel(&first_setup_carousel, &internet_connected, &window); + gameutils_carousel(&first_setup_carousel, &internet_connected, &window); // Add file to window window.set_content(Some(&first_setup_window_toolbarview)) diff --git a/src/first_setup/gameutils_carousel.rs b/src/first_setup/gameutils_carousel.rs new file mode 100644 index 0000000..7fc8fbd --- /dev/null +++ b/src/first_setup/gameutils_carousel.rs @@ -0,0 +1,230 @@ +use std::cell::RefCell; +use std::rc::Rc; +// GTK crates +use adw::ffi::ADW_TOOLBAR_FLAT; +use adw::prelude::*; +use adw::*; +use gdk::Display; +use glib::*; +/// Use all gtk4 libraries (gtk4 -> gtk because cargo) +/// Use all libadwaita libraries (libadwaita -> adw because cargo) +use gtk::prelude::*; +use gtk::*; +use vte::prelude::*; +use vte::*; + +use std::{thread, time}; + +use std::{ + error::Error, + io::Error as ErrorIO, + io::{ErrorKind, Read, Write}, + process::{Command, Stdio}, +}; + +use duct::cmd; +use os_pipe::*; +use std::io::prelude::*; +use std::io::BufReader; + +const GAMEUTILS_INSTALL_PROG: &str = " +#! /bin/bash +set -e +sudo apt update -y && sudo apt install pika-gameutils-meta -y +"; + +fn gameutils_install( + log_loop_sender: async_channel::Sender, +) -> Result<(), std::boxed::Box> { + let (pipe_reader, pipe_writer) = os_pipe::pipe()?; + let child = cmd!("bash", "-c", GAMEUTILS_INSTALL_PROG) + .stderr_to_stdout() + .stdout_file(pipe_writer) + .start()?; + for line in BufReader::new(pipe_reader).lines() { + log_loop_sender + .send_blocking(line?) + .expect("Channel needs to be opened.") + } + child.wait()?; + + Ok(()) +} + +pub fn gameutils_carousel( + first_setup_carousel: &adw::Carousel, + internet_connected: &Rc>, + window: &adw::ApplicationWindow, +) { + let internet_connected_status = internet_connected.clone(); + + let (internet_loop_sender, internet_loop_receiver) = async_channel::unbounded(); + let internet_loop_sender = internet_loop_sender.clone(); + // The long running operation runs now in a separate thread + gio::spawn_blocking(move || loop { + thread::sleep(time::Duration::from_secs(1)); + internet_loop_sender + .send_blocking(true) + .expect("The channel needs to be open."); + }); + + let (log_loop_sender, log_loop_receiver) = async_channel::unbounded(); + let log_loop_sender: async_channel::Sender = log_loop_sender.clone(); + + let (log_status_loop_sender, log_status_loop_receiver) = async_channel::unbounded(); + let log_status_loop_sender: async_channel::Sender = log_status_loop_sender.clone(); + + let first_setup_gameutils_box = gtk::Box::builder() + // that puts items vertically + .orientation(Orientation::Vertical) + .valign(gtk::Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + + let first_setup_gameutils_box_text = adw::StatusPage::builder() + .icon_name("input-gaming") + .title("PikaOS Gaming Meta Package") + .description("Would you like the PikaOS Gaming Meta Package?\n(essential for gaming thus strongly recommended)") + .build(); + first_setup_gameutils_box_text.add_css_class("compact"); + + let first_setup_gameutils_button = gtk::Button::builder() + .label("Install Meta Package") + .sensitive(false) + .build(); + + first_setup_gameutils_button.add_css_class("suggested-action"); + first_setup_gameutils_button.add_css_class("pill"); + + let first_setup_gameutils_skip_button = gtk::Button::builder() + .label("Skip Meta Package Installation") + .sensitive(true) + .width_request(25) + .build(); + + let first_setup_gameutils_buttons_box = gtk::Box::builder() + .orientation(Orientation::Horizontal) + .halign(gtk::Align::Center) + .valign(gtk::Align::End) + .vexpand(true) + .hexpand(true) + .margin_end(15) + .margin_start(15) + .margin_bottom(15) + .margin_top(15) + .spacing(80) + .build(); + + first_setup_gameutils_skip_button.add_css_class("pill"); + + let gameutils_install_log_terminal_buffer = gtk::TextBuffer::builder().build(); + + let gameutils_install_log_terminal = gtk::TextView::builder() + .vexpand(true) + .hexpand(true) + .editable(false) + .buffer(&gameutils_install_log_terminal_buffer) + .build(); + + let gameutils_install_log_terminal_scroll = gtk::ScrolledWindow::builder() + .width_request(400) + .height_request(200) + .vexpand(true) + .hexpand(true) + .child(&gameutils_install_log_terminal) + .build(); + + let gameutils_install_dialog = adw::MessageDialog::builder() + .transient_for(window) + .hide_on_close(true) + .extra_child(&gameutils_install_log_terminal_scroll) + .width_request(400) + .height_request(200) + .heading("Meta Package installation Log") + .build(); + gameutils_install_dialog.add_response("gameutils_install_dialog_ok", "Ok"); + + first_setup_gameutils_buttons_box.append(&first_setup_gameutils_button); + first_setup_gameutils_buttons_box.append(&first_setup_gameutils_skip_button); + + first_setup_gameutils_box.append(&first_setup_gameutils_box_text); + first_setup_gameutils_box.append(&first_setup_gameutils_buttons_box); + + first_setup_carousel.append(&first_setup_gameutils_box); + + let internet_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + internet_loop_context.spawn_local( + clone!(@strong internet_connected_status, @weak first_setup_gameutils_button => async move { + while let Ok(_state) = internet_loop_receiver.recv().await { + if *internet_connected_status.borrow_mut() == true { + first_setup_gameutils_button.set_sensitive(true); + first_setup_gameutils_button.set_label("Install Meta Package"); + } else { + first_setup_gameutils_button.set_sensitive(false); + first_setup_gameutils_button.set_label("Disabled.. Network setup was skipped"); + } + } + }), + ); + + let log_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + log_loop_context.spawn_local(clone!(@weak gameutils_install_log_terminal_buffer, @weak gameutils_install_dialog => async move { + while let Ok(state) = log_loop_receiver.recv().await { + gameutils_install_log_terminal_buffer.insert(&mut gameutils_install_log_terminal_buffer.end_iter(), &("\n".to_string() + &state)) + } + })); + + let log_status_loop_context = MainContext::default(); + // The main loop executes the asynchronous block + log_status_loop_context.spawn_local(clone!(@weak gameutils_install_dialog, @weak first_setup_gameutils_button, @weak first_setup_gameutils_skip_button => async move { + while let Ok(state) = log_status_loop_receiver.recv().await { + if state == true { + gameutils_install_dialog.set_response_enabled("gameutils_install_dialog_ok", true); + gameutils_install_dialog.set_body("Meta Package installation Completed Successfully!"); + first_setup_gameutils_button.remove_css_class("suggested-action"); + first_setup_gameutils_skip_button.set_label("Next"); + first_setup_gameutils_skip_button.add_css_class("suggested-action"); + } else { + first_setup_gameutils_skip_button.remove_css_class("suggested-action"); + first_setup_gameutils_skip_button.set_label("Skip Meta Package Installation"); + first_setup_gameutils_button.add_css_class("suggested-action"); + gameutils_install_dialog.set_response_enabled("gameutils_install_dialog_ok", true); + gameutils_install_dialog.set_body("Meta Package installation Failed!\nPlease try again."); + } + } + })); + + gameutils_install_log_terminal_buffer.connect_changed(clone!(@weak gameutils_install_log_terminal, @weak gameutils_install_log_terminal_buffer,@weak gameutils_install_log_terminal_scroll => move |_|{ + if gameutils_install_log_terminal_scroll.vadjustment().upper() - gameutils_install_log_terminal_scroll.vadjustment().value() > 100.0 { + gameutils_install_log_terminal_scroll.vadjustment().set_value(gameutils_install_log_terminal_scroll.vadjustment().upper()) + } + })); + + first_setup_gameutils_button.connect_clicked(clone!(@strong internet_connected_status, @weak gameutils_install_log_terminal,@weak gameutils_install_log_terminal_buffer, @weak gameutils_install_dialog,@weak first_setup_carousel => move |_| { + gameutils_install_log_terminal_buffer.delete(&mut gameutils_install_log_terminal_buffer.bounds().0, &mut gameutils_install_log_terminal_buffer.bounds().1); + gameutils_install_dialog.set_response_enabled("gameutils_install_dialog_ok", false); + gameutils_install_dialog.set_body(""); + gameutils_install_dialog.present(); + // The long running operation runs now in a separate thread + gio::spawn_blocking(clone!(@strong log_loop_sender, @strong log_status_loop_sender => move || { + let command = gameutils_install(log_loop_sender); + match command { + Ok(_) => { + println!("Status: gameutils install Successful"); + log_status_loop_sender.send_blocking(true).expect("The channel needs to be open."); + } + Err(_) => { + println!("Status: gameutils install Failed"); + log_status_loop_sender.send_blocking(false).expect("The channel needs to be open."); + } + } + })); + })); + + first_setup_gameutils_skip_button.connect_clicked(clone!(@weak first_setup_carousel => move |_|{ + first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(7), true); + })); +} diff --git a/src/first_setup/mod.rs b/src/first_setup/mod.rs index 94ffa40..2a36736 100644 --- a/src/first_setup/mod.rs +++ b/src/first_setup/mod.rs @@ -3,3 +3,6 @@ pub mod initial_carousel; pub mod internet_carousel; pub mod update_carousel; pub mod user_carousel; +pub mod driver_carousel; +pub mod codec_carousel; +pub mod gameutils_carousel; diff --git a/src/first_setup/update_carousel.rs b/src/first_setup/update_carousel.rs index 6a21263..331d739 100644 --- a/src/first_setup/update_carousel.rs +++ b/src/first_setup/update_carousel.rs @@ -77,7 +77,6 @@ pub fn update_carousel( let first_setup_update_box = gtk::Box::builder() // that puts items vertically .orientation(Orientation::Vertical) - .vexpand(true) .valign(gtk::Align::Center) .hexpand(true) .vexpand(true) @@ -180,12 +179,18 @@ pub fn update_carousel( let log_status_loop_context = MainContext::default(); // The main loop executes the asynchronous block - log_status_loop_context.spawn_local(clone!(@weak system_update_dialog => async move { + log_status_loop_context.spawn_local(clone!(@weak system_update_dialog, @weak first_setup_update_button, @weak first_setup_update_skip_button => async move { while let Ok(state) = log_status_loop_receiver.recv().await { if state == true { system_update_dialog.set_response_enabled("system_update_dialog_ok", true); system_update_dialog.set_body("Update Completed Successfully!"); + first_setup_update_button.remove_css_class("suggested-action"); + first_setup_update_skip_button.set_label("Next"); + first_setup_update_skip_button.add_css_class("suggested-action"); } else { + first_setup_update_skip_button.remove_css_class("suggested-action"); + first_setup_update_skip_button.set_label("Skip"); + first_setup_update_button.add_css_class("suggested-action"); system_update_dialog.set_response_enabled("system_update_dialog_ok", true); system_update_dialog.set_body("Update Failed!\nPlease try again."); }