Darktable ha una propria libreria di widget per la GUI, per gli slider e le combobox (ovvero i menu a tendina o caselle di selezione), chiamata Bauhaus (nel codice sorgente si trova in src/bauhaus/bauhaus.c). Sebbene usino Gtk come backend, i Bauhaus sono oggetti personalizzati. E come molte cose in Darktable, personalizzato equivale a marcio.
Nel 2022 ho notato ridisegni parassiti e latenze usandoli, che portavano a un’esperienza utente frustrante: il ridisegno del widget sembrava attendere il completamento dei ricalcoli della pipeline, il che significava che gli utenti non erano davvero sicuri che la loro modifica di valore fosse stata registrata, cosa che poteva indurli a riprovare, avviando un altro ciclo di costoso ricalcolo e di fatto congelando il loro computer per diversi minuti molto frustranti di inutili ricalcoli intermedi della pipeline.
Come si scopre, gli strumenti di analisi statica del codice rilevano che la libreria Bauhaus è anche il 4° file di codice sorgente più complesso dell’intero software Darktable in termini di complessità ciclomatica, con un punteggio di 735 e un debito tecnico stimato in 1 giorno e 7 ore. Se non sei un programmatore, la complessità ciclomatica misura il numero di percorsi diversi che il codice può prendere, e valori elevati lo rendono non solo più difficile da capire (e quindi da debuggare), ma anche più soggetto a casi limite, bug e strani problemi dipendenti dal contesto. La complessità ciclomatica è una metrica indiretta della probabilità che questo codice ti esploda in faccia quando meno te lo aspetti, qualcosa da tenere in considerazione quando gli «sviluppatori» più prolifici del tuo team sono una maestra di scuola elementare, un pediatra e un consulente bancario.
Ciò che è particolarmente frustrante è che avevo già lavorato per semplificare questo file, nel 2019. 3 anni dopo era come se non avessi fatto nulla, grazie al feature creep e al supporto per MIDI/gamepad. Quando sono comparsi i ridisegni parassiti, mi sono ritrovato con codice spaghetti incomprensibile che ero del tutto incapace di correggere. Un primo tentativo di correggere le cose sbagliate ha portato a un vicolo cieco, nell’agosto 2022, e mi ha scoraggiato. Cercare di aggirare il problema non sarebbe bastato.
Così ho dovuto riscriverlo quasi completamente, cosa che non è stata affatto divertente e mi ha richiesto una quantità folle di ore (ho smesso di contare a 3 settimane, a tempo pieno, e questo era per il secondo tentativo, tra agosto e novembre 2023).
Lo sapevi che, una volta che una combobox aveva il focus (perché ci avevi cliccato sopra o le avevi dato il focus tramite una scorciatoia da tastiera), potevi iniziare a digitare le prime lettere dell’etichetta che volevi selezionare e avrebbe selezionato automaticamente l’elemento più vicino nella lista? Nemmeno io lo sapevo prima di intraprendere questo compito, perché non è documentato da nessuna parte. Come lo erano molte funzioni nascoste lì dentro, aggiunte per soddisfare casi d’uso devianti e marginali, ma che complicavano la struttura del codice per tutti. (Attenzione spoiler: ho mantenuto questa particolare funzione, ma ne ho rimosse altre).
Elenco dei miglioramenti
- Le coordinate del cursore (nei popup) sono calcolate una sola volta, poi memorizzate. Questo risparmia molti ricalcoli intermedi, alcuni dei quali incoerenti perché il codice era copiato-incollato e duplicato invece di usare getter e setter . Ora gli offset e le variazioni delle coordinate sono gestiti tramite getter e setter unificati, il che significa che qualsiasi modifica futura dovrà avvenire in un solo posto, e tutto il codice usa quello.
- I nuovi valori (dagli slider e dalle combobox) non vengono più inviati alla pixel pipeline durante lo scorrimento o il trascinamento, ma solo alla fine. Questo si basa su un timeout appreso automaticamente che registra il tempo medio necessario a calcolare una pipeline completa. La GUI attenderà da 20 ms a 2,5 s prima di inviare le modifiche alla pipeline, evitando di ricalcolare a ogni passo di scorrimento o trascinamento, che genera calcoli inutili e ridondanti, eppure costosi, che rendono solo il software lento. Analogamente, ora vengono inviati solo sull’evento di rilascio del pulsante, invece che sugli eventi di pressione e rilascio del pulsante (dato che un tipico clic del mouse invia entrambi gli eventi). Dovrebbe risparmiare parecchi ricalcoli inutili della pipeline.
- I widget vengono ridisegnati immediatamente sugli eventi dell’utente, prima che i nuovi valori siano inviati alla pixel pipeline. Questo garantisce un feedback immediato all’utente, anche se il risultato effettivo dei pixel può arrivare più tardi, e limita la frustrazione sui computer lenti.
- Non inviare eventi di variazione del valore se i widget hanno ricevuto un’interazione dell’utente ma il loro valore non è effettivamente cambiato.
- Catturare i clic sul chevron delle combobox (freccia destra). In precedenza dovevi cliccare sull’etichetta per aprire il menu a tendina della combobox, cosa super frustrante se provenivi da un software con una GUI decente. Il chevron stesso non rispondeva ai clic.
- Non far scorrere i menu a tendina delle combobox. Quella funzione era nata come un progetto figo: avere l’elemento attualmente selezionato allineato con l’etichetta. Il problema è che il posizionamento del popup a tendina è in ultima analisi gestito dall’ambiente desktop, possiamo solo chiedergli gentilmente di fare ciò che desideriamo, ma non c’è garanzia che i nostri desideri vengano onorati. Inoltre è possibile far scorrere il popup fuori dalla viewport, nulla lo impedisce. Tutto sommato è fragile e casuale, meglio attenersi al posizionamento predefinito delle finestre.
Funzioni deprecate
La possibilità di assegnare scorciatoie da tastiera a slider e combobox è per ora rimossa. Slider e combobox sono comunque collegati ai tasti freccia e allo scorrimento del mouse una volta che ottengono il focus1, ed è ancora possibile assegnare la cattura del focus a una scorciatoia da tastiera. La logica attuale è quindi di richiedere il focus tramite scorciatoie da tastiera, poi modificare il valore usando i tasti freccia. Le richieste di focus rendono anche automaticamente visibile il widget nella GUI, scorrendo la barra laterale se necessario.
Comunque, l’attuale sistema di scorciatoie dovrà essere interamente sostituito dagli acceleratori nativi di Gtk, che sono già usati per il menu globale (ed erano usati in Darktable prima del 2021). Attualmente abbiamo entrambi i sistemi, uno dei quali è una mostruosità di complessità e probabilmente responsabile di rallentamenti. KISS.
Avvertenze
La combobox «format» nel modulo di esportazione non inizializza correttamente il proprio valore. Si tratta di un widget Bauhaus non standard che richiede attenzioni particolari. Per il momento dovrai aggiornare l’opzione di archiviazione o ricaricare un preset.
Ogni volta che togli la vecchia vernice scrostata che teneva insieme le pareti arrugginite, rischi di causare danni collaterali. Fai attenzione con Ansel quando usi l’AppImage e l’EXE Win contrassegnati come Ansel-57ed58d di stasera, e segnala qualsiasi cosa strana.
Download
Dettagli tecnici
Le modifiche attuali hanno ridotto:
- la complessità ciclomatica da 735 a 494
- la complessità cognitiva da 796 a 432
- il debito tecnico da 1 giorno e 7 ore a 4 ore e 50 min.
Se non sei un programmatore, queste metriche significano semplicemente che il codice sarà più facile e meno dispendioso in termini di tempo da mantenere in futuro, e probabilmente meno soggetto a bug.
Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.
In GUI programming, a widget has the focus when it is the one recording keyboard events. Text entries are the most obvious example, but Darktable hacked that concept to generalize to pretty much every widget. ↩︎