[gtk+/gtk-2-24-quartz] win32: fix themed notebook tab renderering



commit 2c49155044b7827093ba5313b4f6109c61c4dfc5
Author: Jerome Lambourg <lambourg adacore com>
Date:   Fri Oct 21 14:03:22 2011 +0200

    win32: fix themed notebook tab renderering
    
    The ms-windows engine incorrectly displays notebooks: the
    tabs are not attached to the body, and look more like
    regular buttons than actual notebook tabs. Also, the frame
    around the notebooks is also incorrectly drawn.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=650300

 modules/engines/ms-windows/msw_style.c |  502 ++++++++++++++++++--------------
 modules/engines/ms-windows/xp_theme.c  |    3 +
 modules/engines/ms-windows/xp_theme.h  |    1 +
 3 files changed, 283 insertions(+), 223 deletions(-)
---
diff --git a/modules/engines/ms-windows/msw_style.c b/modules/engines/ms-windows/msw_style.c
index 829b6ae..9be99aa 100755
--- a/modules/engines/ms-windows/msw_style.c
+++ b/modules/engines/ms-windows/msw_style.c
@@ -41,7 +41,7 @@
 #include <string.h>
 #include <stdio.h>
 
-#include "gtk/gtk.h"
+#include "gdk/gdk.h"
 #include "gtk/gtk.h"
 
 #ifdef BUILDING_STANDALONE
@@ -2348,231 +2348,290 @@ DrawTab (HDC hdc, const RECT R, gint32 aPosition, gboolean aSelected,
     DrawEdge (hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag);
 }
 
+static void
+get_notebook_tab_position (GtkNotebook *notebook,
+                           gboolean *start,
+                           gboolean *end)
+{
+  gboolean found_start = FALSE, found_end = FALSE;
+  gint i, n_pages;
+
+  /* default value */
+  *start = TRUE;
+  *end = FALSE;
+
+  n_pages = gtk_notebook_get_n_pages (notebook);
+  for (i = 0; i < n_pages; i++)
+    {
+      GtkWidget *tab_child;
+      GtkWidget *tab_label;
+      gboolean expand;
+      GtkPackType pack_type;
+      gboolean is_selected;
+
+      tab_child = gtk_notebook_get_nth_page (notebook, i);
+      is_selected = gtk_notebook_get_current_page (notebook) == i;
+
+      /* Skip invisible tabs */
+      tab_label = gtk_notebook_get_tab_label (notebook, tab_child);
+      if (!tab_label || !GTK_WIDGET_VISIBLE (tab_label))
+        continue;
+
+      /* Mimics what the notebook does internally. */
+      if (tab_label && !gtk_widget_get_child_visible (tab_label))
+        {
+          /* One child is hidden because scroll arrows are present.
+           * So both corners are rounded. */
+          *start = FALSE;
+          *end = FALSE;
+          return;
+        }
+
+      gtk_notebook_query_tab_label_packing (notebook, tab_child, &expand,
+                                            NULL, /* don't need fill */
+                                            &pack_type);
+
+      if (pack_type == GTK_PACK_START)
+        {
+          if (!found_start)
+            {
+              /* This is the first tab with PACK_START pack type */
+              found_start = TRUE;
+
+              if (is_selected)
+                {
+                  /* first PACK_START item is selected: set start to TRUE */
+                  *start = TRUE;
+
+                  if (expand && !found_end)
+                    {
+                      /* tentatively set end to TRUE: will be invalidated if we
+                       * find other items */
+                      *end = TRUE;
+                    }
+                }
+              else
+                {
+                  *start = FALSE;
+                }
+            }
+          else if (!found_end && !is_selected)
+            {
+              /* an unselected item exists, and no item with PACK_END pack type */
+              *end = FALSE;
+            }
+        }
+
+      if (pack_type == GTK_PACK_END)
+        {
+          if (!found_end)
+            {
+              /* This is the first tab with PACK_END pack type */
+              found_end = TRUE;
+
+              if (is_selected)
+                {
+                  /* first PACK_END item is selected: set end to TRUE */
+                  *end = TRUE;
+
+                  if (expand && !found_start)
+                    {
+                      /* tentatively set start to TRUE: will be invalidated if
+                       * we find other items */
+                      *start = TRUE;
+                    }
+                }
+              else
+                {
+                *end = FALSE;
+                }
+            }
+          else if (!found_start && !is_selected)
+            {
+              *start = FALSE;
+            }
+        }
+    }
+}
+
 static gboolean
 draw_themed_tab_button (GtkStyle *style,
 			GdkWindow *window,
 			GtkStateType state_type,
 			GtkNotebook *notebook,
-			gint x, gint y,
-			gint width, gint height, gint gap_side)
+			gint x,
+			gint y,
+			gint width,
+			gint height,
+			gint gap_side)
 {
   GdkPixmap *pixmap = NULL;
-  gint border_width =
-    gtk_container_get_border_width (GTK_CONTAINER (notebook));
-  GtkWidget *widget = GTK_WIDGET (notebook);
   GdkRectangle draw_rect, clip_rect;
-  GdkPixbufRotation rotation = GDK_PIXBUF_ROTATE_NONE;
   cairo_t *cr;
+  gboolean start, stop;
+  XpThemeElement element;
+  gint d_w, d_h;
 
-  if (gap_side == GTK_POS_TOP)
-    {
-      int widget_right;
-
-      if (state_type == GTK_STATE_NORMAL)
-	{
-	  draw_rect.x = x;
-	  draw_rect.y = y;
-	  draw_rect.width = width + 2;
-	  draw_rect.height = height;
+  get_notebook_tab_position (notebook, &start, &stop);
 
-	  clip_rect = draw_rect;
-	  clip_rect.height--;
-	}
-      else
-	{
-	  draw_rect.x = x + 2;
-	  draw_rect.y = y;
-	  draw_rect.width = width - 2;
-	  draw_rect.height = height - 2;
-	  clip_rect = draw_rect;
-	}
-
-      /* If we are currently drawing the right-most tab, and if that tab is the selected tab... */
-      widget_right = widget->allocation.x + widget->allocation.width - border_width - 2;
-
-      if (draw_rect.x + draw_rect.width >= widget_right)
-	{
-	  draw_rect.width = clip_rect.width = widget_right - draw_rect.x;
-	}
-    }
-  if (gap_side == GTK_POS_BOTTOM)
+  if (state_type == GTK_STATE_NORMAL)
     {
-      int widget_right;
-
-      if (state_type == GTK_STATE_NORMAL)
-	{
-	  draw_rect.x = x;
-	  draw_rect.y = y;
-	  draw_rect.width = width + 2;
-	  draw_rect.height = height;
-
-	  clip_rect = draw_rect;
-	}
+      if (start && stop)
+        {
+          /* Both edges of the notebook are covered by the item */
+          element = XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE;
+        }
+      else if (start)
+        {
+          /* The start edge is covered by the item */
+          element = XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE;
+        }
+      else if (stop)
+        {
+          /* the stop edge is reached by the item */
+          element = XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE;
+        }
       else
-	{
-	  draw_rect.x = x + 2;
-	  draw_rect.y = y + 2;
-	  draw_rect.width = width - 2;
-	  draw_rect.height = height - 2;
-	  clip_rect = draw_rect;
-	}
-
-      /* If we are currently drawing the right-most tab, and if that tab is the selected tab... */
-      widget_right = widget->allocation.x + widget->allocation.width - border_width - 2;
-
-      if (draw_rect.x + draw_rect.width >= widget_right)
-	{
-	  draw_rect.width = clip_rect.width = widget_right - draw_rect.x;
-	}
-
-      rotation = GDK_PIXBUF_ROTATE_UPSIDEDOWN;
+        {
+          /* no edge should be aligned with the tab */
+          element = XP_THEME_ELEMENT_TAB_ITEM;
+        }
     }
-  else if (gap_side == GTK_POS_LEFT)
+  else
     {
-      int widget_bottom;
-
-      if (state_type == GTK_STATE_NORMAL)
-	{
-	  draw_rect.x = x;
-	  draw_rect.y = y;
-	  draw_rect.width = width;
-	  draw_rect.height = height + 2;
-
-	  clip_rect = draw_rect;
-	  clip_rect.width--;
-	}
-      else
-	{
-	  draw_rect.x = x;
-	  draw_rect.y = y + 2;
-	  draw_rect.width = width - 2;
-	  draw_rect.height = height - 2;
-	  clip_rect = draw_rect;
-	}
+      /* Ideally, we should do the same here. Unfortunately, we don't have ways
+       * to determine what tab widget is actually being drawn here, so we can't
+       * determine its position relative to the borders */
+      element = XP_THEME_ELEMENT_TAB_ITEM;
+    }
 
-      /* If we are currently drawing the bottom-most tab, and if that tab is the selected tab... */
-      widget_bottom = widget->allocation.x + widget->allocation.height - border_width - 2;
+  draw_rect.x = x;
+  draw_rect.y = y;
+  draw_rect.width = width;
+  draw_rect.height = height;
 
-      if (draw_rect.y + draw_rect.height >= widget_bottom)
-	{
-	  draw_rect.height = clip_rect.height = widget_bottom - draw_rect.y;
-	}
-
-      rotation = GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE;
+  /* Perform adjustments required to have the theme perfectly aligned */
+  if (state_type == GTK_STATE_ACTIVE)
+    {
+      switch (gap_side)
+        {
+        case GTK_POS_TOP:
+          draw_rect.x += 2;
+          draw_rect.width -= 2;
+          draw_rect.height -= 1;
+          break;
+        case GTK_POS_BOTTOM:
+          draw_rect.x += 2;
+          draw_rect.width -= 2;
+          draw_rect.y += 1;
+          draw_rect.height -= 1;
+          break;
+        case GTK_POS_LEFT:
+          draw_rect.y += 2;
+          draw_rect.height -= 2;
+          draw_rect.width -= 1;
+          break;
+        case GTK_POS_RIGHT:
+          draw_rect.y += 2;
+          draw_rect.height -= 2;
+          draw_rect.x += 1;
+          draw_rect.width -= 1;
+          break;
+        }
     }
-  else if (gap_side == GTK_POS_RIGHT)
+  else
     {
-      int widget_bottom;
-
-      if (state_type == GTK_STATE_NORMAL)
-	{
-	  draw_rect.x = x + 1;
-	  draw_rect.y = y;
-	  draw_rect.width = width;
-	  draw_rect.height = height + 2;
-
-	  clip_rect = draw_rect;
-	  clip_rect.width--;
-	}
-      else
-	{
-	  draw_rect.x = x + 2;
-	  draw_rect.y = y + 2;
-	  draw_rect.width = width - 2;
-	  draw_rect.height = height - 2;
-	  clip_rect = draw_rect;
-	}
+      switch (gap_side)
+        {
+        case GTK_POS_TOP:
+          draw_rect.height += 1;
+          draw_rect.width += 2;
+          break;
+        case GTK_POS_BOTTOM:
+          draw_rect.y -= 1;
+          draw_rect.height += 1;
+          draw_rect.width += 2;
+          break;
+        case GTK_POS_LEFT:
+          draw_rect.width += 1;
+          draw_rect.height += 2;
+          break;
+        case GTK_POS_RIGHT:
+          draw_rect.x -= 1;
+          draw_rect.width += 1;
+          draw_rect.height += 2;
+          break;
+        }
+    }
 
-      /* If we are currently drawing the bottom-most tab, and if that tab is the selected tab... */
-      widget_bottom = widget->allocation.x + widget->allocation.height - border_width - 2;
+  clip_rect = draw_rect;
 
-      if (draw_rect.y + draw_rect.height >= widget_bottom)
-	{
-	  draw_rect.height = clip_rect.height = widget_bottom - draw_rect.y;
-	}
+  /* Take care of obvious case where the clipping is an empty region */
+  if (clip_rect.width <= 0 || clip_rect.height <= 0)
+    return TRUE;
 
-      rotation = GDK_PIXBUF_ROTATE_CLOCKWISE;
+  /* Simple case: tabs on top are just drawn as is */
+  if (gap_side == GTK_POS_TOP)
+    {
+       return xp_theme_draw (window, element, style,
+	                     draw_rect.x, draw_rect.y,
+	                     draw_rect.width, draw_rect.height,
+	                     state_type, &clip_rect);
     }
 
-  if (gap_side == GTK_POS_TOP)
+  /* For other cases, we need to print the tab on a pixmap, and then rotate
+   * it according to the gap side */
+  if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT)
     {
-      if (!xp_theme_draw (window, XP_THEME_ELEMENT_TAB_ITEM, style,
-			  draw_rect.x, draw_rect.y,
-			  draw_rect.width, draw_rect.height,
-			  state_type, &clip_rect))
-	{
-	  return FALSE;
-	}
+      /* pixmap will have width/height inverted as we'll rotate +- PI / 2 */
+      d_w = draw_rect.height;
+      d_h = draw_rect.width;
     }
   else
     {
-      GdkPixbuf *pixbuf;
-      GdkPixbuf *rotated;
-
-      if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT)
-	{
-	  pixmap = gdk_pixmap_new (window, clip_rect.height, clip_rect.width, -1);
-
-	  if (!xp_theme_draw (pixmap, XP_THEME_ELEMENT_TAB_ITEM, style,
-			      draw_rect.y - clip_rect.y, draw_rect.x - clip_rect.x,
-			      draw_rect.height, draw_rect.width, state_type, 0))
-	    {
-	      g_object_unref (pixmap);
-	      return FALSE;
-	    }
-
-	  pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, NULL, 0, 0, 0, 0,
-						 clip_rect.height, clip_rect.width);
-	  g_object_unref (pixmap);
-	}
-      else
-	{
-	  pixmap = gdk_pixmap_new (window, clip_rect.width, clip_rect.height, -1);
-
-	  if (!xp_theme_draw (pixmap, XP_THEME_ELEMENT_TAB_ITEM, style,
-			      draw_rect.x - clip_rect.x, draw_rect.y - clip_rect.y,
-			      draw_rect.width, draw_rect.height, state_type, 0))
-	    {
-	      g_object_unref (pixmap);
-	      return FALSE;
-	    }
-
-	  pixbuf = gdk_pixbuf_get_from_drawable (NULL, pixmap, NULL, 0, 0, 0, 0,
-						 clip_rect.width, clip_rect.height);
-	  g_object_unref (pixmap);
-	}
-
-      rotated = gdk_pixbuf_rotate_simple (pixbuf, rotation);
-      g_object_unref (pixbuf);
-      pixbuf = rotated;
+      d_w = draw_rect.width;
+      d_h = draw_rect.height;
+    }
 
-      // XXX - This is really hacky and evil.  When we're drawing the left-most tab
-      //       while it is active on a bottom-oriented notebook, there is one white
-      //       pixel at the top.  There may be a better solution than this if someone
-      //       has time to discover it.
-      if (gap_side == GTK_POS_BOTTOM && state_type == GTK_STATE_NORMAL
-	  && x == widget->allocation.x)
-	{
-	  int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-	  int n_channels = gdk_pixbuf_get_n_channels (pixbuf);
-	  int psub = 0;
+  pixmap = gdk_pixmap_new (window, d_w, d_h, -1);
 
-	  guchar *pixels = gdk_pixbuf_get_pixels (pixbuf);
-	  guchar *p = pixels + rowstride;
+  /* First copy the previously saved window background */
+  cr = gdk_cairo_create (pixmap);
 
-	  for (psub = 0; psub < n_channels; psub++)
-	    {
-	      pixels[psub] = p[psub];
-	    }
-	}
+  /* pixmaps unfortunately don't handle the alpha channel. We then
+   * paint it first in white, hoping the actual background is clear */
+  cairo_set_source_rgb (cr, 1, 1, 1);
+  cairo_paint (cr);
+  cairo_destroy (cr);
 
-      cr = gdk_cairo_create (window);
-      gdk_cairo_set_source_pixbuf (cr, pixbuf, clip_rect.x, clip_rect.y);
-      cairo_paint (cr);
-      cairo_destroy (cr);
-      g_object_unref (pixbuf);
+  if (!xp_theme_draw (pixmap, element, style, 0, 0, d_w, d_h, state_type, 0))
+    {
+      g_object_unref (pixmap);
+      return FALSE;
     }
 
+  /* Now we have the pixmap, we need to flip/rotate it according to its
+   * final position. We'll do it using cairo on the dest window */
+  cr = gdk_cairo_create (window);
+  cairo_rectangle (cr, clip_rect.x, clip_rect.y,
+                   clip_rect.width, clip_rect.height);
+  cairo_clip (cr);
+  cairo_translate(cr, draw_rect.x + draw_rect.width * 0.5,
+                  draw_rect.y + draw_rect.height * 0.5);
+
+  if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_RIGHT) {
+    cairo_rotate (cr, M_PI/2.0);
+  }
+
+  if (gap_side == GTK_POS_LEFT || gap_side == GTK_POS_BOTTOM) {
+    cairo_scale (cr, 1, -1);
+  }
+
+  cairo_translate(cr, -d_w * 0.5, -d_h * 0.5);
+  gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0);
+  cairo_paint (cr);
+  cairo_destroy (cr);
+
   return TRUE;
 }
 
@@ -2669,46 +2728,43 @@ draw_extension (GtkStyle *style,
 }
 
 static void
-draw_box_gap (GtkStyle *style, GdkWindow *window, GtkStateType state_type,
-	      GtkShadowType shadow_type, GdkRectangle *area,
-	      GtkWidget *widget, const gchar *detail, gint x,
-	      gint y, gint width, gint height, GtkPositionType gap_side,
-	      gint gap_x, gint gap_width)
+draw_box_gap (GtkStyle *style,
+              GdkWindow *window,
+              GtkStateType state_type,
+	      GtkShadowType shadow_type,
+	      GdkRectangle *area,
+	      GtkWidget *widget,
+	      const gchar *detail,
+	      gint x,
+	      gint y,
+	      gint width,
+	      gint height,
+	      GtkPositionType gap_side,
+	      gint gap_x,
+	      gint gap_width)
 {
   if (GTK_IS_NOTEBOOK (widget) && detail && !strcmp (detail, "notebook"))
     {
       GtkNotebook *notebook = GTK_NOTEBOOK (widget);
+
       int side = gtk_notebook_get_tab_pos (notebook);
-      int x2 = x, y2 = y, w2 = width, h2 = height;
+      int x2 = x, y2 = y;
+      int w2 = width + style->xthickness, h2 = height + style->ythickness;
 
-      if (side == GTK_POS_TOP)
-	{
-	  x2 = x;
-	  y2 = y - gtk_notebook_get_tab_vborder (notebook);
-	  w2 = width;
-	  h2 = height + gtk_notebook_get_tab_vborder (notebook) * 2;
-	}
-      else if (side == GTK_POS_BOTTOM)
-	{
-	  x2 = x;
-	  y2 = y;
-	  w2 = width;
-	  h2 = height + gtk_notebook_get_tab_vborder (notebook) * 2;
-	}
-      else if (side == GTK_POS_LEFT)
-	{
-	  x2 = x - gtk_notebook_get_tab_hborder (notebook);
-	  y2 = y;
-	  w2 = width + gtk_notebook_get_tab_hborder (notebook);
-	  h2 = height;
-	}
-      else if (side == GTK_POS_RIGHT)
-	{
-	  x2 = x;
-	  y2 = y;
-	  w2 = width + gtk_notebook_get_tab_hborder (notebook) * 2;
-	  h2 = height;
-	}
+      switch (side)
+        {
+        case GTK_POS_TOP:
+          y2 -= 1;
+          break;
+        case GTK_POS_BOTTOM:
+          break;
+        case GTK_POS_LEFT:
+          x2 -= 1;
+          break;
+        case GTK_POS_RIGHT:
+          w2 += 1;
+          break;
+        }
 
       if (xp_theme_draw (window, XP_THEME_ELEMENT_TAB_PANE, style,
 			 x2, y2, w2, h2, state_type, area))
diff --git a/modules/engines/ms-windows/xp_theme.c b/modules/engines/ms-windows/xp_theme.c
index bdd4240..48fb594 100755
--- a/modules/engines/ms-windows/xp_theme.c
+++ b/modules/engines/ms-windows/xp_theme.c
@@ -121,6 +121,7 @@ static const short element_part_map[XP_THEME_ELEMENT__SIZEOF] = {
   TABP_TABITEM,
   TABP_TABITEMLEFTEDGE,
   TABP_TABITEMRIGHTEDGE,
+  TABP_TABITEMBOTHEDGE,
   TABP_PANE,
   SBP_THUMBBTNHORZ,
   SBP_THUMBBTNVERT,
@@ -408,6 +409,7 @@ xp_theme_get_handle_by_element (XpThemeElement element)
     case XP_THEME_ELEMENT_TAB_ITEM:
     case XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE:
     case XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE:
+    case XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE:
     case XP_THEME_ELEMENT_TAB_PANE:
       klazz = XP_THEME_CLASS_TAB;
       break;
@@ -536,6 +538,7 @@ xp_theme_map_gtk_state (XpThemeElement element, GtkStateType state)
 
     case XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE:
     case XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE:
+    case XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE:
     case XP_THEME_ELEMENT_TAB_ITEM:
       switch (state)
 	{
diff --git a/modules/engines/ms-windows/xp_theme.h b/modules/engines/ms-windows/xp_theme.h
index dfacb43..33a56b3 100755
--- a/modules/engines/ms-windows/xp_theme.h
+++ b/modules/engines/ms-windows/xp_theme.h
@@ -59,6 +59,7 @@ typedef enum
   XP_THEME_ELEMENT_TAB_ITEM,
   XP_THEME_ELEMENT_TAB_ITEM_LEFT_EDGE,
   XP_THEME_ELEMENT_TAB_ITEM_RIGHT_EDGE,
+  XP_THEME_ELEMENT_TAB_ITEM_BOTH_EDGE,
   XP_THEME_ELEMENT_TAB_PANE,
   XP_THEME_ELEMENT_SCROLLBAR_H,
   XP_THEME_ELEMENT_SCROLLBAR_V,



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