Ver código fonte

tools: add ability to modify Unit's components

Daniele Bartolini 6 anos atrás
pai
commit
df44798248

+ 18 - 8
tools/level_editor/level_editor.vala

@@ -58,6 +58,7 @@ namespace Crown
 
 		// Level data
 		private Project _project;
+		private ProjectStore _project_store;
 		private Database _database;
 		private Level _level;
 		private string _level_filename;
@@ -71,6 +72,8 @@ namespace Crown
 		private PropertiesView _properties_view;
 		private PreferencesDialog _preferences_dialog;
 		private ResourceBrowser _resource_browser;
+		private ResourceBrowser _resource_selection;
+		private Gtk.Popover _resource_popover;
 
 		private Slide _engine_view_slide;
 		private Slide _level_tree_view_slide;
@@ -238,10 +241,17 @@ namespace Crown
 			_data_compiler = new DataCompiler(_compiler);
 
 			// Widgets
+			_project_store = new ProjectStore(_project);
+
+			_resource_browser = new ResourceBrowser(_project, _project_store, true);
+			_resource_browser.resource_selected.connect(on_resource_browser_resource_selected);
+
+			_resource_selection = new ResourceBrowser(_project, _project_store, false);
+
 			_console_view = new ConsoleView(_engine, _project);
 			_level_treeview = new LevelTreeView(_database, _level);
 			_level_layers_treeview = new LevelLayersTreeView(_database, _level);
-			_properties_view = new PropertiesView(_level);
+			_properties_view = new PropertiesView(_level, _project_store);
 
 			_engine_view_slide = new Slide();
 			_level_tree_view_slide = new Slide();
@@ -276,6 +286,12 @@ namespace Crown
 			_toolbar.set_icon_size(Gtk.IconSize.SMALL_TOOLBAR);
 			_toolbar.set_style(Gtk.ToolbarStyle.ICONS);
 
+			_resource_popover = new Gtk.Popover(_toolbar);
+			_resource_popover.delete_event.connect(() => { _resource_popover.hide(); return true; });
+			_resource_popover.modal = true;
+			_resource_browser.resource_selected.connect(() => { _resource_popover.hide(); });
+			_resource_popover.add(_resource_browser);
+
 			_pane_left = new Gtk.Paned(Gtk.Orientation.VERTICAL);
 			_pane_left.pack1(_engine_view_slide, true, true);
 			_pane_left.pack2(_console_view, true, true);
@@ -307,12 +323,6 @@ namespace Crown
 			_file_filter.set_filter_name("Level (*.level)");
 			_file_filter.add_pattern("*.level");
 
-			_resource_browser = new ResourceBrowser(_project);
-			_resource_browser.relative_to = _toolbar;
-			_resource_browser.resource_selected.connect(on_resource_browser_resource_selected);
-			_resource_browser.delete_event.connect(() => { _resource_browser.hide(); return true; });
-			_resource_browser.modal = true;
-
 			// Save level once every 5 minutes.
 			GLib.Timeout.add_seconds(5*3600, save_timeout);
 
@@ -1328,7 +1338,7 @@ namespace Crown
 
 		private void on_resource_browser(Gtk.Action action)
 		{
-			_resource_browser.show_all();
+			_resource_popover.show_all();
 		}
 
 		private void on_fullscreen(Gtk.Action action)

+ 52 - 0
tools/level_editor/project_store.vala

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2012-2018 Daniele Bartolini and individual contributors.
+ * License: https://github.com/dbartolini/crown/blob/master/LICENSE
+ */
+
+using Gtk;
+using Gee;
+
+namespace Crown
+{
+	public class ProjectStore
+	{
+		// Data
+		public Project _project;
+		public Gtk.TreeStore _tree_store;
+
+		public ProjectStore(Project project)
+		{
+			// Data
+			_project = project;
+			_project.changed.connect(on_project_changed);
+
+			_tree_store = new Gtk.TreeStore(2
+				, typeof(string) // resource name
+				, typeof(string) // resource type
+				);
+
+			read_project();
+		}
+
+		private void read_project()
+		{
+			_tree_store.clear();
+
+			Database db = _project.files();
+			HashSet<Guid?> files = db.get_property_set(GUID_ZERO, "data", new Gee.HashSet<Guid?>());
+			files.foreach((id) => {
+				Gtk.TreeIter resource_iter;
+				_tree_store.append(out resource_iter, null);
+				string name = db.get_property_string(id, "name");
+				string type = db.get_property_string(id, "type");
+				_tree_store.set(resource_iter, 0, name, 1, type, -1);
+				return true;
+			});
+		}
+
+		private void on_project_changed()
+		{
+			read_project();
+		}
+	}
+}

+ 86 - 51
tools/level_editor/properties_view.vala

@@ -106,23 +106,23 @@ namespace Crown
 		Level _level;
 
 		// Widgets
-		private Gtk.Entry _mesh_resource;
+		private ReferenceChooser _mesh_resource;
 		private Gtk.Entry _geometry;
-		private Gtk.Entry _material;
+		private ReferenceChooser _material;
 		private CheckBox _visible;
 
-		public MeshRendererComponentView(Level level)
+		public MeshRendererComponentView(Level level, ProjectStore store)
 		{
 			// Data
 			_level = level;
 
 			// Widgets
-			_mesh_resource = new Gtk.Entry();
-			_mesh_resource.sensitive = false;
+			_mesh_resource = new ReferenceChooser(store, "mesh");
+			_mesh_resource.value_changed.connect(on_value_changed);
 			_geometry = new Gtk.Entry();
 			_geometry.sensitive = false;
-			_material = new Gtk.Entry();
-			_material.sensitive = false;
+			_material = new ReferenceChooser(store, "material");
+			_material.value_changed.connect(on_value_changed);
 			_visible = new CheckBox();
 
 			add_row("Mesh", _mesh_resource);
@@ -131,13 +131,24 @@ namespace Crown
 			add_row("Visible", _visible);
 		}
 
+		private void on_value_changed()
+		{
+			_level.set_mesh(_id
+				, _component_id
+				, _mesh_resource.value
+				, _geometry.text
+				, _material.value
+				, _visible.value
+				);
+		}
+
 		public override void update()
 		{
 			Unit unit = new Unit(_level._db, _id, _level._prefabs);
-			_mesh_resource.text = unit.get_component_property_string(_component_id, "data.mesh_resource");
-			_geometry.text      = unit.get_component_property_string(_component_id, "data.geometry_name");
-			_material.text      = unit.get_component_property_string(_component_id, "data.material");
-			_visible.value      = unit.get_component_property_bool  (_component_id, "data.visible");
+			_mesh_resource.value = unit.get_component_property_string(_component_id, "data.mesh_resource");
+			_geometry.text       = unit.get_component_property_string(_component_id, "data.geometry_name");
+			_material.value      = unit.get_component_property_string(_component_id, "data.material");
+			_visible.value       = unit.get_component_property_bool  (_component_id, "data.visible");
 		}
 	}
 
@@ -147,28 +158,28 @@ namespace Crown
 		Level _level;
 
 		// Widgets
-		private Gtk.Entry _sprite_resource;
-		private Gtk.Entry _material;
+		private ReferenceChooser _sprite_resource;
+		private ReferenceChooser _material;
 		private SpinButtonDouble _layer;
 		private SpinButtonDouble _depth;
 		private CheckBox _visible;
 
-		public SpriteRendererComponentView(Level level)
+		public SpriteRendererComponentView(Level level, ProjectStore store)
 		{
 			// Data
 			_level = level;
 
 			// Widgets
-			_sprite_resource = new Gtk.Entry();
-			_material = new Gtk.Entry();
+			_sprite_resource = new ReferenceChooser(store, "sprite");
+			_sprite_resource.value_changed.connect(on_value_changed);
+			_material = new ReferenceChooser(store, "material");
+			_material.value_changed.connect(on_value_changed);
 			_layer = new SpinButtonDouble(0.0, 0.0, 7.0);
 			_layer.value_changed.connect(on_value_changed);
 			_depth = new SpinButtonDouble(0.0, 0.0, (double)uint32.MAX);
 			_depth.value_changed.connect(on_value_changed);
 			_visible = new CheckBox();
 			_visible.value_changed.connect(on_value_changed);
-			_sprite_resource.sensitive = false;
-			_material.sensitive = false;
 
 			add_row("Sprite", _sprite_resource);
 			add_row("Material", _material);
@@ -183,8 +194,8 @@ namespace Crown
 				, _component_id
 				, _layer.value
 				, _depth.value
-				, _material.text
-				, _sprite_resource.text
+				, _material.value
+				, _sprite_resource.value
 				, _visible.value
 				);
 		}
@@ -192,11 +203,11 @@ namespace Crown
 		public override void update()
 		{
 			Unit unit = new Unit(_level._db, _id, _level._prefabs);
-			_sprite_resource.text = unit.get_component_property_string(_component_id, "data.sprite_resource");
-			_material.text        = unit.get_component_property_string(_component_id, "data.material");
-			_layer.value          = unit.get_component_property_double(_component_id, "data.layer");
-			_depth.value          = unit.get_component_property_double(_component_id, "data.depth");
-			_visible.value        = unit.get_component_property_bool  (_component_id, "data.visible");
+			_sprite_resource.value = unit.get_component_property_string(_component_id, "data.sprite_resource");
+			_material.value        = unit.get_component_property_string(_component_id, "data.material");
+			_layer.value           = unit.get_component_property_double(_component_id, "data.layer");
+			_depth.value           = unit.get_component_property_double(_component_id, "data.depth");
+			_visible.value         = unit.get_component_property_bool  (_component_id, "data.visible");
 		}
 	}
 
@@ -309,7 +320,7 @@ namespace Crown
 			unit.set_component_property_double(_component_id, "data.fov",        _fov.value*(Math.PI/180.0));
 			unit.set_component_property_double(_component_id, "data.near_range", _near_range.value);
 			unit.set_component_property_double(_component_id, "data.far_range",  _far_range.value);
-			unit.set_component_property_string(_component_id, "type", "camera");
+			unit.set_component_property_string(_component_id, "type",            "camera");
 		}
 
 		public override void update()
@@ -334,11 +345,11 @@ namespace Crown
 
 		// Widgets
 		private Gtk.Entry _shape;
-		private Gtk.Entry _scene;
+		private ReferenceChooser _scene;
 		private Gtk.Entry _name;
 		private Gtk.Entry _material;
 
-		public ColliderComponentView(Level level)
+		public ColliderComponentView(Level level, ProjectStore store)
 		{
 			// Data
 			_level = level;
@@ -346,8 +357,8 @@ namespace Crown
 			// Widgets
 			_shape = new Gtk.Entry();
 			_shape.sensitive = false;
-			_scene = new Gtk.Entry();
-			_scene.sensitive = false;
+			_scene = new ReferenceChooser(store, "mesh");
+			_scene.value_changed.connect(on_value_changed);
 			_name = new Gtk.Entry();
 			_name.sensitive = false;
 			_material = new Gtk.Entry();
@@ -359,11 +370,21 @@ namespace Crown
 			add_row("Material", _material);
 		}
 
+		private void on_value_changed()
+		{
+			Unit unit = new Unit(_level._db, _id, _level._prefabs);
+			unit.set_component_property_string(_component_id, "data.shape",    _shape.text);
+			unit.set_component_property_string(_component_id, "data.scene",    _scene.value);
+			unit.set_component_property_string(_component_id, "data.name",     _name.text);
+			unit.set_component_property_string(_component_id, "data.material", _material.text);
+			unit.set_component_property_string(_component_id, "type",          "collider");
+		}
+
 		public override void update()
 		{
 			Unit unit = new Unit(_level._db, _id, _level._prefabs);
 			_shape.text    = unit.get_component_property_string(_component_id, "data.shape");
-			_scene.text    = unit.get_component_property_string(_component_id, "data.scene");
+			_scene.value   = unit.get_component_property_string(_component_id, "data.scene");
 			_name.text     = unit.get_component_property_string(_component_id, "data.name");
 			_material.text = unit.get_component_property_string(_component_id, "data.material");
 		}
@@ -404,11 +425,11 @@ namespace Crown
 		private void on_value_changed()
 		{
 			Unit unit = new Unit(_level._db, _id, _level._prefabs);
-			unit.set_component_property_string(_component_id, "data.class", _class.text);
+			unit.set_component_property_string(_component_id, "data.class",            _class.text);
 			unit.set_component_property_string(_component_id, "data.collision_filter", _collision_filter.text);
-			unit.set_component_property_string(_component_id, "data.material", _material.text);
-			unit.set_component_property_double(_component_id, "data.mass", _mass.value);
-			unit.set_component_property_string(_component_id, "type", "actor");
+			unit.set_component_property_string(_component_id, "data.material",         _material.text);
+			unit.set_component_property_double(_component_id, "data.mass",             _mass.value);
+			unit.set_component_property_string(_component_id, "type",                  "actor");
 		}
 
 		public override void update()
@@ -427,24 +448,31 @@ namespace Crown
 		Level _level;
 
 		// Widgets
-		private Gtk.Entry _script_resource;
+		private ReferenceChooser _script_resource;
 
-		public ScriptComponentView(Level level)
+		public ScriptComponentView(Level level, ProjectStore store)
 		{
 			// Data
 			_level = level;
 
 			// Widgets
-			_script_resource = new Gtk.Entry();
-			_script_resource.sensitive = false;
+			_script_resource = new ReferenceChooser(store, "lua");
+			_script_resource.value_changed.connect(on_value_changed);
 
 			add_row("Script", _script_resource);
 		}
 
+		private void on_value_changed()
+		{
+			Unit unit = new Unit(_level._db, _id, _level._prefabs);
+			unit.set_component_property_string(_component_id, "data.script_resource", _script_resource.value);
+			unit.set_component_property_string(_component_id, "type",                 "script");
+		}
+
 		public override void update()
 		{
 			Unit unit = new Unit(_level._db, _id, _level._prefabs);
-			_script_resource.text = unit.get_component_property_string(_component_id, "data.script_resource");
+			_script_resource.value = unit.get_component_property_string(_component_id, "data.script_resource");
 		}
 	}
 
@@ -454,24 +482,31 @@ namespace Crown
 		Level _level;
 
 		// Widgets
-		private Gtk.Entry _state_machine_resource;
+		private ReferenceChooser _state_machine_resource;
 
-		public AnimationStateMachine(Level level)
+		public AnimationStateMachine(Level level, ProjectStore store)
 		{
 			// Data
 			_level = level;
 
 			// Widgets
-			_state_machine_resource = new Gtk.Entry();
-			_state_machine_resource.sensitive = false;
+			_state_machine_resource = new ReferenceChooser(store, "state_machine");
+			_state_machine_resource.value_changed.connect(on_value_changed);
 
 			add_row("State Machine", _state_machine_resource);
 		}
 
+		private void on_value_changed()
+		{
+			Unit unit = new Unit(_level._db, _id, _level._prefabs);
+			unit.set_component_property_string(_component_id, "data.state_machine_resource", _state_machine_resource.value);
+			unit.set_component_property_string(_component_id, "type",                        "animation_state_machine");
+		}
+
 		public override void update()
 		{
 			Unit unit = new Unit(_level._db, _id, _level._prefabs);
-			_state_machine_resource.text = unit.get_component_property_string(_component_id, "data.state_machine_resource");
+			_state_machine_resource.value = unit.get_component_property_string(_component_id, "data.state_machine_resource");
 		}
 	}
 
@@ -621,7 +656,7 @@ namespace Crown
 		private Gtk.Box _components_vbox;
 		private Gtk.Widget _current_widget;
 
-		public PropertiesView(Level level)
+		public PropertiesView(Level level, ProjectStore store)
 		{
 			// Data
 			_level = level;
@@ -640,12 +675,12 @@ namespace Crown
 			add_component_view("Transform",               "transform",               0, new TransformComponentView(_level));
 			add_component_view("Light",                   "light",                   1, new LightComponentView(_level));
 			add_component_view("Camera",                  "camera",                  2, new CameraComponentView(_level));
-			add_component_view("Mesh Renderer",           "mesh_renderer",           3, new MeshRendererComponentView(_level));
-			add_component_view("Sprite Renderer",         "sprite_renderer",         3, new SpriteRendererComponentView(_level));
-			add_component_view("Collider",                "collider",                3, new ColliderComponentView(_level));
+			add_component_view("Mesh Renderer",           "mesh_renderer",           3, new MeshRendererComponentView(_level, store));
+			add_component_view("Sprite Renderer",         "sprite_renderer",         3, new SpriteRendererComponentView(_level, store));
+			add_component_view("Collider",                "collider",                3, new ColliderComponentView(_level, store));
 			add_component_view("Actor",                   "actor",                   3, new ActorComponentView(_level));
-			add_component_view("Script",                  "script",                  3, new ScriptComponentView(_level));
-			add_component_view("Animation State Machine", "animation_state_machine", 3, new AnimationStateMachine(_level));
+			add_component_view("Script",                  "script",                  3, new ScriptComponentView(_level, store));
+			add_component_view("Animation State Machine", "animation_state_machine", 3, new AnimationStateMachine(_level, store));
 
 			// Sound
 			add_component_view("Transform", "sound_transform",  0, new SoundTransformView(_level));

+ 73 - 106
tools/level_editor/resource_browser.vala

@@ -8,51 +8,49 @@ using Gee;
 
 namespace Crown
 {
-	const string resource_types[] =
+	// Returns true if the item should be filtered out
+	private bool user_filter(string type, string name)
 	{
-		"unit",
-		"sound"
-	};
-
-	public bool is_type(string type)
-	{
-		for (int i = 0; i < resource_types.length; ++i)
-		{
-			if (resource_types[i] == type)
-				return true;
-		}
-		return false;
+		return (type == "unit" || type == "sound") && !name.has_prefix("core/");
 	}
 
-	public class ResourceBrowser : Gtk.Popover
+	public delegate bool UserFilter(string type, string name);
+
+	public class ResourceBrowser : Gtk.Box
 	{
 		// Data
-		private Project _project;
-		private GLib.Subprocess _engine_process;
-		private ConsoleClient _console_client;
+		public Project _project;
+		public GLib.Subprocess _engine_process;
+		public ConsoleClient _console_client;
+		public Gtk.TreeStore _tree_store;
+		public bool _preview;
+		public unowned UserFilter _user_filter;
+		public string _name;
 
 		// Widgets
-		private Gtk.Entry _filter_entry;
-		private Gtk.TreeStore _tree_store;
-		private Gtk.TreeModelFilter _tree_filter;
-		private Gtk.TreeModelSort _tree_sort;
-		private Gtk.TreeView _tree_view;
-		private Gtk.TreeSelection _tree_selection;
-		private Gtk.ScrolledWindow _scrolled_window;
-		private Gtk.Box _box;
+		public Gtk.Entry _filter_entry;
+		public Gtk.TreeModelFilter _tree_filter;
+		public Gtk.TreeModelSort _tree_sort;
+		public Gtk.TreeView _tree_view;
+		public Gtk.TreeSelection _tree_selection;
+		public Gtk.ScrolledWindow _scrolled_window;
 
-		private EngineView _engine_view;
+		public EngineView _engine_view;
 
 		// Signals
 		public signal void resource_selected(string type, string name);
 
-		public ResourceBrowser(Project project)
+		public ResourceBrowser(Project? project, ProjectStore project_store, bool preview)
 		{
+			Object(orientation: Gtk.Orientation.VERTICAL, spacing: 0);
+
 			// Data
 			_project = project;
-			_project.changed.connect(on_project_changed);
 
 			_console_client = new ConsoleClient();
+			_tree_store = project_store._tree_store;
+			_preview = preview;
+			_user_filter = user_filter;
 
 			// Widgets
 			this.destroy.connect(on_destroy);
@@ -62,8 +60,6 @@ namespace Crown
 			_filter_entry.changed.connect(on_filter_entry_text_changed);
 			_filter_entry.key_press_event.connect(on_filter_entry_key_pressed);
 
-			_tree_store = new Gtk.TreeStore(2, typeof(string), typeof(string));
-
 			_tree_filter = new Gtk.TreeModelFilter(_tree_store, null);
 			_tree_filter.set_visible_func(filter_tree);
 
@@ -83,7 +79,6 @@ namespace Crown
 			_tree_view.headers_visible = false;
 			_tree_view.can_focus = false;
 			_tree_view.row_activated.connect(on_row_activated);
-			_tree_view.button_press_event.connect(on_button_pressed);
 			_tree_view.button_release_event.connect(on_button_released);
 
 			_tree_selection = _tree_view.get_selection();
@@ -94,18 +89,19 @@ namespace Crown
 			_scrolled_window.add(_tree_view);
 			_scrolled_window.set_size_request(300, 400);
 
-			read_project();
-
-			_engine_view = new EngineView(_console_client, false);
-			_engine_view.realized.connect(on_engine_view_realized);
-			_engine_view.set_size_request(300, 300);
-
-			_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-			_box.pack_start(_filter_entry, false, false, 0);
-			_box.pack_start(_engine_view, true, true, 0);
-			_box.pack_start(_scrolled_window, false, false, 0);
+			if (_preview)
+			{
+				_engine_view = new EngineView(_console_client, false);
+				_engine_view.realized.connect(on_engine_view_realized);
+				_engine_view.set_size_request(300, 300);
+			}
 
-			this.add(_box);
+			this.pack_start(_filter_entry, false, false, 0);
+			if (_preview)
+			{
+				this.pack_start(_engine_view, true, true, 0);
+			}
+			this.pack_start(_scrolled_window, false, false, 0);
 		}
 
 		private void on_row_activated(Gtk.TreePath path, TreeViewColumn column)
@@ -119,37 +115,11 @@ namespace Crown
 				Value type;
 				_tree_store.get_value(iter, 0, out name);
 				_tree_store.get_value(iter, 1, out type);
+				_name = (string)name;
 				resource_selected((string)type, (string)name);
-				this.hide();
 			}
 		}
 
-		private bool on_button_pressed(Gdk.EventButton ev)
-		{
-			if (ev.button == 1)
-			{
-				Gtk.TreePath path;
-				int cell_x;
-				int cell_y;
-				if (_tree_view.get_path_at_pos((int)ev.x, (int)ev.y, out path, null, out cell_x, out cell_y))
-				{
-					Gtk.TreePath filter_path = _tree_sort.convert_path_to_child_path(path);
-					Gtk.TreePath child_path = _tree_filter.convert_path_to_child_path(filter_path);
-					Gtk.TreeIter iter;
-					if (_tree_store.get_iter(out iter, child_path))
-					{
-						Value name;
-						Value type;
-						_tree_store.get_value(iter, 0, out name);
-						_tree_store.get_value(iter, 1, out type);
-						resource_selected((string)type, (string)name);
-					}
-				}
-			}
-
-			return false;
-		}
-
 		private bool on_button_released(Gdk.EventButton ev)
 		{
 			if (ev.button == 1)
@@ -160,7 +130,20 @@ namespace Crown
 				if (_tree_view.get_path_at_pos((int)ev.x, (int)ev.y, out path, null, out cell_x, out cell_y))
 				{
 					if (_tree_view.get_selection().path_is_selected(path))
-						this.hide();
+					{
+						Gtk.TreePath filter_path = _tree_sort.convert_path_to_child_path(path);
+						Gtk.TreePath child_path = _tree_filter.convert_path_to_child_path(filter_path);
+						Gtk.TreeIter iter;
+						if (_tree_store.get_iter(out iter, child_path))
+						{
+							Value name;
+							Value type;
+							_tree_store.get_value(iter, 0, out name);
+							_tree_store.get_value(iter, 1, out type);
+							_name = (string)name;
+							resource_selected((string)type, (string)name);
+						}
+					}
 				}
 			}
 
@@ -247,17 +230,17 @@ namespace Crown
 
 		private bool filter_tree(Gtk.TreeModel model, Gtk.TreeIter iter)
 		{
-			Value name;
 			Value type;
-			model.get_value(iter, 0, out name);
+			Value name;
 			model.get_value(iter, 1, out type);
+			model.get_value(iter, 0, out name);
 
-			string name_str = (string)name;
 			string type_str = (string)type;
+			string name_str = (string)name;
 
-			return name_str != null
-				&& type_str != null
-				&& is_type(type_str)
+			return type_str != null
+				&& name_str != null
+				&& _user_filter(type_str, name_str)
 				&& (_filter_entry.text.length == 0 || name_str.index_of(_filter_entry.text) > -1)
 				;
 		}
@@ -292,9 +275,9 @@ namespace Crown
 					Value type;
 					model.get_value(iter, 0, out name);
 					model.get_value(iter, 1, out type);
+					_name = (string)name;
 					resource_selected((string)type, (string)name);
 				}
-				this.hide();
 			}
 			else
 				return false;
@@ -304,41 +287,25 @@ namespace Crown
 
 		private void on_tree_selection_changed()
 		{
-			Gtk.TreeModel model;
-			Gtk.TreeIter iter;
-			if (_tree_selection.get_selected(out model, out iter))
+			if (_preview)
 			{
-				Value name;
-				Value type;
-				model.get_value(iter, 0, out name);
-				model.get_value(iter, 1, out type);
-				_console_client.send_script(UnitPreviewApi.set_preview_resource((string)type, (string)name));
+				Gtk.TreeModel model;
+				Gtk.TreeIter iter;
+				if (_tree_selection.get_selected(out model, out iter))
+				{
+					Value name;
+					Value type;
+					model.get_value(iter, 0, out name);
+					model.get_value(iter, 1, out type);
+					_console_client.send_script(UnitPreviewApi.set_preview_resource((string)type, (string)name));
+				}
 			}
 		}
 
-		private void read_project()
+		public void set_type_filter(owned UserFilter filter)
 		{
-			_tree_store.clear();
-
-			Database db = _project.files();
-			HashSet<Guid?> files = db.get_property_set(GUID_ZERO, "data", new Gee.HashSet<Guid?>());
-			files.foreach((id) => {
-				Gtk.TreeIter resource_iter;
-				_tree_store.append(out resource_iter, null);
-				string name = db.get_property_string(id, "name");
-				if (name.has_prefix("core/"))
-					return true;
-
-				string type = db.get_property_string(id, "type");
-				_tree_store.set(resource_iter, 0, name, 1, type, -1);
-				return true;
-			});
+			_user_filter = filter;
 			_tree_filter.refilter();
 		}
-
-		private void on_project_changed()
-		{
-			read_project();
-		}
 	}
 }

+ 92 - 0
tools/widgets/reference_chooser.vala

@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2012-2018 Daniele Bartolini and individual contributors.
+ * License: https://github.com/dbartolini/crown/blob/master/LICENSE
+ */
+
+using Gtk;
+
+namespace Crown
+{
+	/// <summary>
+	/// Vector2 spin button.
+	/// </summary>
+	public class ReferenceChooser : Gtk.Box
+	{
+		// Data
+		private bool _stop_emit;
+		private string _type;
+
+		// Widgets
+		private Gtk.Entry _name;
+		private Gtk.Button _selector;
+		private ProjectStore _project_store;
+
+		public string value
+		{
+			get
+			{
+				return _name.text;
+			}
+			set
+			{
+				_stop_emit = true;
+				_name.text = value;
+				_stop_emit = false;
+			}
+		}
+
+		// Signals
+		public signal void value_changed();
+
+		public ReferenceChooser(ProjectStore store, string type)
+		{
+			Object(orientation: Gtk.Orientation.HORIZONTAL, spacing: 0);
+
+			// Data
+			_stop_emit = false;
+			_type = type;
+
+			// Widgets
+			_name = new Gtk.Entry();
+			_name.sensitive = false;
+			_name.hexpand = true;
+			_name.changed.connect(on_value_changed);
+			_selector = new Gtk.Button.from_icon_name("document-open-symbolic");
+			_selector.clicked.connect(on_selector_clicked);
+			_project_store = store;
+
+			add(_name);
+			add(_selector);
+		}
+
+		private void on_value_changed()
+		{
+			if (!_stop_emit)
+				value_changed();
+		}
+
+		private void on_selector_clicked()
+		{
+			Gtk.Dialog dg = new Gtk.Dialog.with_buttons("Select resource"
+				, null
+				, DialogFlags.MODAL
+				, null
+				);
+
+			var rb = new ResourceBrowser(null, _project_store, false);
+			rb.set_type_filter(type_filter);
+			rb.resource_selected.connect(() => { _name.text = rb._name; dg.response(ResponseType.OK); });
+
+			dg.skip_taskbar_hint = true;
+			dg.get_content_area().add(rb);
+			dg.show_all();
+			dg.run();
+			dg.destroy();
+		}
+
+		private bool type_filter(string type, string name)
+		{
+			return _type == type;
+		}
+	}
+}