From 3ddff9f7109f4c0173737f28ec248d735ea3d1ff Mon Sep 17 00:00:00 2001 From: Hellx2 Date: Sun, 11 Aug 2024 21:34:57 +1000 Subject: [PATCH] Add documentation to installed.rs --- src/packages/browse.rs | 95 +++++++++- src/packages/installed.rs | 354 +++++++++++++++++++++++++++++++------- 2 files changed, 383 insertions(+), 66 deletions(-) diff --git a/src/packages/browse.rs b/src/packages/browse.rs index 5d1ca1a..8f3b0fb 100644 --- a/src/packages/browse.rs +++ b/src/packages/browse.rs @@ -1,6 +1,6 @@ use gtk::{ - prelude::{BoxExt, ButtonExt, EditableExt}, - Button, PositionType, Window, + prelude::{BoxExt, ButtonExt, EditableExt, GtkWindowExt}, + Button, Label, PositionType, Window, }; use std::process::Command; @@ -156,7 +156,96 @@ fn show_installed_package(pkg: String) { return; } - let _pkg_window = Window::builder().build(); + let pkg_window = Window::builder().title(&pkg).build(); + let pkg_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .build(); + + let info = String::from_utf8( + Command::new("dnf") + .args(["info", pkg.as_str()]) + .output() + .unwrap() + .stdout, + ) + .unwrap(); + + let info = if info.contains("Installed Packages") { + let a = info.split_once("Installed Packages").unwrap().1.trim(); + a.split_once("Available Packages") + .unwrap_or((a, "")) + .0 + .trim() + } else { + info.split_once("Available Packages").unwrap().0.trim() + }; + + let mut version = "1.0.0"; + let mut size = "Unknown"; + let mut license = "Unknown"; + + for line in info.lines() { + if let Some(e) = line.trim().strip_prefix("Version") { + version = e.split_once(":").unwrap().1.trim(); + } else if let Some(e) = line.trim().strip_prefix("Size") { + size = e.split_once(":").unwrap().1.trim(); + } else if let Some(e) = line.trim().strip_prefix("License") { + license = e.split_once(":").unwrap().1.trim(); + } + if line.trim().starts_with("Description") { + break; + } + } + + let description = info + .split_once("Description") + .unwrap() + .1 + .split_once(":") + .unwrap() + .1 + .trim() + .lines() + .map(|x| x.split_once(":").unwrap_or(("", x)).1.trim().to_owned() + "\n") + .collect::(); + + pkg_box.append( + &Label::builder() + .label("Description: ".to_owned() + description.as_str()) + .build(), + ); + pkg_box.append( + &Label::builder() + .label("Version: ".to_owned() + version) + .build(), + ); + pkg_box.append(&Label::builder().label("Size: ".to_owned() + size).build()); + pkg_box.append( + &Label::builder() + .label("License: ".to_owned() + license) + .build(), + ); + + let buttons_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .css_classes(["buttons-box"]) + .build(); + + let install_btn = Button::builder().label("Install").build(); + + install_btn.connect_clicked(move |_| { + Command::new("pkexec") + .args(["dnf", "install", pkg.as_str()]) + .spawn() + .unwrap(); + }); + + buttons_box.append(&install_btn); + + pkg_box.append(&buttons_box); + + pkg_window.set_child(Some(&pkg_box)); + pkg_window.present(); } unsafe fn search(f: &SearchEntry, skip_list: &Vec<&str>) { diff --git a/src/packages/installed.rs b/src/packages/installed.rs index c219b2a..535dabe 100644 --- a/src/packages/installed.rs +++ b/src/packages/installed.rs @@ -1,6 +1,6 @@ use gtk::{ - prelude::{BoxExt, ButtonExt, Cast, EditableExt, ListBoxRowExt}, - Button, PositionType, Window, + prelude::{BoxExt, ButtonExt, EditableExt, GtkWindowExt}, + Button, Label, PositionType, Window, }; use std::process::Command; @@ -9,8 +9,12 @@ use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry}; pub static mut PACKAGES_LIST: Option = None; pub static mut PKG_LIST: Vec = vec![]; pub static mut PKG_LIST_INDEX: usize = 0; +pub static mut PKG_LISTBOX: Option = None; +/// Function to initialise a box that contains a +/// list of installed packages. pub fn installed() -> gtk::Box { + // The main box that encompasses everything else let packages_box = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) .css_classes(["packages-box"]) @@ -19,6 +23,7 @@ pub fn installed() -> gtk::Box { .hexpand(true) .build(); + // Search bar let pkgs_search = SearchBar::builder() .halign(Align::Fill) .valign(Align::Start) @@ -35,8 +40,16 @@ pub fn installed() -> gtk::Box { .hexpand(true) .build(); + /* + List of names to skip when searching, this is to remove the + labels "Available Packages", "Last refreshed at ..." and "Installed Packages" + */ let skip_list = vec!["available", "installed", "last"]; + /* + Initialize the packages list, needs to be a static + mutable variable to allow usage with GTK functions. + */ unsafe { PACKAGES_LIST = Some( ListBox::builder() @@ -48,16 +61,23 @@ pub fn installed() -> gtk::Box { ) }; + // TODO: Make this part asynchronous so that the app opens faster + let pkg_list = String::from_utf8( + Command::new("dnf") + .args(["list", "--installed"]) + .output() + .unwrap() + .stdout, + ) + .unwrap(); + unsafe { - // TODO: Make this part asynchronous so that the app opens faster - let pkg_list = String::from_utf8( - Command::new("dnf") - .args(["list", "--installed"]) - .output() - .unwrap() - .stdout, - ) - .unwrap(); + /* + Gets the list of packages, which also needs to be static + mutable for the same reason as PACKAGES_LIST, and remove + the description and architecture details, and also filter + out the lines that aren't actually packages. + */ PKG_LIST = pkg_list .lines() .map(|x| { @@ -76,11 +96,15 @@ pub fn installed() -> gtk::Box { && !x.contains("=") && !skip_list.contains(&x.to_lowercase().trim()) }) - .collect() - }; + .collect(); - unsafe { - for pkg in PKG_LIST[0..50].to_vec() { + /* + Add buttons for the first 50 packages in the list, to + keep the RAM usage to a minimum and not have every single + package and a button for it in the memory at the same time, + which has caused as much as 1.4GB of RAM usage during tests. + */ + for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() { PKG_LIST_INDEX += 1; let pkg_btn = Button::builder() .label(pkg) @@ -92,72 +116,276 @@ pub fn installed() -> gtk::Box { }); PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn) } - } - pkgs_entry.connect_search_changed(move |x| { - let x = x.clone(); + /* + Create a scrollable area for the package list so that it + doesn't just go off the screen and hide all of the list. + */ + PKG_LISTBOX = Some( + ScrolledWindow::builder() + .css_classes(["package-list-scrollable"]) + .valign(Align::Center) + .halign(Align::Center) + .width_request(800) + .height_request(600) + .build(), + ); - // TODO: Possibly refactor to use `dnf search` instead of a filter function. - unsafe { - PACKAGES_LIST.as_ref().unwrap().set_filter_func(move |y| { - y.child() - .unwrap() - .downcast::