[gnome-shell] Add support for colored borders



commit 2a0adc0fc855b00774ec79149fcca93c7cc8ac75
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Sun Sep 20 16:21:47 2009 -0400

    Add support for colored borders
    
    Use BigRectangle to draw the border and background if there's
    a border width or border radius and no border image. (Only
    uniform borders are supported for now with some deviations
    from the CSS model noted in the comments.)
    
    The background color and image parameters are removed from
    StWidget's draw_background() method since they were not used
    for StButton (the only current user) and the encapsulation
    break that they presented caused some minor problems.
    
    Add a test case for borders, and also use borders to style
    the buttons in the 'inline-style' test case.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=595993

 src/st/st-button.c                |    6 +--
 src/st/st-widget.c                |  103 ++++++++++++++++++++++++++++++++-----
 src/st/st-widget.h                |    6 +--
 tests/interactive/borders.js      |   57 ++++++++++++++++++++
 tests/interactive/inline-style.js |    8 ++--
 tests/testcommon/border-image.png |  Bin 0 -> 981 bytes
 tests/testcommon/test.css         |   20 +++++++
 7 files changed, 175 insertions(+), 25 deletions(-)
---
diff --git a/src/st/st-button.c b/src/st/st-button.c
index 19493fb..4c8b7f2 100644
--- a/src/st/st-button.c
+++ b/src/st/st-button.c
@@ -425,13 +425,11 @@ st_button_unmap (ClutterActor *self)
 }
 
 static void
-st_button_draw_background (StWidget           *widget,
-                           ClutterActor       *background,
-                           const ClutterColor *color)
+st_button_draw_background (StWidget         *widget)
 {
   StButtonPrivate *priv;
 
-  ST_WIDGET_CLASS (st_button_parent_class)->draw_background (widget, background, color);
+  ST_WIDGET_CLASS (st_button_parent_class)->draw_background (widget);
 
   priv = ST_BUTTON (widget)->priv;
 
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 6e9f6a5..bc2e0c9 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -43,6 +43,8 @@
 #include "st-theme-context.h"
 #include "st-tooltip.h"
 
+#include <big/rectangle.h>
+
 /*
  * Forward declaration for sake of StWidgetChild
  */
@@ -61,6 +63,7 @@ struct _StWidgetPrivate
   gboolean      is_stylable : 1;
   gboolean      has_tooltip : 1;
   gboolean      is_style_dirty : 1;
+  gboolean      draw_bg_color : 1;
 
   StTooltip    *tooltip;
 };
@@ -351,18 +354,18 @@ st_widget_allocate (ClutterActor          *actor,
 }
 
 static void
-st_widget_real_draw_background (StWidget           *self,
-                                ClutterActor       *background,
-                                const ClutterColor *color)
+st_widget_real_draw_background (StWidget *self)
 {
+  StWidgetPrivate *priv = self->priv;
+
   /* Default implementation just draws the background
    * colour and the image on top
    */
-  if (color && color->alpha != 0)
+  if (priv->draw_bg_color)
     {
       ClutterActor *actor = CLUTTER_ACTOR (self);
       ClutterActorBox allocation = { 0, };
-      ClutterColor bg_color = *color;
+      ClutterColor bg_color = priv->bg_color;
       gfloat w, h;
 
       bg_color.alpha = clutter_actor_get_paint_opacity (actor)
@@ -381,8 +384,8 @@ st_widget_real_draw_background (StWidget           *self,
       cogl_rectangle (0, 0, w, h);
     }
 
-  if (background)
-    clutter_actor_paint (background);
+  if (priv->border_image)
+    clutter_actor_paint (priv->border_image);
 }
 
 static void
@@ -391,9 +394,7 @@ st_widget_paint (ClutterActor *self)
   StWidgetPrivate *priv = ST_WIDGET (self)->priv;
   StWidgetClass *klass = ST_WIDGET_GET_CLASS (self);
 
-  klass->draw_background (ST_WIDGET (self),
-                          priv->border_image,
-                          &priv->bg_color);
+  klass->draw_background (ST_WIDGET (self));
 
   if (priv->background_image != NULL)
     clutter_actor_paint (priv->background_image);
@@ -486,6 +487,11 @@ st_widget_real_style_changed (StWidget *self)
   gboolean relayout_needed = FALSE;
   gboolean has_changed = FALSE;
   ClutterColor color;
+  guint border_width = 0;
+  guint border_radius = 0;
+  ClutterColor border_color = { 0, };
+  StSide side;
+  StCorner corner;
 
   /* application has request this widget is not stylable */
   if (!priv->is_stylable)
@@ -497,6 +503,7 @@ st_widget_real_style_changed (StWidget *self)
   if (!clutter_color_equal (&color, &priv->bg_color))
     {
       priv->bg_color = color;
+      priv->draw_bg_color = color.alpha != 0;
       has_changed = TRUE;
     }
 
@@ -514,6 +521,62 @@ st_widget_real_style_changed (StWidget *self)
 
   texture_cache = st_texture_cache_get_default ();
 
+  /* StThemeNode supports different widths and colors for different sides
+   * of the border, and different radii for the different corners. We take
+   * the different border widths into account when positioning, but our current
+   * drawing code (using BigRectangle) can only handle a single width, color,
+   * and radius, so we arbitrarily pick the first non-zero width and radius,
+   * and use that.
+   */
+  for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++)
+    {
+      double width = st_theme_node_get_border_width (theme_node, side);
+      if (width > 0.5)
+	{
+	  border_width = round (width);
+	  st_theme_node_get_border_color (theme_node, side, &border_color);
+	  break;
+	}
+    }
+
+  for (corner = ST_CORNER_TOPLEFT; corner <= ST_CORNER_BOTTOMLEFT; corner++)
+    {
+      double radius = st_theme_node_get_border_radius (theme_node, corner);
+      if (radius > 0.5)
+	{
+	  border_radius = round (radius);
+	  break;
+	}
+    }
+
+  /* Rough notes about the relationship of borders and backgrounds in CSS3;
+   * see http://www.w3.org/TR/css3-background/ for more accurate details.
+   *
+   * - Things are drawn in 4 layers, from the bottom:
+   *     Background color
+   *     Background image
+   *     Border color or border image
+   *     Content
+   * - The background color and image extend to and are clipped by the
+   *   edge of the border area, so will be rounded if the border is rounded.
+   *   (CSS3 background-clip property modifies this)
+   * - The border image replaces what would normally be drawn by the border
+   * - The border image is not clipped by a rounded border-radius
+   * - The border radius rounds the background even if the border is
+   *   zero width or a border image is being used.
+   *
+   * Deviations from the above as implemented here:
+   *  - The combination of border image and a non-zero border radius is
+   *    not supported; the background color will be drawn with square
+   *    corners.
+   *  - The background image is drawn above the border color or image,
+   *    not below it.
+   *  - We don't clip the background image to the (rounded) border area.
+   *
+   * The first two allow us always draw with no more than single border_image
+   * and a single background image above it.
+   */
+
   theme_image = st_theme_node_get_background_theme_image (theme_node);
   if (theme_image)
     {
@@ -545,6 +608,22 @@ st_widget_real_style_changed (StWidget *self)
       has_changed = TRUE;
       relayout_needed = TRUE;
     }
+  else if ((border_width > 0 && border_color.alpha != 0) ||
+	   (border_radius > 0 && priv->bg_color.alpha != 0))
+    {
+      priv->draw_bg_color = FALSE;
+      priv->border_image = g_object_new (BIG_TYPE_RECTANGLE,
+					 "color", &priv->bg_color,
+					 "border-width", border_width,
+					 "border-color", &border_color,
+					 "corner-radius", border_radius,
+					 NULL);
+
+      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
+
+      has_changed = TRUE;
+      relayout_needed = TRUE;
+    }
 
   bg_file = st_theme_node_get_background_image (theme_node);
   if (bg_file != NULL)
@@ -1299,7 +1378,5 @@ st_widget_draw_background (StWidget *self)
   priv = self->priv;
 
   klass = ST_WIDGET_GET_CLASS (self);
-  klass->draw_background (ST_WIDGET (self),
-                          priv->border_image,
-                          &priv->bg_color);
+  klass->draw_background (ST_WIDGET (self));
 }
diff --git a/src/st/st-widget.h b/src/st/st-widget.h
index 027aa87..66f7b06 100644
--- a/src/st/st-widget.h
+++ b/src/st/st-widget.h
@@ -72,10 +72,8 @@ struct _StWidgetClass
   ClutterActorClass parent_class;
 
   /* vfuncs */
-  void (* draw_background) (StWidget           *self,
-                            ClutterActor       *background,
-                            const ClutterColor *color);
-  void (* style_changed)   (StWidget           *self);
+  void (* draw_background) (StWidget *self);
+  void (* style_changed)   (StWidget *self);
 };
 
 GType st_widget_get_type (void) G_GNUC_CONST;
diff --git a/tests/interactive/borders.js b/tests/interactive/borders.js
new file mode 100644
index 0000000..258a621
--- /dev/null
+++ b/tests/interactive/borders.js
@@ -0,0 +1,57 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const Clutter = imports.gi.Clutter;
+const St = imports.gi.St;
+
+const UI = imports.testcommon.ui;
+
+UI.init();
+let stage = Clutter.Stage.get_default();
+stage.width = 600;
+stage.height = 600;
+
+let vbox = new St.BoxLayout({ vertical: true,
+                              width: stage.width,
+                              height: stage.height,
+                              spacing: 20,
+                              style: 'padding: 10px; background: #ffee88' });
+stage.add_actor(vbox);
+
+vbox.add(new St.Label({ text: "Hello World",
+                        style: 'border: 1px solid black; '
+                               + 'padding: 5px;' }));
+
+vbox.add(new St.Label({ text: "Hello Round World",
+                        style: 'border: 3px solid green; '
+                               + 'border-radius: 8px; '
+                               + 'padding: 5px;' }));
+
+vbox.add(new St.Label({ text: "Hello Background",
+                        style: 'border: 3px solid green; '
+                               + 'border-radius: 8px; '
+                               + 'background: white; '
+                               + 'padding: 5px;' }));
+
+vbox.add(new St.Label({ text: "Border, Padding, Content: 20px" }));
+
+let b1 = new St.BoxLayout({ vertical: true,
+                            style: 'border: 20px solid black; '
+                                   + 'background: white; '
+                                   + 'padding: 20px;' });
+vbox.add(b1);
+
+b1.add(new St.BoxLayout({ width: 20, height: 20,
+                          style: 'background: black' }));
+
+vbox.add(new St.Label({ text: "Translucent blue border",
+                        style: 'border: 20px solid rgba(0, 0, 255, 0.2); '
+                               + 'background: white; '
+                               + 'padding: 10px;' }));
+
+vbox.add(new St.Label({ text: "Border Image",
+                        style_class: "border-image",
+                        style: "padding: 10px;" }));
+
+stage.show();
+Clutter.main();
+stage.destroy();
diff --git a/tests/interactive/inline-style.js b/tests/interactive/inline-style.js
index 2232c0d..3cac25c 100644
--- a/tests/interactive/inline-style.js
+++ b/tests/interactive/inline-style.js
@@ -25,16 +25,16 @@ function update_size() {
 }
 update_size();
 
-let button = new St.Button ({ label: 'Smaller',
-                              style: 'padding: 4px; background: #eeddcc' });
+let button;
+
+button = new St.Button ({ label: 'Smaller' });
 hbox.add (button);
 button.connect('clicked', function() {
                    size /= 1.2;
                    update_size ();
                });
 
-let button = new St.Button ({ label: 'Bigger',
-                              style: 'padding: 4px; background: #eeddcc' });
+button = new St.Button ({ label: 'Bigger' });
 hbox.add (button);
 button.connect('clicked', function() {
                    size *= 1.2;
diff --git a/tests/testcommon/border-image.png b/tests/testcommon/border-image.png
new file mode 100644
index 0000000..e680020
Binary files /dev/null and b/tests/testcommon/border-image.png differ
diff --git a/tests/testcommon/test.css b/tests/testcommon/test.css
index 6ce3d5d..e0e7b05 100644
--- a/tests/testcommon/test.css
+++ b/tests/testcommon/test.css
@@ -31,3 +31,23 @@ stage {
 .monospace {
     font-family: monospace;
 }
+
+.border-image {
+    border: 15px;
+    -st-background-image: url('border-image.png') 16px;
+}
+
+StButton {
+    background: #eeddbb;
+    border: 1px solid black;
+    border-radius: 8px;
+    padding: 5px;
+}
+
+StButton:hover {
+    background: #ffeecc;
+}
+
+StButton:active {
+    background: #ccbb99;
+}



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