Skip to content
5 changes: 5 additions & 0 deletions data/io.elementary.code.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -243,5 +243,10 @@
<summary>Opened folders.</summary>
<description>Opened folders that should be restored in startup.</description>
</key>
<key name="closed-folders" type="as">
<default>[]</default>
<summary>Recently closed folders.</summary>
<description>Recently closed folders to be shown in project chooser button dropdown.</description>
</key>
</schema>
</schemalist>
2 changes: 1 addition & 1 deletion plugins/fuzzy-search/fuzzy-search-indexer.vala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public class Scratch.Services.FuzzySearchIndexer : GLib.Object {
processing_queue = new Gee.ConcurrentList<IndexerMessage> ();
project_paths = new Gee.HashMap<string, Services.SearchProject> ();

folder_settings = new GLib.Settings ("io.elementary.code.folder-manager");
folder_settings = new GLib.Settings (Constants.PROJECT_NAME + ".folder-manager");
folder_settings.changed["opened-folders"].connect (handle_opened_projects_change);
}

Expand Down
6 changes: 2 additions & 4 deletions plugins/fuzzy-search/fuzzy-search.vala
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Scratch.Services.A

window = w;

folder_settings = new GLib.Settings ("io.elementary.code.folder-manager");
folder_settings = new GLib.Settings (Constants.PROJECT_NAME + ".folder-manager");
add_actions ();
folder_settings.changed["opened-folders"].connect (handle_opened_projects_change);
});
Expand Down Expand Up @@ -118,9 +118,7 @@ public class Scratch.Plugins.FuzzySearch: Peas.ExtensionBase, Scratch.Services.A
}

private void fuzzy_find () {
var settings = new GLib.Settings ("io.elementary.code.folder-manager");

string[] opened_folders = settings.get_strv ("opened-folders");
string[] opened_folders = folder_settings.get_strv ("opened-folders");
if (opened_folders == null || opened_folders.length < 1) {
return;
}
Expand Down
57 changes: 50 additions & 7 deletions src/FolderManager/FileView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
icon_name = "folder-symbolic";
title = _("Folders");

settings = new GLib.Settings ("io.elementary.code.folder-manager");
settings = new GLib.Settings (Constants.PROJECT_NAME + ".folder-manager");

git_manager = Scratch.Services.GitManager.get_instance ();

Expand Down Expand Up @@ -117,12 +117,7 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
foreach (var child in root.children) {
var project_folder_item = (ProjectFolderItem) child;
if (project_folder_item != folder_root) {
toplevel_action_group.activate_action (
MainWindow.ACTION_CLOSE_PROJECT_DOCS,
new Variant.string (project_folder_item.path)
);
root.remove (project_folder_item);
git_manager.remove_project (project_folder_item);
project_folder_item.closed (); // Signal not processed 'til loop complete
}
}
//Make remaining project the active one
Expand Down Expand Up @@ -663,13 +658,18 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane
}
Scratch.Services.GitManager.get_instance ().remove_project (folder_root);
write_settings ();
update_recently_closed_projects (folder_root, true);
});

// We do not want to rewrite settings while restoring from settings
// This interferes with fuzzy-finder plugins_manager
// See https://github.com/elementary/code/issues/1533
if (!restoring) {
// Add opened folder to settings
write_settings ();
// Remove folder from recently closed if present
update_recently_closed_projects (folder_root, false);

}

add_folder.callback ();
Expand Down Expand Up @@ -709,4 +709,47 @@ public class Scratch.FolderManager.FileView : Code.Widgets.SourceList, Code.Pane

settings.set_strv ("opened-folders", to_save);
}

private void update_recently_closed_projects (
ProjectFolderItem closed_folder,
bool to_be_added
) {
string[] recently_closed = settings.get_strv ("closed-folders");
var closed_path = closed_folder.path;
bool found = false;
int index = 0;
foreach (string path in recently_closed) {
if (path == closed_path) {
found = true;
break;
}

index++;
}

if ((to_be_added && found) || (!to_be_added && !found)) {
return;
}

if (to_be_added) {
if (recently_closed.length < 10) {
recently_closed += closed_path;
} else {
recently_closed.move (1, 0, 9);
recently_closed[9] = closed_path;
}
} else {
var size = recently_closed.length;
if (index < size - 1) {
recently_closed.move (index + 1, index, size - index);
} else {
recently_closed[index] = null; // Need to use null for resize to work
}


recently_closed.resize (size - 1);
}

settings.set_strv ("closed-folders", recently_closed);
}
}
105 changes: 76 additions & 29 deletions src/Widgets/ChooseProjectButton.vala
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ public class Code.ChooseProjectButton : Gtk.Bin {
return (((ProjectRow) row).project_name.down ().contains (project_filter.text.down ().strip ()));
});

project_listbox.set_header_func ((row, before) => {
if (before == null && ((ProjectRow) row).is_open) {
row.set_header (new Granite.HeaderLabel (_("Open Projects")) { halign = START });
} else if (!((ProjectRow) row).is_open && (before == null || ((ProjectRow) before).is_open)) {
row.set_header (new Granite.HeaderLabel (_("Recently Closed Projects")) { halign = START });
}
});

project_filter.changed.connect (() => {
project_listbox.invalidate_filter ();
});
Expand Down Expand Up @@ -98,30 +106,13 @@ public class Code.ChooseProjectButton : Gtk.Bin {
child = menu_button;

// Initialise with any pre-existing projects (needed for second and subsequent window)
var git_manager = Scratch.Services.GitManager.get_instance ();
var src = git_manager.project_liststore;
for (int index = 0; index < src.n_items; index++) {
var item = src.get_object (index);
if (item is Scratch.FolderManager.ProjectFolderItem) {
var row = create_project_row ((Scratch.FolderManager.ProjectFolderItem)item);
project_listbox.insert (row, index);
}
}
insert_project_rows ();

git_manager.project_liststore.items_changed.connect ((src, pos, n_removed, n_added) => {
var rows = project_listbox.get_children ();
for (int index = (int)pos; index < pos + n_removed; index++) {
var row = rows.nth_data (index);
row.destroy ();
}

for (int index = (int)pos; index < pos + n_added; index++) {
var item = src.get_object (index);
if (item is Scratch.FolderManager.ProjectFolderItem) {
var row = create_project_row ((Scratch.FolderManager.ProjectFolderItem)item);
project_listbox.insert (row, index);
}
}
// Update the list when projects added to or deleted from git manager
var git_manager = Scratch.Services.GitManager.get_instance ();
git_manager.project_liststore.items_changed.connect (() => {
// Inserting is delayed so the settings should have been updated when runs
insert_project_rows ();
});

menu_button.activate.connect (() => {
Expand All @@ -139,6 +130,51 @@ public class Code.ChooseProjectButton : Gtk.Bin {
update_button ();
}

// Throttle rebuilding the list as gitmanager emits multiple changed signals on startup
// and we need to wait for settings to update
uint insert_timeout_id = 0;
bool insert_wait = false;
private void insert_project_rows () {
if (insert_timeout_id == 0) {
insert_wait = false;
Timeout.add (20, () => {
if (insert_wait) {
insert_wait = false;
return Source.CONTINUE;
}

var rows = project_listbox.get_children ();
foreach (var row in rows) {
row.destroy ();
}

var git_manager = Scratch.Services.GitManager.get_instance ();
var src = git_manager.project_liststore;
int index = 0;
for (index = 0; index < src.n_items; index++) {
var item = src.get_object (index);
if (item is Scratch.FolderManager.ProjectFolderItem) {
var row = create_project_row ((Scratch.FolderManager.ProjectFolderItem)item, true);
project_listbox.insert (row, index);
}
}

var settings = new GLib.Settings (Constants.PROJECT_NAME + ".folder-manager");
var recently_closed = settings.get_strv ("closed-folders");
var size = recently_closed.length;
// List in reverse order so most recently closed appears first
for (int idx = size - 1; idx >= 0; idx--) {
var row = new ProjectRow (recently_closed[idx], false);
project_listbox.insert (row, index++);
}

return Source.REMOVE;
});
} else {
insert_wait = true;
}
}

// Set appearance (only) of project chooser button and list according to active path
private void update_button () {
unowned var active_path = Scratch.Services.GitManager.get_instance ().active_project_path;
Expand All @@ -151,9 +187,12 @@ public class Code.ChooseProjectButton : Gtk.Bin {
}
}

private Gtk.Widget create_project_row (Scratch.FolderManager.ProjectFolderItem project_folder) {
private Gtk.Widget create_project_row (
Scratch.FolderManager.ProjectFolderItem project_folder,
bool is_open = true
) {
var project_path = project_folder.file.file.get_path ();
var project_row = new ProjectRow (project_path);
var project_row = new ProjectRow (project_path, is_open);
// Project folder items cannot be renamed in UI, no need to handle

return project_row;
Expand All @@ -172,15 +211,18 @@ public class Code.ChooseProjectButton : Gtk.Bin {
}

public string project_path { get; construct; }
public bool is_open { get; construct; }

public string project_name {
get {
return check_button.label;
}
}

public ProjectRow (string project_path) {
public ProjectRow (string project_path, bool is_open) {
Object (
project_path: project_path
project_path: project_path,
is_open: is_open
);
}

Expand All @@ -192,8 +234,13 @@ public class Code.ChooseProjectButton : Gtk.Bin {

construct {
can_focus = true;
action_name = Scratch.MainWindow.ACTION_PREFIX + Scratch.MainWindow.ACTION_SET_ACTIVE_PROJECT;
action_name = Scratch.MainWindow.ACTION_PREFIX;
action_target = new Variant.string (project_path);
if (is_open) {
action_name += Scratch.MainWindow.ACTION_SET_ACTIVE_PROJECT;
} else {
action_name += Scratch.MainWindow.ACTION_OPEN_FOLDER;
}

check_button = new Gtk.CheckButton.with_label (Path.get_basename (project_path)) {
can_focus = false
Expand All @@ -206,7 +253,7 @@ public class Code.ChooseProjectButton : Gtk.Bin {
button = 0
};
button_controller.released.connect (() => {
activate ();
activate (); // Trigger the linked action
});

show_all ();
Expand Down