2026-09-21 10:40:32 +02:00
|
|
|
using System.Drawing.Drawing2D;
|
|
|
|
|
|
2026-09-21 11:18:43 +02:00
|
|
|
namespace PrepStep;
|
2026-09-21 10:40:32 +02:00
|
|
|
|
|
|
|
|
/// <summary>Stile bottoni condiviso tra la form principale e i dialog secondari: piatto con
|
|
|
|
|
/// bordo sottile e hover, per un aspetto coerente sia per l'azione primaria di ogni fase
|
|
|
|
|
/// (blu pieno) sia per le azioni secondarie, al posto del bottone grigio di default.</summary>
|
|
|
|
|
internal static class UiStile
|
|
|
|
|
{
|
2026-09-21 14:16:32 +02:00
|
|
|
/// <summary>Blu del bottone primario, esposto qui per essere riusato anche da elementi
|
2026-09-22 10:02:14 +02:00
|
|
|
/// non-bottone (es. il colore di selezione delle griglie in Form1) invece di duplicare
|
|
|
|
|
/// l'RGB.</summary>
|
2026-09-21 14:16:32 +02:00
|
|
|
public static readonly Color ColorePrimario = Color.FromArgb(0, 99, 177);
|
|
|
|
|
|
2026-09-22 15:50:29 +02:00
|
|
|
/// <summary>Sfondo/bordo delle card di stato (es. riepilogo Database/cartella di lavoro in
|
|
|
|
|
/// fase 1): un grigio-azzurro appena percettibile rispetto al bianco del resto della
|
|
|
|
|
/// pagina, sufficiente a separare "stato attuale" da "istruzioni" senza ombre/gradienti.</summary>
|
|
|
|
|
public static readonly Color ColoreSfondoCard = Color.FromArgb(247, 249, 251);
|
|
|
|
|
public static readonly Color ColoreBordoCard = Color.FromArgb(226, 231, 236);
|
|
|
|
|
|
2026-09-21 10:40:32 +02:00
|
|
|
// AutoSize+Padding invece di una Size fissa in pixel: il bottone si dimensiona sempre
|
|
|
|
|
// al proprio testo (a qualunque lunghezza/font), niente più testo che va a capo e viene
|
|
|
|
|
// tagliato in verticale quando non ci sta nella larghezza indovinata a mano.
|
|
|
|
|
private static void DimensionamentoAutomatico(Button b)
|
|
|
|
|
{
|
|
|
|
|
b.AutoSize = true;
|
|
|
|
|
b.AutoSizeMode = AutoSizeMode.GrowAndShrink;
|
|
|
|
|
b.Padding = new Padding(18, 4, 18, 4);
|
|
|
|
|
b.MinimumSize = new Size(0, 32);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Primario(Button b)
|
|
|
|
|
{
|
|
|
|
|
DimensionamentoAutomatico(b);
|
|
|
|
|
b.FlatStyle = FlatStyle.Flat;
|
|
|
|
|
b.FlatAppearance.BorderSize = 0;
|
|
|
|
|
b.FlatAppearance.MouseOverBackColor = Color.FromArgb(0, 84, 152);
|
|
|
|
|
b.FlatAppearance.MouseDownBackColor = Color.FromArgb(0, 70, 130);
|
2026-09-21 14:16:32 +02:00
|
|
|
b.BackColor = ColorePrimario;
|
2026-09-21 10:40:32 +02:00
|
|
|
b.ForeColor = Color.White;
|
|
|
|
|
b.Font = new Font(b.Font, FontStyle.Bold);
|
|
|
|
|
b.Cursor = Cursors.Hand;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Secondario(Button b)
|
|
|
|
|
{
|
|
|
|
|
DimensionamentoAutomatico(b);
|
|
|
|
|
b.FlatStyle = FlatStyle.Flat;
|
|
|
|
|
b.FlatAppearance.BorderColor = Color.FromArgb(190, 195, 200);
|
|
|
|
|
b.FlatAppearance.BorderSize = 1;
|
|
|
|
|
b.FlatAppearance.MouseOverBackColor = Color.FromArgb(230, 238, 245);
|
|
|
|
|
b.FlatAppearance.MouseDownBackColor = Color.FromArgb(213, 226, 238);
|
|
|
|
|
b.BackColor = Color.White;
|
|
|
|
|
b.Cursor = Cursors.Hand;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-22 16:13:34 +02:00
|
|
|
/// <summary>Come Secondario, ma pieno invece che a bordo: sulle card bianche delle floatbar
|
|
|
|
|
/// (vedi FloatBar) il bordo sottile di Secondario è quasi invisibile su sfondo bianco, e il
|
|
|
|
|
/// bottone risulterebbe visivamente diverso dal Primario pieno accanto — stessa costruzione
|
|
|
|
|
/// (nessun bordo, riempimento pieno), solo grigio chiaro invece che blu.</summary>
|
|
|
|
|
public static void SecondarioPieno(Button b)
|
|
|
|
|
{
|
|
|
|
|
DimensionamentoAutomatico(b);
|
|
|
|
|
b.FlatStyle = FlatStyle.Flat;
|
|
|
|
|
b.FlatAppearance.BorderSize = 0;
|
|
|
|
|
b.FlatAppearance.MouseOverBackColor = Color.FromArgb(230, 233, 237);
|
|
|
|
|
b.FlatAppearance.MouseDownBackColor = Color.FromArgb(213, 217, 222);
|
|
|
|
|
b.BackColor = Color.FromArgb(241, 243, 245);
|
|
|
|
|
b.Cursor = Cursors.Hand;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-22 15:50:29 +02:00
|
|
|
/// <summary>Arrotonda gli angoli del controllo (bottone o card). Si aggancia a SizeChanged
|
|
|
|
|
/// invece di calcolare la Region una volta sola: con AutoSize la dimensione finale si
|
|
|
|
|
/// conosce solo a layout avvenuto (dopo Text/Padding), non subito dopo la creazione del
|
|
|
|
|
/// controllo.</summary>
|
|
|
|
|
public static void Arrotonda(Control c, int raggio = 6)
|
2026-09-21 10:40:32 +02:00
|
|
|
{
|
2026-09-22 15:50:29 +02:00
|
|
|
c.SizeChanged += (_, _) => AggiornaRegione(c, raggio);
|
|
|
|
|
AggiornaRegione(c, raggio);
|
2026-09-21 10:40:32 +02:00
|
|
|
}
|
|
|
|
|
|
2026-09-22 15:50:29 +02:00
|
|
|
private static void AggiornaRegione(Control c, int raggio)
|
2026-09-21 10:40:32 +02:00
|
|
|
{
|
2026-09-22 15:50:29 +02:00
|
|
|
if (c.Width <= 0 || c.Height <= 0)
|
2026-09-21 10:40:32 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
int d = raggio * 2;
|
2026-09-22 15:50:29 +02:00
|
|
|
var rect = new Rectangle(Point.Empty, c.Size);
|
2026-09-21 10:40:32 +02:00
|
|
|
using var percorso = new GraphicsPath();
|
|
|
|
|
percorso.AddArc(rect.X, rect.Y, d, d, 180, 90);
|
|
|
|
|
percorso.AddArc(rect.Right - d, rect.Y, d, d, 270, 90);
|
|
|
|
|
percorso.AddArc(rect.Right - d, rect.Bottom - d, d, d, 0, 90);
|
|
|
|
|
percorso.AddArc(rect.X, rect.Bottom - d, d, d, 90, 90);
|
|
|
|
|
percorso.CloseFigure();
|
|
|
|
|
|
2026-09-22 15:50:29 +02:00
|
|
|
c.Region?.Dispose();
|
|
|
|
|
c.Region = new Region(percorso);
|
2026-09-21 10:40:32 +02:00
|
|
|
}
|
2026-09-22 15:50:29 +02:00
|
|
|
|
|
|
|
|
/// <summary>Card di stato: sfondo/bordo leggermente distinti dalla pagina, angoli arrotondati,
|
|
|
|
|
/// padding interno. Il bordo va disegnato a mano (Paint) perché Region ritaglia il
|
|
|
|
|
/// controllo ma non lascia spazio a un Border WinForms nativo sugli angoli arrotondati.</summary>
|
|
|
|
|
public static Panel Card()
|
|
|
|
|
{
|
|
|
|
|
var card = new Panel
|
|
|
|
|
{
|
|
|
|
|
Dock = DockStyle.Top,
|
|
|
|
|
AutoSize = true,
|
|
|
|
|
AutoSizeMode = AutoSizeMode.GrowAndShrink,
|
|
|
|
|
BackColor = ColoreSfondoCard,
|
|
|
|
|
Padding = new Padding(18, 16, 18, 16),
|
|
|
|
|
Margin = new Padding(0, 0, 0, 16),
|
|
|
|
|
};
|
|
|
|
|
card.Paint += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
using var penna = new Pen(ColoreBordoCard);
|
|
|
|
|
e.Graphics.DrawRectangle(penna, 0, 0, card.Width - 1, card.Height - 1);
|
|
|
|
|
};
|
|
|
|
|
Arrotonda(card, 8);
|
|
|
|
|
return card;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-22 16:13:34 +02:00
|
|
|
/// <summary>Barra di bottoni "flottante" (Avanti/Indietro, Apri cartella raw/Chiudi, ecc.):
|
|
|
|
|
/// una card bianca staccata su tutti e 4 i lati dal bordo della finestra — non un footer a
|
|
|
|
|
/// tutta larghezza — con angoli arrotondati e bordo sottile, sospesa sopra lo sfondo grigio
|
|
|
|
|
/// della pagina invece che ancorata al bordo. Lo sfondo del contenitore esterno deve
|
|
|
|
|
/// combaciare con quello di default della Form (nessun BackColor impostato altrove:
|
|
|
|
|
/// SystemColors.Control) perché il "distacco" si veda solo nel margine attorno alla card,
|
|
|
|
|
/// non in una fascia di colore diverso.</summary>
|
|
|
|
|
public static (Panel Contenitore, FlowLayoutPanel Bottoni) FloatBar()
|
|
|
|
|
{
|
|
|
|
|
// Altezza minima per non tagliare il contenuto: bottone (MinimumSize.Height = 32) +
|
|
|
|
|
// padding verticale della riga bottoni (12+12) + padding verticale del contenitore
|
|
|
|
|
// esterno (10+16) = 82, con un po' di margine in più.
|
|
|
|
|
var contenitore = new Panel
|
|
|
|
|
{
|
|
|
|
|
Dock = DockStyle.Bottom,
|
|
|
|
|
Height = 88,
|
|
|
|
|
BackColor = SystemColors.Control,
|
|
|
|
|
Padding = new Padding(16, 10, 16, 16),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var card = new Panel { Dock = DockStyle.Fill, BackColor = Color.White };
|
|
|
|
|
card.Paint += (_, e) =>
|
|
|
|
|
{
|
|
|
|
|
using var penna = new Pen(ColoreBordoCard);
|
|
|
|
|
e.Graphics.DrawRectangle(penna, 0, 0, card.Width - 1, card.Height - 1);
|
|
|
|
|
};
|
|
|
|
|
Arrotonda(card, 14);
|
|
|
|
|
|
|
|
|
|
var bottoni = new FlowLayoutPanel
|
|
|
|
|
{
|
|
|
|
|
Dock = DockStyle.Fill,
|
|
|
|
|
FlowDirection = FlowDirection.RightToLeft,
|
|
|
|
|
Padding = new Padding(16, 12, 16, 12),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
card.Controls.Add(bottoni);
|
|
|
|
|
contenitore.Controls.Add(card);
|
|
|
|
|
return (contenitore, bottoni);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Forza le stesse dimensioni per due bottoni della stessa barra (es. Avanti/
|
|
|
|
|
/// Indietro): AutoSize li dimensiona in base al proprio testo, e con testi di lunghezza
|
|
|
|
|
/// diversa (o solo uno dei due in grassetto, come Primario/Secondario) risulterebbero
|
|
|
|
|
/// disallineati sia in larghezza che in altezza — un dettaglio che un designer non
|
|
|
|
|
/// lascerebbe passare. Va chiamato dopo aver impostato Text/Font su entrambi.</summary>
|
|
|
|
|
public static void LarghezzaUguale(params Button[] bottoni)
|
|
|
|
|
{
|
|
|
|
|
int larghezza = bottoni.Max(b => b.PreferredSize.Width);
|
|
|
|
|
int altezza = bottoni.Max(b => b.PreferredSize.Height);
|
|
|
|
|
foreach (var b in bottoni)
|
|
|
|
|
b.MinimumSize = new Size(larghezza, altezza);
|
|
|
|
|
}
|
2026-09-21 10:40:32 +02:00
|
|
|
}
|