structural updates + power_management

This commit is contained in:
Jiří Maxmilián Stříbrný 2026-03-24 14:50:55 +01:00
parent 22670f6abd
commit c2c4881942
7 changed files with 125 additions and 51 deletions

View file

@ -38,13 +38,12 @@ impl App {
if let Some(elem) = self if let Some(elem) = self
.widgets .widgets
.iter() .iter()
.find(|widget| widget.has_window(id)) .find_map(|widget| widget.render_window(id))
.map(|widget| widget.view(id))
{ {
return elem; 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])) .padding(iced::Padding::from([0, 5]))
.height(iced::Length::Fill) .height(iced::Length::Fill)
.width(iced::Length::Fill) .width(iced::Length::Fill)

View file

@ -9,9 +9,9 @@ use crate::widgets::powerbutton::ShutdownEvents;
pub trait PanelWidget { pub trait PanelWidget {
fn update(&mut self, message: &Message) -> Task<Message>; fn update(&mut self, message: &Message) -> Task<Message>;
fn subscribe(&self) -> Subscription<Message>; fn subscribe(&self) -> Subscription<Message>;
fn view(&self, id: iced::window::Id) -> Element<'_, Message>; fn view(&self) -> Option<Element<'_, Message>>;
fn has_window(&self, _id: iced::window::Id) -> bool { fn render_window(&self, _id: iced::window::Id) -> Option<Element<'_, Message>> {
false None
} }
} }

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 { match self.capacity {
Some(cap) => text!("{} {}", cap, Self::icon(cap)), Some(cap) => Some(text!("{} {}", cap, Self::icon(cap)).into()),
None => text!("󰂃"), None => None,
} }
.into()
} }
} }

View file

@ -28,8 +28,8 @@ impl PanelWidget for ClockWidget {
iced::time::every(Duration::from_secs(1)).map(|_| Message::Time) 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"); 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,12 +1,20 @@
use iced::{Subscription, Task, widget::text}; use iced::{
use ppd::PpdProxyBlocking; Subscription, Task,
widget::{Column, button, text},
};
use iced_layershell::reexport::{Anchor, NewLayerShellSettings};
use ppd::{PpdProxyBlocking, Result};
use zbus::blocking::Connection; use zbus::blocking::Connection;
use crate::widget::{Message, PanelWidget}; use crate::widget::{Message, PanelWidget};
pub struct PowerManagementWidget<'a> { pub struct PowerManagementWidget<'a> {
connection: Option<Connected<'a>>,
}
struct Connected<'a> {
window: Option<iced::window::Id>, window: Option<iced::window::Id>,
current_mode: usize, current_mode: Box<str>,
modes: Box<[Box<str>]>, modes: Box<[Box<str>]>,
proxy: PpdProxyBlocking<'a>, proxy: PpdProxyBlocking<'a>,
} }
@ -19,28 +27,29 @@ pub enum PowerManagement {
impl PowerManagementWidget<'_> { impl PowerManagementWidget<'_> {
pub fn new() -> Self { pub fn new() -> Self {
Self {
connection: Self::establish_connection().ok(),
}
}
fn establish_connection<'a>() -> Result<Connected<'a>> {
let conn = Connection::system().unwrap(); let conn = Connection::system().unwrap();
let proxy = PpdProxyBlocking::new(&conn).unwrap(); let proxy = PpdProxyBlocking::new(&conn)?;
let modes: Box<[Box<str>]> = proxy let modes: Box<[Box<str>]> = proxy
.profiles() .profiles()?
.unwrap()
.iter() .iter()
.map(|f| f.profile.clone().into_boxed_str()) .map(|f| f.profile.clone().into_boxed_str())
.collect(); .collect();
let current_mode_str = proxy.active_profile().unwrap(); let current_mode = proxy.active_profile()?.into_boxed_str();
let current_mode = modes
.iter()
.position(|m| m.as_ref() == current_mode_str.as_str())
.unwrap();
Self { Ok(Connected {
window: None,
modes,
current_mode, current_mode,
modes,
proxy, proxy,
} window: None,
})
} }
} }
@ -50,18 +59,81 @@ impl PanelWidget for PowerManagementWidget<'_> {
return Task::none(); return Task::none();
}; };
let Some(conn) = &mut self.connection 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() Task::none()
} }
}
}
fn subscribe(&self) -> iced::Subscription<crate::widget::Message> { fn subscribe(&self) -> iced::Subscription<crate::widget::Message> {
Subscription::none() Subscription::none()
} }
fn view(&self, _id: iced::window::Id) -> iced::Element<'_, crate::widget::Message> { fn view(&self) -> Option<iced::Element<'_, crate::widget::Message>> {
text!("{}", self.modes[self.current_mode]).into() 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 has_window(&self, id: iced::window::Id) -> bool { fn render_window(
self.window == Some(id) &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 crate::widget::{Message, PanelWidget};
use iced::{ use iced::{
Subscription, Task, Element, Subscription, Task,
widget::{button, row, text}, widget::{button, row, text},
}; };
use iced_layershell::reexport::{Anchor, NewLayerShellSettings}; use iced_layershell::reexport::{Anchor, NewLayerShellSettings};
@ -67,24 +67,28 @@ impl PanelWidget for ShutdownWidget {
Subscription::none() Subscription::none()
} }
fn has_window(&self, id: iced::window::Id) -> bool { fn render_window(&self, id: iced::window::Id) -> Option<Element<'_, Message>> {
self.window == Some(id) if self.window != Some(id) {
return None;
} }
fn view(&self, id: iced::window::Id) -> iced::Element<'_, crate::widget::Message> { let output = row![
match self.window {
Some(child_id) if id == child_id => row![
text("Shut down?"), text("Shut down?"),
button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownConfirmed)), button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownConfirmed)),
button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownCanceled)), button("").on_press(Message::ShutdownEvent(ShutdownEvents::ShutdownCanceled)),
] ]
.spacing(8) .spacing(8)
.align_y(iced::Alignment::Center) .align_y(iced::Alignment::Center)
.into(), .into();
_ => button("") Some(output)
.on_press(Message::ShutdownEvent(ShutdownEvents::PowerButtonPressed))
.into(),
} }
fn view(&self) -> Option<iced::Element<'_, crate::widget::Message>> {
let output = button("")
.on_press(Message::ShutdownEvent(ShutdownEvents::PowerButtonPressed))
.into();
Some(output)
} }
} }

View file

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