refactor(android): the unlock screen moves to HTML, Skia and the style go

The caret in an Android TEdit does not land where you touch, which makes a long bearer token impossible to correct - the reason the last FMX screen had to go. A field in the engine inherits the system's text handling (caret, selection, handles, paste) for free.

With no FMX control left, two things became dead weight and are gone: the vendored Dark.style, which had nothing left to style, and Skia - 25 MB carried solely so FMX labels would shape Arabic. The engine does its own shaping; checked on the device, an Arabic filename still reads correctly. The APK drops from 22.0 to 12.7 MB.

Delphi stays the owner of the server settings; the page only edits them and hands them back when a field loses focus, one navigation per changed field rather than one per keystroke.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-17 10:40:06 +01:00
parent d1d5d29879
commit 77809b09c8
8 changed files with 244 additions and 5914 deletions
+14 -210
View File
@@ -1,157 +1,36 @@
unit PMA.Theme;
{
Palette et controles peints a la main.
La palette, et le fond de la forme.
POURQUOI PAS UN STYLE : aucun des styles livres avec Delphi n'est pense pour
un mobile - degrades gris, angles vifs, cadres epais, tous datent de
l'epoque bureau. Et la journee a montre que tout ce qui passe par un gabarit
de style peut ne pas exister dans le style charge : le gabarit de ligne de
liste, l'invite de recherche, la ligne de detail ont disparu tour a tour.
CE QU'IL RESTE D'UN THEME. Cette unite habillait autrefois des boutons, des
surfaces et des libelles peints a la main, et chargeait un style pour les
controles natifs qu'on ne reecrivait pas. L'interface est passee au moteur du
systeme (android-client/web) : il n'y a plus un seul controle FMX a habiller,
donc tout cela est parti - avec le style vendu, qui n'avait plus rien a styler.
Un style RESTE charge malgre tout, comme socle : les champs de saisie, la
liste et le memo sont des controles natifs qu'on ne va pas reecrire, et sans
style ils s'afficheraient en clair. Tout le reste - boutons, surfaces,
couleurs - est peint ici, ou rien ne peut disparaitre.
Le style vient de Dark.style, livre avec Delphi, recopie dans le depot pour
que la compilation ne depende pas d'un chemin d'installation.
Ce qui subsiste sert au CADRE, que la page ne peut pas atteindre : le fond de
la forme, visible un instant avant le premier rendu du moteur, et les deux
couleurs que prennent les barres systeme. Elles doivent suivre celles de
app.html, sinon la jonction se voit.
}
interface
uses
// FMX.Graphics pour TBrush, que PaintBackground prend en parametre.
System.UITypes, System.Classes, FMX.Types, FMX.Graphics, FMX.Objects,
FMX.StdCtrls;
System.UITypes, FMX.Types, FMX.Graphics;
const
// Releve sur la recherche globale du telephone, qui est la reference pour
// tout ce qui se touche - l'application doit ressembler a l'endroit ou elle
// tourne, pas seulement au service qu'elle prolonge.
//
// CE QUI FAIT CE STYLE, et qu'il faut tenir : un DEGRADE bleu en fond, des
// blocs qui ne sont que du BLANC TRANSLUCIDE pose dessus, AUCUNE bordure, de
// grands arrondis, du texte blanc. La profondeur vient de la transparence,
// pas des filets - c'est pour ca que les cadres ont disparu.
// Releve sur la recherche globale du telephone : un degrade bleu, des blocs
// qui ne sont que du blanc translucide pose dessus, aucune bordure.
COL_BG_TOP = TAlphaColor($FF3E6394); // haut du degrade
COL_BG = TAlphaColor($FF1B3563); // bas du degrade, et repli uni
COL_SURFACE = TAlphaColor($1FFFFFFF); // bloc : 12 % de blanc
COL_RAISED = TAlphaColor($2EFFFFFF); // bouton au repos : 18 %
COL_ACCENT = TAlphaColor($4DFFFFFF); // action principale : 30 %
COL_LINE = TAlphaColor($00000000); // plus de filet : garde pour l'API
COL_TEXT = TAlphaColor($FFFFFFFF);
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;
/// Bouton plat. APrimary le remplit de cyan ; sinon il reste sobre.
/// Renvoie un TRectangle et non un TButton : c'est justement pour ne plus
/// dependre du gabarit qu'un TButton irait chercher dans le style.
function FlatButton(AParent: TFmxObject; const AText: string;
AOnClick: TNotifyEvent; APrimary: Boolean = False): TRectangle;
/// Fixe un arrondi choisi, au lieu de la pilule automatique. Pour les petits
/// boutons de ligne, qu'une pilule rendrait presque ovales.
procedure SetCornerRadius(AButton: TRectangle; AValue: Single);
/// Le libelle est un enfant du bouton, retenu par TagObject.
procedure SetButtonText(AButton: TRectangle; const AText: string);
procedure SetButtonEnabled(AButton: TRectangle; AEnabled: Boolean);
/// Bloc de fond, coins arrondis, filet discret.
function Surface(AParent: TFmxObject): TRectangle;
/// Intitule au-dessus d'une valeur : petit et discret, sans majuscules.
procedure SectionLabel(ALabel: TLabel);
COL_BG = TAlphaColor($FF1B3563); // bas du degrade
/// Applique le degrade bleu du fond. A poser sur le Fill de la forme.
procedure PaintBackground(AFill: TBrush);
/// Met un libelle a la palette. AMuted pour les intitules discrets.
procedure Tint(ALabel: TLabel; ASize: Single; AMuted: Boolean = False);
implementation
uses
System.SysUtils, System.Types, FMX.Styles, FMX.Controls;
{$R 'theme\theme.res' 'theme\theme.rc'}
procedure ApplyDarkTheme;
var
LStyle: TFmxObject;
begin
try
// La lecture appartient a TStyleStreaming, l'application a TStyleManager.
LStyle := TStyleStreaming.LoadFromResource(HInstance, 'PMDARKSTYLE', RT_RCDATA);
if LStyle <> nil then
TStyleManager.SetStyle(LStyle);
except
// Rien : l'apparence par defaut reste utilisable.
end;
end;
type
/// Retient l'etat au repos pour le restaurer apres l'appui. Un TRectangle
/// n'a pas d'etat « enfonce » : sans retour visuel, on ne sait pas si le
/// doigt a porte.
TPressFeedback = class(TComponent)
public
Rest: TAlphaColor;
procedure Down(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
X, Y: Single);
procedure Up(Sender: TObject; Button: TMouseButton; Shift: TShiftState;
X, Y: Single);
procedure Leave(Sender: TObject);
end;
procedure TPressFeedback.Down(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if Sender is TRectangle then TRectangle(Sender).Opacity := 0.62;
end;
procedure TPressFeedback.Up(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if Sender is TRectangle then TRectangle(Sender).Opacity := 1;
end;
procedure TPressFeedback.Leave(Sender: TObject);
begin
if Sender is TRectangle then TRectangle(Sender).Opacity := 1;
end;
function Surface(AParent: TFmxObject): TRectangle;
begin
Result := TRectangle.Create(AParent);
Result.Parent := AParent;
Result.Fill.Color := COL_SURFACE;
// AUCUNE bordure, et un grand arrondi : c'est la translucidite qui detache
// le bloc du fond. Un filet rendrait tout de suite l'air « application de
// bureau ».
Result.Stroke.Kind := TBrushKind.None;
Result.XRadius := 20;
Result.YRadius := 20;
end;
/// Intitule au-dessus d'une valeur. Ni majuscules ni petites capitales : rien
/// n'est en majuscules dans la reference, le second plan s'y marque par la
/// couleur et la taille seules.
procedure SectionLabel(ALabel: TLabel);
begin
ALabel.StyledSettings := [];
ALabel.TextSettings.Font.Size := 12;
ALabel.TextSettings.FontColor := COL_MUTED;
end;
/// Peint le degrade de fond sur une forme.
procedure PaintBackground(AFill: TBrush);
begin
AFill.Kind := TBrushKind.Gradient;
@@ -160,79 +39,4 @@ begin
// Rien de plus : un TGradient neuf descend deja du haut vers le bas.
end;
function FlatButton(AParent: TFmxObject; const AText: string;
AOnClick: TNotifyEvent; APrimary: Boolean): TRectangle;
var
LCaption: TLabel;
LFeedback: TPressFeedback;
begin
Result := TRectangle.Create(AParent);
Result.Parent := AParent;
// 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,
// pas a une couleur : la reference n'a aucun accent colore.
if APrimary then Result.Fill.Color := COL_ACCENT
else Result.Fill.Color := COL_RAISED;
Result.OnClick := AOnClick;
LCaption := TLabel.Create(Result);
LCaption.Parent := Result;
LCaption.Align := TAlignLayout.Client;
LCaption.HitTest := False; // le clic doit atteindre le rectangle
LCaption.Text := AText;
LCaption.TextSettings.HorzAlign := TTextAlign.Center;
LCaption.TextSettings.VertAlign := TTextAlign.Center;
LCaption.StyledSettings := []; // on gouverne police ET couleur
LCaption.TextSettings.Font.Size := 15;
if APrimary then LCaption.TextSettings.FontColor := COL_ON_ACC
else LCaption.TextSettings.FontColor := COL_TEXT;
Result.TagObject := LCaption;
// Le retour d'appui appartient au bouton : il vit et meurt avec lui.
LFeedback := TPressFeedback.Create(Result);
LFeedback.Rest := Result.Fill.Color;
Result.OnMouseDown := LFeedback.Down;
Result.OnMouseUp := LFeedback.Up;
Result.OnMouseLeave := LFeedback.Leave;
end;
procedure SetCornerRadius(AButton: TRectangle; AValue: Single);
begin
if AButton = nil then Exit;
// Detache le recalcul automatique, sinon la prochaine mise en page le
// ramenerait a la pilule.
AButton.OnResize := nil;
AButton.XRadius := AValue;
AButton.YRadius := AValue;
end;
procedure SetButtonText(AButton: TRectangle; const AText: string);
begin
if (AButton <> nil) and (AButton.TagObject is TLabel) then
TLabel(AButton.TagObject).Text := AText;
end;
procedure SetButtonEnabled(AButton: TRectangle; AEnabled: Boolean);
begin
if AButton = nil then Exit;
AButton.Enabled := AEnabled;
// Enabled seul ne se voit pas sur une forme peinte : sans ce grise, un
// bouton inactif invite a insister.
if AEnabled then AButton.Opacity := 1 else AButton.Opacity := 0.4;
end;
procedure Tint(ALabel: TLabel; ASize: Single; AMuted: Boolean);
begin
ALabel.StyledSettings := [];
ALabel.TextSettings.Font.Size := ASize;
if AMuted then ALabel.TextSettings.FontColor := COL_MUTED
else ALabel.TextSettings.FontColor := COL_TEXT;
end;
end.