Editor: "New Script" button on the Script inspector
Stage-10 editor-UX follow-up. The Script component's inspector section now ends with a name field + "➕ New Script" button (Enter in the field also submits): it writes a fresh .rhai from the tested template into assets/scripts/ via create_script_file (sanitized stem, collisions suffixed), registers it in the asset database, saves the manifest, and assigns it to the component's `source` through the normal edit path — so the assignment is one undoable SetFieldCmd. Disabled with a hint while no project is open; failures land in the Console via the log. GUI piece — needs an eye-check before promotion to main. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -525,6 +525,8 @@ pub struct Shell {
|
|||||||
open_project_path: String,
|
open_project_path: String,
|
||||||
/// Text entered in the "Groups" editor's "add group" field.
|
/// Text entered in the "Groups" editor's "add group" field.
|
||||||
new_group_name: String,
|
new_group_name: String,
|
||||||
|
/// Text entered in the Script inspector's "New Script" name field.
|
||||||
|
new_script_name: String,
|
||||||
/// The Console panel's command-input buffer (the terminal prompt).
|
/// The Console panel's command-input buffer (the terminal prompt).
|
||||||
terminal_input: String,
|
terminal_input: String,
|
||||||
|
|
||||||
@@ -624,6 +626,7 @@ impl Shell {
|
|||||||
new_project_name: String::new(),
|
new_project_name: String::new(),
|
||||||
open_project_path: String::new(),
|
open_project_path: String::new(),
|
||||||
new_group_name: String::new(),
|
new_group_name: String::new(),
|
||||||
|
new_script_name: String::new(),
|
||||||
terminal_input: String::new(),
|
terminal_input: String::new(),
|
||||||
quit_requested: false,
|
quit_requested: false,
|
||||||
viewport_rect_px: None,
|
viewport_rect_px: None,
|
||||||
@@ -1125,6 +1128,7 @@ impl Shell {
|
|||||||
extensions: &mut self.extensions,
|
extensions: &mut self.extensions,
|
||||||
pending: &mut self.pending,
|
pending: &mut self.pending,
|
||||||
rename_buf: &mut self.rename_buf,
|
rename_buf: &mut self.rename_buf,
|
||||||
|
new_script_name: &mut self.new_script_name,
|
||||||
terminal_input: &mut self.terminal_input,
|
terminal_input: &mut self.terminal_input,
|
||||||
rot_euler: &mut self.rot_euler,
|
rot_euler: &mut self.rot_euler,
|
||||||
euler_for: &mut self.euler_for,
|
euler_for: &mut self.euler_for,
|
||||||
@@ -2154,6 +2158,9 @@ struct ShellTabViewer<'a> {
|
|||||||
extensions: &'a mut EditorExtensions,
|
extensions: &'a mut EditorExtensions,
|
||||||
pending: &'a mut Vec<PendingAction>,
|
pending: &'a mut Vec<PendingAction>,
|
||||||
rename_buf: &'a mut String,
|
rename_buf: &'a mut String,
|
||||||
|
/// The Script inspector's "New Script" name field. Borrowed from
|
||||||
|
/// [`Shell::new_script_name`].
|
||||||
|
new_script_name: &'a mut String,
|
||||||
/// The Console command-input buffer (the terminal prompt).
|
/// The Console command-input buffer (the terminal prompt).
|
||||||
terminal_input: &'a mut String,
|
terminal_input: &'a mut String,
|
||||||
rot_euler: &'a mut Vec3,
|
rot_euler: &'a mut Vec3,
|
||||||
@@ -3364,6 +3371,14 @@ impl<'a> ShellTabViewer<'a> {
|
|||||||
edits.push((comp.name, row.info.name, new_ron));
|
edits.push((comp.name, row.info.name, new_ron));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Stage-10 UX: author a script without leaving the editor. The
|
||||||
|
// assignment goes through `edits` → SetFieldCmd like any field
|
||||||
|
// change, so it is undoable (the file itself stays — harmless).
|
||||||
|
if comp.name == "Script" {
|
||||||
|
if let Some(ron) = self.new_script_row(ui) {
|
||||||
|
edits.push((comp.name, "source", ron));
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3409,6 +3424,59 @@ impl<'a> ShellTabViewer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The "New Script" row at the bottom of a `Script` component's section:
|
||||||
|
/// a name field + create button that writes a `.rhai` template into
|
||||||
|
/// `assets/scripts/`, registers it in the asset database, and returns the
|
||||||
|
/// RON for the component's `source` field — the caller routes it through
|
||||||
|
/// the normal edit path so the assignment is undoable (undo detaches the
|
||||||
|
/// script; the created file stays, which is harmless). Disabled until a
|
||||||
|
/// project is open. Errors go to the log, i.e. the Console panel.
|
||||||
|
fn new_script_row(&mut self, ui: &mut egui::Ui) -> Option<String> {
|
||||||
|
let mut created = None;
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let open = self.state.asset_db.is_some();
|
||||||
|
ui.add_enabled_ui(open, |ui| {
|
||||||
|
let name = ui.add(
|
||||||
|
egui::TextEdit::singleline(self.new_script_name)
|
||||||
|
.hint_text("new_script")
|
||||||
|
.desired_width(120.0),
|
||||||
|
);
|
||||||
|
let submitted = name.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter));
|
||||||
|
let clicked = ui
|
||||||
|
.small_button("➕ New Script")
|
||||||
|
.on_hover_text(
|
||||||
|
"Create a .rhai file from the template in assets/scripts/ \
|
||||||
|
and assign it to this component",
|
||||||
|
)
|
||||||
|
.clicked();
|
||||||
|
if clicked || submitted {
|
||||||
|
let db = self
|
||||||
|
.state
|
||||||
|
.asset_db
|
||||||
|
.as_mut()
|
||||||
|
.expect("row is enabled only with a project open");
|
||||||
|
match crate::assets::create_script_file(&db.assets_dir(), self.new_script_name)
|
||||||
|
{
|
||||||
|
Ok(rel) => {
|
||||||
|
let uid = db.register(&rel);
|
||||||
|
if let Err(err) = db.save() {
|
||||||
|
log::warn!("could not write asset manifest: {err}");
|
||||||
|
}
|
||||||
|
log::info!("created {rel}");
|
||||||
|
self.new_script_name.clear();
|
||||||
|
created = ron::to_string(&Some(uid)).ok();
|
||||||
|
}
|
||||||
|
Err(err) => log::warn!("could not create script: {err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if !open {
|
||||||
|
ui.label(egui::RichText::new("(open a project to create scripts)").weak());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
created
|
||||||
|
}
|
||||||
|
|
||||||
/// Renders one reflected field as a typed widget chosen from its
|
/// Renders one reflected field as a typed widget chosen from its
|
||||||
/// `type_name`, returning the field's new RON if the user changed it.
|
/// `type_name`, returning the field's new RON if the user changed it.
|
||||||
/// Unknown types fall back to an editable RON text box, so the inspector is
|
/// Unknown types fall back to an editable RON text box, so the inspector is
|
||||||
|
|||||||
Reference in New Issue
Block a user