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 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();
            }
        }
        void DrawString(string text, bool isMarkup, Cairo.Context context, int x, int y, int width, double opacity, Pango.Context pango, StatusArea.RenderArg arg)
        {
            Pango.Layout pl = new Pango.Layout(pango);
            if (isMarkup)
            {
                pl.SetMarkup(text);
            }
            else
            {
                pl.SetText(text);
            }
            pl.FontDescription = Styles.StatusFont;
            pl.FontDescription.AbsoluteSize = Pango.Units.FromPixels(Styles.StatusFontPixelHeight);
            pl.Ellipsize = Pango.EllipsizeMode.End;
            pl.Width     = Pango.Units.FromPixels(width);

            int w, h;

            pl.GetPixelSize(out w, out h);

            context.Save();
            // use widget height instead of message box height as message box does not have a true height when no widgets are packed in it
            // also ensures animations work properly instead of getting clipped
            context.Rectangle(new Rectangle(x, arg.Allocation.Y, width, arg.Allocation.Height));
            context.Clip();

            // Subtract off remainder instead of drop to prefer higher centering when centering an odd number of pixels
            context.MoveTo(x, y - h / 2 - (h % 2));
            context.SetSourceColor(Styles.WithAlpha(FontColor(), opacity));

            Pango.CairoHelper.ShowLayout(context, pl);

            IsEllipsized = pl.IsEllipsized;

            pl.Dispose();
            context.Restore();
        }
        void DrawProgressBar(Cairo.Context context, double progress, Gdk.Rectangle bounding, StatusArea.RenderArg arg)
        {
            LayoutRoundedRectangle(context, new Gdk.Rectangle(bounding.X, bounding.Y, (int)(bounding.Width * progress), bounding.Height), 0, 0, 1);
            context.Clip();

            LayoutRoundedRectangle(context, bounding, 0, 0, 1);
            context.SetSourceColor(Styles.WithAlpha(Styles.StatusBarProgressBackgroundColor, Styles.StatusBarProgressBackgroundColor.A * arg.ProgressBarAlpha));
            context.FillPreserve();

            context.ResetClip();

            context.SetSourceColor(Styles.WithAlpha(Styles.StatusBarProgressOutlineColor, Styles.StatusBarProgressOutlineColor.A * arg.ProgressBarAlpha));
            context.LineWidth = 1;
            context.Stroke();
        }