0.7.1: i tasti delle azioni rapide dichiarano la loro altezza
In Silica il tasto piglia l'altezza dal tema (itemSizeExtraSmall, ~56 px) e non dal contenuto: con icona e etichetta (~110) il testo usciva dalla scatola e la riga di contorno della colonna finiva sopra le etichette. Ora height: Theme.itemSizeMedium. Aggiunto il controllo in tests/label_fit_check.py e il registro di posizione e altezza di ogni riga della Home.
This commit is contained in:
@@ -58,7 +58,7 @@ CREDS = BASE / "aira.json"
|
|||||||
TOKENS = BASE / "aira_tokens.json"
|
TOKENS = BASE / "aira_tokens.json"
|
||||||
POOL_CFG = BASE / "aira_config.json"
|
POOL_CFG = BASE / "aira_config.json"
|
||||||
|
|
||||||
VERSION = "0.7.0"
|
VERSION = "0.7.1"
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
@@ -693,6 +693,12 @@ def boost_restanti(st: dict) -> int:
|
|||||||
quando = datetime.fromisoformat(fine.replace("Z", "+00:00"))
|
quando = datetime.fromisoformat(fine.replace("Z", "+00:00"))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return 0
|
return 0
|
||||||
|
# L'impianto manda la fine del boost ora con il fuso, ora senza (ora
|
||||||
|
# locale): senza, va letta nel fuso dell'IMPIANTO, altrimenti la
|
||||||
|
# sottrazione con "adesso" (che il fuso ce l'ha) solleva TypeError e
|
||||||
|
# /summary risponde 502 -- l'app resta senza dati ("Nessun dato").
|
||||||
|
if quando.tzinfo is None:
|
||||||
|
quando = quando.replace(tzinfo=PLANT_TZ)
|
||||||
return max(0, int((quando - datetime.now(timezone.utc)).total_seconds()))
|
return max(0, int((quando - datetime.now(timezone.utc)).total_seconds()))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -231,6 +231,18 @@ check("boost in corso: restano ~1200 secondi",
|
|||||||
check("boost finito: zero secondi",
|
check("boost finito: zero secondi",
|
||||||
b.boost_restanti({"hot_water_boost": {"end_time": "2026-01-01T00:00:00Z"}}) == 0)
|
b.boost_restanti({"hot_water_boost": {"end_time": "2026-01-01T00:00:00Z"}}) == 0)
|
||||||
check("boost mai avviato: zero secondi", b.boost_restanti({}) == 0)
|
check("boost mai avviato: zero secondi", b.boost_restanti({}) == 0)
|
||||||
|
|
||||||
|
# L'impianto puo' mandare la fine del boost SENZA fuso (ora locale). E' la forma
|
||||||
|
# che ha fatto rispondere 502 a /summary: la sottrazione con "adesso" (che il
|
||||||
|
# fuso ce l'ha) solleva TypeError, e l'app resta senza dati ("Nessun dato").
|
||||||
|
# Il conto alla rovescia deve reggere entrambe le forme.
|
||||||
|
_fine_naive = (datetime.now(b.PLANT_TZ) + timedelta(seconds=1200)).strftime("%Y-%m-%dT%H:%M:%S")
|
||||||
|
check("boost con fine senza fuso (ora dell'impianto)",
|
||||||
|
abs(b.boost_restanti({"hot_water_boost": {"end_time": _fine_naive}}) - 1200) <= 3)
|
||||||
|
check("riassunto con fine senza fuso (la forma che dava 502)",
|
||||||
|
(b.summarize({"hot_water_boost": {"end_time": _fine_naive}}) or {}).get("boost_active") is True)
|
||||||
|
check("boost con fine fuori orario (fuso diverso)",
|
||||||
|
b.boost_restanti({"hot_water_boost": {"end_time": "2020-01-01T00:00:00"}}) == 0)
|
||||||
riassunto = b.summarize({"hot_water_boost": {"end_time": fra}})
|
riassunto = b.summarize({"hot_water_boost": {"end_time": fra}})
|
||||||
check("riassunto: dice che il boost e' in corso", riassunto.get("boost_active") is True)
|
check("riassunto: dice che il boost e' in corso", riassunto.get("boost_active") is True)
|
||||||
check("riassunto: porta i secondi del boost",
|
check("riassunto: porta i secondi del boost",
|
||||||
|
|||||||
+26
-1
@@ -410,7 +410,32 @@ respinto con 400 **prima** di partire.
|
|||||||
4. mentre il boost è in corso si deve vedere un **conto alla rovescia da 30:00 a
|
4. mentre il boost è in corso si deve vedere un **conto alla rovescia da 30:00 a
|
||||||
00:00**.
|
00:00**.
|
||||||
|
|
||||||
## 12. Conseguenze dell'uso reale (0.7.0)
|
## 12. I tasti delle azioni rapide e la riga che li copriva — v0.7.1
|
||||||
|
|
||||||
|
Carlo: *"Quando premo un bottone la riga sotto si disallinea coprendo i
|
||||||
|
pulsanti"*.
|
||||||
|
|
||||||
|
Non era la riga a spostarsi: era il **tasto** a essere piu' basso del suo
|
||||||
|
contenuto. In Silica `Button` dichiara `height: implicitHeight` e
|
||||||
|
`implicitHeight: Theme.itemSizeExtraSmall` (~56 px), un'altezza pensata per
|
||||||
|
un'icona *accanto* a un'etichetta corta. I tre tasti delle azioni rapide hanno
|
||||||
|
icona **e** etichetta (ingombro visibile ~110-160 px, quello che io avevo
|
||||||
|
misurato come "tasto 161 px"): il contenuto esce dalla scatola, e la riga
|
||||||
|
successiva della colonna -- le letture di contorno -- viene messa subito sotto
|
||||||
|
la scatola, cioe' **sopra** l'etichetta che sporge. Da li' l'impressione che si
|
||||||
|
"disallinei".
|
||||||
|
|
||||||
|
**Come è stato fatto (0.7.1).**
|
||||||
|
- i tre tasti dichiarano `height: Theme.itemSizeMedium` (`Button.qml` del target
|
||||||
|
Sailfish letto dentro il motore di build, non indovinato);
|
||||||
|
- `tests/label_fit_check.py` adesso pretende l'altezza dichiarata su ogni tasto
|
||||||
|
che porta icona e testo: senza, il tema ne da' ~56 px contro ~110 di
|
||||||
|
contenuto;
|
||||||
|
- il registro dell'avvio (dopo un secondo) elenca **posizione e altezza di ogni
|
||||||
|
riga della Home**, cosi' se la disposizione non torna si legge dal registro
|
||||||
|
quale riga finisce dove invece di dedurlo da uno screenshot.
|
||||||
|
|
||||||
|
## 13. Conseguenze dell'uso reale (0.7.0)
|
||||||
|
|
||||||
Deciso con l'uso, non in anticipo:
|
Deciso con l'uso, non in anticipo:
|
||||||
|
|
||||||
|
|||||||
+24
-2
@@ -261,6 +261,13 @@ Page {
|
|||||||
Button {
|
Button {
|
||||||
id: boostBtn
|
id: boostBtn
|
||||||
width: (parent.width - 2 * parent.spacing) / 3
|
width: (parent.width - 2 * parent.spacing) / 3
|
||||||
|
// In Silica il tasto piglia l'altezza dal tema
|
||||||
|
// (itemSizeExtraSmall ~56 px), ma icona E etichetta ne
|
||||||
|
// occupano ~110: senza questa riga l'etichetta esce dalla
|
||||||
|
// scatola e la riga successiva della colonna le finisce
|
||||||
|
// sopra (segnalato da Carlo: "la riga sotto copre i
|
||||||
|
// pulsanti"). Vedi tests/label_fit_check.py.
|
||||||
|
height: Theme.itemSizeMedium
|
||||||
text: qsTr("Boost")
|
text: qsTr("Boost")
|
||||||
// La casella dell'icona la decide il tema: senza, l'Image
|
// La casella dell'icona la decide il tema: senza, l'Image
|
||||||
// prende la misura del file e si mangia il testo.
|
// prende la misura del file e si mangia il testo.
|
||||||
@@ -274,6 +281,7 @@ Page {
|
|||||||
Button {
|
Button {
|
||||||
id: assenzaBtn
|
id: assenzaBtn
|
||||||
width: (parent.width - 2 * parent.spacing) / 3
|
width: (parent.width - 2 * parent.spacing) / 3
|
||||||
|
height: Theme.itemSizeMedium
|
||||||
// Il pulsante dice cosa succede premendolo, non lo stato.
|
// Il pulsante dice cosa succede premendolo, non lo stato.
|
||||||
// Etichette corte: nel tasto, largo un terzo di schermo,
|
// Etichette corte: nel tasto, largo un terzo di schermo,
|
||||||
// icona e testo devono starci insieme.
|
// icona e testo devono starci insieme.
|
||||||
@@ -302,6 +310,7 @@ Page {
|
|||||||
|
|
||||||
Button {
|
Button {
|
||||||
width: (parent.width - 2 * parent.spacing) / 3
|
width: (parent.width - 2 * parent.spacing) / 3
|
||||||
|
height: Theme.itemSizeMedium
|
||||||
// "Comandi" (8 lettere) non ci sta accanto a un'icona in un
|
// "Comandi" (8 lettere) non ci sta accanto a un'icona in un
|
||||||
// terzo di schermo: la pagina resta "Comandi impianto".
|
// terzo di schermo: la pagina resta "Comandi impianto".
|
||||||
text: qsTr("More")
|
text: qsTr("More")
|
||||||
@@ -350,7 +359,20 @@ Page {
|
|||||||
Timer {
|
Timer {
|
||||||
id: misura
|
id: misura
|
||||||
interval: 1000
|
interval: 1000
|
||||||
onTriggered: console.log("harbour-aira home: contenuto", flick.contentHeight,
|
onTriggered: {
|
||||||
"schermo", height)
|
console.log("harbour-aira home: contenuto", flick.contentHeight,
|
||||||
|
"schermo", height)
|
||||||
|
// Ogni riga della Home con la sua posizione: se una riga finisce
|
||||||
|
// addosso a un'altra, qui si legge quale e di quanto. E' la riga da
|
||||||
|
// mandare al primo avvio se la disposizione non torna.
|
||||||
|
for (var i = 0; i < col.children.length; ++i) {
|
||||||
|
var r = col.children[i]
|
||||||
|
if (r.height === undefined)
|
||||||
|
continue
|
||||||
|
console.log("harbour-aira riga", i, "y", Math.round(r.y),
|
||||||
|
"h", Math.round(r.height),
|
||||||
|
r.text !== undefined ? JSON.stringify(r.text) : "")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: harbour-aira
|
Name: harbour-aira
|
||||||
Summary: Control your Aira heat pump through a local bridge
|
Summary: Control your Aira heat pump through a local bridge
|
||||||
Version: 0.7.0
|
Version: 0.7.1
|
||||||
Release: 1
|
Release: 1
|
||||||
Group: Qt/Qt
|
Group: Qt/Qt
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -37,6 +37,13 @@ Address and token of the bridge live in the app settings (AppConfigLocation);
|
|||||||
no personal data is stored in the binary.
|
no personal data is stored in the binary.
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.7.1-1
|
||||||
|
- i tre tasti delle azioni rapide dichiarano la loro altezza: in Silica il tasto
|
||||||
|
la piglia dal tema (Theme.itemSizeExtraSmall, ~56 px) e non dal contenuto,
|
||||||
|
quindi con icona E etichetta il testo (~110 px) usciva dalla scatola e la riga
|
||||||
|
di contorno finiva sopra le etichette (segnalato: "la riga sotto si disallinea
|
||||||
|
coprendo i pulsanti").
|
||||||
|
- il registro dell'avvio elenca posizione e altezza di ogni riga della Home.
|
||||||
* Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.7.0-1
|
* Sun Oct 11 2026 Carlo <carlo@carlobaratto.it> - 0.7.0-1
|
||||||
- assenza con le date: popup con inizio e fine (DatePickerDialog, componenti
|
- assenza con le date: popup con inizio e fine (DatePickerDialog, componenti
|
||||||
verificati nella guida Silica dell'SDK). Il bridge manda inizio e fine come
|
verificati nella guida Silica dell'SDK). Il bridge manda inizio e fine come
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||||||
|
|
||||||
app->setOrganizationName(QStringLiteral("harbour"));
|
app->setOrganizationName(QStringLiteral("harbour"));
|
||||||
app->setApplicationName(QStringLiteral("aira"));
|
app->setApplicationName(QStringLiteral("aira"));
|
||||||
app->setApplicationVersion(QStringLiteral("0.7.0"));
|
app->setApplicationVersion(QStringLiteral("0.7.1"));
|
||||||
|
|
||||||
Settings settings;
|
Settings settings;
|
||||||
ApiClient api(&settings);
|
ApiClient api(&settings);
|
||||||
@@ -36,7 +36,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// Prima riga del log: dice subito se il device sta girando la build nuova
|
// Prima riga del log: dice subito se il device sta girando la build nuova
|
||||||
// o una stale (trappola nota del build engine).
|
// o una stale (trappola nota del build engine).
|
||||||
qDebug() << "harbour-aira v0.7.0 build" << __DATE__ << __TIME__;
|
qDebug() << "harbour-aira v0.7.1 build" << __DATE__ << __TIME__;
|
||||||
|
|
||||||
view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings);
|
view->rootContext()->setContextProperty(QStringLiteral("appSettings"), &settings);
|
||||||
view->rootContext()->setContextProperty(QStringLiteral("api"), &api);
|
view->rootContext()->setContextProperty(QStringLiteral("api"), &api);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ FONT = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
|
|||||||
|
|
||||||
|
|
||||||
def testi_dei_tasti(qml: str):
|
def testi_dei_tasti(qml: str):
|
||||||
"""I testi dei tasti che hanno un'icona, nell'ordine in cui appaiono."""
|
"""I tasti con icona: (testo, altezza dichiarata o None), in ordine."""
|
||||||
fuori = []
|
fuori = []
|
||||||
for blocco in re.finditer(r"Button\s*\{(.*?)\n\s*\}", qml, re.S):
|
for blocco in re.finditer(r"Button\s*\{(.*?)\n\s*\}", qml, re.S):
|
||||||
corpo = blocco.group(1)
|
corpo = blocco.group(1)
|
||||||
@@ -40,7 +40,11 @@ def testi_dei_tasti(qml: str):
|
|||||||
riga = re.search(r"text:\s*(.+)", corpo)
|
riga = re.search(r"text:\s*(.+)", corpo)
|
||||||
if not riga:
|
if not riga:
|
||||||
continue
|
continue
|
||||||
fuori.extend(re.findall(r'qsTr\("([^"]+)"\)', riga.group(1)))
|
testo = re.findall(r'qsTr\("([^"]+)"\)', riga.group(1))
|
||||||
|
if not testo:
|
||||||
|
continue
|
||||||
|
alt = re.search(r"^\s*height:\s*(.+)$", corpo, re.M)
|
||||||
|
fuori.append((testo[0], alt.group(1).strip() if alt else None))
|
||||||
return fuori
|
return fuori
|
||||||
|
|
||||||
|
|
||||||
@@ -62,13 +66,24 @@ def main():
|
|||||||
qml = (RADICE / "qml" / "pages" / "MainPage.qml").read_text(encoding="utf-8")
|
qml = (RADICE / "qml" / "pages" / "MainPage.qml").read_text(encoding="utf-8")
|
||||||
ts = (RADICE / "translations" / "harbour-aira-it.ts").read_text(encoding="utf-8")
|
ts = (RADICE / "translations" / "harbour-aira-it.ts").read_text(encoding="utf-8")
|
||||||
it = traduzioni(ts)
|
it = traduzioni(ts)
|
||||||
sorgenti = testi_dei_tasti(qml)
|
origine = testi_dei_tasti(qml)
|
||||||
if len(sorgenti) < 3:
|
if len(origine) < 3:
|
||||||
print("ESITO: trovati %d testi con icona, ne servono 3" % len(sorgenti))
|
print("ESITO: trovati %d testi con icona, ne servono 3" % len(origine))
|
||||||
return 1
|
return 1
|
||||||
|
sorgenti = [t for t, _ in origine]
|
||||||
|
|
||||||
disponibile = TASTO - 2 * RIENTRO - ICONA - DISTACCO
|
|
||||||
problema = 0
|
problema = 0
|
||||||
|
|
||||||
|
# 1. L'altezza. In Silica il tasto la piglia dal tema
|
||||||
|
# (Theme.itemSizeExtraSmall, ~56 px) e non dal contenuto: con icona E
|
||||||
|
# etichetta il contenuto ne occupa ~110, esce dalla scatola, e la riga
|
||||||
|
# successiva della colonna viene messa sopra la scatola -- cioe' sopra
|
||||||
|
# l'etichetta che sporge. Chi ha entrambi deve dichiarare l'altezza.
|
||||||
|
for testo, alt in origine:
|
||||||
|
if not alt:
|
||||||
|
print(" %-9s nessuna altezza dichiarata: il tema ne da' ~56 px, il contenuto ~110" % testo)
|
||||||
|
problema += 1
|
||||||
|
disponibile = TASTO - 2 * RIENTRO - ICONA - DISTACCO
|
||||||
f = ImageFont.truetype(FONT, CARATTERE)
|
f = ImageFont.truetype(FONT, CARATTERE)
|
||||||
|
|
||||||
def largo(testo):
|
def largo(testo):
|
||||||
|
|||||||
Reference in New Issue
Block a user