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