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:
@@ -165,8 +165,12 @@ type
|
||||
// without a stored username.
|
||||
// AUsernameOnly = True → type ONLY the username (no Tab, no password);
|
||||
// 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;
|
||||
const AUsername, APassword: string; AUsernameOnly: Boolean = False);
|
||||
const AUsername, APassword: string; AUsernameOnly: Boolean = False;
|
||||
ARestoreAfter: Boolean = False);
|
||||
property SecureClipboard: TSecureClipboard read FSecureClipboard;
|
||||
property TrayAdded: Boolean read FTrayAdded;
|
||||
property AutofillRegistered: Boolean read FAutofillRegistered;
|
||||
@@ -638,6 +642,16 @@ begin
|
||||
LFormHwnd := MainFormHWND(FMainForm);
|
||||
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,
|
||||
// then show and foreground the form.
|
||||
if LAppHwnd <> 0 then
|
||||
@@ -1124,53 +1138,66 @@ begin
|
||||
end;
|
||||
|
||||
procedure TPMBridge.ExecuteAutofill(ATargetHWND: HWND;
|
||||
const AUsername, APassword: string; AUsernameOnly: Boolean = False);
|
||||
const AUsername, APassword: string; AUsernameOnly: Boolean = False;
|
||||
ARestoreAfter: Boolean = False);
|
||||
const
|
||||
MinimizeSettleMs = 80;
|
||||
FocusSettleDelayMs = 120;
|
||||
var
|
||||
OwnFormHwnd: HWND;
|
||||
LMinimizedSelf: Boolean;
|
||||
begin
|
||||
OwnFormHwnd := MainFormHWND(FMainForm);
|
||||
LMinimizedSelf := False;
|
||||
if GetForegroundWindow = OwnFormHwnd then
|
||||
begin
|
||||
ShowWindow(OwnFormHwnd, SW_MINIMIZE);
|
||||
LMinimizedSelf := True;
|
||||
Sleep(MinimizeSettleMs);
|
||||
end;
|
||||
|
||||
if ATargetHWND <> 0 then
|
||||
ForceForegroundWindow(ATargetHWND);
|
||||
try
|
||||
if ATargetHWND <> 0 then
|
||||
ForceForegroundWindow(ATargetHWND);
|
||||
|
||||
WaitForModifierRelease(1000);
|
||||
Sleep(FocusSettleDelayMs);
|
||||
WaitForModifierRelease(1000);
|
||||
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;
|
||||
Sleep(60);
|
||||
SendUnicodeString(AUsername);
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if AUsername = '' then
|
||||
begin
|
||||
Sleep(200);
|
||||
SendVKey(VK_TAB);
|
||||
Sleep(200);
|
||||
SendSelectAllAndDelete;
|
||||
Sleep(60);
|
||||
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;
|
||||
|
||||
SendSelectAllAndDelete;
|
||||
Sleep(60);
|
||||
SendUnicodeString(AUsername);
|
||||
Sleep(200);
|
||||
SendVKey(VK_TAB);
|
||||
Sleep(200);
|
||||
SendSelectAllAndDelete;
|
||||
Sleep(60);
|
||||
SendUnicodeString(APassword);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
@@ -1466,7 +1466,11 @@ begin
|
||||
TTimer(Sender).Enabled := False;
|
||||
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;
|
||||
LogLine(Format('Autofill executed — target=%s, foreground_after=%s, match=%s',
|
||||
[IntToHex(TargetHwnd, 8), IntToHex(ForegroundAfter, 8),
|
||||
|
||||
Reference in New Issue
Block a user