namespace PrepStep; public sealed class ProgressForm : Form { private readonly Label _lblCartella = new(); private readonly ProgressBar _bar = new(); private readonly Label _lblConteggio = new(); private readonly Label _lblDettaglio = new(); private readonly Label _lblEta = new(); private readonly Button _btnAnnulla = new(); private readonly ToolTip _tipCartella = new(); private const int Margine = 16; public event EventHandler? Annullato; public ProgressForm() { Text = "Aggiornamento indice"; FormBorderStyle = FormBorderStyle.FixedDialog; StartPosition = FormStartPosition.CenterParent; MinimizeBox = false; MaximizeBox = false; ControlBox = false; ClientSize = new Size(420, 190); _lblCartella.Text = "Preparazione..."; _lblCartella.Location = new Point(Margine, Margine); // AutoSize=false + AutoEllipsis: un percorso lungo (share di rete) va troncato con "..." // invece di sforare il bordo del dialog, che è FixedDialog e non si può allargare. Il // testo completo resta disponibile nel tooltip. _lblCartella.AutoSize = false; _lblCartella.AutoEllipsis = true; _lblCartella.Size = new Size(ClientSize.Width - Margine * 2, 20); _bar.Location = new Point(Margine, 40); _bar.Size = new Size(ClientSize.Width - Margine * 2, 24); _bar.Minimum = 0; _bar.Maximum = 100; _lblConteggio.Location = new Point(Margine, 72); _lblConteggio.AutoSize = true; _lblDettaglio.Location = new Point(Margine, 96); _lblDettaglio.AutoSize = true; _lblEta.Location = new Point(Margine, 120); _lblEta.AutoSize = true; _btnAnnulla.Text = "Annulla"; _btnAnnulla.Size = new Size(90, 28); _btnAnnulla.Click += (_, e) => Annullato?.Invoke(this, e); Controls.AddRange(new Control[] { _lblCartella, _bar, _lblConteggio, _lblDettaglio, _lblEta, _btnAnnulla }); _btnAnnulla.Location = new Point( ClientSize.Width - Margine - _btnAnnulla.Width, ClientSize.Height - Margine - _btnAnnulla.Height); } public void Aggiorna(ScanProgress p) { _lblCartella.Text = $"Scansione: {p.Cartella}"; _tipCartella.SetToolTip(_lblCartella, _lblCartella.Text); _bar.Value = p.PrevistiTotali > 0 ? (int)Math.Clamp(100.0 * p.FattiTotali / p.PrevistiTotali, 0, 100) : 0; _lblConteggio.Text = p.PrevistiTotali > 0 ? $"{p.FattiTotali:N0} / ~{p.PrevistiTotali:N0} file" : $"{p.FattiTotali:N0} file processati"; _lblDettaglio.Text = $"nuovi: {p.Nuovi:N0} aggiornati: {p.Aggiornati:N0} invariati: {p.Invariati:N0}" + (p.Rimossi > 0 ? $" rimossi: {p.Rimossi:N0}" : ""); _lblEta.Text = $"Trascorso: {FormatTempo(p.Trascorso)} — {StimaRimanente(p)}"; } private static string StimaRimanente(ScanProgress p) { if (p.PrevistiTotali <= 0 || p.FattiTotali <= 0) { return "stima non disponibile"; } double filePerSecondo = p.FattiTotali / Math.Max(p.Trascorso.TotalSeconds, 0.001); if (filePerSecondo <= 0) { return "stima non disponibile"; } long rimanenti = Math.Max(p.PrevistiTotali - p.FattiTotali, 0); var eta = TimeSpan.FromSeconds(rimanenti / filePerSecondo); return $"tempo rimanente stimato: {FormatTempo(eta)}"; } private static string FormatTempo(TimeSpan t) { if (t.TotalHours >= 1) { return $"{(int)t.TotalHours}h {t.Minutes}m {t.Seconds}s"; } if (t.TotalMinutes >= 1) { return $"{t.Minutes}m {t.Seconds}s"; } return $"{t.Seconds}s"; } }