gtk2: fixed application deactivate on switch using alt-tab

This commit is contained in:
mattias 2024-12-13 12:11:48 +01:00
parent 8e72df6b62
commit b66a32c36b
4 changed files with 52 additions and 21 deletions

View File

@ -971,7 +971,6 @@ begin
{$ENDIF} {$ENDIF}
//DebugLn('GTKKillFocusCB ',DbgSName(TObject(Data)),' ',GetWidgetDebugReport(Widget)); //DebugLn('GTKKillFocusCB ',DbgSName(TObject(Data)),' ',GetWidgetDebugReport(Widget));
{$IFDEF VerboseFocus} {$IFDEF VerboseFocus}
NeedShiftUpdateAfternFocus := True; // <- JRA: this doesn't look like simply log !!!
LCLObject:=TObject(data); LCLObject:=TObject(data);
DebugLn(['GTKillFocusCB Widget=',DbgS(Widget),' Event^.theIn=',Event^._in, DebugLn(['GTKillFocusCB Widget=',DbgS(Widget),' Event^.theIn=',Event^._in,
' LCLObject=',dbgsName(LCLobject)]); ' LCLObject=',dbgsName(LCLobject)]);
@ -1002,7 +1001,6 @@ begin
{$ENDIF} {$ENDIF}
//DebugLn('GTKKillFocusCBAfter ',DbgSName(TObject(Data)),' ',GetWidgetDebugReport(Widget)); //DebugLn('GTKKillFocusCBAfter ',DbgSName(TObject(Data)),' ',GetWidgetDebugReport(Widget));
{$IFDEF VerboseFocus} {$IFDEF VerboseFocus}
NeedShiftUpdateAfternFocus := True; // <- JRA: this doesn't look like simply log !!!
LCLObject:=TObject(data); LCLObject:=TObject(data);
DebugLnEnter(['GTKillFocusCBAfter INIT Widget=',DbgS(Widget),' Event^.theIn=',Event^._in, DebugLnEnter(['GTKillFocusCBAfter INIT Widget=',DbgS(Widget),' Event^.theIn=',Event^._in,
' LCLObject=',dbgsName(LCLObject)]); ' LCLObject=',dbgsName(LCLObject)]);

View File

@ -325,7 +325,8 @@ type
procedure HideAllHints; procedure HideAllHints;
procedure RestoreAllHints; procedure RestoreAllHints;
{$ENDIF} {$ENDIF}
procedure StartFocusTimer; procedure StartAppFocusTimer;
procedure StopAppFocusTimer;
property AppActive: Boolean read GetAppActive write SetAppActive; property AppActive: Boolean read GetAppActive write SetAppActive;
property IsLibraryInstance: Boolean read FIsLibraryInstance; property IsLibraryInstance: Boolean read FIsLibraryInstance;
property GtkIsTerminated: Boolean read FGtkTerminated; property GtkIsTerminated: Boolean read FGtkTerminated;

View File

@ -2776,13 +2776,12 @@ function gtkAppFocusTimer({%H-}Data: gPointer):gBoolean; cdecl;
// needed by app activate/deactivate // needed by app activate/deactivate
begin begin
Result := CallBackDefaultReturn; Result := CallBackDefaultReturn;
TGtk2WidgetSet(WidgetSet).StopAppFocusTimer;
if TGtk2WidgetSet(WidgetSet).LastFocusIn = nil then if TGtk2WidgetSet(WidgetSet).LastFocusIn = nil then
TGtk2WidgetSet(WidgetSet).AppActive := False; TGtk2WidgetSet(WidgetSet).AppActive := False;
gtk_timeout_remove(TGtk2WidgetSet(WidgetSet).FocusTimer);
TGtk2WidgetSet(WidgetSet).FocusTimer := 0;
end; end;
procedure TGtk2WidgetSet.StartFocusTimer; procedure TGtk2WidgetSet.StartAppFocusTimer;
begin begin
FLastFocusIn := nil; FLastFocusIn := nil;
if FocusTimer <> 0 then if FocusTimer <> 0 then
@ -2790,6 +2789,13 @@ begin
FocusTimer := gtk_timeout_add(50, TGtkFunction(@gtkAppFocusTimer), nil); FocusTimer := gtk_timeout_add(50, TGtkFunction(@gtkAppFocusTimer), nil);
end; end;
procedure TGtk2WidgetSet.StopAppFocusTimer;
begin
if FocusTimer = 0 then exit;
gtk_timeout_remove(FocusTimer);
FocusTimer := 0;
end;
procedure TGtk2WidgetSet.InitStockItems; procedure TGtk2WidgetSet.InitStockItems;
var var
LogBrush: TLogBrush; LogBrush: TLogBrush;

View File

@ -269,31 +269,52 @@ begin
GDK_FOCUS_CHANGE: GDK_FOCUS_CHANGE:
begin begin
ACtl := TWinControl(Data); ACtl := TWinControl(Data);
// Application.Activate/Deactivate is done via a timer
// Every time a form looses the focus a timer is started
// and every time focus is gained the timer is stopped.
// Note: It seems gtk2 itself does not always detect focus lost
// For example in LinuxMint switching from a TEdit using Alt-Tab to another
// app generates a focus out, but gtk2 still shows the TEdit focused
// with blinking cursor.
// Switching focus using the mouse to another app, also generates
// a focus out, and gtk2 hides the blinking cursor and focus coloring.
// Switching to another workspace does not generate a focus out event <sigh>.
if PGdkEventFocus(event)^._in = 0 then if PGdkEventFocus(event)^._in = 0 then
begin begin
{$IFDEF HASX} {$IFDEF HASX}
XDisplay := gdk_display; if ACtl.Parent<>nil then
XGetInputFocus(XDisplay, @Window, @RevertStatus); begin
// Window - 1 is our frame ! XDisplay := gdk_display;
if (RevertStatus = RevertToParent) and XGetInputFocus(XDisplay, @Window, @RevertStatus);
(GDK_WINDOW_XID(Widget^.Window) = Window - 1) then // Window - 1 is our frame !
exit(True); if (RevertStatus = RevertToParent) and
(GDK_WINDOW_XID(Widget^.Window) = Window - 1) then
begin
// Note: on LinuxMint switching via Alt-Tab to another window
// generates RevertToParent. The above
//
exit(True);
end;
end;
{$ENDIF} {$ENDIF}
with Gtk2WidgetSet do with Gtk2WidgetSet do
begin begin
LastFocusOut := {%H-}PGtkWidget(ACtl.Handle); LastFocusOut := {%H-}PGtkWidget(ACtl.Handle);
if LastFocusOut = LastFocusIn then if LastFocusOut = LastFocusIn then
StartFocusTimer; StartAppFocusTimer;
end; end;
end else end else
begin begin
with Gtk2WidgetSet do with Gtk2WidgetSet do
begin begin
StopAppFocusTimer;
LastFocusIn := {%H-}PGtkWidget(ACtl.Handle); LastFocusIn := {%H-}PGtkWidget(ACtl.Handle);
if not AppActive then if not AppActive then
AppActive := True; AppActive := True;
end; end;
end; end;
if GTK_IS_WINDOW(Widget) and if GTK_IS_WINDOW(Widget) and
(g_object_get_data({%H-}PGObject(ACtl.Handle),'lcl_nonmodal_over_modal') <> nil) then (g_object_get_data({%H-}PGObject(ACtl.Handle),'lcl_nonmodal_over_modal') <> nil) then
begin begin
@ -308,9 +329,14 @@ end;
class procedure TGtk2WSCustomForm.SetCallbacks(const AWidget: PGtkWidget; class procedure TGtk2WSCustomForm.SetCallbacks(const AWidget: PGtkWidget;
const AWidgetInfo: PWidgetInfo); const AWidgetInfo: PWidgetInfo);
var
aLCLObj: TObject;
aLCLControl: TWinControl;
begin begin
TGtk2WSWinControl.SetCallbacks(PGtkObject(AWidget), TComponent(AWidgetInfo^.LCLObject)); aLCLObj:=AWidgetInfo^.LCLObject;
if (TWinControl(AWidgetInfo^.LCLObject).Parent = nil) and (TWinControl(AWidgetInfo^.LCLObject).ParentWindow = 0) then aLCLControl:=TWinControl(aLCLObj);
TGtk2WSWinControl.SetCallbacks(PGtkObject(AWidget), aLCLControl);
if (aLCLControl.Parent = nil) and (aLCLControl.ParentWindow = 0) then
with TGTK2WidgetSet(Widgetset) do with TGTK2WidgetSet(Widgetset) do
begin begin
{$IFDEF HASX} {$IFDEF HASX}
@ -318,18 +344,18 @@ begin
// see http://bugs.freepascal.org/view.php?id=17523 // see http://bugs.freepascal.org/view.php?id=17523
if not compositeManagerRunning then if not compositeManagerRunning then
{$ENDIF} {$ENDIF}
SetCallback(LM_CONFIGUREEVENT, PGtkObject(AWidget), AWidgetInfo^.LCLObject); SetCallback(LM_CONFIGUREEVENT, PGtkObject(AWidget), aLCLObj);
SetCallback(LM_CLOSEQUERY, PGtkObject(AWidget), AWidgetInfo^.LCLObject); SetCallback(LM_CLOSEQUERY, PGtkObject(AWidget), aLCLObj);
SetCallBack(LM_ACTIVATE, PGtkObject(AWidget), AWidgetInfo^.LCLObject); SetCallBack(LM_ACTIVATE, PGtkObject(AWidget), aLCLObj);
if (gtk_major_version = 2) and (gtk_minor_version <= 8) then if (gtk_major_version = 2) and (gtk_minor_version <= 8) then
begin begin
SetCallback(LM_HSCROLL, PGtkObject(AWidget), AWidgetInfo^.LCLObject); SetCallback(LM_HSCROLL, PGtkObject(AWidget), aLCLObj);
SetCallback(LM_VSCROLL, PGtkObject(AWidget), AWidgetInfo^.LCLObject); SetCallback(LM_VSCROLL, PGtkObject(AWidget), aLCLObj);
end; end;
end; end;
g_signal_connect(PGtkObject(AWidgetInfo^.CoreWidget), 'event', g_signal_connect(PGtkObject(AWidgetInfo^.CoreWidget), 'event',
gtk_signal_func(@Gtk2FormEvent), AWidgetInfo^.LCLObject); gtk_signal_func(@Gtk2FormEvent), aLCLObj);
end; end;
class function TGtk2WSCustomForm.CanFocus(const AWinControl: TWinControl class function TGtk2WSCustomForm.CanFocus(const AWinControl: TWinControl