LevelOS-Core/CPM/ComboBox/LV_COMBOBOX.cs

243 lines
8.6 KiB
C#

using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace CPM
{
/// <summary>
/// ComboBox customizado do sistema LevelOS.
/// Segue o mesmo padrão visual do LV_TEXTBOX1.
/// </summary>
public class LV_COMBOBOX : UserControl
{
// ── CAMPOS INTERNOS ───────────────────────────────────────────────────
private ComboBox _combo;
private bool _isFocused = false;
// ── PROPRIEDADES VISUAIS ──────────────────────────────────────────────
private Color _borderColor = Color.FromArgb(226, 232, 240);
private Color _borderFocusColor = Color.FromArgb(37, 99, 235);
private Color _arrowColor = Color.FromArgb(100, 116, 139);
private int _borderRadius = 6;
private int _borderSize = 1;
public Color BorderColor
{
get => _borderColor;
set { _borderColor = value; Invalidate(); }
}
public Color BorderFocusColor
{
get => _borderFocusColor;
set { _borderFocusColor = value; Invalidate(); }
}
public Color ArrowColor
{
get => _arrowColor;
set { _arrowColor = value; Invalidate(); }
}
public int BorderRadius
{
get => _borderRadius;
set { _borderRadius = value; Invalidate(); }
}
public int BorderSize
{
get => _borderSize;
set { _borderSize = value; Invalidate(); }
}
// ── PROPRIEDADES DO COMBOBOX ──────────────────────────────────────────
public ComboBoxStyle DropDownStyle
{
get => _combo.DropDownStyle;
set => _combo.DropDownStyle = value;
}
public System.Windows.Forms.ComboBox.ObjectCollection Items => _combo.Items;
public object? SelectedItem
{
get => _combo.SelectedItem;
set => _combo.SelectedItem = value;
}
public int SelectedIndex
{
get => _combo.SelectedIndex;
set => _combo.SelectedIndex = value;
}
public string Text
{
get => _combo.Text;
set => _combo.Text = value;
}
public override Font Font
{
get => base.Font;
set
{
base.Font = value;
_combo.Font = value;
AjustarAltura();
}
}
public new bool Enabled
{
get => base.Enabled;
set
{
base.Enabled = value;
_combo.Enabled = value;
BackColor = value ? Color.White : Color.FromArgb(241, 245, 249);
Invalidate();
}
}
// ── EVENTOS REPASSADOS ────────────────────────────────────────────────
public event EventHandler? SelectedIndexChanged
{
add => _combo.SelectedIndexChanged += value;
remove => _combo.SelectedIndexChanged -= value;
}
public new event EventHandler? TextChanged
{
add => _combo.TextChanged += value;
remove => _combo.TextChanged -= value;
}
// ── CONSTRUTOR ────────────────────────────────────────────────────────
public LV_COMBOBOX()
{
_combo = new ComboBox
{
DropDownStyle = ComboBoxStyle.DropDownList,
FlatStyle = FlatStyle.Flat,
Font = new Font("Segoe UI", 9f),
BackColor = Color.White,
ForeColor = Color.FromArgb(30, 41, 59),
Dock = DockStyle.None,
IntegralHeight = false
};
// Configura o controle pai
Padding = new Padding(8, 0, 30, 0); // espaço para seta customizada
BackColor = Color.White;
ForeColor = Color.FromArgb(30, 41, 59);
Font = new Font("Segoe UI", 9f);
Size = new Size(200, 36);
DoubleBuffered = true;
Controls.Add(_combo);
AjustarAltura();
// Eventos de foco
_combo.GotFocus += (s, e) => { _isFocused = true; Invalidate(); };
_combo.LostFocus += (s, e) => { _isFocused = false; Invalidate(); };
// Propaga clique na área do controle para o combo
this.Click += (s, e) => _combo.Focus();
_combo.DropDown += (s, e) => Invalidate();
_combo.DropDownClosed += (s, e) => Invalidate();
}
// ── AJUSTE DE ALTURA ──────────────────────────────────────────────────
private void AjustarAltura()
{
int altura = _combo.PreferredHeight;
this.Height = altura + 10;
_combo.Width = this.Width - 38; // reserva espaço para seta
_combo.Left = 8;
_combo.Top = (this.Height - _combo.Height) / 2;
}
// ── RESIZE ────────────────────────────────────────────────────────────
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
_combo.Width = this.Width - 38;
_combo.Left = 8;
_combo.Top = (this.Height - _combo.Height) / 2;
Invalidate();
}
// ── PAINT ─────────────────────────────────────────────────────────────
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
var cor = _isFocused ? _borderFocusColor : _borderColor;
var rect = new Rectangle(0, 0, Width - 1, Height - 1);
var bgRect = new Rectangle(1, 1, Width - 2, Height - 2);
// Fundo
using var bgBrush = new SolidBrush(Enabled ? BackColor : Color.FromArgb(241, 245, 249));
g.FillRoundedRectangle(bgBrush, bgRect, _borderRadius);
// Borda
using var pen = new Pen(cor, _borderSize);
g.DrawRoundedRectangle(pen, rect, _borderRadius);
// Seta customizada
DesenharSeta(g);
}
private void DesenharSeta(Graphics g)
{
int arrowX = Width - 22;
int arrowY = Height / 2;
using var brush = new SolidBrush(_isFocused ? _borderFocusColor : _arrowColor);
var pontos = new Point[]
{
new Point(arrowX - 5, arrowY - 3),
new Point(arrowX + 5, arrowY - 3),
new Point(arrowX, arrowY + 4)
};
g.FillPolygon(brush, pontos);
}
}
// ── EXTENSÕES DE GRAPHICS PARA ROUNDED RECT ───────────────────────────────
internal static class GraphicsExtensions
{
public static void FillRoundedRectangle(this Graphics g, Brush brush, Rectangle rect, int radius)
{
using var path = GetRoundedPath(rect, radius);
g.FillPath(brush, path);
}
public static void DrawRoundedRectangle(this Graphics g, Pen pen, Rectangle rect, int radius)
{
using var path = GetRoundedPath(rect, radius);
g.DrawPath(pen, path);
}
private static GraphicsPath GetRoundedPath(Rectangle rect, int radius)
{
int r = Math.Min(radius, Math.Min(rect.Width, rect.Height) / 2);
var d = r * 2;
var path = new GraphicsPath();
path.AddArc(rect.X, rect.Y, d, d, 180, 90);
path.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
path.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90);
path.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
path.CloseFigure();
return path;
}
}
}