Hello Mattias
Can you explain, what you are trying to do? How do you want to solve the bug?
I think I just solved that bug. It was quite easy, once I understand
what to do and especially where to apply the change. I've included the
complete patch (including the show modal patch posted earlier) in the
attachment.
What I did is simple. I saved the (topmost) state of the windows to a
list (which is part of the TWin32WidgetSet) and changed their behavior
to fix the bug. After the modal window is closed, I recall the state of
the windows according to the list.
This is very similar to what the VCL does and I tried to use the same
conventions if possible. Looking at the VCL source code it might be
useful to have those new public functions: NormalizeAllTopMosts;
NormalizeTopMosts; RestoreTopMosts; for other things (like minimize etc.)
The patch needs to be verified, but here everything works as expected.
Christian
P.S.: Any ideas what to do with this patch now?
Index: win32callback.inc
===================================================================
--- win32callback.inc (revision 9708)
+++ win32callback.inc (working copy)
@@ -657,26 +657,37 @@
lControl: TControl;
BoundsOffset: TRect;
begin
- if (lWinControl <> nil) and not (csDesigning in lWinControl.ComponentState)
- and (Lo(LParam) = HTCLIENT) then
- begin
- Windows.GetCursorPos(Windows.POINT(P));
- Windows.ScreenToClient(Window, Windows.POINT(P));
- if GetLCLClientBoundsOffset(lWinControl.Parent, BoundsOffset) then
- begin
- Dec(P.X, BoundsOffset.Left);
- Dec(P.Y, BoundsOffset.Top);
+ if (lWinControl <> nil) and not (csDesigning in
lWinControl.ComponentState) then
+ case Lo(LParam) of
+ HTCLIENT:
+ begin
+ Windows.GetCursorPos(Windows.POINT(P));
+ Windows.ScreenToClient(Window, Windows.POINT(P));
+ if GetLCLClientBoundsOffset(lWinControl.Parent, BoundsOffset) then
+ begin
+ Dec(P.X, BoundsOffset.Left);
+ Dec(P.Y, BoundsOffset.Top);
+ end;
+ // statictext controls do not get WM_SETCURSOR messages...
+ lControl := lWinControl.ControlAtPos(P, false, true);
+ if lControl = nil then
+ lControl := lWinControl;
+ if lControl.Cursor <> crDefault then
+ begin
+ Windows.SetCursor(Windows.LoadCursor(0,
LclCursorToWin32CursorMap[lControl.Cursor]));
+ LMessage.Result := 1;
+ end;
+ end;
+ HTERROR:
+ begin
+ if (Hi(LParam) = WM_LBUTTONDOWN) and
(TWin32WidgetSet(WidgetSet).AppHandle <> 0) and
+ (GetForegroundWindow <>
GetLastActivePopup(TWin32WidgetSet(WidgetSet).AppHandle)) then
+ begin
+ Application.BringToFront;
+ Exit;
+ end;
+ end;
end;
- // statictext controls do not get WM_SETCURSOR messages...
- lControl := lWinControl.ControlAtPos(P, false, true);
- if lControl = nil then
- lControl := lWinControl;
- if lControl.Cursor <> crDefault then
- begin
- Windows.SetCursor(Windows.LoadCursor(0,
LclCursorToWin32CursorMap[lControl.Cursor]));
- LMessage.Result := 1;
- end;
- end;
if LMessage.Result = 0 then
begin
LMessage.Msg := LM_SETCURSOR;
@@ -1144,9 +1155,14 @@
LMessage.Msg := LM_SETEDITABLE;
If Window=TWin32WidgetSet(WidgetSet).FAppHandle then
if WParam=0 then
- DisableApplicationWindows(Window)
- else
+ begin
+ DisableApplicationWindows(Window);
+ TWin32WidgetSet(WidgetSet).NormalizeAllTopMosts;
+ end else
+ begin
+ TWin32WidgetSet(WidgetSet).RestoreTopMosts;
EnableApplicationWindows(Window);
+ end;
If (lWinControl is TCustomFloatSpinEdit) then
EnableFloatSpinEditBuddy(Window, WParam<>0);
Index: win32int.pp
===================================================================
--- win32int.pp (revision 9708)
+++ win32int.pp (working copy)
@@ -146,6 +146,9 @@
FWaitHandlers: array of TWaitHandler;
FWaitPipeHandlers: PPipeEventInfo;
+ FTopMostList: TList;
+ FTopMostLevel: Integer;
+
FThemesActive: boolean;
FThemeLibrary: HMODULE;
IsThemeActive: function: LongBool; stdcall;
@@ -167,6 +170,7 @@
Function WinRegister: Boolean;
Procedure NormalizeIconName(Var IconName: String);
Procedure NormalizeIconName(Var IconName: PChar);
+ procedure DoNormalizeTopMosts(IncludeMain: Boolean);
Public
{ Creates a callback of Lazarus message Msg for Sender }
@@ -198,6 +202,10 @@
procedure UpdateThemesActive;
procedure ShowHide(Sender: TObject);
+ procedure NormalizeAllTopMosts;
+ procedure NormalizeTopMosts;
+ procedure RestoreTopMosts;
+
// create and destroy
function CreateComponent(Sender : TObject): THandle; override;
function CreateTimer(Interval: integer; TimerFunc: TFNTimerProc) :
integer; override;
@@ -316,4 +324,4 @@
Assert(False, 'Trace:win32int.pp - Finalization');
-end.
\ No newline at end of file
+end.
Index: win32object.inc
===================================================================
--- win32object.inc (revision 9708)
+++ win32object.inc (working copy)
@@ -32,6 +32,7 @@
Begin
Inherited Create;
FTimerData := TList.Create;
+ FTopMostList := TList.Create;
FMetrics.cbSize := SizeOf(FMetrics);
FMetricsFailed := not Windows.SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
SizeOf(FMetrics), @FMetrics, 0);
@@ -105,7 +106,9 @@
end;
FTimerData.Free;
+ FTopMostList.Free;
+
if FAppHandle <> 0 then
DestroyWindow(FAppHandle);
@@ -224,8 +227,16 @@
Brings the entire application on top of all other non-topmost programs
------------------------------------------------------------------------------}
procedure TWin32WidgetSet.AppBringToFront;
+var
+ TopWindow: HWnd;
begin
- Windows.SetForegroundWindow(FAppHandle);
+ if FAppHandle <> 0 then
+ begin
+ TopWindow := GetLastActivePopup(FAppHandle);
+ if (TopWindow <> 0) and (TopWindow <> FAppHandle) and
+ IsWindowVisible(TopWindow) and IsWindowEnabled(TopWindow) then
+ Windows.SetForegroundWindow(TopWindow);
+ end;
end;
procedure TWin32WidgetSet.SetDesigning(AComponent: TComponent);
@@ -564,6 +575,56 @@
IconName := StrToPChar(Str);
End;
+type
+ PTopMostEnumInfo = ^TTopMostEnumInfo;
+ TTopMostEnumInfo = record
+ TopWindow: HWND;
+ IncludeMain: Boolean;
+ W32Widget: TWin32WidgetSet;
+ end;
+
+function GetTopMostWindows(Handle: HWND; Info: LongInt): BOOL; stdcall;
+begin
+ Result := True;
+ if GetWindow(Handle, GW_OWNER) = PTopMostEnumInfo(Info)^.W32Widget.AppHandle
then
+ if (GetWindowLong(Handle, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0) and
+ (PTopMostEnumInfo(Info)^.IncludeMain {or (Application.MainForm = nil) or
+ (Handle <> Application.MainForm.Handle)}) then
+ PTopMostEnumInfo(Info)^.W32Widget.FTopMostList.Add(Pointer(Handle))
+ else
+ begin
+ PTopMostEnumInfo(Info)^.TopWindow := Handle;
+ Result := False;
+ end;
+end;
+
+procedure TWin32WidgetSet.DoNormalizeTopMosts(IncludeMain: Boolean);
+var
+ I: Integer;
+ Info: TTopMostEnumInfo;
+begin
+ if FAppHandle <> 0 then
+ begin
+ if FTopMostLevel = 0 then
+ begin
+ Info.TopWindow := FAppHandle;
+ Info.IncludeMain := IncludeMain;
+ Info.W32Widget := self;
+ EnumWindows(@GetTopMostWindows, Longint(@Info));
+ if FTopMostList.Count <> 0 then
+ begin
+ Info.TopWindow := GetWindow(Info.TopWindow, GW_HWNDPREV);
+ if GetWindowLong(Info.TopWindow, GWL_EXSTYLE) and WS_EX_TOPMOST <> 0
then
+ Info.TopWindow := HWND_NOTOPMOST;
+ for I := FTopMostList.Count - 1 downto 0 do
+ SetWindowPos(HWND(FTopMostList[I]), Info.TopWindow, 0, 0, 0, 0,
+ SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOOWNERZORDER);
+ end;
+ end;
+ Inc(FTopMostLevel);
+ end;
+end;
+
{------------------------------------------------------------------------------
Function: TWin32WidgetSet.CreateComponent
Params: Sender - object for which to create visual representation
@@ -637,6 +698,33 @@
End;
End;
+procedure TWin32WidgetSet.NormalizeAllTopMosts;
+begin
+ DoNormalizeTopMosts(True);
+end;
+
+procedure TWin32WidgetSet.NormalizeTopMosts;
+begin
+ DoNormalizeTopMosts(False);
+end;
+
+procedure TWin32WidgetSet.RestoreTopMosts;
+var
+ I: Integer;
+begin
+ if (AppHandle <> 0) and (FTopMostLevel > 0) then
+ begin
+ Dec(FTopMostLevel);
+ if FTopMostLevel = 0 then
+ begin
+ for I := FTopMostList.Count - 1 downto 0 do
+ SetWindowPos(HWND(FTopMostList[I]), HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE or SWP_NOSIZE or SWP_NOACTIVATE or SWP_NOOWNERZORDER);
+ FTopMostList.Clear;
+ end;
+ end;
+end;
+
{------------------------------------------------------------------------------
Method: TWin32WidgetSet.DCReDraw
Params: CanvasHandle - HDC to redraw