[gtk+] themingengine: Implement 'dotted' and 'dashed'



commit 1b9e15485ee6ff07d044dc256339ed5bc2b6f401
Author: Benjamin Otte <otte redhat com>
Date:   Sun Jan 8 01:17:48 2012 +0100

    themingengine: Implement 'dotted' and 'dashed'

 gtk/gtkroundedbox.c        |   96 +++++++++++++++++++++++++
 gtk/gtkroundedboxprivate.h |    6 ++
 gtk/gtkthemingengine.c     |  165 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 267 insertions(+), 0 deletions(-)
---
diff --git a/gtk/gtkroundedbox.c b/gtk/gtkroundedbox.c
index 0519bfe..4e7c19d 100644
--- a/gtk/gtkroundedbox.c
+++ b/gtk/gtkroundedbox.c
@@ -246,6 +246,102 @@ _gtk_rounded_box_path (const GtkRoundedBox *box,
                    G_PI / 2, G_PI);
 }
 
+double
+_gtk_rounded_box_guess_length (const GtkRoundedBox *box,
+                               GtkCssSide           side)
+{
+  double length;
+  GtkCssCorner before, after;
+
+  before = side;
+  after = (side + 1) % 4;
+
+  if (side & 1)
+    length = box->box.height
+             - box->corner[before].vertical
+             - box->corner[after].vertical;
+  else
+    length = box->box.width
+             - box->corner[before].horizontal
+             - box->corner[after].horizontal;
+
+  length += G_PI * 0.125 * (box->corner[before].horizontal
+                            + box->corner[before].vertical
+                            + box->corner[after].horizontal
+                            + box->corner[after].vertical);
+
+  return length;
+}
+
+void
+_gtk_rounded_box_path_side (const GtkRoundedBox *box,
+                            cairo_t             *cr,
+                            GtkCssSide           side)
+{
+  switch (side)
+    {
+    case GTK_CSS_TOP:
+      _cairo_ellipsis (cr,
+                       box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
+                       box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
+                       box->corner[GTK_CSS_TOP_LEFT].horizontal,
+                       box->corner[GTK_CSS_TOP_LEFT].vertical,
+                       5 * G_PI / 4, 3 * G_PI / 2);
+      _cairo_ellipsis (cr, 
+                       box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
+                       box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
+                       box->corner[GTK_CSS_TOP_RIGHT].horizontal,
+                       box->corner[GTK_CSS_TOP_RIGHT].vertical,
+                       - G_PI / 2, -G_PI / 4);
+      break;
+    case GTK_CSS_RIGHT:
+      _cairo_ellipsis (cr, 
+                       box->box.x + box->box.width - box->corner[GTK_CSS_TOP_RIGHT].horizontal,
+                       box->box.y + box->corner[GTK_CSS_TOP_RIGHT].vertical,
+                       box->corner[GTK_CSS_TOP_RIGHT].horizontal,
+                       box->corner[GTK_CSS_TOP_RIGHT].vertical,
+                       - G_PI / 4, 0);
+      _cairo_ellipsis (cr,
+                       box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
+                       box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
+                       box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
+                       box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
+                       0, G_PI / 4);
+      break;
+    case GTK_CSS_BOTTOM:
+      _cairo_ellipsis (cr,
+                       box->box.x + box->box.width - box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
+                       box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
+                       box->corner[GTK_CSS_BOTTOM_RIGHT].horizontal,
+                       box->corner[GTK_CSS_BOTTOM_RIGHT].vertical,
+                       G_PI / 4, G_PI / 2);
+      _cairo_ellipsis (cr,
+                       box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
+                       box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
+                       box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
+                       box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
+                       G_PI / 2, 3 * G_PI / 4);
+      break;
+    case GTK_CSS_LEFT:
+      _cairo_ellipsis (cr,
+                       box->box.x + box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
+                       box->box.y + box->box.height - box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
+                       box->corner[GTK_CSS_BOTTOM_LEFT].horizontal,
+                       box->corner[GTK_CSS_BOTTOM_LEFT].vertical,
+                       3 * G_PI / 4, G_PI);
+      _cairo_ellipsis (cr,
+                       box->box.x + box->corner[GTK_CSS_TOP_LEFT].horizontal,
+                       box->box.y + box->corner[GTK_CSS_TOP_LEFT].vertical,
+                       box->corner[GTK_CSS_TOP_LEFT].horizontal,
+                       box->corner[GTK_CSS_TOP_LEFT].vertical,
+                       G_PI, 5 * G_PI / 4);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
 void
 _gtk_rounded_box_path_top (const GtkRoundedBox *outer,
                            const GtkRoundedBox *inner,
diff --git a/gtk/gtkroundedboxprivate.h b/gtk/gtkroundedboxprivate.h
index a832b9b..98309e7 100644
--- a/gtk/gtkroundedboxprivate.h
+++ b/gtk/gtkroundedboxprivate.h
@@ -56,8 +56,14 @@ void            _gtk_rounded_box_move                           (GtkRoundedBox
                                                                  double               dx,
                                                                  double               dy);
 
+double          _gtk_rounded_box_guess_length                   (const GtkRoundedBox *box,
+                                                                 GtkCssSide           side);
+
 void            _gtk_rounded_box_path                           (const GtkRoundedBox *box,
                                                                  cairo_t             *cr);
+void            _gtk_rounded_box_path_side                      (const GtkRoundedBox *box,
+                                                                 cairo_t             *cr,
+                                                                 GtkCssSide           side);
 void            _gtk_rounded_box_path_top                       (const GtkRoundedBox *outer,
                                                                  const GtkRoundedBox *inner,
                                                                  cairo_t             *cr);
diff --git a/gtk/gtkthemingengine.c b/gtk/gtkthemingengine.c
index b9e2e38..149c5b3 100644
--- a/gtk/gtkthemingengine.c
+++ b/gtk/gtkthemingengine.c
@@ -1435,6 +1435,158 @@ render_frame_fill (cairo_t       *cr,
 }
 
 static void
+set_stroke_style (cairo_t        *cr,
+                  double          line_width,
+                  GtkBorderStyle  style,
+                  double          length)
+{
+  double segments[2];
+  double n;
+
+  cairo_set_line_width (cr, line_width);
+
+  if (style == GTK_BORDER_STYLE_DOTTED)
+    {
+      n = round (0.5 * length / line_width);
+
+      segments[0] = 0;
+      segments[1] = n ? length / n : 2;
+      cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
+
+      cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+      cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    }
+  else
+    {
+      n = length / line_width;
+      /* Optimize the common case of an integer-sized rectangle
+       * Again, we care about focus rectangles.
+       */
+      if (n == nearbyint (n))
+        {
+          segments[0] = 1;
+          segments[1] = 2;
+        }
+      else
+        {
+          n = round ((1. / 3) * n);
+
+          segments[0] = n ? (1. / 3) * length / n : 1;
+          segments[1] = 2 * segments[1];
+        }
+      cairo_set_dash (cr, segments, G_N_ELEMENTS (segments), 0);
+
+      cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+      cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
+    }
+}
+
+static int
+get_border_side (GtkBorder  *border,
+                 GtkCssSide  side)
+{
+  switch (side)
+    {
+    case GTK_CSS_TOP:
+      return border->top;
+    case GTK_CSS_RIGHT:
+      return border->right;
+    case GTK_CSS_BOTTOM:
+      return border->bottom;
+    case GTK_CSS_LEFT:
+      return border->left;
+    default:
+      g_assert_not_reached ();
+      return 0;
+    }
+}
+
+static void
+render_frame_stroke (cairo_t       *cr,
+                     GtkRoundedBox *border_box,
+                     GtkBorder     *border,
+                     GdkRGBA        colors[4],
+                     guint          hidden_side,
+                     GtkBorderStyle stroke_style)
+{
+  gboolean different_colors, different_borders;
+  GtkRoundedBox stroke_box;
+  guint i;
+
+  different_colors = !gdk_rgba_equal (&colors[0], &colors[1]) ||
+                     !gdk_rgba_equal (&colors[0], &colors[2]) ||
+                     !gdk_rgba_equal (&colors[0], &colors[3]);
+  different_borders = border->top != border->right ||
+                      border->top != border->bottom ||
+                      border->top != border->left;
+
+  stroke_box = *border_box;
+  _gtk_rounded_box_shrink (&stroke_box,
+                           border->top / 2.0,
+                           border->right / 2.0,
+                           border->bottom / 2.0,
+                           border->left / 2.0);
+
+  if (!different_colors && !different_borders && hidden_side == 0)
+    {
+      double length = 0;
+
+      /* FAST PATH:
+       * Mostly expected to trigger for focus rectangles */
+      for (i = 0; i < 4; i++) 
+        {
+          length += _gtk_rounded_box_guess_length (&stroke_box, i);
+          _gtk_rounded_box_path_side (&stroke_box, cr, i);
+        }
+
+      gdk_cairo_set_source_rgba (cr, &colors[0]);
+      set_stroke_style (cr, border->top, stroke_style, length);
+      cairo_stroke (cr);
+    }
+  else
+    {
+      GtkRoundedBox padding_box;
+
+      padding_box = *border_box;
+      _gtk_rounded_box_path (&padding_box, cr);
+      _gtk_rounded_box_shrink (&padding_box,
+                               border->top,
+                               border->right,
+                               border->bottom,
+                               border->left);
+
+      for (i = 0; i < 4; i++) 
+        {
+          if (hidden_side & (1 << i))
+            continue;
+
+          cairo_save (cr);
+
+          if (i == 0)
+            _gtk_rounded_box_path_top (border_box, &padding_box, cr);
+          else if (i == 1)
+            _gtk_rounded_box_path_right (border_box, &padding_box, cr);
+          else if (i == 2)
+            _gtk_rounded_box_path_bottom (border_box, &padding_box, cr);
+          else if (i == 3)
+            _gtk_rounded_box_path_left (border_box, &padding_box, cr);
+          cairo_clip (cr);
+
+          _gtk_rounded_box_path_side (&stroke_box, cr, i);
+
+          gdk_cairo_set_source_rgba (cr, &colors[i]);
+          set_stroke_style (cr,
+                            get_border_side (border, i),
+                            stroke_style,
+                            _gtk_rounded_box_guess_length (&stroke_box, i));
+          cairo_stroke (cr);
+
+          cairo_restore (cr);
+        }
+    }
+}
+
+static void
 render_frame_internal (GtkThemingEngine *engine,
                        cairo_t          *cr,
                        gdouble           x,
@@ -1539,6 +1691,19 @@ render_frame_internal (GtkThemingEngine *engine,
           break;
         case GTK_BORDER_STYLE_DOTTED:
         case GTK_BORDER_STYLE_DASHED:
+          {
+            guint dont_draw = hidden_side;
+
+            for (j = 0; j < 4; j++)
+              {
+                if (border_style[j] == border_style[i])
+                  hidden_side |= (1 << j);
+                else
+                  dont_draw |= (1 << j);
+              }
+            
+            render_frame_stroke (cr, &border_box, &border, colors, dont_draw, border_style[i]);
+          }
           break;
         case GTK_BORDER_STYLE_DOUBLE:
           {



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