diff --git a/crates/op-host-web/src/widget_host/overlay_cursor.rs b/crates/op-host-web/src/widget_host/overlay_cursor.rs index 9ffd2ecf5..f39aa96ba 100644 --- a/crates/op-host-web/src/widget_host/overlay_cursor.rs +++ b/crates/op-host-web/src/widget_host/overlay_cursor.rs @@ -130,8 +130,8 @@ impl WidgetHost { let over_dropdown = self.over_dropdown_overlay(x, y, self.last_viewport_w, self.last_viewport_h); let dropdown_changed = self.update_dropdown_hover(x, y); - let layer_cleared = over_dropdown && self.clear_layer_panel_hover(); - if dropdown_changed || layer_cleared || over_dropdown { + let underlay_cleared = over_dropdown && self.clear_hover_under_dropdown_overlay(); + if dropdown_changed || underlay_cleared || over_dropdown { return true; } if self.editor_state.editor_ui.variables_panel_open { @@ -155,6 +155,40 @@ impl WidgetHost { false } + fn clear_hover_under_dropdown_overlay(&mut self) -> bool { + let mut changed = false; + { + let ui = &mut self.editor_state.editor_ui; + changed |= ui.canvas_hover_node.take().is_some(); + changed |= ui.hovered_layer_id.take().is_some(); + changed |= ui.hovered_page_index.take().is_some(); + changed |= ui.fill_type_picker.hover.take().is_some(); + changed |= ui.toolbar_hover.take().is_some(); + changed |= ui.align_toolbar_hover.take().is_some(); + changed |= ui.statusbar_hover.take().is_some(); + changed |= ui.topbar_button_hover.take().is_some(); + changed |= ui.chat_model_picker.hover.take().is_some(); + changed |= ui.chat_design_block_hover.take().is_some(); + changed |= ui.chat_footer_hover.take().is_some(); + changed |= ui.chat_example_hover.take().is_some(); + changed |= ui.export_picker_hover.take().is_some(); + changed |= ui.property_action_hover.take().is_some(); + changed |= ui.property_tab_hover.take().is_some(); + if let Some(menu) = ui.layer_context_menu.as_mut() { + changed |= menu.menu.hover.take().is_some(); + } + } + if let Some(menu) = self.editor_state.ui.path_anchor_menu.as_mut() { + changed |= menu.menu.hover.take().is_some(); + } + changed |= self.editor_state.codegen.framework_hover.take().is_some(); + changed |= self.editor_state.codegen.action_hover.take().is_some(); + if changed { + self.mark_dirty(); + } + changed + } + /// Update the file-menu / locale / shape-picker dropdown hover highlights /// from the cursor. At most one is open at a time; a top-most /// floating panel covering the point suppresses updates. Returns diff --git a/crates/op-host-web/src/widget_host/property_hover_tests.rs b/crates/op-host-web/src/widget_host/property_hover_tests.rs index 4d87cda27..ade15822f 100644 --- a/crates/op-host-web/src/widget_host/property_hover_tests.rs +++ b/crates/op-host-web/src/widget_host/property_hover_tests.rs @@ -80,6 +80,33 @@ fn property_panel_blank_hover_consumes_and_clears_lower_hover() { assert_eq!(host.editor_state.editor_ui.canvas_hover_node, None); } +#[test] +fn locale_dropdown_hover_clears_property_panel_hover_underlay() { + let mut host = WidgetHost::new(); + host.editor_state = EditorState::sample(); + host.mark_dirty(); + host.last_viewport_w = 1200.0; + host.last_viewport_h = 800.0; + host.editor_state.editor_ui.property_action_hover = Some(0); + host.editor_state.editor_ui.locale_picker.open = true; + + let picker = host.locale_picker_rect(host.last_viewport_w); + let point = Point2D::new( + picker.origin.x + picker.size.x / 2.0, + picker.origin.y + op_editor_ui::widgets::LocalePicker::row_height() * 1.5, + ); + + assert!(host.apply_cursor_move(point.x, point.y)); + assert_eq!( + host.editor_state.editor_ui.property_action_hover, None, + "top-bar dropdowns are visually above the inspector and must clear stale inspector hover" + ); + assert!( + host.editor_state.editor_ui.locale_picker.hover.is_some(), + "the dropdown row hover itself should still update" + ); +} + #[test] fn codegen_hover_tracks_idle_generate_button() { let mut host = WidgetHost::new();