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:
+14
-210
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user