diff --git a/Cargo.lock b/Cargo.lock index 5cc99af..da5fe74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.79" @@ -427,6 +436,7 @@ dependencies = [ "async-channel", "gtk4", "libadwaita", + "regex", ] [[package]] @@ -640,6 +650,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rustc_version" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index adc2e58..3ba9777 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" adw = { version = "0.5.3", package = "libadwaita", features = ["v1_4"] } gtk = { version = "0.7.3", package = "gtk4", features = ["v4_12"] } async-channel = "2.1.1" +regex = "1.10.3" diff --git a/src/first_setup/first_setup.rs b/src/first_setup/first_setup.rs index fd26eb1..5f15be5 100644 --- a/src/first_setup/first_setup.rs +++ b/src/first_setup/first_setup.rs @@ -17,6 +17,7 @@ use crate::connection_check::check_internet_connection; // carousel crates use crate::first_setup::initial_carousel::initial_carousel; use crate::first_setup::internet_carousel::internet_carousel; +use crate::first_setup::user_carousel::user_carousel; use crate::first_setup::update_carousel::update_carousel; pub fn first_setup(window: &adw::ApplicationWindow) { @@ -63,7 +64,7 @@ pub fn first_setup(window: &adw::ApplicationWindow) { // 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); // Add file to window diff --git a/src/first_setup/internet_carousel.rs b/src/first_setup/internet_carousel.rs index 6ef30d9..67b02ec 100644 --- a/src/first_setup/internet_carousel.rs +++ b/src/first_setup/internet_carousel.rs @@ -89,7 +89,6 @@ pub fn internet_carousel(first_setup_carousel: &adw::Carousel, internet_connecte .valign(Align::End) .vexpand(true) .hexpand(true) - .hexpand(true) .margin_end(15) .margin_start(15) .margin_bottom(15) @@ -165,8 +164,8 @@ pub fn internet_carousel(first_setup_carousel: &adw::Carousel, internet_connecte 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("gtk-launch") - .arg("gnome-network-panel.desktop") + Command::new("gnome-control-center") + .arg("network") .spawn() .expect("gnome-control-center failed to start"); } else { diff --git a/src/first_setup/mod.rs b/src/first_setup/mod.rs index bf09ffb..b0cfdc7 100644 --- a/src/first_setup/mod.rs +++ b/src/first_setup/mod.rs @@ -1,5 +1,5 @@ pub mod first_setup; pub mod initial_carousel; pub mod internet_carousel; - -pub mod update_carousel; \ No newline at end of file +pub mod user_carousel; +pub mod update_carousel; diff --git a/src/first_setup/user_carousel.rs b/src/first_setup/user_carousel.rs new file mode 100644 index 0000000..fdb2331 --- /dev/null +++ b/src/first_setup/user_carousel.rs @@ -0,0 +1,189 @@ +// GTK crates +/// Use all gtk4 libraries (gtk4 -> gtk because cargo) +/// Use all libadwaita libraries (libadwaita -> adw because cargo) +use gtk::prelude::*; +use gtk::*; +use adw::prelude::*; +use adw::*; +use glib::*; +use gdk::Display; + +//use crate::check_internet_connection; +use std::process::Command; +use std::cell::RefCell; +use std::rc::Rc; +use std::borrow::Borrow as the_rc_borrow; +use regex::Regex; +use std::env; +use gtk::Align::Center; +use gtk::gio::ffi::GAsyncReadyCallback; +use gtk::pango::TextTransform::Capitalize; + +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_passwords_valid = Rc::new(RefCell::new(false)); + + + + 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("User setup") + .description("Create a user account.") + .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("Username:") + .input_purpose(InputPurpose::Alpha) + .input_hints(InputHints::LOWERCASE) + .build(); + + let user_info_full_name = adw::EntryRow::builder() + .hexpand(true) + .title("Full name:") + .input_purpose(InputPurpose::Name) + .build(); + + let user_info_password = adw::PasswordEntryRow::builder() + .hexpand(true) + .title("User password:") + .build(); + + let user_info_password_verify = adw::PasswordEntryRow::builder() + .hexpand(true) + .title("Enter User password again:") + .visible(false) + .build(); + + let user_info_avatar = adw::Avatar::builder() + .show_initials(true) + .size(128) + .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 user_next_button = gtk::Button::builder() + .label("Next") + .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_password); + user_info_listbox.append(&user_info_password_verify); + + 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(&user_next_button); + + + user_info_username.connect_changed(clone!(@strong user_info_username_valid, @weak user_info_username, @weak user_info_full_name => 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); + } + + if !only_alphanumeric(&user_info_username_string) { + *user_info_username_valid.borrow_mut()=true; + } else { + *user_info_username_valid.borrow_mut()=false; + } + + if user_info_username_string != "pikaos" { + *user_info_username_valid.borrow_mut()=true; + } else { + *user_info_username_valid.borrow_mut()=false; + } + + if user_info_username_string != "root" { + *user_info_username_valid.borrow_mut()=true; + } else { + *user_info_username_valid.borrow_mut()=false; + } + })); + + user_info_full_name.connect_changed(clone!(@strong user_info_full_name_valid, @weak user_info_full_name => 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); + } + })); + + + user_next_button.connect_clicked(clone!(@weak first_setup_carousel => move |_| { + first_setup_carousel.scroll_to(&first_setup_carousel.nth_page(3), true); + })); +} \ No newline at end of file