feat(android): copy from the list, server params grouped, HTML probe
The card no longer swallows the tap: it covered the whole row and OnItemClick never fired. It paints, nothing more - HitTest false - and its children keep theirs. The two buttons take a Left and a Right rather than fixed coordinates, which put them outside the frame. The sync password joins the URL and the token: it is a server parameter, and it filled the home screen although it is typed once. The panel opens by itself when one of the three is missing - folding away empty fields hides the only thing left to do. And an HTML probe, to answer a question rather than argue it: the same list painted by the system engine. It renders well, Arabic included. Two lessons: FMX passes a nil encoding to loadDataWithBaseURL, and this source file carries no byte-order mark, so Delphi reads it as ANSI and an Arabic literal is already broken at compile time - hence the numeric references. Neither is a weakness of the engine, which is what I first suspected. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+120
-11
@@ -29,6 +29,7 @@ uses
|
||||
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Platform,
|
||||
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Memo, FMX.Memo.Types,
|
||||
FMX.ScrollBox, FMX.Layouts, FMX.Edit, FMX.ListBox, FMX.Objects,
|
||||
FMX.WebBrowser,
|
||||
PMA.Vault;
|
||||
|
||||
const
|
||||
@@ -100,7 +101,11 @@ type
|
||||
// --- ecran 1 : deverrouillage
|
||||
FLayUnlock: TLayout;
|
||||
FUrl, FToken, FPwd: TEdit;
|
||||
FBtnOpen, FBtnSync, FBtnSettings, FBtnLog: TRectangle;
|
||||
FBtnOpen, FBtnSync, FBtnSettings, FBtnLog, FBtnHtml: TRectangle;
|
||||
/// Sondage HTML, cree au premier appui seulement : un moteur de rendu
|
||||
/// entier ne se paie pas au lancement pour un essai.
|
||||
FLayWeb: TLayout;
|
||||
FWeb: TWebBrowser;
|
||||
FLaySettings: TLayout;
|
||||
FStatus: TLabel;
|
||||
FLog: TMemo;
|
||||
@@ -197,6 +202,8 @@ type
|
||||
procedure OnBackTap(Sender: TObject);
|
||||
procedure OnToggleSettings(Sender: TObject);
|
||||
procedure OnToggleLog(Sender: TObject);
|
||||
procedure OnHtmlTest(Sender: TObject);
|
||||
procedure OnHtmlClose(Sender: TObject);
|
||||
procedure OnClipTimer(Sender: TObject);
|
||||
procedure OnHardwareBack(Sender: TObject; var Key: Word;
|
||||
var KeyChar: WideChar; Shift: TShiftState);
|
||||
@@ -350,10 +357,15 @@ begin
|
||||
FLaySettings := TLayout.Create(Self);
|
||||
FLaySettings.Parent := FLayUnlock;
|
||||
FLaySettings.Align := TAlignLayout.Top;
|
||||
FLaySettings.Height := 116;
|
||||
FLaySettings.Visible := False;
|
||||
FLaySettings.Height := 174;
|
||||
// Les trois vont ensemble : ce sont les coordonnees du serveur, dont le mot
|
||||
// de passe fait partie. Il occupait l'ecran d'accueil en permanence alors
|
||||
// qu'on n'y touche qu'une fois. TopEdit empile du bas vers le haut, d'ou
|
||||
// l'ordre inverse ici.
|
||||
FPwd := TopEdit(FLaySettings, 'sync password', TEST_PWD, True);
|
||||
FToken := TopEdit(FLaySettings, 'bearer token', FIRSTRUN_TOKEN);
|
||||
FUrl := TopEdit(FLaySettings, 'endpoint URL', FIRSTRUN_URL);
|
||||
FPwd.Margins.Rect := RectF(16, 8, 16, 0);
|
||||
FToken.Margins.Rect := RectF(16, 8, 16, 0);
|
||||
FUrl.Margins.Rect := RectF(16, 8, 16, 0);
|
||||
// Ce que l'appareil a retenu l'emporte sur les valeurs de premier lancement.
|
||||
@@ -362,6 +374,9 @@ begin
|
||||
FUrl.Text := LUrl;
|
||||
if LToken <> '' then FToken.Text := LToken;
|
||||
end;
|
||||
// Replier des champs vides revient a cacher la seule chose a faire.
|
||||
FLaySettings.Visible := (FUrl.Text.Trim = '') or (FToken.Text.Trim = '')
|
||||
or (FPwd.Text = '');
|
||||
|
||||
// Deux commandes discretes cote a cote : elles ne servent qu'au reglage et
|
||||
// au diagnostic, et n'ont pas a peser autant que les actions principales.
|
||||
@@ -379,14 +394,18 @@ begin
|
||||
FBtnSettings := FlatButton(LRow, 'Server settings', OnToggleSettings);
|
||||
FBtnSettings.Align := TAlignLayout.Client;
|
||||
|
||||
// Sondage, pas une fonction : il repond a une seule question, celle de
|
||||
// savoir si une page HTML peinte par le moteur du systeme rend mieux que
|
||||
// ces controles-ci. A retirer des que la reponse est prise.
|
||||
FBtnHtml := FlatButton(LRow, 'HTML', OnHtmlTest);
|
||||
FBtnHtml.Align := TAlignLayout.Left;
|
||||
FBtnHtml.Width := 90;
|
||||
FBtnHtml.Margins.Right := 8;
|
||||
|
||||
FBtnSync := TopButton(FLayUnlock, 'Sync', DoSync);
|
||||
// Accentue : c'est l'action attendue quand aucun coffre n'est encore la.
|
||||
FBtnOpen := TopButton(FLayUnlock, 'Open vault', DoOpenLocal, 52, True);
|
||||
|
||||
// Pre-rempli depuis le fichier local, jamais depuis ce fichier-ci.
|
||||
FPwd := TopEdit(FLayUnlock, 'sync password', TEST_PWD, True);
|
||||
FPwd.Margins.Rect := RectF(16, 16, 16, 0);
|
||||
|
||||
FStatus := TLabel.Create(Self);
|
||||
FStatus.Parent := FLayUnlock;
|
||||
FStatus.Align := TAlignLayout.Top;
|
||||
@@ -892,6 +911,89 @@ begin
|
||||
FLog.Visible := not FLog.Visible;
|
||||
end;
|
||||
|
||||
/// Sondage HTML : la meme liste, les memes blocs, mais peints par le moteur du
|
||||
/// systeme au lieu de FMX. Ce qu'il faut regarder : le rendu de l'arabe, la
|
||||
/// fluidite du defilement, et si la page se fond dans le degrade ou flotte
|
||||
/// dans un rectangle blanc. Contenu factice - rien du coffre ne passe ici.
|
||||
procedure TForm8.OnHtmlTest(Sender: TObject);
|
||||
const
|
||||
PAGE =
|
||||
// SANS LA DECLARATION DE JEU DE CARACTERES l'arabe sort en charabia : le
|
||||
// moteur recoit de l'UTF-8 et le lit en latin-1 faute d'indication.
|
||||
'<!doctype html><meta charset="utf-8">' +
|
||||
'<meta name="viewport" content="width=device-width,initial-scale=1">' +
|
||||
'<style>' +
|
||||
'html,body{margin:0;height:100%;background:linear-gradient(#3E6394,#1B3563);' +
|
||||
'color:#fff;font:15px system-ui,sans-serif;-webkit-tap-highlight-color:transparent}' +
|
||||
'.wrap{padding:16px}' +
|
||||
'h1{font-size:19px;font-weight:600;margin:4px 0 14px}' +
|
||||
'.s{display:flex;align-items:center;background:rgba(255,255,255,.10);' +
|
||||
'border-radius:14px;padding:12px 14px;margin-bottom:14px;color:#E8F0FA}' +
|
||||
'.row{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.12);' +
|
||||
'border-radius:20px;padding:12px;margin-bottom:8px}' +
|
||||
'.av{width:40px;height:40px;border-radius:20px;background:rgba(255,255,255,.18);' +
|
||||
'display:flex;align-items:center;justify-content:center;font-weight:600}' +
|
||||
'.t{flex:1;min-width:0}.t b{display:block;font-weight:500}' +
|
||||
'.t span{color:#B3E8F0FA;font-size:13px}' +
|
||||
'.b{background:rgba(255,255,255,.18);border:0;color:#fff;border-radius:14px;' +
|
||||
'padding:10px 12px;font-size:13px}' +
|
||||
'.b:active{opacity:.62}' +
|
||||
'</style><div class=wrap><h1>Rendu HTML</h1>' +
|
||||
'<div class=s>Rechercher</div>' +
|
||||
'<div class=row><div class=av>G</div><div class=t><b>Gitea</b>' +
|
||||
'<span>git.example.com</span></div><button class=b>User</button>' +
|
||||
'<button class=b>Pass</button></div>' +
|
||||
// L'ARABE EN REFERENCES NUMERIQUES, pas en clair : ce fichier n'a pas de
|
||||
// marque d'ordre d'octets, Delphi le lit donc en ANSI et abimerait le
|
||||
// litteral bien avant que le moteur de rendu le voie. Le detail affiche
|
||||
// de l'arabe correct parce que celui-la vient du JSON, a l'execution.
|
||||
'<div class=row><div class=av>م</div>' +
|
||||
'<div class=t><b>مرحبا بالعالم</b>' +
|
||||
'<span>اختبار العربية</span></div>' +
|
||||
'<button class=b>User</button><button class=b>Pass</button></div>' +
|
||||
'<div class=row><div class=av>N</div><div class=t><b>Nextcloud</b>' +
|
||||
'<span>cloud.example.com</span></div><button class=b>User</button>' +
|
||||
'<button class=b>Pass</button></div></div>';
|
||||
var
|
||||
LBar: TLayout;
|
||||
LBtn: TRectangle;
|
||||
begin
|
||||
if FLayWeb = nil then
|
||||
begin
|
||||
FLayWeb := TLayout.Create(Self);
|
||||
FLayWeb.Parent := Self;
|
||||
FLayWeb.Align := TAlignLayout.Client;
|
||||
|
||||
// La barre est POSEE AVANT le navigateur et alignee en bas : sur Android
|
||||
// le moteur est une vraie vue du systeme, qui se peint par-dessus tout ce
|
||||
// que FMX dessine. Elle ne survivrait pas sous lui.
|
||||
LBar := TLayout.Create(FLayWeb);
|
||||
LBar.Parent := FLayWeb;
|
||||
LBar.Align := TAlignLayout.Bottom;
|
||||
LBar.Height := 60;
|
||||
LBtn := FlatButton(LBar, 'Close', OnHtmlClose, True);
|
||||
LBtn.Align := TAlignLayout.Client;
|
||||
LBtn.Margins.Rect := RectF(16, 4, 16, 10);
|
||||
|
||||
FWeb := TWebBrowser.Create(Self);
|
||||
FWeb.Parent := FLayWeb;
|
||||
FWeb.Align := TAlignLayout.Client;
|
||||
// La page est en ASCII pur, d'ou les references numeriques plus haut :
|
||||
// FMX appelle loadDataWithBaseURL en laissant l'encodage a nil, et une
|
||||
// URL « data: », qui le porterait, est refusee par Navigate.
|
||||
// ponytail: sonde. Une vraie page serait servie en HTTP, comme sur le
|
||||
// poste, et la question de l'encodage ne se poserait plus.
|
||||
FWeb.LoadFromStrings(PAGE, '');
|
||||
end;
|
||||
FLayWeb.Visible := True;
|
||||
FLayWeb.BringToFront;
|
||||
end;
|
||||
|
||||
procedure TForm8.OnHtmlClose(Sender: TObject);
|
||||
begin
|
||||
FLayWeb.Visible := False;
|
||||
end;
|
||||
|
||||
{ ----------------------------------------------------------------------------
|
||||
Ouverture, synchronisation, verrouillage
|
||||
---------------------------------------------------------------------------- }
|
||||
@@ -1486,6 +1588,10 @@ begin
|
||||
LCard := Surface(LItem);
|
||||
LCard.Align := TAlignLayout.Client;
|
||||
LCard.Margins.Rect := RectF(4, 3, 4, 3);
|
||||
// SANS CECI L'ENTREE N'EST PLUS CLIQUABLE : le bloc couvre toute la
|
||||
// ligne et absorbe le tap, donc OnItemClick ne part jamais. Il ne sert
|
||||
// qu'a peindre. Ses enfants, eux, gardent leur propre HitTest.
|
||||
LCard.HitTest := False;
|
||||
|
||||
// Copier sans ouvrir l'entree - le geste le plus frequent. Les deux
|
||||
// boutons sont poses a la coordonnee dans leur propre conteneur : deux
|
||||
@@ -1496,14 +1602,17 @@ begin
|
||||
LActions.Width := 150;
|
||||
LActions.Margins.Rect := RectF(0, 8, 8, 8);
|
||||
|
||||
// Un Left et un Right, jamais deux fois le meme cote : leur ordre serait
|
||||
// alors laisse au hasard. Ils prennent la hauteur du conteneur, ce qui
|
||||
// evite de poser des coordonnees qui debordaient du bloc.
|
||||
LBtnUser := FlatButton(LActions, 'User', OnRowCopyUser);
|
||||
LBtnUser.Align := TAlignLayout.None;
|
||||
LBtnUser.SetBounds(0, 0, 70, 54);
|
||||
LBtnUser.Align := TAlignLayout.Left;
|
||||
LBtnUser.Width := 70;
|
||||
LBtnUser.Tag := FFiltered[I];
|
||||
|
||||
LBtnPwd := FlatButton(LActions, 'Pass', OnRowCopyPwd);
|
||||
LBtnPwd.Align := TAlignLayout.None;
|
||||
LBtnPwd.SetBounds(76, 0, 70, 54);
|
||||
LBtnPwd.Align := TAlignLayout.Right;
|
||||
LBtnPwd.Width := 70;
|
||||
LBtnPwd.Tag := FFiltered[I];
|
||||
// CONTENU CONSTRUIT A LA MAIN, sans passer par Text/Detail/Bitmap ni par
|
||||
// un StyleLookup. Ces gabarits n'existent pas dans tous les styles :
|
||||
|
||||
Reference in New Issue
Block a user