properties_view.vala 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. /*
  2. * Copyright (c) 2012-2024 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. using Gtk;
  6. using Gee;
  7. namespace Crown
  8. {
  9. public class PropertyGrid : Gtk.Grid
  10. {
  11. // Data
  12. public Database? _db;
  13. public Guid _id;
  14. public Guid _component_id;
  15. public int _rows;
  16. public PropertyGrid(Database? db = null)
  17. {
  18. this.row_spacing = 4;
  19. this.row_homogeneous = true;
  20. this.column_spacing = 12;
  21. // Data
  22. _db = db;
  23. _id = GUID_ZERO;
  24. _component_id = GUID_ZERO;
  25. _rows = 0;
  26. }
  27. public Gtk.Widget add_row(string label, Gtk.Widget w)
  28. {
  29. Gtk.Label l = new Label(label);
  30. l.width_chars = 13;
  31. l.set_alignment(1.0f, 0.5f);
  32. w.hexpand = true;
  33. this.attach(l, 0, (int)_rows);
  34. this.attach(w, 1, (int)_rows);
  35. ++_rows;
  36. return l;
  37. }
  38. public virtual void update()
  39. {
  40. }
  41. }
  42. public class PropertyGridSet : Gtk.Box
  43. {
  44. public PropertyGridSet()
  45. {
  46. Object(orientation: Gtk.Orientation.VERTICAL, spacing: 0);
  47. }
  48. public Gtk.Expander add_property_grid(PropertyGrid cv, string label)
  49. {
  50. Gtk.Label lb = new Gtk.Label(null);
  51. lb.set_markup("<b>%s</b>".printf(label));
  52. lb.set_alignment(0.0f, 0.5f);
  53. Gtk.Expander expander = new Gtk.Expander("");
  54. expander.label_widget = lb;
  55. expander.expanded = true;
  56. expander.add(cv);
  57. this.pack_start(expander, false, true, 0);
  58. return expander;
  59. }
  60. }
  61. public class TransformPropertyGrid : PropertyGrid
  62. {
  63. // Widgets
  64. private EntryPosition _position;
  65. private EntryRotation _rotation;
  66. private EntryScale _scale;
  67. public TransformPropertyGrid(Database db)
  68. {
  69. base(db);
  70. // Widgets
  71. _position = new EntryPosition();
  72. _position.value_changed.connect(on_value_changed);
  73. _rotation = new EntryRotation();
  74. _rotation.value_changed.connect(on_value_changed);
  75. _scale = new EntryScale();
  76. _scale.value_changed.connect(on_value_changed);
  77. add_row("Position", _position);
  78. add_row("Rotation", _rotation);
  79. add_row("Scale", _scale);
  80. }
  81. private void on_value_changed()
  82. {
  83. Unit unit = new Unit(_db, _id);
  84. unit.set_component_property_vector3 (_component_id, "data.position", _position.value);
  85. unit.set_component_property_quaternion(_component_id, "data.rotation", _rotation.value);
  86. unit.set_component_property_vector3 (_component_id, "data.scale", _scale.value);
  87. _db.add_restore_point((int)ActionType.SET_TRANSFORM, new Guid?[] { _id, _component_id });
  88. }
  89. public override void update()
  90. {
  91. Unit unit = new Unit(_db, _id);
  92. _position.value = unit.get_component_property_vector3 (_component_id, "data.position");
  93. _rotation.value = unit.get_component_property_quaternion(_component_id, "data.rotation");
  94. _scale.value = unit.get_component_property_vector3 (_component_id, "data.scale");
  95. }
  96. }
  97. public class MeshRendererPropertyGrid : PropertyGrid
  98. {
  99. // Widgets
  100. private Project _project;
  101. private ResourceChooserButton _scene;
  102. private ComboBoxMap _geometry;
  103. private ResourceChooserButton _material;
  104. private CheckBox _visible;
  105. private void decode(Hashtable mesh_resource)
  106. {
  107. const string keys[] = { "geometries" };
  108. ComboBoxMap combos[] = { _geometry };
  109. for (int i = 0; i < keys.length; ++i) {
  110. combos[i].clear();
  111. if (mesh_resource.has_key(keys[i])) {
  112. Hashtable obj = (Hashtable)mesh_resource[keys[i]];
  113. foreach (var e in obj)
  114. combos[i].append(e.key, e.key);
  115. }
  116. }
  117. }
  118. private void decode_from_resource(string type, string name)
  119. {
  120. string path = ResourceId.path(type, name);
  121. decode(SJSON.load_from_path(_project.absolute_path(path)));
  122. }
  123. public MeshRendererPropertyGrid(Database db, ProjectStore store)
  124. {
  125. base(db);
  126. _project = store._project;
  127. // Widgets
  128. _scene = new ResourceChooserButton(store, "mesh");
  129. _scene.value_changed.connect(on_scene_value_changed);
  130. _geometry = new ComboBoxMap();
  131. _material = new ResourceChooserButton(store, "material");
  132. _material.value_changed.connect(on_value_changed);
  133. _visible = new CheckBox();
  134. _visible.value_changed.connect(on_value_changed);
  135. add_row("Scene", _scene);
  136. add_row("Geometry", _geometry);
  137. add_row("Material", _material);
  138. add_row("Visible", _visible);
  139. }
  140. private void on_scene_value_changed()
  141. {
  142. decode_from_resource("mesh", _scene.value);
  143. _geometry.value = _geometry.any_valid_id();
  144. on_value_changed();
  145. }
  146. private void on_value_changed()
  147. {
  148. Unit unit = new Unit(_db, _id);
  149. unit.set_component_property_string(_component_id, "data.mesh_resource", _scene.value);
  150. unit.set_component_property_string(_component_id, "data.geometry_name", _geometry.value);
  151. unit.set_component_property_string(_component_id, "data.material", _material.value);
  152. unit.set_component_property_bool (_component_id, "data.visible", _visible.value);
  153. _db.add_restore_point((int)ActionType.SET_MESH, new Guid?[] { _id, _component_id });
  154. }
  155. private void update_mesh_and_geometry(Unit unit)
  156. {
  157. _scene.value = unit.get_component_property_string(_component_id, "data.mesh_resource");
  158. decode_from_resource("mesh", _scene.value);
  159. _geometry.value = unit.get_component_property_string(_component_id, "data.geometry_name");
  160. }
  161. public override void update()
  162. {
  163. Unit unit = new Unit(_db, _id);
  164. update_mesh_and_geometry(unit);
  165. _material.value = unit.get_component_property_string(_component_id, "data.material");
  166. _visible.value = unit.get_component_property_bool (_component_id, "data.visible");
  167. }
  168. }
  169. public class SpriteRendererPropertyGrid : PropertyGrid
  170. {
  171. // Widgets
  172. private ResourceChooserButton _sprite_resource;
  173. private ResourceChooserButton _material;
  174. private EntryDouble _layer;
  175. private EntryDouble _depth;
  176. private CheckBox _visible;
  177. public SpriteRendererPropertyGrid(Database db, ProjectStore store)
  178. {
  179. base(db);
  180. // Widgets
  181. _sprite_resource = new ResourceChooserButton(store, "sprite");
  182. _sprite_resource.value_changed.connect(on_value_changed);
  183. _material = new ResourceChooserButton(store, "material");
  184. _material.value_changed.connect(on_value_changed);
  185. _layer = new EntryDouble(0.0, 0.0, 7.0);
  186. _layer.value_changed.connect(on_value_changed);
  187. _depth = new EntryDouble(0.0, 0.0, (double)uint32.MAX);
  188. _depth.value_changed.connect(on_value_changed);
  189. _visible = new CheckBox();
  190. _visible.value_changed.connect(on_value_changed);
  191. add_row("Sprite", _sprite_resource);
  192. add_row("Material", _material);
  193. add_row("Layer", _layer);
  194. add_row("Depth", _depth);
  195. add_row("Visible", _visible);
  196. }
  197. private void on_value_changed()
  198. {
  199. Unit unit = new Unit(_db, _id);
  200. unit.set_component_property_string(_component_id, "data.sprite_resource", _sprite_resource.value);
  201. unit.set_component_property_string(_component_id, "data.material", _material.value);
  202. unit.set_component_property_double(_component_id, "data.layer", _layer.value);
  203. unit.set_component_property_double(_component_id, "data.depth", _depth.value);
  204. unit.set_component_property_bool (_component_id, "data.visible", _visible.value);
  205. _db.add_restore_point((int)ActionType.SET_SPRITE, new Guid?[] { _id, _component_id });
  206. }
  207. public override void update()
  208. {
  209. Unit unit = new Unit(_db, _id);
  210. _sprite_resource.value = unit.get_component_property_string(_component_id, "data.sprite_resource");
  211. _material.value = unit.get_component_property_string(_component_id, "data.material");
  212. _layer.value = unit.get_component_property_double(_component_id, "data.layer");
  213. _depth.value = unit.get_component_property_double(_component_id, "data.depth");
  214. _visible.value = unit.get_component_property_bool (_component_id, "data.visible");
  215. }
  216. }
  217. public class LightPropertyGrid : PropertyGrid
  218. {
  219. // Widgets
  220. private ComboBoxMap _type;
  221. private EntryDouble _range;
  222. private EntryDouble _intensity;
  223. private EntryDouble _spot_angle;
  224. private ColorButtonVector3 _color;
  225. public LightPropertyGrid(Database db)
  226. {
  227. base(db);
  228. // Widgets
  229. _type = new ComboBoxMap();
  230. _type.value_changed.connect(on_value_changed);
  231. _type.append("directional", "Directional");
  232. _type.append("omni", "Omni");
  233. _type.append("spot", "Spot");
  234. _range = new EntryDouble(0.0, 0.0, double.MAX);
  235. _range.value_changed.connect(on_value_changed);
  236. _intensity = new EntryDouble(0.0, 0.0, double.MAX);
  237. _intensity.value_changed.connect(on_value_changed);
  238. _spot_angle = new EntryDouble(0.0, 0.0, 90.0);
  239. _spot_angle.value_changed.connect(on_value_changed);
  240. _color = new ColorButtonVector3();
  241. _color.value_changed.connect(on_value_changed);
  242. add_row("Type", _type);
  243. add_row("Range", _range);
  244. add_row("Intensity", _intensity);
  245. add_row("Spot Angle", _spot_angle);
  246. add_row("Color", _color);
  247. }
  248. private void on_value_changed()
  249. {
  250. Unit unit = new Unit(_db, _id);
  251. unit.set_component_property_string (_component_id, "data.type", _type.value);
  252. unit.set_component_property_double (_component_id, "data.range", _range.value);
  253. unit.set_component_property_double (_component_id, "data.intensity", _intensity.value);
  254. unit.set_component_property_double (_component_id, "data.spot_angle", _spot_angle.value * (Math.PI/180.0));
  255. unit.set_component_property_vector3(_component_id, "data.color", _color.value);
  256. _db.add_restore_point((int)ActionType.SET_LIGHT, new Guid?[] { _id, _component_id });
  257. }
  258. public override void update()
  259. {
  260. Unit unit = new Unit(_db, _id);
  261. _type.value = unit.get_component_property_string (_component_id, "data.type");
  262. _range.value = unit.get_component_property_double (_component_id, "data.range");
  263. _intensity.value = unit.get_component_property_double (_component_id, "data.intensity");
  264. _spot_angle.value = unit.get_component_property_double (_component_id, "data.spot_angle") * (180.0/Math.PI);
  265. _color.value = unit.get_component_property_vector3(_component_id, "data.color");
  266. }
  267. }
  268. public class CameraPropertyGrid : PropertyGrid
  269. {
  270. // Widgets
  271. private ComboBoxMap _projection;
  272. private EntryDouble _fov;
  273. private EntryDouble _near_range;
  274. private EntryDouble _far_range;
  275. public CameraPropertyGrid(Database db)
  276. {
  277. base(db);
  278. // Widgets
  279. _projection = new ComboBoxMap();
  280. _projection.append("orthographic", "Orthographic");
  281. _projection.append("perspective", "Perspective");
  282. _projection.value_changed.connect(on_value_changed);
  283. _fov = new EntryDouble(0.0, 1.0, 90.0);
  284. _fov.value_changed.connect(on_value_changed);
  285. _near_range = new EntryDouble(0.001, double.MIN, double.MAX);
  286. _near_range.value_changed.connect(on_value_changed);
  287. _far_range = new EntryDouble(1000.000, double.MIN, double.MAX);
  288. _far_range.value_changed.connect(on_value_changed);
  289. add_row("Projection", _projection);
  290. add_row("FOV", _fov);
  291. add_row("Near Range", _near_range);
  292. add_row("Far Range", _far_range);
  293. }
  294. private void on_value_changed()
  295. {
  296. Unit unit = new Unit(_db, _id);
  297. unit.set_component_property_string(_component_id, "data.projection", _projection.value);
  298. unit.set_component_property_double(_component_id, "data.fov", _fov.value * (Math.PI/180.0));
  299. unit.set_component_property_double(_component_id, "data.near_range", _near_range.value);
  300. unit.set_component_property_double(_component_id, "data.far_range", _far_range.value);
  301. _db.add_restore_point((int)ActionType.SET_CAMERA, new Guid?[] { _id, _component_id });
  302. }
  303. public override void update()
  304. {
  305. Unit unit = new Unit(_db, _id);
  306. _projection.value = unit.get_component_property_string(_component_id, "data.projection");
  307. _fov.value = unit.get_component_property_double(_component_id, "data.fov") * (180.0/Math.PI);
  308. _near_range.value = unit.get_component_property_double(_component_id, "data.near_range");
  309. _far_range.value = unit.get_component_property_double(_component_id, "data.far_range");
  310. }
  311. }
  312. public class ColliderPropertyGrid : PropertyGrid
  313. {
  314. // Widgets.
  315. private Project _project;
  316. private ComboBoxMap _source;
  317. private ResourceChooserButton _scene;
  318. private ComboBoxMap _geometry;
  319. private ComboBoxMap _shape;
  320. // Inline colliders.
  321. private EntryPosition _position;
  322. private EntryRotation _rotation;
  323. private EntryVector3 _half_extents; // Box only.
  324. private EntryDouble _radius; // Sphere and capsule only.
  325. private EntryDouble _height; // Capsule only.
  326. private void decode(Hashtable mesh_resource)
  327. {
  328. const string keys[] = { "geometries" };
  329. ComboBoxMap combos[] = { _geometry };
  330. for (int i = 0; i < keys.length; ++i) {
  331. combos[i].clear();
  332. if (mesh_resource.has_key(keys[i])) {
  333. Hashtable obj = (Hashtable)mesh_resource[keys[i]];
  334. foreach (var e in obj)
  335. combos[i].append(e.key, e.key);
  336. }
  337. }
  338. }
  339. private void decode_from_resource(string type, string name)
  340. {
  341. string path = ResourceId.path(type, name);
  342. decode(SJSON.load_from_path(_project.absolute_path(path)));
  343. }
  344. public ColliderPropertyGrid(Database db, ProjectStore store)
  345. {
  346. base(db);
  347. _project = store._project;
  348. // Widgets.
  349. _source = new ComboBoxMap();
  350. _source.append("inline", "inline");
  351. _source.append("mesh", "mesh");
  352. _source.value_changed.connect(on_source_value_changed);
  353. _scene = new ResourceChooserButton(store, "mesh");
  354. _scene.value_changed.connect(on_scene_value_changed);
  355. _geometry = new ComboBoxMap();
  356. _geometry.value_changed.connect(on_value_changed);
  357. _shape = new ComboBoxMap();
  358. _shape.append("sphere", "sphere");
  359. _shape.append("capsule", "capsule");
  360. _shape.append("box", "box");
  361. _shape.append("convex_hull", "convex_hull");
  362. _shape.append("mesh", "mesh");
  363. _shape.value_changed.connect(on_shape_value_changed);
  364. _position = new EntryPosition();
  365. _position.value_changed.connect(on_value_changed);
  366. _rotation = new EntryRotation();
  367. _rotation.value_changed.connect(on_value_changed);
  368. _half_extents = new EntryVector3(Vector3(0.5, 0.5, 0.5), VECTOR3_ZERO, VECTOR3_MAX);
  369. _half_extents.value_changed.connect(on_value_changed);
  370. _radius = new EntryDouble(0.5, 0.0, double.MAX);
  371. _radius.value_changed.connect(on_value_changed);
  372. _height = new EntryDouble(1.0, 0.0, double.MAX);
  373. _height.value_changed.connect(on_value_changed);
  374. add_row("Source", _source);
  375. add_row("Scene", _scene);
  376. add_row("Geometry", _geometry);
  377. add_row("Shape", _shape);
  378. add_row("Position", _position);
  379. add_row("Rotation", _rotation);
  380. add_row("Half Extents", _half_extents);
  381. add_row("Radius", _radius);
  382. add_row("Height", _height);
  383. }
  384. private void on_source_value_changed()
  385. {
  386. if (_source.value == "inline") {
  387. _shape.value = _shape.any_valid_id();
  388. } else if (_source.value == "mesh") {
  389. _scene.value = "core/units/primitives/cube";
  390. decode_from_resource("mesh", _scene.value);
  391. _geometry.value = "Cube";
  392. _shape.value = "mesh";
  393. } else {
  394. assert(false);
  395. }
  396. enable_disable_properties();
  397. on_value_changed();
  398. }
  399. private void on_scene_value_changed()
  400. {
  401. decode_from_resource("mesh", _scene.value);
  402. _geometry.value = _geometry.any_valid_id();
  403. on_value_changed();
  404. }
  405. private void on_shape_value_changed()
  406. {
  407. enable_disable_properties();
  408. on_value_changed();
  409. }
  410. private void enable_disable_properties()
  411. {
  412. if (_source.value == "inline") {
  413. _scene.sensitive = false;
  414. _geometry.sensitive = false;
  415. _position.sensitive = true;
  416. _rotation.sensitive = true;
  417. if (_shape.value == "sphere") {
  418. _half_extents.sensitive = false;
  419. _radius.sensitive = true;
  420. _height.sensitive = false;
  421. } else if (_shape.value == "capsule") {
  422. _half_extents.sensitive = false;
  423. _radius.sensitive = true;
  424. _height.sensitive = true;
  425. } else if (_shape.value == "box") {
  426. _half_extents.sensitive = true;
  427. _radius.sensitive = false;
  428. _height.sensitive = false;
  429. } else {
  430. _position.sensitive = false;
  431. _rotation.sensitive = false;
  432. _half_extents.sensitive = false;
  433. _radius.sensitive = false;
  434. _height.sensitive = false;
  435. }
  436. } else if (_source.value == "mesh") {
  437. _scene.sensitive = true;
  438. _geometry.sensitive = true;
  439. _position.sensitive = false;
  440. _rotation.sensitive = false;
  441. _half_extents.sensitive = false;
  442. _radius.sensitive = false;
  443. _height.sensitive = false;
  444. } else {
  445. assert(false);
  446. }
  447. }
  448. private void on_value_changed()
  449. {
  450. Unit unit = new Unit(_db, _id);
  451. unit.set_component_property_string(_component_id, "data.source", _source.value);
  452. unit.set_component_property_string(_component_id, "data.scene", _scene.value);
  453. unit.set_component_property_string(_component_id, "data.name", _geometry.value);
  454. unit.set_component_property_string(_component_id, "data.shape", _shape.value);
  455. unit.set_component_property_vector3(_component_id, "data.collider_data.position", _position.value);
  456. unit.set_component_property_quaternion(_component_id, "data.collider_data.rotation", _rotation.value);
  457. unit.set_component_property_vector3(_component_id, "data.collider_data.half_extents", _half_extents.value);
  458. unit.set_component_property_double(_component_id, "data.collider_data.radius", _radius.value);
  459. unit.set_component_property_double(_component_id, "data.collider_data.height", _height.value);
  460. _db.add_restore_point((int)ActionType.SET_COLLIDER, new Guid?[] { _id, _component_id });
  461. }
  462. public override void update()
  463. {
  464. Unit unit = new Unit(_db, _id);
  465. if (unit.get_component_property(_component_id, "data.source") == null)
  466. _source.value = "mesh";
  467. else
  468. _source.value = unit.get_component_property_string(_component_id, "data.source");
  469. if (unit.get_component_property(_component_id, "data.scene") == null) {
  470. _scene.value = "core/units/primitives/cube";
  471. decode_from_resource("mesh", _scene.value);
  472. _geometry.value = _geometry.any_valid_id();
  473. } else {
  474. _scene.value = unit.get_component_property_string(_component_id, "data.scene");
  475. decode_from_resource("mesh", _scene.value);
  476. _geometry.value = unit.get_component_property_string(_component_id, "data.name");
  477. }
  478. _shape.value = unit.get_component_property_string(_component_id, "data.shape");
  479. if (unit.get_component_property(_component_id, "data.collider_data.position") != null)
  480. _position.value = unit.get_component_property_vector3(_component_id, "data.collider_data.position");
  481. if (unit.get_component_property(_component_id, "data.collider_data.rotation") != null)
  482. _rotation.value = unit.get_component_property_quaternion(_component_id, "data.collider_data.rotation");
  483. if (unit.get_component_property(_component_id, "data.collider_data.half_extents") != null)
  484. _half_extents.value = unit.get_component_property_vector3(_component_id, "data.collider_data.half_extents");
  485. if (unit.get_component_property(_component_id, "data.collider_data.radius") != null)
  486. _radius.value = unit.get_component_property_double(_component_id, "data.collider_data.radius");
  487. if (unit.get_component_property(_component_id, "data.collider_data.height") != null)
  488. _height.value = unit.get_component_property_double(_component_id, "data.collider_data.height");
  489. enable_disable_properties();
  490. }
  491. }
  492. public class ActorPropertyGrid : PropertyGrid
  493. {
  494. // Widgets
  495. private Project _project;
  496. private ComboBoxMap _class;
  497. private ComboBoxMap _collision_filter;
  498. private EntryDouble _mass;
  499. private ComboBoxMap _material;
  500. private CheckBox3 _lock_translation;
  501. private CheckBox3 _lock_rotation;
  502. private void decode_global_physics_config(Hashtable global)
  503. {
  504. const string keys[] = { "actors", "collision_filters", "materials" };
  505. ComboBoxMap combos[] = { _class, _collision_filter, _material };
  506. for (int i = 0; i < keys.length; ++i) {
  507. combos[i].clear();
  508. if (global.has_key(keys[i])) {
  509. Hashtable obj = (Hashtable)global[keys[i]];
  510. foreach (var e in obj)
  511. combos[i].append(e.key, e.key);
  512. }
  513. }
  514. if (_id != GUID_ZERO)
  515. update();
  516. }
  517. private void on_project_file_added_or_changed(string type, string name, uint64 size, uint64 mtime)
  518. {
  519. if (type != "physics_config" || name != "global")
  520. return;
  521. string path = ResourceId.path("physics_config", "global");
  522. Hashtable global = SJSON.load_from_path(_project.absolute_path(path));
  523. decode_global_physics_config(global);
  524. }
  525. private void on_project_file_removed(string type, string name)
  526. {
  527. if (type != "physics_config" || name != "global")
  528. return;
  529. decode_global_physics_config(new Hashtable());
  530. }
  531. public ActorPropertyGrid(Database db, Project prj)
  532. {
  533. base(db);
  534. _project = prj;
  535. // Widgets
  536. _class = new ComboBoxMap();
  537. _class.value_changed.connect(on_value_changed);
  538. _collision_filter = new ComboBoxMap();
  539. _collision_filter.value_changed.connect(on_value_changed);
  540. _material = new ComboBoxMap();
  541. _material.value_changed.connect(on_value_changed);
  542. _mass = new EntryDouble(1.0, 0.0, double.MAX);
  543. _mass.value_changed.connect(on_value_changed);
  544. _lock_translation = new CheckBox3();
  545. _lock_translation.value_changed.connect(on_value_changed);
  546. _lock_rotation = new CheckBox3();
  547. _lock_rotation.value_changed.connect(on_value_changed);
  548. add_row("Class", _class);
  549. add_row("Collision Filter", _collision_filter);
  550. add_row("Material", _material);
  551. add_row("Mass", _mass);
  552. add_row("Lock Translation", _lock_translation);
  553. add_row("Lock Rotation", _lock_rotation);
  554. prj.file_added.connect(on_project_file_added_or_changed);
  555. prj.file_changed.connect(on_project_file_added_or_changed);
  556. prj.file_removed.connect(on_project_file_removed);
  557. }
  558. private bool get_component_property_bool_optional(Unit unit, Guid component_id, string key)
  559. {
  560. return unit.get_component_property(component_id, key) != null
  561. ? (bool)unit.get_component_property_bool(component_id, key)
  562. : false
  563. ;
  564. }
  565. private void on_value_changed()
  566. {
  567. Unit unit = new Unit(_db, _id);
  568. if (!_class.is_inconsistent() && _class.value != null)
  569. unit.set_component_property_string(_component_id, "data.class", _class.value);
  570. if (!_collision_filter.is_inconsistent() && _collision_filter.value != null)
  571. unit.set_component_property_string(_component_id, "data.collision_filter", _collision_filter.value);
  572. if (!_material.is_inconsistent() && _material.value != null)
  573. unit.set_component_property_string(_component_id, "data.material", _material.value);
  574. unit.set_component_property_double(_component_id, "data.mass", _mass.value);
  575. unit.set_component_property_bool (_component_id, "data.lock_translation_x", _lock_translation._x.value);
  576. unit.set_component_property_bool (_component_id, "data.lock_translation_y", _lock_translation._y.value);
  577. unit.set_component_property_bool (_component_id, "data.lock_translation_z", _lock_translation._z.value);
  578. unit.set_component_property_bool (_component_id, "data.lock_rotation_x", _lock_rotation._x.value);
  579. unit.set_component_property_bool (_component_id, "data.lock_rotation_y", _lock_rotation._y.value);
  580. unit.set_component_property_bool (_component_id, "data.lock_rotation_z", _lock_rotation._z.value);
  581. _db.add_restore_point((int)ActionType.SET_ACTOR, new Guid?[] { _id, _component_id });
  582. }
  583. public override void update()
  584. {
  585. Unit unit = new Unit(_db, _id);
  586. _class.value = unit.get_component_property_string(_component_id, "data.class");
  587. _collision_filter.value = unit.get_component_property_string(_component_id, "data.collision_filter");
  588. _material.value = unit.get_component_property_string(_component_id, "data.material");
  589. _mass.value = unit.get_component_property_double(_component_id, "data.mass");
  590. _lock_translation._x.value = get_component_property_bool_optional(unit, _component_id, "data.lock_translation_x");
  591. _lock_translation._y.value = get_component_property_bool_optional(unit, _component_id, "data.lock_translation_y");
  592. _lock_translation._z.value = get_component_property_bool_optional(unit, _component_id, "data.lock_translation_z");
  593. _lock_rotation._x.value = get_component_property_bool_optional(unit, _component_id, "data.lock_rotation_x");
  594. _lock_rotation._y.value = get_component_property_bool_optional(unit, _component_id, "data.lock_rotation_y");
  595. _lock_rotation._z.value = get_component_property_bool_optional(unit, _component_id, "data.lock_rotation_z");
  596. }
  597. }
  598. public class ScriptPropertyGrid : PropertyGrid
  599. {
  600. // Widgets
  601. private ResourceChooserButton _script_resource;
  602. public ScriptPropertyGrid(Database db, ProjectStore store)
  603. {
  604. base(db);
  605. // Widgets
  606. _script_resource = new ResourceChooserButton(store, "lua");
  607. _script_resource.value_changed.connect(on_value_changed);
  608. add_row("Script", _script_resource);
  609. }
  610. private void on_value_changed()
  611. {
  612. Unit unit = new Unit(_db, _id);
  613. unit.set_component_property_string(_component_id, "data.script_resource", _script_resource.value);
  614. _db.add_restore_point((int)ActionType.SET_SCRIPT, new Guid?[] { _id, _component_id });
  615. }
  616. public override void update()
  617. {
  618. Unit unit = new Unit(_db, _id);
  619. _script_resource.value = unit.get_component_property_string(_component_id, "data.script_resource");
  620. }
  621. }
  622. public class AnimationStateMachine : PropertyGrid
  623. {
  624. // Widgets
  625. private ResourceChooserButton _state_machine_resource;
  626. public AnimationStateMachine(Database db, ProjectStore store)
  627. {
  628. base(db);
  629. // Widgets
  630. _state_machine_resource = new ResourceChooserButton(store, "state_machine");
  631. _state_machine_resource.value_changed.connect(on_value_changed);
  632. add_row("State Machine", _state_machine_resource);
  633. }
  634. private void on_value_changed()
  635. {
  636. Unit unit = new Unit(_db, _id);
  637. unit.set_component_property_string(_component_id, "data.state_machine_resource", _state_machine_resource.value);
  638. _db.add_restore_point((int)ActionType.SET_ANIMATION_STATE_MACHINE, new Guid?[] { _id, _component_id });
  639. }
  640. public override void update()
  641. {
  642. Unit unit = new Unit(_db, _id);
  643. _state_machine_resource.value = unit.get_component_property_string(_component_id, "data.state_machine_resource");
  644. }
  645. }
  646. public class UnitView : PropertyGrid
  647. {
  648. // Widgets
  649. private ProjectStore _store;
  650. private ResourceChooserButton _prefab;
  651. private Gtk.MenuButton _component_add;
  652. private Gtk.Box _components;
  653. private Gtk.Popover _add_popover;
  654. private void on_add_component_clicked(Gtk.Button button)
  655. {
  656. Gtk.Application app = ((Gtk.Window)this.get_toplevel()).application;
  657. app.activate_action("unit-add-component", new GLib.Variant.string(button.label));
  658. _add_popover.hide();
  659. }
  660. public static Gtk.Menu component_menu(string object_type)
  661. {
  662. Gtk.Menu menu = new Gtk.Menu();
  663. Gtk.MenuItem mi;
  664. mi = new Gtk.MenuItem.with_label("Remove Component");
  665. mi.activate.connect(() => {
  666. GLib.Application.get_default().activate_action("unit-remove-component", new GLib.Variant.string(object_type));
  667. });
  668. menu.add(mi);
  669. return menu;
  670. }
  671. public UnitView(Database db, ProjectStore store)
  672. {
  673. base(db);
  674. _store = store;
  675. // Widgets
  676. _prefab = new ResourceChooserButton(store, "unit");
  677. _prefab._selector.sensitive = false;
  678. // List of component types.
  679. const string components[] =
  680. {
  681. OBJECT_TYPE_TRANSFORM,
  682. OBJECT_TYPE_LIGHT,
  683. OBJECT_TYPE_CAMERA,
  684. OBJECT_TYPE_MESH_RENDERER,
  685. OBJECT_TYPE_SPRITE_RENDERER,
  686. OBJECT_TYPE_COLLIDER,
  687. OBJECT_TYPE_ACTOR,
  688. OBJECT_TYPE_SCRIPT,
  689. OBJECT_TYPE_ANIMATION_STATE_MACHINE
  690. };
  691. Gtk.Box add_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
  692. for (int cc = 0; cc < components.length; ++cc) {
  693. Gtk.Button mb;
  694. mb = new Gtk.Button.with_label(components[cc]);
  695. mb.clicked.connect(on_add_component_clicked);
  696. add_box.pack_start(mb);
  697. }
  698. add_box.show_all();
  699. _add_popover = new Gtk.Popover(null);
  700. _add_popover.add(add_box);
  701. _component_add = new Gtk.MenuButton();
  702. _component_add.label = "Add Component";
  703. _component_add.set_popover(_add_popover);
  704. _components = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
  705. _components.homogeneous = true;
  706. _components.pack_start(_component_add);
  707. add_row("Prefab", _prefab);
  708. add_row("Components", _components);
  709. }
  710. public override void update()
  711. {
  712. if (_db.has_property(_id, "prefab")) {
  713. _prefab.value = _db.get_property_string(_id, "prefab");
  714. } else {
  715. _prefab.value = "<none>";
  716. }
  717. }
  718. }
  719. public class SoundTransformView : PropertyGrid
  720. {
  721. // Widgets
  722. private EntryVector3 _position;
  723. private EntryRotation _rotation;
  724. public SoundTransformView(Database db)
  725. {
  726. base(db);
  727. // Widgets
  728. _position = new EntryPosition();
  729. _rotation = new EntryRotation();
  730. _position.value_changed.connect(on_value_changed);
  731. _rotation.value_changed.connect(on_value_changed);
  732. add_row("Position", _position);
  733. add_row("Rotation", _rotation);
  734. }
  735. private void on_value_changed()
  736. {
  737. _db.set_property_vector3 (_id, "position", _position.value);
  738. _db.set_property_quaternion(_id, "rotation", _rotation.value);
  739. _db.add_restore_point((int)ActionType.SET_SOUND, new Guid?[] { _id });
  740. }
  741. public override void update()
  742. {
  743. Vector3 pos = _db.get_property_vector3 (_id, "position");
  744. Quaternion rot = _db.get_property_quaternion(_id, "rotation");
  745. _position.value = pos;
  746. _rotation.value = rot;
  747. }
  748. }
  749. public class SoundView : PropertyGrid
  750. {
  751. // Widgets
  752. private ResourceChooserButton _name;
  753. private EntryDouble _range;
  754. private EntryDouble _volume;
  755. private CheckBox _loop;
  756. public SoundView(Database db, ProjectStore store)
  757. {
  758. base(db);
  759. // Widgets
  760. _name = new ResourceChooserButton(store, "sound");
  761. _name.value_changed.connect(on_value_changed);
  762. _range = new EntryDouble(1.0, 0.0, double.MAX);
  763. _range.value_changed.connect(on_value_changed);
  764. _volume = new EntryDouble(1.0, 0.0, 1.0);
  765. _volume.value_changed.connect(on_value_changed);
  766. _loop = new CheckBox();
  767. _loop.value_changed.connect(on_value_changed);
  768. add_row("Name", _name);
  769. add_row("Range", _range);
  770. add_row("Volume", _volume);
  771. add_row("Loop", _loop);
  772. }
  773. private void on_value_changed()
  774. {
  775. _db.set_property_string(_id, "name", _name.value);
  776. _db.set_property_double(_id, "range", _range.value);
  777. _db.set_property_double(_id, "volume", _volume.value);
  778. _db.set_property_bool (_id, "loop", _loop.value);
  779. _db.add_restore_point((int)ActionType.SET_SOUND, new Guid?[] { _id });
  780. }
  781. public override void update()
  782. {
  783. _name.value = _db.get_property_string(_id, "name");
  784. _range.value = _db.get_property_double(_id, "range");
  785. _volume.value = _db.get_property_double(_id, "volume");
  786. _loop.value = _db.get_property_bool (_id, "loop");
  787. }
  788. }
  789. public class PropertiesView : Gtk.Bin
  790. {
  791. public struct ComponentEntry
  792. {
  793. string type;
  794. int position;
  795. }
  796. // Data
  797. private Database _db;
  798. private HashMap<string, Gtk.Expander> _expanders;
  799. private HashMap<string, PropertyGrid> _objects;
  800. private ArrayList<ComponentEntry?> _entries;
  801. private Gee.ArrayList<Guid?>? _selection;
  802. // Widgets
  803. private Gtk.Label _nothing_to_show;
  804. private Gtk.Label _unknown_object_type;
  805. private Gtk.Viewport _viewport;
  806. private Gtk.ScrolledWindow _scrolled_window;
  807. private PropertyGridSet _object_view;
  808. private Gtk.Stack _stack;
  809. [CCode (has_target = false)]
  810. public delegate Gtk.Menu ContextMenu(string object_type);
  811. public PropertiesView(Database db, ProjectStore store)
  812. {
  813. // Data
  814. _db = db;
  815. _expanders = new HashMap<string, Gtk.Expander>();
  816. _objects = new HashMap<string, PropertyGrid>();
  817. _entries = new ArrayList<ComponentEntry?>();
  818. _selection = null;
  819. // Widgets
  820. _object_view = new PropertyGridSet();
  821. _object_view.border_width = 6;
  822. // Unit
  823. register_object_type("Unit", "name", 0, new UnitView(_db, store));
  824. register_object_type("Transform", OBJECT_TYPE_TRANSFORM, 0, new TransformPropertyGrid(_db), UnitView.component_menu);
  825. register_object_type("Light", OBJECT_TYPE_LIGHT, 1, new LightPropertyGrid(_db), UnitView.component_menu);
  826. register_object_type("Camera", OBJECT_TYPE_CAMERA, 2, new CameraPropertyGrid(_db), UnitView.component_menu);
  827. register_object_type("Mesh Renderer", OBJECT_TYPE_MESH_RENDERER, 3, new MeshRendererPropertyGrid(_db, store), UnitView.component_menu);
  828. register_object_type("Sprite Renderer", OBJECT_TYPE_SPRITE_RENDERER, 3, new SpriteRendererPropertyGrid(_db, store), UnitView.component_menu);
  829. register_object_type("Collider", OBJECT_TYPE_COLLIDER, 3, new ColliderPropertyGrid(_db, store), UnitView.component_menu);
  830. register_object_type("Actor", OBJECT_TYPE_ACTOR, 3, new ActorPropertyGrid(_db, store._project), UnitView.component_menu);
  831. register_object_type("Script", OBJECT_TYPE_SCRIPT, 3, new ScriptPropertyGrid(_db, store), UnitView.component_menu);
  832. register_object_type("Animation State Machine", OBJECT_TYPE_ANIMATION_STATE_MACHINE, 3, new AnimationStateMachine(_db, store), UnitView.component_menu);
  833. // Sound
  834. register_object_type("Transform", "sound_transform", 0, new SoundTransformView(_db));
  835. register_object_type("Sound", "sound_properties", 1, new SoundView(_db, store));
  836. _nothing_to_show = new Gtk.Label("Select an object to start editing");
  837. _unknown_object_type = new Gtk.Label("Unknown object type");
  838. _viewport = new Gtk.Viewport(null, null);
  839. _viewport.add(_object_view);
  840. _scrolled_window = new Gtk.ScrolledWindow(null, null);
  841. _scrolled_window.add(_viewport);
  842. _stack = new Gtk.Stack();
  843. _stack.add(_nothing_to_show);
  844. _stack.add(_scrolled_window);
  845. _stack.add(_unknown_object_type);
  846. this.add(_stack);
  847. this.get_style_context().add_class("properties-view");
  848. store._project.project_reset.connect(on_project_reset);
  849. }
  850. private void register_object_type(string label, string object_type, int position, PropertyGrid cv, ContextMenu? action = null)
  851. {
  852. Gtk.Expander expander = _object_view.add_property_grid(cv, label);
  853. if (action != null) {
  854. expander.button_release_event.connect((ev) => {
  855. if (ev.button == Gdk.BUTTON_SECONDARY) {
  856. Gtk.Menu menu = action(object_type);
  857. menu.show_all();
  858. menu.popup_at_pointer(ev);
  859. return Gdk.EVENT_STOP;
  860. }
  861. return Gdk.EVENT_PROPAGATE;
  862. });
  863. }
  864. _objects[object_type] = cv;
  865. _expanders[object_type] = expander;
  866. _entries.add({ object_type, position });
  867. }
  868. public void show_unit(Guid id)
  869. {
  870. _stack.set_visible_child(_scrolled_window);
  871. foreach (var entry in _entries) {
  872. Gtk.Expander expander = _expanders[entry.type];
  873. Unit unit = new Unit(_db, id);
  874. Guid component_id;
  875. Guid owner_id;
  876. if (unit.has_component_with_owner(out component_id, out owner_id, entry.type) || entry.type == "name") {
  877. PropertyGrid cv = _objects[entry.type];
  878. cv._id = id;
  879. cv._component_id = component_id;
  880. cv.update();
  881. if (id == owner_id)
  882. expander.get_style_context().remove_class("inherited");
  883. else
  884. expander.get_style_context().add_class("inherited");
  885. expander.show_all();
  886. } else {
  887. expander.hide();
  888. }
  889. }
  890. }
  891. public void show_sound_source(Guid id)
  892. {
  893. _stack.set_visible_child(_scrolled_window);
  894. foreach (var entry in _entries) {
  895. Gtk.Expander expander = _expanders[entry.type];
  896. if (entry.type == "sound_transform" || entry.type == "sound_properties") {
  897. PropertyGrid cv = _objects[entry.type];
  898. cv._id = id;
  899. cv.update();
  900. expander.show_all();
  901. } else {
  902. expander.hide();
  903. }
  904. }
  905. }
  906. public void show_or_hide_properties()
  907. {
  908. if (_selection == null || _selection.size != 1) {
  909. _stack.set_visible_child(_nothing_to_show);
  910. return;
  911. }
  912. Guid id = _selection[_selection.size - 1];
  913. if (!_db.has_object(id))
  914. return;
  915. if (_db.object_type(id) == OBJECT_TYPE_UNIT)
  916. show_unit(id);
  917. else if (_db.object_type(id) == OBJECT_TYPE_SOUND_SOURCE)
  918. show_sound_source(id);
  919. else
  920. _stack.set_visible_child(_unknown_object_type);
  921. }
  922. public void on_selection_changed(Gee.ArrayList<Guid?> selection)
  923. {
  924. _selection = selection;
  925. show_or_hide_properties();
  926. }
  927. public override void map()
  928. {
  929. base.map();
  930. show_or_hide_properties();
  931. }
  932. public void on_project_reset()
  933. {
  934. foreach (var obj in _objects) {
  935. PropertyGrid cv = obj.value;
  936. cv._id = GUID_ZERO;
  937. cv._component_id = GUID_ZERO;
  938. }
  939. }
  940. }
  941. } /* namespace Crown */