RAVAL v2.10.2

Log e impostazioni — RAVAL 2.2

Le due cose che ogni programma vero si scrive il primo giorno: un log che dice cosa è successo, anche sul computer del cliente, e un file di impostazioni che ricorda le scelte dell'utente. In RAVAL sono scritte da zero, con licenza MIT come il resto della libreria; le idee vengono dal modo in cui lavorano i programmi dell'autore (livelli, file che ruota, riga di avvio, valori mancanti aggiunti al file).

Spiegazione con un esempio completo: TUTORIAL.md, capitolo 16. Ogni membro, con la sua firma, è nel riferimento API.md. Le prove sono in TEST_RAVAL\ProveApp.cs.

Il log — App\RLog.cs, UI\RLogView.cs

RLog.Start("Astrip", "1.4.0", "log/astrip.log");    // la prima riga: nome, versione, computer, .NET, pid
RLog.CatchUnhandled();                              // anche le eccezioni che nessuno prende
RLog.Info("progetto aperto: " + percorso);
RLog.Error("salvataggio non riuscito", ex);
RLog.Stop();                                        // alla fine: scrive quello che resta
MembroFa
Error, Warning, Info, Debug, Traceuna riga del livello: «2026-11-06 15:04:05.123 INF [1] testo» (data, livello, thread, testo)
Levelil livello meno importante scritto (Info: niente Debug e Trace); Enabled(livello) per non costruire testi costosi che non verrebbero scritti
Console, Colors, TrueColorle righe anche sulla console, colorate: l'ora attenuata, la sigla del livello nel suo colore (errori rossi, avvisi arancio, Info blu, Debug e Trace grigi), il testo nel colore del livello o nel suo; con TrueColor (da solo in Windows Terminal e con COLORTERM=truecolor) i colori esatti a 24 bit, se no i più vicini fra i 16 della console; mandata in un file, la console resta senza colori
Info(testo, "#2b8a3e"), Debug(testo, colore), Add(livello, testo, colore)una riga in un colore suo, da trovare a colpo d'occhio (una fase, un apparecchio, un risultato)
MaxBytes, MaxFilesil file ruota oltre 10 MB e ne restano 5: app.log, app.1.log ... app.4.log
Recent(), RecentLinesle ultime 2000 righe in memoria, per una finestra del log aperta dopo
Writtenogni riga scritta, sul thread del log
Flush()aspetta che sia scritto tutto quello che è stato registrato finora
RLogViewil log dal vivo in una finestra, con gli stessi colori della console; filtro, «Pause», «Clear»

Le impostazioni — App\RSettings.cs, RJsonSettings.cs, RIniSettings.cs

RSettings s = RSettings.Open(RSettings.Folder("Astrip") + "/settings.json");   // .ini per un file INI
int porta = s.GetInt("server.port", 5080);
s.Set("server.port", 5081);                          // il file si salva da solo mezzo secondo dopo
s.Remember(this, "window.main");                     // la finestra riapre dov'era
var recenti = new RRecentFiles(s, "files.recent");   // File → Recenti
MembroFa
Open(file)le impostazioni di un file, già lette: INI per «.ini», JSON per le altre estensioni
Folder(app)la cartella delle impostazioni: %APPDATA%\app su Windows, ~/.config/app su Linux e macOS
GetString, GetInt, GetDouble, GetBool, GetListun valore con il suo predefinito; un valore mancante o illeggibile dà il predefinito
Set, SetList, Remove, Contains, Keys()scrivere, togliere, cercare
AddMissinguna chiave letta e non trovata si scrive con il suo predefinito (vero)
AutoSave, SaveDelayMs, Save(), Dirtyil salvataggio ritardato, o subito
Changeddopo ogni Set che ha cambiato un valore, con la chiave
Remember(finestra, nome)posizione, misura e finestra ingrandita (RWindowMemory)
RRecentFilesgli ultimi file aperti, in cima il più recente, e il menu che li elenca

Le chiavi sono percorsi con i punti. In JSON «server.port» è il campo port dell'oggetto server; in INI è la chiave port della sezione [server], e la sezione è tutto quello che sta prima dell'ultimo punto («window.main.x» è x in [window.main]). Le stesse chiamate valgono per i due formati.

Il JSON si scrive rientrato e con gli accenti come sono («città», non città): il file è fatto anche per essere aperto e corretto a mano.

Perché è fatto così

  • Chi scrive nel log non aspetta il disco. La riga va in una coda e la scrive un thread del log: una chiamata costa uguale sul thread dell'interfaccia e su un thread di lavoro, e le righe di thread diversi non si mescolano mai a metà (la prova scrive 400 righe da quattro thread e le ritrova tutte intere).
  • Il file del log ruota. Un programma che resta acceso per mesi non riempie il disco.
  • Ogni lettura ha il suo predefinito. Un file mancante o rovinato non ferma mai il programma; un file che non si legge si rinomina «.bad» e si scrive un avviso nel log, così la copia dell'utente non si perde.
  • Le chiavi lette vengono scritte nel file (AddMissing): dopo il primo avvio il file elenca tutte le opzioni del programma, e chi lo modifica a mano le vede tutte.
  • Il salvataggio aspetta mezzo secondo. Una finestra trascinata manda centinaia di posizioni: il file si scrive una volta sola, alla fine. Si scrive un file temporaneo e poi lo si rinomina, così un arresto a metà lascia il file vecchio, non uno spezzato.
  • Il file INI resta quello dell'utente. Commenti, righe vuote e ordine delle chiavi restano dove sono; un valore cambiato riscrive solo la sua riga. Il valore è tutto quello che segue il primo «=»: un «;» dentro un valore (un percorso, una lista) ne fa parte, e i commenti vanno su una riga a sé.
  • La finestra ricorda la misura di quando era normale. Chiusa ingrandita, riapre ingrandita, e quando la si riduce torna al suo posto. Se lo schermo su cui stava non c'è più (un portatile senza il monitor), si apre al centro dello schermo principale invece che fuori dalla vista.

Il tema — App\RTheme.cs, UI\RThemes.cs

MembroFa
RThemes.Standard()i colori dei controlli di RAVAL nei due temi (Back, Panel, Ink, InkSoft, Border, Bar, Tree, StatusBar, StatusInk, ChartBack, ChartGrid, ChartInk): la tavolozza grafite di ASTRIP; si aggiungono i propri con Color prima di Apply
RTheme.Mode (RThemeMode: Light, Dark, System) · IsDark · Toggle()la scelta, che cambia il tema di tutta l'applicazione mentre gira; se sullo schermo ora è scuro; chiaro ↔ scuro
RTheme.Remember(impostazioni, chiave, predefinito)legge «ui.theme», lo applica, lo riscrive a ogni cambio
RTheme.Fill(menu) · RTheme.Button() · Changedil menu con le tre scelte e la spunta; il bottone sole/luna; l'evento

Le lingue — App\RLang.cs

MembroFa
RLang.Open(cartella, lingua) · Use(lingua) · Language · Languages()i file delle lingue («it.json» o «it.ini»), la lingua in uso («» all'apertura: quella del sistema), quelle che hanno un file
RLang.T(chiave, predefinito) · T(chiave, predefinito, valori...)il testo nella lingua in uso; una chiave che manca si scrive nel file con il predefinito, da tradurre
controllo.Tr(chiave, predefinito) · RLang.Bind(oggetto, proprietà, chiave, predefinito)un testo legato che cambia con la lingua (Text, Header, Title, PlaceholderText o Content secondo il controllo); legami deboli
Changed · Save() · Extension · BoundCountdopo un cambio; il file scritto subito; «.json» o «.ini» per le lingue nuove
le chiavi «raval.*»i testi di RAVAL (bottoni e titoli di RDialog, menu del tema), già in italiano

Inciampi

  • Un pennello tenuto in un campo non cambia con il tema (trovato in ASTRIP): un controllo disegnato a mano chiede i colori in Render con ThemeBrush e si ridisegna con OnThemeChanged; preso all'avvio, il pennello resta quello del primo tema. Per fotografare nei due temi, fra il cambio e la foto si fa girare il dispatcher.
  • Le righe scritte prima che la vista entri nella finestra andavano perse. RLogView leggeva le righe in memoria quando veniva creata e ascoltava Written solo dopo, quando entrava nella finestra: quello che il programma scriveva in mezzo non compariva da nessuna parte. Ora la vista prima si iscrive a Written e poi legge Recent. Una riga scritta proprio in quel momento sta in tutti e due; il numero progressivo della riga (RLogEntry.Number) fa saltare la seconda copia.
  • I numeri delle righe non arrivano in ordine. Il numero si assegna quando la riga nasce, ma due thread possono mettere in coda la 6 prima della 5. Per questo la vista riconosce le copie con l'insieme dei numeri già presi, e non con «numero minore dell'ultimo», che avrebbe scartato la 5.
  • Le voci di un ListBox di Fluent sono alte come un bottone (margine interno 12 × 9): un log ne mostra la metà. RLogView le stringe con uno stile messo solo sulla sua lista: RStyles.Apply(controllo), che non tocca il resto dell'applicazione.