Add size groups

This commit is contained in:
Ward from fusion-voyager-3 2024-02-24 21:17:38 +03:00
parent 02e7cac3a9
commit 7028ce4682
15 changed files with 508 additions and 307 deletions

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -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"
}
]
}

View File

@ -1,7 +1,4 @@
// GTK crates // GTK crates
use adw::prelude::*;
use adw::*;
use gtk::Orientation;
/// Use all gtk4 libraries (gtk4 -> gtk because cargo) /// Use all gtk4 libraries (gtk4 -> gtk because cargo)
/// Use all libadwaita libraries (libadwaita -> adw because cargo) /// Use all libadwaita libraries (libadwaita -> adw because cargo)
@ -10,6 +7,9 @@ use gtk::Orientation;
use crate::config::*; use crate::config::*;
use crate::save_window_size::save_window_size; use crate::save_window_size::save_window_size;
use crate::welcome_content_page::welcome_content_page; use crate::welcome_content_page::welcome_content_page;
use adw::prelude::*;
use adw::*;
use gtk::Orientation;
pub fn build_ui(app: &adw::Application) { pub fn build_ui(app: &adw::Application) {
// setup glib // setup glib

View File

@ -1,7 +1,6 @@
// GTK crates // GTK crates
mod config; mod config;
use std::env;
use adw::prelude::*; use adw::prelude::*;
use adw::*; use adw::*;
use gdk::Display; use gdk::Display;
@ -9,10 +8,11 @@ use gdk::Display;
/// Use all libadwaita libraries (libadwaita -> adw because cargo) /// Use all libadwaita libraries (libadwaita -> adw because cargo)
use gtk::*; use gtk::*;
use single_instance::SingleInstance; use single_instance::SingleInstance;
use std::env;
use config::APP_ID;
use std::boxed::Box; use std::boxed::Box;
use users::*; use users::*;
use config::APP_ID;
// application crates // application crates
mod build_ui; mod build_ui;
@ -33,10 +33,7 @@ fn main() {
None => panic!("$LANG is not set"), None => panic!("$LANG is not set"),
}; };
rust_i18n::set_locale(current_locale.strip_suffix(".UTF-8").unwrap()); rust_i18n::set_locale(current_locale.strip_suffix(".UTF-8").unwrap());
let application = adw::Application::new( let application = adw::Application::new(Some(APP_ID), Default::default());
Some(APP_ID),
Default::default(),
);
application.connect_startup(|app| { application.connect_startup(|app| {
// The CSS "magic" happens here. // The CSS "magic" happens here.
let provider = CssProvider::new(); let provider = CssProvider::new();
@ -60,5 +57,4 @@ fn main() {
println!("Error: This program can only be run via an installed system user"); println!("Error: This program can only be run via an installed system user");
std::process::exit(1) std::process::exit(1)
} }
} }

View File

@ -1,4 +1,4 @@
use adw::{gio,}; use adw::gio;
use adw::prelude::SettingsExt; use adw::prelude::SettingsExt;
use gtk::prelude::GtkWindowExt; use gtk::prelude::GtkWindowExt;
pub fn save_window_size(window: &adw::ApplicationWindow, glib_settings: &gio::Settings) { pub fn save_window_size(window: &adw::ApplicationWindow, glib_settings: &gio::Settings) {

View File

@ -1,30 +1,30 @@
// GTK crates // 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::prelude::*;
use adw::*; use adw::*;
use duct::cmd;
use glib::*; 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)] #[allow(non_camel_case_types)]
#[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)]
struct setup_steps_entry { struct community_entry {
id: i32, id: i32,
title: String, title: String,
subtitle: String, subtitle: String,
icon: String, icon: String,
button: String, button: String,
command: String command: String,
} }
pub fn setup_steps_page( pub fn community_page(
setup_steps_content_page_stack: &gtk::Stack, community_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow, window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>> internet_connected: &Rc<RefCell<bool>>,
) { ) {
let internet_connected_status = internet_connected.clone(); let internet_connected_status = internet_connected.clone();
@ -38,12 +38,9 @@ pub fn setup_steps_page(
.expect("The channel needs to be open."); .expect("The channel needs to be open.");
}); });
let setup_steps_page_box = gtk::Box::builder() let community_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build();
.vexpand(true)
.hexpand(true)
.build();
let setup_steps_page_listbox = gtk::ListBox::builder() let community_page_listbox = gtk::ListBox::builder()
.margin_top(20) .margin_top(20)
.margin_bottom(20) .margin_bottom(20)
.margin_start(20) .margin_start(20)
@ -51,13 +48,13 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.hexpand(true) .hexpand(true)
.build(); .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 // that puts items vertically
.hexpand(true) .hexpand(true)
.vexpand(true) .vexpand(true)
.child(&setup_steps_page_box) .child(&community_page_box)
.propagate_natural_width(true) .propagate_natural_width(true)
.propagate_natural_height(true) .propagate_natural_height(true)
.build(); .build();
@ -65,37 +62,43 @@ pub fn setup_steps_page(
let internet_loop_context = MainContext::default(); let internet_loop_context = MainContext::default();
// The main loop executes the asynchronous block // The main loop executes the asynchronous block
internet_loop_context.spawn_local( 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 { while let Ok(_state) = internet_loop_receiver.recv().await {
if *internet_connected_status.borrow_mut() == true { if *internet_connected_status.borrow_mut() == true {
setup_steps_page_box.set_sensitive(true); community_page_box.set_sensitive(true);
} else { } else {
setup_steps_page_box.set_sensitive(false); community_page_box.set_sensitive(false);
} }
} }
}), }),
); );
let mut json_array: Vec<setup_steps_entry> = Vec::new(); let mut json_array: Vec<community_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; 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 = 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 =
if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { serde_json::from_str(&json_data).expect("JSON format invalid");
for setup_steps_entry in setup_steps { if let serde_json::Value::Array(community) = &json_data["community"] {
let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); for community_entry in community {
json_array.push(setup_steps_entry_struct); 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_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both);
let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded();
let entry_command_status_loop_sender: async_channel::Sender<bool> = entry_command_status_loop_sender.clone();
let entry_title = setup_steps_entry.title; for community_entry in json_array {
let entry_subtitle = setup_steps_entry.subtitle; let (entry_command_status_loop_sender, entry_command_status_loop_receiver) =
let entry_icon = setup_steps_entry.icon; async_channel::unbounded();
let entry_button = setup_steps_entry.button; let entry_command_status_loop_sender: async_channel::Sender<bool> =
let entry_command = setup_steps_entry.command; 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() let entry_row = adw::ActionRow::builder()
.title(t!(&entry_title)) .title(t!(&entry_title))
.subtitle(t!(&entry_subtitle)) .subtitle(t!(&entry_subtitle))
@ -113,6 +116,7 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.valign(gtk::Align::Center) .valign(gtk::Align::Center)
.build(); .build();
entry_buttons_size_group.add_widget(&entry_row_button);
entry_row.add_prefix(&entry_row_icon); entry_row.add_prefix(&entry_row_icon);
entry_row.add_suffix(&entry_row_button); entry_row.add_suffix(&entry_row_button);
@ -137,21 +141,30 @@ pub fn setup_steps_page(
.heading(t!("cmd_err_dialog_heading")) .heading(t!("cmd_err_dialog_heading"))
.transient_for(window) .transient_for(window)
.build(); .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(); let entry_command_status_loop_context = MainContext::default();
// The main loop executes the asynchronous block // 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 { entry_command_status_loop_context.spawn_local(
while let Ok(state) = entry_command_status_loop_receiver.recv().await { clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move {
if state == false { while let Ok(state) = entry_command_status_loop_receiver.recv().await {
cmd_err_dialog.present(); 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()); community_content_page_stack.add_titled(
&community_page_scroll,
Some("community_page"),
&t!("community_page_title").to_string(),
);
} }

View File

@ -1,30 +1,30 @@
// GTK crates // 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::prelude::*;
use adw::*; use adw::*;
use duct::cmd;
use glib::*; 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)] #[allow(non_camel_case_types)]
#[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)]
struct setup_steps_entry { struct contribute_entry {
id: i32, id: i32,
title: String, title: String,
subtitle: String, subtitle: String,
icon: String, icon: String,
button: String, button: String,
command: String command: String,
} }
pub fn setup_steps_page( pub fn contribute_page(
setup_steps_content_page_stack: &gtk::Stack, contribute_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow, window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>> internet_connected: &Rc<RefCell<bool>>,
) { ) {
let internet_connected_status = internet_connected.clone(); let internet_connected_status = internet_connected.clone();
@ -38,12 +38,9 @@ pub fn setup_steps_page(
.expect("The channel needs to be open."); .expect("The channel needs to be open.");
}); });
let setup_steps_page_box = gtk::Box::builder() let contribute_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build();
.vexpand(true)
.hexpand(true)
.build();
let setup_steps_page_listbox = gtk::ListBox::builder() let contribute_page_listbox = gtk::ListBox::builder()
.margin_top(20) .margin_top(20)
.margin_bottom(20) .margin_bottom(20)
.margin_start(20) .margin_start(20)
@ -51,13 +48,13 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.hexpand(true) .hexpand(true)
.build(); .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 // that puts items vertically
.hexpand(true) .hexpand(true)
.vexpand(true) .vexpand(true)
.child(&setup_steps_page_box) .child(&contribute_page_box)
.propagate_natural_width(true) .propagate_natural_width(true)
.propagate_natural_height(true) .propagate_natural_height(true)
.build(); .build();
@ -65,37 +62,43 @@ pub fn setup_steps_page(
let internet_loop_context = MainContext::default(); let internet_loop_context = MainContext::default();
// The main loop executes the asynchronous block // The main loop executes the asynchronous block
internet_loop_context.spawn_local( 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 { while let Ok(_state) = internet_loop_receiver.recv().await {
if *internet_connected_status.borrow_mut() == true { if *internet_connected_status.borrow_mut() == true {
setup_steps_page_box.set_sensitive(true); contribute_page_box.set_sensitive(true);
} else { } else {
setup_steps_page_box.set_sensitive(false); contribute_page_box.set_sensitive(false);
} }
} }
}), }),
); );
let mut json_array: Vec<setup_steps_entry> = Vec::new(); let mut json_array: Vec<contribute_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; 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 = 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 =
if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { serde_json::from_str(&json_data).expect("JSON format invalid");
for setup_steps_entry in setup_steps { if let serde_json::Value::Array(contribute) = &json_data["contribute"] {
let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); for contribute_entry in contribute {
json_array.push(setup_steps_entry_struct); 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_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both);
let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded();
let entry_command_status_loop_sender: async_channel::Sender<bool> = entry_command_status_loop_sender.clone();
let entry_title = setup_steps_entry.title; for contribute_entry in json_array {
let entry_subtitle = setup_steps_entry.subtitle; let (entry_command_status_loop_sender, entry_command_status_loop_receiver) =
let entry_icon = setup_steps_entry.icon; async_channel::unbounded();
let entry_button = setup_steps_entry.button; let entry_command_status_loop_sender: async_channel::Sender<bool> =
let entry_command = setup_steps_entry.command; 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() let entry_row = adw::ActionRow::builder()
.title(t!(&entry_title)) .title(t!(&entry_title))
.subtitle(t!(&entry_subtitle)) .subtitle(t!(&entry_subtitle))
@ -113,6 +116,7 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.valign(gtk::Align::Center) .valign(gtk::Align::Center)
.build(); .build();
entry_buttons_size_group.add_widget(&entry_row_button);
entry_row.add_prefix(&entry_row_icon); entry_row.add_prefix(&entry_row_icon);
entry_row.add_suffix(&entry_row_button); entry_row.add_suffix(&entry_row_button);
@ -137,21 +141,30 @@ pub fn setup_steps_page(
.heading(t!("cmd_err_dialog_heading")) .heading(t!("cmd_err_dialog_heading"))
.transient_for(window) .transient_for(window)
.build(); .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(); let entry_command_status_loop_context = MainContext::default();
// The main loop executes the asynchronous block // 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 { entry_command_status_loop_context.spawn_local(
while let Ok(state) = entry_command_status_loop_receiver.recv().await { clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move {
if state == false { while let Ok(state) = entry_command_status_loop_receiver.recv().await {
cmd_err_dialog.present(); 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()); contribute_content_page_stack.add_titled(
&contribute_page_scroll,
Some("contribute_page"),
&t!("contribute_page_title").to_string(),
);
} }

View File

@ -1,30 +1,30 @@
// GTK crates // 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::prelude::*;
use adw::*; use adw::*;
use duct::cmd;
use glib::*; 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)] #[allow(non_camel_case_types)]
#[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)]
struct setup_steps_entry { struct look_and_feel_entry {
id: i32, id: i32,
title: String, title: String,
subtitle: String, subtitle: String,
icon: String, icon: String,
button: String, button: String,
command: String command: String,
} }
pub fn setup_steps_page( pub fn look_and_feel_page(
setup_steps_content_page_stack: &gtk::Stack, look_and_feel_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow, window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>> internet_connected: &Rc<RefCell<bool>>,
) { ) {
let internet_connected_status = internet_connected.clone(); let internet_connected_status = internet_connected.clone();
@ -38,12 +38,9 @@ pub fn setup_steps_page(
.expect("The channel needs to be open."); .expect("The channel needs to be open.");
}); });
let setup_steps_page_box = gtk::Box::builder() let look_and_feel_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build();
.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_top(20)
.margin_bottom(20) .margin_bottom(20)
.margin_start(20) .margin_start(20)
@ -51,13 +48,13 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.hexpand(true) .hexpand(true)
.build(); .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 // that puts items vertically
.hexpand(true) .hexpand(true)
.vexpand(true) .vexpand(true)
.child(&setup_steps_page_box) .child(&look_and_feel_page_box)
.propagate_natural_width(true) .propagate_natural_width(true)
.propagate_natural_height(true) .propagate_natural_height(true)
.build(); .build();
@ -65,37 +62,43 @@ pub fn setup_steps_page(
let internet_loop_context = MainContext::default(); let internet_loop_context = MainContext::default();
// The main loop executes the asynchronous block // The main loop executes the asynchronous block
internet_loop_context.spawn_local( 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 { while let Ok(_state) = internet_loop_receiver.recv().await {
if *internet_connected_status.borrow_mut() == true { if *internet_connected_status.borrow_mut() == true {
setup_steps_page_box.set_sensitive(true); look_and_feel_page_box.set_sensitive(true);
} else { } else {
setup_steps_page_box.set_sensitive(false); look_and_feel_page_box.set_sensitive(false);
} }
} }
}), }),
); );
let mut json_array: Vec<setup_steps_entry> = Vec::new(); let mut json_array: Vec<look_and_feel_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; 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 = 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 =
if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { serde_json::from_str(&json_data).expect("JSON format invalid");
for setup_steps_entry in setup_steps { if let serde_json::Value::Array(look_and_feel) = &json_data["look_and_feel"] {
let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); for look_and_feel_entry in look_and_feel {
json_array.push(setup_steps_entry_struct); 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_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both);
let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded();
let entry_command_status_loop_sender: async_channel::Sender<bool> = entry_command_status_loop_sender.clone();
let entry_title = setup_steps_entry.title; for look_and_feel_entry in json_array {
let entry_subtitle = setup_steps_entry.subtitle; let (entry_command_status_loop_sender, entry_command_status_loop_receiver) =
let entry_icon = setup_steps_entry.icon; async_channel::unbounded();
let entry_button = setup_steps_entry.button; let entry_command_status_loop_sender: async_channel::Sender<bool> =
let entry_command = setup_steps_entry.command; 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() let entry_row = adw::ActionRow::builder()
.title(t!(&entry_title)) .title(t!(&entry_title))
.subtitle(t!(&entry_subtitle)) .subtitle(t!(&entry_subtitle))
@ -113,6 +116,8 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.valign(gtk::Align::Center) .valign(gtk::Align::Center)
.build(); .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_prefix(&entry_row_icon);
entry_row.add_suffix(&entry_row_button); entry_row.add_suffix(&entry_row_button);
@ -137,21 +142,30 @@ pub fn setup_steps_page(
.heading(t!("cmd_err_dialog_heading")) .heading(t!("cmd_err_dialog_heading"))
.transient_for(window) .transient_for(window)
.build(); .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(); let entry_command_status_loop_context = MainContext::default();
// The main loop executes the asynchronous block // 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 { entry_command_status_loop_context.spawn_local(
while let Ok(state) = entry_command_status_loop_receiver.recv().await { clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move {
if state == false { while let Ok(state) = entry_command_status_loop_receiver.recv().await {
cmd_err_dialog.present(); 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()); 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(),
);
} }

View File

@ -8,13 +8,21 @@ use std::process::Command;
use std::rc::Rc; use std::rc::Rc;
// stack crates // stack crates
mod welcome_page; mod community_page;
mod setup_steps_page; mod contribute_page;
mod look_and_feel_page;
mod recommended_addons_page; mod recommended_addons_page;
mod setup_steps_page;
mod troubleshoot_page;
mod welcome_page;
use welcome_page::welcome_page; use community_page::community_page;
use setup_steps_page::setup_steps_page; use contribute_page::contribute_page;
use look_and_feel_page::look_and_feel_page;
use recommended_addons_page::recommended_addons_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}; use crate::config::{APP_GITHUB, APP_ICON, APP_ID, VERSION};
@ -52,9 +60,7 @@ pub fn welcome_content_page(window: &adw::ApplicationWindow, content_box: &gtk::
} }
}); });
let window_banner = adw::Banner::builder() let window_banner = adw::Banner::builder().revealed(false).build();
.revealed(false)
.build();
let window_title_bar = gtk::HeaderBar::builder().show_title_buttons(true).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: &gtk::
let mut json_array: Vec<GString> = Vec::new(); let mut json_array: Vec<GString> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/credits.json"; 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 = 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"] { if let serde_json::Value::Array(developers) = &json_data["developers"] {
for developer in developers { for developer in developers {
json_array.push(developer["dev"].as_str().to_owned().unwrap().into()) 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: &gtk::
.bidirectional() .bidirectional()
.build(); .build();
let welcome_content_page_split_view_breakpoint = adw::Breakpoint::new(BreakpointCondition::new_length(BreakpointConditionLengthType::MaxWidth, 600.0, LengthUnit::Px)); let welcome_content_page_split_view_breakpoint =
welcome_content_page_split_view_breakpoint.add_setter(&welcome_content_page_split_view, "collapsed", &true.to_value()); adw::Breakpoint::new(BreakpointCondition::new_length(
welcome_content_page_split_view_breakpoint.add_setter(&startup_switch, "visible", &false.to_value()); BreakpointConditionLengthType::MaxWidth,
welcome_content_page_split_view_breakpoint.add_setter(&sidebar_toggle_button, "visible", &true.to_value()); 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); window.add_breakpoint(welcome_content_page_split_view_breakpoint);
@ -165,7 +189,15 @@ pub fn welcome_content_page(window: &adw::ApplicationWindow, content_box: &gtk::
} }
})); }));
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); setup_steps_page(&welcome_content_page_stack, &window, &internet_connected);
recommended_addons_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);
} }

View File

@ -1,18 +1,17 @@
// GTK crates // 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::prelude::*;
use adw::*; use adw::*;
use duct::cmd;
use glib::*; use glib::*;
use serde::Deserialize;
use std::cell::RefCell;
use std::error::Error; use std::error::Error;
use std::fs;
use std::io::BufRead;
use std::io::BufReader;
use std::process::Command; use std::process::Command;
use std::rc::Rc;
use std::{thread, time};
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)]
@ -22,7 +21,7 @@ struct recommended_addons_entry {
subtitle: String, subtitle: String,
icon: String, icon: String,
checkpkg: String, checkpkg: String,
packages: String packages: String,
} }
const ADDON_COMMAND_PROG1: &str = r###" const ADDON_COMMAND_PROG1: &str = r###"
@ -44,10 +43,21 @@ fn run_addon_command(
entry_packages: &str, entry_packages: &str,
) -> Result<(), std::boxed::Box<dyn Error + Send + Sync>> { ) -> Result<(), std::boxed::Box<dyn Error + Send + Sync>> {
let (pipe_reader, pipe_writer) = os_pipe::pipe()?; 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) let child = cmd!(
.stderr_to_stdout() "pkexec",
.stdout_file(pipe_writer) "bash",
.start()?; "-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() { for line in BufReader::new(pipe_reader).lines() {
log_loop_sender log_loop_sender
.send_blocking(line?) .send_blocking(line?)
@ -61,7 +71,7 @@ fn run_addon_command(
pub fn recommended_addons_page( pub fn recommended_addons_page(
recommended_addons_content_page_stack: &gtk::Stack, recommended_addons_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow, window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>> internet_connected: &Rc<RefCell<bool>>,
) { ) {
let internet_connected_status = internet_connected.clone(); let internet_connected_status = internet_connected.clone();
@ -75,10 +85,7 @@ pub fn recommended_addons_page(
.expect("The channel needs to be open."); .expect("The channel needs to be open.");
}); });
let recommended_addons_page_box = gtk::Box::builder() let recommended_addons_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build();
.vexpand(true)
.hexpand(true)
.build();
let recommended_addons_page_listbox = gtk::ListBox::builder() let recommended_addons_page_listbox = gtk::ListBox::builder()
.margin_top(20) .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<recommended_addons_entry> = Vec::new(); let mut json_array: Vec<recommended_addons_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/recommended_addons.json"; 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 = 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"] { if let serde_json::Value::Array(recommended_addons) = &json_data["recommended_addons"] {
for recommended_addons_entry in 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); json_array.push(recommended_addons_entry_struct);
} }
} }
for recommended_addons_entry in json_array { for recommended_addons_entry in json_array {
let (checkpkg_status_loop_sender, checkpkg_status_loop_receiver) = async_channel::unbounded(); let (checkpkg_status_loop_sender, checkpkg_status_loop_receiver) =
let checkpkg_status_loop_sender: async_channel::Sender<bool> = checkpkg_status_loop_sender.clone(); async_channel::unbounded();
let checkpkg_status_loop_sender: async_channel::Sender<bool> =
checkpkg_status_loop_sender.clone();
let (log_loop_sender, log_loop_receiver) = async_channel::unbounded(); let (log_loop_sender, log_loop_receiver) = async_channel::unbounded();
let log_loop_sender: async_channel::Sender<String> = log_loop_sender.clone(); 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, log_status_loop_receiver) = async_channel::unbounded();
let log_status_loop_sender: async_channel::Sender<bool> = let log_status_loop_sender: async_channel::Sender<bool> = log_status_loop_sender.clone();
log_status_loop_sender.clone();
let entry_title = recommended_addons_entry.title; let entry_title = recommended_addons_entry.title;
let entry_subtitle = recommended_addons_entry.subtitle; 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_checkpkg = recommended_addons_entry.checkpkg;
let entry_packages = recommended_addons_entry.packages; let entry_packages = recommended_addons_entry.packages;
gio::spawn_blocking(clone!(@strong checkpkg_status_loop_sender, @strong entry_checkpkg => move || { gio::spawn_blocking(
let checkpkg_command = Command::new("dpkg") clone!(@strong checkpkg_status_loop_sender, @strong entry_checkpkg => move || {
.arg("-s") let checkpkg_command = Command::new("dpkg")
.arg(entry_checkpkg) .arg("-s")
.output() .arg(entry_checkpkg)
.expect("failed to execute process"); .output()
if checkpkg_command.status.success() { .expect("failed to execute process");
checkpkg_status_loop_sender.send_blocking(true).expect("The channel needs to be open."); if checkpkg_command.status.success() {
} else { checkpkg_status_loop_sender.send_blocking(true).expect("The channel needs to be open.");
checkpkg_status_loop_sender.send_blocking(false).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() let entry_row = adw::ActionRow::builder()
.title(t!(&entry_title)) .title(t!(&entry_title))
@ -170,6 +184,7 @@ pub fn recommended_addons_page(
.vexpand(true) .vexpand(true)
.valign(gtk::Align::Center) .valign(gtk::Align::Center)
.build(); .build();
entry_buttons_size_group.add_widget(&entry_row_button);
entry_row.add_prefix(&entry_row_icon); entry_row.add_prefix(&entry_row_icon);
entry_row.add_suffix(&entry_row_button); entry_row.add_suffix(&entry_row_button);
@ -205,21 +220,23 @@ pub fn recommended_addons_page(
let checkpkg_status_loop_context = MainContext::default(); let checkpkg_status_loop_context = MainContext::default();
// The main loop executes the asynchronous block // 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 { checkpkg_status_loop_context.spawn_local(
while let Ok(state) = checkpkg_status_loop_receiver.recv().await { clone!(@weak entry_row_button, @strong checkpkg_status_loop_receiver => async move {
if state == false { while let Ok(state) = checkpkg_status_loop_receiver.recv().await {
entry_row_button.remove_css_class("destructive-action"); if state == false {
entry_row_button.set_label(&t!("entry_row_button_install").to_string()); entry_row_button.remove_css_class("destructive-action");
entry_row_button.add_css_class("suggested-action"); entry_row_button.set_label(&t!("entry_row_button_install").to_string());
entry_row_button.set_widget_name("false") entry_row_button.add_css_class("suggested-action");
} else { entry_row_button.set_widget_name("false")
entry_row_button.remove_css_class("suggested-action"); } else {
entry_row_button.set_label(&t!("entry_row_button_remove").to_string()); entry_row_button.remove_css_class("suggested-action");
entry_row_button.add_css_class("destructive-action"); entry_row_button.set_label(&t!("entry_row_button_remove").to_string());
entry_row_button.set_widget_name("true") entry_row_button.add_css_class("destructive-action");
entry_row_button.set_widget_name("true")
}
} }
} }),
})); );
// //
let log_loop_context = MainContext::default(); 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_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()); recommended_addons_content_page_stack.add_titled(
&recommended_addons_page_scroll,
Some("recommended_addons_page"),
&t!("recommended_addons_page_title").to_string(),
);
} }

View File

@ -1,14 +1,14 @@
// GTK crates // 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::prelude::*;
use adw::*; use adw::*;
use duct::cmd;
use glib::*; 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)] #[allow(non_camel_case_types)]
#[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)]
@ -18,13 +18,13 @@ struct setup_steps_entry {
subtitle: String, subtitle: String,
icon: String, icon: String,
button: String, button: String,
command: String command: String,
} }
pub fn setup_steps_page( pub fn setup_steps_page(
setup_steps_content_page_stack: &gtk::Stack, setup_steps_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow, window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>> internet_connected: &Rc<RefCell<bool>>,
) { ) {
let internet_connected_status = internet_connected.clone(); let internet_connected_status = internet_connected.clone();
@ -38,10 +38,7 @@ pub fn setup_steps_page(
.expect("The channel needs to be open."); .expect("The channel needs to be open.");
}); });
let setup_steps_page_box = gtk::Box::builder() let setup_steps_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build();
.vexpand(true)
.hexpand(true)
.build();
let setup_steps_page_listbox = gtk::ListBox::builder() let setup_steps_page_listbox = gtk::ListBox::builder()
.margin_top(20) .margin_top(20)
@ -79,17 +76,23 @@ pub fn setup_steps_page(
let mut json_array: Vec<setup_steps_entry> = Vec::new(); let mut json_array: Vec<setup_steps_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; 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 = 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"] { if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] {
for setup_steps_entry in 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); 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 { 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, entry_command_status_loop_receiver) =
let entry_command_status_loop_sender: async_channel::Sender<bool> = entry_command_status_loop_sender.clone(); async_channel::unbounded();
let entry_command_status_loop_sender: async_channel::Sender<bool> =
entry_command_status_loop_sender.clone();
let entry_title = setup_steps_entry.title; let entry_title = setup_steps_entry.title;
let entry_subtitle = setup_steps_entry.subtitle; let entry_subtitle = setup_steps_entry.subtitle;
@ -113,6 +116,7 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.valign(gtk::Align::Center) .valign(gtk::Align::Center)
.build(); .build();
entry_buttons_size_group.add_widget(&entry_row_button);
entry_row.add_prefix(&entry_row_icon); entry_row.add_prefix(&entry_row_icon);
entry_row.add_suffix(&entry_row_button); entry_row.add_suffix(&entry_row_button);
@ -137,21 +141,30 @@ pub fn setup_steps_page(
.heading(t!("cmd_err_dialog_heading")) .heading(t!("cmd_err_dialog_heading"))
.transient_for(window) .transient_for(window)
.build(); .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(); let entry_command_status_loop_context = MainContext::default();
// The main loop executes the asynchronous block // 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 { entry_command_status_loop_context.spawn_local(
while let Ok(state) = entry_command_status_loop_receiver.recv().await { clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move {
if state == false { while let Ok(state) = entry_command_status_loop_receiver.recv().await {
cmd_err_dialog.present(); if state == false {
cmd_err_dialog.present();
}
} }
} }),
})); );
setup_steps_page_listbox.append(&entry_row) setup_steps_page_listbox.append(&entry_row)
} }
setup_steps_page_box.append(&setup_steps_page_listbox); 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()); setup_steps_content_page_stack.add_titled(
&setup_steps_page_scroll,
Some("setup_steps_page"),
&t!("setup_steps_page_title").to_string(),
);
} }

View File

@ -1,30 +1,30 @@
// GTK crates // 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::prelude::*;
use adw::*; use adw::*;
use duct::cmd;
use glib::*; 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)] #[allow(non_camel_case_types)]
#[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)] #[derive(PartialEq, Debug, Eq, Hash, Clone, Ord, PartialOrd, Deserialize)]
struct setup_steps_entry { struct troubleshoot_entry {
id: i32, id: i32,
title: String, title: String,
subtitle: String, subtitle: String,
icon: String, icon: String,
button: String, button: String,
command: String command: String,
} }
pub fn setup_steps_page( pub fn troubleshoot_page(
setup_steps_content_page_stack: &gtk::Stack, troubleshoot_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow, window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>> internet_connected: &Rc<RefCell<bool>>,
) { ) {
let internet_connected_status = internet_connected.clone(); let internet_connected_status = internet_connected.clone();
@ -38,12 +38,9 @@ pub fn setup_steps_page(
.expect("The channel needs to be open."); .expect("The channel needs to be open.");
}); });
let setup_steps_page_box = gtk::Box::builder() let troubleshoot_page_box = gtk::Box::builder().vexpand(true).hexpand(true).build();
.vexpand(true)
.hexpand(true)
.build();
let setup_steps_page_listbox = gtk::ListBox::builder() let troubleshoot_page_listbox = gtk::ListBox::builder()
.margin_top(20) .margin_top(20)
.margin_bottom(20) .margin_bottom(20)
.margin_start(20) .margin_start(20)
@ -51,13 +48,13 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.hexpand(true) .hexpand(true)
.build(); .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 // that puts items vertically
.hexpand(true) .hexpand(true)
.vexpand(true) .vexpand(true)
.child(&setup_steps_page_box) .child(&troubleshoot_page_box)
.propagate_natural_width(true) .propagate_natural_width(true)
.propagate_natural_height(true) .propagate_natural_height(true)
.build(); .build();
@ -65,37 +62,43 @@ pub fn setup_steps_page(
let internet_loop_context = MainContext::default(); let internet_loop_context = MainContext::default();
// The main loop executes the asynchronous block // The main loop executes the asynchronous block
internet_loop_context.spawn_local( 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 { while let Ok(_state) = internet_loop_receiver.recv().await {
if *internet_connected_status.borrow_mut() == true { if *internet_connected_status.borrow_mut() == true {
setup_steps_page_box.set_sensitive(true); troubleshoot_page_box.set_sensitive(true);
} else { } else {
setup_steps_page_box.set_sensitive(false); troubleshoot_page_box.set_sensitive(false);
} }
} }
}), }),
); );
let mut json_array: Vec<setup_steps_entry> = Vec::new(); let mut json_array: Vec<troubleshoot_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json"; 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 = 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 =
if let serde_json::Value::Array(setup_steps) = &json_data["setup_steps"] { serde_json::from_str(&json_data).expect("JSON format invalid");
for setup_steps_entry in setup_steps { if let serde_json::Value::Array(troubleshoot) = &json_data["troubleshoot"] {
let setup_steps_entry_struct: setup_steps_entry = serde_json::from_value(setup_steps_entry.clone()).unwrap(); for troubleshoot_entry in troubleshoot {
json_array.push(setup_steps_entry_struct); 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_buttons_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Both);
let (entry_command_status_loop_sender, entry_command_status_loop_receiver) = async_channel::unbounded();
let entry_command_status_loop_sender: async_channel::Sender<bool> = entry_command_status_loop_sender.clone();
let entry_title = setup_steps_entry.title; for troubleshoot_entry in json_array {
let entry_subtitle = setup_steps_entry.subtitle; let (entry_command_status_loop_sender, entry_command_status_loop_receiver) =
let entry_icon = setup_steps_entry.icon; async_channel::unbounded();
let entry_button = setup_steps_entry.button; let entry_command_status_loop_sender: async_channel::Sender<bool> =
let entry_command = setup_steps_entry.command; 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() let entry_row = adw::ActionRow::builder()
.title(t!(&entry_title)) .title(t!(&entry_title))
.subtitle(t!(&entry_subtitle)) .subtitle(t!(&entry_subtitle))
@ -113,6 +116,7 @@ pub fn setup_steps_page(
.vexpand(true) .vexpand(true)
.valign(gtk::Align::Center) .valign(gtk::Align::Center)
.build(); .build();
entry_buttons_size_group.add_widget(&entry_row_button);
entry_row.add_prefix(&entry_row_icon); entry_row.add_prefix(&entry_row_icon);
entry_row.add_suffix(&entry_row_button); entry_row.add_suffix(&entry_row_button);
@ -137,21 +141,30 @@ pub fn setup_steps_page(
.heading(t!("cmd_err_dialog_heading")) .heading(t!("cmd_err_dialog_heading"))
.transient_for(window) .transient_for(window)
.build(); .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(); let entry_command_status_loop_context = MainContext::default();
// The main loop executes the asynchronous block // 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 { entry_command_status_loop_context.spawn_local(
while let Ok(state) = entry_command_status_loop_receiver.recv().await { clone!(@weak cmd_err_dialog, @strong entry_command_status_loop_receiver => async move {
if state == false { while let Ok(state) = entry_command_status_loop_receiver.recv().await {
cmd_err_dialog.present(); 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()); troubleshoot_content_page_stack.add_titled(
&troubleshoot_page_scroll,
Some("troubleshoot_page"),
&t!("troubleshoot_page_title").to_string(),
);
} }

View File

@ -1,16 +1,16 @@
// GTK crates // GTK crates
use std::{thread, time}; use crate::config::DISTRO_ICON;
use std::rc::Rc;
use std::cell::RefCell;
use adw::prelude::*; use adw::prelude::*;
use adw::*; use adw::*;
use glib::*; use glib::*;
use crate::config::DISTRO_ICON; use std::cell::RefCell;
use std::rc::Rc;
use std::{thread, time};
pub fn welcome_page( pub fn welcome_page(
welcome_content_page_stack: &gtk::Stack, welcome_content_page_stack: &gtk::Stack,
window_banner: &adw::Banner, window_banner: &adw::Banner,
internet_connected: &Rc<RefCell<bool>> internet_connected: &Rc<RefCell<bool>>,
) { ) {
let internet_connected_status = internet_connected.clone(); 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()); welcome_content_page_stack.add_titled(
&welcome_page_scroll,
Some("welcome_page"),
&t!("welcome_page_title").to_string(),
);
} }