Make packages scroll incrementally instead of using 1.4GB of ram

This commit is contained in:
Hellx2 2024-08-11 19:52:55 +10:00
parent 55d9f15afd
commit f0ae517e46
2 changed files with 216 additions and 85 deletions

View file

@ -1,12 +1,15 @@
use gtk::{
prelude::{BoxExt, ButtonExt, Cast, EditableExt, ListBoxRowExt},
Button, Window,
prelude::{BoxExt, ButtonExt, EditableExt},
Button, PositionType, Window,
};
use std::process::Command;
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
pub static mut PACKAGES_LIST: Option<ListBox> = None;
pub static mut PKG_LIST: Vec<String> = vec![];
pub static mut PKG_LIST_INDEX: usize = 0;
pub static mut PKG_LISTBOX: Option<ScrolledWindow> = None;
pub fn browse() -> gtk::Box {
let packages_box = gtk::Box::builder()
@ -33,6 +36,8 @@ pub fn browse() -> gtk::Box {
.hexpand(true)
.build();
let skip_list = vec!["available", "installed", "last"];
unsafe {
PACKAGES_LIST = Some(
ListBox::builder()
@ -54,20 +59,29 @@ pub fn browse() -> gtk::Box {
)
.unwrap();
let pkg_list = pkg_list
unsafe {
PKG_LIST = pkg_list
.lines()
.skip(1)
.map(|x| {
x.split_whitespace()
let y = x.to_string();
let b = y
.split_whitespace()
.next()
.unwrap_or("")
.split(".")
.next()
.unwrap()
.unwrap();
b.to_string()
})
.filter(|x| !x.trim().is_empty());
.filter(|x| {
!x.trim().is_empty()
&& !x.contains("=")
&& !skip_list.contains(&x.to_lowercase().trim())
})
.collect();
for pkg in pkg_list {
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() {
PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder()
.label(pkg)
.css_classes(["package-name"])
@ -76,46 +90,64 @@ pub fn browse() -> gtk::Box {
pkg_btn.connect_clicked(|x| {
show_installed_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
});
unsafe { PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn) };
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
}
}
pkgs_entry.connect_search_changed(move |x| {
let x = x.clone();
// 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::<Button>()
.unwrap()
.label()
.map(|x| x.to_string())
.unwrap_or("".to_string())
.contains(x.text().to_string().as_str())
})
};
});
let pkg_listbox = ScrolledWindow::builder()
PKG_LISTBOX = Some(
ScrolledWindow::builder()
.css_classes(["package-list-scrollable"])
.valign(Align::Center)
.halign(Align::Center)
.width_request(800)
.height_request(600)
.build(),
);
PKG_LISTBOX
.as_ref()
.unwrap()
.connect_edge_reached(|_, edge| {
if edge == PositionType::Bottom {
for i in PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(PKG_LIST.len())) {
let pkg_btn = Button::builder()
.label(PKG_LIST[i].as_str())
.css_classes(["package-name"])
.build();
pkg_btn.connect_clicked(|x| {
show_installed_package(
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
);
});
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn);
PKG_LIST_INDEX += 1;
}
}
})
};
pkgs_entry.connect_search_changed(move |x| {
let f = x.clone();
// TODO: Possibly refactor to use `dnf search` instead of a filter function.
unsafe { search(&f, &skip_list) }
});
pkgs_search.connect_entry(&pkgs_entry);
packages_box.append(&pkgs_search);
packages_box.append(&pkgs_entry);
unsafe {
packages_box.append(PKG_LISTBOX.as_ref().unwrap());
packages_box.append(&pkg_listbox);
pkg_listbox.set_child(Some(unsafe { PACKAGES_LIST.as_ref().unwrap() }));
dbg!(&pkg_listbox);
PKG_LISTBOX
.as_ref()
.unwrap()
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
};
packages_box
}
@ -124,5 +156,67 @@ fn show_installed_package(pkg: String) {
return;
}
let pkg_window = Window::builder().build();
let _pkg_window = Window::builder().build();
}
unsafe fn search(f: &SearchEntry, skip_list: &Vec<&str>) {
let pkg_list = String::from_utf8(
Command::new("dnf")
.args(["search", f.text().to_string().as_str()])
.output()
.unwrap()
.stdout,
)
.unwrap();
PKG_LIST = pkg_list
.lines()
.map(|x| {
let y = x.to_string();
let b = y
.split_whitespace()
.next()
.unwrap_or("")
.split(".")
.next()
.unwrap();
b.to_string()
})
.filter(|x| {
!x.trim().is_empty()
&& !x.contains("=")
&& !skip_list.contains(&x.to_lowercase().trim())
})
.collect();
std::mem::drop(PACKAGES_LIST.take());
PACKAGES_LIST = Some(
ListBox::builder()
.css_classes(["packages-list"])
.valign(Align::Center)
.halign(Align::Fill)
.hexpand(true)
.build(),
);
PKG_LIST_INDEX = 0;
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() {
PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder()
.label(pkg)
.css_classes(["package-name"])
.build();
pkg_btn.connect_clicked(|x| {
show_installed_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
});
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
}
PKG_LISTBOX
.as_ref()
.unwrap()
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
}

View file

@ -1,12 +1,14 @@
use gtk::{
prelude::{BoxExt, ButtonExt, Cast, EditableExt, ListBoxRowExt},
Button, Window,
Button, PositionType, Window,
};
use std::process::Command;
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
pub static mut PACKAGES_LIST: Option<ListBox> = None;
pub static mut PKG_LIST: Vec<String> = vec![];
pub static mut PKG_LIST_INDEX: usize = 0;
pub fn installed() -> gtk::Box {
let packages_box = gtk::Box::builder()
@ -33,6 +35,8 @@ pub fn installed() -> gtk::Box {
.hexpand(true)
.build();
let skip_list = vec!["available", "installed", "last"];
unsafe {
PACKAGES_LIST = Some(
ListBox::builder()
@ -44,6 +48,7 @@ pub fn installed() -> gtk::Box {
)
};
unsafe {
// TODO: Make this part asynchronous so that the app opens faster
let pkg_list = String::from_utf8(
Command::new("dnf")
@ -53,21 +58,30 @@ pub fn installed() -> gtk::Box {
.stdout,
)
.unwrap();
let pkg_list = pkg_list
PKG_LIST = pkg_list
.lines()
.skip(1)
.map(|x| {
x.split_whitespace()
let y = x.to_string();
let b = y
.split_whitespace()
.next()
.unwrap_or("")
.split(".")
.next()
.unwrap()
.unwrap();
b.to_string()
})
.filter(|x| !x.trim().is_empty());
.filter(|x| {
!x.trim().is_empty()
&& !x.contains("=")
&& !skip_list.contains(&x.to_lowercase().trim())
})
.collect()
};
for pkg in pkg_list {
unsafe {
for pkg in PKG_LIST[0..50].to_vec() {
PKG_LIST_INDEX += 1;
let pkg_btn = Button::builder()
.label(pkg)
.css_classes(["package-name"])
@ -76,11 +90,14 @@ pub fn installed() -> gtk::Box {
pkg_btn.connect_clicked(|x| {
show_installed_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
});
unsafe { PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn) };
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
}
}
pkgs_entry.connect_search_changed(move |x| {
let x = x.clone();
// 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()
@ -103,6 +120,28 @@ pub fn installed() -> gtk::Box {
.height_request(600)
.build();
pkg_listbox.connect_edge_reached(|_, edge| {
if edge == PositionType::Bottom {
unsafe {
for i in PKG_LIST_INDEX..(PKG_LIST_INDEX + 50) {
let pkg_btn = Button::builder()
.label(PKG_LIST[i].as_str())
.css_classes(["package-name"])
.build();
pkg_btn.connect_clicked(|x| {
show_installed_package(
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
);
});
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn);
PKG_LIST_INDEX += 1;
}
}
}
});
pkgs_search.connect_entry(&pkgs_entry);
packages_box.append(&pkgs_search);
@ -112,8 +151,6 @@ pub fn installed() -> gtk::Box {
pkg_listbox.set_child(Some(unsafe { PACKAGES_LIST.as_ref().unwrap() }));
dbg!(&pkg_listbox);
packages_box
}
@ -122,5 +159,5 @@ fn show_installed_package(pkg: String) {
return;
}
let pkg_window = Window::builder().build();
let _pkg_window = Window::builder().build();
}