From 2e6542e9f72cc2a472fe2f48658cec45f326d51a Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Mon, 4 Oct 2021 05:57:20 -0400 Subject: [PATCH] Updated installer for new GTK API --- citadel-installer-ui/Cargo.toml | 10 +- citadel-installer-ui/src/builder.rs | 14 +- citadel-installer-ui/src/main.rs | 9 +- citadel-installer-ui/src/rowdata.rs | 226 ++++++++++++---------------- citadel-installer-ui/src/ui.rs | 107 ++++++------- 5 files changed, 170 insertions(+), 196 deletions(-) diff --git a/citadel-installer-ui/Cargo.toml b/citadel-installer-ui/Cargo.toml index a7ec897..dbb2608 100644 --- a/citadel-installer-ui/Cargo.toml +++ b/citadel-installer-ui/Cargo.toml @@ -10,8 +10,10 @@ homepage = "https://subgraph.com" libcitadel = { path = "../libcitadel" } failure = "0.1.8" dbus = "0.8.4" -gtk = { version = "0.9.0", features = ["v3_16"] } -gio = "0.9.1" -glib = "0.10.2" -gdk = "0.13.2" +gtk = { version = "0.14.0", features = ["v3_24"] } +gio = "0.14.0" +glib = "0.14.0" +glib-macros = "0.14.0" +gdk = "0.14.0" pango = "0.9.1" +once_cell = "1.0" diff --git a/citadel-installer-ui/src/builder.rs b/citadel-installer-ui/src/builder.rs index 0c2e31b..be330ba 100644 --- a/citadel-installer-ui/src/builder.rs +++ b/citadel-installer-ui/src/builder.rs @@ -17,32 +17,32 @@ impl Builder { } pub fn get_entry(&self, name: &str) -> Result { - Self::ok_or_err("GtkEntry", name, self.builder.get_object(name)) + Self::ok_or_err("GtkEntry", name, self.builder.object(name)) } pub fn get_box(&self, name: &str) -> Result { - Self::ok_or_err("GtkBox", name, self.builder.get_object(name)) + Self::ok_or_err("GtkBox", name, self.builder.object(name)) } pub fn get_label(&self, name: &str) -> Result { - Self::ok_or_err("GtkLabel", name, self.builder.get_object(name)) + Self::ok_or_err("GtkLabel", name, self.builder.object(name)) } pub fn get_listbox(&self, name: &str) -> Result { - Self::ok_or_err("GtkListBox", name, self.builder.get_object(name)) + Self::ok_or_err("GtkListBox", name, self.builder.object(name)) } pub fn get_progress_bar(&self, name: &str) -> Result { - Self::ok_or_err("GtkProgressBar", name, self.builder.get_object(name)) + Self::ok_or_err("GtkProgressBar", name, self.builder.object(name)) } pub fn get_textview(&self, name: &str) -> Result { - Self::ok_or_err("GtkTextView", name, self.builder.get_object(name)) + Self::ok_or_err("GtkTextView", name, self.builder.object(name)) } pub fn get_scrolled_window(&self, name: &str) -> Result { - Self::ok_or_err("GtkScrolledWindow", name, self.builder.get_object(name)) + Self::ok_or_err("GtkScrolledWindow", name, self.builder.object(name)) } } diff --git a/citadel-installer-ui/src/main.rs b/citadel-installer-ui/src/main.rs index 5179daf..cc59b8c 100644 --- a/citadel-installer-ui/src/main.rs +++ b/citadel-installer-ui/src/main.rs @@ -1,13 +1,11 @@ #![allow(deprecated)] -#[macro_use] extern crate glib; use gtk::prelude::*; -use gio::prelude::*; -use std::env::args; mod ui; mod builder; mod error; mod rowdata; mod dbus_client; + use libcitadel::CommandLine; use ui::Ui; @@ -15,8 +13,7 @@ pub use error::{Result,Error}; fn main() { let application = - gtk::Application::new(Some("com.subgraph.citadel-installer"), Default::default()) - .expect("Initialization failed..."); + gtk::Application::new(Some("com.subgraph.citadel-installer"), Default::default()); application.connect_activate(|app| { if !(CommandLine::live_mode() || CommandLine::install_mode()) { @@ -39,5 +36,5 @@ fn main() { } } }); - application.run(&args().collect::>()); + application.run(); } diff --git a/citadel-installer-ui/src/rowdata.rs b/citadel-installer-ui/src/rowdata.rs index 4ecbcaa..84d2541 100644 --- a/citadel-installer-ui/src/rowdata.rs +++ b/citadel-installer-ui/src/rowdata.rs @@ -1,156 +1,130 @@ - use gio::prelude::*; use std::fmt; -pub mod row_data { +use glib::subclass::prelude::*; +use glib::ParamSpec; +use gtk::glib; - use super::*; +use std::cell::RefCell; - use glib::subclass; - use glib::subclass::prelude::*; - use glib::translate::*; +#[derive(Default)] +pub struct RowDataImpl { + model: RefCell>, + path: RefCell>, + size: RefCell>, + removable: RefCell, +} - mod imp { - use super::*; - use std::cell::RefCell; +#[glib::object_subclass] +impl ObjectSubclass for RowDataImpl { + const NAME: &'static str = "RowData"; + type Type = RowData; + type ParentType = glib::Object; +} - pub struct RowData { - model: RefCell>, - path: RefCell>, - size: RefCell>, - removable: RefCell, - } - - static PROPERTIES: [subclass::Property; 4] = [ - subclass::Property("model", |name| { - glib::ParamSpec::string( - name, +impl ObjectImpl for RowDataImpl { + fn properties() -> &'static [ParamSpec] { + use once_cell::sync::Lazy; + static PROPERTIES: Lazy> = Lazy::new(|| { + vec![ + glib::ParamSpec::new_string( + "model", "Model", "Model", - None, // Default value + None, glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("path", |name| { - glib::ParamSpec::string( - name, + ), + glib::ParamSpec::new_string( + "path", "Path", "Path", - None, // Default value + None, glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("size", |name| { - glib::ParamSpec::string( - name, + ), + glib::ParamSpec::new_string( + "size", "Size", "Size", - None, // Default value + None, glib::ParamFlags::READWRITE, - ) - }), - subclass::Property("removable", |name| { - glib::ParamSpec::boolean( - name, + ), + glib::ParamSpec::new_boolean( + "removable", "Removable", "Removable", - false, // Default value + false, glib::ParamFlags::READWRITE, - ) - }), - ]; + ), + ] + }); + PROPERTIES.as_ref() + } - impl ObjectSubclass for RowData { - const NAME: &'static str = "RowData"; - type ParentType = glib::Object; - type Instance = subclass::simple::InstanceStruct; - type Class = subclass::simple::ClassStruct; - - glib_object_subclass!(); - - fn class_init(klass: &mut Self::Class) { - klass.install_properties(&PROPERTIES); + fn set_property( + &self, + _obj: &Self::Type, + _id: usize, + value: &glib::Value, + pspec: &glib::ParamSpec, + ) { + match pspec.name() { + "model" => { + let model = value + .get() + .expect("type conformity checked by `Object::set_property`"); + self.model.replace(model); } - - fn new() -> Self { - Self { - model: RefCell::new(None), - path: RefCell::new(None), - size: RefCell::new(None), - removable: RefCell::new(false), - } + "path" => { + let path = value + .get() + .expect("type conformity checked by `Object::set_property`"); + self.path.replace(path); } - } - - impl ObjectImpl for RowData { - glib_object_impl!(); - - fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("model", ..) => { - let model = value - .get() - .expect("type conformity checked by `Object::set_property`"); - self.model.replace(model); - } - subclass::Property("path", ..) => { - let path = value - .get() - .expect("type conformity checked by `Object::set_property`"); - self.path.replace(path); - } - subclass::Property("size", ..) => { - let size = value - .get() - .expect("type conformity checked by `Object::set_property`"); - self.size.replace(size); - } - subclass::Property("removable", ..) => { - let removable = value - .get_some() - .expect("type conformity checked by `Object::set_property`"); - self.removable.replace(removable); - } - _ => unimplemented!(), - } + "size" => { + let size = value + .get() + .expect("type conformity checked by `Object::set_property`"); + self.size.replace(size); } - - fn get_property(&self, _obj: &glib::Object, id: usize) -> Result { - let prop = &PROPERTIES[id]; - - match *prop { - subclass::Property("model", ..) => Ok(self.model.borrow().to_value()), - subclass::Property("path", ..) => Ok(self.path.borrow().to_value()), - subclass::Property("size", ..) => Ok(self.size.borrow().to_value()), - subclass::Property("removable", ..) => Ok(self.removable.borrow().to_value()), - _ => unimplemented!(), - } + "removable" => { + let removable = value + .get() + .expect("type conformity checked by `Object::set_property`"); + self.removable.replace(removable); } + _ => unimplemented!(), } } - glib_wrapper! { - pub struct RowData(Object, subclass::simple::ClassStruct, RowDataClass>); - - match fn { - get_type => || imp::RowData::get_type().to_glib(), - } - } - - impl RowData { - pub fn new(model: &str, path: &str, size: &str, removable: bool) -> RowData { - glib::Object::new(Self::static_type(), &[("model", &model), ("path", &path), ("size", &size), ("removable", &removable)]) - .expect("Failed to create row data") - .downcast() - .expect("Created row data is of wrong type") - } - } - - impl fmt::Display for RowData { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.1) + fn property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { + match pspec.name() { + "model" => self.model.borrow().to_value(), + "path" => self.path.borrow().to_value(), + "size" => self.size.borrow().to_value(), + "removable" => self.removable.borrow().to_value(), + _ => unimplemented!(), } } } + +glib::wrapper! { + pub struct RowData(ObjectSubclass); +} + +impl RowData { + pub fn new(model: &str, path: &str, size: &str, removable: bool) -> RowData { + glib::Object::new(&[ + ("model", &model), + ("path", &path), + ("size", &size), + ("removable", &removable), + ]) + .expect("Failed to create row data") + } +} + +impl fmt::Display for RowData { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} diff --git a/citadel-installer-ui/src/ui.rs b/citadel-installer-ui/src/ui.rs index 3b36e5c..b8850e9 100644 --- a/citadel-installer-ui/src/ui.rs +++ b/citadel-installer-ui/src/ui.rs @@ -1,13 +1,13 @@ use gtk::prelude::*; +use gtk::glib; -use gio::prelude::*; use dbus::Message; use std::time::Duration; use std::thread; use std::collections::HashMap; use dbus::blocking::{Connection, Proxy}; use crate::builder::*; -use crate::rowdata::row_data::RowData; +use crate::rowdata::RowData; use crate::{Result, Error}; use crate::dbus_client::*; @@ -41,6 +41,7 @@ pub struct Ui { pub luks_password_status_label: gtk::Label, pub disks_listbox: gtk::ListBox, pub disks_model: gio::ListStore, + pub disk_rows: Vec, pub confirm_install_label: gtk::Label, pub install_page: gtk::Box, pub install_progress: gtk::ProgressBar, @@ -57,11 +58,11 @@ impl Ui { assistant.set_position(gtk::WindowPosition::CenterAlways); assistant.set_application(Some(application)); - assistant.connect_delete_event(clone!(@strong application => move |_, _| { + assistant.connect_delete_event(glib::clone!(@strong application => move |_, _| { application.quit(); gtk::Inhibit(false) })); - assistant.connect_cancel(clone!(@strong application => move |_| { + assistant.connect_cancel(glib::clone!(@strong application => move |_| { application.quit(); })); let welcome_builder = Builder::new(WELCOME_UI); @@ -91,7 +92,7 @@ impl Ui { let item = item.downcast_ref::().expect("Row data is of wrong type"); let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 5); hbox.set_homogeneous(true); - let removable = item.get_property("removable").unwrap().get().unwrap().unwrap(); + let removable= item.property("removable").unwrap().get::().unwrap(); let icon_name = Self::get_disk_icon(removable); let disk_icon = gtk::Image::from_icon_name(Some(&icon_name), gtk::IconSize::LargeToolbar); disk_icon.set_halign(gtk::Align::Start); @@ -121,7 +122,7 @@ impl Ui { row.show_all(); row.upcast::() }); - disks_listbox.connect_row_selected(clone!(@strong assistant, @strong install_destination_page => move |_, listbox_row | { + disks_listbox.connect_row_selected(glib::clone!(@strong assistant, @strong install_destination_page => move |_, listbox_row | { if let Some(_) = listbox_row { assistant.set_page_complete(&install_destination_page, true); } @@ -156,6 +157,7 @@ impl Ui { luks_password_status_label, disks_listbox, disks_model, + disk_rows: disks.clone(), confirm_install_label, install_page, install_progress, @@ -163,12 +165,12 @@ impl Ui { install_textview, sender, }; - receiver.attach(None,clone!(@strong ui, @strong application => move |msg| { + receiver.attach(None,glib::clone!(@strong ui, @strong application => move |msg| { match msg { Msg::InstallStarted => { ui.install_progress.set_fraction(0.1428); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); let text = format!( "+ Installing Citadel to {}. \nFor a full log, consult the systemd journal by running the following command:\n sudo journalctl -u citadel-installer-backend.service\n", ui.get_install_destination()); @@ -177,41 +179,41 @@ impl Ui { }, Msg::LuksSetup(text) => { ui.install_progress.set_fraction(0.1428 * 2.0); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); buffer.insert(&mut iter, &text); }, Msg::LvmSetup(text) => { ui.install_progress.set_fraction(0.1428 * 3.0); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); buffer.insert(&mut iter, &text); }, Msg::BootSetup(text) => { ui.install_progress.set_fraction(0.1428 * 4.0); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); buffer.insert(&mut iter, &text); }, Msg::StorageCreated(text) => { ui.install_progress.set_fraction(0.1428 * 5.0); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); buffer.insert(&mut iter, &text); }, Msg::RootfsInstalled(text) => { ui.install_progress.set_fraction(0.1428 * 6.0); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); buffer.insert(&mut iter, &text); }, Msg::InstallCompleted => { ui.install_progress.set_fraction(1.0); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); buffer.insert(&mut iter, "+ Completed the installation successfully\n"); let quit_button = gtk::Button::with_label("Quit"); - quit_button.connect_clicked(clone!(@strong application => move |_| { + quit_button.connect_clicked(glib::clone!(@strong application => move |_| { application.quit(); })); quit_button.set_sensitive(true); @@ -220,12 +222,12 @@ impl Ui { }, Msg::InstallFailed(error) => { ui.install_progress.set_fraction(100.0); - let buffer = ui.install_textview.get_buffer().unwrap(); - let mut iter = buffer.get_end_iter(); + let buffer = ui.install_textview.buffer().unwrap(); + let mut iter = buffer.end_iter(); let text = format!("+ Install failed with error:\n{}\n", error); buffer.insert_markup(&mut iter, &text); let quit_button = gtk::Button::with_label("Quit"); - quit_button.connect_clicked(clone!(@strong application => move |_| { + quit_button.connect_clicked(glib::clone!(@strong application => move |_| { application.quit(); })); quit_button.set_sensitive(true); @@ -270,9 +272,9 @@ impl Ui { pub fn setup_entry_signals(&self, page: >k::Box, first_entry: >k::Entry, second_entry: >k::Entry, status_label: >k::Label) { let ui = self.clone(); let assistant = ui.assistant.clone(); - first_entry.connect_changed(clone!(@weak assistant, @weak page, @weak second_entry, @weak status_label => move |entry| { - let password = entry.get_text(); - let confirm = second_entry.get_text(); + first_entry.connect_changed(glib::clone!(@weak assistant, @weak page, @weak second_entry, @weak status_label => move |entry| { + let password = entry.text(); + let confirm = second_entry.text(); if password != "" && confirm != "" { let matches = password == confirm; if !matches { @@ -283,15 +285,15 @@ impl Ui { assistant.set_page_complete(&page, matches); } })); - first_entry.connect_activate(clone!(@weak second_entry => move |_| { + first_entry.connect_activate(glib::clone!(@weak second_entry => move |_| { second_entry.grab_focus(); })); } pub fn setup_prepare_signal(&self) { let ui = self.clone(); - ui.assistant.connect_prepare(clone!(@strong ui => move |assistant, page| { - let page_type = assistant.get_page_type(page); + ui.assistant.connect_prepare(glib::clone!(@strong ui => move |assistant, page| { + let page_type = assistant.page_type(page); if page_type == gtk::AssistantPageType::Confirm { let path = ui.get_install_destination(); let text = format!("{}", path); @@ -302,7 +304,7 @@ impl Ui { pub fn setup_apply_signal(&self) { let ui = self.clone(); - ui.assistant.connect_apply(clone!(@strong ui => move |_| { + ui.assistant.connect_apply(glib::clone!(@strong ui => move |_| { let citadel_password = ui.get_citadel_password(); let luks_password = ui.get_luks_password(); let destination = ui.get_install_destination(); @@ -318,9 +320,9 @@ impl Ui { pub fn setup_autoscroll_signal(&self) { let ui = self.clone(); let scrolled_window = ui.install_scrolled_window; - ui.install_textview.connect_size_allocate(clone!(@weak scrolled_window => move |_, _| { - let adjustment = scrolled_window.get_vadjustment().unwrap(); - adjustment.set_value(adjustment.get_upper() - adjustment.get_page_size()); + ui.install_textview.connect_size_allocate(glib::clone!(@weak scrolled_window => move |_, _| { + let adjustment = scrolled_window.vadjustment(); + adjustment.set_value(adjustment.upper() - adjustment.page_size()); })); } @@ -346,63 +348,62 @@ impl Ui { println!("Error parsing CSS style: {}", err); return; } - if let Some(screen) = gdk::Screen::get_default() { + if let Some(screen) = gdk::Screen::default() { gtk::StyleContext::add_provider_for_screen(&screen, &css, gtk::STYLE_PROVIDER_PRIORITY_USER); } } pub fn get_citadel_password(&self) -> String { let ui = self.clone(); - let password = ui.citadel_password_entry.get_text(); + let password = ui.citadel_password_entry.text(); password.to_string() } pub fn get_luks_password(&self) -> String { let ui = self.clone(); - let password = ui.luks_password_entry.get_text(); + let password = ui.luks_password_entry.text(); password.to_string() } pub fn get_install_destination(&self) -> String { let ui = self.clone(); - let model = ui.disks_model; - if let Some(row) = ui.disks_listbox.get_selected_row() { - let index = row.get_index() as u32; - let data = model.get_object(index).unwrap(); - let data = data.downcast_ref::().expect("Row data is of wrong type"); - // TODO: Fix unwrap - let path: String = data.get_property("path").unwrap().get().unwrap().unwrap(); - return path.to_string(); + if let Some(row) = ui.disks_listbox.selected_row() { + let index = row.index() as usize; + if ui.disk_rows.len() > index { + let data = &ui.disk_rows[index]; + let path: String = data.property("path").unwrap().get::().unwrap(); + return path.to_string(); + } } "".to_string() } fn setup_signal_matchers(&self, proxy: Proxy<&Connection>) { let sender = self.sender.clone(); - let _ = proxy.match_signal(clone!(@strong sender => move |_: ComSubgraphInstallerManagerInstallCompleted, _: &Connection, _: &Message| { + let _ = proxy.match_signal(glib::clone!(@strong sender => move |_: ComSubgraphInstallerManagerInstallCompleted, _: &Connection, _: &Message| { let _ = sender.send(Msg::InstallCompleted); true })); - let _ = proxy.match_signal(clone!(@strong sender => move |h: ComSubgraphInstallerManagerLvmSetup, _: &Connection, _: &Message| { + let _ = proxy.match_signal(glib::clone!(@strong sender => move |h: ComSubgraphInstallerManagerLvmSetup, _: &Connection, _: &Message| { let _ = sender.send(Msg::LvmSetup(h.text)); true })); - let _ = proxy.match_signal(clone!(@strong sender => move |h: ComSubgraphInstallerManagerLuksSetup, _: &Connection, _: &Message| { + let _ = proxy.match_signal(glib::clone!(@strong sender => move |h: ComSubgraphInstallerManagerLuksSetup, _: &Connection, _: &Message| { let _ = sender.send(Msg::LuksSetup(h.text)); true })); - let _ = proxy.match_signal(clone!(@strong sender => move |h: ComSubgraphInstallerManagerBootSetup, _: &Connection, _: &Message| { + let _ = proxy.match_signal(glib::clone!(@strong sender => move |h: ComSubgraphInstallerManagerBootSetup, _: &Connection, _: &Message| { let _ = sender.send(Msg::BootSetup(h.text)); true })); - let _ = proxy.match_signal(clone!(@strong sender => move |h: ComSubgraphInstallerManagerStorageCreated, _: &Connection, _: &Message| { + let _ = proxy.match_signal(glib::clone!(@strong sender => move |h: ComSubgraphInstallerManagerStorageCreated, _: &Connection, _: &Message| { let _ = sender.send(Msg::StorageCreated(h.text)); true })); - let _ = proxy.match_signal(clone!(@strong sender => move |h: ComSubgraphInstallerManagerRootfsInstalled, _: &Connection, _: &Message| { + let _ = proxy.match_signal(glib::clone!(@strong sender => move |h: ComSubgraphInstallerManagerRootfsInstalled, _: &Connection, _: &Message| { let _ = sender.send(Msg::RootfsInstalled(h.text)); true })); - let _ = proxy.match_signal(clone!(@strong sender => move |h: ComSubgraphInstallerManagerInstallFailed, _: &Connection, _: &Message| { + let _ = proxy.match_signal(glib::clone!(@strong sender => move |h: ComSubgraphInstallerManagerInstallFailed, _: &Connection, _: &Message| { let _ = sender.send(Msg::InstallFailed(h.text)); true }));