diff --git a/data/com.github.marhkb.Pods.gschema.xml.in b/data/com.github.marhkb.Pods.gschema.xml.in
index 68aa6705..69453405 100644
--- a/data/com.github.marhkb.Pods.gschema.xml.in
+++ b/data/com.github.marhkb.Pods.gschema.xml.in
@@ -26,6 +26,15 @@
The last used view
+
+
+
+
+
+ 'grid'
+ Containers representation
+
+
false
Whether to hide intermediate images
diff --git a/data/com.github.marhkb.Pods.metainfo.xml.in.in b/data/com.github.marhkb.Pods.metainfo.xml.in.in
index 0a6f8203..2a52cc9b 100644
--- a/data/com.github.marhkb.Pods.metainfo.xml.in.in
+++ b/data/com.github.marhkb.Pods.metainfo.xml.in.in
@@ -46,6 +46,7 @@
The search entry to a toggle button in the headerbar. (#830)
Search is now possible in the connection chooser page. (#830)
The control elements of the header bar are now moved to a lower bar if space becomes too tight. (#837)
+ Allow switching between grid and list view in the containers panel. (#838)
diff --git a/src/main.rs b/src/main.rs
index 4ef3f021..2691c462 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -211,7 +211,9 @@ fn init() {
view::ContainerTerminalPage::static_type();
view::ContainerVolumeRow::static_type();
view::ContainersCountBar::static_type();
+ view::ContainersGridView::static_type();
view::ContainersGroup::static_type();
+ view::ContainersListView::static_type();
view::ContainersPanel::static_type();
view::ContainersPrunePage::static_type();
view::ContainersRow::static_type();
diff --git a/src/resources.gresource.xml b/src/resources.gresource.xml
index c88c4535..44c3d2e9 100644
--- a/src/resources.gresource.xml
+++ b/src/resources.gresource.xml
@@ -30,7 +30,9 @@
view/container_terminal_page.ui
view/container_volume_row.ui
view/containers_count_bar.ui
+ view/containers_grid_view.ui
view/containers_group.ui
+ view/containers_list_view.ui
view/containers_panel.ui
view/containers_prune_page.ui
view/containers_row.ui
diff --git a/src/view/container_row.rs b/src/view/container_row.rs
index 36d65c8a..15901e86 100644
--- a/src/view/container_row.rs
+++ b/src/view/container_row.rs
@@ -1,3 +1,5 @@
+use std::cell::RefCell;
+
use adw::prelude::*;
use adw::subclass::prelude::*;
use gettextrs::gettext;
@@ -8,6 +10,7 @@ use gtk::glib;
use gtk::CompositeTemplate;
use crate::model;
+use crate::model::prelude::*;
use crate::utils;
use crate::view;
use crate::widget;
@@ -19,30 +22,42 @@ mod imp {
#[properties(wrapper_type = super::ContainerRow)]
#[template(resource = "/com/github/marhkb/Pods/ui/view/container_row.ui")]
pub(crate) struct ContainerRow {
+ pub(super) bindings: RefCell>,
#[property(get, set, construct, nullable)]
pub(super) container: glib::WeakRef,
#[template_child]
pub(super) spinner: TemplateChild,
- // #[template_child]
- // pub(super) name_label: TemplateChild,
- // #[template_child]
- // pub(super) repo_label: TemplateChild,
+ #[template_child]
+ pub(super) check_button_revealer: TemplateChild,
+ #[template_child]
+ pub(super) check_button: TemplateChild,
+ #[template_child]
+ pub(super) name_label: TemplateChild,
+ #[template_child]
+ pub(super) pod_image: TemplateChild,
+ #[template_child]
+ pub(super) repo_label: TemplateChild,
+ #[template_child]
+ pub(super) ports_flow_box: TemplateChild,
#[template_child]
pub(super) stats_box: TemplateChild,
#[template_child]
pub(super) cpu_bar: TemplateChild,
#[template_child]
pub(super) mem_bar: TemplateChild,
+ #[template_child]
+ pub(super) end_box_revealer: TemplateChild,
}
#[glib::object_subclass]
impl ObjectSubclass for ContainerRow {
const NAME: &'static str = "PdsContainerRow";
type Type = super::ContainerRow;
- type ParentType = adw::ActionRow;
+ type ParentType = gtk::ListBoxRow;
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
+ klass.bind_template_callbacks();
klass.install_action("container-row.activate", None, |widget, _, _| {
widget.activate();
@@ -76,9 +91,20 @@ mod imp {
let container_list_expr =
container_expr.chain_property::("container-list");
+ let selection_mode_expr =
+ container_list_expr.chain_property::("selection-mode");
+
+ selection_mode_expr.bind(&*self.check_button_revealer, "reveal-child", Some(obj));
+ selection_mode_expr
+ .chain_closure::(closure!(|_: Self::Type, is_selection_mode: bool| {
+ !is_selection_mode
+ }))
+ .bind(&*self.end_box_revealer, "reveal-child", Some(obj));
+
let status_expr = container_expr.chain_property::("status");
let health_status_expr =
container_expr.chain_property::("health-status");
+ let pod_expr = container_expr.chain_property::("pod");
let stats_expr = container_expr.chain_property::("stats");
container_expr
@@ -137,14 +163,33 @@ mod imp {
}
}),
)
- .bind(obj, "title", Some(obj));
+ .bind(&*self.name_label, "label", Some(obj));
+
+ pod_expr
+ .chain_closure::(closure!(
+ |_: Self::Type, pod: Option| pod.is_some()
+ ))
+ .bind(&*self.pod_image, "visible", Some(obj));
+
+ gtk::ClosureExpression::new::(
+ [
+ &pod_expr,
+ &container_expr
+ .chain_property::("ports")
+ .chain_property::("len"),
+ ],
+ closure!(|_: Self::Type, pod: Option<&model::Pod>, len: u32| {
+ pod.is_none() && len > 0
+ }),
+ )
+ .bind(&*self.ports_flow_box, "visible", Some(obj));
container_expr
.chain_property::("image-name")
.chain_closure::(closure!(|_: Self::Type, name: Option| {
utils::escape(&utils::format_option(name))
}))
- .bind(obj, "subtitle", Some(obj));
+ .bind(&*self.repo_label, "label", Some(obj));
status_expr
.chain_closure::(closure!(
@@ -194,13 +239,78 @@ mod imp {
impl WidgetImpl for ContainerRow {}
impl ListBoxRowImpl for ContainerRow {}
- impl PreferencesRowImpl for ContainerRow {}
- impl ActionRowImpl for ContainerRow {}
+
+ #[gtk::template_callbacks]
+ impl ContainerRow {
+ #[template_callback]
+ fn on_notify_container(&self) {
+ let mut bindings = self.bindings.borrow_mut();
+ while let Some(binding) = bindings.pop() {
+ binding.unbind();
+ }
+
+ let obj = &*self.obj();
+
+ if let Some(container) = obj.container() {
+ let binding = container
+ .bind_property("selected", &*self.check_button, "active")
+ .flags(glib::BindingFlags::SYNC_CREATE | glib::BindingFlags::BIDIRECTIONAL)
+ .build();
+
+ bindings.push(binding);
+
+ if !container.has_pod() {
+ self.ports_flow_box.bind_model(
+ Some(&container.ports()),
+ clone!(@weak obj => @default-panic, move |item| {
+ let port_mapping =
+ item.downcast_ref::().unwrap();
+
+ let label = gtk::Label::builder()
+ .css_classes([
+ "status-badge-small",
+ "numeric",
+ ])
+ .halign(gtk::Align::Center)
+ .label(format!(
+ "{}/{}",
+ port_mapping.host_port(),
+ port_mapping.protocol()
+ ))
+ .build();
+
+ let css_classes = utils::css_classes(&label);
+ super::ContainerRow::this_expression("container")
+ .chain_property::("status")
+ .chain_closure::>(closure!(
+ |_: super::ContainerRow, status: model::ContainerStatus| {
+ css_classes
+ .iter()
+ .cloned()
+ .chain(Some(String::from(
+ super::super::container_status_css_class(status)
+ )))
+ .collect::>()
+ }
+ ))
+ .bind(&label, "css-classes", Some(&obj));
+
+ gtk::FlowBoxChild::builder()
+ .halign(gtk::Align::Start)
+ .child(&label)
+ .build()
+ .upcast()
+ }),
+ );
+ }
+ }
+ }
+ }
}
glib::wrapper! {
pub(crate) struct ContainerRow(ObjectSubclass)
- @extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow, adw::ActionRow,
+ @extends gtk::Widget, gtk::ListBoxRow,
@implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
}
@@ -248,19 +358,27 @@ impl ContainerRow {
fn activate(&self) {
if let Some(container) = self.container().as_ref() {
- let nav_page = adw::NavigationPage::builder()
- .child(&view::ContainerDetailsPage::from(container))
- .build();
-
- Self::this_expression("container")
- .chain_property::("name")
- .chain_closure::(closure!(|_: Self, name: &str| gettext!(
- "Container {}",
- name
- )))
- .bind(&nav_page, "title", Some(self));
-
- utils::navigation_view(self).push(&nav_page);
+ if container
+ .container_list()
+ .map(|list| list.is_selection_mode())
+ .unwrap_or(false)
+ {
+ container.select();
+ } else {
+ let nav_page = adw::NavigationPage::builder()
+ .child(&view::ContainerDetailsPage::from(container))
+ .build();
+
+ Self::this_expression("container")
+ .chain_property::("name")
+ .chain_closure::(closure!(|_: Self, name: &str| gettext!(
+ "Container {}",
+ name
+ )))
+ .bind(&nav_page, "title", Some(self));
+
+ utils::navigation_view(self).push(&nav_page);
+ }
}
}
}
diff --git a/src/view/container_row.ui b/src/view/container_row.ui
index 15fe8715..5045e163 100644
--- a/src/view/container_row.ui
+++ b/src/view/container_row.ui
@@ -1,30 +1,28 @@
-
+
+
container-row.activate
- True
-
-
-
-
-
+