Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build/2.1.3 #836

Open
wants to merge 9 commits into
base: pods-2-1
Choose a base branch
from
168 changes: 123 additions & 45 deletions src/model/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,25 @@ use std::cell::Cell;
use std::cell::OnceCell;
use std::cell::RefCell;
use std::ffi::OsStr;
use std::mem;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;

use adw::prelude::*;
use futures::lock::Mutex;
use futures::stream;
use futures::FutureExt;
use futures::StreamExt;
use futures::TryFutureExt;
use futures::TryStreamExt;
use gettextrs::gettext;
use gio::subclass::prelude::*;
use glib::clone;
use glib::Properties;
use gtk::gio;
use gtk::glib;
use serde::Deserialize;
use tokio::io::AsyncWriteExt;
use tokio::io::BufWriter;

use crate::model;
use crate::model::AbstractContainerListExt;
Expand Down Expand Up @@ -568,56 +572,130 @@ impl Action {

let abort_registration = obj.setup_abort_handle();

obj.insert_line(&gettext("Copying bytes into memory…"));
obj.insert_line(&gettext("Writing to file…"));

let buf = Arc::new(Mutex::new(Vec::new()));
obj.insert_line(&gettext!("Size: {}", glib::format_size(0)));

utils::run_stream_with_finish_handler(
container.api().unwrap(),
|container| {
stream::Abortable::new(container.copy_from(container_path), abort_registration)
.boxed()
utils::do_async(
async move {
tokio::fs::File::options()
.write(true)
.create(true)
.truncate(true)
.open(&host_path)
.await
},
clone!(
@weak obj,
@strong buf
=> @default-return glib::ControlFlow::Break, move |result: podman::Result<Vec<u8>>|
{
match result {
Ok(chunk) => {
let mut buf = buf.lock().unwrap();
buf.extend(chunk);
obj.replace_last_line(&gettext!("Size: {}", glib::format_size(buf.len() as u64)));
glib::ControlFlow::Continue
}
Err(e) => {
obj.insert_line(&e.to_string());
obj.set_state(State::Failed);
glib::ControlFlow::Break
}
}
}),
clone!(@weak obj => move || {
let mut buf_ = Vec::<u8>::new();
mem::swap(&mut *buf.lock().unwrap(), &mut buf_);

utils::do_async({
let path = host_path.clone();
async move { tokio::fs::write(path, buf_).await }
},
clone!(@weak obj => move |result| match result {
Ok(_) => {
obj.insert_line(&gettext("Finished"));
obj.set_state(State::Finished);
}
#[weak]
obj,
#[weak]
container,
move |file| {
match file {
Err(e) => {
obj.insert_line(&e.to_string());
obj.set_state(State::Failed);
}
})
);
}),
Ok(file) => {
let writer = Arc::new(Mutex::new(BufWriter::new(file)));
obj.insert_line(&gettext!("Written: {}", glib::format_size(0)));

utils::run_stream_with_finish_handler(
container.api().unwrap(),
{
let writer = writer.clone();
move |container| {
stream::Abortable::new(
container.copy_from(container_path),
abort_registration,
)
.map_err(anyhow::Error::from)
.scan(
Ok((writer, 0)),
|state: &mut anyhow::Result<_>, chunk| match state {
Err(_) => futures::future::ready(None).boxed(),
Ok((writer, written)) => match chunk {
Err(e) => {
futures::future::ready(Some(Err(e))).boxed()
}
Ok(chunk) => {
*written += chunk.len();

let writer = writer.clone();
let written = *written;
async move {
Some({
let mut writer =
writer.lock().await;
writer
.write_all(&chunk)
.map_err(anyhow::Error::from)
.map_ok(|_| written)
.await
})
}
.boxed()
}
},
},
)
.boxed()
}
},
clone!(
#[weak]
obj,
#[upgrade_or]
glib::ControlFlow::Break,
move |result: anyhow::Result<usize>| {
match result {
Ok(written) => {
obj.replace_last_line(&gettext!(
"Written: {}",
glib::format_size(written as u64)
));
glib::ControlFlow::Continue
}
Err(e) => {
obj.insert_line(&e.to_string());
obj.set_state(State::Failed);
glib::ControlFlow::Break
}
}
}
),
clone!(
#[weak]
obj,
move || {
obj.insert_line(&gettext("Flushing…"));
utils::do_async(
{
let writer = writer.clone();
async move { writer.lock().await.flush().await }
},
clone!(
#[weak]
obj,
move |result| {
match result {
Ok(_) => {
obj.insert_line(&gettext("Finished"));
obj.set_state(State::Finished);
}
Err(e) => {
obj.insert_line(&e.to_string());
obj.set_state(State::Failed);
}
}
}
),
);
}
),
);
}
}
}
),
);

obj
Expand Down
133 changes: 87 additions & 46 deletions src/view/containers_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ mod imp {
);

klass.install_action(ACTION_SHOW_ALL_CONTAINERS, None, |widget, _, _| {
widget.set_show_only_running_containers(false);
widget.show_all_containers();
});
}

Expand Down Expand Up @@ -364,73 +364,109 @@ mod imp {
self.update_filter(filter_change);
}

pub(super) fn set_container_list(&self, value: Option<&model::ContainerList>) {
pub(super) fn set_container_list(&self, value: &model::ContainerList) {
let obj = &*self.obj();
if obj.container_list().as_ref() == value {
if obj.container_list().as_ref() == Some(value) {
return;
}

ACTIONS_SELECTION
.iter()
.for_each(|action_name| obj.action_set_enabled(action_name, false));

if let Some(container_list) = value {
container_list.connect_notify_local(
Some("num-selected"),
clone!(@weak obj => move |list, _| {
ACTIONS_SELECTION
.iter()
.for_each(|action_name| {
obj.action_set_enabled(action_name, list.num_selected() > 0);
value.connect_notify_local(
Some("num-selected"),
clone!(
#[weak]
obj,
move |list, _| {
ACTIONS_SELECTION.iter().for_each(|action_name| {
obj.action_set_enabled(action_name, list.num_selected() > 0);
});
}),
);
}
),
);

container_list.connect_notify_local(
Some("running"),
clone!(@weak obj => move |_, _| {
obj.imp().update_filter(gtk::FilterChange::Different)
}),
);
value.connect_notify_local(
Some("running"),
clone!(
#[weak]
obj,
move |_, _| obj.imp().update_filter(gtk::FilterChange::Different)
),
);

container_list.connect_container_name_changed(clone!(@weak obj => move |_, _| {
value.connect_container_name_changed(clone!(
#[weak]
obj,
move |_, _| {
obj.imp().update_filter(gtk::FilterChange::Different);
glib::timeout_add_seconds_local_once(
1,
clone!(@weak obj => move || obj.imp().update_sorter()),
clone!(
#[weak]
obj,
move || obj.imp().update_sorter()
),
);
}));
}
));

let model = gtk::SortListModel::new(
Some(gtk::FilterListModel::new(
Some(container_list.to_owned()),
self.filter.get().cloned(),
)),
self.sorter.get().cloned(),
);

self.flow_box.bind_model(Some(&model), |item| {
gtk::FlowBoxChild::builder()
.focusable(false)
.child(&view::ContainerCard::from(item.downcast_ref().unwrap()))
.build()
.upcast()
});
let model = gtk::SortListModel::new(
Some(gtk::FilterListModel::new(
Some(value.to_owned()),
self.filter.get().cloned(),
)),
self.sorter.get().cloned(),
);

self.filter_stack
.set_visible_child_name(if model.n_items() > 0 { "list" } else { "empty" });
model.connect_items_changed(clone!(@weak obj => move |model, _, removed, _| {
obj.imp()
.filter_stack
.set_visible_child_name(if model.n_items() > 0 { "list" } else { "empty" });
self.flow_box.bind_model(Some(&model), |item| {
gtk::FlowBoxChild::builder()
.focusable(false)
.child(&view::ContainerCard::from(item.downcast_ref().unwrap()))
.build()
.upcast()
});

self.set_filter_stack_visible_child(value, &model);
model.connect_items_changed(clone!(
#[weak]
obj,
#[weak]
value,
move |model, _, removed, _| {
obj.imp().set_filter_stack_visible_child(&value, model);

if removed > 0 {
obj.deselect_hidden_containers(model.upcast_ref());
}
}));
}
}
));
value.connect_initialized_notify(clone!(
#[weak]
obj,
#[weak]
model,
move |container_list| obj
.imp()
.set_filter_stack_visible_child(container_list, &model)
));

self.container_list.set(value);
self.container_list.set(Some(value));
}

fn set_filter_stack_visible_child(
&self,
container_list: &model::ContainerList,
model: &impl IsA<gio::ListModel>,
) {
self.filter_stack.set_visible_child_name(
if model.n_items() > 0 || !container_list.initialized() {
"list"
} else {
"empty"
},
);
}

fn update_filter(&self, filter_change: gtk::FilterChange) {
Expand Down Expand Up @@ -467,6 +503,11 @@ impl ContainersPanel {
.and_then(model::ContainerList::client)
}

pub(crate) fn show_all_containers(&self) {
self.set_show_only_running_containers(false);
self.set_search_mode(false);
}

pub(crate) fn set_search_mode(&self, value: bool) {
self.imp().search_bar.set_search_mode(value);
}
Expand Down
2 changes: 1 addition & 1 deletion src/view/containers_panel.ui
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@
<property name="child">
<object class="AdwStatusPage">
<property name="icon-name">package-x-generic-symbolic</property>
<property name="title" translatable="yes">No Running Containers</property>
<property name="title" translatable="yes">No Matching Containers</property>

<child>
<object class="GtkButton">
Expand Down
Loading
Loading