resource_chooser.vala 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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. // Returns true if the item should be filtered out
  10. private bool user_filter(string type, string name)
  11. {
  12. return (type == "unit" || type == "sound") && !name.has_prefix("core/");
  13. }
  14. public delegate bool UserFilter(string type, string name);
  15. public class ResourceChooser : Gtk.Box
  16. {
  17. // Data
  18. public Project _project;
  19. public Gtk.ListStore _list_store;
  20. public Gtk.Stack? _editor_stack;
  21. public RuntimeInstance? _resource_preview;
  22. public unowned UserFilter _user_filter;
  23. public string _name;
  24. // Widgets
  25. public EntrySearch _filter_entry;
  26. public Gtk.TreeModelFilter _tree_filter;
  27. public Gtk.TreeModelSort _tree_sort;
  28. public Gtk.TreeView _tree_view;
  29. public Gtk.TreeSelection _tree_selection;
  30. public Gtk.ScrolledWindow _scrolled_window;
  31. // Signals
  32. public signal void resource_selected(string type, string name);
  33. public ResourceChooser(Project? project
  34. , ProjectStore project_store
  35. , Gtk.Stack? editor_stack = null
  36. , RuntimeInstance? resource_preview = null
  37. )
  38. {
  39. Object(orientation: Gtk.Orientation.VERTICAL, spacing: 0);
  40. // Data
  41. _project = project;
  42. _list_store = project_store._list_store;
  43. _editor_stack = editor_stack;
  44. _resource_preview = resource_preview;
  45. _user_filter = user_filter;
  46. // Widgets
  47. _filter_entry = new EntrySearch();
  48. _filter_entry.set_placeholder_text("Search...");
  49. _filter_entry.search_changed.connect(on_filter_entry_text_changed);
  50. _filter_entry.key_press_event.connect(on_filter_entry_key_pressed);
  51. _tree_filter = new Gtk.TreeModelFilter(_list_store, null);
  52. _tree_filter.set_visible_func((model, iter) => {
  53. Value type;
  54. Value name;
  55. model.get_value(iter, ProjectStore.Column.TYPE, out type);
  56. model.get_value(iter, ProjectStore.Column.NAME, out name);
  57. string type_str = (string)type;
  58. string name_str = (string)name;
  59. return type_str != null
  60. && name_str != null
  61. && _user_filter(type_str, name_str)
  62. && (_filter_entry.text.length == 0 || name_str.index_of(_filter_entry.text) > -1)
  63. ;
  64. });
  65. _tree_sort = new Gtk.TreeModelSort.with_model(_tree_filter);
  66. _tree_sort.set_default_sort_func((model, iter_a, iter_b) => {
  67. Value id_a;
  68. Value id_b;
  69. model.get_value(iter_a, ProjectStore.Column.NAME, out id_a);
  70. model.get_value(iter_b, ProjectStore.Column.NAME, out id_b);
  71. return strcmp((string)id_a, (string)id_b);
  72. });
  73. _tree_view = new Gtk.TreeView();
  74. _tree_view.insert_column_with_attributes(-1
  75. , "Name"
  76. , new Gtk.CellRendererText()
  77. , "text"
  78. , ProjectStore.Column.NAME
  79. , null
  80. );
  81. #if 0
  82. // For debugging.
  83. _tree_view.insert_column_with_attributes(-1
  84. , "Type"
  85. , new Gtk.CellRendererText()
  86. , "text"
  87. , ProjectStore.Column.TYPE
  88. , null
  89. );
  90. #endif
  91. _tree_view.model = _tree_sort;
  92. _tree_view.headers_visible = false;
  93. _tree_view.can_focus = false;
  94. _tree_view.row_activated.connect(on_row_activated);
  95. _tree_view.button_release_event.connect(on_button_released);
  96. _tree_selection = _tree_view.get_selection();
  97. _tree_selection.set_mode(Gtk.SelectionMode.BROWSE);
  98. _tree_selection.changed.connect(on_tree_selection_changed);
  99. _scrolled_window = new Gtk.ScrolledWindow(null, null);
  100. _scrolled_window.add(_tree_view);
  101. _scrolled_window.set_size_request(300, 400);
  102. this.pack_start(_filter_entry, false, true, 0);
  103. if (_editor_stack != null)
  104. this.pack_start(_editor_stack, true, true, 0);
  105. this.pack_start(_scrolled_window, true, true, 0);
  106. this.unmap.connect(on_unmap);
  107. }
  108. private void on_row_activated(Gtk.TreePath path, TreeViewColumn column)
  109. {
  110. Gtk.TreePath filter_path = _tree_sort.convert_path_to_child_path(path);
  111. Gtk.TreePath child_path = _tree_filter.convert_path_to_child_path(filter_path);
  112. Gtk.TreeIter iter;
  113. if (_list_store.get_iter(out iter, child_path)) {
  114. Value name;
  115. Value type;
  116. _list_store.get_value(iter, ProjectStore.Column.NAME, out name);
  117. _list_store.get_value(iter, ProjectStore.Column.TYPE, out type);
  118. _name = (string)name;
  119. resource_selected((string)type, (string)name);
  120. }
  121. }
  122. private bool on_button_released(Gdk.EventButton ev)
  123. {
  124. if (ev.button == Gdk.BUTTON_PRIMARY) {
  125. Gtk.TreePath path;
  126. if (_tree_view.get_path_at_pos((int)ev.x, (int)ev.y, out path, null, null, null)) {
  127. if (_tree_view.get_selection().path_is_selected(path)) {
  128. Gtk.TreePath filter_path = _tree_sort.convert_path_to_child_path(path);
  129. Gtk.TreePath child_path = _tree_filter.convert_path_to_child_path(filter_path);
  130. Gtk.TreeIter iter;
  131. if (_list_store.get_iter(out iter, child_path)) {
  132. Value name;
  133. Value type;
  134. _list_store.get_value(iter, ProjectStore.Column.NAME, out name);
  135. _list_store.get_value(iter, ProjectStore.Column.TYPE, out type);
  136. _name = (string)name;
  137. resource_selected((string)type, (string)name);
  138. }
  139. }
  140. }
  141. }
  142. return Gdk.EVENT_PROPAGATE;
  143. }
  144. private void on_unmap()
  145. {
  146. _filter_entry.text = "";
  147. }
  148. private void on_filter_entry_text_changed()
  149. {
  150. _tree_selection.changed.disconnect(on_tree_selection_changed);
  151. _tree_filter.refilter();
  152. _tree_selection.changed.connect(on_tree_selection_changed);
  153. _tree_view.set_cursor(new Gtk.TreePath.first(), null, false);
  154. }
  155. private bool on_filter_entry_key_pressed(Gdk.EventKey ev)
  156. {
  157. Gtk.TreeModel model;
  158. Gtk.TreeIter iter;
  159. bool selected = _tree_selection.get_selected(out model, out iter);
  160. if (ev.keyval == Gdk.Key.Down) {
  161. if (selected && model.iter_next(ref iter)) {
  162. _tree_selection.select_iter(iter);
  163. _tree_view.scroll_to_cell(model.get_path(iter), null, true, 1.0f, 0.0f);
  164. }
  165. return Gdk.EVENT_STOP;
  166. } else if (ev.keyval == Gdk.Key.Up) {
  167. if (selected && model.iter_previous(ref iter)) {
  168. _tree_selection.select_iter(iter);
  169. _tree_view.scroll_to_cell(model.get_path(iter), null, true, 1.0f, 0.0f);
  170. }
  171. return Gdk.EVENT_STOP;
  172. } else if (ev.keyval == Gdk.Key.Return) {
  173. if (selected) {
  174. Value name;
  175. Value type;
  176. model.get_value(iter, ProjectStore.Column.NAME, out name);
  177. model.get_value(iter, ProjectStore.Column.TYPE, out type);
  178. _name = (string)name;
  179. resource_selected((string)type, (string)name);
  180. }
  181. return Gdk.EVENT_STOP;
  182. }
  183. return Gdk.EVENT_PROPAGATE;
  184. }
  185. private void on_tree_selection_changed()
  186. {
  187. if (_editor_stack != null) {
  188. Gtk.TreeModel model;
  189. Gtk.TreeIter iter;
  190. if (_tree_selection.get_selected(out model, out iter)) {
  191. Value name;
  192. Value type;
  193. model.get_value(iter, ProjectStore.Column.NAME, out name);
  194. model.get_value(iter, ProjectStore.Column.TYPE, out type);
  195. _resource_preview.send_script(UnitPreviewApi.set_preview_resource((string)type, (string)name));
  196. _resource_preview.send(DeviceApi.frame());
  197. }
  198. }
  199. }
  200. public void set_type_filter(owned UserFilter filter)
  201. {
  202. _user_filter = filter;
  203. _tree_filter.refilter();
  204. }
  205. }
  206. } /* namespace Crown */