From a76cc79f88fc23b5308ef130bbc77bab2fc88f32 Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Tue, 21 Aug 2012 21:13:33 +0200 Subject: [PATCH] ScreenShield: add a drop shadow to the animated arrows Introduce a StShadowHelper to manage drop shadows from JS (which cannot use Cogl directly), and use it in a new StWidget-derived JS class to draw the arrow. https://bugzilla.gnome.org/show_bug.cgi?id=682285 --- data/theme/gnome-shell.css | 3 +- js/ui/screenShield.js | 76 +++++++++++++++------ src/st/st-shadow.c | 132 ++++++++++++++++++++++++++++++++++--- src/st/st-shadow.h | 17 +++++ 4 files changed, 199 insertions(+), 29 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 3be3db90e..968b2cdf8 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -2208,11 +2208,12 @@ StButton.popup-menu-item:insensitive { padding-bottom: 3em; } -.screen-shield-arrows > StDrawingArea { +.screen-shield-arrows Gjs_Arrow { color: white; width: 80px; height: 48px; -arrow-thickness: 12px; + -arrow-shadow: 0 1px 1px rgba(0,0,0,0.4); } .screen-shield-clock { diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js index 32ea2da01..ad8ecaed4 100644 --- a/js/ui/screenShield.js +++ b/js/ui/screenShield.js @@ -274,6 +274,62 @@ const NotificationsBox = new Lang.Class({ }, }); +const Arrow = new Lang.Class({ + Name: 'Arrow', + Extends: St.Bin, + + _init: function(params) { + this.parent(params); + this.x_fill = this.y_fill = true; + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._drawingArea = new St.DrawingArea(); + this._drawingArea.connect('repaint', Lang.bind(this, this._drawArrow)); + this.child = this._drawingArea; + + this._shadowHelper = null; + this._shadowWidth = this._shadowHeight = 0; + }, + + _drawArrow: function(arrow) { + let cr = arrow.get_context(); + let [w, h] = arrow.get_surface_size(); + let node = this.get_theme_node(); + let thickness = node.get_length('-arrow-thickness'); + + Clutter.cairo_set_source_color(cr, node.get_foreground_color()); + + cr.setLineCap(Cairo.LineCap.ROUND); + cr.setLineWidth(thickness); + + cr.moveTo(thickness / 2, h - thickness / 2); + cr.lineTo(w/2, thickness); + cr.lineTo(w - thickness / 2, h - thickness / 2); + cr.stroke(); + }, + + vfunc_style_changed: function() { + let node = this.get_theme_node(); + this._shadow = node.get_shadow('-arrow-shadow'); + if (this._shadow) + this._shadowHelper = St.ShadowHelper.new(this._shadow); + else + this._shadowHelper = null; + }, + + vfunc_paint: function() { + if (this._shadowHelper) { + this._shadowHelper.update(this._drawingArea); + + let allocation = this._drawingArea.get_allocation_box(); + let paintOpacity = this._drawingArea.get_paint_opacity(); + this._shadowHelper.paint(allocation, paintOpacity); + } + + this._drawingArea.paint(); + } +}); + /** * To test screen shield, make sure to kill gnome-screensaver. * @@ -318,8 +374,7 @@ const ScreenShield = new Lang.Class({ y_expand: true }); for (let i = 0; i < N_ARROWS; i++) { - let arrow = new St.DrawingArea({ opacity: 0 }); - arrow.connect('repaint', Lang.bind(this, this._drawArrow)); + let arrow = new Arrow({ opacity: 0 }); this._arrowContainer.add_actor(arrow); } this._lockScreenContents.add_actor(this._arrowContainer); @@ -384,23 +439,6 @@ const ScreenShield = new Lang.Class({ return true; }, - _drawArrow: function(arrow) { - let cr = arrow.get_context(); - let [w, h] = arrow.get_surface_size(); - let node = arrow.get_theme_node(); - let thickness = node.get_length('-arrow-thickness'); - - Clutter.cairo_set_source_color(cr, node.get_foreground_color()); - - cr.setLineCap(Cairo.LineCap.ROUND); - cr.setLineWidth(thickness); - - cr.moveTo(thickness / 2, h - thickness / 2); - cr.lineTo(w/2, thickness); - cr.lineTo(w - thickness / 2, h - thickness / 2); - cr.stroke(); - }, - _animateArrows: function() { let arrows = this._arrowContainer.get_children(); let unitaryDelay = ARROW_ANIMATION_TIME / (arrows.length + 1); diff --git a/src/st/st-shadow.c b/src/st/st-shadow.c index 9c3784a55..df1787f99 100644 --- a/src/st/st-shadow.c +++ b/src/st/st-shadow.c @@ -21,6 +21,10 @@ #include "config.h" #include "st-shadow.h" +#include "st-private.h" + +G_DEFINE_BOXED_TYPE (StShadow, st_shadow, st_shadow_ref, st_shadow_unref) +G_DEFINE_BOXED_TYPE (StShadowHelper, st_shadow_helper, st_shadow_helper_copy, st_shadow_helper_free) /** * SECTION: st-shadow @@ -175,16 +179,126 @@ st_shadow_get_box (StShadow *shadow, + shadow->blur + shadow->spread; } -GType -st_shadow_get_type (void) +/** + * SECTION:st-shadow-helper: + * + * An helper for implementing a drop shadow on a actor. + * The actor is expected to recreate the helper whenever its contents + * or size change. Then, it would call st_shadow_helper_paint() inside + * its paint() virtual function. + */ + +struct _StShadowHelper { + StShadow *shadow; + CoglMaterial *material; + + gfloat width; + gfloat height; +}; + +/** + * st_shadow_helper_new: + * @shadow: a #StShadow representing the shadow properties + * + * Builds a #StShadowHelper that will build a drop shadow + * using @source as the mask. + * + * Returns: (transfer full): a new #StShadowHelper + */ +StShadowHelper * +st_shadow_helper_new (StShadow *shadow) { - static GType _st_shadow_type = 0; + StShadowHelper *helper; - if (G_UNLIKELY (_st_shadow_type == 0)) - _st_shadow_type = - g_boxed_type_register_static ("StShadow", - (GBoxedCopyFunc) st_shadow_ref, - (GBoxedFreeFunc) st_shadow_unref); + helper = g_slice_new0 (StShadowHelper); + helper->shadow = st_shadow_ref (shadow); - return _st_shadow_type; + return helper; +} + +void +st_shadow_helper_update (StShadowHelper *helper, + ClutterActor *source) +{ + gfloat width, height; + + clutter_actor_get_size (source, &width, &height); + + if (helper->material == NULL || + helper->width != width || + helper->height != height) + { + if (helper->material) + cogl_object_unref (helper->material); + + helper->material = _st_create_shadow_material_from_actor (helper->shadow, source); + helper->width = width; + helper->height = height; + } +} + +/** + * st_shadow_helper_copy: + * @helper: the #StShadowHelper to copy + * + * Returns: (transfer full): a copy of @helper + */ +StShadowHelper * +st_shadow_helper_copy (StShadowHelper *helper) +{ + StShadowHelper *copy; + + copy = g_slice_new (StShadowHelper); + *copy = *helper; + if (copy->material) + cogl_object_ref (copy->material); + st_shadow_ref (copy->shadow); + + return copy; +} + +/** + * st_shadow_helper_free: + * @helper: a #StShadowHelper + * + * Free resources associated with @helper. + */ +void +st_shadow_helper_free (StShadowHelper *helper) +{ + if (helper->material) + cogl_object_unref (helper->material); + st_shadow_unref (helper->shadow); + + g_slice_free (StShadowHelper, helper); +} + +/** + * st_shadow_helper_paint: + * @helper: a #StShadowHelper + * @actor_box: the bounding box of the shadow + * @paint_opacity: the opacity at which the shadow is painted + * + * Paints the shadow associated with @helper This must only + * be called from the implementation of ClutterActor::paint(). + */ +void +st_shadow_helper_paint (StShadowHelper *helper, + ClutterActorBox *actor_box, + guint8 paint_opacity) +{ + ClutterActorBox allocation; + float width, height; + + clutter_actor_box_get_size (actor_box, &width, &height); + + allocation.x1 = (width - helper->width) / 2; + allocation.y1 = (height - helper->height) / 2; + allocation.x2 = allocation.x1 + helper->width; + allocation.y2 = allocation.y1 + helper->height; + + _st_paint_shadow_with_opacity (helper->shadow, + helper->material, + &allocation, + paint_opacity); } diff --git a/src/st/st-shadow.h b/src/st/st-shadow.h index 3523cdd9c..40c214958 100644 --- a/src/st/st-shadow.h +++ b/src/st/st-shadow.h @@ -26,8 +26,10 @@ G_BEGIN_DECLS #define ST_TYPE_SHADOW (st_shadow_get_type ()) +#define ST_TYPE_SHADOW_HELPER (st_shadow_get_type ()) typedef struct _StShadow StShadow; +typedef struct _StShadowHelper StShadowHelper; /** * StShadow: @@ -70,6 +72,21 @@ void st_shadow_get_box (StShadow *shadow, const ClutterActorBox *actor_box, ClutterActorBox *shadow_box); + +GType st_shadow_helper_get_type (void) G_GNUC_CONST; + +StShadowHelper *st_shadow_helper_new (StShadow *shadow); + +StShadowHelper *st_shadow_helper_copy (StShadowHelper *helper); +void st_shadow_helper_free (StShadowHelper *helper); + +void st_shadow_helper_update (StShadowHelper *helper, + ClutterActor *source); + +void st_shadow_helper_paint (StShadowHelper *helper, + ClutterActorBox *actor_box, + guint8 paint_opacity); + G_END_DECLS #endif /* __ST_SHADOW__ */