panel_projects_list.vala 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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 ProjectRow : Gtk.ListBoxRow
  8. {
  9. public Gtk.Box _vbox;
  10. public Gtk.Box _hbox;
  11. public Gtk.Label _name;
  12. public Gtk.Label _source_dir;
  13. public Gtk.Button _remove_button;
  14. public Gtk.Button _open_button;
  15. public PanelProjectsList _projects_list;
  16. public ProjectRow(string source_dir, string time, string name, PanelProjectsList pl)
  17. {
  18. this.set_data("source_dir", source_dir);
  19. this.set_data("mtime", time);
  20. _projects_list = pl;
  21. _vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
  22. _name = new Gtk.Label(null);
  23. _name.set_margin_start(12);
  24. _name.set_margin_end(12);
  25. _name.set_margin_top(8);
  26. _name.set_margin_bottom(8);
  27. _name.set_markup("<b>%s</b>".printf(name));
  28. _name.set_xalign(0.0f);
  29. _vbox.pack_start(_name);
  30. _source_dir = new Gtk.Label(null);
  31. _source_dir.set_margin_start(12);
  32. _source_dir.set_margin_end(12);
  33. _source_dir.set_margin_bottom(8);
  34. _source_dir.set_markup("<small>%s</small>".printf(source_dir));
  35. _source_dir.set_xalign(0.0f);
  36. _vbox.pack_start(_source_dir);
  37. _hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6);
  38. _hbox.pack_start(_vbox);
  39. _remove_button = new Gtk.Button.from_icon_name("list-remove-symbolic");
  40. _remove_button.get_style_context().add_class("flat");
  41. _remove_button.get_style_context().add_class("destructive-action");
  42. _remove_button.set_halign(Gtk.Align.CENTER);
  43. _remove_button.set_valign(Gtk.Align.CENTER);
  44. _remove_button.set_margin_end(12);
  45. _hbox.pack_end(_remove_button, false, false, 0);
  46. _open_button = new Gtk.Button.with_label("Open");
  47. _open_button.get_style_context().add_class("flat");
  48. _open_button.set_halign(Gtk.Align.CENTER);
  49. _open_button.set_valign(Gtk.Align.CENTER);
  50. // _open_button.set_margin_end(12);
  51. _hbox.pack_end(_open_button, false, false, 0);
  52. _remove_button.clicked.connect(on_remove_button_clicked);
  53. _open_button.clicked.connect(on_open_button_clicked);
  54. this.add(_hbox);
  55. }
  56. public void on_remove_button_clicked()
  57. {
  58. GLib.Application.get_default().activate_action("remove-project"
  59. , new GLib.Variant.string(this.get_data("source_dir"))
  60. );
  61. }
  62. public void on_open_button_clicked()
  63. {
  64. GLib.Application.get_default().activate_action("open-project"
  65. , new GLib.Variant.string(this.get_data("source_dir"))
  66. );
  67. }
  68. }
  69. public class PanelProjectsList : Gtk.ScrolledWindow
  70. {
  71. // Data
  72. public User _user;
  73. // Widgets
  74. public Gtk.Label _projects_list_label;
  75. public Gtk.Label _local_label;
  76. public Gtk.Box _project_list_empty;
  77. public Gtk.ListBox _list_projects;
  78. public Gtk.Button _button_import_project;
  79. public Gtk.Button _button_new_project;
  80. public Gtk.Box _buttons_box;
  81. public Gtk.Box _projects_box;
  82. public Clamp _clamp;
  83. public PanelProjectsList(User user)
  84. {
  85. this.shadow_type = Gtk.ShadowType.NONE;
  86. // Data
  87. _user = user;
  88. _projects_list_label = new Gtk.Label(null);
  89. _projects_list_label.xalign = 0;
  90. _projects_list_label.set_markup("<span font_weight=\"bold\" size=\"x-large\">Projects</span>");
  91. _local_label = new Gtk.Label("Local");
  92. _local_label.set_markup("<span font_weight=\"bold\">Local</span>");
  93. _project_list_empty = new Gtk.Box(Gtk.Orientation.VERTICAL, 6);
  94. _project_list_empty.margin_top = 12;
  95. _project_list_empty.margin_bottom = 12;
  96. var label = new Gtk.Label(null);
  97. label.set_markup("<span font_size=\"large\"><b>No projects found</b></span>");
  98. _project_list_empty.pack_start(label, false, false);
  99. label = new Gtk.Label(null);
  100. label.set_markup("Use the buttons above to create a new project or import an already existing one.");
  101. _project_list_empty.pack_start(label, false, false);
  102. _project_list_empty.show_all();
  103. _list_projects = new Gtk.ListBox();
  104. _list_projects.set_placeholder(_project_list_empty);
  105. _list_projects.set_sort_func((row1, row2) => {
  106. int64 mtime1 = int64.parse(row1.get_data("mtime"));
  107. int64 mtime2 = int64.parse(row2.get_data("mtime"));
  108. return mtime1 > mtime2 ? -1 : 1; // LRU
  109. });
  110. _button_import_project = new Gtk.Button.with_label("Import...");
  111. _button_import_project.clicked.connect(() => {
  112. GLib.Application.get_default().activate_action("add-project", null);
  113. });
  114. _button_new_project = new Gtk.Button.with_label("Create New");
  115. _button_new_project.get_style_context().add_class("suggested-action");
  116. _button_new_project.clicked.connect(() => {
  117. var app = (LevelEditorApplication)GLib.Application.get_default();
  118. app.show_panel("panel_new_project", Gtk.StackTransitionType.SLIDE_DOWN);
  119. });
  120. _buttons_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
  121. _buttons_box.spacing = 6;
  122. _buttons_box.pack_start(_local_label, false, true);
  123. _buttons_box.pack_end(_button_new_project, false, true);
  124. _buttons_box.pack_end(_button_import_project, false, true);
  125. _projects_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
  126. _projects_box.margin_start = 12;
  127. _projects_box.margin_end = 12;
  128. _projects_box.margin_top = 32;
  129. _projects_box.margin_bottom = 32;
  130. _projects_box.spacing = 12;
  131. _projects_box.pack_start(_projects_list_label, false, true);
  132. _projects_box.pack_start(_buttons_box, false, true);
  133. _projects_box.pack_start(_list_projects, false, true);
  134. _clamp = new Clamp();
  135. _clamp.set_child(_projects_box);
  136. this.add(_clamp);
  137. _user.recent_project_added.connect(on_recent_project_added);
  138. _user.recent_project_touched.connect(on_recent_project_touched);
  139. _user.recent_project_removed.connect(on_recent_project_removed);
  140. }
  141. public void on_recent_project_added(string source_dir, string name, string time)
  142. {
  143. // Add project row.
  144. var row = new ProjectRow(source_dir, time, name, this);
  145. _list_projects.add(row);
  146. _list_projects.show_all(); // Otherwise the list is not always updated...
  147. if (!GLib.FileUtils.test(source_dir, FileTest.EXISTS))
  148. row._open_button.sensitive = false;
  149. invalidate_sort();
  150. }
  151. public void on_recent_project_touched(string source_dir, string mtime)
  152. {
  153. _list_projects.foreach((row) => {
  154. if (row.get_data<string>("source_dir") == source_dir) {
  155. row.set_data("mtime", mtime);
  156. return;
  157. }
  158. });
  159. invalidate_sort();
  160. }
  161. public void on_recent_project_removed(string source_dir)
  162. {
  163. _list_projects.foreach((row) => {
  164. if (row.get_data<string>("source_dir") == source_dir) {
  165. _list_projects.remove(row);
  166. return;
  167. }
  168. });
  169. }
  170. public void invalidate_sort()
  171. {
  172. _list_projects.invalidate_sort();
  173. // Give focus to most recent project's open button.
  174. ProjectRow? first_row = (ProjectRow?)_list_projects.get_row_at_index(0);
  175. if (first_row != null)
  176. first_row._open_button.has_focus = true;
  177. }
  178. }
  179. } /* namespace Crown */