diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 73c01a3..ede3b6e 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -9,6 +9,7 @@ + diff --git a/src/apt_package_row/imp.rs b/src/apt_package_row/imp.rs index 949421b..dcaaf71 100644 --- a/src/apt_package_row/imp.rs +++ b/src/apt_package_row/imp.rs @@ -33,6 +33,8 @@ pub struct AptPackageRow { package_size: RefCell, #[property(get, set)] package_installed_size: RefCell, + #[property(get, set)] + package_marked: RefCell, } // ANCHOR_END: custom_button @@ -50,7 +52,7 @@ impl ObjectSubclass for AptPackageRow { impl ObjectImpl for AptPackageRow { fn signals() -> &'static [Signal] { static SIGNALS: OnceLock> = OnceLock::new(); - SIGNALS.get_or_init(|| vec![Signal::builder("row-deleted").build()]) + SIGNALS.get_or_init(|| vec![Signal::builder("checkbutton-toggled").build(), Signal::builder("checkbutton-untoggled").build()]) } fn constructed(&self) { let current_locale = match env::var_os("LANG") { @@ -96,11 +98,32 @@ impl ObjectImpl for AptPackageRow { obj.add_prefix(&prefix_box); obj.add_row(&expandable_box); - //let obj = self.obj(); - //obj.bind_property("package-name", &package_label, "label") - // .sync_create() - // .bidirectional() - // .build(); + let suffix_toggle = gtk::CheckButton::builder() + .tooltip_text(t!("mark_for_update")) + .halign(Align::Center) + .valign(Align::Center) + .hexpand(false) + .vexpand(false) + .build(); + + suffix_toggle.connect_toggled(clone!( @weak obj, @weak suffix_toggle => move |_| { + if suffix_toggle.is_active() { + obj.emit_by_name::<()>("checkbutton-toggled", &[]); + } else { + obj.emit_by_name::<()>("checkbutton-untoggled", &[]); + } + })); + + obj.add_suffix(&suffix_toggle); + + let obj = self.obj(); + obj.bind_property("package-marked", &suffix_toggle, "active") + .sync_create() + .bidirectional() + .build(); + + // turn on by default + obj.set_property("package-marked", true) } } // Trait shared by all widgets diff --git a/src/apt_update_page/mod.rs b/src/apt_update_page/mod.rs index 6322820..ed5391e 100644 --- a/src/apt_update_page/mod.rs +++ b/src/apt_update_page/mod.rs @@ -124,6 +124,35 @@ pub fn apt_update_page(window: adw::ApplicationWindow) -> gtk::Box { .width_request(500) .build(); + let bottom_bar = gtk::Box::builder() + .valign(Align::End) + .build(); + + let select_button = gtk::Button::builder() + .halign(Align::End) + .valign(Align::Center) + .hexpand(true) + .margin_start(10) + .margin_end(30) + .margin_top(2) + .margin_bottom(15) + .label(t!("select_button_deselect_all")) + .build(); + + select_button.connect_clicked(clone!(@weak select_button, @weak packages_boxedlist => move |_| { + let select_button_label = select_button.label().unwrap(); + let value_to_mark = if select_button_label == t!("select_button_select_all").to_string() { + true + } else if select_button_label == t!("select_button_deselect_all").to_string() { + false + } else { + panic!("Unexpected label on selection button") + }; + set_all_apt_row_marks_to(&packages_boxedlist, value_to_mark) + })); + + bottom_bar.append(&select_button); + let update_percent_server_context = MainContext::default(); // The main loop executes the asynchronous block update_percent_server_context.spawn_local(clone!(@weak apt_update_dialog_progress_bar, @weak apt_update_dialog, @strong get_upgradable_sender => async move { @@ -192,26 +221,39 @@ pub fn apt_update_page(window: adw::ApplicationWindow) -> gtk::Box { get_upgradable_server_context.spawn_local( clone!(@weak packages_boxedlist => async move { while let Ok(state) = get_upgradable_receiver.recv().await { - packages_boxedlist.append(&AptPackageRow::new(AptPackageSocket { - name: state.name, - arch: state.arch, - installed_version: state.installed_version, - candidate_version: state.candidate_version, - description: state.description, - source_uri: state.source_uri, - maintainer: state.maintainer, - size: state.size, - installed_size: state.installed_size, + let apt_row = AptPackageRow::new(AptPackageSocket { + name: state.name, + arch: state.arch, + installed_version: state.installed_version, + candidate_version: state.candidate_version, + description: state.description, + source_uri: state.source_uri, + maintainer: state.maintainer, + size: state.size, + installed_size: state.installed_size, is_last: state.is_last - })); - if state.is_last { - packages_boxedlist.set_sensitive(true); - let mut counter = packages_boxedlist.first_child(); - while let Some(row) = counter { - let row_next_sibling = row.next_sibling(); - row.downcast::().unwrap().add_suffix(>k::Button::builder().label("gg").build()); - counter = row_next_sibling; - } + }); + apt_row.connect_closure( + "checkbutton-toggled", + false, + closure_local!(@strong apt_row, @strong select_button, @strong packages_boxedlist => move |apt_row: AptPackageRow| { + if is_widget_select_all_ready(&packages_boxedlist) { + select_button.set_label(&t!("select_button_select_all").to_string()) + } else { + select_button.set_label(&t!("select_button_deselect_all").to_string()) + } + }), + ); + apt_row.connect_closure( + "checkbutton-untoggled", + false, + closure_local!(@strong apt_row, @strong select_button, @strong packages_boxedlist => move |apt_row: AptPackageRow| { + select_button.set_label(&t!("select_button_select_all").to_string()) + }), + ); + packages_boxedlist.append(&apt_row); + if state.is_last { + packages_boxedlist.set_sensitive(true); } } }), @@ -238,11 +280,37 @@ pub fn apt_update_page(window: adw::ApplicationWindow) -> gtk::Box { main_box.append(&searchbar); main_box.append(&packages_viewport); + main_box.append(&bottom_bar); apt_update_dialog.present(); main_box } +fn is_widget_select_all_ready(parent_listbox: &impl IsA) -> bool { + let mut is_ready = false; + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + if !downcast.package_marked() { + is_ready = true; + break + } + child_counter = next_child + } + is_ready +} + +fn set_all_apt_row_marks_to(parent_listbox: &impl IsA, value: bool) { + let mut child_counter = parent_listbox.borrow().first_child(); + while let Some(child) = child_counter { + let next_child = child.next_sibling(); + let downcast = child.downcast::().unwrap(); + downcast.set_package_marked(value); + child_counter = next_child + } +} + async fn update_percent_socket_server(buffer_sender: async_channel::Sender) { // Path to the Unix socket file let socket_path = "/tmp/pika_apt_update_percent.sock";