fix(window): route every tray restore through one guard-clearing helper
Checking the hook's blast radius surfaced a real interaction: ExecuteAutofill genuinely minimises the window (SW_MINIMIZE) before typing, which raises the minimise guard. If the window is then HIDDEN to the tray rather than un-minimised — the quick-search hide-after flow does exactly that — MINIMIZEEND may never fire and the guard would stay raised for the rest of the session, silently freezing the tracked window state. All six FBridge.RestoreFromTray call sites now go through RestoreWindowFromTray, which clears the guard first. RestoreFromTray replays its own saved placement, so nothing is left for the guard to protect there. MinimizeToTray itself is unaffected: it uses SW_HIDE, which raises no minimise event at all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -169,6 +169,9 @@ type
|
|||||||
public
|
public
|
||||||
// Called from the WinEvent hook. WINEVENT_OUTOFCONTEXT delivers through
|
// Called from the WinEvent hook. WINEVENT_OUTOFCONTEXT delivers through
|
||||||
// our own message queue, so these already run on the main thread.
|
// our own message queue, so these already run on the main thread.
|
||||||
|
// Every tray restore goes through here so the minimise guard cannot get
|
||||||
|
// stuck raised (see the implementation).
|
||||||
|
procedure RestoreWindowFromTray;
|
||||||
procedure WinStateMinimizeStart;
|
procedure WinStateMinimizeStart;
|
||||||
procedure WinStateMinimizeEnd;
|
procedure WinStateMinimizeEnd;
|
||||||
end;
|
end;
|
||||||
@@ -407,7 +410,7 @@ begin
|
|||||||
// it vanishes from the taskbar it came from. Restored-from-iconic just
|
// it vanishes from the taskbar it came from. Restored-from-iconic just
|
||||||
// stays open after the modal closes.
|
// stays open after the modal closes.
|
||||||
LWasHidden := not Self.Visible;
|
LWasHidden := not Self.Visible;
|
||||||
FBridge.RestoreFromTray;
|
RestoreWindowFromTray;
|
||||||
LWasHiddenJs := BoolToStr(LWasHidden, True).ToLower;
|
LWasHiddenJs := BoolToStr(LWasHidden, True).ToLower;
|
||||||
// Second arg forFill=true tells JS to SendInput on pick instead of copy.
|
// Second arg forFill=true tells JS to SendInput on pick instead of copy.
|
||||||
WebBrowser.ExecuteJavaScript(
|
WebBrowser.ExecuteJavaScript(
|
||||||
@@ -431,7 +434,7 @@ begin
|
|||||||
// can paste into the target app without an extra alt-tab. Iconic-only
|
// can paste into the target app without an extra alt-tab. Iconic-only
|
||||||
// (taskbar) does NOT count — see BridgeQuickSearchHotkey.
|
// (taskbar) does NOT count — see BridgeQuickSearchHotkey.
|
||||||
LWasHidden := not Self.Visible;
|
LWasHidden := not Self.Visible;
|
||||||
FBridge.RestoreFromTray;
|
RestoreWindowFromTray;
|
||||||
LWasHiddenJs := BoolToStr(LWasHidden, True).ToLower;
|
LWasHiddenJs := BoolToStr(LWasHidden, True).ToLower;
|
||||||
WebBrowser.ExecuteJavaScript(
|
WebBrowser.ExecuteJavaScript(
|
||||||
'if(window.Bridge&&typeof Bridge.openQuickSearch==="function")' +
|
'if(window.Bridge&&typeof Bridge.openQuickSearch==="function")' +
|
||||||
@@ -444,7 +447,7 @@ var
|
|||||||
EscapedTitle: string;
|
EscapedTitle: string;
|
||||||
begin
|
begin
|
||||||
if not FServer.Active then Exit;
|
if not FServer.Active then Exit;
|
||||||
FBridge.RestoreFromTray;
|
RestoreWindowFromTray;
|
||||||
EscapedTitle := StringReplace(AWindowTitle, '\', '\\', [rfReplaceAll]);
|
EscapedTitle := StringReplace(AWindowTitle, '\', '\\', [rfReplaceAll]);
|
||||||
EscapedTitle := StringReplace(EscapedTitle, '"', '\"', [rfReplaceAll]);
|
EscapedTitle := StringReplace(EscapedTitle, '"', '\"', [rfReplaceAll]);
|
||||||
WebBrowser.ExecuteJavaScript(
|
WebBrowser.ExecuteJavaScript(
|
||||||
@@ -1027,7 +1030,7 @@ begin
|
|||||||
// restores it later via ForceForegroundWindow.
|
// restores it later via ForceForegroundWindow.
|
||||||
else if ACmd = 'app/focus' then
|
else if ACmd = 'app/focus' then
|
||||||
begin
|
begin
|
||||||
FBridge.RestoreFromTray;
|
RestoreWindowFromTray;
|
||||||
LogLine('App brought to front (autofill picker)');
|
LogLine('App brought to front (autofill picker)');
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1519,7 +1522,7 @@ end;
|
|||||||
|
|
||||||
procedure TMainForm.BridgeTrayRestore;
|
procedure TMainForm.BridgeTrayRestore;
|
||||||
begin
|
begin
|
||||||
FBridge.RestoreFromTray;
|
RestoreWindowFromTray;
|
||||||
// Notify the JS layer: the UI may want to reset the auto-lock timer,
|
// Notify the JS layer: the UI may want to reset the auto-lock timer,
|
||||||
// refresh state, or show a "welcome back" toast.
|
// refresh state, or show a "welcome back" toast.
|
||||||
WebBrowser.ExecuteJavaScript(
|
WebBrowser.ExecuteJavaScript(
|
||||||
@@ -1545,7 +1548,7 @@ begin
|
|||||||
// Restore the form first so the tray icon goes away and FormDestroy
|
// Restore the form first so the tray icon goes away and FormDestroy
|
||||||
// executes from a normal (non-hidden) state. RestoreFromTray also
|
// executes from a normal (non-hidden) state. RestoreFromTray also
|
||||||
// deletes the tray icon.
|
// deletes the tray icon.
|
||||||
FBridge.RestoreFromTray;
|
RestoreWindowFromTray;
|
||||||
Application.Terminate;
|
Application.Terminate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@@ -1572,6 +1575,18 @@ begin
|
|||||||
else if AEvent = EVENT_SYSTEM_MINIMIZEEND then MainForm.WinStateMinimizeEnd;
|
else if AEvent = EVENT_SYSTEM_MINIMIZEEND then MainForm.WinStateMinimizeEnd;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Single entry point for every tray restore. ExecuteAutofill really minimises
|
||||||
|
// the window (SW_MINIMIZE) before typing, which raises the guard; if the window
|
||||||
|
// is then HIDDEN to the tray instead of being un-minimised, MINIMIZEEND may
|
||||||
|
// never arrive and the guard would stay up forever, silently freezing the
|
||||||
|
// tracked window state. RestoreFromTray replays its own saved placement, so
|
||||||
|
// there is nothing left for the guard to protect by then.
|
||||||
|
procedure TMainForm.RestoreWindowFromTray;
|
||||||
|
begin
|
||||||
|
FIgnoreResizes := False;
|
||||||
|
FBridge.RestoreFromTray;
|
||||||
|
end;
|
||||||
|
|
||||||
// The window is going down: from here until the restore settles, every resize
|
// The window is going down: from here until the restore settles, every resize
|
||||||
// FMX fires is part of that transition, never a user gesture.
|
// FMX fires is part of that transition, never a user gesture.
|
||||||
procedure TMainForm.WinStateMinimizeStart;
|
procedure TMainForm.WinStateMinimizeStart;
|
||||||
|
|||||||
Reference in New Issue
Block a user