diff --git a/PrepStep/GuidaForm.cs b/PrepStep/GuidaForm.cs
index d07fa62..429a912 100644
--- a/PrepStep/GuidaForm.cs
+++ b/PrepStep/GuidaForm.cs
@@ -1,71 +1,195 @@
namespace PrepStep;
-/// Guida interattiva richiamabile da "Aiuto ▸ Guida" (F1): sintesi delle 5 fasi del
-/// wizard, per chi apre l'app senza aver letto i docs. Il dettaglio completo (formato .asm,
-/// schema DB, ecc.) resta solo in docs/architettura.md e affini, per non duplicarlo qui.
+/// Guida interattiva richiamabile da "Aiuto ▸ Guida" (F1): stessa guida completa di
+/// docs/guida-utente.md (passo-passo fino a Creo Distributed Batch), pensata per chi apre
+/// l'app senza aver letto i docs e senza esperienza pregressa. Il dettaglio tecnico (formato
+/// .asm, schema DB, ecc.) resta solo in docs/architettura.md e affini, per non duplicarlo qui.
+///
+/// Gerarchia tipografica nel pannello di lettura (vedi ScriviSezione): titolo sezione in blu
+/// primario/grassetto/14pt, corpo in grigio quasi-nero/10pt, passi numerati con il numero in
+/// blu grassetto per farli scorrere a colpo d'occhio, callout con sfondo tinta piatta (nessun
+/// bordo/ombra: RichTextBox non li supporta) — blu tenue per le note informative, ambra tenue
+/// per gli avvisi importanti, con etichetta ("Nota"/"Attenzione") in grassetto a inizio blocco
+/// invece di un colore isolato, così restano leggibili anche senza vedere i colori.
public class GuidaForm : Form
{
- private static readonly (string Titolo, string Testo)[] Sezioni =
- {
- ("Panoramica", """
- Cevolani StepPrep prepara i file CAD Pro-ENGINEER/Creo da passare a Creo
- Distributed Batch per l'esportazione STEP: indicizza i file di una share di
- rete e guida attraverso 5 fasi, una alla volta, con "Avanti"/"Indietro".
+ private sealed record Sezione(
+ string Titolo,
+ string Intro,
+ string[]? Passi = null,
+ string? NotaTesto = null,
+ bool NotaAttenzione = false);
- "File ▸ Nuovo" (Ctrl+N) azzera distinta/raw/verifica per
- ripartire con una nuova distinta base, senza dover ricaricare il database.
- """),
- ("1. Database", """
- Carica o aggiorna l'indice "cad_index.db" a partire dalle cartelle di primo
- livello del disco/share (radice di default X:\). Un pallino verde/rosso indica
- se un database è caricato. La cartella di lavoro, dove finiscono CSV, "raw" e
- "step", si cambia da "File ▸ Scegli cartella di lavoro...".
- """),
- ("2. Importa Distinta Base", """
- Converte il file della distinta (trascinabile nella finestra, o scelto col
- bottone "1. Carica la distinta") in un CSV componente;descrizione nella cartella
- di lavoro. Se il file segue la convenzione del gestionale, aggiunge anche
- l'assieme generale come riga. Poi "2. Verifica la presenza dei file sul server"
- cerca i componenti nell'indice: è questo passo, non il solo import, a sbloccare
- "Avanti".
- """),
- ("3. Crea cartella raw", """
- Cerca nell'indice i file CAD dei componenti del CSV e li copia nella
- sottocartella "raw" della cartella di lavoro.
- """),
- ("4. Trova componenti referenziati", """
- Legge i file .asm già in "raw" per risolvere ricorsivamente sotto-assiemi e
- parti citate, copiando anche quelli in "raw": componenti soppressi (non in
- distinta ma necessari per aprire l'assieme) e riferimenti esterni (assembly
- feature, datum copiati) inclusi.
- """),
- ("5. Verifica completezza", """
- Mostra i file .asm della distinta già in "raw", trascinabili su Creo
- Distributed Batch per l'esportazione STEP (passo manuale, fuori da
- quest'app): esporta in "step", dentro la cartella di lavoro. Se Distributed
- Batch fallisce con "[-4] Dati di input non completi", quasi sempre manca un
- file nell'albero dei componenti: torna alla fase 4 e controlla i "non
- trovati".
- """),
+ // Palette: blu primario dell'app per titoli/passi, ambra tenue solo per gli avvisi che
+ // possono far perdere tempo se ignorati (es. l'errore Creo [-4]) — il rosso resta riservato
+ // ai messaggi di errore veri e propri altrove nell'app.
+ private static readonly Color ColoreTesto = Color.FromArgb(40, 40, 40);
+ private static readonly Color ColoreNotaInfoSfondo = Color.FromArgb(232, 240, 250);
+ private static readonly Color ColoreNotaInfoTesto = Color.FromArgb(15, 65, 120);
+ private static readonly Color ColoreNotaAttenzioneSfondo = Color.FromArgb(253, 240, 219);
+ private static readonly Color ColoreNotaAttenzioneTesto = Color.FromArgb(140, 90, 10);
+
+ private static readonly Sezione[] Sezioni =
+ {
+ new("Panoramica",
+ "Cevolani StepPrep prepara i file CAD Pro-ENGINEER/Creo da passare a Creo " +
+ "Distributed Batch per l'esportazione STEP: indicizza i file di una share di rete " +
+ "e guida attraverso 5 fasi, una alla volta, con \"Avanti\"/\"Indietro\".",
+ NotaTesto: "\"File ▸ Nuovo\" (Ctrl+N) azzera distinta/raw/verifica per ripartire " +
+ "con una nuova distinta base, senza dover ricaricare il database."),
+
+ new("Prima di iniziare",
+ "Prima di importare la prima distinta:",
+ Passi:
+ [
+ "Serve accesso alla share di rete con i file CAD (di default X:\\).",
+ "Serve l'estrazione \"Distinta Base\" da Fata (il gestionale): il file .xls " +
+ "così come Fata lo esporta, senza risalvarlo o convertirlo in altri " +
+ "formati — l'app riconosce da sola se sotto l'estensione .xls c'è XML o " +
+ "Excel binario vero.",
+ "Se il file segue la convenzione di nome DBP__, l'app " +
+ "aggiunge da sola l'assieme generale come componente in più: non serve " +
+ "fare nulla di diverso.",
+ "Conviene impostare la cartella di lavoro giusta (fase 1, o \"File ▸ Scegli " +
+ "cartella di lavoro...\") prima di importare la distinta: è lì che " +
+ "finiscono CSV, \"raw\" e, dopo l'esportazione con Creo, \"step\".",
+ "Se la cartella di lavoro è già stata usata per un'altra distinta, conviene " +
+ "pulire \"raw\" e \"step\" prima di ripartire (\"File ▸ Pulisci raw e " +
+ "step...\"): \"File ▸ Nuovo\" azzera solo lo stato dell'app, non i file " +
+ "già copiati su disco.",
+ ]),
+
+ new("1. Database",
+ "Carica o aggiorna l'indice \"cad_index.db\" a partire dalle cartelle di primo " +
+ "livello del disco/share (radice di default X:\\). Un pallino verde/rosso indica " +
+ "se un database è caricato. La cartella di lavoro, dove finiscono CSV, \"raw\" e " +
+ "\"step\", si cambia da \"File ▸ Scegli cartella di lavoro...\"."),
+
+ new("2. Importa Distinta Base",
+ "Due passi in sequenza:",
+ Passi:
+ [
+ "Carica la distinta: trascina il file .xls nella finestra, oppure premi " +
+ "\"Scegli file distinta...\". Le righe importate compaiono nella griglia " +
+ "(Componente/Descrizione) e viene scritto un CSV nella cartella di lavoro.",
+ "Verifica la presenza dei file sul server: compare solo dopo un import " +
+ "riuscito. Cerca ogni componente nel database — le righe della griglia si " +
+ "colorano di verde (trovato) o rosso (non trovato nell'indice).",
+ ],
+ NotaTesto: "\"Avanti\" si sblocca solo dopo il passo 2, non basta il solo import: " +
+ "è il modo per accorgersi subito se il database è disallineato rispetto alla " +
+ "share prima di andare avanti.",
+ NotaAttenzione: true),
+
+ new("3. Crea cartella raw",
+ "Premi \"Crea cartella raw\": per ogni componente della distinta trovato " +
+ "nell'indice, l'app ne copia il file CAD dalla share nella cartella \"raw\" " +
+ "(dentro la cartella di lavoro). La griglia mostra il percorso CAD copiato per " +
+ "ogni componente."),
+
+ new("4. Trova componenti referenziati",
+ "Gli assiemi (.asm) referenziano altri file (sotto-assiemi, parti, componenti " +
+ "soppressi, riferimenti esterni) che Creo deve poter caricare insieme " +
+ "all'assieme principale, anche se non compaiono nella distinta del gestionale. " +
+ "Premi \"Trova componenti referenziati\": l'app legge i file .asm già in \"raw\", " +
+ "risolve ricorsivamente tutto l'albero e copia anche quei file in \"raw\".\n\n" +
+ "Si apre una finestra con l'elenco di quanto trovato e copiato, con in fondo " +
+ "eventuali codici non corrispondenti a nessun file indicizzato (possono essere " +
+ "disegni, componenti davvero mancanti, o falsi positivi — vale la pena " +
+ "controllarli). Da lì si può anche aprire direttamente la cartella \"raw\".",
+ NotaTesto: "Passo opzionale se la distinta contiene solo parti singole senza " +
+ "sotto-assiemi: in quel caso si può passare direttamente alla fase successiva."),
+
+ new("5. Verifica completezza",
+ "Mostra i file .asm della distinta già in \"raw\", trascinabili. Da qui il " +
+ "passaggio a Creo è manuale, fuori da quest'app:",
+ Passi:
+ [
+ "Apri Creo Distributed Batch (dal menu Start di Windows, o dal file " +
+ "ptcdbatch.bat nella cartella bin dell'installazione Creo).",
+ "Crea (o riusa, se già presente in azienda) un gruppo di attività per " +
+ "l'esportazione STEP: menu Actions ▸ New Task Group.",
+ "Aggiungi i file: trascina i .asm dalla finestra dell'app (o dalla cartella " +
+ "\"raw\" in Esplora risorse) sul gruppo di attività, oppure sfoglia e " +
+ "selezionali a mano.",
+ "Verifica che il gruppo di attività punti al config.pro/profilo di export " +
+ "STEP giusto (sezione Configuration Files) — di norma è già impostato " +
+ "una volta sola per tutti.",
+ "Avvia l'elaborazione (Start/Run): Distributed Batch legge ogni .asm, con " +
+ "tutto l'albero di sotto-componenti già raccolto nelle fasi 3-4, ed " +
+ "esporta i file STEP nella cartella \"step\" (dentro la cartella di " +
+ "lavoro, accanto a \"raw\").",
+ ],
+ NotaTesto: "Se Distributed Batch fallisce con \"[-4] Dati di input non " +
+ "completi\", quasi sempre manca un file nell'albero dei componenti — Creo " +
+ "non dice quale. Torna alla fase 4 e controlla l'elenco dei \"non trovati\": " +
+ "è il punto di partenza per capire cosa recuperare prima di riprovare.",
+ NotaAttenzione: true),
+
+ new("Ripartire da capo",
+ "Quando devi processare un'altra distinta, usa \"File ▸ Nuovo\" (Ctrl+N): azzera " +
+ "CSV, raw e verifica delle fasi 2-5 e riporta al passo 2, senza dover ricaricare " +
+ "il database o la cartella di lavoro (restano quelli già impostati in fase 1).",
+ NotaTesto: "Per ripulire le cartelle \"raw\" e \"step\" della cartella di lavoro " +
+ "prima di un nuovo giro (senza toccare l'indice), usa \"File ▸ Pulisci raw e " +
+ "step...\"."),
};
public GuidaForm()
{
Text = "Guida";
- ClientSize = new Size(640, 420);
- MinimumSize = new Size(480, 320);
+ ClientSize = new Size(780, 520);
+ MinimumSize = new Size(580, 400);
StartPosition = FormStartPosition.CenterParent;
Font = new Font("Segoe UI", 9.5f);
+ try
+ {
+ Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
+ }
+ catch (Exception ex) when (ex is IOException or ArgumentException)
+ {
+ // ponytail: nessuna icona nell'exe (es. lanciato da dotnet run) -> resta quella di default
+ }
+ var headerPanel = new Panel { Dock = DockStyle.Top, Height = 40, BackColor = SystemColors.ControlLight };
+ var lblHeader = new Label
+ {
+ Dock = DockStyle.Fill,
+ Text = "Guida",
+ TextAlign = ContentAlignment.MiddleLeft,
+ Padding = new Padding(16, 0, 0, 0),
+ Font = new Font(Font, FontStyle.Bold),
+ };
+ headerPanel.Controls.Add(lblHeader);
+
+ // Sidebar disegnata a mano (OwnerDrawFixed) invece della selezione blu di sistema:
+ // stessa identità visiva del resto dell'app (UiStile.ColorePrimario) invece del colore
+ // di selezione standard di Windows, che varia con il tema dell'utente.
var lista = new ListBox
{
Dock = DockStyle.Left,
- Width = 200,
+ Width = 230,
IntegralHeight = false,
BorderStyle = BorderStyle.None,
+ DrawMode = DrawMode.OwnerDrawFixed,
+ ItemHeight = 34,
+ BackColor = UiStile.ColoreSfondoCard,
+ };
+ foreach (var sezione in Sezioni)
+ lista.Items.Add(sezione.Titolo);
+ lista.DrawItem += (_, e) =>
+ {
+ if (e.Index < 0)
+ return;
+ bool selezionato = (e.State & DrawItemState.Selected) != 0;
+ using var sfondo = new SolidBrush(selezionato ? UiStile.ColorePrimario : UiStile.ColoreSfondoCard);
+ e.Graphics.FillRectangle(sfondo, e.Bounds);
+ var colore = selezionato ? Color.White : ColoreTesto;
+ using var testoFont = new Font(lista.Font, selezionato ? FontStyle.Bold : FontStyle.Regular);
+ var rettangoloTesto = new Rectangle(e.Bounds.X + 16, e.Bounds.Y, e.Bounds.Width - 20, e.Bounds.Height);
+ TextRenderer.DrawText(e.Graphics, lista.Items[e.Index].ToString(), testoFont, rettangoloTesto,
+ colore, TextFormatFlags.VerticalCenter | TextFormatFlags.Left | TextFormatFlags.EndEllipsis);
};
- foreach (var (titolo, _) in Sezioni)
- lista.Items.Add(titolo);
var testo = new RichTextBox
{
@@ -79,19 +203,79 @@ public class GuidaForm : Form
lista.SelectedIndexChanged += (_, _) =>
{
if (lista.SelectedIndex >= 0)
- testo.Text = Sezioni[lista.SelectedIndex].Testo.Trim();
+ ScriviSezione(testo, Sezioni[lista.SelectedIndex]);
};
- var btnChiudi = new Button { Text = "Chiudi", Dock = DockStyle.Bottom, Height = 34 };
- UiStile.Secondario(btnChiudi);
+ var (barraChiudi, azioniChiudi) = UiStile.FloatBar();
+ var btnChiudi = new Button { Text = "Chiudi" };
+ UiStile.Primario(btnChiudi);
+ UiStile.Arrotonda(btnChiudi, 10);
btnChiudi.Click += (_, _) => Close();
+ azioniChiudi.Controls.Add(btnChiudi);
+ CancelButton = btnChiudi;
- var contenuto = new Panel { Dock = DockStyle.Fill, Padding = new Padding(16) };
+ var contenuto = new Panel { Dock = DockStyle.Fill, Padding = new Padding(24, 20, 24, 20), BackColor = Color.White };
contenuto.Controls.Add(testo);
+
Controls.Add(contenuto);
Controls.Add(lista);
- Controls.Add(btnChiudi);
+ Controls.Add(barraChiudi);
+ Controls.Add(headerPanel);
lista.SelectedIndex = 0;
}
+
+ /// Costruisce il pannello di lettura con gerarchia tipografica esplicita: titolo
+ /// (blu primario, grassetto, 14pt), corpo (grigio quasi-nero, 10pt), passi numerati (numero
+ /// in blu grassetto, indentati) e callout finale con sfondo a tinta piatta ed etichetta in
+ /// grassetto — informativo (blu tenue) o di attenzione (ambra tenue). Applica la
+ /// formattazione DOPO aver scritto il testo, selezionando il range appena aggiunto: è il
+ /// solo modo affidabile di formattare i paragrafi in un RichTextBox (impostare
+ /// SelectionFont/Color su una selezione vuota prima di AppendText non è garantito che
+ /// "si attacchi" al testo che segue).
+ private static void ScriviSezione(RichTextBox box, Sezione sezione)
+ {
+ box.Clear();
+ var titoloFont = new Font(box.Font.FontFamily, 14f, FontStyle.Bold);
+ var corpoFont = new Font(box.Font.FontFamily, 10f, FontStyle.Regular);
+ var corpoGrassetto = new Font(box.Font.FontFamily, 10f, FontStyle.Bold);
+
+ void Scrivi(string testo, Font font, Color colore, int indent = 0, Color? sfondo = null)
+ {
+ int inizio = box.TextLength;
+ box.AppendText(testo);
+ box.Select(inizio, box.TextLength - inizio);
+ box.SelectionFont = font;
+ box.SelectionColor = colore;
+ box.SelectionBackColor = sfondo ?? box.BackColor;
+ box.SelectionIndent = indent;
+ }
+
+ Scrivi(sezione.Titolo + "\n\n", titoloFont, UiStile.ColorePrimario);
+ Scrivi(sezione.Intro + "\n", corpoFont, ColoreTesto);
+
+ if (sezione.Passi is { Length: > 0 })
+ {
+ Scrivi("\n", corpoFont, ColoreTesto);
+ for (int i = 0; i < sezione.Passi.Length; i++)
+ {
+ Scrivi($"{i + 1}. ", corpoGrassetto, UiStile.ColorePrimario, indent: 8);
+ Scrivi(sezione.Passi[i] + "\n\n", corpoFont, ColoreTesto, indent: 8);
+ }
+ }
+
+ if (sezione.NotaTesto is not null)
+ {
+ var sfondo = sezione.NotaAttenzione ? ColoreNotaAttenzioneSfondo : ColoreNotaInfoSfondo;
+ var coloreEtichetta = sezione.NotaAttenzione ? ColoreNotaAttenzioneTesto : ColoreNotaInfoTesto;
+ string etichetta = sezione.NotaAttenzione ? "Attenzione" : "Nota";
+
+ Scrivi("\n", corpoFont, ColoreTesto);
+ Scrivi(" " + etichetta + "\n", corpoGrassetto, coloreEtichetta, indent: 12, sfondo: sfondo);
+ Scrivi(" " + sezione.NotaTesto + " \n", corpoFont, ColoreTesto, indent: 12, sfondo: sfondo);
+ }
+
+ box.SelectionStart = 0;
+ box.SelectionLength = 0;
+ }
}