Skip to content

Dock-able motiongfx_editor - #121

Merged
nixonyh merged 29 commits into
mainfrom
nixon/dock-ui
Jul 23, 2026
Merged

Dock-able motiongfx_editor#121
nixonyh merged 29 commits into
mainfrom
nixon/dock-ui

Conversation

@nixonyh

@nixonyh nixonyh commented Jul 18, 2026

Copy link
Copy Markdown
Member
image
  • Added motiongfx_editor_ui
  • Added motiongfx_editor_ui_kernel
  • Moved motiongfx_editor into editor/ folder, and make it a binary instead of a lib.
  • Removed editor.rs example and move its content to motiongfx_editor/src/main.rs as a stub content until we have proper serialization implementation.

Relates to #70 & #89

nixonyh added 12 commits July 17, 2026 14:09
Port jackdaw_panels' generic docking engine into `ui/dock`: a pure-data
`DockTree` (splits + tabbed leaves, unit-tested), a reconciler that
materializes it into UI, resizable split handles, tab bars with close
buttons and an add-window popup, and pointer-drag tab reorder / merge /
edge-split with drop overlays.

Editor integration: Viewport, Timeline and Settings are dockable
windows; the scene renders to an offscreen preview image that
letterboxes into its dock area. Settings is a generic reflect
inspector (`ui/inspector`) over `EditorSettings` with an explicit
Save. `ui` stays UI-only so it can split into its own crate later;
editor wiring lives in `EditorUiPlugin` in lib.rs.
New `ui/glass`: a frosted-glass `UiMaterial` (SDF rounded-rect mask
with optional feathered edge, backdrop frost blur, thin near-opaque
border rim, cursor glow on hovered interactables) applied across the
dock chrome via a declarative `Glass` preset component, plus a
`glass_button` widget replacing the feathers-themed buttons. Frost
activates when a node is tagged `GlassBackdrop`; no source is tagged
right now, so panes render as tint + rim + glow.

New `ui/theme`: `EditorTheme` resource carrying the Monokai Pro
palette (mirrors assets/typst/monokai_pro.typ) and semantic slots;
glass presets and dock text colors derive from it.

Tabs gain a hover pill (`Glass::TabHover`), tile panes are squared,
and plugins now sit at the top of their modules.
The drag ghost now reuses the shared tab-tile builder (identical
square pill, padding, font, active material) instead of a distinct
rounded card, and the source tab is hidden while dragging. Fix
`drop_on_edge` losing the tab when anchored to its own leaf's edge:
split before removing so the target leaf stays valid.
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: eb2328ff-5ffe-4dbf-9fee-19155bd2db92

📥 Commits

Reviewing files that changed from the base of the PR and between d5f59a5 and 8ae516a.

📒 Files selected for processing (8)
  • editor/motiongfx_editor/src/lib.rs
  • editor/motiongfx_editor/src/main.rs
  • editor/motiongfx_editor/src/playback.rs
  • editor/motiongfx_editor/src/scene.rs
  • editor/motiongfx_editor/src/view.rs
  • editor/motiongfx_editor_ui/src/dock/registry.rs
  • editor/motiongfx_editor_ui/src/dock/tree.rs
  • editor/motiongfx_editor_ui/src/glass/backdrop.rs
🚧 Files skipped from review as they are similar to previous changes (7)
  • editor/motiongfx_editor_ui/src/glass/backdrop.rs
  • editor/motiongfx_editor_ui/src/dock/registry.rs
  • editor/motiongfx_editor/src/view.rs
  • editor/motiongfx_editor/src/playback.rs
  • editor/motiongfx_editor/src/lib.rs
  • editor/motiongfx_editor/src/scene.rs
  • editor/motiongfx_editor_ui/src/dock/tree.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added a dockable editor interface with draggable tabs, resizable panels, previews, settings, and hierarchy browsing.
    • Added timeline playback, scrubbing, play/pause controls, and automatic stopping at track ends.
    • Added reusable reactive UI components, frosted-glass styling, themed widgets, and reflective settings inspection.
    • Added a standalone docking demonstration.
  • Documentation
    • Added documentation describing the reactive UI framework and its usage.
  • Chores
    • Updated workspace configuration and ignore rules.
    • Normalized formatting across configuration, license, and theme files.

Walkthrough

The PR adds reusable reactive UI, docking, glass styling, and inspector crates, then rebuilds the MotionGfx editor around them. It also updates workspace wiring, adds a docking example, adjusts timeline composition, removes the previous editor implementation, and normalizes repository formatting.

Changes

Editor platform

Layer / File(s) Summary
Reactive UI kernel and Bevy integration
editor/motiongfx_editor_ui_kernel/..., editor/motiongfx_editor_ui/src/reactive.rs
Adds host-agnostic watchers, bindings, flushing, Bevy ECS integration, and change predicates.
Themed glass UI and reflection inspector
editor/motiongfx_editor_ui/src/glass/*, editor/motiongfx_editor_ui/src/theme.rs, editor/motiongfx_editor_ui/src/inspector.rs
Adds shader-backed glass materials, presets, widgets, backdrop and cursor effects, theming, and reflective boolean/numeric field editing.
Dock tree and registry
editor/motiongfx_editor_ui/src/dock/tree.rs, editor/motiongfx_editor_ui/src/dock/registry.rs
Adds dock nodes, tabs, splits, mutation and cleanup operations, window descriptors, registry lookup, and tests.
Dock rendering and interactions
editor/motiongfx_editor_ui/src/dock/{area,drag,reconcile,split,tabs,add_popup}.rs
Materializes dock trees and adds tab activation, popup insertion, dragging, dropping, splitting, resizing, and active-content bindings.
Editor runtime and timeline flow
editor/motiongfx_editor/src/*
Adds the docked editor plugin, preview layout, hierarchy browser, timeline controls, playback, scrubbing, settings, and track rendering while removing the previous editor modules.
Workspace and example integration
Cargo.toml, editor/*/Cargo.toml, examples/bevy_examples/*
Registers new workspace crates, updates dependency paths and features, and adds a docking demonstration.
Repository support
.github/workflows/rust.yml, .gitignore, LICENSE-*, assets/themes/*, rustfmt.toml
Normalizes formatting and line endings while adding ignore patterns.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant MotionGfxEditorPlugin
  participant KernelPlugin
  participant EditorScene
  participant DockTree
  participant Playback
  App->>MotionGfxEditorPlugin: register editor plugins
  MotionGfxEditorPlugin->>KernelPlugin: install reactive UI root
  EditorScene->>DockTree: build dock layout and windows
  EditorScene->>Playback: bind timeline and playback state
  Playback->>EditorScene: update timeline UI and preview
Loading

Possibly related PRs

Suggested reviewers: jaghov

Poem

I’m a rabbit with tabs in a row,
Watching glass panels shimmer and glow.
I hop through the tree,
Scrub timelines with glee,
While kernels make widgets all grow!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately highlights the main change: making motiongfx_editor dockable.
Description check ✅ Passed The description clearly matches the changeset, covering the new UI crates, move to editor/, and binary conversion.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/rust.yml:
- Around line 1-7: Update the top-level workflow configuration around the name
and on declarations to explicitly set GITHUB_TOKEN permissions to read-only
using the workflow’s permissions setting. Keep the existing pull_request and
push triggers unchanged.

In `@editor/motiongfx_editor_ui/src/dock.rs`:
- Around line 7-16: Update the module-level documentation in dock.rs to reflect
the current integration: remove the claim that DockPlugin, WindowRegistry,
DockTreeHost, and DockTree still need to be wired or registered, and remove “add
window” popup from the out-of-scope list because AddWindowPopupPlugin is
registered. Preserve the remaining documented scope accurately.

In `@editor/motiongfx_editor_ui/src/dock/add_popup.rs`:
- Around line 80-86: Update the AddWindowPopupBackdrop Pickable configuration so
outside-click dismissal does not propagate to controls behind the popup, while
preserving the backdrop’s ability to detect the click and close the popup. Keep
the change scoped to should_block_lower and retain the existing hover behavior
unless required by the dismissal flow.

In `@editor/motiongfx_editor_ui/src/dock/drag.rs`:
- Around line 250-263: Update the child-position calculation in the
drag-selection loop to use each child’s transform from node_query.get, rather
than reusing the row-level ui_transform. Derive child_center from
_child_transform while preserving the existing scale, size, distance, and
nearest-tab selection logic.

In `@editor/motiongfx_editor_ui/src/dock/reconcile.rs`:
- Around line 55-65: Update reconcile_tree and reconcile_at to propagate a
registry_changed/force-rebuild flag when WindowRegistry changes, rather than
only reacting to DockTree changes. Include that flag in each leaf’s
needs_rebuild decision so unregistering or replacing a descriptor rebuilds
existing content, labels, and build output even when tab IDs are unchanged.

In `@editor/motiongfx_editor_ui/src/dock/registry.rs`:
- Around line 26-44: Update DockRegistry::register to handle an existing
descriptor ID before appending: replace the descriptor at its current index, or
reject the duplicate according to the intended registry semantics. Ensure
windows contains at most one descriptor per ID and keep index consistent;
preserve unregister and iter behavior for unique registrations.

In `@editor/motiongfx_editor_ui/src/dock/tree.rs`:
- Around line 582-604: Adjust the insertion index in the tab-moving logic around
the source removal and destination insertion: when from and to refer to the same
leaf and the dragged tab’s original position is before the requested index,
decrement the index after removal before clamping and inserting. Preserve
cross-leaf and backward moves, and add regression coverage for moving the first
tab before and after later tabs.

In `@editor/motiongfx_editor_ui/src/glass/backdrop.rs`:
- Around line 24-43: Update the cached state in the backdrop update system
around the last rect check to include the current GlassBackdrop.0 handle or
otherwise detect GlassBackdrop changes. Only return early when both the
rectangle and image handle are unchanged, and propagate a newly changed backdrop
image to every material even when rect remains the same.

In `@editor/motiongfx_editor_ui/src/inspector.rs`:
- Around line 119-124: Update the generic inspector conversion and edit handling
around the u32/u64 branches and the associated event path (including the code
covering the referenced later range) to preserve concrete integer types instead
of routing them through f64 or i64. Use checked conversions for unsigned values,
reject values outside the target type’s valid range, and ensure edits write back
the original integer type without precision loss.

In `@editor/motiongfx_editor_ui/src/lib.rs`:
- Around line 148-150: Remove the manually inserted Hovered component from the
Button node construction, leaving bevy_picking to manage hover state through
pointer events while preserving the surrounding button setup.

In `@editor/motiongfx_editor/src/layout.rs`:
- Around line 69-75: Update editor/motiongfx_editor/src/layout.rs:69-75 to build
and tear down generated content independently for each TimelineContent instead
of using a singleton query. Update
editor/motiongfx_editor/src/hierarchy.rs:47-49 to populate every HierarchyPanel,
editor/motiongfx_editor/src/playback.rs:233-237 to synchronize every
PlayPauseLabel, and editor/motiongfx_editor/src/scene.rs:431-435 to resolve the
NamePanel associated with the dragged divider; preserve each instance’s entity
relationships when iterating duplicate docked windows.

In `@editor/motiongfx_editor/src/playback.rs`:
- Around line 38-47: Update the player toggle loop in the playback control
function to compute one target play/pause state before iterating, then assign
that state to every player’s is_playing field instead of individually inverting
each value. Preserve resetting time_scale to 1.0 and the existing rewind logic
based on the resulting shared state.
- Around line 101-123: Update the timeline press/drag handling around
on_track_press to resolve press.entity or drag.entity to its owning
TimelineContent before using q_track or changing Scrubbing. Apply Scrubbing to
the resolved owner rather than descendant clips or groups. Add Pointer<Release>
handling that resolves the same owner and removes Scrubbing, including releases
that occur before dragging begins.

In `@editor/motiongfx_editor/src/view.rs`:
- Around line 61-68: Clamp the editable composition dimensions from
settings.physical_size to the same valid minimums used when creating the preview
target before computing aspect. Update the sizing logic around comp and aspect
so zero-valued width or height cannot produce infinity or NaN, while preserving
the existing available-area fitting behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ad36a2e4-3a2e-4881-9fb1-fef9f948b07c

📥 Commits

Reviewing files that changed from the base of the PR and between efe472e and 1bf0f8d.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (45)
  • .github/workflows/rust.yml
  • .gitignore
  • Cargo.toml
  • LICENSE-APACHE
  • LICENSE-MIT
  • assets/themes/Monokai Pro.tmTheme
  • crates/bevy_motiongfx/Cargo.toml
  • crates/motiongfx/Cargo.toml
  • crates/motiongfx/src/lib.rs
  • crates/motiongfx/src/track.rs
  • crates/motiongfx/src/track/meta.rs
  • crates/motiongfx_editor/src/lib.rs
  • crates/motiongfx_editor/src/ui.rs
  • editor/motiongfx_editor/Cargo.toml
  • editor/motiongfx_editor/docs/HANDOFF.md
  • editor/motiongfx_editor/src/hierarchy.rs
  • editor/motiongfx_editor/src/layout.rs
  • editor/motiongfx_editor/src/lib.rs
  • editor/motiongfx_editor/src/main.rs
  • editor/motiongfx_editor/src/playback.rs
  • editor/motiongfx_editor/src/scene.rs
  • editor/motiongfx_editor/src/view.rs
  • editor/motiongfx_editor_ui/Cargo.toml
  • editor/motiongfx_editor_ui/src/dock.rs
  • editor/motiongfx_editor_ui/src/dock/add_popup.rs
  • editor/motiongfx_editor_ui/src/dock/area.rs
  • editor/motiongfx_editor_ui/src/dock/drag.rs
  • editor/motiongfx_editor_ui/src/dock/reconcile.rs
  • editor/motiongfx_editor_ui/src/dock/registry.rs
  • editor/motiongfx_editor_ui/src/dock/split.rs
  • editor/motiongfx_editor_ui/src/dock/tabs.rs
  • editor/motiongfx_editor_ui/src/dock/tree.rs
  • editor/motiongfx_editor_ui/src/glass.rs
  • editor/motiongfx_editor_ui/src/glass.wgsl
  • editor/motiongfx_editor_ui/src/glass/backdrop.rs
  • editor/motiongfx_editor_ui/src/glass/glow.rs
  • editor/motiongfx_editor_ui/src/glass/material.rs
  • editor/motiongfx_editor_ui/src/glass/preset.rs
  • editor/motiongfx_editor_ui/src/glass/widget.rs
  • editor/motiongfx_editor_ui/src/inspector.rs
  • editor/motiongfx_editor_ui/src/lib.rs
  • editor/motiongfx_editor_ui/src/theme.rs
  • examples/bevy_examples/Cargo.toml
  • examples/bevy_examples/examples/dock_demo.rs
  • rustfmt.toml
💤 Files with no reviewable changes (2)
  • crates/motiongfx_editor/src/lib.rs
  • crates/motiongfx_editor/src/ui.rs

Comment on lines +1 to +7
name: CI
on:
pull_request:
push:
branches:
- main
jobs:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict default permissions for GITHUB_TOKEN.

By default, GitHub Actions may grant broad permissions to the GITHUB_TOKEN depending on repository settings. To follow the principle of least privilege and prevent potential abuse by untrusted code executing during builds or tests, explicitly restrict top-level permissions.

🔒️ Proposed fix to apply read-only permissions
 name: CI
 on: 
   pull_request:
   push:
     branches:
       - main
+
+permissions:
+  contents: read
+
 jobs:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
name: CI
on:
pull_request:
push:
branches:
- main
jobs:
name: CI
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
jobs:
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 1-91: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/rust.yml around lines 1 - 7, Update the top-level workflow
configuration around the name and on declarations to explicitly set GITHUB_TOKEN
permissions to read-only using the workflow’s permissions setting. Keep the
existing pull_request and push triggers unchanged.

Source: Linters/SAST tools

Comment thread editor/motiongfx_editor_ui/src/dock.rs Outdated
Comment on lines +80 to +86
AddWindowPopupBackdrop,
// Catch the outside-click to close, but let hover/clicks
// pass through to the UI beneath instead of freezing it.
Pickable {
should_block_lower: false,
is_hoverable: true,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Block dismissal clicks from reaching controls behind the popup.

With should_block_lower: false, clicking outside closes the popup and also activates the underlying editor target. This can unintentionally trigger buttons or other actions.

Proposed fix
                 Pickable {
-                    should_block_lower: false,
+                    should_block_lower: true,
                     is_hoverable: true,
                 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AddWindowPopupBackdrop,
// Catch the outside-click to close, but let hover/clicks
// pass through to the UI beneath instead of freezing it.
Pickable {
should_block_lower: false,
is_hoverable: true,
},
AddWindowPopupBackdrop,
// Catch the outside-click to close, but let hover/clicks
// pass through to the UI beneath instead of freezing it.
Pickable {
should_block_lower: true,
is_hoverable: true,
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@editor/motiongfx_editor_ui/src/dock/add_popup.rs` around lines 80 - 86,
Update the AddWindowPopupBackdrop Pickable configuration so outside-click
dismissal does not propagate to controls behind the popup, while preserving the
backdrop’s ability to detect the click and close the popup. Keep the change
scoped to should_block_lower and retain the existing hover behavior unless
required by the dismissal flow.

Comment thread editor/motiongfx_editor_ui/src/dock/drag.rs
Comment on lines +55 to +65
fn reconcile_tree(world: &mut World) {
if !world.is_resource_changed::<DockTree>() {
return;
}
let Some(root) = world.resource::<DockTree>().root else {
return;
};
let Some(host) = find_dock_tree_host(world) else {
return;
};
reconcile_at(world, host, root);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reconcile changes to the window registry.

WindowRegistry::unregister or descriptor replacement leaves existing content, labels, and build output materialized because reconciliation only observes DockTree. Even a forced tree pass would skip rebuilding when the tab IDs are unchanged.

Propagate a registry_changed/force-rebuild flag through reconcile_at and include it in each leaf’s needs_rebuild.

Also applies to: 101-103

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@editor/motiongfx_editor_ui/src/dock/reconcile.rs` around lines 55 - 65,
Update reconcile_tree and reconcile_at to propagate a
registry_changed/force-rebuild flag when WindowRegistry changes, rather than
only reacting to DockTree changes. Include that flag in each leaf’s
needs_rebuild decision so unregistering or replacing a descriptor rebuilds
existing content, labels, and build output even when tab IDs are unchanged.

Comment thread editor/motiongfx_editor_ui/src/lib.rs Outdated
Comment thread editor/motiongfx_editor/src/layout.rs Outdated
Comment thread editor/motiongfx_editor/src/playback.rs
Comment on lines +101 to +123
pub(crate) fn on_track_press(
mut press: On<Pointer<Press>>,
state: Res<EditorState>,
ui_scale: Res<UiScale>,
q_track: Query<
(&ComputedNode, &UiGlobalTransform),
With<TimelineContent>,
>,
mut manager: ResMut<MotionGfxManager>,
mut q_players: Query<&mut RealtimePlayer>,
mut commands: Commands,
) {
let track = press.entity;
let Ok((computed, transform)) = q_track.get(track) else {
return;
};
press.propagate(false);
commands.entity(track).insert(Scrubbing);

let cursor = press.pointer_location.position / ui_scale.0;
let time =
time_at_cursor(cursor, computed, transform, state.duration);
scrub_to(time, &state, &mut manager, &mut q_players);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -a 'Cargo.toml' -x rg -n -C2 'bevy\s*=|bevy_motiongfx' {}
rg -n -C4 'on_track_(press|drag|release|cancel)|original_event_target|trigger\(\)\.target' editor/motiongfx_editor/src

Repository: voxell-tech/motiongfx

Length of output: 5666


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,240p' editor/motiongfx_editor/src/playback.rs
printf '\n--- scene excerpt ---\n'
sed -n '180,230p' editor/motiongfx_editor/src/scene.rs
printf '\n--- scrub-related symbols ---\n'
rg -n -C 2 'Scrubbing|on_track_(press|drag|release|cancel)|Pointer<Press>|Pointer<DragEnd>|Pointer<Cancel>|TimelineContent|timeline_track' editor/motiongfx_editor/src

Repository: voxell-tech/motiongfx

Length of output: 16434


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect Bevy picking APIs referenced by the concern.
rg -n -C 3 'original_target|bubble|propagate\(false\)|Pointer<Press>|Pointer<DragEnd>|Pointer<Cancel>|On<Pointer<Press>>|On<Pointer<DragEnd>>' ~/.cargo/registry/src 2>/dev/null | head -n 200

Repository: voxell-tech/motiongfx

Length of output: 159


🌐 Web query:

Bevy 0.19 picking Pointer<Press> Pointer<Release> Pointer<DragEnd> original_target bubbling DragEnd release semantics

💡 Result:

In Bevy 0.19, pointer events are managed by the picking system, which follows a strict interaction state machine [1][2][3]. Regarding your specific query on semantics and event propagation: 1. Pointer Interaction Events: - Pointer and Pointer are fundamental button events [1][2]. These are emitted by the state machine when a pointer button is interacted with [2]. - Pointer is a high-level event that triggers when a pointer, which was previously dragging a target entity, receives a Pointer event [4][5]. 2. Bubbling and Target Semantics: - Events like Pointer and Pointer are defined as bubbling, meaning they propagate up through an entity's ancestors via the ChildOf relationship [6][7]. - With the introduction of Pointer and Pointer in 0.19 (replacing certain uses of Over/Out for hierarchy-aware logic), these new events do not bubble [8]. They are sent specifically to entities based on changes in the hover state, providing a more predictable alternative for UI and interaction logic [8][9]. 3. Propagation Control: - Bevy allows you to control propagation using the event.propagate(false) method within an observer [10]. This is commonly used in On<Pointer<DragEnd>> or other event observers to stop the event from continuing its upward travel through the entity hierarchy [10]. 4. Event Targeting: - The system generally tracks the original_target of an interaction [6]. For events like Pointer, they often target the entity hovered in the previous frame, which is significant when dealing with fast movements or touch pointers that may not be hovering anything at the moment of release [2][3]. For detailed implementation, consult the pointer_events system documentation in the Bevy prelude, as it governs the dispatch order (typically: Press/Click -> Release -> DragDrop -> DragEnd -> DragLeave) [1][2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n editor/motiongfx_editor/src/playback.rs | sed -n '1,220p'

Repository: voxell-tech/motiongfx

Length of output: 8504


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 2 '\bClip\b|\bGroup\b|timeline_track\(|TimelineContent|Children \[' editor/motiongfx_editor/src

Repository: voxell-tech/motiongfx

Length of output: 7518


🌐 Web query:

Bevy Pointer<Press> On event entity original_target fields docs.rs 0.19

💡 Result:

In Bevy 0.19, the Pointer struct is a generic container used for pointer events (such as Press, Click, or Drag) [1][2]. It does not contain a field named original_target [1][2]. The Pointer struct consists of the following fields [1][2]: - entity: The Entity this pointer event happened for [1][2]. - pointer_id: The unique identifier for the pointer [1][2]. - pointer_location: The location of the pointer during the event [1][2]. - event: The event-specific data (e.g., the Press struct) [1][3][2]. The Press struct itself contains only the button (PointerButton) and hit (HitData) information [3]. If you are looking for target information, the Pointer struct implements EntityEvent, which provides the event_target method to retrieve the entity targeted by the event [1][2]. The picking system determines the target based on the pointer's interaction state, and some events (like Click and Release) are specifically noted to target the entity hovered in the previous frame to maintain consistency during release actions [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect local Bevy sources if present to resolve Pointer<Press>/DragEnd field semantics.
BEVY_SRC=$(fd -a 'events.rs' ~/.cargo/registry/src 2>/dev/null | rg '/bevy[^/]*/.+/picking/.+events\.rs$' | head -n 5 || true)
printf '%s\n' "$BEVY_SRC"
if [ -n "$BEVY_SRC" ]; then
  for f in $BEVY_SRC; do
    echo "--- $f ---"
    rg -n -C 2 'struct Pointer<Press>|struct Pointer<DragEnd>|original_target|target:|entity:' "$f"
  done
fi

Repository: voxell-tech/motiongfx

Length of output: 160


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import os, glob
paths = glob.glob(os.path.expanduser("~/.cargo/registry/src/*/bevy-*/crates/bevy_picking/src/**/*.rs"), recursive=True)
for p in paths:
    if p.endswith("events.rs") or p.endswith("pointer_events.rs") or p.endswith("mod.rs"):
        print(p)
PY

Repository: voxell-tech/motiongfx

Length of output: 159


Resolve the timeline owner before scrubbing, and clear scrubbing on plain release.

  • press.entity / drag.entity can be a clip or group descendant; resolve the owning TimelineContent entity before querying or inserting/removing Scrubbing.
  • Handle Pointer<Release> too, so a press released before dragging doesn’t leave Scrubbing set.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@editor/motiongfx_editor/src/playback.rs` around lines 101 - 123, Update the
timeline press/drag handling around on_track_press to resolve press.entity or
drag.entity to its owning TimelineContent before using q_track or changing
Scrubbing. Apply Scrubbing to the resolved owner rather than descendant clips or
groups. Add Pointer<Release> handling that resolves the same owner and removes
Scrubbing, including releases that occur before dragging begins.

Comment thread editor/motiongfx_editor/src/view.rs Outdated
nixonyh added 2 commits July 18, 2026 22:28
The editor visualized the composition tree by walking `Track::meta()`,
which forced `motiongfx` to carry a shadow copy of authoring structure
through its runtime types. The upcoming document format owns that
structure instead, so the runtime doesn't need it.

- delete `layout.rs` (clip/group/toggle placement, group collapse)
- drop `clip_box`/`group_box`/`group_toggle` and their markers from
  `motiongfx_editor_ui`
- restore `motiongfx`/`bevy_motiongfx` to `main`; the `metadata`
  feature was purely additive on this branch, so this is an exact
  revert rather than a hand-edit
- add `playback::sync_timeline_state` for the timeline id, duration and
  track width that `layout.rs` used to provide

Control bar, name column, divider, scroll viewport, playhead and
scrubbing are unchanged; the track is now an empty strip sized to the
composition length.

Claude-Session: https://claude.ai/code/session_01Asj7E3ebcvYUrQ192wtaop
nixonyh added 8 commits July 19, 2026 14:47
Add motiongfx_editor_ui_kernel: a no_std, backend-agnostic kernel of
watchers (rebuild a subtree when a predicate fires) and bindings (write
one field in place), with a Host trait for the backend. reactive.rs is
the bevy adapter.

The dock, tab bar, inspector, hierarchy, add-popup and timeline panel
are now built through it. Structure rides on watchers keyed to a
projection that omits volatile fields, so a splitter drag or a tab
switch updates through bindings instead of rebuilding.
Move `watch` from `Ui` onto `NodeMut`, so a builder reads as "make this
node, then say how it reacts" instead of making a watcher and giving it
a body afterwards.

Add `BevyUiExt::bsn`, which spawns a node from a `bsn!` scene, and drop
the free `widget` function every call site had to wrap with.
Drop Ui::bind_raw and the Ui-level typed binds, so `Ui` only ever
spawns and every watcher or binding is declared on the node it belongs
to. Callers that want an invisible node to scope a binding use
`group()`.
Also refresh the README's Host trait and builder sections, which still
described the removed Widget associated type and the two-phase build.
ChangedFn, ApplyFn and BuildFn become traits with a blanket impl, so
signatures read `impl ChangedFn<H>` instead of spelling out the FnMut
bound plus Send + Sync + 'static every time. The boxed forms the kernel
stores stay as private aliases.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
editor/motiongfx_editor/src/playback.rs (1)

33-56: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Still inverting per-player state instead of computing one shared play/pause target.

If RealtimePlayers ever disagree on is_playing, inverting each independently swaps which players run rather than toggling play/pause as a whole; the rewind guard below also still checks q_players.iter().any(|p| p.is_playing) instead of the newly-computed now_playing. This matches a still-unresolved finding from a previous review round.

Proposed fix
-    let mut now_playing = false;
-    for mut player in &mut q_players {
-        player.is_playing = !player.is_playing;
-        player.time_scale = 1.0;
-        now_playing |= player.is_playing;
-    }
-    state.is_playing = now_playing;
+    let should_play = !q_players.iter().any(|p| p.is_playing);
+    for mut player in &mut q_players {
+        player.is_playing = should_play;
+        player.time_scale = 1.0;
+    }
+    state.is_playing = should_play;

     // Rewind if starting playback from the very end.
-    if let Some(timeline_id) = state.timeline
-        && q_players.iter().any(|p| p.is_playing)
+    if should_play
+        && let Some(timeline_id) = state.timeline
         && let Some(timeline) = manager.get_timeline_mut(&timeline_id)
         && timeline.target_time() >= state.duration
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@editor/motiongfx_editor/src/playback.rs` around lines 33 - 56, Update
on_toggle_playback to compute one shared play/pause target from the current
player state, then assign that target to every RealtimePlayer instead of
independently inverting each player. Use the resulting now_playing value for the
rewind guard rather than querying q_players again, while preserving the existing
time-scale reset and rewind behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@editor/motiongfx_editor_ui/src/glass/backdrop.rs`:
- Around line 23-47: Update the cached state in the binding closure around
backdrops and seen so it stores the GlassBackdrop image asset ID alongside the
rectangle. Compare both the current rectangle and handle ID when computing
changed, and update both cached values so a handle change triggers propagation
even when the rectangle is unchanged.

In `@editor/motiongfx_editor/src/view.rs`:
- Around line 39-60: Clamp EditorSettings.physical_size to at least UVec2::ONE
before converting it to a Vec2 and calculating aspect in preview_fit. Preserve
the existing sizing and early-return behavior while ensuring zero-valued
dimensions cannot produce inf or NaN.

---

Duplicate comments:
In `@editor/motiongfx_editor/src/playback.rs`:
- Around line 33-56: Update on_toggle_playback to compute one shared play/pause
target from the current player state, then assign that target to every
RealtimePlayer instead of independently inverting each player. Use the resulting
now_playing value for the rewind guard rather than querying q_players again,
while preserving the existing time-scale reset and rewind behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bd3c8685-a8d1-4767-9325-ea9ec313e826

📥 Commits

Reviewing files that changed from the base of the PR and between 1bf0f8d and 4d090a6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (33)
  • Cargo.toml
  • crates/motiongfx/Cargo.toml
  • editor/motiongfx_editor/Cargo.toml
  • editor/motiongfx_editor/docs/HANDOFF.md
  • editor/motiongfx_editor/src/hierarchy.rs
  • editor/motiongfx_editor/src/lib.rs
  • editor/motiongfx_editor/src/main.rs
  • editor/motiongfx_editor/src/playback.rs
  • editor/motiongfx_editor/src/scene.rs
  • editor/motiongfx_editor/src/view.rs
  • editor/motiongfx_editor_ui/Cargo.toml
  • editor/motiongfx_editor_ui/src/dock.rs
  • editor/motiongfx_editor_ui/src/dock/add_popup.rs
  • editor/motiongfx_editor_ui/src/dock/area.rs
  • editor/motiongfx_editor_ui/src/dock/drag.rs
  • editor/motiongfx_editor_ui/src/dock/reconcile.rs
  • editor/motiongfx_editor_ui/src/dock/registry.rs
  • editor/motiongfx_editor_ui/src/dock/split.rs
  • editor/motiongfx_editor_ui/src/dock/tabs.rs
  • editor/motiongfx_editor_ui/src/dock/tree.rs
  • editor/motiongfx_editor_ui/src/glass.rs
  • editor/motiongfx_editor_ui/src/glass/backdrop.rs
  • editor/motiongfx_editor_ui/src/glass/material.rs
  • editor/motiongfx_editor_ui/src/glass/preset.rs
  • editor/motiongfx_editor_ui/src/inspector.rs
  • editor/motiongfx_editor_ui/src/lib.rs
  • editor/motiongfx_editor_ui/src/reactive.rs
  • editor/motiongfx_editor_ui_kernel/Cargo.toml
  • editor/motiongfx_editor_ui_kernel/README.md
  • editor/motiongfx_editor_ui_kernel/src/host.rs
  • editor/motiongfx_editor_ui_kernel/src/lib.rs
  • editor/motiongfx_editor_ui_kernel/src/ui.rs
  • examples/bevy_examples/examples/dock_demo.rs
💤 Files with no reviewable changes (1)
  • editor/motiongfx_editor/src/main.rs
🚧 Files skipped from review as they are similar to previous changes (9)
  • editor/motiongfx_editor_ui/src/glass.rs
  • editor/motiongfx_editor_ui/src/dock.rs
  • editor/motiongfx_editor_ui/src/glass/preset.rs
  • editor/motiongfx_editor_ui/src/glass/material.rs
  • editor/motiongfx_editor_ui/src/dock/split.rs
  • editor/motiongfx_editor_ui/src/inspector.rs
  • editor/motiongfx_editor/src/scene.rs
  • editor/motiongfx_editor_ui/src/dock/tree.rs
  • editor/motiongfx_editor_ui/src/dock/drag.rs

Comment thread editor/motiongfx_editor_ui/src/glass/backdrop.rs
Comment thread editor/motiongfx_editor/src/view.rs
nixonyh added 2 commits July 20, 2026 13:30
Three were stale after the kernel refactor (Ui::watch,
BevyUiExt::bind_field, view::fit_preview_image); the rest linked to
private modules, which rustdoc reports either way.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@editor/motiongfx_editor/src/scene.rs`:
- Around line 249-258: Update the track box container in the timeline UI to use
normal flex-column layout instead of PositionType::Absolute, preserving the top
padding through standard layout properties. In build_track_boxes, remove
absolute-position offsets and calculate each track’s vertical placement through
bottom margins or equivalent spacing so generated boxes remain stacked visually
while contributing to TimelineContent’s height and enabling TrackViewport and
NamePanel scrolling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4fe7c881-f19a-4921-ade1-6551900c0976

📥 Commits

Reviewing files that changed from the base of the PR and between 4d090a6 and 87a5c50.

📒 Files selected for processing (6)
  • editor/motiongfx_editor/src/lib.rs
  • editor/motiongfx_editor/src/main.rs
  • editor/motiongfx_editor/src/scene.rs
  • editor/motiongfx_editor_ui/src/glass.rs
  • editor/motiongfx_editor_ui/src/glass/preset.rs
  • editor/motiongfx_editor_ui/src/reactive.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • editor/motiongfx_editor_ui/src/glass.rs
  • editor/motiongfx_editor/src/main.rs
  • editor/motiongfx_editor_ui/src/glass/preset.rs
  • editor/motiongfx_editor/src/lib.rs
  • editor/motiongfx_editor_ui/src/reactive.rs

Comment thread editor/motiongfx_editor/src/scene.rs
nixonyh added 4 commits July 21, 2026 11:28
- registry: `register` replaces in place on a duplicate id instead of
  leaving a stale descriptor that `iter` still yields (+tests)
- tree: compensate the insertion index for same-leaf forward reorders,
  which the pre-removal index otherwise overshoots by one (+tests)
- playback: toggle one shared play/pause state rather than inverting
  each player, and clear `Scrubbing` on a plain `Release` since a click
  without a drag never fires `DragEnd`
- view: floor `physical_size` at 1px so the aspect ratio can't divide
  by zero
- glass: cache the backdrop image id in the binding predicate so a new
  handle repropagates even when the rect is unchanged
@nixonyh
nixonyh merged commit 9f434df into main Jul 23, 2026
8 checks passed
@nixonyh
nixonyh deleted the nixon/dock-ui branch July 23, 2026 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant