use gtk::prelude::*; use crate::core::database::AppImageRecord; use crate::core::fuse::FuseStatus; use crate::core::wayland::WaylandStatus; use super::widgets; /// Build a grid card for an AppImage. pub fn build_app_card(record: &AppImageRecord) -> gtk::FlowBoxChild { let card = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) .spacing(6) .margin_top(12) .margin_bottom(12) .margin_start(12) .margin_end(12) .halign(gtk::Align::Center) .build(); card.add_css_class("app-card"); card.set_size_request(160, -1); // Icon (48x48) let icon = if let Some(ref icon_path) = record.icon_path { let path = std::path::Path::new(icon_path); if path.exists() { let paintable = gtk::gdk::Texture::from_filename(path).ok(); let image = gtk::Image::builder() .pixel_size(48) .build(); if let Some(texture) = paintable { image.set_paintable(Some(&texture)); } else { image.set_icon_name(Some("application-x-executable-symbolic")); } image } else { gtk::Image::builder() .icon_name("application-x-executable-symbolic") .pixel_size(48) .build() } } else { gtk::Image::builder() .icon_name("application-x-executable-symbolic") .pixel_size(48) .build() }; // App name let name = record.app_name.as_deref().unwrap_or(&record.filename); let name_label = gtk::Label::builder() .label(name) .css_classes(["app-card-name"]) .ellipsize(gtk::pango::EllipsizeMode::End) .max_width_chars(18) .build(); // Version let version_text = record.app_version.as_deref().unwrap_or(""); let version_label = gtk::Label::builder() .label(version_text) .css_classes(["app-card-version"]) .ellipsize(gtk::pango::EllipsizeMode::End) .build(); card.append(&icon); card.append(&name_label); if !version_text.is_empty() { card.append(&version_label); } // Status badges row let badges = gtk::Box::builder() .orientation(gtk::Orientation::Horizontal) .spacing(4) .halign(gtk::Align::Center) .build(); badges.add_css_class("badge-row"); // Wayland status badge if let Some(ref ws) = record.wayland_status { let status = WaylandStatus::from_str(ws); if status != WaylandStatus::Unknown { badges.append(&widgets::status_badge(status.label(), status.badge_class())); } } // FUSE status badge if let Some(ref fs) = record.fuse_status { let status = FuseStatus::from_str(fs); if !status.is_functional() { badges.append(&widgets::status_badge(status.label(), status.badge_class())); } } // Update available badge if record.latest_version.is_some() { if let (Some(ref latest), Some(ref current)) = (&record.latest_version, &record.app_version) { if crate::core::updater::version_is_newer(latest, current) { badges.append(&widgets::status_badge("Update", "info")); } } } // Integration badge (only show if not integrated, to reduce clutter) if !record.integrated { badges.append(&widgets::status_badge("Not integrated", "neutral")); } card.append(&badges); let child = gtk::FlowBoxChild::builder() .child(&card) .build(); child }