1 #include "cb-border-effect.h"
2 
3 G_DEFINE_TYPE (CbBorderEffect, cb_border_effect, CLUTTER_TYPE_EFFECT);
4 
5 #define CB_BORDER_EFFECT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
6                                                                         CB_TYPE_BORDER_EFFECT, \
7                                                                         CbBorderEffectPrivate))
8 
9 static const ClutterColor grey = { 0xaa, 0xaa, 0xaa, 0xff };
10 
11 struct _CbBorderEffectPrivate
12 {
13   CoglMaterial *border;
14   ClutterColor  color;
15   gfloat        width;
16 };
17 
18 enum {
19   PROP_0,
20 
21   PROP_COLOR,
22   PROP_WIDTH,
23 
24   PROP_LAST
25 };
26 
27 static GParamSpec *obj_props[PROP_LAST];
28 
29 /* ClutterEffect implementation */
30 static void
cb_border_effect_post_paint(ClutterEffect * self)31 cb_border_effect_post_paint (ClutterEffect *self)
32 {
33   ClutterActor *actor;
34   gfloat width;
35   gfloat height;
36   CbBorderEffectPrivate *priv;
37 
38   priv = CB_BORDER_EFFECT (self)->priv;
39 
40   /* get the associated actor's dimensions */
41   actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
42   clutter_actor_get_size (actor, &width, &height);
43 
44   /* draw Cogl rectangles on top */
45   cogl_set_source (priv->border);
46   cogl_path_new ();
47 
48   /* left rectangle */
49   cogl_path_rectangle (0, 0, priv->width, height);
50 
51   /* top rectangle */
52   cogl_path_rectangle (priv->width, 0, width, priv->width);
53 
54   /* right rectangle */
55   cogl_path_rectangle (width - priv->width, priv->width, width, height);
56 
57   /* bottom rectangle */
58   cogl_path_rectangle (priv->width,
59                        height - priv->width,
60                        width - priv->width,
61                        height);
62 
63   cogl_path_fill ();
64 }
65 
66 /* GObject implementation */
67 static void
cb_border_effect_dispose(GObject * gobject)68 cb_border_effect_dispose (GObject *gobject)
69 {
70   CbBorderEffectPrivate *priv = CB_BORDER_EFFECT (gobject)->priv;
71 
72   if (priv->border != COGL_INVALID_HANDLE)
73     {
74       cogl_handle_unref (priv->border);
75       priv->border = COGL_INVALID_HANDLE;
76     }
77 
78   G_OBJECT_CLASS (cb_border_effect_parent_class)->dispose (gobject);
79 }
80 
81 static void
cb_border_effect_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)82 cb_border_effect_set_property (GObject      *gobject,
83                                guint         prop_id,
84                                const GValue *value,
85                                GParamSpec   *pspec)
86 {
87   CbBorderEffect *effect = CB_BORDER_EFFECT (gobject);
88 
89   switch (prop_id)
90     {
91     case PROP_COLOR:
92       cb_border_effect_set_color (effect, clutter_value_get_color (value));
93       break;
94 
95     case PROP_WIDTH:
96       cb_border_effect_set_width (effect, g_value_get_float (value));
97       break;
98 
99     default:
100       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
101       break;
102     }
103 }
104 
105 static void
cb_border_effect_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)106 cb_border_effect_get_property (GObject    *gobject,
107                                guint       prop_id,
108                                GValue     *value,
109                                GParamSpec *pspec)
110 {
111   CbBorderEffectPrivate *priv = CB_BORDER_EFFECT (gobject)->priv;
112 
113   switch (prop_id)
114     {
115     case PROP_COLOR:
116       g_value_set_object (value, &(priv->color));
117       break;
118 
119     case PROP_WIDTH:
120       g_value_set_float (value, priv->width);
121       break;
122 
123     default:
124       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
125       break;
126     }
127 }
128 
129 /* GObject class and instance init */
130 static void
cb_border_effect_class_init(CbBorderEffectClass * klass)131 cb_border_effect_class_init (CbBorderEffectClass *klass)
132 {
133   ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
134   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
135   GParamSpec *pspec;
136 
137   effect_class->post_paint = cb_border_effect_post_paint;
138 
139   gobject_class->set_property = cb_border_effect_set_property;
140   gobject_class->get_property = cb_border_effect_get_property;
141   gobject_class->dispose = cb_border_effect_dispose;
142 
143   g_type_class_add_private (klass, sizeof (CbBorderEffectPrivate));
144 
145   /**
146    * CbBorderEffect:width:
147    *
148    * The width of the border
149    */
150   pspec = g_param_spec_float ("width",
151                               "Width",
152                               "The width of the border (in pixels)",
153                               1.0, 100.0,
154                               10.0,
155                               G_PARAM_READWRITE);
156   obj_props[PROP_WIDTH] = pspec;
157   g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
158 
159   /**
160    * CbBorderEffect:color:
161    *
162    * The color of the border
163    */
164   pspec = clutter_param_spec_color ("color",
165                                     "Color",
166                                     "The border color",
167                                     &grey,
168                                     G_PARAM_READWRITE);
169   obj_props[PROP_COLOR] = pspec;
170   g_object_class_install_property (gobject_class, PROP_COLOR, pspec);
171 }
172 
173 static void
cb_border_effect_init(CbBorderEffect * self)174 cb_border_effect_init (CbBorderEffect *self)
175 {
176   CbBorderEffectPrivate *priv;
177 
178   priv = self->priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
179 
180   priv->border = cogl_material_new ();
181 
182   priv->color = grey;
183 }
184 
185 /* called each time a property is set on the effect */
186 static void
cb_border_effect_update(CbBorderEffect * self)187 cb_border_effect_update (CbBorderEffect *self)
188 {
189   ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self));
190 
191   if (actor != NULL)
192     clutter_actor_queue_redraw (actor);
193 }
194 
195 /* public API */
196 
197 /**
198  * cb_border_effect_new:
199  * @width: width of the border applied by the effect
200  * @color: a #ClutterColor
201  *
202  * Creates a new #ClutterEffect with the given @width
203  * and of the given @color.
204  */
205 ClutterEffect *
cb_border_effect_new(gfloat width,const ClutterColor * color)206 cb_border_effect_new (gfloat              width,
207                       const ClutterColor *color)
208 {
209   return g_object_new (CB_TYPE_BORDER_EFFECT,
210                        "width", width,
211                        "color", color,
212                        NULL);
213 }
214 
215 /**
216  * cb_border_effect_set_color:
217  * @self: a #CbBorderEffect
218  * @color: a #ClutterColor
219  *
220  * Sets the color of the border provided by the effect @self.
221  */
222 void
cb_border_effect_set_color(CbBorderEffect * self,const ClutterColor * color)223 cb_border_effect_set_color (CbBorderEffect     *self,
224                             const ClutterColor *color)
225 {
226   CbBorderEffectPrivate *priv;
227 
228   g_return_if_fail (CB_IS_BORDER_EFFECT (self));
229   g_return_if_fail (color != NULL);
230 
231   priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
232 
233   priv->color.red = color->red;
234   priv->color.green = color->green;
235   priv->color.blue = color->blue;
236   priv->color.alpha = color->alpha;
237 
238   cogl_material_set_color4ub (priv->border,
239                               color->red,
240                               color->green,
241                               color->blue,
242                               color->alpha);
243 
244   cb_border_effect_update (self);
245 }
246 
247 /**
248  * cb_border_effect_get_color:
249  * @self: a #CbBorderEffect
250  * @color: return location for a #ClutterColor
251  *
252  * Retrieves the color of the border applied by the effect @self.
253  */
254 void
cb_border_effect_get_color(CbBorderEffect * self,ClutterColor * color)255 cb_border_effect_get_color (CbBorderEffect *self,
256                             ClutterColor   *color)
257 {
258   CbBorderEffectPrivate *priv;
259 
260   g_return_if_fail (CB_IS_BORDER_EFFECT (self));
261 
262   priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
263 
264   color->red = priv->color.red;
265   color->green = priv->color.green;
266   color->blue = priv->color.blue;
267   color->alpha = priv->color.alpha;
268 }
269 
270 /**
271  * cb_border_effect_set_width:
272  * @self: a #CbBorderEffect
273  * @width: the width of the border
274  *
275  * Sets the width (in pixels) of the border applied by the effect @self.
276  */
277 void
cb_border_effect_set_width(CbBorderEffect * self,gfloat width)278 cb_border_effect_set_width (CbBorderEffect *self,
279                             gfloat          width)
280 {
281   CbBorderEffectPrivate *priv;
282 
283   g_return_if_fail (CB_IS_BORDER_EFFECT (self));
284 
285   priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
286 
287   priv->width = width;
288 
289   cb_border_effect_update (self);
290 }
291 
292 /**
293  * cb_border_effect_get_width:
294  * @self: a #CbBorderEffect
295  *
296  * Gets the width (in pixels) of the border applied by the effect @self.
297  *
298  * Return value: the border's width, or 0.0 if @self is not
299  * a #CbBorderEffect
300  */
301 gfloat
cb_border_effect_get_width(CbBorderEffect * self)302 cb_border_effect_get_width (CbBorderEffect *self)
303 {
304   CbBorderEffectPrivate *priv;
305 
306   g_return_val_if_fail (CB_IS_BORDER_EFFECT (self), 0.0);
307 
308   priv = CB_BORDER_EFFECT_GET_PRIVATE (self);
309 
310   return priv->width;
311 }
312