windowcapture
исходный код / UI/DarkColorPicker.cs

DarkColorPicker.cs

534 строк · 20,183 байт · модуль UI
  1using System;
  2using System.Drawing;
  3using System.Windows.Forms;
  4using WindowCapture.Helpers;
  5
  6namespace WindowCapture.UI
  7{
  8    // Custom dark color picker dialog
  9    public class DarkColorPicker : Form
 10    {
 11        private static readonly Color DarkBg = Color.FromArgb(30, 30, 32);
 12        private static readonly Color DarkPanel = Color.FromArgb(40, 40, 42);
 13        private static readonly Color DarkControl = Color.FromArgb(50, 50, 52);
 14        private static readonly Color TextColor = Color.FromArgb(200, 200, 200);
 15        private static readonly Color DimText = Color.FromArgb(120, 120, 120);
 16
 17        public Color SelectedColor { get; private set; }
 18
 19        private Panel colorSquare;      // Saturation/Brightness square
 20        private Panel hueSlider;        // Hue vertical slider
 21        private Panel alphaSlider;      // Alpha vertical slider
 22        private Panel previewPanel;     // Color preview
 23        private DarkNumeric nudR, nudG, nudB, nudA;
 24        private TextBox txtHex;
 25
 26        private Bitmap colorSquareBmp;
 27        private Bitmap hueSliderBmp;
 28
 29        private float currentHue = 0;       // 0-360
 30        private float currentSat = 1;       // 0-1
 31        private float currentBri = 1;       // 0-1
 32        private int currentAlpha = 255;
 33
 34        private bool draggingSquare, draggingHue, draggingAlpha;
 35        private bool updatingControls;
 36        private bool closing;
 37
 38        public DarkColorPicker(Color initialColor)
 39        {
 40            SelectedColor = initialColor;
 41            currentAlpha = initialColor.A;
 42
 43            // Convert RGB to HSB
 44            ColorToHSB(initialColor, out currentHue, out currentSat, out currentBri);
 45
 46            InitializeComponents();
 47            UpdateAllFromHSB();
 48        }
 49
 50        private void InitializeComponents()
 51        {
 52            Text = "Color Picker";
 53            Size = new Size(420, 340);
 54            FormBorderStyle = FormBorderStyle.None;
 55            StartPosition = FormStartPosition.CenterParent;
 56            BackColor = DarkBg;
 57            DoubleBuffered = true;
 58
 59            // Title bar
 60            Panel titleBar = new Panel();
 61            titleBar.Location = new Point(0, 0);
 62            titleBar.Size = new Size(Width, 28);
 63            titleBar.BackColor = DarkPanel;
 64
 65            Label lblTitle = new Label();
 66            lblTitle.Text = "Color Picker";
 67            lblTitle.Location = new Point(10, 5);
 68            lblTitle.AutoSize = true;
 69            lblTitle.ForeColor = TextColor;
 70            lblTitle.Font = new Font("Segoe UI", 9f, FontStyle.Bold);
 71            titleBar.Controls.Add(lblTitle);
 72
 73            // Dragging
 74            bool dragging = false;
 75            Point dragStart = Point.Empty;
 76            titleBar.MouseDown += (s, e) => { dragging = true; dragStart = e.Location; };
 77            titleBar.MouseMove += (s, e) => { if (dragging) Location = new Point(Location.X + e.X - dragStart.X, Location.Y + e.Y - dragStart.Y); };
 78            titleBar.MouseUp += (s, e) => { dragging = false; };
 79            lblTitle.MouseDown += (s, e) => { dragging = true; dragStart = e.Location; };
 80            lblTitle.MouseMove += (s, e) => { if (dragging) Location = new Point(Location.X + e.X - dragStart.X, Location.Y + e.Y - dragStart.Y); };
 81            lblTitle.MouseUp += (s, e) => { dragging = false; };
 82
 83            Controls.Add(titleBar);
 84
 85            // Color square (Saturation X, Brightness Y)
 86            colorSquare = new Panel();
 87            colorSquare.Location = new Point(15, 40);
 88            colorSquare.Size = new Size(200, 200);
 89            colorSquare.BackColor = DarkControl;
 90            colorSquare.MouseDown += ColorSquare_MouseDown;
 91            colorSquare.MouseMove += ColorSquare_MouseMove;
 92            colorSquare.MouseUp += (s, e) => draggingSquare = false;
 93            colorSquare.Paint += ColorSquare_Paint;
 94            Controls.Add(colorSquare);
 95
 96            // Hue slider
 97            hueSlider = new Panel();
 98            hueSlider.Location = new Point(225, 40);
 99            hueSlider.Size = new Size(25, 200);
100            hueSlider.BackColor = DarkControl;
101            hueSlider.MouseDown += HueSlider_MouseDown;
102            hueSlider.MouseMove += HueSlider_MouseMove;
103            hueSlider.MouseUp += (s, e) => draggingHue = false;
104            hueSlider.Paint += HueSlider_Paint;
105            Controls.Add(hueSlider);
106
107            // Alpha slider
108            alphaSlider = new Panel();
109            alphaSlider.Location = new Point(260, 40);
110            alphaSlider.Size = new Size(25, 200);
111            alphaSlider.BackColor = DarkControl;
112            alphaSlider.MouseDown += AlphaSlider_MouseDown;
113            alphaSlider.MouseMove += AlphaSlider_MouseMove;
114            alphaSlider.MouseUp += (s, e) => draggingAlpha = false;
115            alphaSlider.Paint += AlphaSlider_Paint;
116            Controls.Add(alphaSlider);
117
118            // Preview
119            Label lblPreview = new Label();
120            lblPreview.Text = "Preview:";
121            lblPreview.Location = new Point(300, 40);
122            lblPreview.AutoSize = true;
123            lblPreview.ForeColor = TextColor;
124            lblPreview.Font = new Font("Segoe UI", 9f);
125            Controls.Add(lblPreview);
126
127            previewPanel = new Panel();
128            previewPanel.Location = new Point(300, 60);
129            previewPanel.Size = new Size(100, 60);
130            previewPanel.Paint += PreviewPanel_Paint;
131            Controls.Add(previewPanel);
132
133            // RGB inputs
134            int inputY = 135;
135            AddLabel("R:", 300, inputY);
136            nudR = AddNumericInput(330, inputY, 0, 255, SelectedColor.R);
137            nudR.ValueChanged += (s, e) => { if (!updatingControls) UpdateFromRGB(); };
138            inputY += 28;
139
140            AddLabel("G:", 300, inputY);
141            nudG = AddNumericInput(330, inputY, 0, 255, SelectedColor.G);
142            nudG.ValueChanged += (s, e) => { if (!updatingControls) UpdateFromRGB(); };
143            inputY += 28;
144
145            AddLabel("B:", 300, inputY);
146            nudB = AddNumericInput(330, inputY, 0, 255, SelectedColor.B);
147            nudB.ValueChanged += (s, e) => { if (!updatingControls) UpdateFromRGB(); };
148            inputY += 28;
149
150            AddLabel("A:", 300, inputY);
151            nudA = AddNumericInput(330, inputY, 0, 255, SelectedColor.A);
152            nudA.ValueChanged += (s, e) => { if (!updatingControls) { currentAlpha = nudA.Value; UpdatePreview(); } };
153
154            // Hex input
155            AddLabel("Hex:", 15, 252);
156            txtHex = new TextBox();
157            txtHex.Location = new Point(50, 250);
158            txtHex.Size = new Size(80, 24);
159            txtHex.BackColor = DarkControl;
160            txtHex.ForeColor = TextColor;
161            txtHex.BorderStyle = BorderStyle.FixedSingle;
162            txtHex.Font = new Font("Consolas", 9f);
163            txtHex.Text = ColorToHex(SelectedColor);
164            txtHex.TextChanged += TxtHex_TextChanged;
165            Controls.Add(txtHex);
166
167            // Buttons
168            Button btnOK = CreateButton("OK", 220, 290, 80, 30);
169            btnOK.Click += (s, e) => { if (closing) return; closing = true; DialogResult = DialogResult.OK; Close(); };
170            Controls.Add(btnOK);
171
172            Button btnCancel = CreateButton("Cancel", 310, 290, 80, 30);
173            btnCancel.Click += (s, e) => { if (closing) return; closing = true; DialogResult = DialogResult.Cancel; Close(); };
174            Controls.Add(btnCancel);
175
176            // Generate bitmaps
177            GenerateHueSliderBitmap();
178            GenerateColorSquareBitmap();
179        }
180
181        private Label AddLabel(string text, int x, int y)
182        {
183            Label lbl = new Label();
184            lbl.Text = text;
185            lbl.Location = new Point(x, y + 3);
186            lbl.AutoSize = true;
187            lbl.ForeColor = TextColor;
188            lbl.Font = new Font("Segoe UI", 9f);
189            Controls.Add(lbl);
190            return lbl;
191        }
192
193        private DarkNumeric AddNumericInput(int x, int y, int min, int max, int value)
194        {
195            DarkNumeric nud = new DarkNumeric();
196            nud.Location = new Point(x, y);
197            nud.Size = new Size(60, 24);
198            nud.Minimum = min;
199            nud.Maximum = max;
200            nud.Value = value;
201            Controls.Add(nud);
202            return nud;
203        }
204
205        private Button CreateButton(string text, int x, int y, int w, int h)
206        {
207            var btn = new Button();
208            btn.Text = text;
209            btn.Location = new Point(x, y);
210            btn.Size = new Size(w, h);
211            btn.FlatStyle = FlatStyle.Flat;
212            btn.FlatAppearance.BorderSize = 0;
213            btn.BackColor = DarkControl;
214            btn.ForeColor = TextColor;
215            btn.Font = new Font("Segoe UI", 9f);
216            btn.Cursor = Cursors.Hand;
217            return btn;
218        }
219
220        private void GenerateHueSliderBitmap()
221        {
222            hueSliderBmp = new Bitmap(hueSlider.Width, hueSlider.Height);
223            using (var g = Graphics.FromImage(hueSliderBmp))
224            {
225                for (int y = 0; y < hueSliderBmp.Height; y++)
226                {
227                    float hue = y * 360f / hueSliderBmp.Height;
228                    Color c = HSBToColor(hue, 1, 1);
229                    using (var pen = new Pen(c))
230                        g.DrawLine(pen, 0, y, hueSliderBmp.Width, y);
231                }
232            }
233        }
234
235        private void GenerateColorSquareBitmap()
236        {
237            int W = colorSquare.Width, H = colorSquare.Height;
238            if (W <= 0 || H <= 0) return;
239
240            // Fill via LockBits instead of 40k SetPixel calls (which made dragging the hue slider lag).
241            var bmp = new Bitmap(W, H, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
242            var data = bmp.LockBits(new Rectangle(0, 0, W, H),
243                System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
244            try
245            {
246                unsafe
247                {
248                    byte* basePtr = (byte*)data.Scan0;
249                    for (int y = 0; y < H; y++)
250                    {
251                        byte* row = basePtr + y * data.Stride;
252                        float bri = 1f - (float)y / H;
253                        for (int x = 0; x < W; x++)
254                        {
255                            float sat = (float)x / W;
256                            Color c = HSBToColor(currentHue, sat, bri);
257                            int i = x * 4;
258                            row[i] = c.B; row[i + 1] = c.G; row[i + 2] = c.R; row[i + 3] = 255;
259                        }
260                    }
261                }
262            }
263            finally { bmp.UnlockBits(data); }
264
265            // Dispose the previous bitmap (the old code leaked it by reassigning).
266            if (colorSquareBmp != null) colorSquareBmp.Dispose();
267            colorSquareBmp = bmp;
268        }
269
270        private void ColorSquare_Paint(object sender, PaintEventArgs e)
271        {
272            if (colorSquareBmp != null)
273                e.Graphics.DrawImage(colorSquareBmp, 0, 0);
274
275            // Draw cursor
276            int cx = (int)(currentSat * colorSquare.Width);
277            int cy = (int)((1 - currentBri) * colorSquare.Height);
278            using (var pen = new Pen(Color.White, 2))
279            {
280                e.Graphics.DrawEllipse(pen, cx - 6, cy - 6, 12, 12);
281            }
282            using (var pen = new Pen(Color.Black, 1))
283            {
284                e.Graphics.DrawEllipse(pen, cx - 5, cy - 5, 10, 10);
285            }
286        }
287
288        private void HueSlider_Paint(object sender, PaintEventArgs e)
289        {
290            if (hueSliderBmp != null)
291                e.Graphics.DrawImage(hueSliderBmp, 0, 0);
292
293            // Draw cursor
294            int cy = (int)(currentHue / 360 * hueSlider.Height);
295            using (var pen = new Pen(Color.White, 2))
296            {
297                e.Graphics.DrawRectangle(pen, 0, cy - 2, hueSlider.Width - 1, 4);
298            }
299        }
300
301        private void AlphaSlider_Paint(object sender, PaintEventArgs e)
302        {
303            // Checkerboard background
304            int checkSize = 5;
305            for (int x = 0; x < alphaSlider.Width; x += checkSize)
306            {
307                for (int y = 0; y < alphaSlider.Height; y += checkSize)
308                {
309                    bool isWhite = ((x / checkSize) + (y / checkSize)) % 2 == 0;
310                    using (var brush = new SolidBrush(isWhite ? Color.White : Color.LightGray))
311                        e.Graphics.FillRectangle(brush, x, y, checkSize, checkSize);
312                }
313            }
314
315            // Alpha gradient
316            Color baseColor = HSBToColor(currentHue, currentSat, currentBri);
317            for (int y = 0; y < alphaSlider.Height; y++)
318            {
319                int alpha = 255 - (y * 255 / alphaSlider.Height);
320                Color c = Color.FromArgb(alpha, baseColor);
321                using (var pen = new Pen(c))
322                    e.Graphics.DrawLine(pen, 0, y, alphaSlider.Width, y);
323            }
324
325            // Draw cursor
326            int cy = (255 - currentAlpha) * alphaSlider.Height / 255;
327            using (var pen = new Pen(Color.White, 2))
328            {
329                e.Graphics.DrawRectangle(pen, 0, cy - 2, alphaSlider.Width - 1, 4);
330            }
331        }
332
333        private void PreviewPanel_Paint(object sender, PaintEventArgs e)
334        {
335            // Checkerboard for transparency
336            int checkSize = 8;
337            for (int x = 0; x < previewPanel.Width; x += checkSize)
338            {
339                for (int y = 0; y < previewPanel.Height; y += checkSize)
340                {
341                    bool isWhite = ((x / checkSize) + (y / checkSize)) % 2 == 0;
342                    using (var brush = new SolidBrush(isWhite ? Color.White : Color.LightGray))
343                        e.Graphics.FillRectangle(brush, x, y, checkSize, checkSize);
344                }
345            }
346
347            // Draw color
348            using (var brush = new SolidBrush(SelectedColor))
349                e.Graphics.FillRectangle(brush, 0, 0, previewPanel.Width, previewPanel.Height);
350
351            // Border
352            using (var pen = new Pen(DarkPanel, 1))
353                e.Graphics.DrawRectangle(pen, 0, 0, previewPanel.Width - 1, previewPanel.Height - 1);
354        }
355
356        private void ColorSquare_MouseDown(object sender, MouseEventArgs e)
357        {
358            draggingSquare = true;
359            UpdateFromSquare(e.Location);
360        }
361
362        private void ColorSquare_MouseMove(object sender, MouseEventArgs e)
363        {
364            if (draggingSquare) UpdateFromSquare(e.Location);
365        }
366
367        private void UpdateFromSquare(Point pt)
368        {
369            currentSat = Math.Max(0, Math.Min(1, (float)pt.X / colorSquare.Width));
370            currentBri = Math.Max(0, Math.Min(1, 1 - (float)pt.Y / colorSquare.Height));
371            UpdateAllFromHSB();
372        }
373
374        private void HueSlider_MouseDown(object sender, MouseEventArgs e)
375        {
376            draggingHue = true;
377            UpdateFromHueSlider(e.Location);
378        }
379
380        private void HueSlider_MouseMove(object sender, MouseEventArgs e)
381        {
382            if (draggingHue) UpdateFromHueSlider(e.Location);
383        }
384
385        private void UpdateFromHueSlider(Point pt)
386        {
387            currentHue = Math.Max(0, Math.Min(360, (float)pt.Y / hueSlider.Height * 360));
388            GenerateColorSquareBitmap();
389            UpdateAllFromHSB();
390        }
391
392        private void AlphaSlider_MouseDown(object sender, MouseEventArgs e)
393        {
394            draggingAlpha = true;
395            UpdateFromAlphaSlider(e.Location);
396        }
397
398        private void AlphaSlider_MouseMove(object sender, MouseEventArgs e)
399        {
400            if (draggingAlpha) UpdateFromAlphaSlider(e.Location);
401        }
402
403        private void UpdateFromAlphaSlider(Point pt)
404        {
405            currentAlpha = Math.Max(0, Math.Min(255, 255 - pt.Y * 255 / alphaSlider.Height));
406            UpdateAllFromHSB();
407        }
408
409        private void UpdateFromRGB()
410        {
411            Color c = Color.FromArgb(currentAlpha, nudR.Value, nudG.Value, nudB.Value);
412            ColorToHSB(c, out currentHue, out currentSat, out currentBri);
413            GenerateColorSquareBitmap();
414            UpdateAllFromHSB();
415        }
416
417        private void TxtHex_TextChanged(object sender, EventArgs e)
418        {
419            if (updatingControls) return;
420            string hex = txtHex.Text.Trim().TrimStart('#');
421            if (hex.Length == 6 || hex.Length == 8)
422            {
423                try
424                {
425                    int argb = Convert.ToInt32(hex.Length == 8 ? hex : "FF" + hex, 16);
426                    Color c = Color.FromArgb(argb);
427                    currentAlpha = c.A;
428                    ColorToHSB(c, out currentHue, out currentSat, out currentBri);
429                    GenerateColorSquareBitmap();
430                    UpdateAllFromHSB();
431                }
432                catch { }
433            }
434        }
435
436        private void UpdateAllFromHSB()
437        {
438            updatingControls = true;
439
440            Color rgb = HSBToColor(currentHue, currentSat, currentBri);
441            SelectedColor = Color.FromArgb(currentAlpha, rgb);
442
443            nudR.Value = rgb.R;
444            nudG.Value = rgb.G;
445            nudB.Value = rgb.B;
446            nudA.Value = currentAlpha;
447            txtHex.Text = ColorToHex(SelectedColor);
448
449            updatingControls = false;
450
451            UpdatePreview();
452        }
453
454        private void UpdatePreview()
455        {
456            Color rgb = HSBToColor(currentHue, currentSat, currentBri);
457            SelectedColor = Color.FromArgb(currentAlpha, rgb);
458
459            colorSquare.Invalidate();
460            hueSlider.Invalidate();
461            alphaSlider.Invalidate();
462            previewPanel.Invalidate();
463        }
464
465        private static void ColorToHSB(Color color, out float hue, out float sat, out float bri)
466        {
467            int max = Math.Max(color.R, Math.Max(color.G, color.B));
468            int min = Math.Min(color.R, Math.Min(color.G, color.B));
469
470            bri = max / 255f;
471            sat = (max == 0) ? 0 : (max - min) / (float)max;
472
473            if (max == min)
474            {
475                hue = 0;
476            }
477            else
478            {
479                float delta = max - min;
480                if (max == color.R)
481                    hue = (color.G - color.B) / delta + (color.G < color.B ? 6 : 0);
482                else if (max == color.G)
483                    hue = (color.B - color.R) / delta + 2;
484                else
485                    hue = (color.R - color.G) / delta + 4;
486                hue *= 60;
487            }
488        }
489
490        private static Color HSBToColor(float hue, float sat, float bri)
491        {
492            if (sat == 0)
493            {
494                int gray = (int)(bri * 255);
495                return Color.FromArgb(gray, gray, gray);
496            }
497
498            float h = hue / 60f;
499            int i = (int)Math.Floor(h) % 6;
500            float f = h - (float)Math.Floor(h);
501            float p = bri * (1 - sat);
502            float q = bri * (1 - sat * f);
503            float t = bri * (1 - sat * (1 - f));
504
505            float r, g, b;
506            switch (i)
507            {
508                case 0: r = bri; g = t; b = p; break;
509                case 1: r = q; g = bri; b = p; break;
510                case 2: r = p; g = bri; b = t; break;
511                case 3: r = p; g = q; b = bri; break;
512                case 4: r = t; g = p; b = bri; break;
513                default: r = bri; g = p; b = q; break;
514            }
515
516            return Color.FromArgb((int)(r * 255), (int)(g * 255), (int)(b * 255));
517        }
518
519        private static string ColorToHex(Color c)
520        {
521            return ColorHelper.ColorToHexWeb(c);
522        }
523
524        protected override void Dispose(bool disposing)
525        {
526            if (disposing)
527            {
528                if (colorSquareBmp != null) colorSquareBmp.Dispose();
529                if (hueSliderBmp != null) hueSliderBmp.Dispose();
530            }
531            base.Dispose(disposing);
532        }
533    }
534}