update iced

This commit is contained in:
Ashley Wulber 2024-10-30 22:51:08 -04:00 committed by Ashley Wulber
parent 11faa567f3
commit 5b5cd77e7c
45 changed files with 2360 additions and 1537 deletions

1687
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -28,11 +28,14 @@ cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", default
"client", "client",
], rev = "c8d3a1c" } ], rev = "c8d3a1c" }
cosmic-settings-subscriptions = { git = "https://github.com/pop-os/cosmic-settings-subscriptions" } cosmic-settings-subscriptions = { git = "https://github.com/pop-os/cosmic-settings-subscriptions" }
# cosmic-settings-subscriptions = { path = "../cosmic-settings-subscriptions" }
cosmic-time = { git = "https://github.com/pop-os/cosmic-time", default-features = false, features = [ cosmic-time = { git = "https://github.com/pop-os/cosmic-time", default-features = false, features = [
"libcosmic",
"once_cell", "once_cell",
] } ] }
# cosmic-time = { path = "../cosmic-time", default-features = false, features = [
# "once_cell",
# ] }
futures = "0.3" futures = "0.3"
futures-util = "0.3" futures-util = "0.3"
@ -44,11 +47,14 @@ i18n-embed-fl = "0.8"
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = [ libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false, features = [
"applet", "applet",
"applet-token", "applet-token",
"multi-window",
"tokio", "tokio",
"wayland", "wayland",
"desktop-systemd-scope", "desktop-systemd-scope",
"winit",
"dbus-config", "dbus-config",
] } ] }
once_cell = "1"
rust-embed = "8.3" rust-embed = "8.3"
rust-embed-utils = "8.3.0" rust-embed-utils = "8.3.0"
rustix = { version = "0.38", features = ["fs", "process"] } rustix = { version = "0.38", features = ["fs", "process"] }
@ -63,8 +69,15 @@ freedesktop-desktop-entry = "0.7.5"
[profile.release] [profile.release]
lto = "fat" lto = "fat"
[workspace.metadata.cargo-machete] [workspace.metadata.cargo-machete]
ignored = ["libcosmic"] ignored = ["libcosmic"]
# [patch."https://github.com/pop-os/libcosmic"] # [patch."https://github.com/pop-os/libcosmic"]
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//" } # cosmic-config = { git = "https://github.com/pop-os/libcosmic//" }
# libcosmic = { git = "https://github.com/pop-os/libcosmic//" } # libcosmic = { git = "https://github.com/pop-os/libcosmic//" }
# cosmic-config = { path = "../libcosmic/cosmic-config" }
# libcosmic = { path = "../libcosmic" }
# [patch."https://github.com/pop-os/winit.git"]
# winit = { git = "https://github.com/rust-windowing/winit.git", rev = "241b7a80bba96c91fa3901729cd5dec66abb9be4" }
# winit = { path = "../winit" }

File diff suppressed because it is too large Load diff

View file

@ -7,7 +7,10 @@ use cctk::{
toplevel_info::ToplevelInfo, toplevel_info::ToplevelInfo,
wayland_client::protocol::wl_output::WlOutput, wayland_client::protocol::wl_output::WlOutput,
}; };
use cosmic::{iced, iced::subscription}; use cosmic::{
iced::{self, stream, Subscription},
iced_core::image::Bytes,
};
use cosmic_protocols::{ use cosmic_protocols::{
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1, workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
@ -19,7 +22,7 @@ use futures::{
SinkExt, StreamExt, SinkExt, StreamExt,
}; };
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::{fmt::Debug, sync::Arc}; use std::fmt::Debug;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use crate::wayland_handler::wayland_handler; use crate::wayland_handler::wayland_handler;
@ -28,16 +31,15 @@ pub static WAYLAND_RX: Lazy<Mutex<Option<UnboundedReceiver<WaylandUpdate>>>> =
Lazy::new(|| Mutex::new(None)); Lazy::new(|| Mutex::new(None));
pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> { pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> {
subscription::channel( Subscription::run_with_id(
std::any::TypeId::of::<WaylandUpdate>(), std::any::TypeId::of::<WaylandUpdate>(),
50, stream::channel(50, move |mut output| async move {
move |mut output| async move {
let mut state = State::Waiting; let mut state = State::Waiting;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}, }),
) )
} }
@ -48,12 +50,19 @@ pub enum State {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct WaylandImage { pub struct WaylandImage {
pub img: Arc<image::RgbaImage>, pub img: Bytes,
pub width: u32,
pub height: u32,
} }
impl WaylandImage { impl WaylandImage {
pub fn new(img: image::RgbaImage) -> Self { pub fn new(img: image::RgbaImage) -> Self {
Self { img: Arc::new(img) } Self {
// TODO avoid copy?
img: Bytes::copy_from_slice(img.as_bytes()),
width: img.width(),
height: img.height(),
}
} }
} }

View file

@ -9,7 +9,7 @@ use std::time::Duration;
use crate::{localize::localize, pulse::DeviceInfo}; use crate::{localize::localize, pulse::DeviceInfo};
use config::AudioAppletConfig; use config::AudioAppletConfig;
use cosmic::{ use cosmic::{
app::Command, app,
applet::{ applet::{
cosmic_panel_config::PanelAnchor, cosmic_panel_config::PanelAnchor,
menu_button, menu_control_padding, padded_control, menu_button, menu_control_padding, padded_control,
@ -24,15 +24,14 @@ use cosmic::{
window, Alignment, Length, Limits, Subscription, window, Alignment, Length, Limits, Subscription,
}, },
iced_runtime::core::alignment::Horizontal, iced_runtime::core::alignment::Horizontal,
iced_style::application,
theme, theme,
widget::{button, divider, horizontal_space, icon, text, Column, Row}, widget::{button, divider, horizontal_space, icon, text, Column, Row},
Element, Renderer, Theme, Element, Renderer, Task, Theme,
}; };
use cosmic_settings_subscriptions::pulse as sub_pulse; use cosmic_settings_subscriptions::pulse as sub_pulse;
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline}; use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
use iced::{ use iced::{
wayland::popup::{destroy_popup, get_popup}, platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::container, widget::container,
}; };
use libpulse_binding::volume::Volume; use libpulse_binding::volume::Volume;
@ -52,7 +51,7 @@ const PLAY: &str = "media-playback-start-symbolic";
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
localize(); localize();
cosmic::applet::run::<Audio>(true, ()) cosmic::applet::run::<Audio>(())
} }
#[derive(Default)] #[derive(Default)]
@ -208,10 +207,10 @@ impl Audio {
Some(match self.core.applet.anchor { Some(match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => Column::with_children(elements) PanelAnchor::Left | PanelAnchor::Right => Column::with_children(elements)
.align_items(Alignment::Center) .align_x(Alignment::Center)
.into(), .into(),
PanelAnchor::Top | PanelAnchor::Bottom => Row::with_children(elements) PanelAnchor::Top | PanelAnchor::Bottom => Row::with_children(elements)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.into(), .into(),
}) })
} else { } else {
@ -225,7 +224,7 @@ impl Audio {
Some( Some(
button::icon(icon::from_name(GO_BACK).size(icon_size).symbolic(true)) button::icon(icon::from_name(GO_BACK).size(icon_size).symbolic(true))
.extra_small() .extra_small()
.style(cosmic::theme::Button::AppletIcon) .class(cosmic::theme::Button::AppletIcon)
.on_press(Message::MprisRequest(MprisRequest::Previous)) .on_press(Message::MprisRequest(MprisRequest::Previous))
.into(), .into(),
) )
@ -241,7 +240,7 @@ impl Audio {
Some( Some(
button::icon(icon::from_name(GO_NEXT).size(icon_size).symbolic(true)) button::icon(icon::from_name(GO_NEXT).size(icon_size).symbolic(true))
.extra_small() .extra_small()
.style(cosmic::theme::Button::AppletIcon) .class(cosmic::theme::Button::AppletIcon)
.on_press(Message::MprisRequest(MprisRequest::Next)) .on_press(Message::MprisRequest(MprisRequest::Next))
.into(), .into(),
) )
@ -292,7 +291,7 @@ impl cosmic::Application for Audio {
type Flags = (); type Flags = ();
const APP_ID: &'static str = "com.system76.CosmicAppletAudio"; const APP_ID: &'static str = "com.system76.CosmicAppletAudio";
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) { fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Task<Message>) {
( (
Self { Self {
core, core,
@ -304,7 +303,7 @@ impl cosmic::Application for Audio {
token_tx: None, token_tx: None,
..Default::default() ..Default::default()
}, },
Command::none(), Task::none(),
) )
} }
@ -316,11 +315,11 @@ impl cosmic::Application for Audio {
&mut self.core &mut self.core
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> app::Task<Message> {
match message { match message {
Message::Frame(now) => self.timeline.now(now), Message::Frame(now) => self.timeline.now(now),
Message::Ignore => {} Message::Ignore => {}
@ -336,7 +335,7 @@ impl cosmic::Application for Audio {
self.timeline = Timeline::new(); self.timeline = Timeline::new();
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, None,
None, None,
@ -360,14 +359,14 @@ impl cosmic::Application for Audio {
} }
Message::SetOutputVolume(vol) => { Message::SetOutputVolume(vol) => {
if self.output_volume == vol { if self.output_volume == vol {
return Command::none(); return Task::none();
} }
self.output_volume = vol; self.output_volume = vol;
self.output_volume_text = format!("{}%", self.output_volume.round()); self.output_volume_text = format!("{}%", self.output_volume.round());
if self.output_volume_debounce { if self.output_volume_debounce {
return Command::none(); return Task::none();
} }
self.output_volume_debounce = true; self.output_volume_debounce = true;
@ -379,14 +378,14 @@ impl cosmic::Application for Audio {
} }
Message::SetInputVolume(vol) => { Message::SetInputVolume(vol) => {
if self.input_volume == vol { if self.input_volume == vol {
return Command::none(); return Task::none();
} }
self.input_volume = vol; self.input_volume = vol;
self.input_volume_text = format!("{}%", self.input_volume.round()); self.input_volume_text = format!("{}%", self.input_volume.round());
if self.input_volume_debounce { if self.input_volume_debounce {
return Command::none(); return Task::none();
} }
self.input_volume_debounce = true; self.input_volume_debounce = true;
@ -583,7 +582,7 @@ impl cosmic::Application for Audio {
Message::MprisRequest(r) => { Message::MprisRequest(r) => {
let Some(player_status) = self.player_status.as_ref() else { let Some(player_status) = self.player_status.as_ref() else {
tracing::error!("No player found"); tracing::error!("No player found");
return Command::none(); return Task::none();
}; };
let player = player_status.player.clone(); let player = player_status.player.clone();
@ -673,7 +672,7 @@ impl cosmic::Application for Audio {
}, },
}; };
Command::none() Task::none()
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
@ -715,28 +714,30 @@ impl cosmic::Application for Audio {
.clamp(0.0, 100.0); .clamp(0.0, 100.0);
Message::SetOutputVolume(new_volume) Message::SetOutputVolume(new_volume)
}); });
let playback_buttons = (!self.core.applet.configure.as_ref().is_some_and(|c| { let playback_buttons = (!self.core.applet.suggested_bounds.as_ref().is_some_and(|c| {
// if we have a configure for width and height, we're in a overflow popup // if we have a configure for width and height, we're in a overflow popup
c.new_size.0.is_some() && c.new_size.1.is_some() c.width > 0. && c.height > 0.
})) }))
.then(|| self.playback_buttons()); .then(|| self.playback_buttons());
if let Some(Some(playback_buttons)) = playback_buttons { self.core
match self.core.applet.anchor { .applet
PanelAnchor::Left | PanelAnchor::Right => { .autosize_window(if let Some(Some(playback_buttons)) = playback_buttons {
Column::with_children(vec![playback_buttons, btn.into()]) match self.core.applet.anchor {
.align_items(Alignment::Center) PanelAnchor::Left | PanelAnchor::Right => Element::from(
.into() Column::with_children(vec![playback_buttons, btn.into()])
.align_x(Alignment::Center),
),
PanelAnchor::Top | PanelAnchor::Bottom => {
Row::with_children(vec![playback_buttons, btn.into()])
.align_y(Alignment::Center)
.into()
}
} }
PanelAnchor::Top | PanelAnchor::Bottom => { } else {
Row::with_children(vec![playback_buttons, btn.into()]) btn.into()
.align_items(Alignment::Center) })
.into() .into()
}
}
} else {
btn.into()
}
} }
fn view_window(&self, _id: window::Id) -> Element<Message> { fn view_window(&self, _id: window::Id) -> Element<Message> {
@ -752,7 +753,7 @@ impl cosmic::Application for Audio {
column![padded_control( column![padded_control(
text::title3(fl!("disconnected")) text::title3(fl!("disconnected"))
.width(Length::Fill) .width(Length::Fill)
.horizontal_alignment(Horizontal::Center) .align_x(Horizontal::Center)
)] )]
} else { } else {
column![ column![
@ -763,7 +764,7 @@ impl cosmic::Application for Audio {
.size(24) .size(24)
.symbolic(true), .symbolic(true),
) )
.style(cosmic::theme::Button::Icon) .class(cosmic::theme::Button::Icon)
.icon_size(24) .icon_size(24)
.line_height(24) .line_height(24)
.on_press(Message::SetOutputMute(!out_mute)), .on_press(Message::SetOutputMute(!out_mute)),
@ -772,10 +773,10 @@ impl cosmic::Application for Audio {
text(&self.output_volume_text) text(&self.output_volume_text)
.size(16) .size(16)
.width(Length::FillPortion(1)) .width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right) .align_x(Horizontal::Right)
] ]
.spacing(12) .spacing(12)
.align_items(Alignment::Center) .align_y(Alignment::Center)
), ),
padded_control( padded_control(
row![ row![
@ -784,7 +785,7 @@ impl cosmic::Application for Audio {
.size(24) .size(24)
.symbolic(true), .symbolic(true),
) )
.style(cosmic::theme::Button::Icon) .class(cosmic::theme::Button::Icon)
.icon_size(24) .icon_size(24)
.line_height(24) .line_height(24)
.on_press(Message::SetInputMute(!in_mute)), .on_press(Message::SetInputMute(!in_mute)),
@ -793,10 +794,10 @@ impl cosmic::Application for Audio {
text(&self.input_volume_text) text(&self.input_volume_text)
.size(16) .size(16)
.width(Length::FillPortion(1)) .width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right) .align_x(Horizontal::Right)
] ]
.spacing(12) .spacing(12)
.align_items(Alignment::Center) .align_y(Alignment::Center)
), ),
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]), padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
revealer( revealer(
@ -836,7 +837,7 @@ impl cosmic::Application for Audio {
Message::InputChanged, Message::InputChanged,
) )
] ]
.align_items(Alignment::Start) .align_x(Alignment::Start)
}; };
if let Some(s) = self.player_status.as_ref() { if let Some(s) = self.player_status.as_ref() {
@ -881,7 +882,7 @@ impl cosmic::Application for Audio {
); );
let mut control_elements = Vec::with_capacity(4); let mut control_elements = Vec::with_capacity(4);
control_elements.push(horizontal_space(Length::Fill).into()); control_elements.push(horizontal_space().width(Length::Fill).into());
if let Some(go_prev) = self.go_previous(32) { if let Some(go_prev) = self.go_previous(32) {
control_elements.push(go_prev); control_elements.push(go_prev);
} }
@ -893,7 +894,7 @@ impl cosmic::Application for Audio {
.symbolic(true), .symbolic(true),
) )
.extra_small() .extra_small()
.style(cosmic::theme::Button::AppletIcon) .class(cosmic::theme::Button::AppletIcon)
.on_press(if play { .on_press(if play {
Message::MprisRequest(MprisRequest::Play) Message::MprisRequest(MprisRequest::Play)
} else { } else {
@ -908,7 +909,7 @@ impl cosmic::Application for Audio {
let control_cnt = control_elements.len() as u16; let control_cnt = control_elements.len() as u16;
elements.push( elements.push(
Row::with_children(control_elements) Row::with_children(control_elements)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.width(Length::FillPortion(control_cnt.saturating_add(1))) .width(Length::FillPortion(control_cnt.saturating_add(1)))
.spacing(8) .spacing(8)
.into(), .into(),
@ -918,7 +919,7 @@ impl cosmic::Application for Audio {
.push(padded_control(divider::horizontal::default()).padding([space_xxs, space_s])); .push(padded_control(divider::horizontal::default()).padding([space_xxs, space_s]));
audio_content = audio_content.push( audio_content = audio_content.push(
Row::with_children(elements) Row::with_children(elements)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(8) .spacing(8)
.padding(menu_control_padding()), .padding(menu_control_padding()),
); );
@ -941,10 +942,14 @@ impl cosmic::Application for Audio {
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]), padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
menu_button(text::body(fl!("sound-settings"))).on_press(Message::OpenSettings) menu_button(text::body(fl!("sound-settings"))).on_press(Message::OpenSettings)
] ]
.align_items(Alignment::Start) .align_x(Alignment::Start)
.padding([8, 0]); .padding([8, 0]);
self.core.applet.popup_container(container(content)).into() self.core
.applet
.popup_container(container(content))
.limits(Limits::NONE.max_width(400.))
.into()
} }
fn on_close_requested(&self, id: window::Id) -> Option<Message> { fn on_close_requested(&self, id: window::Id) -> Option<Message> {

View file

@ -1,6 +1,7 @@
//! A container for capturing mouse events. //! A container for capturing mouse events.
use cosmic::iced_renderer::core::{widget::OperationOutputWrapper, Point}; use cosmic::iced::Vector;
use cosmic::iced_renderer::core::Point;
use cosmic::iced_core::{ use cosmic::iced_core::{
event::{self, Event}, event::{self, Event},
@ -170,7 +171,7 @@ where
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<OperationOutputWrapper<Message>>, operation: &mut dyn Operation<()>,
) { ) {
self.content self.content
.as_widget() .as_widget()
@ -253,17 +254,18 @@ where
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.content self.content
.as_widget_mut() .as_widget_mut()
.overlay(&mut tree.children[0], layout, renderer) .overlay(&mut tree.children[0], layout, renderer, translation)
} }
fn drag_destinations( fn drag_destinations(
&self, &self,
state: &Tree, state: &Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
dnd_rectangles: &mut cosmic::iced_style::core::clipboard::DndDestinationRectangles, dnd_rectangles: &mut cosmic::iced_core::clipboard::DndDestinationRectangles,
) { ) {
if let Some(state) = state.children.iter().next() { if let Some(state) = state.children.iter().next() {
self.content self.content

View file

@ -4,7 +4,7 @@
use std::{borrow::Cow, fmt::Debug, hash::Hash, path::PathBuf}; use std::{borrow::Cow, fmt::Debug, hash::Hash, path::PathBuf};
use cosmic::{ use cosmic::{
iced::{self, subscription}, iced::{self, stream, Subscription},
iced_futures::futures::{self, future::OptionFuture, SinkExt, StreamExt}, iced_futures::futures::{self, future::OptionFuture, SinkExt, StreamExt},
}; };
use mpris2_zbus::{ use mpris2_zbus::{
@ -83,11 +83,14 @@ impl PlayerStatus {
pub fn mpris_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>( pub fn mpris_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
id: I, id: I,
) -> iced::Subscription<MprisUpdate> { ) -> iced::Subscription<MprisUpdate> {
subscription::channel(id, 50, move |mut output| async move { Subscription::run_with_id(
run(&mut output).await; id,
let _ = output.send(MprisUpdate::Finished).await; stream::channel(50, move |mut output| async move {
futures::future::pending().await run(&mut output).await;
}) let _ = output.send(MprisUpdate::Finished).await;
futures::future::pending().await
}),
)
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -6,7 +6,7 @@ use std::{cell::RefCell, mem, rc::Rc, thread};
extern crate libpulse_binding as pulse; extern crate libpulse_binding as pulse;
use cosmic::{ use cosmic::{
iced::{self, subscription}, iced::{self, stream, Subscription},
iced_futures::futures::{self, SinkExt}, iced_futures::futures::{self, SinkExt},
}; };
use cosmic_time::once_cell::sync::Lazy; use cosmic_time::once_cell::sync::Lazy;
@ -31,16 +31,15 @@ pub static FROM_PULSE: Lazy<Mutex<Option<(mpsc::Receiver<Message>, mpsc::Sender<
pub fn connect() -> iced::Subscription<Event> { pub fn connect() -> iced::Subscription<Event> {
struct SomeWorker; struct SomeWorker;
subscription::channel( Subscription::run_with_id(
std::any::TypeId::of::<SomeWorker>(), std::any::TypeId::of::<SomeWorker>(),
50, stream::channel(50, move |mut output| async move {
move |mut output| async move {
let mut state = State::Connecting; let mut state = State::Connecting;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}, }),
) )
} }

View file

@ -14,7 +14,13 @@ i18n-embed.workspace = true
libcosmic.workspace = true libcosmic.workspace = true
once_cell = "1.19.0" once_cell = "1.19.0"
rust-embed.workspace = true rust-embed.workspace = true
tokio = { version = "1.36.0", features = ["sync", "rt", "rt-multi-thread", "fs", "macros"] } tokio = { version = "1.36.0", features = [
"sync",
"rt",
"rt-multi-thread",
"fs",
"macros",
] }
tracing-log.workspace = true tracing-log.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
tracing.workspace = true tracing.workspace = true

View file

@ -20,17 +20,16 @@ use cosmic::{
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced::{ iced::{
alignment::Horizontal, alignment::Horizontal,
wayland::popup::{destroy_popup, get_popup}, platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::{column, container, row, slider}, widget::{column, container, row, slider},
window, Alignment, Length, Subscription, window, Alignment, Length, Subscription,
}, },
iced_core::{alignment::Vertical, Background, Border, Color, Shadow}, iced_core::{alignment::Vertical, Background, Border, Color, Shadow},
iced_runtime::core::layout::Limits, iced_runtime::core::layout::Limits,
iced_style::application,
iced_widget::{Column, Row}, iced_widget::{Column, Row},
theme, theme,
widget::{divider, horizontal_space, icon, scrollable, text, vertical_space}, widget::{divider, horizontal_space, icon, scrollable, text, vertical_space},
Command, Element, Theme, Element, Task,
}; };
use cosmic_settings_subscriptions::{ use cosmic_settings_subscriptions::{
settings_daemon, settings_daemon,
@ -63,7 +62,7 @@ fn format_duration(duration: Duration) -> String {
} }
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
cosmic::applet::run::<CosmicBatteryApplet>(true, ()) cosmic::applet::run::<CosmicBatteryApplet>(())
} }
static MAX_CHARGE: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique); static MAX_CHARGE: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
@ -197,15 +196,14 @@ impl cosmic::Application for CosmicBatteryApplet {
_flags: Self::Flags, _flags: Self::Flags,
) -> ( ) -> (
Self, Self,
cosmic::iced::Command<cosmic::app::Message<Self::Message>>, cosmic::iced::Task<cosmic::app::Message<Self::Message>>,
) { ) {
let zbus_session_cmd = cosmic::iced::Command::perform(zbus::Connection::session(), |res| { let zbus_session_cmd = cosmic::iced::Task::perform(zbus::Connection::session(), |res| {
cosmic::app::Message::App(Message::ZbusConnection(res)) cosmic::app::Message::App(Message::ZbusConnection(res))
}); });
let init_charging_limit_cmd = let init_charging_limit_cmd = cosmic::iced::Task::perform(get_charging_limit(), |limit| {
cosmic::iced::Command::perform(get_charging_limit(), |limit| { cosmic::app::Message::App(Message::InitChargingLimit(limit))
cosmic::app::Message::App(Message::InitChargingLimit(limit)) });
});
( (
Self { Self {
core, core,
@ -215,7 +213,7 @@ impl cosmic::Application for CosmicBatteryApplet {
..Default::default() ..Default::default()
}, },
Command::batch(vec![zbus_session_cmd, init_charging_limit_cmd]), Task::batch(vec![zbus_session_cmd, init_charging_limit_cmd]),
) )
} }
@ -230,7 +228,7 @@ impl cosmic::Application for CosmicBatteryApplet {
fn update( fn update(
&mut self, &mut self,
message: Self::Message, message: Self::Message,
) -> cosmic::iced::Command<cosmic::app::Message<Self::Message>> { ) -> cosmic::iced::Task<cosmic::app::Message<Self::Message>> {
match message { match message {
Message::Frame(now) => self.timeline.now(now), Message::Frame(now) => self.timeline.now(now),
Message::SetKbdBrightness(brightness) => { Message::SetKbdBrightness(brightness) => {
@ -254,7 +252,7 @@ impl cosmic::Application for CosmicBatteryApplet {
self.set_charging_limit(enable); self.set_charging_limit(enable);
if enable { if enable {
return cosmic::iced::Command::perform(set_charging_limit(), |_| { return cosmic::iced::Task::perform(set_charging_limit(), |_| {
cosmic::app::Message::None cosmic::app::Message::None
}); });
} }
@ -275,9 +273,9 @@ impl cosmic::Application for CosmicBatteryApplet {
self.popup.replace(new_id); self.popup.replace(new_id);
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, Some((1, 1)),
None, None,
None, None,
); );
@ -405,7 +403,7 @@ impl cosmic::Application for CosmicBatteryApplet {
} }
}, },
} }
Command::none() Task::none()
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
@ -417,10 +415,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.into(); .into();
if !self.gpus.is_empty() { if !self.gpus.is_empty() {
let dot = container(vertical_space(Length::Fixed(0.0))) let dot = container(vertical_space().height(Length::Fixed(0.0)))
.padding(2.0) .padding(2.0)
.style(<Theme as container::StyleSheet>::Style::Custom(Box::new( .class(cosmic::style::Container::Custom(Box::new(|theme| {
|theme| container::Appearance { container::Style {
text_color: Some(Color::TRANSPARENT), text_color: Some(Color::TRANSPARENT),
background: Some(Background::Color(theme.cosmic().accent_color().into())), background: Some(Background::Color(theme.cosmic().accent_color().into())),
border: Border { border: Border {
@ -430,16 +428,16 @@ impl cosmic::Application for CosmicBatteryApplet {
}, },
shadow: Shadow::default(), shadow: Shadow::default(),
icon_color: Some(Color::TRANSPARENT), icon_color: Some(Color::TRANSPARENT),
}, }
))) })))
.into(); .into();
match self.core.applet.anchor { match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => Column::with_children(vec![btn, dot]) PanelAnchor::Left | PanelAnchor::Right => Column::with_children(vec![btn, dot])
.align_items(Alignment::Center) .align_x(Alignment::Center)
.into(), .into(),
PanelAnchor::Top | PanelAnchor::Bottom => Row::with_children(vec![btn, dot]) PanelAnchor::Top | PanelAnchor::Bottom => Row::with_children(vec![btn, dot])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.into(), .into(),
} }
} else { } else {
@ -471,7 +469,7 @@ impl cosmic::Application for CosmicBatteryApplet {
column![name, description] column![name, description]
] ]
.spacing(8) .spacing(8)
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.into(), .into(),
padded_control(divider::horizontal::default()) padded_control(divider::horizontal::default())
@ -491,10 +489,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.symbolic(true), .symbolic(true),
) )
} else { } else {
container(horizontal_space(1.0)) container(horizontal_space().width(1.0))
} }
] ]
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.on_press(Message::SelectProfile(Power::Battery)) .on_press(Message::SelectProfile(Power::Battery))
.into(), .into(),
@ -512,10 +510,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.symbolic(true), .symbolic(true),
) )
} else { } else {
container(horizontal_space(1.0)) container(horizontal_space().width(1.0))
} }
] ]
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.on_press(Message::SelectProfile(Power::Balanced)) .on_press(Message::SelectProfile(Power::Balanced))
.into(), .into(),
@ -533,10 +531,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.symbolic(true), .symbolic(true),
) )
} else { } else {
container(horizontal_space(1.0)) container(horizontal_space().width(1.0))
} }
] ]
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.on_press(Message::SelectProfile(Power::Performance)) .on_press(Message::SelectProfile(Power::Performance))
.into(), .into(),
@ -580,7 +578,7 @@ impl cosmic::Application for CosmicBatteryApplet {
)) ))
.size(16) .size(16)
.width(Length::Fixed(40.0)) .width(Length::Fixed(40.0))
.horizontal_alignment(Horizontal::Right) .align_x(Horizontal::Right)
] ]
.spacing(12), .spacing(12),
) )
@ -608,7 +606,7 @@ impl cosmic::Application for CosmicBatteryApplet {
)) ))
.size(16) .size(16)
.width(Length::Fixed(40.0)) .width(Length::Fixed(40.0))
.horizontal_alignment(Horizontal::Right) .align_x(Horizontal::Right)
] ]
.spacing(12), .spacing(12),
) )
@ -630,11 +628,11 @@ impl cosmic::Application for CosmicBatteryApplet {
text(fl!("dgpu-running")) text(fl!("dgpu-running"))
.size(16) .size(16)
.width(Length::Fill) .width(Length::Fill)
.horizontal_alignment(Horizontal::Left), .align_x(Horizontal::Left),
container(vertical_space(Length::Fixed(0.0))) container(vertical_space().width(Length::Fixed(0.0)))
.padding(4) .padding(4)
.style(<Theme as container::StyleSheet>::Style::Custom(Box::new( .class(cosmic::style::Container::Custom(Box::new(|theme| {
|theme| container::Appearance { container::Style {
text_color: Some(Color::TRANSPARENT), text_color: Some(Color::TRANSPARENT),
background: Some(Background::Color( background: Some(Background::Color(
theme.cosmic().accent_color().into(), theme.cosmic().accent_color().into(),
@ -646,10 +644,10 @@ impl cosmic::Application for CosmicBatteryApplet {
}, },
shadow: Default::default(), shadow: Default::default(),
icon_color: Some(Color::TRANSPARENT), icon_color: Some(Color::TRANSPARENT),
}, }
))), },))),
] ]
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.into(), .into(),
); );
@ -673,7 +671,7 @@ impl cosmic::Application for CosmicBatteryApplet {
gpu_name = format!("\"{}\"", gpu.name.trim()) gpu_name = format!("\"{}\"", gpu.name.trim())
)) ))
.width(Length::Fill) .width(Length::Fill)
.vertical_alignment(Vertical::Center), .align_y(Vertical::Center),
container( container(
icon::from_name(if gpu.toggled { icon::from_name(if gpu.toggled {
"go-down-symbolic" "go-down-symbolic"
@ -688,16 +686,21 @@ impl cosmic::Application for CosmicBatteryApplet {
.width(Length::Fixed(24.0)) .width(Length::Fixed(24.0))
.height(Length::Fixed(24.0)), .height(Length::Fixed(24.0)),
] ]
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.on_press(Message::ToggleGpuApps(key.clone())) .on_press(Message::ToggleGpuApps(key.clone()))
.into(), .into(),
); );
if gpu.toggled if gpu.toggled
&& !self.core.applet.configure.as_ref().is_some_and(|c| { && !self.core.applet.suggested_bounds.as_ref().is_some_and(|c| {
let suggested_size = self.core.applet.suggested_size(true);
let padding = self.core.applet.suggested_padding(true);
let w = suggested_size.0 + 2 * padding;
let h = suggested_size.1 + 2 * padding;
// if we have a configure for width and height, we're in a overflow popup // if we have a configure for width and height, we're in a overflow popup
c.new_size.0.is_some() && c.new_size.1.is_some() // TODO... we don't exactly have a good way of knowing, unless the size is equal to a suggested size maybe?
c.width as u32 == w as u32 && c.height as u32 == h as u32
}) })
{ {
let app_list = gpu.app_list.as_ref().unwrap(); let app_list = gpu.app_list.as_ref().unwrap();
@ -709,13 +712,13 @@ impl cosmic::Application for CosmicBatteryApplet {
if let Some(icon) = &app.icon { if let Some(icon) = &app.icon {
container(icon::from_name(&**icon).size(12).symbolic(true)) container(icon::from_name(&**icon).size(12).symbolic(true))
} else { } else {
container(horizontal_space(12.0)) container(horizontal_space().width(12.0))
}, },
column![text::body(&app.name), text::caption(&app.secondary)] column![text::body(&app.name), text::caption(&app.secondary)]
.width(Length::Fill), .width(Length::Fill),
] ]
.spacing(8) .spacing(8)
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.into(), .into(),
); );
@ -742,6 +745,8 @@ impl cosmic::Application for CosmicBatteryApplet {
self.core self.core
.applet .applet
.popup_container(Column::with_children(content).padding([8, 0])) .popup_container(Column::with_children(content).padding([8, 0]))
.max_width(372.)
.max_height(600.)
.into() .into()
} }
@ -773,7 +778,7 @@ impl cosmic::Application for CosmicBatteryApplet {
Some(Message::CloseRequested(id)) Some(Message::CloseRequested(id))
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
} }

View file

@ -1,7 +1,10 @@
// Copyright 2023 System76 <info@system76.com> // Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use cosmic::iced::{self, futures::SinkExt, subscription}; use cosmic::{
iced::{self, futures::SinkExt, Subscription},
iced_futures::stream,
};
use std::{fmt::Debug, hash::Hash}; use std::{fmt::Debug, hash::Hash};
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use zbus::{Connection, Result}; use zbus::{Connection, Result};
@ -96,13 +99,16 @@ pub async fn set_power_profile(daemon: Backend<'_>, power: Power) -> Result<()>
pub fn power_profile_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>( pub fn power_profile_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
id: I, id: I,
) -> iced::Subscription<PowerProfileUpdate> { ) -> iced::Subscription<PowerProfileUpdate> {
subscription::channel(id, 50, move |mut output| async move { Subscription::run_with_id(
let mut state = State::Ready; id,
stream::channel(50, move |mut output| async move {
let mut state = State::Ready;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}) }),
)
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -11,7 +11,10 @@ use std::{
time::Duration, time::Duration,
}; };
use cosmic::iced::{self, subscription}; use cosmic::{
iced::{self, Subscription},
iced_futures::stream,
};
use drm::control::Device as ControlDevice; use drm::control::Device as ControlDevice;
use futures::{FutureExt, SinkExt}; use futures::{FutureExt, SinkExt};
use tokio::{ use tokio::{
@ -378,13 +381,16 @@ fn all_gpus<S: AsRef<str>>(seat: S) -> io::Result<Vec<Gpu>> {
pub fn dgpu_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>( pub fn dgpu_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
id: I, id: I,
) -> iced::Subscription<GpuUpdate> { ) -> iced::Subscription<GpuUpdate> {
subscription::channel(id, 50, move |mut output| async move { Subscription::run_with_id(
let mut state = State::Ready; id,
stream::channel(50, move |mut output| async move {
let mut state = State::Ready;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}) }),
)
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -12,7 +12,7 @@ use cosmic::{
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced::{ iced::{
self, self,
wayland::popup::{destroy_popup, get_popup}, platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::{column, container, row, scrollable, Column}, widget::{column, container, row, scrollable, Column},
Alignment, Length, Subscription, Alignment, Length, Subscription,
}, },
@ -21,10 +21,9 @@ use cosmic::{
layout::Limits, layout::Limits,
window, window,
}, },
iced_style::application,
theme, theme,
widget::{button, divider, icon, text}, widget::{button, divider, icon, text},
Command, Element, Theme, Element, Task,
}; };
use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline}; use cosmic_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
use std::{collections::HashMap, time::Duration}; use std::{collections::HashMap, time::Duration};
@ -38,7 +37,7 @@ use crate::{
static BLUETOOTH_ENABLED: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique); static BLUETOOTH_ENABLED: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
cosmic::applet::run::<CosmicBluetoothApplet>(false, ()) cosmic::applet::run::<CosmicBluetoothApplet>(())
} }
#[derive(Default)] #[derive(Default)]
@ -91,7 +90,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
fn init( fn init(
core: cosmic::app::Core, core: cosmic::app::Core,
_flags: Self::Flags, _flags: Self::Flags,
) -> (Self, iced::Command<cosmic::app::Message<Self::Message>>) { ) -> (Self, iced::Task<cosmic::app::Message<Self::Message>>) {
( (
Self { Self {
core, core,
@ -99,7 +98,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
token_tx: None, token_tx: None,
..Default::default() ..Default::default()
}, },
Command::none(), Task::none(),
) )
} }
@ -114,7 +113,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
fn update( fn update(
&mut self, &mut self,
message: Self::Message, message: Self::Message,
) -> iced::Command<cosmic::app::Message<Self::Message>> { ) -> iced::Task<cosmic::app::Message<Self::Message>> {
match message { match message {
Message::TogglePopup => { Message::TogglePopup => {
if let Some(p) = self.popup.take() { if let Some(p) = self.popup.take() {
@ -126,9 +125,9 @@ impl cosmic::Application for CosmicBluetoothApplet {
self.timeline = Timeline::new(); self.timeline = Timeline::new();
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, Some((1, 1)),
None, None,
None, None,
); );
@ -139,8 +138,8 @@ impl cosmic::Application for CosmicBluetoothApplet {
.max_height(800.0) .max_height(800.0)
.max_width(400.0); .max_width(400.0);
let tx = self.bluer_sender.as_ref().cloned(); let tx = self.bluer_sender.as_ref().cloned();
return Command::batch(vec![ return Task::batch(vec![
iced::Command::perform( iced::Task::perform(
async { async {
if let Some(tx) = tx { if let Some(tx) = tx {
let _ = tx.send(BluerRequest::StateUpdate).await; let _ = tx.send(BluerRequest::StateUpdate).await;
@ -327,7 +326,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
Message::Frame(instant) => self.timeline.now(instant), Message::Frame(instant) => self.timeline.now(instant),
Message::ToggleBluetooth(chain, enabled) => { Message::ToggleBluetooth(chain, enabled) => {
if self.bluer_state.bluetooth_enabled == enabled { if self.bluer_state.bluetooth_enabled == enabled {
return Command::none(); return Task::none();
} }
self.timeline.set_chain(chain).start(); self.timeline.set_chain(chain).start();
self.bluer_state.bluetooth_enabled = enabled; self.bluer_state.bluetooth_enabled = enabled;
@ -339,7 +338,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
} }
} }
self.update_icon(); self.update_icon();
Command::none() Task::none()
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
@ -365,11 +364,11 @@ impl cosmic::Application for CosmicBluetoothApplet {
let mut row = row![ let mut row = row![
icon::from_name(dev.icon.as_str()).size(16).symbolic(true), icon::from_name(dev.icon.as_str()).size(16).symbolic(true),
text::body(dev.name.clone()) text::body(dev.name.clone())
.horizontal_alignment(Horizontal::Left) .align_x(Horizontal::Left)
.vertical_alignment(Vertical::Center) .align_y(Vertical::Center)
.width(Length::Fill) .width(Length::Fill)
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(12); .spacing(12);
if let Some(DeviceProperty::BatteryPercentage(battery)) = dev if let Some(DeviceProperty::BatteryPercentage(battery)) = dev
@ -386,7 +385,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
icon::from_name(icon).symbolic(true).size(14), icon::from_name(icon).symbolic(true).size(14),
text::body(format!("{}%", battery)) text::body(format!("{}%", battery))
) )
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(2) .spacing(2)
.width(Length::Shrink); .width(Length::Shrink);
@ -401,8 +400,8 @@ impl cosmic::Application for CosmicBluetoothApplet {
BluerDeviceStatus::Connected => { BluerDeviceStatus::Connected => {
row = row.push( row = row.push(
text::body(fl!("connected")) text::body(fl!("connected"))
.horizontal_alignment(Horizontal::Right) .align_x(Horizontal::Right)
.vertical_alignment(Vertical::Center), .align_y(Vertical::Center),
); );
} }
BluerDeviceStatus::Paired => {} BluerDeviceStatus::Paired => {}
@ -450,7 +449,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
.text_size(14) .text_size(14)
.width(Length::Fill) .width(Length::Fill)
),],] ),],]
.align_items(Alignment::Center) .align_x(Alignment::Center)
.padding([8, 0]); .padding([8, 0]);
if !known_bluetooth.is_empty() { if !known_bluetooth.is_empty() {
content = content content = content
@ -466,7 +465,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
text::body(fl!("other-devices")) text::body(fl!("other-devices"))
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fixed(24.0)) .height(Length::Fixed(24.0))
.vertical_alignment(Vertical::Center), .align_y(Vertical::Center),
container(icon::from_name(dropdown_icon).size(16).symbolic(true)) container(icon::from_name(dropdown_icon).size(16).symbolic(true))
.align_x(Horizontal::Center) .align_x(Horizontal::Center)
.align_y(Vertical::Center) .align_y(Vertical::Center)
@ -489,8 +488,8 @@ impl cosmic::Application for CosmicBluetoothApplet {
.size(16) .size(16)
.symbolic(true), .symbolic(true),
text::body(&device.name) text::body(&device.name)
.horizontal_alignment(Horizontal::Left) .align_x(Horizontal::Left)
.vertical_alignment(Vertical::Center) .align_y(Vertical::Center)
.width(Length::Fill) .width(Length::Fill)
]), ]),
padded_control( padded_control(
@ -498,14 +497,14 @@ impl cosmic::Application for CosmicBluetoothApplet {
"confirm-pin", "confirm-pin",
HashMap::from_iter(vec![("deviceName", device.name.clone())]) HashMap::from_iter(vec![("deviceName", device.name.clone())])
)) ))
.horizontal_alignment(Horizontal::Left) .align_x(Horizontal::Left)
.vertical_alignment(Vertical::Center) .align_y(Vertical::Center)
.width(Length::Fill) .width(Length::Fill)
), ),
padded_control( padded_control(
text::title3(pin) text::title3(pin)
.horizontal_alignment(Horizontal::Center) .align_x(Horizontal::Center)
.vertical_alignment(Vertical::Center) .align_y(Vertical::Center)
.width(Length::Fixed(280.0)) //.size(22) .width(Length::Fixed(280.0)) //.size(22)
) )
.align_x(Horizontal::Center), .align_x(Horizontal::Center),
@ -513,8 +512,8 @@ impl cosmic::Application for CosmicBluetoothApplet {
row![ row![
button::custom( button::custom(
text::body(fl!("cancel")) text::body(fl!("cancel"))
.vertical_alignment(Vertical::Center) .align_y(Vertical::Center)
.horizontal_alignment(Horizontal::Center) .align_x(Horizontal::Center)
) )
.padding([4, 0]) .padding([4, 0])
.height(Length::Fixed(28.0)) .height(Length::Fixed(28.0))
@ -522,8 +521,8 @@ impl cosmic::Application for CosmicBluetoothApplet {
.on_press(Message::Cancel), .on_press(Message::Cancel),
button::custom( button::custom(
text::body(fl!("confirm")) text::body(fl!("confirm"))
.vertical_alignment(Vertical::Center) .align_y(Vertical::Center)
.horizontal_alignment(Horizontal::Center) .align_x(Horizontal::Center)
) )
.padding([4, 0]) .padding([4, 0])
.height(Length::Fixed(28.0)) .height(Length::Fixed(28.0))
@ -532,7 +531,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
] ]
.spacing(self.core.system_theme().cosmic().space_xxs()) .spacing(self.core.system_theme().cosmic().space_xxs())
.width(Length::Shrink) .width(Length::Shrink)
.align_items(Alignment::Center) .align_y(Alignment::Center)
) )
.align_x(Horizontal::Center) .align_x(Horizontal::Center)
]; ];
@ -552,9 +551,9 @@ impl cosmic::Application for CosmicBluetoothApplet {
}) { }) {
let row = row![ let row = row![
icon::from_name(dev.icon.as_str()).size(16).symbolic(true), icon::from_name(dev.icon.as_str()).size(16).symbolic(true),
text::body(dev.name.clone()).horizontal_alignment(Horizontal::Left), text::body(dev.name.clone()).align_x(Horizontal::Left),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(12); .spacing(12);
visible_devices = visible_devices.push( visible_devices = visible_devices.push(
menu_button(row.width(Length::Fill)) menu_button(row.width(Length::Fill))
@ -595,7 +594,7 @@ impl cosmic::Application for CosmicBluetoothApplet {
]) ])
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }

View file

@ -9,10 +9,13 @@ use bluer::{
Adapter, Address, Session, Uuid, Adapter, Address, Session, Uuid,
}; };
use cosmic::iced::{ use cosmic::{
self, iced::{
futures::{SinkExt, StreamExt}, self,
subscription, futures::{SinkExt, StreamExt},
Subscription,
},
iced_futures::stream,
}; };
use rand::Rng; use rand::Rng;
@ -29,13 +32,16 @@ use tokio::{
pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>( pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
id: I, id: I,
) -> iced::Subscription<BluerEvent> { ) -> iced::Subscription<BluerEvent> {
subscription::channel(id, 50, move |mut output| async move { Subscription::run_with_id(
let mut state = State::Ready; id,
stream::channel(50, move |mut output| async move {
let mut state = State::Ready;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}) }),
)
} }
pub enum State { pub enum State {

View file

@ -5,14 +5,14 @@ edition = "2021"
license = "GPL-3.0-only" license = "GPL-3.0-only"
[dependencies] [dependencies]
cosmic-time.workspace = true # cosmic-time.workspace = true
cosmic-comp-config = { git = "https://github.com/pop-os/cosmic-comp.git", rev = "5eb5af4" } cosmic-comp-config = { git = "https://github.com/pop-os/cosmic-comp.git", rev = "5eb5af4" }
i18n-embed-fl.workspace = true i18n-embed-fl.workspace = true
i18n-embed.workspace = true i18n-embed.workspace = true
libcosmic.workspace = true libcosmic.workspace = true
libpulse-binding = "2.28.1" libpulse-binding = "2.28.1"
rust-embed.workspace = true rust-embed.workspace = true
tokio = { version = "1.36.0", features=["full"] } tokio = { version = "1.36.0", features = ["full"] }
tracing-log.workspace = true tracing-log.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
tracing.workspace = true tracing.workspace = true

View file

@ -62,5 +62,5 @@ pub fn run() -> cosmic::iced::Result {
config, config,
layouts, layouts,
}; };
cosmic::applet::run::<Window>(false, flags) cosmic::applet::run::<Window>(flags)
} }

View file

@ -12,14 +12,13 @@ use cosmic::{
cosmic_config::{self, ConfigSet}, cosmic_config::{self, ConfigSet},
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced::{ iced::{
wayland::popup::{destroy_popup, get_popup}, platform_specific::shell::commands::popup::{destroy_popup, get_popup},
widget::{column, row}, widget::{column, row},
window::Id, window::Id,
Command, Limits, Limits, Task,
}, },
iced_futures::Subscription, iced_futures::Subscription,
iced_runtime::core::window, iced_runtime::{core::window, Appearance},
iced_style::application,
prelude::*, prelude::*,
theme, theme,
widget::{self, horizontal_space, vertical_space}, widget::{self, horizontal_space, vertical_space},
@ -75,10 +74,7 @@ impl cosmic::Application for Window {
&mut self.core &mut self.core
} }
fn init( fn init(core: Core, flags: Self::Flags) -> (Self, Task<cosmic::app::Message<Self::Message>>) {
core: Core,
flags: Self::Flags,
) -> (Self, Command<cosmic::app::Message<Self::Message>>) {
let window = Window { let window = Window {
comp_config_handler: flags.comp_config_handler, comp_config_handler: flags.comp_config_handler,
layouts: flags.layouts, layouts: flags.layouts,
@ -89,14 +85,14 @@ impl cosmic::Application for Window {
comp_config: flags.comp_config, comp_config: flags.comp_config,
active_layouts: Vec::new(), active_layouts: Vec::new(),
}; };
(window, Command::none()) (window, Task::none())
} }
fn on_close_requested(&self, id: window::Id) -> Option<Message> { fn on_close_requested(&self, id: window::Id) -> Option<Message> {
Some(Message::PopupClosed(id)) Some(Message::PopupClosed(id))
} }
fn update(&mut self, message: Self::Message) -> Command<cosmic::app::Message<Self::Message>> { fn update(&mut self, message: Self::Message) -> Task<cosmic::app::Message<Self::Message>> {
match message { match message {
Message::Config(config) => self.config = config, Message::Config(config) => self.config = config,
Message::TogglePopup => { Message::TogglePopup => {
@ -105,17 +101,20 @@ impl cosmic::Application for Window {
} else { } else {
let new_id = Id::unique(); let new_id = Id::unique();
self.popup.replace(new_id); self.popup.replace(new_id);
let mut popup_settings = let mut popup_settings = self.core.applet.get_popup_settings(
self.core self.core.main_window_id().unwrap(),
.applet new_id,
.get_popup_settings(Id::MAIN, new_id, None, None, None); None,
None,
None,
);
popup_settings.positioner.size_limits = Limits::NONE popup_settings.positioner.size_limits = Limits::NONE
.max_width(372.0) .max_width(372.0)
.min_width(300.0) .min_width(300.0)
.min_height(1.) .min_height(1.)
.max_height(1080.0); .max_height(1080.0);
get_popup(popup_settings) get_popup(popup_settings)
} };
} }
Message::PopupClosed(id) => { Message::PopupClosed(id) => {
if self.popup.as_ref() == Some(&id) { if self.popup.as_ref() == Some(&id) {
@ -137,7 +136,7 @@ impl cosmic::Application for Window {
.iter() .iter()
.position(|layout| layout == &active_layout) .position(|layout| layout == &active_layout)
else { else {
return Command::none(); return Task::none();
}; };
self.active_layouts.swap(0, i); self.active_layouts.swap(0, i);
@ -164,7 +163,7 @@ impl cosmic::Application for Window {
} }
} }
} }
Command::none() Task::none()
} }
fn view(&self) -> Element<Self::Message> { fn view(&self) -> Element<Self::Message> {
@ -178,7 +177,7 @@ impl cosmic::Application for Window {
row!( row!(
column!( column!(
input_source_text, input_source_text,
horizontal_space(Length::Fixed( horizontal_space().width(Length::Fixed(
(self.core.applet.suggested_size(true).0 (self.core.applet.suggested_size(true).0
+ 2 * self.core.applet.suggested_padding(true)) + 2 * self.core.applet.suggested_padding(true))
as f32 as f32
@ -186,18 +185,18 @@ impl cosmic::Application for Window {
) )
.width(Length::Shrink) .width(Length::Shrink)
.height(Length::Shrink) .height(Length::Shrink)
.align_items(Alignment::Center), .align_x(Alignment::Center),
vertical_space(Length::Fixed( vertical_space().height(Length::Fixed(
(self.core.applet.suggested_size(true).1 (self.core.applet.suggested_size(true).1
+ 2 * self.core.applet.suggested_padding(true)) as f32 + 2 * self.core.applet.suggested_padding(true)) as f32
)) ))
) )
.align_items(Alignment::Center) .align_y(Alignment::Center)
.width(Length::Shrink) .width(Length::Shrink)
.height(Length::Shrink), .height(Length::Shrink),
) )
.on_press_down(Message::TogglePopup) .on_press_down(Message::TogglePopup)
.style(cosmic::theme::Button::AppletIcon) .class(cosmic::theme::Button::AppletIcon)
.into() .into()
} }
@ -228,7 +227,17 @@ impl cosmic::Application for Window {
.on_press(Message::KeyboardSettings), .on_press(Message::KeyboardSettings),
); );
self.core.applet.popup_container(content_list).into() self.core
.applet
.popup_container(content_list)
.limits(
Limits::NONE
.min_height(1.)
.max_height(1080.)
.min_width(1.)
.max_width(372.),
)
.into()
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
@ -264,7 +273,7 @@ impl cosmic::Application for Window {
Subscription::batch(vec![config, xbg_config]) Subscription::batch(vec![config, xbg_config])
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
} }

View file

@ -8,7 +8,7 @@ pub(crate) mod window_image;
use crate::localize::localize; use crate::localize::localize;
use cosmic::{ use cosmic::{
app::Command, app,
applet::cosmic_panel_config::PanelAnchor, applet::cosmic_panel_config::PanelAnchor,
cctk::{ cctk::{
cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1, cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
@ -17,27 +17,29 @@ use cosmic::{
desktop::DesktopEntryData, desktop::DesktopEntryData,
iced::{ iced::{
self, self,
wayland::popup::{destroy_popup, get_popup}, id::Id as WidgetId,
platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::text, widget::text,
window::{self}, window::{self},
Length, Subscription, Length, Limits, Subscription,
}, },
widget::mouse_area, widget::{autosize::autosize, mouse_area},
Task,
}; };
use cosmic::{ use cosmic::iced_widget::{Column, Row};
iced_style::application,
iced_widget::{Column, Row},
};
use cosmic::{widget::tooltip, Element, Theme}; use cosmic::{widget::tooltip, Element};
use once_cell::sync::Lazy;
use wayland_subscription::{ use wayland_subscription::{
ToplevelRequest, ToplevelUpdate, WaylandImage, WaylandRequest, WaylandUpdate, ToplevelRequest, ToplevelUpdate, WaylandImage, WaylandRequest, WaylandUpdate,
}; };
static AUTOSIZE_MAIN_ID: Lazy<WidgetId> = Lazy::new(|| WidgetId::new("autosize-main"));
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
localize(); localize();
cosmic::applet::run::<Minimize>(true, ()) cosmic::applet::run::<Minimize>(())
} }
#[derive(Default)] #[derive(Default)]
@ -56,11 +58,11 @@ struct Minimize {
impl Minimize { impl Minimize {
fn max_icon_count(&self) -> Option<usize> { fn max_icon_count(&self) -> Option<usize> {
let mut index = None; let mut index = None;
let Some(max_major_axis_len) = self.core.applet.configure.as_ref().and_then(|c| { let Some(max_major_axis_len) = self.core.applet.suggested_bounds.as_ref().map(|c| {
// if we have a configure for width and height, we're in a overflow popup // if we have a configure for width and height, we're in a overflow popup
match self.core.applet.anchor { match self.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => c.new_size.0, PanelAnchor::Top | PanelAnchor::Bottom => c.width as u32,
PanelAnchor::Left | PanelAnchor::Right => c.new_size.1, PanelAnchor::Left | PanelAnchor::Right => c.height as u32,
} }
}) else { }) else {
return index; return index;
@ -68,7 +70,7 @@ impl Minimize {
let button_total_size = self.core.applet.suggested_size(true).0 let button_total_size = self.core.applet.suggested_size(true).0
+ self.core.applet.suggested_padding(true) * 2 + self.core.applet.suggested_padding(true) * 2
+ 4; + 4;
let btn_count = max_major_axis_len.get() / button_total_size as u32; let btn_count = max_major_axis_len / button_total_size as u32;
if btn_count >= self.apps.len() as u32 { if btn_count >= self.apps.len() as u32 {
index = None; index = None;
} else { } else {
@ -93,13 +95,13 @@ impl cosmic::Application for Minimize {
type Flags = (); type Flags = ();
const APP_ID: &'static str = "com.system76.CosmicAppletMinimize"; const APP_ID: &'static str = "com.system76.CosmicAppletMinimize";
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) { fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Task<Message>) {
( (
Self { Self {
core, core,
..Default::default() ..Default::default()
}, },
Command::none(), Task::none(),
) )
} }
@ -111,11 +113,11 @@ impl cosmic::Application for Minimize {
&mut self.core &mut self.core
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> app::Task<Message> {
match message { match message {
Message::Wayland(update) => match update { Message::Wayland(update) => match update {
WaylandUpdate::Init(tx) => { WaylandUpdate::Init(tx) => {
@ -190,7 +192,7 @@ impl cosmic::Application for Minimize {
}, },
}; };
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, None,
None, None,
@ -203,7 +205,7 @@ impl cosmic::Application for Minimize {
} }
Message::CloseOverflowPopup => todo!(), Message::CloseOverflowPopup => todo!(),
}; };
Command::none() Task::none()
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
@ -236,7 +238,7 @@ impl cosmic::Application for Minimize {
Message::Activate(handle.clone()), Message::Activate(handle.clone()),
padding, padding,
)), )),
data.name.clone(), text(data.name.clone()).shaping(text::Shaping::Advanced),
// tooltip::Position::FollowCursor, // tooltip::Position::FollowCursor,
// FIXME tooltip fails to appear when created as indicated in design // FIXME tooltip fails to appear when created as indicated in design
// maybe it should be a subsurface // maybe it should be a subsurface
@ -248,7 +250,6 @@ impl cosmic::Application for Minimize {
}, },
) )
.snap_within_viewport(false) .snap_within_viewport(false)
.text_shaping(text::Shaping::Advanced)
.into() .into()
}); });
let overflow_btn = if max_icon_count < self.apps.len() { let overflow_btn = if max_icon_count < self.apps.len() {
@ -272,31 +273,48 @@ impl cosmic::Application for Minimize {
// TODO optional dividers on ends if detects app list neighbor // TODO optional dividers on ends if detects app list neighbor
// not sure the best way to tell if there is an adjacent app-list // not sure the best way to tell if there is an adjacent app-list
let icon_buttons = icon_buttons.chain(overflow_btn.into_iter()); let icon_buttons = icon_buttons.chain(overflow_btn.into_iter());
let content = if matches!( let content: Element<_> = if matches!(
self.core.applet.anchor, self.core.applet.anchor,
PanelAnchor::Top | PanelAnchor::Bottom PanelAnchor::Top | PanelAnchor::Bottom
) { ) {
Row::with_children(icon_buttons) Row::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center) .align_y(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink) .height(Length::Shrink)
.width(Length::Shrink) .width(Length::Shrink)
.spacing(space_xxs) .spacing(space_xxs)
.into() .into()
} else { } else {
Column::with_children(icon_buttons) Column::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center) .align_x(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink) .height(Length::Shrink)
.width(Length::Shrink) .width(Length::Shrink)
.spacing(space_xxs) .spacing(space_xxs)
.into() .into()
}; };
if self.overflow_popup.is_some() {
mouse_area(content) let mut limits = Limits::NONE.min_width(1.).min_height(1.);
.on_press(Message::CloseOverflowPopup)
.into() if let Some(b) = self.core.applet.suggested_bounds {
} else { if b.width as i32 > 0 {
content limits = limits.max_width(b.width);
}
if b.height as i32 > 0 {
limits = limits.max_height(b.height);
}
} }
autosize(
if self.overflow_popup.is_some() {
mouse_area(content)
.on_press(Message::CloseOverflowPopup)
.into()
} else {
content
},
AUTOSIZE_MAIN_ID.clone(),
)
.limits(limits)
.into()
} }
fn view_window(&self, _id: window::Id) -> Element<Self::Message> { fn view_window(&self, _id: window::Id) -> Element<Self::Message> {
@ -325,7 +343,7 @@ impl cosmic::Application for Minimize {
Message::Activate(handle.clone()), Message::Activate(handle.clone()),
padding, padding,
)), )),
data.name.clone(), text(data.name.clone()).shaping(text::Shaping::Advanced),
// tooltip::Position::FollowCursor, // tooltip::Position::FollowCursor,
// FIXME tooltip fails to appear when created as indicated in design // FIXME tooltip fails to appear when created as indicated in design
// maybe it should be a subsurface // maybe it should be a subsurface
@ -337,7 +355,6 @@ impl cosmic::Application for Minimize {
}, },
) )
.snap_within_viewport(false) .snap_within_viewport(false)
.text_shaping(text::Shaping::Advanced)
.into() .into()
}); });
@ -353,14 +370,14 @@ impl cosmic::Application for Minimize {
) { ) {
Element::from( Element::from(
Row::with_children(icon_buttons) Row::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center) .align_y(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink) .height(Length::Shrink)
.width(Length::Shrink) .width(Length::Shrink)
.spacing(space_xxs), .spacing(space_xxs),
) )
} else { } else {
Column::with_children(icon_buttons) Column::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center) .align_x(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink) .height(Length::Shrink)
.width(Length::Shrink) .width(Length::Shrink)
.spacing(space_xxs) .spacing(space_xxs)

View file

@ -6,7 +6,12 @@
//! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data. //! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data.
//! Source: `Interface '/org/freedesktop/UPower/KbdBacklight' from service 'org.freedesktop.UPower' on system bus`. //! Source: `Interface '/org/freedesktop/UPower/KbdBacklight' from service 'org.freedesktop.UPower' on system bus`.
use cctk::{sctk::reexports::calloop, toplevel_info::ToplevelInfo}; use cctk::{sctk::reexports::calloop, toplevel_info::ToplevelInfo};
use cosmic::{cctk, cctk::cosmic_protocols, iced, iced::subscription, iced_futures::futures}; use cosmic::{
cctk::{self, cosmic_protocols},
iced::{self, Subscription},
iced_core::image::Bytes,
iced_futures::{futures, stream},
};
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1; use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1;
use futures::{ use futures::{
channel::mpsc::{unbounded, UnboundedReceiver}, channel::mpsc::{unbounded, UnboundedReceiver},
@ -23,16 +28,15 @@ pub static WAYLAND_RX: Lazy<Mutex<Option<UnboundedReceiver<WaylandUpdate>>>> =
Lazy::new(|| Mutex::new(None)); Lazy::new(|| Mutex::new(None));
pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> { pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> {
subscription::channel( Subscription::run_with_id(
std::any::TypeId::of::<WaylandUpdate>(), std::any::TypeId::of::<WaylandUpdate>(),
50, stream::channel(50, move |mut output| async move {
move |mut output| async move {
let mut state = State::Waiting; let mut state = State::Waiting;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}, }),
) )
} }
@ -86,12 +90,18 @@ pub enum WaylandUpdate {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct WaylandImage { pub struct WaylandImage {
pub img: Arc<image::RgbaImage>, pub img: Bytes,
pub width: u32,
pub height: u32,
} }
impl WaylandImage { impl WaylandImage {
pub fn new(img: image::RgbaImage) -> Self { pub fn new(img: image::RgbaImage) -> Self {
Self { img: Arc::new(img) } Self {
img: Bytes::copy_from_slice(img.as_bytes()),
width: img.width(),
height: img.height(),
}
} }
} }

View file

@ -33,20 +33,16 @@ where
image_button: button::custom( image_button: button::custom(
container( container(
container(if let Some(img) = img { container(if let Some(img) = img {
let max_dim = img.img.width().max(img.img.height()).max(1); let max_dim = img.width.max(img.height).max(1);
let ratio = max_dim as f32 / (size - border * 2.0).max(1.0); let ratio = max_dim as f32 / (size - border * 2.0).max(1.0);
let adjusted_width = img.img.width() as f32 / ratio; let adjusted_width = img.width as f32 / ratio;
let adjusted_height = img.img.height() as f32 / ratio; let adjusted_height = img.height as f32 / ratio;
Element::from( Element::from(
Image::new(Handle::from_pixels( Image::new(Handle::from_rgba(img.width, img.height, img.img))
img.img.width(), .width(Length::Fixed(adjusted_width))
img.img.height(), .height(Length::Fixed(adjusted_height))
img.clone(), .content_fit(cosmic::iced_core::ContentFit::Contain),
))
.width(Length::Fixed(adjusted_width))
.height(Length::Fixed(adjusted_height))
.content_fit(cosmic::iced_core::ContentFit::Contain),
) )
} else { } else {
Element::from( Element::from(
@ -55,15 +51,13 @@ where
.height(Length::Fixed((size - border * 2.0).max(0.))), .height(Length::Fixed((size - border * 2.0).max(0.))),
) )
}) })
.style(Container::Custom(Box::new(move |theme| { .class(Container::Custom(Box::new(move |theme| container::Style {
container::Appearance { border: Border {
border: Border { color: theme.cosmic().bg_divider().into(),
color: theme.cosmic().bg_divider().into(), width: border,
width: border, radius: 0.0.into(),
radius: 0.0.into(), },
}, ..Default::default()
..Default::default()
}
}))) })))
.padding(border as u16) .padding(border as u16)
.height(Length::Shrink) .height(Length::Shrink)
@ -78,7 +72,7 @@ where
.on_press(on_press) .on_press(on_press)
.width(Length::Shrink) .width(Length::Shrink)
.height(Length::Shrink) .height(Length::Shrink)
.style(Button::AppletIcon) .class(Button::AppletIcon)
.padding(0) .padding(0)
.into(), .into(),
icon: icon icon: icon
@ -104,13 +98,16 @@ impl<'a, Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'a, M
state: &'b mut Tree, state: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &cosmic::Renderer, renderer: &cosmic::Renderer,
translation: Vector,
) -> Option<cosmic::iced_core::overlay::Element<'b, Msg, cosmic::Theme, cosmic::Renderer>> { ) -> Option<cosmic::iced_core::overlay::Element<'b, Msg, cosmic::Theme, cosmic::Renderer>> {
let children = [&mut self.image_button, &mut self.icon] let children = [&mut self.image_button, &mut self.icon]
.into_iter() .into_iter()
.zip(&mut state.children) .zip(&mut state.children)
.zip(layout.children()) .zip(layout.children())
.filter_map(|((child, state), layout)| { .filter_map(|((child, state), layout)| {
child.as_widget_mut().overlay(state, layout, renderer) child
.as_widget_mut()
.overlay(state, layout, renderer, translation)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -195,9 +192,7 @@ impl<'a, Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'a, M
tree: &mut cosmic::iced_core::widget::Tree, tree: &mut cosmic::iced_core::widget::Tree,
layout: cosmic::iced_core::Layout<'_>, layout: cosmic::iced_core::Layout<'_>,
renderer: &cosmic::Renderer, renderer: &cosmic::Renderer,
operation: &mut dyn cosmic::widget::Operation< operation: &mut dyn cosmic::widget::Operation<()>,
cosmic::iced_core::widget::OperationOutputWrapper<Msg>,
>,
) { ) {
let layout = layout.children().collect::<Vec<_>>(); let layout = layout.children().collect::<Vec<_>>();
let children = [&self.image_button, &self.icon]; let children = [&self.image_button, &self.icon];

View file

@ -18,7 +18,6 @@ libcosmic.workspace = true
libcosmic.features = [ libcosmic.features = [
"applet", "applet",
"applet-token", "applet-token",
"clipboard",
"tokio", "tokio",
"wayland", "wayland",
"desktop", "desktop",

View file

@ -1,7 +1,7 @@
use std::collections::HashSet; use std::collections::HashSet;
use cosmic::{ use cosmic::{
app::Command, app,
applet::{ applet::{
menu_button, menu_control_padding, padded_control, menu_button, menu_control_padding, padded_control,
token::subscription::{activation_token_subscription, TokenRequest, TokenUpdate}, token::subscription::{activation_token_subscription, TokenRequest, TokenUpdate},
@ -9,7 +9,7 @@ use cosmic::{
cctk::sctk::reexports::calloop, cctk::sctk::reexports::calloop,
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced::{ iced::{
wayland::popup::{destroy_popup, get_popup}, platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::{column, row}, widget::{column, row},
Alignment, Length, Subscription, Alignment, Length, Subscription,
}, },
@ -18,13 +18,14 @@ use cosmic::{
layout::Limits, layout::Limits,
window, window,
}, },
iced_style::application,
iced_widget::Row, iced_widget::Row,
theme, theme,
widget::{ widget::{
button, container, divider, icon, icon::from_name, scrollable, text, text_input, Column, button, container, divider,
icon::{self, from_name},
scrollable, text, text_input, Column,
}, },
Element, Theme, Element, Task,
}; };
use cosmic_dbus_networkmanager::interface::enums::{ use cosmic_dbus_networkmanager::interface::enums::{
ActiveConnectionState, DeviceState, NmConnectivityState, ActiveConnectionState, DeviceState, NmConnectivityState,
@ -45,7 +46,7 @@ use crate::{
}; };
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
cosmic::applet::run::<CosmicNetworkApplet>(false, ()) cosmic::applet::run::<CosmicNetworkApplet>(())
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -233,7 +234,7 @@ impl cosmic::Application for CosmicNetworkApplet {
type Flags = (); type Flags = ();
const APP_ID: &'static str = config::APP_ID; const APP_ID: &'static str = config::APP_ID;
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) { fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Task<Message>) {
( (
Self { Self {
core, core,
@ -241,7 +242,7 @@ impl cosmic::Application for CosmicNetworkApplet {
token_tx: None, token_tx: None,
..Default::default() ..Default::default()
}, },
Command::none(), Task::none(),
) )
} }
@ -253,7 +254,7 @@ impl cosmic::Application for CosmicNetworkApplet {
&mut self.core &mut self.core
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> app::Task<Message> {
match message { match message {
Message::Frame(now) => self.timeline.now(now), Message::Frame(now) => self.timeline.now(now),
Message::TogglePopup => { Message::TogglePopup => {
@ -267,7 +268,7 @@ impl cosmic::Application for CosmicNetworkApplet {
self.timeline = Timeline::new(); self.timeline = Timeline::new();
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, None,
None, None,
@ -394,7 +395,7 @@ impl cosmic::Application for CosmicNetworkApplet {
let tx = if let Some(tx) = self.nm_sender.as_ref() { let tx = if let Some(tx) = self.nm_sender.as_ref() {
tx tx
} else { } else {
return Command::none(); return Task::none();
}; };
let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint( let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint(
@ -422,7 +423,7 @@ impl cosmic::Application for CosmicNetworkApplet {
let tx = if let Some(tx) = self.nm_sender.as_ref() { let tx = if let Some(tx) = self.nm_sender.as_ref() {
tx tx
} else { } else {
return Command::none(); return Task::none();
}; };
if let Some(NewConnectionState::EnterPassword { if let Some(NewConnectionState::EnterPassword {
@ -450,7 +451,7 @@ impl cosmic::Application for CosmicNetworkApplet {
} }
tx tx
} else { } else {
return Command::none(); return Task::none();
}; };
let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint(ssid)); let _ = tx.unbounded_send(NetworkManagerRequest::SelectAccessPoint(ssid));
} }
@ -470,7 +471,7 @@ impl cosmic::Application for CosmicNetworkApplet {
} }
tx tx
} else { } else {
return Command::none(); return Task::none();
}; };
let _ = tx.unbounded_send(NetworkManagerRequest::Disconnect(ssid)); let _ = tx.unbounded_send(NetworkManagerRequest::Disconnect(ssid));
} }
@ -529,7 +530,7 @@ impl cosmic::Application for CosmicNetworkApplet {
ap.clone() ap.clone()
} else { } else {
tracing::warn!("Failed to find known access point with ssid: {}", ssid); tracing::warn!("Failed to find known access point with ssid: {}", ssid);
return Command::none(); return Task::none();
}; };
if let Some(tx) = self.nm_sender.as_ref() { if let Some(tx) = self.nm_sender.as_ref() {
let _ = tx.unbounded_send(NetworkManagerRequest::Forget(ssid.clone())); let _ = tx.unbounded_send(NetworkManagerRequest::Forget(ssid.clone()));
@ -538,7 +539,7 @@ impl cosmic::Application for CosmicNetworkApplet {
} }
} }
} }
Command::none() Task::none()
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
@ -566,7 +567,7 @@ impl cosmic::Application for CosmicNetworkApplet {
} }
vpn_ethernet_col = vpn_ethernet_col.push(column![ vpn_ethernet_col = vpn_ethernet_col.push(column![
row![ row![
icon( icon::icon(
icon::from_name(self.icon_name.clone()) icon::from_name(self.icon_name.clone())
.symbolic(true) .symbolic(true)
.into() .into()
@ -575,9 +576,9 @@ impl cosmic::Application for CosmicNetworkApplet {
Column::with_children(ipv4), Column::with_children(ipv4),
text::body(fl!("connected")) text::body(fl!("connected"))
.width(Length::Fill) .width(Length::Fill)
.horizontal_alignment(Horizontal::Right), .align_x(Horizontal::Right),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(8) .spacing(8)
.padding(menu_control_padding()), .padding(menu_control_padding()),
padded_control(divider::horizontal::default()) padded_control(divider::horizontal::default())
@ -598,7 +599,7 @@ impl cosmic::Application for CosmicNetworkApplet {
vpn_ethernet_col = vpn_ethernet_col.push(column![ vpn_ethernet_col = vpn_ethernet_col.push(column![
row![ row![
icon( icon::icon(
icon::from_name(self.icon_name.clone()) icon::from_name(self.icon_name.clone())
.symbolic(true) .symbolic(true)
.into() .into()
@ -611,9 +612,9 @@ impl cosmic::Application for CosmicNetworkApplet {
fl!("megabits-per-second") fl!("megabits-per-second")
)) ))
.width(Length::Fill) .width(Length::Fill)
.horizontal_alignment(Horizontal::Right), .align_x(Horizontal::Right),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(8) .spacing(8)
.padding(menu_control_padding()), .padding(menu_control_padding()),
padded_control(divider::horizontal::default()) padded_control(divider::horizontal::default())
@ -651,8 +652,8 @@ impl cosmic::Application for CosmicNetworkApplet {
} }
ActiveConnectionState::Activated => btn_content.push( ActiveConnectionState::Activated => btn_content.push(
text::body(fl!("connected")) text::body(fl!("connected"))
.horizontal_alignment(Horizontal::Right) .align_x(Horizontal::Right)
.vertical_alignment(Vertical::Center) .align_y(Vertical::Center)
.into(), .into(),
), ),
_ => {} _ => {}
@ -671,11 +672,11 @@ impl cosmic::Application for CosmicNetworkApplet {
known_wifi.push(Element::from( known_wifi.push(Element::from(
column![menu_button( column![menu_button(
Row::with_children(btn_content) Row::with_children(btn_content)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(8) .spacing(8)
) )
.on_press(Message::Disconnect(name.clone()))] .on_press(Message::Disconnect(name.clone()))]
.align_items(Alignment::Center), .align_x(Alignment::Center),
)); ));
} }
}; };
@ -709,7 +710,7 @@ impl cosmic::Application for CosmicNetworkApplet {
.width(Length::Fill) .width(Length::Fill)
), ),
] ]
.align_items(Alignment::Center); .align_x(Alignment::Center);
if self.nm_state.airplane_mode { if self.nm_state.airplane_mode {
content = content.push( content = content.push(
column!( column!(
@ -722,7 +723,7 @@ impl cosmic::Application for CosmicNetworkApplet {
) )
.spacing(8) .spacing(8)
.padding([0, 0, 8, 0]) .padding([0, 0, 8, 0])
.align_items(Alignment::Center) .align_x(Alignment::Center)
.width(Length::Fill), .width(Length::Fill),
); );
} else { } else {
@ -778,7 +779,7 @@ impl cosmic::Application for CosmicNetworkApplet {
let mut btn = menu_button( let mut btn = menu_button(
Row::with_children(btn_content) Row::with_children(btn_content)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(8), .spacing(8),
); );
btn = match known.state { btn = match known.state {
@ -794,7 +795,7 @@ impl cosmic::Application for CosmicNetworkApplet {
} }
_ => btn, _ => btn,
}; };
known_wifi.push(Element::from(row![btn].align_items(Alignment::Center))); known_wifi.push(Element::from(row![btn].align_y(Alignment::Center)));
} }
let has_known_wifi = !known_wifi.is_empty(); let has_known_wifi = !known_wifi.is_empty();
@ -815,7 +816,7 @@ impl cosmic::Application for CosmicNetworkApplet {
text::body(fl!("visible-wireless-networks")) text::body(fl!("visible-wireless-networks"))
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fixed(24.0)) .height(Length::Fixed(24.0))
.vertical_alignment(Vertical::Center), .align_y(Vertical::Center),
container(icon::from_name(dropdown_icon).size(16).symbolic(true)) container(icon::from_name(dropdown_icon).size(16).symbolic(true))
.align_x(Horizontal::Center) .align_x(Horizontal::Center)
.align_y(Vertical::Center) .align_y(Vertical::Center)
@ -840,7 +841,7 @@ impl cosmic::Application for CosmicNetworkApplet {
.symbolic(true), .symbolic(true),
text::body(&access_point.ssid), text::body(&access_point.ssid),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(12), .spacing(12),
); );
content = content.push(id); content = content.push(id);
@ -862,7 +863,7 @@ impl cosmic::Application for CosmicNetworkApplet {
.spacing(24) .spacing(24)
] ]
.spacing(8) .spacing(8)
.align_items(Alignment::Center), .align_x(Alignment::Center),
) )
.align_x(Horizontal::Center); .align_x(Horizontal::Center);
content = content.push(col); content = content.push(col);
@ -874,7 +875,7 @@ impl cosmic::Application for CosmicNetworkApplet {
.symbolic(true), .symbolic(true),
text::body(&access_point.ssid), text::body(&access_point.ssid),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.width(Length::Fill) .width(Length::Fill)
.spacing(12); .spacing(12);
let connecting = padded_control( let connecting = padded_control(
@ -896,7 +897,7 @@ impl cosmic::Application for CosmicNetworkApplet {
.symbolic(true), .symbolic(true),
text::body(&access_point.ssid), text::body(&access_point.ssid),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(12), .spacing(12),
) )
.align_x(Horizontal::Center); .align_x(Horizontal::Center);
@ -915,7 +916,7 @@ impl cosmic::Application for CosmicNetworkApplet {
.spacing(24) .spacing(24)
] ]
.spacing(16) .spacing(16)
.align_items(Alignment::Center), .align_x(Alignment::Center),
) )
.align_x(Horizontal::Center); .align_x(Horizontal::Center);
content = content.push(col); content = content.push(col);
@ -937,9 +938,9 @@ impl cosmic::Application for CosmicNetworkApplet {
icon::from_name(wifi_icon(ap.strength)) icon::from_name(wifi_icon(ap.strength))
.size(16) .size(16)
.symbolic(true), .symbolic(true),
text::body(&ap.ssid).vertical_alignment(Vertical::Center) text::body(&ap.ssid).align_y(Vertical::Center)
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(12), .spacing(12),
) )
.on_press(Message::SelectWirelessAccessPoint(ap.clone())); .on_press(Message::SelectWirelessAccessPoint(ap.clone()));
@ -956,6 +957,8 @@ impl cosmic::Application for CosmicNetworkApplet {
self.core self.core
.applet .applet
.popup_container(content.padding([8, 0, 8, 0])) .popup_container(content.padding([8, 0, 8, 0]))
.max_width(400.)
.max_height(800.)
.into() .into()
} }
@ -985,7 +988,7 @@ impl cosmic::Application for CosmicNetworkApplet {
} }
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }

View file

@ -1,5 +1,8 @@
use super::{NetworkManagerEvent, NetworkManagerState}; use super::{NetworkManagerEvent, NetworkManagerState};
use cosmic::iced::{self, subscription}; use cosmic::{
iced::{self, Subscription},
iced_futures::stream,
};
use cosmic_dbus_networkmanager::nm::NetworkManager; use cosmic_dbus_networkmanager::nm::NetworkManager;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use std::{fmt::Debug, hash::Hash}; use std::{fmt::Debug, hash::Hash};
@ -10,15 +13,18 @@ pub fn active_conns_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>
conn: Connection, conn: Connection,
) -> iced::Subscription<NetworkManagerEvent> { ) -> iced::Subscription<NetworkManagerEvent> {
let initial = State::Continue(conn); let initial = State::Continue(conn);
subscription::channel(id, 50, move |mut output| { Subscription::run_with_id(
let mut state = initial; id,
stream::channel(50, move |mut output| {
let mut state = initial;
async move { async move {
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
}
} }
} }),
}) )
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -1,5 +1,5 @@
use super::{NetworkManagerEvent, NetworkManagerState}; use super::{NetworkManagerEvent, NetworkManagerState};
use cosmic::iced::{self, subscription}; use cosmic::iced::{self, stream, Subscription};
use cosmic_dbus_networkmanager::nm::NetworkManager; use cosmic_dbus_networkmanager::nm::NetworkManager;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use std::{fmt::Debug, hash::Hash}; use std::{fmt::Debug, hash::Hash};
@ -11,15 +11,18 @@ pub fn devices_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
conn: Connection, conn: Connection,
) -> iced::Subscription<NetworkManagerEvent> { ) -> iced::Subscription<NetworkManagerEvent> {
let initial = State::Continue(conn); let initial = State::Continue(conn);
subscription::channel((id, has_popup), 50, move |mut output| { Subscription::run_with_id(
let mut state = initial.clone(); (id, has_popup),
stream::channel(50, move |mut output| {
let mut state = initial.clone();
async move { async move {
loop { loop {
state = start_listening(state, has_popup, &mut output).await; state = start_listening(state, has_popup, &mut output).await;
}
} }
} }),
}) )
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -6,7 +6,10 @@ pub mod wireless_enabled;
use std::{collections::HashMap, fmt::Debug, time::Duration}; use std::{collections::HashMap, fmt::Debug, time::Duration};
use cosmic::iced::{self, subscription}; use cosmic::{
iced::{self, Subscription},
iced_futures::stream,
};
use cosmic_dbus_networkmanager::{ use cosmic_dbus_networkmanager::{
active_connection::ActiveConnection, active_connection::ActiveConnection,
device::SpecificDevice, device::SpecificDevice,
@ -42,13 +45,16 @@ pub enum State {
pub fn network_manager_subscription<I: Copy + Debug + std::hash::Hash + 'static>( pub fn network_manager_subscription<I: Copy + Debug + std::hash::Hash + 'static>(
id: I, id: I,
) -> iced::Subscription<NetworkManagerEvent> { ) -> iced::Subscription<NetworkManagerEvent> {
subscription::channel(id, 50, |mut output| async move { Subscription::run_with_id(
let mut state = State::Ready; id,
stream::channel(50, |mut output| async move {
let mut state = State::Ready;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}) }),
)
} }
async fn start_listening( async fn start_listening(

View file

@ -1,5 +1,8 @@
use super::{NetworkManagerEvent, NetworkManagerState}; use super::{NetworkManagerEvent, NetworkManagerState};
use cosmic::iced::{self, subscription}; use cosmic::{
iced::{self, Subscription},
iced_futures::stream,
};
use cosmic_dbus_networkmanager::nm::NetworkManager; use cosmic_dbus_networkmanager::nm::NetworkManager;
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use std::{fmt::Debug, hash::Hash}; use std::{fmt::Debug, hash::Hash};
@ -10,15 +13,18 @@ pub fn wireless_enabled_subscription<I: 'static + Hash + Copy + Send + Sync + De
conn: Connection, conn: Connection,
) -> iced::Subscription<NetworkManagerEvent> { ) -> iced::Subscription<NetworkManagerEvent> {
let initial = State::Continue(conn); let initial = State::Continue(conn);
subscription::channel(id, 50, move |mut output| { Subscription::run_with_id(
let mut state = initial; id,
stream::channel(50, move |mut output| {
let mut state = initial;
async move { async move {
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
}
} }
} }),
}) )
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -12,7 +12,7 @@ use cosmic::{
cosmic_config::{Config, CosmicConfigEntry}, cosmic_config::{Config, CosmicConfigEntry},
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced::{ iced::{
wayland::popup::{destroy_popup, get_popup}, platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::{column, row}, widget::{column, row},
window, Alignment, Length, Limits, Subscription, window, Alignment, Length, Limits, Subscription,
}, },
@ -20,10 +20,10 @@ use cosmic::{
iced_widget::{scrollable, Column}, iced_widget::{scrollable, Column},
theme, theme,
widget::{button, container, divider, icon, text}, widget::{button, container, divider, icon, text},
Command, Element, Theme, Element, Task,
}; };
use cosmic::{iced_futures::futures::executor::block_on, iced_style::application}; use cosmic::iced_futures::futures::executor::block_on;
use cosmic_notifications_config::NotificationsConfig; use cosmic_notifications_config::NotificationsConfig;
use cosmic_notifications_util::{Image, Notification}; use cosmic_notifications_util::{Image, Notification};
@ -35,7 +35,7 @@ use tracing::info;
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
localize::localize(); localize::localize();
cosmic::applet::run::<Notifications>(false, ()) cosmic::applet::run::<Notifications>(())
} }
static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique); static DO_NOT_DISTURB: Lazy<id::Toggler> = Lazy::new(id::Toggler::unique);
@ -106,7 +106,7 @@ impl cosmic::Application for Notifications {
_flags: Self::Flags, _flags: Self::Flags,
) -> ( ) -> (
Self, Self,
cosmic::iced::Command<cosmic::app::Message<Self::Message>>, cosmic::iced::Task<cosmic::app::Message<Self::Message>>,
) { ) {
let helper = Config::new( let helper = Config::new(
cosmic_notifications_config::ID, cosmic_notifications_config::ID,
@ -139,7 +139,7 @@ impl cosmic::Application for Notifications {
.expect("Failed to get proxy"), .expect("Failed to get proxy"),
}; };
_self.update_icon(); _self.update_icon();
(_self, Command::none()) (_self, Task::none())
} }
fn core(&self) -> &cosmic::app::Core { fn core(&self) -> &cosmic::app::Core {
@ -150,7 +150,7 @@ impl cosmic::Application for Notifications {
&mut self.core &mut self.core
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
@ -177,7 +177,7 @@ impl cosmic::Application for Notifications {
fn update( fn update(
&mut self, &mut self,
message: Self::Message, message: Self::Message,
) -> cosmic::iced::Command<cosmic::app::Message<Self::Message>> { ) -> cosmic::iced::Task<cosmic::app::Message<Self::Message>> {
match message { match message {
Message::Frame(now) => { Message::Frame(now) => {
self.timeline.now(now); self.timeline.now(now);
@ -191,7 +191,7 @@ impl cosmic::Application for Notifications {
self.timeline = Timeline::new(); self.timeline = Timeline::new();
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, None,
None, None,
@ -317,7 +317,7 @@ impl cosmic::Application for Notifications {
*n_expanded = expanded; *n_expanded = expanded;
id.clone() id.clone()
} else { } else {
return Command::none(); return Task::none();
}; };
self.update_cards(id); self.update_cards(id);
} }
@ -354,7 +354,7 @@ impl cosmic::Application for Notifications {
}, },
}; };
self.update_icon(); self.update_icon();
Command::none() Task::none()
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
@ -387,7 +387,7 @@ impl cosmic::Application for Notifications {
text_icon("cosmic-applet-notification-symbolic", 40), text_icon("cosmic-applet-notification-symbolic", 40),
text::body(no_notifications) text::body(no_notifications)
] ]
.align_items(Alignment::Center) .align_x(Alignment::Center)
) )
.width(Length::Fill) .width(Length::Fill)
.align_x(Horizontal::Center)] .align_x(Horizontal::Center)]
@ -433,7 +433,7 @@ impl cosmic::Application for Notifications {
.symbolic(true), .symbolic(true),
) )
.on_press(Message::Dismissed(n.id)) .on_press(Message::Dismissed(n.id))
.style(cosmic::theme::Button::Text); .class(cosmic::theme::Button::Text);
Element::from( Element::from(
column!( column!(
match n.image() { match n.image() {
@ -445,7 +445,7 @@ impl cosmic::Application for Notifications {
close_notif close_notif
] ]
.spacing(8) .spacing(8)
.align_items(Alignment::Center) .align_y(Alignment::Center)
} }
Some(cosmic_notifications_util::Image::Name(name)) => { Some(cosmic_notifications_util::Image::Name(name)) => {
row![ row![
@ -455,7 +455,7 @@ impl cosmic::Application for Notifications {
close_notif close_notif
] ]
.spacing(8) .spacing(8)
.align_items(Alignment::Center) .align_y(Alignment::Center)
} }
Some(cosmic_notifications_util::Image::Data { Some(cosmic_notifications_util::Image::Data {
width, width,
@ -471,11 +471,11 @@ impl cosmic::Application for Notifications {
close_notif close_notif
] ]
.spacing(8) .spacing(8)
.align_items(Alignment::Center) .align_y(Alignment::Center)
} }
None => row![app_name, duration_since, close_notif] None => row![app_name, duration_since, close_notif]
.spacing(8) .spacing(8)
.align_items(Alignment::Center), .align_y(Alignment::Center),
}, },
column![ column![
text::body(n.summary.lines().next().unwrap_or_default()) text::body(n.summary.lines().next().unwrap_or_default())
@ -546,10 +546,14 @@ impl cosmic::Application for Notifications {
]; ];
let content = column![do_not_disturb, main_content] let content = column![do_not_disturb, main_content]
.align_items(Alignment::Start) .align_x(Alignment::Start)
.padding([8, 0]); .padding([8, 0]);
self.core.applet.popup_container(content).into() self.core
.applet
.popup_container(content)
.limits(Limits::NONE.max_width(444.).max_height(900.))
.into()
} }
fn on_close_requested(&self, id: window::Id) -> Option<Message> { fn on_close_requested(&self, id: window::Id) -> Option<Message> {

View file

@ -1,13 +1,12 @@
// Copyright 2023 System76 <info@system76.com> // Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use std::ops::Sub;
use crate::subscriptions::freedesktop_proxy::NotificationsProxy; use crate::subscriptions::freedesktop_proxy::NotificationsProxy;
use cosmic::{ use cosmic::{
iced::{ iced::futures::{self, SinkExt},
futures::{self, SinkExt}, iced_futures::{stream, Subscription},
subscription,
},
iced_futures::Subscription,
}; };
use tokio::sync::mpsc::{channel, Receiver, Sender}; use tokio::sync::mpsc::{channel, Receiver, Sender};
use tracing::{error, warn}; use tracing::{error, warn};
@ -35,10 +34,9 @@ pub enum Output {
pub fn proxy() -> Subscription<Output> { pub fn proxy() -> Subscription<Output> {
struct SomeWorker; struct SomeWorker;
subscription::channel( Subscription::run_with_id(
std::any::TypeId::of::<SomeWorker>(), std::any::TypeId::of::<SomeWorker>(),
50, stream::channel(50, |mut output| async move {
|mut output| async move {
let mut state = State::Ready; let mut state = State::Ready;
loop { loop {
@ -103,6 +101,6 @@ pub fn proxy() -> Subscription<Output> {
} }
} }
} }
}, }),
) )
} }

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use cosmic::{ use cosmic::{
iced::{futures, subscription}, iced::{futures, stream},
iced_futures::Subscription, iced_futures::Subscription,
}; };
use cosmic_notifications_util::Notification; use cosmic_notifications_util::Notification;
@ -27,10 +27,9 @@ pub enum State {
pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<Notification> { pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<Notification> {
struct SomeWorker; struct SomeWorker;
subscription::channel( Subscription::run_with_id(
std::any::TypeId::of::<SomeWorker>(), std::any::TypeId::of::<SomeWorker>(),
50, stream::channel(50, |mut output| async move {
|mut output| async move {
let mut state = State::WaitingForNotificationEvent(0); let mut state = State::WaitingForNotificationEvent(0);
loop { loop {
@ -76,7 +75,7 @@ pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<N
} }
} }
} }
}, }),
) )
} }

View file

@ -4,34 +4,35 @@
use std::{collections::HashMap, process, time::Duration}; use std::{collections::HashMap, process, time::Duration};
use cosmic::{ use cosmic::{
app::Command, app,
applet::{menu_button, padded_control}, applet::{menu_button, padded_control},
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced,
iced::{ iced::{
self,
alignment::{Horizontal, Vertical}, alignment::{Horizontal, Vertical},
event::{ event::{
listen_with, listen_with,
wayland::{self, LayerEvent}, wayland::{self, LayerEvent},
PlatformSpecific, PlatformSpecific,
}, },
time, platform_specific::{
wayland::{ runtime::wayland::layer_surface::SctkLayerSurfaceSettings,
actions::layer_surface::SctkLayerSurfaceSettings, shell::commands::{
popup::{destroy_popup, get_popup}, layer_surface::{
destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity,
},
popup::{destroy_popup, get_popup},
},
}, },
widget::{self, column, container, row, space::Space}, time,
widget::{self, column, container, row},
window, Alignment, Length, Subscription, window, Alignment, Length, Subscription,
}, },
iced_runtime::core::layout::Limits, iced_runtime::core::layout::Limits,
iced_sctk::commands::layer_surface::{
destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity,
},
iced_style::application,
iced_widget::mouse_area, iced_widget::mouse_area,
theme, theme,
widget::{button, divider, horizontal_space, icon, text, vertical_space, Column}, widget::{button, divider, horizontal_space, icon, text, vertical_space, Column, Space},
Element, Theme, Element, Task,
}; };
use logind_zbus::{ use logind_zbus::{
@ -52,7 +53,7 @@ use crate::{cosmic_session::CosmicSessionProxy, session_manager::SessionManagerP
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
localize::localize(); localize::localize();
cosmic::applet::run::<Power>(false, ()) cosmic::applet::run::<Power>(())
} }
const COUNTDOWN_LENGTH: u8 = 60; const COUNTDOWN_LENGTH: u8 = 60;
@ -76,14 +77,14 @@ enum PowerAction {
} }
impl PowerAction { impl PowerAction {
fn perform(self) -> iced::Command<cosmic::app::Message<Message>> { fn perform(self) -> iced::Task<cosmic::app::Message<Message>> {
let msg = |m| cosmic::app::message::app(Message::Zbus(m)); let msg = |m| cosmic::app::message::app(Message::Zbus(m));
match self { match self {
PowerAction::Lock => iced::Command::perform(lock(), msg), PowerAction::Lock => iced::Task::perform(lock(), msg),
PowerAction::LogOut => iced::Command::perform(log_out(), msg), PowerAction::LogOut => iced::Task::perform(log_out(), msg),
PowerAction::Suspend => iced::Command::perform(suspend(), msg), PowerAction::Suspend => iced::Task::perform(suspend(), msg),
PowerAction::Restart => iced::Command::perform(restart(), msg), PowerAction::Restart => iced::Task::perform(restart(), msg),
PowerAction::Shutdown => iced::Command::perform(shutdown(), msg), PowerAction::Shutdown => iced::Task::perform(shutdown(), msg),
} }
} }
} }
@ -115,14 +116,14 @@ impl cosmic::Application for Power {
&mut self.core &mut self.core
} }
fn init(core: cosmic::app::Core, _flags: ()) -> (Self, Command<Message>) { fn init(core: cosmic::app::Core, _flags: ()) -> (Self, app::Task<Message>) {
( (
Self { Self {
core, core,
icon_name: "system-shutdown-symbolic".to_string(), icon_name: "system-shutdown-symbolic".to_string(),
..Default::default() ..Default::default()
}, },
Command::none(), Task::none(),
) )
} }
@ -132,7 +133,7 @@ impl cosmic::Application for Power {
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
let mut subscriptions = Vec::with_capacity(2); let mut subscriptions = Vec::with_capacity(2);
subscriptions.push(listen_with(|e, _status| match e { subscriptions.push(listen_with(|e, _status, _| match e {
cosmic::iced::Event::PlatformSpecific(PlatformSpecific::Wayland( cosmic::iced::Event::PlatformSpecific(PlatformSpecific::Wayland(
wayland::Event::Layer(LayerEvent::Unfocused, ..), wayland::Event::Layer(LayerEvent::Unfocused, ..),
)) => Some(Message::Cancel), )) => Some(Message::Cancel),
@ -148,7 +149,7 @@ impl cosmic::Application for Power {
Subscription::batch(subscriptions) Subscription::batch(subscriptions)
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> app::Task<Message> {
match message { match message {
Message::TogglePopup => { Message::TogglePopup => {
if let Some(p) = self.popup.take() { if let Some(p) = self.popup.take() {
@ -158,9 +159,9 @@ impl cosmic::Application for Power {
self.popup.replace(new_id); self.popup.replace(new_id);
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, Some((500, 500)),
None, None,
None, None,
); );
@ -174,7 +175,7 @@ impl cosmic::Application for Power {
} }
Message::Settings => { Message::Settings => {
let _ = process::Command::new("cosmic-settings").spawn(); let _ = process::Command::new("cosmic-settings").spawn();
Command::none() Task::none()
} }
Message::Action(action) => { Message::Action(action) => {
// Ask for user confirmation of non-destructive actions only // Ask for user confirmation of non-destructive actions only
@ -201,20 +202,20 @@ impl cosmic::Application for Power {
if let Err(e) = result { if let Err(e) = result {
eprintln!("cosmic-applet-power ERROR: '{}'", e); eprintln!("cosmic-applet-power ERROR: '{}'", e);
} }
Command::none() Task::none()
} }
Message::Confirm => { Message::Confirm => {
if let Some((id, a, _)) = self.action_to_confirm.take() { if let Some((id, a, _)) = self.action_to_confirm.take() {
Command::batch(vec![destroy_layer_surface(id), a.perform()]) app::Task::batch(vec![destroy_layer_surface(id), a.perform()])
} else { } else {
Command::none() Task::none()
} }
} }
Message::Cancel => { Message::Cancel => {
if let Some((id, _, _)) = self.action_to_confirm.take() { if let Some((id, _, _)) = self.action_to_confirm.take() {
return destroy_layer_surface(id); return destroy_layer_surface(id);
} }
Command::none() Task::none()
} }
Message::Countdown => { Message::Countdown => {
if let Some((surface_id, a, countdown)) = self.action_to_confirm.as_mut() { if let Some((surface_id, a, countdown)) = self.action_to_confirm.as_mut() {
@ -224,16 +225,16 @@ impl cosmic::Application for Power {
let a = *a; let a = *a;
self.action_to_confirm = None; self.action_to_confirm = None;
return Command::batch(vec![destroy_layer_surface(id), a.perform()]); return app::Task::batch(vec![destroy_layer_surface(id), a.perform()]);
} }
} }
Command::none() Task::none()
} }
Message::Closed(id) => { Message::Closed(id) => {
if self.popup == Some(id) { if self.popup == Some(id) {
self.popup = None; self.popup = None;
} }
Command::none() Task::none()
} }
Message::LayerFocus => button::focus(CONFIRM_ID.clone()), Message::LayerFocus => button::focus(CONFIRM_ID.clone()),
} }
@ -266,7 +267,7 @@ impl cosmic::Application for Power {
Space::with_width(Length::Fill), Space::with_width(Length::Fill),
text::body(fl!("lock-screen-shortcut")), text::body(fl!("lock-screen-shortcut")),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
) )
.on_press(Message::Action(PowerAction::Lock)), .on_press(Message::Action(PowerAction::Lock)),
@ -277,7 +278,7 @@ impl cosmic::Application for Power {
Space::with_width(Length::Fill), Space::with_width(Length::Fill),
text::body(fl!("log-out-shortcut")), text::body(fl!("log-out-shortcut")),
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
) )
.on_press(Message::Action(PowerAction::LogOut)), .on_press(Message::Action(PowerAction::LogOut)),
@ -301,10 +302,15 @@ impl cosmic::Application for Power {
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]), padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
power power
] ]
.align_items(Alignment::Start) .align_x(Alignment::Start)
.padding([8, 0]); .padding([8, 0]);
self.core.applet.popup_container(content).into() self.core
.applet
.popup_container(content)
.max_height(400.)
.max_width(500.)
.into()
} else if matches!(self.action_to_confirm, Some((c_id, _, _)) if c_id == id) { } else if matches!(self.action_to_confirm, Some((c_id, _, _)) if c_id == id) {
let cosmic_theme = self.core.system_theme().cosmic(); let cosmic_theme = self.core.system_theme().cosmic();
let (_, power_action, countdown) = self.action_to_confirm.as_ref().unwrap(); let (_, power_action, countdown) = self.action_to_confirm.as_ref().unwrap();
@ -335,7 +341,7 @@ impl cosmic::Application for Power {
)) ))
.padding([0, cosmic_theme.space_s()]) .padding([0, cosmic_theme.space_s()])
.id(CONFIRM_ID.clone()) .id(CONFIRM_ID.clone())
.style(theme::Button::Suggested) .class(theme::Button::Suggested)
.on_press(Message::Confirm), .on_press(Message::Confirm),
) )
.secondary_action( .secondary_action(
@ -345,7 +351,7 @@ impl cosmic::Application for Power {
32.0, 32.0,
)) ))
.padding([0, cosmic_theme.space_s()]) .padding([0, cosmic_theme.space_s()])
.style(theme::Button::Standard) .class(theme::Button::Standard)
.on_press(Message::Cancel), .on_press(Message::Cancel),
) )
.icon(text_icon( .icon(text_icon(
@ -365,24 +371,26 @@ impl cosmic::Application for Power {
); );
} }
mouse_area( Element::from(
container(dialog) mouse_area(
.align_x(Horizontal::Center) container(dialog)
.align_y(Vertical::Center) .align_x(Horizontal::Center)
.width(Length::Fill) .align_y(Vertical::Center)
.height(Length::Fill), .width(Length::Fill)
.height(Length::Fill),
)
.on_press(Message::Cancel)
.on_right_press(Message::Cancel)
.on_middle_press(Message::Cancel),
) )
.on_press(Message::Cancel)
.on_right_press(Message::Cancel)
.on_middle_press(Message::Cancel)
.into()
} else { } else {
//panic!("no view for window {}", id.0) //panic!("no view for window {}", id.0)
widget::text("").into() widget::text("").into()
} }
.into()
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
} }
@ -391,12 +399,12 @@ fn power_buttons(name: &str, msg: String) -> cosmic::widget::Button<Message> {
button::custom( button::custom(
column![text_icon(name, 40), text::body(msg)] column![text_icon(name, 40), text::body(msg)]
.spacing(4) .spacing(4)
.align_items(Alignment::Center) .align_x(Alignment::Center)
.width(Length::Fill), .width(Length::Fill),
) )
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fixed(76.0)) .height(Length::Fixed(76.0))
.style(theme::Button::Text) .class(theme::Button::Text)
} }
fn text_icon(name: &str, size: u16) -> cosmic::widget::Icon { fn text_icon(name: &str, size: u16) -> cosmic::widget::Icon {
@ -483,8 +491,8 @@ fn min_width_and_height<'a>(
height: impl Into<Length>, height: impl Into<Length>,
) -> Column<'a, Message> { ) -> Column<'a, Message> {
column![ column![
row![e, vertical_space(height)].align_items(Alignment::Center), row![e, vertical_space().height(height)].align_y(Alignment::Center),
horizontal_space(width) horizontal_space().width(width)
] ]
.align_items(Alignment::Center) .align_x(Alignment::Center)
} }

View file

@ -2,19 +2,15 @@
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use cosmic::{ use cosmic::{
app::{self, Command}, app,
applet::cosmic_panel_config::PanelAnchor, applet::cosmic_panel_config::PanelAnchor,
iced::{ iced::{
self, self,
wayland::{ platform_specific::shell::commands::popup::{destroy_popup, get_popup},
popup::{destroy_popup, get_popup}, window, Limits, Subscription,
window::resize_window,
},
window, Subscription,
}, },
iced_style::application,
widget::mouse_area, widget::mouse_area,
Theme, Element, Task,
}; };
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -50,11 +46,14 @@ impl App {
window::Id::unique() window::Id::unique()
} }
fn resize_window(&self) -> Command<Msg> { fn resize_window(&self) -> app::Task<Msg> {
let icon_size = self.core.applet.suggested_size(true).0 as u32 let icon_size = self.core.applet.suggested_size(true).0 as u32
+ self.core.applet.suggested_padding(true) as u32 * 2; + self.core.applet.suggested_padding(true) as u32 * 2;
let n = self.menus.len() as u32; let n = self.menus.len() as u32;
resize_window(window::Id::MAIN, 1.max(icon_size * n), icon_size) window::resize(
self.core.main_window_id().unwrap(),
iced::Size::new(1.max(icon_size * n) as f32, icon_size as f32),
)
} }
} }
@ -64,13 +63,13 @@ impl cosmic::Application for App {
type Flags = (); type Flags = ();
const APP_ID: &'static str = "com.system76.CosmicAppletStatusArea"; const APP_ID: &'static str = "com.system76.CosmicAppletStatusArea";
fn init(core: app::Core, _flags: ()) -> (Self, app::Command<Msg>) { fn init(core: app::Core, _flags: ()) -> (Self, app::Task<Msg>) {
( (
Self { Self {
core, core,
..Self::default() ..Self::default()
}, },
Command::none(), Task::none(),
) )
} }
@ -82,29 +81,29 @@ impl cosmic::Application for App {
&mut self.core &mut self.core
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
fn update(&mut self, message: Msg) -> Command<Msg> { fn update(&mut self, message: Msg) -> app::Task<Msg> {
match message { match message {
Msg::Closed(surface) => { Msg::Closed(surface) => {
if self.popup == Some(surface) { if self.popup == Some(surface) {
self.popup = None; self.popup = None;
self.open_menu = None; self.open_menu = None;
} }
Command::none() Task::none()
} }
Msg::StatusMenu((id, msg)) => match self.menus.get_mut(&id) { Msg::StatusMenu((id, msg)) => match self.menus.get_mut(&id) {
Some(state) => state Some(state) => state
.update(msg) .update(msg)
.map(move |msg| app::message::app(Msg::StatusMenu((id, msg)))), .map(move |msg| app::message::app(Msg::StatusMenu((id, msg)))),
None => Command::none(), None => Task::none(),
}, },
Msg::StatusNotifier(event) => match event { Msg::StatusNotifier(event) => match event {
status_notifier_watcher::Event::Connected(connection) => { status_notifier_watcher::Event::Connected(connection) => {
self.connection = Some(connection); self.connection = Some(connection);
Command::none() Task::none()
} }
status_notifier_watcher::Event::Registered(name) => { status_notifier_watcher::Event::Registered(name) => {
let (state, cmd) = status_menu::State::new(name); let (state, cmd) = status_menu::State::new(name);
@ -119,7 +118,7 @@ impl cosmic::Application for App {
} }
let id = self.next_menu_id(); let id = self.next_menu_id();
self.menus.insert(id, state); self.menus.insert(id, state);
Command::batch([ app::Task::batch([
self.resize_window(), self.resize_window(),
cmd.map(move |msg| app::message::app(Msg::StatusMenu((id, msg)))), cmd.map(move |msg| app::message::app(Msg::StatusMenu((id, msg)))),
]) ])
@ -140,7 +139,7 @@ impl cosmic::Application for App {
} }
status_notifier_watcher::Event::Error(err) => { status_notifier_watcher::Event::Error(err) => {
eprintln!("Status notifier error: {}", err); eprintln!("Status notifier error: {}", err);
Command::none() Task::none()
} }
}, },
Msg::TogglePopup(id) => { Msg::TogglePopup(id) => {
@ -158,7 +157,7 @@ impl cosmic::Application for App {
} }
let popup_id = self.next_popup_id(); let popup_id = self.next_popup_id();
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
popup_id, popup_id,
None, None,
None, None,
@ -179,13 +178,13 @@ impl cosmic::Application for App {
popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32; popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32;
} }
cmds.push(get_popup(popup_settings)); cmds.push(get_popup(popup_settings));
return Command::batch(cmds); return app::Task::batch(cmds);
} else if let Some(popup_id) = self.popup { } else if let Some(popup_id) = self.popup {
self.menus[&id].closed(); self.menus[&id].closed();
return destroy_popup(popup_id); return destroy_popup(popup_id);
} }
Command::none() Task::none()
} }
Msg::Hovered(id) => { Msg::Hovered(id) => {
let mut cmds = Vec::new(); let mut cmds = Vec::new();
@ -197,14 +196,14 @@ impl cosmic::Application for App {
self.open_menu = Some(id); self.open_menu = Some(id);
} else { } else {
self.open_menu = Some(old_id); self.open_menu = Some(old_id);
return Command::none(); return Task::none();
} }
} else { } else {
return Command::none(); return Task::none();
} }
let popup_id = self.next_popup_id(); let popup_id = self.next_popup_id();
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
popup_id, popup_id,
None, None,
None, None,
@ -225,7 +224,7 @@ impl cosmic::Application for App {
popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32; popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32;
} }
cmds.push(get_popup(popup_settings)); cmds.push(get_popup(popup_settings));
Command::batch(cmds) app::Task::batch(cmds)
} }
} }
} }
@ -254,17 +253,22 @@ impl cosmic::Application for App {
} }
.on_press_down(Msg::TogglePopup(*id)), .on_press_down(Msg::TogglePopup(*id)),
) )
.on_mouse_enter(Msg::Hovered(*id)) .on_enter(Msg::Hovered(*id))
.into() .into()
}); });
if matches!( self.core
self.core.applet.anchor, .applet
PanelAnchor::Left | PanelAnchor::Right .autosize_window(
) { if matches!(
iced::widget::column(children).into() self.core.applet.anchor,
} else { PanelAnchor::Left | PanelAnchor::Right
iced::widget::row(children).into() ) {
} Element::from(iced::widget::column(children))
} else {
iced::widget::row(children).into()
},
)
.into()
} }
fn view_window(&self, _surface: window::Id) -> cosmic::Element<'_, Msg> { fn view_window(&self, _surface: window::Id) -> cosmic::Element<'_, Msg> {
@ -274,6 +278,7 @@ impl cosmic::Application for App {
.core .core
.applet .applet
.popup_container(menu.popup_view().map(move |msg| Msg::StatusMenu((id, msg)))) .popup_container(menu.popup_view().map(move |msg| Msg::StatusMenu((id, msg))))
.limits(Limits::NONE.min_width(1.).min_height(1.).max_width(300.))
.into(), .into(),
None => unreachable!(), None => unreachable!(),
}, },
@ -287,5 +292,5 @@ impl cosmic::Application for App {
} }
pub fn main() -> iced::Result { pub fn main() -> iced::Result {
cosmic::applet::run::<App>(true, ()) cosmic::applet::run::<App>(())
} }

View file

@ -18,18 +18,18 @@ pub struct State {
} }
impl State { impl State {
pub fn new(item: StatusNotifierItem) -> (Self, iced::Command<Msg>) { pub fn new(item: StatusNotifierItem) -> (Self, iced::Task<Msg>) {
( (
Self { Self {
item, item,
layout: None, layout: None,
expanded: None, expanded: None,
}, },
iced::Command::none(), iced::Task::none(),
) )
} }
pub fn update(&mut self, message: Msg) -> iced::Command<Msg> { pub fn update(&mut self, message: Msg) -> iced::Task<Msg> {
match message { match message {
Msg::Layout(layout) => { Msg::Layout(layout) => {
match layout { match layout {
@ -38,7 +38,7 @@ impl State {
} }
Err(err) => eprintln!("Error getting layout from icon: {}", err), Err(err) => eprintln!("Error getting layout from icon: {}", err),
} }
iced::Command::none() iced::Task::none()
} }
Msg::Click(id, is_submenu) => { Msg::Click(id, is_submenu) => {
let menu_proxy = self.item.menu_proxy().clone(); let menu_proxy = self.item.menu_proxy().clone();
@ -54,7 +54,7 @@ impl State {
} else { } else {
// TODO: Close menu? // TODO: Close menu?
} }
iced::Command::none() iced::Task::none()
} }
} }
} }
@ -135,7 +135,7 @@ fn layout_view(layout: &Layout, expanded: Option<i32>) -> cosmic::Element<Msg> {
children.push(icon.into()); children.push(icon.into());
} }
if let Some(icon_data) = i.icon_data() { if let Some(icon_data) = i.icon_data() {
let handle = iced::widget::image::Handle::from_memory(icon_data.to_vec()); let handle = iced::widget::image::Handle::from_bytes(icon_data.to_vec());
children.insert(0, iced::widget::Image::new(handle).into()); children.insert(0, iced::widget::Image::new(handle).into());
} else if let Some(icon_name) = i.icon_name() { } else if let Some(icon_name) = i.icon_name() {
let icon = cosmic::widget::icon::from_name(icon_name) let icon = cosmic::widget::icon::from_name(icon_name)
@ -177,7 +177,7 @@ fn row_button(content: Vec<cosmic::Element<Msg>>) -> cosmic::widget::Button<Msg>
menu_button( menu_button(
iced::widget::Row::with_children(content) iced::widget::Row::with_children(content)
.spacing(8) .spacing(8)
.align_items(iced::Alignment::Center) .align_y(iced::Alignment::Center)
.width(iced::Length::Fill), .width(iced::Length::Fill),
) )
} }

View file

@ -1,7 +1,10 @@
// Copyright 2023 System76 <info@system76.com> // Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
use cosmic::{iced, widget::icon}; use cosmic::{
iced::{self, Subscription},
widget::icon,
};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use zbus::zvariant::{self, OwnedValue}; use zbus::zvariant::{self, OwnedValue};
@ -82,7 +85,7 @@ impl StatusNotifierItem {
// TODO: Only fetch changed part of layout, if that's any faster // TODO: Only fetch changed part of layout, if that's any faster
pub fn layout_subscription(&self) -> iced::Subscription<Result<Layout, String>> { pub fn layout_subscription(&self) -> iced::Subscription<Result<Layout, String>> {
let menu_proxy = self.menu_proxy.clone(); let menu_proxy = self.menu_proxy.clone();
iced::subscription::run_with_id( Subscription::run_with_id(
format!("status-notifier-item-{}", &self.name), format!("status-notifier-item-{}", &self.name),
async move { async move {
let initial = futures::stream::once(get_layout(menu_proxy.clone())); let initial = futures::stream::once(get_layout(menu_proxy.clone()));

View file

@ -3,8 +3,8 @@
// TODO: Both this and server proxy could emit same events, have way to generate stream from either? // TODO: Both this and server proxy could emit same events, have way to generate stream from either?
use cosmic::iced; use cosmic::iced::{self, Subscription};
use futures::StreamExt; use futures::{stream, StreamExt};
use crate::subscriptions::status_notifier_item::StatusNotifierItem; use crate::subscriptions::status_notifier_item::StatusNotifierItem;
@ -26,24 +26,23 @@ enum State {
} }
pub fn subscription() -> iced::Subscription<Event> { pub fn subscription() -> iced::Subscription<Event> {
iced::subscription::unfold( Subscription::run_with_id(
"status-notifier-watcher", "status-notifier-watcher",
State::NotConnected, stream::unfold(State::NotConnected, |state| async move {
|state| async move {
match state { match state {
State::NotConnected => match connect().await { State::NotConnected => match connect().await {
Ok((connection, stream)) => { Ok((connection, stream)) => {
(Event::Connected(connection), State::Connected(stream)) Some((Event::Connected(connection), State::Connected(stream)))
} }
Err(err) => (Event::Error(err.to_string()), State::Failed), Err(err) => Some((Event::Error(err.to_string()), State::Failed)),
}, },
State::Connected(mut stream) => match stream.next().await { State::Connected(mut stream) => match stream.next().await {
Some(event) => (event, State::Connected(stream)), Some(event) => Some((event, State::Connected(stream))),
None => iced::futures::future::pending().await, None => None,
}, },
State::Failed => iced::futures::future::pending().await, State::Failed => None,
} }
}, }),
) )
} }

View file

@ -11,5 +11,5 @@ mod window;
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
localize::localize(); localize::localize();
cosmic::applet::run::<Window>(false, ()) cosmic::applet::run::<Window>(())
} }

View file

@ -6,7 +6,7 @@ use cctk::sctk::reexports::calloop::channel::SyncSender;
use cosmic::iced::{ use cosmic::iced::{
self, self,
futures::{self, channel::mpsc, SinkExt, StreamExt}, futures::{self, channel::mpsc, SinkExt, StreamExt},
subscription, stream, Subscription,
}; };
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState; use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -23,16 +23,15 @@ pub enum WorkspacesUpdate {
} }
pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> { pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> {
subscription::channel( Subscription::run_with_id(
std::any::TypeId::of::<WorkspacesUpdate>(), std::any::TypeId::of::<WorkspacesUpdate>(),
50, stream::channel(50, move |mut output| async move {
move |mut output| async move {
let mut state = State::Waiting; let mut state = State::Waiting;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}, }),
) )
} }

View file

@ -11,11 +11,10 @@ use cosmic::{
cosmic_config::{Config, ConfigSet, CosmicConfigEntry}, cosmic_config::{Config, ConfigSet, CosmicConfigEntry},
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced::{ iced::{
wayland::popup::{destroy_popup, get_popup}, platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
window::Id, window::Id,
Command, Length, Limits, Subscription, Length, Limits, Subscription, Task,
}, },
iced_style::application,
iced_widget::{column, row}, iced_widget::{column, row},
theme, theme,
widget::{ widget::{
@ -23,7 +22,7 @@ use cosmic::{
segmented_button::{self, Entity, SingleSelectModel}, segmented_button::{self, Entity, SingleSelectModel},
segmented_control, spin_button, text, segmented_control, spin_button, text,
}, },
Element, Theme, Element,
}; };
use cosmic_comp_config::{CosmicCompConfig, TileBehavior}; use cosmic_comp_config::{CosmicCompConfig, TileBehavior};
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState; use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState;
@ -77,10 +76,7 @@ impl cosmic::Application for Window {
&mut self.core &mut self.core
} }
fn init( fn init(core: Core, _flags: Self::Flags) -> (Self, Task<cosmic::app::Message<Self::Message>>) {
core: Core,
_flags: Self::Flags,
) -> (Self, Command<cosmic::app::Message<Self::Message>>) {
let mut gaps = spin_button::Model::default().max(99).min(0).step(1); let mut gaps = spin_button::Model::default().max(99).min(0).step(1);
gaps.value = core.system_theme().cosmic().gaps.1 as i32; gaps.value = core.system_theme().cosmic().gaps.1 as i32;
let mut active_hint = spin_button::Model::default().max(99).min(0).step(1); let mut active_hint = spin_button::Model::default().max(99).min(0).step(1);
@ -127,7 +123,7 @@ impl cosmic::Application for Window {
tile_windows: id::Toggler::unique(), tile_windows: id::Toggler::unique(),
active_hint: id::Toggler::unique(), active_hint: id::Toggler::unique(),
}; };
(window, Command::none()) (window, Task::none())
} }
fn on_close_requested(&self, id: Id) -> Option<Message> { fn on_close_requested(&self, id: Id) -> Option<Message> {
@ -148,7 +144,7 @@ impl cosmic::Application for Window {
]) ])
} }
fn update(&mut self, message: Self::Message) -> Command<cosmic::app::Message<Self::Message>> { fn update(&mut self, message: Self::Message) -> Task<cosmic::app::Message<Self::Message>> {
match message { match message {
Message::WorkspaceUpdate(msg) => match msg { Message::WorkspaceUpdate(msg) => match msg {
WorkspacesUpdate::State(state) => { WorkspacesUpdate::State(state) => {
@ -178,11 +174,14 @@ impl cosmic::Application for Window {
self.tile_windows = id::Toggler::unique(); self.tile_windows = id::Toggler::unique();
self.active_hint = id::Toggler::unique(); self.active_hint = id::Toggler::unique();
let new_id = Id::unique(); let new_id = Id::unique();
self.popup.replace(new_id); self.popup = Some(new_id);
let mut popup_settings = let mut popup_settings = self.core.applet.get_popup_settings(
self.core self.core.main_window_id().unwrap(),
.applet new_id,
.get_popup_settings(Id::MAIN, new_id, None, None, None); Some((1, 1)),
None,
None,
);
popup_settings.positioner.size_limits = Limits::NONE popup_settings.positioner.size_limits = Limits::NONE
.max_width(400.0) .max_width(400.0)
.min_width(300.0) .min_width(300.0)
@ -276,7 +275,7 @@ impl cosmic::Application for Window {
tokio::spawn(cosmic::process::spawn(cmd)); tokio::spawn(cosmic::process::spawn(cmd));
} }
} }
Command::none() Task::none()
} }
fn view(&self) -> Element<Self::Message> { fn view(&self) -> Element<Self::Message> {
@ -355,10 +354,14 @@ impl cosmic::Application for Window {
] ]
.padding([8, 0]); .padding([8, 0]);
self.core.applet.popup_container(content_list).into() self.core
.applet
.popup_container(content_list)
.max_width(400.)
.into()
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
} }

View file

@ -10,12 +10,16 @@ chrono-tz = "0.9"
i18n-embed-fl.workspace = true i18n-embed-fl.workspace = true
i18n-embed.workspace = true i18n-embed.workspace = true
libcosmic.workspace = true libcosmic.workspace = true
once_cell = "1" once_cell.workspace = true
rust-embed.workspace = true rust-embed.workspace = true
tokio = { version = "1.36.0", features = ["time"] } tokio = { version = "1.36.0", features = ["time", "macros"] }
tracing-log.workspace = true tracing-log.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
tracing.workspace = true tracing.workspace = true
icu = { version = "1.4.0", features = ["experimental", "compiled_data", "icu_datetime_experimental"]} icu = { version = "1.4.0", features = [
"experimental",
"compiled_data",
"icu_datetime_experimental",
] }
zbus.workspace = true zbus.workspace = true
timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" } timedate-zbus = { git = "https://github.com/pop-os/dbus-settings-bindings" }

View file

@ -11,5 +11,5 @@ use window::Window;
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
localize::localize(); localize::localize();
cosmic::applet::run::<Window>(true, ()) cosmic::applet::run::<Window>(())
} }

View file

@ -4,6 +4,8 @@
use std::str::FromStr; use std::str::FromStr;
use chrono::{Datelike, Timelike}; use chrono::{Datelike, Timelike};
use cosmic::iced_futures::stream;
use cosmic::widget::Id;
use cosmic::{ use cosmic::{
app, app,
applet::{cosmic_panel_config::PanelAnchor, menu_button, padded_control}, applet::{cosmic_panel_config::PanelAnchor, menu_button, padded_control},
@ -11,21 +13,20 @@ use cosmic::{
cosmic_theme::Spacing, cosmic_theme::Spacing,
iced::{ iced::{
futures::{channel::mpsc, SinkExt, StreamExt, TryFutureExt}, futures::{channel::mpsc, SinkExt, StreamExt, TryFutureExt},
subscription, platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
wayland::popup::{destroy_popup, get_popup},
widget::{column, row, vertical_space}, widget::{column, row, vertical_space},
window, Alignment, Length, Rectangle, Subscription, window, Alignment, Length, Rectangle, Subscription,
}, },
iced_core::alignment::{Horizontal, Vertical}, iced_core::alignment::{Horizontal, Vertical},
iced_style::application,
iced_widget::{horizontal_rule, Column}, iced_widget::{horizontal_rule, Column},
theme, theme,
widget::{ widget::{
button, container, divider, grid, horizontal_space, icon, rectangle_tracker::*, text, autosize, button, container, divider, grid, horizontal_space, icon, rectangle_tracker::*,
Button, Grid, Space, text, Button, Grid, Space,
}, },
Command, Element, Theme, Element, Task,
}; };
use once_cell::sync::Lazy;
use timedate_zbus::TimeDateProxy; use timedate_zbus::TimeDateProxy;
use tokio::{sync::watch, time}; use tokio::{sync::watch, time};
@ -46,6 +47,8 @@ use cosmic::applet::token::subscription::{
activation_token_subscription, TokenRequest, TokenUpdate, activation_token_subscription, TokenRequest, TokenUpdate,
}; };
static AUTOSIZE_MAIN_ID: Lazy<Id> = Lazy::new(|| Id::new("autosize-main"));
/// In order to keep the understandable, the chrono types are not globals, /// In order to keep the understandable, the chrono types are not globals,
/// to avoid conflict with icu /// to avoid conflict with icu
@ -114,7 +117,7 @@ impl cosmic::Application for Window {
fn init( fn init(
core: app::Core, core: app::Core,
_flags: Self::Flags, _flags: Self::Flags,
) -> (Self, cosmic::iced::Command<app::Message<Self::Message>>) { ) -> (Self, cosmic::iced::Task<app::Message<Self::Message>>) {
fn get_local() -> Result<Locale, Box<dyn std::error::Error>> { fn get_local() -> Result<Locale, Box<dyn std::error::Error>> {
let locale = std::env::var("LC_TIME").or_else(|_| std::env::var("LANG"))?; let locale = std::env::var("LC_TIME").or_else(|_| std::env::var("LANG"))?;
let locale = locale let locale = locale
@ -157,7 +160,7 @@ impl cosmic::Application for Window {
show_seconds_tx, show_seconds_tx,
locale, locale,
}, },
Command::none(), Task::none(),
) )
} }
@ -169,62 +172,65 @@ impl cosmic::Application for Window {
&mut self.core &mut self.core
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
fn time_subscription(mut show_seconds: watch::Receiver<bool>) -> Subscription<Message> { fn time_subscription(mut show_seconds: watch::Receiver<bool>) -> Subscription<Message> {
subscription::channel("time-sub", 1, |mut output| async move { Subscription::run_with_id(
// Mark this receiver's state as changed so that it always receives an initial "time-sub",
// update during the loop below stream::channel(1, |mut output| async move {
// This allows us to avoid duplicating code from the loop // Mark this receiver's state as changed so that it always receives an initial
show_seconds.mark_changed(); // update during the loop below
let mut period = 1; // This allows us to avoid duplicating code from the loop
let mut timer = time::interval(time::Duration::from_secs(period)); show_seconds.mark_changed();
timer.set_missed_tick_behavior(time::MissedTickBehavior::Skip); let mut period = 1;
let mut timer = time::interval(time::Duration::from_secs(period));
timer.set_missed_tick_behavior(time::MissedTickBehavior::Skip);
loop { loop {
tokio::select! { tokio::select! {
_ = timer.tick() => { _ = timer.tick() => {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
if let Err(err) = output.send(Message::Tick).await { if let Err(err) = output.send(Message::Tick).await {
tracing::error!(?err, "Failed sending tick request to applet"); tracing::error!(?err, "Failed sending tick request to applet");
} }
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
let _ = output.send(Message::Tick).await; let _ = output.send(Message::Tick).await;
// Calculate a delta if we're ticking per minute to keep ticks stable // Calculate a delta if we're ticking per minute to keep ticks stable
// Based on i3status-rust // Based on i3status-rust
let current = chrono::Local::now().second() as u64 % period; let current = chrono::Local::now().second() as u64 % period;
if current != 0 { if current != 0 {
timer.reset_after(time::Duration::from_secs(period - current)); timer.reset_after(time::Duration::from_secs(period - current));
} }
}, },
// Update timer if the user toggles show_seconds // Update timer if the user toggles show_seconds
Ok(()) = show_seconds.changed() => { Ok(()) = show_seconds.changed() => {
let seconds = *show_seconds.borrow_and_update(); let seconds = *show_seconds.borrow_and_update();
if seconds { if seconds {
period = 1; period = 1;
// Subsecond precision isn't needed; skip calculating offset // Subsecond precision isn't needed; skip calculating offset
let period = time::Duration::from_secs(period); let period = time::Duration::from_secs(period);
let start = time::Instant::now() + period; let start = time::Instant::now() + period;
timer = time::interval_at(start, period); timer = time::interval_at(start, period);
} else { } else {
period = 60; period = 60;
let delta = time::Duration::from_secs(period - chrono::Utc::now().second() as u64 % period); let delta = time::Duration::from_secs(period - chrono::Utc::now().second() as u64 % period);
let now = time::Instant::now(); let now = time::Instant::now();
// Start ticking from the next minute to update the time properly // Start ticking from the next minute to update the time properly
let start = now + delta; let start = now + delta;
let period = time::Duration::from_secs(period); let period = time::Duration::from_secs(period);
timer = time::interval_at(start, period); timer = time::interval_at(start, period);
} }
timer.set_missed_tick_behavior(time::MissedTickBehavior::Skip); timer.set_missed_tick_behavior(time::MissedTickBehavior::Skip);
}
} }
} }
} }),
}) )
} }
// Update applet's timezone if the system's timezone changes // Update applet's timezone if the system's timezone changes
@ -249,22 +255,25 @@ impl cosmic::Application for Window {
} }
fn timezone_subscription() -> Subscription<Message> { fn timezone_subscription() -> Subscription<Message> {
subscription::channel("timezone-sub", 1, |mut output| async move { Subscription::run_with_id(
'retry: loop { "timezone-sub",
match timezone_update(&mut output).await { stream::channel(1, |mut output| async move {
Ok(()) => break 'retry, 'retry: loop {
Err(err) => { match timezone_update(&mut output).await {
tracing::error!( Ok(()) => break 'retry,
?err, Err(err) => {
"Automatic timezone updater failed; retrying in one minute" tracing::error!(
); ?err,
tokio::time::sleep(std::time::Duration::from_secs(60)).await; "Automatic timezone updater failed; retrying in one minute"
);
tokio::time::sleep(std::time::Duration::from_secs(60)).await;
}
} }
} }
}
std::future::pending().await std::future::pending().await
}) }),
)
} }
let show_seconds_rx = self.show_seconds_tx.subscribe(); let show_seconds_rx = self.show_seconds_tx.subscribe();
@ -285,7 +294,7 @@ impl cosmic::Application for Window {
fn update( fn update(
&mut self, &mut self,
message: Self::Message, message: Self::Message,
) -> cosmic::iced::Command<app::Message<Self::Message>> { ) -> cosmic::iced::Task<app::Message<Self::Message>> {
match message { match message {
Message::TogglePopup => { Message::TogglePopup => {
if let Some(p) = self.popup.take() { if let Some(p) = self.popup.take() {
@ -294,10 +303,10 @@ impl cosmic::Application for Window {
self.date_selected = chrono::NaiveDate::from(self.now.naive_local()); self.date_selected = chrono::NaiveDate::from(self.now.naive_local());
let new_id = window::Id::unique(); let new_id = window::Id::unique();
self.popup.replace(new_id); self.popup = Some(new_id);
let mut popup_settings = self.core.applet.get_popup_settings( let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN, self.core.main_window_id().unwrap(),
new_id, new_id,
None, None,
None, None,
@ -310,11 +319,14 @@ impl cosmic::Application for Window {
height, height,
} = self.rectangle; } = self.rectangle;
popup_settings.positioner.anchor_rect = Rectangle::<i32> { popup_settings.positioner.anchor_rect = Rectangle::<i32> {
x: x as i32, x: x.max(1.) as i32,
y: y as i32, y: y.max(1.) as i32,
width: width as i32, width: width.max(1.) as i32,
height: height as i32, height: height.max(1.) as i32,
}; };
popup_settings.positioner.size = Some((300, 500));
get_popup(popup_settings) get_popup(popup_settings)
} }
} }
@ -323,7 +335,7 @@ impl cosmic::Application for Window {
.timezone .timezone
.map(|tz| chrono::Local::now().with_timezone(&tz).fixed_offset()) .map(|tz| chrono::Local::now().with_timezone(&tz).fixed_offset())
.unwrap_or_else(|| chrono::Local::now().into()); .unwrap_or_else(|| chrono::Local::now().into());
Command::none() Task::none()
} }
Message::Rectangle(u) => { Message::Rectangle(u) => {
match u { match u {
@ -334,13 +346,13 @@ impl cosmic::Application for Window {
self.rectangle_tracker = Some(tracker); self.rectangle_tracker = Some(tracker);
} }
} }
Command::none() Task::none()
} }
Message::CloseRequested(id) => { Message::CloseRequested(id) => {
if Some(id) == self.popup { if Some(id) == self.popup {
self.popup = None; self.popup = None;
} }
Command::none() Task::none()
} }
Message::SelectDay(_day) => { Message::SelectDay(_day) => {
if let Some(date) = self.date_selected.with_day(_day) { if let Some(date) = self.date_selected.with_day(_day) {
@ -348,7 +360,7 @@ impl cosmic::Application for Window {
} else { } else {
tracing::error!("invalid naivedate"); tracing::error!("invalid naivedate");
} }
Command::none() Task::none()
} }
Message::PreviousMonth => { Message::PreviousMonth => {
if let Some(date) = self if let Some(date) = self
@ -359,7 +371,7 @@ impl cosmic::Application for Window {
} else { } else {
tracing::error!("invalid naivedate"); tracing::error!("invalid naivedate");
} }
Command::none() Task::none()
} }
Message::NextMonth => { Message::NextMonth => {
if let Some(date) = self if let Some(date) = self
@ -370,7 +382,7 @@ impl cosmic::Application for Window {
} else { } else {
tracing::error!("invalid naivedate"); tracing::error!("invalid naivedate");
} }
Command::none() Task::none()
} }
Message::OpenDateTimeSettings => { Message::OpenDateTimeSettings => {
let exec = "cosmic-settings time".to_string(); let exec = "cosmic-settings time".to_string();
@ -382,7 +394,7 @@ impl cosmic::Application for Window {
} else { } else {
tracing::error!("Wayland tx is None"); tracing::error!("Wayland tx is None");
}; };
Command::none() Task::none()
} }
Message::Token(u) => { Message::Token(u) => {
match u { match u {
@ -402,7 +414,7 @@ impl cosmic::Application for Window {
tokio::spawn(cosmic::process::spawn(cmd)); tokio::spawn(cosmic::process::spawn(cmd));
} }
} }
Command::none() Task::none()
} }
Message::ConfigChanged(c) => { Message::ConfigChanged(c) => {
// Don't interrupt the tick subscription unless necessary // Don't interrupt the tick subscription unless necessary
@ -415,7 +427,7 @@ impl cosmic::Application for Window {
} }
}); });
self.config = c; self.config = c;
Command::none() Task::none()
} }
Message::TimezoneUpdate(timezone) => { Message::TimezoneUpdate(timezone) => {
if let Ok(timezone) = timezone.parse::<chrono_tz::Tz>() { if let Ok(timezone) = timezone.parse::<chrono_tz::Tz>() {
@ -464,13 +476,13 @@ impl cosmic::Application for Window {
Element::from( Element::from(
row!( row!(
self.core.applet.text(self.format(bag, &self.now)), self.core.applet.text(self.format(bag, &self.now)),
container(vertical_space(Length::Fixed( container(vertical_space().height(Length::Fixed(
(self.core.applet.suggested_size(true).1 (self.core.applet.suggested_size(true).1
+ 2 * self.core.applet.suggested_padding(true)) + 2 * self.core.applet.suggested_padding(true))
as f32 as f32
))) )))
) )
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
} else { } else {
// vertical layout // vertical layout
@ -518,19 +530,19 @@ impl cosmic::Application for Window {
} }
let date_time_col = Column::with_children(elements) let date_time_col = Column::with_children(elements)
.align_items(Alignment::Center) .align_x(Alignment::Center)
.spacing(4); .spacing(4);
Element::from( Element::from(
column!( column!(
date_time_col, date_time_col,
horizontal_space(Length::Fixed( horizontal_space().width(Length::Fixed(
(self.core.applet.suggested_size(true).0 (self.core.applet.suggested_size(true).0
+ 2 * self.core.applet.suggested_padding(true)) + 2 * self.core.applet.suggested_padding(true))
as f32 as f32
)) ))
) )
.align_items(Alignment::Center), .align_x(Alignment::Center),
) )
}) })
.padding(if horizontal { .padding(if horizontal {
@ -538,14 +550,18 @@ impl cosmic::Application for Window {
} else { } else {
[self.core.applet.suggested_padding(true), 0] [self.core.applet.suggested_padding(true), 0]
}) })
.on_press_down(Message::TogglePopup) .on_press(Message::TogglePopup)
.style(cosmic::theme::Button::AppletIcon); .class(cosmic::theme::Button::AppletIcon);
if let Some(tracker) = self.rectangle_tracker.as_ref() { autosize::autosize(
tracker.container(0, button).ignore_bounds(true).into() if let Some(tracker) = self.rectangle_tracker.as_ref() {
} else { Element::from(tracker.container(0, button).ignore_bounds(true))
button.into() } else {
} button.into()
},
AUTOSIZE_MAIN_ID.clone(),
)
.into()
} }
fn view_window(&self, _id: window::Id) -> Element<Message> { fn view_window(&self, _id: window::Id) -> Element<Message> {
@ -597,7 +613,7 @@ impl cosmic::Application for Window {
text(self.format(weekday_bag, &day_iter.next().unwrap())) text(self.format(weekday_bag, &day_iter.next().unwrap()))
.size(12) .size(12)
.width(Length::Fixed(36.0)) .width(Length::Fixed(36.0))
.horizontal_alignment(Horizontal::Center), .align_x(Horizontal::Center),
); );
first_day_of_week = first_day_of_week.succ(); first_day_of_week = first_day_of_week.succ();
@ -625,7 +641,7 @@ impl cosmic::Application for Window {
Space::with_width(Length::Fill), Space::with_width(Length::Fill),
month_controls, month_controls,
] ]
.align_items(Alignment::Center) .align_y(Alignment::Center)
.padding([12, 20]), .padding([12, 20]),
calender.padding([0, 12].into()), calender.padding([0, 12].into()),
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]), padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
@ -637,6 +653,7 @@ impl cosmic::Application for Window {
self.core self.core
.applet .applet
.popup_container(container(content_list)) .popup_container(container(content_list))
.max_width(300.)
.into() .into()
} }
@ -647,17 +664,17 @@ impl cosmic::Application for Window {
fn date_button(day: u32, is_month: bool, is_day: bool) -> Button<'static, Message> { fn date_button(day: u32, is_month: bool, is_day: bool) -> Button<'static, Message> {
let style = if is_day { let style = if is_day {
button::Style::Suggested button::ButtonClass::Suggested
} else { } else {
button::Style::Text button::ButtonClass::Text
}; };
let button = button::custom( let button = button::custom(
text::body(format!("{day}")) text::body(format!("{day}"))
.horizontal_alignment(Horizontal::Center) .align_x(Horizontal::Center)
.vertical_alignment(Vertical::Center), .align_y(Vertical::Center),
) )
.style(style) .class(style)
.height(Length::Fixed(36.0)) .height(Length::Fixed(36.0))
.width(Length::Fixed(36.0)); .width(Length::Fixed(36.0));

View file

@ -10,15 +10,15 @@ use cosmic::{
mouse::{self, ScrollDelta}, mouse::{self, ScrollDelta},
widget::{button, column, row}, widget::{button, column, row},
Event::Mouse, Event::Mouse,
Length, Subscription, Length, Limits, Subscription,
}, },
iced_core::{Background, Border}, iced_core::{Background, Border},
iced_style::application, widget::{autosize, container, horizontal_space, vertical_space, Id},
widget::{container, horizontal_space, vertical_space}, Element, Task, Theme,
Command, Element, Theme,
}; };
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1; use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1;
use once_cell::sync::Lazy;
use std::cmp::Ordering; use std::cmp::Ordering;
use crate::{ use crate::{
@ -29,8 +29,10 @@ use crate::{
use std::process::Command as ShellCommand; use std::process::Command as ShellCommand;
static AUTOSIZE_MAIN_ID: Lazy<Id> = Lazy::new(|| Id::new("autosize-main"));
pub fn run() -> cosmic::iced::Result { pub fn run() -> cosmic::iced::Result {
cosmic::applet::run::<IcedWorkspacesApplet>(true, ()) cosmic::applet::run::<IcedWorkspacesApplet>(())
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -51,11 +53,11 @@ impl IcedWorkspacesApplet {
/// if it exists. /// if it exists.
fn popup_index(&self) -> Option<usize> { fn popup_index(&self) -> Option<usize> {
let mut index = None; let mut index = None;
let Some(max_major_axis_len) = self.core.applet.configure.as_ref().and_then(|c| { let Some(max_major_axis_len) = self.core.applet.suggested_bounds.as_ref().map(|c| {
// if we have a configure for width and height, we're in a overflow popup // if we have a configure for width and height, we're in a overflow popup
match self.core.applet.anchor { match self.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => c.new_size.0, PanelAnchor::Top | PanelAnchor::Bottom => c.width as u32,
PanelAnchor::Left | PanelAnchor::Right => c.new_size.1, PanelAnchor::Left | PanelAnchor::Right => c.height as u32,
} }
}) else { }) else {
return index; return index;
@ -63,7 +65,7 @@ impl IcedWorkspacesApplet {
let button_total_size = self.core.applet.suggested_size(true).0 let button_total_size = self.core.applet.suggested_size(true).0
+ self.core.applet.suggested_padding(true) * 2 + self.core.applet.suggested_padding(true) * 2
+ 4; + 4;
let btn_count = max_major_axis_len.get() / button_total_size as u32; let btn_count = max_major_axis_len / button_total_size as u32;
if btn_count >= self.workspaces.len() as u32 { if btn_count >= self.workspaces.len() as u32 {
index = None; index = None;
} else { } else {
@ -92,7 +94,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
_flags: Self::Flags, _flags: Self::Flags,
) -> ( ) -> (
Self, Self,
cosmic::iced::Command<cosmic::app::Message<Self::Message>>, cosmic::iced::Task<cosmic::app::Message<Self::Message>>,
) { ) {
( (
Self { Self {
@ -104,7 +106,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
workspaces: Vec::new(), workspaces: Vec::new(),
workspace_tx: Default::default(), workspace_tx: Default::default(),
}, },
Command::none(), Task::none(),
) )
} }
@ -119,7 +121,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
fn update( fn update(
&mut self, &mut self,
message: Self::Message, message: Self::Message,
) -> cosmic::iced::Command<cosmic::app::Message<Self::Message>> { ) -> cosmic::iced::Task<cosmic::app::Message<Self::Message>> {
match message { match message {
Message::WorkspaceUpdate(msg) => match msg { Message::WorkspaceUpdate(msg) => match msg {
WorkspacesUpdate::Workspaces(mut list) => { WorkspacesUpdate::Workspaces(mut list) => {
@ -158,7 +160,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
let _ = ShellCommand::new("cosmic-workspaces").spawn(); let _ = ShellCommand::new("cosmic-workspaces").spawn();
} }
} }
Command::none() Task::none()
} }
fn view(&self) -> Element<Message> { fn view(&self) -> Element<Message> {
@ -187,11 +189,11 @@ impl cosmic::Application for IcedWorkspacesApplet {
(suggested_window_size.0.get() as f32, suggested_total as f32) (suggested_window_size.0.get() as f32, suggested_total as f32)
}; };
let content = row!(content, vertical_space(Length::Fixed(height))) let content = row!(content, vertical_space().height(Length::Fixed(height)))
.align_items(cosmic::iced::Alignment::Center); .align_y(cosmic::iced::Alignment::Center);
let content = column!(content, horizontal_space(Length::Fixed(width))) let content = column!(content, horizontal_space().width(Length::Fixed(width)))
.align_items(cosmic::iced::Alignment::Center); .align_x(cosmic::iced::Alignment::Center);
let btn = button( let btn = button(
container(content) container(content)
@ -210,14 +212,14 @@ impl cosmic::Application for IcedWorkspacesApplet {
.padding(0); .padding(0);
Some( Some(
btn.style(match w.1 { btn.class(match w.1 {
Some(zcosmic_workspace_handle_v1::State::Active) => { Some(zcosmic_workspace_handle_v1::State::Active) => {
cosmic::theme::iced::Button::Primary cosmic::theme::iced::Button::Primary
} }
Some(zcosmic_workspace_handle_v1::State::Urgent) => { Some(zcosmic_workspace_handle_v1::State::Urgent) => {
let appearance = |theme: &Theme| { let appearance = |theme: &Theme| {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
button::Appearance { button::Style {
background: Some(Background::Color( background: Some(Background::Color(
cosmic.palette.neutral_3.into(), cosmic.palette.neutral_3.into(),
)), )),
@ -227,27 +229,31 @@ impl cosmic::Application for IcedWorkspacesApplet {
}, },
border_radius: theme.cosmic().radius_xl().into(), border_radius: theme.cosmic().radius_xl().into(),
text_color: theme.cosmic().destructive_button.base.into(), text_color: theme.cosmic().destructive_button.base.into(),
..button::Appearance::default() ..button::Style::default()
} }
}; };
cosmic::theme::iced::Button::Custom { cosmic::theme::iced::Button::Custom(Box::new(move |theme, status| {
active: Box::new(appearance), match status {
hover: Box::new(move |theme| button::Appearance { button::Status::Active => appearance(theme),
background: Some(Background::Color( button::Status::Hovered => button::Style {
theme.current_container().component.hover.into(), background: Some(Background::Color(
)), theme.current_container().component.hover.into(),
border: Border { )),
radius: theme.cosmic().radius_xl().into(), border: Border {
..Default::default() radius: theme.cosmic().radius_xl().into(),
..Default::default()
},
..appearance(theme)
}, },
..appearance(theme) button::Status::Pressed => appearance(theme),
}), button::Status::Disabled => appearance(theme),
} }
}))
} }
None => { None => {
let appearance = |theme: &Theme| { let appearance = |theme: &Theme| {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
button::Appearance { button::Style {
background: None, background: None,
border: Border { border: Border {
radius: cosmic.radius_xl().into(), radius: cosmic.radius_xl().into(),
@ -255,22 +261,27 @@ impl cosmic::Application for IcedWorkspacesApplet {
}, },
border_radius: cosmic.radius_xl().into(), border_radius: cosmic.radius_xl().into(),
text_color: theme.current_container().component.on.into(), text_color: theme.current_container().component.on.into(),
..button::Appearance::default() ..button::Style::default()
} }
}; };
cosmic::theme::iced::Button::Custom { cosmic::theme::iced::Button::Custom(Box::new(move |theme, status| {
active: Box::new(appearance), match status {
hover: Box::new(move |theme| button::Appearance { button::Status::Active => appearance(theme),
background: Some(Background::Color( button::Status::Hovered => button::Style {
theme.current_container().component.hover.into(), background: Some(Background::Color(
)), theme.current_container().component.hover.into(),
border: Border { )),
radius: theme.cosmic().radius_xl().into(), border: Border {
..Default::default() radius: theme.cosmic().radius_xl().into(),
..Default::default()
},
..appearance(theme)
}, },
..appearance(theme) button::Status::Pressed | button::Status::Disabled => {
}), appearance(theme)
} }
}
}))
} }
_ => return None, _ => return None,
}) })
@ -283,21 +294,35 @@ impl cosmic::Application for IcedWorkspacesApplet {
Layout::Row => row(buttons).spacing(4).into(), Layout::Row => row(buttons).spacing(4).into(),
Layout::Column => column(buttons).spacing(4).into(), Layout::Column => column(buttons).spacing(4).into(),
}; };
let mut limits = Limits::NONE.min_width(1.).min_height(1.);
if let Some(b) = self.core.applet.suggested_bounds {
if b.width as i32 > 0 {
limits = limits.max_width(b.width);
}
if b.height as i32 > 0 {
limits = limits.max_height(b.height);
}
}
container(layout_section).padding(0).into() autosize::autosize(
container(layout_section).padding(0),
AUTOSIZE_MAIN_ID.clone(),
)
.limits(limits)
.into()
} }
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
Subscription::batch(vec![ Subscription::batch(vec![
workspaces().map(Message::WorkspaceUpdate), workspaces().map(Message::WorkspaceUpdate),
event::listen_with(|e, _| match e { event::listen_with(|e, _, _| match e {
Mouse(mouse::Event::WheelScrolled { delta }) => Some(Message::WheelScrolled(delta)), Mouse(mouse::Event::WheelScrolled { delta }) => Some(Message::WheelScrolled(delta)),
_ => None, _ => None,
}), }),
]) ])
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
} }

View file

@ -6,7 +6,7 @@ use cctk::sctk::reexports::calloop::channel::SyncSender;
use cosmic::iced::{ use cosmic::iced::{
self, self,
futures::{channel::mpsc, SinkExt, StreamExt}, futures::{channel::mpsc, SinkExt, StreamExt},
subscription, stream, Subscription,
}; };
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -22,16 +22,15 @@ pub enum WorkspacesUpdate {
} }
pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> { pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> {
subscription::channel( Subscription::run_with_id(
std::any::TypeId::of::<WorkspacesUpdate>(), std::any::TypeId::of::<WorkspacesUpdate>(),
50, stream::channel(50, move |mut output| async move {
move |mut output| async move {
let mut state = State::Waiting; let mut state = State::Waiting;
loop { loop {
state = start_listening(state, &mut output).await; state = start_listening(state, &mut output).await;
} }
}, }),
) )
} }

View file

@ -4,7 +4,7 @@
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
fn main() -> cosmic::iced::Result { fn main() -> cosmic::iced::Result {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt().with_env_filter("warn").init();
let _ = tracing_log::LogTracer::init(); let _ = tracing_log::LogTracer::init();
let Some(applet) = std::env::args().next() else { let Some(applet) = std::env::args().next() else {

View file

@ -11,4 +11,5 @@ tracing.workspace = true
tracing-subscriber.workspace = true tracing-subscriber.workspace = true
tracing-log.workspace = true tracing-log.workspace = true
cosmic-config.workspace = true cosmic-config.workspace = true
once_cell.workspace = true
serde.workspace = true serde.workspace = true

View file

@ -8,19 +8,20 @@ use cosmic::{
cosmic_panel_config::{PanelAnchor, PanelSize}, cosmic_panel_config::{PanelAnchor, PanelSize},
Size, Size,
}, },
iced, iced::{self, Length},
iced::Length,
iced_style::application,
iced_widget::row, iced_widget::row,
theme::Theme, widget::{autosize, vertical_space, Id},
widget::vertical_space, Task,
}; };
use cosmic_config::{Config, CosmicConfigEntry}; use cosmic_config::{Config, CosmicConfigEntry};
use freedesktop_desktop_entry::{get_languages_from_env, DesktopEntry}; use freedesktop_desktop_entry::{get_languages_from_env, DesktopEntry};
use once_cell::sync::Lazy;
use std::{env, fs, process::Command}; use std::{env, fs, process::Command};
mod config; mod config;
static AUTOSIZE_MAIN_ID: Lazy<Id> = Lazy::new(|| Id::new("autosize-main"));
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
struct Desktop { struct Desktop {
name: String, name: String,
@ -46,7 +47,7 @@ impl cosmic::Application for Button {
type Flags = Desktop; type Flags = Desktop;
const APP_ID: &'static str = "com.system76.CosmicPanelButton"; const APP_ID: &'static str = "com.system76.CosmicPanelButton";
fn init(core: cosmic::app::Core, desktop: Desktop) -> (Self, app::Command<Msg>) { fn init(core: cosmic::app::Core, desktop: Desktop) -> (Self, app::Task<Msg>) {
let config = Config::new(Self::APP_ID, CosmicPanelButtonConfig::VERSION) let config = Config::new(Self::APP_ID, CosmicPanelButtonConfig::VERSION)
.ok() .ok()
.and_then(|c| CosmicPanelButtonConfig::get_entry(&c).ok()) .and_then(|c| CosmicPanelButtonConfig::get_entry(&c).ok())
@ -61,7 +62,7 @@ impl cosmic::Application for Button {
desktop, desktop,
config, config,
}, },
app::Command::none(), Task::none(),
) )
} }
@ -73,14 +74,18 @@ impl cosmic::Application for Button {
&mut self.core &mut self.core
} }
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
fn update(&mut self, message: Msg) -> app::Command<Msg> { fn update(&mut self, message: Msg) -> app::Task<Msg> {
match message { match message {
Msg::Press => { Msg::Press => {
let _ = Command::new("sh").arg("-c").arg(&self.desktop.exec).spawn(); let _ = Command::new("sh")
.arg("-c")
.arg(&self.desktop.exec)
.spawn()
.unwrap();
} }
Msg::ConfigUpdated(conf) => { Msg::ConfigUpdated(conf) => {
self.config = conf self.config = conf
@ -90,44 +95,48 @@ impl cosmic::Application for Button {
.unwrap_or_default(); .unwrap_or_default();
} }
} }
app::Command::none() Task::none()
} }
fn view(&self) -> cosmic::Element<Msg> { fn view(&self) -> cosmic::Element<Msg> {
// currently, panel being anchored to the left or right is a hard // currently, panel being anchored to the left or right is a hard
// override for icon, later if text is updated to wrap, we may // override for icon, later if text is updated to wrap, we may
// use Override::Text to override this behavior // use Override::Text to override this behavior
if self.desktop.icon.is_some() autosize::autosize(
&& matches!( if self.desktop.icon.is_some()
self.core.applet.anchor, && matches!(
PanelAnchor::Left | PanelAnchor::Right self.core.applet.anchor,
) PanelAnchor::Left | PanelAnchor::Right
|| matches!(self.config.force_presentation, Some(Override::Icon))
|| matches!(
(&self.core.applet.size, &self.config.force_presentation),
(
Size::PanelSize(PanelSize::M | PanelSize::L | PanelSize::XL),
None
) )
) || matches!(self.config.force_presentation, Some(Override::Icon))
{ || matches!(
self.core.applet.icon_button_from_handle( (&self.core.applet.size, &self.config.force_presentation),
cosmic::widget::icon::from_name(self.desktop.icon.clone().unwrap()).handle(), (
) Size::PanelSize(PanelSize::M | PanelSize::L | PanelSize::XL),
} else { None
let content = row!( )
self.core.applet.text(&self.desktop.name), )
vertical_space(Length::Fixed( {
(self.core.applet.suggested_size(true).1 self.core.applet.icon_button_from_handle(
+ 2 * self.core.applet.suggested_padding(true)) as f32 cosmic::widget::icon::from_name(self.desktop.icon.clone().unwrap()).handle(),
)) )
) } else {
.align_items(iced::Alignment::Center); let content = row!(
cosmic::widget::button::custom(content) self.core.applet.text(&self.desktop.name),
.padding([0, self.core.applet.suggested_padding(true)]) vertical_space().height(Length::Fixed(
.style(cosmic::theme::Button::AppletIcon) (self.core.applet.suggested_size(true).1
} + 2 * self.core.applet.suggested_padding(true))
.on_press_down(Msg::Press) as f32
))
)
.align_y(iced::Alignment::Center);
cosmic::widget::button::custom(content)
.padding([0, self.core.applet.suggested_padding(true)])
.class(cosmic::theme::Button::AppletIcon)
}
.on_press_down(Msg::Press),
AUTOSIZE_MAIN_ID.clone(),
)
.into() .into()
} }
@ -171,5 +180,5 @@ pub fn run() -> iced::Result {
let desktop = desktop.unwrap_or_else(|| { let desktop = desktop.unwrap_or_else(|| {
panic!("Failed to find valid desktop file '{filename}' in search paths") panic!("Failed to find valid desktop file '{filename}' in search paths")
}); });
cosmic::applet::run::<Button>(true, desktop) cosmic::applet::run::<Button>(desktop)
} }