Consolidate desktop controls with editable holidays and a running-window tray

This commit is contained in:
aria committed 2026-10-28 06:02:46 +09:00
1 parent 9908dde9f8
commit 326465952a
55 files changed
+4207 -450

No files matched your search

+80 -13
View File
@@ -1,4 +1,5 @@
mod all_options;
mod calendar_controls;
mod connections;
mod form_controls;
mod navigation;
@@ -66,6 +67,7 @@ fn main() -> cosmic::iced::Result {
Some("bar") => Some(InitialPage::Bar),
Some("appearance") => Some(InitialPage::Settings(Page::Appearance)),
Some("general") => Some(InitialPage::Settings(Page::General)),
Some("calendar") => Some(InitialPage::Settings(Page::Calendar)),
Some("theme") => Some(InitialPage::Settings(Page::Theme)),
Some("tray") => Some(InitialPage::Settings(Page::Tray)),
Some("all-options") => Some(InitialPage::Settings(Page::Configuration)),
@@ -134,6 +136,7 @@ enum Message {
RowVisibility(&'static str, usize, bool),
Search(String),
Sidebar(bool),
RevealNavigation,
Change(Edit),
PanelClearSelection,
PanelPage(panel_controls::PanelPage),
@@ -173,6 +176,8 @@ enum Message {
LockPreviewStarted(Result<(), String>),
WallpaperPicked(wallpaper_controls::Target, Result<Option<String>, String>),
NewCommand(String),
AddHoliday,
CalendarFunction(calendar_controls::FunctionInput),
AddCommand,
AddSwipe(&'static str),
Remove(String, usize),
@@ -207,6 +212,7 @@ struct App {
inputs: HashMap<String, String>,
ranges: HashMap<String, f64>,
new_command: String,
calendar_editor: calendar_controls::FunctionEditor,
font: cosmic::font::Font,
native_palette: visuals::Palette,
family_ids: std::sync::Arc<Vec<String>>,
@@ -324,6 +330,7 @@ impl cosmic::Application for App {
inputs: HashMap::new(),
ranges: HashMap::new(),
new_command: String::new(),
calendar_editor: Default::default(),
font,
native_palette,
gallery_focus: None,
@@ -358,7 +365,10 @@ impl cosmic::Application for App {
} else {
Task::none()
};
let task = Task::batch([task, tray_apps]);
let reveal =
cosmic::iced::advanced::widget::operate(navigation::RevealRow::navigation(app.page))
.map(cosmic::Action::App);
let task = Task::batch([task, tray_apps, reveal]);
let connections = if app.page == Page::Connections {
app.refresh_connections()
} else {
@@ -460,6 +470,7 @@ impl cosmic::Application for App {
|| !self.ranges.is_empty()
|| !self.pending.is_empty()
|| self.options.dirty()
|| self.calendar_editor.dirty(&self.draft)
{
self.status =
"Config changed externally · Reload when your edits are finished"
@@ -469,6 +480,7 @@ impl cosmic::Application for App {
&snapshot.doc,
));
self.options.reset(&snapshot);
self.calendar_editor.refresh(&snapshot);
self.current = snapshot.clone();
self.draft = snapshot;
self.family_sections = theme_controls::FamilySections::new(
@@ -517,11 +529,21 @@ impl cosmic::Application for App {
Message::Sidebar(open) => {
self.sidebar_open = open;
if open {
return cosmic::iced::widget::operation::focus(widget::Id::new(
"settings-search",
));
return Task::batch([
cosmic::iced::widget::operation::focus(widget::Id::new("settings-search")),
cosmic::iced::advanced::widget::operate(navigation::RevealRow::navigation(
self.page,
))
.map(cosmic::Action::App),
]);
}
}
Message::RevealNavigation => {
return cosmic::iced::advanced::widget::operate(navigation::RevealRow::navigation(
self.page,
))
.map(cosmic::Action::App);
}
Message::Page(page) => {
self.sidebar_open = false;
if self.page == page && self.search.is_empty() {
@@ -545,6 +567,13 @@ impl cosmic::Application for App {
widget::Id::new("settings-content"),
cosmic::iced::widget::scrollable::RelativeOffset::START.into(),
);
let reset = Task::batch([
reset,
cosmic::iced::advanced::widget::operate(navigation::RevealRow::navigation(
page,
))
.map(cosmic::Action::App),
]);
if leaving {
let cleanup = self.leave_connections();
let load = match page {
@@ -839,6 +868,7 @@ impl cosmic::Application for App {
Ok(snapshot) => {
ferese_config::locale::set(ferese_config::locale::from_document(&snapshot.doc));
self.options.reset(&snapshot);
self.calendar_editor.refresh(&snapshot);
self.current = snapshot.clone();
self.draft = snapshot;
self.family_sections =
@@ -1012,6 +1042,37 @@ impl cosmic::Application for App {
return self.apply_wallpaper_edits(wallpaper_controls::restore_defaults());
}
Message::NewCommand(value) => self.new_command = value,
Message::CalendarFunction(input) => return self.calendar_function_input(input),
Message::AddHoliday if !self.saving => {
let date = self
.inputs
.get("$holiday.date")
.cloned()
.unwrap_or_default()
.trim()
.to_owned();
let name = self
.inputs
.get("$holiday.name")
.cloned()
.unwrap_or_default()
.trim()
.to_owned();
let holiday = ferese_config::calendar::Holiday { date, name };
if let Err(error) = holiday.validate() {
self.error = Some(ferese_config::locale::translate(&error));
} else {
self.inputs.remove("$holiday.date");
self.inputs.remove("$holiday.name");
return self.change(Edit::Add(
"status.calendar.custom_holidays".into(),
vec![
("date".into(), holiday.date.into()),
("name".into(), holiday.name.into()),
],
));
}
}
Message::AddCommand if !self.saving => match shlex::split(&self.new_command)
.filter(|a| !a.is_empty())
{
@@ -1342,7 +1403,7 @@ mod tests {
app.current = initial.clone();
app.draft = initial.clone();
app.saving = true; // An earlier save has not completed yet.
let id = ItemId("network".into());
let id = ItemId("audio".into());
let _ = app.update(Message::PanelSelect(id.clone()));
let _ = app.update(Message::PanelEdit(panel_edit::Action::Move(
id.clone(),
@@ -1366,11 +1427,9 @@ mod tests {
let panels: Vec<ferese_panel::Panel> =
serde_json::from_value(app.draft.item("panels").unwrap().clone()).unwrap();
ferese_panel::validate(&panels).unwrap();
assert_eq!(panels[0].start.groups[1].items[1].id, id);
assert_eq!(
panels[0].start.groups[1].items[1].overflow,
ferese_panel::OverflowPolicy::Always
);
let moved = panels[0].start.groups[1].items.last().unwrap();
assert_eq!(moved.id, id);
assert_eq!(moved.overflow, ferese_panel::OverflowPolicy::Always);
assert!(
!panels[0].end.groups[0]
.items
@@ -1419,7 +1478,15 @@ mod tests {
let _ = app.update(Message::PanelEdit(panel_edit::Action::Remove(id)));
let _ = app.page_view();
assert!(app.error.is_none());
assert_eq!(app.draft.string("panels.0.end.groups.1.items.0.id", ""), "");
assert!(
!app.preview_panel()
.unwrap()
.end
.groups
.iter()
.flat_map(|group| &group.items)
.any(|item| item.kind == ferese_panel::ItemKind::Clock)
);
}
#[test]
@@ -1509,7 +1576,7 @@ mod tests {
panel_edit::Zone::Center,
)));
let _ = app.update(Message::PanelEdit(panel_edit::Action::Place(
ItemId("network".into()),
ItemId("audio".into()),
panel_edit::Destination {
zone: "end",
group: GroupId("time".into()),
@@ -1523,7 +1590,7 @@ mod tests {
assert!(app.saving && app.pending.is_empty());
assert_eq!(
app.draft.string("panels.0.center.groups.1.items.0.id", ""),
"network"
"audio"
);
assert_eq!(
app.draft.string("panels.0.center.groups.1.items.1.id", ""),