[libpanel/wip/chergert/fix-14] save-dialog: implement save dialog visuals



commit 73b7c859e360bd85992f9818312ed96784077532
Author: Christian Hergert <chergert redhat com>
Date:   Tue Sep 13 08:00:31 2022 -0700

    save-dialog: implement save dialog visuals
    
    This still doesn't pump the save delegates, but it does start to implement
    the bits that are relevant for the mockups with how drafts are handled, as
    well as 1 vs N documents.
    
    I had to shove the list in a preferences page so that we could scroll in
    case things get too large.
    
    Additionally, the button visiblity is a total hack, and we probably want
    proper API for that in libaddwaita directly.

 po/POTFILES.in                      |   1 +
 src/panel-save-dialog-row-private.h |  12 ++-
 src/panel-save-dialog-row.c         |  53 +++++++++-
 src/panel-save-dialog-row.ui        |   6 +-
 src/panel-save-dialog.c             | 193 +++++++++++++++++++++++++++++++++++-
 src/panel-save-dialog.ui            |   8 +-
 6 files changed, 254 insertions(+), 19 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a013c11..e26cda6 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -2,6 +2,7 @@ src/panel-frame-header-bar.c
 src/panel-frame-header-bar.ui
 src/panel-frame.ui
 src/panel-maximized-controls.c
+src/panel-save-dialog-row.c
 src/panel-save-dialog.c
 src/panel-save-dialog.ui
 src/panel-signal-group.c
diff --git a/src/panel-save-dialog-row-private.h b/src/panel-save-dialog-row-private.h
index f6f31db..71e0086 100644
--- a/src/panel-save-dialog-row-private.h
+++ b/src/panel-save-dialog-row-private.h
@@ -30,10 +30,12 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (PanelSaveDialogRow, panel_save_dialog_row, PANEL, SAVE_DIALOG_ROW, AdwActionRow)
 
-GtkWidget         *panel_save_dialog_row_new          (PanelSaveDelegate  *delegate);
-PanelSaveDelegate *panel_save_dialog_row_get_delegate (PanelSaveDialogRow *self);
-gboolean           panel_save_dialog_row_get_selected (PanelSaveDialogRow *self);
-void               panel_save_dialog_row_set_selected (PanelSaveDialogRow *self,
-                                                       gboolean            selected);
+GtkWidget         *panel_save_dialog_row_new                (PanelSaveDelegate  *delegate);
+PanelSaveDelegate *panel_save_dialog_row_get_delegate       (PanelSaveDialogRow *self);
+gboolean           panel_save_dialog_row_get_selected       (PanelSaveDialogRow *self);
+void               panel_save_dialog_row_set_selected       (PanelSaveDialogRow *self,
+                                                             gboolean            selected);
+void               panel_save_dialog_row_set_selection_mode (PanelSaveDialogRow *self,
+                                                             gboolean            selection_mode);
 
 G_END_DECLS
diff --git a/src/panel-save-dialog-row.c b/src/panel-save-dialog-row.c
index d8afcc2..a8d0192 100644
--- a/src/panel-save-dialog-row.c
+++ b/src/panel-save-dialog-row.c
@@ -20,6 +20,8 @@
 
 #include "config.h"
 
+#include <glib/gi18n.h>
+
 #include "panel-save-delegate.h"
 #include "panel-save-dialog-row-private.h"
 
@@ -54,6 +56,46 @@ on_notify_active_cb (PanelSaveDialogRow *self,
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SELECTED]);
 }
 
+static gboolean
+map_title_with_draft (GBinding     *binding,
+                      const GValue *from_value,
+                      GValue       *to_value,
+                      gpointer      user_data)
+{
+  PanelSaveDelegate *delegate = user_data;
+  const char *str;
+
+  g_assert (G_IS_BINDING (binding));
+  g_assert (PANEL_IS_SAVE_DELEGATE (delegate));
+
+  if ((str = g_value_get_string (from_value)))
+    {
+      if (panel_save_delegate_get_is_draft (delegate))
+        g_value_take_string (to_value,
+                             g_strdup_printf ("%s <span fgalpha='32767'>%s</span>",
+                                              str, _("(new)")));
+      else
+        g_value_set_string (to_value, str);
+    }
+
+  return TRUE;
+}
+
+static void
+panel_save_dialog_row_set_delegate (PanelSaveDialogRow *self,
+                                    PanelSaveDelegate  *delegate)
+{
+  g_assert (PANEL_IS_SAVE_DIALOG_ROW (self));
+  g_assert (PANEL_IS_SAVE_DELEGATE (delegate));
+  g_assert (self->delegate == NULL);
+
+  g_set_object (&self->delegate, delegate);
+  g_object_bind_property_full (delegate, "title", self, "title",
+                               G_BINDING_SYNC_CREATE,
+                               map_title_with_draft, NULL,
+                               delegate, NULL);
+}
+
 static void
 panel_save_dialog_row_dispose (GObject *object)
 {
@@ -94,7 +136,7 @@ panel_save_dialog_row_set_property (GObject      *object,
   switch (prop_id)
     {
     case PROP_DELEGATE:
-      self->delegate = g_value_dup_object (value);
+      panel_save_dialog_row_set_delegate (self, g_value_get_object (value));
       break;
 
     default:
@@ -173,3 +215,12 @@ panel_save_dialog_row_set_selected (PanelSaveDialogRow *self,
 
   gtk_check_button_set_active (self->check, selected);
 }
+
+void
+panel_save_dialog_row_set_selection_mode (PanelSaveDialogRow *self,
+                                          gboolean            selection_mode)
+{
+  g_return_if_fail (PANEL_IS_SAVE_DIALOG_ROW (self));
+
+  gtk_widget_set_visible (GTK_WIDGET (self->check), selection_mode);
+}
diff --git a/src/panel-save-dialog-row.ui b/src/panel-save-dialog-row.ui
index 4a08073..d3eb8ab 100644
--- a/src/panel-save-dialog-row.ui
+++ b/src/panel-save-dialog-row.ui
@@ -1,11 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <template class="PanelSaveDialogRow" parent="AdwActionRow">
-    <binding name="title">
-      <lookup name="title" type="PanelSaveDelegate">
-        <lookup name="delegate">PanelSaveDialogRow</lookup>
-      </lookup>
-    </binding>
     <binding name="subtitle">
       <lookup name="subtitle" type="PanelSaveDelegate">
         <lookup name="delegate">PanelSaveDialogRow</lookup>
@@ -18,5 +13,6 @@
         <signal name="notify::active" handler="on_notify_active_cb" swapped="true" 
object="PanelSaveDialogRow"/>
       </object>
     </child>
+    <property name="activatable-widget">check</property>
   </template>
 </interface>
diff --git a/src/panel-save-dialog.c b/src/panel-save-dialog.c
index 0137e93..39398dc 100644
--- a/src/panel-save-dialog.c
+++ b/src/panel-save-dialog.c
@@ -31,9 +31,10 @@
 struct _PanelSaveDialog
 {
   AdwMessageDialog     parent_instance;
+  GPtrArray           *rows;
+  AdwPreferencesPage  *page;
   AdwPreferencesGroup *group;
   GTask               *task;
-  guint                count;
 };
 
 G_DEFINE_FINAL_TYPE (PanelSaveDialog, panel_save_dialog, ADW_TYPE_MESSAGE_DIALOG)
@@ -105,14 +106,71 @@ panel_save_dialog_response_save_cb (PanelSaveDialog *self,
   g_clear_object (&task);
 }
 
+static void
+find_button_and_set_visible (GtkWidget  *parent,
+                             const char *label,
+                             gboolean    visible)
+{
+  for (GtkWidget *child = gtk_widget_get_first_child (parent);
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child))
+    {
+      if (GTK_IS_BUTTON (child) &&
+          g_strcmp0 (label, gtk_button_get_label (GTK_BUTTON (child))) == 0)
+        {
+          gtk_widget_set_visible (child, visible);
+          break;
+        }
+    }
+}
+
+static void
+set_response_visible (AdwMessageDialog *dialog,
+                      const char       *response,
+                      gboolean          visible)
+{
+  const char *label;
+  GObject *wide;
+  GObject *narrow;
+
+  g_assert (ADW_IS_MESSAGE_DIALOG (dialog));
+  g_assert (response != NULL);
+  g_assert (adw_message_dialog_has_response (dialog, response));
+
+  adw_message_dialog_set_response_enabled (dialog, response, visible);
+
+  if (!(label = adw_message_dialog_get_response_label (dialog, response)))
+    return;
+
+  wide = gtk_widget_get_template_child (GTK_WIDGET (dialog), ADW_TYPE_MESSAGE_DIALOG, "wide_response_box");
+  narrow = gtk_widget_get_template_child (GTK_WIDGET (dialog), ADW_TYPE_MESSAGE_DIALOG, 
"narrow_response_box");
+
+  find_button_and_set_visible (GTK_WIDGET (wide), label, visible);
+  find_button_and_set_visible (GTK_WIDGET (narrow), label, visible);
+}
+
+static void
+panel_save_dialog_dispose (GObject *object)
+{
+  PanelSaveDialog *self = (PanelSaveDialog *)object;
+
+  g_clear_pointer (&self->rows, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (panel_save_dialog_parent_class)->dispose (object);
+}
+
 static void
 panel_save_dialog_class_init (PanelSaveDialogClass *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
+  object_class->dispose = panel_save_dialog_dispose;
+
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/libpanel/panel-save-dialog.ui");
 
   gtk_widget_class_bind_template_child (widget_class, PanelSaveDialog, group);
+  gtk_widget_class_bind_template_child (widget_class, PanelSaveDialog, page);
 
   gtk_widget_class_bind_template_callback (widget_class, panel_save_dialog_response_cancel_cb);
   gtk_widget_class_bind_template_callback (widget_class, panel_save_dialog_response_discard_cb);
@@ -122,20 +180,145 @@ panel_save_dialog_class_init (PanelSaveDialogClass *klass)
 static void
 panel_save_dialog_init (PanelSaveDialog *self)
 {
+  self->rows = g_ptr_array_new ();
+
   gtk_widget_init_template (GTK_WIDGET (self));
 }
 
+static void
+panel_save_dialog_update (PanelSaveDialog *self)
+{
+  g_assert (PANEL_IS_SAVE_DIALOG (self));
+
+  if (self->rows->len == 1)
+    {
+      PanelSaveDialogRow *row = g_ptr_array_index (self->rows, 0);
+      PanelSaveDelegate *delegate = panel_save_dialog_row_get_delegate (row);
+
+      panel_save_dialog_row_set_selection_mode (row, FALSE);
+
+      if (panel_save_delegate_get_is_draft (delegate))
+        {
+          const char *title = panel_save_delegate_get_title (delegate);
+          g_autofree char *body = NULL;
+
+          /* translators: %s is replaced with the document title */
+          body = g_strdup_printf (_("The draft “%s” has not been saved. It can be saved or discarded."), 
title);
+
+          adw_message_dialog_set_heading (ADW_MESSAGE_DIALOG (self),
+                                          _("Save or Discard Draft?"));
+          adw_message_dialog_set_body (ADW_MESSAGE_DIALOG (self), body);
+
+          adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (self), "discard", 
ADW_RESPONSE_DESTRUCTIVE);
+          adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (self), "discard", _("_Discard"));
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "discard", TRUE);
+
+          adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (self), "save", 
ADW_RESPONSE_SUGGESTED);
+          adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (self), "save", _("_Save As…"));
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "save", TRUE);
+        }
+      else
+        {
+          const char *title = panel_save_delegate_get_title (delegate);
+          g_autofree char *body = NULL;
+
+          /* translators: %s is replaced with the document title */
+          body = g_strdup_printf (_("“%s” contains unsaved changes. Changes can be saved or discarded."), 
title);
+
+          adw_message_dialog_set_heading (ADW_MESSAGE_DIALOG (self),
+                                          _("Save or Discard Changes?"));
+          adw_message_dialog_set_body (ADW_MESSAGE_DIALOG (self), body);
+
+          adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (self), "discard", 
ADW_RESPONSE_DESTRUCTIVE);
+          adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (self), "discard", _("_Discard"));
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "discard", TRUE);
+
+          adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (self), "save", 
ADW_RESPONSE_SUGGESTED);
+          adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (self), "save", _("_Save"));
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "save", TRUE);
+        }
+
+      gtk_widget_hide (GTK_WIDGET (self->page));
+    }
+  else
+    {
+      gboolean has_selected = FALSE;
+      gboolean has_unselected = FALSE;
+
+      for (guint i = 0; i < self->rows->len; i++)
+        {
+          PanelSaveDialogRow *row = g_ptr_array_index (self->rows, i);
+          gboolean selected = panel_save_dialog_row_get_selected (row);
+
+          has_selected |= selected;
+          has_unselected |= !selected;
+
+          panel_save_dialog_row_set_selection_mode (row, TRUE);
+        }
+
+      adw_message_dialog_set_heading (ADW_MESSAGE_DIALOG (self),
+                                      _("Save or Discard Changes?"));
+      adw_message_dialog_set_body (ADW_MESSAGE_DIALOG (self),
+                                   _("Open documents contain unsaved changes. Changes can be saved or 
discarded."));
+
+      if (has_selected && has_unselected)
+        {
+          adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (self), "save", 
ADW_RESPONSE_DESTRUCTIVE);
+          adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (self), "save", _("Only _Save 
Selected"));
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "save", TRUE);
+
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "discard", FALSE);
+        }
+      else if (has_selected)
+        {
+          adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (self), "save", 
ADW_RESPONSE_SUGGESTED);
+          adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (self), "save", _("Save All"));
+
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "discard", FALSE);
+        }
+      else
+        {
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "save", FALSE);
+
+          adw_message_dialog_set_response_appearance (ADW_MESSAGE_DIALOG (self), "discard", 
ADW_RESPONSE_DESTRUCTIVE);
+          adw_message_dialog_set_response_label (ADW_MESSAGE_DIALOG (self), "discard", _("Discard All"));
+          set_response_visible (ADW_MESSAGE_DIALOG (self), "discard", TRUE);
+        }
+
+      gtk_widget_show (GTK_WIDGET (self->page));
+    }
+}
+
+static void
+panel_save_dialog_notify_selected_cb (PanelSaveDialog    *self,
+                                      GParamSpec         *pspec,
+                                      PanelSaveDialogRow *row)
+{
+  g_assert (PANEL_IS_SAVE_DIALOG (self));
+  g_assert (PANEL_IS_SAVE_DIALOG_ROW (row));
+
+  panel_save_dialog_update (self);
+}
+
 void
 panel_save_dialog_add_delegate (PanelSaveDialog   *self,
                                 PanelSaveDelegate *delegate)
 {
+  GtkWidget *row;
+
   g_return_if_fail (PANEL_IS_SAVE_DIALOG (self));
   g_return_if_fail (PANEL_IS_SAVE_DELEGATE (delegate));
 
-  self->count++;
+  row = panel_save_dialog_row_new (delegate);
+  g_signal_connect_object (row,
+                           "notify::selected",
+                           G_CALLBACK (panel_save_dialog_notify_selected_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+  g_ptr_array_add (self->rows, row);
+  adw_preferences_group_add (self->group, row);
 
-  adw_preferences_group_add (self->group,
-                             panel_save_dialog_row_new (delegate));
+  panel_save_dialog_update (self);
 }
 
 void
@@ -154,7 +337,7 @@ panel_save_dialog_run_async (PanelSaveDialog     *self,
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, panel_save_dialog_run_async);
 
-  if (self->count == 0)
+  if (self->rows->len == 0)
     {
       gtk_window_destroy (GTK_WINDOW (self));
       g_task_return_boolean (task, TRUE);
diff --git a/src/panel-save-dialog.ui b/src/panel-save-dialog.ui
index 6c7c396..d5668e2 100644
--- a/src/panel-save-dialog.ui
+++ b/src/panel-save-dialog.ui
@@ -1,8 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <template class="PanelSaveDialog" parent="AdwMessageDialog">
-    <property name="heading" translatable="yes">Save or Discard Changes?</property>
-    <property name="body" translatable="yes">Open documents contain unsaved changes. Changes which are not 
saved will be permanently lost.</property>
     <property name="default-response">save</property>
     <property name="close-response">cancel</property>
     <signal name="response::cancel" handler="panel_save_dialog_response_cancel_cb"/>
@@ -14,7 +12,11 @@
       <response id="save" translatable="yes" appearance="suggested">_Save</response>
     </responses>
     <property name="extra-child">
-      <object class="AdwPreferencesGroup" id="group">
+      <object class="AdwPreferencesPage" id="page">
+        <child>
+          <object class="AdwPreferencesGroup" id="group">
+          </object>
+        </child>
       </object>
     </property>
   </template>


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