feat(settings): add grouped configuration hub, languages and tray controls

This commit is contained in:
aria committed 2026-10-28 03:37:22 +09:00
1 parent 6f34fd080f
commit cc3b6f903f
90 files changed
+3625 -408

No files matched your search

+111
View File
@@ -0,0 +1,111 @@
//! Read tray applications through the existing watcher without becoming a host.
use super::{Alignment, App, Element, Length, Message, Task, column, row, set};
use ferese_config::locale::text as t;
#[derive(Clone, Debug)]
pub struct Application {
pub id: String,
pub title: String,
}
async fn applications() -> Result<Vec<Application>, String> {
let connection = zbus::Connection::session()
.await
.map_err(|e| e.to_string())?;
let watcher = zbus::Proxy::new(
&connection,
"org.kde.StatusNotifierWatcher",
"/StatusNotifierWatcher",
"org.kde.StatusNotifierWatcher",
)
.await
.map_err(|e| e.to_string())?;
let addresses: Vec<String> = watcher
.get_property("RegisteredStatusNotifierItems")
.await
.map_err(|e| e.to_string())?;
let mut apps = Vec::new();
for address in addresses.into_iter().take(128) {
let (name, path) = address
.find('/')
.map_or((address.as_str(), "/StatusNotifierItem"), |i| {
(&address[..i], &address[i..])
});
let Ok(proxy) =
zbus::Proxy::new(&connection, name, path, "org.kde.StatusNotifierItem").await
else {
continue;
};
let Ok(id) = proxy.get_property::<String>("Id").await else {
continue;
};
let title = proxy
.get_property::<String>("Title")
.await
.unwrap_or_else(|_| id.clone());
if !apps.iter().any(|a: &Application| a.id == id) {
apps.push(Application { id, title });
}
}
apps.sort_by(|a, b| a.title.cmp(&b.title));
Ok(apps)
}
impl App {
pub(super) fn load_tray_apps(&self) -> Task<Message> {
cosmic::task::future(async { Message::TrayApps(applications().await.unwrap_or_default()) })
}
pub(super) fn tray_application_controls(&self) -> Element<'_, Message> {
let hidden = self
.draft
.item("status.tray.hidden_items")
.and_then(serde_json::Value::as_array)
.map(|a| {
a.iter()
.filter_map(|v| v.as_str().map(str::to_owned))
.collect::<Vec<_>>()
})
.unwrap_or_default();
let mut apps = self.tray_apps.clone();
for id in &hidden {
if !apps.iter().any(|a| &a.id == id) {
apps.push(Application {
id: id.clone(),
title: id.clone(),
});
}
}
let mut out = column([])
.spacing(8)
.push(self.panel_heading(t("Application icons"), 15.));
for app in apps {
let visible = !hidden.contains(&app.id);
let mut next = hidden.clone();
if visible {
next.push(app.id);
} else {
next.retain(|id| id != &app.id);
}
out = out.push(
row([])
.spacing(8)
.align_y(Alignment::Center)
.push(self.label(app.title, 13.).width(Length::Fill))
.push(
ferese_theme::controls::switch(
visible,
super::visuals::Palette::from_resolved(&self.resolved.presented),
)
.on_press(Message::Change(set(
"status.tray.hidden_items",
serde_json::json!(next),
))),
),
);
}
out.push(self.settings_button(
t("Refresh applications"),
"M3 10a8 8 0 1 1 1 7 M3 3v7h7",
Some(Message::RefreshTrayApps),
false,
))
.into()
}
}