gnome-shell-3.6.3.1/src/st/st-scroll-view-fade.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 /*
  3  * st-scroll-view-fade.h: Edge fade effect for StScrollView
  4  *
  5  * Copyright 2010 Intel Corporation.
  6  * Copyright 2011 Adel Gadllah
  7  *
  8  * This program is free software; you can redistribute it and/or modify it
  9  * under the terms and conditions of the GNU Lesser General Public License,
 10  * version 2.1, as published by the Free Software Foundation.
 11  *
 12  * This program is distributed in the hope it will be useful, but WITHOUT ANY
 13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 14  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 15  * more details.
 16  *
 17  * You should have received a copy of the GNU Lesser General Public License
 18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
 19  */
 20 
 21 
 22 #define ST_SCROLL_VIEW_FADE_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFadeClass))
 23 #define ST_IS_SCROLL_VIEW_FADE_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_VIEW_FADE))
 24 #define ST_SCROLL_VIEW_FADE_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_VIEW_FADE, StScrollViewFadeClass))
 25 
 26 #include "st-scroll-view-fade.h"
 27 #include "st-scroll-view.h"
 28 #include "st-widget.h"
 29 #include "st-theme-node.h"
 30 #include "st-scroll-bar.h"
 31 #include "st-scrollable.h"
 32 
 33 #include <clutter/clutter.h>
 34 #include <cogl/cogl.h>
 35 
 36 typedef struct _StScrollViewFadeClass  StScrollViewFadeClass;
 37 
 38 #define DEFAULT_FADE_OFFSET 68.0f
 39 
 40 static const gchar *fade_glsl_shader =
 41 "uniform sampler2D tex;\n"
 42 "uniform float height;\n"
 43 "uniform float width;\n"
 44 "uniform float offset_bottom;\n"
 45 "uniform float offset_top;\n"
 46 "uniform float offset_right;\n"
 47 "uniform float offset_left;\n"
 48 /*
 49  * Used to pass the fade area to the shader
 50  *
 51  * [0][0] = x1
 52  * [0][1] = y1
 53  * [1][0] = x2
 54  * [1][1] = y2
 55  *
 56  */
 57 "uniform mat2 fade_area;\n"
 58 "\n"
 59 "void main ()\n"
 60 "{\n"
 61 " vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n"
 62 " float y = height * cogl_tex_coord_in[0].y;\n"
 63 " float x = width * cogl_tex_coord_in[0].x;\n"
 64 " float ratio = 1.0;\n"
 65 " float fade_bottom_start = fade_area[1][1] - offset_bottom;\n"
 66 " float fade_right_start = fade_area[1][0] - offset_right;\n"
 67 " float ratio_top = y / offset_top;\n"
 68 " float ratio_bottom = (fade_area[1][1] - y)/(fade_area[1][1] - fade_bottom_start);\n"
 69 " float ratio_left = x / offset_left;\n"
 70 " float ratio_right = (fade_area[1][0] - x)/(fade_area[1][0] - fade_right_start);\n"
 71 " bool in_scroll_area = fade_area[0][0] <= x && fade_area[1][0] >= x;\n"
 72 " bool fade_top = y < offset_top && in_scroll_area && (y >= fade_area[0][1]);\n"
 73 " bool fade_bottom = y > fade_bottom_start && in_scroll_area && (y <= fade_area[1][1]);\n"
 74 " bool fade_left = x < offset_left && in_scroll_area && (x >= fade_area[0][0]);\n"
 75 " bool fade_right = x > fade_right_start && in_scroll_area && (x <= fade_area[1][0]);\n"
 76 "\n"
 77 " if (fade_top) {\n"
 78 "  ratio *= ratio_top;\n"
 79 " }\n"
 80 "\n"
 81 " if (fade_bottom) {\n"
 82 "  ratio *= ratio_bottom;\n"
 83 " }\n"
 84 "\n"
 85 " if (fade_left) {\n"
 86 "  ratio *= ratio_left;\n"
 87 " }\n"
 88 "\n"
 89 " if (fade_right) {\n"
 90 "  ratio *= ratio_right;\n"
 91 " }\n"
 92 "\n"
 93 "  cogl_color_out = color * ratio;\n"
 94 "}";
 95 
 96 struct _StScrollViewFade
 97 {
 98   ClutterOffscreenEffect parent_instance;
 99 
100   /* a back pointer to our actor, so that we can query it */
101   ClutterActor *actor;
102 
103   CoglHandle shader;
104   CoglHandle program;
105 
106   gint tex_uniform;
107   gint height_uniform;
108   gint width_uniform;
109   gint fade_area_uniform;
110   gint offset_top_uniform;
111   gint offset_bottom_uniform;
112   gint offset_left_uniform;
113   gint offset_right_uniform;
114 
115   StAdjustment *vadjustment;
116   StAdjustment *hadjustment;
117 
118   guint is_attached : 1;
119 
120   float vfade_offset;
121   float hfade_offset;
122 };
123 
124 struct _StScrollViewFadeClass
125 {
126   ClutterOffscreenEffectClass parent_class;
127 };
128 
129 G_DEFINE_TYPE (StScrollViewFade,
130                st_scroll_view_fade,
131                CLUTTER_TYPE_OFFSCREEN_EFFECT);
132 
133 enum {
134   PROP_0,
135 
136   PROP_VFADE_OFFSET,
137   PROP_HFADE_OFFSET
138 };
139 
140 static gboolean
141 st_scroll_view_fade_pre_paint (ClutterEffect *effect)
142 {
143   StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect);
144   ClutterEffectClass *parent_class;
145 
146   if (self->shader == COGL_INVALID_HANDLE)
147     return FALSE;
148 
149   if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
150     return FALSE;
151 
152   if (self->actor == NULL)
153     return FALSE;
154 
155   if (self->program == COGL_INVALID_HANDLE)
156     self->program = cogl_create_program ();
157 
158   if (!self->is_attached)
159     {
160       g_assert (self->shader != COGL_INVALID_HANDLE);
161       g_assert (self->program != COGL_INVALID_HANDLE);
162 
163       cogl_program_attach_shader (self->program, self->shader);
164       cogl_program_link (self->program);
165 
166       cogl_handle_unref (self->shader);
167 
168       self->is_attached = TRUE;
169 
170       self->tex_uniform =
171         cogl_program_get_uniform_location (self->program, "tex");
172       self->height_uniform =
173         cogl_program_get_uniform_location (self->program, "height");
174       self->width_uniform =
175         cogl_program_get_uniform_location (self->program, "width");
176       self->fade_area_uniform =
177         cogl_program_get_uniform_location (self->program, "fade_area");
178       self->offset_top_uniform =
179         cogl_program_get_uniform_location (self->program, "offset_top");
180       self->offset_bottom_uniform =
181         cogl_program_get_uniform_location (self->program, "offset_bottom");
182       self->offset_left_uniform =
183         cogl_program_get_uniform_location (self->program, "offset_left");
184       self->offset_right_uniform =
185         cogl_program_get_uniform_location (self->program, "offset_right");
186     }
187 
188   parent_class = CLUTTER_EFFECT_CLASS (st_scroll_view_fade_parent_class);
189   return parent_class->pre_paint (effect);
190 }
191 
192 static CoglHandle
193 st_scroll_view_fade_create_texture (ClutterOffscreenEffect *effect,
194                                     gfloat                  min_width,
195                                     gfloat                  min_height)
196 {
197   return cogl_texture_new_with_size (min_width,
198                                      min_height,
199                                      COGL_TEXTURE_NO_SLICING,
200                                      COGL_PIXEL_FORMAT_RGBA_8888_PRE);
201 }
202 
203 static void
204 st_scroll_view_fade_paint_target (ClutterOffscreenEffect *effect)
205 {
206   StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect);
207   ClutterOffscreenEffectClass *parent;
208   CoglHandle material;
209 
210   gdouble value, lower, upper, page_size;
211   ClutterActor *vscroll = st_scroll_view_get_vscroll_bar (ST_SCROLL_VIEW (self->actor));
212   ClutterActor *hscroll = st_scroll_view_get_hscroll_bar (ST_SCROLL_VIEW (self->actor));
213   gboolean h_scroll_visible, v_scroll_visible;
214 
215   ClutterActorBox allocation, content_box, paint_box;
216 
217   /*
218    * Used to pass the fade area to the shader
219    *
220    * [0][0] = x1
221    * [0][1] = y1
222    * [1][0] = x2
223    * [1][1] = y2
224    *
225    */
226   float fade_area[2][2];
227   ClutterVertex verts[4];
228 
229   if (self->program == COGL_INVALID_HANDLE)
230     goto out;
231 
232   clutter_actor_get_paint_box (self->actor, &paint_box);
233   clutter_actor_get_abs_allocation_vertices (self->actor, verts);
234 
235   clutter_actor_get_allocation_box (self->actor, &allocation);
236   st_theme_node_get_content_box (st_widget_get_theme_node (ST_WIDGET (self->actor)),
237                                 (const ClutterActorBox *)&allocation, &content_box);
238 
239   /*
240    * The FBO is based on the paint_volume's size which can be larger then the actual
241    * allocation, so we have to account for that when passing the positions
242    */
243   fade_area[0][0] = content_box.x1 + (verts[0].x - paint_box.x1);
244   fade_area[0][1] = content_box.y1 + (verts[0].y - paint_box.y1);
245   fade_area[1][0] = content_box.x2 + (verts[3].x - paint_box.x2);
246   fade_area[1][1] = content_box.y2 + (verts[3].y - paint_box.y2);
247 
248   g_object_get (ST_SCROLL_VIEW (self->actor),
249                 "hscrollbar-visible", &h_scroll_visible,
250                 "vscrollbar-visible", &v_scroll_visible,
251                 NULL);
252 
253   if (v_scroll_visible)
254     {
255       if (clutter_actor_get_text_direction (self->actor) == CLUTTER_TEXT_DIRECTION_RTL)
256           fade_area[0][0] += clutter_actor_get_width (vscroll);
257 
258       fade_area[1][0] -= clutter_actor_get_width (vscroll);
259     }
260 
261   if (h_scroll_visible)
262       fade_area[1][1] -= clutter_actor_get_height (hscroll);
263 
264   st_adjustment_get_values (self->vadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
265 
266   if (self->offset_top_uniform > -1) {
267     if (value > lower + 0.1)
268       cogl_program_set_uniform_1f (self->program, self->offset_top_uniform, self->vfade_offset);
269     else
270       cogl_program_set_uniform_1f (self->program, self->offset_top_uniform, 0.0f);
271   }
272 
273   if (self->offset_bottom_uniform > -1) {
274     if (value < upper - page_size - 0.1)
275       cogl_program_set_uniform_1f (self->program, self->offset_bottom_uniform, self->vfade_offset);
276     else
277       cogl_program_set_uniform_1f (self->program, self->offset_bottom_uniform, 0.0f);
278   }
279 
280   st_adjustment_get_values (self->hadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
281 
282   if (self->offset_left_uniform > -1) {
283     if (value > lower + 0.1)
284       cogl_program_set_uniform_1f (self->program, self->offset_left_uniform, self->hfade_offset);
285     else
286       cogl_program_set_uniform_1f (self->program, self->offset_left_uniform, 0.0f);
287   }
288 
289   if (self->offset_right_uniform > -1) {
290     if (value < upper - page_size - 0.1)
291       cogl_program_set_uniform_1f (self->program, self->offset_right_uniform, self->hfade_offset);
292     else
293       cogl_program_set_uniform_1f (self->program, self->offset_right_uniform, 0.0f);
294   }
295 
296   if (self->tex_uniform > -1)
297     cogl_program_set_uniform_1i (self->program, self->tex_uniform, 0);
298   if (self->height_uniform > -1)
299     cogl_program_set_uniform_1f (self->program, self->height_uniform, clutter_actor_get_height (self->actor));
300   if (self->width_uniform > -1)
301     cogl_program_set_uniform_1f (self->program, self->width_uniform, clutter_actor_get_width (self->actor));
302   if (self->fade_area_uniform > -1)
303     cogl_program_set_uniform_matrix (self->program, self->fade_area_uniform, 2, 1, FALSE, (const float *)fade_area);
304 
305   material = clutter_offscreen_effect_get_target (effect);
306   cogl_material_set_user_program (material, self->program);
307 
308 out:
309   parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (st_scroll_view_fade_parent_class);
310   parent->paint_target (effect);
311 }
312 
313 static void
314 on_adjustment_changed (StAdjustment *adjustment,
315                        ClutterEffect *effect)
316 {
317   gdouble value, lower, upper, page_size;
318   gboolean needs_fade;
319   StScrollViewFade *self = ST_SCROLL_VIEW_FADE (effect);
320 
321   st_adjustment_get_values (self->vadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
322   needs_fade = (value > lower + 0.1) || (value < upper - page_size - 0.1);
323 
324   if (!needs_fade)
325     {
326       st_adjustment_get_values (self->hadjustment, &value, &lower, &upper, NULL, NULL, &page_size);
327       needs_fade = (value > lower + 0.1) || (value < upper - page_size - 0.1);
328     }
329 
330   clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), needs_fade);
331 }
332 
333 static void
334 st_scroll_view_fade_set_actor (ClutterActorMeta *meta,
335                                ClutterActor *actor)
336 {
337   StScrollViewFade *self = ST_SCROLL_VIEW_FADE (meta);
338   ClutterActorMetaClass *parent;
339 
340   g_return_if_fail (actor == NULL || ST_IS_SCROLL_VIEW (actor));
341 
342   if (self->shader == COGL_INVALID_HANDLE)
343     {
344       clutter_actor_meta_set_enabled (meta, FALSE);
345       return;
346     }
347 
348   if (self->vadjustment)
349     {
350       g_signal_handlers_disconnect_by_func (self->vadjustment,
351                                             (gpointer)on_adjustment_changed,
352                                             self);
353       self->vadjustment = NULL;
354     }
355 
356   if (self->hadjustment)
357     {
358       g_signal_handlers_disconnect_by_func (self->hadjustment,
359                                             (gpointer)on_adjustment_changed,
360                                             self);
361       self->hadjustment = NULL;
362     }
363 
364 
365   if (actor)
366     {
367         StScrollView *scroll_view = ST_SCROLL_VIEW (actor);
368         StScrollBar *vscroll = ST_SCROLL_BAR (st_scroll_view_get_vscroll_bar (scroll_view));
369         StScrollBar *hscroll = ST_SCROLL_BAR (st_scroll_view_get_hscroll_bar (scroll_view));
370         self->vadjustment = ST_ADJUSTMENT (st_scroll_bar_get_adjustment (vscroll));
371         self->hadjustment = ST_ADJUSTMENT (st_scroll_bar_get_adjustment (hscroll));
372 
373         g_signal_connect (self->vadjustment, "changed",
374                           G_CALLBACK (on_adjustment_changed),
375                           self);
376 
377         g_signal_connect (self->hadjustment, "changed",
378                           G_CALLBACK (on_adjustment_changed),
379                           self);
380 
381         on_adjustment_changed (NULL, CLUTTER_EFFECT (self));
382     }
383 
384   parent = CLUTTER_ACTOR_META_CLASS (st_scroll_view_fade_parent_class);
385   parent->set_actor (meta, actor);
386 
387   /* we keep a back pointer here, to avoid going through the ActorMeta */
388   self->actor = clutter_actor_meta_get_actor (meta);
389 }
390 
391 static void
392 st_scroll_view_fade_dispose (GObject *gobject)
393 {
394   StScrollViewFade *self = ST_SCROLL_VIEW_FADE (gobject);
395 
396   if (self->program != COGL_INVALID_HANDLE)
397     {
398       cogl_handle_unref (self->program);
399 
400       self->program = COGL_INVALID_HANDLE;
401       self->shader = COGL_INVALID_HANDLE;
402     }
403 
404   if (self->vadjustment)
405     {
406       g_signal_handlers_disconnect_by_func (self->vadjustment,
407                                             (gpointer)on_adjustment_changed,
408                                             self);
409       self->vadjustment = NULL;
410     }
411 
412   if (self->hadjustment)
413     {
414       g_signal_handlers_disconnect_by_func (self->hadjustment,
415                                             (gpointer)on_adjustment_changed,
416                                             self);
417       self->hadjustment = NULL;
418     }
419 
420   self->actor = NULL;
421 
422   G_OBJECT_CLASS (st_scroll_view_fade_parent_class)->dispose (gobject);
423 }
424 
425 static void
426 st_scroll_view_vfade_set_offset (StScrollViewFade *self,
427                                  float fade_offset)
428 {
429   if (self->vfade_offset == fade_offset)
430     return;
431 
432   g_object_freeze_notify (G_OBJECT (self));
433 
434   self->vfade_offset = fade_offset;
435 
436   if (self->actor != NULL)
437     clutter_actor_queue_redraw (self->actor);
438 
439   g_object_notify (G_OBJECT (self), "vfade-offset");
440   g_object_thaw_notify (G_OBJECT (self));
441 }
442 
443 static void
444 st_scroll_view_hfade_set_offset (StScrollViewFade *self,
445                                  float fade_offset)
446 {
447   if (self->hfade_offset == fade_offset)
448     return;
449 
450   g_object_freeze_notify (G_OBJECT (self));
451 
452   self->hfade_offset = fade_offset;
453 
454   if (self->actor != NULL)
455     clutter_actor_queue_redraw (self->actor);
456 
457   g_object_notify (G_OBJECT (self), "hfade-offset");
458   g_object_thaw_notify (G_OBJECT (self));
459 }
460 
461 static void
462 st_scroll_view_fade_set_property (GObject *object,
463                                   guint prop_id,
464                                   const GValue *value,
465                                   GParamSpec *pspec)
466 {
467   StScrollViewFade *self = ST_SCROLL_VIEW_FADE (object);
468 
469   switch (prop_id)
470     {
471     case PROP_VFADE_OFFSET:
472       st_scroll_view_vfade_set_offset (self, g_value_get_float (value));
473       break;
474     case PROP_HFADE_OFFSET:
475       st_scroll_view_hfade_set_offset (self, g_value_get_float (value));
476       break;
477     default:
478       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
479       break;
480     }
481 }
482 
483 static void
484 st_scroll_view_fade_get_property (GObject *object,
485                                   guint prop_id,
486                                   GValue *value,
487                                   GParamSpec *pspec)
488 {
489   StScrollViewFade *self = ST_SCROLL_VIEW_FADE (object);
490 
491   switch (prop_id)
492     {
493     case PROP_HFADE_OFFSET:
494       g_value_set_float (value, self->vfade_offset);
495       break;
496     case PROP_VFADE_OFFSET:
497       g_value_set_float (value, self->hfade_offset);
498       break;
499     default:
500       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
501       break;
502     }
503 }
504 
505 static void
506 st_scroll_view_fade_class_init (StScrollViewFadeClass *klass)
507 {
508   ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
509   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
510   ClutterOffscreenEffectClass *offscreen_class;
511   ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
512 
513   gobject_class->dispose = st_scroll_view_fade_dispose;
514   gobject_class->get_property = st_scroll_view_fade_get_property;
515   gobject_class->set_property = st_scroll_view_fade_set_property;
516 
517   meta_class->set_actor = st_scroll_view_fade_set_actor;
518 
519   effect_class->pre_paint = st_scroll_view_fade_pre_paint;
520 
521   offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
522   offscreen_class->create_texture = st_scroll_view_fade_create_texture;
523   offscreen_class->paint_target = st_scroll_view_fade_paint_target;
524 
525   g_object_class_install_property (gobject_class,
526                                    PROP_VFADE_OFFSET,
527                                    g_param_spec_float ("vfade-offset",
528                                                        "Vertical Fade Offset",
529                                                        "The height of the area which is faded at the edge",
530                                                        0.f, G_MAXFLOAT, DEFAULT_FADE_OFFSET,
531                                                        G_PARAM_READWRITE));
532   g_object_class_install_property (gobject_class,
533                                    PROP_HFADE_OFFSET,
534                                    g_param_spec_float ("hfade-offset",
535                                                        "Horizontal Fade Offset",
536                                                        "The width of the area which is faded at the edge",
537                                                        0.f, G_MAXFLOAT, DEFAULT_FADE_OFFSET,
538                                                        G_PARAM_READWRITE));
539 
540 }
541 
542 static void
543 st_scroll_view_fade_init (StScrollViewFade *self)
544 {
545   static CoglHandle shader = COGL_INVALID_HANDLE;
546 
547   if (shader == COGL_INVALID_HANDLE)
548     {
549       if (clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
550         {
551           shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT);
552           cogl_shader_source (shader, fade_glsl_shader);
553           cogl_shader_compile (shader);
554           if (!cogl_shader_is_compiled (shader))
555             {
556               gchar *log_buf = cogl_shader_get_info_log (shader);
557 
558               g_warning (G_STRLOC ": Unable to compile the fade shader: %s",
559                          log_buf);
560               g_free (log_buf);
561 
562               cogl_handle_unref (shader);
563               shader = COGL_INVALID_HANDLE;
564           }
565         }
566     }
567 
568   self->shader = shader;
569   self->is_attached = FALSE;
570   self->tex_uniform = -1;
571   self->height_uniform = -1;
572   self->width_uniform = -1;
573   self->fade_area_uniform = -1;
574   self->offset_top_uniform = -1;
575   self->offset_bottom_uniform = -1;
576   self->vfade_offset = DEFAULT_FADE_OFFSET;
577   self->hfade_offset = DEFAULT_FADE_OFFSET;
578 
579   if (shader != COGL_INVALID_HANDLE)
580     cogl_handle_ref (self->shader);
581 }
582 
583 ClutterEffect *
584 st_scroll_view_fade_new (void)
585 {
586   return g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
587 }