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.
This commit is contained in:
r-zakarya
2026-08-21 15:46:06 +01:00
parent ad8a037e29
commit 131bf44b36
3 changed files with 54 additions and 64 deletions
-10
View File
@@ -63,21 +63,11 @@
<%services%> <%services%>
<!-- Our activity is a subclass of the built-in NativeActivity framework class. <!-- Our activity is a subclass of the built-in NativeActivity framework class.
This will take care of integrating with our NDK code. --> This will take care of integrating with our NDK code. -->
<!-- windowSoftInputMode : LA FENETRE SE RETRECIT QUAND LE CLAVIER MONTE.
Sans cela il RECOUVRE la page - le cadre garde sa hauteur, le
contenu ne deborde de rien, et le bas du formulaire devient
inatteignable : le champ mot de passe ne se voyait qu'en repliant
le clavier. Mesure a l'appui, clavier leve : innerHeight et
visualViewport.height restaient tous deux a 719, et la boite
defilante montrait 659 pour 659 de contenu. La page n'avait aucun
moyen d'apprendre que le clavier existait ; c'est la fenetre qui
devait le dire. -->
<activity <activity
android:name="com.embarcadero.firemonkey.FMXNativeActivity" android:name="com.embarcadero.firemonkey.FMXNativeActivity"
android:exported="true" android:exported="true"
android:label="%activityLabel%" android:label="%activityLabel%"
android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode" android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTask"> android:launchMode="singleTask">
<!-- Tell NativeActivity the name of our .so --> <!-- Tell NativeActivity the name of our .so -->
<meta-data android:name="android.app.lib_name" android:value="%libNameValue%" /> <meta-data android:name="android.app.lib_name" android:value="%libNameValue%" />
+31
View File
@@ -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, et il n'a toujours jamais tourné. Le provoquer : synchroniser depuis le poste,
puis enregistrer depuis le téléphone sans synchroniser d'abord. 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 ### Ce qui devient facultatif
Sans dépôt configuré, *Sync* n'a rien à faire : les réglages du serveur Sans dépôt configuré, *Sync* n'a rien à faire : les réglages du serveur
+23 -54
View File
@@ -75,7 +75,7 @@
.scroll { flex: 1; overflow-y: auto; overflow-x: hidden; .scroll { flex: 1; overflow-y: auto; overflow-x: hidden;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
overscroll-behavior: contain; 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. /* 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 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 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; if (box && box.scrollLeft && box.id !== 'folders') box.scrollLeft = 0;
}, true); }, 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 cadre est cloue a l'ecran entier et le moteur ne le redimensionne pas
// le redimensionne pas quand le clavier monte : la page garde sa hauteur, le // quand le clavier monte. Trois sources ont ete interrogees sur ce qu'il
// clavier passe par-dessus, et le tiers du bas devient inatteignable. Rien ne // recouvre - visualViewport, innerHeight, la fenetre elle-meme via
// depassait, donc rien ne defilait - le champ mot de passe du formulaire ne se // windowSoftInputMode - et les trois repondent comme s'il n'existait pas :
// voyait qu'en repliant le clavier. // 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 // On cesse donc de demander. Le temps de la saisie, le panneau reserve de la
// retrecit les vues, le panneau defilant retrouve du contenu en trop, et le // place sous son contenu (body.typing, plus haut) - c'est du debordement, donc
// moteur peut de nouveau amener a l'ecran le champ qui prend le focus. // ca defile - et le champ focalise remonte en haut, la ou aucun clavier ne
if (window.visualViewport) { // peut l'atteindre quelle que soit sa hauteur.
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);
}
document.addEventListener('focusin', function (ev) { document.addEventListener('focusin', function (ev) {
var el = ev.target; var el = ev.target;
if (!el.classList || !el.classList.contains('field-in')) return; if (!el.classList || !el.classList.contains('field-in')) return;
document.body.classList.add('typing'); document.body.classList.add('typing');
// Le champ remonte EN HAUT du panneau : la ou le clavier ne peut pas // Differe d'un tour : la place reservee ci-dessus n'est pas encore prise en
// l'atteindre, quelle que soit sa hauteur. Differe d'un tour, sinon la place // compte, et il n'y aurait nulle part ou remonter.
// reservee ci-dessus n'est pas encore prise en compte et il n'y a nulle part
// ou remonter.
setTimeout(function () { setTimeout(function () {
var row = el.closest ? el.closest('.field, .in-wrap') : null; var row = el.closest && el.closest('.field, .in-wrap');
if (row && row.scrollIntoView) row.scrollIntoView({ block: 'start' }); var box = el.closest && el.closest('.scroll');
reportBox('focus'); 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); }, 250);
}); });
document.addEventListener('focusout', function () { document.addEventListener('focusout', function () {