[Xfce4-commits] <xfce4-panel:andrzejr/systray> systray: reworked layout mechanism.

Andrzej noreply at xfce.org
Thu Apr 4 01:14:03 CEST 2013


Updating branch refs/heads/andrzejr/systray
         to 22867c5e4f507864c45cb43f1b984a4f2f2934b9 (commit)
       from bf15d88d9554bba3c39a95b07482a3c5cf4b9736 (commit)

commit 22867c5e4f507864c45cb43f1b984a4f2f2934b9
Author: Andrzej <ndrwrdck at gmail.com>
Date:   Thu Jan 26 18:54:00 2012 +0900

    systray: reworked layout mechanism.
    
    Previous commit was badly broken (non-square icons were braking the layout).
    
    Changes:
    - Propagating orientation settings to systray manager.
    - systray-box size_request/size_allocate use now integer arithmetic.
    - ... and slot mechanism (all slots are assumed to be square,
          this may look like a limitation but the icons look better this way).
    - ... works with any horizontal/vertical icons
          (this is required because addition of the deskbar mode)
    - Icon sizes are slightly smaller than the row size to match icon sizes
      in "small" plugins. This is only an approximation and is not accurate
      across all gtk styles and icon sizes.
    - Scaling down the row size by 1px when the icons don't fit was removed.
      Icons should now fit unless some complex conflicting non-square icons are
      used.
    
    Limitations:
    - Non-square icons (tested with "workrave") sometimes want
      multi-row _and_ multi-column allocation.
      This is not supported (it would complicate the layout significantly
      for little benefit).
      If this happens, one dimension of the icon will be clipped
      to the row size. Workaround: use a larger row size.

 plugins/systray/systray-box.c |  300 +++++++++++++++++++++++------------------
 plugins/systray/systray.c     |   30 +++--
 2 files changed, 187 insertions(+), 143 deletions(-)

diff --git a/plugins/systray/systray-box.c b/plugins/systray/systray-box.c
index 827a1e1..13ab3c8 100644
--- a/plugins/systray/systray-box.c
+++ b/plugins/systray/systray-box.c
@@ -204,12 +204,13 @@ systray_box_size_get_max_child_size (SystrayBox *box,
                                      gint        alloc_size,
                                      gint       *rows_ret,
                                      gint       *row_size_ret,
-                                     gint       *offset_ret)
+                                     gint       *icon_size_ret)
 {
   GtkWidget *widget = GTK_WIDGET (box);
   gint       size;
   gint       rows;
   gint       row_size;
+  gint       icon_size;
 
   alloc_size -= 2 * GTK_CONTAINER (widget)->border_width;
 
@@ -230,7 +231,10 @@ systray_box_size_get_max_child_size (SystrayBox *box,
   rows = MAX (rows, box->nrows);
 
   row_size = (alloc_size - (rows - 1) * SPACING) / rows;
-  row_size = MIN (box->size_max, row_size);
+
+  /* limiting the icon size to the row_size */
+  /* adding an arbitrary padding added to mimic the size of other icons in the panel */
+  icon_size = MIN (box->size_max, row_size - 2);
 
   if (rows_ret != NULL)
     *rows_ret = rows;
@@ -238,13 +242,34 @@ systray_box_size_get_max_child_size (SystrayBox *box,
   if (row_size_ret != NULL)
     *row_size_ret = row_size;
 
-  if (offset_ret != NULL)
-    {
-      rows = MIN (rows, box->n_visible_children);
-      *offset_ret = (alloc_size - (rows * row_size + (rows - 1) * SPACING)) / 2;
-      if (*offset_ret < 1)
-        *offset_ret = 0;
-    }
+  if (icon_size_ret != NULL)
+    *icon_size_ret = icon_size;
+}
+
+
+
+static gint
+systray_box_calc_seq_cells (gint     row_size,
+                            gint     icon_size,
+                            gdouble  aspect,
+                            gint     limit,
+                            gint    *longer_edge_ret)
+{
+  gint            longer_edge;
+  gint            seq_cells_1;
+
+  /* assume aspect > 1 */
+  longer_edge = rint (icon_size * aspect);
+  seq_cells_1 =
+    rint (ceil ((gdouble) (longer_edge - icon_size - SPACING) / (gdouble) row_size));
+
+  if (limit > 0)
+    seq_cells_1 = MIN (seq_cells_1, limit - 1);
+
+  if (longer_edge_ret != NULL)
+    *longer_edge_ret = seq_cells_1 * (row_size + SPACING) + icon_size;
+
+  return seq_cells_1 + 1;
 }
 
 
@@ -259,22 +284,26 @@ systray_box_size_request (GtkWidget      *widget,
   GtkRequisition  child_req;
   gint            n_hidden_childeren = 0;
   gint            rows;
-  gdouble         cols;
+  gint            cols;
   gint            row_size;
-  gdouble         cells;
+  gint            icon_size;
+  gint            cells;
   gint            min_seq_cells = -1;
+  gint            seq_cells;
+  gint            limit;
   gdouble         ratio;
   GSList         *li;
   gboolean        hidden;
+  gboolean        perpendicular;
   gint            col_px;
   gint            row_px;
 
   box->n_visible_children = 0;
 
   /* get some info about the n_rows we're going to allocate */
-  systray_box_size_get_max_child_size (box, box->size_alloc, &rows, &row_size, NULL);
+  systray_box_size_get_max_child_size (box, box->size_alloc, &rows, &row_size, &icon_size);
 
-  for (li = box->childeren, cells = 0.00; li != NULL; li = li->next)
+  for (li = box->childeren, cells = 0; li != NULL; li = li->next)
     {
       child = GTK_WIDGET (li->data);
       panel_return_if_fail (XFCE_IS_SYSTRAY_SOCKET (child));
@@ -299,44 +328,46 @@ systray_box_size_request (GtkWidget      *widget,
           if (G_UNLIKELY (child_req.width != child_req.height))
             {
               ratio = (gdouble) child_req.width / (gdouble) child_req.height;
-              if (!box->horizontal)
-                ratio = 1 / ratio;
+              perpendicular =
+                (ratio > 1.00 &&  box->horizontal) ||
+                (ratio < 1.00 && !box->horizontal);
 
-              if (ratio > 1.00)
-                {
-                  if (G_UNLIKELY (rows > 1))
-                    {
-                      /* align to whole blocks if we have multiple rows */
-                      ratio = ceil (ratio);
+              if (perpendicular)
+                limit = 0;
+              else
+                limit = rows;
 
-                      /* update the min sequential number of blocks */
-                      min_seq_cells = MAX (min_seq_cells, ratio);
-                    }
+              if (ratio < 1.00)
+                ratio = 1.0 / ratio;
 
-                  cells += ratio;
+              /* align to whole blocks */
+              seq_cells = systray_box_calc_seq_cells (row_size, icon_size, ratio, limit, NULL);
 
-                  continue;
-                }
+              /* update the min sequential number of blocks if perpendicular to the row */
+              if (perpendicular)
+                min_seq_cells = MAX (min_seq_cells, seq_cells);
+
+              cells += seq_cells;
+
+              continue;
             }
 
           /* don't do anything with the actual size,
            * just count the number of cells */
-          cells += 1.00;
+          cells += 1;
           box->n_visible_children++;
         }
     }
 
   panel_debug_filtered (PANEL_DEBUG_SYSTRAY,
-      "requested cells=%g, rows=%d, row_size=%d, children=%d",
+      "requested cells=%d, rows=%d, row_size=%d, children=%d",
       cells, rows, row_size, box->n_visible_children);
 
-  if (cells > 0.00)
+  if (cells > 0)
     {
-      cols = cells / (gdouble) rows;
-      if (rows > 1)
-        cols = ceil (cols);
-      if (cols * rows < cells)
-        cols += 1.00;
+      cols = cells / rows;
+      if (rows > 1 && cells % rows > 0)
+        cols += 1;
 
       /* make sure we have enough columns to fix the minimum amount of cells */
       if (min_seq_cells != -1)
@@ -390,12 +421,16 @@ systray_box_size_allocate (GtkWidget     *widget,
   GtkAllocation   child_alloc;
   GtkRequisition  child_req;
   gint            border;
-  gint            rows;
+  gint            cols, rows;
   gint            row_size;
+  gint            icon_size;
+  gint            seq_cells;
+  gint            slot, col, row;
+  GSList         *occupied_slots = NULL;
+  gint            limit;
   gdouble         ratio;
-  gint            x, x_start, x_end;
-  gint            y, y_start, y_end;
-  gint            offset = 0;
+  gboolean        perpendicular;
+  gint            x_start, y_start;
   GSList         *li;
   gint            alloc_size;
   gint            idx;
@@ -406,29 +441,47 @@ systray_box_size_allocate (GtkWidget     *widget,
 
   alloc_size = box->horizontal ? allocation->height : allocation->width;
 
-  systray_box_size_get_max_child_size (box, alloc_size, &rows, &row_size, NULL);
+  systray_box_size_get_max_child_size (box, alloc_size, &rows, &row_size, &icon_size);
+
+  alloc_size = (box->horizontal ? allocation->width : allocation->height) - 2 * border;
+  cols = ceil ((gdouble) (alloc_size - row_size) / (gdouble) (row_size + SPACING)) + 1;
 
   panel_debug_filtered (PANEL_DEBUG_SYSTRAY, "allocate rows=%d, row_size=%d, w=%d, h=%d, horiz=%s, border=%d",
                         rows, row_size, allocation->width, allocation->height,
                         PANEL_DEBUG_BOOL (box->horizontal), border);
 
+
+  /* move all non-square icons to the end of the list */
+  /* they will be later moved to backwards in order */
+  /* to find the most suitable slot */
+  for (li = box->childeren; li != NULL; li = li->next)
+    {
+      child = GTK_WIDGET (li->data);
+      panel_return_if_fail (XFCE_IS_SYSTRAY_SOCKET (child));
+
+      if (GTK_WIDGET_VISIBLE (child))
+        {
+          gtk_widget_get_child_requisition (child, &child_req);
+
+          if (!REQUISITION_IS_INVISIBLE (child_req) &&
+              G_UNLIKELY (child_req.width != child_req.height) &&
+              li->next != NULL)
+            {
+              box->childeren = g_slist_delete_link (box->childeren, li);
+              box->childeren = g_slist_append (box->childeren, child);
+            }
+        }
+    }
+
+
   /* get allocation bounds */
   x_start = allocation->x + border;
-  x_end = allocation->x + allocation->width - border;
-
   y_start = allocation->y + border;
-  y_end = allocation->y + allocation->height - border;
-
-  /* add offset to center the tray contents */
-  if (box->horizontal)
-    y_start += offset;
-  else
-    x_start += offset;
 
   restart_allocation:
 
-  x = x_start;
-  y = y_start;
+  slot = 0;
+  g_slist_free (occupied_slots);
 
   for (li = box->childeren; li != NULL; li = li->next)
     {
@@ -438,6 +491,14 @@ systray_box_size_allocate (GtkWidget     *widget,
       if (!GTK_WIDGET_VISIBLE (child))
         continue;
 
+      /* Check if the current slot is occupied by earlier non-square icons. */
+      /* If so, find the next free slot. */
+      while (g_slist_index (occupied_slots, GINT_TO_POINTER (slot)) != -1)
+        slot += 1;
+
+      row = slot % rows;
+      col = slot / rows;
+
       gtk_widget_get_child_requisition (child, &child_req);
 
       if (REQUISITION_IS_INVISIBLE (child_req)
@@ -451,7 +512,7 @@ systray_box_size_allocate (GtkWidget     *widget,
           /* some implementations (hi nm-applet) start their setup on
            * a size-changed signal, so make sure this event is triggered
            * by allocation a normal size instead of 1x1 */
-          child_alloc.width = child_alloc.height = row_size;
+          child_alloc.width = child_alloc.height = icon_size;
         }
       else
         {
@@ -459,106 +520,77 @@ systray_box_size_allocate (GtkWidget     *widget,
           if (G_UNLIKELY (child_req.width != child_req.height))
             {
               ratio = (gdouble) child_req.width / (gdouble) child_req.height;
+              perpendicular =
+                (ratio > 1.00 &&  box->horizontal) ||
+                (ratio < 1.00 && !box->horizontal);
 
-              if (!box->horizontal)
-                {
-                  child_alloc.height = row_size;
-                  child_alloc.width = row_size * ratio;
-                  child_alloc.y = child_alloc.x = 0;
-
-                  if (rows > 1)
-                    {
-                      ratio = ceil (ratio);
-                      child_alloc.x = ((ratio * row_size) - child_alloc.width) / 2;
-                    }
-                }
+              if (perpendicular)
+                limit = 0;
               else
-                {
-                  ratio = 1 / ratio;
-
-                  child_alloc.width = row_size;
-                  child_alloc.height = row_size * ratio;
-                  child_alloc.x = child_alloc.y = 0;
+                limit = rows;
 
-                  if (rows > 1)
-                    {
-                      ratio = ceil (ratio);
-                      child_alloc.y = ((ratio * row_size) - child_alloc.height) / 2;
-                    }
+              if (ratio > 1.0)
+                {
+                  seq_cells = systray_box_calc_seq_cells (row_size, icon_size, ratio, limit, &child_alloc.width);
+                  child_alloc.height = icon_size;
                 }
-            }
-          else
-            {
-              /* fix icon to row size */
-              child_alloc.width = row_size;
-              child_alloc.height = row_size;
-              child_alloc.x = 0;
-              child_alloc.y = 0;
-
-              ratio = 1.00;
-            }
-
-          if ((!box->horizontal && x + child_alloc.width > x_end)
-              || (box->horizontal && y + child_alloc.height > y_end))
-            {
-              if (ratio >= 2
-                  && li->next != NULL)
+              else
                 {
-                  /* child doesn't fit, but maybe we still have space for the
-                   * next icon, so move the child 1 step forward in the list
-                   * and restart allocating the box */
-                  idx = g_slist_position (box->childeren, li);
-                  box->childeren = g_slist_delete_link (box->childeren, li);
-                  box->childeren = g_slist_insert (box->childeren, child, idx + 1);
-
-                  goto restart_allocation;
+                  seq_cells = systray_box_calc_seq_cells (row_size, icon_size, 1.0 / ratio, limit, &child_alloc.height);
+                  child_alloc.width = icon_size;
                 }
+              child_alloc.x = child_alloc.y = ceil ((row_size - icon_size) / 2.0);
 
-              if (!box->horizontal)
+              /* check if non-square icon fits in the row/column */
+              /* if not, move it one step backwar in the list */
+              /* and restart the allocation */
+              if ((perpendicular && col + seq_cells > cols) ||
+                  (!perpendicular && row + seq_cells > rows))
                 {
-                  x = x_start;
-                  y += row_size + SPACING;
-
-                  if (y > y_end)
+                  if ((idx = g_slist_position (box->childeren, li)) > 0)
                     {
-                      /* we overflow the number of rows, restart
-                       * allocation with 1px smaller icons */
-                      row_size--;
-
-                      panel_debug_filtered (PANEL_DEBUG_SYSTRAY,
-                          "y overflow (%d > %d), restart with row_size=%d",
-                          y, y_end, row_size);
+                      box->childeren = g_slist_delete_link (box->childeren, li);
+                      box->childeren = g_slist_insert (box->childeren, child, idx - 1);
 
                       goto restart_allocation;
                     }
                 }
-              else
-                {
-                  y = y_start;
-                  x += row_size + SPACING;
-
-                  if (x > x_end)
-                    {
-                      /* we overflow the number of rows, restart
-                       * allocation with 1px smaller icons */
-                      row_size--;
 
-                      panel_debug_filtered (PANEL_DEBUG_SYSTRAY,
-                          "x overflow (%d > %d), restart with row_size=%d",
-                          x, x_end, row_size);
-
-                      goto restart_allocation;
-                    }
+              /* Mark slots as occupied so that other icons are not allocated */
+              /* on top of this non-square icon. */
+              /* Only needed for perpendicular icons, */
+              /* for non-perpendicular icons seq_cells is enough. */
+              if (perpendicular)
+                {
+                  for (idx = 1; idx <= seq_cells; idx++)
+                    occupied_slots =
+                      g_slist_prepend (occupied_slots, GINT_TO_POINTER (slot + idx * rows));
                 }
             }
+          else
+            {
+              /* fix size to icon size and center it in the row */
+              child_alloc.width = child_alloc.height = icon_size;
+              child_alloc.x = child_alloc.y = ceil ((row_size - icon_size) / 2.0);
 
-          child_alloc.x += x;
-          child_alloc.y += y;
+              seq_cells = 1;
+            }
 
-          if (!box->horizontal)
-            x += row_size * ratio + SPACING;
+          if (box->horizontal)
+            {
+              child_alloc.x += x_start + (row_size + SPACING) * col;
+              child_alloc.y += y_start + (row_size + SPACING) * row;
+            }
           else
-            y += row_size * ratio + SPACING;
+            {
+              child_alloc.x += x_start + (row_size + SPACING) * row;
+              child_alloc.y += y_start + (row_size + SPACING) * col;
+            }
+
+          if (seq_cells > 1 && perpendicular)
+            seq_cells = 1;
+
+          slot += seq_cells;
         }
 
       panel_debug_filtered (PANEL_DEBUG_SYSTRAY, "allocated %s[%p] at (%d,%d;%d,%d)",
@@ -567,6 +599,8 @@ systray_box_size_allocate (GtkWidget     *widget,
 
       gtk_widget_size_allocate (child, &child_alloc);
     }
+
+  g_slist_free (occupied_slots);
 }
 
 
diff --git a/plugins/systray/systray.c b/plugins/systray/systray.c
index 975cc3f..2616f57 100644
--- a/plugins/systray/systray.c
+++ b/plugins/systray/systray.c
@@ -50,8 +50,8 @@ static void     systray_plugin_set_property                 (GObject
                                                              GParamSpec            *pspec);
 static void     systray_plugin_construct                    (XfcePanelPlugin       *panel_plugin);
 static void     systray_plugin_free_data                    (XfcePanelPlugin       *panel_plugin);
-static void     systray_plugin_orientation_changed          (XfcePanelPlugin       *panel_plugin,
-                                                             GtkOrientation         orientation);
+static void     systray_plugin_mode_changed                 (XfcePanelPlugin       *panel_plugin,
+                                                             XfcePanelPluginMode    mode);
 static gboolean systray_plugin_size_changed                 (XfcePanelPlugin       *panel_plugin,
                                                              gint                   size);
 static void     systray_plugin_nrows_changed                (XfcePanelPlugin       *panel_plugin,
@@ -176,7 +176,7 @@ systray_plugin_class_init (SystrayPluginClass *klass)
   plugin_class->size_changed = systray_plugin_size_changed;
   plugin_class->nrows_changed = systray_plugin_nrows_changed;
   plugin_class->configure_plugin = systray_plugin_configure_plugin;
-  plugin_class->orientation_changed = systray_plugin_orientation_changed;
+  plugin_class->mode_changed = systray_plugin_mode_changed;
 
   g_object_class_install_property (gobject_class,
                                    PROP_SIZE_MAX,
@@ -393,8 +393,8 @@ systray_plugin_screen_changed_idle (gpointer user_data)
   if (systray_manager_register (plugin->manager, screen, &error))
     {
       /* send the plugin orientation */
-      systray_plugin_orientation_changed (XFCE_PANEL_PLUGIN (plugin),
-         xfce_panel_plugin_get_orientation (XFCE_PANEL_PLUGIN (plugin)));
+      systray_plugin_mode_changed (XFCE_PANEL_PLUGIN (plugin),
+         xfce_panel_plugin_get_mode (XFCE_PANEL_PLUGIN (plugin)));
     }
   else
     {
@@ -503,18 +503,28 @@ systray_plugin_free_data (XfcePanelPlugin *panel_plugin)
 
 
 static void
-systray_plugin_orientation_changed (XfcePanelPlugin *panel_plugin,
-                                    GtkOrientation   orientation)
+systray_plugin_mode_changed (XfcePanelPlugin    *panel_plugin,
+                             XfcePanelPluginMode mode)
 {
   SystrayPlugin *plugin = XFCE_SYSTRAY_PLUGIN (panel_plugin);
+  GtkOrientation panel_orientation;
+  GtkOrientation orientation;
+
+  orientation =
+    (mode != XFCE_PANEL_PLUGIN_MODE_VERTICAL) ?
+    GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
 
-  xfce_hvbox_set_orientation (XFCE_HVBOX (plugin->hvbox), orientation);
-  systray_box_set_orientation (XFCE_SYSTRAY_BOX (plugin->box), orientation);
+  panel_orientation =
+    (mode == XFCE_PANEL_PLUGIN_MODE_HORIZONTAL) ?
+    GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
+
+  xfce_hvbox_set_orientation (XFCE_HVBOX (plugin->hvbox), panel_orientation);
+  systray_box_set_orientation (XFCE_SYSTRAY_BOX (plugin->box), panel_orientation);
 
   if (G_LIKELY (plugin->manager != NULL))
     systray_manager_set_orientation (plugin->manager, orientation);
 
-  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+  if (panel_orientation == GTK_ORIENTATION_HORIZONTAL)
     gtk_widget_set_size_request (plugin->button, BUTTON_SIZE, -1);
   else
     gtk_widget_set_size_request (plugin->button, -1, BUTTON_SIZE);


More information about the Xfce4-commits mailing list