[libadwaita/wip/exalm/adaptive-states: 1/7] message-dialog: Stop using AdwSqueezer




commit fbd522bd44491ba2dde43905fab7a1a832a73898
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Tue Oct 4 17:22:49 2022 +0400

    message-dialog: Stop using AdwSqueezer
    
    When originally implementing the dialog, I took a shortcut and used
    squeezer instead of reimplementing the layout manually, and bit the
    bullet with disappearing focus when switching layouts.
    
    Since squeezer is to be deprecated, rework that and use a proper custom
    layout.

 src/adw-message-dialog.c                    | 325 ++++++++++++++++++++--------
 src/adw-message-dialog.ui                   |  19 +-
 src/stylesheet/widgets/_message-dialog.scss |  18 +-
 3 files changed, 248 insertions(+), 114 deletions(-)
---
diff --git a/src/adw-message-dialog.c b/src/adw-message-dialog.c
index 031d387f..6a639769 100644
--- a/src/adw-message-dialog.c
+++ b/src/adw-message-dialog.c
@@ -9,8 +9,9 @@
 #include "config.h"
 #include "adw-message-dialog.h"
 
+#include "adw-gizmo-private.h"
 #include "adw-gtkbuilder-utils-private.h"
-#include "adw-squeezer.h"
+#include "adw-widget-utils-private.h"
 
 /**
  * AdwMessageDialog:
@@ -134,8 +135,8 @@ typedef struct {
   AdwResponseAppearance appearance;
   gboolean enabled;
 
-  GtkWidget *wide_button;
-  GtkWidget *narrow_button;
+  GtkWidget *button;
+  GtkWidget *separator;
 } ResponseInfo;
 
 typedef struct
@@ -143,11 +144,7 @@ typedef struct
   GtkWidget *heading_label;
   GtkWidget *body_label;
   GtkBox *message_area;
-  GtkBox *wide_response_box;
-  GtkBox *narrow_response_box;
-  GtkSizeGroup *wide_size_group;
-  GtkSizeGroup *narrow_size_group;
-  AdwSqueezer *squeezer;
+  GtkWidget *response_area;
 
   char *heading;
   gboolean heading_use_markup;
@@ -367,26 +364,6 @@ create_response_button (AdwMessageDialog *self,
   return button;
 }
 
-static void
-update_default_response (AdwMessageDialog *self)
-{
-  AdwMessageDialogPrivate *priv = adw_message_dialog_get_instance_private (self);
-  ResponseInfo *info;
-
-  if (!priv->default_response)
-    return;
-
-  info = find_response (self, g_quark_to_string (priv->default_response));
-
-  if (!info)
-    return;
-
-  if (adw_squeezer_get_visible_child (priv->squeezer) == GTK_WIDGET (priv->narrow_response_box))
-    gtk_window_set_default_widget (GTK_WINDOW (self), info->narrow_button);
-  else
-    gtk_window_set_default_widget (GTK_WINDOW (self), info->wide_button);
-}
-
 static void
 update_window_title (AdwMessageDialog *self)
 {
@@ -439,8 +416,6 @@ adw_message_dialog_map (GtkWidget *widget)
 
   GTK_WIDGET_CLASS (adw_message_dialog_parent_class)->map (widget);
 
-  update_default_response (self);
-
   /* The rest of the function was copied from gtkdialog.c */
   focus = gtk_window_get_focus (GTK_WINDOW (self));
   if (!focus) {
@@ -468,21 +443,212 @@ adw_message_dialog_map (GtkWidget *widget)
     for (l = priv->responses; l; l = l->next) {
       ResponseInfo *response = l->data;
 
-      if ((focus == NULL || response->wide_button == focus) &&
-           response->wide_button != default_widget &&
+      if ((focus == NULL || response->button == focus) &&
+           response->button != default_widget &&
            default_widget) {
         gtk_widget_grab_focus (default_widget);
         break;
       }
+    }
+  }
+}
 
-      if ((focus == NULL || response->narrow_button == focus) &&
-           response->narrow_button != default_widget &&
-           default_widget) {
-        gtk_widget_grab_focus (default_widget);
-        break;
+static void
+measure_responses_do (AdwMessageDialog *self,
+                      gboolean          compact,
+                      GtkOrientation    orientation,
+                      int              *minimum,
+                      int              *natural)
+{
+  AdwMessageDialogPrivate *priv = adw_message_dialog_get_instance_private (self);
+  GList *l;
+  int min = 0, nat = 0;
+  int button_min = 0, button_nat = 0;
+  int n_buttons = 0;
+  gboolean horiz = (orientation == GTK_ORIENTATION_HORIZONTAL);
+
+  for (l = priv->responses; l; l = l->next) {
+    ResponseInfo *response = l->data;
+    int child_min, child_nat;
+
+    gtk_widget_measure (response->button, orientation, -1,
+                        &child_min, &child_nat, NULL, NULL);
+
+    if (horiz == compact) {
+      min = MAX (min, child_min);
+      nat = MAX (nat, child_min);
+    } else if (horiz) {
+      button_min = MAX (button_min, child_min);
+      button_nat = MAX (button_nat, child_min);
+      n_buttons++;
+    } else {
+      min += child_min;
+      nat += child_nat;
+    }
+
+    if (response->separator) {
+      gtk_widget_measure (response->separator, orientation, -1,
+                          &child_min, &child_nat, NULL, NULL);
+
+    if (horiz == compact) {
+        min = MAX (min, child_min);
+        nat = MAX (nat, child_min);
+      } else {
+        min += child_min;
+        nat += child_nat;
       }
     }
   }
+
+  if (horiz && !compact) {
+    min += button_min * n_buttons;
+    nat += button_nat * n_buttons;
+  }
+
+  if (minimum)
+    *minimum = min;
+  if (natural)
+    *natural = nat;
+}
+
+static GtkSizeRequestMode
+get_responses_request_mode (GtkWidget *widget)
+{
+  return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+}
+
+static void
+measure_responses (GtkWidget      *widget,
+                   GtkOrientation  orientation,
+                   int             for_size,
+                   int            *minimum,
+                   int            *natural,
+                   int            *minimum_baseline,
+                   int            *natural_baseline)
+{
+  AdwMessageDialog *self = ADW_MESSAGE_DIALOG (gtk_widget_get_root (widget));
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL) {
+    measure_responses_do (self, TRUE, orientation, minimum, NULL);
+    measure_responses_do (self, FALSE, orientation, NULL, natural);
+  } else {
+    int wide_min = 0;
+
+    if (for_size >= 0)
+      measure_responses_do (self, FALSE, GTK_ORIENTATION_HORIZONTAL, &wide_min, NULL);
+
+    measure_responses_do (self, for_size >= 0 && for_size < wide_min,
+                          orientation, minimum, natural);
+  }
+
+  if (minimum_baseline)
+    *minimum_baseline = -1;
+  if (natural_baseline)
+    *natural_baseline = -1;
+}
+
+static void
+allocate_responses (GtkWidget *widget,
+                    int        width,
+                    int        height,
+                    int        baseline)
+{
+  AdwMessageDialog *self = ADW_MESSAGE_DIALOG (gtk_widget_get_root (widget));
+  AdwMessageDialogPrivate *priv = adw_message_dialog_get_instance_private (self);
+  gboolean compact;
+  int wide_min;
+
+  measure_responses_do (self, FALSE, GTK_ORIENTATION_HORIZONTAL, &wide_min, NULL);
+
+  compact = wide_min > width;
+
+  if (compact)
+    gtk_widget_add_css_class (widget, "compact");
+  else
+    gtk_widget_remove_css_class (widget, "compact");
+
+  if (compact) {
+    int pos = height;
+    GList *l;
+
+    for (l = priv->responses; l; l = l->next) {
+      ResponseInfo *response = l->data;
+      int child_height;
+
+      if (response->separator) {
+        gtk_widget_measure (response->separator, GTK_ORIENTATION_VERTICAL, -1,
+                            &child_height, NULL, NULL, NULL);
+
+        pos -= child_height;
+
+        gtk_widget_allocate (response->separator, width, child_height, -1,
+                             gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, pos)));
+      }
+
+      gtk_widget_measure (response->button, GTK_ORIENTATION_VERTICAL, -1,
+                          &child_height, NULL, NULL, NULL);
+
+      pos -= child_height;
+
+      gtk_widget_allocate (response->button, width, child_height, -1,
+                           gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, pos)));
+    }
+  } else {
+    gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
+    int pos = is_rtl ? width : 0;
+    int n_buttons = g_list_length (priv->responses);
+    int total_width = width;
+    int button_width;
+    GList *l;
+
+    for (l = priv->responses; l; l = l->next) {
+      ResponseInfo *response = l->data;
+      int separator_width;
+
+      if (!response->separator)
+        continue;
+
+      gtk_widget_measure (response->separator, GTK_ORIENTATION_HORIZONTAL, -1,
+                          &separator_width, NULL, NULL, NULL);
+
+      total_width -= separator_width;
+    }
+
+    button_width = (int) ceil ((double) total_width / n_buttons);
+
+    for (l = priv->responses; l; l = l->next) {
+      ResponseInfo *response = l->data;
+
+      if (response->separator) {
+        int separator_width;
+
+        gtk_widget_measure (response->separator, GTK_ORIENTATION_HORIZONTAL, -1,
+                            &separator_width, NULL, NULL, NULL);
+
+        if (is_rtl)
+          pos -= separator_width;
+
+        gtk_widget_allocate (response->separator, separator_width, height, -1,
+                             gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (pos, 0)));
+
+        if (!is_rtl)
+          pos += separator_width;
+      }
+
+      button_width = MIN (button_width, total_width);
+
+      total_width -= button_width;
+
+      if (is_rtl)
+        pos -= button_width;
+
+      gtk_widget_allocate (response->button, button_width, height, -1,
+                           gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (pos, 0)));
+
+      if (!is_rtl)
+        pos += button_width;
+    }
+  }
 }
 
 static void
@@ -517,12 +683,8 @@ adw_message_dialog_measure (GtkWidget      *widget,
       max_size = DIALOG_MAX_WIDTH;
     }
 
-    gtk_widget_measure (GTK_WIDGET (priv->wide_response_box),
-                        GTK_ORIENTATION_HORIZONTAL, -1,
-                        &wide_min, NULL, NULL, NULL);
-    gtk_widget_measure (GTK_WIDGET (priv->narrow_response_box),
-                        GTK_ORIENTATION_HORIZONTAL, -1,
-                        NULL, &narrow_nat, NULL, NULL);
+    measure_responses_do (self, FALSE, GTK_ORIENTATION_HORIZONTAL, &wide_min, NULL);
+    measure_responses_do (self, TRUE, GTK_ORIENTATION_HORIZONTAL, NULL, &narrow_nat);
 
     narrow_nat = MAX (narrow_nat, DIALOG_MIN_WIDTH);
 
@@ -802,13 +964,7 @@ adw_message_dialog_class_init (AdwMessageDialogClass *klass)
   gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, heading_label);
   gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, body_label);
   gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, message_area);
-  gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, squeezer);
-  gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, wide_response_box);
-  gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, narrow_response_box);
-  gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, wide_size_group);
-  gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, narrow_size_group);
-
-  gtk_widget_class_bind_template_callback (widget_class, update_default_response);
+  gtk_widget_class_bind_template_child_private (widget_class, AdwMessageDialog, response_area);
 
   gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "window.close", NULL);
 
@@ -834,6 +990,16 @@ adw_message_dialog_init (AdwMessageDialog *self)
 
   gtk_widget_init_template (GTK_WIDGET (self));
 
+  gtk_widget_set_layout_manager (priv->response_area,
+                                 gtk_custom_layout_new (get_responses_request_mode,
+                                                        measure_responses,
+                                                        allocate_responses));
+
+  adw_gizmo_set_focus_func (ADW_GIZMO (priv->response_area),
+                            (AdwGizmoFocusFunc) adw_widget_focus_child);
+  adw_gizmo_set_grab_focus_func (ADW_GIZMO (priv->response_area),
+                            (AdwGizmoGrabFocusFunc) adw_widget_grab_focus_child);
+
   parent_changed_cb (self);
   g_signal_connect (self, "notify::transient-for",
                     G_CALLBACK (parent_changed_cb), self);
@@ -1633,30 +1799,18 @@ adw_message_dialog_add_response (AdwMessageDialog *self,
   info->enabled = TRUE;
 
   if (priv->responses) {
-    GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
-    gtk_box_append (priv->wide_response_box, separator);
+    info->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
+    gtk_widget_set_parent (info->separator, priv->response_area);
   }
 
-  info->wide_button = create_response_button (self, info);
-  gtk_widget_set_hexpand (info->wide_button, TRUE);
-  gtk_box_append (priv->wide_response_box, info->wide_button);
-  gtk_size_group_add_widget (priv->wide_size_group, info->wide_button);
-
-  if (priv->responses) {
-    GtkWidget *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
-    gtk_box_prepend (priv->narrow_response_box, separator);
-  }
-
-  info->narrow_button = create_response_button (self, info);
-  gtk_box_prepend (priv->narrow_response_box, info->narrow_button);
-  gtk_size_group_add_widget (priv->narrow_size_group, info->narrow_button);
+  info->button = create_response_button (self, info);
+  gtk_widget_set_parent (info->button, priv->response_area);
 
   priv->responses = g_list_append (priv->responses, info);
   g_hash_table_insert (priv->id_to_response, g_strdup (id), info);
 
-  if (priv->default_response == info->id &&
-      gtk_widget_get_mapped (GTK_WIDGET (self)))
-    update_default_response (self);
+  if (priv->default_response == info->id)
+    gtk_window_set_default_widget (GTK_WINDOW (self), info->button);
 }
 
 /**
@@ -1772,8 +1926,7 @@ adw_message_dialog_set_response_label (AdwMessageDialog *self,
   g_free (info->label);
   info->label = g_strdup (label);
 
-  gtk_button_set_label (GTK_BUTTON (info->wide_button), label);
-  gtk_button_set_label (GTK_BUTTON (info->narrow_button), label);
+  gtk_button_set_label (GTK_BUTTON (info->button), label);
 }
 
 /**
@@ -1850,21 +2003,15 @@ adw_message_dialog_set_response_appearance (AdwMessageDialog      *self,
 
   info->appearance = appearance;
 
-  if (info->appearance == ADW_RESPONSE_SUGGESTED) {
-    gtk_widget_add_css_class (info->wide_button, "suggested");
-    gtk_widget_add_css_class (info->narrow_button, "suggested");
-  } else {
-    gtk_widget_remove_css_class (info->wide_button, "suggested");
-    gtk_widget_remove_css_class (info->narrow_button, "suggested");
-  }
+  if (info->appearance == ADW_RESPONSE_SUGGESTED)
+    gtk_widget_add_css_class (info->button, "suggested");
+  else
+    gtk_widget_remove_css_class (info->button, "suggested");
 
-  if (info->appearance == ADW_RESPONSE_DESTRUCTIVE) {
-    gtk_widget_add_css_class (info->wide_button, "destructive");
-    gtk_widget_add_css_class (info->narrow_button, "destructive");
-  } else {
-    gtk_widget_remove_css_class (info->wide_button, "destructive");
-    gtk_widget_remove_css_class (info->narrow_button, "destructive");
-  }
+  if (info->appearance == ADW_RESPONSE_DESTRUCTIVE)
+    gtk_widget_add_css_class (info->button, "destructive");
+  else
+    gtk_widget_remove_css_class (info->button, "destructive");
 }
 
 /**
@@ -1934,8 +2081,7 @@ adw_message_dialog_set_response_enabled (AdwMessageDialog *self,
 
   info->enabled = enabled;
 
-  gtk_widget_set_sensitive (info->wide_button, info->enabled);
-  gtk_widget_set_sensitive (info->narrow_button, info->enabled);
+  gtk_widget_set_sensitive (info->button, info->enabled);
 }
 
 /**
@@ -1983,6 +2129,7 @@ adw_message_dialog_set_default_response (AdwMessageDialog *self,
 {
   AdwMessageDialogPrivate *priv;
   GQuark quark;
+  ResponseInfo *info;
 
   g_return_if_fail (ADW_IS_MESSAGE_DIALOG (self));
 
@@ -1994,8 +2141,10 @@ adw_message_dialog_set_default_response (AdwMessageDialog *self,
 
   priv->default_response = quark;
 
-  if (gtk_widget_get_mapped (GTK_WIDGET (self)))
-    update_default_response (self);
+  info = find_response (self, response);
+
+  if (info)
+    gtk_window_set_default_widget (GTK_WINDOW (self), info->button);
 
   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DEFAULT_RESPONSE]);
 }
diff --git a/src/adw-message-dialog.ui b/src/adw-message-dialog.ui
index d040a91c..a88bb080 100644
--- a/src/adw-message-dialog.ui
+++ b/src/adw-message-dialog.ui
@@ -51,25 +51,10 @@
               <object class="GtkSeparator"/>
             </child>
             <child>
-              <object class="AdwSqueezer" id="squeezer">
-                <property name="homogeneous">False</property>
-                <signal name="notify::visible-child" handler="update_default_response" swapped="yes"/>
+              <object class="AdwGizmo" id="response_area">
                 <style>
                   <class name="response-area"/>
                 </style>
-                <child>
-                  <object class="GtkBox" id="wide_response_box">
-                    <property name="hexpand">1</property>
-                    <property name="orientation">horizontal</property>
-                    <property name="valign">end</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkBox" id="narrow_response_box">
-                    <property name="hexpand">1</property>
-                    <property name="orientation">vertical</property>
-                  </object>
-                </child>
               </object>
             </child>
           </object>
@@ -77,6 +62,4 @@
       </object>
     </property>
   </template>
-  <object class="GtkSizeGroup" id="wide_size_group"/>
-  <object class="GtkSizeGroup" id="narrow_size_group"/>
 </interface>
diff --git a/src/stylesheet/widgets/_message-dialog.scss b/src/stylesheet/widgets/_message-dialog.scss
index 234bd915..612ba26e 100644
--- a/src/stylesheet/widgets/_message-dialog.scss
+++ b/src/stylesheet/widgets/_message-dialog.scss
@@ -79,7 +79,7 @@ window.messagedialog {
     border-spacing: 10px;
   }
 
-  .response-area > box > button {
+  .response-area > button {
     padding: 10px 14px;
     border-radius: 0;
 
@@ -105,27 +105,29 @@ window.messagedialog {
     border-radius: $window_radius+1;
 
     .response-area {
-      > box.horizontal > button {
+      &:not(.compact) > button {
         margin-top: -1px;
         margin-right: -1px;
         margin-left: -1px;
 
-        &:first-child {
+        &:first-child:dir(ltr),
+        &:last-child:dir(rtl) {
           border-bottom-left-radius: $window_radius+1;
           margin-left: 0;
         }
 
-        &:last-child {
+        &:last-child:dir(ltr),
+        &:first-child:dir(rtl) {
           border-bottom-right-radius: $window_radius+1;
           margin-right: 0;
         }
       }
 
-      > box.vertical > button {
-          margin-top: -1px;
-          margin-bottom: -1px;
+      &.compact > button {
+        margin-top: -1px;
+        margin-bottom: -1px;
 
-        &:last-child {
+        &:first-child {
           border-bottom-left-radius: $window_radius+1;
           border-bottom-right-radius: $window_radius+1;
           margin-bottom: 0;


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