chore: updates after iced-rebase
This commit is contained in:
parent
bd0d180482
commit
71d9d6d5bb
41 changed files with 1786 additions and 2396 deletions
2736
Cargo.lock
generated
2736
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
16
Cargo.toml
16
Cargo.toml
|
|
@ -30,8 +30,6 @@ cosmic-applets-config = { path = "cosmic-applets-config" }
|
|||
cosmic-protocols = { git = "https://github.com/pop-os/cosmic-protocols", default-features = false, features = [
|
||||
"client",
|
||||
], rev = "d0e95be" }
|
||||
cosmic-time = { git = "https://github.com/pop-os/cosmic-time", default-features = false }
|
||||
# cosmic-time = { path = "../cosmic-time", default-features = false ] }
|
||||
|
||||
futures = "0.3"
|
||||
futures-util = "0.3"
|
||||
|
|
@ -59,24 +57,26 @@ tracing = "0.1"
|
|||
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
||||
tracing-log = "0.2.0"
|
||||
tokio = { version = "1.49.0", features = ["full"] }
|
||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
cosmic-config = { git = "https://github.com/pop-os/libcosmic" }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
panic = "abort"
|
||||
lto = "thin"
|
||||
# opt-level = 3
|
||||
# panic = "abort"
|
||||
# lto = "thin"
|
||||
opt-level = 1
|
||||
|
||||
[workspace.metadata.cargo-machete]
|
||||
ignored = ["libcosmic"]
|
||||
|
||||
# [patch."https://github.com/pop-os/libcosmic"]
|
||||
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//", branch = "" }
|
||||
# libcosmic = { git = "https://github.com/pop-os/libcosmic//", branch = "" }
|
||||
# iced_futures = { git = "https://github.com/pop-os/libcosmic//", branch = "" }
|
||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||
# libcosmic = { path = "../libcosmic" }
|
||||
# iced_futures = { path = "../libcosmic/iced/futures" }
|
||||
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//" }
|
||||
# libcosmic = { git = "https://github.com/pop-os/libcosmic//" }
|
||||
# iced_futures = { git = "https://github.com/pop-os/libcosmic//" }
|
||||
|
||||
# [patch."https://github.com/pop-os/winit.git"]
|
||||
# winit = { git = "https://github.com/rust-windowing/winit.git", rev = "241b7a80bba96c91fa3901729cd5dec66abb9be4" }
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ use cctk::{
|
|||
workspace::v1::client::ext_workspace_handle_v1::ExtWorkspaceHandleV1,
|
||||
},
|
||||
};
|
||||
use cosmic::desktop::fde::{self, DesktopEntry, get_languages_from_env, unicase::Ascii};
|
||||
use cosmic::{
|
||||
Apply, Element, Task, app,
|
||||
applet::{
|
||||
|
|
@ -34,20 +33,27 @@ use cosmic::{
|
|||
clipboard::mime::{AllowedMimeTypes, AsMimeTypes},
|
||||
event::listen_with,
|
||||
platform_specific::shell::commands::popup::{destroy_popup, get_popup},
|
||||
widget::{Column, Row, column, mouse_area, row, stack, vertical_rule, vertical_space},
|
||||
widget::{
|
||||
Column, Row, column, mouse_area, row, rule::vertical as vertical_rule,
|
||||
space::horizontal as horizontal_space, space::vertical as vertical_space, stack,
|
||||
},
|
||||
window,
|
||||
},
|
||||
iced_runtime::{core::event, dnd::peek_dnd},
|
||||
surface,
|
||||
theme::{self, Button, Container},
|
||||
widget::{
|
||||
DndDestination, Image, button, container, divider, dnd_source, horizontal_space,
|
||||
DndDestination, Image, button, container, divider, dnd_source,
|
||||
icon::{self, from_name},
|
||||
image::Handle,
|
||||
rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription},
|
||||
svg, text,
|
||||
},
|
||||
};
|
||||
use cosmic::{
|
||||
desktop::fde::{self, DesktopEntry, get_languages_from_env, unicase::Ascii},
|
||||
widget::DndSource,
|
||||
};
|
||||
use cosmic_app_list_config::{APP_ID, AppListConfig};
|
||||
use cosmic_protocols::toplevel_info::v1::client::zcosmic_toplevel_handle_v1::State;
|
||||
use futures::future::pending;
|
||||
|
|
@ -295,7 +301,7 @@ impl DockItem {
|
|||
|
||||
let path = desktop_info.path.clone();
|
||||
let icon_button = if dnd_source_enabled && interaction_enabled {
|
||||
dnd_source(icon_button)
|
||||
DndSource::with_id(icon_button, cosmic::widget::Id::new("asdfasdfadfs"))
|
||||
.window(window_id)
|
||||
.drag_icon(move |_| {
|
||||
(
|
||||
|
|
@ -2410,7 +2416,7 @@ impl cosmic::Application for CosmicAppList {
|
|||
])
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use cctk::{
|
|||
};
|
||||
use cosmic::{
|
||||
iced::{self, Subscription, stream},
|
||||
iced_core::image::Bytes,
|
||||
iced_core::Bytes,
|
||||
};
|
||||
use image::EncodableLayout;
|
||||
|
||||
|
|
@ -31,16 +31,15 @@ pub static WAYLAND_RX: LazyLock<Mutex<Option<UnboundedReceiver<WaylandUpdate>>>>
|
|||
LazyLock::new(|| Mutex::new(None));
|
||||
|
||||
pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> {
|
||||
Subscription::run_with_id(
|
||||
std::any::TypeId::of::<WaylandUpdate>(),
|
||||
Subscription::run_with(std::any::TypeId::of::<WaylandUpdate>(), |_| {
|
||||
stream::channel(50, move |mut output| async move {
|
||||
let mut state = State::Waiting;
|
||||
|
||||
loop {
|
||||
state = start_listening(state, &mut output).await;
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub enum State {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ edition = "2024"
|
|||
anyhow.workspace = true
|
||||
cctk.workspace = true
|
||||
cosmic-protocols.workspace = true
|
||||
cosmic-time.workspace = true
|
||||
i18n-embed-fl.workspace = true
|
||||
i18n-embed.workspace = true
|
||||
libcosmic.workspace = true
|
||||
|
|
@ -19,6 +18,8 @@ tracing.workspace = true
|
|||
|
||||
[dependencies.cosmic-settings-a11y-manager-subscription]
|
||||
git = "https://github.com/pop-os/cosmic-settings"
|
||||
# path = "../../cosmic-settings/subscriptions/a11y-manager"
|
||||
|
||||
[dependencies.cosmic-settings-accessibility-subscription]
|
||||
git = "https://github.com/pop-os/cosmic-settings"
|
||||
# path = "../../cosmic-settings/subscriptions/accessibility"
|
||||
|
|
|
|||
|
|
@ -22,23 +22,16 @@ use cosmic::{
|
|||
},
|
||||
surface,
|
||||
theme::{self, CosmicTheme},
|
||||
widget::{Column, divider, text},
|
||||
widget::{Column, divider, text, toggler},
|
||||
};
|
||||
|
||||
use cosmic_settings_a11y_manager_subscription::{
|
||||
self as cosmic_a11y_manager, AccessibilityEvent, AccessibilityRequest, ColorFilter,
|
||||
};
|
||||
use cosmic_settings_accessibility_subscription::{self as accessibility};
|
||||
use cosmic_time::{Instant, Timeline, anim, chain, id};
|
||||
use std::sync::LazyLock;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
static READER_TOGGLE: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
static FILTER_TOGGLE: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
static HC_TOGGLE: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
static MAGNIFIER_TOGGLE: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
static INVERT_COLORS_TOGGLE: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
|
||||
pub fn run() -> cosmic::iced::Result {
|
||||
cosmic::applet::run::<CosmicA11yApplet>(())
|
||||
}
|
||||
|
|
@ -54,7 +47,6 @@ struct CosmicA11yApplet {
|
|||
dbus_sender: Option<UnboundedSender<accessibility::Request>>,
|
||||
wayland_sender: Option<calloop::channel::Sender<AccessibilityRequest>>,
|
||||
wayland_protocol_version: Option<u32>,
|
||||
timeline: Timeline,
|
||||
token_tx: Option<channel::Sender<TokenRequest>>,
|
||||
screen_filter_active: bool,
|
||||
}
|
||||
|
|
@ -63,12 +55,11 @@ struct CosmicA11yApplet {
|
|||
enum Message {
|
||||
TogglePopup,
|
||||
CloseRequested(window::Id),
|
||||
HighContrastEnabled(chain::Toggler, bool),
|
||||
ScreenReaderEnabled(chain::Toggler, bool),
|
||||
MagnifierEnabled(chain::Toggler, bool),
|
||||
InvertedColorsEnabled(chain::Toggler, bool),
|
||||
FilterColorsEnabled(chain::Toggler, bool),
|
||||
Frame(Instant),
|
||||
HighContrastEnabled(bool),
|
||||
ScreenReaderEnabled(bool),
|
||||
MagnifierEnabled(bool),
|
||||
InvertedColorsEnabled(bool),
|
||||
FilterColorsEnabled(bool),
|
||||
Token(TokenUpdate),
|
||||
OpenSettings,
|
||||
DBusUpdate(accessibility::Response),
|
||||
|
|
@ -104,28 +95,24 @@ impl cosmic::Application for CosmicA11yApplet {
|
|||
|
||||
fn update(&mut self, message: Self::Message) -> app::Task<Self::Message> {
|
||||
match message {
|
||||
Message::Frame(now) => self.timeline.now(now),
|
||||
Message::ScreenReaderEnabled(chain, enabled) => {
|
||||
Message::ScreenReaderEnabled(enabled) => {
|
||||
if let Some(tx) = &self.dbus_sender {
|
||||
self.timeline.set_chain(chain).start();
|
||||
self.reader_enabled = enabled;
|
||||
let _ = tx.send(accessibility::Request::ScreenReader(enabled));
|
||||
} else {
|
||||
self.reader_enabled = false;
|
||||
}
|
||||
}
|
||||
Message::MagnifierEnabled(chain, enabled) => {
|
||||
Message::MagnifierEnabled(enabled) => {
|
||||
if let Some(tx) = &self.wayland_sender {
|
||||
self.timeline.set_chain(chain).start();
|
||||
self.magnifier_enabled = enabled;
|
||||
let _ = tx.send(AccessibilityRequest::Magnifier(enabled));
|
||||
} else {
|
||||
self.magnifier_enabled = false;
|
||||
}
|
||||
}
|
||||
Message::InvertedColorsEnabled(chain, enabled) => {
|
||||
Message::InvertedColorsEnabled(enabled) => {
|
||||
if let Some(tx) = &self.wayland_sender {
|
||||
self.timeline.set_chain(chain).start();
|
||||
self.inverted_colors_enabled = enabled;
|
||||
let _ = tx.send(AccessibilityRequest::ScreenFilter {
|
||||
inverted: enabled,
|
||||
|
|
@ -135,9 +122,8 @@ impl cosmic::Application for CosmicA11yApplet {
|
|||
self.inverted_colors_enabled = false;
|
||||
}
|
||||
}
|
||||
Message::FilterColorsEnabled(chain, enabled) => {
|
||||
Message::FilterColorsEnabled(enabled) => {
|
||||
if let Some(sender) = self.wayland_sender.as_ref() {
|
||||
self.timeline.set_chain(chain).start();
|
||||
self.screen_filter_active = enabled;
|
||||
let _ = sender.send(AccessibilityRequest::ScreenFilter {
|
||||
inverted: self.inverted_colors_enabled,
|
||||
|
|
@ -149,8 +135,6 @@ impl cosmic::Application for CosmicA11yApplet {
|
|||
if let Some(p) = self.popup.take() {
|
||||
return destroy_popup(p);
|
||||
} else {
|
||||
self.timeline = Timeline::new();
|
||||
|
||||
let new_id = window::Id::unique();
|
||||
self.popup.replace(new_id);
|
||||
|
||||
|
|
@ -170,13 +154,13 @@ impl cosmic::Application for CosmicA11yApplet {
|
|||
self.popup = None;
|
||||
}
|
||||
}
|
||||
Message::HighContrastEnabled(chain, enabled) => {
|
||||
Message::HighContrastEnabled(enabled) => {
|
||||
if self.core.system_theme().cosmic().is_high_contrast == enabled
|
||||
|| self.high_contrast.is_some_and(|hc| hc == enabled)
|
||||
{
|
||||
return Task::none();
|
||||
}
|
||||
self.timeline.set_chain(chain).start();
|
||||
|
||||
self.high_contrast = Some(enabled);
|
||||
|
||||
_ = std::thread::spawn(move || {
|
||||
|
|
@ -319,62 +303,44 @@ impl cosmic::Application for CosmicA11yApplet {
|
|||
} = theme::active().cosmic().spacing;
|
||||
|
||||
let reader_toggle = padded_control(
|
||||
anim!(
|
||||
READER_TOGGLE,
|
||||
&self.timeline,
|
||||
fl!("screen-reader"),
|
||||
self.reader_enabled,
|
||||
Message::ScreenReaderEnabled,
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
toggler(self.reader_enabled)
|
||||
.on_toggle(Message::ScreenReaderEnabled)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
.label(fl!("screen-reader")),
|
||||
);
|
||||
let magnifier_toggle = padded_control(
|
||||
anim!(
|
||||
MAGNIFIER_TOGGLE,
|
||||
&self.timeline,
|
||||
fl!("magnifier"),
|
||||
self.magnifier_enabled,
|
||||
Message::MagnifierEnabled,
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
toggler(self.magnifier_enabled)
|
||||
.on_toggle(Message::MagnifierEnabled)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
.label(fl!("magnifier")),
|
||||
);
|
||||
let invert_colors_toggle = padded_control(
|
||||
anim!(
|
||||
INVERT_COLORS_TOGGLE,
|
||||
&self.timeline,
|
||||
fl!("invert-colors"),
|
||||
self.inverted_colors_enabled,
|
||||
Message::InvertedColorsEnabled,
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
toggler(self.inverted_colors_enabled)
|
||||
.on_toggle(Message::InvertedColorsEnabled)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
.label(fl!("invert-colors")),
|
||||
);
|
||||
|
||||
let hc_colors_toggle = padded_control(
|
||||
anim!(
|
||||
HC_TOGGLE,
|
||||
&self.timeline,
|
||||
fl!("high-contrast"),
|
||||
toggler(
|
||||
self.high_contrast
|
||||
.unwrap_or(self.core.system_theme().cosmic().is_high_contrast),
|
||||
Message::HighContrastEnabled,
|
||||
)
|
||||
.on_toggle(Message::HighContrastEnabled)
|
||||
.label(fl!("high-contrast"))
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
);
|
||||
|
||||
let filter_colors_toggle = padded_control(
|
||||
anim!(
|
||||
FILTER_TOGGLE,
|
||||
&self.timeline,
|
||||
fl!("filter-colors"),
|
||||
self.screen_filter_active,
|
||||
Message::FilterColorsEnabled,
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
toggler(self.screen_filter_active)
|
||||
.on_toggle(Message::FilterColorsEnabled)
|
||||
.label(fl!("filter-colors"))
|
||||
.width(Length::Fill)
|
||||
.text_size(14),
|
||||
);
|
||||
|
||||
let content_list = Column::with_capacity(5)
|
||||
|
|
@ -406,9 +372,6 @@ impl cosmic::Application for CosmicA11yApplet {
|
|||
Subscription::batch([
|
||||
accessibility::subscription().map(Message::DBusUpdate),
|
||||
backend::wayland::a11y_subscription().map(Message::WaylandUpdate),
|
||||
self.timeline
|
||||
.as_subscription()
|
||||
.map(|(_, now)| Message::Frame(now)),
|
||||
activation_token_subscription(0).map(Message::Token),
|
||||
])
|
||||
}
|
||||
|
|
@ -417,7 +380,7 @@ impl cosmic::Application for CosmicA11yApplet {
|
|||
Some(Message::CloseRequested(id))
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,16 +26,15 @@ pub enum WaylandUpdate {
|
|||
}
|
||||
|
||||
pub fn a11y_subscription() -> iced::Subscription<WaylandUpdate> {
|
||||
Subscription::run_with_id(
|
||||
std::any::TypeId::of::<WaylandUpdate>(),
|
||||
Subscription::run_with(std::any::TypeId::of::<WaylandUpdate>(), |_| {
|
||||
stream::channel(50, move |mut output| async move {
|
||||
let mut state = State::Waiting;
|
||||
|
||||
loop {
|
||||
state = start_listening(state, &mut output).await;
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_listening(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ edition = "2024"
|
|||
license = "GPL-3.0-only"
|
||||
|
||||
[dependencies]
|
||||
cosmic-time.workspace = true
|
||||
i18n-embed-fl.workspace = true
|
||||
i18n-embed.workspace = true
|
||||
libcosmic.workspace = true
|
||||
|
|
@ -23,3 +22,4 @@ zbus.workspace = true
|
|||
|
||||
[dependencies.cosmic-settings-sound-subscription]
|
||||
git = "https://github.com/pop-os/cosmic-settings"
|
||||
# path = "../../cosmic-settings/subscriptions/sound"
|
||||
|
|
|
|||
|
|
@ -24,20 +24,16 @@ use cosmic::{
|
|||
window,
|
||||
},
|
||||
surface, theme,
|
||||
widget::{Row, button, container, divider, horizontal_space, icon, text},
|
||||
widget::{Row, button, container, divider, icon, space, text, toggler},
|
||||
};
|
||||
use cosmic_settings_sound_subscription as css;
|
||||
use cosmic_time::{Instant, Timeline, anim, chain, id};
|
||||
use iced::platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup};
|
||||
use mpris_subscription::{MprisRequest, MprisUpdate};
|
||||
use mpris2_zbus::player::PlaybackStatus;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
mod config;
|
||||
mod mpris_subscription;
|
||||
|
||||
static SHOW_MEDIA_CONTROLS: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
|
||||
const GO_BACK: &str = "media-skip-backward-symbolic";
|
||||
const GO_NEXT: &str = "media-skip-forward-symbolic";
|
||||
const PAUSE: &str = "media-playback-pause-symbolic";
|
||||
|
|
@ -66,8 +62,6 @@ pub struct Audio {
|
|||
sink_breakpoints: &'static [u32],
|
||||
/// Breakpoitns for the source volume slider.
|
||||
source_breakpoints: &'static [u32],
|
||||
/// Track animations used by the revealers.
|
||||
timeline: Timeline,
|
||||
/// Config file specific to this applet.
|
||||
config: AudioAppletConfig,
|
||||
/// mpris player status
|
||||
|
|
@ -129,8 +123,7 @@ pub enum Message {
|
|||
InputToggle,
|
||||
TogglePopup,
|
||||
CloseRequested(window::Id),
|
||||
ToggleMediaControlsInTopPanel(chain::Toggler, bool),
|
||||
Frame(Instant),
|
||||
ToggleMediaControlsInTopPanel(bool),
|
||||
ConfigChanged(AudioAppletConfig),
|
||||
Mpris(mpris_subscription::MprisUpdate),
|
||||
MprisRequest(MprisRequest),
|
||||
|
|
@ -272,13 +265,12 @@ impl cosmic::Application for Audio {
|
|||
&mut self.core
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> app::Task<Message> {
|
||||
match message {
|
||||
Message::Frame(now) => self.timeline.now(now),
|
||||
Message::Ignore => {}
|
||||
Message::TogglePopup => {
|
||||
if let Some(p) = self.popup.take() {
|
||||
|
|
@ -286,7 +278,6 @@ impl cosmic::Application for Audio {
|
|||
} else {
|
||||
let new_id = window::Id::unique();
|
||||
self.popup.replace(new_id);
|
||||
self.timeline = Timeline::new();
|
||||
|
||||
(self.max_sink_volume, self.sink_breakpoints) = if amplification_sink() {
|
||||
(150, &[100][..])
|
||||
|
|
@ -365,8 +356,7 @@ impl cosmic::Application for Audio {
|
|||
.map(|message| Message::Subscription(message).into());
|
||||
}
|
||||
|
||||
Message::ToggleMediaControlsInTopPanel(chain, enabled) => {
|
||||
self.timeline.set_chain(chain).start();
|
||||
Message::ToggleMediaControlsInTopPanel(enabled) => {
|
||||
self.config.show_media_controls_in_top_panel = enabled;
|
||||
if let Ok(helper) =
|
||||
cosmic::cosmic_config::Config::new(Self::APP_ID, AudioAppletConfig::VERSION)
|
||||
|
|
@ -478,9 +468,6 @@ impl cosmic::Application for Audio {
|
|||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
Subscription::batch([
|
||||
self.timeline
|
||||
.as_subscription()
|
||||
.map(|(_, now)| Message::Frame(now)),
|
||||
self.core.watch_config(Self::APP_ID).map(|u| {
|
||||
for err in u.errors {
|
||||
tracing::error!(?err, "Error watching config");
|
||||
|
|
@ -532,6 +519,7 @@ impl cosmic::Application for Audio {
|
|||
applet_column::Column::with_children(playback_buttons)
|
||||
.push(btn)
|
||||
.align_x(Alignment::Center)
|
||||
.height(Length::Shrink)
|
||||
// TODO configurable variable from the panel?
|
||||
.spacing(
|
||||
-(self.core.applet.suggested_padding(true).0 as f32)
|
||||
|
|
@ -542,6 +530,7 @@ impl cosmic::Application for Audio {
|
|||
applet_row::Row::with_children(playback_buttons)
|
||||
.push(btn)
|
||||
.align_y(Alignment::Center)
|
||||
.width(Length::Shrink)
|
||||
// TODO configurable variable from the panel?
|
||||
.spacing(
|
||||
-(self.core.applet.suggested_padding(true).0 as f32)
|
||||
|
|
@ -696,7 +685,7 @@ impl cosmic::Application for Audio {
|
|||
);
|
||||
|
||||
let mut control_elements = Vec::with_capacity(4);
|
||||
control_elements.push(horizontal_space().width(Length::Fill).into());
|
||||
control_elements.push(space::horizontal().width(Length::Fill).into());
|
||||
if let Some(go_prev) = self.go_previous(32) {
|
||||
control_elements.push(go_prev);
|
||||
}
|
||||
|
|
@ -745,16 +734,11 @@ impl cosmic::Application for Audio {
|
|||
audio_content,
|
||||
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
|
||||
padded_control(
|
||||
anim!(
|
||||
// toggler
|
||||
SHOW_MEDIA_CONTROLS,
|
||||
&self.timeline,
|
||||
Some(fl!("show-media-controls")),
|
||||
self.config.show_media_controls_in_top_panel,
|
||||
Message::ToggleMediaControlsInTopPanel,
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
toggler(self.config.show_media_controls_in_top_panel)
|
||||
.on_toggle(Message::ToggleMediaControlsInTopPanel)
|
||||
.label(fl!("show-media-controls"))
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
),
|
||||
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
|
||||
menu_button(text::body(fl!("sound-settings"))).on_press(Message::OpenSettings)
|
||||
|
|
|
|||
|
|
@ -156,50 +156,51 @@ where
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
self.content
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.layout(&mut tree.children[0], renderer, limits)
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn Operation<()>,
|
||||
) {
|
||||
self.content
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.operate(&mut tree.children[0], layout, renderer, operation);
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
fn update(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: Event,
|
||||
event: &Event,
|
||||
layout: Layout<'_>,
|
||||
cursor: mouse::Cursor,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
viewport: &Rectangle,
|
||||
) -> event::Status {
|
||||
if let event::Status::Captured = self.content.as_widget_mut().on_event(
|
||||
) {
|
||||
self.content.as_widget_mut().update(
|
||||
&mut tree.children[0],
|
||||
event.clone(),
|
||||
&event,
|
||||
layout,
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
viewport,
|
||||
) {
|
||||
return event::Status::Captured;
|
||||
);
|
||||
if shell.is_event_captured() {
|
||||
return;
|
||||
}
|
||||
|
||||
update(
|
||||
|
|
@ -209,7 +210,7 @@ where
|
|||
cursor,
|
||||
shell,
|
||||
tree.state.downcast_mut::<State>(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
|
|
@ -249,17 +250,24 @@ where
|
|||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
layout: Layout<'b>,
|
||||
renderer: &Renderer,
|
||||
viewport: &Rectangle,
|
||||
translation: Vector,
|
||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||
self.content
|
||||
.as_widget_mut()
|
||||
.overlay(&mut tree.children[0], layout, renderer, translation)
|
||||
self.content.as_widget_mut().overlay(
|
||||
&mut tree.children[0],
|
||||
layout,
|
||||
renderer,
|
||||
viewport,
|
||||
translation,
|
||||
)
|
||||
}
|
||||
|
||||
fn drag_destinations(
|
||||
&self,
|
||||
state: &Tree,
|
||||
|
|
@ -298,7 +306,7 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
cursor: mouse::Cursor,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
state: &mut State,
|
||||
) -> event::Status {
|
||||
) {
|
||||
if !cursor.is_over(layout.bounds()) {
|
||||
if !state.is_out_of_bounds {
|
||||
if widget
|
||||
|
|
@ -312,12 +320,13 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
if let Some(message) = widget.on_mouse_exit.as_ref() {
|
||||
shell.publish(message.clone());
|
||||
}
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return event::Status::Ignored;
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_press.as_ref() {
|
||||
|
|
@ -326,8 +335,8 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
{
|
||||
state.drag_initiated = cursor.position();
|
||||
shell.publish(message.clone());
|
||||
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -337,32 +346,32 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
{
|
||||
state.drag_initiated = None;
|
||||
shell.publish(message.clone());
|
||||
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_right_press.as_ref() {
|
||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = event {
|
||||
shell.publish(message.clone());
|
||||
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_right_release.as_ref() {
|
||||
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right)) = event {
|
||||
shell.publish(message.clone());
|
||||
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(message) = widget.on_middle_press.as_ref() {
|
||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) = event {
|
||||
shell.publish(message.clone());
|
||||
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -370,7 +379,8 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle)) = event {
|
||||
shell.publish(message.clone());
|
||||
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(message) = widget
|
||||
|
|
@ -384,7 +394,8 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
if widget.on_mouse_enter.is_some() {
|
||||
shell.publish(message.clone());
|
||||
}
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -400,8 +411,8 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
if position.distance(drag_source) > 1.0 {
|
||||
state.drag_initiated = None;
|
||||
shell.publish(message.clone());
|
||||
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -409,9 +420,8 @@ fn update<Message: Clone, Theme, Renderer>(
|
|||
if let Some(message) = widget.on_mouse_wheel.as_ref() {
|
||||
if let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event {
|
||||
shell.publish((message)(*delta));
|
||||
return event::Status::Captured;
|
||||
shell.capture_event();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event::Status::Ignored
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,14 +83,13 @@ impl PlayerStatus {
|
|||
pub fn mpris_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||
id: I,
|
||||
) -> iced::Subscription<MprisUpdate> {
|
||||
Subscription::run_with_id(
|
||||
id,
|
||||
Subscription::run_with(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)]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ license = "GPL-3.0-only"
|
|||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
cosmic-time.workspace = true
|
||||
cosmic-config.workspace = true
|
||||
cosmic-applets-config.workspace = true
|
||||
drm = "0.14.1"
|
||||
|
|
@ -26,6 +25,8 @@ serde.workspace = true
|
|||
|
||||
[dependencies.cosmic-settings-upower-subscription]
|
||||
git = "https://github.com/pop-os/cosmic-settings"
|
||||
# path = "../../cosmic-settings/subscriptions/upower"
|
||||
|
||||
[dependencies.cosmic-settings-daemon-subscription]
|
||||
git = "https://github.com/pop-os/cosmic-settings"
|
||||
# path = "../../cosmic-settings/subscriptions/settings-daemon"
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use cosmic::{
|
|||
iced_core::{Alignment, Background, Border, Color, Shadow},
|
||||
surface,
|
||||
theme::{self, Button},
|
||||
widget::{button, divider, horizontal_space, icon, scrollable, slider, text, vertical_space},
|
||||
widget::{button, divider, icon, scrollable, slider, space, text, toggler},
|
||||
};
|
||||
use cosmic_applets_config::battery::BatteryAppletConfig;
|
||||
use cosmic_config::{Config, CosmicConfigEntry};
|
||||
|
|
@ -39,8 +39,6 @@ use cosmic_settings_upower_subscription::{
|
|||
kbdbacklight::{KeyboardBacklightRequest, KeyboardBacklightUpdate, kbd_backlight_subscription},
|
||||
};
|
||||
|
||||
use cosmic_time::{Instant, Timeline, anim, chain, id};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::{path::PathBuf, sync::LazyLock, time::Duration};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
|
@ -65,8 +63,6 @@ pub fn run() -> cosmic::iced::Result {
|
|||
cosmic::applet::run::<CosmicBatteryApplet>(())
|
||||
}
|
||||
|
||||
static MAX_CHARGE: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct GPUData {
|
||||
name: String,
|
||||
|
|
@ -95,7 +91,6 @@ struct CosmicBatteryApplet {
|
|||
kbd_sender: Option<UnboundedSender<KeyboardBacklightRequest>>,
|
||||
power_profile: Power,
|
||||
power_profile_sender: Option<UnboundedSender<PowerProfileRequest>>,
|
||||
timeline: Timeline,
|
||||
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
|
||||
zbus_connection: Option<zbus::Connection>,
|
||||
dragging_screen_brightness: bool,
|
||||
|
|
@ -186,7 +181,7 @@ enum Message {
|
|||
SetScreenBrightnessDebounced,
|
||||
ReleaseScreenBrightness,
|
||||
InitChargingLimit(Option<bool>),
|
||||
SetChargingLimit(chain::Toggler, bool),
|
||||
SetChargingLimit(bool),
|
||||
KeyboardBacklight(KeyboardBacklightUpdate),
|
||||
UpowerDevice(DeviceDbusEvent),
|
||||
GpuInit(UnboundedSender<()>),
|
||||
|
|
@ -197,7 +192,6 @@ enum Message {
|
|||
InitProfile(UnboundedSender<PowerProfileRequest>, Power),
|
||||
Profile(Power),
|
||||
SelectProfile(Power),
|
||||
Frame(Instant),
|
||||
ConfigChanged(BatteryAppletConfig),
|
||||
Token(TokenUpdate),
|
||||
OpenSettings,
|
||||
|
|
@ -248,7 +242,6 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
|
||||
fn update(&mut self, message: Self::Message) -> app::Task<Self::Message> {
|
||||
match message {
|
||||
Message::Frame(now) => self.timeline.now(now),
|
||||
Message::SetKbdBrightness(brightness) => {
|
||||
self.kbd_brightness = Some(brightness);
|
||||
|
||||
|
|
@ -330,8 +323,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
self.set_charging_limit(enable);
|
||||
}
|
||||
}
|
||||
Message::SetChargingLimit(chain, enable) => {
|
||||
self.timeline.set_chain(chain).start();
|
||||
Message::SetChargingLimit(enable) => {
|
||||
self.set_charging_limit(enable);
|
||||
|
||||
if enable {
|
||||
|
|
@ -357,7 +349,6 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
if let Some(tx) = &self.kbd_sender {
|
||||
let _ = tx.send(KeyboardBacklightRequest::Get);
|
||||
}
|
||||
self.timeline = Timeline::new();
|
||||
|
||||
let new_id = window::Id::unique();
|
||||
self.popup.replace(new_id);
|
||||
|
|
@ -582,7 +573,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
let content = if self.gpus.is_empty() {
|
||||
btn
|
||||
} else {
|
||||
let dot = container(vertical_space().height(Length::Fixed(0.0)))
|
||||
let dot = container(space::vertical().height(Length::Fixed(0.0)))
|
||||
.padding(2.0)
|
||||
.class(cosmic::style::Container::Custom(Box::new(|theme| {
|
||||
container::Style {
|
||||
|
|
@ -595,6 +586,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
},
|
||||
shadow: Shadow::default(),
|
||||
icon_color: Some(Color::TRANSPARENT),
|
||||
snap: true,
|
||||
}
|
||||
})));
|
||||
let (dot_align_x, dot_align_y) = match self.core.applet.anchor {
|
||||
|
|
@ -638,7 +630,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
},
|
||||
);
|
||||
|
||||
let mut content = vec![
|
||||
let mut content: Vec<Element<'_, Message>> = vec![
|
||||
padded_control(
|
||||
row![
|
||||
icon::from_name(&*self.icon_name).size(24).symbolic(true),
|
||||
|
|
@ -665,7 +657,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
.symbolic(true),
|
||||
)
|
||||
} else {
|
||||
container(horizontal_space().width(1.0))
|
||||
container(space::horizontal().width(1.0))
|
||||
}
|
||||
]
|
||||
.align_y(Alignment::Center),
|
||||
|
|
@ -686,7 +678,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
.symbolic(true),
|
||||
)
|
||||
} else {
|
||||
container(horizontal_space().width(1.0))
|
||||
container(space::horizontal().width(1.0))
|
||||
}
|
||||
]
|
||||
.align_y(Alignment::Center),
|
||||
|
|
@ -707,7 +699,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
.symbolic(true),
|
||||
)
|
||||
} else {
|
||||
container(horizontal_space().width(1.0))
|
||||
container(space::horizontal().width(1.0))
|
||||
}
|
||||
]
|
||||
.align_y(Alignment::Center),
|
||||
|
|
@ -722,16 +714,11 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
if let Some(charging_limit) = self.charging_limit {
|
||||
content.push(
|
||||
padded_control(
|
||||
anim!(
|
||||
//toggler
|
||||
MAX_CHARGE,
|
||||
&self.timeline,
|
||||
fl!("max-charge"),
|
||||
charging_limit,
|
||||
Message::SetChargingLimit,
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
toggler(charging_limit)
|
||||
.on_toggle(Message::SetChargingLimit)
|
||||
.label(fl!("max-charge"))
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -819,7 +806,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
.width(Length::Fill)
|
||||
.align_x(Alignment::Start),
|
||||
container(
|
||||
vertical_space()
|
||||
space::vertical()
|
||||
.width(Length::Fixed(0.0))
|
||||
.height(Length::Fixed(0.0))
|
||||
)
|
||||
|
|
@ -837,6 +824,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
},
|
||||
shadow: Shadow::default(),
|
||||
icon_color: Some(Color::TRANSPARENT),
|
||||
snap: true,
|
||||
}
|
||||
},))),
|
||||
]
|
||||
|
|
@ -902,7 +890,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
if let Some(icon) = &app.icon {
|
||||
container(icon::from_name(&**icon).size(12).symbolic(true))
|
||||
} else {
|
||||
container(horizontal_space().width(12.0))
|
||||
container(space::horizontal().width(12.0))
|
||||
},
|
||||
column![text::body(&app.name), text::caption(&app.secondary)]
|
||||
.width(Length::Fill),
|
||||
|
|
@ -952,9 +940,6 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
GpuUpdate::On(path, name, list) => Message::GpuOn(path, name, list),
|
||||
GpuUpdate::Off(path) => Message::GpuOff(path),
|
||||
}),
|
||||
self.timeline
|
||||
.as_subscription()
|
||||
.map(|(_, now)| Message::Frame(now)),
|
||||
activation_token_subscription(0).map(Message::Token),
|
||||
self.core.watch_config(Self::APP_ID).map(|u| {
|
||||
for err in u.errors {
|
||||
|
|
@ -973,7 +958,7 @@ impl cosmic::Application for CosmicBatteryApplet {
|
|||
Some(Message::CloseRequested(id))
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,16 +98,15 @@ 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::run_with_id(
|
||||
id,
|
||||
Subscription::run_with(id, |_| {
|
||||
stream::channel(50, move |mut output| async move {
|
||||
let mut state = State::Ready;
|
||||
|
||||
loop {
|
||||
state = start_listening(state, &mut output).await;
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -418,16 +418,15 @@ 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::run_with_id(
|
||||
id,
|
||||
Subscription::run_with(id, |_| {
|
||||
stream::channel(50, move |mut output| async move {
|
||||
let mut state = State::Ready;
|
||||
|
||||
loop {
|
||||
state = start_listening(state, &mut output).await;
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ license = "GPL-3.0-only"
|
|||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bluer = { version = "0.17", features = ["bluetoothd", "id"] }
|
||||
cosmic-time.workspace = true
|
||||
futures.workspace = true
|
||||
i18n-embed-fl.workspace = true
|
||||
i18n-embed.workspace = true
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use cosmic::{
|
|||
applet::token::subscription::{TokenRequest, TokenUpdate, activation_token_subscription},
|
||||
cctk::sctk::reexports::calloop,
|
||||
surface,
|
||||
widget::toggler,
|
||||
};
|
||||
|
||||
use cosmic::{
|
||||
|
|
@ -22,7 +23,6 @@ use cosmic::{
|
|||
theme,
|
||||
widget::{button, divider, icon, scrollable, text},
|
||||
};
|
||||
use cosmic_time::{Instant, Timeline, anim, chain, id};
|
||||
use futures::FutureExt;
|
||||
use std::{collections::HashMap, sync::LazyLock, time::Duration};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
|
@ -32,8 +32,6 @@ use crate::{
|
|||
config, fl,
|
||||
};
|
||||
|
||||
static BLUETOOTH_ENABLED: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
|
||||
#[inline]
|
||||
pub fn run() -> cosmic::iced::Result {
|
||||
cosmic::applet::run::<CosmicBluetoothApplet>(())
|
||||
|
|
@ -50,7 +48,6 @@ struct CosmicBluetoothApplet {
|
|||
show_visible_devices: bool,
|
||||
request_confirmation: Option<(BluerDevice, String, Sender<bool>)>,
|
||||
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
|
||||
timeline: Timeline,
|
||||
}
|
||||
|
||||
impl CosmicBluetoothApplet {
|
||||
|
|
@ -77,8 +74,7 @@ enum Message {
|
|||
Confirm,
|
||||
Token(TokenUpdate),
|
||||
OpenSettings,
|
||||
Frame(Instant),
|
||||
ToggleBluetooth(chain::Toggler, bool),
|
||||
ToggleBluetooth(bool),
|
||||
Surface(surface::Action),
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +123,6 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
|||
// TODO request update of state maybe
|
||||
let new_id = window::Id::unique();
|
||||
self.popup.replace(new_id);
|
||||
self.timeline = Timeline::new();
|
||||
|
||||
let popup_settings = self.core.applet.get_popup_settings(
|
||||
self.core.main_window_id().unwrap(),
|
||||
|
|
@ -157,15 +152,6 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
|||
if let Some(err_msg) = err_msg {
|
||||
eprintln!("bluetooth request error: {err_msg}");
|
||||
}
|
||||
if self.bluer_state.bluetooth_enabled != state.bluetooth_enabled {
|
||||
self.timeline
|
||||
.set_chain(if state.bluetooth_enabled {
|
||||
chain::Toggler::on(BLUETOOTH_ENABLED.clone(), 1.0)
|
||||
} else {
|
||||
chain::Toggler::off(BLUETOOTH_ENABLED.clone(), 1.0)
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
self.bluer_state = state;
|
||||
}
|
||||
|
|
@ -300,12 +286,10 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
|||
tokio::spawn(cosmic::process::spawn(cmd));
|
||||
}
|
||||
},
|
||||
Message::Frame(instant) => self.timeline.now(instant),
|
||||
Message::ToggleBluetooth(chain, enabled) => {
|
||||
Message::ToggleBluetooth(enabled) => {
|
||||
if self.bluer_state.bluetooth_enabled == enabled {
|
||||
return Task::none();
|
||||
}
|
||||
self.timeline.set_chain(chain).start();
|
||||
self.bluer_state.bluetooth_enabled = enabled;
|
||||
if let Some(tx) = self.bluer_sender.clone() {
|
||||
tokio::spawn(async move {
|
||||
|
|
@ -416,16 +400,11 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
|||
}
|
||||
|
||||
let mut content = column![column![padded_control(
|
||||
anim!(
|
||||
//toggler
|
||||
BLUETOOTH_ENABLED,
|
||||
&self.timeline,
|
||||
fl!("bluetooth"),
|
||||
self.bluer_state.bluetooth_enabled,
|
||||
Message::ToggleBluetooth,
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
toggler(self.bluer_state.bluetooth_enabled)
|
||||
.label(fl!("bluetooth"))
|
||||
.on_toggle(Message::ToggleBluetooth)
|
||||
.width(Length::Fill)
|
||||
.text_size(14)
|
||||
),],]
|
||||
.align_x(Alignment::Center)
|
||||
.padding([8, 0]);
|
||||
|
|
@ -549,13 +528,10 @@ impl cosmic::Application for CosmicBluetoothApplet {
|
|||
Subscription::batch([
|
||||
activation_token_subscription(0).map(Message::Token),
|
||||
bluetooth_subscription(0).map(Message::BluetoothEvent),
|
||||
self.timeline
|
||||
.as_subscription()
|
||||
.map(|(_, now)| Message::Frame(now)),
|
||||
])
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use rustc_hash::FxHashMap;
|
|||
use std::{
|
||||
fmt::Debug,
|
||||
hash::Hash,
|
||||
mem,
|
||||
sync::{
|
||||
Arc, LazyLock,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
|
|
@ -90,100 +89,104 @@ fn rfkill_path_var() -> std::ffi::OsString {
|
|||
pub fn bluetooth_subscription<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||
id: I,
|
||||
) -> iced::Subscription<BluerEvent> {
|
||||
Subscription::run_with_id(
|
||||
id,
|
||||
stream::channel(50, move |mut output| async move {
|
||||
let mut retry_count = 0u32;
|
||||
Subscription::run_with(id, |_| {
|
||||
stream::channel(
|
||||
50,
|
||||
move |mut output: futures::channel::mpsc::Sender<BluerEvent>| async move {
|
||||
let mut retry_count = 0u32;
|
||||
|
||||
// Initialize connection.
|
||||
let mut session_state = loop {
|
||||
if let Ok(session) = Session::new().await {
|
||||
if let Ok(state) = BluerSessionState::new(session).await {
|
||||
break state;
|
||||
}
|
||||
}
|
||||
|
||||
retry_count = retry_count.saturating_add(1);
|
||||
() = tokio::time::sleep(Duration::from_millis(
|
||||
2_u64.saturating_pow(retry_count).min(68719476734),
|
||||
))
|
||||
.await;
|
||||
};
|
||||
|
||||
let state = bluer_state(&session_state.adapter).await;
|
||||
|
||||
// reconnect to paired and trusted devices
|
||||
if state.bluetooth_enabled {
|
||||
for d in &state.devices {
|
||||
if d.paired_and_trusted() && !matches!(d.status, BluerDeviceStatus::Connected) {
|
||||
_ = session_state
|
||||
.req_tx
|
||||
.send(BluerRequest::ConnectDevice(d.address))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = output
|
||||
.send(BluerEvent::Init {
|
||||
sender: session_state.req_tx.clone(),
|
||||
state: state.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut event_handler = async |event| {
|
||||
let message = match event {
|
||||
BluerSessionEvent::ChangesProcessed(state) => {
|
||||
BluerEvent::DevicesChanged { state }
|
||||
}
|
||||
|
||||
BluerSessionEvent::RequestResponse {
|
||||
req,
|
||||
state,
|
||||
err_msg,
|
||||
} => BluerEvent::RequestResponse {
|
||||
req,
|
||||
state,
|
||||
err_msg,
|
||||
},
|
||||
|
||||
BluerSessionEvent::AgentEvent(e) => BluerEvent::AgentEvent(e),
|
||||
|
||||
_ => return,
|
||||
};
|
||||
|
||||
_ = output.send(message).await;
|
||||
};
|
||||
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(10));
|
||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
loop {
|
||||
let Some(mut session_rx) = session_state.rx.take() else {
|
||||
break;
|
||||
};
|
||||
|
||||
if let Some(event) = session_rx.recv().await {
|
||||
event_handler(event).await;
|
||||
// Consume any additional available events.
|
||||
let mut count = 0;
|
||||
while let Ok(event) = session_rx.try_recv() {
|
||||
event_handler(event).await;
|
||||
count += 1;
|
||||
if count == 100 {
|
||||
break;
|
||||
// Initialize connection.
|
||||
let mut session_state = loop {
|
||||
if let Ok(session) = Session::new().await {
|
||||
if let Ok(state) = BluerSessionState::new(session).await {
|
||||
break state;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
retry_count = retry_count.saturating_add(1);
|
||||
() = tokio::time::sleep(Duration::from_millis(
|
||||
2_u64.saturating_pow(retry_count).min(68719476734),
|
||||
))
|
||||
.await;
|
||||
};
|
||||
|
||||
let state = bluer_state(&session_state.adapter).await;
|
||||
|
||||
// reconnect to paired and trusted devices
|
||||
if state.bluetooth_enabled {
|
||||
for d in &state.devices {
|
||||
if d.paired_and_trusted()
|
||||
&& !matches!(d.status, BluerDeviceStatus::Connected)
|
||||
{
|
||||
_ = session_state
|
||||
.req_tx
|
||||
.send(BluerRequest::ConnectDevice(d.address))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = output
|
||||
.send(BluerEvent::Init {
|
||||
sender: session_state.req_tx.clone(),
|
||||
state: state.clone(),
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut event_handler = async |event| {
|
||||
let message = match event {
|
||||
BluerSessionEvent::ChangesProcessed(state) => {
|
||||
BluerEvent::DevicesChanged { state }
|
||||
}
|
||||
|
||||
BluerSessionEvent::RequestResponse {
|
||||
req,
|
||||
state,
|
||||
err_msg,
|
||||
} => BluerEvent::RequestResponse {
|
||||
req,
|
||||
state,
|
||||
err_msg,
|
||||
},
|
||||
|
||||
BluerSessionEvent::AgentEvent(e) => BluerEvent::AgentEvent(e),
|
||||
|
||||
_ => return,
|
||||
};
|
||||
|
||||
_ = output.send(message).await;
|
||||
};
|
||||
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(10));
|
||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
|
||||
loop {
|
||||
let Some(mut session_rx) = session_state.rx.take() else {
|
||||
break;
|
||||
};
|
||||
|
||||
if let Some(event) = session_rx.recv().await {
|
||||
event_handler(event).await;
|
||||
// Consume any additional available events.
|
||||
let mut count = 0;
|
||||
while let Ok(event) = session_rx.try_recv() {
|
||||
event_handler(event).await;
|
||||
count += 1;
|
||||
if count == 100 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
session_state.rx = Some(session_rx);
|
||||
interval.tick().await;
|
||||
}
|
||||
|
||||
session_state.rx = Some(session_rx);
|
||||
interval.tick().await;
|
||||
}
|
||||
|
||||
_ = output.send(BluerEvent::Finished).await;
|
||||
futures::future::pending().await
|
||||
}),
|
||||
)
|
||||
_ = output.send(BluerEvent::Finished).await;
|
||||
futures::future::pending().await
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ edition = "2024"
|
|||
license = "GPL-3.0-only"
|
||||
|
||||
[dependencies]
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ use cosmic::{
|
|||
prelude::*,
|
||||
surface, theme,
|
||||
widget::{
|
||||
self, autosize, horizontal_space,
|
||||
self, autosize,
|
||||
rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription},
|
||||
vertical_space,
|
||||
space,
|
||||
},
|
||||
};
|
||||
use cosmic_comp_config::CosmicCompConfig;
|
||||
|
|
@ -291,7 +291,7 @@ impl cosmic::Application for Window {
|
|||
])
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ impl cosmic::Application for Minimize {
|
|||
&mut self.core
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use cosmic::{
|
|||
wayland_protocols::ext::foreign_toplevel_list::v1::client::ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||
},
|
||||
iced::{self, Subscription},
|
||||
iced_core::image::Bytes,
|
||||
iced_core::Bytes,
|
||||
iced_futures::{futures, stream},
|
||||
};
|
||||
use futures::SinkExt;
|
||||
|
|
@ -22,24 +22,26 @@ use std::fmt::Debug;
|
|||
use crate::wayland_handler::wayland_handler;
|
||||
|
||||
pub fn wayland_subscription() -> iced::Subscription<WaylandUpdate> {
|
||||
Subscription::run_with_id(
|
||||
std::any::TypeId::of::<WaylandUpdate>(),
|
||||
stream::channel(1, move |mut output| async move {
|
||||
let (calloop_tx, calloop_rx) = calloop::channel::channel();
|
||||
let runtime = tokio::runtime::Handle::current();
|
||||
Subscription::run_with(std::any::TypeId::of::<WaylandUpdate>(), |_| {
|
||||
stream::channel(
|
||||
1,
|
||||
move |mut output: futures::channel::mpsc::Sender<WaylandUpdate>| async move {
|
||||
let (calloop_tx, calloop_rx) = calloop::channel::channel();
|
||||
let runtime = tokio::runtime::Handle::current();
|
||||
|
||||
let _ = std::thread::spawn(move || {
|
||||
runtime.block_on(async move {
|
||||
_ = output.send(WaylandUpdate::Init(calloop_tx)).await;
|
||||
wayland_handler(output.clone(), calloop_rx);
|
||||
tracing::error!("Wayland handler thread died");
|
||||
_ = output.send(WaylandUpdate::Finished).await;
|
||||
let _ = std::thread::spawn(move || {
|
||||
runtime.block_on(async move {
|
||||
_ = output.send(WaylandUpdate::Init(calloop_tx)).await;
|
||||
wayland_handler(output.clone(), calloop_rx);
|
||||
tracing::error!("Wayland handler thread died");
|
||||
_ = output.send(WaylandUpdate::Finished).await;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
futures::future::pending().await
|
||||
}),
|
||||
)
|
||||
futures::future::pending().await
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
|||
|
|
@ -93,8 +93,9 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
fn overlay<'b>(
|
||||
&'b mut self,
|
||||
state: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
layout: Layout<'b>,
|
||||
renderer: &cosmic::Renderer,
|
||||
viewport: &cosmic::iced_core::Rectangle,
|
||||
translation: Vector,
|
||||
) -> Option<cosmic::iced_core::overlay::Element<'b, Msg, cosmic::Theme, cosmic::Renderer>> {
|
||||
let children = [&mut self.image_button, &mut self.icon]
|
||||
|
|
@ -104,7 +105,7 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
.filter_map(|((child, state), layout)| {
|
||||
child
|
||||
.as_widget_mut()
|
||||
.overlay(state, layout, renderer, translation)
|
||||
.overlay(state, layout, renderer, viewport, translation)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
|
@ -116,7 +117,7 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut cosmic::iced_core::widget::Tree,
|
||||
renderer: &cosmic::Renderer,
|
||||
limits: &cosmic::iced_core::layout::Limits,
|
||||
|
|
@ -125,7 +126,7 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
let button = &mut children[0];
|
||||
let button_node = self
|
||||
.image_button
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.layout(button, renderer, limits);
|
||||
let img_node = &button_node.children()[0].children()[0];
|
||||
|
||||
|
|
@ -135,7 +136,7 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
let icon = &mut children[1];
|
||||
let icon_node = self
|
||||
.icon
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.layout(
|
||||
icon,
|
||||
renderer,
|
||||
|
|
@ -185,14 +186,14 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
tree: &mut cosmic::iced_core::widget::Tree,
|
||||
layout: cosmic::iced_core::Layout<'_>,
|
||||
renderer: &cosmic::Renderer,
|
||||
operation: &mut dyn cosmic::widget::Operation<()>,
|
||||
) {
|
||||
let layout = layout.children().collect::<Vec<_>>();
|
||||
let children = [&self.image_button, &self.icon];
|
||||
let children = [&mut self.image_button, &mut self.icon];
|
||||
for (i, (layout, child)) in layout
|
||||
.into_iter()
|
||||
.zip(children.into_iter())
|
||||
|
|
@ -200,26 +201,27 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
.rev()
|
||||
{
|
||||
let tree = &mut tree.children[i];
|
||||
child.as_widget().operate(tree, layout, renderer, operation);
|
||||
child
|
||||
.as_widget_mut()
|
||||
.operate(tree, layout, renderer, operation);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
fn update(
|
||||
&mut self,
|
||||
state: &mut cosmic::iced_core::widget::Tree,
|
||||
event: cosmic::iced_core::Event,
|
||||
event: &cosmic::iced_core::Event,
|
||||
layout: cosmic::iced_core::Layout<'_>,
|
||||
cursor: cosmic::iced_core::mouse::Cursor,
|
||||
renderer: &cosmic::Renderer,
|
||||
clipboard: &mut dyn cosmic::iced_core::Clipboard,
|
||||
shell: &mut cosmic::iced_core::Shell<'_, Msg>,
|
||||
viewport: &cosmic::iced_core::Rectangle,
|
||||
) -> cosmic::iced_core::event::Status {
|
||||
) {
|
||||
let children = [&mut self.image_button, &mut self.icon];
|
||||
|
||||
let layout = layout.children().collect::<Vec<_>>();
|
||||
// draw children in order
|
||||
let mut status = cosmic::iced_core::event::Status::Ignored;
|
||||
for (i, (layout, child)) in layout
|
||||
.into_iter()
|
||||
.zip(children.into_iter())
|
||||
|
|
@ -228,21 +230,13 @@ impl<Msg> Widget<Msg, cosmic::Theme, cosmic::Renderer> for WindowImage<'_, Msg>
|
|||
{
|
||||
let tree = &mut state.children[i];
|
||||
|
||||
status = child.as_widget_mut().on_event(
|
||||
tree,
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
viewport,
|
||||
child.as_widget_mut().update(
|
||||
tree, event, layout, cursor, renderer, clipboard, shell, viewport,
|
||||
);
|
||||
if matches!(status, cosmic::iced_core::event::Status::Captured) {
|
||||
return status;
|
||||
if shell.is_event_captured() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ license = "GPL-3.0-or-later"
|
|||
anyhow.workspace = true
|
||||
async-fn-stream = "0.3"
|
||||
cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||
cosmic-time.workspace = true
|
||||
futures.workspace = true
|
||||
futures-util.workspace = true
|
||||
i18n-embed-fl.workspace = true
|
||||
|
|
@ -35,7 +34,8 @@ uuid = { version = "1.21.0", features = ["v4"] }
|
|||
|
||||
[dependencies.cosmic-settings-network-manager-subscription]
|
||||
git = "https://github.com/pop-os/cosmic-settings/"
|
||||
# path = "../../cosmic-settings/subscriptions/network-manager"
|
||||
|
||||
[dependencies.cosmic-settings-airplane-mode-subscription]
|
||||
git = "https://github.com/pop-os/cosmic-settings/"
|
||||
|
||||
# path = "../../cosmic-settings/subscriptions/airplane-mode"
|
||||
|
|
|
|||
|
|
@ -36,14 +36,13 @@ use cosmic::{
|
|||
widget::{
|
||||
Column, Id, Row, button, container, divider,
|
||||
icon::{self, from_name},
|
||||
scrollable, secure_input, text, text_input,
|
||||
scrollable, secure_input, text, text_input, toggler,
|
||||
},
|
||||
};
|
||||
use cosmic_dbus_networkmanager::interface::{
|
||||
access_point,
|
||||
enums::{ActiveConnectionState, DeviceState, NmConnectivityState, NmState},
|
||||
};
|
||||
use cosmic_time::{Instant, Timeline, anim, chain, id};
|
||||
|
||||
use futures::{StreamExt, channel::mpsc::TrySendError};
|
||||
use zbus::{Connection, zvariant::ObjectPath};
|
||||
|
|
@ -96,9 +95,6 @@ impl From<NewConnectionState> for AccessPoint {
|
|||
}
|
||||
}
|
||||
|
||||
static WIFI: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
static AIRPLANE_MODE: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
|
||||
pub static SECURE_INPUT_WIFI: LazyLock<Id> = LazyLock::new(Id::unique);
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
|
|
@ -170,7 +166,6 @@ struct CosmicNetworkApplet {
|
|||
show_available_vpns: bool,
|
||||
new_connection: Option<NewConnectionState>,
|
||||
conn: Option<Connection>,
|
||||
timeline: Timeline,
|
||||
toggle_wifi_ctr: u128,
|
||||
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
|
||||
failed_known_ssids: FxHashSet<Arc<str>>,
|
||||
|
|
@ -363,31 +358,15 @@ impl CosmicNetworkApplet {
|
|||
}
|
||||
|
||||
fn update_togglers(&mut self, state: &NetworkManagerState) {
|
||||
let timeline = &mut self.timeline;
|
||||
let mut changed = false;
|
||||
if self.nm_state.nm_state.wifi_enabled != state.wifi_enabled {
|
||||
self.nm_state.nm_state.wifi_enabled = state.wifi_enabled;
|
||||
changed = true;
|
||||
let chain = if state.wifi_enabled {
|
||||
chain::Toggler::on(WIFI.clone(), 1.)
|
||||
} else {
|
||||
chain::Toggler::off(WIFI.clone(), 1.)
|
||||
};
|
||||
timeline.set_chain(chain);
|
||||
}
|
||||
|
||||
if self.nm_state.nm_state.airplane_mode != state.airplane_mode {
|
||||
self.nm_state.nm_state.airplane_mode = state.airplane_mode;
|
||||
changed = true;
|
||||
let chain = if state.airplane_mode {
|
||||
chain::Toggler::on(AIRPLANE_MODE.clone(), 1.)
|
||||
} else {
|
||||
chain::Toggler::off(AIRPLANE_MODE.clone(), 1.)
|
||||
};
|
||||
timeline.set_chain(chain);
|
||||
}
|
||||
if changed {
|
||||
timeline.start();
|
||||
}
|
||||
}
|
||||
fn view_window_return<'a>(&self, mut content: Column<'a, Message>) -> Element<'a, Message> {
|
||||
|
|
@ -463,7 +442,6 @@ pub(crate) enum Message {
|
|||
ToggleVisibleNetworks,
|
||||
SelectWirelessAccessPoint(AccessPoint),
|
||||
CancelNewConnection,
|
||||
Frame(Instant),
|
||||
Token(TokenUpdate),
|
||||
OpenSettings,
|
||||
ResetFailedKnownSsid(String, HwAddress),
|
||||
|
|
@ -789,7 +767,6 @@ impl cosmic::Application for CosmicNetworkApplet {
|
|||
|
||||
fn update(&mut self, message: Message) -> app::Task<Message> {
|
||||
match message {
|
||||
Message::Frame(now) => self.timeline.now(now),
|
||||
Message::TogglePopup => {
|
||||
if let Some(p) = self.popup.take() {
|
||||
self.show_visible_networks = false;
|
||||
|
|
@ -817,7 +794,6 @@ impl cosmic::Application for CosmicNetworkApplet {
|
|||
// TODO request update of state maybe
|
||||
let new_id = window::Id::unique();
|
||||
self.popup.replace(new_id);
|
||||
self.timeline = Timeline::new();
|
||||
|
||||
let popup_settings = self.core.applet.get_popup_settings(
|
||||
self.core.main_window_id().unwrap(),
|
||||
|
|
@ -1604,16 +1580,11 @@ impl cosmic::Application for CosmicNetworkApplet {
|
|||
column![
|
||||
vpn_ethernet_col,
|
||||
padded_control(
|
||||
anim!(
|
||||
//toggler
|
||||
AIRPLANE_MODE,
|
||||
&self.timeline,
|
||||
fl!("airplane-mode"),
|
||||
self.nm_state.nm_state.airplane_mode,
|
||||
|_chain, enable| { Message::ToggleAirplaneMode(enable) },
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
toggler(self.nm_state.nm_state.airplane_mode,)
|
||||
.label(fl!("airplane-mode"))
|
||||
.on_toggle(Message::ToggleAirplaneMode)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
),
|
||||
padded_control(divider::horizontal::default())
|
||||
.padding([space_xxs, space_s]),
|
||||
|
|
@ -1621,16 +1592,11 @@ impl cosmic::Application for CosmicNetworkApplet {
|
|||
.align_x(Alignment::Center)
|
||||
),
|
||||
padded_control(
|
||||
anim!(
|
||||
//toggler
|
||||
WIFI,
|
||||
&self.timeline,
|
||||
fl!("wifi"),
|
||||
self.nm_state.nm_state.wifi_enabled,
|
||||
|_chain, enable| { Message::WiFiEnable(enable) },
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
toggler(self.nm_state.nm_state.wifi_enabled,)
|
||||
.label(fl!("wifi"))
|
||||
.on_toggle(Message::WiFiEnable)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
),
|
||||
]
|
||||
.align_x(Alignment::Center)
|
||||
|
|
@ -1993,16 +1959,10 @@ impl cosmic::Application for CosmicNetworkApplet {
|
|||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
let timeline = self
|
||||
.timeline
|
||||
.as_subscription()
|
||||
.map(|(_, now)| Message::Frame(now));
|
||||
let token_sub = activation_token_subscription(0).map(Message::Token);
|
||||
|
||||
Subscription::batch([timeline, token_sub])
|
||||
activation_token_subscription(0).map(Message::Token)
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ license = "GPL-3.0-only"
|
|||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
cosmic-time.workspace = true
|
||||
libcosmic.workspace = true
|
||||
tokio.workspace = true
|
||||
cosmic-notifications-util = { git = "https://github.com/pop-os/cosmic-notifications" }
|
||||
|
|
|
|||
|
|
@ -16,18 +16,17 @@ use cosmic::{
|
|||
Alignment, Length, Subscription,
|
||||
advanced::text::{Ellipsize, EllipsizeHeightLimit},
|
||||
platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
|
||||
widget::{column, row},
|
||||
widget::{self, column, row},
|
||||
window,
|
||||
},
|
||||
surface, theme,
|
||||
widget::{Column, button, container, divider, icon, scrollable, text},
|
||||
widget::{Column, button, cards, container, divider, icon, scrollable, space, text, toggler},
|
||||
};
|
||||
|
||||
use cosmic::iced_futures::futures::executor::block_on;
|
||||
|
||||
use cosmic_notifications_config::NotificationsConfig;
|
||||
use cosmic_notifications_util::{ActionId, Image, Notification};
|
||||
use cosmic_time::{Instant, Timeline, anim, chain, id};
|
||||
use std::{borrow::Cow, collections::HashMap, path::PathBuf, sync::LazyLock};
|
||||
use subscriptions::notifications::{self, NotificationsAppletProxy};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
|
@ -38,8 +37,6 @@ pub fn run() -> cosmic::iced::Result {
|
|||
cosmic::applet::run::<Notifications>(())
|
||||
}
|
||||
|
||||
static DO_NOT_DISTURB: LazyLock<id::Toggler> = LazyLock::new(id::Toggler::unique);
|
||||
|
||||
struct Notifications {
|
||||
core: cosmic::app::Core,
|
||||
config: NotificationsConfig,
|
||||
|
|
@ -47,27 +44,14 @@ struct Notifications {
|
|||
icon_name: String,
|
||||
popup: Option<window::Id>,
|
||||
// notifications: Vec<Notification>,
|
||||
timeline: Timeline,
|
||||
dbus_sender: Option<Sender<subscriptions::dbus::Input>>,
|
||||
cards: Vec<(id::Cards, Vec<Notification>, bool, String, String, String)>,
|
||||
cards: Vec<(widget::Id, Vec<Notification>, bool, String, String, String)>,
|
||||
token_tx: Option<calloop::channel::Sender<TokenRequest>>,
|
||||
proxy: NotificationsAppletProxy<'static>,
|
||||
notifications_tx: Option<Sender<notifications::Input>>,
|
||||
}
|
||||
|
||||
impl Notifications {
|
||||
fn update_cards(&mut self, id: id::Cards) {
|
||||
if let Some((id, _, card_value, ..)) = self.cards.iter().find(|c| c.0 == id) {
|
||||
let chain = if *card_value {
|
||||
chain::Cards::on(id.clone(), 1.)
|
||||
} else {
|
||||
chain::Cards::off(id.clone(), 1.)
|
||||
};
|
||||
self.timeline.set_chain(chain);
|
||||
self.timeline.start();
|
||||
}
|
||||
}
|
||||
|
||||
fn update_icon(&mut self) {
|
||||
self.icon_name = if self.config.do_not_disturb {
|
||||
"cosmic-applet-notification-disabled-symbolic"
|
||||
|
|
@ -84,8 +68,7 @@ impl Notifications {
|
|||
enum Message {
|
||||
TogglePopup,
|
||||
CloseRequested(window::Id),
|
||||
DoNotDisturb(chain::Toggler, bool),
|
||||
Frame(Instant),
|
||||
DoNotDisturb(bool),
|
||||
NotificationEvent(notifications::Output),
|
||||
Config(NotificationsConfig),
|
||||
DbusEvent(subscriptions::dbus::Output),
|
||||
|
|
@ -128,7 +111,6 @@ impl cosmic::Application for Notifications {
|
|||
config,
|
||||
icon_name: String::default(),
|
||||
popup: None,
|
||||
timeline: Timeline::default(),
|
||||
dbus_sender: Option::default(),
|
||||
cards: Vec::new(),
|
||||
token_tx: Option::default(),
|
||||
|
|
@ -148,7 +130,7 @@ impl cosmic::Application for Notifications {
|
|||
&mut self.core
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
|
|
@ -162,9 +144,6 @@ impl cosmic::Application for Notifications {
|
|||
}
|
||||
Message::Config(res.config)
|
||||
}),
|
||||
self.timeline
|
||||
.as_subscription()
|
||||
.map(|(_, now)| Message::Frame(now)),
|
||||
subscriptions::dbus::proxy().map(Message::DbusEvent),
|
||||
subscriptions::notifications::notifications(self.proxy.clone())
|
||||
.map(Message::NotificationEvent),
|
||||
|
|
@ -174,16 +153,12 @@ impl cosmic::Application for Notifications {
|
|||
|
||||
fn update(&mut self, message: Self::Message) -> app::Task<Self::Message> {
|
||||
match message {
|
||||
Message::Frame(now) => {
|
||||
self.timeline.now(now);
|
||||
}
|
||||
Message::TogglePopup => {
|
||||
if let Some(p) = self.popup.take() {
|
||||
return destroy_popup(p);
|
||||
} else {
|
||||
let new_id = window::Id::unique();
|
||||
self.popup.replace(new_id);
|
||||
self.timeline = Timeline::new();
|
||||
|
||||
let popup_settings = self.core.applet.get_popup_settings(
|
||||
self.core.main_window_id().unwrap(),
|
||||
|
|
@ -196,8 +171,7 @@ impl cosmic::Application for Notifications {
|
|||
return get_popup(popup_settings);
|
||||
}
|
||||
}
|
||||
Message::DoNotDisturb(chain, b) => {
|
||||
self.timeline.set_chain(chain).start();
|
||||
Message::DoNotDisturb(b) => {
|
||||
self.config.do_not_disturb = b;
|
||||
if let Some(helper) = &self.config_helper {
|
||||
if let Err(err) = self.config.write_entry(helper) {
|
||||
|
|
@ -223,7 +197,7 @@ impl cosmic::Application for Notifications {
|
|||
}
|
||||
} else {
|
||||
self.cards.push((
|
||||
id::Cards::new(n.app_name.clone()),
|
||||
widget::Id::new(n.app_name.clone()),
|
||||
vec![n],
|
||||
false,
|
||||
fl!("show-more", HashMap::from([("more", "1")])),
|
||||
|
|
@ -315,7 +289,6 @@ impl cosmic::Application for Notifications {
|
|||
} else {
|
||||
return Task::none();
|
||||
};
|
||||
self.update_cards(id);
|
||||
}
|
||||
Message::CloseRequested(id) => {
|
||||
if Some(id) == self.popup {
|
||||
|
|
@ -412,15 +385,11 @@ impl cosmic::Application for Notifications {
|
|||
} = theme::active().cosmic().spacing;
|
||||
|
||||
let do_not_disturb = padded_control(row![
|
||||
anim!(
|
||||
DO_NOT_DISTURB,
|
||||
&self.timeline,
|
||||
fl!("do-not-disturb"),
|
||||
self.config.do_not_disturb,
|
||||
Message::DoNotDisturb
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
toggler(self.config.do_not_disturb)
|
||||
.on_toggle(Message::DoNotDisturb)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
.label(fl!("do-not-disturb"))
|
||||
]);
|
||||
|
||||
let notifications = if self.cards.is_empty() {
|
||||
|
|
@ -522,13 +491,12 @@ impl cosmic::Application for Notifications {
|
|||
Some(cosmic::widget::icon::from_name(n.app_icon.as_str()).handle())
|
||||
}
|
||||
});
|
||||
let card_list = anim!(
|
||||
let card_list = cards(
|
||||
//cards
|
||||
c.0.clone(),
|
||||
&self.timeline,
|
||||
notif_elems,
|
||||
Message::ClearAll(Some(name.clone())),
|
||||
Some(move |_, e| Message::CardsToggled(name.clone(), e)),
|
||||
Some(move |e| Message::CardsToggled(name.clone(), e)),
|
||||
Some(move |id| Message::ActivateNotification(ids[id])),
|
||||
&c.3,
|
||||
&c.4,
|
||||
|
|
@ -536,6 +504,7 @@ impl cosmic::Application for Notifications {
|
|||
show_more_icon,
|
||||
c.2,
|
||||
);
|
||||
// let card_list = space::horizontal().width(Length::Fixed(10.));
|
||||
notifs.push(card_list.into());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,73 +33,75 @@ pub enum Output {
|
|||
pub fn proxy() -> Subscription<Output> {
|
||||
struct SomeWorker;
|
||||
|
||||
Subscription::run_with_id(
|
||||
std::any::TypeId::of::<SomeWorker>(),
|
||||
stream::channel(50, |mut output| async move {
|
||||
let mut state = State::Ready;
|
||||
Subscription::run_with(std::any::TypeId::of::<SomeWorker>(), |_| {
|
||||
stream::channel(
|
||||
50,
|
||||
|mut output: futures::channel::mpsc::Sender<Output>| async move {
|
||||
let mut state = State::Ready;
|
||||
|
||||
loop {
|
||||
match &mut state {
|
||||
State::Ready => {
|
||||
let (sender, receiver) = channel(10);
|
||||
let Ok(conn) = Connection::session().await else {
|
||||
error!("Failed to connect to session bus");
|
||||
state = State::Finished;
|
||||
continue;
|
||||
};
|
||||
loop {
|
||||
match &mut state {
|
||||
State::Ready => {
|
||||
let (sender, receiver) = channel(10);
|
||||
let Ok(conn) = Connection::session().await else {
|
||||
error!("Failed to connect to session bus");
|
||||
state = State::Finished;
|
||||
continue;
|
||||
};
|
||||
|
||||
let Ok(proxy) = NotificationsProxy::new(&conn).await else {
|
||||
error!("Failed to create proxy from session connection");
|
||||
state = State::Finished;
|
||||
continue;
|
||||
};
|
||||
let tx = sender.clone();
|
||||
if let Err(err) = output.send(Output::Ready(sender)).await {
|
||||
error!("Failed to send sender: {}", err);
|
||||
state = State::Finished;
|
||||
continue;
|
||||
}
|
||||
state = match proxy.receive_notification_closed().await {
|
||||
Ok(mut s) => {
|
||||
tokio::spawn(async move {
|
||||
while let Some(msg) = s.next().await {
|
||||
let Ok(id) = msg.args().map(|args| args.id) else {
|
||||
continue;
|
||||
};
|
||||
_ = tx.send(Input::CloseEvent(id)).await;
|
||||
}
|
||||
});
|
||||
State::WaitingForNotificationEvent(proxy, receiver)
|
||||
let Ok(proxy) = NotificationsProxy::new(&conn).await else {
|
||||
error!("Failed to create proxy from session connection");
|
||||
state = State::Finished;
|
||||
continue;
|
||||
};
|
||||
let tx = sender.clone();
|
||||
if let Err(err) = output.send(Output::Ready(sender)).await {
|
||||
error!("Failed to send sender: {}", err);
|
||||
state = State::Finished;
|
||||
continue;
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to get a stream of signals for notifications. {}",
|
||||
err
|
||||
);
|
||||
State::Finished
|
||||
state = match proxy.receive_notification_closed().await {
|
||||
Ok(mut s) => {
|
||||
tokio::spawn(async move {
|
||||
while let Some(msg) = s.next().await {
|
||||
let Ok(id) = msg.args().map(|args| args.id) else {
|
||||
continue;
|
||||
};
|
||||
_ = tx.send(Input::CloseEvent(id)).await;
|
||||
}
|
||||
});
|
||||
State::WaitingForNotificationEvent(proxy, receiver)
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to get a stream of signals for notifications. {}",
|
||||
err
|
||||
);
|
||||
State::Finished
|
||||
}
|
||||
};
|
||||
}
|
||||
State::WaitingForNotificationEvent(proxy, rx) => match rx.recv().await {
|
||||
Some(Input::Dismiss(id)) => {
|
||||
if let Err(err) = proxy.close_notification(id).await {
|
||||
error!("Failed to close notification: {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
State::WaitingForNotificationEvent(proxy, rx) => match rx.recv().await {
|
||||
Some(Input::Dismiss(id)) => {
|
||||
if let Err(err) = proxy.close_notification(id).await {
|
||||
error!("Failed to close notification: {}", err);
|
||||
Some(Input::CloseEvent(id)) => {
|
||||
_ = output.send(Output::CloseEvent(id)).await;
|
||||
}
|
||||
None => {
|
||||
warn!("Notification event channel closed");
|
||||
state = State::Finished;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
State::Finished => {
|
||||
let () = futures::future::pending().await;
|
||||
}
|
||||
Some(Input::CloseEvent(id)) => {
|
||||
_ = output.send(Output::CloseEvent(id)).await;
|
||||
}
|
||||
None => {
|
||||
warn!("Notification event channel closed");
|
||||
state = State::Finished;
|
||||
continue;
|
||||
}
|
||||
},
|
||||
State::Finished => {
|
||||
let () = futures::future::pending().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use cosmic_notifications_util::Notification;
|
|||
use futures_util::{SinkExt, StreamExt};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
hash::Hash,
|
||||
os::unix::io::{FromRawFd, RawFd},
|
||||
pin::pin,
|
||||
};
|
||||
|
|
@ -37,89 +38,97 @@ pub enum Output {
|
|||
}
|
||||
|
||||
pub fn notifications(proxy: NotificationsAppletProxy<'static>) -> Subscription<Output> {
|
||||
struct SomeWorker;
|
||||
struct Wrapper(NotificationsAppletProxy<'static>);
|
||||
|
||||
Subscription::run_with_id(
|
||||
std::any::TypeId::of::<SomeWorker>(),
|
||||
stream::channel(50, |mut output| async move {
|
||||
let mut state = State::WaitingForNotificationEvent;
|
||||
let (sender, mut receiver) = mpsc::channel(10);
|
||||
_ = output.send(Output::Ready(sender)).await;
|
||||
impl Hash for Wrapper {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
std::any::TypeId::of::<NotificationsAppletProxy<'static>>().hash(state);
|
||||
}
|
||||
}
|
||||
Subscription::run_with(Wrapper(proxy), |Wrapper(proxy)| {
|
||||
let proxy = proxy.clone();
|
||||
stream::channel(
|
||||
50,
|
||||
move |mut output: futures::channel::mpsc::Sender<Output>| async move {
|
||||
let mut state = State::WaitingForNotificationEvent;
|
||||
let (sender, mut receiver) = mpsc::channel(10);
|
||||
_ = output.send(Output::Ready(sender)).await;
|
||||
|
||||
let mut signal;
|
||||
let mut fail_count: u8 = 0;
|
||||
loop {
|
||||
match proxy.receive_notify().await {
|
||||
Ok(s) => {
|
||||
signal = s;
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to get a stream of signals for notifications. {}",
|
||||
err
|
||||
);
|
||||
fail_count = fail_count.saturating_add(1);
|
||||
if fail_count > 5 {
|
||||
error!("Failed to receive notification events");
|
||||
// exit because the applet needs the notifications daemon in order to work properly
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
let mut signal;
|
||||
let mut fail_count: u8 = 0;
|
||||
loop {
|
||||
match proxy.receive_notify().await {
|
||||
Ok(s) => {
|
||||
signal = s;
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
"failed to get a stream of signals for notifications. {}",
|
||||
err
|
||||
);
|
||||
fail_count = fail_count.saturating_add(1);
|
||||
if fail_count > 5 {
|
||||
error!("Failed to receive notification events");
|
||||
// exit because the applet needs the notifications daemon in order to work properly
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
loop {
|
||||
match &mut state {
|
||||
State::WaitingForNotificationEvent => {
|
||||
trace!("Waiting for notification events...");
|
||||
let mut next_signal = signal.next();
|
||||
let mut next_input = pin!(receiver.recv().fuse());
|
||||
cosmic::iced::futures::select! {
|
||||
v = next_signal => {
|
||||
if let Some(msg) = v {
|
||||
let Ok(args) = msg.args() else {
|
||||
break;
|
||||
};
|
||||
let notification = Notification::new(
|
||||
args.app_name,
|
||||
args.id,
|
||||
args.app_icon,
|
||||
args.summary,
|
||||
args.body,
|
||||
args.actions,
|
||||
args.hints,
|
||||
args.expire_timeout,
|
||||
);
|
||||
_ = output.send(Output::Notification(notification)).await;
|
||||
} else {
|
||||
tracing::error!("Signal stream closed, ending notifications subscription");
|
||||
state = State::Finished;
|
||||
}
|
||||
}
|
||||
v = next_input => {
|
||||
if let Some(Input::Activated(id, action)) = v {
|
||||
if proxy.invoke_action(id, action.clone()).await.is_err() {
|
||||
tracing::error!("Failed to invoke action {id} {action}");
|
||||
loop {
|
||||
match &mut state {
|
||||
State::WaitingForNotificationEvent => {
|
||||
trace!("Waiting for notification events...");
|
||||
let mut next_signal = signal.next();
|
||||
let mut next_input = pin!(receiver.recv().fuse());
|
||||
cosmic::iced::futures::select! {
|
||||
v = next_signal => {
|
||||
if let Some(msg) = v {
|
||||
let Ok(args) = msg.args() else {
|
||||
break;
|
||||
};
|
||||
let notification = Notification::new(
|
||||
args.app_name,
|
||||
args.id,
|
||||
args.app_icon,
|
||||
args.summary,
|
||||
args.body,
|
||||
args.actions,
|
||||
args.hints,
|
||||
args.expire_timeout,
|
||||
);
|
||||
_ = output.send(Output::Notification(notification)).await;
|
||||
} else {
|
||||
tracing::error!("Invoked {action} for {id}");
|
||||
tracing::error!("Signal stream closed, ending notifications subscription");
|
||||
state = State::Finished;
|
||||
}
|
||||
}
|
||||
v = next_input => {
|
||||
if let Some(Input::Activated(id, action)) = v {
|
||||
if proxy.invoke_action(id, action.clone()).await.is_err() {
|
||||
tracing::error!("Failed to invoke action {id} {action}");
|
||||
} else {
|
||||
tracing::error!("Invoked {action} for {id}");
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Channel closed, ending notifications subscription");
|
||||
state = State::Finished;
|
||||
}
|
||||
} else {
|
||||
tracing::error!("Channel closed, ending notifications subscription");
|
||||
state = State::Finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
State::Finished => {
|
||||
let () = futures::future::pending().await;
|
||||
State::Finished => {
|
||||
let () = futures::future::pending().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[proxy(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use cosmic::{
|
|||
window,
|
||||
},
|
||||
surface, theme,
|
||||
widget::{Space, button, divider, icon, text},
|
||||
widget::{Space, button, divider, icon, space, text},
|
||||
};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ impl cosmic::Application for Power {
|
|||
row![
|
||||
text_icon("system-lock-screen-symbolic", 24),
|
||||
text::body(fl!("lock-screen")),
|
||||
Space::with_width(Length::Fill),
|
||||
space::horizontal().width(Length::Fill),
|
||||
text::body(fl!("lock-screen-shortcut")),
|
||||
]
|
||||
.align_y(Alignment::Center)
|
||||
|
|
@ -238,7 +238,7 @@ impl cosmic::Application for Power {
|
|||
row![
|
||||
text_icon("system-log-out-symbolic", 24),
|
||||
text::body(fl!("log-out")),
|
||||
Space::with_width(Length::Fill),
|
||||
space::horizontal().width(Length::Fill),
|
||||
text::body(fl!("log-out-shortcut")),
|
||||
]
|
||||
.align_y(Alignment::Center)
|
||||
|
|
@ -285,7 +285,7 @@ impl cosmic::Application for Power {
|
|||
activation_token_subscription(0).map(Message::Token)
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
|
||||
use cosmic::{
|
||||
Element, Task, app,
|
||||
applet::cosmic_panel_config::PanelAnchor,
|
||||
applet::token::subscription::{TokenRequest, TokenUpdate, activation_token_subscription},
|
||||
applet::{
|
||||
cosmic_panel_config::PanelAnchor,
|
||||
token::subscription::{TokenRequest, TokenUpdate, activation_token_subscription},
|
||||
},
|
||||
cctk::sctk::reexports::calloop,
|
||||
iced::{
|
||||
self, Length, Subscription,
|
||||
platform_specific::shell::commands::popup::{destroy_popup, get_popup},
|
||||
theme::Style,
|
||||
window,
|
||||
},
|
||||
surface,
|
||||
|
|
@ -146,7 +149,7 @@ impl cosmic::Application for App {
|
|||
&mut self.core
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ fn layout_view(layout: &Layout, expanded: Option<i32>) -> cosmic::Element<'_, Ms
|
|||
if !i.visible() {
|
||||
None
|
||||
} else if i.type_() == Some("separator") {
|
||||
Some(iced::widget::horizontal_rule(2).into())
|
||||
Some(iced::widget::rule::horizontal(2).into())
|
||||
} else if let Some(label) = i.label() {
|
||||
// Strip _ when not doubled
|
||||
// TODO: interpret as "access key"? And label with underline.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// Copyright 2023 System76 <info@system76.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use cosmic::iced::{self, Subscription};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::path::PathBuf;
|
||||
use zbus::zvariant::{self, OwnedValue};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -76,22 +77,40 @@ impl StatusNotifierItem {
|
|||
let Some(menu_proxy) = self.menu_proxy.clone() else {
|
||||
return Subscription::none();
|
||||
};
|
||||
Subscription::run_with_id(
|
||||
format!("status-notifier-item-layout-{}", &self.name),
|
||||
async move {
|
||||
let initial = futures::stream::once(get_layout(menu_proxy.clone()));
|
||||
|
||||
let layout_updated = menu_proxy.receive_layout_updated().await.unwrap();
|
||||
let props_updated = menu_proxy.receive_items_properties_updated().await.unwrap();
|
||||
|
||||
// Merge both streams - any update triggers a layout refetch
|
||||
let updates =
|
||||
futures::stream_select!(layout_updated.map(|_| ()), props_updated.map(|_| ()))
|
||||
.then(move |()| get_layout(menu_proxy.clone()));
|
||||
|
||||
initial.chain(updates)
|
||||
struct Wrapper {
|
||||
menu_proxy: DBusMenuProxy<'static>,
|
||||
name: String,
|
||||
}
|
||||
impl Hash for Wrapper {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
}
|
||||
.flatten_stream(),
|
||||
}
|
||||
Subscription::run_with(
|
||||
Wrapper {
|
||||
menu_proxy,
|
||||
name: format!("status-notifier-item-layout-{}", &self.name),
|
||||
},
|
||||
|Wrapper { menu_proxy, .. }| {
|
||||
let menu_proxy = menu_proxy.clone();
|
||||
async move {
|
||||
let initial = futures::stream::once(get_layout(menu_proxy.clone()));
|
||||
|
||||
let layout_updated = menu_proxy.receive_layout_updated().await.unwrap();
|
||||
let props_updated =
|
||||
menu_proxy.receive_items_properties_updated().await.unwrap();
|
||||
|
||||
// Merge both streams - any update triggers a layout refetch
|
||||
let updates = futures::stream_select!(
|
||||
layout_updated.map(|_| ()),
|
||||
props_updated.map(|_| ())
|
||||
)
|
||||
.then(move |()| get_layout(menu_proxy.clone()));
|
||||
|
||||
initial.chain(updates)
|
||||
}
|
||||
.flatten_stream()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -108,15 +127,30 @@ impl StatusNotifierItem {
|
|||
}
|
||||
|
||||
let item_proxy = self.item_proxy.clone();
|
||||
Subscription::run_with_id(
|
||||
format!("status-notifier-item-icon-{}", &self.name),
|
||||
async move {
|
||||
let new_icon_stream = item_proxy.receive_new_icon().await.unwrap();
|
||||
futures::stream::once(async {})
|
||||
.chain(new_icon_stream.map(|_| ()))
|
||||
.then(move |()| icon_events(item_proxy.clone()))
|
||||
struct Wrapper {
|
||||
item_proxy: StatusNotifierItemProxy<'static>,
|
||||
name: String,
|
||||
}
|
||||
impl Hash for Wrapper {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.name.hash(state);
|
||||
}
|
||||
.flatten_stream(),
|
||||
}
|
||||
Subscription::run_with(
|
||||
Wrapper {
|
||||
item_proxy,
|
||||
name: format!("status-notifier-item-icon-{}", &self.name),
|
||||
},
|
||||
|Wrapper { item_proxy, .. }| {
|
||||
let item_proxy = item_proxy.clone();
|
||||
async move {
|
||||
let new_icon_stream = item_proxy.receive_new_icon().await.unwrap();
|
||||
futures::stream::once(async {})
|
||||
.chain(new_icon_stream.map(|_| ()))
|
||||
.then(move |()| icon_events(item_proxy.clone()))
|
||||
}
|
||||
.flatten_stream()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
// TODO: Both this and server proxy could emit same events, have way to generate stream from either?
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
use cosmic::iced::{self, Subscription};
|
||||
use futures::{StreamExt, stream};
|
||||
|
||||
|
|
@ -26,8 +28,8 @@ enum State {
|
|||
}
|
||||
|
||||
pub fn subscription() -> iced::Subscription<Event> {
|
||||
Subscription::run_with_id(
|
||||
"status-notifier-watcher",
|
||||
pub struct MyID;
|
||||
Subscription::run_with(TypeId::of::<MyID>(), |_| {
|
||||
stream::unfold(State::NotConnected, |state| async move {
|
||||
match state {
|
||||
State::NotConnected => match connect().await {
|
||||
|
|
@ -42,8 +44,8 @@ pub fn subscription() -> iced::Subscription<Event> {
|
|||
.map(|event| (event, State::Connected(stream))),
|
||||
State::Failed => None,
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async fn connect() -> zbus::Result<(zbus::Connection, client::EventStream)> {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ anyhow.workspace = true
|
|||
cctk.workspace = true
|
||||
cosmic-comp-config = { git = "https://github.com/pop-os/cosmic-comp.git", rev = "5eb5af4" }
|
||||
cosmic-protocols.workspace = true
|
||||
cosmic-time.workspace = true
|
||||
i18n-embed-fl.workspace = true
|
||||
i18n-embed.workspace = true
|
||||
rust-embed.workspace = true
|
||||
|
|
|
|||
|
|
@ -23,16 +23,15 @@ pub enum WorkspacesUpdate {
|
|||
}
|
||||
|
||||
pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> {
|
||||
Subscription::run_with_id(
|
||||
std::any::TypeId::of::<WorkspacesUpdate>(),
|
||||
Subscription::run_with(std::any::TypeId::of::<WorkspacesUpdate>(), |_| {
|
||||
stream::channel(50, move |mut output| async move {
|
||||
let mut state = State::Waiting;
|
||||
|
||||
loop {
|
||||
state = start_listening(state, &mut output).await;
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_listening(
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ use crate::{
|
|||
};
|
||||
use cctk::sctk::reexports::calloop::channel::SyncSender;
|
||||
use cosmic::{
|
||||
Element, Task, app,
|
||||
app::Core,
|
||||
Element, Task,
|
||||
app::{self, Core},
|
||||
applet::{menu_button, padded_control},
|
||||
cosmic_config::{Config, ConfigSet, CosmicConfigEntry},
|
||||
cosmic_theme::Spacing,
|
||||
|
|
@ -21,12 +21,11 @@ use cosmic::{
|
|||
widget::{
|
||||
container, divider,
|
||||
segmented_button::{self, Entity, SingleSelectModel},
|
||||
segmented_control, text,
|
||||
segmented_control, text, toggler,
|
||||
},
|
||||
};
|
||||
use cosmic_comp_config::{CosmicCompConfig, TileBehavior};
|
||||
use cosmic_protocols::workspace::v2::client::zcosmic_workspace_handle_v2::TilingState;
|
||||
use cosmic_time::{Timeline, anim, chain, id};
|
||||
use std::{thread, time::Instant};
|
||||
use tracing::error;
|
||||
|
||||
|
|
@ -37,7 +36,6 @@ const OFF: &str = "com.system76.CosmicAppletTiling.Off";
|
|||
pub struct Window {
|
||||
core: Core,
|
||||
popup: Option<Id>,
|
||||
timeline: Timeline,
|
||||
config: CosmicCompConfig,
|
||||
config_helper: Config,
|
||||
new_workspace_behavior_model: segmented_button::SingleSelectModel,
|
||||
|
|
@ -45,17 +43,14 @@ pub struct Window {
|
|||
/// may not match the config value if behavior is per-workspace
|
||||
autotiled: bool,
|
||||
workspace_tx: Option<SyncSender<AppRequest>>,
|
||||
tile_windows: id::Toggler,
|
||||
active_hint: id::Toggler,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
TogglePopup,
|
||||
PopupClosed(Id),
|
||||
Frame(Instant),
|
||||
ToggleTileWindows(chain::Toggler, bool),
|
||||
ToggleActiveHint(chain::Toggler, bool),
|
||||
ToggleTileWindows(bool),
|
||||
ToggleActiveHint(bool),
|
||||
MyConfigUpdate(Box<CosmicCompConfig>),
|
||||
WorkspaceUpdate(WorkspacesUpdate),
|
||||
NewWorkspace(Entity),
|
||||
|
|
@ -110,15 +105,12 @@ impl cosmic::Application for Window {
|
|||
let window = Self {
|
||||
core,
|
||||
popup: None,
|
||||
timeline: Timeline::default(),
|
||||
autotiled: config.autotile,
|
||||
config,
|
||||
config_helper,
|
||||
new_workspace_behavior_model,
|
||||
new_workspace_entity,
|
||||
workspace_tx: None,
|
||||
tile_windows: id::Toggler::unique(),
|
||||
active_hint: id::Toggler::unique(),
|
||||
};
|
||||
(window, Task::none())
|
||||
}
|
||||
|
|
@ -128,12 +120,7 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Self::Message> {
|
||||
let timeline = self
|
||||
.timeline
|
||||
.as_subscription()
|
||||
.map(|(_, now)| Message::Frame(now));
|
||||
Subscription::batch([
|
||||
timeline,
|
||||
self.core
|
||||
.watch_config::<CosmicCompConfig>("com.system76.CosmicComp")
|
||||
.map(|u| Message::MyConfigUpdate(Box::new(u.config))),
|
||||
|
|
@ -146,15 +133,7 @@ impl cosmic::Application for Window {
|
|||
Message::WorkspaceUpdate(msg) => match msg {
|
||||
WorkspacesUpdate::State(state) => {
|
||||
self.autotiled = matches!(state, TilingState::TilingEnabled);
|
||||
if self.popup.is_some() {
|
||||
self.timeline
|
||||
.set_chain(if self.autotiled {
|
||||
cosmic_time::chain::Toggler::on(self.tile_windows.clone(), 1.0)
|
||||
} else {
|
||||
cosmic_time::chain::Toggler::off(self.tile_windows.clone(), 1.0)
|
||||
})
|
||||
.start();
|
||||
}
|
||||
if self.popup.is_some() {}
|
||||
}
|
||||
WorkspacesUpdate::Started(tx) => {
|
||||
self.workspace_tx = Some(tx);
|
||||
|
|
@ -167,9 +146,6 @@ impl cosmic::Application for Window {
|
|||
return if let Some(p) = self.popup.take() {
|
||||
destroy_popup(p)
|
||||
} else {
|
||||
self.timeline = Timeline::default();
|
||||
self.tile_windows = id::Toggler::unique();
|
||||
self.active_hint = id::Toggler::unique();
|
||||
let new_id = Id::unique();
|
||||
self.popup = Some(new_id);
|
||||
let popup_settings = self.core.applet.get_popup_settings(
|
||||
|
|
@ -188,9 +164,7 @@ impl cosmic::Application for Window {
|
|||
self.popup = None;
|
||||
}
|
||||
}
|
||||
Message::Frame(now) => self.timeline.now(now),
|
||||
Message::ToggleTileWindows(chain, toggled) => {
|
||||
self.timeline.set_chain(chain).start();
|
||||
Message::ToggleTileWindows(toggled) => {
|
||||
self.autotiled = toggled;
|
||||
|
||||
// set via protocol
|
||||
|
|
@ -206,8 +180,7 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
}
|
||||
}
|
||||
Message::ToggleActiveHint(chain, toggled) => {
|
||||
self.timeline.set_chain(chain).start();
|
||||
Message::ToggleActiveHint(toggled) => {
|
||||
self.config.active_hint = toggled;
|
||||
|
||||
let helper = self.config_helper.clone();
|
||||
|
|
@ -223,16 +196,6 @@ impl cosmic::Application for Window {
|
|||
.activate_position(if c.autotile { 0 } else { 1 });
|
||||
}
|
||||
|
||||
if c.active_hint != self.config.active_hint && self.popup.is_some() {
|
||||
self.timeline
|
||||
.set_chain(if c.active_hint {
|
||||
cosmic_time::chain::Toggler::on(self.active_hint.clone(), 1.0)
|
||||
} else {
|
||||
cosmic_time::chain::Toggler::off(self.active_hint.clone(), 1.0)
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
self.config = *c;
|
||||
}
|
||||
Message::NewWorkspace(e) => {
|
||||
|
|
@ -295,17 +258,12 @@ impl cosmic::Application for Window {
|
|||
.on_activate(Message::NewWorkspace);
|
||||
let content_list = column![
|
||||
padded_control(container(
|
||||
anim!(
|
||||
self.tile_windows,
|
||||
&self.timeline,
|
||||
fl!("tile-current"),
|
||||
self.autotiled,
|
||||
|chain, enable| { Message::ToggleTileWindows(chain, enable) },
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
))
|
||||
.width(Length::Fill),
|
||||
toggler(self.autotiled)
|
||||
.on_toggle(Message::ToggleTileWindows)
|
||||
.text_size(14)
|
||||
.width(Length::Fill)
|
||||
.label(fl!("tile-current"))
|
||||
)),
|
||||
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
|
||||
padded_control(
|
||||
column![
|
||||
|
|
@ -334,15 +292,11 @@ impl cosmic::Application for Window {
|
|||
)),
|
||||
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
|
||||
padded_control(
|
||||
anim!(
|
||||
self.active_hint,
|
||||
&self.timeline,
|
||||
fl!("active-hint"),
|
||||
self.config.active_hint,
|
||||
|chain, enable| { Message::ToggleActiveHint(chain, enable) },
|
||||
)
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
toggler(self.config.active_hint)
|
||||
.on_toggle(Message::ToggleActiveHint)
|
||||
.label(fl!("active-hint"))
|
||||
.text_size(14)
|
||||
.width(Length::Fill),
|
||||
),
|
||||
padded_control(divider::horizontal::default()).padding([space_xxs, space_s]),
|
||||
menu_button(text::body(fl!("window-management-settings")))
|
||||
|
|
@ -353,7 +307,7 @@ impl cosmic::Application for Window {
|
|||
self.core.applet.popup_container(content_list).into()
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ use cosmic::{
|
|||
Alignment, Length, Rectangle, Subscription,
|
||||
futures::{SinkExt, StreamExt, channel::mpsc},
|
||||
platform_specific::shell::wayland::commands::popup::{destroy_popup, get_popup},
|
||||
widget::{column, row, vertical_space},
|
||||
widget::{column, row, rule},
|
||||
window,
|
||||
},
|
||||
iced_futures::stream,
|
||||
iced_widget::{Column, horizontal_rule},
|
||||
iced_widget::Column,
|
||||
surface, theme,
|
||||
widget::{
|
||||
Button, Grid, Id, Space, autosize, button, container, divider, grid, horizontal_space,
|
||||
icon, rectangle_tracker::*, text,
|
||||
Button, Grid, Id, autosize, button, container, divider, grid, icon, rectangle_tracker::*,
|
||||
space, text,
|
||||
},
|
||||
};
|
||||
use jiff::{
|
||||
|
|
@ -28,6 +28,7 @@ use jiff::{
|
|||
tz::TimeZone,
|
||||
};
|
||||
use logind_zbus::manager::ManagerProxy;
|
||||
use std::hash::Hash;
|
||||
use std::sync::LazyLock;
|
||||
use timedate_zbus::TimeDateProxy;
|
||||
use tokio::{sync::watch, time};
|
||||
|
|
@ -215,7 +216,7 @@ impl Window {
|
|||
elements.push(self.core.applet.text(p.to_owned()).into());
|
||||
}
|
||||
elements.push(
|
||||
horizontal_rule(2)
|
||||
rule::horizontal(2)
|
||||
.width(self.core.applet.suggested_size(true).0)
|
||||
.into(),
|
||||
);
|
||||
|
|
@ -245,7 +246,7 @@ impl Window {
|
|||
Element::from(
|
||||
column!(
|
||||
date_time_col,
|
||||
horizontal_space().width(Length::Fixed(
|
||||
space::horizontal().width(Length::Fixed(
|
||||
(self.core.applet.suggested_size(true).0
|
||||
+ 2 * self.core.applet.suggested_padding(true).1)
|
||||
as f32
|
||||
|
|
@ -302,7 +303,7 @@ impl Window {
|
|||
Element::from(
|
||||
row!(
|
||||
self.core.applet.text(formatted_date),
|
||||
container(vertical_space().height(Length::Fixed(
|
||||
container(space::vertical().height(Length::Fixed(
|
||||
(self.core.applet.suggested_size(true).1
|
||||
+ 2 * self.core.applet.suggested_padding(true).1)
|
||||
as f32
|
||||
|
|
@ -355,64 +356,79 @@ impl cosmic::Application for Window {
|
|||
&mut self.core
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
fn time_subscription(mut show_seconds: watch::Receiver<bool>) -> Subscription<Message> {
|
||||
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);
|
||||
struct Wrapper {
|
||||
inner: watch::Receiver<bool>,
|
||||
id: &'static str,
|
||||
}
|
||||
impl Hash for Wrapper {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.id.hash(state);
|
||||
}
|
||||
}
|
||||
Subscription::run_with(
|
||||
Wrapper {
|
||||
inner: show_seconds,
|
||||
id: "time-sub",
|
||||
},
|
||||
|Wrapper { inner, id }| {
|
||||
let mut show_seconds = inner.clone();
|
||||
stream::channel(1, move |mut output: mpsc::Sender<Message>| 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 = Timestamp::now().as_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 - Timestamp::now().as_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 = Timestamp::now().as_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 - Timestamp::now().as_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -438,8 +454,7 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
|
||||
fn timezone_subscription() -> Subscription<Message> {
|
||||
Subscription::run_with_id(
|
||||
"timezone-sub",
|
||||
Subscription::run_with("timezone-sub", |_| {
|
||||
stream::channel(1, |mut output| async move {
|
||||
'retry: loop {
|
||||
match timezone_update(&mut output).await {
|
||||
|
|
@ -455,8 +470,8 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
|
||||
std::future::pending().await
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Update the time when waking from sleep
|
||||
|
|
@ -474,14 +489,13 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
|
||||
fn wake_from_sleep_subscription() -> Subscription<Message> {
|
||||
Subscription::run_with_id(
|
||||
"wake-from-suspend-sub",
|
||||
Subscription::run_with("wake-from-suspend-sub", |_| {
|
||||
stream::channel(1, |mut output| async move {
|
||||
if let Err(err) = wake_from_sleep(&mut output).await {
|
||||
tracing::error!(?err, "Failed to subscribe to wake-from-sleep signal");
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
let show_seconds_rx = self.show_seconds_tx.subscribe();
|
||||
|
|
@ -728,7 +742,7 @@ impl cosmic::Application for Window {
|
|||
let content_list = column![
|
||||
row![
|
||||
column![date, day_of_week],
|
||||
Space::with_width(Length::Fill),
|
||||
space::horizontal().width(Length::Fill),
|
||||
month_controls,
|
||||
]
|
||||
.align_y(Alignment::Center)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use cosmic::{
|
|||
iced_core::{Background, Border},
|
||||
scroll::DiscreteScrollState,
|
||||
surface,
|
||||
widget::{Id, autosize, container, horizontal_space, vertical_space},
|
||||
widget::{Id, autosize, container, space},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -196,10 +196,10 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
(suggested_window_size.0.get() as f32, suggested_total as f32)
|
||||
};
|
||||
|
||||
let content = row!(content, vertical_space().height(Length::Fixed(height)))
|
||||
let content = row!(content, space::vertical().height(Length::Fixed(height)))
|
||||
.align_y(Alignment::Center);
|
||||
|
||||
let content = column!(content, horizontal_space().width(Length::Fixed(width)))
|
||||
let content = column!(content, space::horizontal().width(Length::Fixed(width)))
|
||||
.align_x(Alignment::Center);
|
||||
|
||||
let btn = button(
|
||||
|
|
@ -323,7 +323,7 @@ impl cosmic::Application for IcedWorkspacesApplet {
|
|||
])
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,16 +22,15 @@ pub enum WorkspacesUpdate {
|
|||
}
|
||||
|
||||
pub fn workspaces() -> iced::Subscription<WorkspacesUpdate> {
|
||||
Subscription::run_with_id(
|
||||
std::any::TypeId::of::<WorkspacesUpdate>(),
|
||||
Subscription::run_with(std::any::TypeId::of::<WorkspacesUpdate>(), |_| {
|
||||
stream::channel(50, move |mut output| async move {
|
||||
let mut state = State::Waiting;
|
||||
|
||||
loop {
|
||||
state = start_listening(state, &mut output).await;
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_listening(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
use config::{CosmicPanelButtonConfig, IndividualConfig, Override};
|
||||
use cosmic::desktop::fde::{self, DesktopEntry, get_languages_from_env};
|
||||
use cosmic::widget::space;
|
||||
use cosmic::{
|
||||
Task, app,
|
||||
applet::{
|
||||
|
|
@ -12,7 +13,7 @@ use cosmic::{
|
|||
iced::{self, Length},
|
||||
iced_widget::row,
|
||||
surface,
|
||||
widget::{Id, autosize, vertical_space},
|
||||
widget::{Id, autosize},
|
||||
};
|
||||
use cosmic_config::{Config, CosmicConfigEntry};
|
||||
use std::{env, fs, process::Command, sync::LazyLock};
|
||||
|
|
@ -115,7 +116,7 @@ impl cosmic::Application for Button {
|
|||
&mut self.core
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
|
||||
fn style(&self) -> Option<iced::theme::Style> {
|
||||
Some(cosmic::applet::style())
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +180,7 @@ impl cosmic::Application for Button {
|
|||
} else {
|
||||
let content = row!(
|
||||
self.core.applet.text(&self.desktop.name),
|
||||
vertical_space().height(Length::Fixed(
|
||||
space::vertical().height(Length::Fixed(
|
||||
(self.core.applet.suggested_size(true).1
|
||||
+ 2 * self.core.applet.suggested_padding(true).1)
|
||||
as f32
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue