feat: provide command to install missing dependencies in missing dependencies dialog

This commit is contained in:
Gabriele Musco 2024-01-19 13:37:28 +01:00
parent 24d07b94b2
commit e53b282210
No known key found for this signature in database
GPG key ID: 1068D795C80E51DE
3 changed files with 162 additions and 49 deletions

View file

@ -9,39 +9,56 @@ pub enum LinuxDistro {
Debian,
Fedora,
Gentoo,
// TODO: add Nix,
Suse,
}
pub fn get_distro() -> Option<LinuxDistro> {
if let Some(mut reader) = get_reader("/etc/issue") {
let mut buf = String::default();
if reader.read_to_string(&mut buf).is_ok() {
buf = buf.trim().to_lowercase();
if buf.contains("arch linux")
|| buf.contains("manjaro")
|| buf.contains("steamos")
|| buf.contains("steam os")
{
return Some(LinuxDistro::Arch);
}
if buf.contains("debian")
|| buf.contains("ubuntu")
|| buf.contains("mint")
|| buf.contains("elementary")
|| buf.contains("pop")
{
return Some(LinuxDistro::Debian);
}
if buf.contains("fedora") || buf.contains("nobara") {
return Some(LinuxDistro::Fedora);
}
if buf.contains("gentoo") {
return Some(LinuxDistro::Gentoo);
}
if buf.contains("alpine") || buf.contains("postmarket") {
return Some(LinuxDistro::Alpine);
impl LinuxDistro {
pub fn get() -> Option<Self> {
if let Some(mut reader) = get_reader("/etc/issue") {
let mut buf = String::default();
if reader.read_to_string(&mut buf).is_ok() {
buf = buf.trim().to_lowercase();
if buf.contains("arch linux")
|| buf.contains("manjaro")
|| buf.contains("steamos")
|| buf.contains("steam os")
{
return Some(Self::Arch);
}
if buf.contains("debian")
|| buf.contains("ubuntu")
|| buf.contains("mint")
|| buf.contains("elementary")
|| buf.contains("pop")
{
return Some(Self::Debian);
}
if buf.contains("fedora") || buf.contains("nobara") {
return Some(Self::Fedora);
}
if buf.contains("gentoo") {
return Some(Self::Gentoo);
}
if buf.contains("alpine") || buf.contains("postmarket") {
return Some(Self::Alpine);
}
// TODO: detect suse, sles, rhel, nix
}
}
None
}
None
pub fn install_command(&self, packages: &[String]) -> String {
match self {
Self::Arch => format!("sudo pacman -Syu {}", packages.join(" ")),
Self::Alpine => format!("sudo apk add {}", packages.join(" ")),
Self::Debian => format!("sudo apt install {}", packages.join(" ")),
Self::Fedora => format!("sudo dnf install {}", packages.join(" ")),
Self::Gentoo => format!("sudo emerge {}", packages.join(" ")),
Self::Suse => format!("sudo zypper install {}", packages.join(" ")),
}
}
}

View file

@ -1,7 +1,10 @@
use gtk::traits::{GtkApplicationExt, GtkWindowExt};
use gtk::{
prelude::IsA,
traits::{GtkApplicationExt, GtkWindowExt},
};
use relm4::{adw::traits::MessageDialogExt, prelude::*};
pub fn alert(title: &str, msg: Option<&str>, parent: Option<&gtk::Window>) {
fn alert_base(title: &str, msg: Option<&str>, parent: Option<&gtk::Window>) -> adw::MessageDialog {
let d = adw::MessageDialog::builder()
.modal(true)
.heading(title)
@ -15,5 +18,23 @@ pub fn alert(title: &str, msg: Option<&str>, parent: Option<&gtk::Window>) {
d.set_transient_for(gtk::Application::default().active_window().as_ref());
}
d.add_response("ok", "_Ok");
d
}
pub fn alert(title: &str, msg: Option<&str>, parent: Option<&gtk::Window>) {
let d = alert_base(title, msg, parent);
d.present();
}
pub fn alert_w_widget(
title: &str,
msg: Option<&str>,
widget: Option<&gtk::Widget>,
parent: Option<&gtk::Window>,
) {
let d = alert_base(title, msg, parent);
if let Some(w) = widget {
d.set_extra_child(Some(w));
}
d.present();
}

View file

@ -1,5 +1,5 @@
use super::about_dialog::AboutDialog;
use super::alert::alert;
use super::alert::{alert, alert_w_widget};
use super::build_window::{BuildStatus, BuildWindow};
use super::debug_view::{DebugView, DebugViewMsg};
use super::fbt_config_editor::{FbtConfigEditor, FbtConfigEditorInit, FbtConfigEditorMsg};
@ -8,7 +8,7 @@ use super::job_worker::job::WorkerJob;
use super::job_worker::JobWorker;
use super::libsurvive_setup_window::LibsurviveSetupWindow;
use super::main_view::MainViewMsg;
use super::util::open_with_default_handler;
use super::util::{copy_text, open_with_default_handler};
use super::wivrn_conf_editor::{WivrnConfEditor, WivrnConfEditorInit, WivrnConfEditorMsg};
use crate::builders::build_basalt::get_build_basalt_jobs;
use crate::builders::build_libsurvive::get_build_libsurvive_jobs;
@ -34,7 +34,7 @@ use crate::file_builders::openvrpaths_vrpath::{
set_current_openvrpaths_to_profile, set_current_openvrpaths_to_steam,
};
use crate::file_utils::setcap_cap_sys_nice_eip;
use crate::linux_distro::get_distro;
use crate::linux_distro::LinuxDistro;
use crate::log_parser::MonadoLog;
use crate::paths::{get_data_dir, get_ipc_file_path};
use crate::profile::{Profile, XRServiceType};
@ -466,26 +466,101 @@ impl SimpleComponent for App {
if !missing_deps.is_empty() {
missing_deps.sort_unstable();
missing_deps.dedup(); // dedup only works if sorted, hence the above
let distro = get_distro();
alert(
"Missing dependencies:",
Some(
let distro = LinuxDistro::get();
let (missing_package_list, install_missing_widget): (
String,
Option<gtk::Widget>,
) = if let Some(d) = distro {
(
missing_deps
.iter()
.map(|dep| {
if let Some(d) = distro {
return dep
.packages
.get(&d)
.unwrap_or_else(|| &dep.name)
.clone();
}
dep.name.clone()
dep.packages.get(&d).unwrap_or_else(|| &dep.name).clone()
})
.collect::<Vec<String>>()
.join(", ")
.as_str(),
),
.join(", "),
{
let packages = missing_deps
.iter()
.filter_map(|dep| {
dep.packages.get(&d).and_then(|s| Some(s.clone()))
})
.collect::<Vec<String>>();
if packages.is_empty() {
None
} else {
let cmd = d.install_command(&packages);
{
let container = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal)
.spacing(6)
.build();
let btn = gtk::Button::builder()
.css_classes(["flat", "circular"])
.tooltip_text("Copy")
.icon_name("edit-copy-symbolic")
.vexpand(false)
.hexpand(false)
.valign(gtk::Align::Center)
.halign(gtk::Align::Center)
.build();
btn.connect_clicked(
clone!(@strong cmd => move |_| copy_text(&cmd)),
);
container.append(
&gtk::ScrolledWindow::builder()
.vscrollbar_policy(gtk::PolicyType::Never)
.hscrollbar_policy(gtk::PolicyType::Automatic)
.css_classes(["card"])
.overflow(gtk::Overflow::Hidden)
.child(
&gtk::TextView::builder()
.hexpand(true)
.vexpand(false)
.monospace(true)
.editable(false)
.left_margin(6)
.right_margin(6)
.top_margin(6)
.bottom_margin(18)
.buffer(
&gtk::TextBuffer::builder()
.text(&cmd)
.enable_undo(false)
.build(),
)
.build(),
)
.build(),
);
container.append(&btn);
Some(container.upcast())
}
}
},
)
} else {
(
missing_deps
.iter()
.map(|dep| dep.name.clone())
.collect::<Vec<String>>()
.join(", "),
None,
)
};
alert_w_widget(
"Missing dependencies:",
Some(&format!(
"{}{}",
missing_package_list,
if install_missing_widget.is_some() {
"\n\nYou can install them with the following command:"
} else {
""
}
)),
install_missing_widget.as_ref(),
Some(&self.app_win.clone().upcast::<gtk::Window>()),
);
return;