fix(quick-search): stop teleporting + hiding an already-open window

Two bugs when Ctrl+Shift+Q fires while the app window is open beside the
target app:
- RestoreFromTray re-applied FSavedPlacement (captured at the LAST
  MinimizeToTray) to an already-visible window -> it jumped to a stale
  position. Now: visible and not iconic -> just SetForegroundWindow.
- ExecuteAutofill minimizes our window when it is foreground (the user just
  clicked the entry) and never brought it back when hide_after was false.
  New ARestoreAfter param (= not HideAfter): restore with SW_SHOWNOACTIVATE
  after the fill, so the window returns to its position without stealing
  focus from the freshly-filled target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-07-11 19:52:26 +01:00
parent 01426a48b1
commit 3b32a621e8
2 changed files with 58 additions and 27 deletions
+53 -26
View File
@@ -165,8 +165,12 @@ type
// without a stored username. // without a stored username.
// AUsernameOnly = True → type ONLY the username (no Tab, no password); // AUsernameOnly = True → type ONLY the username (no Tab, no password);
// used by the quick-search "autofill username" action. // used by the quick-search "autofill username" action.
// ARestoreAfter = True → if the fill had to minimise our own window (it
// was foreground, e.g. quick-search pick), show it again afterwards
// WITHOUT activating it, so an app the user had open doesn't vanish.
procedure ExecuteAutofill(ATargetHWND: HWND; procedure ExecuteAutofill(ATargetHWND: HWND;
const AUsername, APassword: string; AUsernameOnly: Boolean = False); const AUsername, APassword: string; AUsernameOnly: Boolean = False;
ARestoreAfter: Boolean = False);
property SecureClipboard: TSecureClipboard read FSecureClipboard; property SecureClipboard: TSecureClipboard read FSecureClipboard;
property TrayAdded: Boolean read FTrayAdded; property TrayAdded: Boolean read FTrayAdded;
property AutofillRegistered: Boolean read FAutofillRegistered; property AutofillRegistered: Boolean read FAutofillRegistered;
@@ -638,6 +642,16 @@ begin
LFormHwnd := MainFormHWND(FMainForm); LFormHwnd := MainFormHWND(FMainForm);
LAppHwnd := FindFMXAppWindow; LAppHwnd := FindFMXAppWindow;
// Already visible and not minimised (quick-search / focus hotkey while the
// window is open): just bring it to front. Re-applying FSavedPlacement —
// captured at the LAST MinimizeToTray — would teleport the window to a
// stale position the user has since moved away from.
if FMainForm.Visible and (not IsIconic(LFormHwnd)) then
begin
SetForegroundWindow(LFormHwnd);
Exit;
end;
// Reverse order: show the app proxy first so the taskbar entry comes back, // Reverse order: show the app proxy first so the taskbar entry comes back,
// then show and foreground the form. // then show and foreground the form.
if LAppHwnd <> 0 then if LAppHwnd <> 0 then
@@ -1124,53 +1138,66 @@ begin
end; end;
procedure TPMBridge.ExecuteAutofill(ATargetHWND: HWND; procedure TPMBridge.ExecuteAutofill(ATargetHWND: HWND;
const AUsername, APassword: string; AUsernameOnly: Boolean = False); const AUsername, APassword: string; AUsernameOnly: Boolean = False;
ARestoreAfter: Boolean = False);
const const
MinimizeSettleMs = 80; MinimizeSettleMs = 80;
FocusSettleDelayMs = 120; FocusSettleDelayMs = 120;
var var
OwnFormHwnd: HWND; OwnFormHwnd: HWND;
LMinimizedSelf: Boolean;
begin begin
OwnFormHwnd := MainFormHWND(FMainForm); OwnFormHwnd := MainFormHWND(FMainForm);
LMinimizedSelf := False;
if GetForegroundWindow = OwnFormHwnd then if GetForegroundWindow = OwnFormHwnd then
begin begin
ShowWindow(OwnFormHwnd, SW_MINIMIZE); ShowWindow(OwnFormHwnd, SW_MINIMIZE);
LMinimizedSelf := True;
Sleep(MinimizeSettleMs); Sleep(MinimizeSettleMs);
end; end;
if ATargetHWND <> 0 then try
ForceForegroundWindow(ATargetHWND); if ATargetHWND <> 0 then
ForceForegroundWindow(ATargetHWND);
WaitForModifierRelease(1000); WaitForModifierRelease(1000);
Sleep(FocusSettleDelayMs); Sleep(FocusSettleDelayMs);
// Username-only: type just the username into the focused field, no Tab,
// no password. Used by the quick-search right-click / Shift+Enter path.
if AUsernameOnly then
begin
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(AUsername);
Exit;
end;
if AUsername = '' then
begin
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(APassword);
Exit;
end;
// Username-only: type just the username into the focused field, no Tab,
// no password. Used by the quick-search right-click / Shift+Enter path.
if AUsernameOnly then
begin
SendSelectAllAndDelete; SendSelectAllAndDelete;
Sleep(60); Sleep(60);
SendUnicodeString(AUsername); SendUnicodeString(AUsername);
Exit; Sleep(200);
end; SendVKey(VK_TAB);
Sleep(200);
if AUsername = '' then
begin
SendSelectAllAndDelete; SendSelectAllAndDelete;
Sleep(60); Sleep(60);
SendUnicodeString(APassword); SendUnicodeString(APassword);
Exit; finally
// We shoved our own window out of the way to give the target the
// foreground. If it was open before the fill (quick-search pick while
// the app sits next to the target), bring it back at its old position
// — SW_SHOWNOACTIVATE so the freshly-filled app keeps the focus.
if LMinimizedSelf and ARestoreAfter then
ShowWindow(OwnFormHwnd, SW_SHOWNOACTIVATE);
end; end;
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(AUsername);
Sleep(200);
SendVKey(VK_TAB);
Sleep(200);
SendSelectAllAndDelete;
Sleep(60);
SendUnicodeString(APassword);
end; end;
end. end.
+5 -1
View File
@@ -1466,7 +1466,11 @@ begin
TTimer(Sender).Enabled := False; TTimer(Sender).Enabled := False;
TTimer(Sender).Free; TTimer(Sender).Free;
FBridge.ExecuteAutofill(TargetHwnd, PendingUser, PendingPass, UserOnly); // ARestoreAfter = not HideAfter: if the app was open before the hotkey
// (quick-search beside the target), it comes back after the fill; if it
// started hidden, the MinimizeToTray below re-hides it anyway.
FBridge.ExecuteAutofill(TargetHwnd, PendingUser, PendingPass, UserOnly,
not HideAfter);
ForegroundAfter := GetForegroundWindow; ForegroundAfter := GetForegroundWindow;
LogLine(Format('Autofill executed — target=%s, foreground_after=%s, match=%s', LogLine(Format('Autofill executed — target=%s, foreground_after=%s, match=%s',
[IntToHex(TargetHwnd, 8), IntToHex(ForegroundAfter, 8), [IntToHex(TargetHwnd, 8), IntToHex(ForegroundAfter, 8),