Compare commits

...
Sign in to create a new pull request.

6 commits
main ... main

10 changed files with 356 additions and 47 deletions

174
Cargo.lock generated
View file

@ -67,6 +67,56 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
]
[[package]]
name = "anyhow"
version = "1.0.102"
@ -418,6 +468,65 @@ dependencies = [
"windows-link",
]
[[package]]
name = "clap"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_complete"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19c9f1dde76b736e3681f28cec9d5a61299cbaae0fce80a68e43724ad56031eb"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "clap_mangen"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e30ffc187e2e3aeafcd1c6e2aa416e29739454c0ccaa419226d5ecd181f2d78"
dependencies = [
"clap",
"roff",
]
[[package]]
name = "clipboard-win"
version = "5.4.1"
@ -468,6 +577,12 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "combine"
version = "4.6.7"
@ -1256,9 +1371,9 @@ dependencies = [
[[package]]
name = "iced_exdevtools"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd37abad853d711ca1197a01a1fac657dd225826faf68d23595ab0380aba25ed"
checksum = "79aa630d54ce3fd340bd2cf64faee63aa614f5fb19cf8158f98ecb289d5a7608"
dependencies = [
"iced_debug",
"iced_devtools",
@ -1303,9 +1418,9 @@ dependencies = [
[[package]]
name = "iced_layershell"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b93788b3b04289379ab3ada875dd93139d9c871a1fbb99286355d2b517b02c7"
checksum = "22a8e513ce86c82210538861618b3e948bad9b82abc0d7e127f061371c989269"
dependencies = [
"enumflags2",
"futures",
@ -1328,9 +1443,9 @@ dependencies = [
[[package]]
name = "iced_layershell_macros"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de12a0d2d2faabd9d6b49cd1621ae1b6d7e0f1365332c18673d810116e89675e"
checksum = "8625bb576fdb0438e85d8e57ac7229868dde8e410cf32c004fffd588a2777bee"
dependencies = [
"darling",
"manyhow",
@ -1468,6 +1583,12 @@ dependencies = [
"serde_core",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
version = "1.0.17"
@ -1545,9 +1666,9 @@ dependencies = [
[[package]]
name = "layershellev"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a90cbea1375f775abb76c9f2fc7b475dd17d740936dfbeeb0026bd2f09ad5624"
checksum = "66e9699736661513c75de0cbc887aa4656823b8c22c4ac4a955cd2ddea63e1b1"
dependencies = [
"bitflags 2.11.0",
"calloop 0.14.4",
@ -2104,6 +2225,12 @@ version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "orbclient"
version = "0.3.51"
@ -2261,6 +2388,20 @@ dependencies = [
"portable-atomic",
]
[[package]]
name = "ppd"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c9336eeb6b2ea4e61900f44b539ca0d095593a19ba8e831825c30e906aa475c"
dependencies = [
"clap",
"clap_complete",
"clap_mangen",
"serde",
"thiserror 2.0.18",
"zbus",
]
[[package]]
name = "presser"
version = "0.3.1"
@ -2404,6 +2545,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "roff"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf2048e0e979efb2ca7b91c4f1a8d77c91853e9b987c94c555668a8994915ad"
[[package]]
name = "roxmltree"
version = "0.20.0"
@ -3000,6 +3147,12 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.22.0"
@ -3154,9 +3307,9 @@ dependencies = [
[[package]]
name = "waycrate_xkbkeycode"
version = "0.15.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4865c30460c0d213dac0ea8a5eadb0d3b620e6154bd95ca058377e4c1873515"
checksum = "caa5bc93c1dfce7c8d9b4a864028c2d5f64625f249b08900805acdbe8619561d"
dependencies = [
"bitflags 2.11.0",
"calloop 0.14.4",
@ -3310,6 +3463,7 @@ dependencies = [
"chrono",
"iced",
"iced_layershell",
"ppd",
"tokio",
"winit",
"zbus",

View file

@ -6,7 +6,8 @@ edition = "2024"
[dependencies]
chrono = "0.4.44"
iced = { version = "0.14.0", default-features = false, features = ["wgpu", "wayland", "tokio"] }
iced_layershell = { version = "0.15.0", default-features = false }
iced_layershell = { version = "0.16.0", default-features = false }
ppd = "0.1.7"
tokio = { version = "1.50.0", features = ["time"] }
winit = { version = "0.30.12", default-features = false, features = ["wayland"] }
zbus = "5.14.0"

View file

@ -1,13 +1,14 @@
use crate::widget::{Message, PanelWidget};
use crate::widgets::battery::BatteryWidget;
use crate::widgets::clock::ClockWidget;
use crate::widgets::power_management::PowerManagementWidget;
use crate::widgets::powerbutton::ShutdownWidget;
use crate::widgets::spacer::Spacer;
use iced::Color;
use iced::Element;
use iced::Subscription;
use iced::Task;
use iced::{Color, Theme};
use iced::{Element, color};
pub struct App {
widgets: Vec<Box<dyn PanelWidget>>,
@ -19,11 +20,11 @@ impl App {
pub fn new() -> Self {
Self {
widgets: vec![
Box::new(ShutdownWidget::new()),
Box::new(ClockWidget::new()),
Box::new(Spacer::new(iced::Length::Fill)),
Box::new(ShutdownWidget::new()),
Box::new(Spacer::new(iced::Length::Fill)),
Box::new(BatteryWidget::new()),
Box::new(PowerManagementWidget::new()),
],
}
}
@ -36,16 +37,16 @@ impl App {
if let Some(elem) = self
.widgets
.iter()
.find(|widget| widget.has_window(id))
.map(|widget| widget.view(id))
.find_map(|widget| widget.render_window(id))
{
return elem;
}
iced::widget::Row::with_children(self.widgets.iter().map(|widget| widget.view(id)))
iced::widget::Row::with_children(self.widgets.iter().filter_map(|widget| widget.view()))
.padding(iced::Padding::from([0, 5]))
.height(iced::Length::Fill)
.width(iced::Length::Fill)
.spacing(5)
.align_y(iced::Alignment::Center)
.into()
}
@ -67,7 +68,16 @@ impl App {
}
pub fn theme(&self, _id: iced::window::Id) -> iced::Theme {
iced::Theme::GruvboxDark
let palette = iced::theme::Palette {
background: color!(0x282828), // dark BG_0
text: color!(0xfbf1c7), // dark FG0_29
primary: color!(0x8A493B), // dark BLUE_4
success: color!(0x98971a), // dark GREEN_2
warning: color!(0xd79921), // dark YELLOW_3
danger: color!(0xcc241d), // dark RED_1
};
Theme::custom("Better gruvbox", palette)
}
pub fn subscription(&self) -> iced::Subscription<Message> {

View file

@ -3,14 +3,15 @@ use iced::Subscription;
use iced::Task;
use iced_layershell::to_layer_message;
use crate::widgets::power_management::PowerManagement;
use crate::widgets::powerbutton::ShutdownEvents;
pub trait PanelWidget {
fn update(&mut self, message: &Message) -> Task<Message>;
fn subscribe(&self) -> Subscription<Message>;
fn view(&self, id: iced::window::Id) -> Element<'_, Message>;
fn has_window(&self, _id: iced::window::Id) -> bool {
false
fn view(&self) -> Option<Element<'_, Message>>;
fn render_window(&self, _id: iced::window::Id) -> Option<Element<'_, Message>> {
None
}
}
@ -20,4 +21,5 @@ pub enum Message {
Battery(Option<f64>),
Time,
ShutdownEvent(ShutdownEvents),
PowerManagement(PowerManagement),
}

View file

@ -65,12 +65,11 @@ impl PanelWidget for BatteryWidget {
})
}
fn view(&self, _id: iced::window::Id) -> Element<'_, Message> {
fn view(&self) -> Option<Element<'_, Message>> {
match self.capacity {
Some(cap) => text!("{} {}", cap, Self::icon(cap)),
None => text!("󰂃"),
Some(cap) => Some(text!("{} {}", cap, Self::icon(cap)).into()),
None => None,
}
.into()
}
}

View file

@ -28,8 +28,8 @@ impl PanelWidget for ClockWidget {
iced::time::every(Duration::from_secs(1)).map(|_| Message::Time)
}
fn view(&self, _id: iced::window::Id) -> iced::Element<'_, Message> {
fn view(&self) -> Option<iced::Element<'_, Message>> {
let formatted_time = self.current_time.format("%H:%M");
iced::widget::text!("{}", formatted_time).into()
Some(iced::widget::text!("{}", formatted_time).into())
}
}

View file

@ -1,4 +1,5 @@
pub mod battery;
pub mod clock;
pub mod power_management;
pub mod powerbutton;
pub mod spacer;

View file

@ -0,0 +1,138 @@
use iced::{
Subscription, Task,
widget::{Column, button, text},
};
use iced_layershell::reexport::{Anchor, NewLayerShellSettings};
use ppd::{PpdProxyBlocking, Result};
use zbus::blocking::Connection;
use crate::widget::{Message, PanelWidget};
pub struct PowerManagementWidget<'a> {
connection: Option<Connected<'a>>,
}
struct Connected<'a> {
window: Option<iced::window::Id>,
current_mode: Box<str>,
modes: Box<[Box<str>]>,
proxy: PpdProxyBlocking<'a>,
}
#[derive(Debug, Clone)]
pub enum PowerManagement {
ToggleWindow,
SetMode(String),
}
impl PowerManagementWidget<'_> {
pub fn new() -> Self {
Self {
connection: Self::establish_connection().ok(),
}
}
fn establish_connection<'a>() -> Result<Connected<'a>> {
let conn = Connection::system().unwrap();
let proxy = PpdProxyBlocking::new(&conn)?;
let modes: Box<[Box<str>]> = proxy
.profiles()?
.iter()
.map(|f| f.profile.clone().into_boxed_str())
.collect();
let current_mode = proxy.active_profile()?.into_boxed_str();
Ok(Connected {
current_mode,
modes,
proxy,
window: None,
})
}
}
impl PanelWidget for PowerManagementWidget<'_> {
fn update(&mut self, message: &crate::widget::Message) -> iced::Task<crate::widget::Message> {
let Some(conn) = &mut self.connection else {
return Task::none();
};
let Message::PowerManagement(msg) = message else {
return Task::none();
};
match msg {
PowerManagement::ToggleWindow => match conn.window {
Some(child) => {
conn.window = None;
Task::done(Message::RemoveWindow(child))
}
None => {
let id = iced::window::Id::unique();
conn.window = Some(id);
Task::done(Message::NewLayerShell {
settings: NewLayerShellSettings {
size: Some((220, 400)),
anchor: Anchor::Top,
layer: iced_layershell::reexport::Layer::Overlay,
keyboard_interactivity:
iced_layershell::reexport::KeyboardInteractivity::OnDemand,
exclusive_zone: None,
..Default::default()
},
id,
})
}
},
PowerManagement::SetMode(mode) => {
let _ = conn.proxy.set_active_profile(mode.into());
conn.current_mode = conn.proxy.active_profile().unwrap().into_boxed_str();
Task::none()
}
}
}
fn subscribe(&self) -> iced::Subscription<crate::widget::Message> {
Subscription::none()
}
fn view(&self) -> Option<iced::Element<'_, crate::widget::Message>> {
let Some(conn) = &self.connection else {
return None;
};
let output = button(text!("{}", conn.current_mode))
.on_press(Message::PowerManagement(PowerManagement::ToggleWindow))
.into();
Some(output)
}
fn render_window(
&self,
id: iced::window::Id,
) -> Option<iced::Element<'_, crate::widget::Message>> {
let Some(conn) = &self.connection else {
return None;
};
if conn.window != Some(id) {
return None;
}
let output = Column::with_children(conn.modes.iter().map(|f| {
button(text!("{}", f))
.on_press(Message::PowerManagement(PowerManagement::SetMode(
f.clone().into_string(),
)))
.into()
}))
.into();
Some(output)
}
}

View file

@ -1,6 +1,6 @@
use crate::widget::{Message, PanelWidget};
use iced::{
Subscription, Task,
Element, Subscription, Task,
widget::{button, row, text},
};
use iced_layershell::reexport::{Anchor, NewLayerShellSettings};
@ -67,24 +67,28 @@ impl PanelWidget for ShutdownWidget {
Subscription::none()
}
fn has_window(&self, id: iced::window::Id) -> bool {
self.window == Some(id)
fn render_window(&self, id: iced::window::Id) -> Option<Element<'_, Message>> {
if self.window != Some(id) {
return None;
}
let output = row![
text("Shut down?"),
button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownConfirmed)),
button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownCanceled)),
]
.spacing(8)
.align_y(iced::Alignment::Center)
.into();
Some(output)
}
fn view(&self, id: iced::window::Id) -> iced::Element<'_, crate::widget::Message> {
match self.window {
Some(child_id) if id == child_id => row![
text("Shut down?"),
button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownConfirmed)),
button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownCanceled)),
]
.spacing(8)
.align_y(iced::Alignment::Center)
.into(),
fn view(&self) -> Option<iced::Element<'_, crate::widget::Message>> {
let output = button("")
.on_press(Message::ShutdownEvent(ShutdownEvents::PowerButtonPressed))
.into();
_ => button("")
.on_press(Message::ShutdownEvent(ShutdownEvents::PowerButtonPressed))
.into(),
}
Some(output)
}
}

View file

@ -19,7 +19,7 @@ impl PanelWidget for Spacer {
iced::Subscription::none()
}
fn view(&self, _id: iced::window::Id) -> iced::Element<'_, crate::widget::Message> {
iced::widget::space().width(self.space).into()
fn view(&self) -> Option<iced::Element<'_, crate::widget::Message>> {
Some(iced::widget::space().width(self.space).into())
}
}