From 9ed85e5c2452fbdbbe5e37aaebbd3ed020830e76 Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Thu, 23 Jul 2026 23:13:29 +0800 Subject: [PATCH] fix(renderer): refresh scenes after property edits --- .../src/property_edit_mutators.rs | 30 +++++++++++++++---- .../src/widget_host/input_tests.rs | 7 +++-- .../src/widget_host/preview_frame_tests.rs | 10 +++++-- .../property_panel_interactions_tests.rs | 8 +++++ 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/crates/op-editor-core/src/property_edit_mutators.rs b/crates/op-editor-core/src/property_edit_mutators.rs index 071e5fa10..51e8e26a9 100644 --- a/crates/op-editor-core/src/property_edit_mutators.rs +++ b/crates/op-editor-core/src/property_edit_mutators.rs @@ -641,6 +641,9 @@ impl EditorState { if removed && index == 0 { self.ui.variables.fill_refs.remove(&sel); } + if removed { + self.mark_document_changed(); + } removed } @@ -667,19 +670,28 @@ impl EditorState { let Some(node) = find_node_mut(self.active_children_mut(), &sel) else { return false; }; - if crate::image_node_props::set_image_node_mode(node, mode) { + let changed = if crate::image_node_props::set_image_node_mode(node, mode) { true } else { crate::fills::set_primary_image_fill_mode(node, mode) + }; + if changed { + self.mark_document_changed(); } + changed } /// Set TILE scale on a primary image fill. Unlike mode/adjustments, this /// deliberately does not fall back to a standalone Image node because its /// schema has no tileScale property. pub fn set_selected_image_tile_scale(&mut self, value: f32) -> bool { - self.with_selected_node(|node| crate::fills::set_primary_image_tile_scale(node, value)) - .unwrap_or(false) + let changed = self + .with_selected_node(|node| crate::fills::set_primary_image_tile_scale(node, value)) + .unwrap_or(false); + if changed { + self.mark_document_changed(); + } + changed } pub fn set_selected_image_adjustment( @@ -694,11 +706,15 @@ impl EditorState { let Some(node) = find_node_mut(self.active_children_mut(), &sel) else { return false; }; - if crate::image_node_props::set_image_node_adjustment(node, field, value) { + let changed = if crate::image_node_props::set_image_node_adjustment(node, field, value) { true } else { crate::fills::set_primary_image_adjustment(node, field, value) + }; + if changed { + self.mark_document_changed(); } + changed } pub fn reset_selected_image_adjustments(&mut self) -> bool { @@ -709,11 +725,15 @@ impl EditorState { let Some(node) = find_node_mut(self.active_children_mut(), &sel) else { return false; }; - if crate::image_node_props::reset_image_node_adjustments(node) { + let changed = if crate::image_node_props::reset_image_node_adjustments(node) { true } else { crate::fills::reset_primary_image_adjustments(node) + }; + if changed { + self.mark_document_changed(); } + changed } } diff --git a/crates/op-host-native/src/widget_host/input_tests.rs b/crates/op-host-native/src/widget_host/input_tests.rs index ba25fbfd4..e2b9c9330 100644 --- a/crates/op-host-native/src/widget_host/input_tests.rs +++ b/crates/op-host-native/src/widget_host/input_tests.rs @@ -989,6 +989,7 @@ fn status_bar_search_click_frames_content_in_viewport() { host.editor_state_mut().viewport.zoom = 0.2; let (vw, vh) = (1200.0, 800.0); + let (_, _, canvas_w, canvas_h) = host.canvas_region(vw, vh); let r = host .status_bar_rect(vw, vh) .expect("status bar visible at this size"); @@ -997,9 +998,11 @@ fn status_bar_search_click_frames_content_in_viewport() { assert!(consumed, "search-icon click must be consumed"); let v = host.editor_state().viewport; + let expected_pan_x = canvas_w / 2.0 - 250.0; + let expected_pan_y = canvas_h / 2.0 - 200.0; assert!((v.zoom - 1.0).abs() < 1e-3, "zoom {}", v.zoom); - assert!((v.pan_x - 230.0).abs() < 1e-2, "pan_x {}", v.pan_x); - assert!((v.pan_y - 180.0).abs() < 1e-2, "pan_y {}", v.pan_y); + assert!((v.pan_x - expected_pan_x).abs() < 1e-2, "pan_x {}", v.pan_x); + assert!((v.pan_y - expected_pan_y).abs() < 1e-2, "pan_y {}", v.pan_y); } #[test] diff --git a/crates/op-host-native/src/widget_host/preview_frame_tests.rs b/crates/op-host-native/src/widget_host/preview_frame_tests.rs index 180172f90..5dcba2987 100644 --- a/crates/op-host-native/src/widget_host/preview_frame_tests.rs +++ b/crates/op-host-native/src/widget_host/preview_frame_tests.rs @@ -140,10 +140,14 @@ fn manual_pick_wins_until_exit_then_reinfers() { fn device_scroll_divides_by_fit_and_clamps() { let _guard = test_lock(); let mut host = host_with_doc(phone_doc(2000)); - host.last_viewport_w = 300.0; + host.last_viewport_w = 800.0; host.last_viewport_h = 400.0; - host.enter_preview((300.0, 400.0)); - host.recompute_device_frame(300.0, 400.0); + assert!( + host.enter_preview((800.0, 400.0)), + "preview entry failed: {:?}", + host.editor_state().editor_ui.preview_warnings + ); + host.recompute_device_frame(800.0, 400.0); let fit = host.preview_device_frame.as_ref().unwrap().fit; host.apply_device_scroll(-100.0); assert!((host.preview_scroll_y - 100.0 / fit).abs() < 0.5); diff --git a/crates/op-host-native/src/widget_host/property_panel_interactions_tests.rs b/crates/op-host-native/src/widget_host/property_panel_interactions_tests.rs index dbf98f5f1..0ccc312aa 100644 --- a/crates/op-host-native/src/widget_host/property_panel_interactions_tests.rs +++ b/crates/op-host-native/src/widget_host/property_panel_interactions_tests.rs @@ -223,6 +223,14 @@ fn interaction_menu_hover_tracks_the_row_under_the_cursor() { ); host.editor_state_mut() .set_single_selection(NodeId::new("f1")); + let property_rect = host.property_rect(VIEWPORT_W, VIEWPORT_H); + assert!(host.apply_wheel( + property_rect.origin.x + property_rect.size.x / 2.0, + property_rect.origin.y + property_rect.size.y / 2.0, + -10_000.0, + VIEWPORT_W, + VIEWPORT_H, + )); host.apply_property_action(PropertyPanelAction::ToggleInteractionMenu); assert!(host.editor_state().editor_ui.interaction_menu_open); assert_eq!(host.editor_state().editor_ui.interaction_menu_hover, None);