wip: update libcosmic (#93)
* wip: update libcosmic * fix: damge issue resolved by updating iced * fix: high cpu usage by time applet and app-list * refactor subscriptions to produce fewer events * refactor network applet to use less cpu * fix: text size * refactor: i18n for audio applet * refactor: power applet i18n setup * fix (battery): always send profile update * fix (battery): set toggler width to layout correctly * fix (app-list): backoff for restarts of toplevel subscription * fix (network): alignment * feat: ask for comfirmation before applying power applet actions * wip: integrate cosmic-config * update zbus * feat: update to use latest libcosmic * update iced * udpate deps * update deps * refactor: move applet helpers to this repo, outside of libcosmic. this should help alleviate some dependency hell * chore update deps * update deps * cleanup
This commit is contained in:
parent
8b46cc209f
commit
9ebd9b511a
48 changed files with 2841 additions and 1681 deletions
47
cosmic-applet-power/src/localize.rs
Normal file
47
cosmic-applet-power/src/localize.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// SPDX-License-Identifier: MPL-2.0-only
|
||||
|
||||
use i18n_embed::{
|
||||
fluent::{fluent_language_loader, FluentLanguageLoader},
|
||||
DefaultLocalizer, LanguageLoader, Localizer,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "i18n/"]
|
||||
struct Localizations;
|
||||
|
||||
pub static LANGUAGE_LOADER: Lazy<FluentLanguageLoader> = Lazy::new(|| {
|
||||
let loader: FluentLanguageLoader = fluent_language_loader!();
|
||||
|
||||
loader
|
||||
.load_fallback_language(&Localizations)
|
||||
.expect("Error while loading fallback language");
|
||||
|
||||
loader
|
||||
});
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fl {
|
||||
($message_id:literal) => {{
|
||||
i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id)
|
||||
}};
|
||||
|
||||
($message_id:literal, $($args:expr),*) => {{
|
||||
i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id, $($args), *)
|
||||
}};
|
||||
}
|
||||
|
||||
// Get the `Localizer` to be used for localizing this library.
|
||||
pub fn localizer() -> Box<dyn Localizer> {
|
||||
Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations))
|
||||
}
|
||||
|
||||
pub fn localize() {
|
||||
let localizer = localizer();
|
||||
let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages();
|
||||
|
||||
if let Err(error) = localizer.select(&requested_languages) {
|
||||
eprintln!("Error while loading language for App List {}", error);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,28 @@
|
|||
use std::collections::HashMap;
|
||||
use std::process;
|
||||
use std::time::Duration;
|
||||
|
||||
use cosmic::applet::{CosmicAppletHelper, APPLET_BUTTON_THEME};
|
||||
use cosmic::iced::alignment::{Horizontal, Vertical};
|
||||
use cosmic::iced::event::wayland::{self, LayerEvent};
|
||||
use cosmic::iced::event::PlatformSpecific;
|
||||
use cosmic::iced::subscription::events_with;
|
||||
use cosmic::iced::wayland::actions::layer_surface::SctkLayerSurfaceSettings;
|
||||
use cosmic::iced::wayland::popup::{destroy_popup, get_popup};
|
||||
use cosmic::iced_native::layout::Limits;
|
||||
use cosmic::iced_native::widget::Space;
|
||||
use cosmic::iced_runtime::core::layout::Limits;
|
||||
use cosmic::iced_sctk::commands::layer_surface::{
|
||||
destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity,
|
||||
};
|
||||
use cosmic::iced_widget::mouse_area;
|
||||
use cosmic::widget::{button, divider, icon};
|
||||
use cosmic::Renderer;
|
||||
use cosmic_applet::{applet_button_theme, CosmicAppletHelper};
|
||||
|
||||
use cosmic::iced::Color;
|
||||
use cosmic::iced::{
|
||||
widget::{self, column, container, row, Row},
|
||||
widget::{self, column, container, row, space::Space, text, Row},
|
||||
window, Alignment, Application, Command, Length, Subscription,
|
||||
};
|
||||
use cosmic::iced_style::application::{self, Appearance};
|
||||
use cosmic::iced_style::Color;
|
||||
use cosmic::theme::{self, Svg};
|
||||
use cosmic::{Element, Theme};
|
||||
|
||||
|
|
@ -20,9 +30,11 @@ use logind_zbus::manager::ManagerProxy;
|
|||
use logind_zbus::session::{SessionProxy, SessionType};
|
||||
use logind_zbus::user::UserProxy;
|
||||
use nix::unistd::getuid;
|
||||
use tokio::time::sleep;
|
||||
use zbus::Connection;
|
||||
|
||||
pub mod cosmic_session;
|
||||
mod localize;
|
||||
pub mod session_manager;
|
||||
|
||||
use crate::cosmic_session::CosmicSessionProxy;
|
||||
|
|
@ -39,19 +51,28 @@ struct Power {
|
|||
icon_name: String,
|
||||
theme: Theme,
|
||||
popup: Option<window::Id>,
|
||||
id_ctr: u32,
|
||||
id_ctr: u128,
|
||||
action_to_confirm: Option<(window::Id, PowerAction)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum PowerAction {
|
||||
Lock,
|
||||
LogOut,
|
||||
Suspend,
|
||||
Restart,
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
Timeout(window::Id),
|
||||
Action(PowerAction),
|
||||
TogglePopup,
|
||||
Settings,
|
||||
Ignore,
|
||||
Confirm,
|
||||
Cancel,
|
||||
Closed(window::Id),
|
||||
Zbus(Result<(), zbus::Error>),
|
||||
}
|
||||
|
||||
|
|
@ -72,26 +93,34 @@ impl Application for Power {
|
|||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Power")
|
||||
fl!("power")
|
||||
}
|
||||
|
||||
fn theme(&self) -> Theme {
|
||||
self.theme
|
||||
self.theme.clone()
|
||||
}
|
||||
|
||||
fn close_requested(&self, _id: window::Id) -> Self::Message {
|
||||
Message::Ignore
|
||||
fn close_requested(&self, id: window::Id) -> Self::Message {
|
||||
Message::Closed(id)
|
||||
}
|
||||
|
||||
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
|
||||
<Self::Theme as application::StyleSheet>::Style::Custom(|theme| Appearance {
|
||||
<Self::Theme as application::StyleSheet>::Style::Custom(Box::new(|theme| Appearance {
|
||||
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
|
||||
text_color: theme.cosmic().on_bg_color().into(),
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
Subscription::none()
|
||||
events_with(|e, _status| match e {
|
||||
cosmic::iced::Event::PlatformSpecific(PlatformSpecific::Wayland(
|
||||
wayland::Event::Layer(LayerEvent::Unfocused, ..),
|
||||
)) => Some(Message::Cancel),
|
||||
// cosmic::iced::Event::PlatformSpecific(PlatformSpecific::Wayland(
|
||||
// wayland::Event::Seat(wayland::SeatEvent::Leave, _),
|
||||
// )) => Some(Message::Cancel),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
|
|
@ -101,21 +130,21 @@ impl Application for Power {
|
|||
destroy_popup(p)
|
||||
} else {
|
||||
self.id_ctr += 1;
|
||||
let new_id = window::Id::new(self.id_ctr);
|
||||
let new_id = window::Id(self.id_ctr);
|
||||
self.popup.replace(new_id);
|
||||
|
||||
let mut popup_settings = self.applet_helper.get_popup_settings(
|
||||
window::Id::new(0),
|
||||
window::Id(0),
|
||||
new_id,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
popup_settings.positioner.size_limits = Limits::NONE
|
||||
.min_width(100)
|
||||
.min_height(100)
|
||||
.max_height(400)
|
||||
.max_width(500);
|
||||
.min_width(100.0)
|
||||
.min_height(100.0)
|
||||
.max_height(400.0)
|
||||
.max_width(500.0);
|
||||
get_popup(popup_settings)
|
||||
}
|
||||
}
|
||||
|
|
@ -123,51 +152,117 @@ impl Application for Power {
|
|||
let _ = process::Command::new("cosmic-settings").spawn();
|
||||
Command::none()
|
||||
}
|
||||
Message::Lock => Command::perform(lock(), Message::Zbus),
|
||||
Message::LogOut => Command::perform(log_out(), Message::Zbus),
|
||||
Message::Suspend => Command::perform(suspend(), Message::Zbus),
|
||||
Message::Restart => Command::perform(restart(), Message::Zbus),
|
||||
Message::Shutdown => Command::perform(shutdown(), Message::Zbus),
|
||||
Message::Action(action) => {
|
||||
self.id_ctr += 1;
|
||||
let id = window::Id(self.id_ctr);
|
||||
self.action_to_confirm = Some((id, action));
|
||||
return Command::batch(vec![
|
||||
Command::perform(sleep(Duration::from_secs(60)), move |_| {
|
||||
Message::Timeout(id)
|
||||
}),
|
||||
get_layer_surface(SctkLayerSurfaceSettings {
|
||||
id,
|
||||
keyboard_interactivity: KeyboardInteractivity::None,
|
||||
anchor: Anchor::all(),
|
||||
namespace: "dialog".into(),
|
||||
size: Some((None, None)),
|
||||
size_limits: Limits::NONE.min_width(1.0).min_height(1.0),
|
||||
..Default::default()
|
||||
}),
|
||||
]);
|
||||
}
|
||||
Message::Zbus(result) => {
|
||||
if let Err(e) = result {
|
||||
eprintln!("cosmic-applet-power ERROR: '{}'", e);
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
Message::Ignore => Command::none(),
|
||||
Message::Confirm => {
|
||||
if let Some((id, a)) = self.action_to_confirm.take() {
|
||||
Command::batch(vec![
|
||||
destroy_layer_surface(id),
|
||||
match a {
|
||||
PowerAction::Lock => Command::perform(lock(), Message::Zbus),
|
||||
PowerAction::LogOut => Command::perform(log_out(), Message::Zbus),
|
||||
PowerAction::Suspend => Command::perform(suspend(), Message::Zbus),
|
||||
PowerAction::Restart => Command::perform(restart(), Message::Zbus),
|
||||
PowerAction::Shutdown => Command::perform(shutdown(), Message::Zbus),
|
||||
},
|
||||
])
|
||||
} else {
|
||||
Command::none()
|
||||
}
|
||||
}
|
||||
Message::Cancel => {
|
||||
if let Some((id, _)) = self.action_to_confirm.take() {
|
||||
return destroy_layer_surface(id);
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
Message::Closed(id) => {
|
||||
if let Some((surface_id, _)) = self.action_to_confirm {
|
||||
if id == surface_id {
|
||||
self.action_to_confirm = None;
|
||||
return destroy_layer_surface(id);
|
||||
}
|
||||
}
|
||||
if id == window::Id(0) {
|
||||
process::exit(0);
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
Message::Timeout(id) => {
|
||||
if let Some((surface_id, a)) = self.action_to_confirm {
|
||||
if id == surface_id {
|
||||
self.action_to_confirm = None;
|
||||
return Command::batch(vec![
|
||||
destroy_layer_surface(id),
|
||||
match a {
|
||||
PowerAction::Lock => Command::perform(lock(), Message::Zbus),
|
||||
PowerAction::LogOut => Command::perform(log_out(), Message::Zbus),
|
||||
PowerAction::Suspend => Command::perform(suspend(), Message::Zbus),
|
||||
PowerAction::Restart => Command::perform(restart(), Message::Zbus),
|
||||
PowerAction::Shutdown => {
|
||||
Command::perform(shutdown(), Message::Zbus)
|
||||
}
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
Command::none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, id: window::Id) -> Element<Message> {
|
||||
if id == window::Id::new(0) {
|
||||
self.applet_helper
|
||||
.icon_button(&self.icon_name)
|
||||
.on_press(Message::TogglePopup)
|
||||
.into()
|
||||
} else {
|
||||
let settings = row_button(vec!["Settings...".into()]).on_press(Message::Settings);
|
||||
if matches!(self.popup, Some(p) if p == id) {
|
||||
let settings =
|
||||
row_button(vec![text(fl!("settings")).size(14).into()]).on_press(Message::Settings);
|
||||
|
||||
let session = column![
|
||||
row_button(vec![
|
||||
text_icon("system-lock-screen-symbolic", 24).into(),
|
||||
"Lock Screen".into(),
|
||||
text(fl!("lock-screen")).size(14).into(),
|
||||
Space::with_width(Length::Fill).into(),
|
||||
"Super + Escape".into(),
|
||||
text(fl!("lock-screen-shortcut")).size(14).into(),
|
||||
])
|
||||
.on_press(Message::Lock),
|
||||
.on_press(Message::Action(PowerAction::Lock)),
|
||||
row_button(vec![
|
||||
text_icon("system-log-out-symbolic", 24).into(),
|
||||
"Log Out".into(),
|
||||
text(fl!("log-out")).size(14).into(),
|
||||
Space::with_width(Length::Fill).into(),
|
||||
"Ctrl + Alt + Delete".into(),
|
||||
text(fl!("log-out-shortcut")).size(14).into(),
|
||||
])
|
||||
.on_press(Message::LogOut),
|
||||
.on_press(Message::Action(PowerAction::LogOut)),
|
||||
];
|
||||
|
||||
let power = row![
|
||||
power_buttons("system-lock-screen-symbolic", "Suspend").on_press(Message::Suspend),
|
||||
power_buttons("system-restart-symbolic", "Restart").on_press(Message::Restart),
|
||||
power_buttons("system-shutdown-symbolic", "Shutdown").on_press(Message::Shutdown),
|
||||
power_buttons("system-lock-screen-symbolic", fl!("suspend"))
|
||||
.on_press(Message::Action(PowerAction::Suspend)),
|
||||
power_buttons("system-restart-symbolic", fl!("restart"))
|
||||
.on_press(Message::Action(PowerAction::Restart)),
|
||||
power_buttons("system-shutdown-symbolic", fl!("shutdown"))
|
||||
.on_press(Message::Action(PowerAction::Shutdown)),
|
||||
]
|
||||
.spacing(24)
|
||||
.padding([0, 24]);
|
||||
|
|
@ -188,6 +283,65 @@ impl Application for Power {
|
|||
.padding([8, 0]);
|
||||
|
||||
self.applet_helper.popup_container(content).into()
|
||||
} else if matches!(self.action_to_confirm, Some((c_id, _)) if c_id == id) {
|
||||
let action = match self.action_to_confirm.as_ref().unwrap().1 {
|
||||
PowerAction::Lock => "lock-screen",
|
||||
PowerAction::LogOut => "log-out",
|
||||
PowerAction::Suspend => "suspend",
|
||||
PowerAction::Restart => "restart",
|
||||
PowerAction::Shutdown => "shutdown",
|
||||
};
|
||||
// TODO actual countdown
|
||||
let content = column![
|
||||
text(fl!(
|
||||
"confirm-question",
|
||||
HashMap::from_iter(vec![("action", action), ("countdown", "60")])
|
||||
))
|
||||
.size(16),
|
||||
row![
|
||||
button(theme::Button::Primary)
|
||||
.custom(vec![text(fl!("confirm")).size(14).into()])
|
||||
.on_press(Message::Confirm),
|
||||
button(theme::Button::Primary)
|
||||
.custom(vec![text(fl!("cancel")).size(14).into()])
|
||||
.on_press(Message::Cancel),
|
||||
]
|
||||
.spacing(24)
|
||||
]
|
||||
.align_items(Alignment::Center)
|
||||
.spacing(12)
|
||||
.padding(24);
|
||||
mouse_area(
|
||||
container(
|
||||
container(content)
|
||||
.style(cosmic::theme::Container::custom(|theme| {
|
||||
cosmic::iced_style::container::Appearance {
|
||||
text_color: Some(theme.cosmic().background.on.into()),
|
||||
background: Some(
|
||||
Color::from(theme.cosmic().background.base).into(),
|
||||
),
|
||||
border_radius: 12.0.into(),
|
||||
border_width: 2.0,
|
||||
border_color: theme.cosmic().bg_divider().into(),
|
||||
}
|
||||
}))
|
||||
.width(Length::Shrink)
|
||||
.height(Length::Shrink),
|
||||
)
|
||||
.align_x(Horizontal::Center)
|
||||
.align_y(Vertical::Center)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.on_press(Message::Cancel)
|
||||
.on_right_press(Message::Cancel)
|
||||
.on_middle_press(Message::Cancel)
|
||||
.into()
|
||||
} else {
|
||||
self.applet_helper
|
||||
.icon_button(&self.icon_name)
|
||||
.on_press(Message::TogglePopup)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,7 +349,7 @@ impl Application for Power {
|
|||
// ### UI Helplers
|
||||
|
||||
fn row_button(content: Vec<Element<Message>>) -> widget::Button<Message, Renderer> {
|
||||
button(APPLET_BUTTON_THEME)
|
||||
button(applet_button_theme())
|
||||
.custom(vec![Row::with_children(content)
|
||||
.spacing(4)
|
||||
.align_items(Alignment::Center)
|
||||
|
|
@ -204,14 +358,14 @@ fn row_button(content: Vec<Element<Message>>) -> widget::Button<Message, Rendere
|
|||
.padding([8, 24])
|
||||
}
|
||||
|
||||
fn power_buttons<'a>(name: &'a str, text: &'a str) -> widget::Button<'a, Message, Renderer> {
|
||||
fn power_buttons<'a>(name: &'a str, msg: String) -> widget::Button<'a, Message, Renderer> {
|
||||
widget::button(
|
||||
column![text_icon(name, 40), text]
|
||||
column![text_icon(name, 40), text(msg).size(14)]
|
||||
.spacing(4)
|
||||
.align_items(Alignment::Center),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Units(76))
|
||||
.height(Length::Fixed(76.0))
|
||||
.style(theme::Button::Text)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue