Fix live operator UI regressions
Some checks are pending
CI / Format, lint, test, build (push) Waiting to run
Some checks are pending
CI / Format, lint, test, build (push) Waiting to run
This commit is contained in:
parent
1ab3886028
commit
c55f34780f
@ -563,6 +563,7 @@ fn command_palette_ui(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut open = palette.open;
|
let mut open = palette.open;
|
||||||
|
let mut dismiss_requested = false;
|
||||||
let palette_width = (ctx.content_rect().width() - 32.0).clamp(320.0, 520.0);
|
let palette_width = (ctx.content_rect().width() - 32.0).clamp(320.0, 520.0);
|
||||||
egui::Window::new("Command Palette")
|
egui::Window::new("Command Palette")
|
||||||
.open(&mut open)
|
.open(&mut open)
|
||||||
@ -611,13 +612,11 @@ fn command_palette_ui(
|
|||||||
if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::Enter)) {
|
if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::Enter)) {
|
||||||
if let Some(entry) = filtered_entries.get(palette.selected_index) {
|
if let Some(entry) = filtered_entries.get(palette.selected_index) {
|
||||||
palette.pending_run = Some(entry.name.clone());
|
palette.pending_run = Some(entry.name.clone());
|
||||||
palette.open = false;
|
dismiss_requested = true;
|
||||||
ui.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::Escape)) {
|
if ui.input_mut(|input| input.consume_key(egui::Modifiers::NONE, egui::Key::Escape)) {
|
||||||
palette.open = false;
|
dismiss_requested = true;
|
||||||
ui.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
@ -639,17 +638,20 @@ fn command_palette_ui(
|
|||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
palette.selected_index = index;
|
palette.selected_index = index;
|
||||||
palette.pending_run = Some(entry.name.clone());
|
palette.pending_run = Some(entry.name.clone());
|
||||||
palette.open = false;
|
dismiss_requested = true;
|
||||||
ui.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
palette.open = open;
|
palette.open = resolve_palette_open(open, dismiss_requested);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_palette_open(window_open: bool, dismiss_requested: bool) -> bool {
|
||||||
|
window_open && !dismiss_requested
|
||||||
|
}
|
||||||
|
|
||||||
fn filtered_command_entries(
|
fn filtered_command_entries(
|
||||||
registry: &EditorCommandRegistry,
|
registry: &EditorCommandRegistry,
|
||||||
filter: &str,
|
filter: &str,
|
||||||
@ -766,6 +768,13 @@ mod tests {
|
|||||||
assert_eq!(entries[1].name, "scene.reset_lighting");
|
assert_eq!(entries[1].name, "scene.reset_lighting");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_palette_dismissal_wins_over_stale_window_open_state() {
|
||||||
|
assert!(!resolve_palette_open(true, true));
|
||||||
|
assert!(!resolve_palette_open(false, false));
|
||||||
|
assert!(resolve_palette_open(true, false));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn grouping_dispatch_is_one_history_transaction_and_round_trips() {
|
fn grouping_dispatch_is_one_history_transaction_and_round_trips() {
|
||||||
let mut world = command_world();
|
let mut world = command_world();
|
||||||
@ -824,10 +833,12 @@ mod tests {
|
|||||||
assert!(committed
|
assert!(committed
|
||||||
.iter()
|
.iter()
|
||||||
.any(|(name, parent, _)| name == "Second" && parent.as_deref() == Some("Group")));
|
.any(|(name, parent, _)| name == "Second" && parent.as_deref() == Some("Group")));
|
||||||
|
assert_group_has_visibility_hierarchy(&mut world);
|
||||||
apply_command_undo(&mut world);
|
apply_command_undo(&mut world);
|
||||||
assert_eq!(hierarchy_projection(&mut world), initial);
|
assert_eq!(hierarchy_projection(&mut world), initial);
|
||||||
apply_command_redo(&mut world);
|
apply_command_redo(&mut world);
|
||||||
assert_eq!(hierarchy_projection(&mut world), committed);
|
assert_eq!(hierarchy_projection(&mut world), committed);
|
||||||
|
assert_group_has_visibility_hierarchy(&mut world);
|
||||||
assert!(world.get::<ChildOf>(unselected).is_none());
|
assert!(world.get::<ChildOf>(unselected).is_none());
|
||||||
apply_command_undo(&mut world);
|
apply_command_undo(&mut world);
|
||||||
assert_eq!(hierarchy_projection(&mut world), initial);
|
assert_eq!(hierarchy_projection(&mut world), initial);
|
||||||
@ -836,6 +847,17 @@ mod tests {
|
|||||||
assert!(world.get::<ChildOf>(unselected).is_none());
|
assert!(world.get::<ChildOf>(unselected).is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assert_group_has_visibility_hierarchy(world: &mut World) {
|
||||||
|
let group = world
|
||||||
|
.query::<(Entity, &Name)>()
|
||||||
|
.iter(world)
|
||||||
|
.find_map(|(entity, name)| (name.as_str() == "Group").then_some(entity))
|
||||||
|
.expect("group command should spawn the group actor");
|
||||||
|
assert!(world.get::<Visibility>(group).is_some());
|
||||||
|
assert!(world.get::<InheritedVisibility>(group).is_some());
|
||||||
|
assert!(world.get::<ViewVisibility>(group).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reset_lighting_dispatch_is_one_undoable_group() {
|
fn reset_lighting_dispatch_is_one_undoable_group() {
|
||||||
let mut world = command_world();
|
let mut world = command_world();
|
||||||
|
|||||||
@ -198,7 +198,20 @@ fn spawn_snapshot_with_parent(
|
|||||||
snapshot: &EditorEntitySnapshot,
|
snapshot: &EditorEntitySnapshot,
|
||||||
parent: Option<Entity>,
|
parent: Option<Entity>,
|
||||||
) -> Entity {
|
) -> Entity {
|
||||||
let mut entity_mut = world.spawn((LevelObject, snapshot.actor_kind, snapshot.transform));
|
let visibility = if snapshot.editor_visibility.visible {
|
||||||
|
Visibility::Visible
|
||||||
|
} else {
|
||||||
|
Visibility::Hidden
|
||||||
|
};
|
||||||
|
let mut entity_mut = world.spawn((
|
||||||
|
LevelObject,
|
||||||
|
snapshot.actor_kind,
|
||||||
|
snapshot.transform,
|
||||||
|
snapshot.editor_visibility,
|
||||||
|
visibility,
|
||||||
|
InheritedVisibility::default(),
|
||||||
|
ViewVisibility::default(),
|
||||||
|
));
|
||||||
entity_mut.insert(
|
entity_mut.insert(
|
||||||
snapshot
|
snapshot
|
||||||
.actor_id
|
.actor_id
|
||||||
@ -291,7 +304,6 @@ fn spawn_snapshot_with_parent(
|
|||||||
entity_mut.insert(link.clone());
|
entity_mut.insert(link.clone());
|
||||||
}
|
}
|
||||||
entity_mut.insert(HierarchySiblingIndex(snapshot.hierarchy_sibling_index));
|
entity_mut.insert(HierarchySiblingIndex(snapshot.hierarchy_sibling_index));
|
||||||
entity_mut.insert(snapshot.editor_visibility);
|
|
||||||
if let Some(order) = &snapshot.inspector_order {
|
if let Some(order) = &snapshot.inspector_order {
|
||||||
entity_mut.insert(order.clone());
|
entity_mut.insert(order.clone());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,9 +11,11 @@ release candidate or replace the clean-checkout rerun required by production-rea
|
|||||||
## Accepted Coverage
|
## Accepted Coverage
|
||||||
|
|
||||||
- Registered commands distinguish immediate completion from modal Preview ownership; validation
|
- Registered commands distinguish immediate completion from modal Preview ownership; validation
|
||||||
failures terminate with their stable command ID.
|
failures terminate with their stable command ID. Palette execution and cancellation fully dismiss
|
||||||
|
the palette instead of leaving an empty title bar.
|
||||||
- Group Selection is one history transaction and preserves unrelated hierarchy rows through repeated
|
- Group Selection is one history transaction and preserves unrelated hierarchy rows through repeated
|
||||||
undo/redo even though the transient group entity is respawned.
|
undo/redo even though the transient group entity is respawned. Newly spawned and redone group
|
||||||
|
actors own their visibility hierarchy before viewport icon children attach.
|
||||||
- Reset Lighting and Project Sun changes are grouped, undoable light transactions.
|
- Reset Lighting and Project Sun changes are grouped, undoable light transactions.
|
||||||
- Asset/sub-asset placement; material/texture/audio/animation assignment; and exact viewport material
|
- Asset/sub-asset placement; material/texture/audio/animation assignment; and exact viewport material
|
||||||
drops cover commit, block/failure, cancel where applicable, and semantic undo/redo projections.
|
drops cover commit, block/failure, cancel where applicable, and semantic undo/redo projections.
|
||||||
@ -39,6 +41,15 @@ The publication worktree passed:
|
|||||||
|
|
||||||
The Gitea closure comment records these results with the exact publication commit.
|
The Gitea closure comment records these results with the exact publication commit.
|
||||||
|
|
||||||
|
## Native Editor Follow-up
|
||||||
|
|
||||||
|
A native-editor replay on 2026-07-13 loaded the collider diagnostics scene, exercised Draw Brush
|
||||||
|
Preview/cancel, and grouped two hierarchy actors through undo/redo. The replay exposed two issues
|
||||||
|
that source-only checks did not make visible: command execution left a collapsed palette title bar,
|
||||||
|
and the viewport icon child briefly inherited from a group actor without Bevy visibility hierarchy
|
||||||
|
components. Both paths now have focused regression assertions; a rebuilt replay dismissed the
|
||||||
|
palette completely and emitted no hierarchy warning.
|
||||||
|
|
||||||
No screenshot is required for this ticket: the accepted surface is lifecycle, rollback, cleanup, and
|
No screenshot is required for this ticket: the accepted surface is lifecycle, rollback, cleanup, and
|
||||||
history behavior exercised through headless production entry points. Live command/status checks add
|
history behavior exercised through headless production entry points. Live command/status checks add
|
||||||
confidence but are not substituted for semantic assertions. Packaged-runtime testing remains
|
confidence but are not substituted for semantic assertions. Packaged-runtime testing remains
|
||||||
|
|||||||
@ -43,8 +43,8 @@ The test must mutate a world and prove restoration.
|
|||||||
| Workflow | Coverage |
|
| Workflow | Coverage |
|
||||||
|----------|----------|
|
|----------|----------|
|
||||||
| Generic `EditorOperator` | Commit cleanup, preview/commit failure rollback, blocked no-op, terminal ID/phase |
|
| Generic `EditorOperator` | Commit cleanup, preview/commit failure rollback, blocked no-op, terminal ID/phase |
|
||||||
| Palette and registered commands | Typed immediate commit versus modal preview ownership; failed CSG start terminates |
|
| Palette and registered commands | Typed immediate commit versus modal preview ownership; Enter/click/Escape dismissal cannot be overwritten by stale window state; failed CSG start terminates |
|
||||||
| Selection and scene commands | Atomic Group Selection across repeated undo/redo; grouped Reset Lighting and Project Sun history |
|
| Selection and scene commands | Atomic Group Selection across repeated undo/redo; spawned group actors have an immediate Bevy visibility hierarchy; grouped Reset Lighting and Project Sun history |
|
||||||
| Asset workflows | Asset/sub-asset placement; material/texture group assignment; audio/animation assignment and incompatible targets |
|
| Asset workflows | Asset/sub-asset placement; material/texture group assignment; audio/animation assignment and incompatible targets |
|
||||||
| Viewport material drop | Exact renderer slot/primitive/brush-face preview, cancel, commit, cleanup, undo, and redo |
|
| Viewport material drop | Exact renderer slot/primitive/brush-face preview, cancel, commit, cleanup, undo, and redo |
|
||||||
| Draw Brush and CSG | Modal cancel; decomposed commit; CSG validation/read-only block, cancel, grouped commit, and deleted-brush restoration |
|
| Draw Brush and CSG | Modal cancel; decomposed commit; CSG validation/read-only block, cancel, grouped commit, and deleted-brush restoration |
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user