[gtk+] label: Use css nodes for links



commit 3811eb4f32bbb719d15954f4a4c422e34001a91b
Author: Matthias Clasen <mclasen redhat com>
Date:   Sun Nov 15 01:00:20 2015 -0500

    label: Use css nodes for links
    
    Use a subnode with name link for links in labels. These subnodes
    carry the :link or :visited state.

 gtk/gtklabel.c |  104 ++++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 75 insertions(+), 29 deletions(-)
---
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index e0f564e..7da71bf 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -75,10 +75,24 @@
  *
  * # CSS nodes
  *
+ * |[<!-- language="plain" -->
+ * label
+ * ├── [selection]
+ * ├── [link]
+ * ┊
+ * ╰── [link]
+ * ]|
+ *
  * GtkLabel has a single CSS node with the name label. A wide variety
  * of style classes may be applied to labels, such as .title, .subtitle,
  * .dim-label, etc.
  *
+ * If the label has a selection, it gets a subnode with name selection.
+ *
+ * If the label has links, there is one subnode per link. These subnodes
+ * carry the link or visited state depending on whether they have been
+ * visited.
+ *
  * # GtkLabel as GtkBuildable
  *
  * The GtkLabel implementation of the GtkBuildable interface supports a
@@ -301,6 +315,9 @@ typedef struct
 {
   gchar *uri;
   gchar *title;     /* the title attribute, used as tooltip */
+
+  GtkCssNode *cssnode;
+
   gboolean visited; /* get set when the link is activated; this flag
                      * gets preserved over later set_markup() calls
                      */
@@ -2376,6 +2393,8 @@ start_element_handler (GMarkupParseContext  *context,
       gint line_number;
       gint char_number;
       gint i;
+      GtkCssNode *widget_node;
+      GtkStateFlags state;
 
       g_markup_parse_context_get_position (context, &line_number, &char_number);
 
@@ -2429,6 +2448,19 @@ start_element_handler (GMarkupParseContext  *context,
       link = g_new0 (GtkLabelLink, 1);
       link->uri = g_strdup (uri);
       link->title = g_strdup (title);
+
+      widget_node = gtk_widget_get_css_node (GTK_WIDGET (pdata->label));
+      link->cssnode = gtk_css_node_new ();
+      gtk_css_node_set_name (link->cssnode, I_("link"));
+      gtk_css_node_set_parent (link->cssnode, widget_node);
+      state = gtk_css_node_get_state (widget_node);
+      if (visited)
+        state |= GTK_STATE_FLAG_VISITED;
+      else
+        state |= GTK_STATE_FLAG_LINK;
+      gtk_css_node_set_state (link->cssnode, state);
+      g_object_unref (link->cssnode);
+
       link->visited = visited;
       link->start = pdata->text_len;
       pdata->links = g_list_prepend (pdata->links, link);
@@ -2515,6 +2547,7 @@ xml_isspace (gchar c)
 static void
 link_free (GtkLabelLink *link)
 {
+  gtk_css_node_set_parent (link->cssnode, NULL);
   g_free (link->uri);
   g_free (link->title);
   g_free (link);
@@ -3456,26 +3489,18 @@ gtk_label_update_layout_attributes (GtkLabel *label)
 
       attrs = pango_attr_list_new ();
 
-      gtk_style_context_save (context);
-
       for (list = priv->select_info->links; list; list = list->next)
         {
           GtkLabelLink *link = list->data;
-          GtkStateFlags state;
 
           attribute = pango_attr_underline_new (TRUE);
           attribute->start_index = link->start;
           attribute->end_index = link->end;
           pango_attr_list_insert (attrs, attribute);
 
-          state = gtk_widget_get_state_flags (widget);
-          if (link->visited)
-            state |= GTK_STATE_FLAG_VISITED;
-          else
-            state |= GTK_STATE_FLAG_LINK;
-
-          gtk_style_context_set_state (context, state);
-          gtk_style_context_get_color (context, state, &link_color);
+          gtk_style_context_save_to_node (context, link->cssnode);
+          gtk_style_context_get_color (context, gtk_style_context_get_state (context), &link_color);
+          gtk_style_context_restore (context);
 
           attribute = pango_attr_foreground_new (link_color.red * 65535,
                                                  link_color.green * 65535,
@@ -3484,8 +3509,6 @@ gtk_label_update_layout_attributes (GtkLabel *label)
           attribute->end_index = link->end;
           pango_attr_list_insert (attrs, attribute);
         }
-
-      gtk_style_context_restore (context);
     }
   else if (priv->markup_attrs && priv->attrs)
     attrs = pango_attr_list_new ();
@@ -4125,6 +4148,36 @@ gtk_label_update_cursor (GtkLabel *label)
 }
 
 static void
+update_link_state (GtkLabel *label)
+{
+  GtkLabelPrivate *priv = label->priv;
+  GList *l;
+  GtkStateFlags state;
+
+  if (!priv->select_info)
+    return;
+
+  for (l = priv->select_info->links; l; l = l->next)
+    {
+      GtkLabelLink *link = l->data;
+
+      state = gtk_widget_get_state_flags (GTK_WIDGET (label));
+      if (link->visited)
+        state |= GTK_STATE_FLAG_VISITED;
+      else
+        state |= GTK_STATE_FLAG_LINK;
+      if (link == priv->select_info->active_link)
+        {
+          if (priv->select_info->link_clicked)
+            state |= GTK_STATE_FLAG_ACTIVE;
+          else
+            state |= GTK_STATE_FLAG_PRELIGHT;
+        }
+      gtk_css_node_set_state (link->cssnode, state);
+    }
+}
+
+static void
 gtk_label_state_flags_changed (GtkWidget     *widget,
                                GtkStateFlags  prev_state)
 {
@@ -4137,6 +4190,7 @@ gtk_label_state_flags_changed (GtkWidget     *widget,
         gtk_label_select_region (label, 0, 0);
 
       gtk_label_update_cursor (label);
+      update_link_state (label);
     }
 
   if (GTK_WIDGET_CLASS (gtk_label_parent_class)->state_flags_changed)
@@ -4226,7 +4280,6 @@ gtk_label_draw (GtkWidget *widget,
   GtkLabelSelectionInfo *info = priv->select_info;
   GtkAllocation allocation;
   GtkStyleContext *context;
-  GtkStateFlags state;
   gint x, y;
 
   gtk_label_ensure_layout (label);
@@ -4315,7 +4368,7 @@ gtk_label_draw (GtkWidget *widget,
               range[1] = active_link->end;
 
               cairo_save (cr);
-              gtk_style_context_save (context);
+              gtk_style_context_save_to_node (context, active_link->cssnode);
 
               clip = gdk_pango_layout_get_clip_region (priv->layout,
                                                        x, y,
@@ -4326,20 +4379,6 @@ gtk_label_draw (GtkWidget *widget,
               cairo_clip (cr);
               cairo_region_destroy (clip);
 
-              state = gtk_style_context_get_state (context);
-
-              if (info->link_clicked)
-                state |= GTK_STATE_FLAG_ACTIVE;
-              else
-                state |= GTK_STATE_FLAG_PRELIGHT;
-
-              if (active_link->visited)
-                state |= GTK_STATE_FLAG_VISITED;
-              else
-                state |= GTK_STATE_FLAG_LINK;
-
-              gtk_style_context_set_state (context, state);
-
               gtk_render_background (context, cr,
                                      allocation.x, allocation.y,
                                      allocation.width, allocation.height);
@@ -4942,12 +4981,14 @@ gtk_label_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
       if (gdk_event_triggers_context_menu (event))
         {
           info->link_clicked = 1;
+          update_link_state (label);
           gtk_label_do_popup (label, (GdkEventButton*) event);
           return;
         }
       else if (button == GDK_BUTTON_PRIMARY)
         {
           info->link_clicked = 1;
+          update_link_state (label);
           gtk_widget_queue_draw (widget);
           if (!info->selectable)
             return;
@@ -5313,6 +5354,7 @@ gtk_label_motion (GtkWidget      *widget,
             {
               info->link_clicked = 0;
               info->active_link = link;
+              update_link_state (label);
               gtk_label_update_cursor (label);
               gtk_widget_queue_draw (widget);
             }
@@ -5323,6 +5365,7 @@ gtk_label_motion (GtkWidget      *widget,
             {
               info->link_clicked = 0;
               info->active_link = NULL;
+              update_link_state (label);
               gtk_label_update_cursor (label);
               gtk_widget_queue_draw (widget);
             }
@@ -6709,11 +6752,14 @@ emit_activate_link (GtkLabel     *label,
 {
   GtkLabelPrivate *priv = label->priv;
   gboolean handled;
+  GtkStateFlags state;
 
   g_signal_emit (label, signals[ACTIVATE_LINK], 0, link->uri, &handled);
   if (handled && priv->track_links && !link->visited)
     {
       link->visited = TRUE;
+      state = gtk_css_node_get_state (link->cssnode);
+      gtk_css_node_set_state (link->cssnode, (state & ~GTK_STATE_FLAG_LINK) | GTK_STATE_FLAG_VISITED);
       /* FIXME: shouldn't have to redo everything here */
       gtk_label_clear_layout (label);
     }


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