void DrawErrorAnimation(Cairo.Context context, StatusArea.RenderArg arg)
        {
            const int surfaceWidth = 2000;
            double    opacity;
            int       progress;

            if (arg.ErrorAnimationProgress < .5f)
            {
                progress = (int)(arg.ErrorAnimationProgress * arg.Allocation.Width * 2.4);
                opacity  = 1.0d;
            }
            else
            {
                progress = (int)(arg.ErrorAnimationProgress * arg.Allocation.Width * 2.4);
                opacity  = 1.0d - (arg.ErrorAnimationProgress - .5d) * 2;
            }

            LayoutRoundedRectangle(context, arg.Allocation);

            context.Clip();
            context.CachedDraw(surface: ref errorSurface,
                               position: new Gdk.Point(arg.Allocation.X - surfaceWidth + progress, arg.Allocation.Y),
                               size: new Gdk.Size(surfaceWidth, arg.Allocation.Height),
                               opacity: (float)opacity,
                               draw: (c, o) => {
                // The smaller the pixel range of our gradient the less error there will be in it.
                using (var lg = new LinearGradient(surfaceWidth - 250, 0, surfaceWidth, 0)) {
                    lg.AddColorStop(0.00, Styles.WithAlpha(Styles.StatusBarErrorColor, 0.15 * o));
                    lg.AddColorStop(0.10, Styles.WithAlpha(Styles.StatusBarErrorColor, 0.15 * o));
                    lg.AddColorStop(0.88, Styles.WithAlpha(Styles.StatusBarErrorColor, 0.30 * o));
                    lg.AddColorStop(1.00, Styles.WithAlpha(Styles.StatusBarErrorColor, 0.00 * o));

                    c.SetSource(lg);
                    c.Paint();
                }
            });
            context.ResetClip();
        }
 public static void CachedDraw(this Cairo.Context self, ref SurfaceWrapper surface, Gdk.Point position, Gdk.Size size,
                               object parameters = null, float opacity = 1.0f, Action <Cairo.Context, float> draw = null, double?forceScale = null)
 {
     self.CachedDraw(ref surface, new Gdk.Rectangle(position, size), parameters, opacity, draw, forceScale);
 }
        public void Render(Cairo.Context context, StatusArea.RenderArg arg, Gtk.Widget widget)
        {
            context.CachedDraw(surface: ref backgroundSurface,
                               region: arg.Allocation,
                               draw: (c, o) => DrawBackground(c, new Gdk.Rectangle(0, 0, arg.Allocation.Width, arg.Allocation.Height)));

            if (arg.BuildAnimationOpacity > 0.001f)
            {
                DrawBuildEffect(context, arg.Allocation, arg.BuildAnimationProgress, arg.BuildAnimationOpacity);
            }

            if (arg.ErrorAnimationProgress > 0.001 && arg.ErrorAnimationProgress < .999)
            {
                DrawErrorAnimation(context, arg);
            }

            DrawBorder(context, arg.Allocation);

            if (arg.HoverProgress > 0.001f)
            {
                context.Clip();
                int x1 = arg.Allocation.X + arg.MousePosition.X - 200;
                int x2 = x1 + 400;
                using (Cairo.LinearGradient gradient = new LinearGradient(x1, 0, x2, 0))
                {
                    Cairo.Color targetColor      = Styles.StatusBarFill1Color;
                    Cairo.Color transparentColor = targetColor;
                    targetColor.A      = .7;
                    transparentColor.A = 0;

                    targetColor.A = .7 * arg.HoverProgress;

                    gradient.AddColorStop(0.0, transparentColor);
                    gradient.AddColorStop(0.5, targetColor);
                    gradient.AddColorStop(1.0, transparentColor);

                    context.SetSource(gradient);

                    context.Rectangle(x1, arg.Allocation.Y, x2 - x1, arg.Allocation.Height);
                    context.Fill();
                }
                context.ResetClip();
            }
            else
            {
                context.NewPath();
            }

            int progress_bar_x     = arg.ChildAllocation.X;
            int progress_bar_width = arg.ChildAllocation.Width;

            if (arg.CurrentPixbuf != null)
            {
                int y = arg.Allocation.Y + (arg.Allocation.Height - (int)arg.CurrentPixbuf.Size.Height) / 2;
                context.DrawImage(widget, arg.CurrentPixbuf, arg.ChildAllocation.X, y);
                progress_bar_x     += (int)arg.CurrentPixbuf.Width + Styles.ProgressBarOuterPadding;
                progress_bar_width -= (int)arg.CurrentPixbuf.Width + Styles.ProgressBarOuterPadding;
            }

            int center = arg.Allocation.Y + arg.Allocation.Height / 2;

            Gdk.Rectangle progressArea = new Gdk.Rectangle(progress_bar_x, center - Styles.ProgressBarHeight / 2, progress_bar_width, Styles.ProgressBarHeight);
            if (arg.ShowProgressBar || arg.ProgressBarAlpha > 0)
            {
                DrawProgressBar(context, arg.ProgressBarFraction, progressArea, arg);
                ClipProgressBar(context, progressArea);
            }

            int text_x     = progress_bar_x + Styles.ProgressBarInnerPadding;
            int text_width = progress_bar_width - (Styles.ProgressBarInnerPadding * 2);

            double textTweenValue = arg.TextAnimationProgress;

            if (arg.LastText != null)
            {
                double opacity = Math.Max(0.0f, 1.0f - textTweenValue);
                DrawString(arg.LastText, arg.LastTextIsMarkup, context, text_x,
                           center - (int)(textTweenValue * arg.Allocation.Height * 0.3), text_width, opacity, arg.Pango, arg);
            }

            if (arg.CurrentText != null)
            {
                DrawString(arg.CurrentText, arg.CurrentTextIsMarkup, context, text_x,
                           center + (int)((1.0f - textTweenValue) * arg.Allocation.Height * 0.3), text_width, Math.Min(textTweenValue, 1.0), arg.Pango, arg);
            }

            if (arg.ShowProgressBar || arg.ProgressBarAlpha > 0)
            {
                context.ResetClip();
            }
        }