Adaptive Layout
This commit is contained in:
parent
cfee89e952
commit
a8d62bdb0d
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<schemalist>
|
||||
<schema id="com.github.pikaos-linux.pikawelcome" path="/com/github/pikaos-linux/pikafirstsetup/">
|
||||
<schema id="com.github.pikaos-linux.pikawelcome" path="/com/github/pikaos-linux/pikawelcome/">
|
||||
<key name="window-width" type="i">
|
||||
<default>700</default>
|
||||
<summary>Default window width</summary>
|
||||
@ -13,5 +13,12 @@
|
||||
<default>false</default>
|
||||
<summary>Default window maximized behaviour</summary>
|
||||
</key>
|
||||
<key type="b" name="startup-show">
|
||||
<default>true</default>
|
||||
<summary>Show PikaOS Welcome on startup.</summary>
|
||||
<description>
|
||||
Show PikaOS Welcome on startup.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
2
debian/control
vendored
2
debian/control
vendored
@ -15,4 +15,4 @@ Rules-Requires-Root: no
|
||||
Package: pika-welcome
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}
|
||||
Description: A frontend in GTK 4 and Libadwaita for initial user setup in pika.
|
||||
Description: A frontend in GTK 4 and Libadwaita for Post install PikaOS setup.
|
||||
|
@ -1,41 +1,56 @@
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
use gtk::Orientation;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
|
||||
// application crates
|
||||
/// first setup crates
|
||||
use crate::config::*;
|
||||
use crate::first_setup::*;
|
||||
use crate::save_window_size::save_window_size;
|
||||
use crate::welcome_content_page::welcome_content_page;
|
||||
|
||||
pub fn build_ui(app: &adw::Application) {
|
||||
// setup glib
|
||||
gtk::glib::set_prgname(Some(t!("app_name").to_string()));
|
||||
glib::set_application_name(&t!("app_name").to_string());
|
||||
let glib_settings = gio::Settings::new(APP_ID);
|
||||
|
||||
let content_box = gtk::Box::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
|
||||
// create the main Application window
|
||||
let window = adw::ApplicationWindow::builder()
|
||||
// The text on the titlebar
|
||||
.title(t!("app_name"))
|
||||
// link it to the application "app"
|
||||
.application(app)
|
||||
// Add the box called "window_box" to it
|
||||
// Application icon
|
||||
.content(&content_box)
|
||||
.icon_name(APP_ICON)
|
||||
// Minimum Size/Default
|
||||
.width_request(700)
|
||||
.default_width(glib_settings.int("window-width"))
|
||||
.default_height(glib_settings.int("window-height"))
|
||||
.width_request(300)
|
||||
.height_request(500)
|
||||
// Hide window instead of destroy
|
||||
.hide_on_close(true)
|
||||
.deletable(false)
|
||||
// Startup
|
||||
.startup_id(APP_ID)
|
||||
// build the window
|
||||
.build();
|
||||
|
||||
first_setup(&window);
|
||||
if glib_settings.boolean("is-maximized") == true {
|
||||
window.maximize()
|
||||
}
|
||||
|
||||
window.connect_close_request(move |window| {
|
||||
if let Some(application) = window.application() {
|
||||
save_window_size(&window, &glib_settings);
|
||||
application.remove_window(window);
|
||||
}
|
||||
glib::Propagation::Proceed
|
||||
});
|
||||
|
||||
//
|
||||
welcome_content_page(&window, &content_box);
|
||||
// show the window
|
||||
window.present()
|
||||
}
|
@ -1,223 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
|
||||
|
||||
|
||||
use std::{thread, time};
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
};
|
||||
|
||||
use duct::cmd;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
|
||||
const CODEC_INSTALL_PROG: &str = "
|
||||
#! /bin/bash
|
||||
set -e
|
||||
/usr/lib/pika/pika-welcome/scripts/pika-sudo.sh apt update -y && /usr/lib/pika/pika-welcome/scripts/pika-sudo.sh apt install pika-codecs-meta -y
|
||||
";
|
||||
|
||||
fn codec_install(
|
||||
log_loop_sender: async_channel::Sender<String>,
|
||||
) -> Result<(), std::boxed::Box<dyn Error + Send + Sync>> {
|
||||
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<RefCell<bool>>,
|
||||
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<String> = log_loop_sender.clone();
|
||||
|
||||
let (log_status_loop_sender, log_status_loop_receiver) = async_channel::unbounded();
|
||||
let log_status_loop_sender: async_channel::Sender<bool> = 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(t!("first_setup_codec_box_text_title"))
|
||||
.description(t!("first_setup_codec_box_text_description"))
|
||||
.build();
|
||||
first_setup_codec_box_text.add_css_class("compact");
|
||||
|
||||
let first_setup_codec_button = gtk::Button::builder()
|
||||
.label(t!("first_setup_codec_button_label"))
|
||||
.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(t!("first_setup_codec_skip_button_label"))
|
||||
.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(t!("codec_install_dialog_heading"))
|
||||
.build();
|
||||
codec_install_dialog.add_response("codec_install_dialog_ok", &t!("system_update_dialog_ok_label"));
|
||||
|
||||
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(&t!("first_setup_codec_button_label"));
|
||||
} else {
|
||||
first_setup_codec_button.set_sensitive(false);
|
||||
first_setup_codec_button.set_label(&t!("internet_network_disabled"));
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
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(&t!("codec_install_dialog_success_true"));
|
||||
first_setup_codec_button.remove_css_class("suggested-action");
|
||||
first_setup_codec_skip_button.set_label(&t!("internet_next_button_label"));
|
||||
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(&t!("first_setup_codec_skip_button_label"));
|
||||
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(&t!("codec_install_dialog_success_false"));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
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);
|
||||
}));
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
|
||||
|
||||
|
||||
use std::{thread, time};
|
||||
|
||||
use std::{
|
||||
process::{Command},
|
||||
};
|
||||
|
||||
pub fn driver_carousel(
|
||||
first_setup_carousel: &adw::Carousel,
|
||||
internet_connected: &Rc<RefCell<bool>>,
|
||||
) {
|
||||
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(t!("first_setup_driver_box_text_title"))
|
||||
.description(t!("first_setup_driver_box_text_description"))
|
||||
.build();
|
||||
first_setup_driver_box_text.add_css_class("compact");
|
||||
|
||||
let first_setup_driver_button = gtk::Button::builder()
|
||||
.label(t!("first_setup_driver_button_label"))
|
||||
.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(t!("first_setup_driver_skip_button_label"))
|
||||
.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(&t!("first_setup_driver_button_label"));
|
||||
} else {
|
||||
first_setup_driver_button.set_sensitive(false);
|
||||
first_setup_driver_button.set_label(&t!("internet_network_disabled"));
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
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(&t!("internet_next_button_label"));
|
||||
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);
|
||||
}));
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
|
||||
|
||||
|
||||
use duct::cmd;
|
||||
|
||||
const REBOOT_PROG: &str = r###"
|
||||
#! /bin/bash
|
||||
userdel -r -f pikaos
|
||||
reboot
|
||||
"###;
|
||||
|
||||
pub fn final_carousel(first_setup_carousel: &adw::Carousel) {
|
||||
let first_setup_final_box = gtk::Box::builder()
|
||||
// that puts items vertically
|
||||
.orientation(Orientation::Vertical)
|
||||
.vexpand(true)
|
||||
.valign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
|
||||
let first_setup_final_box_text = adw::StatusPage::builder()
|
||||
.icon_name("emblem-favorite")
|
||||
.title(t!("first_setup_final_box_text_title"))
|
||||
.description(t!("first_setup_final_box_text_description"))
|
||||
.build();
|
||||
first_setup_final_box_text.add_css_class("compact");
|
||||
|
||||
let first_setup_start_button = gtk::Button::builder()
|
||||
.label(t!("first_setup_reboot_button_label"))
|
||||
.halign(Align::Center)
|
||||
.build();
|
||||
|
||||
first_setup_start_button.add_css_class("suggested-action");
|
||||
first_setup_start_button.add_css_class("pill");
|
||||
|
||||
first_setup_final_box.append(&first_setup_final_box_text);
|
||||
first_setup_final_box.append(&first_setup_start_button);
|
||||
|
||||
first_setup_carousel.append(&first_setup_final_box);
|
||||
|
||||
first_setup_start_button.connect_clicked( move |_| {
|
||||
let _ = cmd!("/usr/lib/pika/pika-welcome/scripts/pika-sudo.sh", "bash", "-c", REBOOT_PROG).read();
|
||||
});
|
||||
}
|
@ -1,223 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
|
||||
|
||||
|
||||
use std::{thread, time};
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
};
|
||||
|
||||
use duct::cmd;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
|
||||
const GAMEUTILS_INSTALL_PROG: &str = "
|
||||
#! /bin/bash
|
||||
set -e
|
||||
/usr/lib/pika/pika-welcome/scripts/pika-sudo.sh apt update -y && /usr/lib/pika/pika-welcome/scripts/pika-sudo.sh apt install pika-gameutils-meta -y
|
||||
";
|
||||
|
||||
fn gameutils_install(
|
||||
log_loop_sender: async_channel::Sender<String>,
|
||||
) -> Result<(), std::boxed::Box<dyn Error + Send + Sync>> {
|
||||
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<RefCell<bool>>,
|
||||
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<String> = log_loop_sender.clone();
|
||||
|
||||
let (log_status_loop_sender, log_status_loop_receiver) = async_channel::unbounded();
|
||||
let log_status_loop_sender: async_channel::Sender<bool> = 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(t!("first_setup_gameutils_box_text_title"))
|
||||
.description(t!("first_setup_gameutils_box_text_description"))
|
||||
.build();
|
||||
first_setup_gameutils_box_text.add_css_class("compact");
|
||||
|
||||
let first_setup_gameutils_button = gtk::Button::builder()
|
||||
.label(t!("first_setup_gameutils_button_label"))
|
||||
.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(t!("first_setup_gameutils_skip_button_label"))
|
||||
.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(t!("gameutils_install_dialog_heading"))
|
||||
.build();
|
||||
gameutils_install_dialog.add_response("gameutils_install_dialog_ok", &t!("system_update_dialog_ok_label"));
|
||||
|
||||
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(&t!("first_setup_gameutils_button_label"));
|
||||
} else {
|
||||
first_setup_gameutils_button.set_sensitive(false);
|
||||
first_setup_gameutils_button.set_label(&t!("internet_network_disabled"));
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
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(&t!("gameutils_install_dialog_success_true"));
|
||||
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(&t!("gameutils_install_dialog_success_false"));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
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);
|
||||
}));
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
|
||||
|
||||
use crate::config::{DISTRO_ICON};
|
||||
|
||||
pub fn initial_carousel(first_setup_carousel: &adw::Carousel) {
|
||||
let first_setup_initial_box = gtk::Box::builder()
|
||||
// that puts items vertically
|
||||
.orientation(Orientation::Vertical)
|
||||
.vexpand(true)
|
||||
.valign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
|
||||
let first_setup_initial_box_text = adw::StatusPage::builder()
|
||||
.icon_name(DISTRO_ICON)
|
||||
.title(t!("first_setup_initial_box_text_title"))
|
||||
.description(t!("first_setup_initial_box_text_description"))
|
||||
.build();
|
||||
first_setup_initial_box_text.add_css_class("compact");
|
||||
|
||||
let first_setup_start_button = gtk::Button::builder()
|
||||
.label(t!("first_setup_start_button_label"))
|
||||
.halign(Align::Center)
|
||||
.build();
|
||||
|
||||
first_setup_start_button.add_css_class("suggested-action");
|
||||
first_setup_start_button.add_css_class("pill");
|
||||
|
||||
first_setup_initial_box.append(&first_setup_initial_box_text);
|
||||
first_setup_initial_box.append(&first_setup_start_button);
|
||||
|
||||
first_setup_carousel.append(&first_setup_initial_box);
|
||||
|
||||
first_setup_start_button.connect_clicked(clone!(@weak first_setup_carousel => move |_| {
|
||||
first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(1), true)
|
||||
}));
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
|
||||
|
||||
|
||||
//use crate::check_internet_connection;
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub fn internet_carousel(
|
||||
first_setup_carousel: &adw::Carousel,
|
||||
internet_connected: &Rc<RefCell<bool>>,
|
||||
window: &adw::ApplicationWindow,
|
||||
) {
|
||||
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 {
|
||||
//match check_internet_connection() {
|
||||
// Ok(_) => {
|
||||
// internet_loop_sender.send_blocking(true).expect("The channel needs to be open.");
|
||||
// }
|
||||
// Err(_) => {
|
||||
// internet_loop_sender.send_blocking(false).expect("The channel needs to be open.");
|
||||
// }
|
||||
//}
|
||||
let check_internet_connection_cli = Command::new("ping")
|
||||
.arg("ppa.pika-os.com")
|
||||
.arg("-c 1")
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
if check_internet_connection_cli.status.success() {
|
||||
internet_loop_sender
|
||||
.send_blocking(true)
|
||||
.expect("The channel needs to be open.");
|
||||
} else {
|
||||
internet_loop_sender
|
||||
.send_blocking(false)
|
||||
.expect("The channel needs to be open.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let first_setup_internet_box = gtk::Box::builder()
|
||||
// that puts items vertically
|
||||
.orientation(Orientation::Vertical)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.margin_end(15)
|
||||
.margin_start(15)
|
||||
.margin_bottom(15)
|
||||
.margin_top(15)
|
||||
.build();
|
||||
|
||||
let first_setup_internet_box_text = adw::StatusPage::builder()
|
||||
.icon_name("network-cellular-acquiring")
|
||||
.title(t!("first_setup_internet_box_text_title"))
|
||||
.description(t!("first_setup_internet_box_text_description"))
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.valign(Align::Start)
|
||||
.build();
|
||||
first_setup_internet_box_text.add_css_class("compact");
|
||||
|
||||
let internet_skip_button = gtk::Button::builder()
|
||||
.label(t!("internet_skip_button_label"))
|
||||
.halign(Align::Center)
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
internet_skip_button.add_css_class("destructive-action");
|
||||
internet_skip_button.add_css_class("pill");
|
||||
|
||||
let internet_next_button = gtk::Button::builder()
|
||||
.label(t!("internet_next_button_label"))
|
||||
.halign(Align::Center)
|
||||
.sensitive(false)
|
||||
.build();
|
||||
|
||||
internet_next_button.add_css_class("suggested-action");
|
||||
internet_next_button.add_css_class("pill");
|
||||
|
||||
let internet_buttons_box = gtk::Box::builder()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.halign(Align::Center)
|
||||
.valign(Align::End)
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.margin_end(15)
|
||||
.margin_start(15)
|
||||
.margin_bottom(15)
|
||||
.margin_top(15)
|
||||
.spacing(80)
|
||||
.build();
|
||||
|
||||
let first_setup_internet_button_content_box = gtk::Box::builder()
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
|
||||
let first_setup_internet_button_content_text = gtk::Label::builder()
|
||||
.label(t!("first_setup_internet_button_content_text_label"))
|
||||
.build();
|
||||
|
||||
let first_setup_internet_button_content = adw::ButtonContent::builder()
|
||||
.label(t!("first_setup_internet_button_content_label"))
|
||||
.icon_name("network-wired")
|
||||
.build();
|
||||
|
||||
let first_setup_internet_button = gtk::Button::builder()
|
||||
.child(&first_setup_internet_button_content_box)
|
||||
.halign(Align::Center)
|
||||
.valign(Align::Center)
|
||||
.build();
|
||||
|
||||
internet_buttons_box.append(&internet_skip_button);
|
||||
internet_buttons_box.append(&internet_next_button);
|
||||
|
||||
first_setup_internet_button_content_box.append(&first_setup_internet_button_content);
|
||||
first_setup_internet_button_content_box.append(&first_setup_internet_button_content_text);
|
||||
|
||||
first_setup_carousel.append(&first_setup_internet_box);
|
||||
|
||||
first_setup_internet_box.append(&first_setup_internet_box_text);
|
||||
first_setup_internet_box.append(&first_setup_internet_button);
|
||||
first_setup_internet_box.append(&internet_buttons_box);
|
||||
|
||||
let first_setup_internet_skip_dialog = adw::MessageDialog::builder()
|
||||
.heading(t!("first_setup_internet_skip_dialog_heading"))
|
||||
.body(t!("first_setup_internet_skip_dialog_body"))
|
||||
.transient_for(window)
|
||||
.hide_on_close(true)
|
||||
.build();
|
||||
|
||||
first_setup_internet_skip_dialog.add_response("skip_cancel", &t!("first_setup_internet_skip_dialog_skip_cancel_label"));
|
||||
first_setup_internet_skip_dialog.add_response("skip_confirm", &t!("first_setup_internet_skip_dialog_skip_confirm_label"));
|
||||
first_setup_internet_skip_dialog
|
||||
.set_response_appearance("skip_confirm", adw::ResponseAppearance::Destructive);
|
||||
|
||||
let internet_connected_status = internet_connected.clone();
|
||||
|
||||
let internet_loop_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
internet_loop_context.spawn_local(clone!(@weak internet_skip_button, @weak internet_next_button, @weak first_setup_internet_box_text => async move {
|
||||
while let Ok(state) = internet_loop_receiver.recv().await {
|
||||
if state == true {
|
||||
internet_skip_button.set_sensitive(false);
|
||||
internet_next_button.set_sensitive(true);
|
||||
first_setup_internet_box_text.set_description(Some(&t!("first_setup_internet_box_text_description_true")));
|
||||
first_setup_internet_box_text.set_icon_name(Some("network-cellular-signal-excellent"));
|
||||
*internet_connected_status.borrow_mut()=true;
|
||||
} else {
|
||||
internet_next_button.set_sensitive(false);
|
||||
internet_skip_button.set_sensitive(true);
|
||||
first_setup_internet_box_text.set_description(Some(&t!("first_setup_internet_box_text_description_false")));
|
||||
first_setup_internet_box_text.set_icon_name(Some("network-cellular-offline"));
|
||||
*internet_connected_status.borrow_mut()=false;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
first_setup_internet_button.connect_clicked(move |_| {
|
||||
let env_xdg_session_desktop = env::var("XDG_SESSION_DESKTOP").unwrap();
|
||||
if env_xdg_session_desktop.contains("gnome") || env_xdg_session_desktop.contains("ubuntu") {
|
||||
Command::new("gnome-control-center")
|
||||
.arg("network")
|
||||
.spawn()
|
||||
.expect("gnome-control-center failed to start");
|
||||
} else {
|
||||
Command::new("nm-connection-editor")
|
||||
.spawn()
|
||||
.expect("nm-connection-editor failed to start");
|
||||
}
|
||||
});
|
||||
|
||||
internet_next_button.connect_clicked(clone!(@weak first_setup_carousel => move |_| {
|
||||
first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(2), true);
|
||||
}));
|
||||
|
||||
internet_skip_button.connect_clicked(
|
||||
clone!(@weak first_setup_carousel, @weak first_setup_internet_skip_dialog => move |_| {
|
||||
first_setup_internet_skip_dialog.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
if choice == "skip_confirm" {
|
||||
first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(2), true);
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
|
||||
|
||||
|
||||
//
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
// carousel crates
|
||||
mod initial_carousel;
|
||||
mod internet_carousel;
|
||||
mod user_carousel;
|
||||
mod update_carousel;
|
||||
mod driver_carousel;
|
||||
mod codec_carousel;
|
||||
mod gameutils_carousel;
|
||||
mod final_carousel;
|
||||
|
||||
use initial_carousel::*;
|
||||
use internet_carousel::*;
|
||||
use user_carousel::*;
|
||||
use update_carousel::*;
|
||||
use driver_carousel::*;
|
||||
use codec_carousel::*;
|
||||
use gameutils_carousel::*;
|
||||
use final_carousel::*;
|
||||
|
||||
pub fn first_setup(window: &adw::ApplicationWindow) {
|
||||
let first_setup_carousel = adw::Carousel::builder()
|
||||
.allow_long_swipes(false)
|
||||
.allow_mouse_drag(false)
|
||||
.allow_scroll_wheel(false)
|
||||
.interactive(false)
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.build();
|
||||
|
||||
let first_setup_carousel_indicator = adw::CarouselIndicatorDots::builder()
|
||||
.carousel(&first_setup_carousel)
|
||||
.build();
|
||||
|
||||
let first_setup_window_headerbar_back_button = gtk::Button::builder().label(t!("first_setup_window_headerbar_back_button_label")).build();
|
||||
|
||||
let first_setup_window_headerbar = adw::HeaderBar::builder()
|
||||
.show_start_title_buttons(true)
|
||||
.title_widget(&first_setup_carousel_indicator)
|
||||
.build();
|
||||
|
||||
let first_setup_window_toolbarview = adw::ToolbarView::builder()
|
||||
.top_bar_style(ToolbarStyle::Flat)
|
||||
.content(&first_setup_carousel)
|
||||
.build();
|
||||
|
||||
let internet_connected = Rc::new(RefCell::new(false));
|
||||
|
||||
first_setup_window_headerbar.pack_start(&first_setup_window_headerbar_back_button);
|
||||
first_setup_window_toolbarview.add_top_bar(&first_setup_window_headerbar);
|
||||
|
||||
first_setup_window_headerbar_back_button.connect_clicked(clone!(@weak first_setup_carousel => move |_| {
|
||||
let first_setup_prev_page = first_setup_carousel.position() - 1.0;
|
||||
first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(first_setup_prev_page as u32), true)
|
||||
}));
|
||||
|
||||
// CAROUSELS
|
||||
|
||||
// Initial Carousel
|
||||
initial_carousel(&first_setup_carousel);
|
||||
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);
|
||||
final_carousel(&first_setup_carousel);
|
||||
|
||||
// Add file to window
|
||||
window.set_content(Some(&first_setup_window_toolbarview))
|
||||
}
|
@ -1,324 +0,0 @@
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
|
||||
//
|
||||
use regex::Regex;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::{thread, time};
|
||||
use duct::cmd;
|
||||
|
||||
const USER_CREATE_PROG: &str = r###"
|
||||
#! /bin/bash
|
||||
USERNAME="$0"
|
||||
PASSWORD="$1"
|
||||
FULLNAME="$2"
|
||||
HOSTNAME="$3"
|
||||
adduser --quiet --disabled-password --shell /bin/bash --gecos "${FULLNAME}" "${USERNAME}"
|
||||
echo "${USERNAME}":"${PASSWORD}" | chpasswd
|
||||
usermod -a -G sudo "${USERNAME}"
|
||||
mkdir -p /home/"${USERNAME}"
|
||||
cp -rvf /etc/skel/.* /home/"${USERNAME}"/ || true
|
||||
chown -R "${USERNAME}":"${USERNAME}" /home/"${USERNAME}"
|
||||
usermod -a -G adm,cdrom,sudo,render,dip,video,plugdev,input,render,lpadmin "${USERNAME}"
|
||||
rm -rf /etc/sddm.conf.d/zautologin.conf || true
|
||||
hostnamectl set-hostname "${HOSTNAME}"
|
||||
echo "127.0.0.1 ${HOSTNAME}" >> /etc/hosts
|
||||
"###;
|
||||
|
||||
fn only_alphanumeric(input: &str) -> bool {
|
||||
return input.chars().all(|c| c.is_alphanumeric());
|
||||
}
|
||||
|
||||
fn uppercase_first_letter(s: &str) -> String {
|
||||
let mut c = s.chars();
|
||||
match c.next() {
|
||||
None => String::new(),
|
||||
Some(f) => f.to_uppercase().chain(c).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn user_carousel(first_setup_carousel: &adw::Carousel) {
|
||||
let user_info_username_valid = Rc::new(RefCell::new(false));
|
||||
let user_info_full_name_valid = Rc::new(RefCell::new(false));
|
||||
let user_info_hostname_valid = Rc::new(RefCell::new(false));
|
||||
let user_info_passwords_valid = Rc::new(RefCell::new(false));
|
||||
|
||||
let (user_loop_sender, user_loop_receiver) = async_channel::unbounded();
|
||||
let user_loop_sender = user_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));
|
||||
user_loop_sender
|
||||
.send_blocking(true)
|
||||
.expect("The channel needs to be open.");
|
||||
});
|
||||
|
||||
let first_setup_user_box = gtk::Box::builder()
|
||||
// that puts items vertically
|
||||
.orientation(Orientation::Vertical)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.margin_end(15)
|
||||
.margin_start(15)
|
||||
.margin_bottom(15)
|
||||
.margin_top(15)
|
||||
.build();
|
||||
|
||||
let first_setup_user_box_text = adw::StatusPage::builder()
|
||||
.title(t!("first_setup_user_box_text_title"))
|
||||
.description(t!("first_setup_user_box_text_description"))
|
||||
.hexpand(true)
|
||||
.valign(Align::Start)
|
||||
.build();
|
||||
first_setup_user_box_text.add_css_class("compact");
|
||||
|
||||
let user_info_box = gtk::Box::builder()
|
||||
.orientation(Orientation::Vertical)
|
||||
.valign(Align::Center)
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.build();
|
||||
|
||||
let user_info_box_clamp = adw::Clamp::builder()
|
||||
.child(&user_info_box)
|
||||
.maximum_size(500)
|
||||
.build();
|
||||
|
||||
let user_info_username = adw::EntryRow::builder()
|
||||
.hexpand(true)
|
||||
.title(t!("user_info_username_title"))
|
||||
.input_purpose(InputPurpose::Alpha)
|
||||
.input_hints(InputHints::LOWERCASE)
|
||||
.build();
|
||||
|
||||
let user_info_full_name = adw::EntryRow::builder()
|
||||
.hexpand(true)
|
||||
.title(t!("user_info_full_name_title"))
|
||||
.input_purpose(InputPurpose::Name)
|
||||
.build();
|
||||
|
||||
let user_info_password = adw::PasswordEntryRow::builder()
|
||||
.hexpand(true)
|
||||
.title(t!("user_info_password_title"))
|
||||
.build();
|
||||
|
||||
let user_info_hostname = adw::EntryRow::builder()
|
||||
.hexpand(true)
|
||||
.title(t!("user_info_hostname_title"))
|
||||
.build();
|
||||
|
||||
let user_info_password_verify = adw::PasswordEntryRow::builder()
|
||||
.hexpand(true)
|
||||
.title(t!("user_info_password_verify_title"))
|
||||
.build();
|
||||
|
||||
let user_info_password_verify_revealer = gtk::Revealer::builder()
|
||||
.child(&user_info_password_verify)
|
||||
.reveal_child(false)
|
||||
.transition_type(RevealerTransitionType::SwingDown)
|
||||
.build();
|
||||
|
||||
let user_info_avatar = adw::Avatar::builder()
|
||||
.show_initials(true)
|
||||
.size(128)
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.build();
|
||||
|
||||
let _user_info_avatar_full_name_binding = user_info_full_name
|
||||
.bind_property("text", &user_info_avatar, "text")
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
let user_info_listbox = gtk::ListBox::builder()
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.build();
|
||||
user_info_listbox.add_css_class("boxed-list");
|
||||
|
||||
let error_label = gtk::Label::builder()
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.visible(false)
|
||||
.build();
|
||||
|
||||
error_label.add_css_class("red-text");
|
||||
|
||||
let user_next_button = gtk::Button::builder()
|
||||
.label(t!("internet_next_button_label"))
|
||||
.sensitive(false)
|
||||
.halign(Align::Center)
|
||||
.valign(Align::End)
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.build();
|
||||
|
||||
user_next_button.add_css_class("suggested-action");
|
||||
user_next_button.add_css_class("pill");
|
||||
|
||||
first_setup_carousel.append(&first_setup_user_box);
|
||||
|
||||
user_info_listbox.append(&user_info_username);
|
||||
user_info_listbox.append(&user_info_full_name);
|
||||
user_info_listbox.append(&user_info_hostname);
|
||||
user_info_listbox.append(&user_info_password);
|
||||
user_info_listbox.append(&user_info_password_verify_revealer);
|
||||
|
||||
user_info_box.append(&user_info_avatar);
|
||||
user_info_box.append(&user_info_listbox);
|
||||
|
||||
first_setup_user_box.append(&first_setup_user_box_text);
|
||||
first_setup_user_box.append(&user_info_box_clamp);
|
||||
first_setup_user_box.append(&error_label);
|
||||
first_setup_user_box.append(&user_next_button);
|
||||
|
||||
// The main loop executes the asynchronous block
|
||||
let user_loop_context = MainContext::default();
|
||||
user_loop_context.spawn_local(clone!(@strong user_info_username_valid, @strong user_info_full_name_valid,@strong user_info_hostname_valid, @strong user_info_passwords_valid, @weak user_next_button => async move {
|
||||
while let Ok(_state) = user_loop_receiver.recv().await {
|
||||
if *user_info_username_valid.borrow_mut() == true && *user_info_full_name_valid.borrow_mut() == true && *user_info_hostname_valid.borrow_mut() && *user_info_passwords_valid.borrow_mut() == true {
|
||||
user_next_button.set_sensitive(true);
|
||||
} else {
|
||||
user_next_button.set_sensitive(false);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
user_info_username.connect_changed(clone!(@strong user_info_username_valid, @weak user_info_username, @weak user_info_full_name, @weak error_label => move |_| {
|
||||
let user_info_username_string = user_info_username.text().to_string();
|
||||
|
||||
user_info_full_name.set_text(&uppercase_first_letter(&user_info_username_string));
|
||||
|
||||
if user_info_username_string.len() > 32 {
|
||||
user_info_username.set_text(&user_info_username_string[..32]);
|
||||
user_info_username.set_position(-1);
|
||||
}
|
||||
|
||||
if Regex::new(r"[A-Z]").unwrap().is_match(&user_info_username_string) {
|
||||
user_info_username.set_text(&user_info_username_string.to_lowercase());
|
||||
user_info_username.set_position(-1);
|
||||
}
|
||||
|
||||
let mut _username_is_root = false;
|
||||
let mut _username_is_pikaos = false;
|
||||
let mut _username_is_special = false;
|
||||
|
||||
if user_info_username_string != "root" {
|
||||
_username_is_root=false;
|
||||
} else {
|
||||
error_label.set_label(&t!("error_label_is_root_label"));
|
||||
_username_is_root=true;
|
||||
}
|
||||
|
||||
if user_info_username_string != "pikaos" {
|
||||
_username_is_pikaos=false;
|
||||
} else {
|
||||
error_label.set_label(&t!("error_label_is_pikaos_label"));
|
||||
_username_is_pikaos=true;
|
||||
}
|
||||
|
||||
if only_alphanumeric(&user_info_username_string) {
|
||||
_username_is_special=false;
|
||||
} else {
|
||||
error_label.set_label(&t!("error_label_username_is_special_label"));
|
||||
_username_is_special=true;
|
||||
}
|
||||
|
||||
if _username_is_root == false && _username_is_pikaos == false && _username_is_special == false {
|
||||
error_label.set_visible(false);
|
||||
if !user_info_username.text().is_empty() {
|
||||
*user_info_username_valid.borrow_mut()=true;
|
||||
}
|
||||
} else {
|
||||
*user_info_username_valid.borrow_mut()=false;
|
||||
error_label.set_visible(true);
|
||||
}
|
||||
}));
|
||||
|
||||
user_info_full_name.connect_changed(clone!(@strong user_info_full_name_valid, @weak user_info_full_name, @weak error_label => move |_| {
|
||||
let user_info_full_name_string = user_info_full_name.text().to_string();
|
||||
|
||||
if user_info_full_name_string.len() > 32 {
|
||||
user_info_full_name.set_text(&user_info_full_name_string[..32]);
|
||||
user_info_full_name.set_position(-1);
|
||||
}
|
||||
|
||||
if user_info_full_name.text().is_empty() {
|
||||
*user_info_full_name_valid.borrow_mut()=false;
|
||||
} else {
|
||||
*user_info_full_name_valid.borrow_mut()=true;
|
||||
}
|
||||
}));
|
||||
|
||||
user_info_hostname.connect_changed(clone!(@strong user_info_hostname_valid, @weak user_info_hostname, @weak user_info_full_name, @weak error_label => move |_| {
|
||||
let user_info_hostname_string = user_info_hostname.text().to_string();
|
||||
|
||||
if user_info_hostname_string.len() > 32 {
|
||||
user_info_hostname.set_text(&user_info_hostname_string[..32]);
|
||||
user_info_hostname.set_position(-1);
|
||||
}
|
||||
|
||||
let mut _hostname_is_special = false;
|
||||
|
||||
if Regex::new(r"^[A-Za-z0-9\.]*$").unwrap().is_match(&user_info_hostname_string) {
|
||||
_hostname_is_special=false;
|
||||
} else {
|
||||
error_label.set_label(&t!("error_label_hostname_is_special_label"));
|
||||
_hostname_is_special=true;
|
||||
}
|
||||
|
||||
if _hostname_is_special == false {
|
||||
error_label.set_visible(false);
|
||||
if !user_info_hostname.text().is_empty() {
|
||||
*user_info_hostname_valid.borrow_mut()=true;
|
||||
}
|
||||
} else {
|
||||
*user_info_hostname_valid.borrow_mut()=false;
|
||||
error_label.set_visible(true);
|
||||
}
|
||||
}));
|
||||
|
||||
user_info_password.connect_changed(clone!(@strong user_info_passwords_valid,@weak user_info_password_verify_revealer, @weak user_info_password, @weak user_info_password_verify, @weak error_label => move |_| {
|
||||
if user_info_password.text().is_empty() {
|
||||
user_info_password_verify_revealer.set_reveal_child(false)
|
||||
} else {
|
||||
user_info_password_verify_revealer.set_reveal_child(true)
|
||||
}
|
||||
|
||||
if user_info_password.text() == user_info_password_verify.text() {
|
||||
error_label.set_visible(false);
|
||||
*user_info_passwords_valid.borrow_mut()=true;
|
||||
} else {
|
||||
*user_info_passwords_valid.borrow_mut()=false;
|
||||
}
|
||||
}));
|
||||
|
||||
user_info_password_verify.connect_changed(clone!(@strong user_info_passwords_valid, @weak user_info_password, @weak user_info_password_verify, @weak error_label => move |_| {
|
||||
if user_info_password.text() == user_info_password_verify.text() {
|
||||
error_label.set_visible(false);
|
||||
*user_info_passwords_valid.borrow_mut()=true;
|
||||
} else {
|
||||
error_label.set_visible(true);
|
||||
error_label.set_label(&t!("error_label_password_mismatch_label"));
|
||||
*user_info_passwords_valid.borrow_mut()=false;
|
||||
}
|
||||
}));
|
||||
|
||||
user_next_button.connect_clicked(clone!(@weak first_setup_carousel, @weak user_info_username, @weak user_info_hostname, @weak user_info_password, @weak user_info_full_name, @weak user_info_hostname_valid => move |_| {
|
||||
let result = cmd!("/usr/lib/pika/pika-welcome/scripts/pika-sudo.sh", "bash", "-c", USER_CREATE_PROG, &user_info_username.text(), &user_info_password.text(), &user_info_full_name.text(), &user_info_hostname.text()).read();
|
||||
assert!(result.is_ok());
|
||||
first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(3), true);
|
||||
}));
|
||||
}
|
@ -9,6 +9,8 @@ use gdk::Display;
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
use gtk::*;
|
||||
use single_instance::SingleInstance;
|
||||
use glib::*;
|
||||
use glib::prelude::*;
|
||||
|
||||
use std::boxed::Box;
|
||||
use users::*;
|
||||
@ -17,8 +19,9 @@ use config::{APP_ID};
|
||||
// application crates
|
||||
mod build_ui;
|
||||
use crate::build_ui::build_ui;
|
||||
mod save_window_size;
|
||||
/// first setup crates
|
||||
mod first_setup;
|
||||
mod welcome_content_page;
|
||||
|
||||
// Init translations for current crate.
|
||||
#[macro_use]
|
||||
|
14
src/save_window_size/mod.rs
Normal file
14
src/save_window_size/mod.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use adw::ffi::AdwApplicationWindow;
|
||||
use adw::{gio, glib};
|
||||
use adw::prelude::SettingsExt;
|
||||
use adw::subclass::window;
|
||||
use gtk::prelude::GtkWindowExt;
|
||||
use glib::*;
|
||||
use glib::prelude::*;
|
||||
pub fn save_window_size(window: &adw::ApplicationWindow, glib_settings: &gio::Settings) {
|
||||
let size = window.default_size();
|
||||
|
||||
let _ = glib_settings.set_int("window-width", size.0);
|
||||
let _ = glib_settings.set_int("window-height", size.1);
|
||||
let _ = glib_settings.set_boolean("is-maximized", window.is_maximized());
|
||||
}
|
131
src/welcome_content_page/mod.rs
Normal file
131
src/welcome_content_page/mod.rs
Normal file
@ -0,0 +1,131 @@
|
||||
// GTK crates
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use glib::*;
|
||||
/// Use all gtk4 libraries (gtk4 -> gtk because cargo)
|
||||
/// Use all libadwaita libraries (libadwaita -> adw because cargo)
|
||||
|
||||
|
||||
|
||||
//
|
||||
use std::cell::RefCell;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use adw::glib::gobject_ffi::GValue;
|
||||
use gtk::Orientation::Horizontal;
|
||||
|
||||
// carousel crates
|
||||
mod update_carousel;
|
||||
|
||||
use update_carousel::*;
|
||||
use crate::config::{APP_GITHUB, APP_ICON, VERSION};
|
||||
|
||||
pub fn welcome_content_page(window: &adw::ApplicationWindow, content_box: >k::Box) {
|
||||
let internet_connected = Rc::new(RefCell::new(false));
|
||||
|
||||
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 {
|
||||
//match check_internet_connection() {
|
||||
// Ok(_) => {
|
||||
// internet_loop_sender.send_blocking(true).expect("The channel needs to be open.");
|
||||
// }
|
||||
// Err(_) => {
|
||||
// internet_loop_sender.send_blocking(false).expect("The channel needs to be open.");
|
||||
// }
|
||||
//}
|
||||
let check_internet_connection_cli = Command::new("ping")
|
||||
.arg("ppa.pika-os.com")
|
||||
.arg("-c 1")
|
||||
.output()
|
||||
.expect("failed to execute process");
|
||||
if check_internet_connection_cli.status.success() {
|
||||
internet_loop_sender
|
||||
.send_blocking(true)
|
||||
.expect("The channel needs to be open.");
|
||||
} else {
|
||||
internet_loop_sender
|
||||
.send_blocking(false)
|
||||
.expect("The channel needs to be open.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let window_title_bar = gtk::HeaderBar::builder().show_title_buttons(true).build();
|
||||
|
||||
let credits_button = gtk::Button::builder()
|
||||
.icon_name("dialog-information-symbolic")
|
||||
.build();
|
||||
|
||||
let credits_window = adw::AboutWindow::builder()
|
||||
.application_icon(APP_ICON)
|
||||
.application_name(t!("app_name"))
|
||||
.transient_for(window)
|
||||
.version(VERSION)
|
||||
.hide_on_close(true)
|
||||
.developer_name(t!("app_dev"))
|
||||
.issue_url(APP_GITHUB.to_owned() + "/issues")
|
||||
.build();
|
||||
|
||||
content_box.append(&window_title_bar);
|
||||
|
||||
let welcome_content_page_stack = gtk::Stack::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.build();
|
||||
|
||||
let welcome_content_page_stack_sidebar = gtk::StackSidebar::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.stack(&welcome_content_page_stack)
|
||||
.build();
|
||||
|
||||
let welcome_content_page_split_view = adw::OverlaySplitView::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.content(&welcome_content_page_stack)
|
||||
.sidebar(&welcome_content_page_stack_sidebar)
|
||||
.max_sidebar_width(300.0)
|
||||
.min_sidebar_width(300.0)
|
||||
.enable_hide_gesture(true)
|
||||
.enable_show_gesture(true)
|
||||
.build();
|
||||
|
||||
let sidebar_toggle_button = gtk::ToggleButton::builder()
|
||||
.icon_name("view-right-pane-symbolic")
|
||||
.build();
|
||||
|
||||
let _sidebar_toggle_button_binding = welcome_content_page_split_view
|
||||
.bind_property("show_sidebar", &sidebar_toggle_button, "active")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
let welcome_content_page_split_view_breakpoint = adw::Breakpoint::new(BreakpointCondition::new_length(BreakpointConditionLengthType::MaxWidth, 400.0, LengthUnit::Px));
|
||||
welcome_content_page_split_view_breakpoint.add_setter(&welcome_content_page_split_view, "show_sidebar", &false.to_value());
|
||||
|
||||
window.add_breakpoint(welcome_content_page_split_view_breakpoint);
|
||||
|
||||
window_title_bar.pack_end(&credits_button);
|
||||
window_title_bar.pack_start(&sidebar_toggle_button);
|
||||
content_box.append(&welcome_content_page_split_view);
|
||||
|
||||
credits_button
|
||||
.connect_clicked(clone!(@weak credits_button => move |_| credits_window.present()));
|
||||
|
||||
let internet_connected_status = internet_connected.clone();
|
||||
|
||||
let internet_loop_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
internet_loop_context.spawn_local(clone!(@weak window => async move {
|
||||
while let Ok(state) = internet_loop_receiver.recv().await {
|
||||
if state == true {
|
||||
*internet_connected_status.borrow_mut()=true;
|
||||
} else {
|
||||
*internet_connected_status.borrow_mut()=false;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
Loading…
Reference in New Issue
Block a user