texture_settings_dialog.vala 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. /*
  2. * Copyright (c) 2012-2025 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. namespace Crown
  6. {
  7. public class TextureSettingsDialog : Gtk.Window
  8. {
  9. public Project _project;
  10. public Database _database;
  11. public Database _texture_database;
  12. public Guid _texture_id;
  13. public PropertyGridSet _texture_set;
  14. public Gtk.ListStore _platforms_store;
  15. public Gtk.TreeView _platforms;
  16. public Gtk.Stack _stack;
  17. public bool _never_opened_before;
  18. public string _texture_path;
  19. // Input page.
  20. public InputResource _texture_name;
  21. public InputString _source;
  22. // Output page.
  23. public InputEnum _format;
  24. public InputBool _generate_mips;
  25. public InputDouble _mip_skip_smallest;
  26. public InputBool _normal_map;
  27. public Gtk.Box _box;
  28. public Gtk.EventControllerKey _controller_key;
  29. public Gtk.Button _cancel;
  30. public Gtk.Button _save;
  31. public Gtk.HeaderBar _header_bar;
  32. public signal void texture_saved();
  33. private void text_func(Gtk.CellLayout cell_layout, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
  34. {
  35. Value? platform;
  36. model.get_value(iter, 0, out platform);
  37. cell.set_property("text", ((TargetPlatform)platform).to_label());
  38. }
  39. public TextureSettingsDialog(Project project, Database database)
  40. {
  41. _project = project;
  42. _database = database;
  43. _texture_database = new Database(project);
  44. _texture_id = GUID_ZERO;
  45. _platforms_store = new Gtk.ListStore(1
  46. , typeof(TargetPlatform) // platform name
  47. );
  48. for (int p = 0; p < TargetPlatform.COUNT; ++p) {
  49. Gtk.TreeIter iter;
  50. _platforms_store.insert_with_values(out iter, -1, 0, (TargetPlatform)p, -1);
  51. }
  52. Gtk.CellRendererText text_renderer = new Gtk.CellRendererText();
  53. Gtk.TreeViewColumn column = new Gtk.TreeViewColumn.with_attributes("Target Platform", text_renderer, null);
  54. column.set_cell_data_func(text_renderer, text_func);
  55. _platforms = new Gtk.TreeView.with_model(_platforms_store);
  56. _platforms.append_column(column);
  57. _platforms.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE);
  58. _platforms.get_selection().changed.connect(on_platforms_selection_changed);
  59. this.set_icon_name(CROWN_EDITOR_ICON_NAME);
  60. _texture_set = new PropertyGridSet();
  61. _texture_path = "";
  62. // Input grid.
  63. _texture_name = new InputResource(OBJECT_TYPE_TEXTURE, database);
  64. _texture_name.value_changed.connect(on_texture_resource_value_changed);
  65. _source = new InputString();
  66. _source.sensitive = false;
  67. PropertyGrid cv;
  68. cv = new PropertyGrid();
  69. cv.column_homogeneous = true;
  70. cv.add_row("Name", _texture_name);
  71. _texture_set.add_property_grid(cv, "Texture");
  72. cv = new PropertyGrid();
  73. cv.column_homogeneous = true;
  74. cv.add_row("Source", _source);
  75. _texture_set.add_property_grid(cv, "Input");
  76. // Output grid.
  77. string[] texture_formats = new string[TextureFormat.COUNT];
  78. for (int tf = 0; tf < TextureFormat.COUNT; ++tf)
  79. texture_formats[tf] = ((TextureFormat)tf).to_key();
  80. _format = new InputEnum(texture_formats[TextureFormat.BC1]
  81. , texture_formats
  82. , texture_formats
  83. );
  84. _format.value_changed.connect(on_format_value_changed);
  85. _generate_mips = new InputBool();
  86. _generate_mips.value = true;
  87. _generate_mips.value_changed.connect(on_generate_mips_value_changed);
  88. _mip_skip_smallest = new InputDouble(0, 0, 32);
  89. _mip_skip_smallest.value_changed.connect(on_mip_skip_smallest_value_changed);
  90. _normal_map = new InputBool();
  91. _normal_map.value = false;
  92. _normal_map.value_changed.connect(on_normal_map_value_changed);
  93. cv = new PropertyGrid();
  94. cv.column_homogeneous = true;
  95. cv.add_row("Format", _format);
  96. cv.add_row("Generate Mips", _generate_mips);
  97. cv.add_row("Skip Smallest Mips", _mip_skip_smallest);
  98. cv.add_row("Normal Map", _normal_map);
  99. _texture_set.add_property_grid(cv, "Output");
  100. _stack = new Gtk.Stack();
  101. _stack.add_named(new Gtk.Label("Select one or more platforms to change its settings"), "none-selected");
  102. _stack.add_named(_texture_set, "some-selected");
  103. _box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
  104. _box.pack_start(_platforms, false, true, 0);
  105. _box.pack_start(_stack, false, true, 0);
  106. _box.vexpand = true;
  107. this.add(_box);
  108. _controller_key = new Gtk.EventControllerKey(this);
  109. _controller_key.key_pressed.connect((keyval, keycode, state) => {
  110. if (keyval == Gdk.Key.Escape) {
  111. close();
  112. return Gdk.EVENT_STOP;
  113. }
  114. return Gdk.EVENT_PROPAGATE;
  115. });
  116. _cancel = new Gtk.Button.with_label("Cancel");
  117. _cancel.clicked.connect(() => {
  118. close();
  119. });
  120. _save = new Gtk.Button.with_label("Save & Reload");
  121. _save.get_style_context().add_class("suggested-action");
  122. _save.clicked.connect(() => {
  123. save();
  124. });
  125. _header_bar = new Gtk.HeaderBar();
  126. _header_bar.title = "Texture Settings";
  127. _header_bar.show_close_button = true;
  128. _header_bar.pack_start(_cancel);
  129. _header_bar.pack_end(_save);
  130. this.set_titlebar(_header_bar);
  131. _never_opened_before = true;
  132. _stack.map.connect(on_stack_map);
  133. this.delete_event.connect(on_delete_event);
  134. }
  135. public void on_stack_map()
  136. {
  137. if (_never_opened_before) {
  138. _never_opened_before = false;
  139. TargetPlatform host_platform = TargetPlatform.COUNT;
  140. #if CROWN_PLATFORM_WINDOWS
  141. host_platform = TargetPlatform.WINDOWS;
  142. #elif CROWN_PLATFORM_LINUX
  143. host_platform = TargetPlatform.LINUX;
  144. #endif
  145. if (host_platform == TargetPlatform.COUNT) {
  146. _platforms.get_selection().select_path(new Gtk.TreePath.first());
  147. } else {
  148. _platforms_store.foreach((model, path, iter) => {
  149. Value platform;
  150. model.get_value(iter, 0, out platform);
  151. if (((TargetPlatform)platform) == host_platform) {
  152. _platforms.get_selection().select_iter(iter);
  153. return true;
  154. }
  155. return false;
  156. });
  157. }
  158. }
  159. _platforms.grab_focus();
  160. }
  161. public int load_texture(string texture_name)
  162. {
  163. string new_texture_path = texture_name + ".texture";
  164. if (_texture_path != new_texture_path)
  165. save();
  166. _texture_database.reset();
  167. if (_texture_database.add_from_resource_path(out _texture_id, new_texture_path) != 0) {
  168. _texture_id = GUID_ZERO;
  169. return -1;
  170. }
  171. _texture_path = new_texture_path;
  172. on_platforms_selection_changed();
  173. return 0;
  174. }
  175. public void set_texture(string texture_name)
  176. {
  177. if (load_texture(texture_name) == 0)
  178. _texture_name.value = texture_name;
  179. }
  180. public void on_texture_resource_value_changed()
  181. {
  182. load_texture(_texture_name.value);
  183. }
  184. public bool are_values_equal(Value? a, Value? b)
  185. {
  186. if (a.type() != b.type())
  187. return false;
  188. if (a.holds(typeof(bool))) {
  189. return (bool)a == (bool)b;
  190. } else if (a.holds(typeof(double))) {
  191. return (double)a == (double)b;
  192. } else if (a.holds(typeof(string))) {
  193. return (string)a == (string)b;
  194. } else if (a == null && b == null) {
  195. return true;
  196. }
  197. return false;
  198. }
  199. public void on_platforms_selection_changed()
  200. {
  201. if (_texture_id == GUID_ZERO)
  202. return;
  203. if (_platforms.get_selection().count_selected_rows() > 0) {
  204. _stack.set_visible_child_full("some-selected", Gtk.StackTransitionType.NONE);
  205. } else {
  206. _stack.set_visible_child_full("none-selected", Gtk.StackTransitionType.NONE);
  207. return;
  208. }
  209. string property_names[] = { "source", "format", "generate_mips", "mip_skip_smallest", "normal_map" };
  210. InputField properties[] = { _source, _format, _generate_mips, _mip_skip_smallest, _normal_map };
  211. _format.value_changed.disconnect(on_format_value_changed);
  212. _generate_mips.value_changed.disconnect(on_generate_mips_value_changed);
  213. _mip_skip_smallest.value_changed.disconnect(on_mip_skip_smallest_value_changed);
  214. _normal_map.value_changed.disconnect(on_normal_map_value_changed);
  215. for (int i = 0; i < properties.length; ++i)
  216. properties[i].set_data("init", false);
  217. for (int i = 0; i < properties.length; ++i) {
  218. _platforms.get_selection().selected_foreach((model, path, iter) => {
  219. Value? platform;
  220. model.get_value(iter, 0, out platform);
  221. string key = platform_property(((TargetPlatform)platform).to_key(), property_names[i]);
  222. bool init = properties[i].get_data<bool>("init");
  223. // Try <platform>.<property> first. Fallback to <property>.
  224. if (!_texture_database.has_property(_texture_id, key))
  225. key = property_names[i];
  226. if (_texture_database.has_property(_texture_id, key)) {
  227. Value? val = _texture_database.get_property(_texture_id, key);
  228. if (!init) {
  229. properties[i].set_data("init", true);
  230. properties[i].set_union_value(val);
  231. properties[i].set_inconsistent(false);
  232. } else if (!are_values_equal(val, properties[i].union_value())) {
  233. properties[i].set_inconsistent(true);
  234. }
  235. } else {
  236. properties[i].set_inconsistent(true);
  237. if (!init) {
  238. properties[i].set_data("init", true);
  239. }
  240. }
  241. });
  242. }
  243. _format.value_changed.connect(on_format_value_changed);
  244. _generate_mips.value_changed.connect(on_generate_mips_value_changed);
  245. _mip_skip_smallest.value_changed.connect(on_mip_skip_smallest_value_changed);
  246. _normal_map.value_changed.connect(on_normal_map_value_changed);
  247. }
  248. public void on_format_value_changed()
  249. {
  250. on_property_value_changed("format", _format);
  251. }
  252. public void on_generate_mips_value_changed()
  253. {
  254. on_property_value_changed("generate_mips", _generate_mips);
  255. }
  256. public void on_mip_skip_smallest_value_changed()
  257. {
  258. on_property_value_changed("mip_skip_smallest", _mip_skip_smallest);
  259. }
  260. public void on_normal_map_value_changed()
  261. {
  262. on_property_value_changed("normal_map", _normal_map);
  263. }
  264. public void on_property_value_changed(string property_name, InputField property_value)
  265. {
  266. if (_texture_id == GUID_ZERO)
  267. return;
  268. Value val = property_value.union_value();
  269. // For backward compatibility.
  270. if (property_name == "generate_mips" || property_name == "normal_map") {
  271. if (_texture_database.has_property(_texture_id, property_name))
  272. _texture_database.set_property(_texture_id, property_name, val);
  273. }
  274. _platforms.get_selection().selected_foreach((model, path, iter) => {
  275. Value? platform;
  276. model.get_value(iter, 0, out platform);
  277. string key = platform_property(((TargetPlatform)platform).to_key(), property_name);
  278. _texture_database.set_property(_texture_id, key, val);
  279. });
  280. }
  281. public void save()
  282. {
  283. if (_texture_id == GUID_ZERO)
  284. return;
  285. if (_texture_database.dump(_project.absolute_path(_texture_path), _texture_id) == 0)
  286. texture_saved();
  287. }
  288. public string platform_property(string platform_name, string property)
  289. {
  290. return "output."
  291. + platform_name
  292. + "."
  293. + property
  294. ;
  295. }
  296. public bool on_delete_event(Gdk.EventAny event)
  297. {
  298. _texture_id = GUID_ZERO;
  299. return Gdk.EVENT_PROPAGATE;
  300. }
  301. }
  302. } /* namespace Crown */