diff options
author | Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | 2016-06-06 14:13:54 -0300 |
---|---|---|
committer | Georges Basile Stavracas Neto <georges.stavracas@gmail.com> | 2016-06-14 12:16:43 -0300 |
commit | cb1e0c4a01d9893086fddb57f3068fa0f61e48cd (patch) | |
tree | 295257042363302b4609d45c898f047b512f0fe1 /shell | |
parent | 2777fd583d150938beb8f2c110de50c55f3a3fbd (diff) | |
download | gnome-control-center-cb1e0c4a01d9893086fddb57f3068fa0f61e48cd.tar.gz |
panel-list: create a custom class to handle the sidelist
As the sidelist gets more complex, managing it in CcWindow
would make it very confusing.
This patch introduces the CcPanelList, a widget that manages
the sidelist.
https://bugzilla.gnome.org/show_bug.cgi?id=767301
Diffstat (limited to 'shell')
-rw-r--r-- | shell/Makefile.am | 7 | ||||
-rw-r--r-- | shell/alt/Makefile.am | 3 | ||||
-rw-r--r-- | shell/alt/cc-panel-list.c | 808 | ||||
-rw-r--r-- | shell/alt/cc-panel-list.h | 67 | ||||
-rw-r--r-- | shell/gnome-control-center.gresource.xml | 1 | ||||
-rw-r--r-- | shell/panel-list.ui | 209 |
6 files changed, 1095 insertions, 0 deletions
diff --git a/shell/Makefile.am b/shell/Makefile.am index d10520e3f..45f63639e 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -64,6 +64,12 @@ gnome_control_center_alt_SOURCES = \ gnome_control_center_LDFLAGS = -export-dynamic gnome_control_center_alt_LDFLAGS = -export-dynamic +# Temporarily add the CC_ENABLE_ALT_CATEGORIES compile-time +# flag to keep the current panels working. +gnome_control_center_alt_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -DCC_ENABLE_ALT_CATEGORIES + gnome_control_center_LDADD = \ libshell.la \ $(SHELL_LIBS) \ @@ -153,6 +159,7 @@ EXTRA_DIST = \ $(completion_in_files) \ gnome-control-center.gresource.xml \ help-overlay.ui \ + panel-list.ui \ window.ui \ $(resource_files) \ list-panel.sh diff --git a/shell/alt/Makefile.am b/shell/alt/Makefile.am index b933eea7a..8e84811f3 100644 --- a/shell/alt/Makefile.am +++ b/shell/alt/Makefile.am @@ -1,5 +1,6 @@ AM_CPPFLAGS = \ -DGNOMELOCALEDIR="\"$(datadir)/locale\""\ + -DCC_ENABLE_ALT_CATEGORIES \ -I$(top_srcdir) \ $(SHELL_CFLAGS) \ $(CHEESE_CFLAGS) \ @@ -11,6 +12,8 @@ AM_CPPFLAGS = \ noinst_LTLIBRARIES = libshell_alt.la libshell_alt_la_SOURCES = \ + cc-panel-list.c \ + cc-panel-list.h \ cc-window.c \ cc-window.h diff --git a/shell/alt/cc-panel-list.c b/shell/alt/cc-panel-list.c new file mode 100644 index 000000000..1c26dd2de --- /dev/null +++ b/shell/alt/cc-panel-list.c @@ -0,0 +1,808 @@ +/* cc-panel-list.c + * + * Copyright (C) 2016 Endless, Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author: Georges Basile Stavracas Neto <gbsneto@gnome.org> + */ + +#include "cc-panel-list.h" +#include "cc-util.h" + +typedef struct +{ + GtkWidget *row; + GtkWidget *description_label; + CcPanelCategory category; + gchar *id; + gchar *name; + gchar *description; +} RowData; + +struct _CcPanelList +{ + GtkStack parent; + + GtkWidget *details_listbox; + GtkWidget *devices_listbox; + GtkWidget *main_listbox; + GtkWidget *search_listbox; + + GtkListBoxRow *details_row; + GtkListBoxRow *devices_row; + + GtkWidget *empty_search_placeholder; + + gchar *search_query; + + CcPanelListView previous_view; + CcPanelListView view; +}; + +G_DEFINE_TYPE (CcPanelList, cc_panel_list, GTK_TYPE_STACK) + +enum +{ + PROP_0, + PROP_SEARCH_MODE, + PROP_SEARCH_QUERY, + PROP_VIEW, + N_PROPS +}; + +enum +{ + SHOW_PANEL, + LAST_SIGNAL +}; + +static GParamSpec *properties [N_PROPS] = { NULL, }; +static gint signals [LAST_SIGNAL] = { 0, }; + +/* + * Auxiliary methods + */ +static GtkWidget* +get_listbox_from_view (CcPanelList *self, + CcPanelListView view) +{ + switch (view) + { + case CC_PANEL_LIST_MAIN: + return self->main_listbox; + + case CC_PANEL_LIST_DETAILS: + return self->details_listbox; + + case CC_PANEL_LIST_DEVICES: + return self->devices_listbox; + + case CC_PANEL_LIST_SEARCH: + return self->search_listbox; + + default: + return NULL; + } +} + +static CcPanelListView +get_view_from_listbox (CcPanelList *self, + GtkWidget *listbox) +{ + if (listbox == self->main_listbox) + return CC_PANEL_LIST_MAIN; + + if (listbox == self->details_listbox) + return CC_PANEL_LIST_DETAILS; + + if (listbox == self->devices_listbox) + return CC_PANEL_LIST_DEVICES; + + return CC_PANEL_LIST_SEARCH; +} + +static void +update_search (CcPanelList *self) +{ + /* + * Only change to the search view is there's a + * search query available. + */ + if (self->search_query && + g_utf8_strlen (self->search_query, -1) > 0) + { + if (self->view == CC_PANEL_LIST_MAIN) + cc_panel_list_set_view (self, CC_PANEL_LIST_SEARCH); + } + else + { + if (self->view == CC_PANEL_LIST_SEARCH) + cc_panel_list_set_view (self, self->previous_view); + } + + gtk_list_box_invalidate_filter (GTK_LIST_BOX (self->search_listbox)); + gtk_list_box_unselect_all (GTK_LIST_BOX (self->search_listbox)); +} + +/* + * RowData functions + */ +static void +row_data_free (RowData *data) +{ + g_free (data->description); + g_free (data->name); + g_free (data); +} + +static RowData* +row_data_new (CcPanelCategory category, + const gchar *id, + const gchar *name, + const gchar *description, + const gchar *icon) +{ + GtkWidget *label, *grid, *image; + RowData *data; + + data = g_new0 (RowData, 1); + data->category = category; + data->row = gtk_list_box_row_new (); + data->id = g_strdup (id); + data->name = g_strdup (name); + data->description = g_strdup (description); + + /* Setup the row */ + grid = g_object_new (GTK_TYPE_GRID, + "visible", TRUE, + "hexpand", TRUE, + "border-width", 12, + "column-spacing", 12, + NULL); + + /* Icon */ + image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON); + gtk_style_context_add_class (gtk_widget_get_style_context (image), "dim-label"); + + gtk_grid_attach (GTK_GRID (grid), image, 0, 0, 1, 1); + + gtk_widget_show (image); + + /* Name label */ + label = g_object_new (GTK_TYPE_LABEL, + "label", name, + "visible", TRUE, + "xalign", 0.0, + "hexpand", TRUE, + NULL); + gtk_grid_attach (GTK_GRID (grid), label, 1, 0, 1, 1); + + /* Description label */ + label = g_object_new (GTK_TYPE_LABEL, + "label", description, + "visible", FALSE, + "xalign", 0.0, + "hexpand", TRUE, + NULL); + gtk_label_set_max_width_chars (GTK_LABEL (label), 25); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + + gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label"); + gtk_grid_attach (GTK_GRID (grid), label, 1, 1, 1, 1); + + data->description_label = label; + + gtk_container_add (GTK_CONTAINER (data->row), grid); + gtk_widget_show (data->row); + + g_object_set_data_full (G_OBJECT (data->row), "data", data, (GDestroyNotify) row_data_free); + + return data; +} + +/* + * GtkListBox functions + */ +static gboolean +filter_func (GtkListBoxRow *row, + gpointer user_data) +{ + CcPanelList *self; + RowData *data; + gchar *search_text, *panel_text, *panel_description; + gboolean retval; + + self = CC_PANEL_LIST (user_data); + data = g_object_get_data (G_OBJECT (row), "data"); + + if (!self->search_query) + return TRUE; + + panel_text = cc_util_normalize_casefold_and_unaccent (data->name); + search_text = cc_util_normalize_casefold_and_unaccent (self->search_query); + panel_description = cc_util_normalize_casefold_and_unaccent (data->description); + + g_strstrip (panel_text); + g_strstrip (search_text); + g_strstrip (panel_description); + + /* + * The description label is only visible when the search is + * happening. + */ + gtk_widget_set_visible (data->description_label, self->view == CC_PANEL_LIST_SEARCH); + + retval = g_strstr_len (panel_text, -1, search_text) != NULL || + g_strstr_len (panel_description, -1, search_text) != NULL; + + g_free (panel_text); + g_free (search_text); + g_free (panel_description); + + return retval; +} + +static gint +sort_function (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer user_data) +{ + CcPanelList *self; + RowData *a_data, *b_data; + + self = CC_PANEL_LIST (user_data); + + /* Handle the Devices and the Details rows */ + if (a == self->details_row && b == self->devices_row) + return 1; + if (a == self->devices_row && b == self->details_row) + return -1; + if (a == self->details_row || a == self->devices_row) + return 1; + if (b == self->details_row || b == self->devices_row) + return -1; + + /* + * We can only retrieve the data after assuring that none + * of the rows are Devices and Details. + */ + a_data = g_object_get_data (G_OBJECT (a), "data"); + b_data = g_object_get_data (G_OBJECT (b), "data"); + + if (a_data->category != b_data->category) + return a_data->category - b_data->category; + + return g_strcmp0 (a_data->name, b_data->name); +} + +static gint +search_sort_function (GtkListBoxRow *a, + GtkListBoxRow *b, + gpointer user_data) +{ + CcPanelList *self; + RowData *a_data, *b_data; + gchar *a_name, *b_name, *search, *a_strstr, *b_strstr; + gint a_distance, b_distance; + gint retval; + + self = CC_PANEL_LIST (user_data); + search = NULL; + a_data = g_object_get_data (G_OBJECT (a), "data"); + b_data = g_object_get_data (G_OBJECT (b), "data"); + + a_distance = b_distance = G_MAXINT; + + a_name = cc_util_normalize_casefold_and_unaccent (a_data->name); + b_name = cc_util_normalize_casefold_and_unaccent (b_data->name); + g_strstrip (a_name); + g_strstrip (b_name); + + if (self->search_query) + { + search = cc_util_normalize_casefold_and_unaccent (self->search_query); + g_strstrip (search); + } + + /* Default result for empty search */ + if (!search || g_utf8_strlen (search, -1) == 0) + { + retval = g_strcmp0 (a_name, b_name); + goto out; + } + + a_strstr = g_strstr_len (a_name, -1, search); + b_strstr = g_strstr_len (b_name, -1, search); + + if (a_strstr) + a_distance = g_strstr_len (a_name, -1, search) - a_name; + + if (b_strstr) + b_distance = g_strstr_len (b_name, -1, search) - b_name; + + retval = a_distance - b_distance; + +out: + g_free (a_name); + g_free (b_name); + g_free (search); + + return retval; +} + +static void +header_func (GtkListBoxRow *row, + GtkListBoxRow *before, + gpointer user_data) +{ + CcPanelList *self = CC_PANEL_LIST (user_data); + + if (!before) + return; + + /* The Details row always have the separator */ + if (row == self->details_row) + { + GtkWidget *separator; + + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_widget_set_hexpand (separator, TRUE); + gtk_widget_show (separator); + + gtk_list_box_row_set_header (row, separator); + } + else + { + RowData *row_data, *before_data; + + if (row == self->devices_row || + before == self->details_row || + before == self->devices_row) + { + return; + } + + /* + * We can only retrieve the data after assuring that none + * of the rows are Devices and Details. + */ + row_data = g_object_get_data (G_OBJECT (row), "data"); + before_data = g_object_get_data (G_OBJECT (before), "data"); + + if (row_data->category != before_data->category) + { + GtkWidget *separator; + + separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_widget_set_hexpand (separator, TRUE); + gtk_widget_show (separator); + + gtk_list_box_row_set_header (row, separator); + } + } +} + +/* + * Callbacks + */ +static void +row_activated_cb (GtkWidget *listbox, + GtkListBoxRow *row, + CcPanelList *self) +{ + RowData *data; + + /* Details */ + if (row == self->details_row) + { + cc_panel_list_set_view (self, CC_PANEL_LIST_DETAILS); + return; + } + + /* Devices */ + if (row == self->devices_row) + { + cc_panel_list_set_view (self, CC_PANEL_LIST_DEVICES); + return; + } + + /* + * When a panel is selected, the previous one should be + * unselected, except when it's search. + */ + if (listbox != self->search_listbox) + { + if (listbox != self->main_listbox) + gtk_list_box_unselect_all (GTK_LIST_BOX (self->main_listbox)); + + if (listbox != self->details_listbox) + gtk_list_box_unselect_all (GTK_LIST_BOX (self->details_listbox)); + + if (listbox != self->devices_listbox) + gtk_list_box_unselect_all (GTK_LIST_BOX (self->devices_listbox)); + } + + /* + * Since we're not sure that the activated row is in the + * current view, set the view here. + */ + cc_panel_list_set_view (self, get_view_from_listbox (self, listbox)); + + data = g_object_get_data (G_OBJECT (row), "data"); + + g_signal_emit (self, signals[SHOW_PANEL], 0, data->id); +} + +static void +search_row_activated_cb (GtkWidget *listbox, + GtkListBoxRow *row, + CcPanelList *self) +{ + GtkWidget *real_listbox; + RowData *data; + GList *children, *l; + + data = g_object_get_data (G_OBJECT (row), "data"); + + if (data->category == CC_CATEGORY_DETAILS) + real_listbox = self->details_listbox; + else if (data->category == CC_CATEGORY_DEVICES) + real_listbox = self->devices_listbox; + else + real_listbox = self->main_listbox; + + /* Select the correct row */ + children = gtk_container_get_children (GTK_CONTAINER (real_listbox)); + + for (l = children; l != NULL; l = l->next) + { + RowData *real_row_data; + + real_row_data = g_object_get_data (l->data, "data"); + + /* + * The main listbox has the Details & Devices rows, and neither + * of them contains "data", so we have to ensure we have valid + * data before going on. + */ + if (!real_row_data) + continue; + + if (g_strcmp0 (real_row_data->id, data->id) == 0) + { + GtkListBoxRow *real_row; + + real_row = GTK_LIST_BOX_ROW (real_row_data->row); + + gtk_list_box_select_row (GTK_LIST_BOX (real_listbox), real_row); + gtk_widget_grab_focus (GTK_WIDGET (real_row)); + + g_signal_emit_by_name (real_row, "activate"); + break; + } + } + + g_list_free (children); +} + +static void +cc_panel_list_finalize (GObject *object) +{ + CcPanelList *self = (CcPanelList *)object; + + g_clear_pointer (&self->search_query, g_free); + + G_OBJECT_CLASS (cc_panel_list_parent_class)->finalize (object); +} + +static void +cc_panel_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcPanelList *self = CC_PANEL_LIST (object); + + switch (prop_id) + { + case PROP_SEARCH_MODE: + g_value_set_boolean (value, self->view == CC_PANEL_LIST_SEARCH); + break; + + case PROP_SEARCH_QUERY: + g_value_set_string (value, self->search_query); + break; + + case PROP_VIEW: + g_value_set_int (value, self->view); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_panel_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcPanelList *self = CC_PANEL_LIST (object); + + switch (prop_id) + { + case PROP_SEARCH_MODE: + update_search (self); + break; + + case PROP_SEARCH_QUERY: + cc_panel_list_set_search_query (self, g_value_get_string (value)); + break; + + case PROP_VIEW: + cc_panel_list_set_view (self, g_value_get_int (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_panel_list_class_init (CcPanelListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = cc_panel_list_finalize; + object_class->get_property = cc_panel_list_get_property; + object_class->set_property = cc_panel_list_set_property; + + /** + * CcPanelList:show-panel: + * + * Emited when a panel is selected. + */ + signals[SHOW_PANEL] = g_signal_new ("show-panel", + CC_TYPE_PANEL_LIST, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, + 1, + G_TYPE_STRING); + + /** + * CcPanelList:search-mode: + * + * Whether the search is visible or not. + */ + properties[PROP_SEARCH_MODE] = g_param_spec_boolean ("search-mode", + "Search mode", + "Whether it's in search mode or not", + FALSE, + G_PARAM_READWRITE); + + /** + * CcPanelList:search-query: + * + * The search that is being applied to sidelist. + */ + properties[PROP_SEARCH_QUERY] = g_param_spec_string ("search-query", + "Search query", + "The current search query", + NULL, + G_PARAM_READWRITE); + + /** + * CcPanelList:view: + * + * The current view of the sidelist. + */ + properties[PROP_VIEW] = g_param_spec_int ("view", + "View", + "The current view of the sidelist", + CC_PANEL_LIST_MAIN, + CC_PANEL_LIST_SEARCH, + CC_PANEL_LIST_MAIN, + G_PARAM_READWRITE); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/ControlCenter/gtk/panel-list.ui"); + + gtk_widget_class_bind_template_child (widget_class, CcPanelList, details_listbox); + gtk_widget_class_bind_template_child (widget_class, CcPanelList, details_row); + gtk_widget_class_bind_template_child (widget_class, CcPanelList, devices_listbox); + gtk_widget_class_bind_template_child (widget_class, CcPanelList, devices_row); + gtk_widget_class_bind_template_child (widget_class, CcPanelList, empty_search_placeholder); + gtk_widget_class_bind_template_child (widget_class, CcPanelList, main_listbox); + gtk_widget_class_bind_template_child (widget_class, CcPanelList, search_listbox); + + gtk_widget_class_bind_template_callback (widget_class, row_activated_cb); + gtk_widget_class_bind_template_callback (widget_class, search_row_activated_cb); +} + +static void +cc_panel_list_init (CcPanelList *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->view = CC_PANEL_LIST_MAIN; + + gtk_list_box_set_sort_func (GTK_LIST_BOX (self->main_listbox), + sort_function, + self, + NULL); + + gtk_list_box_set_header_func (GTK_LIST_BOX (self->main_listbox), + header_func, + self, + NULL); + + /* Search listbox */ + gtk_list_box_set_sort_func (GTK_LIST_BOX (self->search_listbox), + search_sort_function, + self, + NULL); + + gtk_list_box_set_filter_func (GTK_LIST_BOX (self->search_listbox), + filter_func, + self, + NULL); + + gtk_list_box_set_placeholder (GTK_LIST_BOX (self->search_listbox), self->empty_search_placeholder); +} + +GtkWidget* +cc_panel_list_new (void) +{ + return g_object_new (CC_TYPE_PANEL_LIST, NULL); +} + +gboolean +cc_panel_list_activate (CcPanelList *self) +{ + GtkListBoxRow *row; + GtkWidget *listbox; + + 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); + + /* If the row is valid, activate it */ + if (row) + { + gtk_list_box_select_row (GTK_LIST_BOX (listbox), row); + gtk_widget_grab_focus (GTK_WIDGET (row)); + + g_signal_emit_by_name (row, "activate"); + } + + return row != NULL; +} + +const gchar* +cc_panel_list_get_search_query (CcPanelList *self) +{ + g_return_val_if_fail (CC_IS_PANEL_LIST (self), NULL); + + return self->search_query; +} + +void +cc_panel_list_set_search_query (CcPanelList *self, + const gchar *search) +{ + g_return_if_fail (CC_IS_PANEL_LIST (self)); + + if (g_strcmp0 (self->search_query, search) != 0) + { + g_clear_pointer (&self->search_query, g_free); + self->search_query = g_strdup (search); + + update_search (self); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_QUERY]); + + gtk_list_box_invalidate_filter (GTK_LIST_BOX (self->search_listbox)); + gtk_list_box_invalidate_sort (GTK_LIST_BOX (self->search_listbox)); + } +} + +CcPanelListView +cc_panel_list_get_view (CcPanelList *self) +{ + g_return_val_if_fail (CC_IS_PANEL_LIST (self), -1); + + return self->view; +} + +void +cc_panel_list_set_view (CcPanelList *self, + CcPanelListView view) +{ + g_return_if_fail (CC_IS_PANEL_LIST (self)); + + if (self->view != view) + { + GtkWidget *visible_child; + gboolean should_crossfade; + + self->previous_view = self->view; + self->view = view; + + /* + * When changing to or from the search view, the animation should + * be crossfade. Otherwise, it's the previous-forward movement. + */ + should_crossfade = view == CC_PANEL_LIST_SEARCH || + self->previous_view == CC_PANEL_LIST_SEARCH; + + gtk_stack_set_transition_type (GTK_STACK (self), + should_crossfade ? GTK_STACK_TRANSITION_TYPE_CROSSFADE : + GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT); + + visible_child = get_listbox_from_view (self, view); + + gtk_stack_set_visible_child (GTK_STACK (self), visible_child); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VIEW]); + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_MODE]); + } +} + +void +cc_panel_list_add_panel (CcPanelList *self, + CcPanelCategory category, + const gchar *id, + const gchar *title, + const gchar *description, + const gchar *icon) +{ + GtkWidget *listbox; + RowData *data, *search_data; + + g_return_if_fail (CC_IS_PANEL_LIST (self)); + + /* Add the panel to the proper listbox */ + data = row_data_new (category, id, title, description, 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; + } + + gtk_container_add (GTK_CONTAINER (listbox), data->row); + + /* And add to the search listbox too */ + search_data = row_data_new (category, id, title, description, icon); + gtk_container_add (GTK_CONTAINER (self->search_listbox), search_data->row); +} diff --git a/shell/alt/cc-panel-list.h b/shell/alt/cc-panel-list.h new file mode 100644 index 000000000..0cd1880c8 --- /dev/null +++ b/shell/alt/cc-panel-list.h @@ -0,0 +1,67 @@ +/* cc-panel-list.c + * + * Copyright (C) 2016 Endless, Inc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author: Georges Basile Stavracas Neto <gbsneto@gnome.org> + */ + +#ifndef CC_PANEL_LIST_H +#define CC_PANEL_LIST_H + +#include <glib-object.h> + +#include "cc-panel.h" +#include "cc-shell-model.h" + +G_BEGIN_DECLS + +typedef enum +{ + CC_PANEL_LIST_MAIN, + CC_PANEL_LIST_DETAILS, + CC_PANEL_LIST_DEVICES, + CC_PANEL_LIST_SEARCH +} CcPanelListView; + +#define CC_TYPE_PANEL_LIST (cc_panel_list_get_type()) + +G_DECLARE_FINAL_TYPE (CcPanelList, cc_panel_list, CC, PANEL_LIST, GtkStack) + +GtkWidget* cc_panel_list_new (void); + +gboolean cc_panel_list_activate (CcPanelList *self); + +const gchar* cc_panel_list_get_search_query (CcPanelList *self); + +void cc_panel_list_set_search_query (CcPanelList *self, + const gchar *search); + +CcPanelListView cc_panel_list_get_view (CcPanelList *self); + +void cc_panel_list_set_view (CcPanelList *self, + CcPanelListView view); + +void cc_panel_list_add_panel (CcPanelList *self, + CcPanelCategory category, + const gchar *id, + const gchar *title, + const gchar *description, + const gchar *icon); + +G_END_DECLS + +#endif /* CC_PANEL_LIST_H */ + diff --git a/shell/gnome-control-center.gresource.xml b/shell/gnome-control-center.gresource.xml index d4256cb49..24823939f 100644 --- a/shell/gnome-control-center.gresource.xml +++ b/shell/gnome-control-center.gresource.xml @@ -2,6 +2,7 @@ <gresources> <gresource prefix="/org/gnome/ControlCenter/gtk"> <file preprocess="xml-stripblanks">help-overlay.ui</file> + <file preprocess="xml-stripblanks">panel-list.ui</file> <file preprocess="xml-stripblanks">window.ui</file> </gresource> </gresources> diff --git a/shell/panel-list.ui b/shell/panel-list.ui new file mode 100644 index 000000000..5ebc62117 --- /dev/null +++ b/shell/panel-list.ui @@ -0,0 +1,209 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <requires lib="gtk+" version="3.20"/> + <template class="CcPanelList" parent="GtkStack"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="vhomogeneous">False</property> + <property name="hhomogeneous">True</property> + <property name="transition_type">slide-left-right</property> + <child> + <object class="GtkListBox" id="main_listbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="row-activated" handler="row_activated_cb" object="CcPanelList" swapped="no" /> + <child> + <object class="GtkListBoxRow" id="devices_row"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="selectable">False</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="border_width">12</property> + <property name="spacing">12</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">applications-system-symbolic</property> + <style> + <class name="dim-label" /> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Devices</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">go-next-symbolic</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + <child> + <object class="GtkListBoxRow" id="details_row"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="selectable">False</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="border_width">12</property> + <property name="spacing">12</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">applications-system-symbolic</property> + <style> + <class name="dim-label" /> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="label" translatable="yes">Details</property> + <property name="xalign">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="icon_name">go-next-symbolic</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="name">main</property> + </packing> + </child> + <child> + <object class="GtkListBox" id="devices_listbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="row-activated" handler="row_activated_cb" object="CcPanelList" swapped="no" /> + </object> + <packing> + <property name="name">devices</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkListBox" id="details_listbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="row-activated" handler="row_activated_cb" object="CcPanelList" swapped="no" /> + </object> + <packing> + <property name="name">details</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkListBox" id="search_listbox"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <signal name="row-activated" handler="search_row_activated_cb" object="CcPanelList" swapped="no" /> + </object> + <packing> + <property name="name">search</property> + <property name="position">3</property> + </packing> + </child> + </template> + <object class="GtkBox" id="empty_search_placeholder"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="border_width">18</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="pixel_size">72</property> + <property name="icon_name">edit-find-symbolic</property> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">No results found</property> + <attributes> + <attribute name="weight" value="bold"/> + <attribute name="scale" value="1.44"/> + </attributes> + </object> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Try a different search</property> + <style> + <class name="dim-label"/> + </style> + </object> + </child> + </object> +</interface> |