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

+81 -3
View File
@@ -1137,7 +1137,7 @@ fn appearance_cards_reflow_without_overlap_and_keep_every_destination() {
.operate(&mut tree, Layout::new(&node), &renderer, &mut cards);
assert_eq!(
cards.bounds.len(),
14,
15,
"{width}: all settings destinations must be reachable"
);
for bounds in cards.bounds.values() {
@@ -1174,6 +1174,7 @@ fn sidebar_rows_center_inside_the_full_button_hit_area() {
struct Centers {
buttons: std::collections::HashMap<usize, Rectangle>,
parts: Vec<(usize, Rectangle)>,
sidebar: Option<Rectangle>,
}
impl Operation for Centers {
fn traverse(&mut self, children: &mut dyn FnMut(&mut dyn Operation)) {
@@ -1197,6 +1198,9 @@ fn sidebar_rows_center_inside_the_full_button_hit_area() {
}
}
fn container(&mut self, id: Option<&widget::Id>, bounds: Rectangle) {
if id == Some(&widget::Id::new("settings-sidebar")) {
self.sidebar = Some(bounds);
}
for page in Page::ALL {
for part in ["content", "icon", "label", "marker"] {
if id
@@ -1243,8 +1247,16 @@ fn sidebar_rows_center_inside_the_full_button_hit_area() {
let mut centers = Centers::default();
view.as_widget_mut()
.operate(&mut tree, Layout::new(&node), &renderer, &mut centers);
assert_eq!(centers.buttons.len(), 15);
assert_eq!(centers.parts.len(), 60);
assert_eq!(centers.buttons.len(), 16);
assert_eq!(centers.parts.len(), 64);
let sidebar = centers.sidebar.unwrap();
assert!(
centers
.buttons
.values()
.all(|button| button.x + button.width <= sidebar.x + sidebar.width - 24. + 0.5),
"navigation hitboxes must leave a separate scrollbar lane"
);
for (page, part) in &centers.parts {
let button = centers.buttons[page];
assert_eq!(button.height, 36.);
@@ -1365,3 +1377,69 @@ fn double_clicking_blank_edges_toggles_maximize_without_changing_settings() {
"field labels must not act as window chrome"
);
}
#[test]
fn holiday_function_editor_keeps_state_when_focus_and_text_rebuild_the_page() {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let renderer = runtime
.block_on(<cosmic::Renderer as Headless>::new(
Font::default(),
Pixels(14.),
Some("tiny-skia"),
))
.unwrap();
let mut app = App::init(
Core::default(),
(
"/unused/calendar-editor.kdl".into(),
Snapshot::parse(String::new()),
Some(InitialPage::Settings(Page::Calendar)),
),
)
.0;
let _ = app.update(Message::CalendarFunction(
calendar_controls::FunctionInput::Toggle,
));
let mut tree = None;
for action in [
cosmic::widget::text_editor::Action::Click(Point::new(20., 20.)),
cosmic::widget::text_editor::Action::SelectAll,
cosmic::widget::text_editor::Action::Edit(cosmic::widget::text_editor::Edit::Paste(
std::sync::Arc::new("fn japanese_holidays(year) { #{} }".into()),
)),
cosmic::widget::text_editor::Action::Move(
cosmic::widget::text_editor::Motion::DocumentStart,
),
] {
let mut view = app.page_view();
let mounted = tree.is_some();
let tree = tree.get_or_insert_with(|| Tree::new(&view));
if mounted {
cosmic::iced::advanced::widget::tree::NAMED
.with(|named| *named.borrow_mut() = tree.take_all_named());
}
tree.diff(view.as_widget_mut());
let node = view.as_widget_mut().layout(
tree,
&renderer,
&layout::Limits::new(Size::ZERO, Size::new(1280., 760.)),
);
cosmic::iced::advanced::widget::tree::NAMED.with(|named| named.borrow_mut().clear());
let mut focus = cosmic::iced::advanced::widget::operation::focusable::focus::<()>(
widget::Id::new("calendar-function-editor"),
);
view.as_widget_mut()
.operate(tree, Layout::new(&node), &renderer, &mut focus);
drop(view);
let _ = app.update(Message::CalendarFunction(
calendar_controls::FunctionInput::Edit(action),
));
}
assert!(
app.pending.is_empty(),
"editor changes require explicit Apply"
);
}