From d4c0becea510a07793c80e6da649e496392a448b Mon Sep 17 00:00:00 2001 From: Ward from fusion-voyager-3 Date: Mon, 5 Aug 2024 03:25:05 +0300 Subject: [PATCH] Language Page --- locales/en_US.json | 9 +- src/build_ui.rs | 19 +- src/installer_stack_page/imp.rs | 91 +++++++-- src/language_page/mod.rs | 336 ++++++++++++++++++++++++++++++++ src/main.rs | 2 + src/welcome_page/mod.rs | 61 ++++++ 6 files changed, 488 insertions(+), 30 deletions(-) create mode 100644 src/language_page/mod.rs create mode 100644 src/welcome_page/mod.rs diff --git a/locales/en_US.json b/locales/en_US.json index 57581a9..f753886 100644 --- a/locales/en_US.json +++ b/locales/en_US.json @@ -37,9 +37,9 @@ "fstab_badfs_swap": ") Must not be swap to be used as [SWAP]!", "fstab_bad_mountpoint": "Filesystem Table Error: Invalid mountpoint: (", "fstab_bad_mountpoint_msg": ") Is not a valid mountpoint!", - "select_a_language": "Select a language", - "please_select_locale": "Please select a locale for the system to use", - "no_locale_selected": "No locale selected", + "select_a_language": "Select a Language", + "please_select_locale": "Please Select a Locale for The System to Use", + "no_locale_selected": "No Locale selected", "language": "Language", "select_a_keyboard": "Select a keyboard", "please_select_keyboard": "Please select a Keyboard layout for the system to use", @@ -94,5 +94,6 @@ "done": "Done", "partitioning": "Partitioning", "luks2_password_confirm": "Confirm LUKS Password", - "luks_not_match": "The LUKS Passwords do not match!" + "luks_not_match": "The LUKS Passwords do not match!", + "search_for_language": "Search for a Language" } diff --git a/src/build_ui.rs b/src/build_ui.rs index 65eec4c..cb1399a 100644 --- a/src/build_ui.rs +++ b/src/build_ui.rs @@ -1,8 +1,8 @@ use std::path::Path; use gtk::{prelude::*, glib as glib}; -use vte::ffi::VTE_ALIGN_CENTER; use crate::efi_error_page; -use crate::installer_stack_page; +use crate::welcome_page; +use crate::language_page; pub fn build_ui(app: &adw::Application) { glib::set_prgname(Some("pikaos_installer")); @@ -47,20 +47,11 @@ pub fn build_ui(app: &adw::Application) { .build(); match Path::new("/sys/firmware/efi/efivars").exists() { - true => { - let page = installer_stack_page::InstallerStackPage::new(); - page.set_page_icon("pika-logo"); - page.set_page_title("Title"); - page.set_page_subtitle("Subtitle"); - let gbox = gtk::Box::new(gtk::Orientation::Vertical, 0); - gbox.append(>k::Image::builder().icon_name("pika-logo").build()); - page.set_child_widget(&gbox); - carousel.append(&page); - } + true => welcome_page::welcome_page(&window, &carousel), _ => efi_error_page::efi_error_page(&window, &carousel) } - //welcome_page(&window, &carousel), - //language_page(&window, &carousel); + + language_page::language_page(&window, &carousel); window.present() } \ No newline at end of file diff --git a/src/installer_stack_page/imp.rs b/src/installer_stack_page/imp.rs index 2f7ce70..92aa580 100644 --- a/src/installer_stack_page/imp.rs +++ b/src/installer_stack_page/imp.rs @@ -1,5 +1,5 @@ use std::{cell::RefCell, env, rc::Rc, sync::OnceLock}; -use gtk::{prelude::*, subclass::prelude::*, glib as glib}; +use gtk::{prelude::*, subclass::prelude::*, glib as glib, Justification}; use adw::{prelude::*, subclass::prelude::*}; use glib::{clone, subclass::prelude::*, subclass::Signal}; @@ -15,6 +15,14 @@ pub struct InstallerStackPage { #[property(get, set)] page_subtitle: RefCell, #[property(get, set)] + back_sensitive: RefCell, + #[property(get, set)] + next_sensitive: RefCell, + #[property(get, set)] + back_visible: RefCell, + #[property(get, set)] + next_visible: RefCell, + #[property(get, set)] child_widget: Rc>, } // ANCHOR_END: custom_button @@ -45,16 +53,21 @@ impl ObjectImpl for InstallerStackPage { obj.set_vexpand(true); // - let main_box = gtk::Box::new(gtk::Orientation::Horizontal, 0); - + let main_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .hexpand(true) + .build(); // let back_button = gtk::Button::builder() .icon_name("pan-start-symbolic") .valign(gtk::Align::Center) .halign(gtk::Align::Start) - .margin_start(5) + .sensitive(true) + .visible(true) + .margin_start(10) .margin_end(5) + .tooltip_text(t!("back")) .build(); back_button.add_css_class("circular"); @@ -67,14 +80,27 @@ impl ObjectImpl for InstallerStackPage { } )); + obj.bind_property("back_sensitive", &back_button, "sensitive") + .sync_create() + .bidirectional() + .build(); + + obj.bind_property("back_visible", &back_button, "visible") + .sync_create() + .bidirectional() + .build(); + // let next_button = gtk::Button::builder() .icon_name("pan-end-symbolic") .valign(gtk::Align::Center) .halign(gtk::Align::End) + .sensitive(false) + .visible(true) .margin_start(5) - .margin_end(5) + .margin_end(10) + .tooltip_text(t!("next")) .build(); next_button.add_css_class("circular"); @@ -88,41 +114,82 @@ impl ObjectImpl for InstallerStackPage { } )); + obj.bind_property("next_sensitive", &next_button, "sensitive") + .sync_create() + .bidirectional() + .build(); + + obj.bind_property("next_visible", &next_button, "visible") + .sync_create() + .bidirectional() + .build(); + // let content_box = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) - .halign(gtk::Align::Center) .hexpand(true) .build(); - let info_status_page = adw::StatusPage::builder() + // + + let installer_page_icon = gtk::Image::builder() + .pixel_size(80) .valign(gtk::Align::Start) .halign(gtk::Align::Center) .hexpand(true) .build(); - info_status_page.add_css_class("compact"); + let installer_page_title = gtk::Label::builder() + .valign(gtk::Align::Start) + .halign(gtk::Align::Center) + .hexpand(true) + .wrap(true) + .justify(Justification::Center) + .width_chars(20) + .margin_top(5) + .margin_bottom(5) + .margin_start(5) + .margin_end(5) + .build(); - obj.bind_property("page_icon", &info_status_page, "icon_name") + installer_page_title.add_css_class("title-1"); + + let installer_page_subtitle = gtk::Label::builder() + .valign(gtk::Align::Start) + .halign(gtk::Align::Center) + .hexpand(true) + .justify(Justification::Center) + .width_chars(20) + .margin_top(5) + .margin_bottom(5) + .margin_start(5) + .margin_end(5) + .build(); + + obj.bind_property("page_icon", &installer_page_icon, "icon_name") .sync_create() .bidirectional() .build(); - obj.bind_property("page_title", &info_status_page, "title") + obj.bind_property("page_title", &installer_page_title, "label") .sync_create() .bidirectional() .build(); - obj.bind_property("page_subtitle", &info_status_page, "description") + obj.bind_property("page_subtitle", &installer_page_subtitle, "label") .sync_create() .bidirectional() .build(); let child_bin = adw::Bin::builder() + .vexpand(true) + .hexpand(true) .build(); - content_box.append(&info_status_page); + content_box.append(&installer_page_icon); + content_box.append(&installer_page_title); + content_box.append(&installer_page_subtitle); content_box.append(&child_bin); obj.connect_child_widget_notify(clone!( diff --git a/src/language_page/mod.rs b/src/language_page/mod.rs new file mode 100644 index 0000000..ebe9868 --- /dev/null +++ b/src/language_page/mod.rs @@ -0,0 +1,336 @@ +use crate::installer_stack_page; +use crate::config; +use gtk::{prelude::*, glib as glib, Justification}; +use adw::{prelude::*}; +use glib::{clone, closure_local}; +use std::{process::Command, env}; +pub fn language_page(window: &adw::ApplicationWindow, main_carousel: &adw::Carousel) { + let language_page = installer_stack_page::InstallerStackPage::new(); + language_page.set_page_title(t!("select_a_language")); + language_page.set_page_subtitle(t!("please_select_locale")); + language_page.set_page_icon("preferences-desktop-locale-symbolic"); + language_page.set_back_visible(true); + language_page.set_next_visible(true); + language_page.set_back_sensitive(true); + language_page.set_next_sensitive(false); + + let content_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .hexpand(true) + .vexpand(true) + .margin_start(15) + .margin_end(15) + .margin_top(15) + .margin_bottom(15) + .build(); + + let null_checkbutton = gtk::CheckButton::builder() + .label(t!("no_locale_selected")) + .build(); + + let language_selection_row_viewport = + gtk::ScrolledWindow::builder() + .vexpand(true) + .hexpand(true) + .build(); + + let language_selection_row_viewport_box = gtk::ListBox::builder().build(); + language_selection_row_viewport_box.add_css_class("boxed-list"); + + language_selection_row_viewport + .set_child(Some(&language_selection_row_viewport_box)); + + let language_selection_row_viewport_listbox = gtk::ListBox::builder() + .selection_mode(gtk::SelectionMode::None) + .margin_top(15) + .margin_bottom(15) + .margin_start(15) + .margin_end(15) + .build(); + language_selection_row_viewport_listbox.add_css_class("boxed-list"); + + let language_search_bar = gtk::SearchEntry::builder() + .hexpand(true) + .margin_top(15) + .margin_bottom(15) + .placeholder_text(t!("search_for_language")) + .search_delay(500) + .build(); + + let current_locale = match env::var_os("LANG") { + Some(v) => v.into_string().unwrap(), + None => panic!("$LANG is not set"), + }; + + let locale_list = ["ab_GE", + "aa_DJ", + "af_ZA", + "ak_GH", + "sq_AL", + "am_ET", + "ar_EG", + "an_ES", + "hy_AM", + "as_IN", + "ar_AE", + "az_AZ", + "bs_BA", + "eu_ES", + "be_BY", + "bn_BD", + "ar_BH", + "bi_VU", + "bs_BA", + "br_FR", + "bg_BG", + "my_MM", + "ca_ES", + "de_CH", + "ce_RU", + "zh_CN", + "cv_RU", + "kw_GB", + "es_CO", + "es_CR", + "hr_HR", + "cs_CZ", + "da_DK", + "dv_MV", + "nl_NL", + "dz_BT", + "en_US", + "en_GB", + "eo", + "et_EE", + "et_EE", + "fo_FO", + "hif_FJ", + "fi_FI", + "fr_FR", + "ff_SN", + "gl_ES", + "ka_GE", + "de_DE", + "el_GR", + "gu_IN", + "ht_HT", + "ha_NG", + "he_IL", + "hi_IN", + "hu_HU", + "ia_FR", + "id_ID", + "en_IE", + "ga_IE", + "ig_NG", + "ik_CA", + "is_IS", + "it_IT", + "iu_CA", + "ja_JP", + "kl_GL", + "kn_IN", + "ko_KR", + "kk_KZ", + "km_KH", + "rw_RW", + "ky_KG", + "ky_KG", + "ko_KR", + "ku_TR", + "lo_LA", + "lb_LU", + "lg_UG", + "li_NL", + "ln_CD", + "lo_LA", + "lt_LT", + "fr_LU", + "lv_LV", + "gv_GB", + "mk_MK", + "mg_MG", + "ms_MY", + "ml_IN", + "mt_MT", + "mi_NZ", + "mr_IN", + "mn_MN", + "ne_NP", + "en_NG", + "nb_NO", + "nn_NO", + "no_NO", + "nr_ZA", + "oc_FR", + "es_CU", + "om_ET", + "or_IN", + "os_RU", + "pa_IN", + "fa_IR", + "pl_PL", + "ps_AF", + "pt_BR", + "ro_RO", + "ru_RU", + "sa_IN", + "sc_IT", + "sd_IN", + "se_NO", + "sm_WS", + "en_SG", + "sr_RS", + "gd_GB", + "wo_SN", + "si_LK", + "sk_SK", + "sl_SI", + "so_SO", + "st_ZA", + "es_ES", + "sw_KE", + "ss_ZA", + "sv_SE", + "ta_IN", + "te_IN", + "tg_TJ", + "th_TH", + "ti_ER", + "bo_CN", + "tk_TM", + "tl_PH", + "tn_ZA", + "to_TO", + "tr_TR", + "ts_ZA", + "tt_RU", + "zh_TW", + "ug_CN", + "uk_UA", + "ur_PK", + "ve_ZA", + "vi_VN", + "wa_BE", + "cy_GB", + "wo_SN", + "fy_NL", + "xh_ZA", + "yi_US", + "yo_NG", + "zu_ZA", + "zu_ZA", + "pt_BR", + "pt_PT",]; + + let lang_data_buffer = gtk::TextBuffer::builder().build(); + + for locale in locale_list.iter() { + let locale = locale.to_string(); + let locale_name_cli = + Command::new("/usr/lib/pika/pika-installer-gtk4/scripts/locale-name.py") + .arg(locale.clone()) + .output() + .expect("failed to execute process"); + let locale_name = String::from_utf8(locale_name_cli.stdout).unwrap(); + let locale_clone = locale.clone(); + let locale_checkbutton = gtk::CheckButton::builder() + .valign(gtk::Align::Center) + .can_focus(false) + .build(); + let locale_row = adw::ActionRow::builder() + .activatable_widget(&locale_checkbutton) + .title(locale_name) + .subtitle(locale.clone()) + .build(); + locale_row.add_prefix(&locale_checkbutton); + locale_checkbutton.set_group(Some(&null_checkbutton)); + language_selection_row_viewport_box.append(&locale_row); + locale_checkbutton.connect_toggled(clone!( + #[weak] + locale_checkbutton, + #[weak] + lang_data_buffer, + #[weak] + language_page, + move |_| + { + if locale_checkbutton.is_active() == true { + language_page.set_next_sensitive(true); + lang_data_buffer.set_text(&locale); + } + } + )); + if current_locale.contains(&(locale_clone)) + && current_locale != "C.UTF-8" + && current_locale != "C" + && current_locale != "C.utf8" + && current_locale != "POSIX" + { + locale_checkbutton.set_active(true); + } + } + + // / content_box appends + //// add text and and entry to language page selections + content_box.append(&language_search_bar); + content_box.append(&language_selection_row_viewport); + + let lang_data_buffer_clone = lang_data_buffer.clone(); + + language_search_bar.connect_search_changed(clone!( + #[weak] + language_search_bar, + #[weak] + language_selection_row_viewport_box, + move |_| + { + let mut counter = language_selection_row_viewport_box.first_child(); + while let Some(row) = counter { + if row.widget_name() == "AdwActionRow" { + if !language_search_bar.text().is_empty() { + if row.property::("subtitle").to_lowercase().contains(&language_search_bar.text().to_string().to_lowercase()) || row.property::("title").to_lowercase().contains(&language_search_bar.text().to_string().to_lowercase()) { + row.set_property("visible", true); + language_search_bar.grab_focus(); + } else { + row.set_property("visible", false); + } + } else { + row.set_property("visible", true); + } + } + counter = row.next_sibling(); + } + } + )); + + language_page.set_child_widget(&content_box); + + language_page.connect_closure( + "back-button-pressed", + false, + closure_local!( + #[weak] + main_carousel, + move |language_page: installer_stack_page::InstallerStackPage| + { + main_carousel.scroll_to(&main_carousel.nth_page(0), true) + } + ) + ); + + language_page.connect_closure( + "next-button-pressed", + false, + closure_local!( + #[weak] + main_carousel, + move |language_page: installer_stack_page::InstallerStackPage| + { + main_carousel.scroll_to(&main_carousel.nth_page(2), true) + } + ) + ); + + main_carousel.append(&language_page); +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index dd393ea..c6f96e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ mod config; mod build_ui; mod efi_error_page; mod installer_stack_page; +mod welcome_page; +mod language_page; #[macro_use] extern crate rust_i18n; diff --git a/src/welcome_page/mod.rs b/src/welcome_page/mod.rs new file mode 100644 index 0000000..998d052 --- /dev/null +++ b/src/welcome_page/mod.rs @@ -0,0 +1,61 @@ +use crate::installer_stack_page; +use crate::config; +use gtk::{prelude::*, glib as glib, Justification}; +use glib::clone; +pub fn welcome_page(window: &adw::ApplicationWindow, main_carousel: &adw::Carousel) { + let welcome_page = installer_stack_page::InstallerStackPage::new(); + welcome_page.set_page_title(t!("welcome")); + welcome_page.set_page_subtitle(t!("welcome_to_pikaos")); + welcome_page.set_page_icon(config::DISTRO_ICON); + welcome_page.set_back_visible(false); + welcome_page.set_next_visible(false); + + let content_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .margin_start(15) + .margin_end(15) + .margin_top(15) + .margin_bottom(15) + .valign(gtk::Align::Center) + .halign(gtk::Align::Center) + .vexpand(true) + .hexpand(true) + .build(); + + content_box.add_css_class("linked"); + + let live_media_button = gtk::Button::builder() + .icon_name("drive-optical") + .label(t!("use_pikaos_in_live_media")) + .build(); + + let install_media_button = gtk::Button::builder() + .icon_name("drive-harddisk") + .label(t!("install_distro_to_system")) + .build(); + + install_media_button.connect_clicked( + clone!( + #[weak] + main_carousel, + move |_| + main_carousel.scroll_to(&main_carousel.nth_page(1), true) + ) + ); + + live_media_button.connect_clicked( + clone!( + #[weak] + window, + move |_| + window.close() + ) + ); + + content_box.append(&install_media_button); + content_box.append(&live_media_button); + + welcome_page.set_child_widget(&content_box); + + main_carousel.append(&welcome_page); +} \ No newline at end of file