resource_chooser.vala 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * Copyright (c) 2012-2026 Daniele Bartolini et al.
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. namespace Crown
  6. {
  7. // Returns true if the item should be filtered out
  8. public bool user_filter(string type, string name)
  9. {
  10. return (type == OBJECT_TYPE_UNIT || type == OBJECT_TYPE_SOUND) && !name.has_prefix("core/");
  11. }
  12. public delegate bool UserFilter(string type, string name);
  13. public class ResourceChooser : Gtk.Box
  14. {
  15. // Data
  16. public Project _project;
  17. public Gtk.ListStore _list_store;
  18. public Gtk.Stack? _editor_stack;
  19. public RuntimeInstance? _resource_preview;
  20. public unowned UserFilter _user_filter;
  21. public string _name;
  22. // Widgets
  23. public EntrySearch _filter_entry;
  24. public Gtk.EventControllerKey _filter_entry_controller_key;
  25. public Gtk.TreeModelFilter _tree_filter;
  26. public Gtk.TreeModelSort _tree_sort;
  27. public Gtk.TreeView _tree_view;
  28. public Gtk.GestureMultiPress _tree_view_gesture_click;
  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_controller_key = new Gtk.EventControllerKey(_filter_entry);
  51. _filter_entry_controller_key.key_pressed.connect(on_filter_entry_key_pressed);
  52. _tree_filter = new Gtk.TreeModelFilter(_list_store, null);
  53. _tree_filter.set_visible_func((model, iter) => {
  54. Value type;
  55. Value name;
  56. model.get_value(iter, ProjectStore.Column.TYPE, out type);
  57. model.get_value(iter, ProjectStore.Column.NAME, out name);
  58. string type_str = (string)type;
  59. string name_str = (string)name;
  60. return type_str != null
  61. && name_str != null
  62. && _user_filter(type_str, name_str)
  63. && (_filter_entry.text.length == 0 || name_str.index_of(_filter_entry.text) > -1)
  64. ;
  65. });
  66. _tree_sort = new Gtk.TreeModelSort.with_model(_tree_filter);
  67. _tree_sort.set_default_sort_func((model, iter_a, iter_b) => {
  68. Value id_a;
  69. Value id_b;
  70. model.get_value(iter_a, ProjectStore.Column.NAME, out id_a);
  71. model.get_value(iter_b, ProjectStore.Column.NAME, out id_b);
  72. return strcmp((string)id_a, (string)id_b);
  73. });
  74. _tree_view = new Gtk.TreeView();
  75. _tree_view.insert_column_with_attributes(-1
  76. , "Name"
  77. , new Gtk.CellRendererText()
  78. , "text"
  79. , ProjectStore.Column.NAME
  80. , null
  81. );
  82. #if 0
  83. // For debugging.
  84. _tree_view.insert_column_with_attributes(-1
  85. , "Type"
  86. , new Gtk.CellRendererText()
  87. , "text"
  88. , ProjectStore.Column.TYPE
  89. , null
  90. );
  91. #endif
  92. _tree_view.model = _tree_sort;
  93. _tree_view.headers_visible = false;
  94. _tree_view.can_focus = false;
  95. _tree_view.row_activated.connect(on_row_activated);
  96. _tree_view_gesture_click = new Gtk.GestureMultiPress(_tree_view);
  97. _tree_view_gesture_click.set_button(0);
  98. _tree_view_gesture_click.released.connect(on_button_released);
  99. _tree_selection = _tree_view.get_selection();
  100. _tree_selection.set_mode(Gtk.SelectionMode.BROWSE);
  101. _tree_selection.changed.connect(on_tree_selection_changed);
  102. _scrolled_window = new Gtk.ScrolledWindow(null, null);
  103. _scrolled_window.add(_tree_view);
  104. _scrolled_window.set_size_request(300, 400);
  105. this.pack_start(_filter_entry, false, true, 0);
  106. if (_editor_stack != null)
  107. this.pack_start(_editor_stack, true, true, 0);
  108. this.pack_start(_scrolled_window, true, true, 0);
  109. this.unmap.connect(on_unmap);
  110. }
  111. public void on_row_activated(Gtk.TreePath path, Gtk.TreeViewColumn column)
  112. {
  113. Gtk.TreePath filter_path = _tree_sort.convert_path_to_child_path(path);
  114. Gtk.TreePath child_path = _tree_filter.convert_path_to_child_path(filter_path);
  115. Gtk.TreeIter iter;
  116. if (_list_store.get_iter(out iter, child_path)) {
  117. Value name;
  118. Value type;
  119. _list_store.get_value(iter, ProjectStore.Column.NAME, out name);
  120. _list_store.get_value(iter, ProjectStore.Column.TYPE, out type);
  121. _name = (string)name;
  122. resource_selected((string)type, (string)name);
  123. }
  124. }
  125. public void on_button_released(int n_press, double x, double y)
  126. {
  127. uint button = _tree_view_gesture_click.get_current_button();
  128. if (button == Gdk.BUTTON_PRIMARY) {
  129. int bx;
  130. int by;
  131. Gtk.TreePath path;
  132. _tree_view.convert_widget_to_bin_window_coords((int)x, (int)y, out bx, out by);
  133. if (_tree_view.get_path_at_pos(bx, by, out path, null, null, null)) {
  134. if (_tree_view.get_selection().path_is_selected(path)) {
  135. Gtk.TreePath filter_path = _tree_sort.convert_path_to_child_path(path);
  136. Gtk.TreePath child_path = _tree_filter.convert_path_to_child_path(filter_path);
  137. Gtk.TreeIter iter;
  138. if (_list_store.get_iter(out iter, child_path)) {
  139. Value name;
  140. Value type;
  141. _list_store.get_value(iter, ProjectStore.Column.NAME, out name);
  142. _list_store.get_value(iter, ProjectStore.Column.TYPE, out type);
  143. _name = (string)name;
  144. resource_selected((string)type, (string)name);
  145. }
  146. }
  147. }
  148. }
  149. }
  150. public void on_unmap()
  151. {
  152. _filter_entry.text = "";
  153. }
  154. public void on_filter_entry_text_changed()
  155. {
  156. _tree_selection.changed.disconnect(on_tree_selection_changed);
  157. _tree_filter.refilter();
  158. _tree_selection.changed.connect(on_tree_selection_changed);
  159. _tree_view.set_cursor(new Gtk.TreePath.first(), null, false);
  160. }
  161. public bool on_filter_entry_key_pressed(uint keyval, uint keycode, Gdk.ModifierType state)
  162. {
  163. Gtk.TreeModel model;
  164. Gtk.TreeIter iter;
  165. bool selected = _tree_selection.get_selected(out model, out iter);
  166. if (keyval == Gdk.Key.Down) {
  167. if (selected && model.iter_next(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 (keyval == Gdk.Key.Up) {
  173. if (selected && model.iter_previous(ref iter)) {
  174. _tree_selection.select_iter(iter);
  175. _tree_view.scroll_to_cell(model.get_path(iter), null, true, 1.0f, 0.0f);
  176. }
  177. return Gdk.EVENT_STOP;
  178. } else if (keyval == Gdk.Key.Return) {
  179. if (selected) {
  180. Value name;
  181. Value type;
  182. model.get_value(iter, ProjectStore.Column.NAME, out name);
  183. model.get_value(iter, ProjectStore.Column.TYPE, out type);
  184. _name = (string)name;
  185. resource_selected((string)type, (string)name);
  186. }
  187. return Gdk.EVENT_STOP;
  188. }
  189. return Gdk.EVENT_PROPAGATE;
  190. }
  191. public void on_tree_selection_changed()
  192. {
  193. if (_editor_stack != null) {
  194. Gtk.TreeModel model;
  195. Gtk.TreeIter iter;
  196. if (_tree_selection.get_selected(out model, out iter)) {
  197. Value name;
  198. Value type;
  199. model.get_value(iter, ProjectStore.Column.NAME, out name);
  200. model.get_value(iter, ProjectStore.Column.TYPE, out type);
  201. _resource_preview.send_script(UnitPreviewApi.set_preview_resource((string)type, (string)name));
  202. _resource_preview.send(DeviceApi.frame());
  203. }
  204. }
  205. }
  206. public void set_type_filter(owned UserFilter filter)
  207. {
  208. _user_filter = filter;
  209. _tree_filter.refilter();
  210. }
  211. }
  212. } /* namespace Crown */