From 7028ce4682d46d66962f897e5cf35415dd8a68e1 Mon Sep 17 00:00:00 2001 From: Ward from fusion-voyager-3 Date: Sat, 24 Feb 2024 21:17:38 +0300 Subject: [PATCH] Add size groups --- data/config/community.json | 20 +++ data/config/contribute.json | 20 +++ data/config/look_and_feel.json | 22 +++ data/config/troubleshoot.json | 20 +++ src/build_ui.rs | 8 +- src/main.rs | 10 +- src/save_window_size/mod.rs | 2 +- .../community_page/mod.rs | 111 ++++++++------- .../contribute_page/mod.rs | 111 ++++++++------- .../look_and_feel_page/mod.rs | 112 ++++++++------- src/welcome_content_page/mod.rs | 58 ++++++-- .../recommended_addons_page/mod.rs | 127 ++++++++++-------- .../setup_steps_page/mod.rs | 65 +++++---- .../troubleshoot_page/mod.rs | 111 ++++++++------- src/welcome_content_page/welcome_page/mod.rs | 18 ++- 15 files changed, 508 insertions(+), 307 deletions(-) create mode 100644 data/config/community.json create mode 100644 data/config/contribute.json create mode 100644 data/config/look_and_feel.json create mode 100644 data/config/troubleshoot.json diff --git a/data/config/community.json b/data/config/community.json new file mode 100644 index 0000000..7305e17 --- /dev/null +++ b/data/config/community.json @@ -0,0 +1,20 @@ +{ + "setup_steps": [ + { + "id": 0, + "title": "update-pikaos-title", + "subtitle": "update-pikaos-subtitle", + "icon": "pika-system-software-update", + "button": "update-pikaos-button-label", + "command": "echo update" + }, + { + "id": 1, + "title": "install-media-codec-title", + "subtitle": "install-media-codec-subtitle", + "icon": "pika-media-tape", + "button": "install-media-codec-button-label", + "command": "echo codec" + } + ] +} \ No newline at end of file diff --git a/data/config/contribute.json b/data/config/contribute.json new file mode 100644 index 0000000..7305e17 --- /dev/null +++ b/data/config/contribute.json @@ -0,0 +1,20 @@ +{ + "setup_steps": [ + { + "id": 0, + "title": "update-pikaos-title", + "subtitle": "update-pikaos-subtitle", + "icon": "pika-system-software-update", + "button": "update-pikaos-button-label", + "command": "echo update" + }, + { + "id": 1, + "title": "install-media-codec-title", + "subtitle": "install-media-codec-subtitle", + "icon": "pika-media-tape", + "button": "install-media-codec-button-label", + "command": "echo codec" + } + ] +} \ No newline at end of file diff --git a/data/config/look_and_feel.json b/data/config/look_and_feel.json new file mode 100644 index 0000000..e1e05fd --- /dev/null +++ b/data/config/look_and_feel.json @@ -0,0 +1,22 @@ +{ + "look_and_feel": [ + { + "id": 0, + "title": "update-pikaos-title", + "subtitle": "update-pikaos-subtitle", + "icon": "pika-system-software-update", + "button": "update-pikaos-button-label", + "only-in": "", + "command": "echo update" + }, + { + "id": 1, + "title": "update-pikaos-title", + "subtitle": "update-pikaos-subtitle", + "icon": "pika-system-software-update", + "button": "update-pikaos-button-label", + "only-in": "gnome", + "command": "echo update" + } + ] +} \ No newline at end of file diff --git a/data/config/troubleshoot.json b/data/config/troubleshoot.json new file mode 100644 index 0000000..7305e17 --- /dev/null +++ b/data/config/troubleshoot.json @@ -0,0 +1,20 @@ +{ + "setup_steps": [ + { + "id": 0, + "title": "update-pikaos-title", + "subtitle": "update-pikaos-subtitle", + "icon": "pika-system-software-update", + "button": "update-pikaos-button-label", + "command": "echo update" + }, + { + "id": 1, + "title": "install-media-codec-title", + "subtitle": "install-media-codec-subtitle", + "icon": "pika-media-tape", + "button": "install-media-codec-button-label", + "command": "echo codec" + } + ] +} \ No newline at end of file diff --git a/src/build_ui.rs b/src/build_ui.rs index 63517a6..a9e49cf 100644 --- a/src/build_ui.rs +++ b/src/build_ui.rs @@ -1,7 +1,4 @@ // GTK crates -use adw::prelude::*; -use adw::*; -use gtk::Orientation; /// Use all gtk4 libraries (gtk4 -> gtk because cargo) /// Use all libadwaita libraries (libadwaita -> adw because cargo) @@ -10,6 +7,9 @@ use gtk::Orientation; use crate::config::*; use crate::save_window_size::save_window_size; use crate::welcome_content_page::welcome_content_page; +use adw::prelude::*; +use adw::*; +use gtk::Orientation; pub fn build_ui(app: &adw::Application) { // setup glib @@ -52,4 +52,4 @@ pub fn build_ui(app: &adw::Application) { welcome_content_page(&window, &content_box); // show the window window.present() -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 18dcbb2..312d580 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ // GTK crates mod config; -use std::env; use adw::prelude::*; use adw::*; use gdk::Display; @@ -9,10 +8,11 @@ use gdk::Display; /// Use all libadwaita libraries (libadwaita -> adw because cargo) use gtk::*; use single_instance::SingleInstance; +use std::env; +use config::APP_ID; use std::boxed::Box; use users::*; -use config::APP_ID; // application crates mod build_ui; @@ -33,10 +33,7 @@ fn main() { None => panic!("$LANG is not set"), }; rust_i18n::set_locale(current_locale.strip_suffix(".UTF-8").unwrap()); - let application = adw::Application::new( - Some(APP_ID), - Default::default(), - ); + let application = adw::Application::new(Some(APP_ID), Default::default()); application.connect_startup(|app| { // The CSS "magic" happens here. let provider = CssProvider::new(); @@ -60,5 +57,4 @@ fn main() { println!("Error: This program can only be run via an installed system user"); std::process::exit(1) } - } diff --git a/src/save_window_size/mod.rs b/src/save_window_size/mod.rs index 844b555..f84fad0 100644 --- a/src/save_window_size/mod.rs +++ b/src/save_window_size/mod.rs @@ -1,4 +1,4 @@ -use adw::{gio,}; +use adw::gio; use adw::prelude::SettingsExt; use gtk::prelude::GtkWindowExt; pub fn save_window_size(window: &adw::ApplicationWindow, glib_settings: &gio::Settings) { diff --git a/src/welcome_content_page/community_page/mod.rs b/src/welcome_content_page/community_page/mod.rs index 720fc5e..986e34e 100644 --- a/src/welcome_content_page/community_page/mod.rs +++ b/src/welcome_content_page/community_page/mod.rs @@ -1,30 +1,30 @@ // GTK crates -use duct::cmd; -use std::path::Path; -use std::fs; -use serde::Deserialize; -use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; use adw::prelude::*; use adw::*; +use duct::cmd; use glib::*; +use serde::Deserialize; +use std::cell::RefCell; +use std::fs; +use std::path::Path; +use std::rc::Rc; +use std::{thread, time}; #[allow(non_camel_case_types)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] -struct setup_steps_entry { +struct community_entry { id: i32, title: String, subtitle: String, icon: String, button: String, - command: String + command: String, } -pub fn setup_steps_page( - setup_steps_content_page_stack: >k::Stack, +pub fn community_page( + community_content_page_stack: >k::Stack, window: &adw::ApplicationWindow, - internet_connected: &Rc> + internet_connected: &Rc>, ) { let internet_connected_status = internet_connected.clone(); @@ -38,12 +38,9 @@ pub fn setup_steps_page( .expect("The channel needs to be open."); }); - let setup_steps_page_box = gtk::Box::builder() - .vexpand(true) - .hexpand(true) - .build(); + let community_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build(); - let setup_steps_page_listbox = gtk::ListBox::builder() + let community_page_listbox = gtk::ListBox::builder() .margin_top(20) .margin_bottom(20) .margin_start(20) @@ -51,13 +48,13 @@ pub fn setup_steps_page( .vexpand(true) .hexpand(true) .build(); - setup_steps_page_listbox.add_css_class("boxed-list"); + community_page_listbox.add_css_class("boxed-list"); - let setup_steps_page_scroll = gtk::ScrolledWindow::builder() + let community_page_scroll = gtk::ScrolledWindow::builder() // that puts items vertically .hexpand(true) .vexpand(true) - .child(&setup_steps_page_box) + .child(&community_page_box) .propagate_natural_width(true) .propagate_natural_height(true) .build(); @@ -65,37 +62,43 @@ pub fn setup_steps_page( let internet_loop_context = MainContext::default(); // The main loop executes the asynchronous block internet_loop_context.spawn_local( - clone!(@strong internet_connected_status, @weak setup_steps_page_box => async move { + clone!(@strong internet_connected_status, @weak community_page_box => async move { while let Ok(_state) = internet_loop_receiver.recv().await { if *internet_connected_status.borrow_mut() == true { - setup_steps_page_box.set_sensitive(true); + community_page_box.set_sensitive(true); } else { - setup_steps_page_box.set_sensitive(false); + community_page_box.set_sensitive(false); } } }), ); - let mut json_array: Vec = Vec::new(); - let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; + let mut json_array: Vec = Vec::new(); + let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/community.json"; let json_data = fs::read_to_string(json_path).expect("Unable to read json"); - let json_data: serde_json::Value = serde_json::from_str(&json_data).expect("JSON format invalid"); - if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { - for setup_steps_entry in setup_steps { - let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); - json_array.push(setup_steps_entry_struct); + let json_data: serde_json::Value = + serde_json::from_str(&json_data).expect("JSON format invalid"); + if let serde_json::Value::Array(community) = &json_data["community"] { + for community_entry in community { + let community_entry_struct: community_entry = + serde_json::from_value(community_entry.clone()).unwrap(); + json_array.push(community_entry_struct); } } - for setup_steps_entry in json_array { - let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded(); - let entry_command_status_loop_sender: async_channel::Sender = entry_command_status_loop_sender.clone(); + let entry_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both); - let entry_title = setup_steps_entry.title; - let entry_subtitle = setup_steps_entry.subtitle; - let entry_icon = setup_steps_entry.icon; - let entry_button = setup_steps_entry.button; - let entry_command = setup_steps_entry.command; + for community_entry in json_array { + let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = + async_channel::unbounded(); + let entry_command_status_loop_sender: async_channel::Sender = + entry_command_status_loop_sender.clone(); + + let entry_title = community_entry.title; + let entry_subtitle = community_entry.subtitle; + let entry_icon = community_entry.icon; + let entry_button = community_entry.button; + let entry_command = community_entry.command; let entry_row = adw::ActionRow::builder() .title(t!(&entry_title)) .subtitle(t!(&entry_subtitle)) @@ -113,6 +116,7 @@ pub fn setup_steps_page( .vexpand(true) .valign(gtk::Align::Center) .build(); + entry_buttons_size_group.add_widget(&entry_row_button); entry_row.add_prefix(&entry_row_icon); entry_row.add_suffix(&entry_row_button); @@ -137,21 +141,30 @@ pub fn setup_steps_page( .heading(t!("cmd_err_dialog_heading")) .transient_for(window) .build(); - cmd_err_dialog.add_response("cmd_err_dialog_ok", &t!("cmd_err_dialog_ok_label").to_string()); + cmd_err_dialog.add_response( + "cmd_err_dialog_ok", + &t!("cmd_err_dialog_ok_label").to_string(), + ); let entry_command_status_loop_context = MainContext::default(); // The main loop executes the asynchronous block - entry_command_status_loop_context.spawn_local(clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { - while let Ok(state) = entry_command_status_loop_receiver.recv().await { - if state == false { - cmd_err_dialog.present(); + entry_command_status_loop_context.spawn_local( + clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { + while let Ok(state) = entry_command_status_loop_receiver.recv().await { + if state == false { + cmd_err_dialog.present(); + } } - } - })); - setup_steps_page_listbox.append(&entry_row) + }), + ); + community_page_listbox.append(&entry_row) } - setup_steps_page_box.append(&setup_steps_page_listbox); + community_page_box.append(&community_page_listbox); - setup_steps_content_page_stack.add_titled(&setup_steps_page_scroll, Some("setup_steps_page"), &t!("setup_steps_page_title").to_string()); -} \ No newline at end of file + community_content_page_stack.add_titled( + &community_page_scroll, + Some("community_page"), + &t!("community_page_title").to_string(), + ); +} diff --git a/src/welcome_content_page/contribute_page/mod.rs b/src/welcome_content_page/contribute_page/mod.rs index 720fc5e..7e5da63 100644 --- a/src/welcome_content_page/contribute_page/mod.rs +++ b/src/welcome_content_page/contribute_page/mod.rs @@ -1,30 +1,30 @@ // GTK crates -use duct::cmd; -use std::path::Path; -use std::fs; -use serde::Deserialize; -use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; use adw::prelude::*; use adw::*; +use duct::cmd; use glib::*; +use serde::Deserialize; +use std::cell::RefCell; +use std::fs; +use std::path::Path; +use std::rc::Rc; +use std::{thread, time}; #[allow(non_camel_case_types)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] -struct setup_steps_entry { +struct contribute_entry { id: i32, title: String, subtitle: String, icon: String, button: String, - command: String + command: String, } -pub fn setup_steps_page( - setup_steps_content_page_stack: >k::Stack, +pub fn contribute_page( + contribute_content_page_stack: >k::Stack, window: &adw::ApplicationWindow, - internet_connected: &Rc> + internet_connected: &Rc>, ) { let internet_connected_status = internet_connected.clone(); @@ -38,12 +38,9 @@ pub fn setup_steps_page( .expect("The channel needs to be open."); }); - let setup_steps_page_box = gtk::Box::builder() - .vexpand(true) - .hexpand(true) - .build(); + let contribute_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build(); - let setup_steps_page_listbox = gtk::ListBox::builder() + let contribute_page_listbox = gtk::ListBox::builder() .margin_top(20) .margin_bottom(20) .margin_start(20) @@ -51,13 +48,13 @@ pub fn setup_steps_page( .vexpand(true) .hexpand(true) .build(); - setup_steps_page_listbox.add_css_class("boxed-list"); + contribute_page_listbox.add_css_class("boxed-list"); - let setup_steps_page_scroll = gtk::ScrolledWindow::builder() + let contribute_page_scroll = gtk::ScrolledWindow::builder() // that puts items vertically .hexpand(true) .vexpand(true) - .child(&setup_steps_page_box) + .child(&contribute_page_box) .propagate_natural_width(true) .propagate_natural_height(true) .build(); @@ -65,37 +62,43 @@ pub fn setup_steps_page( let internet_loop_context = MainContext::default(); // The main loop executes the asynchronous block internet_loop_context.spawn_local( - clone!(@strong internet_connected_status, @weak setup_steps_page_box => async move { + clone!(@strong internet_connected_status, @weak contribute_page_box => async move { while let Ok(_state) = internet_loop_receiver.recv().await { if *internet_connected_status.borrow_mut() == true { - setup_steps_page_box.set_sensitive(true); + contribute_page_box.set_sensitive(true); } else { - setup_steps_page_box.set_sensitive(false); + contribute_page_box.set_sensitive(false); } } }), ); - let mut json_array: Vec = Vec::new(); - let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; + let mut json_array: Vec = Vec::new(); + let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/contribute.json"; let json_data = fs::read_to_string(json_path).expect("Unable to read json"); - let json_data: serde_json::Value = serde_json::from_str(&json_data).expect("JSON format invalid"); - if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { - for setup_steps_entry in setup_steps { - let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); - json_array.push(setup_steps_entry_struct); + let json_data: serde_json::Value = + serde_json::from_str(&json_data).expect("JSON format invalid"); + if let serde_json::Value::Array(contribute) = &json_data["contribute"] { + for contribute_entry in contribute { + let contribute_entry_struct: contribute_entry = + serde_json::from_value(contribute_entry.clone()).unwrap(); + json_array.push(contribute_entry_struct); } } - for setup_steps_entry in json_array { - let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded(); - let entry_command_status_loop_sender: async_channel::Sender = entry_command_status_loop_sender.clone(); + let entry_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both); - let entry_title = setup_steps_entry.title; - let entry_subtitle = setup_steps_entry.subtitle; - let entry_icon = setup_steps_entry.icon; - let entry_button = setup_steps_entry.button; - let entry_command = setup_steps_entry.command; + for contribute_entry in json_array { + let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = + async_channel::unbounded(); + let entry_command_status_loop_sender: async_channel::Sender = + entry_command_status_loop_sender.clone(); + + let entry_title = contribute_entry.title; + let entry_subtitle = contribute_entry.subtitle; + let entry_icon = contribute_entry.icon; + let entry_button = contribute_entry.button; + let entry_command = contribute_entry.command; let entry_row = adw::ActionRow::builder() .title(t!(&entry_title)) .subtitle(t!(&entry_subtitle)) @@ -113,6 +116,7 @@ pub fn setup_steps_page( .vexpand(true) .valign(gtk::Align::Center) .build(); + entry_buttons_size_group.add_widget(&entry_row_button); entry_row.add_prefix(&entry_row_icon); entry_row.add_suffix(&entry_row_button); @@ -137,21 +141,30 @@ pub fn setup_steps_page( .heading(t!("cmd_err_dialog_heading")) .transient_for(window) .build(); - cmd_err_dialog.add_response("cmd_err_dialog_ok", &t!("cmd_err_dialog_ok_label").to_string()); + cmd_err_dialog.add_response( + "cmd_err_dialog_ok", + &t!("cmd_err_dialog_ok_label").to_string(), + ); let entry_command_status_loop_context = MainContext::default(); // The main loop executes the asynchronous block - entry_command_status_loop_context.spawn_local(clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { - while let Ok(state) = entry_command_status_loop_receiver.recv().await { - if state == false { - cmd_err_dialog.present(); + entry_command_status_loop_context.spawn_local( + clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { + while let Ok(state) = entry_command_status_loop_receiver.recv().await { + if state == false { + cmd_err_dialog.present(); + } } - } - })); - setup_steps_page_listbox.append(&entry_row) + }), + ); + contribute_page_listbox.append(&entry_row) } - setup_steps_page_box.append(&setup_steps_page_listbox); + contribute_page_box.append(&contribute_page_listbox); - setup_steps_content_page_stack.add_titled(&setup_steps_page_scroll, Some("setup_steps_page"), &t!("setup_steps_page_title").to_string()); -} \ No newline at end of file + contribute_content_page_stack.add_titled( + &contribute_page_scroll, + Some("contribute_page"), + &t!("contribute_page_title").to_string(), + ); +} diff --git a/src/welcome_content_page/look_and_feel_page/mod.rs b/src/welcome_content_page/look_and_feel_page/mod.rs index 720fc5e..b3cdad9 100644 --- a/src/welcome_content_page/look_and_feel_page/mod.rs +++ b/src/welcome_content_page/look_and_feel_page/mod.rs @@ -1,30 +1,30 @@ // GTK crates -use duct::cmd; -use std::path::Path; -use std::fs; -use serde::Deserialize; -use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; use adw::prelude::*; use adw::*; +use duct::cmd; use glib::*; +use serde::Deserialize; +use std::cell::RefCell; +use std::fs; +use std::path::Path; +use std::rc::Rc; +use std::{thread, time}; #[allow(non_camel_case_types)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] -struct setup_steps_entry { +struct look_and_feel_entry { id: i32, title: String, subtitle: String, icon: String, button: String, - command: String + command: String, } -pub fn setup_steps_page( - setup_steps_content_page_stack: >k::Stack, +pub fn look_and_feel_page( + look_and_feel_content_page_stack: >k::Stack, window: &adw::ApplicationWindow, - internet_connected: &Rc> + internet_connected: &Rc>, ) { let internet_connected_status = internet_connected.clone(); @@ -38,12 +38,9 @@ pub fn setup_steps_page( .expect("The channel needs to be open."); }); - let setup_steps_page_box = gtk::Box::builder() - .vexpand(true) - .hexpand(true) - .build(); + let look_and_feel_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build(); - let setup_steps_page_listbox = gtk::ListBox::builder() + let look_and_feel_page_listbox = gtk::ListBox::builder() .margin_top(20) .margin_bottom(20) .margin_start(20) @@ -51,13 +48,13 @@ pub fn setup_steps_page( .vexpand(true) .hexpand(true) .build(); - setup_steps_page_listbox.add_css_class("boxed-list"); + look_and_feel_page_listbox.add_css_class("boxed-list"); - let setup_steps_page_scroll = gtk::ScrolledWindow::builder() + let look_and_feel_page_scroll = gtk::ScrolledWindow::builder() // that puts items vertically .hexpand(true) .vexpand(true) - .child(&setup_steps_page_box) + .child(&look_and_feel_page_box) .propagate_natural_width(true) .propagate_natural_height(true) .build(); @@ -65,37 +62,43 @@ pub fn setup_steps_page( let internet_loop_context = MainContext::default(); // The main loop executes the asynchronous block internet_loop_context.spawn_local( - clone!(@strong internet_connected_status, @weak setup_steps_page_box => async move { + clone!(@strong internet_connected_status, @weak look_and_feel_page_box => async move { while let Ok(_state) = internet_loop_receiver.recv().await { if *internet_connected_status.borrow_mut() == true { - setup_steps_page_box.set_sensitive(true); + look_and_feel_page_box.set_sensitive(true); } else { - setup_steps_page_box.set_sensitive(false); + look_and_feel_page_box.set_sensitive(false); } } }), ); - let mut json_array: Vec = Vec::new(); - let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; + let mut json_array: Vec = Vec::new(); + let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/look_and_feel.json"; let json_data = fs::read_to_string(json_path).expect("Unable to read json"); - let json_data: serde_json::Value = serde_json::from_str(&json_data).expect("JSON format invalid"); - if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { - for setup_steps_entry in setup_steps { - let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); - json_array.push(setup_steps_entry_struct); + let json_data: serde_json::Value = + serde_json::from_str(&json_data).expect("JSON format invalid"); + if let serde_json::Value::Array(look_and_feel) = &json_data["look_and_feel"] { + for look_and_feel_entry in look_and_feel { + let look_and_feel_entry_struct: look_and_feel_entry = + serde_json::from_value(look_and_feel_entry.clone()).unwrap(); + json_array.push(look_and_feel_entry_struct); } } - for setup_steps_entry in json_array { - let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded(); - let entry_command_status_loop_sender: async_channel::Sender = entry_command_status_loop_sender.clone(); + let entry_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both); - let entry_title = setup_steps_entry.title; - let entry_subtitle = setup_steps_entry.subtitle; - let entry_icon = setup_steps_entry.icon; - let entry_button = setup_steps_entry.button; - let entry_command = setup_steps_entry.command; + for look_and_feel_entry in json_array { + let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = + async_channel::unbounded(); + let entry_command_status_loop_sender: async_channel::Sender = + entry_command_status_loop_sender.clone(); + + let entry_title = look_and_feel_entry.title; + let entry_subtitle = look_and_feel_entry.subtitle; + let entry_icon = look_and_feel_entry.icon; + let entry_button = look_and_feel_entry.button; + let entry_command = look_and_feel_entry.command; let entry_row = adw::ActionRow::builder() .title(t!(&entry_title)) .subtitle(t!(&entry_subtitle)) @@ -113,6 +116,8 @@ pub fn setup_steps_page( .vexpand(true) .valign(gtk::Align::Center) .build(); + entry_buttons_size_group.add_widget(&entry_row_button); + entry_buttons_size_group.add_widget(&entry_row_button); entry_row.add_prefix(&entry_row_icon); entry_row.add_suffix(&entry_row_button); @@ -137,21 +142,30 @@ pub fn setup_steps_page( .heading(t!("cmd_err_dialog_heading")) .transient_for(window) .build(); - cmd_err_dialog.add_response("cmd_err_dialog_ok", &t!("cmd_err_dialog_ok_label").to_string()); + cmd_err_dialog.add_response( + "cmd_err_dialog_ok", + &t!("cmd_err_dialog_ok_label").to_string(), + ); let entry_command_status_loop_context = MainContext::default(); // The main loop executes the asynchronous block - entry_command_status_loop_context.spawn_local(clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { - while let Ok(state) = entry_command_status_loop_receiver.recv().await { - if state == false { - cmd_err_dialog.present(); + entry_command_status_loop_context.spawn_local( + clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { + while let Ok(state) = entry_command_status_loop_receiver.recv().await { + if state == false { + cmd_err_dialog.present(); + } } - } - })); - setup_steps_page_listbox.append(&entry_row) + }), + ); + look_and_feel_page_listbox.append(&entry_row) } - setup_steps_page_box.append(&setup_steps_page_listbox); + look_and_feel_page_box.append(&look_and_feel_page_listbox); - setup_steps_content_page_stack.add_titled(&setup_steps_page_scroll, Some("setup_steps_page"), &t!("setup_steps_page_title").to_string()); -} \ No newline at end of file + look_and_feel_content_page_stack.add_titled( + &look_and_feel_page_scroll, + Some("look_and_feel_page"), + &t!("look_and_feel_page_title").to_string(), + ); +} diff --git a/src/welcome_content_page/mod.rs b/src/welcome_content_page/mod.rs index 9cc32f6..e5f88e0 100644 --- a/src/welcome_content_page/mod.rs +++ b/src/welcome_content_page/mod.rs @@ -8,13 +8,21 @@ use std::process::Command; use std::rc::Rc; // stack crates -mod welcome_page; -mod setup_steps_page; +mod community_page; +mod contribute_page; +mod look_and_feel_page; mod recommended_addons_page; +mod setup_steps_page; +mod troubleshoot_page; +mod welcome_page; -use welcome_page::welcome_page; -use setup_steps_page::setup_steps_page; +use community_page::community_page; +use contribute_page::contribute_page; +use look_and_feel_page::look_and_feel_page; use recommended_addons_page::recommended_addons_page; +use setup_steps_page::setup_steps_page; +use troubleshoot_page::troubleshoot_page; +use welcome_page::welcome_page; use crate::config::{APP_GITHUB, APP_ICON, APP_ID, VERSION}; @@ -52,9 +60,7 @@ pub fn welcome_content_page(window: &adw::ApplicationWindow, content_box: >k:: } }); - let window_banner = adw::Banner::builder() - .revealed(false) - .build(); + let window_banner = adw::Banner::builder().revealed(false).build(); let window_title_bar = gtk::HeaderBar::builder().show_title_buttons(true).build(); @@ -65,7 +71,8 @@ pub fn welcome_content_page(window: &adw::ApplicationWindow, content_box: >k:: let mut json_array: Vec = Vec::new(); let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/credits.json"; let json_data = std::fs::read_to_string(json_path).expect("Unable to read json"); - let json_data: serde_json::Value = serde_json::from_str(&json_data).expect("JSON format invalid"); + let json_data: serde_json::Value = + serde_json::from_str(&json_data).expect("JSON format invalid"); if let serde_json::Value::Array(developers) = &json_data["developers"] { for developer in developers { json_array.push(developer["dev"].as_str().to_owned().unwrap().into()) @@ -130,10 +137,27 @@ pub fn welcome_content_page(window: &adw::ApplicationWindow, content_box: >k:: .bidirectional() .build(); - let welcome_content_page_split_view_breakpoint = adw::Breakpoint::new(BreakpointCondition::new_length(BreakpointConditionLengthType::MaxWidth, 600.0, LengthUnit::Px)); - welcome_content_page_split_view_breakpoint.add_setter(&welcome_content_page_split_view, "collapsed", &true.to_value()); - welcome_content_page_split_view_breakpoint.add_setter(&startup_switch, "visible", &false.to_value()); - welcome_content_page_split_view_breakpoint.add_setter(&sidebar_toggle_button, "visible", &true.to_value()); + let welcome_content_page_split_view_breakpoint = + adw::Breakpoint::new(BreakpointCondition::new_length( + BreakpointConditionLengthType::MaxWidth, + 600.0, + LengthUnit::Px, + )); + welcome_content_page_split_view_breakpoint.add_setter( + &welcome_content_page_split_view, + "collapsed", + &true.to_value(), + ); + welcome_content_page_split_view_breakpoint.add_setter( + &startup_switch, + "visible", + &false.to_value(), + ); + welcome_content_page_split_view_breakpoint.add_setter( + &sidebar_toggle_button, + "visible", + &true.to_value(), + ); window.add_breakpoint(welcome_content_page_split_view_breakpoint); @@ -165,7 +189,15 @@ pub fn welcome_content_page(window: &adw::ApplicationWindow, content_box: >k:: } })); - welcome_page(&welcome_content_page_stack, &window_banner, &internet_connected); + welcome_page( + &welcome_content_page_stack, + &window_banner, + &internet_connected, + ); setup_steps_page(&welcome_content_page_stack, &window, &internet_connected); recommended_addons_page(&welcome_content_page_stack, &window, &internet_connected); + look_and_feel_page(&welcome_content_page_stack, &window, &internet_connected); + troubleshoot_page(&welcome_content_page_stack, &window, &internet_connected); + community_page(&welcome_content_page_stack, &window, &internet_connected); + contribute_page(&welcome_content_page_stack, &window, &internet_connected); } diff --git a/src/welcome_content_page/recommended_addons_page/mod.rs b/src/welcome_content_page/recommended_addons_page/mod.rs index 72a0ea0..461c696 100644 --- a/src/welcome_content_page/recommended_addons_page/mod.rs +++ b/src/welcome_content_page/recommended_addons_page/mod.rs @@ -1,18 +1,17 @@ // GTK crates -use std::io::BufReader; -use duct::cmd; -use std::io::BufRead; -use std::path::Path; -use std::fs; -use serde::Deserialize; -use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; use adw::prelude::*; use adw::*; +use duct::cmd; use glib::*; +use serde::Deserialize; +use std::cell::RefCell; use std::error::Error; +use std::fs; +use std::io::BufRead; +use std::io::BufReader; use std::process::Command; +use std::rc::Rc; +use std::{thread, time}; #[allow(non_camel_case_types)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] @@ -22,7 +21,7 @@ struct recommended_addons_entry { subtitle: String, icon: String, checkpkg: String, - packages: String + packages: String, } const ADDON_COMMAND_PROG1: &str = r###" @@ -44,10 +43,21 @@ fn run_addon_command( entry_packages: &str, ) -> Result<(), std::boxed::Box> { let (pipe_reader, pipe_writer) = os_pipe::pipe()?; - let child = cmd!("pkexec", "bash", "-c", ADDON_COMMAND_PROG1.to_owned() + "apt " + operation + " " + &entry_packages + r###" -y -o Dpkg::Options::="--force-confnew""### + ADDON_COMMAND_PROG2) - .stderr_to_stdout() - .stdout_file(pipe_writer) - .start()?; + let child = cmd!( + "pkexec", + "bash", + "-c", + ADDON_COMMAND_PROG1.to_owned() + + "apt " + + operation + + " " + + &entry_packages + + r###" -y -o Dpkg::Options::="--force-confnew""### + + ADDON_COMMAND_PROG2 + ) + .stderr_to_stdout() + .stdout_file(pipe_writer) + .start()?; for line in BufReader::new(pipe_reader).lines() { log_loop_sender .send_blocking(line?) @@ -61,7 +71,7 @@ fn run_addon_command( pub fn recommended_addons_page( recommended_addons_content_page_stack: >k::Stack, window: &adw::ApplicationWindow, - internet_connected: &Rc> + internet_connected: &Rc>, ) { let internet_connected_status = internet_connected.clone(); @@ -75,10 +85,7 @@ pub fn recommended_addons_page( .expect("The channel needs to be open."); }); - let recommended_addons_page_box = gtk::Box::builder() - .vexpand(true) - .hexpand(true) - .build(); + let recommended_addons_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build(); let recommended_addons_page_listbox = gtk::ListBox::builder() .margin_top(20) @@ -113,27 +120,32 @@ pub fn recommended_addons_page( }), ); + let entry_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both); + let mut json_array: Vec = Vec::new(); let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/recommended_addons.json"; let json_data = fs::read_to_string(json_path).expect("Unable to read json"); - let json_data: serde_json::Value = serde_json::from_str(&json_data).expect("JSON format invalid"); + let json_data: serde_json::Value = + serde_json::from_str(&json_data).expect("JSON format invalid"); if let serde_json::Value::Array(recommended_addons) = &json_data["recommended_addons"] { for recommended_addons_entry in recommended_addons { - let recommended_addons_entry_struct: recommended_addons_entry = serde_json::from_value(recommended_addons_entry.clone()).unwrap(); + let recommended_addons_entry_struct: recommended_addons_entry = + serde_json::from_value(recommended_addons_entry.clone()).unwrap(); json_array.push(recommended_addons_entry_struct); } } for recommended_addons_entry in json_array { - let (checkpkg_status_loop_sender, checkpkg_status_loop_receiver) = async_channel::unbounded(); - let checkpkg_status_loop_sender: async_channel::Sender = checkpkg_status_loop_sender.clone(); + let (checkpkg_status_loop_sender, checkpkg_status_loop_receiver) = + async_channel::unbounded(); + let checkpkg_status_loop_sender: async_channel::Sender = + checkpkg_status_loop_sender.clone(); 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 log_status_loop_sender: async_channel::Sender = log_status_loop_sender.clone(); let entry_title = recommended_addons_entry.title; let entry_subtitle = recommended_addons_entry.subtitle; @@ -141,18 +153,20 @@ pub fn recommended_addons_page( let entry_checkpkg = recommended_addons_entry.checkpkg; let entry_packages = recommended_addons_entry.packages; - gio::spawn_blocking(clone!(@strong checkpkg_status_loop_sender, @strong entry_checkpkg => move || { - let checkpkg_command = Command::new("dpkg") - .arg("-s") - .arg(entry_checkpkg) - .output() - .expect("failed to execute process"); - if checkpkg_command.status.success() { - checkpkg_status_loop_sender.send_blocking(true).expect("The channel needs to be open."); - } else { - checkpkg_status_loop_sender.send_blocking(false).expect("The channel needs to be open."); - } - })); + gio::spawn_blocking( + clone!(@strong checkpkg_status_loop_sender, @strong entry_checkpkg => move || { + let checkpkg_command = Command::new("dpkg") + .arg("-s") + .arg(entry_checkpkg) + .output() + .expect("failed to execute process"); + if checkpkg_command.status.success() { + checkpkg_status_loop_sender.send_blocking(true).expect("The channel needs to be open."); + } else { + checkpkg_status_loop_sender.send_blocking(false).expect("The channel needs to be open."); + } + }), + ); let entry_row = adw::ActionRow::builder() .title(t!(&entry_title)) @@ -170,6 +184,7 @@ pub fn recommended_addons_page( .vexpand(true) .valign(gtk::Align::Center) .build(); + entry_buttons_size_group.add_widget(&entry_row_button); entry_row.add_prefix(&entry_row_icon); entry_row.add_suffix(&entry_row_button); @@ -205,21 +220,23 @@ pub fn recommended_addons_page( let checkpkg_status_loop_context = MainContext::default(); // The main loop executes the asynchronous block - checkpkg_status_loop_context.spawn_local(clone!(@weak entry_row_button, @strong checkpkg_status_loop_receiver => async move { - while let Ok(state) = checkpkg_status_loop_receiver.recv().await { - if state == false { - entry_row_button.remove_css_class("destructive-action"); - entry_row_button.set_label(&t!("entry_row_button_install").to_string()); - entry_row_button.add_css_class("suggested-action"); - entry_row_button.set_widget_name("false") - } else { - entry_row_button.remove_css_class("suggested-action"); - entry_row_button.set_label(&t!("entry_row_button_remove").to_string()); - entry_row_button.add_css_class("destructive-action"); - entry_row_button.set_widget_name("true") + checkpkg_status_loop_context.spawn_local( + clone!(@weak entry_row_button, @strong checkpkg_status_loop_receiver => async move { + while let Ok(state) = checkpkg_status_loop_receiver.recv().await { + if state == false { + entry_row_button.remove_css_class("destructive-action"); + entry_row_button.set_label(&t!("entry_row_button_install").to_string()); + entry_row_button.add_css_class("suggested-action"); + entry_row_button.set_widget_name("false") + } else { + entry_row_button.remove_css_class("suggested-action"); + entry_row_button.set_label(&t!("entry_row_button_remove").to_string()); + entry_row_button.add_css_class("destructive-action"); + entry_row_button.set_widget_name("true") + } } - } - })); + }), + ); // let log_loop_context = MainContext::default(); @@ -291,5 +308,9 @@ pub fn recommended_addons_page( recommended_addons_page_box.append(&recommended_addons_page_listbox); - recommended_addons_content_page_stack.add_titled(&recommended_addons_page_scroll, Some("recommended_addons_page"), &t!("recommended_addons_page_title").to_string()); -} \ No newline at end of file + recommended_addons_content_page_stack.add_titled( + &recommended_addons_page_scroll, + Some("recommended_addons_page"), + &t!("recommended_addons_page_title").to_string(), + ); +} diff --git a/src/welcome_content_page/setup_steps_page/mod.rs b/src/welcome_content_page/setup_steps_page/mod.rs index 720fc5e..c9b524f 100644 --- a/src/welcome_content_page/setup_steps_page/mod.rs +++ b/src/welcome_content_page/setup_steps_page/mod.rs @@ -1,14 +1,14 @@ // GTK crates -use duct::cmd; -use std::path::Path; -use std::fs; -use serde::Deserialize; -use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; use adw::prelude::*; use adw::*; +use duct::cmd; use glib::*; +use serde::Deserialize; +use std::cell::RefCell; +use std::fs; +use std::path::Path; +use std::rc::Rc; +use std::{thread, time}; #[allow(non_camel_case_types)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] @@ -18,13 +18,13 @@ struct setup_steps_entry { subtitle: String, icon: String, button: String, - command: String + command: String, } pub fn setup_steps_page( setup_steps_content_page_stack: >k::Stack, window: &adw::ApplicationWindow, - internet_connected: &Rc> + internet_connected: &Rc>, ) { let internet_connected_status = internet_connected.clone(); @@ -38,10 +38,7 @@ pub fn setup_steps_page( .expect("The channel needs to be open."); }); - let setup_steps_page_box = gtk::Box::builder() - .vexpand(true) - .hexpand(true) - .build(); + let setup_steps_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build(); let setup_steps_page_listbox = gtk::ListBox::builder() .margin_top(20) @@ -79,17 +76,23 @@ pub fn setup_steps_page( let mut json_array: Vec = Vec::new(); let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; let json_data = fs::read_to_string(json_path).expect("Unable to read json"); - let json_data: serde_json::Value = serde_json::from_str(&json_data).expect("JSON format invalid"); + let json_data: serde_json::Value = + serde_json::from_str(&json_data).expect("JSON format invalid"); if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { for setup_steps_entry in setup_steps { - let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); + let setup_steps_entry_struct: setup_steps_entry = + serde_json::from_value(setup_steps_entry.clone()).unwrap(); json_array.push(setup_steps_entry_struct); } } + let entry_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both); + for setup_steps_entry in json_array { - let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded(); - let entry_command_status_loop_sender: async_channel::Sender = entry_command_status_loop_sender.clone(); + let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = + async_channel::unbounded(); + let entry_command_status_loop_sender: async_channel::Sender = + entry_command_status_loop_sender.clone(); let entry_title = setup_steps_entry.title; let entry_subtitle = setup_steps_entry.subtitle; @@ -113,6 +116,7 @@ pub fn setup_steps_page( .vexpand(true) .valign(gtk::Align::Center) .build(); + entry_buttons_size_group.add_widget(&entry_row_button); entry_row.add_prefix(&entry_row_icon); entry_row.add_suffix(&entry_row_button); @@ -137,21 +141,30 @@ pub fn setup_steps_page( .heading(t!("cmd_err_dialog_heading")) .transient_for(window) .build(); - cmd_err_dialog.add_response("cmd_err_dialog_ok", &t!("cmd_err_dialog_ok_label").to_string()); + cmd_err_dialog.add_response( + "cmd_err_dialog_ok", + &t!("cmd_err_dialog_ok_label").to_string(), + ); let entry_command_status_loop_context = MainContext::default(); // The main loop executes the asynchronous block - entry_command_status_loop_context.spawn_local(clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { - while let Ok(state) = entry_command_status_loop_receiver.recv().await { - if state == false { - cmd_err_dialog.present(); + entry_command_status_loop_context.spawn_local( + clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { + while let Ok(state) = entry_command_status_loop_receiver.recv().await { + if state == false { + cmd_err_dialog.present(); + } } - } - })); + }), + ); setup_steps_page_listbox.append(&entry_row) } setup_steps_page_box.append(&setup_steps_page_listbox); - setup_steps_content_page_stack.add_titled(&setup_steps_page_scroll, Some("setup_steps_page"), &t!("setup_steps_page_title").to_string()); -} \ No newline at end of file + setup_steps_content_page_stack.add_titled( + &setup_steps_page_scroll, + Some("setup_steps_page"), + &t!("setup_steps_page_title").to_string(), + ); +} diff --git a/src/welcome_content_page/troubleshoot_page/mod.rs b/src/welcome_content_page/troubleshoot_page/mod.rs index 720fc5e..c21f799 100644 --- a/src/welcome_content_page/troubleshoot_page/mod.rs +++ b/src/welcome_content_page/troubleshoot_page/mod.rs @@ -1,30 +1,30 @@ // GTK crates -use duct::cmd; -use std::path::Path; -use std::fs; -use serde::Deserialize; -use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; use adw::prelude::*; use adw::*; +use duct::cmd; use glib::*; +use serde::Deserialize; +use std::cell::RefCell; +use std::fs; +use std::path::Path; +use std::rc::Rc; +use std::{thread, time}; #[allow(non_camel_case_types)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] -struct setup_steps_entry { +struct troubleshoot_entry { id: i32, title: String, subtitle: String, icon: String, button: String, - command: String + command: String, } -pub fn setup_steps_page( - setup_steps_content_page_stack: >k::Stack, +pub fn troubleshoot_page( + troubleshoot_content_page_stack: >k::Stack, window: &adw::ApplicationWindow, - internet_connected: &Rc> + internet_connected: &Rc>, ) { let internet_connected_status = internet_connected.clone(); @@ -38,12 +38,9 @@ pub fn setup_steps_page( .expect("The channel needs to be open."); }); - let setup_steps_page_box = gtk::Box::builder() - .vexpand(true) - .hexpand(true) - .build(); + let troubleshoot_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build(); - let setup_steps_page_listbox = gtk::ListBox::builder() + let troubleshoot_page_listbox = gtk::ListBox::builder() .margin_top(20) .margin_bottom(20) .margin_start(20) @@ -51,13 +48,13 @@ pub fn setup_steps_page( .vexpand(true) .hexpand(true) .build(); - setup_steps_page_listbox.add_css_class("boxed-list"); + troubleshoot_page_listbox.add_css_class("boxed-list"); - let setup_steps_page_scroll = gtk::ScrolledWindow::builder() + let troubleshoot_page_scroll = gtk::ScrolledWindow::builder() // that puts items vertically .hexpand(true) .vexpand(true) - .child(&setup_steps_page_box) + .child(&troubleshoot_page_box) .propagate_natural_width(true) .propagate_natural_height(true) .build(); @@ -65,37 +62,43 @@ pub fn setup_steps_page( let internet_loop_context = MainContext::default(); // The main loop executes the asynchronous block internet_loop_context.spawn_local( - clone!(@strong internet_connected_status, @weak setup_steps_page_box => async move { + clone!(@strong internet_connected_status, @weak troubleshoot_page_box => async move { while let Ok(_state) = internet_loop_receiver.recv().await { if *internet_connected_status.borrow_mut() == true { - setup_steps_page_box.set_sensitive(true); + troubleshoot_page_box.set_sensitive(true); } else { - setup_steps_page_box.set_sensitive(false); + troubleshoot_page_box.set_sensitive(false); } } }), ); - let mut json_array: Vec = Vec::new(); - let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; + let mut json_array: Vec = Vec::new(); + let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/troubleshoot.json"; let json_data = fs::read_to_string(json_path).expect("Unable to read json"); - let json_data: serde_json::Value = serde_json::from_str(&json_data).expect("JSON format invalid"); - if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { - for setup_steps_entry in setup_steps { - let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); - json_array.push(setup_steps_entry_struct); + let json_data: serde_json::Value = + serde_json::from_str(&json_data).expect("JSON format invalid"); + if let serde_json::Value::Array(troubleshoot) = &json_data["troubleshoot"] { + for troubleshoot_entry in troubleshoot { + let troubleshoot_entry_struct: troubleshoot_entry = + serde_json::from_value(troubleshoot_entry.clone()).unwrap(); + json_array.push(troubleshoot_entry_struct); } } - for setup_steps_entry in json_array { - let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded(); - let entry_command_status_loop_sender: async_channel::Sender = entry_command_status_loop_sender.clone(); + let entry_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both); - let entry_title = setup_steps_entry.title; - let entry_subtitle = setup_steps_entry.subtitle; - let entry_icon = setup_steps_entry.icon; - let entry_button = setup_steps_entry.button; - let entry_command = setup_steps_entry.command; + for troubleshoot_entry in json_array { + let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = + async_channel::unbounded(); + let entry_command_status_loop_sender: async_channel::Sender = + entry_command_status_loop_sender.clone(); + + let entry_title = troubleshoot_entry.title; + let entry_subtitle = troubleshoot_entry.subtitle; + let entry_icon = troubleshoot_entry.icon; + let entry_button = troubleshoot_entry.button; + let entry_command = troubleshoot_entry.command; let entry_row = adw::ActionRow::builder() .title(t!(&entry_title)) .subtitle(t!(&entry_subtitle)) @@ -113,6 +116,7 @@ pub fn setup_steps_page( .vexpand(true) .valign(gtk::Align::Center) .build(); + entry_buttons_size_group.add_widget(&entry_row_button); entry_row.add_prefix(&entry_row_icon); entry_row.add_suffix(&entry_row_button); @@ -137,21 +141,30 @@ pub fn setup_steps_page( .heading(t!("cmd_err_dialog_heading")) .transient_for(window) .build(); - cmd_err_dialog.add_response("cmd_err_dialog_ok", &t!("cmd_err_dialog_ok_label").to_string()); + cmd_err_dialog.add_response( + "cmd_err_dialog_ok", + &t!("cmd_err_dialog_ok_label").to_string(), + ); let entry_command_status_loop_context = MainContext::default(); // The main loop executes the asynchronous block - entry_command_status_loop_context.spawn_local(clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { - while let Ok(state) = entry_command_status_loop_receiver.recv().await { - if state == false { - cmd_err_dialog.present(); + entry_command_status_loop_context.spawn_local( + clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move { + while let Ok(state) = entry_command_status_loop_receiver.recv().await { + if state == false { + cmd_err_dialog.present(); + } } - } - })); - setup_steps_page_listbox.append(&entry_row) + }), + ); + troubleshoot_page_listbox.append(&entry_row) } - setup_steps_page_box.append(&setup_steps_page_listbox); + troubleshoot_page_box.append(&troubleshoot_page_listbox); - setup_steps_content_page_stack.add_titled(&setup_steps_page_scroll, Some("setup_steps_page"), &t!("setup_steps_page_title").to_string()); -} \ No newline at end of file + troubleshoot_content_page_stack.add_titled( + &troubleshoot_page_scroll, + Some("troubleshoot_page"), + &t!("troubleshoot_page_title").to_string(), + ); +} diff --git a/src/welcome_content_page/welcome_page/mod.rs b/src/welcome_content_page/welcome_page/mod.rs index 1e28701..a3da03e 100644 --- a/src/welcome_content_page/welcome_page/mod.rs +++ b/src/welcome_content_page/welcome_page/mod.rs @@ -1,16 +1,16 @@ // GTK crates -use std::{thread, time}; -use std::rc::Rc; -use std::cell::RefCell; +use crate::config::DISTRO_ICON; use adw::prelude::*; use adw::*; use glib::*; -use crate::config::DISTRO_ICON; +use std::cell::RefCell; +use std::rc::Rc; +use std::{thread, time}; pub fn welcome_page( welcome_content_page_stack: >k::Stack, window_banner: &adw::Banner, - internet_connected: &Rc> + internet_connected: &Rc>, ) { let internet_connected_status = internet_connected.clone(); @@ -56,5 +56,9 @@ pub fn welcome_page( }), ); - welcome_content_page_stack.add_titled(&welcome_page_scroll, Some("welcome_page"), &t!("welcome_page_title").to_string()); -} \ No newline at end of file + welcome_content_page_stack.add_titled( + &welcome_page_scroll, + Some("welcome_page"), + &t!("welcome_page_title").to_string(), + ); +}