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",
], rev = "c8d3a1c" }
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 = [
"libcosmic",
"once_cell",
] }
# cosmic-time = { path = "../cosmic-time", default-features = false, features = [
# "once_cell",
# ] }
futures = "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 = [
"applet",
"applet-token",
"multi-window",
"tokio",
"wayland",
"desktop-systemd-scope",
"winit",
"dbus-config",
] }
once_cell = "1"
rust-embed = "8.3"
rust-embed-utils = "8.3.0"
rustix = { version = "0.38", features = ["fs", "process"] }
@ -63,8 +69,15 @@ freedesktop-desktop-entry = "0.7.5"
[profile.release]
lto = "fat"
[workspace.metadata.cargo-machete]
ignored = ["libcosmic"]
# [patch."https://github.com/pop-os/libcosmic"]
# cosmic-config = { 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,
wayland_client::protocol::wl_output::WlOutput,
};
use cosmic::{iced, iced::subscription};
use cosmic::{
iced::{self, stream, Subscription},
iced_core::image::Bytes,
};
use cosmic_protocols::{
toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
workspace::v1::client::zcosmic_workspace_handle_v1::ZcosmicWorkspaceHandleV1,
@ -19,7 +22,7 @@ use futures::{
SinkExt, StreamExt,
};
use once_cell::sync::Lazy;
use std::{fmt::Debug, sync::Arc};
use std::fmt::Debug;
use tokio::sync::Mutex;
use crate::wayland_handler::wayland_handler;
@ -28,16 +31,15 @@ pub static WAYLAND_RX: Lazy<Mutex<Option<UnboundedReceiver<WaylandUpdate>>>> =
Lazy::new(|| Mutex::new(None));
pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> {
subscription::channel(
Subscription::run_with_id(
std::any::TypeId::of::<WaylandUpdate>(),
50,
move |mut output| async move {
stream::channel(50, move |mut output| async move {
let mut state = State::Waiting;
loop {
state = start_listening(state, &mut output).await;
}
},
}),
)
}
@ -48,12 +50,19 @@ pub enum State {
#[derive(Debug, Clone)]
pub struct WaylandImage {
pub img: Arc<image::RgbaImage>,
pub img: Bytes,
pub width: u32,
pub height: u32,
}
impl WaylandImage {
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 config::AudioAppletConfig;
use cosmic::{
app::Command,
app,
applet::{
cosmic_panel_config::PanelAnchor,
menu_button, menu_control_padding, padded_control,
@ -24,15 +24,14 @@ use cosmic::{
window, Alignment, Length, Limits, Subscription,
},
iced_runtime::core::alignment::Horizontal,
iced_style::application,
theme,
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_time::{anim, chain, id, once_cell::sync::Lazy, Instant, Timeline};
use iced::{
wayland::popup::{destroy_popup, get_popup},
platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::container,
};
use libpulse_binding::volume::Volume;
@ -52,7 +51,7 @@ const PLAY: &str = "media-playback-start-symbolic";
pub fn run() -> cosmic::iced::Result {
localize();
cosmic::applet::run::<Audio>(true, ())
cosmic::applet::run::<Audio>(())
}
#[derive(Default)]
@ -208,10 +207,10 @@ impl Audio {
Some(match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => Column::with_children(elements)
.align_items(Alignment::Center)
.align_x(Alignment::Center)
.into(),
PanelAnchor::Top | PanelAnchor::Bottom => Row::with_children(elements)
.align_items(Alignment::Center)
.align_y(Alignment::Center)
.into(),
})
} else {
@ -225,7 +224,7 @@ impl Audio {
Some(
button::icon(icon::from_name(GO_BACK).size(icon_size).symbolic(true))
.extra_small()
.style(cosmic::theme::Button::AppletIcon)
.class(cosmic::theme::Button::AppletIcon)
.on_press(Message::MprisRequest(MprisRequest::Previous))
.into(),
)
@ -241,7 +240,7 @@ impl Audio {
Some(
button::icon(icon::from_name(GO_NEXT).size(icon_size).symbolic(true))
.extra_small()
.style(cosmic::theme::Button::AppletIcon)
.class(cosmic::theme::Button::AppletIcon)
.on_press(Message::MprisRequest(MprisRequest::Next))
.into(),
)
@ -292,7 +291,7 @@ impl cosmic::Application for Audio {
type Flags = ();
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 {
core,
@ -304,7 +303,7 @@ impl cosmic::Application for Audio {
token_tx: None,
..Default::default()
},
Command::none(),
Task::none(),
)
}
@ -316,11 +315,11 @@ impl cosmic::Application for Audio {
&mut self.core
}
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style())
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(&mut self, message: Message) -> app::Task<Message> {
match message {
Message::Frame(now) => self.timeline.now(now),
Message::Ignore => {}
@ -336,7 +335,7 @@ impl cosmic::Application for Audio {
self.timeline = Timeline::new();
let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
self.core.main_window_id().unwrap(),
new_id,
None,
None,
@ -360,14 +359,14 @@ impl cosmic::Application for Audio {
}
Message::SetOutputVolume(vol) => {
if self.output_volume == vol {
return Command::none();
return Task::none();
}
self.output_volume = vol;
self.output_volume_text = format!("{}%", self.output_volume.round());
if self.output_volume_debounce {
return Command::none();
return Task::none();
}
self.output_volume_debounce = true;
@ -379,14 +378,14 @@ impl cosmic::Application for Audio {
}
Message::SetInputVolume(vol) => {
if self.input_volume == vol {
return Command::none();
return Task::none();
}
self.input_volume = vol;
self.input_volume_text = format!("{}%", self.input_volume.round());
if self.input_volume_debounce {
return Command::none();
return Task::none();
}
self.input_volume_debounce = true;
@ -583,7 +582,7 @@ impl cosmic::Application for Audio {
Message::MprisRequest(r) => {
let Some(player_status) = self.player_status.as_ref() else {
tracing::error!("No player found");
return Command::none();
return Task::none();
};
let player = player_status.player.clone();
@ -673,7 +672,7 @@ impl cosmic::Application for Audio {
},
};
Command::none()
Task::none()
}
fn subscription(&self) -> Subscription<Message> {
@ -715,28 +714,30 @@ impl cosmic::Application for Audio {
.clamp(0.0, 100.0);
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
c.new_size.0.is_some() && c.new_size.1.is_some()
c.width > 0. && c.height > 0.
}))
.then(|| self.playback_buttons());
if let Some(Some(playback_buttons)) = playback_buttons {
match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => {
Column::with_children(vec![playback_buttons, btn.into()])
.align_items(Alignment::Center)
.into()
self.core
.applet
.autosize_window(if let Some(Some(playback_buttons)) = playback_buttons {
match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => Element::from(
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 => {
Row::with_children(vec![playback_buttons, btn.into()])
.align_items(Alignment::Center)
.into()
}
}
} else {
btn.into()
}
} else {
btn.into()
})
.into()
}
fn view_window(&self, _id: window::Id) -> Element<Message> {
@ -752,7 +753,7 @@ impl cosmic::Application for Audio {
column![padded_control(
text::title3(fl!("disconnected"))
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.align_x(Horizontal::Center)
)]
} else {
column![
@ -763,7 +764,7 @@ impl cosmic::Application for Audio {
.size(24)
.symbolic(true),
)
.style(cosmic::theme::Button::Icon)
.class(cosmic::theme::Button::Icon)
.icon_size(24)
.line_height(24)
.on_press(Message::SetOutputMute(!out_mute)),
@ -772,10 +773,10 @@ impl cosmic::Application for Audio {
text(&self.output_volume_text)
.size(16)
.width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right)
.align_x(Horizontal::Right)
]
.spacing(12)
.align_items(Alignment::Center)
.align_y(Alignment::Center)
),
padded_control(
row![
@ -784,7 +785,7 @@ impl cosmic::Application for Audio {
.size(24)
.symbolic(true),
)
.style(cosmic::theme::Button::Icon)
.class(cosmic::theme::Button::Icon)
.icon_size(24)
.line_height(24)
.on_press(Message::SetInputMute(!in_mute)),
@ -793,10 +794,10 @@ impl cosmic::Application for Audio {
text(&self.input_volume_text)
.size(16)
.width(Length::FillPortion(1))
.horizontal_alignment(Horizontal::Right)
.align_x(Horizontal::Right)
]
.spacing(12)
.align_items(Alignment::Center)
.align_y(Alignment::Center)
),
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
revealer(
@ -836,7 +837,7 @@ impl cosmic::Application for Audio {
Message::InputChanged,
)
]
.align_items(Alignment::Start)
.align_x(Alignment::Start)
};
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);
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) {
control_elements.push(go_prev);
}
@ -893,7 +894,7 @@ impl cosmic::Application for Audio {
.symbolic(true),
)
.extra_small()
.style(cosmic::theme::Button::AppletIcon)
.class(cosmic::theme::Button::AppletIcon)
.on_press(if play {
Message::MprisRequest(MprisRequest::Play)
} else {
@ -908,7 +909,7 @@ impl cosmic::Application for Audio {
let control_cnt = control_elements.len() as u16;
elements.push(
Row::with_children(control_elements)
.align_items(Alignment::Center)
.align_y(Alignment::Center)
.width(Length::FillPortion(control_cnt.saturating_add(1)))
.spacing(8)
.into(),
@ -918,7 +919,7 @@ impl cosmic::Application for Audio {
.push(padded_control(divider::horizontal::default()).padding([space_xxs, space_s]));
audio_content = audio_content.push(
Row::with_children(elements)
.align_items(Alignment::Center)
.align_y(Alignment::Center)
.spacing(8)
.padding(menu_control_padding()),
);
@ -941,10 +942,14 @@ impl cosmic::Application for Audio {
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
menu_button(text::body(fl!("sound-settings"))).on_press(Message::OpenSettings)
]
.align_items(Alignment::Start)
.align_x(Alignment::Start)
.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> {

View file

@ -1,6 +1,7 @@
//! 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::{
event::{self, Event},
@ -170,7 +171,7 @@ where
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation<OperationOutputWrapper<Message>>,
operation: &mut dyn Operation<()>,
) {
self.content
.as_widget()
@ -253,17 +254,18 @@ where
tree: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.content
.as_widget_mut()
.overlay(&mut tree.children[0], layout, renderer)
.overlay(&mut tree.children[0], layout, renderer, translation)
}
fn drag_destinations(
&self,
state: &Tree,
layout: Layout<'_>,
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() {
self.content

View file

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

View file

@ -6,7 +6,7 @@ use std::{cell::RefCell, mem, rc::Rc, thread};
extern crate libpulse_binding as pulse;
use cosmic::{
iced::{self, subscription},
iced::{self, stream, Subscription},
iced_futures::futures::{self, SinkExt},
};
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> {
struct SomeWorker;
subscription::channel(
Subscription::run_with_id(
std::any::TypeId::of::<SomeWorker>(),
50,
move |mut output| async move {
stream::channel(50, move |mut output| async move {
let mut state = State::Connecting;
loop {
state = start_listening(state, &mut output).await;
}
},
}),
)
}

View file

@ -14,7 +14,13 @@ i18n-embed.workspace = true
libcosmic.workspace = true
once_cell = "1.19.0"
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-subscriber.workspace = true
tracing.workspace = true

View file

@ -20,17 +20,16 @@ use cosmic::{
cosmic_theme::Spacing,
iced::{
alignment::Horizontal,
wayland::popup::{destroy_popup, get_popup},
platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::{column, container, row, slider},
window, Alignment, Length, Subscription,
},
iced_core::{alignment::Vertical, Background, Border, Color, Shadow},
iced_runtime::core::layout::Limits,
iced_style::application,
iced_widget::{Column, Row},
theme,
widget::{divider, horizontal_space, icon, scrollable, text, vertical_space},
Command, Element, Theme,
Element, Task,
};
use cosmic_settings_subscriptions::{
settings_daemon,
@ -63,7 +62,7 @@ fn format_duration(duration: Duration) -> String {
}
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);
@ -197,15 +196,14 @@ impl cosmic::Application for CosmicBatteryApplet {
_flags: Self::Flags,
) -> (
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))
});
let init_charging_limit_cmd =
cosmic::iced::Command::perform(get_charging_limit(), |limit| {
cosmic::app::Message::App(Message::InitChargingLimit(limit))
});
let init_charging_limit_cmd = cosmic::iced::Task::perform(get_charging_limit(), |limit| {
cosmic::app::Message::App(Message::InitChargingLimit(limit))
});
(
Self {
core,
@ -215,7 +213,7 @@ impl cosmic::Application for CosmicBatteryApplet {
..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(
&mut self,
message: Self::Message,
) -> cosmic::iced::Command<cosmic::app::Message<Self::Message>> {
) -> cosmic::iced::Task<cosmic::app::Message<Self::Message>> {
match message {
Message::Frame(now) => self.timeline.now(now),
Message::SetKbdBrightness(brightness) => {
@ -254,7 +252,7 @@ impl cosmic::Application for CosmicBatteryApplet {
self.set_charging_limit(enable);
if enable {
return cosmic::iced::Command::perform(set_charging_limit(), |_| {
return cosmic::iced::Task::perform(set_charging_limit(), |_| {
cosmic::app::Message::None
});
}
@ -275,9 +273,9 @@ impl cosmic::Application for CosmicBatteryApplet {
self.popup.replace(new_id);
let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
self.core.main_window_id().unwrap(),
new_id,
None,
Some((1, 1)),
None,
None,
);
@ -405,7 +403,7 @@ impl cosmic::Application for CosmicBatteryApplet {
}
},
}
Command::none()
Task::none()
}
fn view(&self) -> Element<Message> {
@ -417,10 +415,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.into();
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)
.style(<Theme as container::StyleSheet>::Style::Custom(Box::new(
|theme| container::Appearance {
.class(cosmic::style::Container::Custom(Box::new(|theme| {
container::Style {
text_color: Some(Color::TRANSPARENT),
background: Some(Background::Color(theme.cosmic().accent_color().into())),
border: Border {
@ -430,16 +428,16 @@ impl cosmic::Application for CosmicBatteryApplet {
},
shadow: Shadow::default(),
icon_color: Some(Color::TRANSPARENT),
},
)))
}
})))
.into();
match self.core.applet.anchor {
PanelAnchor::Left | PanelAnchor::Right => Column::with_children(vec![btn, dot])
.align_items(Alignment::Center)
.align_x(Alignment::Center)
.into(),
PanelAnchor::Top | PanelAnchor::Bottom => Row::with_children(vec![btn, dot])
.align_items(Alignment::Center)
.align_y(Alignment::Center)
.into(),
}
} else {
@ -471,7 +469,7 @@ impl cosmic::Application for CosmicBatteryApplet {
column![name, description]
]
.spacing(8)
.align_items(Alignment::Center),
.align_y(Alignment::Center),
)
.into(),
padded_control(divider::horizontal::default())
@ -491,10 +489,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.symbolic(true),
)
} 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))
.into(),
@ -512,10 +510,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.symbolic(true),
)
} 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))
.into(),
@ -533,10 +531,10 @@ impl cosmic::Application for CosmicBatteryApplet {
.symbolic(true),
)
} 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))
.into(),
@ -580,7 +578,7 @@ impl cosmic::Application for CosmicBatteryApplet {
))
.size(16)
.width(Length::Fixed(40.0))
.horizontal_alignment(Horizontal::Right)
.align_x(Horizontal::Right)
]
.spacing(12),
)
@ -608,7 +606,7 @@ impl cosmic::Application for CosmicBatteryApplet {
))
.size(16)
.width(Length::Fixed(40.0))
.horizontal_alignment(Horizontal::Right)
.align_x(Horizontal::Right)
]
.spacing(12),
)
@ -630,11 +628,11 @@ impl cosmic::Application for CosmicBatteryApplet {
text(fl!("dgpu-running"))
.size(16)
.width(Length::Fill)
.horizontal_alignment(Horizontal::Left),
container(vertical_space(Length::Fixed(0.0)))
.align_x(Horizontal::Left),
container(vertical_space().width(Length::Fixed(0.0)))
.padding(4)
.style(<Theme as container::StyleSheet>::Style::Custom(Box::new(
|theme| container::Appearance {
.class(cosmic::style::Container::Custom(Box::new(|theme| {
container::Style {
text_color: Some(Color::TRANSPARENT),
background: Some(Background::Color(
theme.cosmic().accent_color().into(),
@ -646,10 +644,10 @@ impl cosmic::Application for CosmicBatteryApplet {
},
shadow: Default::default(),
icon_color: Some(Color::TRANSPARENT),
},
))),
}
},))),
]
.align_items(Alignment::Center),
.align_y(Alignment::Center),
)
.into(),
);
@ -673,7 +671,7 @@ impl cosmic::Application for CosmicBatteryApplet {
gpu_name = format!("\"{}\"", gpu.name.trim())
))
.width(Length::Fill)
.vertical_alignment(Vertical::Center),
.align_y(Vertical::Center),
container(
icon::from_name(if gpu.toggled {
"go-down-symbolic"
@ -688,16 +686,21 @@ impl cosmic::Application for CosmicBatteryApplet {
.width(Length::Fixed(24.0))
.height(Length::Fixed(24.0)),
]
.align_items(Alignment::Center),
.align_y(Alignment::Center),
)
.on_press(Message::ToggleGpuApps(key.clone()))
.into(),
);
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
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();
@ -709,13 +712,13 @@ impl cosmic::Application for CosmicBatteryApplet {
if let Some(icon) = &app.icon {
container(icon::from_name(&**icon).size(12).symbolic(true))
} else {
container(horizontal_space(12.0))
container(horizontal_space().width(12.0))
},
column![text::body(&app.name), text::caption(&app.secondary)]
.width(Length::Fill),
]
.spacing(8)
.align_items(Alignment::Center),
.align_y(Alignment::Center),
)
.into(),
);
@ -742,6 +745,8 @@ impl cosmic::Application for CosmicBatteryApplet {
self.core
.applet
.popup_container(Column::with_children(content).padding([8, 0]))
.max_width(372.)
.max_height(600.)
.into()
}
@ -773,7 +778,7 @@ impl cosmic::Application for CosmicBatteryApplet {
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())
}
}

View file

@ -1,7 +1,10 @@
// Copyright 2023 System76 <info@system76.com>
// 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 tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
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>(
id: I,
) -> iced::Subscription<PowerProfileUpdate> {
subscription::channel(id, 50, move |mut output| async move {
let mut state = State::Ready;
Subscription::run_with_id(
id,
stream::channel(50, move |mut output| async move {
let mut state = State::Ready;
loop {
state = start_listening(state, &mut output).await;
}
})
loop {
state = start_listening(state, &mut output).await;
}
}),
)
}
#[derive(Debug)]

View file

@ -11,7 +11,10 @@ use std::{
time::Duration,
};
use cosmic::iced::{self, subscription};
use cosmic::{
iced::{self, Subscription},
iced_futures::stream,
};
use drm::control::Device as ControlDevice;
use futures::{FutureExt, SinkExt};
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>(
id: I,
) -> iced::Subscription<GpuUpdate> {
subscription::channel(id, 50, move |mut output| async move {
let mut state = State::Ready;
Subscription::run_with_id(
id,
stream::channel(50, move |mut output| async move {
let mut state = State::Ready;
loop {
state = start_listening(state, &mut output).await;
}
})
loop {
state = start_listening(state, &mut output).await;
}
}),
)
}
#[derive(Debug)]

View file

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

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@ pub(crate) mod window_image;
use crate::localize::localize;
use cosmic::{
app::Command,
app,
applet::cosmic_panel_config::PanelAnchor,
cctk::{
cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1,
@ -17,27 +17,29 @@ use cosmic::{
desktop::DesktopEntryData,
iced::{
self,
wayland::popup::{destroy_popup, get_popup},
id::Id as WidgetId,
platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
widget::text,
window::{self},
Length, Subscription,
Length, Limits, Subscription,
},
widget::mouse_area,
widget::{autosize::autosize, mouse_area},
Task,
};
use cosmic::{
iced_style::application,
iced_widget::{Column, Row},
};
use cosmic::iced_widget::{Column, Row};
use cosmic::{widget::tooltip, Element, Theme};
use cosmic::{widget::tooltip, Element};
use once_cell::sync::Lazy;
use wayland_subscription::{
ToplevelRequest, ToplevelUpdate, WaylandImage, WaylandRequest, WaylandUpdate,
};
static AUTOSIZE_MAIN_ID: Lazy<WidgetId> = Lazy::new(|| WidgetId::new("autosize-main"));
pub fn run() -> cosmic::iced::Result {
localize();
cosmic::applet::run::<Minimize>(true, ())
cosmic::applet::run::<Minimize>(())
}
#[derive(Default)]
@ -56,11 +58,11 @@ struct Minimize {
impl Minimize {
fn max_icon_count(&self) -> Option<usize> {
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
match self.core.applet.anchor {
PanelAnchor::Top | PanelAnchor::Bottom => c.new_size.0,
PanelAnchor::Left | PanelAnchor::Right => c.new_size.1,
PanelAnchor::Top | PanelAnchor::Bottom => c.width as u32,
PanelAnchor::Left | PanelAnchor::Right => c.height as u32,
}
}) else {
return index;
@ -68,7 +70,7 @@ impl Minimize {
let button_total_size = self.core.applet.suggested_size(true).0
+ self.core.applet.suggested_padding(true) * 2
+ 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 {
index = None;
} else {
@ -93,13 +95,13 @@ impl cosmic::Application for Minimize {
type Flags = ();
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 {
core,
..Default::default()
},
Command::none(),
Task::none(),
)
}
@ -111,11 +113,11 @@ impl cosmic::Application for Minimize {
&mut self.core
}
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style())
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(&mut self, message: Message) -> app::Task<Message> {
match message {
Message::Wayland(update) => match update {
WaylandUpdate::Init(tx) => {
@ -190,7 +192,7 @@ impl cosmic::Application for Minimize {
},
};
let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
self.core.main_window_id().unwrap(),
new_id,
None,
None,
@ -203,7 +205,7 @@ impl cosmic::Application for Minimize {
}
Message::CloseOverflowPopup => todo!(),
};
Command::none()
Task::none()
}
fn subscription(&self) -> Subscription<Message> {
@ -236,7 +238,7 @@ impl cosmic::Application for Minimize {
Message::Activate(handle.clone()),
padding,
)),
data.name.clone(),
text(data.name.clone()).shaping(text::Shaping::Advanced),
// tooltip::Position::FollowCursor,
// FIXME tooltip fails to appear when created as indicated in design
// maybe it should be a subsurface
@ -248,7 +250,6 @@ impl cosmic::Application for Minimize {
},
)
.snap_within_viewport(false)
.text_shaping(text::Shaping::Advanced)
.into()
});
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
// 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 content = if matches!(
let content: Element<_> = if matches!(
self.core.applet.anchor,
PanelAnchor::Top | PanelAnchor::Bottom
) {
Row::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center)
.align_y(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink)
.width(Length::Shrink)
.spacing(space_xxs)
.into()
} else {
Column::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center)
.align_x(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink)
.width(Length::Shrink)
.spacing(space_xxs)
.into()
};
if self.overflow_popup.is_some() {
mouse_area(content)
.on_press(Message::CloseOverflowPopup)
.into()
} else {
content
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);
}
}
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> {
@ -325,7 +343,7 @@ impl cosmic::Application for Minimize {
Message::Activate(handle.clone()),
padding,
)),
data.name.clone(),
text(data.name.clone()).shaping(text::Shaping::Advanced),
// tooltip::Position::FollowCursor,
// FIXME tooltip fails to appear when created as indicated in design
// maybe it should be a subsurface
@ -337,7 +355,6 @@ impl cosmic::Application for Minimize {
},
)
.snap_within_viewport(false)
.text_shaping(text::Shaping::Advanced)
.into()
});
@ -353,14 +370,14 @@ impl cosmic::Application for Minimize {
) {
Element::from(
Row::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center)
.align_y(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink)
.width(Length::Shrink)
.spacing(space_xxs),
)
} else {
Column::with_children(icon_buttons)
.align_items(cosmic::iced_core::Alignment::Center)
.align_x(cosmic::iced_core::Alignment::Center)
.height(Length::Shrink)
.width(Length::Shrink)
.spacing(space_xxs)

View file

@ -6,7 +6,12 @@
//! 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`.
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 futures::{
channel::mpsc::{unbounded, UnboundedReceiver},
@ -23,16 +28,15 @@ pub static WAYLAND_RX: Lazy<Mutex<Option<UnboundedReceiver<WaylandUpdate>>>> =
Lazy::new(|| Mutex::new(None));
pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> {
subscription::channel(
Subscription::run_with_id(
std::any::TypeId::of::<WaylandUpdate>(),
50,
move |mut output| async move {
stream::channel(50, move |mut output| async move {
let mut state = State::Waiting;
loop {
state = start_listening(state, &mut output).await;
}
},
}),
)
}
@ -86,12 +90,18 @@ pub enum WaylandUpdate {
#[derive(Debug, Clone)]
pub struct WaylandImage {
pub img: Arc<image::RgbaImage>,
pub img: Bytes,
pub width: u32,
pub height: u32,
}
impl WaylandImage {
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(
container(
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 adjusted_width = img.img.width() as f32 / ratio;
let adjusted_height = img.img.height() as f32 / ratio;
let adjusted_width = img.width as f32 / ratio;
let adjusted_height = img.height as f32 / ratio;
Element::from(
Image::new(Handle::from_pixels(
img.img.width(),
img.img.height(),
img.clone(),
))
.width(Length::Fixed(adjusted_width))
.height(Length::Fixed(adjusted_height))
.content_fit(cosmic::iced_core::ContentFit::Contain),
Image::new(Handle::from_rgba(img.width, img.height, img.img))
.width(Length::Fixed(adjusted_width))
.height(Length::Fixed(adjusted_height))
.content_fit(cosmic::iced_core::ContentFit::Contain),
)
} else {
Element::from(
@ -55,15 +51,13 @@ where
.height(Length::Fixed((size - border * 2.0).max(0.))),
)
})
.style(Container::Custom(Box::new(move |theme| {
container::Appearance {
border: Border {
color: theme.cosmic().bg_divider().into(),
width: border,
radius: 0.0.into(),
},
..Default::default()
}
.class(Container::Custom(Box::new(move |theme| container::Style {
border: Border {
color: theme.cosmic().bg_divider().into(),
width: border,
radius: 0.0.into(),
},
..Default::default()
})))
.padding(border as u16)
.height(Length::Shrink)
@ -78,7 +72,7 @@ where
.on_press(on_press)
.width(Length::Shrink)
.height(Length::Shrink)
.style(Button::AppletIcon)
.class(Button::AppletIcon)
.padding(0)
.into(),
icon: icon
@ -104,13 +98,16 @@ impl<'a, Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'a, M
state: &'b mut Tree,
layout: Layout<'_>,
renderer: &cosmic::Renderer,
translation: Vector,
) -> Option<cosmic::iced_core::overlay::Element<'b, Msg, cosmic::Theme, cosmic::Renderer>> {
let children = [&mut self.image_button, &mut self.icon]
.into_iter()
.zip(&mut state.children)
.zip(layout.children())
.filter_map(|((child, state), layout)| {
child.as_widget_mut().overlay(state, layout, renderer)
child
.as_widget_mut()
.overlay(state, layout, renderer, translation)
})
.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,
layout: cosmic::iced_core::Layout<'_>,
renderer: &cosmic::Renderer,
operation: &mut dyn cosmic::widget::Operation<
cosmic::iced_core::widget::OperationOutputWrapper<Msg>,
>,
operation: &mut dyn cosmic::widget::Operation<()>,
) {
let layout = layout.children().collect::<Vec<_>>();
let children = [&self.image_button, &self.icon];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{
iced::{futures, subscription},
iced::{futures, stream},
iced_futures::Subscription,
};
use cosmic_notifications_util::Notification;
@ -27,10 +27,9 @@ pub enum State {
pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<Notification> {
struct SomeWorker;
subscription::channel(
Subscription::run_with_id(
std::any::TypeId::of::<SomeWorker>(),
50,
|mut output| async move {
stream::channel(50, |mut output| async move {
let mut state = State::WaitingForNotificationEvent(0);
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 cosmic::{
app::Command,
app,
applet::{menu_button, padded_control},
cosmic_theme::Spacing,
iced,
iced::{
self,
alignment::{Horizontal, Vertical},
event::{
listen_with,
wayland::{self, LayerEvent},
PlatformSpecific,
},
time,
wayland::{
actions::layer_surface::SctkLayerSurfaceSettings,
popup::{destroy_popup, get_popup},
platform_specific::{
runtime::wayland::layer_surface::SctkLayerSurfaceSettings,
shell::commands::{
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,
},
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,
theme,
widget::{button, divider, horizontal_space, icon, text, vertical_space, Column},
Element, Theme,
widget::{button, divider, horizontal_space, icon, text, vertical_space, Column, Space},
Element, Task,
};
use logind_zbus::{
@ -52,7 +53,7 @@ use crate::{cosmic_session::CosmicSessionProxy, session_manager::SessionManagerP
pub fn run() -> cosmic::iced::Result {
localize::localize();
cosmic::applet::run::<Power>(false, ())
cosmic::applet::run::<Power>(())
}
const COUNTDOWN_LENGTH: u8 = 60;
@ -76,14 +77,14 @@ enum 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));
match self {
PowerAction::Lock => iced::Command::perform(lock(), msg),
PowerAction::LogOut => iced::Command::perform(log_out(), msg),
PowerAction::Suspend => iced::Command::perform(suspend(), msg),
PowerAction::Restart => iced::Command::perform(restart(), msg),
PowerAction::Shutdown => iced::Command::perform(shutdown(), msg),
PowerAction::Lock => iced::Task::perform(lock(), msg),
PowerAction::LogOut => iced::Task::perform(log_out(), msg),
PowerAction::Suspend => iced::Task::perform(suspend(), msg),
PowerAction::Restart => iced::Task::perform(restart(), msg),
PowerAction::Shutdown => iced::Task::perform(shutdown(), msg),
}
}
}
@ -115,14 +116,14 @@ impl cosmic::Application for Power {
&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 {
core,
icon_name: "system-shutdown-symbolic".to_string(),
..Default::default()
},
Command::none(),
Task::none(),
)
}
@ -132,7 +133,7 @@ impl cosmic::Application for Power {
fn subscription(&self) -> Subscription<Message> {
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(
wayland::Event::Layer(LayerEvent::Unfocused, ..),
)) => Some(Message::Cancel),
@ -148,7 +149,7 @@ impl cosmic::Application for Power {
Subscription::batch(subscriptions)
}
fn update(&mut self, message: Message) -> Command<Message> {
fn update(&mut self, message: Message) -> app::Task<Message> {
match message {
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
@ -158,9 +159,9 @@ impl cosmic::Application for Power {
self.popup.replace(new_id);
let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
self.core.main_window_id().unwrap(),
new_id,
None,
Some((500, 500)),
None,
None,
);
@ -174,7 +175,7 @@ impl cosmic::Application for Power {
}
Message::Settings => {
let _ = process::Command::new("cosmic-settings").spawn();
Command::none()
Task::none()
}
Message::Action(action) => {
// Ask for user confirmation of non-destructive actions only
@ -201,20 +202,20 @@ impl cosmic::Application for Power {
if let Err(e) = result {
eprintln!("cosmic-applet-power ERROR: '{}'", e);
}
Command::none()
Task::none()
}
Message::Confirm => {
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 {
Command::none()
Task::none()
}
}
Message::Cancel => {
if let Some((id, _, _)) = self.action_to_confirm.take() {
return destroy_layer_surface(id);
}
Command::none()
Task::none()
}
Message::Countdown => {
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;
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) => {
if self.popup == Some(id) {
self.popup = None;
}
Command::none()
Task::none()
}
Message::LayerFocus => button::focus(CONFIRM_ID.clone()),
}
@ -266,7 +267,7 @@ impl cosmic::Application for Power {
Space::with_width(Length::Fill),
text::body(fl!("lock-screen-shortcut")),
]
.align_items(Alignment::Center)
.align_y(Alignment::Center)
.spacing(space_xxs)
)
.on_press(Message::Action(PowerAction::Lock)),
@ -277,7 +278,7 @@ impl cosmic::Application for Power {
Space::with_width(Length::Fill),
text::body(fl!("log-out-shortcut")),
]
.align_items(Alignment::Center)
.align_y(Alignment::Center)
.spacing(space_xxs)
)
.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]),
power
]
.align_items(Alignment::Start)
.align_x(Alignment::Start)
.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) {
let cosmic_theme = self.core.system_theme().cosmic();
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()])
.id(CONFIRM_ID.clone())
.style(theme::Button::Suggested)
.class(theme::Button::Suggested)
.on_press(Message::Confirm),
)
.secondary_action(
@ -345,7 +351,7 @@ impl cosmic::Application for Power {
32.0,
))
.padding([0, cosmic_theme.space_s()])
.style(theme::Button::Standard)
.class(theme::Button::Standard)
.on_press(Message::Cancel),
)
.icon(text_icon(
@ -365,24 +371,26 @@ impl cosmic::Application for Power {
);
}
mouse_area(
container(dialog)
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.width(Length::Fill)
.height(Length::Fill),
Element::from(
mouse_area(
container(dialog)
.align_x(Horizontal::Center)
.align_y(Vertical::Center)
.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 {
//panic!("no view for window {}", id.0)
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())
}
}
@ -391,12 +399,12 @@ fn power_buttons(name: &str, msg: String) -> cosmic::widget::Button<Message> {
button::custom(
column![text_icon(name, 40), text::body(msg)]
.spacing(4)
.align_items(Alignment::Center)
.align_x(Alignment::Center)
.width(Length::Fill),
)
.width(Length::Fill)
.height(Length::Fixed(76.0))
.style(theme::Button::Text)
.class(theme::Button::Text)
}
fn text_icon(name: &str, size: u16) -> cosmic::widget::Icon {
@ -483,8 +491,8 @@ fn min_width_and_height<'a>(
height: impl Into<Length>,
) -> Column<'a, Message> {
column![
row![e, vertical_space(height)].align_items(Alignment::Center),
horizontal_space(width)
row![e, vertical_space().height(height)].align_y(Alignment::Center),
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
use cosmic::{
app::{self, Command},
app,
applet::cosmic_panel_config::PanelAnchor,
iced::{
self,
wayland::{
popup::{destroy_popup, get_popup},
window::resize_window,
},
window, Subscription,
platform_specific::shell::commands::popup::{destroy_popup, get_popup},
window, Limits, Subscription,
},
iced_style::application,
widget::mouse_area,
Theme,
Element, Task,
};
use std::collections::BTreeMap;
@ -50,11 +46,14 @@ impl App {
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
+ self.core.applet.suggested_padding(true) as u32 * 2;
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 = ();
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 {
core,
..Self::default()
},
Command::none(),
Task::none(),
)
}
@ -82,29 +81,29 @@ impl cosmic::Application for App {
&mut self.core
}
fn style(&self) -> Option<<Theme as application::StyleSheet>::Style> {
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style())
}
fn update(&mut self, message: Msg) -> Command<Msg> {
fn update(&mut self, message: Msg) -> app::Task<Msg> {
match message {
Msg::Closed(surface) => {
if self.popup == Some(surface) {
self.popup = None;
self.open_menu = None;
}
Command::none()
Task::none()
}
Msg::StatusMenu((id, msg)) => match self.menus.get_mut(&id) {
Some(state) => state
.update(msg)
.map(move |msg| app::message::app(Msg::StatusMenu((id, msg)))),
None => Command::none(),
None => Task::none(),
},
Msg::StatusNotifier(event) => match event {
status_notifier_watcher::Event::Connected(connection) => {
self.connection = Some(connection);
Command::none()
Task::none()
}
status_notifier_watcher::Event::Registered(name) => {
let (state, cmd) = status_menu::State::new(name);
@ -119,7 +118,7 @@ impl cosmic::Application for App {
}
let id = self.next_menu_id();
self.menus.insert(id, state);
Command::batch([
app::Task::batch([
self.resize_window(),
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) => {
eprintln!("Status notifier error: {}", err);
Command::none()
Task::none()
}
},
Msg::TogglePopup(id) => {
@ -158,7 +157,7 @@ impl cosmic::Application for App {
}
let popup_id = self.next_popup_id();
let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
self.core.main_window_id().unwrap(),
popup_id,
None,
None,
@ -179,13 +178,13 @@ impl cosmic::Application for App {
popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32;
}
cmds.push(get_popup(popup_settings));
return Command::batch(cmds);
return app::Task::batch(cmds);
} else if let Some(popup_id) = self.popup {
self.menus[&id].closed();
return destroy_popup(popup_id);
}
Command::none()
Task::none()
}
Msg::Hovered(id) => {
let mut cmds = Vec::new();
@ -197,14 +196,14 @@ impl cosmic::Application for App {
self.open_menu = Some(id);
} else {
self.open_menu = Some(old_id);
return Command::none();
return Task::none();
}
} else {
return Command::none();
return Task::none();
}
let popup_id = self.next_popup_id();
let mut popup_settings = self.core.applet.get_popup_settings(
window::Id::MAIN,
self.core.main_window_id().unwrap(),
popup_id,
None,
None,
@ -225,7 +224,7 @@ impl cosmic::Application for App {
popup_settings.positioner.anchor_rect.x = i as i32 * suggested_size as i32;
}
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_mouse_enter(Msg::Hovered(*id))
.on_enter(Msg::Hovered(*id))
.into()
});
if matches!(
self.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
iced::widget::column(children).into()
} else {
iced::widget::row(children).into()
}
self.core
.applet
.autosize_window(
if matches!(
self.core.applet.anchor,
PanelAnchor::Left | PanelAnchor::Right
) {
Element::from(iced::widget::column(children))
} else {
iced::widget::row(children).into()
},
)
.into()
}
fn view_window(&self, _surface: window::Id) -> cosmic::Element<'_, Msg> {
@ -274,6 +278,7 @@ impl cosmic::Application for App {
.core
.applet
.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(),
None => unreachable!(),
},
@ -287,5 +292,5 @@ impl cosmic::Application for App {
}
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 {
pub fn new(item: StatusNotifierItem) -> (Self, iced::Command<Msg>) {
pub fn new(item: StatusNotifierItem) -> (Self, iced::Task<Msg>) {
(
Self {
item,
layout: 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 {
Msg::Layout(layout) => {
match layout {
@ -38,7 +38,7 @@ impl State {
}
Err(err) => eprintln!("Error getting layout from icon: {}", err),
}
iced::Command::none()
iced::Task::none()
}
Msg::Click(id, is_submenu) => {
let menu_proxy = self.item.menu_proxy().clone();
@ -54,7 +54,7 @@ impl State {
} else {
// 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());
}
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());
} else if let Some(icon_name) = i.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(
iced::widget::Row::with_children(content)
.spacing(8)
.align_items(iced::Alignment::Center)
.align_y(iced::Alignment::Center)
.width(iced::Length::Fill),
)
}

View file

@ -1,7 +1,10 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{iced, widget::icon};
use cosmic::{
iced::{self, Subscription},
widget::icon,
};
use futures::{FutureExt, StreamExt};
use zbus::zvariant::{self, OwnedValue};
@ -82,7 +85,7 @@ impl StatusNotifierItem {
// TODO: Only fetch changed part of layout, if that's any faster
pub fn layout_subscription(&self) -> iced::Subscription<Result<Layout, String>> {
let menu_proxy = self.menu_proxy.clone();
iced::subscription::run_with_id(
Subscription::run_with_id(
format!("status-notifier-item-{}", &self.name),
async move {
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?
use cosmic::iced;
use futures::StreamExt;
use cosmic::iced::{self, Subscription};
use futures::{stream, StreamExt};
use crate::subscriptions::status_notifier_item::StatusNotifierItem;
@ -26,24 +26,23 @@ enum State {
}
pub fn subscription() -> iced::Subscription<Event> {
iced::subscription::unfold(
Subscription::run_with_id(
"status-notifier-watcher",
State::NotConnected,
|state| async move {
stream::unfold(State::NotConnected, |state| async move {
match state {
State::NotConnected => match connect().await {
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 {
Some(event) => (event, State::Connected(stream)),
None => iced::futures::future::pending().await,
Some(event) => Some((event, State::Connected(stream))),
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 {
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::{
self,
futures::{self, channel::mpsc, SinkExt, StreamExt},
subscription,
stream, Subscription,
};
use cosmic_protocols::workspace::v1::client::zcosmic_workspace_handle_v1::TilingState;
use once_cell::sync::Lazy;
@ -23,16 +23,15 @@ pub enum WorkspacesUpdate {
}
pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> {
subscription::channel(
Subscription::run_with_id(
std::any::TypeId::of::<WorkspacesUpdate>(),
50,
move |mut output| async move {
stream::channel(50, move |mut output| async move {
let mut state = State::Waiting;
loop {
state = start_listening(state, &mut output).await;
}
},
}),
)
}

View file

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

View file

@ -10,12 +10,16 @@ chrono-tz = "0.9"
i18n-embed-fl.workspace = true
i18n-embed.workspace = true
libcosmic.workspace = true
once_cell = "1"
once_cell.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-subscriber.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
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 {
localize::localize();
cosmic::applet::run::<Window>(true, ())
cosmic::applet::run::<Window>(())
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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