Editor: Unity-style file explorer in the Project panel

The last item of the Stage-10 editor-UX batch. The Project panel's
fixed typed-folder listing becomes a real file explorer over assets/:

- breadcrumbs + double-click folder navigation, " New Folder", inline
  rename rows, context menus (Open / Rename / Delete), drag a row onto
  a folder (or "..") to move it, drag files in from the OS to import
  into the current folder, double-click to open (scripts via the
  external-editor flow, others via xdg-open).
- All behavior lives egui-free in editor/src/explorer.rs (listing,
  breadcrumbs, name validation/uniquing, create/rename/move/delete/
  import) and is unit-tested; the shell only renders it. Renames and
  moves ride the uid-preserving AssetDatabase ops so saved AssetRefs
  keep resolving; unregistered files fall back to fs::rename. Folders
  delete only when empty — no recursive asset deletion.
- Engine: AssetDatabase::scan now walks the WHOLE assets/ tree instead
  of just the typed folders, so assets organised into custom folders
  register and survive rescans (covered by updated unit tests).

File operations act immediately and bypass the undo stack, like the
hierarchy's structural edits. GUI piece — needs an eye-check before
promotion to main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Homer
2026-07-10 21:01:05 +02:00
parent 48003ffea4
commit c47efa876f
6 changed files with 941 additions and 81 deletions
+27 -8
View File
@@ -138,7 +138,7 @@ gives an absolute path the server loads and deduplicates.
use oxide_engine::asset::{AssetDatabase, AssetServer, GltfModel};
let mut db = AssetDatabase::open(project_root); // reads assets.manifest if present
db.scan(); // discover files in fonts/ models/ …
db.scan(); // walk assets/ — register new files, prune missing
db.save().unwrap(); // persist any newly-assigned uids
let uid = db.uid_of("models/cube.glb").unwrap();
@@ -224,13 +224,32 @@ type) is recognised the same way.
`EditorState` holds an `asset_db` whenever a project is open: the editor opens
and scans it on project open/create and rescans when the file watcher reports
changes under `assets/`, so importing an asset is just **dropping the file into
the matching typed folder** (`fonts/`, `textures/`, …) — no separate import
step. The **Project panel** is an asset browser listing each typed folder's
assets from the database, and an asset-reference field in the inspector renders
as a **picker** populated from it, filtered to the field's target kind. New
projects are seeded with a bundled default UI font (Inter, SIL OFL) under
`fonts/`, referenced by its project-relative path like any other asset.
changes under `assets/`, so importing an asset is as simple as **dropping the
file anywhere under `assets/`** — no separate import step. An asset-reference
field in the inspector renders as a **picker** populated from the database,
filtered to the field's target kind. New projects are seeded with a bundled
default UI font (Inter, SIL OFL) under `fonts/`, referenced by its
project-relative path like any other asset.
The **Project panel** hosts a Unity-style **file explorer** over `assets/`
(`oxide_editor::explorer` holds the egui-free behavior layer; the shell only
renders it):
- **breadcrumbs + folder navigation** (double-click a folder, click a crumb);
- ** New Folder**, and per-row context menus with **Rename** and **Delete**
(folders delete only when empty — recursive asset deletion is deliberately
not offered);
- **drag a row onto a folder** (or the `..` row) to move it;
- **drag files in from the OS** to import them into the current folder
(copied in under a collision-free name, registered, manifest saved);
- **double-click a file** to open it — scripts via the external-editor flow
(see [scripting.md](scripting.md)), everything else via `xdg-open`.
Renames and moves go through the database's uid-preserving file ops, so saved
`AssetRef`s keep resolving after any reorganisation; unregistered files
(licenses, notes) fall back to plain filesystem operations. File operations
act immediately and bypass the undo stack, like the hierarchy's structural
edits.
[`AssetDatabase`]: ../engine/src/asset/database.rs
[`AssetDbError`]: ../engine/src/asset/database.rs