properties_view.vala 34 KB

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