From 9f241503eb8f548d30715026803417e8f71e08ba Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sun, 9 Aug 2026 19:35:56 +0100 Subject: [PATCH 1/5] Remove extraneous whitespace --- plugins/fuzzy-search/fuzzy-search-popover.vala | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/fuzzy-search/fuzzy-search-popover.vala b/plugins/fuzzy-search/fuzzy-search-popover.vala index 37a3132161..355c7062ae 100644 --- a/plugins/fuzzy-search/fuzzy-search-popover.vala +++ b/plugins/fuzzy-search/fuzzy-search-popover.vala @@ -181,8 +181,6 @@ public class Scratch.FuzzySearchPopover : Gtk.Popover { bool first = true; - - foreach (var c in search_result_container.get_children ()) { search_result_container.remove (c); } From c7c50a8fec3e5526bf9578a81a18a736f8fc16e3 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Mon, 10 Aug 2026 17:54:26 +0100 Subject: [PATCH 2/5] Use bound liststore; rebuild list to filter; simplify --- src/Widgets/ChooseProjectButton.vala | 131 +++++++++++++-------------- 1 file changed, 64 insertions(+), 67 deletions(-) diff --git a/src/Widgets/ChooseProjectButton.vala b/src/Widgets/ChooseProjectButton.vala index 132e2c74d4..bebb9e6159 100644 --- a/src/Widgets/ChooseProjectButton.vala +++ b/src/Widgets/ChooseProjectButton.vala @@ -10,6 +10,9 @@ public class Code.ChooseProjectButton : Gtk.Bin { private const string PROJECT_TOOLTIP = N_("Active Git Project: %s"); private Gtk.Label label_widget; private Gtk.ListBox project_listbox; + private Gtk.SearchEntry project_filter; + private ListStore project_liststore; + private Scratch.Services.GitManager git_manager; public signal void project_chosen (); @@ -35,11 +38,14 @@ public class Code.ChooseProjectButton : Gtk.Bin { box.add (label_widget); box.add (cloning_spinner); + project_liststore = new ListStore (typeof (ProjectRow)); project_listbox = new Gtk.ListBox () { selection_mode = SINGLE }; - var project_filter = new Gtk.SearchEntry () { + project_listbox.bind_model (project_liststore, (obj) => (ProjectRow) obj); + + project_filter = new Gtk.SearchEntry () { margin_top = 12, margin_bottom = 6, margin_start = 12, @@ -47,13 +53,8 @@ public class Code.ChooseProjectButton : Gtk.Bin { placeholder_text = _("Filter projects") }; - project_listbox.set_filter_func ((row) => { - //Both are lowercased so that the case doesn't matter when comparing. - return (((ProjectRow) row).project_name.down ().contains (project_filter.text.down ().strip ())); - }); - project_filter.changed.connect (() => { - project_listbox.invalidate_filter (); + insert_project_rows (); }); var project_scrolled = new Gtk.ScrolledWindow (null, null) { @@ -98,50 +99,25 @@ 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 = Scratch.Services.GitManager.get_instance (); 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); - } - } + insert_project_rows (); }); - menu_button.activate.connect (() => { - if (menu_button.active) { - unowned var active_path = Scratch.Services.GitManager.get_instance ().active_project_path; - foreach (var child in project_listbox.get_children ()) { - var project_row = ((ProjectRow) child); - // All paths must not end in directory separator so can be compared directly - project_row.active = active_path == project_row.project_path; - } - } - }); + git_manager.notify["active-project-path"].connect (update_active_project); + update_active_project (); + } - git_manager.notify["active-project-path"].connect (update_button); - update_button (); + private bool filter_func (Scratch.FolderManager.ProjectFolderItem project) { + var project_name = Path.get_basename (project.path); + //Both are lowercased so that the case doesn't matter when comparing. + return project_name.down ().contains (project_filter.text.down ().strip ()); } // 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; + private void update_active_project () { + unowned var active_path = git_manager.active_project_path; if (active_path != "") { label_widget.label = Path.get_basename (active_path); tooltip_text = _(PROJECT_TOOLTIP).printf (Scratch.Utils.replace_home_with_tilde (active_path)); @@ -149,34 +125,53 @@ public class Code.ChooseProjectButton : Gtk.Bin { label_widget.label = Path.get_basename (_(NO_PROJECT_SELECTED)); tooltip_text = _(PROJECT_TOOLTIP).printf (_(NO_PROJECT_SELECTED)); } + + for (int index = 0; index < project_liststore.n_items; index++) { + var project_row = (ProjectRow) project_liststore.get_item (index); + project_row.update_active (active_path); + } } - private Gtk.Widget create_project_row (Scratch.FolderManager.ProjectFolderItem project_folder) { - var project_path = project_folder.file.file.get_path (); - var project_row = new ProjectRow (project_path); - // Project folder items cannot be renamed in UI, no need to handle + // Throttle rebuilding the list as gitmanager emits multiple changed signals on startup + uint insert_timeout_id = 0; + bool insert_wait = false; + private void insert_project_rows () { + if (insert_timeout_id == 0) { + insert_wait = false; + Timeout.add (200, () => { + if (insert_wait) { + insert_wait = false; + return Source.CONTINUE; + } - return project_row; - } + project_liststore.remove_all (); + + var src = git_manager.project_liststore; + unowned var active_path = git_manager.active_project_path; + for (int index = 0; index < src.n_items; index++) { + var item = src.get_object (index); + if (item is Scratch.FolderManager.ProjectFolderItem) { + var project = (Scratch.FolderManager.ProjectFolderItem) item; + if (filter_func (project)) { + var row = new ProjectRow (project.path); + // The GitManager project store is already sorted so just add. + project_liststore.append (row); + row.update_active (active_path); + } + } + } - public class ProjectRow : Gtk.ListBoxRow { - private Gtk.CheckButton check_button; - public bool active { - get { - return check_button.active; - } - - set { - check_button.active = value; - } + return Source.REMOVE; + }); + } else { + insert_wait = true; } + } + public class ProjectRow : Gtk.ListBoxRow { public string project_path { get; construct; } - public string project_name { - get { - return check_button.label; - } - } + private Gtk.CheckButton check_button; + private Gtk.GestureMultiPress button_controller; public ProjectRow (string project_path) { Object ( @@ -184,8 +179,6 @@ public class Code.ChooseProjectButton : Gtk.Bin { ); } - private Gtk.GestureMultiPress button_controller; - class construct { set_css_name (Gtk.STYLE_CLASS_MENUITEM); } @@ -211,5 +204,9 @@ public class Code.ChooseProjectButton : Gtk.Bin { show_all (); } + + public void update_active (string active_path) { + check_button.active = active_path == project_path; + } } } From 956908bb77241a89cc1128f8b1ef4fa72aae492f Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sun, 6 Sep 2026 18:23:34 +0100 Subject: [PATCH 3/5] Use GitManager project liststore as model, set filter func --- src/Widgets/ChooseProjectButton.vala | 67 ++++++++-------------------- 1 file changed, 18 insertions(+), 49 deletions(-) diff --git a/src/Widgets/ChooseProjectButton.vala b/src/Widgets/ChooseProjectButton.vala index bebb9e6159..feaa9f3257 100644 --- a/src/Widgets/ChooseProjectButton.vala +++ b/src/Widgets/ChooseProjectButton.vala @@ -38,12 +38,17 @@ public class Code.ChooseProjectButton : Gtk.Bin { box.add (label_widget); box.add (cloning_spinner); - project_liststore = new ListStore (typeof (ProjectRow)); + git_manager = Scratch.Services.GitManager.get_instance (); + project_liststore = git_manager.project_liststore; project_listbox = new Gtk.ListBox () { selection_mode = SINGLE }; - project_listbox.bind_model (project_liststore, (obj) => (ProjectRow) obj); + //TODO Use list of objects not widgets + project_listbox.bind_model (project_liststore, (obj) => { + var path = ((Scratch.FolderManager.Item) obj).path; + return new ProjectRow (path); + }); project_filter = new Gtk.SearchEntry () { margin_top = 12, @@ -53,8 +58,13 @@ public class Code.ChooseProjectButton : Gtk.Bin { placeholder_text = _("Filter projects") }; + project_listbox.set_filter_func ((row) => { + var name = Path.get_basename (((ProjectRow) row).project_path); + return name.contains (project_filter.text); //TODO Is "has_prefix" more useful? + }); + project_filter.changed.connect (() => { - insert_project_rows (); + project_listbox.invalidate_filter (); }); var project_scrolled = new Gtk.ScrolledWindow (null, null) { @@ -98,13 +108,6 @@ public class Code.ChooseProjectButton : Gtk.Bin { child = menu_button; - // Initialise with any pre-existing projects (needed for second and subsequent window) - insert_project_rows (); - git_manager = Scratch.Services.GitManager.get_instance (); - git_manager.project_liststore.items_changed.connect ((src, pos, n_removed, n_added) => { - insert_project_rows (); - }); - git_manager.notify["active-project-path"].connect (update_active_project); update_active_project (); } @@ -126,45 +129,11 @@ public class Code.ChooseProjectButton : Gtk.Bin { tooltip_text = _(PROJECT_TOOLTIP).printf (_(NO_PROJECT_SELECTED)); } - for (int index = 0; index < project_liststore.n_items; index++) { - var project_row = (ProjectRow) project_liststore.get_item (index); - project_row.update_active (active_path); - } - } - - // Throttle rebuilding the list as gitmanager emits multiple changed signals on startup - uint insert_timeout_id = 0; - bool insert_wait = false; - private void insert_project_rows () { - if (insert_timeout_id == 0) { - insert_wait = false; - Timeout.add (200, () => { - if (insert_wait) { - insert_wait = false; - return Source.CONTINUE; - } - - project_liststore.remove_all (); - - var src = git_manager.project_liststore; - unowned var active_path = git_manager.active_project_path; - for (int index = 0; index < src.n_items; index++) { - var item = src.get_object (index); - if (item is Scratch.FolderManager.ProjectFolderItem) { - var project = (Scratch.FolderManager.ProjectFolderItem) item; - if (filter_func (project)) { - var row = new ProjectRow (project.path); - // The GitManager project store is already sorted so just add. - project_liststore.append (row); - row.update_active (active_path); - } - } - } - - return Source.REMOVE; - }); - } else { - insert_wait = true; + var index = 0; + var project_row = project_listbox.get_row_at_index (index); + while (project_row != null) { + ((ProjectRow) project_row).update_active (active_path); + project_row = project_listbox.get_row_at_index (++index); } } From e9526722cba933923a2871c08d15ff068a97b9e3 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sun, 6 Sep 2026 18:47:00 +0100 Subject: [PATCH 4/5] Close popover when active project changes, update when popover opens --- src/Widgets/ChooseProjectButton.vala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Widgets/ChooseProjectButton.vala b/src/Widgets/ChooseProjectButton.vala index feaa9f3257..9698ca3ccd 100644 --- a/src/Widgets/ChooseProjectButton.vala +++ b/src/Widgets/ChooseProjectButton.vala @@ -13,6 +13,7 @@ public class Code.ChooseProjectButton : Gtk.Bin { private Gtk.SearchEntry project_filter; private ListStore project_liststore; private Scratch.Services.GitManager git_manager; + private Gtk.MenuButton menu_button; public signal void project_chosen (); @@ -101,15 +102,17 @@ public class Code.ChooseProjectButton : Gtk.Bin { child = popover_content }; - var menu_button = new Gtk.MenuButton () { + menu_button = new Gtk.MenuButton () { child = box, popover = project_popover }; child = menu_button; - git_manager.notify["active-project-path"].connect (update_active_project); - update_active_project (); + git_manager.notify["active-project-path"].connect (menu_button.popover.popdown); + menu_button.clicked.connect (update_active_project); + + update_active_project (); // Needed to update the menubutton label } private bool filter_func (Scratch.FolderManager.ProjectFolderItem project) { @@ -154,6 +157,7 @@ public class Code.ChooseProjectButton : Gtk.Bin { construct { can_focus = true; + activatable = true; action_name = Scratch.MainWindow.ACTION_PREFIX + Scratch.MainWindow.ACTION_SET_ACTIVE_PROJECT; action_target = new Variant.string (project_path); @@ -168,7 +172,7 @@ public class Code.ChooseProjectButton : Gtk.Bin { button = 0 }; button_controller.released.connect (() => { - activate (); + activate (); // This activates the *action* (no "row-activated" signal sent) }); show_all (); @@ -176,6 +180,7 @@ public class Code.ChooseProjectButton : Gtk.Bin { public void update_active (string active_path) { check_button.active = active_path == project_path; + warning ("update active path %s - active %s", active_path, check_button.active.to_string ()); } } } From a8425005bd9dd0f968332cf08ad609a58752ca46 Mon Sep 17 00:00:00 2001 From: Jeremy Wootten Date: Sun, 6 Sep 2026 18:50:44 +0100 Subject: [PATCH 5/5] Removed unused signal and whitespace --- src/Services/GitManager.vala | 4 ---- src/Widgets/ChooseProjectButton.vala | 2 -- 2 files changed, 6 deletions(-) diff --git a/src/Services/GitManager.vala b/src/Services/GitManager.vala index 10dd8acf2a..831e105185 100644 --- a/src/Services/GitManager.vala +++ b/src/Services/GitManager.vala @@ -26,10 +26,6 @@ namespace Scratch.Services { private static Once instance; private Gee.HashMap project_gitrepo_map; - - - - construct { // Used to populate the ChooseProject popover in sorted order Ggit.init (); diff --git a/src/Widgets/ChooseProjectButton.vala b/src/Widgets/ChooseProjectButton.vala index 9698ca3ccd..c831e1f74b 100644 --- a/src/Widgets/ChooseProjectButton.vala +++ b/src/Widgets/ChooseProjectButton.vala @@ -15,8 +15,6 @@ public class Code.ChooseProjectButton : Gtk.Bin { private Scratch.Services.GitManager git_manager; private Gtk.MenuButton menu_button; - public signal void project_chosen (); - construct { var img = new Gtk.Image.from_icon_name ("git-symbolic", SMALL_TOOLBAR);