feat(android): copy from the list, framed entries, one radius everywhere
The cross moves inside the search field, where it belonged. My first attempt failed with a TLabel, whose HitTest is false by default, and I concluded too quickly that no child of a TEdit can take a touch. A TRectangle takes it. Buttons drop the pill and take the fields' radius: a stadium beside a 14-radius field put two shape vocabularies on one line, and consistency is worth more here than fidelity to the reference. List entries get their own block, like the detail fields - on a gradient a bare line of text has no readable edge. And each row carries User and Pass buttons, so the commonest gesture no longer requires opening the entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,10 @@ const
|
||||
COL_MUTED = TAlphaColor($B3E8F0FA);
|
||||
COL_ON_ACC = TAlphaColor($FFFFFFFF); // le blanc reste lisible sur 30 %
|
||||
|
||||
/// Un seul arrondi pour tout ce qui se touche - champs comme boutons. Il
|
||||
/// doit suivre celui de make-style.ps1, qui l'applique aux champs natifs.
|
||||
RADIUS = 14;
|
||||
|
||||
/// A appeler une fois, avant la creation de la forme.
|
||||
procedure ApplyDarkTheme;
|
||||
|
||||
@@ -105,10 +109,6 @@ type
|
||||
procedure Up(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
|
||||
X, Y: Single);
|
||||
procedure Leave(Sender: TObject);
|
||||
/// L'arrondi se recalcule a chaque changement de taille : la hauteur est
|
||||
/// posee APRES la creation, et un arrondi fixe superieur a la moitie de
|
||||
/// celle-ci transforme le bouton en ellipse.
|
||||
procedure Resized(Sender: TObject);
|
||||
end;
|
||||
|
||||
procedure TPressFeedback.Down(Sender: TObject; Button: TMouseButton;
|
||||
@@ -128,16 +128,6 @@ begin
|
||||
if Sender is TRectangle then TRectangle(Sender).Opacity := 1;
|
||||
end;
|
||||
|
||||
procedure TPressFeedback.Resized(Sender: TObject);
|
||||
var
|
||||
LR: TRectangle;
|
||||
begin
|
||||
if not (Sender is TRectangle) then Exit;
|
||||
LR := TRectangle(Sender);
|
||||
LR.XRadius := LR.Height / 2;
|
||||
LR.YRadius := LR.Height / 2;
|
||||
end;
|
||||
|
||||
function Surface(AParent: TFmxObject): TRectangle;
|
||||
begin
|
||||
Result := TRectangle.Create(AParent);
|
||||
@@ -178,9 +168,11 @@ var
|
||||
begin
|
||||
Result := TRectangle.Create(AParent);
|
||||
Result.Parent := AParent;
|
||||
// Pilule : l'arrondi vaut exactement la moitie de la hauteur, et se
|
||||
// recalcule quand elle change (voir TPressFeedback.Resized). Tout ce qui se
|
||||
// touche est arrondi ainsi dans la reference.
|
||||
// MEME ARRONDI QUE LES CHAMPS. Un bouton en pilule a cote d'un champ a 14
|
||||
// donnait deux vocabulaires de forme sur la meme ligne ; la coherence vaut
|
||||
// mieux que la fidelite a la reference sur ce point.
|
||||
Result.XRadius := RADIUS;
|
||||
Result.YRadius := RADIUS;
|
||||
Result.Cursor := crHandPoint;
|
||||
Result.Stroke.Kind := TBrushKind.None;
|
||||
// La difference entre principal et secondaire tient a l'opacite du blanc,
|
||||
@@ -208,8 +200,6 @@ begin
|
||||
Result.OnMouseDown := LFeedback.Down;
|
||||
Result.OnMouseUp := LFeedback.Up;
|
||||
Result.OnMouseLeave := LFeedback.Leave;
|
||||
Result.OnResize := LFeedback.Resized;
|
||||
LFeedback.Resized(Result);
|
||||
end;
|
||||
|
||||
procedure SetCornerRadius(AButton: TRectangle; AValue: Single);
|
||||
|
||||
+75
-39
@@ -185,6 +185,8 @@ type
|
||||
|
||||
procedure OnSearchChanged(Sender: TObject);
|
||||
procedure OnSearchClear(Sender: TObject);
|
||||
procedure OnRowCopyUser(Sender: TObject);
|
||||
procedure OnRowCopyPwd(Sender: TObject);
|
||||
procedure OnFolderTap(Sender: TObject);
|
||||
procedure OnFolderPick(Sender: TObject);
|
||||
procedure PopulateFolders;
|
||||
@@ -395,9 +397,8 @@ end;
|
||||
|
||||
procedure TForm8.BuildList;
|
||||
var
|
||||
LBar, LTop, LSearchWrap: TLayout;
|
||||
LBar, LTop: TLayout;
|
||||
LBtnLock: TRectangle;
|
||||
LClearMark: TLabel;
|
||||
LGlass: TCircle;
|
||||
LHandle: TLine;
|
||||
begin
|
||||
@@ -452,41 +453,11 @@ begin
|
||||
FBtnFolder.Width := 170;
|
||||
FBtnFolder.Margins.Left := 8;
|
||||
|
||||
// La croix vit A COTE du champ, pas dedans. Un enfant place dans un TEdit
|
||||
// ne recoit pas les touches de facon fiable : le controle natif les capte
|
||||
// sur toute sa surface. Un conteneur avec un Right et un Client leve en
|
||||
// plus toute question d'ordre.
|
||||
LSearchWrap := TLayout.Create(Self);
|
||||
LSearchWrap.Parent := LTop;
|
||||
LSearchWrap.Align := TAlignLayout.Client;
|
||||
|
||||
// La place de la croix est RESERVEE en permanence : la montrer et la cacher
|
||||
// faisait changer la largeur du champ a chaque frappe, et la ligne sautait.
|
||||
// Seule la croix elle-meme apparait et disparait.
|
||||
FSearchClear := TRectangle.Create(Self);
|
||||
FSearchClear.Parent := LSearchWrap;
|
||||
FSearchClear.Align := TAlignLayout.Right;
|
||||
FSearchClear.Width := 46;
|
||||
FSearchClear.Fill.Kind := TBrushKind.None; // invisible, mais cliquable
|
||||
FSearchClear.Stroke.Kind := TBrushKind.None;
|
||||
FSearchClear.OnClick := OnSearchClear;
|
||||
|
||||
LClearMark := TLabel.Create(FSearchClear);
|
||||
LClearMark.Parent := FSearchClear;
|
||||
LClearMark.Align := TAlignLayout.Client;
|
||||
LClearMark.HitTest := False; // le clic va au rectangle
|
||||
LClearMark.Text := 'X';
|
||||
LClearMark.TextSettings.HorzAlign := TTextAlign.Center;
|
||||
LClearMark.TextSettings.VertAlign := TTextAlign.Center;
|
||||
LClearMark.Visible := False;
|
||||
Tint(LClearMark, 17, True);
|
||||
FSearchClearMark := LClearMark;
|
||||
|
||||
// Le champ reste natif - il faut un clavier - et son fond vient desormais du
|
||||
// style, recolore a la palette par make-style.ps1. PAS de rectangle derriere
|
||||
// lui : il en resultait deux cadres arrondis emboites.
|
||||
FSearch := TEdit.Create(Self);
|
||||
FSearch.Parent := LSearchWrap;
|
||||
FSearch.Parent := LTop;
|
||||
FSearch.Align := TAlignLayout.Client;
|
||||
// Le texte saisi commence apres la loupe.
|
||||
FSearch.Padding.Left := 32;
|
||||
@@ -527,6 +498,29 @@ begin
|
||||
FSearchHint.Opacity := 0.45;
|
||||
FSearchHint.Text := 'Search';
|
||||
|
||||
// Croix DANS le champ, et non plus a cote. Le premier essai avait echoue
|
||||
// avec un TLabel, dont HitTest vaut faux par defaut ; j'en avais conclu trop
|
||||
// vite qu'un enfant de TEdit ne recoit jamais les touches. Un TRectangle les
|
||||
// prend. Sa place est occupee en permanence pour que la largeur du champ ne
|
||||
// bouge pas ; seule la croix apparait.
|
||||
FSearchClear := TRectangle.Create(FSearch);
|
||||
FSearchClear.Parent := FSearch;
|
||||
FSearchClear.Align := TAlignLayout.Right;
|
||||
FSearchClear.Width := 46;
|
||||
FSearchClear.Fill.Kind := TBrushKind.None; // invisible, mais cliquable
|
||||
FSearchClear.Stroke.Kind := TBrushKind.None;
|
||||
FSearchClear.OnClick := OnSearchClear;
|
||||
|
||||
FSearchClearMark := TLabel.Create(FSearchClear);
|
||||
FSearchClearMark.Parent := FSearchClear;
|
||||
FSearchClearMark.Align := TAlignLayout.Client;
|
||||
FSearchClearMark.HitTest := False; // le clic va au rectangle
|
||||
FSearchClearMark.Text := 'X';
|
||||
FSearchClearMark.TextSettings.HorzAlign := TTextAlign.Center;
|
||||
FSearchClearMark.TextSettings.VertAlign := TTextAlign.Center;
|
||||
FSearchClearMark.Visible := False;
|
||||
Tint(FSearchClearMark, 17, True);
|
||||
|
||||
// Croix d'effacement, montree seulement quand il y a quelque chose a
|
||||
// effacer. Un TLabel et non un bouton : dans un champ, une croix se touche,
|
||||
// elle ne se dessine pas comme une commande.
|
||||
@@ -1363,6 +1357,22 @@ begin
|
||||
SetButtonText(FBtnFolder, FFolderNames[FFolderSel]);
|
||||
end;
|
||||
|
||||
/// Copie depuis la liste. Le Tag du bouton porte l'indice dans le coffre, pas
|
||||
/// dans la liste affichee - la recherche renumerote les lignes, pas le coffre.
|
||||
procedure TForm8.OnRowCopyUser(Sender: TObject);
|
||||
begin
|
||||
Touch;
|
||||
if (Sender is TRectangle) and (TRectangle(Sender).Tag < FVault.Count) then
|
||||
CopyToClipboard(FVault.Entry(TRectangle(Sender).Tag).Username, 'Username');
|
||||
end;
|
||||
|
||||
procedure TForm8.OnRowCopyPwd(Sender: TObject);
|
||||
begin
|
||||
Touch;
|
||||
if (Sender is TRectangle) and (TRectangle(Sender).Tag < FVault.Count) then
|
||||
CopyToClipboard(FVault.Entry(TRectangle(Sender).Tag).Password, 'Password');
|
||||
end;
|
||||
|
||||
procedure TForm8.OnSearchClear(Sender: TObject);
|
||||
begin
|
||||
FSearch.Text := '';
|
||||
@@ -1441,7 +1451,8 @@ var
|
||||
LFolder: string;
|
||||
LBmp: TBitmap;
|
||||
LImg: TImage;
|
||||
LBody: TLayout;
|
||||
LBody, LActions: TLayout;
|
||||
LCard, LBtnUser, LBtnPwd: TRectangle;
|
||||
LTitle, LSub: TLabel;
|
||||
begin
|
||||
// ponytail: refonte complete de la liste a chaque frappe. Sur un coffre de
|
||||
@@ -1467,8 +1478,33 @@ begin
|
||||
E := FVault.Entry(FFiltered[I]);
|
||||
LItem := TListBoxItem.Create(FList);
|
||||
LItem.Parent := FList;
|
||||
LItem.Height := 56;
|
||||
LItem.Height := 76;
|
||||
LItem.Tag := FFiltered[I]; // indice dans le coffre, pas dans la liste
|
||||
|
||||
// Chaque entree dans son bloc, comme les champs du detail : sur un fond
|
||||
// en degrade, une simple ligne de texte flotte sans limite lisible.
|
||||
LCard := Surface(LItem);
|
||||
LCard.Align := TAlignLayout.Client;
|
||||
LCard.Margins.Rect := RectF(4, 3, 4, 3);
|
||||
|
||||
// Copier sans ouvrir l'entree - le geste le plus frequent. Les deux
|
||||
// boutons sont poses a la coordonnee dans leur propre conteneur : deux
|
||||
// controles alignes a droite laisseraient leur ordre au hasard.
|
||||
LActions := TLayout.Create(LCard);
|
||||
LActions.Parent := LCard;
|
||||
LActions.Align := TAlignLayout.Right;
|
||||
LActions.Width := 150;
|
||||
LActions.Margins.Rect := RectF(0, 8, 8, 8);
|
||||
|
||||
LBtnUser := FlatButton(LActions, 'User', OnRowCopyUser);
|
||||
LBtnUser.Align := TAlignLayout.None;
|
||||
LBtnUser.SetBounds(0, 0, 70, 54);
|
||||
LBtnUser.Tag := FFiltered[I];
|
||||
|
||||
LBtnPwd := FlatButton(LActions, 'Pass', OnRowCopyPwd);
|
||||
LBtnPwd.Align := TAlignLayout.None;
|
||||
LBtnPwd.SetBounds(76, 0, 70, 54);
|
||||
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 :
|
||||
// avec Dark.style, 'listboxitembottomdetail' disparait et emporte
|
||||
@@ -1476,8 +1512,8 @@ begin
|
||||
// alignes Left et Client - il n'y a alors pas d'ordre a deviner.
|
||||
if FIcons.TryGetValue(FFiltered[I], LBmp) then
|
||||
begin
|
||||
LImg := TImage.Create(LItem);
|
||||
LImg.Parent := LItem;
|
||||
LImg := TImage.Create(LCard);
|
||||
LImg.Parent := LCard;
|
||||
LImg.Align := TAlignLayout.Left;
|
||||
LImg.Width := 40;
|
||||
LImg.Margins.Rect := RectF(4, 8, 8, 8);
|
||||
@@ -1485,8 +1521,8 @@ begin
|
||||
LImg.Bitmap.Assign(LBmp);
|
||||
end;
|
||||
|
||||
LBody := TLayout.Create(LItem);
|
||||
LBody.Parent := LItem;
|
||||
LBody := TLayout.Create(LCard);
|
||||
LBody.Parent := LCard;
|
||||
LBody.Align := TAlignLayout.Client;
|
||||
LBody.Margins.Rect := RectF(8, 4, 8, 4);
|
||||
LBody.HitTest := False;
|
||||
|
||||
Reference in New Issue
Block a user