feat(autofill): un troisieme bouton 2FA, et la barrette suit le theme
SetPreferredAppMode(ForceDark) ne themise que ce que le systeme dessine lui-meme - menus, ascenseurs, infobulles. Un bouton Win32 standard reste donc gris clair sur fond sombre, ce que la capture montrait. BS_OWNERDRAW + WM_DRAWITEM : le systeme garde le suivi de la souris et l'etat enfonce, nous ne reprenons que les couleurs, recopiees des variables de css/style.css. Le cadre passe dans WM_ERASEBKGND plutot que par WS_BORDER, qui se sert dans les couleurs systeme. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -162,9 +162,9 @@ via `settings_json`).
|
|||||||
|
|
||||||
## Lazy mode (remplir à la souris)
|
## Lazy mode (remplir à la souris)
|
||||||
|
|
||||||
Menu du tray → **Lazy mode (fill with the mouse)** : une barrette de deux
|
Menu du tray → **Lazy mode (fill with the mouse)** : une barrette de trois
|
||||||
boutons *Full* / *Password* qui flotte au-dessus de tout et fait exactement ce
|
boutons *Full* / *Password* / *2FA* qui flotte au-dessus de tout et fait
|
||||||
que font `Ctrl+Shift+L` / `Ctrl+Shift+P`.
|
exactement ce que font `Ctrl+Shift+L` / `Ctrl+Shift+P` / `Ctrl+Shift+T`.
|
||||||
|
|
||||||
**`WS_EX_NOACTIVATE` est toute l'astuce.** Une fenêtre ordinaire prendrait le
|
**`WS_EX_NOACTIVATE` est toute l'astuce.** Une fenêtre ordinaire prendrait le
|
||||||
premier plan au clic — le champ que l'utilisateur vient de cliquer perdrait son
|
premier plan au clic — le champ que l'utilisateur vient de cliquer perdrait son
|
||||||
@@ -185,6 +185,14 @@ Détails :
|
|||||||
pousser). `WM_ERASEBKGND` peint le fond — la classe `TPUtilWindow`
|
pousser). `WM_ERASEBKGND` peint le fond — la classe `TPUtilWindow`
|
||||||
d'`AllocateHWnd` n'a **pas** de brosse de fond.
|
d'`AllocateHWnd` n'a **pas** de brosse de fond.
|
||||||
- Position : coin bas-droit de la *work area* au démarrage, **pas persistée**.
|
- Position : coin bas-droit de la *work area* au démarrage, **pas persistée**.
|
||||||
|
- **Thème** : boutons `BS_OWNERDRAW` + `WM_DRAWITEM`, palette recopiée des
|
||||||
|
variables de `css/style.css` en COLORREF (`$00BBGGRR` — octets **inversés**
|
||||||
|
par rapport à l'hexa CSS). `SetPreferredAppMode(ForceDark)` ne themise que ce
|
||||||
|
que le système dessine lui-même (menus, ascenseurs, infobulles) : un bouton
|
||||||
|
Win32 standard reste gris clair sur fond sombre. `ApplyTitleBarTheme` mémorise
|
||||||
|
le mode dans `FDarkTheme` et `RedrawWindow(… RDW_ALLCHILDREN)` la barrette.
|
||||||
|
Le cadre est peint dans `WM_ERASEBKGND` plutôt que par `WS_BORDER`, qui se
|
||||||
|
sert dans les couleurs système.
|
||||||
|
|
||||||
## Debug mode
|
## Debug mode
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ unit PM.Bridge;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.SysUtils, System.Classes, System.Math,
|
System.SysUtils, System.Classes, System.Math, System.Types,
|
||||||
FMX.Types, FMX.Forms,
|
FMX.Types, FMX.Forms,
|
||||||
Winapi.Windows, Winapi.ShellAPI, Winapi.Messages,
|
Winapi.Windows, Winapi.ShellAPI, Winapi.Messages,
|
||||||
PM.SingleInstance;
|
PM.SingleInstance;
|
||||||
@@ -152,7 +152,15 @@ type
|
|||||||
// foreground: the browser stays active, its field stays focused, and the
|
// foreground: the browser stays active, its field stays focused, and the
|
||||||
// capture below sees exactly what the hotkey path sees.
|
// capture below sees exactly what the hotkey path sees.
|
||||||
FLazyWnd: HWND;
|
FLazyWnd: HWND;
|
||||||
|
// Dernier theme annonce par la page. La barrette est peinte a la main, donc
|
||||||
|
// elle a besoin de savoir - un bouton Win32 standard reste gris clair meme
|
||||||
|
// sous SetPreferredAppMode(ForceDark), qui ne themise que ce que le systeme
|
||||||
|
// dessine lui-meme (menus, ascenseurs, infobulles).
|
||||||
|
FDarkTheme: Boolean;
|
||||||
procedure LazyWindowHandler(var AMsg: TMessage);
|
procedure LazyWindowHandler(var AMsg: TMessage);
|
||||||
|
/// Peint un bouton de la barrette (BS_OWNERDRAW). Le systeme garde le suivi
|
||||||
|
/// du clic et l'etat enfonce ; nous ne reprenons que les couleurs.
|
||||||
|
procedure DrawLazyButton(ADraw: PDrawItemStruct);
|
||||||
// Capture the foreground window and hand it to the host. Shared by the
|
// Capture the foreground window and hand it to the host. Shared by the
|
||||||
// autofill hotkeys and the lazy strip - same target, same title matching,
|
// autofill hotkeys and the lazy strip - same target, same title matching,
|
||||||
// same picker. Only the gesture differs.
|
// same picker. Only the gesture differs.
|
||||||
@@ -377,9 +385,25 @@ const
|
|||||||
const
|
const
|
||||||
LAZY_ID_FULL = 101;
|
LAZY_ID_FULL = 101;
|
||||||
LAZY_ID_PWD = 102;
|
LAZY_ID_PWD = 102;
|
||||||
LAZY_W = 148;
|
LAZY_ID_TOTP = 103;
|
||||||
|
LAZY_W = 206;
|
||||||
LAZY_H = 40;
|
LAZY_H = 40;
|
||||||
|
|
||||||
|
// Palette de la barrette, en COLORREF ($00BBGGRR - les octets sont INVERSES
|
||||||
|
// par rapport a l'hexadecimal CSS). Reprise des variables de css/style.css pour
|
||||||
|
// que la barrette et la fenetre principale ne soient pas deux gris differents.
|
||||||
|
const
|
||||||
|
LAZY_DARK_BG = $1C1515; // --bg #15151c
|
||||||
|
LAZY_DARK_FACE = $322929; // --bg-elev-2 #292932
|
||||||
|
LAZY_DARK_DOWN = $3F3434; // --bg-elev-3 #34343f
|
||||||
|
LAZY_DARK_EDGE = $413535; // --border #353541
|
||||||
|
LAZY_DARK_INK = $F5F1F1; // --text #f1f1f5
|
||||||
|
LAZY_LITE_BG = $F5F7F7; // --bg #f7f7f5
|
||||||
|
LAZY_LITE_FACE = $FFFFFF; // --bg-elev #ffffff
|
||||||
|
LAZY_LITE_DOWN = $DDE3E3; // --bg-elev-3 #e3e3dd
|
||||||
|
LAZY_LITE_EDGE = $D7DCDC; // --border #dcdcd7
|
||||||
|
LAZY_LITE_INK = $1F1A1A; // --text #1a1a1f
|
||||||
|
|
||||||
// Dynamic WTS function pointers — wtsapi32.dll is not guaranteed on all
|
// Dynamic WTS function pointers — wtsapi32.dll is not guaranteed on all
|
||||||
// Windows SKUs (e.g. minimal Server Core without Session Services), so
|
// Windows SKUs (e.g. minimal Server Core without Session Services), so
|
||||||
// we load it at runtime and tolerate absence gracefully.
|
// we load it at runtime and tolerate absence gracefully.
|
||||||
@@ -1011,6 +1035,41 @@ begin
|
|||||||
FOnAutofillRequest(AKind, LTarget, LTitle);
|
FOnAutofillRequest(AKind, LTarget, LTitle);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TPMBridge.DrawLazyButton(ADraw: PDrawItemStruct);
|
||||||
|
var
|
||||||
|
LRect: TRect;
|
||||||
|
LBrush: HBRUSH;
|
||||||
|
LFace, LEdge, LInk: COLORREF;
|
||||||
|
LText: array[0..31] of Char;
|
||||||
|
begin
|
||||||
|
LRect := ADraw.rcItem;
|
||||||
|
if FDarkTheme then
|
||||||
|
begin
|
||||||
|
LFace := LAZY_DARK_FACE; LEdge := LAZY_DARK_EDGE; LInk := LAZY_DARK_INK;
|
||||||
|
if (ADraw.itemState and ODS_SELECTED) <> 0 then LFace := LAZY_DARK_DOWN;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
LFace := LAZY_LITE_FACE; LEdge := LAZY_LITE_EDGE; LInk := LAZY_LITE_INK;
|
||||||
|
if (ADraw.itemState and ODS_SELECTED) <> 0 then LFace := LAZY_LITE_DOWN;
|
||||||
|
end;
|
||||||
|
|
||||||
|
LBrush := CreateSolidBrush(LFace);
|
||||||
|
FillRect(ADraw.hDC, LRect, LBrush);
|
||||||
|
DeleteObject(LBrush);
|
||||||
|
LBrush := CreateSolidBrush(LEdge);
|
||||||
|
FrameRect(ADraw.hDC, LRect, LBrush);
|
||||||
|
DeleteObject(LBrush);
|
||||||
|
|
||||||
|
// Le libelle est celui de la fenetre-bouton : une seule verite, posee a la
|
||||||
|
// creation, plutot qu'une table de correspondance a tenir a jour.
|
||||||
|
GetWindowText(ADraw.hwndItem, LText, Length(LText));
|
||||||
|
SetBkMode(ADraw.hDC, TRANSPARENT);
|
||||||
|
SetTextColor(ADraw.hDC, LInk);
|
||||||
|
DrawText(ADraw.hDC, LText, -1, LRect,
|
||||||
|
DT_CENTER or DT_VCENTER or DT_SINGLELINE);
|
||||||
|
end;
|
||||||
|
|
||||||
function TPMBridge.LazyBarVisible: Boolean;
|
function TPMBridge.LazyBarVisible: Boolean;
|
||||||
begin
|
begin
|
||||||
Result := FLazyWnd <> 0;
|
Result := FLazyWnd <> 0;
|
||||||
@@ -1019,6 +1078,7 @@ end;
|
|||||||
procedure TPMBridge.LazyWindowHandler(var AMsg: TMessage);
|
procedure TPMBridge.LazyWindowHandler(var AMsg: TMessage);
|
||||||
var
|
var
|
||||||
LRect: TRect;
|
LRect: TRect;
|
||||||
|
LBrush: HBRUSH;
|
||||||
begin
|
begin
|
||||||
case AMsg.Msg of
|
case AMsg.Msg of
|
||||||
// THE STRIP IS ITS OWN TITLE BAR. It has none to grab, and it will end up
|
// THE STRIP IS ITS OWN TITLE BAR. It has none to grab, and it will end up
|
||||||
@@ -1034,7 +1094,20 @@ begin
|
|||||||
WM_ERASEBKGND:
|
WM_ERASEBKGND:
|
||||||
begin
|
begin
|
||||||
GetClientRect(FLazyWnd, LRect);
|
GetClientRect(FLazyWnd, LRect);
|
||||||
FillRect(HDC(AMsg.WParam), LRect, HBRUSH(COLOR_BTNFACE + 1));
|
LBrush := CreateSolidBrush(IfThen(FDarkTheme, LAZY_DARK_BG, LAZY_LITE_BG));
|
||||||
|
FillRect(HDC(AMsg.WParam), LRect, LBrush);
|
||||||
|
DeleteObject(LBrush);
|
||||||
|
// Le cadre est peint ici plutot que par WS_BORDER : celui-ci se sert
|
||||||
|
// dans les couleurs systeme, qui ignorent notre theme.
|
||||||
|
LBrush := CreateSolidBrush(IfThen(FDarkTheme, LAZY_DARK_EDGE, LAZY_LITE_EDGE));
|
||||||
|
FrameRect(HDC(AMsg.WParam), LRect, LBrush);
|
||||||
|
DeleteObject(LBrush);
|
||||||
|
AMsg.Result := 1;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
WM_DRAWITEM:
|
||||||
|
begin
|
||||||
|
DrawLazyButton(PDrawItemStruct(AMsg.LParam));
|
||||||
AMsg.Result := 1;
|
AMsg.Result := 1;
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
@@ -1044,6 +1117,7 @@ begin
|
|||||||
case LoWord(AMsg.WParam) of
|
case LoWord(AMsg.WParam) of
|
||||||
LAZY_ID_FULL: FireAutofillRequest(akFull);
|
LAZY_ID_FULL: FireAutofillRequest(akFull);
|
||||||
LAZY_ID_PWD: FireAutofillRequest(akPasswordOnly);
|
LAZY_ID_PWD: FireAutofillRequest(akPasswordOnly);
|
||||||
|
LAZY_ID_TOTP: FireAutofillRequest(akTotp);
|
||||||
end;
|
end;
|
||||||
AMsg.Result := 0;
|
AMsg.Result := 0;
|
||||||
Exit;
|
Exit;
|
||||||
@@ -1054,12 +1128,16 @@ end;
|
|||||||
|
|
||||||
procedure TPMBridge.SetLazyBar(AOn: Boolean);
|
procedure TPMBridge.SetLazyBar(AOn: Boolean);
|
||||||
|
|
||||||
procedure MakeButton(AId: Integer; const ACaption: string; AX: Integer);
|
// BS_OWNERDRAW : le systeme garde le suivi de la souris et l'etat enfonce,
|
||||||
|
// nous ne reprenons que les couleurs. Un bouton standard resterait gris clair
|
||||||
|
// sur fond sombre, comme sur la capture qui a motive ce changement.
|
||||||
|
procedure MakeButton(AId: Integer; const ACaption: string;
|
||||||
|
AX, AWidth: Integer);
|
||||||
var
|
var
|
||||||
LBtn: HWND;
|
LBtn: HWND;
|
||||||
begin
|
begin
|
||||||
LBtn := CreateWindow('BUTTON', PChar(ACaption),
|
LBtn := CreateWindow('BUTTON', PChar(ACaption),
|
||||||
WS_CHILD or WS_VISIBLE or BS_PUSHBUTTON, AX, 5, 66, 28,
|
WS_CHILD or WS_VISIBLE or BS_OWNERDRAW, AX, 5, AWidth, 28,
|
||||||
FLazyWnd, HMENU(AId), HInstance, nil);
|
FLazyWnd, HMENU(AId), HInstance, nil);
|
||||||
// Without this the button wears the 1995 system font.
|
// Without this the button wears the 1995 system font.
|
||||||
if LBtn <> 0 then
|
if LBtn <> 0 then
|
||||||
@@ -1083,11 +1161,10 @@ begin
|
|||||||
SetWindowLong(FLazyWnd, GWL_EXSTYLE,
|
SetWindowLong(FLazyWnd, GWL_EXSTYLE,
|
||||||
GetWindowLong(FLazyWnd, GWL_EXSTYLE) or
|
GetWindowLong(FLazyWnd, GWL_EXSTYLE) or
|
||||||
WS_EX_NOACTIVATE or WS_EX_TOPMOST or WS_EX_TOOLWINDOW);
|
WS_EX_NOACTIVATE or WS_EX_TOPMOST or WS_EX_TOOLWINDOW);
|
||||||
SetWindowLong(FLazyWnd, GWL_STYLE,
|
|
||||||
GetWindowLong(FLazyWnd, GWL_STYLE) or WS_BORDER);
|
|
||||||
|
|
||||||
MakeButton(LAZY_ID_FULL, 'Full', 5);
|
MakeButton(LAZY_ID_FULL, 'Full', 5, 56);
|
||||||
MakeButton(LAZY_ID_PWD, 'Password', 77);
|
MakeButton(LAZY_ID_PWD, 'Password', 67, 76);
|
||||||
|
MakeButton(LAZY_ID_TOTP, '2FA', 149, 52);
|
||||||
|
|
||||||
// Bottom right of the WORK area, so it does not start life under the
|
// Bottom right of the WORK area, so it does not start life under the
|
||||||
// taskbar. From there the user drags it wherever the login form is.
|
// taskbar. From there the user drags it wherever the login form is.
|
||||||
@@ -1330,6 +1407,13 @@ var
|
|||||||
DarkFlag: BOOL;
|
DarkFlag: BOOL;
|
||||||
FormHwnd: HWND;
|
FormHwnd: HWND;
|
||||||
begin
|
begin
|
||||||
|
FDarkTheme := ADark;
|
||||||
|
// La barrette ne suit aucun theme systeme : c'est nous qui la peignons, donc
|
||||||
|
// c'est nous qui la redessinons quand la page bascule.
|
||||||
|
if FLazyWnd <> 0 then
|
||||||
|
RedrawWindow(FLazyWnd, nil, 0,
|
||||||
|
RDW_INVALIDATE or RDW_ERASE or RDW_ALLCHILDREN or RDW_UPDATENOW);
|
||||||
|
|
||||||
if FMainForm = nil then Exit;
|
if FMainForm = nil then Exit;
|
||||||
FormHwnd := MainFormHWND(FMainForm);
|
FormHwnd := MainFormHWND(FMainForm);
|
||||||
if FormHwnd = 0 then Exit;
|
if FormHwnd = 0 then Exit;
|
||||||
|
|||||||
Reference in New Issue
Block a user