From f88c851b7f4681f0748c7dabf0ee81f1432fb9fc Mon Sep 17 00:00:00 2001 From: Boris Date: Mon, 9 Nov 2026 17:00:55 +0300 Subject: [PATCH] fix(menu): check mark for layer targets of toggle_layer_render The flag lookup matched surface ids only, so a row toggling a layer stayed unchecked even while that layer was drawing. Resolve the target in the surface the menu was opened from, which is where the action itself toggles it, and report that layer live render state. Co-Authored-By: Claude Opus 5 --- src/window/window_context_menu.rs | 101 +++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 17 deletions(-) diff --git a/src/window/window_context_menu.rs b/src/window/window_context_menu.rs index f92f1213..f35a4b44 100644 --- a/src/window/window_context_menu.rs +++ b/src/window/window_context_menu.rs @@ -189,23 +189,14 @@ pub(super) fn context_menu_action_flags( theme, &state.mouse_action_overrides, ); - effective - .surfaces - .iter() - .position(|surface| surface.id.eq_ignore_ascii_case(target)) - .map(|surface_index| { - let runtime = theme_runtime_for_surface( - &effective, - surface_index, - theme_runtime_from_state(state), - ); - theme_engine::surface_should_render( - &effective, - surface_index, - state.data.as_ref(), - runtime, - ) - }) + let (origin_surface, _) = context_menu_action_origin(origin.cloned())?; + toggle_layer_render_state( + &effective, + origin_surface, + target, + state.data.as_ref(), + theme_runtime_from_state(state), + ) }) .unwrap_or(false), _ => false, @@ -227,6 +218,49 @@ pub(super) fn context_menu_action_flags( } } +/// Whether a `toggle_layer_render` target currently draws, so its menu row can +/// carry a check mark. The target is a surface id, or a layer inside the +/// surface the menu was opened from, which is what the action itself toggles. +/// An unknown id has no state to report. +pub(super) fn toggle_layer_render_state( + theme: &ThemeDocument, + origin_surface: usize, + target: &str, + data: Option<&AppUsageData>, + base_runtime: ThemeRuntime, +) -> Option { + if let Some(surface_index) = theme + .surfaces + .iter() + .position(|surface| surface.id.eq_ignore_ascii_case(target)) + { + let runtime = theme_runtime_for_surface(theme, surface_index, base_runtime); + return Some(theme_engine::surface_should_render( + theme, + surface_index, + data, + runtime, + )); + } + let object_index = theme + .surfaces + .get(origin_surface)? + .children + .iter() + .position(|object| object.id.eq_ignore_ascii_case(target))?; + let runtime = theme_runtime_for_surface(theme, origin_surface, base_runtime); + Some( + theme_engine::resolve_object_bounds_with_runtime( + theme, + origin_surface, + object_index, + data, + runtime, + ) + .is_some(), + ) +} + pub(super) fn context_menu_action_origin( origin: Option<(usize, String)>, ) -> Option<(usize, String)> { @@ -407,4 +441,37 @@ mod tests { } } } + + #[test] + fn a_layer_toggle_row_reports_the_layer_state_a_surface_row_already_reported() { + let mut theme = ThemeDocument::starter(); + let runtime = ThemeRuntime::new(true, true, true); + let surface_id = theme.surfaces[0].id.clone(); + let layer_id = theme.surfaces[0].children[0].id.clone(); + theme.surfaces[0].children[0].render = 1.0.into(); + theme.prepare_runtime(); + + // A surface id keeps reporting the surface, as it always did. + assert_eq!( + toggle_layer_render_state(&theme, 0, &surface_id, None, runtime), + Some(true) + ); + // A layer id used to report nothing, leaving the row unchecked even + // while the layer was drawing. + assert_eq!( + toggle_layer_render_state(&theme, 0, &layer_id, None, runtime), + Some(true) + ); + theme.surfaces[0].children[0].render = 0.0.into(); + theme.prepare_runtime(); + assert_eq!( + toggle_layer_render_state(&theme, 0, &layer_id, None, runtime), + Some(false) + ); + // An id that matches nothing has no state to report. + assert_eq!( + toggle_layer_render_state(&theme, 0, "no-such-object", None, runtime), + None + ); + } }