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

feat: show popup on active notifications applet output #175

Closed
wants to merge 1 commit into from
Closed
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
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion cosmic-app-list/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::config::{APP_ID, VERSION};
fn main() -> cosmic::iced::Result {
// Initialize logger
pretty_env_logger::init();
info!("Iced Workspaces Applet ({})", APP_ID);
info!("Iced Dock Applet ({})", APP_ID);
info!("Version: {}", VERSION);
// Prepare i18n
localize();
Expand Down
3 changes: 3 additions & 0 deletions cosmic-applet-notifications/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ i18n-embed = { version = "0.13.4", features = [
i18n-embed-fl = "0.6.4"
rust-embed = "6.3.0"
rust-embed-utils = "7.5.0"
cctk.workspace = true
cosmic-protocols.workspace = true
url = "2.4.0"
futures = "0.3.21"
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ Icon=com.system76.CosmicAppletNotifications
NoDisplay=true
X-CosmicApplet=true
X-NotificationsApplet=true
X-HostWaylandDisplay=true
165 changes: 135 additions & 30 deletions cosmic-applet-notifications/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
mod localize;
mod subscriptions;
use cosmic::applet::token::subscription::{
activation_token_subscription, TokenRequest, TokenUpdate,
};
mod wayland_handler;
mod wayland_subscription;
use cosmic::applet::{menu_button, menu_control_padding, padded_control};
use cosmic::cctk::sctk::reexports::calloop;
use cosmic::cosmic_config::{config_subscription, Config, CosmicConfigEntry};
Expand All @@ -27,7 +26,9 @@
use std::collections::HashMap;
use std::path::PathBuf;
use tokio::sync::mpsc::Sender;
use tokio::time::Duration;
use tracing::info;
use wayland_subscription::{WaylandRequest, WaylandUpdate};

#[tokio::main(flavor = "current_thread")]
pub async fn main() -> cosmic::iced::Result {
Expand All @@ -42,18 +43,25 @@

static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);

#[derive(Debug, Clone)]
pub enum Popup {
NewNotifications(Vec<u32>),
UserActivated,
}

#[derive(Default)]
struct Notifications {
core: cosmic::app::Core,
config: NotificationsConfig,
config_helper: Option<Config>,
icon_name: String,
popup: Option<window::Id>,
popup: Option<(window::Id, Popup)>,
// notifications: Vec<Notification>,
timeline: Timeline,
dbus_sender: Option<Sender<subscriptions::dbus::Input>>,
cards: Vec<(id::Cards, Vec<Notification>, bool, String, String, String)>,
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
wayland_tx: Option<calloop::channel::Sender<WaylandRequest>>,
on_active_output: bool,
}

impl Notifications {
Expand Down Expand Up @@ -93,8 +101,9 @@
Dismissed(u32),
ClearAll(String),
CardsToggled(String, bool),
Token(TokenUpdate),
OpenSettings,
WaylandUpdate(wayland_subscription::WaylandUpdate),
Timeout(u32),
}

impl cosmic::Application for Notifications {
Expand Down Expand Up @@ -131,6 +140,7 @@
core,
config_helper: helper,
config,
on_active_output: false,
..Default::default()
};
_self.update_icon();
Expand Down Expand Up @@ -170,7 +180,7 @@
.map(|(_, now)| Message::Frame(now)),
subscriptions::dbus::proxy().map(Message::DbusEvent),
subscriptions::notifications::notifications().map(Message::NotificationEvent),
activation_token_subscription(0).map(Message::Token),
wayland_subscription::wayland_subscription(0).map(Message::WaylandUpdate),
])
}

Expand All @@ -183,11 +193,11 @@
self.timeline.now(now);
}
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
if let Some((p, _popup_type)) = self.popup.take() {
return destroy_popup(p);
} else {
let new_id = window::Id::unique();
self.popup.replace(new_id);
self.popup.replace((new_id, Popup::UserActivated));

let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
Expand All @@ -214,6 +224,9 @@
}
}
Message::NotificationEvent(n) => {
let id = n.id;
let timeout = n.expire_timeout;
let urgency = n.urgency();
if let Some(c) = self
.cards
.iter_mut()
Expand All @@ -238,12 +251,66 @@
fl!("clear-all"),
));
}
if self.on_active_output {
// create new notification popup if none exists
let timeout_instant = if urgency == 2 {
if timeout > 0 {
Some(
tokio::time::Instant::now() + Duration::from_millis(timeout as u64),
)
} else {
None
}
} else {
if timeout > 0 {
Some(
tokio::time::Instant::now()
+ Duration::from_millis(timeout.max(10000) as u64),
)
} else {
Some(tokio::time::Instant::now() + Duration::from_millis(5000))
}
};

Check warning on line 273 in cosmic-applet-notifications/src/main.rs

View workflow job for this annotation

GitHub Actions / linting

this `else { if .. }` block can be collapsed

warning: this `else { if .. }` block can be collapsed --> cosmic-applet-notifications/src/main.rs:264:28 | 264 | } else { | ____________________________^ 265 | | if timeout > 0 { 266 | | Some( 267 | | tokio::time::Instant::now() ... | 272 | | } 273 | | }; | |_____________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if = note: `#[warn(clippy::collapsible_else_if)]` on by default help: collapse nested if block | 264 ~ } else if timeout > 0 { 265 + Some( 266 + tokio::time::Instant::now() 267 + + Duration::from_millis(timeout.max(10000) as u64), 268 + ) 269 + } else { 270 + Some(tokio::time::Instant::now() + Duration::from_millis(5000)) 271 ~ }; |

Check warning on line 273 in cosmic-applet-notifications/src/main.rs

View workflow job for this annotation

GitHub Actions / linting

this `else { if .. }` block can be collapsed

warning: this `else { if .. }` block can be collapsed --> cosmic-applet-notifications/src/main.rs:264:28 | 264 | } else { | ____________________________^ 265 | | if timeout > 0 { 266 | | Some( 267 | | tokio::time::Instant::now() ... | 272 | | } 273 | | }; | |_____________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if = note: `#[warn(clippy::collapsible_else_if)]` on by default help: collapse nested if block | 264 ~ } else if timeout > 0 { 265 + Some( 266 + tokio::time::Instant::now() 267 + + Duration::from_millis(timeout.max(10000) as u64), 268 + ) 269 + } else { 270 + Some(tokio::time::Instant::now() + Duration::from_millis(5000)) 271 ~ }; |
let mut cmds = if let Some(timeout_instant) = timeout_instant {
vec![Command::perform(
async move {
tokio::time::sleep_until(timeout_instant).await;
Message::Timeout(id)
},
|msg| msg,
)
.map(cosmic::app::Message::App)]
} else {
Vec::new()
};
if let Some((_, Popup::NewNotifications(notifs))) = &mut self.popup {
notifs.push(id);
} else {
let new_id = window::Id::unique();
self.popup
.replace((new_id, Popup::NewNotifications(vec![id])));

let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
new_id,
None,
None,
None,
);
popup_settings.positioner.size_limits = Limits::NONE
.min_width(1.0)
.max_width(444.0)
.min_height(100.0)
.max_height(900.0);
cmds.push(get_popup(popup_settings));
}
return Command::batch(cmds);
}
}
Message::Config(config) => {
self.config = config;
}
Message::Dismissed(id) => {
info!("Dismissed {}", id);
for c in &mut self.cards {
c.1.retain(|n| n.id != id);
}
Expand All @@ -257,6 +324,14 @@
}
});
}

if let Some((p_id, Popup::NewNotifications(mut notifs))) = self.popup.take() {
notifs.retain(|n| n != &id);
if notifs.is_empty() {
self.popup = None;
return destroy_popup(p_id);
}
}
}
Message::DbusEvent(e) => match e {
subscriptions::dbus::Output::Ready(tx) => {
Expand Down Expand Up @@ -307,27 +382,27 @@
self.update_cards(id);
}
Message::CloseRequested(id) => {
if Some(id) == self.popup {
if Some(id) == self.popup.as_ref().map(|(id, _)| *id) {
self.popup = None;
}
}
Message::OpenSettings => {
let exec = "cosmic-settings notifications".to_string();
if let Some(tx) = self.token_tx.as_ref() {
let _ = tx.send(TokenRequest {
if let Some(tx) = self.wayland_tx.as_ref() {
let _ = tx.send(WaylandRequest::TokenRequest {
app_id: Self::APP_ID.to_string(),
exec,
});
}
}
Message::Token(u) => match u {
TokenUpdate::Init(tx) => {
self.token_tx = Some(tx);
Message::WaylandUpdate(u) => match u {
WaylandUpdate::Init(tx) => {
self.wayland_tx = Some(tx);
}
TokenUpdate::Finished => {
self.token_tx = None;
WaylandUpdate::Finished => {
self.wayland_tx = None;
}
TokenUpdate::ActivationToken { token, .. } => {
WaylandUpdate::ActivationToken { token, .. } => {
let mut cmd = std::process::Command::new("cosmic-settings");
cmd.arg("notifications");
if let Some(token) = token {
Expand All @@ -336,7 +411,22 @@
}
cosmic::process::spawn(cmd);
}
WaylandUpdate::ActiveOutput(active) => {
info!("is output active {:?}", active);
self.on_active_output = active;
}
},
Message::Timeout(t) => {
if let Some((id, Popup::NewNotifications(mut notifs))) = self.popup.take() {
notifs.retain(|n| n != &t);
if notifs.is_empty() {
self.popup = None;
return destroy_popup(id);
} else {
self.popup = Some((id, Popup::NewNotifications(notifs)));
}
}
}
};
self.update_icon();
Command::none()
Expand Down Expand Up @@ -387,6 +477,13 @@
.1
.iter()
.rev()
.filter(|n| {
if let Some((_, Popup::NewNotifications(ids))) = &self.popup {
ids.contains(&n.id)
} else {
true
}
})
.map(|n| {
let app_name = text(if n.app_name.len() > 24 {
Cow::from(format!(
Expand Down Expand Up @@ -465,7 +562,6 @@
})
.collect();
let show_more_icon = c.1.last().and_then(|n| {
info!("app_icon: {:?}", &n.app_icon);
if n.app_icon.is_empty() {
match n.image().cloned() {
Some(Image::File(p)) => Some(cosmic::widget::icon::from_path(p)),
Expand Down Expand Up @@ -506,24 +602,33 @@
notifs.push(card_list.into());
}

row!(scrollable(
let ret = row!(scrollable(
Column::with_children(notifs)
.spacing(8)
.height(Length::Shrink),
)
.height(Length::Shrink))
.padding(menu_control_padding())
};
.padding(menu_control_padding());

let main_content = column![
padded_control(divider::horizontal::default()),
notifications,
padded_control(divider::horizontal::default())
];
ret
};

let content = column![do_not_disturb, main_content, settings]
let content: Element<_> = if matches!(&self.popup, Some((_, Popup::NewNotifications(_)))) {
container(notifications).padding([8, 0]).into()
} else {
column![
do_not_disturb,
column![
padded_control(divider::horizontal::default()),
notifications,
padded_control(divider::horizontal::default())
],
settings
]
.align_items(Alignment::Start)
.padding([8, 0]);
.padding([8, 0])
.into()
};

self.core.applet.popup_container(content).into()
}
Expand Down
Loading
Loading