| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424 | using System;using System.ComponentModel;using System.Drawing;using System.Drawing.Drawing2D;using System.Drawing.Imaging;using System.Windows.Forms;using System.Windows.Forms.VisualStyles;namespace PaintDotNet{    public sealed class ArrowButton        : ButtonBase    {        public bool drawTransparentBackColor = true;        private ArrowDirection arrowDirection = ArrowDirection.Right;        private bool drawWithGradient = false;        private bool reverseArrowColors = false;        private float arrowOutlineWidth = 1.0f;        private bool forcedPushed = false;        private Surface backBufferSurface = null;        private RenderArgs backBuffer = null;        public bool ForcedPushedAppearance        {            get            {                return this.forcedPushed;            }            set            {                if (this.forcedPushed != value)                {                    this.forcedPushed = value;                    Invalidate();                }            }        }        public bool ReverseArrowColors        {            get            {                return this.reverseArrowColors;            }            set            {                if (this.reverseArrowColors != value)                {                    this.reverseArrowColors = value;                    Invalidate();                }            }        }        public float ArrowOutlineWidth        {            get            {                return this.arrowOutlineWidth;            }            set            {                if (this.arrowOutlineWidth != value)                {                    this.arrowOutlineWidth = value;                    Invalidate();                }            }        }        public ArrowDirection ArrowDirection        {            get            {                return this.arrowDirection;            }            set            {                if (this.arrowDirection != value)                {                    this.arrowDirection = value;                    Invalidate();                }            }        }        public bool DrawWithGradient        {            get            {                return this.drawWithGradient;            }            set            {                if (this.drawWithGradient != value)                {                    this.drawWithGradient = value;                    Invalidate();                }            }        }        public ArrowButton()        {            InitializeComponent();        }        private void InitializeComponent()        {            this.BackColor = this.drawTransparentBackColor ? Color.Transparent : Color.FromArgb(192, SystemColors.ControlDark);        }        protected override void Dispose(bool disposing)        {            if (disposing)            {                if (this.backBuffer != null)                {                    this.backBuffer.Dispose();                    this.backBuffer = null;                }                if (this.backBufferSurface != null)                {                    this.backBufferSurface.Dispose();                    this.backBufferSurface = null;                }            }            base.Dispose(disposing);        }        protected override void OnPaintButton(Graphics g, PushButtonState state, bool drawFocusCues, bool drawKeyboardCues)        {            PushButtonState newState;            if (this.forcedPushed)            {                newState = PushButtonState.Pressed;            }            else            {                newState = state;            }            OnPaintButtonImpl(g, newState, drawFocusCues, drawKeyboardCues);        }        private void OnPaintButtonImpl(Graphics g, PushButtonState state, bool drawFocusCues, bool drawKeyboardCues)        {            Color backColor;            Color outlineColor;            Color arrowFillColor;            Color arrowOutlineColor;            switch (state)            {                case PushButtonState.Disabled:                    backColor = this.drawTransparentBackColor ? Color.Transparent : Color.FromArgb(192, SystemColors.ControlDark);                    outlineColor = BackColor;                    arrowFillColor = Color.Gray;                    arrowOutlineColor = Color.Black;                    break;                case PushButtonState.Hot:                    backColor = Color.FromArgb(64, SystemColors.HotTrack);                    outlineColor = backColor;                    arrowFillColor = Color.Blue;                    arrowOutlineColor = Color.White;                    break;                case PushButtonState.Default:                case PushButtonState.Normal:                    backColor = this.drawTransparentBackColor ? Color.Transparent : Color.FromArgb(192, SystemColors.ControlDark);                    outlineColor = Color.Transparent;                    arrowFillColor = Color.Black;                    arrowOutlineColor = Color.White;                    break;                case PushButtonState.Pressed:                    backColor = Color.FromArgb(192, SystemColors.Highlight);                    outlineColor = Color.FromArgb(192, SystemColors.Highlight);                    arrowFillColor = Color.Blue;                    arrowOutlineColor = Color.White;                    break;                default:                    throw new InvalidEnumArgumentException("buttonState");            }            // Draw parent background            IPaintBackground asIpb = Parent as IPaintBackground;            if (!this.drawWithGradient || asIpb == null)            {                if (asIpb != null)                {                    Rectangle screenRect = RectangleToScreen(ClientRectangle);                    Rectangle parentRect = Parent.RectangleToClient(screenRect);                    g.TranslateTransform(-Left, -Top, MatrixOrder.Append);                    asIpb.PaintBackground(g, parentRect);                    g.TranslateTransform(+Left, +Top, MatrixOrder.Append);                }                else                {                    using (SolidBrush backBrush = new SolidBrush(BackColor))                    {                        g.FillRectangle(backBrush, ClientRectangle);                    }                }            }            else            {                if (this.backBufferSurface != null &&                    (this.backBufferSurface.Width != ClientSize.Width || this.backBufferSurface.Height != ClientSize.Height))                {                    this.backBuffer.Dispose();                    this.backBuffer = null;                    this.backBufferSurface.Dispose();                    this.backBufferSurface = null;                }                if (this.backBufferSurface == null)                {                    this.backBufferSurface = new Surface(ClientSize.Width, ClientSize.Height);                    this.backBuffer = new RenderArgs(this.backBufferSurface);                }                Rectangle screenRect = RectangleToScreen(ClientRectangle);                Rectangle parentRect = Parent.RectangleToClient(screenRect);                using (Graphics bg = Graphics.FromImage(this.backBuffer.Bitmap))                {                    bg.TranslateTransform(-Left, -Top, MatrixOrder.Append);                    asIpb.PaintBackground(bg, parentRect);                }                BitmapData bitmapData = this.backBuffer.Bitmap.LockBits(                    new Rectangle(0, 0, this.backBuffer.Bitmap.Width, this.backBuffer.Bitmap.Height),                    ImageLockMode.ReadWrite,                    PixelFormat.Format32bppArgb);                int startAlpha;                int finishAlpha;                if (this.arrowDirection == ArrowDirection.Left || this.arrowDirection == ArrowDirection.Up)                {                    startAlpha = 255;                    finishAlpha = 0;                }                else if (this.arrowDirection == ArrowDirection.Right || this.ArrowDirection == ArrowDirection.Down)                {                    startAlpha = 0;                    finishAlpha = 255;                }                else                {                    throw new InvalidEnumArgumentException("this.arrowDirection");                }                unsafe                {                    if (this.arrowDirection == ArrowDirection.Left || this.arrowDirection == ArrowDirection.Right)                    {                        for (int x = 0; x < this.backBuffer.Bitmap.Width; ++x)                        {                            float lerp = (float)x / (float)(this.backBuffer.Bitmap.Width - 1);                            if (this.arrowDirection == ArrowDirection.Left)                            {                                lerp = 1.0f - (float)Math.Cos(lerp * (Math.PI / 2.0));                            }                            else                            {                                lerp = (float)Math.Sin(lerp * (Math.PI / 2.0));                            }                            byte alpha = (byte)(startAlpha + ((int)(lerp * (finishAlpha - startAlpha))));                            byte* pb = (byte*)bitmapData.Scan0.ToPointer() + (x * 4) + 3; // *4 because 4-bytes per pixel, +3 to get to alpha channel                            for (int y = 0; y < this.backBuffer.Bitmap.Height; ++y)                            {                                *pb = alpha;                                pb += bitmapData.Stride;                            }                        }                    }                    else if (this.arrowDirection == ArrowDirection.Up || this.arrowDirection == ArrowDirection.Down)                    {                        for (int y = 0; y < this.backBuffer.Bitmap.Height; ++y)                        {                            float lerp = (float)y / (float)(this.backBuffer.Bitmap.Height - 1);                            lerp = 1.0f - (float)Math.Cos(lerp * (Math.PI / 2.0));                            byte alpha = (byte)(startAlpha + ((int)(lerp * (finishAlpha - startAlpha))));                            byte* pb = (byte*)bitmapData.Scan0.ToPointer() + (y * bitmapData.Stride) + 3; // *Stride for access to start of row, +3 to get to alpha channel                            for (int x = 0; x < this.backBuffer.Bitmap.Width; ++x)                            {                                *pb = alpha;                                pb += 4; // 4 for byte size of pixel                            }                        }                    }                }                this.backBuffer.Bitmap.UnlockBits(bitmapData);                bitmapData = null;                g.DrawImage(this.backBuffer.Bitmap, new Point(0, 0));            }            using (SolidBrush fillBrush = new SolidBrush(backColor))            {                g.FillRectangle(fillBrush, ClientRectangle);            }            // Draw outline            using (Pen outlinePen = new Pen(outlineColor))            {                g.DrawRectangle(outlinePen, new Rectangle(0, 0, ClientSize.Width - 1, ClientSize.Height - 1));            }            // Draw button            g.SmoothingMode = SmoothingMode.AntiAlias;            const int arrowInset = 3;            int arrowSize = Math.Min(ClientSize.Width - arrowInset * 2, ClientSize.Height - arrowInset * 2) - 1;            PointF a;            PointF b;            PointF c;            switch (this.arrowDirection)            {                case ArrowDirection.Left:                    a = new PointF(arrowInset, ClientSize.Height / 2);                    b = new PointF(ClientSize.Width - arrowInset, (ClientSize.Height - arrowSize) / 2);                    c = new PointF(ClientSize.Width - arrowInset, (ClientSize.Height + arrowSize) / 2);                    break;                case ArrowDirection.Right:                    a = new PointF(ClientSize.Width - arrowInset, ClientSize.Height / 2);                    b = new PointF(arrowInset, (ClientSize.Height - arrowSize) / 2);                    c = new PointF(arrowInset, (ClientSize.Height + arrowSize) / 2);                    break;                case ArrowDirection.Up:                    a = new PointF(ClientSize.Width / 2, (ClientSize.Height - arrowSize) / 2);                    b = new PointF((ClientSize.Width - arrowSize) / 2, (ClientSize.Height + arrowSize) / 2);                    c = new PointF((ClientSize.Width + arrowSize) / 2, (ClientSize.Height + arrowSize) / 2);                    break;                case ArrowDirection.Down:                    a = new PointF(ClientSize.Width / 2, (ClientSize.Height + arrowSize) / 2);                    b = new PointF((ClientSize.Width - arrowSize) / 2, (ClientSize.Height - arrowSize) / 2);                    c = new PointF((ClientSize.Width + arrowSize) / 2, (ClientSize.Height - arrowSize) / 2);                    break;                default:                    throw new InvalidEnumArgumentException("this.arrowDirection");            }            // SPIKE in order to get this rendering correctly right away            if (this.arrowDirection == ArrowDirection.Down)            {                SmoothingMode oldSM = g.SmoothingMode;                g.SmoothingMode = SmoothingMode.None;                float top = b.Y - 2;                float left = b.X;                float right = c.X;                int squareCount = (int)((right - left) / 3);                Brush outlineBrush = new SolidBrush(arrowOutlineColor);                Brush interiorBrush = new SolidBrush(arrowFillColor);                g.FillRectangle(interiorBrush, left, top, right - left + 1, 3);                ++left;                while (left < right)                {                    RectangleF rect = new RectangleF(left, top + 1, 1, 1);                    g.FillRectangle(outlineBrush, rect);                    left += 2;                }                outlineBrush.Dispose();                outlineBrush = null;                interiorBrush.Dispose();                interiorBrush = null;                a.Y += 2;                b.Y += 2;                c.Y += 2;                g.SmoothingMode = oldSM;            }            if (this.reverseArrowColors)            {                Utility.Swap(ref arrowFillColor, ref arrowOutlineColor);            }            using (Brush buttonBrush = new SolidBrush(arrowFillColor))            {                g.FillPolygon(buttonBrush, new PointF[] { a, b, c });            }            using (Pen buttonPen = new Pen(arrowOutlineColor, this.arrowOutlineWidth))            {                g.DrawPolygon(buttonPen, new PointF[] { a, b, c });            }        }    }}
 |