[epiphany] Add EphyDataDialog



commit fa64698731653082b6734cb9c31f5abb3731e59a
Author: Adrien Plazas <kekun plazas laposte net>
Date:   Tue Jun 25 09:01:11 2019 +0200

    Add EphyDataDialog

 src/ephy-data-dialog.c               | 584 +++++++++++++++++++++++++++++++++++
 src/ephy-data-dialog.h               |  58 ++++
 src/meson.build                      |   1 +
 src/resources/epiphany.gresource.xml |   1 +
 src/resources/gtk/data-dialog.ui     | 269 ++++++++++++++++
 5 files changed, 913 insertions(+)
---
diff --git a/src/ephy-data-dialog.c b/src/ephy-data-dialog.c
new file mode 100644
index 000000000..a70145c12
--- /dev/null
+++ b/src/ephy-data-dialog.c
@@ -0,0 +1,584 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2019 Purism SPC
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-data-dialog.h"
+
+#include <ctype.h>
+#include <glib/gi18n.h>
+#define HANDY_USE_UNSTABLE_API
+#include <handy.h>
+
+typedef struct {
+  GtkWidget *box;
+  GtkWidget *child;
+  GtkWidget *clear_all_button;
+  GtkWidget *search_bar;
+  GtkWidget *search_entry;
+  GtkWidget *search_button;
+  GtkWidget *stack;
+  GtkWidget *empty_title_label;
+  GtkWidget *empty_description_label;
+  GtkWidget *spinner;
+
+  gboolean is_loading : 1;
+  gboolean has_data : 1;
+  gboolean has_search_results : 1;
+  gboolean can_clear : 1;
+  char *search_text;
+} EphyDataDialogPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (EphyDataDialog, ephy_data_dialog, GTK_TYPE_WINDOW)
+
+enum {
+  PROP_0,
+  PROP_CLEAR_ALL_ACTION_NAME,
+  PROP_CLEAR_ALL_ACTION_TARGET,
+  PROP_CLEAR_ALL_DESCRIPTION,
+  PROP_SEARCH_DESCRIPTION,
+  PROP_EMPTY_TITLE,
+  PROP_EMPTY_DESCRIPTION,
+  PROP_SEARCH_TEXT,
+  PROP_IS_LOADING,
+  PROP_HAS_DATA,
+  PROP_HAS_SEARCH_RESULTS,
+  PROP_CAN_CLEAR,
+  LAST_PROP,
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+enum {
+  CLEAR_ALL_CLICKED,
+  LAST_SIGNAL,
+};
+
+static gint signals[LAST_SIGNAL] = { 0 };
+
+static void
+update (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+  gboolean has_data = priv->has_data && priv->child && gtk_widget_get_visible (priv->child);
+
+  if (priv->is_loading) {
+    gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "loading");
+    gtk_spinner_start (GTK_SPINNER (priv->spinner));
+  } else {
+    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->search_button))) {
+      if (has_data && priv->has_search_results)
+        gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
+      else
+        gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "no-results");
+    } else {
+      if (has_data)
+        gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->child);
+      else
+        gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "empty");
+    }
+    gtk_spinner_stop (GTK_SPINNER (priv->spinner));
+  }
+
+  gtk_widget_set_sensitive (priv->clear_all_button, has_data && priv->can_clear);
+  gtk_widget_set_sensitive (priv->search_button, has_data);
+}
+
+static void
+on_clear_all_button_clicked (EphyDataDialog *self)
+{
+  g_signal_emit (self, signals[CLEAR_ALL_CLICKED], 0);
+}
+
+static void
+on_search_entry_changed (GtkSearchEntry *entry,
+                         EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+  const char *text;
+
+  text = gtk_entry_get_text (GTK_ENTRY (entry));
+  g_free (priv->search_text);
+  priv->search_text = g_strdup (text);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_SEARCH_TEXT]);
+}
+
+static gboolean
+on_key_press_event (EphyDataDialog *self,
+                    GdkEvent       *event,
+                    gpointer        user_data)
+{
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+  GdkEventKey *key = (GdkEventKey *)event;
+  gint result;
+
+  result = hdy_search_bar_handle_event (HDY_SEARCH_BAR (priv->search_bar), event);
+
+  if (result == GDK_EVENT_STOP)
+    return result;
+
+  if (key->keyval == GDK_KEY_Escape) {
+    if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->search_button)))
+      gtk_widget_destroy (GTK_WIDGET (self));
+    else
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->search_button), FALSE);
+  } else if (isprint (key->keyval))
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->search_button), TRUE);
+
+  return result;
+}
+
+static void
+ephy_data_dialog_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+  EphyDataDialog *self = EPHY_DATA_DIALOG (object);
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+
+  switch (prop_id) {
+    case PROP_CLEAR_ALL_ACTION_NAME:
+      gtk_actionable_set_action_name (GTK_ACTIONABLE (priv->clear_all_button), g_value_get_string (value));
+      break;
+    case PROP_CLEAR_ALL_ACTION_TARGET:
+      gtk_actionable_set_action_target_value (GTK_ACTIONABLE (priv->clear_all_button), g_value_get_variant 
(value));
+      break;
+    case PROP_CLEAR_ALL_DESCRIPTION:
+      ephy_data_dialog_set_clear_all_description (self, g_value_get_string (value));
+      break;
+    case PROP_SEARCH_DESCRIPTION:
+      gtk_entry_set_placeholder_text (GTK_ENTRY (priv->search_entry), g_value_get_string (value));
+      atk_object_set_description (gtk_widget_get_accessible (GTK_WIDGET (self)), g_value_get_string (value));
+      break;
+    case PROP_EMPTY_TITLE:
+      gtk_label_set_text (GTK_LABEL (priv->empty_title_label), g_value_get_string (value));
+      break;
+    case PROP_EMPTY_DESCRIPTION:
+      gtk_label_set_text (GTK_LABEL (priv->empty_description_label), g_value_get_string (value));
+      break;
+    case PROP_IS_LOADING:
+      ephy_data_dialog_set_is_loading (self, g_value_get_boolean (value));
+      break;
+    case PROP_HAS_DATA:
+      ephy_data_dialog_set_has_data (self, g_value_get_boolean (value));
+      break;
+    case PROP_HAS_SEARCH_RESULTS:
+      ephy_data_dialog_set_has_search_results (self, g_value_get_boolean (value));
+      break;
+    case PROP_CAN_CLEAR:
+      ephy_data_dialog_set_can_clear (self, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+ephy_data_dialog_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+  EphyDataDialog *self = EPHY_DATA_DIALOG (object);
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+
+  switch (prop_id) {
+    case PROP_CLEAR_ALL_ACTION_NAME:
+      g_value_set_string (value, gtk_actionable_get_action_name (GTK_ACTIONABLE (priv->clear_all_button)));
+      break;
+    case PROP_CLEAR_ALL_ACTION_TARGET:
+      g_value_set_variant (value, gtk_actionable_get_action_target_value (GTK_ACTIONABLE 
(priv->clear_all_button)));
+      break;
+    case PROP_CLEAR_ALL_DESCRIPTION:
+      g_value_set_string (value, ephy_data_dialog_get_clear_all_description (self));
+      break;
+    case PROP_SEARCH_DESCRIPTION:
+      g_value_set_string (value, gtk_entry_get_placeholder_text (GTK_ENTRY (priv->search_entry)));
+      break;
+    case PROP_EMPTY_TITLE:
+      g_value_set_string (value, gtk_label_get_text (GTK_LABEL (priv->empty_title_label)));
+      break;
+    case PROP_EMPTY_DESCRIPTION:
+      g_value_set_string (value, gtk_label_get_text (GTK_LABEL (priv->empty_description_label)));
+      break;
+    case PROP_SEARCH_TEXT:
+      g_value_set_string (value, ephy_data_dialog_get_search_text (self));
+      break;
+    case PROP_IS_LOADING:
+      g_value_set_boolean (value, ephy_data_dialog_get_is_loading (self));
+      break;
+    case PROP_HAS_DATA:
+      g_value_set_boolean (value, ephy_data_dialog_get_has_data (self));
+      break;
+    case PROP_HAS_SEARCH_RESULTS:
+      g_value_set_boolean (value, ephy_data_dialog_get_has_search_results (self));
+      break;
+    case PROP_CAN_CLEAR:
+      g_value_set_boolean (value, ephy_data_dialog_get_can_clear (self));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+ephy_data_dialog_finalize (GObject *object)
+{
+  EphyDataDialog *self = EPHY_DATA_DIALOG (object);
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+
+  g_free (priv->search_text);
+
+  G_OBJECT_CLASS (ephy_data_dialog_parent_class)->finalize (object);
+}
+
+static void
+ephy_data_dialog_add (GtkContainer *container,
+                      GtkWidget    *child)
+{
+  EphyDataDialog *self = EPHY_DATA_DIALOG (container);
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+
+  if (!priv->box) {
+    GTK_CONTAINER_CLASS (ephy_data_dialog_parent_class)->add (container, child);
+    return;
+  }
+
+  g_assert (!priv->child);
+
+  priv->child = child;
+  gtk_container_add (GTK_CONTAINER (priv->stack), child);
+
+  update (self);
+}
+
+static void
+ephy_data_dialog_class_init (EphyDataDialogClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
+
+  object_class->set_property = ephy_data_dialog_set_property;
+  object_class->get_property = ephy_data_dialog_get_property;
+  object_class->finalize = ephy_data_dialog_finalize;
+
+  container_class->add = ephy_data_dialog_add;
+
+  obj_properties[PROP_CLEAR_ALL_ACTION_NAME] =
+    g_param_spec_string ("clear-all-action-name",
+                         _("'Clear all' action name"),
+                         _("The name of the action associated to the 'Clear all' button"),
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_CLEAR_ALL_ACTION_TARGET] =
+    g_param_spec_variant ("clear-all-action-target",
+                          _("'Clear all' action target value"),
+                          _("The parameter for 'Clear all' action invocations"),
+                          G_VARIANT_TYPE_ANY, NULL,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_CLEAR_ALL_DESCRIPTION] =
+    g_param_spec_string ("clear-all-description",
+                         _("'Clear all' description"),
+                         _("The description of the 'Clear all' action"),
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_SEARCH_DESCRIPTION] =
+    g_param_spec_string ("search-description",
+                         _("'Search' description"),
+                         _("The description of the 'Search' action"),
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_EMPTY_TITLE] =
+    g_param_spec_string ("empty-title",
+                         _("'Empty' title"),
+                         _("The title of the 'Empty' state page"),
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_EMPTY_DESCRIPTION] =
+    g_param_spec_string ("empty-description",
+                         _("'Empty' description"),
+                         _("The description of the 'Empty' state page"),
+                         NULL,
+                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_SEARCH_TEXT] =
+    g_param_spec_string ("search-text",
+                         _("Search text"),
+                         _("The text of the search entry"),
+                         NULL,
+                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_IS_LOADING] =
+    g_param_spec_boolean ("is-loading",
+                          _("Is loading"),
+                          _("Whether the dialog is loading its data"),
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_HAS_DATA] =
+    g_param_spec_boolean ("has-data",
+                          _("Has data"),
+                          _("Whether the dialog has data"),
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_HAS_SEARCH_RESULTS] =
+    g_param_spec_boolean ("has-search-results",
+                          _("Has search results"),
+                          _("Whether the dialog has search results"),
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  obj_properties[PROP_CAN_CLEAR] =
+    g_param_spec_boolean ("can-clear",
+                          _("Can clear"),
+                          _("Whether the data can be cleared"),
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+
+  /**
+   * EphyLocationEntry::user-changed:
+   * @entry: the object on which the signal is emitted
+   *
+   * Emitted when the user changes the contents of the internal #GtkEntry
+   *
+   */
+  signals[CLEAR_ALL_CLICKED] =
+    g_signal_new ("clear-all-clicked",
+                  G_OBJECT_CLASS_TYPE (klass),
+                  G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE,
+                  0,
+                  G_TYPE_NONE);
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/epiphany/gtk/data-dialog.ui");
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, box);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, clear_all_button);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, empty_title_label);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, empty_description_label);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, search_bar);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, search_button);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, search_entry);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, spinner);
+  gtk_widget_class_bind_template_child_private (widget_class, EphyDataDialog, stack);
+
+  gtk_widget_class_bind_template_callback (widget_class, on_key_press_event);
+  gtk_widget_class_bind_template_callback (widget_class, on_clear_all_button_clicked);
+  gtk_widget_class_bind_template_callback (widget_class, on_search_entry_changed);
+}
+
+static void
+ephy_data_dialog_init (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv = ephy_data_dialog_get_instance_private (self);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  hdy_search_bar_connect_entry (HDY_SEARCH_BAR (priv->search_bar), GTK_ENTRY (priv->search_entry));
+
+  update (self);
+}
+
+const gchar *
+ephy_data_dialog_get_clear_all_description (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  return gtk_widget_get_tooltip_text (GTK_WIDGET (priv->clear_all_button));
+}
+
+void
+ephy_data_dialog_set_clear_all_description (EphyDataDialog *self,
+                                            const gchar    *description)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  if (g_strcmp0 (gtk_widget_get_tooltip_text (GTK_WIDGET (priv->clear_all_button)), description) == 0)
+    return;
+
+  gtk_widget_set_tooltip_text (GTK_WIDGET (priv->clear_all_button), description);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_CLEAR_ALL_DESCRIPTION]);
+}
+
+gboolean
+ephy_data_dialog_get_is_loading (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  return priv->is_loading;
+}
+
+void
+ephy_data_dialog_set_is_loading (EphyDataDialog *self,
+                                 gboolean        is_loading)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+  is_loading = !!is_loading;
+
+  if (priv->is_loading == is_loading)
+    return;
+
+  priv->is_loading = is_loading;
+
+  update (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_IS_LOADING]);
+}
+
+gboolean
+ephy_data_dialog_get_has_data (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  return priv->has_data;
+}
+
+void
+ephy_data_dialog_set_has_data (EphyDataDialog *self,
+                               gboolean        has_data)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+  has_data = !!has_data;
+
+  if (priv->has_data == has_data)
+    return;
+
+  priv->has_data = has_data;
+
+  update (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_HAS_DATA]);
+}
+
+gboolean
+ephy_data_dialog_get_has_search_results (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  return priv->has_search_results;
+}
+
+void
+ephy_data_dialog_set_has_search_results (EphyDataDialog *self,
+                                         gboolean        has_search_results)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+  has_search_results = !!has_search_results;
+
+  if (priv->has_search_results == has_search_results)
+    return;
+
+  priv->has_search_results = has_search_results;
+
+  update (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_HAS_SEARCH_RESULTS]);
+}
+
+gboolean
+ephy_data_dialog_get_can_clear (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  return priv->can_clear;
+}
+
+void
+ephy_data_dialog_set_can_clear (EphyDataDialog *self,
+                                gboolean        can_clear)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+  can_clear = !!can_clear;
+
+  if (priv->can_clear == can_clear)
+    return;
+
+  priv->can_clear = can_clear;
+
+  update (self);
+
+  g_object_notify_by_pspec (G_OBJECT (self), obj_properties[PROP_CAN_CLEAR]);
+}
+
+const gchar *
+ephy_data_dialog_get_search_text (EphyDataDialog *self)
+{
+  EphyDataDialogPrivate *priv;
+
+  g_assert (EPHY_IS_DATA_DIALOG (self));
+
+  priv = ephy_data_dialog_get_instance_private (self);
+
+  return priv->search_text;
+}
diff --git a/src/ephy-data-dialog.h b/src/ephy-data-dialog.h
new file mode 100644
index 000000000..3b1e57554
--- /dev/null
+++ b/src/ephy-data-dialog.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ *  Copyright © 2019 Purism SPC
+ *
+ *  This file is part of Epiphany.
+ *
+ *  Epiphany 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Epiphany 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 Epiphany.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_DATA_DIALOG (ephy_data_dialog_get_type ())
+
+G_DECLARE_DERIVABLE_TYPE (EphyDataDialog, ephy_data_dialog, EPHY, DATA_DIALOG, GtkWindow)
+
+struct _EphyDataDialogClass
+{
+  GtkWindowClass parent_class;
+};
+
+const gchar *ephy_data_dialog_get_clear_all_description (EphyDataDialog *self);
+void         ephy_data_dialog_set_clear_all_description (EphyDataDialog *self,
+                                                         const gchar    *description);
+
+gboolean ephy_data_dialog_get_is_loading (EphyDataDialog *self);
+void     ephy_data_dialog_set_is_loading (EphyDataDialog *self,
+                                          gboolean        is_loading);
+
+gboolean ephy_data_dialog_get_has_data (EphyDataDialog *self);
+void     ephy_data_dialog_set_has_data (EphyDataDialog *self,
+                                        gboolean        has_data);
+
+gboolean ephy_data_dialog_get_has_search_results (EphyDataDialog *self);
+void     ephy_data_dialog_set_has_search_results (EphyDataDialog *self,
+                                                  gboolean        has_search_results);
+
+gboolean ephy_data_dialog_get_can_clear (EphyDataDialog *self);
+void     ephy_data_dialog_set_can_clear (EphyDataDialog *self,
+                                         gboolean        can_clear);
+
+const gchar *ephy_data_dialog_get_search_text (EphyDataDialog *self);
+
+G_END_DECLS
diff --git a/src/meson.build b/src/meson.build
index 8a3a9a8cd..95ff9fdd1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -25,6 +25,7 @@ libephymain_sources = [
   'ephy-action-bar-end.c',
   'ephy-action-bar-start.c',
   'ephy-action-helper.c',
+  'ephy-data-dialog.c',
   'ephy-desktop-utils.c',
   'ephy-encoding-dialog.c',
   'ephy-encoding-row.c',
diff --git a/src/resources/epiphany.gresource.xml b/src/resources/epiphany.gresource.xml
index b0ead00fc..ed8ec8cd8 100644
--- a/src/resources/epiphany.gresource.xml
+++ b/src/resources/epiphany.gresource.xml
@@ -22,6 +22,7 @@
     <file preprocess="xml-stripblanks" compressed="true">gtk/bookmarks-popover.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/clear-data-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/cookies-dialog.ui</file>
+    <file preprocess="xml-stripblanks" compressed="true">gtk/data-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/encoding-dialog.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/encoding-row.ui</file>
     <file preprocess="xml-stripblanks" compressed="true">gtk/history-dialog.ui</file>
diff --git a/src/resources/gtk/data-dialog.ui b/src/resources/gtk/data-dialog.ui
new file mode 100644
index 000000000..fe42a1240
--- /dev/null
+++ b/src/resources/gtk/data-dialog.ui
@@ -0,0 +1,269 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+  <requires lib="gtk+" version="3.20"/>
+  <requires lib="libhandy" version="0.0"/>
+  <object class="GtkImage" id="search_image">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="icon_name">edit-find-symbolic</property>
+  </object>
+  <object class="GtkImage" id="clear_all_image">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="icon_name">user-trash-symbolic</property>
+  </object>
+  <template class="EphyDataDialog" parent="GtkWindow">
+    <property name="can_focus">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="default_width">1000</property>
+    <property name="default_height">600</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <signal name="key-press-event" handler="on_key_press_event" swapped="no"/>
+    <child type="titlebar">
+      <object class="GtkHeaderBar">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="show_close_button">True</property>
+        <child>
+          <object class="GtkButton" id="clear_all_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="image">clear_all_image</property>
+            <property name="always_show_image">True</property>
+            <signal name="clicked" handler="on_clear_all_button_clicked" swapped="yes"/>
+            <accelerator key="Delete" modifiers="GDK_SHIFT_MASK" signal="clicked"/>
+            <style>
+              <class name="destructive-action"/>
+              <class name="image-button"/>
+            </style>
+          </object>
+        </child>
+        <child>
+          <object class="GtkToggleButton" id="search_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="image">search_image</property>
+            <property name="always_show_image">True</property>
+            <property name="active" bind-source="search_bar" bind-property="search-mode-enabled" 
bind-flags="sync-create|bidirectional"/>
+            <accelerator key="F" modifiers="GDK_CONTROL_MASK" signal="clicked"/>
+            <style>
+              <class name="image-button"/>
+            </style>
+            <child internal-child="accessible">
+              <object class="AtkObject">
+                <property name="AtkObject::accessible-name" translatable="yes">Search</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="pack_type">end</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="HdySearchBar" id="search_bar">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="valign">start</property>
+            <property name="hexpand">True</property>
+            <property name="search_mode_enabled">False</property>
+            <child>
+              <object class="HdyColumn">
+                <property name="visible">True</property>
+                <property name="hexpand">True</property>
+                <property name="maximum_width">400</property>
+                <property name="linear_growth_width">300</property>
+                <child>
+                  <object class="GtkSearchEntry" id="search_entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="hexpand">True</property>
+                    <property name="primary_icon_name">edit-find-symbolic</property>
+                    <property name="primary_icon_activatable">False</property>
+                    <property name="primary_icon_sensitive">False</property>
+                    <signal name="search-changed" handler="on_search_entry_changed" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkStack" id="stack">
+            <property name="can_focus">False</property>
+            <property name="expand">True</property>
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkSpinner" id="spinner">
+                <property name="can_focus">False</property>
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="name">loading</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="can_focus">False</property>
+                <property name="expand">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="orientation">vertical</property>
+                    <property name="valign">center</property>
+                    <property name="vexpand">True</property>
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">web-browser-symbolic</property>
+                        <property name="icon_size">0</property>
+                        <property name="margin_bottom">18</property>
+                        <property name="pixel_size">128</property>
+                        <property name="valign">center</property>
+                        <property name="visible">True</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="can_focus">False</property>
+                        <property name="margin_end">12</property>
+                        <property name="margin_start">12</property>
+                        <property name="orientation">vertical</property>
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkLabel" id="empty_title_label">
+                            <property name="can_focus">False</property>
+                            <property name="halign">center</property>
+                            <property name="justify">center</property>
+                            <property name="margin_bottom">12</property>
+                            <property name="opacity">0.5</property>
+                            <property name="visible">True</property>
+                            <property name="wrap">True</property>
+                            <attributes>
+                              <attribute name="scale" value="2"/>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="empty_description_label">
+                            <property name="can_focus">False</property>
+                            <property name="justify">center</property>
+                            <property name="margin_bottom">6</property>
+                            <property name="opacity">0.5</property>
+                            <property name="use_markup">True</property>
+                            <property name="visible">True</property>
+                            <property name="wrap">True</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="name">empty</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="can_focus">False</property>
+                <property name="expand">True</property>
+                <property name="hscrollbar_policy">never</property>
+                <property name="visible">True</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="can_focus">False</property>
+                    <property name="halign">center</property>
+                    <property name="orientation">vertical</property>
+                    <property name="valign">center</property>
+                    <property name="vexpand">True</property>
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="can_focus">False</property>
+                        <property name="icon_name">edit-find-symbolic</property>
+                        <property name="icon_size">0</property>
+                        <property name="margin_bottom">18</property>
+                        <property name="pixel_size">128</property>
+                        <property name="valign">center</property>
+                        <property name="visible">True</property>
+                        <style>
+                          <class name="dim-label"/>
+                        </style>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkBox">
+                        <property name="can_focus">False</property>
+                        <property name="margin_end">12</property>
+                        <property name="margin_start">12</property>
+                        <property name="orientation">vertical</property>
+                        <property name="visible">True</property>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="can_focus">False</property>
+                            <property name="halign">center</property>
+                            <property name="justify">center</property>
+                            <property name="label" translatable="yes">No Results Found</property>
+                            <property name="margin_bottom">12</property>
+                            <property name="opacity">0.5</property>
+                            <property name="visible">True</property>
+                            <property name="wrap">True</property>
+                            <attributes>
+                              <attribute name="scale" value="2"/>
+                              <attribute name="weight" value="bold"/>
+                            </attributes>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkLabel">
+                            <property name="can_focus">False</property>
+                            <property name="justify">center</property>
+                            <property name="label" translatable="yes">Try a different search</property>
+                            <property name="margin_bottom">6</property>
+                            <property name="opacity">0.5</property>
+                            <property name="use_markup">True</property>
+                            <property name="visible">True</property>
+                            <property name="wrap">True</property>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="name">no-results</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>


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