feat(autofill): la barrette s'efface quand la souris la quitte
Clic droit dessus -> 100/75/50/25 %, persiste en pref DPAPI. Pas dans settings_json : la barrette peut etre allumee coffre ferme, ou aucune session n'existe pour lire les reglages du serveur. Le survol est sonde toutes les 200 ms plutot que suivi par TrackMouseEvent : avec quatre fenetres enfants, passer de la barrette a un bouton envoie deja un WM_MOUSELEAVE au parent, et il faudrait suivre la souris sur les cinq fenetres pour recoller les morceaux. Le menu appartient a la fenetre message-only : TrackPopupMenu reclame un SetForegroundWindow pour se refermer seul, et le faire sur la barrette detruirait le NOACTIVATE qui la fait fonctionner. Transparente n'est pas traversante - les clics sont toujours avales. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -190,6 +190,21 @@ Détails :
|
|||||||
- Le `X` **poste** `WM_LAZY_CLOSE` à la fenêtre message-only au lieu de détruire
|
- Le `X` **poste** `WM_LAZY_CLOSE` à la fenêtre message-only au lieu de détruire
|
||||||
sur place : libérer une fenêtre depuis son propre gestionnaire libérerait, en
|
sur place : libérer une fenêtre depuis son propre gestionnaire libérerait, en
|
||||||
plein envoi, l'instance qui lui sert de window proc.
|
plein envoi, l'instance qui lui sert de window proc.
|
||||||
|
- **Transparence au repos** (`WS_EX_LAYERED` + `SetLayeredWindowAttributes`) :
|
||||||
|
pleine opacité quand la souris est dessus, `FLazyAlphaPct` sinon. Réglée par
|
||||||
|
**clic droit sur la barrette** → 100/75/50/25 %, persistée en pref DPAPI
|
||||||
|
(`lazyAlphaPct`, défaut 60) — **pas** dans `settings_json`, qui exige une
|
||||||
|
session alors que la barrette peut être allumée coffre fermé. Le menu
|
||||||
|
appartient à la fenêtre message-only : `TrackPopupMenu` réclame un
|
||||||
|
`SetForegroundWindow` pour se refermer seul, et le faire sur la barrette
|
||||||
|
détruirait le `NOACTIVATE` qui la fait fonctionner. Deux portes vers ce menu —
|
||||||
|
`WM_NCRBUTTONUP` (le fond est non-client à cause de `HTCAPTION`) et
|
||||||
|
`WM_CONTEXTMENU` (remonté par les boutons).
|
||||||
|
- Le survol est **sondé** par un `SetTimer` de 200 ms, pas par `TrackMouseEvent` :
|
||||||
|
avec quatre fenêtres enfants, passer de la barrette à un bouton envoie déjà un
|
||||||
|
`WM_MOUSELEAVE` au parent. Comparer le curseur au rectangle ignore ce piège.
|
||||||
|
- Transparente **n'est pas traversante** : la barrette avale toujours les clics
|
||||||
|
sur toute sa surface (`WS_EX_TRANSPARENT` contredirait ses boutons).
|
||||||
- **Thème** : boutons `BS_OWNERDRAW` + `WM_DRAWITEM`, palette recopiée des
|
- **Thème** : boutons `BS_OWNERDRAW` + `WM_DRAWITEM`, palette recopiée des
|
||||||
variables de `css/style.css` en COLORREF (`$00BBGGRR` — octets **inversés**
|
variables de `css/style.css` en COLORREF (`$00BBGGRR` — octets **inversés**
|
||||||
par rapport à l'hexa CSS). `SetPreferredAppMode(ForceDark)` ne themise que ce
|
par rapport à l'hexa CSS). `SetPreferredAppMode(ForceDark)` ne themise que ce
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ uses
|
|||||||
System.SysUtils, System.Classes, System.Math, System.Types,
|
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, PM.UserPrefs;
|
||||||
|
|
||||||
type
|
type
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@@ -157,7 +157,19 @@ type
|
|||||||
// sous SetPreferredAppMode(ForceDark), qui ne themise que ce que le systeme
|
// sous SetPreferredAppMode(ForceDark), qui ne themise que ce que le systeme
|
||||||
// dessine lui-meme (menus, ascenseurs, infobulles).
|
// dessine lui-meme (menus, ascenseurs, infobulles).
|
||||||
FDarkTheme: Boolean;
|
FDarkTheme: Boolean;
|
||||||
|
// Opacite AU REPOS, en pourcentage - la barrette redevient pleine des que
|
||||||
|
// la souris entre. 100 = jamais transparente. Vit en pref DPAPI et non dans
|
||||||
|
// settings_json : elle peut etre allumee coffre ferme, quand aucune session
|
||||||
|
// n'existe pour lire les reglages du serveur.
|
||||||
|
FLazyAlphaPct: Integer;
|
||||||
|
// Dernier etat connu du survol, pour ne reposer l'opacite que sur un
|
||||||
|
// changement - SetLayeredWindowAttributes force un repaint a chaque appel.
|
||||||
|
FLazyHover: Boolean;
|
||||||
procedure LazyWindowHandler(var AMsg: TMessage);
|
procedure LazyWindowHandler(var AMsg: TMessage);
|
||||||
|
/// Pose l'opacite qui convient a l'etat de survol donne.
|
||||||
|
procedure ApplyLazyAlpha(AHover: Boolean);
|
||||||
|
/// Menu du clic droit sur la barrette : le choix de l'opacite au repos.
|
||||||
|
procedure ShowLazyMenu;
|
||||||
/// Peint un bouton de la barrette (BS_OWNERDRAW). Le systeme garde le suivi
|
/// 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.
|
/// du clic et l'etat enfonce ; nous ne reprenons que les couleurs.
|
||||||
procedure DrawLazyButton(ADraw: PDrawItemStruct);
|
procedure DrawLazyButton(ADraw: PDrawItemStruct);
|
||||||
@@ -391,6 +403,17 @@ const
|
|||||||
LAZY_ID_PWD = 102;
|
LAZY_ID_PWD = 102;
|
||||||
LAZY_ID_TOTP = 103;
|
LAZY_ID_TOTP = 103;
|
||||||
LAZY_ID_CLOSE = 104;
|
LAZY_ID_CLOSE = 104;
|
||||||
|
// LE SONDAGE PLUTOT QUE TrackMouseEvent. La barrette a quatre fenetres
|
||||||
|
// enfants : passer d'elle a un bouton envoie deja un WM_MOUSELEAVE au parent,
|
||||||
|
// et il faudrait donc suivre la souris sur les cinq fenetres pour recoller
|
||||||
|
// les morceaux. Comparer le curseur au rectangle cinq fois par seconde ne
|
||||||
|
// connait pas ce piege, et ne coute rien de mesurable.
|
||||||
|
LAZY_TIMER_ID = 1;
|
||||||
|
LAZY_POLL_MS = 200;
|
||||||
|
// Cle de la pref, et opacite par defaut. 60 % laisse lire ce qu'il y a
|
||||||
|
// derriere sans faire disparaitre la barrette du regard.
|
||||||
|
LAZY_ALPHA_KEY = 'lazyAlphaPct';
|
||||||
|
LAZY_ALPHA_DEFAULT = 60;
|
||||||
LAZY_W = 236;
|
LAZY_W = 236;
|
||||||
LAZY_H = 40;
|
LAZY_H = 40;
|
||||||
|
|
||||||
@@ -1075,6 +1098,56 @@ begin
|
|||||||
DT_CENTER or DT_VCENTER or DT_SINGLELINE);
|
DT_CENTER or DT_VCENTER or DT_SINGLELINE);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TPMBridge.ApplyLazyAlpha(AHover: Boolean);
|
||||||
|
begin
|
||||||
|
if FLazyWnd = 0 then Exit;
|
||||||
|
// TRANSPARENTE N'EST PAS TRAVERSANTE : la barrette continue d'avaler les
|
||||||
|
// clics sur toute sa surface. C'est un allegement du regard, pas un passage.
|
||||||
|
if AHover or (FLazyAlphaPct >= 100) then
|
||||||
|
SetLayeredWindowAttributes(FLazyWnd, 0, 255, LWA_ALPHA)
|
||||||
|
else
|
||||||
|
SetLayeredWindowAttributes(FLazyWnd, 0,
|
||||||
|
MulDiv(255, FLazyAlphaPct, 100), LWA_ALPHA);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TPMBridge.ShowLazyMenu;
|
||||||
|
const
|
||||||
|
CHOICES: array[0..3] of Integer = (100, 75, 50, 25);
|
||||||
|
var
|
||||||
|
LMenu: HMENU;
|
||||||
|
LPt: TPoint;
|
||||||
|
LCmd: Cardinal;
|
||||||
|
I: Integer;
|
||||||
|
begin
|
||||||
|
LMenu := CreatePopupMenu;
|
||||||
|
if LMenu = 0 then Exit;
|
||||||
|
try
|
||||||
|
for I := Low(CHOICES) to High(CHOICES) do
|
||||||
|
AppendMenu(LMenu,
|
||||||
|
MF_STRING or IfThen(FLazyAlphaPct = CHOICES[I], MF_CHECKED, MF_UNCHECKED),
|
||||||
|
CHOICES[I], PChar(Format('%d%% when the mouse leaves', [CHOICES[I]])));
|
||||||
|
|
||||||
|
GetCursorPos(LPt);
|
||||||
|
// Le proprietaire du menu est la fenetre message-only, JAMAIS la barrette :
|
||||||
|
// TrackPopupMenu exige un SetForegroundWindow pour se refermer tout seul, et
|
||||||
|
// le faire sur la barrette detruirait le NOACTIVATE qui la fait fonctionner.
|
||||||
|
SetForegroundWindow(FMsgWindow);
|
||||||
|
LCmd := Cardinal(TrackPopupMenu(LMenu,
|
||||||
|
TPM_RETURNCMD or TPM_RIGHTBUTTON or TPM_NONOTIFY,
|
||||||
|
LPt.X, LPt.Y, 0, FMsgWindow, nil));
|
||||||
|
PostMessage(FMsgWindow, WM_NULL, 0, 0);
|
||||||
|
|
||||||
|
if LCmd > 0 then
|
||||||
|
begin
|
||||||
|
FLazyAlphaPct := LCmd;
|
||||||
|
SetPref(LAZY_ALPHA_KEY, IntToStr(FLazyAlphaPct));
|
||||||
|
ApplyLazyAlpha(FLazyHover);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
DestroyMenu(LMenu);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TPMBridge.LazyBarVisible: Boolean;
|
function TPMBridge.LazyBarVisible: Boolean;
|
||||||
begin
|
begin
|
||||||
Result := FLazyWnd <> 0;
|
Result := FLazyWnd <> 0;
|
||||||
@@ -1084,8 +1157,31 @@ procedure TPMBridge.LazyWindowHandler(var AMsg: TMessage);
|
|||||||
var
|
var
|
||||||
LRect: TRect;
|
LRect: TRect;
|
||||||
LBrush: HBRUSH;
|
LBrush: HBRUSH;
|
||||||
|
LPt: TPoint;
|
||||||
|
LIn: Boolean;
|
||||||
begin
|
begin
|
||||||
case AMsg.Msg of
|
case AMsg.Msg of
|
||||||
|
WM_TIMER:
|
||||||
|
begin
|
||||||
|
GetCursorPos(LPt);
|
||||||
|
GetWindowRect(FLazyWnd, LRect);
|
||||||
|
LIn := PtInRect(LRect, LPt);
|
||||||
|
if LIn <> FLazyHover then
|
||||||
|
begin
|
||||||
|
FLazyHover := LIn;
|
||||||
|
ApplyLazyAlpha(LIn);
|
||||||
|
end;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
// Deux portes pour un seul menu : le fond de la barrette est declare
|
||||||
|
// non-client (HTCAPTION, pour pouvoir la trainer) et rend donc un clic droit
|
||||||
|
// NC, tandis qu'un clic droit sur un bouton remonte en WM_CONTEXTMENU.
|
||||||
|
WM_NCRBUTTONUP, WM_CONTEXTMENU:
|
||||||
|
begin
|
||||||
|
ShowLazyMenu;
|
||||||
|
AMsg.Result := 0;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
// 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
|
||||||
// sitting over the very field it fills - so every pixel that is not a
|
// sitting over the very field it fills - so every pixel that is not a
|
||||||
// button drags it out of the way.
|
// button drags it out of the way.
|
||||||
@@ -1166,7 +1262,11 @@ begin
|
|||||||
if FLazyWnd = 0 then Exit;
|
if FLazyWnd = 0 then Exit;
|
||||||
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 or WS_EX_LAYERED);
|
||||||
|
|
||||||
|
FLazyAlphaPct := StrToIntDef(GetPref(LAZY_ALPHA_KEY), LAZY_ALPHA_DEFAULT);
|
||||||
|
if (FLazyAlphaPct < 25) or (FLazyAlphaPct > 100) then
|
||||||
|
FLazyAlphaPct := LAZY_ALPHA_DEFAULT;
|
||||||
|
|
||||||
MakeButton(LAZY_ID_FULL, 'Full', 5, 56);
|
MakeButton(LAZY_ID_FULL, 'Full', 5, 56);
|
||||||
MakeButton(LAZY_ID_PWD, 'Password', 67, 76);
|
MakeButton(LAZY_ID_PWD, 'Password', 67, 76);
|
||||||
@@ -1183,6 +1283,12 @@ begin
|
|||||||
SetWindowPos(FLazyWnd, HWND_TOPMOST,
|
SetWindowPos(FLazyWnd, HWND_TOPMOST,
|
||||||
LWork.Right - LAZY_W - 24, LWork.Bottom - LAZY_H - 24, LAZY_W, LAZY_H,
|
LWork.Right - LAZY_W - 24, LWork.Bottom - LAZY_H - 24, LAZY_W, LAZY_H,
|
||||||
SWP_NOACTIVATE or SWP_SHOWWINDOW);
|
SWP_NOACTIVATE or SWP_SHOWWINDOW);
|
||||||
|
|
||||||
|
// La souris est ailleurs a la seconde ou la barrette parait : elle nait donc
|
||||||
|
// au repos, et non pleine puis fondue une fraction de seconde plus tard.
|
||||||
|
FLazyHover := False;
|
||||||
|
ApplyLazyAlpha(False);
|
||||||
|
SetTimer(FLazyWnd, LAZY_TIMER_ID, LAZY_POLL_MS, nil);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TPMBridge.MsgWindowHandler(var AMsg: TMessage);
|
procedure TPMBridge.MsgWindowHandler(var AMsg: TMessage);
|
||||||
|
|||||||
Reference in New Issue
Block a user