Panel applets jammy (#41)
This commit is contained in:
parent
162ff02b12
commit
82ac6aac1c
11 changed files with 1845 additions and 62 deletions
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
use apply::Apply;
|
||||
|
||||
use cosmic_panel_config::CosmicPanelConfig;
|
||||
use cosmic_settings_page::{self as page, section};
|
||||
|
||||
use cosmic::{
|
||||
cosmic_config::config_subscription,
|
||||
iced::{
|
||||
self,
|
||||
event::wayland::{self, WindowEvent, WindowState},
|
||||
|
|
@ -30,13 +32,20 @@ use cosmic::{
|
|||
use crate::{
|
||||
config::Config,
|
||||
pages::{
|
||||
desktop::{self, panel},
|
||||
desktop::{
|
||||
self,
|
||||
panel::{
|
||||
self,
|
||||
applets::{self, APPLET_DND_ICON_ID},
|
||||
},
|
||||
},
|
||||
sound, system, time,
|
||||
},
|
||||
subscription::desktop_files,
|
||||
widget::{page_title, parent_page_button, search_header, sub_page_button},
|
||||
};
|
||||
|
||||
use std::process;
|
||||
use std::{borrow::Cow, process};
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
|
|
@ -77,6 +86,8 @@ pub enum Message {
|
|||
ToggleNavBarCondensed,
|
||||
WindowResize(u32, u32),
|
||||
WindowState(WindowState),
|
||||
PanelConfig(CosmicPanelConfig),
|
||||
DesktopInfo,
|
||||
}
|
||||
|
||||
impl Application for SettingsApp {
|
||||
|
|
@ -114,6 +125,8 @@ impl Application for SettingsApp {
|
|||
// app.insert_page::<bluetooth::Page>();
|
||||
|
||||
let desktop_id = app.insert_page::<desktop::Page>().id();
|
||||
// app.insert_page::<panel::Page>();
|
||||
// app.insert_page::<applets::Page>();
|
||||
|
||||
// app.insert_page::<input::Page>();
|
||||
|
||||
|
|
@ -174,9 +187,22 @@ impl Application for SettingsApp {
|
|||
Subscription::batch(vec![
|
||||
window_break,
|
||||
keyboard_nav::subscription().map(Message::KeyboardNav),
|
||||
desktop_files(0).map(|_| Message::DesktopInfo),
|
||||
config_subscription(0, "com.system76.CosmicPanel.panel".into(), 1).map(
|
||||
|(_, e)| match e {
|
||||
Ok(config) => Message::PanelConfig(config),
|
||||
Err((errors, config)) => {
|
||||
for error in errors {
|
||||
log::error!("Error loading panel config: {:?}", error);
|
||||
}
|
||||
Message::PanelConfig(config)
|
||||
}
|
||||
},
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn update(&mut self, message: Message) -> iced::Command<Self::Message> {
|
||||
let mut ret = Command::none();
|
||||
match message {
|
||||
|
|
@ -257,17 +283,53 @@ impl Application for SettingsApp {
|
|||
page.update(message);
|
||||
}
|
||||
}
|
||||
crate::pages::Message::Applet(message) => {
|
||||
if let Some(page) = self.pages.page_mut::<applets::Page>() {
|
||||
ret = page.update(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
Message::WindowState(state) => {
|
||||
dbg!(&state);
|
||||
self.sharp_corners = matches!(state, WindowState::Activated);
|
||||
}
|
||||
Message::PanelConfig(config) if config.name.to_lowercase().contains("panel") => {
|
||||
if let Some(page) = self.pages.page_mut::<panel::Page>() {
|
||||
page.update(panel::Message::PanelConfig(config.clone()));
|
||||
}
|
||||
if let Some(page) = self.pages.page_mut::<applets::Page>() {
|
||||
_ = page.update(applets::Message::PanelConfig(config));
|
||||
}
|
||||
}
|
||||
Message::PanelConfig(_) => {} // ignore other config changes for now,
|
||||
Message::DesktopInfo => {
|
||||
if let Some(page) = self.pages.page_mut::<applets::Page>() {
|
||||
// collect the potential applets
|
||||
ret = page.update(applets::Message::Applets(
|
||||
freedesktop_desktop_entry::Iter::new(
|
||||
freedesktop_desktop_entry::default_paths(),
|
||||
)
|
||||
.filter_map(|p| applets::Applet::try_from(Cow::from(p)).ok())
|
||||
.collect(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn view(&self, _id: window::Id) -> Element<Message> {
|
||||
fn view(&self, id: window::Id) -> Element<Message> {
|
||||
if let Some(Some(page)) =
|
||||
(id == APPLET_DND_ICON_ID).then(|| self.pages.page::<applets::Page>())
|
||||
{
|
||||
return page.dnd_icon();
|
||||
}
|
||||
if let Some(Some(page)) =
|
||||
(id == applets::ADD_APPLET_DIALOGUE_ID).then(|| self.pages.page::<applets::Page>())
|
||||
{
|
||||
return page.add_applet_view();
|
||||
}
|
||||
|
||||
let (nav_bar_message, nav_bar_toggled) = if self.is_condensed {
|
||||
(
|
||||
Message::ToggleNavBarCondensed,
|
||||
|
|
@ -335,7 +397,7 @@ impl Application for SettingsApp {
|
|||
);
|
||||
}
|
||||
|
||||
let content = container(row(widgets))
|
||||
let content = container(row(widgets).spacing(8))
|
||||
.padding([0, 8, 8, 8])
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ pub mod pages;
|
|||
pub mod theme;
|
||||
pub mod widget;
|
||||
|
||||
pub mod subscription;
|
||||
|
||||
use cosmic::{
|
||||
iced::{wayland::actions::window::SctkWindowSettings, Application, Limits},
|
||||
iced_sctk::settings::InitialSurface,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -17,7 +17,7 @@ use cosmic_settings_page::{self as page, section, Section};
|
|||
use slotmap::SlotMap;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
mod applets;
|
||||
pub mod applets;
|
||||
|
||||
pub struct Page {
|
||||
config_helper: Option<cosmic_config::Config>,
|
||||
|
|
@ -383,6 +383,7 @@ pub enum Message {
|
|||
Applets,
|
||||
OutputAdded(String, WlOutput),
|
||||
OutputRemoved(WlOutput),
|
||||
PanelConfig(CosmicPanelConfig),
|
||||
}
|
||||
|
||||
impl Page {
|
||||
|
|
@ -390,7 +391,7 @@ impl Page {
|
|||
match message {
|
||||
Message::AutoHidePanel(enabled) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.autohide = enabled.then_some(AutoHide {
|
||||
wait_time: 1000,
|
||||
|
|
@ -402,7 +403,7 @@ impl Page {
|
|||
}
|
||||
Message::PanelAnchor(anchor) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.anchor = anchor;
|
||||
|
||||
|
|
@ -410,7 +411,7 @@ impl Page {
|
|||
}
|
||||
Message::Output(name) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.output = match name {
|
||||
s if s == fl!("all") => CosmicPanelOuput::All,
|
||||
|
|
@ -421,7 +422,7 @@ impl Page {
|
|||
}
|
||||
Message::AnchorGap(enabled) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.anchor_gap = enabled;
|
||||
|
||||
|
|
@ -429,7 +430,7 @@ impl Page {
|
|||
}
|
||||
Message::PanelSize(size) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.size = size;
|
||||
|
||||
|
|
@ -437,7 +438,7 @@ impl Page {
|
|||
}
|
||||
Message::Appearance(a) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.background = a.into();
|
||||
|
||||
|
|
@ -445,7 +446,7 @@ impl Page {
|
|||
}
|
||||
Message::ExtendToEdge(enabled) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.expand_to_edges = enabled;
|
||||
|
||||
|
|
@ -453,7 +454,7 @@ impl Page {
|
|||
}
|
||||
Message::Opacity(opacity) => {
|
||||
let helper = self.config_helper.as_ref().unwrap();
|
||||
let panel_config = self.panel_config.as_mut().unwrap();
|
||||
let mut panel_config = self.panel_config.as_mut().unwrap();
|
||||
|
||||
panel_config.opacity = opacity;
|
||||
|
||||
|
|
@ -467,6 +468,9 @@ impl Page {
|
|||
Message::OutputRemoved(output) => {
|
||||
self.outputs.remove(&output.id());
|
||||
}
|
||||
Message::PanelConfig(c) => {
|
||||
self.panel_config = Some(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ pub enum Message {
|
|||
Desktop(desktop::Message),
|
||||
Panel(desktop::panel::Message),
|
||||
DesktopWallpaper(desktop::wallpaper::Message),
|
||||
Applet(desktop::panel::applets::Message),
|
||||
External { id: String, message: Vec<u8> },
|
||||
Page(Entity),
|
||||
}
|
||||
|
|
|
|||
95
app/src/subscription/desktop_files.rs
Normal file
95
app/src/subscription/desktop_files.rs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
use cosmic::iced::subscription;
|
||||
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum State {
|
||||
Ready,
|
||||
Waiting {
|
||||
watcher: RecommendedWatcher,
|
||||
rx: UnboundedReceiver<notify::Result<Event>>,
|
||||
},
|
||||
Finished,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum DesktopFileEvent {
|
||||
Changed,
|
||||
}
|
||||
|
||||
pub fn desktop_files<I: 'static + Hash + Copy + Send + Sync + Debug>(
|
||||
id: I,
|
||||
) -> cosmic::iced::Subscription<(I, DesktopFileEvent)> {
|
||||
subscription::unfold(id, State::Ready, move |mut state| async move {
|
||||
loop {
|
||||
let (event, new_state) = start_watching(id, state).await;
|
||||
state = new_state;
|
||||
if let Some(event) = event {
|
||||
return (event, state);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async fn start_watching<I: Copy>(id: I, state: State) -> (Option<(I, DesktopFileEvent)>, State) {
|
||||
match state {
|
||||
State::Ready => {
|
||||
let paths = freedesktop_desktop_entry::default_paths();
|
||||
// TODO log errors
|
||||
if let Ok((mut watcher, rx)) = async_watcher() {
|
||||
for path in paths {
|
||||
let _ = watcher.watch(path.as_ref(), RecursiveMode::Recursive);
|
||||
}
|
||||
(
|
||||
Some((id, DesktopFileEvent::Changed)),
|
||||
State::Waiting { watcher, rx },
|
||||
)
|
||||
} else {
|
||||
(None, State::Finished)
|
||||
}
|
||||
}
|
||||
State::Waiting { watcher, rx } => {
|
||||
if let Some(rx) = async_watch(rx).await {
|
||||
(
|
||||
Some((id, DesktopFileEvent::Changed)),
|
||||
State::Waiting { watcher, rx },
|
||||
)
|
||||
} else {
|
||||
(None, State::Finished)
|
||||
}
|
||||
}
|
||||
State::Finished => cosmic::iced::futures::future::pending().await,
|
||||
}
|
||||
}
|
||||
|
||||
fn async_watcher() -> notify::Result<(RecommendedWatcher, UnboundedReceiver<notify::Result<Event>>)>
|
||||
{
|
||||
let (tx, rx) = unbounded_channel();
|
||||
|
||||
// Automatically select the best implementation for your platform.
|
||||
// You can also access each implementation directly e.g. INotifyWatcher.
|
||||
let watcher = RecommendedWatcher::new(
|
||||
move |res| {
|
||||
_ = tx.send(res);
|
||||
},
|
||||
Config::default(),
|
||||
)?;
|
||||
|
||||
Ok((watcher, rx))
|
||||
}
|
||||
|
||||
async fn async_watch(
|
||||
mut rx: UnboundedReceiver<notify::Result<Event>>,
|
||||
) -> Option<UnboundedReceiver<notify::Result<Event>>> {
|
||||
// TODO log errors
|
||||
if let Some(res) = rx.recv().await {
|
||||
match res {
|
||||
Ok(_) => return Some(rx),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
2
app/src/subscription/mod.rs
Normal file
2
app/src/subscription/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
mod desktop_files;
|
||||
pub use desktop_files::*;
|
||||
Loading…
Add table
Add a link
Reference in a new issue