fix(winstate): apply the wanted window state inline, killing the restore flash
The MINIMIZEEND correction was posted through ForceQueue. Instrumenting the hook showed the window state identical at the hook and one message-loop turn later, so the queue guarded no race and only bought the normal-sized frame the user saw. ApplyWantedWindowState now runs inline; the deferred pass stays as an idempotent net. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+9
-8
@@ -47,10 +47,10 @@ Préparer : 3 entrées `A`, `B`, `C`, dont `A` dans le dossier `Work` avec le ta
|
||||
- **ACTION** : `A` ouverte + dirty → glisser la ligne `A` sur `Perso` dans la sidebar → Save
|
||||
- **ATTENDU** : identique à A3.✅
|
||||
|
||||
### A6 · glisser une **multi-sélection** sur un dossier (`moveEntriesToFolder`) ⚠️
|
||||
### A6 · glisser une **multi-sélection** sur un dossier (`moveEntriesToFolder`) ✅
|
||||
- **ACTION** : cocher `A` + `B` → glisser sur `Perso` → recharger (F5 impossible : relancer l'app)
|
||||
- **ATTENDU** : dossier changé **et** nom / username / site / tags **intacts**.
|
||||
- **Historique** : ce chemin n'appelait pas `withEncryptedMeta` → il blanchissait les métadonnées chiffrées. Si des entrées ont été déplacées ainsi avant le fix, elles sont peut-être déjà vides — vérifier.✅
|
||||
- **Historique** : ce chemin n'appelait pas `withEncryptedMeta` → il blanchissait les métadonnées chiffrées. Si des entrées ont été déplacées ainsi avant le fix, elles sont peut-être déjà vides — vérifier.
|
||||
|
||||
### A7 · suppression du dossier de l'entrée éditée
|
||||
- **ACTION** : `A` (dossier `Work`) ouverte + dirty → sidebar → supprimer le dossier `Work` → Save
|
||||
@@ -337,22 +337,23 @@ L'app démarre maximisée (`WindowState = wsMaximized` dans la `.fmx`).
|
||||
|
||||
### I1 · maximisé survit à Show desktop ✅
|
||||
- **ACTION** : fenêtre **maximisée** → `Win+D` (ou *Show desktop*) → revenir à l'app (barre des tâches, tray ou Alt+Tab).
|
||||
- **ATTENDU** : elle revient **maximisée**.
|
||||
- **ATTENDU** : elle revient **maximisée**, **sans passer par une frame en taille normale**.
|
||||
- **Historique** : FMX oublie l'état maximisé quand Windows minimise la fenêtre de l'extérieur, et aucun de nos chemins de restore ne tourne pour un clic sur la barre des tâches.
|
||||
- **Historique (flash)** : une fois l'état correct, il restait un scintillement — la fenêtre revenait normale puis se maximisait. La correction était postée via `ForceQueue` ; le log a montré que l'état ne changeait **pas** entre le hook et le tour de boucle suivant, donc la file ne protégeait d'aucune course et n'ajoutait que la frame visible. Elle s'exécute maintenant **en ligne** dans `MINIMIZEEND` (`ApplyWantedWindowState`), la passe différée restant en filet idempotent. Piste écartée au passage : poser `WPF_RESTORETOMAXIMIZED` — `GetWindowPlacement` rapporte déjà ce flag sur toute fenêtre maximisée, donc le stamp est un no-op.
|
||||
|
||||
### I2 · normal survit aussi ✅
|
||||
- **ACTION** : dé-maximiser la fenêtre → `Win+D` → revenir.
|
||||
- **ATTENDU** : elle reste en **taille normale** — le correctif ne doit jamais forcer le maximise.
|
||||
|
||||
### I3 · maximiser / dé-maximiser reste enregistré
|
||||
### I3 · maximiser / dé-maximiser reste enregistré ✅
|
||||
- **ACTION** : dé-maximiser → `Win+D` → retour (normal) → maximiser → `Win+D` → retour.
|
||||
- **ATTENDU** : chaque retour respecte le **dernier geste** de l'utilisateur.
|
||||
|
||||
### I4 · retour depuis le tray
|
||||
### I4 · retour depuis le tray ✅
|
||||
- **ACTION** : fenêtre maximisée → fermer (part au tray) → clic sur l'icône du tray.
|
||||
- **ATTENDU** : revient maximisée (chemin distinct : `RestoreFromTray` rejoue son propre placement).
|
||||
|
||||
### I5 · le garde de minimisation ne reste pas bloqué (autofill → tray) ⚠️
|
||||
### I5 · le garde de minimisation ne reste pas bloqué (autofill → tray) ✅
|
||||
Le chemin le plus subtil : `ExecuteAutofill` **minimise réellement** la fenêtre
|
||||
(`SW_MINIMIZE`) avant de taper, ce qui lève le garde ; si elle est ensuite
|
||||
**cachée** dans le tray au lieu d'être restaurée, `MINIMIZEEND` peut ne jamais
|
||||
@@ -450,7 +451,7 @@ Le flag `-tray` est ce que « Start with Windows » écrit dans `HKCU\…\Run`
|
||||
dossier de sortie. **Ne pas** laisser `-tray` dans *Run → Parameters* de l'IDE,
|
||||
sinon chaque F9 démarre caché.
|
||||
|
||||
### L1 · démarrage caché, sans bouton de barre des tâches
|
||||
### L1 · démarrage caché, sans bouton de barre des tâches ✅
|
||||
- **ACTION** : lancer avec `-tray` (l'app n'étant pas déjà lancée — sinon le
|
||||
mutex single-instance fait sortir la nouvelle instance immédiatement).
|
||||
- **ATTENDU** : **aucune fenêtre**, **aucun bouton** dans la barre des tâches,
|
||||
@@ -462,7 +463,7 @@ sinon chaque F9 démarre caché.
|
||||
non plus : il faut retirer `WS_EX_APPWINDOW` et poser `WS_EX_TOOLWINDOW`,
|
||||
depuis le `.dpr`, juste après `Application.Initialize`.
|
||||
|
||||
### L2 · le bouton revient à la restauration
|
||||
### L2 · le bouton revient à la restauration ✅
|
||||
- **ACTION** : après L1, clic sur l'icône du tray.
|
||||
- **ATTENDU** : la fenêtre s'ouvre **et** le bouton réapparaît dans la barre
|
||||
des tâches.
|
||||
|
||||
@@ -176,6 +176,7 @@ type
|
||||
procedure RestoreWindowFromTray;
|
||||
procedure WinStateMinimizeStart;
|
||||
procedure WinStateMinimizeEnd;
|
||||
procedure ApplyWantedWindowState;
|
||||
end;
|
||||
|
||||
var
|
||||
@@ -1657,18 +1658,31 @@ begin
|
||||
FIgnoreResizes := True;
|
||||
end;
|
||||
|
||||
// Back up: re-apply what the user had. Queued so it lands after FMX has
|
||||
// finished sizing the window — inline loses that race.
|
||||
// Back up: re-apply what the user had.
|
||||
procedure TMainForm.WinStateMinimizeEnd;
|
||||
begin
|
||||
// Inline, not queued: by the time this hook fires Windows has already put the
|
||||
// window back at its normal size, and measurement showed nothing touches the
|
||||
// state afterwards — the extra message-loop turn a ForceQueue costs was the
|
||||
// whole flicker, one painted normal-sized frame before the correction landed.
|
||||
ApplyWantedWindowState;
|
||||
// Second pass anyway, in case FMX sizes the window after us on some path.
|
||||
// Idempotent: it only acts when the state still disagrees.
|
||||
TThread.ForceQueue(nil,
|
||||
procedure
|
||||
var
|
||||
begin
|
||||
ApplyWantedWindowState;
|
||||
// Release the guard only after our own ShowWindow's resize has passed.
|
||||
TThread.ForceQueue(nil, procedure begin FIgnoreResizes := False; end);
|
||||
end);
|
||||
end;
|
||||
|
||||
procedure TMainForm.ApplyWantedWindowState;
|
||||
var
|
||||
LHwnd: HWND;
|
||||
begin
|
||||
begin
|
||||
LHwnd := WindowHandleToPlatform(Self.Handle).Wnd;
|
||||
if (LHwnd <> 0) and (not IsIconic(LHwnd)) then
|
||||
begin
|
||||
if (LHwnd = 0) or IsIconic(LHwnd) then Exit;
|
||||
if FWantMaximized and (not IsZoomed(LHwnd)) then
|
||||
begin
|
||||
LogLine('[winstate] re-maximising after restore');
|
||||
@@ -1679,10 +1693,6 @@ begin
|
||||
LogLine('[winstate] restoring to normal after restore');
|
||||
ShowWindow(LHwnd, SW_RESTORE);
|
||||
end;
|
||||
end;
|
||||
// Release the guard only after our own ShowWindow's resize has passed.
|
||||
TThread.ForceQueue(nil, procedure begin FIgnoreResizes := False; end);
|
||||
end);
|
||||
end;
|
||||
|
||||
// Track the state the user settles on. Ignored entirely while a minimise /
|
||||
|
||||
Reference in New Issue
Block a user