diff --git a/android-client/PMA.Theme.pas b/android-client/PMA.Theme.pas index 72a4650..31defb5 100644 --- a/android-client/PMA.Theme.pas +++ b/android-client/PMA.Theme.pas @@ -27,6 +27,7 @@ const COL_BG = TAlphaColor($FF0F172A); // fond general COL_SURFACE = TAlphaColor($FF1E293B); // blocs poses dessus COL_RAISED = TAlphaColor($FF334155); // bouton au repos + COL_LINE = TAlphaColor($FF334155); // filet des cadres COL_TEXT = TAlphaColor($FFE2E8F0); COL_MUTED = TAlphaColor($FF94A3B8); COL_ACCENT = TAlphaColor($FF22D3EE); @@ -45,9 +46,12 @@ function FlatButton(AParent: TFmxObject; const AText: string; procedure SetButtonText(AButton: TRectangle; const AText: string); procedure SetButtonEnabled(AButton: TRectangle; AEnabled: Boolean); -/// Bloc de fond, coins arrondis. +/// Bloc de fond, coins arrondis, filet discret. function Surface(AParent: TFmxObject): TRectangle; +/// Intitule de section : majuscules, petit, discret. +procedure SectionLabel(ALabel: TLabel); + /// Met un libelle a la palette. AMuted pour les intitules discrets. procedure Tint(ALabel: TLabel; ASize: Single; AMuted: Boolean = False); @@ -108,9 +112,23 @@ begin Result := TRectangle.Create(AParent); Result.Parent := AParent; Result.Fill.Color := COL_SURFACE; - Result.Stroke.Kind := TBrushKind.None; - Result.XRadius := 12; - Result.YRadius := 12; + // Filet discret plutot qu'aucune bordure : c'est lui qui detache un bloc du + // fond sans avoir a l'eclaircir, comme sur le poste Windows. + Result.Stroke.Color := COL_LINE; + Result.Stroke.Thickness := 1; + Result.XRadius := 10; + Result.YRadius := 10; +end; + +/// Intitule de section : majuscules, petit, discret. C'est ce qui donne au +/// poste Windows sa lisibilite - l'oeil saute d'un intitule a l'autre sans +/// confondre avec les valeurs. +procedure SectionLabel(ALabel: TLabel); +begin + ALabel.StyledSettings := []; + ALabel.TextSettings.Font.Size := 11; + ALabel.TextSettings.FontColor := COL_MUTED; + ALabel.Text := ALabel.Text.ToUpper; end; function FlatButton(AParent: TFmxObject; const AText: string; @@ -121,12 +139,20 @@ var begin Result := TRectangle.Create(AParent); Result.Parent := AParent; - Result.Stroke.Kind := TBrushKind.None; Result.XRadius := 10; Result.YRadius := 10; Result.Cursor := crHandPoint; - if APrimary then Result.Fill.Color := COL_ACCENT - else Result.Fill.Color := COL_RAISED; + if APrimary then + begin + Result.Fill.Color := COL_ACCENT; + Result.Stroke.Kind := TBrushKind.None; + end + else + begin + Result.Fill.Color := COL_SURFACE; + Result.Stroke.Color := COL_LINE; + Result.Stroke.Thickness := 1; + end; Result.OnClick := AOnClick; LCaption := TLabel.Create(Result); diff --git a/mainApk.pas b/mainApk.pas index b802f89..b40f1f7 100644 --- a/mainApk.pas +++ b/mainApk.pas @@ -103,7 +103,7 @@ type // --- ecran 2 : liste FLayList: TLayout; FSearch: TEdit; - FSearchHint: TLabel; + FSearchHint, FSearchClear: TLabel; /// Choix du dossier : un bouton peint, et un panneau de selection montre /// a la demande. FFolders[0] est toujours « tous les dossiers ». FBtnFolder: TRectangle; @@ -132,6 +132,7 @@ type // --- TOTP : une ligne vivante, rafraichie chaque seconde FTotpTimer: TTimer; FTotpValue: TLabel; + FTotpBar: TRectangle; FTotpCode: string; // --- presse-papiers @@ -175,6 +176,7 @@ type procedure LockVault; procedure OnSearchChanged(Sender: TObject); + procedure OnSearchClear(Sender: TObject); procedure OnFolderTap(Sender: TObject); procedure OnFolderPick(Sender: TObject); procedure PopulateFolders; @@ -454,6 +456,22 @@ begin FSearchHint.HitTest := False; FSearchHint.Opacity := 0.45; FSearchHint.Text := 'Search'; + + // 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. + FSearchClear := TLabel.Create(FSearch); + FSearchClear.Parent := FSearch; + FSearchClear.Align := TAlignLayout.Right; + FSearchClear.Width := 44; + FSearchClear.Visible := False; + FSearchClear.Text := 'X'; + FSearchClear.TextSettings.HorzAlign := TTextAlign.Center; + FSearchClear.TextSettings.VertAlign := TTextAlign.Center; + FSearchClear.StyledSettings := []; + FSearchClear.TextSettings.Font.Size := 17; + FSearchClear.TextSettings.FontColor := COL_MUTED; + FSearchClear.OnClick := OnSearchClear; end; /// Intitule discret au-dessus d'une valeur. Meme forme pour toutes les @@ -469,28 +487,33 @@ function MakeRow(AParent: TFmxObject; const ACaption: string; AOrder: Integer; out AValue: TLabel): TLayout; var LCap: TLabel; + LBox: TRectangle; begin + // Chaque champ dans son cadre, comme sur le poste Windows : c'est le cadre + // qui separe les valeurs, pas l'espacement. + LBox := Surface(AParent); + LBox.Align := TAlignLayout.Top; + LBox.Index := AOrder; + LBox.Height := 76; + LBox.Margins.Rect := RectF(12, 8, 12, 0); + Result := TLayout.Create(nil); // detruite a la main, voir ClearDetailRows - Result.Parent := AParent; - Result.Index := AOrder; - Result.Align := TAlignLayout.Top; - Result.Height := 62; - Result.Margins.Rect := RectF(12, 10, 12, 0); + Result.Parent := LBox; + Result.Align := TAlignLayout.Client; + Result.Margins.Rect := RectF(12, 8, 12, 8); + Result.TagObject := LBox; // le cadre meurt avec la ligne LCap := TLabel.Create(Result); LCap.Parent := Result; LCap.Align := TAlignLayout.Top; - LCap.Height := 20; + LCap.Height := 18; LCap.Text := ACaption; - LCap.Opacity := 0.6; - LCap.StyledSettings := LCap.StyledSettings - [TStyledSetting.Size]; - LCap.TextSettings.Font.Size := 12; + SectionLabel(LCap); AValue := TLabel.Create(Result); AValue.Parent := Result; AValue.Align := TAlignLayout.Client; - AValue.StyledSettings := AValue.StyledSettings - [TStyledSetting.Size]; - AValue.TextSettings.Font.Size := 16; + Tint(AValue, 16); end; function AddRowButton(ARow: TLayout; const AText: string; ATag: Integer; @@ -595,6 +618,7 @@ begin // Appelable avant la fin du constructeur (ShowList), d'ou la garde. if FTotpTimer <> nil then FTotpTimer.Enabled := False; FTotpValue := nil; + FTotpBar := nil; FTotpCode := ''; // LE CONTENEUR ENTIER EST DETRUIT, pas seulement ses lignes. // @@ -604,7 +628,10 @@ begin // population est fiable, on n'en fait jamais de seconde - un conteneur neuf // par entree. Ca coute la creation d'un controle, ca supprime une classe // entiere de bizarreries. - for LRow in FRowCtls do LRow.Free; + // On detruit le CADRE, pas la ligne : la ligne est son enfant et part avec. + for LRow in FRowCtls do + if LRow.TagObject is TRectangle then TRectangle(LRow.TagObject).Free + else LRow.Free; SetLength(FRowCtls, 0); FreeAndNil(FDetBody); SetLength(FRowValues, 0); @@ -690,8 +717,21 @@ begin Inc(LOrder); SetLength(FRowCtls, Length(FRowCtls) + 1); FRowCtls[High(FRowCtls)] := LRow; - FTotpValue.TextSettings.Font.Size := 26; + FTotpValue.TextSettings.Font.Size := 28; + FTotpValue.TextSettings.FontColor := COL_ACCENT; AddRowButton(LRow, 'Copy', -1, OnCopyTap); + + // Barre de progression sous le code, comme sur le poste : elle dit d'un + // coup d'oeil s'il reste le temps de le recopier. + FTotpBar := TRectangle.Create(LRow); + FTotpBar.Parent := LRow; + FTotpBar.Align := TAlignLayout.None; + FTotpBar.Fill.Color := COL_ACCENT; + FTotpBar.Stroke.Kind := TBrushKind.None; + FTotpBar.XRadius := 1; + FTotpBar.YRadius := 1; + FTotpBar.HitTest := False; + FTotpBar.SetBounds(0, 56, 0, 3); // largeur posee a chaque seconde OnTotpTick(nil); // pas d'attente d'une seconde a l'ouverture FTotpTimer.Enabled := True; end; @@ -716,8 +756,8 @@ begin // fait passer pour tronquee alors qu'elle est juste collee. // La derniere ligne affichee touche sinon le bord du defilement, ce qui la // fait passer pour tronquee alors qu'elle est juste collee. - if Length(FRows) > 0 then - FRowCtls[High(FRows)].Margins.Bottom := 12; + if (Length(FRows) > 0) and (FRowCtls[High(FRows)].TagObject is TRectangle) then + TRectangle(FRowCtls[High(FRows)].TagObject).Margins.Bottom := 12; FLayList.Visible := False; FLayDetail.Visible := True; @@ -741,10 +781,11 @@ begin Exit; end; // Coupe en deux, comme partout ailleurs : six chiffres d'affilee se - // recopient mal. + // recopient mal. Le temps restant passe dans la barre, plus dans le texte. FTotpValue.Text := Copy(FTotpCode, 1, Length(FTotpCode) div 2) + ' ' + - Copy(FTotpCode, Length(FTotpCode) div 2 + 1, MaxInt) + - ' ' + LLeft.ToString + ' s'; + Copy(FTotpCode, Length(FTotpCode) div 2 + 1, MaxInt); + if (FTotpBar <> nil) and (LPeriod > 0) then + FTotpBar.Width := FTotpBar.ParentControl.Width * LLeft / LPeriod; end; procedure TForm8.OnBackTap(Sender: TObject); @@ -1245,6 +1286,14 @@ begin SetButtonText(FBtnFolder, FFolderNames[FFolderSel]); end; +procedure TForm8.OnSearchClear(Sender: TObject); +begin + FSearch.Text := ''; + // OnChangeTracking ne se declenche pas sur une affectation par code : la + // liste resterait filtree par une requete devenue vide. + OnSearchChanged(nil); +end; + procedure TForm8.OnFolderTap(Sender: TObject); var I: Integer; @@ -1323,6 +1372,7 @@ begin // virtuel (TListView) le jour ou ca rame. Touch; FSearchHint.Visible := FSearch.Text = ''; + FSearchClear.Visible := FSearch.Text <> ''; LFolder := ''; if (FFolderSel > 0) and (FFolderSel <= High(FFolderNames)) then LFolder := FFolderNames[FFolderSel]; // 0 = tous les dossiers