Fix safety, remove static mutables.
This commit is contained in:
parent
941f66662e
commit
ca87b0d177
4 changed files with 425 additions and 353 deletions
|
@ -5,16 +5,10 @@ use gtk::{
|
||||||
Button, PositionType,
|
Button, PositionType,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::process::Command;
|
use std::{process::Command, rc::Rc, sync::Mutex};
|
||||||
|
|
||||||
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
|
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
|
||||||
|
|
||||||
pub static mut FULL_PKG_LIST: Vec<String> = vec![];
|
|
||||||
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 {
|
pub fn browse() -> gtk::Box {
|
||||||
// The main box that encompasses everything else
|
// The main box that encompasses everything else
|
||||||
let packages_box = gtk::Box::builder()
|
let packages_box = gtk::Box::builder()
|
||||||
|
@ -48,21 +42,26 @@ pub fn browse() -> gtk::Box {
|
||||||
*/
|
*/
|
||||||
let skip_list = ["available", "installed", "last"];
|
let skip_list = ["available", "installed", "last"];
|
||||||
|
|
||||||
|
let pkg_list_index_mut = Rc::new(Mutex::new(0));
|
||||||
|
let mut pkg_list_index = pkg_list_index_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialize the packages list, needs to be a static
|
Initialize the packages list, needs to be a static
|
||||||
mutable variable to allow usage with GTK functions.
|
mutable variable to allow usage with GTK functions.
|
||||||
*/
|
*/
|
||||||
unsafe {
|
let packages_list_mut = Rc::new(Mutex::new(
|
||||||
PACKAGES_LIST = Some(
|
ListBox::builder()
|
||||||
ListBox::builder()
|
.css_classes(["packages-list"])
|
||||||
.css_classes(["packages-list"])
|
.valign(Align::Center)
|
||||||
.valign(Align::Center)
|
.halign(Align::Fill)
|
||||||
.halign(Align::Fill)
|
.hexpand(true)
|
||||||
.hexpand(true)
|
.build(),
|
||||||
.build(),
|
));
|
||||||
);
|
|
||||||
|
|
||||||
FULL_PKG_LIST = String::from_utf8(
|
let packages_list = packages_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
|
let full_pkg_list: Rc<Vec<String>> = Rc::new(
|
||||||
|
String::from_utf8(
|
||||||
Command::new("dnf")
|
Command::new("dnf")
|
||||||
.args(["list", "--available"])
|
.args(["list", "--available"])
|
||||||
.output()
|
.output()
|
||||||
|
@ -88,103 +87,116 @@ pub fn browse() -> gtk::Box {
|
||||||
&& !skip_list.contains(&x.to_lowercase().trim())
|
&& !skip_list.contains(&x.to_lowercase().trim())
|
||||||
})
|
})
|
||||||
.dedup()
|
.dedup()
|
||||||
.collect();
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
PKG_LIST = FULL_PKG_LIST.clone();
|
let pkg_list_mut = Rc::new(Mutex::new((*full_pkg_list).clone()));
|
||||||
|
let pkg_list = pkg_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets the list of packages, which also needs to be static
|
Gets the list of packages, which also needs to be static
|
||||||
mutable for the same reason as PACKAGES_LIST, and remove
|
mutable for the same reason as PACKAGES_LIST, and remove
|
||||||
the description and architecture details, and also filter
|
the description and architecture details, and also filter
|
||||||
out the lines that aren't actually packages.
|
out the lines that aren't actually packages.
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
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 full_pkg_list[0..(50.min(full_pkg_list.len()))].iter() {
|
||||||
|
*pkg_list_index += 1;
|
||||||
|
let pkg_btn = Button::builder()
|
||||||
|
.label(pkg)
|
||||||
|
.css_classes(["package-name"])
|
||||||
|
.build();
|
||||||
|
|
||||||
/*
|
pkg_btn.connect_clicked(|x| {
|
||||||
Add buttons for the first 50 packages in the list, to
|
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
||||||
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,
|
packages_list.append(&pkg_btn)
|
||||||
which has caused as much as 1.4GB of RAM usage during tests.
|
}
|
||||||
*/
|
|
||||||
for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].iter() {
|
|
||||||
PKG_LIST_INDEX += 1;
|
|
||||||
let pkg_btn = Button::builder()
|
|
||||||
.label(pkg)
|
|
||||||
.css_classes(["package-name"])
|
|
||||||
.build();
|
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
/*
|
||||||
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
Create a scrollable area for the package list so that it
|
||||||
});
|
doesn't just go off the screen and hide all of the list.
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
|
*/
|
||||||
}
|
let pkg_listbox = Rc::new(
|
||||||
|
ScrolledWindow::builder()
|
||||||
/*
|
.css_classes(["package-list-scrollable"])
|
||||||
Create a scrollable area for the package list so that it
|
.valign(Align::Center)
|
||||||
doesn't just go off the screen and hide all of the list.
|
.halign(Align::Center)
|
||||||
*/
|
.width_request(800)
|
||||||
PKG_LISTBOX = Some(
|
.height_request(600)
|
||||||
ScrolledWindow::builder()
|
.build(),
|
||||||
.css_classes(["package-list-scrollable"])
|
);
|
||||||
.valign(Align::Center)
|
|
||||||
.halign(Align::Center)
|
|
||||||
.width_request(800)
|
|
||||||
.height_request(600)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
{
|
||||||
|
let pkg_list = pkg_list.clone();
|
||||||
|
let packages_list = packages_list.clone();
|
||||||
|
let pkg_list_index_mut = pkg_list_index_mut.clone();
|
||||||
/*
|
/*
|
||||||
Make it so that on reaching the end of the list it adds
|
Make it so that on reaching the end of the list it adds
|
||||||
buttons for 50 more packages.
|
buttons for 50 more packages.
|
||||||
*/
|
*/
|
||||||
PKG_LISTBOX
|
pkg_listbox.connect_edge_reached(move |_, edge| {
|
||||||
.as_ref()
|
let mut pkg_list_index = pkg_list_index_mut.lock().unwrap();
|
||||||
.unwrap()
|
if edge == PositionType::Bottom {
|
||||||
.connect_edge_reached(|_, edge| {
|
for i in
|
||||||
if edge == PositionType::Bottom {
|
pkg_list[*pkg_list_index..((*pkg_list_index + 50).min(pkg_list.len()))].iter()
|
||||||
for i in
|
{
|
||||||
PKG_LIST[PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(PKG_LIST.len()))].iter()
|
let pkg_btn = Button::builder()
|
||||||
{
|
.label(i.as_str())
|
||||||
let pkg_btn = Button::builder()
|
.css_classes(["package-name"])
|
||||||
.label(i.as_str())
|
.build();
|
||||||
.css_classes(["package-name"])
|
|
||||||
.build();
|
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
pkg_btn.connect_clicked(|x| {
|
||||||
super::show_package(
|
super::show_package(
|
||||||
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
|
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn);
|
packages_list.append(&pkg_btn);
|
||||||
|
|
||||||
PKG_LIST_INDEX += 1;
|
*pkg_list_index += 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
{
|
||||||
Run the `search` function when the search is changed.
|
let pkg_list_mut = pkg_list_mut.clone();
|
||||||
TODO: Make this asynchronous.
|
let full_pkg_list = full_pkg_list.clone();
|
||||||
*/
|
let packages_list_mut = packages_list_mut.clone();
|
||||||
pkgs_entry.connect_search_changed(move |x| {
|
let pkg_list_index_mut = pkg_list_index_mut.clone();
|
||||||
let f = x.clone();
|
let pkg_listbox = pkg_listbox.clone();
|
||||||
|
|
||||||
unsafe { search(&f) }
|
/*
|
||||||
});
|
Run the `search` function when the search is changed.
|
||||||
|
TODO: Make this asynchronous.
|
||||||
|
*/
|
||||||
|
pkgs_entry.connect_search_changed(move |x| {
|
||||||
|
let f = x.clone();
|
||||||
|
|
||||||
|
search(
|
||||||
|
&f,
|
||||||
|
pkg_list_mut.clone(),
|
||||||
|
full_pkg_list.clone(),
|
||||||
|
packages_list_mut.clone(),
|
||||||
|
pkg_list_index_mut.clone(),
|
||||||
|
pkg_listbox.clone(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pkgs_search.connect_entry(&pkgs_entry);
|
pkgs_search.connect_entry(&pkgs_entry);
|
||||||
|
|
||||||
// Add all of the parts to the box and return it.
|
// Add all of the parts to the box and return it.
|
||||||
packages_box.append(&pkgs_search);
|
packages_box.append(&pkgs_search);
|
||||||
packages_box.append(&pkgs_entry);
|
packages_box.append(&pkgs_entry);
|
||||||
unsafe {
|
packages_box.append(&(*pkg_listbox));
|
||||||
packages_box.append(PKG_LISTBOX.as_ref().unwrap());
|
|
||||||
|
|
||||||
PKG_LISTBOX
|
pkg_listbox.set_child(Some(&(*packages_list)));
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
|
|
||||||
};
|
|
||||||
packages_box
|
packages_box
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,14 +205,22 @@ Function that takes a search entry and updates
|
||||||
the package lists accordingly, skipping the values
|
the package lists accordingly, skipping the values
|
||||||
stated in `skip_list`
|
stated in `skip_list`
|
||||||
*/
|
*/
|
||||||
unsafe fn search(f: &SearchEntry) {
|
fn search(
|
||||||
|
f: &SearchEntry,
|
||||||
|
pkg_list_mut: Rc<Mutex<Vec<String>>>,
|
||||||
|
full_pkg_list: Rc<Vec<String>>,
|
||||||
|
packages_list_mut: Rc<Mutex<ListBox>>,
|
||||||
|
pkg_list_index_mut: Rc<Mutex<usize>>,
|
||||||
|
pkg_listbox: Rc<ScrolledWindow>,
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
Reinitialize PKG_LIST with an extra predicate
|
Reinitialize PKG_LIST with an extra predicate
|
||||||
in the filter closure to ensure that the list
|
in the filter closure to ensure that the list
|
||||||
of packages contains only ones that match the
|
of packages contains only ones that match the
|
||||||
query
|
query
|
||||||
*/
|
*/
|
||||||
PKG_LIST = FULL_PKG_LIST
|
let mut pkg_list = pkg_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
*pkg_list = full_pkg_list
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
x.to_lowercase()
|
x.to_lowercase()
|
||||||
|
@ -215,30 +235,31 @@ unsafe fn search(f: &SearchEntry) {
|
||||||
|
|
||||||
TODO: Check if this is necessary
|
TODO: Check if this is necessary
|
||||||
*/
|
*/
|
||||||
std::mem::drop(PACKAGES_LIST.take());
|
// std::mem::drop(PACKAGES_LIST.take());
|
||||||
|
|
||||||
|
let mut packages_list = packages_list_mut.lock().expect("Failed to lock Mutex.");
|
||||||
/*
|
/*
|
||||||
Reinitialize PACKAGES_LIST as a new ListBox.
|
Reinitialize PACKAGES_LIST as a new ListBox.
|
||||||
*/
|
*/
|
||||||
PACKAGES_LIST = Some(
|
*packages_list = ListBox::builder()
|
||||||
ListBox::builder()
|
.css_classes(["packages-list"])
|
||||||
.css_classes(["packages-list"])
|
.valign(Align::Center)
|
||||||
.valign(Align::Center)
|
.halign(Align::Fill)
|
||||||
.halign(Align::Fill)
|
.hexpand(true)
|
||||||
.hexpand(true)
|
.build();
|
||||||
.build(),
|
|
||||||
);
|
let mut pkg_list_index = pkg_list_index_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
// Reset the list index so that it starts at 0 in the package list
|
// Reset the list index so that it starts at 0 in the package list
|
||||||
PKG_LIST_INDEX = 0;
|
*pkg_list_index = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Loop through the package list from 0 until
|
Loop through the package list from 0 until
|
||||||
either 50 or the length of the list, whichever
|
either 50 or the length of the list, whichever
|
||||||
is less, and make buttons in the list for each
|
is less, and make buttons in the list for each
|
||||||
*/
|
*/
|
||||||
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].iter() {
|
for pkg in pkg_list[0..(50.min(pkg_list.len()))].iter() {
|
||||||
PKG_LIST_INDEX += 1;
|
*pkg_list_index += 1;
|
||||||
let pkg_btn = Button::builder()
|
let pkg_btn = Button::builder()
|
||||||
.label(pkg)
|
.label(pkg)
|
||||||
.css_classes(["package-name"])
|
.css_classes(["package-name"])
|
||||||
|
@ -247,15 +268,12 @@ unsafe fn search(f: &SearchEntry) {
|
||||||
pkg_btn.connect_clicked(|x| {
|
pkg_btn.connect_clicked(|x| {
|
||||||
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
||||||
});
|
});
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
|
packages_list.append(&pkg_btn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reinitialise PKG_LISTBOX since the value
|
Reinitialise PKG_LISTBOX since the value
|
||||||
it points to has now been changed.
|
it points to has now been changed.
|
||||||
*/
|
*/
|
||||||
PKG_LISTBOX
|
pkg_listbox.set_child(Some(&(*packages_list)));
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,14 @@
|
||||||
|
#![allow(clippy::assigning_clones)]
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
prelude::{BoxExt, ButtonExt, EditableExt},
|
prelude::{BoxExt, ButtonExt, EditableExt},
|
||||||
Button, PositionType,
|
Button, PositionType,
|
||||||
};
|
};
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use std::{process::Command, rc::Rc, sync::Mutex};
|
||||||
|
|
||||||
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
|
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
|
||||||
|
|
||||||
use super::show_package;
|
|
||||||
|
|
||||||
pub static mut FULL_PKG_LIST: Vec<String> = vec![];
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Function to initialise a box that contains a
|
|
||||||
list of installed packages.
|
|
||||||
*/
|
|
||||||
pub fn installed() -> gtk::Box {
|
pub fn installed() -> gtk::Box {
|
||||||
// The main box that encompasses everything else
|
// The main box that encompasses everything else
|
||||||
let packages_box = gtk::Box::builder()
|
let packages_box = gtk::Box::builder()
|
||||||
|
@ -53,27 +42,26 @@ pub fn installed() -> gtk::Box {
|
||||||
*/
|
*/
|
||||||
let skip_list = ["available", "installed", "last"];
|
let skip_list = ["available", "installed", "last"];
|
||||||
|
|
||||||
|
let pkg_list_index_mut = Rc::new(Mutex::new(0));
|
||||||
|
let mut pkg_list_index = pkg_list_index_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialize the packages list, needs to be a static
|
Initialize the packages list, needs to be a static
|
||||||
mutable variable to allow usage with GTK functions.
|
mutable variable to allow usage with GTK functions.
|
||||||
*/
|
*/
|
||||||
unsafe {
|
let packages_list_mut = Rc::new(Mutex::new(
|
||||||
PACKAGES_LIST = Some(
|
ListBox::builder()
|
||||||
ListBox::builder()
|
.css_classes(["packages-list"])
|
||||||
.css_classes(["packages-list"])
|
.valign(Align::Center)
|
||||||
.valign(Align::Center)
|
.halign(Align::Fill)
|
||||||
.halign(Align::Fill)
|
.hexpand(true)
|
||||||
.hexpand(true)
|
.build(),
|
||||||
.build(),
|
));
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
let packages_list = packages_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
Gets the list of packages, which also needs to be static
|
|
||||||
mutable for the same reason as PACKAGES_LIST, and remove
|
let full_pkg_list: Rc<Vec<String>> = Rc::new(
|
||||||
the description and architecture details, and also filter
|
String::from_utf8(
|
||||||
out the lines that aren't actually packages.
|
|
||||||
*/
|
|
||||||
FULL_PKG_LIST = String::from_utf8(
|
|
||||||
Command::new("dnf")
|
Command::new("dnf")
|
||||||
.args(["list", "--installed"])
|
.args(["list", "--installed"])
|
||||||
.output()
|
.output()
|
||||||
|
@ -99,94 +87,116 @@ pub fn installed() -> gtk::Box {
|
||||||
&& !skip_list.contains(&x.to_lowercase().trim())
|
&& !skip_list.contains(&x.to_lowercase().trim())
|
||||||
})
|
})
|
||||||
.dedup()
|
.dedup()
|
||||||
.collect::<Vec<String>>();
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
let pkg_list_mut = Rc::new(Mutex::new((*full_pkg_list).clone()));
|
||||||
Add buttons for the first 50 packages in the list, to
|
let pkg_list = pkg_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
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 FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].iter() {
|
|
||||||
PKG_LIST_INDEX += 1;
|
|
||||||
let pkg_btn = Button::builder()
|
|
||||||
.label(pkg)
|
|
||||||
.css_classes(["package-name"])
|
|
||||||
.build();
|
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
/*
|
||||||
show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
Gets the list of packages, which also needs to be static
|
||||||
});
|
mutable for the same reason as PACKAGES_LIST, and remove
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
|
the description and architecture details, and also filter
|
||||||
}
|
out the lines that aren't actually packages.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
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 full_pkg_list[0..(50.min(full_pkg_list.len()))].iter() {
|
||||||
|
*pkg_list_index += 1;
|
||||||
|
let pkg_btn = Button::builder()
|
||||||
|
.label(pkg)
|
||||||
|
.css_classes(["package-name"])
|
||||||
|
.build();
|
||||||
|
|
||||||
/*
|
pkg_btn.connect_clicked(|x| {
|
||||||
Create a scrollable area for the package list so that it
|
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
||||||
doesn't just go off the screen and hide all of the list.
|
});
|
||||||
*/
|
packages_list.append(&pkg_btn)
|
||||||
PKG_LISTBOX = Some(
|
}
|
||||||
ScrolledWindow::builder()
|
|
||||||
.css_classes(["package-list-scrollable"])
|
|
||||||
.valign(Align::Center)
|
|
||||||
.halign(Align::Center)
|
|
||||||
.width_request(800)
|
|
||||||
.height_request(600)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a scrollable area for the package list so that it
|
||||||
|
doesn't just go off the screen and hide all of the list.
|
||||||
|
*/
|
||||||
|
let pkg_listbox = Rc::new(
|
||||||
|
ScrolledWindow::builder()
|
||||||
|
.css_classes(["package-list-scrollable"])
|
||||||
|
.valign(Align::Center)
|
||||||
|
.halign(Align::Center)
|
||||||
|
.width_request(800)
|
||||||
|
.height_request(600)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
let pkg_list = pkg_list.clone();
|
||||||
|
let packages_list = packages_list.clone();
|
||||||
|
let pkg_list_index_mut = pkg_list_index_mut.clone();
|
||||||
/*
|
/*
|
||||||
Make it so that on reaching the end of the list it adds
|
Make it so that on reaching the end of the list it adds
|
||||||
buttons for 50 more packages.
|
buttons for 50 more packages.
|
||||||
*/
|
*/
|
||||||
PKG_LISTBOX
|
pkg_listbox.connect_edge_reached(move |_, edge| {
|
||||||
.as_ref()
|
let mut pkg_list_index = pkg_list_index_mut.lock().unwrap();
|
||||||
.unwrap()
|
if edge == PositionType::Bottom {
|
||||||
.connect_edge_reached(|_, edge| {
|
for i in
|
||||||
if edge == PositionType::Bottom {
|
pkg_list[*pkg_list_index..((*pkg_list_index + 50).min(pkg_list.len()))].iter()
|
||||||
(PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(FULL_PKG_LIST.len()))).for_each(
|
{
|
||||||
|i| {
|
let pkg_btn = Button::builder()
|
||||||
let pkg_btn = Button::builder()
|
.label(i.as_str())
|
||||||
.label(FULL_PKG_LIST[i].as_str())
|
.css_classes(["package-name"])
|
||||||
.css_classes(["package-name"])
|
.build();
|
||||||
.build();
|
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
pkg_btn.connect_clicked(|x| {
|
||||||
show_package(
|
super::show_package(
|
||||||
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
|
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn);
|
packages_list.append(&pkg_btn);
|
||||||
|
|
||||||
PKG_LIST_INDEX += 1;
|
*pkg_list_index += 1;
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
{
|
||||||
Run the `search` function when the search is changed.
|
let pkg_list_mut = pkg_list_mut.clone();
|
||||||
TODO: Make this asynchronous.
|
let full_pkg_list = full_pkg_list.clone();
|
||||||
*/
|
let packages_list_mut = packages_list_mut.clone();
|
||||||
pkgs_entry.connect_search_changed(move |x| {
|
let pkg_list_index_mut = pkg_list_index_mut.clone();
|
||||||
let f = x.clone();
|
let pkg_listbox = pkg_listbox.clone();
|
||||||
|
|
||||||
unsafe { search(&f) }
|
/*
|
||||||
});
|
Run the `search` function when the search is changed.
|
||||||
|
TODO: Make this asynchronous.
|
||||||
|
*/
|
||||||
|
pkgs_entry.connect_search_changed(move |x| {
|
||||||
|
let f = x.clone();
|
||||||
|
|
||||||
|
search(
|
||||||
|
&f,
|
||||||
|
pkg_list_mut.clone(),
|
||||||
|
full_pkg_list.clone(),
|
||||||
|
packages_list_mut.clone(),
|
||||||
|
pkg_list_index_mut.clone(),
|
||||||
|
pkg_listbox.clone(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pkgs_search.connect_entry(&pkgs_entry);
|
pkgs_search.connect_entry(&pkgs_entry);
|
||||||
|
|
||||||
// Add all of the parts to the box and return it.
|
// Add all of the parts to the box and return it.
|
||||||
packages_box.append(&pkgs_search);
|
packages_box.append(&pkgs_search);
|
||||||
packages_box.append(&pkgs_entry);
|
packages_box.append(&pkgs_entry);
|
||||||
unsafe {
|
packages_box.append(&(*pkg_listbox));
|
||||||
packages_box.append(PKG_LISTBOX.as_ref().unwrap());
|
|
||||||
|
|
||||||
PKG_LISTBOX
|
pkg_listbox.set_child(Some(&(*packages_list)));
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
|
|
||||||
};
|
|
||||||
packages_box
|
packages_box
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,14 +205,22 @@ Function that takes a search entry and updates
|
||||||
the package lists accordingly, skipping the values
|
the package lists accordingly, skipping the values
|
||||||
stated in `skip_list`
|
stated in `skip_list`
|
||||||
*/
|
*/
|
||||||
unsafe fn search(f: &SearchEntry) {
|
fn search(
|
||||||
|
f: &SearchEntry,
|
||||||
|
pkg_list_mut: Rc<Mutex<Vec<String>>>,
|
||||||
|
full_pkg_list: Rc<Vec<String>>,
|
||||||
|
packages_list_mut: Rc<Mutex<ListBox>>,
|
||||||
|
pkg_list_index_mut: Rc<Mutex<usize>>,
|
||||||
|
pkg_listbox: Rc<ScrolledWindow>,
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
Reinitialize PKG_LIST with an extra predicate
|
Reinitialize PKG_LIST with an extra predicate
|
||||||
in the filter closure to ensure that the list
|
in the filter closure to ensure that the list
|
||||||
of packages contains only ones that match the
|
of packages contains only ones that match the
|
||||||
query
|
query
|
||||||
*/
|
*/
|
||||||
PKG_LIST = FULL_PKG_LIST
|
let mut pkg_list = pkg_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
*pkg_list = full_pkg_list
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
x.to_lowercase()
|
x.to_lowercase()
|
||||||
|
@ -217,47 +235,45 @@ unsafe fn search(f: &SearchEntry) {
|
||||||
|
|
||||||
TODO: Check if this is necessary
|
TODO: Check if this is necessary
|
||||||
*/
|
*/
|
||||||
std::mem::drop(PACKAGES_LIST.take());
|
// std::mem::drop(PACKAGES_LIST.take());
|
||||||
|
|
||||||
|
let mut packages_list = packages_list_mut.lock().expect("Failed to lock Mutex.");
|
||||||
/*
|
/*
|
||||||
Reinitialize PACKAGES_LIST as a new ListBox.
|
Reinitialize PACKAGES_LIST as a new ListBox.
|
||||||
*/
|
*/
|
||||||
PACKAGES_LIST = Some(
|
*packages_list = ListBox::builder()
|
||||||
ListBox::builder()
|
.css_classes(["packages-list"])
|
||||||
.css_classes(["packages-list"])
|
.valign(Align::Center)
|
||||||
.valign(Align::Center)
|
.halign(Align::Fill)
|
||||||
.halign(Align::Fill)
|
.hexpand(true)
|
||||||
.hexpand(true)
|
.build();
|
||||||
.build(),
|
|
||||||
);
|
let mut pkg_list_index = pkg_list_index_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
// Reset the list index so that it starts at 0 in the package list
|
// Reset the list index so that it starts at 0 in the package list
|
||||||
PKG_LIST_INDEX = 0;
|
*pkg_list_index = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Loop through the package list from 0 until
|
Loop through the package list from 0 until
|
||||||
either 50 or the length of the list, whichever
|
either 50 or the length of the list, whichever
|
||||||
is less, and make buttons in the list for each
|
is less, and make buttons in the list for each
|
||||||
*/
|
*/
|
||||||
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].iter() {
|
for pkg in pkg_list[0..(50.min(pkg_list.len()))].iter() {
|
||||||
PKG_LIST_INDEX += 1;
|
*pkg_list_index += 1;
|
||||||
let pkg_btn = Button::builder()
|
let pkg_btn = Button::builder()
|
||||||
.label(pkg)
|
.label(pkg)
|
||||||
.css_classes(["package-name"])
|
.css_classes(["package-name"])
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
pkg_btn.connect_clicked(|x| {
|
||||||
show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
||||||
});
|
});
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
|
packages_list.append(&pkg_btn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reinitialise PKG_LISTBOX since the value
|
Reinitialise PKG_LISTBOX since the value
|
||||||
it points to has now been changed.
|
it points to has now been changed.
|
||||||
*/
|
*/
|
||||||
PKG_LISTBOX
|
pkg_listbox.set_child(Some(&(*packages_list)));
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,9 @@ fn show_package(pkg: String) {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let mut is_updatable = true;
|
||||||
let mut is_installed = false;
|
let mut is_installed = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get the correct info for whether the package
|
Get the correct info for whether the package
|
||||||
is installed or not and use the data from
|
is installed or not and use the data from
|
||||||
|
@ -96,6 +98,7 @@ fn show_package(pkg: String) {
|
||||||
.0
|
.0
|
||||||
.trim()
|
.trim()
|
||||||
} else {
|
} else {
|
||||||
|
is_updatable = false;
|
||||||
info.split_once("Available Packages").unwrap().1.trim()
|
info.split_once("Available Packages").unwrap().1.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,6 +106,7 @@ fn show_package(pkg: String) {
|
||||||
let mut version = "1.0.0";
|
let mut version = "1.0.0";
|
||||||
let mut size = "Unknown";
|
let mut size = "Unknown";
|
||||||
let mut license = "Unknown";
|
let mut license = "Unknown";
|
||||||
|
let mut architecture = "Unknown";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Sort through each of the lines and add the
|
Sort through each of the lines and add the
|
||||||
|
@ -112,17 +116,35 @@ fn show_package(pkg: String) {
|
||||||
*/
|
*/
|
||||||
for line in info.lines() {
|
for line in info.lines() {
|
||||||
if let Some(e) = line.trim().strip_prefix("Version") {
|
if let Some(e) = line.trim().strip_prefix("Version") {
|
||||||
version = e.split_once(':').unwrap().1.trim();
|
version = e.split_once(':').unwrap().1.trim()
|
||||||
} else if let Some(e) = line.trim().strip_prefix("Size") {
|
} else if let Some(e) = line.trim().strip_prefix("Size") {
|
||||||
size = e.split_once(':').unwrap().1.trim();
|
size = e.split_once(':').unwrap().1.trim()
|
||||||
} else if let Some(e) = line.trim().strip_prefix("License") {
|
} else if let Some(e) = line.trim().strip_prefix("License") {
|
||||||
license = e.split_once(':').unwrap().1.trim();
|
license = e.split_once(':').unwrap().1.trim()
|
||||||
|
} else if let Some(e) = line.trim().strip_prefix("Architecture") {
|
||||||
|
architecture = e.split_once(':').unwrap().1.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
if line.trim().starts_with("Description") {
|
if line.trim().starts_with("Description") {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_updatable = is_updatable
|
||||||
|
&& architecture
|
||||||
|
== info
|
||||||
|
.split_once("Available Packages")
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.trim()
|
||||||
|
.lines()
|
||||||
|
.find(|x| x.starts_with("Architecture"))
|
||||||
|
.expect("Failed to get available architecture")
|
||||||
|
.split_once(':')
|
||||||
|
.unwrap()
|
||||||
|
.1
|
||||||
|
.trim();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get the description of the package, removing
|
Get the description of the package, removing
|
||||||
the colons and whitespace at the start of each
|
the colons and whitespace at the start of each
|
||||||
|
@ -244,7 +266,7 @@ fn show_package(pkg: String) {
|
||||||
buttons_box.append(&install_btn);
|
buttons_box.append(&install_btn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if unsafe { updates::FULL_PKG_LIST.contains(&pkg) } {
|
if is_updatable {
|
||||||
let update_btn = Button::builder().label("Update").build();
|
let update_btn = Button::builder().label("Update").build();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,25 +1,14 @@
|
||||||
|
#![allow(clippy::assigning_clones)]
|
||||||
|
|
||||||
use gtk::{
|
use gtk::{
|
||||||
prelude::{BoxExt, ButtonExt, EditableExt},
|
prelude::{BoxExt, ButtonExt, EditableExt},
|
||||||
Button, PositionType,
|
Button, PositionType,
|
||||||
};
|
};
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use std::{process::Command, rc::Rc, sync::Mutex};
|
||||||
|
|
||||||
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
|
use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry};
|
||||||
|
|
||||||
use super::show_package;
|
|
||||||
|
|
||||||
pub static mut FULL_PKG_LIST: Vec<String> = vec![];
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Function to initialise a box that contains a
|
|
||||||
list of updatable packages.
|
|
||||||
*/
|
|
||||||
pub fn updates() -> gtk::Box {
|
pub fn updates() -> gtk::Box {
|
||||||
// The main box that encompasses everything else
|
// The main box that encompasses everything else
|
||||||
let packages_box = gtk::Box::builder()
|
let packages_box = gtk::Box::builder()
|
||||||
|
@ -53,29 +42,28 @@ pub fn updates() -> gtk::Box {
|
||||||
*/
|
*/
|
||||||
let skip_list = ["available", "installed", "last"];
|
let skip_list = ["available", "installed", "last"];
|
||||||
|
|
||||||
|
let pkg_list_index_mut = Rc::new(Mutex::new(0));
|
||||||
|
let mut pkg_list_index = pkg_list_index_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Initialize the packages list, needs to be a static
|
Initialize the packages list, needs to be a static
|
||||||
mutable variable to allow usage with GTK functions.
|
mutable variable to allow usage with GTK functions.
|
||||||
*/
|
*/
|
||||||
unsafe {
|
let packages_list_mut = Rc::new(Mutex::new(
|
||||||
PACKAGES_LIST = Some(
|
ListBox::builder()
|
||||||
ListBox::builder()
|
.css_classes(["packages-list"])
|
||||||
.css_classes(["packages-list"])
|
.valign(Align::Center)
|
||||||
.valign(Align::Center)
|
.halign(Align::Fill)
|
||||||
.halign(Align::Fill)
|
.hexpand(true)
|
||||||
.hexpand(true)
|
.build(),
|
||||||
.build(),
|
));
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
let packages_list = packages_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
Gets the list of packages, which also needs to be static
|
|
||||||
mutable for the same reason as PACKAGES_LIST, and remove
|
let full_pkg_list: Rc<Vec<String>> = Rc::new(
|
||||||
the description and architecture details, and also filter
|
String::from_utf8(
|
||||||
out the lines that aren't actually packages.
|
|
||||||
*/
|
|
||||||
FULL_PKG_LIST = String::from_utf8(
|
|
||||||
Command::new("dnf")
|
Command::new("dnf")
|
||||||
.args(["check-update"])
|
.arg("check-update")
|
||||||
.output()
|
.output()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.stdout,
|
.stdout,
|
||||||
|
@ -99,94 +87,116 @@ pub fn updates() -> gtk::Box {
|
||||||
&& !skip_list.contains(&x.to_lowercase().trim())
|
&& !skip_list.contains(&x.to_lowercase().trim())
|
||||||
})
|
})
|
||||||
.dedup()
|
.dedup()
|
||||||
.collect::<Vec<String>>();
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
/*
|
let pkg_list_mut = Rc::new(Mutex::new((*full_pkg_list).clone()));
|
||||||
Add buttons for the first 50 packages in the list, to
|
let pkg_list = pkg_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
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 FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].iter() {
|
|
||||||
PKG_LIST_INDEX += 1;
|
|
||||||
let pkg_btn = Button::builder()
|
|
||||||
.label(pkg)
|
|
||||||
.css_classes(["package-name"])
|
|
||||||
.build();
|
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
/*
|
||||||
show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
Gets the list of packages, which also needs to be static
|
||||||
});
|
mutable for the same reason as PACKAGES_LIST, and remove
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
|
the description and architecture details, and also filter
|
||||||
}
|
out the lines that aren't actually packages.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
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 full_pkg_list[0..(50.min(full_pkg_list.len()))].iter() {
|
||||||
|
*pkg_list_index += 1;
|
||||||
|
let pkg_btn = Button::builder()
|
||||||
|
.label(pkg)
|
||||||
|
.css_classes(["package-name"])
|
||||||
|
.build();
|
||||||
|
|
||||||
/*
|
pkg_btn.connect_clicked(|x| {
|
||||||
Create a scrollable area for the package list so that it
|
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
||||||
doesn't just go off the screen and hide all of the list.
|
});
|
||||||
*/
|
packages_list.append(&pkg_btn)
|
||||||
PKG_LISTBOX = Some(
|
}
|
||||||
ScrolledWindow::builder()
|
|
||||||
.css_classes(["package-list-scrollable"])
|
|
||||||
.valign(Align::Center)
|
|
||||||
.halign(Align::Center)
|
|
||||||
.width_request(800)
|
|
||||||
.height_request(600)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a scrollable area for the package list so that it
|
||||||
|
doesn't just go off the screen and hide all of the list.
|
||||||
|
*/
|
||||||
|
let pkg_listbox = Rc::new(
|
||||||
|
ScrolledWindow::builder()
|
||||||
|
.css_classes(["package-list-scrollable"])
|
||||||
|
.valign(Align::Center)
|
||||||
|
.halign(Align::Center)
|
||||||
|
.width_request(800)
|
||||||
|
.height_request(600)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
let pkg_list = pkg_list.clone();
|
||||||
|
let packages_list = packages_list.clone();
|
||||||
|
let pkg_list_index_mut = pkg_list_index_mut.clone();
|
||||||
/*
|
/*
|
||||||
Make it so that on reaching the end of the list it adds
|
Make it so that on reaching the end of the list it adds
|
||||||
buttons for 50 more packages.
|
buttons for 50 more packages.
|
||||||
*/
|
*/
|
||||||
PKG_LISTBOX
|
pkg_listbox.connect_edge_reached(move |_, edge| {
|
||||||
.as_ref()
|
let mut pkg_list_index = pkg_list_index_mut.lock().unwrap();
|
||||||
.unwrap()
|
if edge == PositionType::Bottom {
|
||||||
.connect_edge_reached(|_, edge| {
|
for i in
|
||||||
if edge == PositionType::Bottom {
|
pkg_list[*pkg_list_index..((*pkg_list_index + 50).min(pkg_list.len()))].iter()
|
||||||
(PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(FULL_PKG_LIST.len()))).for_each(
|
{
|
||||||
|i| {
|
let pkg_btn = Button::builder()
|
||||||
let pkg_btn = Button::builder()
|
.label(i.as_str())
|
||||||
.label(FULL_PKG_LIST[i].as_str())
|
.css_classes(["package-name"])
|
||||||
.css_classes(["package-name"])
|
.build();
|
||||||
.build();
|
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
pkg_btn.connect_clicked(|x| {
|
||||||
show_package(
|
super::show_package(
|
||||||
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
|
x.label().map(|x| x.to_string()).unwrap_or("".to_owned()),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn);
|
packages_list.append(&pkg_btn);
|
||||||
|
|
||||||
PKG_LIST_INDEX += 1;
|
*pkg_list_index += 1;
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
{
|
||||||
Run the `search` function when the search is changed.
|
let pkg_list_mut = pkg_list_mut.clone();
|
||||||
TODO: Make this asynchronous.
|
let full_pkg_list = full_pkg_list.clone();
|
||||||
*/
|
let packages_list_mut = packages_list_mut.clone();
|
||||||
pkgs_entry.connect_search_changed(move |x| {
|
let pkg_list_index_mut = pkg_list_index_mut.clone();
|
||||||
let f = x.clone();
|
let pkg_listbox = pkg_listbox.clone();
|
||||||
|
|
||||||
unsafe { search(&f) }
|
/*
|
||||||
});
|
Run the `search` function when the search is changed.
|
||||||
|
TODO: Make this asynchronous.
|
||||||
|
*/
|
||||||
|
pkgs_entry.connect_search_changed(move |x| {
|
||||||
|
let f = x.clone();
|
||||||
|
|
||||||
|
search(
|
||||||
|
&f,
|
||||||
|
pkg_list_mut.clone(),
|
||||||
|
full_pkg_list.clone(),
|
||||||
|
packages_list_mut.clone(),
|
||||||
|
pkg_list_index_mut.clone(),
|
||||||
|
pkg_listbox.clone(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pkgs_search.connect_entry(&pkgs_entry);
|
pkgs_search.connect_entry(&pkgs_entry);
|
||||||
|
|
||||||
// Add all of the parts to the box and return it.
|
// Add all of the parts to the box and return it.
|
||||||
packages_box.append(&pkgs_search);
|
packages_box.append(&pkgs_search);
|
||||||
packages_box.append(&pkgs_entry);
|
packages_box.append(&pkgs_entry);
|
||||||
unsafe {
|
packages_box.append(&(*pkg_listbox));
|
||||||
packages_box.append(PKG_LISTBOX.as_ref().unwrap());
|
|
||||||
|
|
||||||
PKG_LISTBOX
|
pkg_listbox.set_child(Some(&(*packages_list)));
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
|
|
||||||
};
|
|
||||||
packages_box
|
packages_box
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,14 +205,22 @@ Function that takes a search entry and updates
|
||||||
the package lists accordingly, skipping the values
|
the package lists accordingly, skipping the values
|
||||||
stated in `skip_list`
|
stated in `skip_list`
|
||||||
*/
|
*/
|
||||||
unsafe fn search(f: &SearchEntry) {
|
fn search(
|
||||||
|
f: &SearchEntry,
|
||||||
|
pkg_list_mut: Rc<Mutex<Vec<String>>>,
|
||||||
|
full_pkg_list: Rc<Vec<String>>,
|
||||||
|
packages_list_mut: Rc<Mutex<ListBox>>,
|
||||||
|
pkg_list_index_mut: Rc<Mutex<usize>>,
|
||||||
|
pkg_listbox: Rc<ScrolledWindow>,
|
||||||
|
) {
|
||||||
/*
|
/*
|
||||||
Reinitialize PKG_LIST with an extra predicate
|
Reinitialize PKG_LIST with an extra predicate
|
||||||
in the filter closure to ensure that the list
|
in the filter closure to ensure that the list
|
||||||
of packages contains only ones that match the
|
of packages contains only ones that match the
|
||||||
query
|
query
|
||||||
*/
|
*/
|
||||||
PKG_LIST = FULL_PKG_LIST
|
let mut pkg_list = pkg_list_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
*pkg_list = full_pkg_list
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|x| {
|
.filter(|x| {
|
||||||
x.to_lowercase()
|
x.to_lowercase()
|
||||||
|
@ -217,47 +235,45 @@ unsafe fn search(f: &SearchEntry) {
|
||||||
|
|
||||||
TODO: Check if this is necessary
|
TODO: Check if this is necessary
|
||||||
*/
|
*/
|
||||||
std::mem::drop(PACKAGES_LIST.take());
|
// std::mem::drop(PACKAGES_LIST.take());
|
||||||
|
|
||||||
|
let mut packages_list = packages_list_mut.lock().expect("Failed to lock Mutex.");
|
||||||
/*
|
/*
|
||||||
Reinitialize PACKAGES_LIST as a new ListBox.
|
Reinitialize PACKAGES_LIST as a new ListBox.
|
||||||
*/
|
*/
|
||||||
PACKAGES_LIST = Some(
|
*packages_list = ListBox::builder()
|
||||||
ListBox::builder()
|
.css_classes(["packages-list"])
|
||||||
.css_classes(["packages-list"])
|
.valign(Align::Center)
|
||||||
.valign(Align::Center)
|
.halign(Align::Fill)
|
||||||
.halign(Align::Fill)
|
.hexpand(true)
|
||||||
.hexpand(true)
|
.build();
|
||||||
.build(),
|
|
||||||
);
|
let mut pkg_list_index = pkg_list_index_mut.lock().expect("Failed to lock mutex.");
|
||||||
|
|
||||||
// Reset the list index so that it starts at 0 in the package list
|
// Reset the list index so that it starts at 0 in the package list
|
||||||
PKG_LIST_INDEX = 0;
|
*pkg_list_index = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Loop through the package list from 0 until
|
Loop through the package list from 0 until
|
||||||
either 50 or the length of the list, whichever
|
either 50 or the length of the list, whichever
|
||||||
is less, and make buttons in the list for each
|
is less, and make buttons in the list for each
|
||||||
*/
|
*/
|
||||||
for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].iter() {
|
for pkg in pkg_list[0..(50.min(pkg_list.len()))].iter() {
|
||||||
PKG_LIST_INDEX += 1;
|
*pkg_list_index += 1;
|
||||||
let pkg_btn = Button::builder()
|
let pkg_btn = Button::builder()
|
||||||
.label(pkg)
|
.label(pkg)
|
||||||
.css_classes(["package-name"])
|
.css_classes(["package-name"])
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
pkg_btn.connect_clicked(|x| {
|
pkg_btn.connect_clicked(|x| {
|
||||||
show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
super::show_package(x.label().map(|x| x.to_string()).unwrap_or("".to_owned()));
|
||||||
});
|
});
|
||||||
PACKAGES_LIST.as_ref().unwrap().append(&pkg_btn)
|
packages_list.append(&pkg_btn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Reinitialise PKG_LISTBOX since the value
|
Reinitialise PKG_LISTBOX since the value
|
||||||
it points to has now been changed.
|
it points to has now been changed.
|
||||||
*/
|
*/
|
||||||
PKG_LISTBOX
|
pkg_listbox.set_child(Some(&(*packages_list)));
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.set_child(Some(PACKAGES_LIST.as_ref().unwrap()));
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue