diff --git a/.idea/pikman-update-manager.iml b/.idea/bin-apt.iml similarity index 82% rename from .idea/pikman-update-manager.iml rename to .idea/bin-apt.iml index cf84ae4..af5ed37 100644 --- a/.idea/pikman-update-manager.iml +++ b/.idea/bin-apt.iml @@ -3,6 +3,7 @@ + diff --git a/.idea/bin-gui.iml b/.idea/bin-gui.iml new file mode 100644 index 0000000..6272d62 --- /dev/null +++ b/.idea/bin-gui.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/lib.iml b/.idea/lib.iml new file mode 100644 index 0000000..b91f2c4 --- /dev/null +++ b/.idea/lib.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index e2a5217..1ee0cb2 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,9 @@ - + + + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index e6f330c..f204661 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -8,17 +8,17 @@ + + + + - - - + + + - - - - - + - { - "keyToString": { - "ASKED_ADD_EXTERNAL_FILES": "true", - "Cargo.Build all.executor": "Run", - "Cargo.Run apt_update.executor": "Run", - "Cargo.Run pikman-update-manager.executor": "Run", - "Cargo.Test pikman-update-manager.executor": "Run", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.rust.reset.selective.auto.import": "true", - "git-widget-placeholder": "main", - "last_opened_file_path": "/home/ward/RustroverProjects/pkg-pikman-update-manager", - "node.js.detected.package.eslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "org.rust.cargo.project.model.PROJECT_DISCOVERY": "true", - "org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "", - "org.rust.disableDetachedFileInspection/home/ward/RustroverProjects/pkg-pikman-update-manager/src/apt_update_progress_socket/lib.rs": "true", - "org.rust.first.attach.projects": "true" + +}]]> + @@ -82,7 +89,27 @@ + + + + + + + + @@ -181,4 +246,15 @@ + + + + + file://$PROJECT_DIR$/src/bin/gui/flatpak_update_page/mod.rs + 492 + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 9c5fc76..304188d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,7 +160,7 @@ checksum = "797fd5a634dcb0ad0d7d583df794deb0a236d88e759cd34b7da20198c6c9d145" dependencies = [ "bitflags 2.6.0", "cairo-sys-rs", - "glib", + "glib 0.20.0", "libc", "thiserror", ] @@ -171,9 +171,9 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f" dependencies = [ - "glib-sys", + "glib-sys 0.20.0", "libc", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -503,8 +503,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28bb53ecb56857c683c9ec859908e076dd3969c7d67598bd8b1ce095d211304a" dependencies = [ "gdk-pixbuf-sys", - "gio", - "glib", + "gio 0.20.0", + "glib 0.20.0", "libc", ] @@ -514,11 +514,11 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f6681a0c1330d1d3968bec1529f7172d62819ef0bdbb0d18022320654158b03" dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "libc", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -530,8 +530,8 @@ dependencies = [ "cairo-rs", "gdk-pixbuf", "gdk4-sys", - "gio", - "glib", + "gio 0.20.0", + "glib 0.20.0", "libc", "pango", ] @@ -544,13 +544,13 @@ checksum = "a67576c8ec012156d7f680e201a807b4432a77babb3157e0555e990ab6bcd878" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "libc", "pango-sys", "pkg-config", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -568,6 +568,24 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "gio" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys 0.19.8", + "glib 0.19.9", + "libc", + "pin-project-lite", + "smallvec 1.13.2", + "thiserror", +] + [[package]] name = "gio" version = "0.20.0" @@ -578,27 +596,62 @@ dependencies = [ "futures-core", "futures-io", "futures-util", - "gio-sys", - "glib", + "gio-sys 0.20.0", + "glib 0.20.0", "libc", "pin-project-lite", "smallvec 1.13.2", "thiserror", ] +[[package]] +name = "gio-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef" +dependencies = [ + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "system-deps 6.2.2", + "windows-sys 0.52.0", +] + [[package]] name = "gio-sys" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4feb96b31c32730ea3e1e89aecd2e4e37ecb1c473ad8f685e3430a159419f63" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "libc", - "system-deps", + "system-deps 7.0.1", "windows-sys 0.52.0", ] +[[package]] +name = "glib" +version = "0.19.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44" +dependencies = [ + "bitflags 2.6.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys 0.19.8", + "glib-macros 0.19.9", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "memchr", + "smallvec 1.13.2", + "thiserror", +] + [[package]] name = "glib" version = "0.20.0" @@ -611,16 +664,29 @@ dependencies = [ "futures-executor", "futures-task", "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", + "gio-sys 0.20.0", + "glib-macros 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "libc", "memchr", "smallvec 1.13.2", "thiserror", ] +[[package]] +name = "glib-macros" +version = "0.19.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4429b0277a14ae9751350ad9b658b1be0abb5b54faa5bcdf6e74a3372582fad7" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "glib-macros" version = "0.20.0" @@ -634,6 +700,16 @@ dependencies = [ "syn", ] +[[package]] +name = "glib-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5" +dependencies = [ + "libc", + "system-deps 6.2.2", +] + [[package]] name = "glib-sys" version = "0.20.0" @@ -641,7 +717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4958c26e5a01c9af00dea669a97369eccbec29a8e6d125c24ea2d85ee7467b60" dependencies = [ "libc", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -674,15 +750,26 @@ dependencies = [ "walkdir", ] +[[package]] +name = "gobject-sys" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e" +dependencies = [ + "glib-sys 0.19.8", + "libc", + "system-deps 6.2.2", +] + [[package]] name = "gobject-sys" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6908864f5ffff15b56df7e90346863904f49b949337ed0456b9287af61903b8" dependencies = [ - "glib-sys", + "glib-sys 0.20.0", "libc", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -691,7 +778,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630e940ad5824f90221d6579043a9cd1f8bec86b4a17faaf7827d58eb16e8c1f" dependencies = [ - "glib", + "glib 0.20.0", "graphene-sys", "libc", ] @@ -702,10 +789,10 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8fade7b754982f47ebbed241fd2680816fdd4598321784da10b9e1168836a" dependencies = [ - "glib-sys", + "glib-sys 0.20.0", "libc", "pkg-config", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -716,7 +803,7 @@ checksum = "1f3cf2091e1af185b347b3450817d93dea6fe435df7abd4c2cd7fb5bcb4cfda8" dependencies = [ "cairo-rs", "gdk4", - "glib", + "glib 0.20.0", "graphene-rs", "gsk4-sys", "libc", @@ -731,12 +818,12 @@ checksum = "6aa69614a26d8760c186c3690f1b0fbb917572ca23ef83137445770ceddf8cde" dependencies = [ "cairo-sys-rs", "gdk4-sys", - "glib-sys", - "gobject-sys", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "graphene-sys", "libc", "pango-sys", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -750,8 +837,8 @@ dependencies = [ "futures-channel", "gdk-pixbuf", "gdk4", - "gio", - "glib", + "gio 0.20.0", + "glib 0.20.0", "graphene-rs", "gsk4", "gtk4-macros", @@ -781,14 +868,14 @@ dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gdk4-sys", - "gio-sys", - "glib-sys", - "gobject-sys", + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "graphene-sys", "gsk4-sys", "libc", "pango-sys", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -930,8 +1017,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878" dependencies = [ "gdk4", - "gio", - "glib", + "gio 0.20.0", + "glib 0.20.0", "gtk4", "libadwaita-sys", "libc", @@ -945,13 +1032,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c44d8bdbad31d6639e1f20cc9c1424f1a8e02d751fc28d44659bf743fb9eca6" dependencies = [ "gdk4-sys", - "gio-sys", - "glib-sys", - "gobject-sys", + "gio-sys 0.20.0", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "gtk4-sys", "libc", "pango-sys", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -960,6 +1047,32 @@ version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +[[package]] +name = "libflatpak" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0466e5bc49c8a9d03c1daf924833474640bf3b334b01efde4234e77efe9780" +dependencies = [ + "gio 0.19.8", + "glib 0.19.9", + "libc", + "libflatpak-sys", +] + +[[package]] +name = "libflatpak-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0022c1b20fa3959a52299cd531c6afc9433182f612b1210f18776a7857a58f2b" +dependencies = [ + "gio-sys 0.19.8", + "glib-sys 0.19.8", + "gobject-sys 0.19.8", + "libc", + "pkg-config", + "system-deps 6.2.2", +] + [[package]] name = "link-cplusplus" version = "1.0.9" @@ -1149,8 +1262,8 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54768854025df6903061d0084fd9702a253ddfd60db7d9b751d43b76689a7f0a" dependencies = [ - "gio", - "glib", + "gio 0.20.0", + "glib 0.20.0", "libc", "pango-sys", ] @@ -1161,10 +1274,10 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b07cc57d10cee4ec661f718a6902cee18c2f4cfae08e87e5a390525946913390" dependencies = [ - "glib-sys", - "gobject-sys", + "glib-sys 0.20.0", + "gobject-sys 0.20.0", "libc", - "system-deps", + "system-deps 7.0.1", ] [[package]] @@ -1239,6 +1352,7 @@ dependencies = [ "futures 0.3.30", "gtk4", "libadwaita", + "libflatpak", "lock_api 0.4.12", "pretty-bytes", "rust-apt", @@ -1607,6 +1721,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml 0.8.14", + "version-compare", +] + [[package]] name = "system-deps" version = "7.0.1" diff --git a/Cargo.toml b/Cargo.toml index 81384f0..8ef248e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,4 +36,5 @@ pretty-bytes = "0.2.2" crossbeam-utils = "0.8.20" chrono = "0.4.38" lock_api = "0.4.2" +libflatpak = { version = "0.5.0", package = "libflatpak", features = ["v1_11_1"] } diff --git a/locales/en_US.json b/locales/en_US.json index fa909a6..efa03b2 100644 --- a/locales/en_US.json +++ b/locales/en_US.json @@ -53,5 +53,14 @@ "banner_text_no_internet": "Warning: No Internet Connection!", "refresh_button_tooltip_text": "Refresh Opened Page", "apt_update_page_title": "Native Updates (APT)", - "packages_no_viewport_page_title": "All Native APT Packages are Up to date!" + "apt_packages_no_viewport_page_title": "All Native APT Packages are Up to date!", + "summary_button_label": "Summary", + "flatpak_extra_info_ref_name": "Ref Codename", + "flatpak_extra_info_download_size": "Download Size", + "flatpak_extra_info_installed_size": "Size on Disk", + "remote_label_label": "Remote", + "flatpak_update_dialog_heading": "Flatpak Appstream Sync", + "flatpak_update_dialog_retry_label": "Retry", + "flatpak_update_dialog_status_failed": "Flatpak Appstream Sync: Failed", + "flatpak_packages_no_viewport_page_title": "All Flatpak Packages are Up to date!" } \ No newline at end of file diff --git a/src/bin/gui/apt_package_row/imp.rs b/src/bin/gui/apt_package_row/imp.rs index eace26f..330341d 100644 --- a/src/bin/gui/apt_package_row/imp.rs +++ b/src/bin/gui/apt_package_row/imp.rs @@ -468,8 +468,8 @@ fn description_stack_page(package_description: &str) -> gtk::Box { .buffer(&description_text_buffer) .hexpand(true) .vexpand(true) - .margin_top(15) - .margin_bottom(15) + .margin_top(0) + .margin_bottom(10) .margin_start(15) .margin_end(15) .editable(false) diff --git a/src/bin/gui/apt_update_page/mod.rs b/src/bin/gui/apt_update_page/mod.rs index 89280ae..bbfc940 100644 --- a/src/bin/gui/apt_update_page/mod.rs +++ b/src/bin/gui/apt_update_page/mod.rs @@ -116,7 +116,7 @@ pub fn apt_update_page( let packages_no_viewport_page = adw::StatusPage::builder() .icon_name("emblem-default-symbolic") - .title(t!("packages_no_viewport_page_title")) + .title(t!("apt_packages_no_viewport_page_title")) .hexpand(true) .vexpand(true) .build(); diff --git a/src/bin/gui/build_ui/mod.rs b/src/bin/gui/build_ui/mod.rs index 13b14fa..d6b4a75 100644 --- a/src/bin/gui/build_ui/mod.rs +++ b/src/bin/gui/build_ui/mod.rs @@ -1,5 +1,6 @@ use crate::apt_update_page; use crate::config::{APP_GITHUB, APP_ICON, APP_ID, VERSION}; +use crate::flatpak_update_page; use adw::prelude::*; use adw::*; use gtk::glib::{clone, MainContext}; @@ -130,8 +131,30 @@ pub fn build_ui(app: &Application) { // Apt Update Page let apt_retry_signal_action = gio::SimpleAction::new("retry", None); + //let apt_update_view_stack_bin = Bin::builder() + // .child(&apt_update_page::apt_update_page( + // window.clone(), + // &apt_retry_signal_action, + // )) + // .build(); + + // apt_retry_signal_action.connect_activate(clone!( + // #[weak] + // window, + // #[strong] + // apt_retry_signal_action, + // #[strong] + // apt_update_view_stack_bin, + // move |_, _| { + // apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page( + // window, + // &apt_retry_signal_action, + // ))); + // } + // )); + let apt_update_view_stack_bin = Bin::builder() - .child(&apt_update_page::apt_update_page( + .child(&flatpak_update_page::flatpak_update_page( window.clone(), &apt_retry_signal_action, )) @@ -145,7 +168,7 @@ pub fn build_ui(app: &Application) { #[strong] apt_update_view_stack_bin, move |_, _| { - apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page( + apt_update_view_stack_bin.set_child(Some(&flatpak_update_page::flatpak_update_page( window, &apt_retry_signal_action, ))); diff --git a/src/bin/gui/flatpak_ref_row/imp.rs b/src/bin/gui/flatpak_ref_row/imp.rs new file mode 100644 index 0000000..3f11b0d --- /dev/null +++ b/src/bin/gui/flatpak_ref_row/imp.rs @@ -0,0 +1,486 @@ +use std::{cell::RefCell, sync::OnceLock}; + +use adw::*; +use adw::{prelude::*, subclass::prelude::*}; +use glib::{clone, subclass::Signal, Properties}; +use gtk::*; +use pretty_bytes::converter::convert; +use std::env; + +// ANCHOR: custom_button +// Object holding the state +#[derive(Properties, Default)] +#[properties(wrapper_type = super::FlatpakRefRow)] +pub struct FlatpakRefRow { + #[property(get, set)] + flatref_name: RefCell, + #[property(get, set)] + flatref_arch: RefCell, + #[property(get, set)] + flatref_ref_name: RefCell, + #[property(get, set)] + flatref_summary: RefCell, + #[property(get, set)] + flatref_remote_name: RefCell, + #[property(get, set)] + flatref_installed_size_installed: RefCell, + #[property(get, set)] + flatref_installed_size_remote: RefCell, + #[property(get, set)] + flatref_download_size: RefCell, + #[property(get, set)] + flatref_ref_format: RefCell, + #[property(get, set)] + flatref_is_system: RefCell, + #[property(get, set)] + flatref_marked: RefCell, +} +// ANCHOR_END: custom_button + +// The central trait for subclassing a GObject +#[glib::object_subclass] +impl ObjectSubclass for FlatpakRefRow { + const NAME: &'static str = "FlatpakRefRow"; + type Type = super::FlatpakRefRow; + type ParentType = ExpanderRow; +} + +// ANCHOR: object_impl +// Trait shared by all GObjects +#[glib::derived_properties] +impl ObjectImpl for FlatpakRefRow { + fn signals() -> &'static [Signal] { + static SIGNALS: OnceLock> = OnceLock::new(); + SIGNALS.get_or_init(|| { + vec![ + Signal::builder("checkbutton-toggled").build(), + Signal::builder("checkbutton-untoggled").build(), + ] + }) + } + fn constructed(&self) { + let current_locale = match env::var_os("LANG") { + Some(v) => v + .into_string() + .unwrap() + .chars() + .take_while(|&ch| ch != '.') + .collect::(), + None => panic!("$LANG is not set"), + }; + rust_i18n::set_locale(¤t_locale); + + self.parent_constructed(); + + // Bind label to number + // `SYNC_CREATE` ensures that the label will be immediately set + let obj = self.obj(); + + let prefix_box = Box::new(Orientation::Vertical, 0); + + let expandable_box = Box::new(Orientation::Vertical, 0); + + obj.connect_flatref_name_notify(clone!( + #[weak] + prefix_box, + #[weak] + expandable_box, + #[strong] + obj, + move |_| { + remove_all_children_from_box(&prefix_box); + remove_all_children_from_box(&expandable_box); + // + let flatref_name = obj.flatref_name(); + let flatref_arch = obj.flatref_arch(); + let flatref_ref_name = obj.flatref_ref_name(); + let flatref_summary = obj.flatref_summary(); + let flatref_remote_name = obj.flatref_remote_name(); + let flatref_installed_size_installed = obj.flatref_installed_size_installed(); + let flatref_installed_size_remote = obj.flatref_installed_size_remote(); + let flatref_download_size = obj.flatref_download_size(); + let flatref_ref_format = obj.flatref_download_size(); + let flatref_is_system = obj.flatref_is_system(); + let flatref_marked = obj.flatref_marked(); + // + create_prefix_content( + &prefix_box, + &flatref_name, + &flatref_arch, + flatref_is_system, + &flatref_remote_name, + ); + // + create_expandable_content( + &obj, + &expandable_box, + flatref_ref_name, + flatref_summary, + flatref_download_size, + flatref_installed_size_remote, + ); + } + )); + + obj.add_prefix(&prefix_box); + obj.add_row(&expandable_box); + + let suffix_toggle = CheckButton::builder() + .tooltip_text(t!("mark_for_update")) + .halign(Align::Center) + .valign(Align::Center) + .hexpand(false) + .vexpand(false) + .build(); + + suffix_toggle.connect_toggled(clone!( + #[weak] + obj, + #[weak] + suffix_toggle, + move |_| { + if suffix_toggle.is_active() { + obj.emit_by_name::<()>("checkbutton-toggled", &[]); + } else { + obj.emit_by_name::<()>("checkbutton-untoggled", &[]); + } + } + )); + + obj.add_suffix(&suffix_toggle); + + let obj = self.obj(); + obj.bind_property("flatref_marked", &suffix_toggle, "active") + .sync_create() + .bidirectional() + .build(); + + // turn on by default + obj.set_property("flatref_marked", true) + } +} +// Trait shared by all widgets +impl WidgetImpl for FlatpakRefRow {} + +// Trait shared by all buttons +// Trait shared by all buttons + +impl ListBoxRowImpl for FlatpakRefRow {} +impl PreferencesRowImpl for FlatpakRefRow {} +impl ExpanderRowImpl for FlatpakRefRow {} + +fn create_remote_badge(remote_name: &str) -> ListBox { + let remote_label = Label::builder() + .halign(Align::Start) + .hexpand(false) + .label(format!("{}: {}", t!("remote_label_label"), remote_name)) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&remote_label); + boxedlist +} +fn create_arch_badge(arch: &str) -> ListBox { + let arch_label = Label::builder() + .halign(Align::Start) + .hexpand(false) + .label(format!("{}: {}", t!("arch_label_label"), arch)) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&arch_label); + boxedlist +} + +fn create_system_badge(is_system: bool) -> ListBox { + let system_label = Label::builder() + .halign(Align::Start) + .hexpand(false) + .label(match is_system { + true => "System", + false => "User", + }) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .build(); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::End) + .margin_start(5) + .margin_end(5) + .margin_bottom(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&system_label); + boxedlist +} + +fn remove_all_children_from_box(parent: >k::Box) { + while let Some(child) = parent.last_child() { + parent.remove(&child); + } +} + +fn create_prefix_content( + prefix_box: >k::Box, + flatref_name: &str, + flatref_arch: &str, + flatref_is_system: bool, + flatref_remote_name: &str, +) { + let package_label = Label::builder() + .halign(Align::Start) + .margin_start(5) + .margin_end(5) + .margin_bottom(5) + .margin_top(5) + .label(flatref_name) + .build(); + package_label.add_css_class("size-20-bold-text"); + let prefix_badge_box = Box::new(Orientation::Horizontal, 0); + prefix_badge_box.append(&create_remote_badge(flatref_remote_name)); + prefix_badge_box.append(&create_arch_badge(flatref_arch)); + prefix_badge_box.append(&create_system_badge(flatref_is_system)); + prefix_box.append(&package_label); + prefix_box.append(&prefix_badge_box); +} + +fn create_expandable_content( + flatpak_package_row: &impl IsA, + expandable_box: >k::Box, + flatref_ref_name: String, + flatref_summary: String, + flatref_download_size: u64, + flatref_installed_size_remote: u64, +) { + let expandable_page_selection_box = Box::builder() + .orientation(Orientation::Horizontal) + .hexpand(false) + .vexpand(false) + .halign(Align::Start) + .valign(Align::Start) + .margin_bottom(10) + .margin_top(10) + .margin_start(10) + .margin_end(10) + .build(); + expandable_page_selection_box.add_css_class("linked"); + // + let summary_page_button = ToggleButton::builder() + .label(t!("summary_button_label")) + .active(true) + .build(); + let extra_info_page_button = ToggleButton::builder() + .label(t!("extra_info_page_button_label")) + .group(&summary_page_button) + .build(); + expandable_page_selection_box.append(&summary_page_button); + expandable_page_selection_box.append(&extra_info_page_button); + // + expandable_box.append(&expandable_page_selection_box); + // + let expandable_bin = Bin::builder().hexpand(true).vexpand(true).build(); + // + summary_page_button.connect_clicked(clone!( + #[strong] + expandable_bin, + #[strong] + summary_page_button, + move |_| { + if summary_page_button.is_active() { + expandable_bin.set_child(Some(&summary_stack_page(&flatref_summary))); + } + } + )); + + extra_info_page_button.connect_clicked(clone!( + #[strong] + expandable_bin, + #[strong] + extra_info_page_button, + move |_| { + if extra_info_page_button.is_active() { + expandable_bin.set_child(Some(&extra_info_stack_page( + &flatref_ref_name, + flatref_download_size, + flatref_installed_size_remote, + ))); + } + } + )); + + flatpak_package_row.connect_expanded_notify(clone!( + #[strong] + expandable_bin, + #[strong] + expandable_box, + #[strong] + flatpak_package_row, + #[strong] + summary_page_button, + move |_| { + if flatpak_package_row.property("expanded") { + summary_page_button.set_active(true); + summary_page_button.emit_by_name::<()>("clicked", &[]); + expandable_box.append(&expandable_bin) + } else { + expandable_box.remove(&expandable_bin) + } + } + )); +} + +fn summary_stack_page(flatref_summary: &str) -> gtk::Box { + let summary_content_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + let summary_text_buffer = TextBuffer::builder() + .text(flatref_summary.to_owned() + "\n") + .build(); + let summary_text_view = TextView::builder() + .buffer(&summary_text_buffer) + .hexpand(true) + .vexpand(true) + .margin_top(0) + .margin_bottom(10) + .margin_start(15) + .margin_end(15) + .editable(false) + .build(); + summary_content_box.append(&summary_text_view); + summary_content_box +} + +fn extra_info_stack_page( + flatref_ref_name: &str, + flatref_download_size: u64, + flatref_installed_size_remote: u64, +) -> gtk::Box { + let extra_info_badges_content_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + let extra_info_badges_size_group = SizeGroup::new(SizeGroupMode::Both); + let extra_info_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both); + let extra_info_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both); + let package_size = flatref_download_size as f64; + let package_installed_size = flatref_installed_size_remote as f64; + extra_info_badges_content_box.append(&create_color_badge( + &t!("flatpak_extra_info_ref_name").to_string(), + flatref_ref_name, + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box.append(&create_color_badge( + &t!("flatpak_extra_info_download_size").to_string(), + &convert(package_size), + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box.append(&create_color_badge( + &t!("flatpak_extra_info_installed_size").to_string(), + &convert(package_installed_size), + "background-accent-bg", + &extra_info_badges_size_group, + &extra_info_badges_size_group0, + &extra_info_badges_size_group1, + )); + extra_info_badges_content_box +} +fn create_color_badge( + label0_text: &str, + label1_text: &str, + css_style: &str, + group_size: &SizeGroup, + group_size0: &SizeGroup, + group_size1: &SizeGroup, +) -> ListBox { + let badge_box = Box::builder().build(); + + let label0 = Label::builder() + .label(label0_text) + .margin_start(5) + .margin_end(5) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size0.add_widget(&label0); + + let label_separator = Separator::builder().build(); + + let label1 = Label::builder() + .label(label1_text) + .margin_start(3) + .margin_end(0) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size1.add_widget(&label1); + + label1.add_css_class(css_style); + + badge_box.append(&label0); + badge_box.append(&label_separator); + badge_box.append(&label1); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Start) + .valign(Align::Start) + .margin_start(10) + .margin_end(10) + .margin_bottom(10) + .margin_top(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&badge_box); + group_size.add_widget(&boxedlist); + boxedlist +} diff --git a/src/bin/gui/flatpak_ref_row/mod.rs b/src/bin/gui/flatpak_ref_row/mod.rs new file mode 100644 index 0000000..7f03258 --- /dev/null +++ b/src/bin/gui/flatpak_ref_row/mod.rs @@ -0,0 +1,54 @@ +mod imp; + +use crate::flatpak_update_page::FlatpakRefStruct; +use glib::Object; +use gtk::glib; + +glib::wrapper! { + pub struct FlatpakRefRow(ObjectSubclass) + @extends adw::ExpanderRow, gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, + @implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget; +} + +impl FlatpakRefRow { + pub fn new(flatref: &FlatpakRefStruct) -> Self { + let flatref = flatref.clone(); + Object::builder() + .property("flatref-name", flatref.name) + .property("flatref-arch", flatref.arch) + .property("flatref-ref-name", flatref.ref_name) + .property("flatref-summary", flatref.summary) + .property("flatref-remote-name", flatref.remote_name) + .property( + "flatref-installed-size-installed", + flatref.installed_size_installed, + ) + .property( + "flatref-installed-size-remote", + flatref.installed_size_remote, + ) + .property("flatref-download-size", flatref.download_size) + .property("flatref-ref-format", flatref.ref_format) + .property("flatref-is-system", flatref.is_system) + .build() + } +} +// ANCHOR_END: mod + +impl Default for FlatpakRefRow { + fn default() -> Self { + Self::new(&FlatpakRefStruct { + ref_name: "??".to_owned(), + name: "??".to_owned(), + arch: "??".to_owned(), + summary: "??".to_owned(), + remote_name: "??".to_owned(), + installed_size_installed: 0, + installed_size_remote: 0, + download_size: 0, + ref_format: "??".to_owned(), + is_system: false, + is_last: false, + }) + } +} diff --git a/src/bin/gui/flatpak_update_page/mod.rs b/src/bin/gui/flatpak_update_page/mod.rs new file mode 100644 index 0000000..dd4e2b3 --- /dev/null +++ b/src/bin/gui/flatpak_update_page/mod.rs @@ -0,0 +1,743 @@ +mod process; + +use crate::apt_package_row::AptPackageRow; +use crate::flatpak_ref_row::FlatpakRefRow; +use adw::gio::SimpleAction; +use adw::prelude::*; +use gtk::glib::*; +use gtk::*; +use libflatpak::prelude::*; +use libflatpak::InstalledRef; +use std::cell::RefCell; +use std::process::Command; +use std::rc::Rc; +use std::thread; + +#[derive(Clone)] +pub struct FlatpakRefStruct { + pub ref_name: String, + pub name: String, + pub arch: String, + pub summary: String, + pub remote_name: String, + pub installed_size_installed: u64, + pub installed_size_remote: u64, + pub download_size: u64, + pub ref_format: String, + pub is_system: bool, + pub is_last: bool, +} +pub fn flatpak_update_page( + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) -> gtk::Box { + let (appstream_sync_percent_sender, appstream_sync_percent_receiver) = + async_channel::unbounded::(); + let appstream_sync_percent_sender = appstream_sync_percent_sender.clone(); + let (appstream_sync_status_sender, appstream_sync_status_receiver) = + async_channel::unbounded::(); + let appstream_sync_status_sender = appstream_sync_status_sender.clone(); + let appstream_sync_status_sender_clone0 = appstream_sync_status_sender.clone(); + + let system_refs_for_upgrade_vec: Rc>> = Rc::new(RefCell::new(Vec::new())); + + let user_refs_for_upgrade_vec: Rc>> = Rc::new(RefCell::new(Vec::new())); + + let cancellable_no = libflatpak::gio::Cancellable::NONE; + + thread::spawn(move || { + let cancellable_no = libflatpak::gio::Cancellable::NONE; + let flatpak_system_installation = + libflatpak::Installation::new_user(cancellable_no).unwrap(); + if let Ok(remotes) = + libflatpak::Installation::list_remotes(&flatpak_system_installation, cancellable_no) + { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + let mut remote_clousre = |status: &str, progress: u32, _: bool| { + appstream_sync_percent_sender + .send_blocking(progress) + .expect("appstream_sync_percent_receiver closed"); + appstream_sync_status_sender + .send_blocking(format!( + "System - {}: {}", + remote.name().unwrap_or("Unknown remote".into()), + status + )) + .expect("appstream_sync_status_receiver closed"); + }; + match libflatpak::Installation::update_appstream_full_sync( + &flatpak_system_installation, + &remote.name().unwrap(), + None, + Some(&mut remote_clousre), + cancellable_no, + ) { + Ok(_) => {} + Err(e) => { + appstream_sync_status_sender + .send_blocking(e.to_string()) + .expect("appstream_sync_status_receiver closed"); + appstream_sync_status_sender + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .expect("appstream_sync_status_receiver closed"); + break; + } + } + } + } + let flatpak_user_installation = libflatpak::Installation::new_user(cancellable_no).unwrap(); + if let Ok(remotes) = + libflatpak::Installation::list_remotes(&flatpak_user_installation, cancellable_no) + { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + let mut remote_clousre = |status: &str, progress: u32, _: bool| { + appstream_sync_percent_sender + .send_blocking(progress) + .expect("appstream_sync_percent_receiver closed"); + appstream_sync_status_sender + .send_blocking(format!( + "User - {}: {}", + remote.name().unwrap_or("Unknown remote".into()), + status + )) + .expect("appstream_sync_status_receiver closed"); + }; + match libflatpak::Installation::update_appstream_full_sync( + &flatpak_user_installation, + &remote.name().unwrap(), + None, + Some(&mut remote_clousre), + cancellable_no, + ) { + Ok(_) => { + appstream_sync_status_sender + .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned()) + .expect("appstream_sync_status_receiver closed"); + } + Err(e) => { + appstream_sync_status_sender + .send_blocking(e.to_string()) + .expect("appstream_sync_status_receiver closed"); + appstream_sync_status_sender + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .expect("appstream_sync_status_receiver closed"); + break; + } + } + } + } + }); + + let main_box = Box::builder() + .hexpand(true) + .vexpand(true) + .orientation(Orientation::Vertical) + .build(); + + let searchbar = SearchEntry::builder() + .search_delay(500) + .margin_top(15) + .margin_bottom(15) + .margin_end(30) + .margin_start(30) + .build(); + searchbar.add_css_class("rounded-all-25"); + + let packages_boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .margin_end(15) + .margin_start(15) + .sensitive(false) + .build(); + packages_boxedlist.add_css_class("boxed-list"); + + let packages_viewport = ScrolledWindow::builder() + .vexpand(true) + .hexpand(true) + .margin_bottom(15) + .margin_top(15) + .margin_end(15) + .margin_start(15) + .height_request(390) + .child(&packages_boxedlist) + .build(); + + let packages_no_viewport_page = adw::StatusPage::builder() + .icon_name("emblem-default-symbolic") + .title(t!("flatpak_packages_no_viewport_page_title")) + .hexpand(true) + .vexpand(true) + .build(); + + let viewport_bin = adw::Bin::builder() + .child(&packages_no_viewport_page) + .build(); + + let flatpak_update_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build(); + + let flatpak_update_dialog_progress_bar = + ProgressBar::builder().show_text(true).hexpand(true).build(); + + let flatpak_update_dialog_spinner = Spinner::builder() + .hexpand(true) + .valign(Align::Start) + .halign(Align::Center) + .spinning(true) + .height_request(128) + .width_request(128) + .build(); + + flatpak_update_dialog_child_box.append(&flatpak_update_dialog_spinner); + flatpak_update_dialog_child_box.append(&flatpak_update_dialog_progress_bar); + + let flatpak_update_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .extra_child(&flatpak_update_dialog_child_box) + .heading(t!("flatpak_update_dialog_heading")) + .width_request(500) + .build(); + + flatpak_update_dialog.add_response( + "flatpak_update_dialog_retry", + &t!("flatpak_update_dialog_retry_label").to_string(), + ); + + flatpak_update_dialog.set_response_appearance( + "flatpak_update_dialog_retry", + adw::ResponseAppearance::Suggested, + ); + + flatpak_update_dialog.set_response_enabled("flatpak_update_dialog_retry", false); + + let retry_signal_action0 = retry_signal_action.clone(); + + flatpak_update_dialog + .clone() + .choose(None::<&gio::Cancellable>, move |choice| { + if choice == "flatpak_update_dialog_retry" { + retry_signal_action0.activate(None); + } + }); + + let bottom_bar = Box::builder().valign(Align::End).build(); + + let select_button = Button::builder() + .halign(Align::End) + .valign(Align::Center) + .hexpand(true) + .margin_start(10) + .margin_end(10) + .margin_bottom(15) + .label(t!("select_button_deselect_all")) + .build(); + + select_button.connect_clicked(clone!( + #[weak] + select_button, + #[weak] + packages_boxedlist, + move |_| { + let select_button_label = select_button.label().unwrap(); + let value_to_mark = if select_button_label == t!("select_button_select_all").to_string() + { + true + } else if select_button_label == t!("select_button_deselect_all").to_string() { + false + } else { + panic!("Unexpected label on selection button") + }; + set_all_flatpak_row_marks_to(&packages_boxedlist, value_to_mark) + } + )); + + let update_button = Button::builder() + .halign(Align::End) + .valign(Align::Center) + .hexpand(false) + .margin_start(10) + .margin_end(30) + .margin_bottom(15) + .label(t!("update_button_label")) + .build(); + update_button.add_css_class("destructive-action"); + + update_button.connect_clicked(clone!( + #[weak] + window, + #[weak] + retry_signal_action, + #[strong] + system_refs_for_upgrade_vec, + #[strong] + user_refs_for_upgrade_vec, + move |_| { + dbg!(&system_refs_for_upgrade_vec); + dbg!(&user_refs_for_upgrade_vec); + } + )); + + bottom_bar.append(&select_button); + bottom_bar.append(&update_button); + + let appstream_sync_percent_server_context = MainContext::default(); + // The main loop executes the asynchronous block + appstream_sync_percent_server_context.spawn_local(clone!( + #[weak] + flatpak_update_dialog_progress_bar, + async move { + while let Ok(state) = appstream_sync_percent_receiver.recv().await { + flatpak_update_dialog_progress_bar.set_fraction(state as f64 / 100.0) + } + } + )); + + let appstream_sync_status_server_context = MainContext::default(); + // The main loop executes the asynchronous block + appstream_sync_status_server_context.spawn_local(clone!( + #[weak] + flatpak_update_dialog, + #[weak] + flatpak_update_dialog_child_box, + #[strong] + packages_boxedlist, + #[strong] + viewport_bin, + #[strong] + packages_viewport, + async move { + while let Ok(state) = appstream_sync_status_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => { + let flatpak_system_installation = + libflatpak::Installation::new_system(cancellable_no).unwrap(); + let flatpak_system_updates = flatpak_system_installation + .list_installed_refs_for_update(cancellable_no) + .unwrap(); + let flatpak_system_transaction = libflatpak::Transaction::for_installation( + &flatpak_system_installation, + cancellable_no, + ) + .unwrap(); + // + let flatpak_user_installation = + libflatpak::Installation::new_user(cancellable_no).unwrap(); + let flatpak_user_updates = flatpak_user_installation + .list_installed_refs_for_update(cancellable_no) + .unwrap(); + let flatpak_user_transaction = libflatpak::Transaction::for_installation( + &flatpak_user_installation, + cancellable_no, + ) + .unwrap(); + // + let mut system_last_triggered = false; + let mut user_last_triggered = false; + // + if !flatpak_system_updates.is_empty() || !flatpak_user_updates.is_empty() { + viewport_bin.set_child(Some(&packages_viewport)); + // + let mut flatpak_system_updates_iter = + &mut flatpak_system_updates.iter().peekable(); + // + while let Some(flatpak_ref) = flatpak_system_updates_iter.next() { + let mut remote_flatpak_ref: Option = None; + while let Ok(remotes) = libflatpak::Installation::list_remotes( + &flatpak_system_installation, + cancellable_no, + ) { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + match libflatpak::Installation::fetch_remote_ref_sync( + &flatpak_system_installation, + &match remote.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.kind(), + &match flatpak_ref.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.arch().as_deref(), + flatpak_ref.branch().as_deref(), + cancellable_no, + ) { + Ok(t) => { + remote_flatpak_ref = Some(t); + break; + } + Err(_) => continue, + } + } + if remote_flatpak_ref.is_some() { + break; + } + } + let flatref_struct = FlatpakRefStruct { + ref_name: flatpak_ref + .name() + .unwrap_or("Unknown".into()) + .to_string(), + name: flatpak_ref + .appdata_name() + .unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into())) + .to_string(), + arch: flatpak_ref + .arch() + .unwrap_or("Unknown Arch".into()) + .to_string(), + summary: flatpak_ref + .appdata_summary() + .unwrap_or("No Summary".into()) + .to_string(), + remote_name: match remote_flatpak_ref { + Some(ref t) => { + t.remote_name().unwrap_or("Unknown".into()).to_string() + } + None => "Unknown".into(), + }, + installed_size_installed: flatpak_ref.installed_size(), + installed_size_remote: match remote_flatpak_ref { + Some(ref t) => t.installed_size(), + None => 0, + }, + download_size: match remote_flatpak_ref { + Some(t) => t.download_size(), + None => 0, + }, + ref_format: flatpak_ref.format_ref().unwrap().into(), + is_system: true, + is_last: flatpak_system_updates_iter.peek().is_none(), + }; + + system_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_ref.format_ref().unwrap().into()); + + let flatpak_row = FlatpakRefRow::new(&flatref_struct); + + flatpak_row.connect_closure( + "checkbutton-toggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + system_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + if is_widget_select_all_ready(&packages_boxedlist) { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + } else { + select_button.set_label( + &t!("select_button_deselect_all").to_string(), + ); + } + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + system_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_row.flatref_ref_format()); + } + ), + ); + flatpak_row.connect_closure( + "checkbutton-untoggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + system_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + system_refs_for_upgrade_vec + .borrow_mut() + .retain(|x| x != &flatpak_row.flatref_ref_format()); + } + ), + ); + + packages_boxedlist.append(&flatpak_row); + if flatpak_system_updates.is_empty() + || flatref_struct.is_system && flatref_struct.is_last + { + system_last_triggered = true + } + } + // + let mut flatpak_user_updates_iter = + &mut flatpak_user_updates.iter().peekable(); + // + while let Some(flatpak_ref) = flatpak_user_updates_iter.next() { + let mut remote_flatpak_ref: Option = None; + while let Ok(remotes) = libflatpak::Installation::list_remotes( + &flatpak_system_installation, + cancellable_no, + ) { + for remote in remotes { + if remote.is_disabled() { + continue; + }; + match libflatpak::Installation::fetch_remote_ref_sync( + &flatpak_system_installation, + &match remote.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.kind(), + &match flatpak_ref.name() { + Some(t) => t, + None => continue, + }, + flatpak_ref.arch().as_deref(), + flatpak_ref.branch().as_deref(), + cancellable_no, + ) { + Ok(t) => { + remote_flatpak_ref = Some(t); + break; + } + Err(_) => continue, + } + } + if remote_flatpak_ref.is_some() { + break; + } + } + let flatref_struct = FlatpakRefStruct { + ref_name: flatpak_ref + .name() + .unwrap_or("Unknown".into()) + .to_string(), + name: flatpak_ref + .appdata_name() + .unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into())) + .to_string(), + arch: flatpak_ref + .arch() + .unwrap_or("Unknown Arch".into()) + .to_string(), + summary: flatpak_ref + .appdata_summary() + .unwrap_or("No Summary".into()) + .to_string(), + remote_name: match remote_flatpak_ref { + Some(ref t) => { + t.remote_name().unwrap_or("Unknown".into()).to_string() + } + None => "Unknown".into(), + }, + installed_size_installed: flatpak_ref.installed_size(), + installed_size_remote: match remote_flatpak_ref { + Some(ref t) => t.installed_size(), + None => 0, + }, + download_size: match remote_flatpak_ref { + Some(t) => t.download_size(), + None => 0, + }, + ref_format: flatpak_ref.format_ref().unwrap().into(), + is_system: false, + is_last: flatpak_user_updates_iter.peek().is_none(), + }; + + user_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_ref.format_ref().unwrap().into()); + + let flatpak_row = FlatpakRefRow::new(&flatref_struct); + + flatpak_row.connect_closure( + "checkbutton-toggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + user_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + if is_widget_select_all_ready(&packages_boxedlist) { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + } else { + select_button.set_label( + &t!("select_button_deselect_all").to_string(), + ); + } + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + user_refs_for_upgrade_vec + .borrow_mut() + .push(flatpak_row.flatref_ref_format()); + } + ), + ); + flatpak_row.connect_closure( + "checkbutton-untoggled", + false, + closure_local!( + #[strong] + select_button, + #[strong] + update_button, + #[strong] + packages_boxedlist, + #[strong] + user_refs_for_upgrade_vec, + move |flatpak_row: FlatpakRefRow| { + select_button.set_label( + &t!("select_button_select_all").to_string(), + ); + update_button.set_sensitive(!is_all_children_unmarked( + &packages_boxedlist, + )); + user_refs_for_upgrade_vec + .borrow_mut() + .retain(|x| x != &flatpak_row.flatref_ref_format()); + } + ), + ); + packages_boxedlist.append(&flatpak_row); + if flatpak_user_updates.is_empty() + || !flatref_struct.is_system && flatref_struct.is_last + { + user_last_triggered = true + } + } + if user_last_triggered && system_last_triggered { + packages_boxedlist.set_sensitive(true); + } + } + flatpak_update_dialog.close(); + } + "FN_OVERRIDE_FAILED" => { + flatpak_update_dialog_child_box.set_visible(false); + flatpak_update_dialog.set_extra_child(Some( + &Image::builder() + .pixel_size(128) + .icon_name("dialog-error-symbolic") + .halign(Align::Center) + .build(), + )); + flatpak_update_dialog.set_title(Some( + &t!("flatpak_update_dialog_status_failed").to_string(), + )); + flatpak_update_dialog + .set_response_enabled("flatpak_update_dialog_retry", true); + } + _ => flatpak_update_dialog.set_body(&state), + } + } + } + )); + + searchbar.connect_search_changed(clone!( + #[weak] + searchbar, + #[weak] + packages_boxedlist, + move |_| { + let mut counter = packages_boxedlist.first_child(); + while let Some(row) = counter { + if row.widget_name() == "FlatpakRefRow" { + if !searchbar.text().is_empty() { + if row + .property::("flatref-name") + .to_lowercase() + .contains(&searchbar.text().to_string().to_lowercase()) + || row + .property::("flatref-ref-name") + .to_lowercase() + .contains(&searchbar.text().to_string().to_lowercase()) + { + row.set_property("visible", true); + searchbar.grab_focus(); + } else { + row.set_property("visible", false); + } + } else { + row.set_property("visible", true); + } + } + counter = row.next_sibling(); + } + } + )); + + main_box.append(&searchbar); + main_box.append(&viewport_bin); + main_box.append(&bottom_bar); + + flatpak_update_dialog.present(); + main_box +} + +fn is_widget_select_all_ready(parent_listbox: &impl adw::prelude::IsA) -> bool { + let mut is_ready = false; + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + if !downcast.flatref_marked() { + is_ready = true; + break; + } + child_counter = next_child + } + is_ready +} + +fn is_all_children_unmarked(parent_listbox: &impl adw::prelude::IsA) -> bool { + let mut is_all_unmarked = true; + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + if downcast.flatref_marked() { + is_all_unmarked = false; + break; + } + child_counter = next_child + } + is_all_unmarked +} + +fn set_all_flatpak_row_marks_to(parent_listbox: &impl adw::prelude::IsA, value: bool) { + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + downcast.set_flatref_marked(value); + child_counter = next_child + } +} diff --git a/src/bin/gui/flatpak_update_page/process.rs b/src/bin/gui/flatpak_update_page/process.rs new file mode 100644 index 0000000..5ed3cb3 --- /dev/null +++ b/src/bin/gui/flatpak_update_page/process.rs @@ -0,0 +1,617 @@ +use adw::gio::SimpleAction; +use adw::prelude::*; +use gtk::glib::*; +use gtk::*; +use pika_unixsocket_tools::pika_unixsocket_tools::{ + start_socket_server, start_socket_server_no_log, +}; +use pretty_bytes::converter::convert; +use rust_apt::cache::Upgrade; +use rust_apt::new_cache; +use serde::Serialize; +use serde_json::Value; +use std::cell::RefCell; +use std::path::Path; +use std::process::Command; +use std::rc::Rc; +use std::thread; +use tokio::runtime::Runtime; + +struct AptChangesInfo { + package_count_upgrade: u64, + package_count_install: u64, + package_count_downgrade: u64, + package_count_remove: u64, + total_download_size: u64, + total_installed_size: i64, +} +#[derive(Serialize)] +struct Exclusions { + exclusions: Vec, +} + +impl AptChangesInfo { + fn add_upgrade(&mut self) { + self.package_count_upgrade += 1; + } + fn add_install(&mut self) { + self.package_count_install += 1; + } + fn add_downgrade(&mut self) { + self.package_count_downgrade += 1; + } + fn add_remove(&mut self) { + self.package_count_remove += 1; + } + + fn increase_total_download_size_by(&mut self, value: u64) { + self.total_download_size += value; + } + + fn increase_total_installed_size_by(&mut self, value: u64) { + self.total_installed_size += value as i64; + } + + fn decrease_total_installed_size_by(&mut self, value: u64) { + self.total_installed_size -= value as i64; + } +} + +pub fn apt_process_update( + excluded_updates_vec: &Vec, + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let excluded_updates_alert_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .heading(t!("excluded_updates_alert_dialog_heading")) + .body(t!("excluded_updates_alert_dialog_body")) + .build(); + + excluded_updates_alert_dialog.add_response( + "excluded_updates_alert_dialog_cancel", + &t!("excluded_updates_alert_dialog_cancel_label").to_string(), + ); + + excluded_updates_alert_dialog.add_response( + "excluded_updates_alert_continue", + &t!("excluded_updates_alert_continue_label").to_string(), + ); + + excluded_updates_alert_dialog.set_response_appearance( + "excluded_updates_alert_continue", + adw::ResponseAppearance::Destructive, + ); + + excluded_updates_alert_dialog.set_default_response(Some("excluded_updates_alert_continue")); + + let excluded_updates_alert_dialog_action = + SimpleAction::new("excluded_updates_alert_dialog_action", None); + + excluded_updates_alert_dialog_action.connect_activate(clone!( + #[weak] + window, + #[weak] + retry_signal_action, + #[strong] + excluded_updates_vec, + move |_, _| { apt_confirm_window(&excluded_updates_vec, window, &retry_signal_action) } + )); + + if excluded_updates_vec.is_empty() { + excluded_updates_alert_dialog_action.activate(None); + } else { + excluded_updates_alert_dialog.choose(None::<&gio::Cancellable>, move |choice| { + if choice == "excluded_updates_alert_continue" { + excluded_updates_alert_dialog_action.activate(None); + } + }); + } +} + +fn apt_confirm_window( + excluded_updates_vec: &Vec, + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let to_be_removed_packages_vec: Rc>> = Rc::new(RefCell::new(Vec::new())); + // Emulate Apt Full Upgrade to get transaction info + let mut apt_changes_struct = AptChangesInfo { + package_count_upgrade: 0, + package_count_install: 0, + package_count_downgrade: 0, + package_count_remove: 0, + total_download_size: 0, + total_installed_size: 0, + }; + + let apt_cache = new_cache!().unwrap(); + let apt_upgrade_cache = new_cache!().unwrap(); + + apt_cache.upgrade(Upgrade::FullUpgrade).unwrap(); + + for change in apt_cache.get_changes(false) { + if !excluded_updates_vec + .iter() + .any(|e| change.name().contains(e)) + { + let pkg = apt_upgrade_cache.get(change.name()).unwrap(); + if change.marked_upgrade() || change.marked_install() || change.marked_downgrade() { + pkg.mark_install(true, false); + } else if change.marked_delete() { + pkg.mark_delete(false); + to_be_removed_packages_vec + .borrow_mut() + .push(pkg.name().to_owned()); + } + pkg.protect(); + } + } + + apt_upgrade_cache.resolve(true).unwrap(); + + println!("{}", t!("gui_changes_emu_msg_0")); + for change in apt_upgrade_cache.get_changes(false) { + if change.is_installed() { + apt_changes_struct + .decrease_total_installed_size_by(change.installed().unwrap().installed_size()); + } + if change.marked_upgrade() && change.is_installed() { + println!("{}: {}", t!("gui_changes_emu_msg_upgrading"), change.name()); + apt_changes_struct.add_upgrade(); + apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size()); + apt_changes_struct + .increase_total_installed_size_by(change.candidate().unwrap().installed_size()); + } else if change.marked_install() || change.marked_upgrade() && !change.is_installed() { + println!( + "{}: {}", + t!("gui_changes_emu_msg_installing"), + change.name() + ); + apt_changes_struct.add_install(); + apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size()); + apt_changes_struct + .increase_total_installed_size_by(change.candidate().unwrap().installed_size()); + } else if change.marked_downgrade() { + println!( + "{}: {}", + t!("gui_changes_emu_msg_downgrading"), + change.name() + ); + apt_changes_struct.add_downgrade(); + apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size()); + apt_changes_struct + .increase_total_installed_size_by(change.candidate().unwrap().installed_size()); + } else if change.marked_delete() { + println!("{}: {}", t!("gui_changes_emu_msg_removing"), change.name()); + apt_changes_struct.add_remove(); + } + } + + let apt_confirm_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build(); + + let apt_update_dialog_badges_size_group = SizeGroup::new(SizeGroupMode::Both); + let apt_update_dialog_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both); + let apt_update_dialog_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_upgrade_badge_label"), + &apt_changes_struct.package_count_upgrade.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_install_badge_label"), + &apt_changes_struct.package_count_install.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_downgrade_badge_label"), + &apt_changes_struct.package_count_downgrade.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("package_count_remove_badge_label"), + &apt_changes_struct.package_count_remove.to_string(), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("total_download_size_badge_label"), + &convert(apt_changes_struct.total_download_size as f64), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + apt_confirm_dialog_child_box.append(&create_color_badge( + &t!("total_installed_size_badge_label"), + &convert(apt_changes_struct.total_installed_size as f64), + "background-accent-bg", + &apt_update_dialog_badges_size_group, + &apt_update_dialog_badges_size_group0, + &apt_update_dialog_badges_size_group1, + )); + + let apt_confirm_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .heading(t!("apt_confirm_dialog_heading")) + .body(t!("apt_confirm_dialog_body")) + .extra_child(&apt_confirm_dialog_child_box) + .build(); + + apt_confirm_dialog.add_response( + "apt_confirm_dialog_cancel", + &t!("apt_confirm_dialog_cancel_label").to_string(), + ); + + apt_confirm_dialog.add_response( + "apt_confirm_dialog_confirm", + &t!("apt_confirm_dialog_confirm_label").to_string(), + ); + + apt_confirm_dialog.set_response_appearance( + "apt_confirm_dialog_confirm", + adw::ResponseAppearance::Destructive, + ); + + apt_confirm_dialog.set_default_response(Some("apt_confirm_dialog_confirm")); + apt_confirm_dialog.set_close_response("apt_confirm_dialog_cancel"); + + let json_file_path = "/tmp/pika-apt-exclusions.json"; + + if Path::new(json_file_path).exists() { + std::fs::remove_file(json_file_path).expect("Failed to remove old json file"); + } + + if !excluded_updates_vec.is_empty() { + let exclusions_array = Exclusions { + exclusions: excluded_updates_vec + .into_iter() + .map(|i| serde_json::from_str(format!("{{\"package\":\"{}\"}}", i).as_str())) + .collect::, _>>() + .unwrap(), + }; + + std::fs::write( + json_file_path, + serde_json::to_string_pretty(&exclusions_array).unwrap(), + ) + .expect("Failed to write to json file"); + } + + let apt_confirm_start_signal_action = SimpleAction::new("apt_confirm_start", None); + + apt_confirm_start_signal_action.connect_activate(clone!( + #[weak] + window, + #[strong] + retry_signal_action, + #[strong] + apt_confirm_dialog, + move |_, _| { + let retry_signal_action0 = retry_signal_action.clone(); + apt_confirm_dialog + .clone() + .choose(None::<&gio::Cancellable>, move |choice| { + if choice == "apt_confirm_dialog_confirm" { + apt_full_upgrade_from_socket(window, &retry_signal_action0); + } + }); + } + )); + + let to_be_removed_packages_borrow = to_be_removed_packages_vec.borrow(); + if to_be_removed_packages_borrow.is_empty() { + apt_confirm_start_signal_action.activate(None); + } else { + let apt_remove_confirm_text_buffer = TextBuffer::builder() + .text( + to_be_removed_packages_borrow + .iter() + .map(|x| x.to_string() + "\n") + .collect::() + + "\n", + ) + .build(); + + let apt_remove_confirm_text_view = TextView::builder() + .buffer(&apt_remove_confirm_text_buffer) + .hexpand(true) + .vexpand(true) + .margin_top(15) + .margin_bottom(15) + .margin_start(15) + .margin_end(15) + .editable(false) + .build(); + + let apt_remove_confirm_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .heading(t!("apt_remove_confirm_dialog_heading")) + .body(t!("apt_remove_confirm_dialog_body")) + .extra_child(&apt_remove_confirm_text_view) + .build(); + + apt_remove_confirm_dialog.add_response( + "apt_remove_confirm_dialog_cancel", + &t!("apt_remove_confirm_dialog_cancel_label").to_string(), + ); + + apt_remove_confirm_dialog.add_response( + "apt_remove_confirm_dialog_confirm", + &t!("apt_remove_confirm_dialog_confirm_label").to_string(), + ); + + apt_remove_confirm_dialog.set_response_appearance( + "apt_remove_confirm_dialog_confirm", + adw::ResponseAppearance::Destructive, + ); + + apt_remove_confirm_dialog.set_default_response(Some("apt_remove_confirm_dialog_confirm")); + apt_remove_confirm_dialog.set_close_response("apt_remove_confirm_dialog_cancel"); + + apt_remove_confirm_dialog.choose(None::<&gio::Cancellable>, move |choice| { + if choice == "apt_remove_confirm_dialog_confirm" { + apt_confirm_start_signal_action.activate(None); + } + }); + } +} + +fn apt_full_upgrade_from_socket( + window: adw::ApplicationWindow, + retry_signal_action: &SimpleAction, +) { + let (upgrade_percent_sender, upgrade_percent_receiver) = async_channel::unbounded::(); + let upgrade_percent_sender = upgrade_percent_sender.clone(); + let (upgrade_status_sender, upgrade_status_receiver) = async_channel::unbounded::(); + let upgrade_status_sender = upgrade_status_sender.clone(); + let upgrade_status_sender_clone0 = upgrade_status_sender.clone(); + + let log_file_path = format!( + "/tmp/pika-apt-upgrade_{}.log", + chrono::offset::Local::now().format("%Y-%m-%d_%H:%M") + ); + let log_file_path_clone0 = log_file_path.clone(); + + thread::spawn(move || { + Runtime::new().unwrap().block_on(start_socket_server_no_log( + upgrade_percent_sender, + "/tmp/pika_apt_upgrade_percent.sock", + )); + }); + + thread::spawn(move || { + Runtime::new().unwrap().block_on(start_socket_server( + upgrade_status_sender, + "/tmp/pika_apt_upgrade_status.sock", + &log_file_path, + )); + }); + + thread::spawn(move || { + let apt_upgrade_command = Command::new("pkexec") + .args([ + "/home/ward/RustroverProjects/pkg-pikman-update-manager/target/debug/apt_full_upgrade", + ]) + .status() + .unwrap(); + match apt_upgrade_command.code().unwrap() { + 0 => upgrade_status_sender_clone0 + .send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned()) + .unwrap(), + 53 => {} + _ => { + upgrade_status_sender_clone0 + .send_blocking(t!("upgrade_status_error_perms").to_string()) + .unwrap(); + upgrade_status_sender_clone0 + .send_blocking("FN_OVERRIDE_FAILED".to_owned()) + .unwrap() + } + } + }); + + let apt_upgrade_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build(); + + let apt_upgrade_dialog_progress_bar = + ProgressBar::builder().show_text(true).hexpand(true).build(); + + let apt_upgrade_dialog_spinner = Spinner::builder() + .hexpand(true) + .valign(Align::Start) + .halign(Align::Center) + .spinning(true) + .height_request(128) + .width_request(128) + .build(); + + apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_spinner); + apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_progress_bar); + + let apt_upgrade_dialog = adw::MessageDialog::builder() + .transient_for(&window) + .extra_child(&apt_upgrade_dialog_child_box) + .heading(t!("apt_upgrade_dialog_heading")) + .width_request(500) + .build(); + + apt_upgrade_dialog.add_response( + "apt_upgrade_dialog_ok", + &t!("apt_upgrade_dialog_ok_label").to_string(), + ); + + let apt_upgrade_dialog_child_box_done = + Box::builder().orientation(Orientation::Vertical).build(); + + let apt_upgrade_log_image = Image::builder() + .pixel_size(128) + .halign(Align::Center) + .build(); + + let apt_upgrade_log_button = Button::builder() + .label(t!("apt_upgrade_dialog_open_log_file_label")) + .halign(Align::Center) + .margin_start(15) + .margin_end(15) + .margin_top(15) + .margin_bottom(15) + .build(); + + apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_image); + apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_button); + + apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", false); + apt_upgrade_dialog.set_close_response("apt_upgrade_dialog_ok"); + + let upgrade_percent_server_context = MainContext::default(); + // The main loop executes the asynchronous block + upgrade_percent_server_context.spawn_local(clone!( + #[weak] + apt_upgrade_dialog_progress_bar, + async move { + while let Ok(state) = upgrade_percent_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => {} + _ => match state.parse::() { + Ok(p) => apt_upgrade_dialog_progress_bar.set_fraction(p / 100.0), + Err(_) => {} + }, + } + } + } + )); + + let upgrade_status_server_context = MainContext::default(); + // The main loop executes the asynchronous block + upgrade_status_server_context.spawn_local(clone!( + #[weak] + apt_upgrade_dialog, + #[weak] + apt_upgrade_dialog_child_box, + #[strong] + apt_upgrade_dialog_child_box_done, + #[strong] + apt_upgrade_log_image, + async move { + while let Ok(state) = upgrade_status_receiver.recv().await { + match state.as_ref() { + "FN_OVERRIDE_SUCCESSFUL" => { + apt_upgrade_dialog_child_box.set_visible(false); + apt_upgrade_log_image.set_icon_name(Some("face-cool-symbolic")); + apt_upgrade_dialog + .set_extra_child(Some(&apt_upgrade_dialog_child_box_done)); + apt_upgrade_dialog.set_title(Some( + &t!("apt_upgrade_dialog_status_successful").to_string(), + )); + apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true); + } + "FN_OVERRIDE_FAILED" => { + apt_upgrade_dialog_child_box.set_visible(false); + apt_upgrade_log_image.set_icon_name(Some("dialog-error-symbolic")); + apt_upgrade_dialog + .set_extra_child(Some(&apt_upgrade_dialog_child_box_done)); + apt_upgrade_dialog + .set_title(Some(&t!("apt_upgrade_dialog_status_failed").to_string())); + apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true); + apt_upgrade_dialog + .set_response_enabled("apt_upgrade_dialog_open_log_file", true); + } + _ => apt_upgrade_dialog.set_body(&state), + } + } + } + )); + + let retry_signal_action0 = retry_signal_action.clone(); + + apt_upgrade_log_button.connect_clicked(move |_| { + let _ = Command::new("xdg-open") + .arg(log_file_path_clone0.to_owned()) + .spawn(); + }); + + apt_upgrade_dialog.choose(None::<&gio::Cancellable>, move |choice| { + match choice.as_str() { + "apt_upgrade_dialog_ok" => { + retry_signal_action0.activate(None); + } + _ => {} + } + }); +} + +fn create_color_badge( + label0_text: &str, + label1_text: &str, + css_style: &str, + group_size: &SizeGroup, + group_size0: &SizeGroup, + group_size1: &SizeGroup, +) -> ListBox { + let badge_box = Box::builder().build(); + + let label0 = Label::builder() + .label(label0_text) + .margin_start(5) + .margin_end(5) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size0.add_widget(&label0); + + let label_separator = Separator::builder().build(); + + let label1 = Label::builder() + .label(label1_text) + .margin_start(3) + .margin_end(0) + .margin_bottom(1) + .margin_top(1) + .valign(Align::Center) + .halign(Align::Center) + .hexpand(true) + .vexpand(true) + .build(); + group_size1.add_widget(&label1); + + label1.add_css_class(css_style); + + badge_box.append(&label0); + badge_box.append(&label_separator); + badge_box.append(&label1); + + let boxedlist = ListBox::builder() + .selection_mode(SelectionMode::None) + .halign(Align::Center) + .margin_start(10) + .margin_end(10) + .margin_bottom(10) + .margin_top(10) + .build(); + + boxedlist.add_css_class("boxed-list"); + boxedlist.append(&badge_box); + group_size.add_widget(&boxedlist); + boxedlist +} diff --git a/src/bin/gui/main.rs b/src/bin/gui/main.rs index 15e66e2..bbbc648 100644 --- a/src/bin/gui/main.rs +++ b/src/bin/gui/main.rs @@ -2,6 +2,8 @@ mod apt_package_row; mod apt_update_page; mod build_ui; mod config; +mod flatpak_ref_row; +mod flatpak_update_page; use crate::config::APP_ID; use adw::prelude::*;