Skip to content

Commit 27c061d

Browse files
committed
Code editor: status bar, Outline/Problems/Scripts panels, scripting workspace overhaul
Adds the IDE chrome that the Scripting workspace needs to feel like a real code editor instead of a generic egui panel. Code editor (renzora_code_editor): - Status bar (file path · modified dot · language · line:col) - Save All button on the toolbar - Show whitespace toggle (overlays dot for space, arrow for tab) - Minimap toggle - Minimap on the right with viewport indicator + click/drag to scroll - Save-or-discard confirm modal when closing modified tabs - save_file(idx) helper alongside save_active() - Missing files now log a warning with the full path instead of an opaque read_to_string error - script_path resolution matches the runtime: project_root.join(rel_path) with no fake 'scripts/' fallback (scripts can live in any directory) New panels (registered with the editor): - Outline: extracts function/struct symbols from the active tab and jumps the cursor to them on click - Problems: aggregates ScriptError across every open file; click to switch tab and goto-line - Scripts: lists script entries on the selected entity with eye-toggle (enable/disable) and trash (detach), plus a 'New Script' button that creates scripts/new_script_N.lua, attaches it (creating ScriptComponent if absent), and opens it as a new tab Asset browser: - AssetBrowserExtensionFilter resource (in renzora_editor_framework) - AssetBrowserState.passes_filter() applied in collect_entries and the tree's inline file listing - ui() syncs the filter from the resource each frame Layout / UX: - Scripting workspace reshuffled: Hierarchy / (Scripts+Outline tabbed) / Assets in the left rail (~13%), Code editor over Console+Problems in the center (~62%), Viewport over Script Variables on the right (~25%); Inspector + History dropped from this workspace - sync_asset_filter_for_scripting limits the asset browser to script / shader / config extensions while the Scripting layout is active - auto_switch_to_ui_layout_on_selection now only fires when already in Scene or UI, so selecting an entity in Scripting/Animation/etc. no longer hijacks the workspace
1 parent b27b3d1 commit 27c061d

13 files changed

Lines changed: 1369 additions & 35 deletions

File tree

crates/renzora_asset_browser/src/grid.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub(crate) fn collect_entries(state: &AssetBrowserState) -> Option<Vec<GridEntry
5050
Ok(iter) => iter
5151
.filter_map(|e| e.ok())
5252
.filter(|e| !is_hidden(&e.path()))
53+
.filter(|e| state.passes_filter(&e.path()))
5354
.map(|e| {
5455
let is_dir = e.file_type().map(|ft| ft.is_dir()).unwrap_or(false);
5556
let name = e.file_name().to_string_lossy().to_string();

crates/renzora_asset_browser/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ impl EditorPanel for AssetBrowserPanel {
6969
}
7070
}
7171

72+
// Sync extension allow-list from the global filter resource each frame.
73+
let filter_now = world
74+
.get_resource::<renzora_editor_framework::AssetBrowserExtensionFilter>()
75+
.and_then(|r| r.0.clone());
76+
if state.extension_filter != filter_now {
77+
state.extension_filter = filter_now;
78+
}
79+
7280
// Initialize current folder on first render
7381
if state.current_folder.is_none() {
7482
let root = state.root();

crates/renzora_asset_browser/src/state.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ pub struct AssetBrowserState {
160160
pub sort_mode: SortMode,
161161
/// Current sort direction.
162162
pub sort_direction: SortDirection,
163+
164+
/// When set, files outside this allow-list are hidden in grid/list/tree
165+
/// (folders always remain visible). Synced each frame from the
166+
/// `AssetBrowserExtensionFilter` resource.
167+
pub extension_filter: Option<Vec<String>>,
163168
}
164169

165170
impl Default for AssetBrowserState {
@@ -211,11 +216,29 @@ impl Default for AssetBrowserState {
211216
recent_expanded: false,
212217
sort_mode: SortMode::default(),
213218
sort_direction: SortDirection::default(),
219+
extension_filter: None,
214220
}
215221
}
216222
}
217223

218224
impl AssetBrowserState {
225+
/// Whether `path` survives the active extension filter. Folders always
226+
/// pass; files only pass if their lower-cased extension is in the list.
227+
pub fn passes_filter(&self, path: &Path) -> bool {
228+
let Some(allow) = self.extension_filter.as_ref() else {
229+
return true;
230+
};
231+
if path.is_dir() {
232+
return true;
233+
}
234+
let ext = path
235+
.extension()
236+
.and_then(|e| e.to_str())
237+
.map(|s| s.to_lowercase())
238+
.unwrap_or_default();
239+
allow.iter().any(|a| a.eq_ignore_ascii_case(&ext))
240+
}
241+
219242
/// Duplicate all selected files/folders. For a single file, enters rename mode on the copy.
220243
pub fn duplicate_selected(&mut self) {
221244
let selected: Vec<PathBuf> = self.selected_assets.iter().cloned().collect();

crates/renzora_asset_browser/src/tree.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,7 @@ fn render_folder_files(
623623
.filter_map(|e| e.ok())
624624
.filter(|e| e.file_type().map(|ft| ft.is_file()).unwrap_or(false))
625625
.filter(|e| !is_hidden(&e.path()))
626+
.filter(|e| state.passes_filter(&e.path()))
626627
.map(|e| e.path())
627628
.collect(),
628629
Err(_) => return,

crates/renzora_code_editor/src/lib.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
pub mod actions;
22
pub mod autocomplete;
33
pub mod highlight;
4+
pub mod outline;
5+
pub mod problems;
46
pub mod render;
7+
pub mod scripts_on_entity;
58
pub mod state;
69

710
pub use state::*;
@@ -167,10 +170,16 @@ fn sync_selection_scripts(
167170

168171
for entry in &sc.scripts {
169172
if let Some(ref rel_path) = entry.script_path {
170-
let full_path = project.path.join(rel_path);
171-
if full_path.exists() {
173+
// Project-relative path; same convention as
174+
// ScriptEngine::resolve_path.
175+
let resolved = if rel_path.is_absolute() {
176+
rel_path.clone()
177+
} else {
178+
project.path.join(rel_path)
179+
};
180+
if resolved.exists() {
172181
// open_file is idempotent — if already open it just switches tab
173-
state.open_file(full_path);
182+
state.open_file(resolved);
174183
}
175184
}
176185
}
@@ -199,10 +208,14 @@ impl Plugin for CodeEditorPlugin {
199208
sync_code_editor_bridge,
200209
sync_selection_scripts,
201210
consume_open_code_editor_file,
211+
sync_asset_filter_for_scripting,
202212
).run_if(in_state(SplashState::Editor)),
203213
);
204214

205215
app.register_panel(CodeEditorPanel::new(arc));
216+
app.register_panel(outline::OutlinePanel);
217+
app.register_panel(problems::ProblemsPanel);
218+
app.register_panel(scripts_on_entity::ScriptsOnEntityPanel);
206219
}
207220
}
208221

@@ -217,4 +230,27 @@ fn consume_open_code_editor_file(
217230
commands.remove_resource::<renzora::core::OpenCodeEditorFile>();
218231
}
219232

233+
/// While the Scripting workspace is active, restrict the asset browser to text
234+
/// formats (scripts, shaders, configs). Reset when leaving so other workspaces
235+
/// see the full asset list again.
236+
fn sync_asset_filter_for_scripting(
237+
layout_mgr: Res<renzora_editor_framework::LayoutManager>,
238+
mut filter: ResMut<renzora_editor_framework::AssetBrowserExtensionFilter>,
239+
) {
240+
let is_scripting = layout_mgr.active_name() == "Scripting";
241+
let desired: Option<Vec<String>> = if is_scripting {
242+
Some(
243+
["lua", "rhai", "wgsl", "glsl", "json", "ron", "toml", "txt", "md"]
244+
.iter()
245+
.map(|s| s.to_string())
246+
.collect(),
247+
)
248+
} else {
249+
None
250+
};
251+
if filter.0 != desired {
252+
filter.0 = desired;
253+
}
254+
}
255+
220256
renzora::add!(CodeEditorPlugin, Editor);

0 commit comments

Comments
 (0)