diff --git a/README.it.md b/README.it.md index 9ba87a0..a091524 100644 --- a/README.it.md +++ b/README.it.md @@ -165,6 +165,7 @@ nessuno. Impiegano millisecondi e dicono il file di cui non sono contenti: ```sh python3 tests/qml_check_bindings.py qml # nessuna proprietà assegnata due volte python3 tests/qml_focus_check.py # un tocco apre solo quello che ha toccato +python3 tests/label_fit_check.py # le etichette dei tasti ci stanno ``` ## Stato @@ -234,6 +235,7 @@ tests/core_test.cpp verifiche del nucleo (headless) tests/ui_contract_check.py chiavi dei QML contro quelle del bridge (bridge attivo) tests/qml_check_bindings.py assegnazioni duplicate di proprieta' nei QML tests/qml_focus_check.py un tocco apre solo le regolazioni di quell'oggetto +tests/label_fit_check.py le etichette dei tasti ci stanno accanto all'icona bridge/aira_bridge.py servizio HTTP/1.1 + JSON verso il cloud (gRPC) bridge/setup_creds.py salva le credenziali Aira (file 600) bridge/aira_probe.py sonda di sola lettura: login + stato impianto diff --git a/README.md b/README.md index 134261b..4fce3e7 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ milliseconds and name the file they are unhappy about: ```sh python3 tests/qml_check_bindings.py qml # no property assigned twice in one object python3 tests/qml_focus_check.py # a tap opens only the object it touched +python3 tests/label_fit_check.py # quick-action labels fit the button ``` ## Status @@ -248,6 +249,7 @@ tests/core_test.cpp core checks (headless) tests/ui_contract_check.py QML keys vs bridge keys (bridge must be running) tests/qml_check_bindings.py duplicate property assignments in the QML tests/qml_focus_check.py a tap opens only that object's settings +tests/label_fit_check.py quick-action labels fit next to the icon (measured on a phone) bridge/ local HTTP bridge -> see bridge/README.md translations/ harbour-aira-it.ts (English is the source language) docs/PIANO.md project plan and decisions (Italian) diff --git a/docs/PIANO.md b/docs/PIANO.md index 33496b2..48543cd 100644 --- a/docs/PIANO.md +++ b/docs/PIANO.md @@ -362,8 +362,9 @@ la geometria misurata sul telefono (161 px di tasto, rientri 16, distacco 24) le etichette **Assenza**, **Torna**, **Altro** e le inglesi **Away**, **Home**, **More** ci stanno con margine; "Comandi" (8 lettere) no, quindi il tasto dice "Altro" e la pagina resta "Comandi impianto". Prova ripetibile: -`docs/icona/tasti-alla-misura-vera.png` (script in scratch, geometria nei -commenti); il pulsante Silica non espone il carattere, quindi non si può +`tests/label_fit_check.py` (le etichette si leggono dai QML e dal catalogo: se +una si allunga il controllo lo segnala) e `docs/icona/tasti-alla-misura-vera.png` +per l'occhio. Il pulsante Silica non espone il carattere, quindi non si può rimpicciolire il testo: l'unica leva è l'icona. **Stato attuale (0.6.8).** 85/85 verifiche del nucleo, 48/48 del bridge, diff --git a/tests/label_fit_check.py b/tests/label_fit_check.py new file mode 100644 index 0000000..631ef39 --- /dev/null +++ b/tests/label_fit_check.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +"""Controllo statico: le etichette dei tre tasti delle azioni rapide ci stanno? + +Sul pulsante Silica il carattere non si puo' rimpicciolire (il tipo non espone +proprieta' `font`), quindi lo spazio per il testo dipende solo dall'icona. I +numeri qui sotto sono MISURATI sullo screenshot di un telefono vero (0.6.7): + + tasto 161 px logici, rientri 16, distacco icona-testo 24 + icona di Silica senza dichiarazione = 64 px (Theme.iconSizeMedium) + -> restavano 41 px di testo: le etichette uscivano tagliate + +Con Theme.iconSizeExtraSmall (32 px) restano 73 px. Il carattere di Sailfish e' +piu' stretto di quello usato qui per misurare (DejaVu): dal confronto sul +telefono risulta circa 0,78 volte, quindi si misura col fattore di sicurezza. + +Il test legge i testi veri da qml/pages/MainPage.qml e le traduzioni da +translations/harbour-aira-it.ts: non ci sono etichette scritte a mano qui. +Se cambia la geometria della riga (margini, spazi, altezza) questi numeri vanno +rimisurati su uno screenshot nuovo. +""" +import pathlib +import re +import sys + +RADICE = pathlib.Path(__file__).resolve().parent.parent +TASTO, RIENTRO, DISTACCO = 161.0, 16.0, 24.0 +ICONA = 32.0 # Theme.iconSizeExtraSmall +CARATTERE = 22 # carattere dei pulsanti Silica +FATTORE = 0.78 # Sailfish (Nokia Pure) rispetto a DejaVu +FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf" + + +def testi_dei_tasti(qml: str): + """I testi dei tasti che hanno un'icona, nell'ordine in cui appaiono.""" + fuori = [] + for blocco in re.finditer(r"Button\s*\{(.*?)\n\s*\}", qml, re.S): + corpo = blocco.group(1) + if "../icons/quick-" not in corpo: + continue + riga = re.search(r"text:\s*(.+)", corpo) + if not riga: + continue + fuori.extend(re.findall(r'qsTr\("([^"]+)"\)', riga.group(1))) + return fuori + + +def traduzioni(ts: str): + coppie = {} + for blocco in re.finditer(r"(.*?)\s*]*>(.*?)", + ts, re.S): + coppie[blocco.group(1)] = blocco.group(2) + return coppie + + +def main(): + try: + from PIL import ImageFont + except ImportError: + print("salto: manca Pillow (serve solo per misurare il testo)") + return 0 + + qml = (RADICE / "qml" / "pages" / "MainPage.qml").read_text(encoding="utf-8") + ts = (RADICE / "translations" / "harbour-aira-it.ts").read_text(encoding="utf-8") + it = traduzioni(ts) + sorgenti = testi_dei_tasti(qml) + if len(sorgenti) < 3: + print("ESITO: trovati %d testi con icona, ne servono 3" % len(sorgenti)) + return 1 + + disponibile = TASTO - 2 * RIENTRO - ICONA - DISTACCO + problema = 0 + f = ImageFont.truetype(FONT, CARATTERE) + + def largo(testo): + # Pillow >= 9.2 ha getlength; prima si misurava con getsize + if hasattr(f, "getlength"): + return f.getlength(testo) + from PIL import Image, ImageDraw + return ImageDraw.Draw(Image.new("L", (1, 1))).textlength(testo, font=f) + + prova = [("it", [it.get(s, s) for s in sorgenti]), ("en", sorgenti)] + print("spazio per il testo: %.0f px (tasto %.0f, icona %.0f)" % (disponibile, TASTO, ICONA)) + for lingua, etichette in prova: + for t in etichette: + serve = largo(t) * FATTORE + esito = "ok" if serve <= disponibile else "NON CI STA" + if serve > disponibile: + problema += 1 + print(" %s %-9s %5.0f px %s" % (lingua, t, serve, esito)) + if problema: + print("ESITO: %d etichette non ci stanno: accorciarle o ridurre l'icona" % problema) + return 1 + print("ESITO: tutte le etichette ci stanno") + return 0 + + +if __name__ == "__main__": + sys.exit(main())