From b261bcaf998f186e3889086963521c9dc28341bd Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 8 Apr 2018 16:36:33 -0300 Subject: [PATCH 1/8] panel: Update code style This is just an almost-trivial commit that ports CcPanel to use the latest code practices around. --- shell/cc-panel.c | 93 ++++++++++++++++++++++-------------------------- shell/cc-panel.h | 40 ++++----------------- 2 files changed, 50 insertions(+), 83 deletions(-) diff --git a/shell/cc-panel.c b/shell/cc-panel.c index 1042397fd0..1fd13fa23a 100644 --- a/shell/cc-panel.c +++ b/shell/cc-panel.c @@ -42,7 +42,7 @@ #define CC_PANEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_PANEL, CcPanelPrivate)) -struct CcPanelPrivate +typedef struct { gchar *id; gchar *display_name; @@ -51,16 +51,19 @@ struct CcPanelPrivate gboolean is_active; CcShell *shell; -}; +} CcPanelPrivate; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (CcPanel, cc_panel, GTK_TYPE_BIN) enum { - PROP_0, - PROP_SHELL, - PROP_PARAMETERS + PROP_0, + PROP_SHELL, + PROP_PARAMETERS, + N_PROPS }; -G_DEFINE_ABSTRACT_TYPE (CcPanel, cc_panel, GTK_TYPE_BIN) +static GParamSpec *properties [N_PROPS]; static void cc_panel_set_property (GObject *object, @@ -68,23 +71,23 @@ cc_panel_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - CcPanel *panel; - - panel = CC_PANEL (object); + CcPanelPrivate *priv = cc_panel_get_instance_private (CC_PANEL (object)); switch (prop_id) { case PROP_SHELL: /* construct only property */ - panel->priv->shell = g_value_get_object (value); + priv->shell = g_value_get_object (value); break; case PROP_PARAMETERS: { - GVariant *parameters = g_value_get_variant (value); - GVariant *v; + g_autoptr (GVariant) v = NULL; + GVariant *parameters; gsize n_parameters; + parameters = g_value_get_variant (value); + if (parameters == NULL) return; @@ -100,8 +103,6 @@ cc_panel_set_property (GObject *object, else if (g_variant_n_children (v) > 0) g_warning ("Ignoring additional flags"); - g_variant_unref (v); - if (n_parameters > 1) g_warning ("Ignoring additional parameters"); @@ -119,14 +120,12 @@ cc_panel_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - CcPanel *panel; - - panel = CC_PANEL (object); + CcPanelPrivate *priv = cc_panel_get_instance_private (CC_PANEL (object)); switch (prop_id) { case PROP_SHELL: - g_value_set_object (value, panel->priv->shell); + g_value_set_object (value, priv->shell); break; default: @@ -138,15 +137,10 @@ cc_panel_get_property (GObject *object, static void cc_panel_finalize (GObject *object) { - CcPanel *panel; - - g_return_if_fail (object != NULL); - g_return_if_fail (CC_IS_PANEL (object)); + CcPanelPrivate *priv = cc_panel_get_instance_private (CC_PANEL (object)); - panel = CC_PANEL (object); - - g_free (panel->priv->id); - g_free (panel->priv->display_name); + g_clear_pointer (&priv->id, g_free); + g_clear_pointer (&priv->display_name, g_free); G_OBJECT_CLASS (cc_panel_parent_class)->finalize (object); } @@ -206,9 +200,8 @@ cc_panel_size_allocate (GtkWidget *widget, static void cc_panel_class_init (CcPanelClass *klass) { - GParamSpec *pspec; - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->get_property = cc_panel_get_property; object_class->set_property = cc_panel_set_property; @@ -218,31 +211,25 @@ cc_panel_class_init (CcPanelClass *klass) widget_class->get_preferred_height = cc_panel_get_preferred_height; widget_class->size_allocate = cc_panel_size_allocate; - gtk_container_class_handle_border_width (GTK_CONTAINER_CLASS (klass)); - - g_type_class_add_private (klass, sizeof (CcPanelPrivate)); - - pspec = g_param_spec_object ("shell", - "Shell", - "Shell the Panel resides in", - CC_TYPE_SHELL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS - | G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (object_class, PROP_SHELL, pspec); - - pspec = g_param_spec_variant ("parameters", - "Structured parameters", - "Additional parameters passed externally (ie. command line, dbus activation)", - G_VARIANT_TYPE ("av"), - NULL, - G_PARAM_WRITABLE); - g_object_class_install_property (object_class, PROP_PARAMETERS, pspec); + properties[PROP_SHELL] = g_param_spec_object ("shell", + "Shell", + "Shell the Panel resides in", + CC_TYPE_SHELL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + properties[PROP_PARAMETERS] = g_param_spec_variant ("parameters", + "Structured parameters", + "Additional parameters passed externally (ie. command line, D-Bus activation)", + G_VARIANT_TYPE ("av"), + NULL, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); } static void cc_panel_init (CcPanel *panel) { - panel->priv = CC_PANEL_GET_PRIVATE (panel); } /** @@ -256,7 +243,13 @@ cc_panel_init (CcPanel *panel) CcShell * cc_panel_get_shell (CcPanel *panel) { - return panel->priv->shell; + CcPanelPrivate *priv; + + g_return_val_if_fail (CC_IS_PANEL (panel), NULL); + + priv = cc_panel_get_instance_private (panel); + + return priv->shell; } GPermission * diff --git a/shell/cc-panel.h b/shell/cc-panel.h index c9cff10d74..c517164bf9 100644 --- a/shell/cc-panel.h +++ b/shell/cc-panel.h @@ -20,52 +20,27 @@ * Thomas Wood */ - -#ifndef __CC_PANEL_H -#define __CC_PANEL_H +#pragma once #include #include - -G_BEGIN_DECLS - -#define CC_TYPE_PANEL (cc_panel_get_type ()) -#define CC_PANEL(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CC_TYPE_PANEL, CcPanel)) -#define CC_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), CC_TYPE_PANEL, CcPanelClass)) -#define CC_IS_PANEL(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CC_TYPE_PANEL)) -#define CC_IS_PANEL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CC_TYPE_PANEL)) -#define CC_PANEL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CC_TYPE_PANEL, CcPanelClass)) - -/*• +/** * Utility macro used to register panels * * use: CC_PANEL_REGISTER (PluginName, plugin_name) */ -#define CC_PANEL_REGISTER(PluginName, plugin_name) \ - G_DEFINE_TYPE (PluginName, plugin_name, CC_TYPE_PANEL) +#define CC_PANEL_REGISTER(PluginName, plugin_name) G_DEFINE_TYPE (PluginName, plugin_name, CC_TYPE_PANEL) -typedef struct CcPanelPrivate CcPanelPrivate; +#define CC_TYPE_PANEL (cc_panel_get_type()) -typedef struct _CcPanel CcPanel; -typedef struct _CcPanelClass CcPanelClass; - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (CcPanel, g_object_unref) +G_DECLARE_DERIVABLE_TYPE (CcPanel, cc_panel, CC, PANEL, GtkBin) /* cc-shell.h requires CcPanel, so make sure it is defined first */ #include "cc-shell.h" -/** - * CcPanel: - * - * The contents of this struct are private and should not be accessed directly. - */ -struct _CcPanel -{ - /*< private >*/ - GtkBin parent; - CcPanelPrivate *priv; -}; +G_BEGIN_DECLS + /** * CcPanelClass: * @@ -94,4 +69,3 @@ GtkWidget *cc_panel_get_title_widget (CcPanel *panel); G_END_DECLS -#endif /* __CC_PANEL_H */ -- GitLab From 489a7ae5ddb980abde0d45b2d5b32e165c68516c Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 8 Apr 2018 17:13:42 -0300 Subject: [PATCH 2/8] window: Delegate model creation to CcApplication In order be able to modify panel information statically, we need to have access to the CcShellModel from static functions. CcApplication, thus, is a better place for the model to live, since we can access it outside any scope using g_application_get_default(). It also makes sense from the modeling point of view, since the model is not tied to the shell anymore. --- shell/cc-application.c | 13 ++++++- shell/cc-application.h | 6 ++- shell/cc-window.c | 88 +++++++++++++++++++++++++++++------------- shell/cc-window.h | 4 +- 4 files changed, 81 insertions(+), 30 deletions(-) diff --git a/shell/cc-application.c b/shell/cc-application.c index 862baab9e2..8ffb2bdbfc 100644 --- a/shell/cc-application.c +++ b/shell/cc-application.c @@ -39,6 +39,8 @@ struct _CcApplication { GtkApplication parent; + CcShellModel *model; + CcWindow *window; }; @@ -259,7 +261,8 @@ cc_application_startup (GApplication *application) gtk_application_set_accels_for_action (GTK_APPLICATION (application), "app.help", help_accels); - self->window = cc_window_new (GTK_APPLICATION (application)); + self->model = cc_shell_model_new (); + self->window = cc_window_new (GTK_APPLICATION (application), self->model); } static void @@ -320,3 +323,11 @@ cc_application_new (void) "flags", G_APPLICATION_HANDLES_COMMAND_LINE, NULL); } + +CcShellModel * +cc_application_get_model (CcApplication *self) +{ + g_return_val_if_fail (CC_IS_APPLICATION (self), NULL); + + return self->model; +} diff --git a/shell/cc-application.h b/shell/cc-application.h index eeda001b0e..e9c8b60af7 100644 --- a/shell/cc-application.h +++ b/shell/cc-application.h @@ -19,6 +19,8 @@ #pragma once +#include "cc-shell-model.h" + #include G_BEGIN_DECLS @@ -29,4 +31,6 @@ G_DECLARE_FINAL_TYPE (CcApplication, cc_application, CC, APPLICATION, GtkApplica GtkApplication *cc_application_new (void); -G_END_DECLS \ No newline at end of file +CcShellModel *cc_application_get_model (CcApplication *self); + +G_END_DECLS diff --git a/shell/cc-window.c b/shell/cc-window.c index 4f66f2a6d7..b041f1d76c 100644 --- a/shell/cc-window.c +++ b/shell/cc-window.c @@ -36,6 +36,7 @@ #include #include +#include "cc-application.h" #include "cc-panel.h" #include "cc-shell.h" #include "cc-shell-category-view.h" @@ -89,7 +90,8 @@ G_DEFINE_TYPE_WITH_CODE (CcWindow, cc_window, GTK_TYPE_APPLICATION_WINDOW, enum { PROP_0, - PROP_ACTIVE_PANEL + PROP_ACTIVE_PANEL, + PROP_MODEL }; /* Auxiliary methods */ @@ -288,7 +290,9 @@ setup_model (CcWindow *shell) GtkTreeIter iter; gboolean valid; - shell->store = (GtkListStore *) cc_shell_model_new (); + /* CcApplication must have a valid model at this point */ + g_assert (shell->store != NULL); + model = GTK_TREE_MODEL (shell->store); cc_panel_loader_fill_model (CC_SHELL_MODEL (shell->store)); @@ -700,6 +704,11 @@ cc_window_get_property (GObject *object, case PROP_ACTIVE_PANEL: g_value_set_object (value, self->active_panel); break; + + case PROP_MODEL: + g_value_set_object (value, self->store); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } @@ -718,11 +727,49 @@ cc_window_set_property (GObject *object, case PROP_ACTIVE_PANEL: set_active_panel (shell, g_value_get_object (value)); break; + + case PROP_MODEL: + g_assert (shell->store == NULL); + shell->store = g_value_dup_object (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } +static void +cc_window_constructed (GObject *object) +{ + g_autofree char *id = NULL; + GtkSettings *settings; + CcWindow *self; + + self = CC_WINDOW (object); + + /* Handle decorations for the split headers. */ + settings = gtk_settings_get_default (); + g_signal_connect (settings, + "notify::gtk-decoration-layout", + G_CALLBACK (split_decorations_cb), + self); + + split_decorations_cb (settings, NULL, self); + + /* Add the panels */ + setup_model (self); + + /* After everything is loaded, select the last used panel, if any, + * or the first visible panel */ + id = g_settings_get_string (self->settings, "last-panel"); + if (id != NULL && cc_shell_model_has_panel (CC_SHELL_MODEL (self->store), id)) + cc_panel_list_set_active_panel (CC_PANEL_LIST (self->panel_list), id); + else + cc_panel_list_activate (CC_PANEL_LIST (self->panel_list)); + + G_OBJECT_CLASS (cc_window_parent_class)->constructed (object); +} + static void cc_window_dispose (GObject *object) { @@ -760,6 +807,7 @@ cc_window_class_init (CcWindowClass *klass) object_class->get_property = cc_window_get_property; object_class->set_property = cc_window_set_property; + object_class->constructed = cc_window_constructed; object_class->dispose = cc_window_dispose; object_class->finalize = cc_window_finalize; @@ -767,6 +815,14 @@ cc_window_class_init (CcWindowClass *klass) g_object_class_override_property (object_class, PROP_ACTIVE_PANEL, "active-panel"); + g_object_class_install_property (object_class, + PROP_MODEL, + g_param_spec_object ("model", + "Model", + "The CcShellModel of this application", + CC_TYPE_SHELL_MODEL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/ControlCenter/gtk/window.ui"); gtk_widget_class_bind_template_child (widget_class, CcWindow, development_warning_dialog); @@ -800,45 +856,22 @@ cc_window_class_init (CcWindowClass *klass) static void cc_window_init (CcWindow *self) { - GtkSettings *settings; - g_autofree char *id = NULL; - gtk_widget_init_template (GTK_WIDGET (self)); gtk_widget_add_events (GTK_WIDGET (self), GDK_BUTTON_RELEASE_MASK); self->settings = g_settings_new ("org.gnome.ControlCenter"); - - /* Handle decorations for the split headers. */ - settings = gtk_settings_get_default (); - g_signal_connect (settings, - "notify::gtk-decoration-layout", - G_CALLBACK (split_decorations_cb), - self); - - split_decorations_cb (settings, NULL, self); - - /* Add the panels */ self->custom_widgets = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); self->previous_panels = g_queue_new (); - setup_model (self); - - /* After everything is loaded, select the last used panel, if any, - * or the first visible panel */ - id = g_settings_get_string (self->settings, "last-panel"); - if (id != NULL && cc_shell_model_has_panel (CC_SHELL_MODEL (self->store), id)) - cc_panel_list_set_active_panel (CC_PANEL_LIST (self->panel_list), id); - else - cc_panel_list_activate (CC_PANEL_LIST (self->panel_list)); - /* Add a custom CSS class on development builds */ if (in_flatpak_sandbox ()) add_development_build_css (self); } CcWindow * -cc_window_new (GtkApplication *application) +cc_window_new (GtkApplication *application, + CcShellModel *model) { g_return_val_if_fail (GTK_IS_APPLICATION (application), NULL); @@ -849,6 +882,7 @@ cc_window_new (GtkApplication *application) "icon-name", DEFAULT_WINDOW_ICON_NAME, "window-position", GTK_WIN_POS_CENTER, "show-menubar", FALSE, + "model", model, NULL); } diff --git a/shell/cc-window.h b/shell/cc-window.h index a6ec98e46b..a2e53eaabd 100644 --- a/shell/cc-window.h +++ b/shell/cc-window.h @@ -23,6 +23,7 @@ #include #include "cc-shell.h" +#include "cc-shell-model.h" G_BEGIN_DECLS @@ -30,7 +31,8 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (CcWindow, cc_window, CC, WINDOW, GtkApplicationWindow) -CcWindow *cc_window_new (GtkApplication *application); +CcWindow *cc_window_new (GtkApplication *application, + CcShellModel *model); void cc_window_set_search_item (CcWindow *center, const char *search); -- GitLab From a78cbe39634192e6e050c9bd2e6518aabf462a18 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 8 Apr 2018 19:10:25 -0300 Subject: [PATCH 3/8] model: Add visibility field This field can be used to communicate the visibility of the panel from a static context (i.e. without any instances of a CcPanel nor any access to CcShell). --- shell/cc-panel-list.c | 173 +++++++++++++++++++++++++++++++---------- shell/cc-panel-list.h | 7 +- shell/cc-panel.h | 14 ++++ shell/cc-shell-model.c | 45 ++++++++++- shell/cc-shell-model.h | 29 ++++--- shell/cc-window.c | 41 ++++++++-- 6 files changed, 246 insertions(+), 63 deletions(-) diff --git a/shell/cc-panel-list.c b/shell/cc-panel-list.c index f5b83509d6..f752c231b3 100644 --- a/shell/cc-panel-list.c +++ b/shell/cc-panel-list.c @@ -18,20 +18,24 @@ * Author: Georges Basile Stavracas Neto */ +#define G_LOG_DOMAIN "cc-panel-list" + #include +#include "cc-debug.h" #include "cc-panel-list.h" #include "cc-util.h" typedef struct { - GtkWidget *row; - GtkWidget *description_label; - CcPanelCategory category; - gchar *id; - gchar *name; - gchar *description; - gchar **keywords; + GtkWidget *row; + GtkWidget *description_label; + CcPanelCategory category; + gchar *id; + gchar *name; + gchar *description; + gchar **keywords; + CcPanelVisibility visibility; } RowData; struct _CcPanelList @@ -106,6 +110,49 @@ get_listbox_from_view (CcPanelList *self, } } +static GtkWidget * +get_listbox_from_category (CcPanelList *self, + CcPanelCategory category) +{ + + switch (category) + { + case CC_CATEGORY_DEVICES: + return self->devices_listbox; + break; + + case CC_CATEGORY_DETAILS: + return self->details_listbox; + break; + + default: + return self->main_listbox; + break; + } + + return NULL; +} + +static void +activate_row_below (CcPanelList *self, + RowData *data) +{ + GtkListBoxRow *next_row; + GtkListBox *listbox; + guint row_index; + + row_index = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (data->row)); + listbox = GTK_LIST_BOX (get_listbox_from_category (self, data->category)); + next_row = gtk_list_box_get_row_at_index (listbox, row_index + 1); + + /* Try the previous one if the current is invalid */ + if (!next_row || next_row == self->devices_row || next_row == self->details_row) + next_row = gtk_list_box_get_row_at_index (listbox, row_index - 1); + + if (next_row) + g_signal_emit_by_name (next_row, "activate"); +} + static CcPanelListView get_view_from_listbox (CcPanelList *self, GtkWidget *listbox) @@ -159,12 +206,13 @@ row_data_free (RowData *data) } static RowData* -row_data_new (CcPanelCategory category, - const gchar *id, - const gchar *name, - const gchar *description, - gchar **keywords, - const gchar *icon) +row_data_new (CcPanelCategory category, + const gchar *id, + const gchar *name, + const gchar *description, + gchar **keywords, + const gchar *icon, + CcPanelVisibility visibility) { GtkWidget *label, *grid, *image; RowData *data; @@ -222,6 +270,9 @@ row_data_new (CcPanelCategory category, g_object_set_data_full (G_OBJECT (data->row), "data", data, (GDestroyNotify) row_data_free); + data->visibility = visibility; + gtk_widget_set_visible (data->row, visibility == CC_PANEL_VISIBLE); + return data; } @@ -766,18 +817,21 @@ cc_panel_list_activate (CcPanelList *self) { GtkListBoxRow *row; GtkWidget *listbox; + guint i = 0; + + CC_ENTRY; g_return_val_if_fail (CC_IS_PANEL_LIST (self), FALSE); listbox = get_listbox_from_view (self, self->view); - if (self->view == CC_PANEL_LIST_SEARCH) - row = gtk_list_box_get_row_at_y (GTK_LIST_BOX (listbox), 0); - else - row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (listbox), 0); + /* Select the first visible row */ + do + row = gtk_list_box_get_row_at_index (GTK_LIST_BOX (listbox), i++); + while (row && !gtk_widget_get_visible (GTK_WIDGET (row))); /* If the row is valid, activate it */ - if (row) + if (row && !gtk_list_box_row_is_selected (row)) { gtk_list_box_select_row (GTK_LIST_BOX (listbox), row); gtk_widget_grab_focus (GTK_WIDGET (row)); @@ -785,7 +839,7 @@ cc_panel_list_activate (CcPanelList *self) g_signal_emit_by_name (row, "activate"); } - return row != NULL; + CC_RETURN (row != NULL); } const gchar* @@ -865,13 +919,14 @@ cc_panel_list_set_view (CcPanelList *self, } void -cc_panel_list_add_panel (CcPanelList *self, - CcPanelCategory category, - const gchar *id, - const gchar *title, - const gchar *description, - gchar **keywords, - const gchar *icon) +cc_panel_list_add_panel (CcPanelList *self, + CcPanelCategory category, + const gchar *id, + const gchar *title, + const gchar *description, + gchar **keywords, + const gchar *icon, + CcPanelVisibility visibility) { GtkWidget *listbox; RowData *data, *search_data; @@ -879,27 +934,13 @@ cc_panel_list_add_panel (CcPanelList *self, g_return_if_fail (CC_IS_PANEL_LIST (self)); /* Add the panel to the proper listbox */ - data = row_data_new (category, id, title, description, keywords, icon); - - switch (category) - { - case CC_CATEGORY_DEVICES: - listbox = self->devices_listbox; - break; - - case CC_CATEGORY_DETAILS: - listbox = self->details_listbox; - break; - - default: - listbox = self->main_listbox; - break; - } + data = row_data_new (category, id, title, description, keywords, icon, visibility); + listbox = get_listbox_from_category (self, category); gtk_container_add (GTK_CONTAINER (listbox), data->row); /* And add to the search listbox too */ - search_data = row_data_new (category, id, title, description, keywords, icon); + search_data = row_data_new (category, id, title, description, keywords, icon, visibility); gtk_container_add (GTK_CONTAINER (self->search_listbox), search_data->row); g_hash_table_insert (self->id_to_data, data->id, data); @@ -925,8 +966,19 @@ cc_panel_list_set_active_panel (CcPanelList *self, g_assert (data != NULL); + /* Stop if row is supposed to be always hidden */ + if (data->visibility == CC_PANEL_HIDDEN) + { + g_debug ("Panel '%s' is always hidden, stopping.", id); + cc_panel_list_activate (self); + return; + } + listbox = gtk_widget_get_parent (data->row); + /* The row might be hidden now, so make sure it's visible */ + gtk_widget_show (data->row); + gtk_list_box_select_row (GTK_LIST_BOX (listbox), GTK_LIST_BOX_ROW (data->row)); gtk_widget_grab_focus (data->row); @@ -937,3 +989,38 @@ cc_panel_list_set_active_panel (CcPanelList *self, g_signal_emit_by_name (data->row, "activate"); } + +/** + * cc_panel_list_set_panel_visibility: + * @self: a #CcPanelList + * @id: the id of the panel + * @visibility: visibility of panel with @id + * + * Sets the visibility of panel with @id. @id must be a valid + * id with a corresponding panel. + */ +void +cc_panel_list_set_panel_visibility (CcPanelList *self, + const gchar *id, + CcPanelVisibility visibility) +{ + RowData *data; + + g_return_if_fail (CC_IS_PANEL_LIST (self)); + + data = g_hash_table_lookup (self->id_to_data, id); + + g_assert (data != NULL); + + data->visibility = visibility; + + /* If this is the currently selected row, and the panel can't be displayed + * (i.e. visibility != VISIBLE), then select the next possible row */ + if (gtk_list_box_row_is_selected (GTK_LIST_BOX_ROW (data->row)) && + visibility != CC_PANEL_VISIBLE) + { + activate_row_below (self, data); + } + + gtk_widget_set_visible (data->row, visibility == CC_PANEL_VISIBLE); +} diff --git a/shell/cc-panel-list.h b/shell/cc-panel-list.h index 14db2fece6..303ce1d9c8 100644 --- a/shell/cc-panel-list.h +++ b/shell/cc-panel-list.h @@ -60,11 +60,16 @@ void cc_panel_list_add_panel (CcPanelList const gchar *title, const gchar *description, gchar **keywords, - const gchar *icon); + const gchar *icon, + CcPanelVisibility visibility); void cc_panel_list_set_active_panel (CcPanelList *self, const gchar *id); +void cc_panel_list_set_panel_visibility (CcPanelList *self, + const gchar *id, + CcPanelVisibility visibility); + G_END_DECLS #endif /* CC_PANEL_LIST_H */ diff --git a/shell/cc-panel.h b/shell/cc-panel.h index c517164bf9..c14bad8931 100644 --- a/shell/cc-panel.h +++ b/shell/cc-panel.h @@ -36,6 +36,20 @@ G_DECLARE_DERIVABLE_TYPE (CcPanel, cc_panel, CC, PANEL, GtkBin) +/** + * CcPanelVisibility: + * + * @CC_PANEL_HIDDEN: Panel is hidden from search and sidebar, and not reachable. + * @CC_PANEL_VISIBLE_IN_SEARCH: Panel is hidden from main view, but can be accessed from search. + * @CC_PANEL_VISIBLE: Panel is visible everywhere. + */ +typedef enum +{ + CC_PANEL_HIDDEN, + CC_PANEL_VISIBLE_IN_SEARCH, + CC_PANEL_VISIBLE, +} CcPanelVisibility; + /* cc-shell.h requires CcPanel, so make sure it is defined first */ #include "cc-shell.h" diff --git a/shell/cc-shell-model.c b/shell/cc-shell-model.c index 4dcddba458..2e209a7d02 100644 --- a/shell/cc-shell-model.c +++ b/shell/cc-shell-model.c @@ -254,8 +254,8 @@ cc_shell_model_class_init (CcShellModelClass *klass) static void cc_shell_model_init (CcShellModel *self) { - GType types[] = {G_TYPE_STRING, G_TYPE_STRING, G_TYPE_APP_INFO, G_TYPE_STRING, - G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_ICON, G_TYPE_STRV}; + GType types[] = {G_TYPE_STRING, G_TYPE_STRING, G_TYPE_APP_INFO, G_TYPE_STRING, G_TYPE_UINT, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_ICON, G_TYPE_STRV, G_TYPE_UINT }; gtk_list_store_set_column_types (GTK_LIST_STORE (self), N_COLS, types); @@ -318,6 +318,7 @@ cc_shell_model_add_item (CcShellModel *model, COL_CASEFOLDED_DESCRIPTION, casefolded_description, COL_GICON, icon, COL_KEYWORDS, keywords, + COL_VISIBILITY, CC_PANEL_VISIBLE, -1); g_free (casefolded_name); @@ -399,3 +400,43 @@ cc_shell_model_set_sort_terms (CcShellModel *self, self, NULL); } + +void +cc_shell_model_set_panel_visibility (CcShellModel *self, + const gchar *id, + CcPanelVisibility visibility) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean valid; + + g_return_if_fail (CC_IS_SHELL_MODEL (self)); + + model = GTK_TREE_MODEL (self); + + /* Find the iter for the panel with the given id */ + valid = gtk_tree_model_get_iter_first (model, &iter); + + while (valid) + { + g_autofree gchar *item_id = NULL; + + gtk_tree_model_get (model, &iter, COL_ID, &item_id, -1); + + /* Found the iter */ + if (g_str_equal (id, item_id)) + break; + + /* If not found, continue */ + valid = gtk_tree_model_iter_next (model, &iter); + } + + /* If we don't find any panel with the given id, we'll iterate until + * valid == FALSE, so we can use this variable to determine if the + * panel was found or not. It is a programming error to try to set + * the visibility of a non-existant panel. + */ + g_assert (valid); + + gtk_list_store_set (GTK_LIST_STORE (self), &iter, COL_VISIBILITY, visibility, -1); +} diff --git a/shell/cc-shell-model.h b/shell/cc-shell-model.h index b0fe50ff60..397fc7d221 100644 --- a/shell/cc-shell-model.h +++ b/shell/cc-shell-model.h @@ -20,6 +20,8 @@ #pragma once +#include "cc-panel.h" + #include G_BEGIN_DECLS @@ -50,6 +52,7 @@ enum COL_CASEFOLDED_DESCRIPTION, COL_GICON, COL_KEYWORDS, + COL_VISIBILITY, N_COLS }; @@ -57,19 +60,23 @@ enum CcShellModel* cc_shell_model_new (void); -void cc_shell_model_add_item (CcShellModel *model, - CcPanelCategory category, - GAppInfo *appinfo, - const char *id); +void cc_shell_model_add_item (CcShellModel *model, + CcPanelCategory category, + GAppInfo *appinfo, + const char *id); + +gboolean cc_shell_model_has_panel (CcShellModel *model, + const char *id); -gboolean cc_shell_model_has_panel (CcShellModel *model, - const char *id); +gboolean cc_shell_model_iter_matches_search (CcShellModel *model, + GtkTreeIter *iter, + const char *term); -gboolean cc_shell_model_iter_matches_search (CcShellModel *model, - GtkTreeIter *iter, - const char *term); +void cc_shell_model_set_sort_terms (CcShellModel *model, + GStrv terms); -void cc_shell_model_set_sort_terms (CcShellModel *model, - GStrv terms); +void cc_shell_model_set_panel_visibility (CcShellModel *self, + const gchar *id, + CcPanelVisibility visible); G_END_DECLS diff --git a/shell/cc-window.c b/shell/cc-window.c index b041f1d76c..1d417a4ea7 100644 --- a/shell/cc-window.c +++ b/shell/cc-window.c @@ -162,11 +162,12 @@ get_symbolic_icon_name_from_g_icon (GIcon *gicon) } static gboolean -activate_panel (CcWindow *self, - const gchar *id, - GVariant *parameters, - const gchar *name, - GIcon *gicon) +activate_panel (CcWindow *self, + const gchar *id, + GVariant *parameters, + const gchar *name, + GIcon *gicon, + CcPanelVisibility visibility) { g_autoptr (GTimer) timer = NULL; GtkWidget *box, *title_widget; @@ -175,6 +176,9 @@ activate_panel (CcWindow *self, if (!id) return FALSE; + if (visibility == CC_PANEL_HIDDEN) + return FALSE; + timer = g_timer_new (); g_settings_set_string (self->settings, "last-panel", id); @@ -283,6 +287,23 @@ update_list_title (CcWindow *self) gtk_header_bar_set_title (GTK_HEADER_BAR (self->header), title); } +static void +on_row_changed_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + CcWindow *self) +{ + g_autofree gchar *id = NULL; + CcPanelVisibility visibility; + + gtk_tree_model_get (model, iter, + COL_ID, &id, + COL_VISIBILITY, &visibility, + -1); + + cc_panel_list_set_panel_visibility (CC_PANEL_LIST (self->panel_list), id, visibility); +} + static void setup_model (CcWindow *shell) { @@ -309,6 +330,7 @@ setup_model (CcWindow *shell) g_autofree gchar *id = NULL; g_autofree gchar *icon_name = NULL; g_autofree GStrv keywords = NULL; + CcPanelVisibility visibility; gtk_tree_model_get (model, &iter, COL_CATEGORY, &category, @@ -317,6 +339,7 @@ setup_model (CcWindow *shell) COL_ID, &id, COL_NAME, &name, COL_KEYWORDS, &keywords, + COL_VISIBILITY, &visibility, -1); icon_name = get_symbolic_icon_name_from_g_icon (icon); @@ -327,10 +350,14 @@ setup_model (CcWindow *shell) name, description, keywords, - icon_name); + icon_name, + visibility); valid = gtk_tree_model_iter_next (model, &iter); } + + /* React to visibility changes */ + g_signal_connect_object (model, "row-changed", G_CALLBACK (on_row_changed_cb), shell, 0); } @@ -343,6 +370,7 @@ set_active_panel_from_id (CcShell *shell, { g_autoptr(GIcon) gicon = NULL; g_autofree gchar *name = NULL; + CcPanelVisibility visibility; GtkTreeIter iter; GtkWidget *old_panel; CcWindow *self; @@ -372,6 +400,7 @@ set_active_panel_from_id (CcShell *shell, COL_NAME, &name, COL_GICON, &gicon, COL_ID, &id, + COL_VISIBILITY, &visibility, -1); if (id && strcmp (id, start_id) == 0) -- GitLab From 10cf920fef3cab239bfd1bf5fc952d2121686cf0 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 8 Apr 2018 19:22:39 -0300 Subject: [PATCH 4/8] panel-loader: Add support for static init functions Static init functions are functions that initialize resources or monitor daemons without needing any contextual information, such as a CcPanel instance or access to a CcShell implementation. These functions are meant to be used for monitoring a panel's visibility, when it makes sense. Usually, panels that represent hardware that potentially is not available or is not supported should hide itself. Following this commit, the Wi-Fi panel panel will be adapted as the first user of this new API. Other panels that require this functionality will be adapted later. --- shell/cc-panel-loader.c | 68 +++++++++++++++++++++++++---------------- shell/cc-panel.h | 14 +++++++++ 2 files changed, 55 insertions(+), 27 deletions(-) diff --git a/shell/cc-panel-loader.c b/shell/cc-panel-loader.c index 55492ef984..b64ad034a7 100644 --- a/shell/cc-panel-loader.c +++ b/shell/cc-panel-loader.c @@ -24,6 +24,7 @@ #include #include +#include "cc-panel.h" #include "cc-panel-loader.h" #ifndef CC_PANEL_LOADER_NO_GTYPES @@ -63,11 +64,11 @@ extern GType cc_user_panel_get_type (void); extern GType cc_wacom_panel_get_type (void); #endif /* BUILD_WACOM */ -#define PANEL_TYPE(name, get_type) { name, get_type } +#define PANEL_TYPE(name, get_type, init_func) { name, get_type, init_func } #else /* CC_PANEL_LOADER_NO_GTYPES */ -#define PANEL_TYPE(name, get_type) { name } +#define PANEL_TYPE(name, get_type, init_func) { name } #endif @@ -75,40 +76,41 @@ static struct { const char *name; #ifndef CC_PANEL_LOADER_NO_GTYPES GType (*get_type)(void); + CcPanelStaticInitFunc static_init_func; #endif } all_panels[] = { - PANEL_TYPE("background", cc_background_panel_get_type ), + PANEL_TYPE("background", cc_background_panel_get_type, NULL), #ifdef BUILD_BLUETOOTH - PANEL_TYPE("bluetooth", cc_bluetooth_panel_get_type ), + PANEL_TYPE("bluetooth", cc_bluetooth_panel_get_type, NULL), #endif - PANEL_TYPE("color", cc_color_panel_get_type ), - PANEL_TYPE("datetime", cc_date_time_panel_get_type ), - PANEL_TYPE("display", cc_display_panel_get_type ), - PANEL_TYPE("info-overview", cc_info_overview_panel_get_type), - PANEL_TYPE("default-apps", cc_info_default_apps_panel_get_type), - PANEL_TYPE("removable-media", cc_info_removable_media_panel_get_type), - PANEL_TYPE("keyboard", cc_keyboard_panel_get_type ), - PANEL_TYPE("mouse", cc_mouse_panel_get_type ), + PANEL_TYPE("color", cc_color_panel_get_type, NULL), + PANEL_TYPE("datetime", cc_date_time_panel_get_type, NULL), + PANEL_TYPE("display", cc_display_panel_get_type, NULL), + PANEL_TYPE("info-overview", cc_info_overview_panel_get_type, NULL), + PANEL_TYPE("default-apps", cc_info_default_apps_panel_get_type, NULL), + PANEL_TYPE("removable-media", cc_info_removable_media_panel_get_type, NULL), + PANEL_TYPE("keyboard", cc_keyboard_panel_get_type, NULL), + PANEL_TYPE("mouse", cc_mouse_panel_get_type, NULL), #ifdef BUILD_NETWORK - PANEL_TYPE("network", cc_network_panel_get_type ), - PANEL_TYPE("wifi", cc_wifi_panel_get_type ), + PANEL_TYPE("network", cc_network_panel_get_type, NULL), + PANEL_TYPE("wifi", cc_wifi_panel_get_type, NULL), #endif - PANEL_TYPE("notifications", cc_notifications_panel_get_type), - PANEL_TYPE("online-accounts", cc_goa_panel_get_type ), - PANEL_TYPE("power", cc_power_panel_get_type ), - PANEL_TYPE("printers", cc_printers_panel_get_type ), - PANEL_TYPE("privacy", cc_privacy_panel_get_type ), - PANEL_TYPE("region", cc_region_panel_get_type ), - PANEL_TYPE("search", cc_search_panel_get_type ), - PANEL_TYPE("sharing", cc_sharing_panel_get_type ), - PANEL_TYPE("sound", cc_sound_panel_get_type ), + PANEL_TYPE("notifications", cc_notifications_panel_get_type, NULL), + PANEL_TYPE("online-accounts", cc_goa_panel_get_type, NULL), + PANEL_TYPE("power", cc_power_panel_get_type, NULL), + PANEL_TYPE("printers", cc_printers_panel_get_type, NULL), + PANEL_TYPE("privacy", cc_privacy_panel_get_type, NULL), + PANEL_TYPE("region", cc_region_panel_get_type, NULL), + PANEL_TYPE("search", cc_search_panel_get_type, NULL), + PANEL_TYPE("sharing", cc_sharing_panel_get_type, NULL), + PANEL_TYPE("sound", cc_sound_panel_get_type, NULL), #ifdef BUILD_THUNDERBOLT - PANEL_TYPE("thunderbolt", cc_bolt_panel_get_type ), + PANEL_TYPE("thunderbolt", cc_bolt_panel_get_type, NULL), #endif - PANEL_TYPE("universal-access", cc_ua_panel_get_type ), - PANEL_TYPE("user-accounts", cc_user_panel_get_type ), + PANEL_TYPE("universal-access", cc_ua_panel_get_type, NULL), + PANEL_TYPE("user-accounts", cc_user_panel_get_type, NULL), #ifdef BUILD_WACOM - PANEL_TYPE("wacom", cc_wacom_panel_get_type ), + PANEL_TYPE("wacom", cc_wacom_panel_get_type, NULL), #endif }; @@ -192,6 +194,18 @@ cc_panel_loader_fill_model (CcShellModel *model) cc_shell_model_add_item (model, category, G_APP_INFO (app), all_panels[i].name); } + + /* If there's an static init function, execute it after adding all panels to + * the model. This will allow the panels to show or hide themselves without + * having an instance running. + */ +#ifndef CC_PANEL_LOADER_NO_GTYPES + for (i = 0; i < G_N_ELEMENTS (all_panels); i++) + { + if (all_panels[i].static_init_func) + all_panels[i].static_init_func (); + } +#endif } #ifndef CC_PANEL_LOADER_NO_GTYPES diff --git a/shell/cc-panel.h b/shell/cc-panel.h index c14bad8931..5c705a4aa3 100644 --- a/shell/cc-panel.h +++ b/shell/cc-panel.h @@ -32,6 +32,20 @@ */ #define CC_PANEL_REGISTER(PluginName, plugin_name) G_DEFINE_TYPE (PluginName, plugin_name, CC_TYPE_PANEL) +/** + * CcPanelStaticInitFunc: + * + * Function that statically allocates resources and initializes + * any data that the panel will make use of during runtime. + * + * If panels represent hardware that can potentially not exist, + * e.g. the Wi-Fi panel, these panels can use this function to + * show or hide themselves without needing to have an instance + * created and running. + */ +typedef void (*CcPanelStaticInitFunc) (void); + + #define CC_TYPE_PANEL (cc_panel_get_type()) G_DECLARE_DERIVABLE_TYPE (CcPanel, cc_panel, CC, PANEL, GtkBin) -- GitLab From 4151af5ea2d475b4e6ac8194523fc0c929d33039 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 8 Apr 2018 21:40:28 -0300 Subject: [PATCH 5/8] wifi: Hide when no Wi-Fi adapter is available It's WIP. --- panels/network/cc-wifi-panel.c | 63 ++++++++++++++++++++++++++++++++++ panels/network/cc-wifi-panel.h | 2 ++ shell/cc-panel-loader.c | 7 +++- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/panels/network/cc-wifi-panel.c b/panels/network/cc-wifi-panel.c index d785bad0d1..d773cb0f24 100644 --- a/panels/network/cc-wifi-panel.c +++ b/panels/network/cc-wifi-panel.c @@ -23,6 +23,8 @@ #include "net-device-wifi.h" #include "network-dialogs.h" +#include "shell/cc-application.h" +#include "shell/cc-debug.h" #include "shell/cc-object-storage.h" #include "list-box-helper.h" @@ -81,6 +83,67 @@ enum N_PROPS }; +/* Static init function */ + +static void +update_panel_visibility (NMClient *client) +{ + const GPtrArray *devices; + CcApplication *application; + gboolean visible; + guint i; + + CC_TRACE_MSG ("Updating Wi-Fi panel visibility"); + + devices = nm_client_get_devices (client); + visible = FALSE; + + for (i = 0; devices && i < devices->len; i++) + { + NMDevice *device = g_ptr_array_index (devices, i); + + visible |= NM_IS_DEVICE_WIFI (device); + + if (visible) + break; + } + + /* Set the new visibility */ + application = CC_APPLICATION (g_application_get_default ()); + cc_shell_model_set_panel_visibility (cc_application_get_model (application), + "wifi", + visible ? CC_PANEL_VISIBLE : CC_PANEL_VISIBLE_IN_SEARCH); + + CC_TRACE_MSG ("Wi-Fi panel visible: %s", visible ? "yes" : "no"); +} + +void +cc_wifi_panel_static_init_func (void) +{ + NMClient *client; + + g_debug ("Monitoring NetworkManager for Wi-Fi devices"); + + /* Create and store a NMClient instance if it doesn't exist yet */ + if (!cc_object_storage_has_object (CC_OBJECT_NMCLIENT)) + { + client = nm_client_new (NULL, NULL); + cc_object_storage_add_object (CC_OBJECT_NMCLIENT, client); + g_object_unref (client); + } + + client = cc_object_storage_get_object (CC_OBJECT_NMCLIENT); + + /* Update the panel visibility and monitor for changes */ + + g_signal_connect (client, "device-added", G_CALLBACK (update_panel_visibility), NULL); + g_signal_connect (client, "device-removed", G_CALLBACK (update_panel_visibility), NULL); + + update_panel_visibility (client); + + g_object_unref (client); +} + /* Auxiliary methods */ static void diff --git a/panels/network/cc-wifi-panel.h b/panels/network/cc-wifi-panel.h index 43ad4bf96b..059507dc96 100644 --- a/panels/network/cc-wifi-panel.h +++ b/panels/network/cc-wifi-panel.h @@ -28,6 +28,8 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (CcWifiPanel, cc_wifi_panel, CC, WIFI_PANEL, CcPanel) +void cc_wifi_panel_static_init_func (void); + G_END_DECLS #endif /* CC_WIFI_PANEL_H */ diff --git a/shell/cc-panel-loader.c b/shell/cc-panel-loader.c index b64ad034a7..3beba0bfdd 100644 --- a/shell/cc-panel-loader.c +++ b/shell/cc-panel-loader.c @@ -64,6 +64,11 @@ extern GType cc_user_panel_get_type (void); extern GType cc_wacom_panel_get_type (void); #endif /* BUILD_WACOM */ +/* Static init functions */ +#ifdef BUILD_NETWORK +extern void cc_wifi_panel_static_init_func (void); +#endif /* BUILD_NETWORK */ + #define PANEL_TYPE(name, get_type, init_func) { name, get_type, init_func } #else /* CC_PANEL_LOADER_NO_GTYPES */ @@ -93,7 +98,7 @@ static struct { PANEL_TYPE("mouse", cc_mouse_panel_get_type, NULL), #ifdef BUILD_NETWORK PANEL_TYPE("network", cc_network_panel_get_type, NULL), - PANEL_TYPE("wifi", cc_wifi_panel_get_type, NULL), + PANEL_TYPE("wifi", cc_wifi_panel_get_type, cc_wifi_panel_static_init_func), #endif PANEL_TYPE("notifications", cc_notifications_panel_get_type, NULL), PANEL_TYPE("online-accounts", cc_goa_panel_get_type, NULL), -- GitLab From 90e7f050576852328b9668e6822e7ea9d2c2c6e4 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 3 May 2018 10:11:35 -0300 Subject: [PATCH 6/8] wi-fi: Remove unused header --- panels/network/cc-wifi-panel.c | 1 - 1 file changed, 1 deletion(-) diff --git a/panels/network/cc-wifi-panel.c b/panels/network/cc-wifi-panel.c index d773cb0f24..be8f787e65 100644 --- a/panels/network/cc-wifi-panel.c +++ b/panels/network/cc-wifi-panel.c @@ -26,7 +26,6 @@ #include "shell/cc-application.h" #include "shell/cc-debug.h" #include "shell/cc-object-storage.h" -#include "list-box-helper.h" #include #include -- GitLab From a461045f379721fa248572f70ca96ec406b25223 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 3 May 2018 11:27:12 -0300 Subject: [PATCH 7/8] window: Don't remove header widgets before changing panels When changing panels, CcWindow can only remove the previous panel's header widgets after making sure the new panel was successfully set. However, this is not the case, and when the current panel fails to be set, the previous panel's header widget is removed. Fix that by waiting to remove the header widgets until the panel is successfully set. --- shell/cc-window.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/shell/cc-window.c b/shell/cc-window.c index 1d417a4ea7..7e1620c21b 100644 --- a/shell/cc-window.c +++ b/shell/cc-window.c @@ -386,9 +386,6 @@ set_active_panel_from_id (CcShell *shell, return TRUE; } - /* clear any custom widgets */ - remove_all_custom_widgets (self); - iter_valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->store), &iter); /* find the details for this item */ @@ -421,12 +418,18 @@ set_active_panel_from_id (CcShell *shell, } /* Activate the panel */ - activated = activate_panel (CC_WINDOW (shell), start_id, parameters, name, gicon); + activated = activate_panel (CC_WINDOW (shell), start_id, parameters, name, gicon, visibility); /* Failed to activate the panel for some reason, let's keep the old * panel around instead */ if (!activated) - return TRUE; + { + g_debug ("Failed to activate panel"); + return TRUE; + } + + /* clear any custom widgets */ + remove_all_custom_widgets (self); if (add_to_history) add_current_panel_to_history (shell, start_id); -- GitLab From 62cfe91689f1f2486c1d34cfe968dc7cf3371cb5 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Tue, 29 May 2018 18:03:05 -0300 Subject: [PATCH 8/8] panel-list: Improve search behavior There are a couple of small problems with the search behavior here. First, if we select a panel from search and leave it, it will continue to stay there forever. Second, if panels change their visibility status during runtime, the panel list wouldn't respond accordingly. --- shell/cc-panel-list.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/shell/cc-panel-list.c b/shell/cc-panel-list.c index f752c231b3..408a0de77b 100644 --- a/shell/cc-panel-list.c +++ b/shell/cc-panel-list.c @@ -57,11 +57,13 @@ struct _CcPanelList GtkWidget *empty_search_placeholder; + gchar *current_panel_id; gchar *search_query; CcPanelListView previous_view; CcPanelListView view; GHashTable *id_to_data; + GHashTable *id_to_search_data; }; G_DEFINE_TYPE (CcPanelList, cc_panel_list, GTK_TYPE_STACK) @@ -271,7 +273,6 @@ row_data_new (CcPanelCategory category, g_object_set_data_full (G_OBJECT (data->row), "data", data, (GDestroyNotify) row_data_free); data->visibility = visibility; - gtk_widget_set_visible (data->row, visibility == CC_PANEL_VISIBLE); return data; } @@ -631,7 +632,9 @@ cc_panel_list_finalize (GObject *object) CcPanelList *self = (CcPanelList *)object; g_clear_pointer (&self->search_query, g_free); + g_clear_pointer (&self->current_panel_id, g_free); g_clear_pointer (&self->id_to_data, g_hash_table_destroy); + g_clear_pointer (&self->id_to_search_data, g_hash_table_destroy); G_OBJECT_CLASS (cc_panel_list_parent_class)->finalize (object); } @@ -770,6 +773,7 @@ cc_panel_list_init (CcPanelList *self) gtk_widget_init_template (GTK_WIDGET (self)); self->id_to_data = g_hash_table_new (g_str_hash, g_str_equal); + self->id_to_search_data = g_hash_table_new (g_str_hash, g_str_equal); self->view = CC_PANEL_LIST_MAIN; gtk_list_box_set_sort_func (GTK_LIST_BOX (self->main_listbox), @@ -935,15 +939,19 @@ cc_panel_list_add_panel (CcPanelList *self, /* Add the panel to the proper listbox */ data = row_data_new (category, id, title, description, keywords, icon, visibility); + gtk_widget_set_visible (data->row, visibility == CC_PANEL_VISIBLE); listbox = get_listbox_from_category (self, category); gtk_container_add (GTK_CONTAINER (listbox), data->row); /* And add to the search listbox too */ search_data = row_data_new (category, id, title, description, keywords, icon, visibility); + gtk_widget_set_visible (search_data->row, visibility != CC_PANEL_HIDDEN); + gtk_container_add (GTK_CONTAINER (self->search_listbox), search_data->row); g_hash_table_insert (self->id_to_data, data->id, data); + g_hash_table_insert (self->id_to_search_data, search_data->id, search_data); } /** @@ -974,6 +982,22 @@ cc_panel_list_set_active_panel (CcPanelList *self, return; } + /* If the currently selected panel is not always visible, for example when + * the panel is only visible on search and we're temporarily seeing it, make + * sure to hide it after the user moves out. + */ + if (self->current_panel_id != NULL && g_strcmp0 (self->current_panel_id, id) != 0) + { + RowData *current_row_data; + + current_row_data = g_hash_table_lookup (self->id_to_data, self->current_panel_id); + + /* We cannot be showing a non-existant panel */ + g_assert (current_row_data != NULL); + + gtk_widget_set_visible (current_row_data->row, current_row_data->visibility == CC_PANEL_VISIBLE); + } + listbox = gtk_widget_get_parent (data->row); /* The row might be hidden now, so make sure it's visible */ @@ -988,6 +1012,10 @@ cc_panel_list_set_active_panel (CcPanelList *self, self->autoselect_panel = FALSE; g_signal_emit_by_name (data->row, "activate"); + + /* Store the current panel id */ + g_clear_pointer (&self->current_panel_id, g_free); + self->current_panel_id = g_strdup (id); } /** @@ -1004,13 +1032,15 @@ cc_panel_list_set_panel_visibility (CcPanelList *self, const gchar *id, CcPanelVisibility visibility) { - RowData *data; + RowData *data, *search_data; g_return_if_fail (CC_IS_PANEL_LIST (self)); data = g_hash_table_lookup (self->id_to_data, id); + search_data = g_hash_table_lookup (self->id_to_search_data, id); g_assert (data != NULL); + g_assert (search_data != NULL); data->visibility = visibility; @@ -1023,4 +1053,5 @@ cc_panel_list_set_panel_visibility (CcPanelList *self, } gtk_widget_set_visible (data->row, visibility == CC_PANEL_VISIBLE); + gtk_widget_set_visible (search_data->row, visibility =! CC_PANEL_HIDDEN); } -- GitLab