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
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

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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: &gtk::Stack,
pub fn community_page(
community_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>>
internet_connected: &Rc<RefCell<bool>>,
) {
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<setup_steps_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json";
let mut json_array: Vec<community_entry> = 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<bool> = 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<bool> =
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 {
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());
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
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: &gtk::Stack,
pub fn contribute_page(
contribute_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>>
internet_connected: &Rc<RefCell<bool>>,
) {
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<setup_steps_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json";
let mut json_array: Vec<contribute_entry> = 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<bool> = 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<bool> =
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 {
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());
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
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: &gtk::Stack,
pub fn look_and_feel_page(
look_and_feel_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>>
internet_connected: &Rc<RefCell<bool>>,
) {
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<setup_steps_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json";
let mut json_array: Vec<look_and_feel_entry> = 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<bool> = 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<bool> =
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 {
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());
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;
// 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: &gtk::
}
});
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: &gtk::
let mut json_array: Vec<GString> = 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: &gtk::
.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: &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);
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
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,7 +43,18 @@ fn run_addon_command(
entry_packages: &str,
) -> Result<(), std::boxed::Box<dyn Error + Send + Sync>> {
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!(
"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()?;
@ -61,7 +71,7 @@ fn run_addon_command(
pub fn recommended_addons_page(
recommended_addons_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>>
internet_connected: &Rc<RefCell<bool>>,
) {
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<recommended_addons_entry> = 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<bool> = 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<bool> =
checkpkg_status_loop_sender.clone();
let (log_loop_sender, log_loop_receiver) = async_channel::unbounded();
let log_loop_sender: async_channel::Sender<String> = log_loop_sender.clone();
let (log_status_loop_sender, log_status_loop_receiver) = async_channel::unbounded();
let log_status_loop_sender: async_channel::Sender<bool> =
log_status_loop_sender.clone();
let log_status_loop_sender: async_channel::Sender<bool> = log_status_loop_sender.clone();
let entry_title = recommended_addons_entry.title;
let entry_subtitle = recommended_addons_entry.subtitle;
@ -141,7 +153,8 @@ 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 || {
gio::spawn_blocking(
clone!(@strong checkpkg_status_loop_sender, @strong entry_checkpkg => move || {
let checkpkg_command = Command::new("dpkg")
.arg("-s")
.arg(entry_checkpkg)
@ -152,7 +165,8 @@ pub fn recommended_addons_page(
} 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,7 +220,8 @@ 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 {
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");
@ -219,7 +235,8 @@ pub fn recommended_addons_page(
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());
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
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: &gtk::Stack,
window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>>
internet_connected: &Rc<RefCell<bool>>,
) {
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<setup_steps_entry> = 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<bool> = 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<bool> =
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 {
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());
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
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: &gtk::Stack,
pub fn troubleshoot_page(
troubleshoot_content_page_stack: &gtk::Stack,
window: &adw::ApplicationWindow,
internet_connected: &Rc<RefCell<bool>>
internet_connected: &Rc<RefCell<bool>>,
) {
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<setup_steps_entry> = Vec::new();
let json_path = "/home/ward/builds/pkg-pika-welcome/data/config/setup_steps.json";
let mut json_array: Vec<troubleshoot_entry> = 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<bool> = 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<bool> =
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 {
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());
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
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: &gtk::Stack,
window_banner: &adw::Banner,
internet_connected: &Rc<RefCell<bool>>
internet_connected: &Rc<RefCell<bool>>,
) {
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(),
);
}