#region ================== Namespaces using System; using System.Drawing; using System.Windows.Forms; using System.Globalization; #endregion namespace CodeImp.DoomBuilder.ColorPicker.Controls { public partial class ColorPickerControl : UserControl { private enum ChangeStyle { MouseMove, RGB, None } private const string COLOR_INFO_RGB = "RGB"; private const string COLOR_INFO_HEX = "Hex"; private const string COLOR_INFO_FLOAT = "Float"; private readonly object[] COLOR_INFO = new object[] { COLOR_INFO_RGB, COLOR_INFO_HEX, COLOR_INFO_FLOAT }; private static int colorInfoMode; private ChangeStyle changeType = ChangeStyle.None; private Point selectedPoint; private ColorWheel colorWheel; private ColorHandler.RGB RGB; public ColorHandler.RGB CurrentColor { get { return RGB; } } private bool isInUpdate; private Color startColor; //events public event EventHandler OnColorChanged; public event EventHandler OnOkPressed; public event EventHandler OnCancelPressed; public Button OkButton { get { return btnOK; } } public Button CancelButton { get { return btnCancel; } } public void Initialize(Color startColor) { this.startColor = startColor; isInUpdate = true; InitializeComponent(); isInUpdate = false; cbColorInfo.Items.AddRange(COLOR_INFO); cbColorInfo.SelectedIndex = colorInfoMode; } private void NudValueChanged(object sender, EventArgs e) { // If the R, G, or B values change, use this code to update the HSV values and invalidate // the color wheel (so it updates the pointers). // Check the isInUpdate flag to avoid recursive events when you update the NumericUpdownControls. if(!isInUpdate) { changeType = ChangeStyle.RGB; RGB = new ColorHandler.RGB((int)nudRed.Value, (int)nudGreen.Value, (int)nudBlue.Value); UpdateColorInfo(RGB); this.Invalidate(); } } private void SetRGB(ColorHandler.RGB RGB) { // Update the RGB values on the form, but don't trigger the ValueChanged event of the form. The isInUpdate // variable ensures that the event procedures exit without doing anything. isInUpdate = true; UpdateColorInfo(RGB); isInUpdate = false; } private void UpdateColorInfo(ColorHandler.RGB RGB) { this.RGB = RGB; btnOK.BackColor = ColorHandler.RGBtoColor(RGB); btnOK.ForeColor = (RGB.Red < 180 && RGB.Green < 180) ? Color.White : Color.Black; //update color info switch(cbColorInfo.SelectedItem.ToString()) { case COLOR_INFO_RGB: RefreshNudValue(nudRed, RGB.Red); RefreshNudValue(nudBlue, RGB.Blue); RefreshNudValue(nudGreen, RGB.Green); break; case COLOR_INFO_HEX: string r = RGB.Red.ToString("X02"); string g = RGB.Green.ToString("X02"); string b = RGB.Blue.ToString("X02"); isInUpdate = true; tbFloatVals.Text = r + g + b; isInUpdate = false; break; case COLOR_INFO_FLOAT: string r2 = ((float)Math.Round(RGB.Red / 255f, 2)).ToString("F02", CultureInfo.InvariantCulture); string g2 = ((float)Math.Round(RGB.Green / 255f, 2)).ToString("F02", CultureInfo.InvariantCulture); string b2 = ((float)Math.Round(RGB.Blue / 255f, 2)).ToString("F02", CultureInfo.InvariantCulture); isInUpdate = true; tbFloatVals.Text = r2 + " " + g2 + " " + b2; isInUpdate = false; break; } //dispatch event further EventHandler handler = OnColorChanged; if(handler != null) handler(this, new ColorChangedEventArgs(RGB, ColorHandler.RGBtoHSV(RGB))); } private void UpdateCancelButton(ColorHandler.RGB RGB) { btnCancel.BackColor = ColorHandler.RGBtoColor(RGB); btnCancel.ForeColor = (RGB.Red < 180 && RGB.Green < 180) ? Color.White : Color.Black; } private static void RefreshNudValue(NumericUpDown nud, int value) { // Update the value of the NumericUpDown control, if the value is different than the current value. // Refresh the control, causing an immediate repaint. if(nud.Value != value) { nud.Value = value; nud.Refresh(); } } public void SetCurrentColor(Color c) { isInUpdate = true; changeType = ChangeStyle.RGB; RGB = new ColorHandler.RGB(c.R, c.G, c.B); UpdateColorInfo(RGB); isInUpdate = false; this.Invalidate(); } public void SetInitialColor(Color c) { UpdateCancelButton(new ColorHandler.RGB(c.R, c.G, c.B)); } //events private void ColorPickerControl_Load(object sender, EventArgs e) { // Turn on double-buffering, so the form looks better. this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); this.SetStyle(ControlStyles.UserPaint, true); this.SetStyle(ControlStyles.DoubleBuffer, true); Rectangle BrightnessRectangle = new Rectangle(pnlBrightness.Location, pnlBrightness.Size); Rectangle ColorRectangle = new Rectangle(pnlColor.Location, pnlColor.Size); // Create the new ColorWheel class, indicating the locations of the color wheel itself, the // brightness area, and the position of the selected color. colorWheel = new ColorWheel(ColorRectangle, BrightnessRectangle); colorWheel.ColorChanged += ColorChanged; //set initial colors SetCurrentColor(startColor); UpdateCancelButton(RGB); } private void ColorChanged(object sender, ColorChangedEventArgs e) { SetRGB(e.RGB); } private void OnPaint(object sender, PaintEventArgs e) { // Depending on the circumstances, force a repaint of the color wheel passing different information. switch(changeType) { case ChangeStyle.MouseMove: case ChangeStyle.None: colorWheel.Draw(e.Graphics, selectedPoint); break; case ChangeStyle.RGB: colorWheel.Draw(e.Graphics, RGB); break; } } private void ColorPickerControl_MouseDown(object sender, MouseEventArgs e) { if(e.Button == MouseButtons.Left) { changeType = ChangeStyle.MouseMove; selectedPoint = new Point(e.X, e.Y); this.Invalidate(); } } private void OnMouseMove(object sender, MouseEventArgs e) { if(e.Button == MouseButtons.Left) { changeType = ChangeStyle.MouseMove; selectedPoint = new Point(e.X, e.Y); this.Invalidate(); } } private void OnMouseUp(object sender, MouseEventArgs e) { colorWheel.SetMouseUp(); changeType = ChangeStyle.None; } private void OnMouseLeave(object sender, EventArgs e) { colorWheel.SetMouseUp(); changeType = ChangeStyle.None; selectedPoint = Point.Empty; } private void btnOK_Click(object sender, EventArgs e) { //dispatch event further EventHandler handler = OnOkPressed; if(handler != null) handler(this, e); } private void btnCancel_Click(object sender, EventArgs e) { //dispatch event further EventHandler handler = OnCancelPressed; if(handler != null) handler(this, e); } private void cbColorInfo_SelectedIndexChanged(object sender, EventArgs e) { if(cbColorInfo.SelectedItem.ToString() == COLOR_INFO_RGB) { pRGB.Visible = true; tbFloatVals.Visible = false; } else { pRGB.Visible = false; tbFloatVals.Visible = true; } colorInfoMode = cbColorInfo.SelectedIndex; UpdateColorInfo(RGB); } private void tbFloatVals_TextChanged(object sender, EventArgs e) { if(isInUpdate) return; if(COLOR_INFO[colorInfoMode].ToString() == COLOR_INFO_FLOAT) { string[] parts = tbFloatVals.Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if(parts.Length != 3) return; ColorHandler.RGB rgb = new ColorHandler.RGB(); float c; if(!float.TryParse(parts[0].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out c)) return; rgb.Red = (int)(General.Clamp(Math.Abs(c), 0.0f, 1.0f) * 255); if(!float.TryParse(parts[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out c)) return; rgb.Green = (int)(General.Clamp(Math.Abs(c), 0.0f, 1.0f) * 255); if(!float.TryParse(parts[2].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out c)) return; rgb.Blue = (int)(General.Clamp(Math.Abs(c), 0.0f, 1.0f) * 255); changeType = ChangeStyle.RGB; UpdateColorInfo(rgb); this.Invalidate(); } else if(COLOR_INFO[colorInfoMode].ToString() == COLOR_INFO_HEX) { string hexColor = tbFloatVals.Text.Trim().Replace("-", ""); if(hexColor.Length != 6) return; ColorHandler.RGB rgb = new ColorHandler.RGB(); int color; string colorStr = hexColor.Substring(0, 2); if(!int.TryParse(colorStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out color)) return; rgb.Red = color; colorStr = hexColor.Substring(2, 2); if(!int.TryParse(colorStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out color)) return; rgb.Green = color; colorStr = hexColor.Substring(4, 2); if(!int.TryParse(colorStr, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out color)) return; rgb.Blue = color; changeType = ChangeStyle.RGB; UpdateColorInfo(rgb); this.Invalidate(); } } } }