Просмотр исходного кода

tools: project-browser: add sorting modes to folder view

Closes: #204
Daniele Bartolini 1 год назад
Родитель
Сommit
a7e4f3a265
2 измененных файлов с 200 добавлено и 18 удалено
  1. 5 0
      docs/changelog.rst
  2. 195 18
      tools/level_editor/project_browser.vala

+ 5 - 0
docs/changelog.rst

@@ -4,6 +4,11 @@ Changelog
 0.54.0 --- DD MMM YYYY
 ----------------------
 
+**Tools**
+
+* Items in the Project Browser can now be sorted by name, type, size or last modification time.
+* Added a list-view mode to the Project Browser.
+
 0.53.0 --- 30 Nov 2024
 ----------------------
 

+ 195 - 18
tools/level_editor/project_browser.vala

@@ -371,7 +371,6 @@ public class ProjectFolderView : Gtk.Bin
 			, typeof(uint64)     // Column.SIZE
 			, typeof(uint64)     // Column.MTIME
 			);
-		_list_store.set_sort_column_id(Column.TYPE, Gtk.SortType.ASCENDING);
 
 		_icon_view = new Gtk.IconView();
 		_icon_view.set_model(_list_store);
@@ -726,6 +725,44 @@ public class ProjectFolderView : Gtk.Bin
 
 public class ProjectBrowser : Gtk.Bin
 {
+	public enum SortMode
+	{
+		NAME_AZ,
+		NAME_ZA,
+		TYPE_AZ,
+		TYPE_ZA,
+		SIZE_MIN_MAX,
+		SIZE_MAX_MIN,
+		LAST_MTIME,
+		FIRST_MTIME,
+
+		COUNT;
+
+		public string to_label()
+		{
+			switch (this) {
+			case NAME_AZ:
+				return "Name A-Z";
+			case NAME_ZA:
+				return "Name Z-A";
+			case TYPE_AZ:
+				return "Type A-Z";
+			case TYPE_ZA:
+				return "Type Z-A";
+			case SIZE_MIN_MAX:
+				return "Size min-Max";
+			case SIZE_MAX_MIN:
+				return "Size Max-min";
+			case LAST_MTIME:
+				return "Last Modified";
+			case FIRST_MTIME:
+				return "First Modified";
+			default:
+				return "Unknown";
+			}
+		}
+	}
+
 	// Data
 	public ProjectStore _project_store;
 	public ThumbnailCache _thumbnail_cache;
@@ -741,6 +778,12 @@ public class ProjectBrowser : Gtk.Bin
 	public Gtk.Button _toggle_folder_view;
 	public Gtk.Box _tree_view_content;
 	public Gtk.Button _toggle_icon_view;
+	public Gtk.ListStore _folder_list_store;
+	public Gtk.TreeModelSort _folder_list_sort;
+	public SortMode _sort_mode;
+	public Gtk.Box _sort_items_box;
+	public Gtk.Popover _sort_items_popover;
+	public Gtk.MenuButton _sort_items;
 	public Gtk.Box _folder_view_content;
 	public Gtk.ScrolledWindow _scrolled_window;
 	public Gtk.Paned _paned;
@@ -952,6 +995,22 @@ public class ProjectBrowser : Gtk.Bin
 		_tree_view_content.pack_start(_tree_view_control, false);
 		_tree_view_content.pack_start(_scrolled_window, true, true);
 
+		// Setup sort menu button popover.
+		_sort_items_box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
+
+		Gtk.RadioButton? button = null;
+		for (int i = 0; i < SortMode.COUNT; ++i)
+			button = add_sort_item(button, (SortMode)i);
+
+		_sort_items_box.show_all();
+		_sort_items_popover = new Gtk.Popover(null);
+		_sort_items_popover.add(_sort_items_box);
+		_sort_items = new Gtk.MenuButton();
+		_sort_items.add(new Gtk.Image.from_icon_name("list-sort", Gtk.IconSize.SMALL_TOOLBAR));
+		_sort_items.get_style_context().add_class("flat");
+		_sort_items.can_focus = false;
+		_sort_items.set_popover(_sort_items_popover);
+
 		bool _show_icon_view = true;
 		_toggle_icon_view = new Gtk.Button.from_icon_name("browser-list-view", Gtk.IconSize.SMALL_TOOLBAR);
 		_toggle_icon_view.get_style_context().add_class("flat");
@@ -982,6 +1041,7 @@ public class ProjectBrowser : Gtk.Bin
 
 		var _folder_view_control = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
 		_folder_view_control.pack_end(_toggle_icon_view, false, false);
+		_folder_view_control.pack_end(_sort_items, false, false);
 
 		_folder_view_content = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
 		_folder_view_content.pack_start(_folder_view_control, false);
@@ -996,6 +1056,78 @@ public class ProjectBrowser : Gtk.Bin
 
 		_hide_core_resources = true;
 
+		_folder_list_store = new Gtk.ListStore(ProjectStore.Column.COUNT
+			, typeof(string) // ProjectStore.Column.NAME
+			, typeof(string) // ProjectStore.Column.TYPE
+			, typeof(uint64) // ProjectStore.Column.SIZE
+			, typeof(uint64) // ProjectStore.Column.MTIME
+			);
+
+		_folder_list_sort = new Gtk.TreeModelSort.with_model(_folder_list_store);
+		_folder_list_sort.set_default_sort_func((model, iter_a, iter_b) => {
+				Value type_a;
+				Value type_b;
+				model.get_value(iter_a, ProjectStore.Column.TYPE, out type_a);
+				model.get_value(iter_b, ProjectStore.Column.TYPE, out type_b);
+				Value name_a;
+				Value name_b;
+				model.get_value(iter_a, ProjectStore.Column.NAME, out name_a);
+				model.get_value(iter_b, ProjectStore.Column.NAME, out name_b);
+
+				// Folders are always on top.
+				if ((string)type_a == "<folder>" && (string)type_b != "<folder>") {
+					return -1;
+				} else if ((string)type_a != "<folder>" && (string)type_b == "<folder>") {
+					return 1;
+				} else if ((string)type_a == "<folder>" && (string)type_b == "<folder>") {
+					// Special folders always first.
+					if ((string)name_a == "..")
+						return -1;
+					else if ((string)name_b == "..")
+						return 1;
+				}
+
+				switch (_sort_mode) {
+				case SortMode.NAME_AZ:
+				case SortMode.NAME_ZA: {
+					int cmp = strcmp((string)name_a, (string)name_b);
+					return _sort_mode == SortMode.NAME_AZ ? cmp : -cmp;
+				}
+
+				case SortMode.TYPE_AZ:
+				case SortMode.TYPE_ZA: {
+					int cmp = strcmp((string)type_a, (string)type_b);
+					return _sort_mode == SortMode.TYPE_AZ ? cmp : -cmp;
+
+				}
+
+				case SortMode.SIZE_MIN_MAX:
+				case SortMode.SIZE_MAX_MIN: {
+					Value size_a;
+					Value size_b;
+					model.get_value(iter_a, ProjectStore.Column.SIZE, out size_a);
+					model.get_value(iter_b, ProjectStore.Column.SIZE, out size_b);
+
+					int cmp = (uint64)size_a <= (uint64)size_b ? -1 : 1;
+					return _sort_mode == SortMode.SIZE_MIN_MAX ? cmp : -cmp;
+				}
+
+				case SortMode.LAST_MTIME:
+				case SortMode.FIRST_MTIME: {
+					Value mtime_a;
+					Value mtime_b;
+					model.get_value(iter_a, ProjectStore.Column.MTIME, out mtime_a);
+					model.get_value(iter_b, ProjectStore.Column.MTIME, out mtime_b);
+
+					int cmp = (uint64)mtime_a >= (uint64)mtime_b ? -1 : 1;
+					return _sort_mode == SortMode.LAST_MTIME ? cmp : -cmp;
+				}
+
+				default:
+					return 0;
+				}
+			});
+
 		// Actions.
 		GLib.ActionEntry[] action_entries =
 		{
@@ -1183,12 +1315,13 @@ public class ProjectBrowser : Gtk.Bin
 
 	private void update_folder_view()
 	{
+		// Return if selection is empty.
 		Gtk.TreeModel selected_model;
 		Gtk.TreeIter selected_iter;
 		if (!_tree_selection.get_selected(out selected_model, out selected_iter))
 			return;
 
-		// If there is a selected node.
+		_folder_list_store.clear();
 		_folder_view._list_store.clear();
 
 		// Get the selected node's type and name.
@@ -1206,21 +1339,21 @@ public class ProjectBrowser : Gtk.Bin
 			// Add parent folder.
 			if (selected_name != "") {
 				Gtk.TreeIter dummy;
-				_folder_view._list_store.insert_with_values(out dummy
+				_folder_list_store.insert_with_values(out dummy
 					, -1
-					, ProjectFolderView.Column.TYPE
+					, ProjectStore.Column.TYPE
 					, "<folder>"
-					, ProjectFolderView.Column.NAME
+					, ProjectStore.Column.NAME
 					, ".."
-					, ProjectFolderView.Column.SIZE
+					, ProjectStore.Column.SIZE
 					, 0u
-					, ProjectFolderView.Column.MTIME
+					, ProjectStore.Column.MTIME
 					, 0u
 					, -1
 					);
 			}
 
-			// Fill the icon view list with paths matching the selected node's name.
+			// Fill the intermediate icon view list with paths matching the selected node's name.
 			_project_store._list_store.foreach((model, path, iter) => {
 					string type;
 					string name;
@@ -1262,15 +1395,15 @@ public class ProjectBrowser : Gtk.Bin
 
 					// Add the path to the list.
 					Gtk.TreeIter dummy;
-					_folder_view._list_store.insert_with_values(out dummy
+					_folder_list_store.insert_with_values(out dummy
 						, -1
-						, ProjectFolderView.Column.TYPE
+						, ProjectStore.Column.TYPE
 						, type
-						, ProjectFolderView.Column.NAME
+						, ProjectStore.Column.NAME
 						, name
-						, ProjectFolderView.Column.SIZE
+						, ProjectStore.Column.SIZE
 						, size
-						, ProjectFolderView.Column.MTIME
+						, ProjectStore.Column.MTIME
 						, mtime
 						, -1
 						);
@@ -1303,21 +1436,53 @@ public class ProjectBrowser : Gtk.Bin
 
 					// Add the path to the list.
 					Gtk.TreeIter dummy;
-					_folder_view._list_store.insert_with_values(out dummy
+					_folder_list_store.insert_with_values(out dummy
 						, -1
-						, ProjectFolderView.Column.TYPE
+						, ProjectStore.Column.TYPE
 						, type
-						, ProjectFolderView.Column.NAME
+						, ProjectStore.Column.NAME
 						, name
-						, ProjectFolderView.Column.SIZE
+						, ProjectStore.Column.SIZE
 						, size
-						, ProjectFolderView.Column.MTIME
+						, ProjectStore.Column.MTIME
 						, mtime
 						, -1
 						);
 					return false;
 				});
 		}
+
+		// Now, fill the actual icon view list with correctly sorted paths.
+		_folder_list_sort.foreach((model, path, iter) => {
+				string type;
+				string name;
+				uint64 size;
+				uint64 mtime;
+				model.get_value(iter, ProjectStore.Column.TYPE, out val);
+				type = (string)val;
+				model.get_value(iter, ProjectStore.Column.NAME, out val);
+				name = (string)val;
+				model.get_value(iter, ProjectStore.Column.SIZE, out val);
+				size = (uint64)val;
+				model.get_value(iter, ProjectStore.Column.MTIME, out val);
+				mtime = (uint64)val;
+
+				// Add the path to the list.
+				Gtk.TreeIter dummy;
+				_folder_view._list_store.insert_with_values(out dummy
+					, -1
+					, ProjectFolderView.Column.TYPE
+					, type
+					, ProjectFolderView.Column.NAME
+					, name
+					, ProjectFolderView.Column.SIZE
+					, size
+					, ProjectFolderView.Column.MTIME
+					, mtime
+					, -1
+					);
+				return false;
+			});
 	}
 
 	public void select_project_root()
@@ -1366,6 +1531,18 @@ public class ProjectBrowser : Gtk.Bin
 			cell.set_property("text", ResourceId.path((string)type, basename));
 		}
 	}
+
+	private Gtk.RadioButton add_sort_item(Gtk.RadioButton? group, SortMode mode)
+	{
+		var button = new Gtk.RadioButton.with_label_from_widget(group, mode.to_label());
+		button.toggled.connect(() => {
+				_sort_mode = mode;
+				update_folder_view();
+				_sort_items_popover.popdown();
+			});
+		_sort_items_box.pack_start(button, false, false);
+		return button;
+	}
 }
 
 } /* namespace Crown */