[gnome-control-center] keyboard: Move keyboard shortcuts configuration to a dialog window



commit 238327e0ae5e875d76aff973c754c860d4fd0ae2
Author: Ian Douglas Scott <idscott system76 com>
Date:   Thu Jul 9 14:54:20 2020 -0700

    keyboard: Move keyboard shortcuts configuration to a dialog window

 panels/keyboard/cc-keyboard-panel.c                | 564 +-------------
 panels/keyboard/cc-keyboard-panel.h                |   2 -
 panels/keyboard/cc-keyboard-panel.ui               | 127 +--
 panels/keyboard/cc-keyboard-shortcut-dialog.c      | 847 +++++++++++++++++++++
 panels/keyboard/cc-keyboard-shortcut-dialog.h      |  35 +
 panels/keyboard/cc-keyboard-shortcut-dialog.ui     | 257 +++++++
 panels/keyboard/gnome-keyboard-panel.desktop.in.in |   2 +-
 panels/keyboard/keyboard.gresource.xml             |   1 +
 panels/keyboard/meson.build                        |   1 +
 9 files changed, 1200 insertions(+), 636 deletions(-)
---
diff --git a/panels/keyboard/cc-keyboard-panel.c b/panels/keyboard/cc-keyboard-panel.c
index 213ac7f7e..20fd2c907 100644
--- a/panels/keyboard/cc-keyboard-panel.c
+++ b/panels/keyboard/cc-keyboard-panel.c
@@ -1,6 +1,8 @@
-/*
+/* cc-keyboard-panel.c
+ *
  * Copyright (C) 2010 Intel, Inc
  * Copyright (C) 2016 Endless, Inc
+ * Copyright (C) 2020 System76, 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
@@ -17,59 +19,30 @@
  *
  * Author: Thomas Wood <thomas wood intel com>
  *         Georges Basile Stavracas Neto <gbsneto gnome org>
+ *         Ian Douglas Scott <idscott system76 com>
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include <glib/gi18n.h>
 
-#include "cc-keyboard-shortcut-row.h"
-#include "cc-keyboard-item.h"
-#include "cc-keyboard-manager.h"
 #include "cc-keyboard-panel.h"
 #include "cc-keyboard-resources.h"
-#include "cc-keyboard-shortcut-editor.h"
+#include "cc-keyboard-shortcut-dialog.h"
 #include "cc-xkb-modifier-dialog.h"
 
 #include "keyboard-shortcuts.h"
 
-#include "cc-util.h"
-
-#define SHORTCUT_DELIMITERS "+ "
-
-typedef struct {
-  CcKeyboardItem *item;
-  gchar          *section_title;
-  gchar          *section_id;
-} RowData;
-
 struct _CcKeyboardPanel
 {
   CcPanel             parent_instance;
 
-  /* Search */
-  GtkWidget          *empty_search_placeholder;
-  GtkWidget          *reset_button;
-  GtkWidget          *search_bar;
-  GtkWidget          *search_button;
-  GtkWidget          *search_entry;
-  guint               search_bar_handler_id;
-
-  /* Shortcuts */
-  GtkWidget          *shortcuts_listbox;
-  GtkListBoxRow      *add_shortcut_row;
-  GtkSizeGroup       *accelerator_sizegroup;
-
   /* Alternate characters key */
   CcXkbModifierDialog *xkb_modifier_dialog;
   GSettings           *input_source_settings;
   GtkWidget           *value_alternate_chars;
 
-  /* Custom shortcut dialog */
-  GtkWidget          *shortcut_editor;
-
-  GRegex             *pictures_regex;
-
-  CcKeyboardManager  *manager;
+  GtkListBoxRow       *common_shortcuts_row;
 };
 
 CC_PANEL_REGISTER (CcKeyboardPanel, cc_keyboard_panel)
@@ -79,11 +52,6 @@ enum {
   PROP_PARAMETERS
 };
 
-static const gchar* custom_css =
-"button.reset-shortcut-button {"
-"    padding: 0;"
-"}";
-
 static const XkbModifier LV3_MODIFIER = {
   "lv3:",
   N_("Alternate Characters Key"),
@@ -100,420 +68,35 @@ static const XkbModifier LV3_MODIFIER = {
   "lv3:ralt_switch",
 };
 
-/* RowData functions */
-static RowData *
-row_data_new (CcKeyboardItem *item,
-              const gchar    *section_id,
-              const gchar    *section_title)
-{
-  RowData *data;
-
-  data = g_new0 (RowData, 1);
-  data->item = g_object_ref (item);
-  data->section_id = g_strdup (section_id);
-  data->section_title = g_strdup (section_title);
-
-  return data;
-}
-
-static void
-row_data_free (RowData *data)
-{
-  g_object_unref (data->item);
-  g_free (data->section_id);
-  g_free (data->section_title);
-  g_free (data);
-}
-
-static void
-reset_all_shortcuts_cb (GtkWidget *widget,
-                        gpointer   user_data)
-{
-  CcKeyboardPanel *self;
-  RowData *data;
-
-  self = user_data;
-
-  if (widget == (GtkWidget *) self->add_shortcut_row)
-    return;
-
-  data = g_object_get_data (G_OBJECT (widget), "data");
-
-  /* Don't reset custom shortcuts */
-  if (cc_keyboard_item_get_item_type (data->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
-    return;
-
-  /* cc_keyboard_manager_reset_shortcut() already resets conflicting shortcuts,
-   * so no other check is needed here. */
-  cc_keyboard_manager_reset_shortcut (self->manager, data->item);
-}
-
-static void
-reset_all_clicked_cb (CcKeyboardPanel *self)
-{
-  GtkWidget *dialog, *toplevel, *button;
-  guint response;
-
-  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
-  dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel),
-                                   GTK_DIALOG_MODAL | GTK_DIALOG_USE_HEADER_BAR | 
GTK_DIALOG_DESTROY_WITH_PARENT,
-                                   GTK_MESSAGE_WARNING,
-                                   GTK_BUTTONS_NONE,
-                                   _("Reset All Shortcuts?"));
-
-  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
-                                            _("Resetting the shortcuts may affect your custom shortcuts. "
-                                              "This cannot be undone."));
-
-  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
-                          _("Cancel"), GTK_RESPONSE_CANCEL,
-                          _("Reset All"), GTK_RESPONSE_ACCEPT,
-                          NULL);
-
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
-
-  /* Make the "Reset All" button destructive */
-  button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
-  gtk_style_context_add_class (gtk_widget_get_style_context (button), "destructive-action");
-
-  /* Reset shortcuts if accepted */
-  response = gtk_dialog_run (GTK_DIALOG (dialog));
-
-  if (response == GTK_RESPONSE_ACCEPT)
-    {
-      gtk_container_foreach (GTK_CONTAINER (self->shortcuts_listbox),
-                             reset_all_shortcuts_cb,
-                             self);
-    }
-
-  gtk_widget_destroy (dialog);
-}
-
-static void
-add_item (CcKeyboardPanel *self,
-          CcKeyboardItem  *item,
-          const gchar     *section_id,
-          const gchar     *section_title)
-{
-  GtkWidget *row;
-
-  row = GTK_WIDGET(cc_keyboard_shortcut_row_new(item,
-                                               self->manager,
-                                               CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor),
-                                               self->accelerator_sizegroup));
-  g_object_set_data_full (G_OBJECT (row),
-                          "data",
-                          row_data_new (item, section_id, section_title),
-                          (GDestroyNotify) row_data_free);
-  gtk_container_add (GTK_CONTAINER (self->shortcuts_listbox), row);
-}
-
-static void
-remove_item (CcKeyboardPanel *self,
-             CcKeyboardItem  *item)
-{
-  GList *children, *l;
-
-  children = gtk_container_get_children (GTK_CONTAINER (self->shortcuts_listbox));
-
-  for (l = children; l != NULL; l = l->next)
-    {
-      RowData *row_data;
-
-      row_data = g_object_get_data (l->data, "data");
-
-      if (row_data->item == item)
-        {
-          gtk_container_remove (GTK_CONTAINER (self->shortcuts_listbox), l->data);
-          break;
-        }
-    }
-
-  g_list_free (children);
-}
-
-static gboolean
-strv_contains_prefix_or_match (gchar       **strv,
-                               const gchar  *prefix)
-{
-  guint i;
-
-  const struct {
-    const gchar *key;
-    const gchar *untranslated;
-    const gchar *synonym;
-  } key_aliases[] =
-    {
-      { "ctrl",   "Ctrl",  "ctrl" },
-      { "win",    "Super", "super" },
-      { "option",  NULL,   "alt" },
-      { "command", NULL,   "super" },
-      { "apple",   NULL,   "super" },
-    };
-
-  for (i = 0; strv[i]; i++)
-    {
-      if (g_str_has_prefix (strv[i], prefix))
-        return TRUE;
-    }
-
-  for (i = 0; i < G_N_ELEMENTS (key_aliases); i++)
-    {
-      g_autofree gchar *alias = NULL;
-      const gchar *synonym;
-
-      if (!g_str_has_prefix (key_aliases[i].key, prefix))
-        continue;
-
-      if (key_aliases[i].untranslated)
-        {
-          const gchar *translated_label;
-
-          /* Steal GTK+'s translation */
-          translated_label = g_dpgettext2 ("gtk30", "keyboard label", key_aliases[i].untranslated);
-          alias = g_utf8_strdown (translated_label, -1);
-        }
-
-      synonym = key_aliases[i].synonym;
-
-      /* If a translation or synonym of the key is in the accelerator, and we typed
-       * the key, also consider that a prefix */
-      if ((alias && g_strv_contains ((const gchar * const *) strv, alias)) ||
-          (synonym && g_strv_contains ((const gchar * const *) strv, synonym)))
-        {
-          return TRUE;
-        }
-    }
-
-  return FALSE;
-}
-
-static gboolean
-search_match_shortcut (CcKeyboardItem *item,
-                       const gchar    *search)
-{
-  GStrv shortcut_tokens, search_tokens;
-  g_autofree gchar *normalized_accel = NULL;
-  g_autofree gchar *accel = NULL;
-  gboolean match;
-  guint i;
-  GList *key_combos, *l;
-  CcKeyCombo *combo;
-
-  key_combos = cc_keyboard_item_get_key_combos (item);
-  for (l = key_combos; l != NULL; l = l->next)
-    {
-      combo = l->data;
-
-      if (is_empty_binding (combo))
-        continue;
-
-      match = TRUE;
-      accel = convert_keysym_state_to_string (combo);
-      normalized_accel = cc_util_normalize_casefold_and_unaccent (accel);
-
-      shortcut_tokens = g_strsplit_set (normalized_accel, SHORTCUT_DELIMITERS, -1);
-      search_tokens = g_strsplit_set (search, SHORTCUT_DELIMITERS, -1);
-
-      for (i = 0; search_tokens[i] != NULL; i++)
-        {
-          const gchar *token;
-
-          /* Strip leading and trailing whitespaces */
-          token = g_strstrip (search_tokens[i]);
-
-          if (g_utf8_strlen (token, -1) == 0)
-            continue;
-
-          match = match && strv_contains_prefix_or_match (shortcut_tokens, token);
-
-          if (!match)
-            break;
-        }
-
-      g_strfreev (shortcut_tokens);
-      g_strfreev (search_tokens);
-
-      if (match)
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
-static gint
-sort_function (GtkListBoxRow *a,
-               GtkListBoxRow *b,
-               gpointer       user_data)
-{
-  CcKeyboardPanel *self;
-  RowData *a_data, *b_data;
-  gint retval;
-
-  self = user_data;
-
-  if (a == self->add_shortcut_row)
-    return 1;
-
-  if (b == self->add_shortcut_row)
-    return -1;
-
-  a_data = g_object_get_data (G_OBJECT (a), "data");
-  b_data = g_object_get_data (G_OBJECT (b), "data");
-
-  /* Put custom shortcuts below everything else */
-  if (cc_keyboard_item_get_item_type (a_data->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
-    return 1;
-  else if (cc_keyboard_item_get_item_type (b_data->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
-    return -1;
-
-  retval = g_strcmp0 (a_data->section_title, b_data->section_title);
-
-  if (retval != 0)
-    return retval;
-
-  return g_strcmp0 (cc_keyboard_item_get_description (a_data->item), cc_keyboard_item_get_description 
(b_data->item));
-}
-
 static void
-header_function (GtkListBoxRow *row,
-                 GtkListBoxRow *before,
-                 gpointer       user_data)
+alternate_chars_activated (GtkWidget       *button,
+                           GtkListBoxRow   *row,
+                           CcKeyboardPanel *self)
 {
-  CcKeyboardPanel *self;
-  gboolean add_header;
-  RowData *data;
-
-  self = user_data;
-  add_header = FALSE;
-
-  /* The + row always has a separator */
-  if (row == self->add_shortcut_row)
-    {
-      GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-      gtk_widget_show (separator);
-
-      gtk_list_box_row_set_header (row, separator);
-
-      return;
-    }
-
-  data = g_object_get_data (G_OBJECT (row), "data");
-
-  if (before)
-    {
-      RowData *before_data = g_object_get_data (G_OBJECT (before), "data");
-
-      if (before_data)
-        add_header = g_strcmp0 (before_data->section_id, data->section_id) != 0;
-    }
-  else
-    {
-      add_header = TRUE;
-    }
-
-  if (add_header)
-    {
-      GtkWidget *box, *label, *separator;
-      g_autofree gchar *markup = NULL;
-
-      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
-      gtk_widget_show (box);
-      gtk_widget_set_margin_top (box, before ? 18 : 6);
-
-      markup = g_strdup_printf ("<b>%s</b>", _(data->section_title));
-      label = gtk_label_new (NULL);
-      gtk_label_set_markup (GTK_LABEL (label), markup);
-      gtk_label_set_xalign (GTK_LABEL (label), 0.0);
-      gtk_widget_set_margin_start (label, 6);
-      gtk_widget_show (label);
-      gtk_style_context_add_class (gtk_widget_get_style_context (label), "dim-label");
-      gtk_container_add (GTK_CONTAINER (box), label);
-
-      separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-      gtk_widget_show (separator);
-      gtk_container_add (GTK_CONTAINER (box), separator);
-
-      gtk_list_box_row_set_header (row, box);
-    }
-  else
-    {
-      gtk_list_box_row_set_header (row, NULL);
-    }
-}
+  GtkWindow *window;
 
-static gboolean
-filter_function (GtkListBoxRow *row,
-                 gpointer       user_data)
-{
-  CcKeyboardPanel *self = user_data;
-  CcKeyboardItem *item;
-  RowData *data;
-  gboolean retval;
-  g_autofree gchar *search = NULL;
-  g_autofree gchar *name = NULL;
-  g_auto(GStrv) terms = NULL;
-  guint i;
-
-  if (gtk_entry_get_text_length (GTK_ENTRY (self->search_entry)) == 0)
-    return TRUE;
-
-  /* When searching, the '+' row is always hidden */
-  if (row == self->add_shortcut_row)
-    return FALSE;
-
-  data = g_object_get_data (G_OBJECT (row), "data");
-  item = data->item;
-  name = cc_util_normalize_casefold_and_unaccent (cc_keyboard_item_get_description (item));
-  search = cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (self->search_entry)));
-  terms = g_strsplit (search, " ", -1);
-
-  for (i = 0; terms && terms[i]; i++)
-    {
-      retval = strstr (name, terms[i]) || search_match_shortcut (item, terms[i]);
-      if (!retval)
-        break;
-    }
+  window = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
 
-  return retval;
+  gtk_window_set_transient_for (GTK_WINDOW (self->xkb_modifier_dialog), window);
+  gtk_widget_show (GTK_WIDGET (self->xkb_modifier_dialog));
 }
 
 static void
-shortcut_row_activated (GtkWidget       *button,
-                        GtkListBoxRow   *row,
-                        CcKeyboardPanel *self)
+keyboard_shortcuts_activated (GtkWidget       *button,
+                              GtkListBoxRow   *row,
+                              CcKeyboardPanel *self)
 {
-  CcKeyboardShortcutEditor *editor;
-
-  editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor);
+  GtkWindow *window;
+  GtkWidget *shortcut_dialog;
 
-  if (row != self->add_shortcut_row)
+  if (row == self->common_shortcuts_row)
     {
-      RowData *data = g_object_get_data (G_OBJECT (row), "data");
+      window = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
 
-      cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_EDIT);
-      cc_keyboard_shortcut_editor_set_item (editor, data->item);
+      shortcut_dialog = cc_keyboard_shortcut_dialog_new ();
+      gtk_window_set_transient_for (GTK_WINDOW (shortcut_dialog), window);
+      gtk_widget_show (GTK_WIDGET (shortcut_dialog));
     }
-  else
-    {
-      cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_CREATE);
-      cc_keyboard_shortcut_editor_set_item (editor, NULL);
-    }
-
-  gtk_widget_show (self->shortcut_editor);
-}
-
-static void
-alternate_chars_activated (GtkWidget       *button,
-                           GtkListBoxRow   *row,
-                           CcKeyboardPanel *self)
-{
-  GtkWindow *window;
-
-  window = GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self))));
-
-  gtk_window_set_transient_for (GTK_WINDOW (self->xkb_modifier_dialog), window);
-  gtk_widget_show (GTK_WIDGET (self->xkb_modifier_dialog));
 }
 
 static void
@@ -542,45 +125,12 @@ static void
 cc_keyboard_panel_finalize (GObject *object)
 {
   CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
-  GtkWidget *window;
 
-  g_clear_pointer (&self->pictures_regex, g_regex_unref);
-  g_clear_object (&self->accelerator_sizegroup);
   g_clear_object (&self->input_source_settings);
 
-  if (self->search_bar_handler_id != 0)
-    {
-      window = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
-      g_signal_handler_disconnect (window, self->search_bar_handler_id);
-    }
-
   G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->finalize (object);
 }
 
-static void
-cc_keyboard_panel_constructed (GObject *object)
-{
-  CcKeyboardPanel *self = CC_KEYBOARD_PANEL (object);
-  GtkWindow *toplevel;
-  CcShell *shell;
-
-  G_OBJECT_CLASS (cc_keyboard_panel_parent_class)->constructed (object);
-
-  /* Setup the dialog's transient parent */
-  shell = cc_panel_get_shell (CC_PANEL (self));
-  toplevel = GTK_WINDOW (cc_shell_get_toplevel (shell));
-  gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), toplevel);
-
-  cc_shell_embed_widget_in_header (shell, self->reset_button, GTK_POS_LEFT);
-  cc_shell_embed_widget_in_header (shell, self->search_button, GTK_POS_RIGHT);
-
-  self->search_bar_handler_id =
-    g_signal_connect_swapped (toplevel,
-                              "key-press-event",
-                              G_CALLBACK (gtk_search_bar_handle_event),
-                              self->search_bar);
-}
-
 static void
 cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
 {
@@ -592,45 +142,25 @@ cc_keyboard_panel_class_init (CcKeyboardPanelClass *klass)
 
   object_class->set_property = cc_keyboard_panel_set_property;
   object_class->finalize = cc_keyboard_panel_finalize;
-  object_class->constructed = cc_keyboard_panel_constructed;
 
   g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
 
   gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/keyboard/cc-keyboard-panel.ui");
 
-  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, add_shortcut_row);
-  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, empty_search_placeholder);
-  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, reset_button);
-  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, search_bar);
-  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, search_button);
-  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, search_entry);
-  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, shortcuts_listbox);
   gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, value_alternate_chars);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardPanel, common_shortcuts_row);
 
-  gtk_widget_class_bind_template_callback (widget_class, reset_all_clicked_cb);
-  gtk_widget_class_bind_template_callback (widget_class, shortcut_row_activated);
   gtk_widget_class_bind_template_callback (widget_class, alternate_chars_activated);
+  gtk_widget_class_bind_template_callback (widget_class, keyboard_shortcuts_activated);
 }
 
 static void
 cc_keyboard_panel_init (CcKeyboardPanel *self)
 {
-  GtkCssProvider *provider;
-
   g_resources_register (cc_keyboard_get_resource ());
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
-  /* Custom CSS */
-  provider = gtk_css_provider_new ();
-  gtk_css_provider_load_from_data (provider, custom_css, -1, NULL);
-
-  gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
-                                             GTK_STYLE_PROVIDER (provider),
-                                             GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
-
-  g_object_unref (provider);
-
   /* Alternate characters key */
   self->input_source_settings = g_settings_new ("org.gnome.desktop.input-sources");
   g_settings_bind_with_mapping (self->input_source_settings,
@@ -644,46 +174,4 @@ cc_keyboard_panel_init (CcKeyboardPanel *self)
                                 NULL);
 
   self->xkb_modifier_dialog = cc_xkb_modifier_dialog_new (self->input_source_settings, &LV3_MODIFIER);
-
-  /* Shortcut manager */
-  self->manager = cc_keyboard_manager_new ();
-
-  /* Shortcut editor dialog */
-  self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager);
-
-  /* Use a sizegroup to make the accelerator labels the same width */
-  self->accelerator_sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
-
-  g_signal_connect_object (self->manager,
-                           "shortcut-added",
-                           G_CALLBACK (add_item),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  g_signal_connect_object (self->manager,
-                           "shortcut-removed",
-                           G_CALLBACK (remove_item),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  cc_keyboard_manager_load_shortcuts (self->manager);
-
-  /* Setup the shortcuts shortcuts_listbox */
-  gtk_list_box_set_sort_func (GTK_LIST_BOX (self->shortcuts_listbox),
-                              sort_function,
-                              self,
-                              NULL);
-
-  gtk_list_box_set_header_func (GTK_LIST_BOX (self->shortcuts_listbox),
-                                header_function,
-                                self,
-                                NULL);
-
-  gtk_list_box_set_filter_func (GTK_LIST_BOX (self->shortcuts_listbox),
-                                filter_function,
-                                self,
-                                NULL);
-
-  gtk_list_box_set_placeholder (GTK_LIST_BOX (self->shortcuts_listbox), self->empty_search_placeholder);
 }
-
diff --git a/panels/keyboard/cc-keyboard-panel.h b/panels/keyboard/cc-keyboard-panel.h
index db6e35278..3d3076b2d 100644
--- a/panels/keyboard/cc-keyboard-panel.h
+++ b/panels/keyboard/cc-keyboard-panel.h
@@ -29,6 +29,4 @@ G_BEGIN_DECLS
 #define CC_TYPE_KEYBOARD_PANEL (cc_keyboard_panel_get_type ())
 G_DECLARE_FINAL_TYPE (CcKeyboardPanel, cc_keyboard_panel, CC, KEYBOARD_PANEL, CcPanel)
 
-CcKeyboardItem*      cc_keyboard_panel_create_custom_item        (CcKeyboardPanel    *self);
-
 G_END_DECLS
diff --git a/panels/keyboard/cc-keyboard-panel.ui b/panels/keyboard/cc-keyboard-panel.ui
index 7c5073e33..7c977db69 100644
--- a/panels/keyboard/cc-keyboard-panel.ui
+++ b/panels/keyboard/cc-keyboard-panel.ui
@@ -12,26 +12,11 @@
     <property name="visible">True</property>
     <property name="can_focus">False</property>
     <property name="expand">True</property>
-    <signal name="key-press-event" handler="gtk_search_bar_handle_event" object="search_bar" swapped="yes" />
     <child>
       <object class="GtkBox">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
-        <child>
-          <object class="GtkSearchBar" id="search_bar">
-            <property name="visible">True</property>
-            <property name="hexpand">True</property>
-            <property name="search_mode_enabled" bind-source="search_button" bind-property="active" 
bind-flags="bidirectional" />
-            <child>
-              <object class="GtkSearchEntry" id="search_entry">
-                <property name="visible">True</property>
-                <property name="width_chars">30</property>
-                <signal name="notify::text" handler="gtk_list_box_invalidate_filter" 
object="shortcuts_listbox" swapped="yes" />
-              </object>
-            </child>
-          </object>
-        </child>
         <child>
           <object class="GtkScrolledWindow">
             <property name="visible">True</property>
@@ -53,6 +38,17 @@
                 <property name="margin_right">18</property>
                 <property name="spacing">12</property>
                 <property name="halign">center</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Type Special Characters</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
                 <child>
                   <object class="GtkFrame">
                     <property name="visible">True</property>
@@ -68,7 +64,6 @@
                           <object class="HdyActionRow">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
-                            <property name="use-underline">true</property>
                             <property name="title" translatable="yes">Alternate Characters Key</property>
                             <property name="subtitle" translatable="yes">Hold down and type to enter 
different characters</property>
                             <property name="activatable">True</property>
@@ -94,34 +89,41 @@
                     </child>
                   </object>
                 </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Keyboard Shortcuts</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                </child>
                 <child>
                   <object class="GtkFrame">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <child>
-                      <object class="GtkListBox" id="shortcuts_listbox">
+                      <object class="GtkListBox" id="keyboard_shortcuts_listbox">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="selection-mode">none</property>
                         <property name="width-request">250</property>
-                        <signal name="row-activated" handler="shortcut_row_activated" 
object="CcKeyboardPanel" swapped="no" />
+                        <signal name="row-activated" handler="keyboard_shortcuts_activated" 
object="CcKeyboardPanel" swapped="no" />
                         <child>
-                          <object class="GtkListBoxRow" id="add_shortcut_row">
+                          <object class="HdyActionRow" id="common_shortcuts_row">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
+                            <property name="title" translatable="yes">Customize Shortcuts</property>
+                            <property name="activatable">True</property>
                             <child>
-                              <object class="GtkBox">
+                              <object class="GtkImage">
                                 <property name="visible">True</property>
-                                <property name="can_focus">False</property>
-                                <property name="hexpand">True</property>
-                                <property name="border_width">6</property>
-                                <child type="center">
-                                  <object class="GtkImage">
-                                    <property name="visible">True</property>
-                                    <property name="can_focus">False</property>
-                                    <property name="icon_name">list-add-symbolic</property>
-                                  </object>
-                                </child>
+                                <property name="icon_name">go-next-symbolic</property>
+                                <style>
+                                  <class name="dim-label"/>
+                                </style>
                               </object>
                             </child>
                           </object>
@@ -137,69 +139,4 @@
       </object>
     </child>
   </template>
-
-  <!-- Header widgets -->
-  <object class="GtkToggleButton" id="search_button">
-    <property name="visible">True</property>
-    <style>
-      <class name="image-button" />
-    </style>
-    <child>
-      <object class="GtkImage">
-        <property name="visible">True</property>
-        <property name="icon_name">system-search-symbolic</property>
-      </object>
-    </child>
-  </object>
-  <object class="GtkButton" id="reset_button">
-    <property name="visible">True</property>
-    <property name="can_focus">True</property>
-    <property name="label" translatable="yes">Reset All…</property>
-    <property name="tooltip-text" translatable="yes">Reset all shortcuts to their default 
keybindings</property>
-    <signal name="clicked" handler="reset_all_clicked_cb" object="CcKeyboardPanel" swapped="yes" />
-  </object>
-
-  <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">80</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 keyboard shortcut 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>
diff --git a/panels/keyboard/cc-keyboard-shortcut-dialog.c b/panels/keyboard/cc-keyboard-shortcut-dialog.c
new file mode 100644
index 000000000..c41787eb3
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-dialog.c
@@ -0,0 +1,847 @@
+/* cc-keyboard-shortcut-dialog.c
+ *
+ * Copyright (C) 2010 Intel, Inc
+ * Copyright (C) 2016 Endless, Inc
+ * Copyright (C) 2020 System76, 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: Thomas Wood <thomas wood intel com>
+ *         Georges Basile Stavracas Neto <gbsneto gnome org>
+ *         Ian Douglas Scott <idscott system76 com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <glib/gi18n.h>
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+#include "cc-keyboard-shortcut-dialog.h"
+#include "cc-keyboard-item.h"
+#include "cc-keyboard-manager.h"
+#include "cc-keyboard-shortcut-editor.h"
+#include "cc-keyboard-shortcut-row.h"
+#include "cc-list-row.h"
+#include "cc-util.h"
+#include "list-box-helper.h"
+#include "keyboard-shortcuts.h"
+
+#define SHORTCUT_DELIMITERS "+ "
+
+typedef struct {
+  gchar          *section_title;
+  gchar          *section_id;
+  guint           modified_count;
+  GtkLabel       *modified_label;
+} SectionRowData;
+
+typedef struct {
+  CcKeyboardItem *item;
+  gchar          *section_title;
+  gchar          *section_id;
+  SectionRowData *section_data;
+} ShortcutRowData;
+
+struct _CcKeyboardShortcutDialog
+{
+  GtkDialog  parent_instance;
+
+  GtkSizeGroup       *accelerator_sizegroup;
+  GtkRevealer        *back_revealer;
+  GtkWidget          *custom_shortcut_add_box;
+  guint               custom_shortcut_count;
+  GtkWidget          *empty_custom_shortcuts_placeholder;
+  GtkWidget          *empty_search_placeholder;
+  GtkHeaderBar       *headerbar;
+  GtkRevealer        *reset_all_revealer;
+  GtkSearchEntry     *search_entry;
+  GtkListBox         *section_listbox;
+  GtkListBoxRow      *section_row;
+  GtkScrolledWindow  *section_scrolled_window;
+  GtkListBox         *shortcut_listbox;
+  GtkScrolledWindow  *shortcut_scrolled_window;
+  GtkStack           *stack;
+
+  CcKeyboardManager  *manager;
+  GtkWidget          *shortcut_editor;
+  GHashTable         *sections;
+ };
+
+G_DEFINE_TYPE (CcKeyboardShortcutDialog, cc_keyboard_shortcut_dialog, GTK_TYPE_DIALOG)
+
+static SectionRowData*
+section_row_data_new (const gchar *section_id,
+                      const gchar *section_title,
+                      GtkLabel    *modified_label)
+{
+  SectionRowData *data;
+
+  data = g_new0 (SectionRowData, 1);
+  data->section_id = g_strdup (section_id);
+  data->section_title = g_strdup (section_title);
+  data->modified_count = 0;
+  data->modified_label = modified_label;
+
+  return data;
+}
+
+static void
+section_row_data_free (SectionRowData *data)
+{
+  g_free (data->section_id);
+  g_free (data->section_title);
+  g_free (data);
+}
+
+static ShortcutRowData*
+shortcut_row_data_new (CcKeyboardItem *item,
+                       const gchar    *section_id,
+                       const gchar    *section_title,
+                       SectionRowData *section_data)
+{
+  ShortcutRowData *data;
+
+  data = g_new0 (ShortcutRowData, 1);
+  data->item = g_object_ref (item);
+  data->section_id = g_strdup (section_id);
+  data->section_title = g_strdup (section_title);
+  data->section_data = section_data;
+
+  return data;
+}
+
+static void
+shortcut_row_data_free (ShortcutRowData *data)
+{
+  g_object_unref (data->item);
+  g_free (data->section_id);
+  g_free (data->section_title);
+  g_free (data);
+}
+
+static GtkListBoxRow*
+add_section (CcKeyboardShortcutDialog *self,
+             const gchar     *section_id,
+             const gchar     *section_title)
+{
+  GtkWidget *icon, *modified_label, *label, *box;
+  GtkListBoxRow *row;
+
+  icon = g_object_new (GTK_TYPE_IMAGE,
+                       "visible", 1,
+                       "icon_name", "go-next-symbolic",
+                       NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (icon), "dim-label");
+
+  modified_label = g_object_new (GTK_TYPE_LABEL,
+                                 "visible", 1,
+                                 NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (modified_label), "dim-label");
+
+  label = g_object_new (GTK_TYPE_LABEL,
+                        "visible", 1,
+                        "label", _(section_title),
+                        NULL);
+  gtk_style_context_add_class (gtk_widget_get_style_context (label), "row-label");
+
+  box = g_object_new (GTK_TYPE_BOX,
+                      "visible", 1,
+                      "spacing", 8,
+                      "margin_left", 12,
+                      "margin_right", 12,
+                      "margin_top", 8,
+                      "margin_bottom", 8,
+                      NULL);
+  gtk_container_add (GTK_CONTAINER (box), label);
+  gtk_box_pack_end (GTK_BOX (box), icon, FALSE, FALSE, 0);
+  gtk_box_pack_end (GTK_BOX (box), modified_label, FALSE, FALSE, 0);
+
+  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
+                      "visible", 1,
+                      NULL);
+  gtk_container_add (GTK_CONTAINER (row), box);
+
+  g_object_set_data_full (G_OBJECT (row),
+                          "data",
+                          section_row_data_new (section_id, section_title, GTK_LABEL (modified_label)),
+                          (GDestroyNotify)section_row_data_free);
+
+  g_hash_table_insert (self->sections, g_strdup (section_id), row);
+  gtk_container_add (GTK_CONTAINER (self->section_listbox), GTK_WIDGET (row));
+
+  return row;
+}
+
+static void
+set_custom_shortcut_add_box_visibility (CcKeyboardShortcutDialog *self)
+{
+  SectionRowData *section_data;
+  gboolean is_custom_shortcuts = FALSE;
+
+  if (self->section_row != NULL)
+    {
+      section_data = g_object_get_data (G_OBJECT (self->section_row), "data");
+      is_custom_shortcuts = (strcmp (section_data->section_id, "custom") == 0);
+
+      gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_CROSSFADE);
+      if (is_custom_shortcuts && (self->custom_shortcut_count == 0))
+        gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->empty_custom_shortcuts_placeholder));
+      else
+        gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->shortcut_scrolled_window));
+    }
+
+  gtk_widget_set_visible (self->custom_shortcut_add_box, is_custom_shortcuts);
+}
+ 
+static void
+add_item (CcKeyboardShortcutDialog *self,
+          CcKeyboardItem  *item,
+          const gchar     *section_id,
+          const gchar     *section_title)
+{
+  GtkWidget *row;
+  GtkListBoxRow *section_row;
+  SectionRowData *section_data;
+
+  section_row = g_hash_table_lookup (self->sections, section_id);
+  if (section_row == NULL)
+    section_row = add_section (self, section_id, section_title);
+
+  section_data = g_object_get_data (G_OBJECT (section_row), "data");
+
+  row = GTK_WIDGET (cc_keyboard_shortcut_row_new (item,
+                                                  self->manager,
+                                                  CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor),
+                                                 self->accelerator_sizegroup));
+
+  g_object_set_data_full (G_OBJECT (row),
+                          "data",
+                          shortcut_row_data_new (item, section_id, section_title, section_data),
+                          (GDestroyNotify)shortcut_row_data_free);
+
+  if (strcmp (section_id, "custom") == 0)
+    {
+      self->custom_shortcut_count++;
+      set_custom_shortcut_add_box_visibility (self);
+    }
+
+  gtk_container_add (GTK_CONTAINER (self->shortcut_listbox), row);
+}
+
+static void
+remove_item (CcKeyboardShortcutDialog *self,
+             CcKeyboardItem  *item)
+{
+  g_autoptr(GList) children;
+
+  children = gtk_container_get_children (GTK_CONTAINER (self->shortcut_listbox));
+
+  for (GList *l = children; l != NULL; l = l->next)
+    {
+      ShortcutRowData *row_data;
+
+      row_data = g_object_get_data (l->data, "data");
+
+      if (row_data->item == item)
+        {
+          if (strcmp (row_data->section_id, "custom") == 0)
+            {
+              self->custom_shortcut_count--;
+              set_custom_shortcut_add_box_visibility (self);
+            }
+
+          gtk_container_remove (GTK_CONTAINER (self->shortcut_listbox), l->data);
+          break;
+        }
+    }
+}
+
+static void
+update_modified_counts (CcKeyboardShortcutDialog *self)
+{
+  g_autoptr(GList) sections = NULL, shortcuts = NULL;
+  SectionRowData *section_data;
+  ShortcutRowData *shortcut_data;
+  g_autofree gchar *modified_text = NULL;
+ 
+  sections = gtk_container_get_children (GTK_CONTAINER (self->section_listbox));
+  shortcuts = gtk_container_get_children (GTK_CONTAINER (self->shortcut_listbox));
+
+  for (GList *l = sections; l != NULL; l = l->next)
+    {
+      section_data = g_object_get_data (G_OBJECT (l->data), "data");
+      section_data->modified_count = 0;
+    }
+
+  for (GList *l = shortcuts; l != NULL; l = l->next)
+    {
+      shortcut_data = g_object_get_data (G_OBJECT (l->data), "data");
+      if (!cc_keyboard_item_is_value_default (shortcut_data->item))
+        shortcut_data->section_data->modified_count++;
+    }
+
+  for (GList *l = sections; l != NULL; l = l->next)
+    {
+      section_data = g_object_get_data (G_OBJECT (l->data), "data");
+      if (section_data->modified_count > 0)
+        {
+          modified_text = g_strdup_printf ("%d %s", section_data->modified_count, _("modified"));
+          gtk_label_set_text (section_data->modified_label, modified_text);
+        }
+      else
+        {
+          gtk_label_set_text (section_data->modified_label, "");
+        }
+    }
+}
+
+static void
+show_section_list (CcKeyboardShortcutDialog *self)
+{
+  if (self->section_row != NULL)
+    gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_RIGHT);
+  else
+    gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_NONE);
+  self->section_row = NULL;
+
+  gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->section_scrolled_window));
+  gtk_header_bar_set_title (self->headerbar, _("Keyboard Shortcuts"));
+  gtk_entry_set_text(GTK_ENTRY (self->search_entry), "");
+  gtk_revealer_set_reveal_child (self->reset_all_revealer, TRUE);
+  gtk_revealer_set_reveal_child (self->back_revealer, FALSE);
+  gtk_widget_set_visible (GTK_WIDGET (self->search_entry), TRUE);
+
+  update_modified_counts (self);
+}
+
+static void
+show_shortcut_list (CcKeyboardShortcutDialog *self)
+{
+  SectionRowData *section_data;
+  gchar *title;
+  gboolean is_custom_shortcuts = FALSE;
+
+   title = _("Keyboard Shortcuts");
+   gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_NONE);
+   if (self->section_row != NULL)
+    {
+      section_data = g_object_get_data (G_OBJECT (self->section_row), "data");
+      title = _(section_data->section_title);
+      is_custom_shortcuts = (strcmp (section_data->section_id, "custom") == 0);
+      gtk_stack_set_transition_type (self->stack, GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT);
+    }
+
+  if (is_custom_shortcuts)
+    gtk_list_box_set_placeholder (self->shortcut_listbox, NULL);
+  else
+    gtk_list_box_set_placeholder (self->shortcut_listbox, self->empty_search_placeholder);
+
+  gtk_list_box_invalidate_filter (self->shortcut_listbox);
+
+  if (is_custom_shortcuts && (self->custom_shortcut_count == 0))
+    gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->empty_custom_shortcuts_placeholder));
+  else
+    gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->shortcut_scrolled_window));
+
+  gtk_header_bar_set_title (self->headerbar, title);
+  set_custom_shortcut_add_box_visibility (self);
+  gtk_revealer_set_reveal_child (self->reset_all_revealer, FALSE);
+  gtk_revealer_set_reveal_child (self->back_revealer, TRUE);
+  gtk_widget_set_visible (GTK_WIDGET (self->search_entry), self->section_row == NULL);
+
+}
+
+static void
+section_row_activated (GtkWidget                *button,
+                       GtkListBoxRow            *row,
+                       CcKeyboardShortcutDialog *self)
+{
+  self->section_row = row;
+  show_shortcut_list (self);
+}
+
+static void
+shortcut_row_activated (GtkWidget                *button,
+                        GtkListBoxRow            *row,
+                        CcKeyboardShortcutDialog *self)
+{
+  CcKeyboardShortcutEditor *editor;
+
+  editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor);
+
+  ShortcutRowData *data = g_object_get_data (G_OBJECT (row), "data");
+
+  cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_EDIT);
+  cc_keyboard_shortcut_editor_set_item (editor, data->item);
+
+  gtk_widget_show (self->shortcut_editor);
+}
+
+static void
+add_custom_shortcut_clicked_cb (CcKeyboardShortcutDialog *self)
+{
+  CcKeyboardShortcutEditor *editor;
+
+  editor = CC_KEYBOARD_SHORTCUT_EDITOR (self->shortcut_editor);
+
+  cc_keyboard_shortcut_editor_set_mode (editor, CC_SHORTCUT_EDITOR_CREATE);
+  cc_keyboard_shortcut_editor_set_item (editor, NULL);
+
+  gtk_widget_show (self->shortcut_editor);
+}
+
+static void
+back_button_clicked_cb (CcKeyboardShortcutDialog *self)
+{
+  show_section_list (self);
+}
+
+static void
+reset_all_shortcuts_cb (GtkWidget *widget,
+                        gpointer   user_data)
+{
+  CcKeyboardShortcutDialog *self;
+  ShortcutRowData *data;
+
+  self = user_data;
+
+  data = g_object_get_data (G_OBJECT (widget), "data");
+
+  /* Don't reset custom shortcuts */
+  if (cc_keyboard_item_get_item_type (data->item) == CC_KEYBOARD_ITEM_TYPE_GSETTINGS_PATH)
+    return;
+
+  /* cc_keyboard_manager_reset_shortcut() already resets conflicting shortcuts,
+   * so no other check is needed here. */
+  cc_keyboard_manager_reset_shortcut (self->manager, data->item);
+}
+
+static void
+reset_all_clicked_cb (CcKeyboardShortcutDialog *self)
+{
+  GtkWidget *dialog, *toplevel, *button;
+  guint response;
+
+  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
+  dialog = gtk_message_dialog_new (GTK_WINDOW (toplevel),
+                                   GTK_DIALOG_MODAL | GTK_DIALOG_USE_HEADER_BAR | 
GTK_DIALOG_DESTROY_WITH_PARENT,
+                                   GTK_MESSAGE_WARNING,
+                                   GTK_BUTTONS_NONE,
+                                   _("Reset All Shortcuts?"));
+
+  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                            _("Resetting the shortcuts may affect your custom shortcuts. "
+                                              "This cannot be undone."));
+
+  gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+                          _("Cancel"), GTK_RESPONSE_CANCEL,
+                          _("Reset All"), GTK_RESPONSE_ACCEPT,
+                          NULL);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
+
+  /* Make the "Reset All" button destructive */
+  button = gtk_dialog_get_widget_for_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+  gtk_style_context_add_class (gtk_widget_get_style_context (button), "destructive-action");
+
+  /* Reset shortcuts if accepted */
+  response = gtk_dialog_run (GTK_DIALOG (dialog));
+
+  if (response == GTK_RESPONSE_ACCEPT)
+    {
+      gtk_container_foreach (GTK_CONTAINER (self->shortcut_listbox),
+                             reset_all_shortcuts_cb,
+                             self);
+    }
+
+  gtk_widget_destroy (dialog);
+
+  update_modified_counts (self);
+}
+
+static void
+search_entry_cb (CcKeyboardShortcutDialog *self)
+{
+  if (gtk_entry_get_text_length (GTK_ENTRY (self->search_entry)) == 0 && self->section_row == NULL)
+    show_section_list (self);
+  else if (gtk_stack_get_visible_child (self->stack) != GTK_WIDGET (self->shortcut_scrolled_window))
+    show_shortcut_list (self);
+  else
+    gtk_list_box_invalidate_filter (self->shortcut_listbox);
+}
+
+static void
+key_press_cb (CcKeyboardShortcutDialog *self, GdkEvent *event)
+{
+  if (gtk_widget_get_visible (GTK_WIDGET (self->search_entry)))
+    gtk_search_entry_handle_event (self->search_entry, event);
+}
+
+static gboolean
+strv_contains_prefix_or_match (gchar       **strv,
+                               const gchar  *prefix)
+{
+  const struct {
+    const gchar *key;
+    const gchar *untranslated;
+    const gchar *synonym;
+  } key_aliases[] =
+    {
+      { "ctrl",   "Ctrl",  "ctrl" },
+      { "win",    "Super", "super" },
+      { "option",  NULL,   "alt" },
+      { "command", NULL,   "super" },
+      { "apple",   NULL,   "super" },
+    };
+
+  for (guint i = 0; strv[i]; i++)
+    {
+      if (g_str_has_prefix (strv[i], prefix))
+        return TRUE;
+    }
+
+  for (guint i = 0; i < G_N_ELEMENTS (key_aliases); i++)
+    {
+      g_autofree gchar *alias = NULL;
+      const gchar *synonym;
+
+      if (!g_str_has_prefix (key_aliases[i].key, prefix))
+        continue;
+
+      if (key_aliases[i].untranslated)
+        {
+          const gchar *translated_label;
+
+          /* Steal GTK+'s translation */
+          translated_label = g_dpgettext2 ("gtk30", "keyboard label", key_aliases[i].untranslated);
+          alias = g_utf8_strdown (translated_label, -1);
+        }
+
+      synonym = key_aliases[i].synonym;
+
+      /* If a translation or synonym of the key is in the accelerator, and we typed
+       * the key, also consider that a prefix */
+      if ((alias && g_strv_contains ((const gchar * const *) strv, alias)) ||
+          (synonym && g_strv_contains ((const gchar * const *) strv, synonym)))
+        {
+          return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gboolean
+search_match_shortcut (CcKeyboardItem *item,
+                       const gchar    *search)
+{
+  g_auto(GStrv) shortcut_tokens = NULL, search_tokens = NULL;
+  g_autofree gchar *normalized_accel = NULL;
+  g_autofree gchar *accel = NULL;
+  gboolean match;
+  GList *key_combos;
+  CcKeyCombo *combo;
+
+  key_combos = cc_keyboard_item_get_key_combos (item);
+  for (GList *l = key_combos; l != NULL; l = l->next)
+    {
+      combo = l->data;
+
+      if (is_empty_binding (combo))
+        continue;
+
+      match = TRUE;
+      accel = convert_keysym_state_to_string (combo);
+      normalized_accel = cc_util_normalize_casefold_and_unaccent (accel);
+
+      shortcut_tokens = g_strsplit_set (normalized_accel, SHORTCUT_DELIMITERS, -1);
+      search_tokens = g_strsplit_set (search, SHORTCUT_DELIMITERS, -1);
+
+      for (guint i = 0; search_tokens[i] != NULL; i++)
+        {
+          const gchar *token;
+
+          /* Strip leading and trailing whitespaces */
+          token = g_strstrip (search_tokens[i]);
+
+          if (g_utf8_strlen (token, -1) == 0)
+            continue;
+
+          match = match && strv_contains_prefix_or_match (shortcut_tokens, token);
+
+          if (!match)
+            break;
+        }
+
+      if (match)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gint
+section_sort_function (GtkListBoxRow *a,
+                       GtkListBoxRow *b,
+                       gpointer       user_data)
+{
+  SectionRowData *a_data, *b_data;
+
+  a_data = g_object_get_data (G_OBJECT (a), "data");
+  b_data = g_object_get_data (G_OBJECT (b), "data");
+
+  /* Put custom shortcuts below everything else */
+  if (g_strcmp0 (a_data->section_id, "custom") == 0)
+    return 1;
+
+  return g_strcmp0 (a_data->section_title, b_data->section_title);
+}
+
+static gint
+shortcut_sort_function (GtkListBoxRow *a,
+                        GtkListBoxRow *b,
+                        gpointer       user_data)
+{
+  ShortcutRowData *a_data, *b_data;
+  gint retval;
+
+  a_data = g_object_get_data (G_OBJECT (a), "data");
+  b_data = g_object_get_data (G_OBJECT (b), "data");
+
+  /* Put custom shortcuts below everything else */
+  if (g_strcmp0 (a_data->section_id, "custom") == 0)
+    return 1;
+
+  retval = g_strcmp0 (a_data->section_title, b_data->section_title);
+
+  if (retval != 0)
+    return retval;
+
+  return g_strcmp0 (cc_keyboard_item_get_description (a_data->item), cc_keyboard_item_get_description 
(b_data->item));
+}
+
+static gboolean
+shortcut_filter_function (GtkListBoxRow *row,
+                          gpointer       userdata)
+{
+  CcKeyboardShortcutDialog *self = userdata;
+  SectionRowData  *section_data;
+  ShortcutRowData *data;
+  CcKeyboardItem *item;
+  gboolean retval;
+  g_autofree gchar *search = NULL;
+  g_autofree gchar *name = NULL;
+  g_auto(GStrv) terms = NULL;
+
+  if (self->section_row != NULL)
+  {
+    section_data = g_object_get_data (G_OBJECT (self->section_row), "data");
+    data = g_object_get_data (G_OBJECT (row), "data");
+    if (strcmp (data->section_id, section_data->section_id) != 0)
+      return FALSE;
+  }
+
+  if (gtk_entry_get_text_length (GTK_ENTRY (self->search_entry)) == 0)
+    return TRUE;
+
+  data = g_object_get_data (G_OBJECT (row), "data");
+  item = data->item;
+  name = cc_util_normalize_casefold_and_unaccent (cc_keyboard_item_get_description (item));
+  search = cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (self->search_entry)));
+  terms = g_strsplit (search, " ", -1);
+
+  for (guint i = 0; terms && terms[i]; i++)
+    {
+      retval = strstr (name, terms[i]) || search_match_shortcut (item, terms[i]);
+      if (!retval)
+        break;
+    }
+
+  return retval;
+}
+
+static void
+shortcut_header_function (GtkListBoxRow *row,
+                          GtkListBoxRow *before,
+                          gpointer       user_data)
+{
+  CcKeyboardShortcutDialog *self;
+  gboolean add_header;
+  ShortcutRowData *data, *before_data;
+
+  data = g_object_get_data (G_OBJECT (row), "data");
+
+  self = user_data;
+  add_header = FALSE;
+
+  if (before)
+    {
+      before_data = g_object_get_data (G_OBJECT (before), "data");
+      add_header = g_strcmp0 (before_data->section_id, data->section_id) != 0;
+    }
+  else
+    {
+      add_header = TRUE;
+    }
+
+  if (self->section_row != NULL)
+    add_header = FALSE;
+
+  if (add_header)
+    {
+      GtkWidget *box, *label, *separator;
+      g_autofree gchar *markup = NULL;
+
+      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+      gtk_widget_show (box);
+      if (!before)
+        gtk_widget_set_margin_top (box, 6);
+
+      if (before)
+        {
+          separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+          gtk_widget_show (separator);
+          gtk_container_add (GTK_CONTAINER (box), separator);
+        }
+
+      markup = g_strdup_printf ("<b>%s</b>", _(data->section_title));
+      label = g_object_new (GTK_TYPE_LABEL,
+                            "label", markup,
+                            "use-markup", TRUE,
+                            "xalign", 0.0,
+                            "margin-start", 6,
+                            NULL);
+      gtk_widget_show (label);
+      gtk_container_add (GTK_CONTAINER (box), label);
+
+      separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+      gtk_widget_show (separator);
+      gtk_container_add (GTK_CONTAINER (box), separator);
+
+      gtk_list_box_row_set_header (row, box);
+    }
+  else if (before)
+    {
+      GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+      gtk_widget_show (separator);
+
+      gtk_list_box_row_set_header (row, separator);
+    }
+  else
+    {
+      gtk_list_box_row_set_header (row, NULL);
+    }
+}
+
+static void
+cc_keyboard_shortcut_dialog_constructed (GObject *object)
+{
+  CcKeyboardShortcutDialog *self = CC_KEYBOARD_SHORTCUT_DIALOG (object);
+
+  G_OBJECT_CLASS (cc_keyboard_shortcut_dialog_parent_class)->constructed (object);
+
+  /* Setup the dialog's transient parent */
+  gtk_window_set_transient_for (GTK_WINDOW (self->shortcut_editor), GTK_WINDOW (self));
+}
+
+static void
+cc_keyboard_shortcut_dialog_class_init (CcKeyboardShortcutDialogClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->constructed = cc_keyboard_shortcut_dialog_constructed;
+
+  gtk_widget_class_set_template_from_resource (widget_class, 
"/org/gnome/control-center/keyboard/cc-keyboard-shortcut-dialog.ui");
+
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, accelerator_sizegroup);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, back_revealer);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, custom_shortcut_add_box);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, 
empty_custom_shortcuts_placeholder);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, empty_search_placeholder);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, headerbar);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, reset_all_revealer);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, search_entry);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, section_listbox);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, section_scrolled_window);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, shortcut_listbox);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, shortcut_scrolled_window);
+  gtk_widget_class_bind_template_child (widget_class, CcKeyboardShortcutDialog, stack);
+
+  gtk_widget_class_bind_template_callback (widget_class, add_custom_shortcut_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, back_button_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, key_press_cb);
+  gtk_widget_class_bind_template_callback (widget_class, reset_all_clicked_cb);
+  gtk_widget_class_bind_template_callback (widget_class, search_entry_cb);
+  gtk_widget_class_bind_template_callback (widget_class, section_row_activated);
+  gtk_widget_class_bind_template_callback (widget_class, shortcut_row_activated);
+}
+
+static void
+cc_keyboard_shortcut_dialog_init (CcKeyboardShortcutDialog *self)
+{
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  self->manager = cc_keyboard_manager_new ();
+
+  self->shortcut_editor = cc_keyboard_shortcut_editor_new (self->manager);
+
+  self->sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  self->section_row = NULL;
+
+  g_signal_connect_object (self->manager,
+                           "shortcut-added",
+                           G_CALLBACK (add_item),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->manager,
+                           "shortcut-removed",
+                           G_CALLBACK (remove_item),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  add_section(self, "custom", "Custom Shortcuts");
+  cc_keyboard_manager_load_shortcuts (self->manager);
+
+  gtk_list_box_set_header_func (self->section_listbox, cc_list_box_update_header_func, NULL, NULL);
+  gtk_list_box_set_sort_func (GTK_LIST_BOX (self->section_listbox),
+                              section_sort_function,
+                              self,
+                              NULL);
+
+  gtk_list_box_set_filter_func (self->shortcut_listbox,
+                                shortcut_filter_function,
+                                self,
+                                NULL);
+  gtk_list_box_set_header_func (self->shortcut_listbox,
+                                shortcut_header_function,
+                                self,
+                                NULL);
+  gtk_list_box_set_sort_func (GTK_LIST_BOX (self->shortcut_listbox),
+                              shortcut_sort_function,
+                              self,
+                              NULL);
+
+  show_section_list (self);
+}
+
+GtkWidget*
+cc_keyboard_shortcut_dialog_new (void)
+{
+  return g_object_new (CC_TYPE_KEYBOARD_SHORTCUT_DIALOG,
+                       "use-header-bar", 1,
+                       NULL);
+}
diff --git a/panels/keyboard/cc-keyboard-shortcut-dialog.h b/panels/keyboard/cc-keyboard-shortcut-dialog.h
new file mode 100644
index 000000000..4493dc2ce
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-dialog.h
@@ -0,0 +1,35 @@
+/* cc-keyboard-shortcut-dialog.h
+ *
+ * Copyright (C) 2020 System76, 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: Ian Douglas Scott <idscott system76 com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_KEYBOARD_SHORTCUT_DIALOG (cc_keyboard_shortcut_dialog_get_type ())
+
+G_DECLARE_FINAL_TYPE (CcKeyboardShortcutDialog, cc_keyboard_shortcut_dialog, CC, KEYBOARD_SHORTCUT_DIALOG, 
GtkDialog)
+
+GtkWidget* cc_keyboard_shortcut_dialog_new (void);
+
+G_END_DECLS
\ No newline at end of file
diff --git a/panels/keyboard/cc-keyboard-shortcut-dialog.ui b/panels/keyboard/cc-keyboard-shortcut-dialog.ui
new file mode 100644
index 000000000..b158ef532
--- /dev/null
+++ b/panels/keyboard/cc-keyboard-shortcut-dialog.ui
@@ -0,0 +1,257 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <template class="CcKeyboardShortcutDialog" parent="GtkDialog">
+    <property name="modal">True</property>
+    <signal name="key-press-event" handler="key_press_cb" object="CcKeyboardShortcutDialog" swapped="yes" />
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <child>
+          <object class="GtkSearchEntry" id="search_entry">
+            <property name="visible">True</property>
+            <property name="margin-top">12</property>
+            <property name="width_chars">30</property>
+            <property name="halign">center</property>
+            <signal name="notify::text" handler="search_entry_cb" object="CcKeyboardShortcutDialog" 
swapped="yes" />
+          </object>
+        </child>
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkScrolledWindow" id="section_scrolled_window">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="propagate_natural_width">True</property>
+                <property name="propagate_natural_height">True</property>
+                <property name="max_content_height">350</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="border_width">12</property>
+                    <child>
+                      <object class="GtkListBox" id="section_listbox">
+                        <property name="visible">True</property>
+                        <property name="selection-mode">none</property>
+                        <property name="border_width">12</property>
+                        <signal name="row-activated" handler="section_row_activated" 
object="CcKeyboardShortcutDialog" swapped="no" />
+                        <style>
+                          <class name="frame" />
+                        </style>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow" id="shortcut_scrolled_window">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="hexpand">True</property>
+                <property name="vexpand">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="propagate_natural_width">True</property>
+                <property name="propagate_natural_height">True</property>
+                <property name="max_content_height">350</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="visible">True</property>
+                    <property name="orientation">vertical</property>
+                    <property name="border_width">12</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkListBox" id="custom_shortcut_add_box">
+                        <property name="selection-mode">none</property>
+                        <style>
+                          <class name="frame" />
+                        </style>
+                        <child>
+                          <object class="HdyActionRow">
+                            <property name="visible">True</property>
+                            <property name="title" translatable="yes">Add Custom Shortcuts</property>
+                            <property name="subtitle" translatable="yes">Set up custom shortcuts for 
launching apps, running scripts, and more.</property>
+                            <child>
+                              <object class="GtkButton">
+                                <property name="visible">True</property>
+                                <property name="valign">center</property>
+                                <property name="label" translatable="yes">Add Shortcut</property>
+                                <signal name="clicked" handler="add_custom_shortcut_clicked_cb" 
object="CcKeyboardShortcutDialog" swapped="yes" />
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkListBox" id="shortcut_listbox">
+                        <property name="visible">True</property>
+                        <property name="selection-mode">none</property>
+                        <style>
+                          <class name="frame" />
+                        </style>
+                        <signal name="row-activated" handler="shortcut_row_activated" 
object="CcKeyboardShortcutDialog" swapped="no" />
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child>
+              <object class="GtkBox" id="empty_custom_shortcuts_placeholder">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="border_width">18</property>
+                <property name="spacing">18</property>
+                <property name="valign">center</property>
+                <style>
+                  <class name="background"/>
+                </style>
+                <child>
+                  <object class="GtkImage">
+                    <property name="visible">True</property>
+                    <property name="icon-name">input-keyboard-symbolic</property>
+                    <property name="pixel-size">128</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Add Custom Shortcuts</property>
+                    <attributes>
+                      <attribute name="weight" value="bold" />
+                    </attributes>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="label" translatable="yes">Set up custom shortcuts for launching apps, 
running scripts, and more.</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkButton">
+                    <property name="visible">True</property>
+                    <property name="halign">center</property>
+                    <property name="label" translatable="yes">Add Shortcut</property>
+                    <style>
+                      <class name="suggested-action" />
+                    </style>
+                    <signal name="clicked" handler="add_custom_shortcut_clicked_cb" 
object="CcKeyboardShortcutDialog" swapped="yes" />
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child type="titlebar">
+      <object class="GtkHeaderBar" id="headerbar">
+        <property name="visible">True</property>
+        <property name="show_close_button">True</property>
+        <child>
+          <object class="GtkRevealer" id="back_revealer">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="transition-type">crossfade</property>
+            <child>
+              <object class="GtkButton" id="back">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="receives_default">False</property>
+                <property name="valign">center</property>
+                <property name="use-underline">True</property>
+                <signal name="clicked" handler="back_button_clicked_cb" object="CcKeyboardShortcutDialog" 
swapped="yes" />
+                <style>
+                  <class name="image-button"/>
+                </style>
+                <child internal-child="accessible">
+                  <object class="AtkObject" id="a11y-back">
+                    <property name="accessible-name" translatable="yes">Back</property>
+                  </object>
+                </child>
+                <child>
+                  <object class="GtkImage" id="back_image">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="icon_name">go-previous-symbolic</property>
+                    <property name="icon_size">1</property>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkRevealer" id="reset_all_revealer">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="transition-type">crossfade</property>
+            <child>
+              <object class="GtkButton" id="reset_all_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="label" translatable="yes">Reset All…</property>
+                <property name="tooltip-text" translatable="yes">Reset all shortcuts to their default 
keybindings</property>
+                <signal name="clicked" handler="reset_all_clicked_cb" object="CcKeyboardShortcutDialog" 
swapped="yes" />
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+      </object>
+    </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">80</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 keyboard shortcut 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>
+  <object class="GtkSizeGroup" id="accelerator_sizegroup" />
+</interface>
diff --git a/panels/keyboard/gnome-keyboard-panel.desktop.in.in 
b/panels/keyboard/gnome-keyboard-panel.desktop.in.in
index 9200d692c..b7b562716 100644
--- a/panels/keyboard/gnome-keyboard-panel.desktop.in.in
+++ b/panels/keyboard/gnome-keyboard-panel.desktop.in.in
@@ -1,5 +1,5 @@
 [Desktop Entry]
-Name=Keyboard Shortcuts
+Name=Keyboard
 Comment=View and change keyboard shortcuts and set your typing preferences
 Exec=gnome-control-center keyboard
 # Translators: Do NOT translate or transliterate this text (this is an icon file name)!
diff --git a/panels/keyboard/keyboard.gresource.xml b/panels/keyboard/keyboard.gresource.xml
index fc210ce75..14c05d8a1 100644
--- a/panels/keyboard/keyboard.gresource.xml
+++ b/panels/keyboard/keyboard.gresource.xml
@@ -4,6 +4,7 @@
     <file preprocess="xml-stripblanks">enter-keyboard-shortcut.svg</file>
     <file preprocess="xml-stripblanks">cc-xkb-modifier-dialog.ui</file>
     <file preprocess="xml-stripblanks">cc-keyboard-shortcut-row.ui</file>
+    <file preprocess="xml-stripblanks">cc-keyboard-shortcut-dialog.ui</file>
     <file preprocess="xml-stripblanks">cc-keyboard-panel.ui</file>
     <file preprocess="xml-stripblanks">cc-keyboard-shortcut-editor.ui</file>
   </gresource>
diff --git a/panels/keyboard/meson.build b/panels/keyboard/meson.build
index 64c6a988c..8055becf9 100644
--- a/panels/keyboard/meson.build
+++ b/panels/keyboard/meson.build
@@ -58,6 +58,7 @@ endforeach
 sources = files(
   'cc-xkb-modifier-dialog.c',
   'cc-keyboard-shortcut-row.c',
+  'cc-keyboard-shortcut-dialog.c',
   'cc-keyboard-panel.c',
   'cc-keyboard-item.c',
   'cc-keyboard-manager.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]