using PaintDotNet.SystemLayer; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; namespace PaintDotNet { public sealed class SwatchControl : Control { private List colors = new List(); private const int defaultUnscaledSwatchSize = 12; private int unscaledSwatchSize = defaultUnscaledSwatchSize; private bool mouseDown = false; private int mouseDownIndex = -1; private bool blinkHighlight = false; private const int blinkInterval = 500; private System.Windows.Forms.Timer blinkHighlightTimer; [Browsable(false)] public bool BlinkHighlight { get { return this.blinkHighlight; } set { this.blinkHighlight = value; this.blinkHighlightTimer.Enabled = value; Invalidate(); } } public event EventHandler ColorsChanged; private void OnColorsChanged() { if (ColorsChanged != null) { ColorsChanged(this, EventArgs.Empty); } } [Browsable(false)] public ColorBgra[] Colors { get { return this.colors.ToArray(); } set { this.colors = new List(value); this.mouseDown = false; Invalidate(); OnColorsChanged(); } } [DefaultValue(defaultUnscaledSwatchSize)] [Browsable(true)] public int UnscaledSwatchSize { get { return this.unscaledSwatchSize; } set { this.unscaledSwatchSize = value; this.mouseDown = false; Invalidate(); } } public event EventHandler>> ColorClicked; private void OnColorClicked(int index, MouseButtons buttons) { if (ColorClicked != null) { ColorClicked(this, new EventArgs>(Pair.Create(index, buttons))); } } public SwatchControl() { InitializeComponent(); } private void InitializeComponent() { this.blinkHighlightTimer = new Timer(); this.blinkHighlightTimer.Tick += new EventHandler(BlinkHighlightTimer_Tick); this.blinkHighlightTimer.Enabled = false; this.blinkHighlightTimer.Interval = blinkInterval; this.DoubleBuffered = true; this.ResizeRedraw = true; } private void BlinkHighlightTimer_Tick(object sender, EventArgs e) { Invalidate(); } protected override void Dispose(bool disposing) { if (disposing) { if (this.blinkHighlightTimer != null) { this.blinkHighlightTimer.Dispose(); this.blinkHighlightTimer = null; } } base.Dispose(disposing); } private int MouseXYToColorIndex(int x, int y) { if (x < 0 || y < 0 || x >= ClientSize.Width || y >= ClientSize.Height) { return -1; } int scaledSwatchSize = UI.ScaleWidth(this.unscaledSwatchSize); int swatchColumns = this.ClientSize.Width / scaledSwatchSize; int row = y / scaledSwatchSize; int col = x / scaledSwatchSize; int index = col + (row * swatchColumns); // Make sure they aren't on the last item of a row that actually got clipped off if (col == swatchColumns) { index = -1; } return index; } protected override void OnMouseLeave(EventArgs e) { this.mouseDown = false; Invalidate(); base.OnMouseLeave(e); } protected override void OnMouseDown(MouseEventArgs e) { this.mouseDown = true; this.mouseDownIndex = MouseXYToColorIndex(e.X, e.Y); Invalidate(); base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { int colorIndex = MouseXYToColorIndex(e.X, e.Y); if (colorIndex == this.mouseDownIndex && colorIndex >= 0 && colorIndex < this.colors.Count) { OnColorClicked(colorIndex, e.Button); } this.mouseDown = false; Invalidate(); base.OnMouseUp(e); } protected override void OnMouseMove(MouseEventArgs e) { Invalidate(); base.OnMouseMove(e); } protected override void OnPaint(PaintEventArgs e) { e.Graphics.CompositingMode = CompositingMode.SourceOver; int scaledSwatchSize = UI.ScaleWidth(this.unscaledSwatchSize); int swatchColumns = this.ClientSize.Width / scaledSwatchSize; Point mousePt = Control.MousePosition; mousePt = PointToClient(mousePt); int activeIndex = MouseXYToColorIndex(mousePt.X, mousePt.Y); for (int i = 0; i < this.colors.Count; ++i) { ColorBgra c = this.colors[i]; int swatchX = i % swatchColumns; int swatchY = i / swatchColumns; Rectangle swatchRect = new Rectangle( swatchX * scaledSwatchSize, swatchY * scaledSwatchSize, scaledSwatchSize, scaledSwatchSize); PushButtonState state; if (this.mouseDown) { if (i == this.mouseDownIndex) { state = PushButtonState.Pressed; } else { state = PushButtonState.Normal; } } else if (i == activeIndex) { state = PushButtonState.Hot; } else { state = PushButtonState.Normal; } bool drawOutline; switch (state) { case PushButtonState.Hot: drawOutline = true; break; case PushButtonState.Pressed: drawOutline = false; break; case PushButtonState.Default: case PushButtonState.Disabled: case PushButtonState.Normal: drawOutline = false; break; default: throw new InvalidEnumArgumentException(); } Utility.DrawColorRectangle(e.Graphics, swatchRect, c.ToColor(), drawOutline); } if (this.blinkHighlight) { int period = (Math.Abs(Environment.TickCount) / blinkInterval) % 2; Color color; switch (period) { case 0: color = SystemColors.Window; break; case 1: color = SystemColors.Highlight; break; default: throw new InvalidOperationException(); } using (Pen pen = new Pen(color)) { e.Graphics.DrawRectangle(pen, new Rectangle(0, 0, Width - 1, Height - 1)); } } base.OnPaint(e); } } }