Merge branch 'pop-os:master' into flags-not-clone

This commit is contained in:
wiiznokes 2023-11-24 17:38:33 +01:00 • committed by GitHub
commit 85bec29de5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 91 additions and 92 deletions

View file

@ -128,6 +128,7 @@ optional = true
[dependencies.taffy] [dependencies.taffy]
git = "https://github.com/DioxusLabs/taffy" git = "https://github.com/DioxusLabs/taffy"
rev = "7781c70"
features = ["grid"] features = ["grid"]
[workspace] [workspace]

View file

@ -8,7 +8,7 @@ publish = false
[dependencies] [dependencies]
apply = "0.3.0" apply = "0.3.0"
fraction = "0.13.0" fraction = "0.13.0"
libcosmic = { path = "../..", features = ["debug", "winit", "tokio"] } libcosmic = { path = "../..", features = ["debug", "winit", "tokio", "single-instance"] }
once_cell = "1.18" once_cell = "1.18"
slotmap = "1.0.6" slotmap = "1.0.6"
env_logger = "0.10" env_logger = "0.10"

2
iced

@ -1 +1 @@
Subproject commit 94f8772b2f05195506f7a454e41290afe02eaa40 Subproject commit b3ede4f9a72275cfeb29fac80a31546f728783fd

View file

@ -63,7 +63,8 @@ pub struct Core {
#[cfg(feature = "applet")] #[cfg(feature = "applet")]
pub applet: crate::applet::Context, pub applet: crate::applet::Context,
pub single_instance: bool, #[cfg(feature = "single-instance")]
pub(crate) single_instance: bool,
} }
impl Default for Core { impl Default for Core {
@ -106,6 +107,7 @@ impl Default for Core {
}, },
#[cfg(feature = "applet")] #[cfg(feature = "applet")]
applet: crate::applet::Context::default(), applet: crate::applet::Context::default(),
#[cfg(feature = "single-instance")]
single_instance: false, single_instance: false,
} }
} }

View file

@ -97,6 +97,8 @@ where
super::Message::App(message) => self.app.update(message), super::Message::App(message) => self.app.update(message),
super::Message::Cosmic(message) => self.cosmic_update(message), super::Message::Cosmic(message) => self.cosmic_update(message),
super::Message::None => iced::Command::none(), super::Message::None => iced::Command::none(),
#[cfg(feature = "single-instance")]
super::Message::DbusActivation(message) => self.app.dbus_activation(message),
} }
} }
@ -184,7 +186,7 @@ where
.core() .core()
.single_instance .single_instance
.then(|| super::single_instance_subscription::<T>()) .then(|| super::single_instance_subscription::<T>())
.unwrap_or_else(Subscription::none), .unwrap_or_else(|| Subscription::none()),
]) ])
} }
@ -364,11 +366,12 @@ impl<T: Application> Cosmic<T> {
}); });
} }
} }
Message::Activate(token) => { Message::Activate(_token) => {
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
return iced_sctk::commands::activation::activate( return iced_sctk::commands::activation::activate(
iced::window::Id::default(), iced::window::Id::default(),
token, #[allow(clippy::used_underscore_binding)]
_token,
); );
} }
} }

View file

@ -19,6 +19,9 @@ pub mod message {
App(M), App(M),
/// Internal messages to be handled by libcosmic. /// Internal messages to be handled by libcosmic.
Cosmic(super::cosmic::Message), Cosmic(super::cosmic::Message),
#[cfg(feature = "single-instance")]
/// Dbus activation messages
DbusActivation(super::DbusActivationMessage),
/// Do nothing /// Do nothing
None, None,
} }
@ -36,8 +39,6 @@ pub mod message {
} }
} }
use std::str::FromStr;
pub use self::command::Command; pub use self::command::Command;
pub use self::core::Core; pub use self::core::Core;
pub use self::settings::Settings; pub use self::settings::Settings;
@ -57,12 +58,11 @@ use {
std::collections::HashMap, std::collections::HashMap,
zbus::{dbus_interface, dbus_proxy, zvariant::Value}, zbus::{dbus_interface, dbus_proxy, zvariant::Value},
}; };
/// Launch a COSMIC application with the given [`Settings`].
/// pub(crate) fn iced_settings<App: Application>(
/// # Errors settings: Settings,
/// flags: App::Flags,
/// Returns error on application failure. ) -> iced::Settings<(Core, App::Flags)> {
pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result {
if let Some(icon_theme) = settings.default_icon_theme { if let Some(icon_theme) = settings.default_icon_theme {
crate::icon_theme::set_default(icon_theme); crate::icon_theme::set_default(icon_theme);
} }
@ -72,7 +72,6 @@ pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Res
core.set_scale_factor(settings.scale_factor); core.set_scale_factor(settings.scale_factor);
core.set_window_width(settings.size.0); core.set_window_width(settings.size.0);
core.set_window_height(settings.size.1); core.set_window_height(settings.size.1);
core.single_instance = settings.single_instance;
THEME.with(move |t| { THEME.with(move |t| {
let mut cosmic_theme = t.borrow_mut(); let mut cosmic_theme = t.borrow_mut();
@ -120,8 +119,20 @@ pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Res
iced.window.transparent = settings.transparent; iced.window.transparent = settings.transparent;
} }
cosmic::Cosmic::<App>::run(iced) iced
} }
/// Launch a COSMIC application with the given [`Settings`].
///
/// # Errors
///
/// Returns error on application failure.
pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result {
let settings = iced_settings::<App>(settings, flags);
cosmic::Cosmic::<App>::run(settings)
}
#[cfg(feature = "single-instance")] #[cfg(feature = "single-instance")]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DbusActivationMessage<Action = String, Args = Vec<String>> { pub struct DbusActivationMessage<Action = String, Args = Vec<String>> {
@ -160,7 +171,7 @@ impl DbusActivation {
} }
#[cfg(feature = "single-instance")] #[cfg(feature = "single-instance")]
#[dbus_proxy(interface = "org.freedesktop.DbusActivation")] #[dbus_proxy(interface = "org.freedesktop.DbusActivation", assume_defaults = true)]
pub trait DbusActivationInterface { pub trait DbusActivationInterface {
/// Activate the application. /// Activate the application.
fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) -> zbus::Result<()>; fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) -> zbus::Result<()>;
@ -182,7 +193,7 @@ pub trait DbusActivationInterface {
} }
#[cfg(feature = "single-instance")] #[cfg(feature = "single-instance")]
#[dbus_interface(interface = "org.freedesktop.DbusActivation")] #[dbus_interface(name = "org.freedesktop.DbusActivation")]
impl DbusActivation { impl DbusActivation {
async fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) { async fn activate(&mut self, platform_data: HashMap<&str, Value<'_>>) {
if let Some(tx) = &mut self.0 { if let Some(tx) = &mut self.0 {
@ -259,16 +270,16 @@ impl DbusActivation {
} }
#[cfg(feature = "single-instance")] #[cfg(feature = "single-instance")]
/// Launch a COSMIC application with the given [`Settings`]. /// Launch a COSMIC application with the given [`Settings`].
/// If the application is already running, the arguments will be passed to the /// If the application is already running, the arguments will be passed to the
/// running instance. /// running instance.
/// # Errors /// # Errors
/// Returns error on application failure. /// Returns error on application failure.
pub fn run_single_instance<App: Application>( pub fn run_single_instance<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result
mut settings: Settings, where
flags: App::Flags, App::Flags: CosmicFlags + Clone,
) -> iced::Result { App::Message: Clone + std::fmt::Debug + Send + 'static,
{
let activation_token = std::env::var("XDG_ACTIVATION_TOKEN").ok(); let activation_token = std::env::var("XDG_ACTIVATION_TOKEN").ok();
let override_single = std::env::var("COSMIC_SINGLE_INSTANCE") let override_single = std::env::var("COSMIC_SINGLE_INSTANCE")
@ -279,7 +290,6 @@ pub fn run_single_instance<App: Application>(
} }
let path: String = format!("/{}", App::APP_ID.replace('.', "/")); let path: String = format!("/{}", App::APP_ID.replace('.', "/"));
settings.single_instance = true;
let Ok(conn) = zbus::blocking::Connection::session() else { let Ok(conn) = zbus::blocking::Connection::session() else {
tracing::warn!("Failed to connect to dbus"); tracing::warn!("Failed to connect to dbus");
@ -322,13 +332,15 @@ pub fn run_single_instance<App: Application>(
tracing::info!("Another instance is running"); tracing::info!("Another instance is running");
Ok(()) Ok(())
} else { } else {
run::<App>(settings, flags) let mut settings = iced_settings::<App>(settings, flags);
settings.flags.0.single_instance = true;
cosmic::Cosmic::<App>::run(settings)
} }
} }
pub trait CosmicFlags { pub trait CosmicFlags {
type SubCommand: FromStr + ToString + std::fmt::Debug + Clone + Send + 'static; type SubCommand: ToString + std::fmt::Debug + Clone + Send + 'static;
type Args: TryFrom<Vec<String>> + Into<Vec<String>> + std::fmt::Debug + Clone + Send + 'static; type Args: Into<Vec<String>> + std::fmt::Debug + Clone + Send + 'static;
#[must_use] #[must_use]
fn action(&self) -> Option<&Self::SubCommand> { fn action(&self) -> Option<&Self::SubCommand> {
None None
@ -349,27 +361,9 @@ where
/// Default async executor to use with the app. /// Default async executor to use with the app.
type Executor: iced_futures::Executor; type Executor: iced_futures::Executor;
#[cfg(feature = "single-instance")]
/// Argument received [`Application::new`].
type Flags: Clone + CosmicFlags;
#[cfg(not(feature = "single-instance"))]
/// Argument received [`Application::new`]. /// Argument received [`Application::new`].
type Flags; type Flags;
#[cfg(feature = "single-instance")]
/// Message type specific to our app.
type Message: Clone
+ From<
DbusActivationDetails<
<Self::Flags as CosmicFlags>::SubCommand,
<Self::Flags as CosmicFlags>::Args,
>,
> + std::fmt::Debug
+ Send
+ 'static;
#[cfg(not(feature = "single-instance"))]
/// Message type specific to our app. /// Message type specific to our app.
type Message: Clone + std::fmt::Debug + Send + 'static; type Message: Clone + std::fmt::Debug + Send + 'static;
@ -465,6 +459,15 @@ where
fn style(&self) -> Option<<crate::Theme as iced_style::application::StyleSheet>::Style> { fn style(&self) -> Option<<crate::Theme as iced_style::application::StyleSheet>::Style> {
None None
} }
/// Handles dbus activation messages
#[cfg(feature = "single-instance")]
fn dbus_activation(
&mut self,
msg: DbusActivationMessage,
) -> iced::Command<Message<Self::Message>> {
iced::Command::none()
}
} }
/// Methods automatically derived for all types implementing [`Application`]. /// Methods automatically derived for all types implementing [`Application`].
@ -633,7 +636,7 @@ fn single_instance_subscription<App: ApplicationExt>() -> Subscription<Message<A
iced::subscription::channel( iced::subscription::channel(
TypeId::of::<DbusActivation>(), TypeId::of::<DbusActivation>(),
10, 10,
|mut output| async move { move |mut output| async move {
let mut single_instance: DbusActivation = DbusActivation::new(); let mut single_instance: DbusActivation = DbusActivation::new();
let mut rx = single_instance.rx(); let mut rx = single_instance.rx();
if let Ok(builder) = zbus::ConnectionBuilder::session() { if let Ok(builder) = zbus::ConnectionBuilder::session() {
@ -680,39 +683,11 @@ fn single_instance_subscription<App: ApplicationExt>() -> Subscription<Message<A
tracing::error!(?err, "Failed to send message"); tracing::error!(?err, "Failed to send message");
} }
} }
if let Some(msg) = match msg.msg { if let Err(err) = output.send(Message::DbusActivation(msg)).await {
DbusActivationDetails::Activate => {
Some(DbusActivationDetails::Activate)
}
DbusActivationDetails::Open { url } => {
Some(DbusActivationDetails::Open { url })
}
DbusActivationDetails::ActivateAction { action, args } => {
if let (Ok(action), Ok(args)) = (
<App::Flags as CosmicFlags>::SubCommand::from_str(&action),
<App::Flags as CosmicFlags>::Args::try_from(args),
) {
Some(DbusActivationDetails::ActivateAction::<
<App::Flags as CosmicFlags>::SubCommand,
<App::Flags as CosmicFlags>::Args,
> {
action,
args,
})
} else {
tracing::error!("Invalid action or args");
None
}
}
} {
if let Err(err) =
output.send(Message::App(App::Message::from(msg))).await
{
tracing::error!(?err, "Failed to send message"); tracing::error!(?err, "Failed to send message");
} }
} }
} }
}
} else { } else {
tracing::warn!("Failed to connect to dbus for single instance"); tracing::warn!("Failed to connect to dbus for single instance");
} }

View file

@ -62,9 +62,6 @@ pub struct Settings {
/// Whether the application should exit when there are no open windows /// Whether the application should exit when there are no open windows
pub(crate) exit_on_close: bool, pub(crate) exit_on_close: bool,
/// Only allow a single instance of the application to run
pub single_instance: bool,
} }
impl Settings { impl Settings {
@ -100,7 +97,6 @@ impl Default for Settings {
theme: crate::theme::system_preference(), theme: crate::theme::system_preference(),
transparent: false, transparent: false,
exit_on_close: true, exit_on_close: true,
single_instance: false,
} }
} }
} }

View file

@ -9,7 +9,7 @@ use iced_core::{Alignment, Length, Padding, Point, Size};
use taffy::geometry::{Line, Rect}; use taffy::geometry::{Line, Rect};
use taffy::style::{AlignItems, Dimension, Display, GridPlacement, Style}; use taffy::style::{AlignItems, Dimension, Display, GridPlacement, Style};
use taffy::style_helpers::{auto, length}; use taffy::style_helpers::{auto, length};
use taffy::Taffy; use taffy::TaffyTree;
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub fn resolve<Message>( pub fn resolve<Message>(
@ -30,7 +30,7 @@ pub fn resolve<Message>(
let mut leafs = Vec::with_capacity(items.len()); let mut leafs = Vec::with_capacity(items.len());
let mut nodes = Vec::with_capacity(items.len()); let mut nodes = Vec::with_capacity(items.len());
let mut taffy = Taffy::<()>::with_capacity(items.len() + 1); let mut taffy = TaffyTree::<()>::with_capacity(items.len() + 1);
// Attach widgets as child nodes. // Attach widgets as child nodes.
for (child, assignment) in items.iter().zip(assignments.iter()) { for (child, assignment) in items.iter().zip(assignments.iter()) {

View file

@ -9,7 +9,9 @@ use iced_core::mouse;
use iced_core::overlay; use iced_core::overlay;
use iced_core::renderer; use iced_core::renderer;
use iced_core::widget::{Operation, OperationOutputWrapper, Tree}; use iced_core::widget::{Operation, OperationOutputWrapper, Tree};
use iced_core::{Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Widget}; use iced_core::{
Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget,
};
use std::cell::RefCell; use std::cell::RefCell;
pub use iced_style::container::{Appearance, StyleSheet}; pub use iced_style::container::{Appearance, StyleSheet};
@ -25,19 +27,26 @@ pub struct Popover<'a, Message, Renderer> {
content: Element<'a, Message, Renderer>, content: Element<'a, Message, Renderer>,
// XXX Avoid refcell; improve iced overlay API? // XXX Avoid refcell; improve iced overlay API?
popup: RefCell<Element<'a, Message, Renderer>>, popup: RefCell<Element<'a, Message, Renderer>>,
position: Option<Point>,
} }
impl<'a, Message, Renderer> Popover<'a, Message, Renderer> { impl<'a, Message, Renderer> Popover<'a, Message, Renderer> {
fn new( pub fn new(
content: impl Into<Element<'a, Message, Renderer>>, content: impl Into<Element<'a, Message, Renderer>>,
popup: impl Into<Element<'a, Message, Renderer>>, popup: impl Into<Element<'a, Message, Renderer>>,
) -> Self { ) -> Self {
Self { Self {
content: content.into(), content: content.into(),
popup: RefCell::new(popup.into()), popup: RefCell::new(popup.into()),
position: None,
} }
} }
pub fn position(mut self, position: Point) -> Self {
self.position = Some(position);
self
}
// TODO More options for positioning similar to GdkPopup, xdg_popup // TODO More options for positioning similar to GdkPopup, xdg_popup
} }
@ -145,9 +154,20 @@ where
layout: Layout<'_>, layout: Layout<'_>,
_renderer: &Renderer, _renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> { ) -> Option<overlay::Element<'b, Message, Renderer>> {
// Set position to center of bottom edge
let bounds = layout.bounds(); let bounds = layout.bounds();
let position = Point::new(bounds.x + bounds.width / 2.0, bounds.y + bounds.height); let (position, centered) = match self.position {
Some(relative) => (
bounds.position() + Vector::new(relative.x, relative.y),
false,
),
None => {
// Set position to center of bottom edge
(
Point::new(bounds.x + bounds.width / 2.0, bounds.y + bounds.height),
true,
)
}
};
// XXX needed to use RefCell to get &mut for popup element // XXX needed to use RefCell to get &mut for popup element
Some(overlay::Element::new( Some(overlay::Element::new(
@ -155,6 +175,7 @@ where
Box::new(Overlay { Box::new(Overlay {
tree: &mut tree.children[1], tree: &mut tree.children[1],
content: &self.popup, content: &self.popup,
centered,
}), }),
)) ))
} }
@ -171,9 +192,10 @@ where
} }
} }
struct Overlay<'a, 'b, Message, Renderer> { pub struct Overlay<'a, 'b, Message, Renderer> {
tree: &'a mut Tree, tree: &'a mut Tree,
content: &'a RefCell<Element<'b, Message, Renderer>>, content: &'a RefCell<Element<'b, Message, Renderer>>,
centered: bool,
} }
impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer>
@ -182,13 +204,13 @@ where
Renderer: iced_core::Renderer, Renderer: iced_core::Renderer,
{ {
fn layout(&self, renderer: &Renderer, bounds: Size, mut position: Point) -> layout::Node { fn layout(&self, renderer: &Renderer, bounds: Size, mut position: Point) -> layout::Node {
// Position is set to the center bottom of the lower widget
let limits = layout::Limits::new(Size::UNIT, bounds); let limits = layout::Limits::new(Size::UNIT, bounds);
let mut node = self.content.borrow().as_widget().layout(renderer, &limits); let mut node = self.content.borrow().as_widget().layout(renderer, &limits);
if self.centered {
// Position is set to the center bottom of the lower widget
let width = node.size().width; let width = node.size().width;
position.x = (position.x - width / 2.0).clamp(0.0, bounds.width - width); position.x = (position.x - width / 2.0).clamp(0.0, bounds.width - width);
}
node.move_to(position); node.move_to(position);
node node