From 131bf44b36a0c31819d459d0e3e0a95d367589a9 Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Fri, 21 Aug 2026 15:46:06 +0100 Subject: [PATCH] chore(android): drop the keyboard diagnostic, and the field only jumps if it must The trace has served: it named the cause where two rounds of reasoning had missed it. Out with it, and out with the viewport measurement it proved worthless here - a value that is always zero reads as a mechanism, and the next person would trust it. adjustResize goes too. Measured to do nothing: Android ignores the mode for a fullscreen window, which FMX's is. Leaving it in the manifest would suggest it carries something. A field already near the top of the panel - the settings ones - stays where it is. It was visible as it was, and lifting it only moved under the eye what was already fine. The plan keeps what the measurement cost: on this screen, instrument first. --- AndroidManifest.template.xml | 10 ----- android-client/PLAN.md | 31 +++++++++++++++ android-client/web/app.html | 77 +++++++++++------------------------- 3 files changed, 54 insertions(+), 64 deletions(-) diff --git a/AndroidManifest.template.xml b/AndroidManifest.template.xml index d1d5a93..2e31e2c 100644 --- a/AndroidManifest.template.xml +++ b/AndroidManifest.template.xml @@ -63,21 +63,11 @@ <%services%> - diff --git a/android-client/PLAN.md b/android-client/PLAN.md index 63f6cac..af61732 100644 --- a/android-client/PLAN.md +++ b/android-client/PLAN.md @@ -729,6 +729,37 @@ désormais, qui garantit qu'un enregistrement n'écrase pas un autre appareil, et il n'a toujours jamais tourné. Le provoquer : synchroniser depuis le poste, puis enregistrer depuis le téléphone sans synchroniser d'abord. +### Le clavier : personne ne dit ce qu'il recouvre — 2026-08-21 + +Le bas du formulaire restait sous le clavier, sans moyen d'y accéder. **Trois +sources ont été interrogées sur ce qu'il recouvre, les trois répondent comme +s'il n'existait pas** : + +- `visualViewport.height` — identique à `innerHeight`, clavier levé ; +- `innerHeight` — inchangé, la fenêtre reste en plein écran ; +- `android:windowSoftInputMode="adjustResize"` — sans effet : Android l'ignore + pour une fenêtre plein écran, ce qu'est celle de FMX. Retiré, pour ne pas + laisser croire qu'il porte quelque chose. + +La mesure qui a tranché, clavier levé : + +``` +focus: inner 719 visual 719 | box client 659 scroll 659 | body=on-detail +``` + +`client` = ce que la boîte montre, `scroll` = ce qu'elle contient. Égaux : +aucun débordement, donc rien à faire défiler. Le clavier recouvrait le bas +d'une page qui se croyait en pleine hauteur. + +**On a donc cessé de demander.** Le temps de la saisie, le panneau réserve 60 % +de hauteur sous son contenu — c'est du débordement, donc ça défile — et le champ +focalisé remonte en haut, mais **seulement s'il est dans le bas du panneau** : +un champ déjà visible ne doit pas sauter sous l'œil pour rien. + +Deux corrections antérieures, posées par raisonnement sans instrument, +reposaient toutes deux sur une mesure qui vaut zéro ici. La leçon vaut d'être +écrite : sur cet écran, **instrumenter d'abord**. + ### Ce qui devient facultatif Sans dépôt configuré, *Sync* n'a rien à faire : les réglages du serveur diff --git a/android-client/web/app.html b/android-client/web/app.html index ef14250..4266d7c 100644 --- a/android-client/web/app.html +++ b/android-client/web/app.html @@ -75,7 +75,7 @@ .scroll { flex: 1; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; - padding: 0 12px calc(16px + var(--kb, 0px)); } + padding: 0 12px 16px; } /* DE LA PLACE SOUS LE CONTENU PENDANT QU'ON TAPE, et rien a mesurer. Le clavier recouvre la page sans que rien ne le dise : ni la fenetre, qui reste en plein ecran, ni visualViewport, qui rapporte la meme hauteur @@ -684,66 +684,35 @@ addEventListener('scroll', function (ev) { if (box && box.scrollLeft && box.id !== 'folders') box.scrollLeft = 0; }, true); -// LE CLAVIER RECOUVRE LA PAGE, IL NE LA RETRECIT PAS. +// LE CLAVIER RECOUVRE LA PAGE, ET PERSONNE NE LE DIT. // -// Le cadre est cloue a l'ecran entier (position:fixed, inset:0) et le moteur ne -// le redimensionne pas quand le clavier monte : la page garde sa hauteur, le -// clavier passe par-dessus, et le tiers du bas devient inatteignable. Rien ne -// depassait, donc rien ne defilait - le champ mot de passe du formulaire ne se -// voyait qu'en repliant le clavier. +// Le cadre est cloue a l'ecran entier et le moteur ne le redimensionne pas +// quand le clavier monte. Trois sources ont ete interrogees sur ce qu'il +// recouvre - visualViewport, innerHeight, la fenetre elle-meme via +// windowSoftInputMode - et les trois repondent comme s'il n'existait pas : +// clavier leve, la boite defilante montrait 659 pour 659 de contenu. Aucun +// debordement, donc rien a faire defiler, et le bas du formulaire restait +// sous le clavier definitivement. // -// visualViewport donne la hauteur REELLEMENT visible. La reporter sur le corps -// retrecit les vues, le panneau defilant retrouve du contenu en trop, et le -// moteur peut de nouveau amener a l'ecran le champ qui prend le focus. -if (window.visualViewport) { - var vv = window.visualViewport; - // Ce que le clavier RECOUVRE, en pixels. Zero quand il est replie - et zero - // aussi si le moteur retrecit reellement la fenetre, auquel cas le panneau - // deborde tout seul et n'a besoin de rien. La meme mesure repond donc juste - // dans les deux cas, sans qu'on ait a savoir lequel s'applique. - var measureKeyboard = function () { - var hidden = Math.max(0, Math.round(window.innerHeight - vv.height)); - document.documentElement.style.setProperty('--kb', hidden + 'px'); - reportBox('keyboard'); - }; - vv.addEventListener('resize', measureKeyboard); - measureKeyboard(); -} else { - window._noVisualViewport = true; -} - -// DIAGNOSTIC, temporaire. Deux corrections posees a l'aveugle n'ont rien -// change ; celle-ci dit ce que la page mesure vraiment, dans le journal plutot -// qu'en message fugace, pour qu'on puisse la lire apres coup. -function reportBox(why) { - // PM n'existe pas encore au chargement : cette fonction tourne aussi avant - // lui, et on ne va pas casser la page pour une trace. - // Les trois vues sont TOUTES dans le document - c'est un rail, rien n'est - // masque - donc la vue active se designe par son nom, pas par sa visibilite. - if (!window.PM || !PM.log) return; - var box = document.getElementById(view); - box = box && box.querySelector('.scroll'); - var vh = window.visualViewport ? Math.round(window.visualViewport.height) : -1; - var sc = document.body.className || '(none)'; - if (!box) { PM.log(why + ': no scroll box, body=' + sc); return; } - // clientHeight = ce que la boite montre, scrollHeight = ce qu'elle contient. - // Egaux, il n'y a rien a faire defiler, et c'est TOUT le probleme. - PM.log(why + ': inner ' + window.innerHeight + ' visual ' + vh - + ' | box client ' + box.clientHeight + ' scroll ' + box.scrollHeight - + ' | body=' + sc); -} +// On cesse donc de demander. Le temps de la saisie, le panneau reserve de la +// place sous son contenu (body.typing, plus haut) - c'est du debordement, donc +// ca defile - et le champ focalise remonte en haut, la ou aucun clavier ne +// peut l'atteindre quelle que soit sa hauteur. document.addEventListener('focusin', function (ev) { var el = ev.target; if (!el.classList || !el.classList.contains('field-in')) return; document.body.classList.add('typing'); - // Le champ remonte EN HAUT du panneau : la ou le clavier ne peut pas - // l'atteindre, quelle que soit sa hauteur. Differe d'un tour, sinon la place - // reservee ci-dessus n'est pas encore prise en compte et il n'y a nulle part - // ou remonter. + // Differe d'un tour : la place reservee ci-dessus n'est pas encore prise en + // compte, et il n'y aurait nulle part ou remonter. setTimeout(function () { - var row = el.closest ? el.closest('.field, .in-wrap') : null; - if (row && row.scrollIntoView) row.scrollIntoView({ block: 'start' }); - reportBox('focus'); + var row = el.closest && el.closest('.field, .in-wrap'); + var box = el.closest && el.closest('.scroll'); + if (!row || !box || !row.scrollIntoView) return; + // NE BOUGER QUE SI C'EST UTILE. Un champ deja dans le haut du panneau - + // celui des reglages, par exemple - est visible tel quel, et le faire + // sauter quand meme ne fait que deplacer sous l'oeil ce qui allait bien. + var top = row.getBoundingClientRect().top - box.getBoundingClientRect().top; + if (top > box.clientHeight * 0.4) row.scrollIntoView({ block: 'start' }); }, 250); }); document.addEventListener('focusout', function () {