Skip to content

Commit

Permalink
installed-page: Save extension's toggle focus on state changes
Browse files Browse the repository at this point in the history
The items-changed signal is now emitted unchanged in the model from
ExmManager and is connected after binding the manager in
ExmInstalledPage from where the callback saves, emits items-changed and
restores focus.

This approach allows to keep binding GtkListBox to the extensions model
and not having to manage the changes manually.

Fix #220
  • Loading branch information
oscfdezdz committed Feb 2, 2025
1 parent 8885ff7 commit 64d0c35
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 22 deletions.
6 changes: 6 additions & 0 deletions src/exm-extension-row.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ on_state_changed (GtkSwitch *toggle,
return TRUE;
}

void
exm_search_row_focus_toggle (ExmExtensionRow *self)
{
gtk_widget_grab_focus (GTK_WIDGET (self->ext_toggle));
}

static void
exm_extension_row_class_init (ExmExtensionRowClass *klass)
{
Expand Down
7 changes: 4 additions & 3 deletions src/exm-extension-row.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ G_BEGIN_DECLS

G_DECLARE_FINAL_TYPE (ExmExtensionRow, exm_extension_row, EXM, EXTENSION_ROW, AdwExpanderRow)

ExmExtensionRow *
exm_extension_row_new (ExmExtension *extension,
ExmManager *manager);
ExmExtensionRow *exm_extension_row_new (ExmExtension *extension,
ExmManager *manager);

void exm_search_row_focus_toggle (ExmExtensionRow *self);

G_END_DECLS
2 changes: 1 addition & 1 deletion src/exm-installed-page.blp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ template $ExmInstalledPage: Gtk.Widget {
action-name: "app.logout";
}

Adw.PreferencesPage {
Adw.PreferencesPage prefs_page {
Adw.PreferencesGroup {
Adw.SwitchRow global_toggle {
[prefix]
Expand Down
92 changes: 92 additions & 0 deletions src/exm-installed-page.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct _ExmInstalledPage
// Template Widgets
GtkStack *stack;
AdwBanner *updates_banner;
AdwPreferencesPage *prefs_page;
AdwSwitchRow *global_toggle;
GtkListBox *user_list_box;
GtkListBox *system_list_box;
Expand Down Expand Up @@ -312,6 +313,89 @@ on_updates_available (ExmManager *manager G_GNUC_UNUSED,
g_timeout_add (500, G_SOURCE_FUNC (show_updates_banner), self);
}

static gboolean
focus_matching_extension (GtkListBox *list_box,
ExmExtension *extension)
{
int index = 0;
ExmExtensionRow *row;

while ((row = EXM_EXTENSION_ROW (gtk_list_box_get_row_at_index (list_box, index))))
{
ExmExtension *row_extension;

g_object_get(row, "extension", &row_extension, NULL);

if (is_extension_equal (extension, row_extension))
{
exm_search_row_focus_toggle (row);
g_object_unref (row_extension);

return TRUE;
}

index++;
}

return FALSE;
}

static void
on_extensions_changed (GListModel *model,
guint position,
guint removed,
guint added,
ExmInstalledPage *self)
{
if (!self->sort_enabled_first || (removed > 0 && added > 0))
return;

GtkRoot *toplevel;
GtkWidget *widget;
ExmExtension *extension;

toplevel = gtk_widget_get_root (GTK_WIDGET (self));
widget = gtk_window_get_focus (GTK_WINDOW (toplevel));

extension = EXM_EXTENSION (g_list_model_get_object (model, position));

if (!extension)
return;

GtkAdjustment *adjustment = NULL;
double scroll_pos = 0.0;
gboolean has_focus = widget && gtk_widget_has_focus (widget);

if (has_focus)
{
GtkWidget *child;

// Save scrolled window position
child = gtk_widget_get_first_child (GTK_WIDGET (self->prefs_page));
if (child && GTK_IS_SCROLLED_WINDOW (child))
{
adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (child));
if (adjustment)
scroll_pos = gtk_adjustment_get_value (adjustment);
}
}

if (g_list_store_find_with_equal_func (G_LIST_STORE (model), extension, (GEqualFunc)is_extension_equal, &position))
g_list_model_items_changed (model, position, 1, 1);

if (has_focus && gtk_widget_get_child_visible (GTK_WIDGET (self)))
{
if (!focus_matching_extension (self->user_list_box, extension))
focus_matching_extension (self->system_list_box, extension);
}

// Restore scrolled window position
if (adjustment)
gtk_adjustment_set_value (adjustment, scroll_pos);

g_object_unref (extension);
}

static void
invalidate_model_bindings (ExmInstalledPage *self)
{
Expand All @@ -325,7 +409,14 @@ invalidate_model_bindings (ExmInstalledPage *self)
NULL);

if (ext_model)
{
bind_list_box (ext_model, self);

g_signal_connect (ext_model,
"items-changed",
G_CALLBACK (on_extensions_changed),
self);
}
}

static void
Expand Down Expand Up @@ -382,6 +473,7 @@ exm_installed_page_class_init (ExmInstalledPageClass *klass)

gtk_widget_class_bind_template_child (widget_class, ExmInstalledPage, stack);
gtk_widget_class_bind_template_child (widget_class, ExmInstalledPage, updates_banner);
gtk_widget_class_bind_template_child (widget_class, ExmInstalledPage, prefs_page);
gtk_widget_class_bind_template_child (widget_class, ExmInstalledPage, global_toggle);
gtk_widget_class_bind_template_child (widget_class, ExmInstalledPage, user_list_box);
gtk_widget_class_bind_template_child (widget_class, ExmInstalledPage, system_list_box);
Expand Down
19 changes: 19 additions & 0 deletions src/local/exm-extension.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,25 @@ exm_extension_set_property (GObject *object,
}
}

gint
compare_extension (ExmExtension *a,
ExmExtension *b,
gpointer user_data G_GNUC_UNUSED)
{
const gchar *uuid_a, *uuid_b;
g_object_get (a, "uuid", &uuid_a, NULL);
g_object_get (b, "uuid", &uuid_b, NULL);

return g_strcmp0 (uuid_a, uuid_b);
}

gboolean
is_extension_equal (ExmExtension *a,
ExmExtension *b)
{
return compare_extension (a, b, NULL) == 0;
}

static void
exm_extension_class_init (ExmExtensionClass *klass)
{
Expand Down
9 changes: 8 additions & 1 deletion src/local/exm-extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ G_BEGIN_DECLS

G_DECLARE_FINAL_TYPE (ExmExtension, exm_extension, EXM, EXTENSION, GObject)

ExmExtension *exm_extension_new (const gchar *uuid);
ExmExtension *exm_extension_new (const gchar *uuid);

gint compare_extension (ExmExtension *a,
ExmExtension *b,
gpointer user_data);

gboolean is_extension_equal (ExmExtension *a,
ExmExtension *b);

G_END_DECLS
22 changes: 5 additions & 17 deletions src/local/exm-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -712,16 +712,6 @@ update_extension_list (ExmManager *self)
queue_notify_extension_updates (self);
}

static gboolean
is_extension_equal (ExmExtension *a, ExmExtension *b)
{
const gchar *uuid_a, *uuid_b;
g_object_get (a, "uuid", &uuid_a, NULL);
g_object_get (b, "uuid", &uuid_b, NULL);

return strcmp (uuid_a, uuid_b) == 0;
}

static void
on_state_changed (ShellExtensions *object G_GNUC_UNUSED,
const gchar *arg_uuid,
Expand Down Expand Up @@ -752,25 +742,23 @@ on_state_changed (ShellExtensions *object G_GNUC_UNUSED,

if (is_new)
{
g_list_store_append (list_store, extension);
g_list_store_insert_sorted (list_store, extension, (GCompareDataFunc)compare_extension, NULL);
return;
}

guint position;

if (is_uninstall_operation)
{
guint position;
if (g_list_store_find_with_equal_func (list_store, extension, (GEqualFunc)is_extension_equal, &position))
g_list_store_remove (list_store, position);

return;
}

// Emit items-changed signal to re-sort extension list
{
guint position;
if (g_list_store_find_with_equal_func (list_store, extension, (GEqualFunc)is_extension_equal, &position))
g_list_model_items_changed (G_LIST_MODEL (list_store), position, 1, 1);
}
if (g_list_store_find_with_equal_func (list_store, extension, (GEqualFunc)is_extension_equal, &position))
g_list_model_items_changed (G_LIST_MODEL (list_store), position, 0, 0);

// If the extension that has changed has an update, then
// one or more extensions have updates available. Lazily
Expand Down

0 comments on commit 64d0c35

Please sign in to comment.