[egg-list-box/row-widget: 5/7] listbox: Move state changes and drawing to Row



commit 25bd7cc274b975b683cc808be5e818960dd6cffe
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Jun 7 15:21:01 2013 +0200

    listbox: Move state changes and drawing to Row
    
    Also we add support for borders.

 egg-list-box.c |  344 +++++++++++++++++++++++++++++++++++++-------------------
 test-list.vala |    9 +-
 2 files changed, 233 insertions(+), 120 deletions(-)
---
diff --git a/egg-list-box.c b/egg-list-box.c
index d2504be..e85e852 100644
--- a/egg-list-box.c
+++ b/egg-list-box.c
@@ -818,7 +818,14 @@ egg_list_box_update_selected (EggListBox *list_box,
   if (row != priv->selected_row &&
       (row == NULL || priv->selection_mode != GTK_SELECTION_NONE))
     {
+      if (priv->selected_row)
+        gtk_widget_unset_state_flags (GTK_WIDGET (priv->selected_row),
+                                      GTK_STATE_FLAG_SELECTED);
       priv->selected_row = row;
+      if (priv->selected_row)
+        gtk_widget_set_state_flags (GTK_WIDGET (priv->selected_row),
+                                    GTK_STATE_FLAG_SELECTED,
+                                    FALSE);
       g_signal_emit (list_box, signals[ROW_SELECTED], 0,
                     priv->selected_row);
       gtk_widget_queue_draw (GTK_WIDGET (list_box));
@@ -844,7 +851,14 @@ egg_list_box_update_prelight (EggListBox *list_box, EggListBoxRow *row)
 
   if (row != priv->prelight_row)
     {
+      if (priv->prelight_row)
+        gtk_widget_unset_state_flags (GTK_WIDGET (priv->prelight_row),
+                                      GTK_STATE_FLAG_PRELIGHT);
       priv->prelight_row = row;
+      if (priv->prelight_row)
+        gtk_widget_set_state_flags (GTK_WIDGET (priv->prelight_row),
+                                    GTK_STATE_FLAG_PRELIGHT,
+                                    FALSE);
       gtk_widget_queue_draw (GTK_WIDGET (list_box));
     }
 }
@@ -860,6 +874,13 @@ egg_list_box_update_active (EggListBox *list_box, EggListBoxRow *row)
       val != priv->active_row_active)
     {
       priv->active_row_active = val;
+      if (priv->active_row_active)
+        gtk_widget_set_state_flags (GTK_WIDGET (priv->active_row),
+                                    GTK_STATE_FLAG_ACTIVE,
+                                    FALSE);
+      else
+        gtk_widget_unset_state_flags (GTK_WIDGET (priv->active_row),
+                                      GTK_STATE_FLAG_ACTIVE);
       gtk_widget_queue_draw (GTK_WIDGET (list_box));
     }
 }
@@ -943,19 +964,22 @@ egg_list_box_real_button_press_event (GtkWidget *widget,
       EggListBoxRow *row;
       row = egg_list_box_get_row_at_y (list_box, event->y);
       if (row != NULL)
-       {
-         priv->active_row = row;
-         priv->active_row_active = TRUE;
-         gtk_widget_queue_draw (GTK_WIDGET (list_box));
-         if (event->type == GDK_2BUTTON_PRESS &&
-             !priv->activate_single_click)
-           g_signal_emit (list_box, signals[ROW_ACTIVATED], 0,
-                          row);
+        {
+          priv->active_row = row;
+          priv->active_row_active = TRUE;
+          gtk_widget_set_state_flags (GTK_WIDGET (priv->active_row),
+                                      GTK_STATE_FLAG_ACTIVE,
+                                      FALSE);
+          gtk_widget_queue_draw (GTK_WIDGET (list_box));
+          if (event->type == GDK_2BUTTON_PRESS &&
+              !priv->activate_single_click)
+            g_signal_emit (list_box, signals[ROW_ACTIVATED], 0,
+                           row);
 
-       }
+        }
       /* TODO:
-        Should mark as active while down,
-        and handle grab breaks */
+         Should mark as active while down,
+         and handle grab breaks */
     }
 
   return FALSE;
@@ -978,6 +1002,9 @@ egg_list_box_real_button_release_event (GtkWidget *widget,
           else
             egg_list_box_update_selected (list_box, priv->active_row);
         }
+      if (priv->active_row_active)
+        gtk_widget_unset_state_flags (GTK_WIDGET (priv->active_row),
+                                      GTK_STATE_FLAG_ACTIVE);
       priv->active_row = NULL;
       priv->active_row_active = FALSE;
       gtk_widget_queue_draw (GTK_WIDGET (list_box));
@@ -1133,91 +1160,21 @@ egg_list_box_real_focus (GtkWidget* widget, GtkDirectionType direction)
   return TRUE;
 }
 
-typedef struct {
-  EggListBoxRow *row;
-  GtkStateFlags state;
-} RowFlags;
-
-static RowFlags*
-row_flags_find_or_add (RowFlags *array,
-                       int *array_length,
-                       EggListBoxRow *to_find)
-{
-  gint i;
-
-  for (i = 0; i < *array_length; i++)
-    {
-      if (array[i].row == to_find)
-       return &array[i];
-    }
-
-  *array_length = *array_length + 1;
-  array[*array_length - 1].row = to_find;
-  array[*array_length - 1].state = 0;
-  return &array[*array_length - 1];
-}
-
 static gboolean
 egg_list_box_real_draw (GtkWidget* widget, cairo_t* cr)
 {
-  EggListBox * list_box = EGG_LIST_BOX (widget);
-  EggListBoxPrivate *priv = list_box->priv;
-  GtkAllocation allocation = {0};
-  GtkStyleContext* context;
-  GtkStateFlags state;
-  RowFlags flags[3], *found;
-  gint flags_length;
-  gint focus_pad;
-  int i;
-
-  gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
-  context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
-  state = gtk_widget_get_state_flags (widget);
-  gtk_render_background (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) 
allocation.height);
-  flags_length = 0;
-
-  if (priv->selected_row != NULL)
-    {
-      found = row_flags_find_or_add (flags, &flags_length, priv->selected_row);
-      found->state |= (state | GTK_STATE_FLAG_SELECTED);
-    }
-
-  if (priv->prelight_row != NULL)
-    {
-      found = row_flags_find_or_add (flags, &flags_length, priv->prelight_row);
-      found->state |= (state | GTK_STATE_FLAG_PRELIGHT);
-    }
-
-  if (priv->active_row != NULL && priv->active_row_active)
-    {
-      found = row_flags_find_or_add (flags, &flags_length, priv->active_row);
-      found->state |= (state | GTK_STATE_FLAG_ACTIVE);
-    }
-
-  for (i = 0; i < flags_length; i++)
-    {
-      RowFlags *flag = &flags[i];
-      gtk_style_context_save (context);
-      gtk_style_context_set_state (context, flag->state);
-      gtk_render_background (context, cr, 0, flag->row->priv->y, allocation.width, flag->row->priv->height);
-      gtk_style_context_restore (context);
-    }
+  GtkAllocation allocation;
+  GtkStyleContext *context;
 
-  if (gtk_widget_has_visible_focus (GTK_WIDGET (list_box)) && priv->cursor_row != NULL)
-    {
-      gtk_style_context_get_style (context,
-                                   "focus-padding", &focus_pad,
-                                   NULL);
-      gtk_render_focus (context, cr, focus_pad, priv->cursor_row->priv->y + focus_pad,
-                        allocation.width - 2 * focus_pad, priv->cursor_row->priv->height - 2 * focus_pad);
-    }
+  gtk_widget_get_allocation (widget, &allocation);
+  context = gtk_widget_get_style_context (widget);
+  gtk_render_background (context, cr, 0, 0, allocation.width, allocation.height);
 
-  GTK_WIDGET_CLASS (egg_list_box_parent_class)->draw ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (list_box, 
GTK_TYPE_CONTAINER, GtkContainer), cr);
+  GTK_WIDGET_CLASS (egg_list_box_parent_class)->draw (widget, cr);
 
   return TRUE;
 }
 
-
 static void
 egg_list_box_real_realize (GtkWidget* widget)
 {
@@ -1521,12 +1478,19 @@ egg_list_box_real_remove (GtkContainer* container, GtkWidget* child)
 
   if (row == priv->selected_row)
       egg_list_box_update_selected (list_box, NULL);
-  if (row == priv->prelight_row)
+  if (row == priv->prelight_row) {
+    gtk_widget_unset_state_flags (GTK_WIDGET (priv->prelight_row),
+                                  GTK_STATE_FLAG_PRELIGHT);
     priv->prelight_row = NULL;
+  }
   if (row == priv->cursor_row)
     priv->cursor_row = NULL;
-  if (row == priv->active_row)
+  if (row == priv->active_row) {
+    if (priv->active_row_active)
+      gtk_widget_unset_state_flags (GTK_WIDGET (priv->active_row),
+                                    GTK_STATE_FLAG_ACTIVE);
     priv->active_row = NULL;
+  }
 
   next = egg_list_box_get_next_visible (list_box, row->priv->iter);
   gtk_widget_unparent (child);
@@ -1609,17 +1573,9 @@ egg_list_box_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
   GSequenceIter *iter;
   gint minimum_height;
   gint natural_height;
-  GtkStyleContext *context;
-  gint focus_width;
-  gint focus_pad;
 
   minimum_height = 0;
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
-  gtk_style_context_get_style (context,
-                              "focus-line-width", &focus_width,
-                              "focus-padding", &focus_pad, NULL);
-
   for (iter = g_sequence_get_begin_iter (priv->children);
        !g_sequence_iter_is_end (iter);
        iter = g_sequence_iter_next (iter))
@@ -1636,9 +1592,9 @@ egg_list_box_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
          gtk_widget_get_preferred_height_for_width (row->priv->separator, width, &row_min, NULL);
          minimum_height += row_min;
        }
-      gtk_widget_get_preferred_height_for_width (GTK_WIDGET (row), width - 2 * (focus_width + focus_pad),
+      gtk_widget_get_preferred_height_for_width (GTK_WIDGET (row), width,
                                                 &row_min, NULL);
-      minimum_height += row_min + 2 * (focus_width + focus_pad);
+      minimum_height += row_min;
     }
 
   /* We always allocate the minimum height, since handling
@@ -1660,17 +1616,11 @@ egg_list_box_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_ou
   EggListBoxPrivate *priv = list_box->priv;
   gint minimum_width;
   gint natural_width;
-  GtkStyleContext *context;
-  gint focus_width;
-  gint focus_pad;
   GSequenceIter *iter;
   EggListBoxRow *row;
   gint row_min;
   gint row_nat;
 
-  context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
-  gtk_style_context_get_style (context, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL);
-
   minimum_width = 0;
   natural_width = 0;
 
@@ -1683,8 +1633,8 @@ egg_list_box_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_ou
         continue;
 
       gtk_widget_get_preferred_width (GTK_WIDGET (row), &row_min, &row_nat);
-      minimum_width = MAX (minimum_width, row_min + 2 * (focus_width + focus_pad));
-      natural_width = MAX (natural_width, row_nat + 2 * (focus_width + focus_pad));
+      minimum_width = MAX (minimum_width, row_min);
+      natural_width = MAX (natural_width, row_nat);
 
       if (row->priv->separator != NULL)
         {
@@ -1720,8 +1670,6 @@ egg_list_box_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
   GtkWidget *child;
   GSequenceIter *iter;
   GtkStyleContext *context;
-  gint focus_width;
-  gint focus_pad;
   int child_min;
 
 
@@ -1743,13 +1691,9 @@ egg_list_box_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
                            allocation->width, allocation->height);
 
   context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
-  gtk_style_context_get_style (context,
-                              "focus-line-width", &focus_width,
-                              "focus-padding", &focus_pad,
-                              NULL);
-  child_allocation.x = 0 + focus_width + focus_pad;
+  child_allocation.x = 0;
   child_allocation.y = 0;
-  child_allocation.width = allocation->width - 2 * (focus_width + focus_pad);
+  child_allocation.width = allocation->width;
   separator_allocation.x = 0;
   separator_allocation.width = allocation->width;
 
@@ -1777,15 +1721,14 @@ egg_list_box_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
        }
 
       row->priv->y = child_allocation.y;
-      child_allocation.y += focus_width + focus_pad;
 
       gtk_widget_get_preferred_height_for_width (GTK_WIDGET (row), child_allocation.width, &child_min, NULL);
       child_allocation.height = child_min;
 
-      row->priv->height = child_allocation.height + 2 * (focus_width + focus_pad);
+      row->priv->height = child_allocation.height;
       gtk_widget_size_allocate (GTK_WIDGET (row), &child_allocation);
 
-      child_allocation.y += child_min + focus_width + focus_pad;
+      child_allocation.y += child_min;
     }
 }
 
@@ -2157,6 +2100,165 @@ egg_list_box_row_real_hide (GtkWidget *widget)
     egg_list_box_row_visibility_changed (list_box, row);
 }
 
+static gboolean
+egg_list_box_row_real_draw (GtkWidget* widget, cairo_t* cr)
+{
+  EggListBoxRow *row = EGG_LIST_BOX_ROW (widget);
+  EggListBox *list_box;
+  GtkAllocation allocation = {0};
+  GtkStyleContext* context;
+  GtkStateFlags state;
+  GtkBorder border;
+  gint focus_pad;
+  int i;
+
+  gtk_widget_get_allocation (widget, &allocation);
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
+
+  gtk_render_background (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) 
allocation.height);
+  gtk_render_frame (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) 
allocation.height);
+
+  list_box = egg_list_box_row_get_box (row);
+  if (list_box && gtk_widget_has_visible_focus (GTK_WIDGET (list_box)) &&
+      row == list_box->priv->cursor_row)
+    {
+      gtk_style_context_get_border (context, state, &border);
+
+      gtk_style_context_get_style (context,
+                                   "focus-padding", &focus_pad,
+                                   NULL);
+      gtk_render_focus (context, cr, border.left + focus_pad, border.top + focus_pad,
+                        allocation.width - 2 * focus_pad - border.left - border.right,
+                        allocation.height - 2 * focus_pad - border.top - border.bottom);
+    }
+
+  GTK_WIDGET_CLASS (egg_list_box_row_parent_class)->draw (widget, cr);
+
+  return TRUE;
+}
+
+static void
+egg_list_box_row_get_full_border (EggListBoxRow *row,
+                                  GtkBorder *full_border)
+{
+  GtkWidget *widget = GTK_WIDGET (row);
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  GtkBorder default_border, padding, border;
+  int focus_width, focus_pad;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_style_context_get_state (context);
+
+  gtk_style_context_get_padding (context, state, &padding);
+  gtk_style_context_get_border (context, state, &border);
+  gtk_style_context_get_style (context,
+                               "focus-line-width", &focus_width,
+                               "focus-padding", &focus_pad,
+                               NULL);
+
+  full_border->left = padding.left + border.left + focus_width + focus_pad;
+  full_border->right = padding.right + border.right + focus_width + focus_pad;
+  full_border->top = padding.top + border.top + focus_width + focus_pad;
+  full_border->bottom = padding.bottom + border.bottom + focus_width + focus_pad;
+}
+
+static void egg_list_box_row_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
+                                                                  gint* minimum_height_out, gint* 
natural_height_out);
+static void egg_list_box_row_real_get_preferred_width (GtkWidget* widget,
+                                                       gint* minimum_width_out, gint* natural_width_out);
+
+static void
+egg_list_box_row_real_get_preferred_height (GtkWidget* widget,
+                                            gint* minimum_height,
+                                            gint* natural_height)
+{
+  gint natural_width;
+  egg_list_box_row_real_get_preferred_width (widget, NULL, &natural_width);
+  egg_list_box_row_real_get_preferred_height_for_width (widget, natural_width,
+                                                        minimum_height, natural_height);
+}
+
+static void
+egg_list_box_row_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
+                                                      gint* minimum_height_out, gint* natural_height_out)
+{
+  EggListBoxRow *row = EGG_LIST_BOX_ROW (widget);
+  GtkWidget *child;
+  gint child_min = 0, child_natural = 0;
+  GtkBorder full_border;
+
+  egg_list_box_row_get_full_border (row, &full_border);
+
+  child = gtk_bin_get_child (GTK_BIN (row));
+  if (child && gtk_widget_get_visible (child))
+      gtk_widget_get_preferred_height_for_width (child, width - full_border.left - full_border.right,
+                                                 &child_min, &child_natural);
+
+  if (minimum_height_out)
+    *minimum_height_out = full_border.top + child_min + full_border.bottom;
+  if (natural_height_out)
+    *natural_height_out = full_border.top + child_natural + full_border.bottom;
+}
+
+static void
+egg_list_box_row_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_out, gint* 
natural_width_out)
+{
+  EggListBoxRow *row = EGG_LIST_BOX_ROW (widget);
+  GtkWidget *child;
+  gint child_min = 0, child_natural = 0;
+  GtkBorder full_border;
+
+  egg_list_box_row_get_full_border (row, &full_border);
+
+  child = gtk_bin_get_child (GTK_BIN (row));
+  if (child && gtk_widget_get_visible (child))
+      gtk_widget_get_preferred_width (child,
+                                      &child_min, &child_natural);
+
+  if (minimum_width_out)
+    *minimum_width_out = full_border.left + child_min + full_border.right;
+  if (natural_width_out)
+    *natural_width_out = full_border.left + child_natural + full_border.bottom;
+}
+
+static void
+egg_list_box_row_real_get_preferred_width_for_height (GtkWidget *widget, gint height,
+                                                      gint *minimum_width, gint *natural_width)
+{
+  egg_list_box_row_real_get_preferred_width (widget, minimum_width, natural_width);
+}
+
+static void
+egg_list_box_row_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+  EggListBoxRow *row = EGG_LIST_BOX_ROW (widget);
+  GtkWidget *child;
+
+  gtk_widget_set_allocation (widget, allocation);
+
+  child = gtk_bin_get_child (GTK_BIN (row));
+  if (child && gtk_widget_get_visible (child))
+    {
+      GtkAllocation child_allocation;
+      GtkBorder border;
+      gint baseline;
+
+      egg_list_box_row_get_full_border (row, &border);
+
+      child_allocation.x = allocation->x + border.left;
+      child_allocation.y = allocation->y + border.top;
+      child_allocation.width = allocation->width - border.left - border.right;
+      child_allocation.height = allocation->height - border.top - border.bottom;
+
+      child_allocation.width  = MAX (1, child_allocation.width);
+      child_allocation.height = MAX (1, child_allocation.height);
+
+      gtk_widget_size_allocate (child, &child_allocation);
+    }
+}
+
 void
 egg_list_box_row_changed (EggListBoxRow *row)
 {
@@ -2221,4 +2323,10 @@ egg_list_box_row_class_init (EggListBoxRowClass *klass)
 
   widget_class->show = egg_list_box_row_real_show;
   widget_class->hide = egg_list_box_row_real_hide;
+  widget_class->draw = egg_list_box_row_real_draw;
+  widget_class->get_preferred_height = egg_list_box_row_real_get_preferred_height;
+  widget_class->get_preferred_height_for_width = egg_list_box_row_real_get_preferred_height_for_width;
+  widget_class->get_preferred_width = egg_list_box_row_real_get_preferred_width;
+  widget_class->get_preferred_width_for_height = egg_list_box_row_real_get_preferred_width_for_height;
+  widget_class->size_allocate = egg_list_box_row_real_size_allocate;
 }
diff --git a/test-list.vala b/test-list.vala
index 2c8291f..b121822 100644
--- a/test-list.vala
+++ b/test-list.vala
@@ -208,10 +208,15 @@ main (string[] args) {
   var provider = new Gtk.CssProvider ();
   provider.load_from_data (
 """
-EggListBox:prelight {
+EggListBoxRow {
+ border-width: 1px;
+ border-style: solid;
+ border-color: blue;
+}
+EggListBoxRow:prelight {
 background-color: green;
 }
-EggListBox:active {
+EggListBoxRow:active {
 background-color: red;
 }
 """, -1);


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