Prechádzať zdrojové kódy

tools: reduce ProjectStore usage

Daniele Bartolini 7 mesiacov pred
rodič
commit
97dfd1a160

+ 1 - 1
tools/core/database.vala

@@ -440,7 +440,7 @@ public class Database
 	// Data
 	private Gee.HashMap<Guid?, Gee.HashMap<string, Value?>> _data;
 	private UndoRedo? _undo_redo;
-	private Project _project;
+	public Project _project;
 	// The number of changes to the database since the last successful state
 	// synchronization (load(), save() etc.). If it is less than 0, the changes
 	// came from undo(), otherwise they came from redo() or from regular calls to

+ 7 - 2
tools/level_editor/level_editor.vala

@@ -844,7 +844,7 @@ public class LevelEditorApplication : Gtk.Application
 		_project_browser = new ProjectBrowser(_project_store, _thumbnail_cache);
 		_level_treeview = new LevelTreeView(_database, _level);
 		_level_layers_treeview = new LevelLayersTreeView(_database, _level);
-		_properties_view = new PropertiesView(_database, _project_store);
+		_properties_view = new PropertiesView(_database);
 		_level.selection_changed.connect(_properties_view.on_selection_changed);
 
 		_project_stack = new Gtk.Stack();
@@ -2377,7 +2377,7 @@ public class LevelEditorApplication : Gtk.Application
 
 		_project.import(destination_dir
 			, on_import_result
-			, _project_store
+			, _database
 			, this.active_window
 			);
 	}
@@ -4566,6 +4566,11 @@ public class LevelEditorApplication : Gtk.Application
 			// No-op.
 		}
 	}
+
+	public SelectResourceDialog new_select_resource_dialog(string resource_type)
+	{
+		return new SelectResourceDialog(resource_type, _project_store, this.active_window);
+	}
 }
 
 // Global paths

+ 9 - 9
tools/level_editor/project.vala

@@ -19,7 +19,7 @@ public class Project
 	public const string LEVEL_EDITOR_TEST_NAME = "_level_editor_test";
 
 	public delegate void ImporterDelegate(Import import_result
-		, ProjectStore project_store
+		, Database database
 		, string destination_dir
 		, SList<string> filenames
 		, Gtk.Window? parent_window
@@ -551,13 +551,13 @@ public class Project
 	}
 
 	public static void import_all_extensions(Import import_result
-		, ProjectStore project_store
+		, Database database
 		, string destination_dir
 		, SList<string> filenames
 		, Gtk.Window? parent_window
 		)
 	{
-		Project project = project_store._project;
+		Project project = database._project;
 
 		Gee.ArrayList<string> paths = new Gee.ArrayList<string>();
 		foreach (var item in filenames)
@@ -601,7 +601,7 @@ public class Project
 			foreach (var item in importables)
 				importables_list.append(item);
 
-			importer.delegate(import_result, project_store, destination_dir, importables_list, parent_window);
+			importer.delegate(import_result, database, destination_dir, importables_list, parent_window);
 		}
 	}
 
@@ -691,7 +691,7 @@ public class Project
 		, GLib.SList<string> filenames
 		, Import import_result
 		, ImporterDelegate? importer
-		, ProjectStore project_store
+		, Database database
 		, Gtk.Window? parent_window = null
 		)
 	{
@@ -700,7 +700,7 @@ public class Project
 			_filenames.append(s);
 
 		if (destination_dir != null) {
-			importer(import_result, project_store, this.absolute_path(destination_dir), filenames, parent_window);
+			importer(import_result, database, this.absolute_path(destination_dir), filenames, parent_window);
 		} else {
 			Gtk.FileChooserDialog fcd = new Gtk.FileChooserDialog("Select destination folder..."
 				, parent_window
@@ -718,7 +718,7 @@ public class Project
 
 			fcd.response.connect((response_id) => {
 					if (response_id == Gtk.ResponseType.ACCEPT)
-						importer(import_result, project_store, fcd.get_file().get_path(), _filenames, parent_window);
+						importer(import_result, database, fcd.get_file().get_path(), _filenames, parent_window);
 					fcd.destroy();
 				});
 
@@ -726,7 +726,7 @@ public class Project
 		}
 	}
 
-	public void import(string? destination_dir, Import import_result, ProjectStore project_store, Gtk.Window? parent_window = null)
+	public void import(string? destination_dir, Import import_result, Database database, Gtk.Window? parent_window = null)
 	{
 		Gtk.FileChooserDialog fcd = new Gtk.FileChooserDialog("Import..."
 			, parent_window
@@ -761,7 +761,7 @@ public class Project
 					if (importer == null)
 						importer = _all_extensions_importer_data.delegate;
 
-					import_filenames(destination_dir, filenames, import_result, importer, project_store, parent_window);
+					import_filenames(destination_dir, filenames, import_result, importer, database, parent_window);
 				}
 				fcd.destroy();
 			});

+ 30 - 30
tools/level_editor/properties_view.vala

@@ -38,9 +38,9 @@ public class TransformPropertyGrid : PropertyGrid
 
 public class MeshRendererPropertyGrid : PropertyGrid
 {
-	public MeshRendererPropertyGrid(Database db, ProjectStore store)
+	public MeshRendererPropertyGrid(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		PropertyDefinition[] properties =
 		{
@@ -102,9 +102,9 @@ public void node_name_enum_callback(InputField enum_property, InputEnum combo, P
 
 public class SpriteRendererPropertyGrid : PropertyGrid
 {
-	public SpriteRendererPropertyGrid(Database db, ProjectStore store)
+	public SpriteRendererPropertyGrid(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		PropertyDefinition[] properties =
 		{
@@ -255,9 +255,9 @@ public class CameraPropertyGrid : PropertyGrid
 
 public class ColliderPropertyGrid : PropertyGrid
 {
-	public ColliderPropertyGrid(Database db, ProjectStore store)
+	public ColliderPropertyGrid(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		PropertyDefinition[] properties =
 		{
@@ -346,9 +346,9 @@ public class ColliderPropertyGrid : PropertyGrid
 
 public class ActorPropertyGrid : PropertyGrid
 {
-	public ActorPropertyGrid(Database db, ProjectStore store)
+	public ActorPropertyGrid(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		PropertyDefinition[] properties =
 		{
@@ -487,9 +487,9 @@ public class ActorPropertyGrid : PropertyGrid
 
 public class ScriptPropertyGrid : PropertyGrid
 {
-	public ScriptPropertyGrid(Database db, ProjectStore store)
+	public ScriptPropertyGrid(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		PropertyDefinition[] properties =
 		{
@@ -509,9 +509,9 @@ public class ScriptPropertyGrid : PropertyGrid
 
 public class AnimationStateMachine : PropertyGrid
 {
-	public AnimationStateMachine(Database db, ProjectStore store)
+	public AnimationStateMachine(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		PropertyDefinition[] properties =
 		{
@@ -549,12 +549,12 @@ public class UnitView : PropertyGrid
 		return menu;
 	}
 
-	public UnitView(Database db, ProjectStore store)
+	public UnitView(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		// Widgets
-		_prefab = new InputResource(store, "unit");
+		_prefab = new InputResource("unit", db);
 		_prefab._selector.sensitive = false;
 
 		// List of component types.
@@ -608,9 +608,9 @@ public class UnitView : PropertyGrid
 
 public class SoundSourcePropertyGrid : PropertyGrid
 {
-	public SoundSourcePropertyGrid(Database db, ProjectStore store)
+	public SoundSourcePropertyGrid(Database db)
 	{
-		base(db, store);
+		base(db);
 
 		PropertyDefinition[] properties =
 		{
@@ -689,7 +689,7 @@ public class PropertiesView : Gtk.Stack
 	[CCode (has_target = false)]
 	public delegate GLib.Menu ContextMenu(string object_type);
 
-	public PropertiesView(Database db, ProjectStore store)
+	public PropertiesView(Database db)
 	{
 		// Data
 		_db = db;
@@ -710,17 +710,17 @@ public class PropertiesView : Gtk.Stack
 			;
 
 		// Unit
-		register_object_type("Unit",                    "name",                              0, new UnitView(_db, store));
-		register_object_type("Transform",               OBJECT_TYPE_TRANSFORM,               0, new TransformPropertyGrid(_db),             UnitView.component_menu);
-		register_object_type("Light",                   OBJECT_TYPE_LIGHT,                   1, new LightPropertyGrid(_db),                 UnitView.component_menu);
-		register_object_type("Camera",                  OBJECT_TYPE_CAMERA,                  2, new CameraPropertyGrid(_db),                UnitView.component_menu);
-		register_object_type("Mesh Renderer",           OBJECT_TYPE_MESH_RENDERER,           3, new MeshRendererPropertyGrid(_db, store),   UnitView.component_menu);
-		register_object_type("Sprite Renderer",         OBJECT_TYPE_SPRITE_RENDERER,         3, new SpriteRendererPropertyGrid(_db, store), UnitView.component_menu);
-		register_object_type("Collider",                OBJECT_TYPE_COLLIDER,                3, new ColliderPropertyGrid(_db, store),       UnitView.component_menu);
-		register_object_type("Actor",                   OBJECT_TYPE_ACTOR,                   3, new ActorPropertyGrid(_db, store),          UnitView.component_menu);
-		register_object_type("Script",                  OBJECT_TYPE_SCRIPT,                  3, new ScriptPropertyGrid(_db, store),         UnitView.component_menu);
-		register_object_type("Animation State Machine", OBJECT_TYPE_ANIMATION_STATE_MACHINE, 3, new AnimationStateMachine(_db, store),      UnitView.component_menu);
-		register_object_type("Sound",                   OBJECT_TYPE_SOUND_SOURCE,            0, new SoundSourcePropertyGrid(_db, store));
+		register_object_type("Unit",                    "name",                              0, new UnitView(_db));
+		register_object_type("Transform",               OBJECT_TYPE_TRANSFORM,               0, new TransformPropertyGrid(_db),      UnitView.component_menu);
+		register_object_type("Light",                   OBJECT_TYPE_LIGHT,                   1, new LightPropertyGrid(_db),          UnitView.component_menu);
+		register_object_type("Camera",                  OBJECT_TYPE_CAMERA,                  2, new CameraPropertyGrid(_db),         UnitView.component_menu);
+		register_object_type("Mesh Renderer",           OBJECT_TYPE_MESH_RENDERER,           3, new MeshRendererPropertyGrid(_db),   UnitView.component_menu);
+		register_object_type("Sprite Renderer",         OBJECT_TYPE_SPRITE_RENDERER,         3, new SpriteRendererPropertyGrid(_db), UnitView.component_menu);
+		register_object_type("Collider",                OBJECT_TYPE_COLLIDER,                3, new ColliderPropertyGrid(_db),       UnitView.component_menu);
+		register_object_type("Actor",                   OBJECT_TYPE_ACTOR,                   3, new ActorPropertyGrid(_db),          UnitView.component_menu);
+		register_object_type("Script",                  OBJECT_TYPE_SCRIPT,                  3, new ScriptPropertyGrid(_db),         UnitView.component_menu);
+		register_object_type("Animation State Machine", OBJECT_TYPE_ANIMATION_STATE_MACHINE, 3, new AnimationStateMachine(_db),      UnitView.component_menu);
+		register_object_type("Sound",                   OBJECT_TYPE_SOUND_SOURCE,            0, new SoundSourcePropertyGrid(_db));
 
 		_nothing_to_show = new Gtk.Label("Select an object to start editing");
 		_unknown_object_type = new Gtk.Label("Unknown object type");
@@ -737,7 +737,7 @@ public class PropertiesView : Gtk.Stack
 
 		this.get_style_context().add_class("properties-view");
 
-		store._project.project_reset.connect(on_project_reset);
+		db._project.project_reset.connect(on_project_reset);
 	}
 
 	private void register_object_type(string label, string object_type, int position, PropertyGrid cv, ContextMenu? context_menu = null)

+ 4 - 6
tools/level_editor/property_grid.vala

@@ -12,11 +12,10 @@ public class PropertyGrid : Gtk.Grid
 	public Guid _component_id;
 	public int _rows;
 
-	public ProjectStore _store;
 	public Gee.HashMap<string, InputField> _widgets;
 	public Gee.HashMap<InputField, PropertyDefinition?> _definitions;
 
-	public PropertyGrid(Database? db = null, ProjectStore? store = null)
+	public PropertyGrid(Database? db = null)
 	{
 		this.row_spacing = 4;
 		this.row_homogeneous = true;
@@ -27,7 +26,6 @@ public class PropertyGrid : Gtk.Grid
 		_id = GUID_ZERO;
 		_component_id = GUID_ZERO;
 		_rows = 0;
-		_store = store;
 		_widgets = new Gee.HashMap<string, InputField>();
 		_definitions = new Gee.HashMap<InputField, PropertyDefinition?>();
 	}
@@ -106,7 +104,7 @@ public class PropertyGrid : Gtk.Grid
 				if (def.editor == PropertyEditorType.ENUM)
 					p = new InputEnum((string)def.deffault, def.enum_labels, def.enum_values);
 				else if (def.editor == PropertyEditorType.RESOURCE)
-					p = new InputResource(_store, def.resource_type);
+					p = new InputResource(def.resource_type, _db);
 				else
 					p = new InputString();
 				break;
@@ -249,9 +247,9 @@ public class PropertyGrid : Gtk.Grid
 
 			if (other_def.enum_property == def.name) {
 				if (other_def.enum_callback != null)
-					other_def.enum_callback(p, (InputEnum)_widgets[other_def.name], _store._project);
+					other_def.enum_callback(p, (InputEnum)_widgets[other_def.name], _db._project);
 				if (other_def.resource_callback != null)
-					other_def.resource_callback(p, (InputResource)_widgets[other_def.name], _store._project);
+					other_def.resource_callback(p, (InputResource)_widgets[other_def.name], _db._project);
 			}
 		}
 

+ 1 - 1
tools/level_editor/texture_settings_dialog.vala

@@ -74,7 +74,7 @@ public class TextureSettingsDialog : Gtk.Window
 		_texture_path = "";
 
 		// Input grid.
-		_texture_name = new InputResource(_store, "texture");
+		_texture_name = new InputResource("texture", database);
 		_texture_name.value_changed.connect(on_texture_resource_value_changed);
 
 		_source = new InputString();

+ 4 - 4
tools/resource/font_resource.vala

@@ -141,11 +141,11 @@ public class FontImportDialog : Gtk.Window
 		argb32.write_to_png(path);
 	}
 
-	public FontImportDialog(ProjectStore store, string destination_dir, GLib.SList<string> filenames, Import import_result)
+	public FontImportDialog(Database database, string destination_dir, GLib.SList<string> filenames, Import import_result)
 	{
 		this.set_icon_name(CROWN_EDITOR_ICON_NAME);
 
-		_project = store._project;
+		_project = database._project;
 		_destination_dir = destination_dir;
 		_filenames = new GLib.SList<string>();
 		foreach (var f in filenames)
@@ -418,9 +418,9 @@ public class FontResource
 		return ImportResult.SUCCESS;
 	}
 
-	public static void import(Import import_result, ProjectStore store, string destination_dir, SList<string> filenames, Gtk.Window? parent_window)
+	public static void import(Import import_result, Database database, string destination_dir, SList<string> filenames, Gtk.Window? parent_window)
 	{
-		FontImportDialog dlg = new FontImportDialog(store, destination_dir, filenames, import_result);
+		FontImportDialog dlg = new FontImportDialog(database, destination_dir, filenames, import_result);
 		dlg.set_transient_for(parent_window);
 		dlg.set_modal(true);
 		dlg.show_all();

+ 5 - 5
tools/resource/mesh_resource.vala

@@ -87,9 +87,9 @@ namespace MeshResource
 		}
 	}
 
-	public static ImportResult do_import(ProjectStore project_store, string destination_dir, SList<string> filenames)
+	public static ImportResult do_import(Database database, string destination_dir, SList<string> filenames)
 	{
-		Project project = project_store._project;
+		Project project = database._project;
 
 		foreach (unowned string filename_i in filenames) {
 			GLib.File file_src = File.new_for_path(filename_i);
@@ -171,7 +171,7 @@ namespace MeshResource
 		return ImportResult.SUCCESS;
 	}
 
-	public static void import(Import import_result, ProjectStore project_store, string destination_dir, SList<string> filenames, Gtk.Window? parent_window)
+	public static void import(Import import_result, Database database, string destination_dir, SList<string> filenames, Gtk.Window? parent_window)
 	{
 		SList<string> fbx_filenames = new SList<string>();
 		SList<string> mesh_filenames = new SList<string>();
@@ -187,9 +187,9 @@ namespace MeshResource
 
 		ImportResult res = ImportResult.SUCCESS;
 		if (mesh_filenames != null)
-			res = MeshResource.do_import(project_store, destination_dir, mesh_filenames);
+			res = MeshResource.do_import(database, destination_dir, mesh_filenames);
 		if (res == ImportResult.SUCCESS && fbx_filenames != null)
-			FBXImporter.import(import_result, project_store, destination_dir, fbx_filenames, parent_window);
+			FBXImporter.import(import_result, database, destination_dir, fbx_filenames, parent_window);
 		else
 			import_result(res);
 	}

+ 7 - 7
tools/resource/mesh_resource_fbx.vala

@@ -80,7 +80,7 @@ public class FBXImportOptions
 	public InputBool import_clips;
 	public InputBool create_animations_folder;
 
-	public FBXImportOptions(ProjectStore project_store)
+	public FBXImportOptions(Database db)
 	{
 		import_units = new InputBool();
 		import_units.value = true;
@@ -105,7 +105,7 @@ public class FBXImportOptions
 		new_skeleton = new InputBool();
 		new_skeleton.value = true;
 		new_skeleton.value_changed.connect(on_new_skeleton_changed);
-		target_skeleton = new InputResource(project_store, OBJECT_TYPE_MESH_SKELETON);
+		target_skeleton = new InputResource(OBJECT_TYPE_MESH_SKELETON, db);
 		target_skeleton.sensitive = false;
 		import_clips = new InputBool();
 		import_clips.value = true;
@@ -232,9 +232,9 @@ public class FBXImportDialog : Gtk.Window
 	public Gtk.Button _cancel;
 	public Gtk.HeaderBar _header_bar;
 
-	public FBXImportDialog(ProjectStore project_store, string destination_dir, GLib.SList<string> filenames, Import import_result)
+	public FBXImportDialog(Database database, string destination_dir, GLib.SList<string> filenames, Import import_result)
 	{
-		_project = project_store._project;
+		_project = database._project;
 		_destination_dir = destination_dir;
 		_filenames = new Gee.ArrayList<string>();
 		foreach (var f in filenames)
@@ -243,7 +243,7 @@ public class FBXImportDialog : Gtk.Window
 
 		_general_set = new PropertyGridSet();
 
-		_options = new FBXImportOptions(project_store);
+		_options = new FBXImportOptions(database);
 		GLib.File file_dst;
 		string resource_path;
 		get_destination_file(out file_dst, destination_dir, File.new_for_path(_filenames[0]));
@@ -875,9 +875,9 @@ public class FBXImporter
 		return ImportResult.SUCCESS;
 	}
 
-	public static void import(Import import_result, ProjectStore project_store, string destination_dir, GLib.SList<string> filenames, Gtk.Window? parent_window)
+	public static void import(Import import_result, Database database, string destination_dir, GLib.SList<string> filenames, Gtk.Window? parent_window)
 	{
-		FBXImportDialog dialog = new FBXImportDialog(project_store, destination_dir, filenames, import_result);
+		FBXImportDialog dialog = new FBXImportDialog(database, destination_dir, filenames, import_result);
 		dialog.set_transient_for(parent_window);
 		dialog.set_modal(true);
 		dialog.show_all();

+ 2 - 2
tools/resource/sound_resource.vala

@@ -27,9 +27,9 @@ public struct SoundResource
 		return _db.save(project.absolute_path(resource_name) + "." + OBJECT_TYPE_SOUND, _id);
 	}
 
-	public static void import(Import import_result, ProjectStore project_store, string destination_dir, SList<string> filenames)
+	public static void import(Import import_result, Database database, string destination_dir, SList<string> filenames)
 	{
-		Project project = project_store._project;
+		Project project = database._project;
 
 		foreach (unowned string filename_i in filenames) {
 			GLib.File file_src = File.new_for_path(filename_i);

+ 4 - 4
tools/resource/sprite_resource.vala

@@ -148,11 +148,11 @@ public class SpriteImportDialog : Gtk.Window
 	public Gtk.Button _cancel;
 	public Gtk.HeaderBar _header_bar;
 
-	public SpriteImportDialog(ProjectStore project_store, string destination_dir, GLib.SList<string> filenames, Import import_result)
+	public SpriteImportDialog(Database database, string destination_dir, GLib.SList<string> filenames, Import import_result)
 	{
 		this.set_icon_name(CROWN_EDITOR_ICON_NAME);
 
-		_project = project_store._project;
+		_project = database._project;
 		_destination_dir = destination_dir;
 		_filenames = new GLib.SList<string>();
 		foreach (var f in filenames)
@@ -911,9 +911,9 @@ public class SpriteResource
 		return ImportResult.SUCCESS;
 	}
 
-	public static void import(Import import_result, ProjectStore project_store, string destination_dir, SList<string> filenames, Gtk.Window? parent_window)
+	public static void import(Import import_result, Database database, string destination_dir, SList<string> filenames, Gtk.Window? parent_window)
 	{
-		SpriteImportDialog dlg = new SpriteImportDialog(project_store, destination_dir, filenames, import_result);
+		SpriteImportDialog dlg = new SpriteImportDialog(database, destination_dir, filenames, import_result);
 		dlg.set_transient_for(parent_window);
 		dlg.set_modal(true);
 		dlg.show_all();

+ 2 - 2
tools/resource/texture_resource.vala

@@ -96,9 +96,9 @@ public struct TextureResource
 		return _db.save(project.absolute_path(resource_name) + "." + OBJECT_TYPE_TEXTURE, _id);
 	}
 
-	public static void import(Import import_result, ProjectStore project_store, string destination_dir, SList<string> filenames)
+	public static void import(Import import_result, Database database, string destination_dir, SList<string> filenames)
 	{
-		Project project = project_store._project;
+		Project project = database._project;
 
 		foreach (unowned string filename_i in filenames) {
 			GLib.File file_src = File.new_for_path(filename_i);

+ 13 - 48
tools/widgets/input_resource.vala

@@ -14,10 +14,7 @@ public class InputResource : InputField, Gtk.Box
 	public InputString _name;
 	public Gtk.Button _selector;
 	public Gtk.Button _revealer;
-	public ProjectStore _project_store;
-	public ResourceChooser _chooser;
-	public Gtk.Dialog _dialog;
-	public Gtk.EventControllerKey _dialog_controller_key;
+	public SelectResourceDialog _dialog;
 
 	public void set_inconsistent(bool inconsistent)
 	{
@@ -50,7 +47,7 @@ public class InputResource : InputField, Gtk.Box
 		}
 	}
 
-	public InputResource(ProjectStore store, string type)
+	public InputResource(string type, Database db)
 	{
 		Object(orientation: Gtk.Orientation.HORIZONTAL, spacing: 0);
 
@@ -72,64 +69,32 @@ public class InputResource : InputField, Gtk.Box
 		_selector.clicked.connect(on_selector_clicked);
 		this.pack_end(_selector, false);
 
-		_project_store = store;
-
-		_chooser = new ResourceChooser(null, _project_store);
-		_chooser.set_type_filter(type_filter);
-
-		_project_store._project.file_added.connect(on_file_added_or_changed);
-		_project_store._project.file_changed.connect(on_file_added_or_changed);
-		_project_store._project.file_removed.connect(on_file_removed);
-	}
-
-	~InputResource()
-	{
-		// Prevents a crash when the parent window gets destroyed.
-		_chooser.set_type_filter((type, name) => { return false; });
+		db._project.file_added.connect(on_file_added_or_changed);
+		db._project.file_changed.connect(on_file_added_or_changed);
+		db._project.file_removed.connect(on_file_removed);
 	}
 
 	private void on_selector_clicked()
 	{
 		if (_dialog == null) {
-			_dialog = new Gtk.Dialog.with_buttons("Select a %s".printf(_type)
-				, (Gtk.Window)this.get_toplevel()
-				, Gtk.DialogFlags.MODAL
-				, null
-				);
-			_dialog.delete_event.connect(_dialog.hide_on_delete);
-
-			_chooser.resource_selected.connect(() => {
-					_name.value = _chooser._name;
-					_dialog.hide();
-				});
-
-			_dialog_controller_key = new Gtk.EventControllerKey(_dialog);
-			_dialog_controller_key.set_propagation_phase(Gtk.PropagationPhase.CAPTURE);
-			_dialog_controller_key.key_pressed.connect((keyval) => {
-					if (keyval == Gdk.Key.Escape) {
-						_dialog.hide();
-						return Gdk.EVENT_STOP;
-					}
-
-					return Gdk.EVENT_PROPAGATE;
-				});
-			_dialog.skip_taskbar_hint = true;
-			_dialog.get_content_area().pack_start(_chooser, true, true, 0);
+			_dialog = ((LevelEditorApplication)GLib.Application.get_default()).new_select_resource_dialog(_type);
+			_dialog.resource_selected.connect(on_select_resource_dialog_resource_selected);
 		}
 
 		_dialog.show_all();
 		_dialog.present();
 	}
 
-	private void on_revealer_clicked()
+	private void on_select_resource_dialog_resource_selected(string type, string name)
 	{
-		var tuple = new GLib.Variant.tuple({_type, _name.value});
-		GLib.Application.get_default().activate_action("reveal-resource", tuple);
+		_name.value = name;
+		_dialog.hide();
 	}
 
-	private bool type_filter(string type, string name)
+	private void on_revealer_clicked()
 	{
-		return _type == type;
+		var tuple = new GLib.Variant.tuple({_type, _name.value});
+		GLib.Application.get_default().activate_action("reveal-resource", tuple);
 	}
 
 	private void on_name_value_changed()

+ 68 - 0
tools/widgets/select_resource_dialog.vala

@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012-2025 Daniele Bartolini et al.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+namespace Crown
+{
+public class SelectResourceDialog : Gtk.Window
+{
+	public string _resource_type;
+	public Gtk.EventControllerKey _controller_key;
+	public ResourceChooser _chooser;
+	public Gtk.HeaderBar _header_bar;
+
+	public signal void resource_selected(string type, string name);
+
+	public SelectResourceDialog(string resource_type, ProjectStore project_store, Gtk.Window? parent)
+	{
+		_resource_type = resource_type;
+
+		this.set_icon_name(CROWN_EDITOR_ICON_NAME);
+
+		if (parent != null) {
+			this.set_transient_for(parent);
+			this.set_modal(true);
+		}
+		this.delete_event.connect(on_close);
+
+		_controller_key = new Gtk.EventControllerKey(this);
+		_controller_key.set_propagation_phase(Gtk.PropagationPhase.CAPTURE);
+		_controller_key.key_pressed.connect((keyval) => {
+				if (keyval == Gdk.Key.Escape) {
+					this.close();
+					return Gdk.EVENT_STOP;
+				}
+
+				return Gdk.EVENT_PROPAGATE;
+			});
+
+		_header_bar = new Gtk.HeaderBar();
+		_header_bar.title = "Select a %s".printf(resource_type);
+		_header_bar.show_close_button = true;
+		this.set_titlebar(_header_bar);
+
+		_chooser = new ResourceChooser(null, project_store);
+		_chooser.set_type_filter(on_resource_chooser_filter);
+		_chooser.resource_selected.connect(on_resource_chooser_resource_selected);
+		this.add(_chooser);
+	}
+
+	private bool on_close()
+	{
+		this.hide();
+		return Gdk.EVENT_STOP;
+	}
+
+	private bool on_resource_chooser_filter(string type, string name)
+	{
+		return _resource_type == type;
+	}
+
+	private void on_resource_chooser_resource_selected(string type, string name)
+	{
+		resource_selected(type, name);
+	}
+}
+
+} /* namespace Crown */