wip: subsurfaces

This commit is contained in:
Ashley Wulber 2024-12-27 16:17:16 -05:00
parent 64ddcb3bf2
commit f91369bdbb
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
7 changed files with 175 additions and 15 deletions

View file

@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};
use cosmic::app::Core;
use cosmic::iced::application;
use cosmic::iced::platform_specific::shell::commands::popup::{destroy_popup, get_popup};
@ -6,7 +8,7 @@ use cosmic::iced::{Length, Limits, Task};
use cosmic::iced_runtime::core::window;
use cosmic::theme::iced;
use cosmic::widget::{list_column, settings, toggler};
use cosmic::{Element, Theme};
use cosmic::{applet, Element, Theme};
const ID: &str = "com.system76.CosmicAppletExample";
@ -14,7 +16,7 @@ const ID: &str = "com.system76.CosmicAppletExample";
pub struct Window {
core: Core,
popup: Option<Id>,
example_row: bool,
example_row: Arc<Mutex<bool>>,
}
#[derive(Clone, Debug)]
@ -73,6 +75,32 @@ impl cosmic::Application for Window {
.height(500)
.width(500);
popup_settings.positioner.size = Some((500, 500));
let applet = self.core.applet.clone();
let toggled = self.example_row.clone();
popup_settings = popup_settings.with_view(Box::new(
move || -> Option<cosmic::Element<'static, cosmic::app::Message<Message>>> {
{
let guard = toggled.lock().unwrap();
let content_list =
list_column().padding(5).spacing(0).add(settings::item(
"Example row",
cosmic::widget::container(
toggler(*guard).on_toggle(|value| {
Message::ToggleExampleRow(value)
}),
)
.height(Length::Fixed(50.)),
));
Some(
Element::from(applet.popup_container(content_list))
.map(cosmic::app::Message::App),
)
}
},
));
get_popup(popup_settings)
};
}
@ -81,7 +109,10 @@ impl cosmic::Application for Window {
self.popup = None;
}
}
Message::ToggleExampleRow(toggled) => self.example_row = toggled,
Message::ToggleExampleRow(toggled) => {
let mut guard = self.example_row.lock().unwrap();
*guard = toggled;
}
}
Task::none()
}
@ -95,15 +126,7 @@ impl cosmic::Application for Window {
}
fn view_window(&self, _id: Id) -> Element<Self::Message> {
let content_list = list_column().padding(5).spacing(0).add(settings::item(
"Example row",
cosmic::widget::container(
toggler(self.example_row).on_toggle(|value| Message::ToggleExampleRow(value)),
)
.height(Length::Fixed(50.)),
));
self.core.applet.popup_container(content_list).into()
"oops".into()
}
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {

View file

@ -0,0 +1,89 @@
use std::error::Error;
use std::time::Duration;
use std::{collections::HashMap, convert::Infallible};
use cosmic::{app::Message, Task};
use zbus::{export::futures_util::StreamExt, proxy, zvariant::Value, Connection};
#[proxy(
default_service = "org.freedesktop.Notifications",
default_path = "/org/freedesktop/Notifications"
)]
trait Notifications {
/// Call the org.freedesktop.Notifications.Notify D-Bus method
fn notify(
&self,
app_name: &str,
replaces_id: u32,
app_icon: &str,
summary: &str,
body: &str,
actions: &[&str],
hints: HashMap<&str, &Value<'_>>,
expire_timeout: i32,
) -> zbus::Result<u32>;
// We need to define the signal we want to listen for
#[zbus(signal)]
fn action_invoked(&self, id: u32, action_key: String) -> zbus::Result<()>;
// We need to define the signal we want to listen for
#[zbus(signal)]
fn activation_token(&self, id: u32, activation_token: String) -> zbus::Result<()>;
}
pub fn notif_task<M: std::marker::Send + 'static>() -> Task<cosmic::app::Message<M>> {
Task::future(async {
match notif().await {
Ok(t) => t,
Err(err) => {
tracing::error!("Ooops: {err:?}");
panic!("oops");
}
}
})
}
pub async fn notif<M: std::marker::Send + 'static>(
) -> Result<cosmic::app::Message<M>, Box<dyn Error>> {
tokio::time::sleep(Duration::from_secs(2)).await;
let connection = Connection::session().await?;
let proxy = NotificationsProxy::new(&connection).await?;
let mut actions = proxy.receive_action_invoked().await?;
let mut tokens = proxy.receive_activation_token().await?;
let _reply = proxy
.notify(
"my-app",
0,
"dialog-information",
"A summary",
"Some body",
&["default", "asdf"],
HashMap::new(),
5000,
)
.await?;
let token = if let Some(msg) = tokens.next().await {
// struct `JobNewArgs` is generated from `job_new` signal function arguments
let args: ActivationTokenArgs = msg.args().expect("Error parsing message");
println!("action: id={} action={}", args.id, args.activation_token,);
args.activation_token
} else {
eprintln!("no token");
panic!("oops");
};
if let Some(msg) = actions.next().await {
// struct `JobNewArgs` is generated from `job_new` signal function arguments
let args: ActionInvokedArgs = msg.args().expect("Error parsing message");
println!("action: id={} action={}", args.id, args.action_key,);
}
Ok(Message::Cosmic(cosmic::app::cosmic::Message::Activate(
token,
)))
}

2
iced

@ -1 +1 @@
Subproject commit a8c31a0982b134278e7ecb5a91e0d3ba0fa6d2dd
Subproject commit bb5e4f700de09ca821fe0b1641157ad69d0b184c

View file

@ -13,6 +13,8 @@ use slotmap::Key;
use crate::Theme;
use super::{Element, Task};
/// Status of the nav bar and its panels.
#[derive(Clone)]
pub struct NavBar {

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: MPL-2.0
use std::borrow::Borrow;
use std::collections::HashMap;
use std::sync::Arc;
use super::{Application, ApplicationExt, Core, Subscription};
@ -81,8 +82,11 @@ pub enum Message {
}
#[derive(Default)]
pub struct Cosmic<App> {
pub struct Cosmic<App: Application> {
pub app: App,
#[cfg(feature = "wayland")]
pub subsurfaces:
HashMap<window::Id, Box<dyn Fn(&App) -> Element<'static, super::Message<App::Message>>>>,
}
impl<T: Application> Cosmic<T>
@ -123,6 +127,20 @@ where
super::Message::None => iced::Task::none(),
#[cfg(feature = "single-instance")]
super::Message::DbusActivation(message) => self.app.dbus_activation(message),
#[cfg(feature = "wayland")]
super::Message::Subsurface(settings, view) => {
if let Some(view) = view.and_then(|view| match view.downcast() {
Ok(v) => Some(v),
Err(err) => {
tracing::error!("Invalid view for subsurface view: {err:?}");
None
}
}) {
self.get_subsurface(settings, view)
} else {
iced_winit::commands::subsurface::get_subsurface(settings)
}
}
};
#[cfg(target_env = "gnu")]
@ -287,6 +305,10 @@ where
#[cfg(feature = "multi-window")]
pub fn view(&self, id: window::Id) -> Element<super::Message<T::Message>> {
#[cfg(feature = "wayland")]
if let Some(v) = self.subsurfaces.get(&id) {
return v(&self.app);
}
if !self
.app
.core()
@ -697,6 +719,23 @@ impl<T: Application> Cosmic<T> {
impl<App: Application> Cosmic<App> {
pub fn new(app: App) -> Self {
Self { app }
Self {
app,
#[cfg(feature = "wayland")]
subsurfaces: HashMap::new(),
}
}
#[cfg(feature = "wayland")]
/// Create a subsurface
pub fn get_subsurface(
&mut self,
settings: iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings,
view: Box<dyn Fn(&App) -> Element<'static, super::Message<App::Message>>>,
) -> Task<super::Message<App::Message>> {
use iced_winit::commands::subsurface::get_subsurface;
self.subsurfaces.insert(settings.id, view);
get_subsurface(settings)
}
}

View file

@ -27,6 +27,12 @@ pub mod message {
DbusActivation(super::DbusActivationMessage),
/// Do nothing
None,
#[cfg(feature = "wayland")]
/// Create a subsurface with a view function
Subsurface(
iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings,
Option<Box<dyn std::any::Any>>,
),
}
pub const fn app<M>(message: M) -> Message<M> {

View file

@ -308,6 +308,7 @@ impl Context {
},
parent_size: None,
grab: true,
popup_view: None,
}
}