# Custom classes in the editor In SimVX a scene is Python source: a node is an instance of a class, a class lives in a `.py` file, and the editor's refactorings rewrite those files for you. This guide covers the editor actions for giving nodes classes of their own. It assumes you know the basics from the [editor tutorial](editor_tutorial.md). ## Convert a node to a class To give a built-in node (`Sprite2D`, `Node3D`, ...) its own behaviour, select it and click **Convert to Custom Class** next to the type name in the Properties panel, or right-click it in the scene tree and choose the same entry. The dialog asks for: - **A class name**, defaulting to the node's `name`. - **A template.** `Empty` writes a class with nothing in it; the others write what a node of that kind usually needs (the character templates add movement and a collider, `Timer` connects its `timeout`). Only templates for a class the node already descends from are offered, and the new class extends the node's own type. - **Where the class goes.** By default a new file, `//.py`, where `class_files_dir` is set under `[editor]` in `simvx.toml` (default `src`). An existing file gains the class beside what it holds. **Inline in parent scene file** puts the class at the top of the scene file instead. On accept the editor writes the class, changes the node's construction in the scene source to the new class (keeping its arguments and comments, and adding the import), saves the scene and reloads it. The node is now an instance of the new class, and the file opens in the code workspace. Once a node has a class of your own, the Properties header shows its module and file and an **Edit class file** button instead. Converting the **scene root** rebases the class the scene file defines onto the new class. The conversion refuses, writing nothing, when it would have to guess: | Refused when | What to do | |---|---| | the scene has never been saved | save it first | | the class has two bases, or a dotted base such as `simvx.core.Node3D` | edit the class by hand | | the node is built by a line a save does not rewrite (a factory call, a name bound elsewhere) | build that node with the class by hand | | the node is deeper than a save writes (a save writes the root and its own children) | write the class by hand | | the module name is one Python already finds (a class `Json` would become `json`, the standard library's) | pick another name | From the editor a node is converted once. To extend a class of your own with another, call `simvx.editor.convert_node_to_class` from code. ## Duplicate a node **Ctrl+D**, or **Duplicate** in the scene tree's context menu, adds another instance with the same arguments. **Duplicate...** opens a dialog with three choices: | Choice | Result | |---|---| | **New Instance** (default) | another `(...)` with the original's arguments | | **Subclass** | a new file holding `class (): pass`, and an instance of it | | **Detached Copy** | a new class with the original's body copied, extending the original's **base** rather than the original; for a built-in node it is the same as Subclass | New files go in `class_files_dir`. ## Rename a class Right-click an instance of your class and choose **Rename Class...**. Every file in the project is rewritten in one step, and a failure rolls all of them back: - the class definition; - every `from import `, keeping an alias; - every base-class reference, instantiation, `isinstance` check and annotation. Tick **Also rename file to match** to move the defining file (or package folder) to the new snake_case name, with importers' module paths updated. ## Split and merge class files In the file browser, right-click a `.py` file holding several classes and choose **Extract classes to folder**. The file becomes a package of the same name with one class per file and an `__init__.py` that re-exports them, so existing imports keep working. It refuses a file with fewer than two classes, one with top-level statements other than classes and imports, or one whose target folder already exists. **Inline folder to file**, on a folder with an `__init__.py`, is the inverse: it concatenates the classes back into one file. It refuses module-level side effects, conditional imports and free functions.