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

Dialog improvements #96

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 89 additions & 55 deletions src/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ use cosmic::iced_runtime::platform_specific::wayland::layer_surface::{
};
use cosmic::iced_winit::commands::layer_surface::{destroy_layer_surface, get_layer_surface};
use cosmic::widget::autosize::autosize;
use cosmic::widget::{button, container, dropdown, horizontal_space, icon, text, Id, Row};
use cosmic::widget::{self, button, dropdown, icon, text, Column, Id};
use cosmic::{
iced::{
keyboard::{key::Named, Key},
widget::{column, row},
window, Length,
window,
},
iced_core::Alignment,
};
use std::collections::HashMap;
use tokio::sync::mpsc::Sender;
use zbus::zvariant;

use crate::wayland::WaylandHelper;
use crate::widget::keyboard_wrapper::KeyboardWrapper;
use crate::{app::CosmicPortal, fl};
use crate::{subscription, PortalResponse};

Expand Down Expand Up @@ -49,7 +52,7 @@ impl Access {
}
}

#[zbus::interface(name = "")]
#[zbus::interface(name = "org.freedesktop.impl.portal.Access")]
impl Access {
#[allow(clippy::too_many_arguments)]
async fn access_dialog(
Expand All @@ -66,6 +69,20 @@ impl Access {
// await response via channel
log::debug!("Access dialog {app_id} {parent_window} {title} {subtitle} {body} {options:?}");
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
// `widget::dialog` needs a slice of labels
let choice_labels: Vec<Vec<String>> = options
.choices
.iter()
.flatten()
.map(|(_, _, choices, _)| choices.iter().map(|(_, label)| label.clone()).collect())
.collect();
let active_choices = options
.choices
.iter()
.flatten()
.map(|(id, _, _, initial)| (id.clone(), initial.clone()))
.filter(|(_, value)| !value.is_empty())
.collect();
if let Err(err) = self
.tx
.send(subscription::Event::Access(AccessDialogArgs {
Expand All @@ -76,6 +93,8 @@ impl Access {
subtitle: subtitle.to_string(),
body: body.to_string(),
options,
active_choices,
choice_labels,
tx,
access_id: window::Id::NONE,
autosize: false,
Expand All @@ -100,7 +119,7 @@ pub enum Msg {
Ignore,
}

#[derive(Clone)]
#[derive(Clone, Debug)]
pub(crate) struct AccessDialogArgs {
pub handle: zvariant::ObjectPath<'static>,
pub app_id: String,
Expand All @@ -109,6 +128,8 @@ pub(crate) struct AccessDialogArgs {
pub subtitle: String,
pub body: String,
pub options: AccessDialogOptions,
pub active_choices: HashMap<String, String>,
pub choice_labels: Vec<Vec<String>>,
pub tx: Sender<PortalResponse<AccessDialogResult>>,
pub access_id: window::Id,
pub autosize: bool,
Expand Down Expand Up @@ -158,57 +179,66 @@ pub(crate) fn view(portal: &CosmicPortal) -> cosmic::Element<Msg> {
return text("Oops, no access dialog args").into();
};

let choices = &portal.access_choices;
let mut options = Vec::with_capacity(choices.len() + 3);
for (i, choice) in choices.iter().enumerate() {
options.push(dropdown(choice.1.as_slice(), choice.0, move |j| Msg::Choice(i, j)).into());
let choices = &args.options.choices.as_deref().unwrap_or(&[]);
let mut options = Vec::with_capacity(choices.len());
for (i, ((id, label, choices, initial), choice_labels)) in
choices.iter().zip(&args.choice_labels).enumerate()
{
let label = text(label);
let active_choice = args
.active_choices
.get(id)
.and_then(|choice_id| choices.iter().position(|(x, _)| x == choice_id));
let dropdown = dropdown(&choice_labels, active_choice, move |j| Msg::Choice(i, j));
options.push(row![label, dropdown].into());
}
options.push(horizontal_space().width(Length::Fill).into());
options.push(
button::text(
args.options
.deny_label
.clone()
.unwrap_or_else(|| fl!("cancel")),
)
.on_press(Msg::Cancel)
.into(),
);
options.push(
button::text(

let options = Column::with_children(options)
.spacing(spacing.space_xxs as f32) // space_l
.align_x(Alignment::Center);

let icon = icon::Icon::from(
icon::from_name(
args.options
.grant_label
.clone()
.unwrap_or_else(|| fl!("allow")),
.icon
.as_ref()
.map_or("image-missing", |name| name.as_str()),
)
.on_press(Msg::Allow)
.class(cosmic::theme::Button::Suggested)
.into(),
.size(64),
);

let content = container(
column![
row![
icon::Icon::from(
icon::from_name(
args.options
.icon
.as_ref()
.map_or("image-missing", |name| name.as_str())
)
.size(64)
)
.width(Length::Fixed(64.0))
.height(Length::Fixed(64.0)), // TODO icon for the dialog
text(args.title.as_str()),
text(args.subtitle.as_str()),
text(args.body.as_str()),
],
Row::with_children(options)
.spacing(spacing.space_xxs as f32) // space_l
.align_y(Alignment::Center),
]
.spacing(spacing.space_l as f32), // space_l
let control = column![text(args.body.as_str()), options].spacing(spacing.space_m as f32);

let cancel_button = button::text(
args.options
.deny_label
.clone()
.unwrap_or_else(|| fl!("cancel")),
)
.on_press(Msg::Cancel);

let allow_button = button::text(
args.options
.grant_label
.clone()
.unwrap_or_else(|| fl!("allow")),
)
.on_press(Msg::Allow)
.class(cosmic::theme::Button::Suggested);

let content = KeyboardWrapper::new(
widget::dialog()
.title(&args.title)
.body(&args.subtitle)
.control(control)
.icon(icon)
.secondary_action(cancel_button)
.primary_action(allow_button),
|key| match key {
Key::Named(Named::Enter) => Some(Msg::Allow),
Key::Named(Named::Escape) => Some(Msg::Cancel),
_ => None,
},
);

if args.autosize {
Expand All @@ -226,11 +256,10 @@ pub fn update_msg(portal: &mut CosmicPortal, msg: Msg) -> cosmic::Task<crate::ap
Msg::Allow => {
let args = portal.access_args.take().unwrap();
let tx = args.tx.clone();
let choices = args.active_choices.clone().into_iter().collect();
tokio::spawn(async move {
tx.send(PortalResponse::Success(AccessDialogResult {
choices: vec![],
}))
.await
tx.send(PortalResponse::Success(AccessDialogResult { choices }))
.await
});

args.destroy_surface()
Expand All @@ -247,7 +276,12 @@ pub fn update_msg(portal: &mut CosmicPortal, msg: Msg) -> cosmic::Task<crate::ap
}
Msg::Choice(i, j) => {
let args = portal.access_args.as_mut().unwrap();
portal.access_choices[i].0 = Some(j);
if let Some(choice) = args.options.choices.as_ref().and_then(|x| x.get(i)) {
if let Some((option_id, _)) = choice.2.get(j) {
args.active_choices
.insert(choice.0.clone(), option_id.clone());
}
}
cosmic::iced::Task::none()
}
Msg::Ignore => cosmic::iced::Task::none(),
Expand Down
2 changes: 0 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ pub struct CosmicPortal {
pub config: config::Config,

pub access_args: Option<access::AccessDialogArgs>,
pub access_choices: Vec<(Option<usize>, Vec<String>)>,

pub file_choosers: HashMap<window::Id, (file_chooser::Args, file_chooser::Dialog)>,

Expand Down Expand Up @@ -128,7 +127,6 @@ impl cosmic::Application for CosmicPortal {
config_handler,
config,
access_args: Default::default(),
access_choices: Default::default(),
file_choosers: Default::default(),
screenshot_args: Default::default(),
screencast_args: Default::default(),
Expand Down
2 changes: 1 addition & 1 deletion src/file_chooser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ pub enum Msg {
DialogResult(DialogResult),
}

#[derive(Clone)]
#[derive(Clone, Debug)]
pub(crate) struct Args {
pub handle: zvariant::ObjectPath<'static>,
pub app_id: String,
Expand Down
39 changes: 36 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use cosmic::cosmic_theme::palette::Srgba;
use futures::future::AbortHandle;
use std::collections::HashMap;
use futures::future::{abortable, AbortHandle};
use std::{collections::HashMap, future::Future};
use zbus::zvariant::{self, OwnedValue};

pub use cosmic_portal_config as config;
Expand Down Expand Up @@ -61,6 +61,39 @@ impl Request {
}
}

impl Request {
async fn run<T, DropFut, F, Fut>(
connection: &zbus::Connection,
handle: &zvariant::ObjectPath<'_>,
on_cancel: F,
task: Fut,
) -> PortalResponse<T>
where
T: zvariant::Type + serde::Serialize,
DropFut: Future<Output = ()>,
F: FnOnce() -> DropFut,
Fut: Future<Output = PortalResponse<T>>,
{
let (abortable, abort_handle) = abortable(task);
let _ = connection
.object_server()
.at(handle, Request(abort_handle))
.await;
let resp = abortable.await;
let _ = connection
.object_server()
.remove::<Request, _>(handle)
.await;
match resp {
Ok(resp) => resp,
_ => {
on_cancel().await;
PortalResponse::Cancelled
}
}
}
}

struct Session<T: Send + Sync + 'static> {
data: T,
close_cb: Option<Box<dyn FnOnce(&mut T) + Send + Sync + 'static>>,
Expand Down Expand Up @@ -115,7 +148,7 @@ impl<Data: Send + Sync + 'static> std::ops::DerefMut for Session<Data> {

async fn session_interface<Data: Send + Sync + 'static>(
connection: &zbus::Connection,
session_handle: zvariant::ObjectPath<'_>,
session_handle: &zvariant::ObjectPath<'_>,
) -> Option<zbus::InterfaceRef<Session<Data>>> {
connection
.object_server()
Expand Down
Loading